]>
Commit | Line | Data |
---|---|---|
5ba3f43e A |
1 | /* |
2 | * Copyright (c) 2007 Apple Inc. All rights reserved. | |
3 | */ | |
4 | /* | |
5 | * CDDL HEADER START | |
6 | * | |
7 | * The contents of this file are subject to the terms of the | |
8 | * Common Development and Distribution License, Version 1.0 only | |
9 | * (the "License"). You may not use this file except in compliance | |
10 | * with the License. | |
11 | * | |
12 | * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE | |
13 | * or http://www.opensolaris.org/os/licensing. | |
14 | * See the License for the specific language governing permissions | |
15 | * and limitations under the License. | |
16 | * | |
17 | * When distributing Covered Code, include this CDDL HEADER in each | |
18 | * file and include the License file at usr/src/OPENSOLARIS.LICENSE. | |
19 | * If applicable, add the following below this CDDL HEADER, with the | |
20 | * fields enclosed by brackets "[]" replaced with your own identifying | |
21 | * information: Portions Copyright [yyyy] [name of copyright owner] | |
22 | * | |
23 | * CDDL HEADER END | |
24 | */ | |
25 | /* | |
26 | * Copyright 2005 Sun Microsystems, Inc. All rights reserved. | |
27 | * Use is subject to license terms. | |
28 | */ | |
29 | ||
5ba3f43e A |
30 | #include <sys/fasttrap_isa.h> |
31 | #include <sys/fasttrap_impl.h> | |
32 | #include <sys/dtrace.h> | |
33 | #include <sys/dtrace_impl.h> | |
34 | #include <kern/task.h> | |
35 | #include <vm/pmap.h> | |
36 | #include <vm/vm_map.h> | |
37 | #include <mach/mach_vm.h> | |
38 | #include <arm/proc_reg.h> | |
39 | #include <arm/caches_internal.h> | |
40 | ||
41 | #include <sys/dtrace_ptss.h> | |
42 | #include <kern/debug.h> | |
43 | ||
44 | #include <pexpert/pexpert.h> | |
45 | ||
46 | extern dtrace_id_t dtrace_probeid_error; | |
47 | ||
48 | /* Solaris proc_t is the struct. Darwin's proc_t is a pointer to it. */ | |
49 | #define proc_t struct proc /* Steer clear of the Darwin typedef for proc_t */ | |
50 | ||
51 | extern int dtrace_decode_arm(uint32_t instr); | |
52 | extern int dtrace_decode_thumb(uint32_t instr); | |
53 | ||
54 | /* | |
55 | * Lossless User-Land Tracing on ARM | |
56 | * --------------------------------- | |
57 | * | |
58 | * The details here will be fleshed out as more of this is implemented. The | |
59 | * basic design will be the same way as tracing works in x86. | |
60 | * | |
61 | * Some ARM specific issues: | |
62 | * | |
63 | * We need to patch differently for ARM instructions and Thumb instructions. | |
64 | * When we hit a probe, we check to see if the mode we're currently in is the | |
65 | * same as the mode we're patching for. If not, we remove the tracepoint and | |
66 | * abort. This ARM/Thumb information is pulled in from the arch specific | |
67 | * information in the fasttrap probe. | |
68 | * | |
69 | * On ARM, any instruction that uses registers can also use the pc as a | |
70 | * register. This presents problems during emulation because we have copied | |
71 | * the instruction and thus the pc can be different. Currently we've emulated | |
72 | * any instructions that use the pc if they can be used in a return probe. | |
73 | * Eventually we will want to support all instructions that use the pc, but | |
74 | * to do so requires disassembling the instruction and reconstituting it by | |
75 | * substituting a different register. | |
76 | * | |
77 | */ | |
78 | ||
79 | #define THUMB_INSTR(x) (*(uint16_t*) &(x)) | |
80 | ||
0a7de745 A |
81 | #define SIGNEXTEND(x, v) ((((int) (x)) << (32-(v))) >> (32-(v))) |
82 | #define ALIGNADDR(x, v) (((x) >> (v)) << (v)) | |
5ba3f43e A |
83 | #define GETITSTATE(x) ((((x) >> 8) & 0xFC) | (((x) >> 25) & 0x3)) |
84 | #define ISLASTINIT(x) (((x) & 0xF) == 8) | |
85 | ||
0a7de745 A |
86 | #define SET16(x, w) *((uint16_t*) (x)) = (w) |
87 | #define SET32(x, w) *((uint32_t*) (x)) = (w) | |
5ba3f43e A |
88 | |
89 | #define IS_ARM_NOP(x) ((x) == 0xE1A00000) | |
90 | /* Marker for is-enabled probes */ | |
91 | #define IS_ARM_IS_ENABLED(x) ((x) == 0xE0200000) | |
92 | ||
93 | #define IS_THUMB_NOP(x) ((x) == 0x46C0) | |
94 | /* Marker for is-enabled probes */ | |
95 | #define IS_THUMB_IS_ENABLED(x) ((x) == 0x4040) | |
96 | ||
97 | #define ARM_LDM_UF (1 << 23) | |
98 | #define ARM_LDM_PF (1 << 24) | |
99 | #define ARM_LDM_WF (1 << 21) | |
100 | ||
101 | #define ARM_LDR_UF (1 << 23) | |
102 | #define ARM_LDR_BF (1 << 22) | |
103 | ||
104 | extern int dtrace_arm_condition_true(int cond, int cpsr); | |
105 | ||
5ba3f43e A |
106 | int |
107 | fasttrap_tracepoint_init(proc_t *p, fasttrap_tracepoint_t *tp, | |
0a7de745 | 108 | user_addr_t pc, fasttrap_probe_type_t type) |
5ba3f43e A |
109 | { |
110 | #pragma unused(type) | |
111 | uint32_t instr; | |
112 | ||
113 | /* | |
114 | * Read the instruction at the given address out of the process's | |
115 | * address space. We don't have to worry about a debugger | |
116 | * changing this instruction before we overwrite it with our trap | |
117 | * instruction since P_PR_LOCK is set. Since instructions can span | |
118 | * pages, we potentially read the instruction in two parts. If the | |
119 | * second part fails, we just zero out that part of the instruction. | |
120 | */ | |
0a7de745 | 121 | /* |
5ba3f43e | 122 | * APPLE NOTE: Of course, we do not have a P_PR_LOCK, so this is racey... |
0a7de745 | 123 | */ |
5ba3f43e | 124 | |
0a7de745 A |
125 | if (uread(p, &instr, 4, pc) != 0) { |
126 | return -1; | |
127 | } | |
5ba3f43e A |
128 | |
129 | /* We want &instr to always point to the saved instruction, so just copy the | |
130 | * whole thing When cast to a pointer to a uint16_t, that will give us a | |
131 | * pointer to the first two bytes, which is the thumb instruction. | |
132 | */ | |
133 | tp->ftt_instr = instr; | |
134 | ||
135 | if (tp->ftt_fntype != FASTTRAP_FN_DONE_INIT) { | |
0a7de745 A |
136 | switch (tp->ftt_fntype) { |
137 | case FASTTRAP_FN_UNKNOWN: | |
138 | /* Can't instrument without any information. We can add some heuristics later if necessary. */ | |
139 | return -1; | |
5ba3f43e | 140 | |
0a7de745 A |
141 | case FASTTRAP_FN_USDT: |
142 | if (IS_ARM_NOP(instr) || IS_ARM_IS_ENABLED(instr)) { | |
5ba3f43e | 143 | tp->ftt_thumb = 0; |
0a7de745 | 144 | } else if (IS_THUMB_NOP(THUMB_INSTR(instr)) || IS_THUMB_IS_ENABLED(THUMB_INSTR(instr))) { |
5ba3f43e | 145 | tp->ftt_thumb = 1; |
0a7de745 A |
146 | } else { |
147 | /* Shouldn't reach here - this means we don't recognize | |
148 | * the instruction at one of the USDT probe locations | |
149 | */ | |
150 | return -1; | |
151 | } | |
152 | tp->ftt_fntype = FASTTRAP_FN_DONE_INIT; | |
153 | break; | |
154 | ||
155 | case FASTTRAP_FN_ARM: | |
156 | tp->ftt_thumb = 0; | |
157 | tp->ftt_fntype = FASTTRAP_FN_DONE_INIT; | |
158 | break; | |
159 | ||
160 | case FASTTRAP_FN_THUMB: | |
161 | tp->ftt_thumb = 1; | |
162 | tp->ftt_fntype = FASTTRAP_FN_DONE_INIT; | |
163 | break; | |
5ba3f43e | 164 | |
0a7de745 A |
165 | default: |
166 | return -1; | |
5ba3f43e A |
167 | } |
168 | } | |
169 | ||
170 | if (tp->ftt_thumb) { | |
171 | tp->ftt_type = dtrace_decode_thumb(instr); | |
172 | } else { | |
173 | tp->ftt_type = dtrace_decode_arm(instr); | |
174 | } | |
175 | ||
176 | if (tp->ftt_type == FASTTRAP_T_INV) { | |
177 | /* This is an instruction we either don't recognize or can't instrument */ | |
178 | printf("dtrace: fasttrap: Unrecognized instruction: %08x at %08x\n", | |
0a7de745 A |
179 | (tp->ftt_thumb && dtrace_instr_size(tp->ftt_instr, tp->ftt_thumb) == 2) ? tp->ftt_instr1 : instr, pc); |
180 | return -1; | |
5ba3f43e A |
181 | } |
182 | ||
0a7de745 | 183 | return 0; |
5ba3f43e A |
184 | } |
185 | ||
5ba3f43e A |
186 | int |
187 | fasttrap_tracepoint_install(proc_t *p, fasttrap_tracepoint_t *tp) | |
188 | { | |
189 | /* The thumb patch is a 2 byte instruction regardless of the size of the original instruction */ | |
190 | uint32_t instr; | |
191 | int size = tp->ftt_thumb ? 2 : 4; | |
192 | ||
193 | if (tp->ftt_thumb) { | |
194 | *((uint16_t*) &instr) = FASTTRAP_THUMB_INSTR; | |
195 | } else { | |
196 | instr = FASTTRAP_ARM_INSTR; | |
197 | } | |
198 | ||
0a7de745 A |
199 | if (uwrite(p, &instr, size, tp->ftt_pc) != 0) { |
200 | return -1; | |
201 | } | |
5ba3f43e A |
202 | |
203 | tp->ftt_installed = 1; | |
204 | ||
0a7de745 | 205 | return 0; |
5ba3f43e A |
206 | } |
207 | ||
208 | int | |
209 | fasttrap_tracepoint_remove(proc_t *p, fasttrap_tracepoint_t *tp) | |
210 | { | |
211 | /* The thumb patch is a 2 byte instruction regardless of the size of the original instruction */ | |
212 | uint32_t instr; | |
213 | int size = tp->ftt_thumb ? 2 : 4; | |
214 | ||
215 | /* | |
216 | * Distinguish between read or write failures and a changed | |
217 | * instruction. | |
218 | */ | |
0a7de745 | 219 | if (uread(p, &instr, size, tp->ftt_pc) != 0) { |
5ba3f43e | 220 | goto end; |
0a7de745 | 221 | } |
5ba3f43e | 222 | if (tp->ftt_thumb) { |
0a7de745 | 223 | if (*((uint16_t*) &instr) != FASTTRAP_THUMB_INSTR) { |
5ba3f43e | 224 | goto end; |
0a7de745 | 225 | } |
5ba3f43e | 226 | } else { |
0a7de745 | 227 | if (instr != FASTTRAP_ARM_INSTR) { |
5ba3f43e | 228 | goto end; |
0a7de745 A |
229 | } |
230 | } | |
231 | if (uwrite(p, &tp->ftt_instr, size, tp->ftt_pc) != 0) { | |
232 | return -1; | |
5ba3f43e | 233 | } |
5ba3f43e A |
234 | |
235 | end: | |
236 | tp->ftt_installed = 0; | |
237 | ||
0a7de745 | 238 | return 0; |
5ba3f43e A |
239 | } |
240 | ||
241 | static void | |
242 | fasttrap_return_common(proc_t *p, arm_saved_state_t *regs, user_addr_t pc, user_addr_t new_pc) | |
243 | { | |
244 | pid_t pid = p->p_pid; | |
245 | fasttrap_tracepoint_t *tp; | |
246 | fasttrap_bucket_t *bucket; | |
247 | fasttrap_id_t *id; | |
248 | lck_mtx_t *pid_mtx; | |
249 | int retire_tp = 1; | |
250 | ||
251 | pid_mtx = &cpu_core[CPU->cpu_id].cpuc_pid_lock; | |
252 | lck_mtx_lock(pid_mtx); | |
253 | bucket = &fasttrap_tpoints.fth_table[FASTTRAP_TPOINTS_INDEX(pid, pc)]; | |
254 | ||
255 | for (tp = bucket->ftb_data; tp != NULL; tp = tp->ftt_next) { | |
256 | if (pid == tp->ftt_pid && pc == tp->ftt_pc && | |
0a7de745 | 257 | tp->ftt_proc->ftpc_acount != 0) { |
5ba3f43e | 258 | break; |
0a7de745 | 259 | } |
5ba3f43e A |
260 | } |
261 | ||
262 | /* | |
263 | * Don't sweat it if we can't find the tracepoint again; unlike | |
264 | * when we're in fasttrap_pid_probe(), finding the tracepoint here | |
265 | * is not essential to the correct execution of the process. | |
0a7de745 | 266 | */ |
5ba3f43e A |
267 | if (tp == NULL) { |
268 | lck_mtx_unlock(pid_mtx); | |
269 | return; | |
270 | } | |
271 | ||
272 | for (id = tp->ftt_retids; id != NULL; id = id->fti_next) { | |
273 | fasttrap_probe_t *probe = id->fti_probe; | |
274 | /* | |
275 | * If there's a branch that could act as a return site, we | |
276 | * need to trace it, and check here if the program counter is | |
277 | * external to the function. | |
278 | */ | |
279 | if (tp->ftt_type != FASTTRAP_T_LDM_PC && | |
280 | tp->ftt_type != FASTTRAP_T_POP_PC && | |
0a7de745 | 281 | new_pc - probe->ftp_faddr < probe->ftp_fsize) { |
5ba3f43e | 282 | continue; |
0a7de745 | 283 | } |
5ba3f43e A |
284 | |
285 | if (probe->ftp_prov->ftp_provider_type == DTFTP_PROVIDER_ONESHOT) { | |
cb323159 A |
286 | if (os_atomic_xchg(&probe->ftp_triggered, 1, relaxed)) { |
287 | /* already triggered */ | |
5ba3f43e A |
288 | continue; |
289 | } | |
290 | } | |
291 | /* | |
292 | * If we have at least one probe associated that | |
293 | * is not a oneshot probe, don't remove the | |
294 | * tracepoint | |
295 | */ | |
296 | else { | |
297 | retire_tp = 0; | |
298 | } | |
f427ee49 | 299 | #if defined(XNU_TARGET_OS_OSX) |
5ba3f43e A |
300 | if (ISSET(current_proc()->p_lflag, P_LNOATTACH)) { |
301 | dtrace_probe(dtrace_probeid_error, 0 /* state */, id->fti_probe->ftp_id, | |
0a7de745 | 302 | 1 /* ndx */, -1 /* offset */, DTRACEFLT_UPRIV); |
5ba3f43e A |
303 | #else |
304 | if (FALSE) { | |
f427ee49 | 305 | #endif /* defined(XNU_TARGET_OS_OSX) */ |
5ba3f43e A |
306 | } else { |
307 | dtrace_probe(id->fti_probe->ftp_id, | |
0a7de745 A |
308 | pc - id->fti_probe->ftp_faddr, |
309 | regs->r[0], 0, 0, 0); | |
5ba3f43e A |
310 | } |
311 | } | |
312 | if (retire_tp) { | |
313 | fasttrap_tracepoint_retire(p, tp); | |
314 | } | |
315 | ||
316 | lck_mtx_unlock(pid_mtx); | |
317 | } | |
318 | ||
cb323159 A |
319 | #if DEBUG |
320 | __dead2 | |
321 | #endif | |
5ba3f43e A |
322 | static void |
323 | fasttrap_sigsegv(proc_t *p, uthread_t t, user_addr_t addr, arm_saved_state_t *regs) | |
324 | { | |
325 | /* TODO: This function isn't implemented yet. In debug mode, panic the system to | |
326 | * find out why we're hitting this point. In other modes, kill the process. | |
327 | */ | |
328 | #if DEBUG | |
329 | #pragma unused(p,t,addr,arm_saved_state) | |
330 | panic("fasttrap: sigsegv not yet implemented"); | |
331 | #else | |
332 | #pragma unused(p,t,addr) | |
333 | /* Kill the process */ | |
334 | regs->pc = 0; | |
335 | #endif | |
336 | ||
337 | #if 0 | |
338 | proc_lock(p); | |
339 | ||
340 | /* Set fault address and mark signal */ | |
341 | t->uu_code = addr; | |
342 | t->uu_siglist |= sigmask(SIGSEGV); | |
343 | ||
0a7de745 | 344 | /* |
5ba3f43e A |
345 | * XXX These two line may be redundant; if not, then we need |
346 | * XXX to potentially set the data address in the machine | |
347 | * XXX specific thread state structure to indicate the address. | |
0a7de745 | 348 | */ |
5ba3f43e A |
349 | t->uu_exception = KERN_INVALID_ADDRESS; /* SIGSEGV */ |
350 | t->uu_subcode = 0; /* XXX pad */ | |
0a7de745 A |
351 | |
352 | proc_unlock(p); | |
353 | ||
5ba3f43e A |
354 | /* raise signal */ |
355 | signal_setast(t->uu_context.vc_thread); | |
356 | #endif | |
357 | } | |
358 | ||
359 | static void | |
360 | fasttrap_usdt_args(fasttrap_probe_t *probe, arm_saved_state_t *regs, int argc, | |
361 | uint32_t *argv) | |
362 | { | |
363 | int i, x, cap = MIN(argc, probe->ftp_nargs); | |
364 | ||
365 | for (i = 0; i < cap; i++) { | |
366 | x = probe->ftp_argmap[i]; | |
367 | ||
368 | if (x < 4) { | |
369 | argv[i] = regs->r[x]; | |
370 | } else { | |
371 | fasttrap_fuword32_noerr(regs->sp + (x - 4) * sizeof(uint32_t), &argv[i]); | |
372 | } | |
373 | } | |
374 | ||
375 | for (; i < argc; i++) { | |
376 | argv[i] = 0; | |
377 | } | |
378 | } | |
379 | ||
0a7de745 A |
380 | static void |
381 | set_thumb_flag(arm_saved_state_t *regs, user_addr_t pc) | |
5ba3f43e A |
382 | { |
383 | if (pc & 1) { | |
384 | regs->cpsr |= PSR_TF; | |
385 | } else { | |
386 | regs->cpsr &= ~PSR_TF; | |
387 | } | |
388 | } | |
389 | ||
390 | int | |
391 | fasttrap_pid_probe(arm_saved_state_t *regs) | |
392 | { | |
393 | proc_t *p = current_proc(); | |
394 | user_addr_t new_pc = 0; | |
395 | fasttrap_bucket_t *bucket; | |
396 | lck_mtx_t *pid_mtx; | |
397 | fasttrap_tracepoint_t *tp, tp_local; | |
398 | pid_t pid; | |
399 | dtrace_icookie_t cookie; | |
400 | uint_t is_enabled = 0; | |
401 | int instr_size; | |
402 | int was_simulated = 1, retire_tp = 1; | |
403 | ||
404 | user_addr_t pc = regs->pc; | |
405 | ||
406 | uthread_t uthread = (uthread_t) get_bsdthread_info(current_thread()); | |
407 | ||
408 | /* | |
409 | * It's possible that a user (in a veritable orgy of bad planning) | |
410 | * could redirect this thread's flow of control before it reached the | |
411 | * return probe fasttrap. In this case we need to kill the process | |
412 | * since it's in a unrecoverable state. | |
413 | */ | |
414 | if (uthread->t_dtrace_step) { | |
415 | ASSERT(uthread->t_dtrace_on); | |
416 | fasttrap_sigtrap(p, uthread, pc); | |
0a7de745 | 417 | return 0; |
5ba3f43e A |
418 | } |
419 | ||
420 | /* | |
421 | * Clear all user tracing flags. | |
422 | */ | |
423 | uthread->t_dtrace_ft = 0; | |
424 | uthread->t_dtrace_pc = 0; | |
425 | uthread->t_dtrace_npc = 0; | |
426 | uthread->t_dtrace_scrpc = 0; | |
427 | uthread->t_dtrace_astpc = 0; | |
428 | ||
429 | /* | |
430 | * Treat a child created by a call to vfork(2) as if it were its | |
431 | * parent. We know that there's only one thread of control in such a | |
432 | * process: this one. | |
433 | */ | |
434 | if (p->p_lflag & P_LINVFORK) { | |
435 | proc_list_lock(); | |
0a7de745 | 436 | while (p->p_lflag & P_LINVFORK) { |
5ba3f43e | 437 | p = p->p_pptr; |
0a7de745 | 438 | } |
5ba3f43e A |
439 | proc_list_unlock(); |
440 | } | |
441 | ||
442 | pid = p->p_pid; | |
443 | pid_mtx = &cpu_core[CPU->cpu_id].cpuc_pid_lock; | |
444 | lck_mtx_lock(pid_mtx); | |
0a7de745 | 445 | bucket = &fasttrap_tpoints.fth_table[FASTTRAP_TPOINTS_INDEX(pid, pc)]; |
5ba3f43e A |
446 | |
447 | /* | |
448 | * Lookup the tracepoint that the process just hit. | |
449 | */ | |
450 | for (tp = bucket->ftb_data; tp != NULL; tp = tp->ftt_next) { | |
451 | if (pid == tp->ftt_pid && pc == tp->ftt_pc && | |
0a7de745 | 452 | tp->ftt_proc->ftpc_acount != 0) { |
5ba3f43e | 453 | break; |
0a7de745 | 454 | } |
5ba3f43e A |
455 | } |
456 | ||
457 | /* | |
458 | * If we couldn't find a matching tracepoint, either a tracepoint has | |
459 | * been inserted without using the pid<pid> ioctl interface (see | |
460 | * fasttrap_ioctl), or somehow we have mislaid this tracepoint. | |
461 | */ | |
462 | if (tp == NULL) { | |
463 | lck_mtx_unlock(pid_mtx); | |
0a7de745 | 464 | return -1; |
5ba3f43e A |
465 | } |
466 | ||
467 | /* Default to always execute */ | |
468 | int condition_code = 0xE; | |
469 | if (tp->ftt_thumb) { | |
470 | uint32_t itstate = GETITSTATE(regs->cpsr); | |
471 | if (itstate != 0) { | |
472 | /* In IT block, make sure it's the last statement in the block */ | |
473 | if (ISLASTINIT(itstate)) { | |
474 | condition_code = itstate >> 4; | |
475 | } else { | |
476 | printf("dtrace: fasttrap: Tried to trace instruction %08x at %08x but not at end of IT block\n", | |
0a7de745 | 477 | (tp->ftt_thumb && dtrace_instr_size(tp->ftt_instr, tp->ftt_thumb) == 2) ? tp->ftt_instr1 : tp->ftt_instr, pc); |
5ba3f43e A |
478 | |
479 | fasttrap_tracepoint_remove(p, tp); | |
480 | lck_mtx_unlock(pid_mtx); | |
0a7de745 | 481 | return -1; |
5ba3f43e A |
482 | } |
483 | } | |
484 | } else { | |
485 | condition_code = ARM_CONDCODE(tp->ftt_instr); | |
486 | } | |
487 | ||
488 | if (!tp->ftt_thumb != !(regs->cpsr & PSR_TF)) { | |
489 | /* The ARM/Thumb mode does not match what we expected for this probe. | |
490 | * Remove this probe and bail. | |
491 | */ | |
492 | fasttrap_tracepoint_remove(p, tp); | |
493 | lck_mtx_unlock(pid_mtx); | |
0a7de745 | 494 | return -1; |
5ba3f43e A |
495 | } |
496 | ||
497 | if (tp->ftt_ids != NULL) { | |
498 | fasttrap_id_t *id; | |
499 | ||
500 | uint32_t s4; | |
501 | uint32_t *stack = (uint32_t *)regs->sp; | |
502 | ||
503 | /* First four parameters are passed in registers */ | |
504 | fasttrap_fuword32_noerr((user_addr_t)(uint32_t)stack, &s4); | |
505 | ||
506 | for (id = tp->ftt_ids; id != NULL; id = id->fti_next) { | |
507 | fasttrap_probe_t *probe = id->fti_probe; | |
508 | ||
f427ee49 | 509 | #if defined(XNU_TARGET_OS_OSX) |
5ba3f43e A |
510 | if (ISSET(current_proc()->p_lflag, P_LNOATTACH)) { |
511 | dtrace_probe(dtrace_probeid_error, 0 /* state */, probe->ftp_id, | |
0a7de745 | 512 | 1 /* ndx */, -1 /* offset */, DTRACEFLT_UPRIV); |
5ba3f43e A |
513 | #else |
514 | if (FALSE) { | |
f427ee49 | 515 | #endif /* defined(XNU_TARGET_OS_OSX) */ |
5ba3f43e A |
516 | } else { |
517 | if (probe->ftp_prov->ftp_provider_type == DTFTP_PROVIDER_ONESHOT) { | |
cb323159 A |
518 | if (os_atomic_xchg(&probe->ftp_triggered, 1, relaxed)) { |
519 | /* already triggered */ | |
5ba3f43e A |
520 | continue; |
521 | } | |
522 | } | |
523 | /* | |
524 | * If we have at least probe associated that | |
525 | * is not a oneshot probe, don't remove the | |
526 | * tracepoint | |
527 | */ | |
528 | else { | |
529 | retire_tp = 0; | |
530 | } | |
531 | if (id->fti_ptype == DTFTP_ENTRY) { | |
532 | /* | |
533 | * We note that this was an entry | |
534 | * probe to help ustack() find the | |
535 | * first caller. | |
536 | */ | |
537 | cookie = dtrace_interrupt_disable(); | |
538 | DTRACE_CPUFLAG_SET(CPU_DTRACE_ENTRY); | |
539 | dtrace_probe(probe->ftp_id, regs->r[0], regs->r[1], regs->r[2], regs->r[3], s4); | |
540 | DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_ENTRY); | |
541 | dtrace_interrupt_enable(cookie); | |
542 | } else if (id->fti_ptype == DTFTP_IS_ENABLED) { | |
543 | /* | |
544 | * Note that in this case, we don't | |
545 | * call dtrace_probe() since it's only | |
546 | * an artificial probe meant to change | |
547 | * the flow of control so that it | |
548 | * encounters the true probe. | |
549 | */ | |
550 | is_enabled = 1; | |
551 | } else if (probe->ftp_argmap == NULL) { | |
552 | dtrace_probe(probe->ftp_id, regs->r[0], regs->r[1], regs->r[2], regs->r[3], s4); | |
553 | } else { | |
554 | uint32_t t[5]; | |
555 | ||
556 | fasttrap_usdt_args(probe, regs, 5, t); | |
557 | dtrace_probe(probe->ftp_id, t[0], t[1], t[2], t[3], t[4]); | |
558 | } | |
559 | } | |
560 | } | |
561 | if (retire_tp) { | |
562 | fasttrap_tracepoint_retire(p, tp); | |
563 | } | |
564 | } | |
565 | /* | |
566 | * We're about to do a bunch of work so we cache a local copy of | |
567 | * the tracepoint to emulate the instruction, and then find the | |
568 | * tracepoint again later if we need to light up any return probes. | |
569 | */ | |
570 | tp_local = *tp; | |
571 | lck_mtx_unlock(pid_mtx); | |
572 | tp = &tp_local; | |
573 | ||
574 | /* | |
575 | * If there's an is-enabled probe connected to this tracepoint it | |
576 | * means that there was a 'eor r0,r0,r0' | |
577 | * instruction that was placed there by DTrace when the binary was | |
578 | * linked. As this probe is, in fact, enabled, we need to stuff 1 | |
579 | * into R0. Accordingly, we can bypass all the instruction | |
580 | * emulation logic since we know the inevitable result. It's possible | |
581 | * that a user could construct a scenario where the 'is-enabled' | |
582 | * probe was on some other instruction, but that would be a rather | |
583 | * exotic way to shoot oneself in the foot. | |
584 | */ | |
585 | ||
586 | if (is_enabled) { | |
587 | regs->r[0] = 1; | |
588 | new_pc = regs->pc + (tp->ftt_thumb ? 2 : 4); | |
589 | goto done; | |
590 | } | |
591 | ||
592 | /* For USDT probes, bypass all the emulation logic for the nop instruction */ | |
593 | if ((tp->ftt_thumb && IS_THUMB_NOP(THUMB_INSTR(tp->ftt_instr))) || | |
594 | (!tp->ftt_thumb && IS_ARM_NOP(tp->ftt_instr))) { | |
595 | new_pc = regs->pc + (tp->ftt_thumb ? 2 : 4); | |
596 | goto done; | |
597 | } | |
598 | ||
0a7de745 | 599 | instr_size = dtrace_instr_size(tp->ftt_instr, tp->ftt_thumb); |
5ba3f43e A |
600 | |
601 | switch (tp->ftt_type) { | |
0a7de745 A |
602 | case FASTTRAP_T_MOV_PC_REG: |
603 | case FASTTRAP_T_CPY_PC: | |
604 | { | |
605 | if (!dtrace_arm_condition_true(condition_code, regs->cpsr)) { | |
606 | new_pc = pc + instr_size; | |
607 | break; | |
608 | } | |
5ba3f43e | 609 | |
0a7de745 A |
610 | int rm; |
611 | if (tp->ftt_thumb) { | |
612 | rm = THUMB16_HRM(tp->ftt_instr1); | |
613 | } else { | |
614 | rm = tp->ftt_instr & 0xF; | |
615 | } | |
616 | new_pc = regs->r[rm]; | |
5ba3f43e | 617 | |
0a7de745 | 618 | /* This instruction does not change the Thumb state */ |
5ba3f43e | 619 | |
0a7de745 A |
620 | break; |
621 | } | |
5ba3f43e | 622 | |
0a7de745 A |
623 | case FASTTRAP_T_STM_LR: |
624 | case FASTTRAP_T_PUSH_LR: | |
625 | { | |
626 | /* | |
627 | * This is a very common case, so we want to emulate this instruction if | |
628 | * possible. However, on a push, it is possible that we might reach the end | |
629 | * of a page and have to allocate a new page. Most of the time this will not | |
630 | * happen, and we know that the push instruction can store at most 16 words, | |
631 | * so check to see if we are far from the boundary, and if so, emulate. This | |
632 | * can be made more aggressive by checking the actual number of words being | |
633 | * pushed, but we won't do that for now. | |
634 | * | |
635 | * Some of the same issues that apply to POP_PC probably apply here also. | |
636 | */ | |
5ba3f43e | 637 | |
0a7de745 A |
638 | int reglist; |
639 | int ret; | |
640 | uintptr_t* base; | |
5ba3f43e | 641 | |
0a7de745 A |
642 | if (!dtrace_arm_condition_true(condition_code, regs->cpsr)) { |
643 | new_pc = pc + instr_size; | |
644 | break; | |
645 | } | |
5ba3f43e | 646 | |
0a7de745 A |
647 | base = (uintptr_t*) regs->sp; |
648 | if (((((uintptr_t) base) - 16 * 4) >> PAGE_SHIFT) != (((uintptr_t) base) >> PAGE_SHIFT)) { | |
649 | /* Crosses the page boundary, go to emulation */ | |
650 | goto instr_emulate; | |
651 | } | |
5ba3f43e | 652 | |
0a7de745 A |
653 | if (tp->ftt_thumb) { |
654 | if (instr_size == 4) { | |
5ba3f43e | 655 | /* We know we have to push lr, never push sp or pc */ |
0a7de745 A |
656 | reglist = tp->ftt_instr2 & 0x1FFF; |
657 | } else { | |
658 | reglist = tp->ftt_instr1 & 0xFF; | |
5ba3f43e | 659 | } |
0a7de745 A |
660 | } else { |
661 | /* We know we have to push lr, never push sp or pc */ | |
662 | reglist = tp->ftt_instr & 0x1FFF; | |
663 | } | |
5ba3f43e | 664 | |
0a7de745 A |
665 | /* Push the link register */ |
666 | base--; | |
667 | ret = fasttrap_suword32((uint32_t) base, regs->lr); | |
668 | if (ret == -1) { | |
669 | fasttrap_sigsegv(p, uthread, (user_addr_t) base, regs); | |
670 | new_pc = regs->pc; | |
671 | break; | |
672 | } | |
5ba3f43e | 673 | |
0a7de745 A |
674 | /* Start pushing from $r12 */ |
675 | int regmask = 1 << 12; | |
676 | int regnum = 12; | |
677 | ||
678 | while (regmask) { | |
679 | if (reglist & regmask) { | |
680 | base--; | |
681 | ret = fasttrap_suword32((uint32_t) base, regs->r[regnum]); | |
682 | if (ret == -1) { | |
683 | fasttrap_sigsegv(p, uthread, (user_addr_t) base, regs); | |
684 | new_pc = regs->pc; | |
685 | break; | |
5ba3f43e | 686 | } |
5ba3f43e | 687 | } |
0a7de745 A |
688 | regmask >>= 1; |
689 | regnum--; | |
5ba3f43e A |
690 | } |
691 | ||
0a7de745 | 692 | regs->sp = (uintptr_t) base; |
5ba3f43e | 693 | |
0a7de745 | 694 | new_pc = pc + instr_size; |
5ba3f43e | 695 | |
0a7de745 A |
696 | break; |
697 | } | |
5ba3f43e | 698 | |
5ba3f43e | 699 | |
0a7de745 A |
700 | case FASTTRAP_T_LDM_PC: |
701 | case FASTTRAP_T_POP_PC: | |
702 | { | |
703 | /* TODO Two issues that will eventually need to be resolved: | |
704 | * | |
705 | * 1. Understand what the hardware does if we have to segfault (data abort) in | |
706 | * the middle of a load multiple. We currently don't have a working segfault | |
707 | * handler anyway, and with no swapfile we should never segfault on this load. | |
708 | * If we do, we'll just kill the process by setting the pc to 0. | |
709 | * | |
710 | * 2. The emulation is no longer atomic. We currently only emulate pop for | |
711 | * function epilogues, and so we should never have a race here because one | |
712 | * thread should never be trying to manipulate another thread's stack frames. | |
713 | * That is almost certainly a bug in the program. | |
714 | * | |
715 | * This will need to be fixed if we ever: | |
716 | * a. Ship dtrace externally, as this could be a potential attack vector | |
717 | * b. Support instruction level tracing, as we might then pop/ldm non epilogues. | |
718 | * | |
719 | */ | |
5ba3f43e | 720 | |
0a7de745 | 721 | /* Assume ldmia! sp/pop ... pc */ |
5ba3f43e | 722 | |
0a7de745 A |
723 | int regnum = 0, reglist; |
724 | int ret; | |
725 | uintptr_t* base; | |
5ba3f43e | 726 | |
0a7de745 A |
727 | if (!dtrace_arm_condition_true(condition_code, regs->cpsr)) { |
728 | new_pc = pc + instr_size; | |
5ba3f43e A |
729 | break; |
730 | } | |
731 | ||
0a7de745 A |
732 | if (tp->ftt_thumb) { |
733 | if (instr_size == 4) { | |
734 | /* We know we have to load the pc, don't do it twice */ | |
735 | reglist = tp->ftt_instr2 & 0x7FFF; | |
5ba3f43e | 736 | } else { |
0a7de745 | 737 | reglist = tp->ftt_instr1 & 0xFF; |
5ba3f43e | 738 | } |
0a7de745 A |
739 | } else { |
740 | /* We know we have to load the pc, don't do it twice */ | |
741 | reglist = tp->ftt_instr & 0x7FFF; | |
5ba3f43e A |
742 | } |
743 | ||
0a7de745 A |
744 | base = (uintptr_t*) regs->sp; |
745 | while (reglist) { | |
746 | if (reglist & 1) { | |
747 | ret = fasttrap_fuword32((uint32_t) base, ®s->r[regnum]); | |
748 | if (ret == -1) { | |
749 | fasttrap_sigsegv(p, uthread, (user_addr_t) base, regs); | |
750 | new_pc = regs->pc; | |
751 | break; | |
5ba3f43e | 752 | } |
0a7de745 | 753 | base++; |
5ba3f43e | 754 | } |
0a7de745 A |
755 | reglist >>= 1; |
756 | regnum++; | |
757 | } | |
5ba3f43e | 758 | |
0a7de745 A |
759 | ret = fasttrap_fuword32((uint32_t) base, &new_pc); |
760 | if (ret == -1) { | |
761 | fasttrap_sigsegv(p, uthread, (user_addr_t) base, regs); | |
762 | new_pc = regs->pc; | |
5ba3f43e A |
763 | break; |
764 | } | |
0a7de745 | 765 | base++; |
5ba3f43e | 766 | |
0a7de745 | 767 | regs->sp = (uintptr_t) base; |
5ba3f43e | 768 | |
0a7de745 | 769 | set_thumb_flag(regs, new_pc); |
5ba3f43e | 770 | |
0a7de745 A |
771 | break; |
772 | } | |
773 | ||
774 | case FASTTRAP_T_CB_N_Z: | |
775 | { | |
776 | /* Thumb mode instruction, and not permitted in IT block, so skip the condition code check */ | |
777 | int rn = tp->ftt_instr1 & 0x7; | |
778 | int offset = (((tp->ftt_instr1 & 0x00F8) >> 2) | ((tp->ftt_instr1 & 0x0200) >> 3)) + 4; | |
779 | int nonzero = tp->ftt_instr1 & 0x0800; | |
780 | if (!nonzero != !(regs->r[rn] == 0)) { | |
781 | new_pc = pc + offset; | |
782 | } else { | |
783 | new_pc = pc + instr_size; | |
784 | } | |
785 | break; | |
786 | } | |
787 | ||
788 | case FASTTRAP_T_B_COND: | |
789 | { | |
790 | /* Use the condition code in the instruction and ignore the ITSTATE */ | |
5ba3f43e | 791 | |
0a7de745 A |
792 | int code, offset; |
793 | if (tp->ftt_thumb) { | |
5ba3f43e | 794 | if (instr_size == 4) { |
0a7de745 A |
795 | code = (tp->ftt_instr1 >> 6) & 0xF; |
796 | if (code == 14 || code == 15) { | |
797 | panic("fasttrap: Emulation of invalid branch"); | |
798 | } | |
5ba3f43e A |
799 | int S = (tp->ftt_instr1 >> 10) & 1, |
800 | J1 = (tp->ftt_instr2 >> 13) & 1, | |
801 | J2 = (tp->ftt_instr2 >> 11) & 1; | |
5ba3f43e | 802 | offset = 4 + SIGNEXTEND( |
0a7de745 A |
803 | (S << 20) | (J2 << 19) | (J1 << 18) | |
804 | ((tp->ftt_instr1 & 0x003F) << 12) | | |
805 | ((tp->ftt_instr2 & 0x07FF) << 1), | |
806 | 21); | |
5ba3f43e | 807 | } else { |
0a7de745 A |
808 | code = (tp->ftt_instr1 >> 8) & 0xF; |
809 | if (code == 14 || code == 15) { | |
810 | panic("fasttrap: Emulation of invalid branch"); | |
811 | } | |
812 | offset = 4 + (SIGNEXTEND(tp->ftt_instr1 & 0xFF, 8) << 1); | |
5ba3f43e | 813 | } |
0a7de745 A |
814 | } else { |
815 | code = ARM_CONDCODE(tp->ftt_instr); | |
816 | if (code == 15) { | |
817 | panic("fasttrap: Emulation of invalid branch"); | |
818 | } | |
819 | offset = 8 + (SIGNEXTEND(tp->ftt_instr & 0x00FFFFFF, 24) << 2); | |
820 | } | |
5ba3f43e | 821 | |
0a7de745 | 822 | if (dtrace_arm_condition_true(code, regs->cpsr)) { |
5ba3f43e | 823 | new_pc = pc + offset; |
0a7de745 A |
824 | } else { |
825 | new_pc = pc + instr_size; | |
826 | } | |
827 | ||
828 | break; | |
829 | } | |
830 | ||
831 | case FASTTRAP_T_B_UNCOND: | |
832 | { | |
833 | int offset; | |
834 | ||
835 | /* Unconditional branches can only be taken from Thumb mode */ | |
836 | /* (This is different from an ARM branch with condition code "always") */ | |
837 | ASSERT(tp->ftt_thumb == 1); | |
5ba3f43e | 838 | |
0a7de745 A |
839 | if (!dtrace_arm_condition_true(condition_code, regs->cpsr)) { |
840 | new_pc = pc + instr_size; | |
5ba3f43e A |
841 | break; |
842 | } | |
843 | ||
0a7de745 A |
844 | if (instr_size == 4) { |
845 | int S = (tp->ftt_instr1 >> 10) & 1, | |
846 | J1 = (tp->ftt_instr2 >> 13) & 1, | |
847 | J2 = (tp->ftt_instr2 >> 11) & 1; | |
848 | int I1 = (J1 != S) ? 0 : 1, I2 = (J2 != S) ? 0 : 1; | |
849 | offset = 4 + SIGNEXTEND( | |
850 | (S << 24) | (I1 << 23) | (I2 << 22) | | |
851 | ((tp->ftt_instr1 & 0x03FF) << 12) | | |
852 | ((tp->ftt_instr2 & 0x07FF) << 1), | |
853 | 25); | |
854 | } else { | |
855 | uint32_t instr1 = tp->ftt_instr1; | |
856 | offset = 4 + (SIGNEXTEND(instr1 & 0x7FF, 11) << 1); | |
857 | } | |
5ba3f43e | 858 | |
0a7de745 | 859 | new_pc = pc + offset; |
5ba3f43e | 860 | |
0a7de745 A |
861 | break; |
862 | } | |
863 | ||
864 | case FASTTRAP_T_BX_REG: | |
865 | { | |
866 | int reg; | |
5ba3f43e | 867 | |
0a7de745 A |
868 | if (!dtrace_arm_condition_true(condition_code, regs->cpsr)) { |
869 | new_pc = pc + instr_size; | |
5ba3f43e A |
870 | break; |
871 | } | |
872 | ||
0a7de745 A |
873 | if (tp->ftt_thumb) { |
874 | reg = THUMB16_HRM(tp->ftt_instr1); | |
875 | } else { | |
876 | reg = ARM_RM(tp->ftt_instr); | |
877 | } | |
878 | new_pc = regs->r[reg]; | |
879 | set_thumb_flag(regs, new_pc); | |
5ba3f43e | 880 | |
0a7de745 A |
881 | break; |
882 | } | |
5ba3f43e | 883 | |
0a7de745 A |
884 | case FASTTRAP_T_LDR_PC_IMMED: |
885 | case FASTTRAP_T_VLDR_PC_IMMED: | |
886 | /* Handle these instructions by replacing the PC in the instruction with another | |
887 | * register. They are common, so we'd like to support them, and this way we do so | |
888 | * without any risk of having to simulate a segfault. | |
889 | */ | |
5ba3f43e | 890 | |
0a7de745 | 891 | /* Fall through */ |
5ba3f43e | 892 | |
0a7de745 A |
893 | instr_emulate: |
894 | case FASTTRAP_T_COMMON: | |
895 | { | |
896 | user_addr_t addr; | |
897 | uint8_t scratch[32]; | |
898 | uint_t i = 0; | |
899 | fasttrap_instr_t emul_instr; | |
900 | emul_instr.instr32 = tp->ftt_instr; | |
901 | int emul_instr_size; | |
902 | ||
903 | /* | |
904 | * Unfortunately sometimes when we emulate the instruction and have to replace the | |
905 | * PC, there is no longer a thumb mode equivalent. We end up having to run the | |
906 | * modified instruction in ARM mode. We use this variable to keep track of which | |
907 | * mode we should emulate in. We still use the original variable to determine | |
908 | * what mode to return to. | |
909 | */ | |
910 | uint8_t emul_thumb = tp->ftt_thumb; | |
911 | int save_reg = -1; | |
912 | uint32_t save_val = 0; | |
913 | ||
914 | /* | |
915 | * Dealing with condition codes and emulation: | |
916 | * We can't just uniformly do a condition code check here because not all instructions | |
917 | * have condition codes. We currently do not support an instruction by instruction trace, | |
918 | * so we can assume that either: 1. We are executing a Thumb instruction, in which case | |
919 | * we either are not in an IT block and should execute always, or we are last in an IT | |
920 | * block. Either way, the traced instruction will run correctly, and we won't have any | |
921 | * problems when we return to the original code, because we will no longer be in the IT | |
922 | * block. 2. We are executing an ARM instruction, in which case we are ok as long as | |
923 | * we don't attempt to change the condition code. | |
924 | */ | |
925 | if (tp->ftt_type == FASTTRAP_T_LDR_PC_IMMED) { | |
926 | /* We know we always have a free register (the one we plan to write the | |
927 | * result value to!). So we'll replace the pc with that one. | |
5ba3f43e | 928 | */ |
0a7de745 A |
929 | int new_reg; |
930 | if (tp->ftt_thumb) { | |
931 | /* Check to see if thumb or thumb2 */ | |
932 | if (instr_size == 2) { | |
933 | /* | |
934 | * Sadness. We need to emulate this instruction in ARM mode | |
935 | * because it has an 8 bit immediate offset. Instead of having | |
936 | * to deal with condition codes in the ARM instruction, we'll | |
937 | * just check the condition and abort if the condition is false. | |
938 | */ | |
939 | if (!dtrace_arm_condition_true(condition_code, regs->cpsr)) { | |
940 | new_pc = pc + instr_size; | |
941 | break; | |
5ba3f43e | 942 | } |
0a7de745 A |
943 | |
944 | new_reg = (tp->ftt_instr1 >> 8) & 0x7; | |
945 | regs->r[new_reg] = ALIGNADDR(regs->pc + 4, 2); | |
946 | emul_thumb = 0; | |
947 | emul_instr.instr32 = 0xE5900000 | (new_reg << 16) | (new_reg << 12) | ((tp->ftt_instr1 & 0xFF) << 2); | |
5ba3f43e | 948 | } else { |
0a7de745 A |
949 | /* Thumb2. Just replace the register. */ |
950 | new_reg = (tp->ftt_instr2 >> 12) & 0xF; | |
951 | regs->r[new_reg] = ALIGNADDR(regs->pc + 4, 2); | |
5ba3f43e | 952 | emul_instr.instr16.instr1 &= ~0x000F; |
0a7de745 | 953 | emul_instr.instr16.instr1 |= new_reg; |
5ba3f43e | 954 | } |
0a7de745 A |
955 | } else { |
956 | /* ARM. Just replace the register. */ | |
957 | new_reg = (tp->ftt_instr >> 12) & 0xF; | |
958 | regs->r[new_reg] = ALIGNADDR(regs->pc + 8, 2); | |
959 | emul_instr.instr32 &= ~0x000F0000; | |
960 | emul_instr.instr32 |= new_reg << 16; | |
5ba3f43e | 961 | } |
0a7de745 A |
962 | } else if (tp->ftt_type == FASTTRAP_T_VLDR_PC_IMMED) { |
963 | /* This instruction only uses one register, and if we're here, we know | |
964 | * it must be the pc. So we'll just replace it with R0. | |
5ba3f43e | 965 | */ |
0a7de745 A |
966 | save_reg = 0; |
967 | save_val = regs->r[0]; | |
968 | regs->r[save_reg] = ALIGNADDR(regs->pc + (tp->ftt_thumb ? 4 : 8), 2); | |
969 | if (tp->ftt_thumb) { | |
970 | emul_instr.instr16.instr1 &= ~0x000F; | |
971 | } else { | |
972 | emul_instr.instr32 &= ~0x000F0000; | |
973 | } | |
974 | } | |
5ba3f43e | 975 | |
0a7de745 | 976 | emul_instr_size = dtrace_instr_size(emul_instr.instr32, emul_thumb); |
5ba3f43e | 977 | |
0a7de745 A |
978 | /* |
979 | * At this point: | |
980 | * tp->ftt_thumb = thumb mode of original instruction | |
981 | * emul_thumb = thumb mode for emulation | |
982 | * emul_instr = instruction we are using to emulate original instruction | |
983 | * emul_instr_size = size of emulating instruction | |
984 | */ | |
5ba3f43e | 985 | |
0a7de745 | 986 | addr = uthread->t_dtrace_scratch->addr; |
5ba3f43e | 987 | |
0a7de745 A |
988 | if (addr == 0LL) { |
989 | fasttrap_sigtrap(p, uthread, pc); // Should be killing target proc | |
990 | new_pc = pc; | |
991 | break; | |
992 | } | |
5ba3f43e | 993 | |
0a7de745 A |
994 | uthread->t_dtrace_scrpc = addr; |
995 | if (emul_thumb) { | |
996 | /* | |
997 | * No way to do an unconditional branch in Thumb mode, shove the address | |
998 | * onto the user stack and go to the next location with a pop. This can | |
999 | * segfault if this push happens to cross a stack page, but that's ok, since | |
1000 | * we are running in userland, and the kernel knows how to handle userland | |
1001 | * stack expansions correctly. | |
1002 | * | |
1003 | * Layout of scratch space for Thumb mode: | |
1004 | * Emulated instruction | |
1005 | * ldr save_reg, [pc, #16] (if necessary, restore any register we clobbered) | |
1006 | * push { r0, r1 } | |
1007 | * ldr r0, [pc, #4] | |
1008 | * str r0, [sp, #4] | |
1009 | * pop { r0, pc } | |
1010 | * Location we should return to in original program | |
1011 | * Saved value of clobbered register (if necessary) | |
1012 | */ | |
5ba3f43e | 1013 | |
0a7de745 | 1014 | bcopy(&emul_instr, &scratch[i], emul_instr_size); i += emul_instr_size; |
5ba3f43e | 1015 | |
0a7de745 A |
1016 | if (save_reg != -1) { |
1017 | uint16_t restore_inst = 0x4803; | |
1018 | restore_inst |= (save_reg & 0x7) << 8; | |
1019 | SET16(scratch + i, restore_inst); i += 2; // ldr reg, [pc , #16] | |
1020 | } | |
5ba3f43e | 1021 | |
0a7de745 A |
1022 | SET16(scratch + i, 0xB403); i += 2; // push { r0, r1 } |
1023 | SET16(scratch + i, 0x4801); i += 2; // ldr r0, [pc, #4] | |
1024 | SET16(scratch + i, 0x9001); i += 2; // str r0, [sp, #4] | |
1025 | SET16(scratch + i, 0xBD01); i += 2; // pop { r0, pc } | |
5ba3f43e | 1026 | |
0a7de745 A |
1027 | if (i % 4) { |
1028 | SET16(scratch + i, 0); i += 2; // padding - saved 32 bit words must be aligned | |
1029 | } | |
1030 | SET32(scratch + i, pc + instr_size + (tp->ftt_thumb ? 1 : 0)); i += 4; // Return address | |
1031 | if (save_reg != -1) { | |
1032 | SET32(scratch + i, save_val); i += 4; // saved value of clobbered register | |
1033 | } | |
5ba3f43e | 1034 | |
0a7de745 A |
1035 | uthread->t_dtrace_astpc = addr + i; |
1036 | bcopy(&emul_instr, &scratch[i], emul_instr_size); i += emul_instr_size; | |
1037 | SET16(scratch + i, FASTTRAP_THUMB_RET_INSTR); i += 2; | |
1038 | } else { | |
1039 | /* | |
1040 | * Layout of scratch space for ARM mode: | |
1041 | * Emulated instruction | |
1042 | * ldr save_reg, [pc, #12] (if necessary, restore any register we clobbered) | |
1043 | * ldr pc, [pc, #4] | |
1044 | * Location we should return to in original program | |
1045 | * Saved value of clobbered register (if necessary) | |
1046 | */ | |
5ba3f43e | 1047 | |
0a7de745 | 1048 | bcopy(&emul_instr, &scratch[i], emul_instr_size); i += emul_instr_size; |
5ba3f43e | 1049 | |
0a7de745 A |
1050 | if (save_reg != -1) { |
1051 | uint32_t restore_inst = 0xE59F0004; | |
1052 | restore_inst |= save_reg << 12; | |
1053 | SET32(scratch + i, restore_inst); i += 4; // ldr reg, [pc, #12] | |
5ba3f43e | 1054 | } |
0a7de745 | 1055 | SET32(scratch + i, 0xE51FF004); i += 4; // ldr pc, [pc, #4] |
5ba3f43e | 1056 | |
0a7de745 A |
1057 | SET32(scratch + i, pc + instr_size + (tp->ftt_thumb ? 1 : 0)); i += 4; // Return address |
1058 | if (save_reg != -1) { | |
1059 | SET32(scratch + i, save_val); i += 4; // Saved value of clobbered register | |
5ba3f43e A |
1060 | } |
1061 | ||
0a7de745 A |
1062 | uthread->t_dtrace_astpc = addr + i; |
1063 | bcopy(&emul_instr, &scratch[i], emul_instr_size); i += emul_instr_size; | |
1064 | SET32(scratch + i, FASTTRAP_ARM_RET_INSTR); i += 4; | |
1065 | } | |
5ba3f43e | 1066 | |
0a7de745 A |
1067 | if (uwrite(p, scratch, i, uthread->t_dtrace_scratch->write_addr) != KERN_SUCCESS) { |
1068 | fasttrap_sigtrap(p, uthread, pc); | |
1069 | new_pc = pc; | |
5ba3f43e A |
1070 | break; |
1071 | } | |
1072 | ||
0a7de745 A |
1073 | if (tp->ftt_retids != NULL) { |
1074 | uthread->t_dtrace_step = 1; | |
1075 | uthread->t_dtrace_ret = 1; | |
1076 | new_pc = uthread->t_dtrace_astpc + (emul_thumb ? 1 : 0); | |
1077 | } else { | |
1078 | new_pc = uthread->t_dtrace_scrpc + (emul_thumb ? 1 : 0); | |
1079 | } | |
1080 | ||
1081 | uthread->t_dtrace_pc = pc; | |
1082 | uthread->t_dtrace_npc = pc + instr_size; | |
1083 | uthread->t_dtrace_on = 1; | |
1084 | was_simulated = 0; | |
1085 | set_thumb_flag(regs, new_pc); | |
1086 | break; | |
1087 | } | |
1088 | ||
1089 | default: | |
1090 | panic("fasttrap: mishandled an instruction"); | |
5ba3f43e A |
1091 | } |
1092 | ||
1093 | done: | |
1094 | /* | |
1095 | * APPLE NOTE: | |
1096 | * | |
1097 | * We're setting this earlier than Solaris does, to get a "correct" | |
1098 | * ustack() output. In the Sun code, a() -> b() -> c() -> d() is | |
1099 | * reported at: d, b, a. The new way gives c, b, a, which is closer | |
1100 | * to correct, as the return instruction has already exectued. | |
1101 | */ | |
1102 | regs->pc = new_pc; | |
1103 | ||
0a7de745 | 1104 | /* |
5ba3f43e A |
1105 | * If there were no return probes when we first found the tracepoint, |
1106 | * we should feel no obligation to honor any return probes that were | |
1107 | * subsequently enabled -- they'll just have to wait until the next | |
0a7de745 A |
1108 | * time around. |
1109 | */ | |
5ba3f43e A |
1110 | if (tp->ftt_retids != NULL) { |
1111 | /* | |
1112 | * We need to wait until the results of the instruction are | |
1113 | * apparent before invoking any return probes. If this | |
1114 | * instruction was emulated we can just call | |
1115 | * fasttrap_return_common(); if it needs to be executed, we | |
1116 | * need to wait until the user thread returns to the kernel. | |
1117 | */ | |
1118 | /* | |
1119 | * It used to be that only common instructions were simulated. | |
1120 | * For performance reasons, we now simulate some instructions | |
1121 | * when safe and go back to userland otherwise. The was_simulated | |
1122 | * flag means we don't need to go back to userland. | |
1123 | */ | |
1124 | if (was_simulated) { | |
1125 | fasttrap_return_common(p, regs, pc, new_pc); | |
1126 | } else { | |
1127 | ASSERT(uthread->t_dtrace_ret != 0); | |
1128 | ASSERT(uthread->t_dtrace_pc == pc); | |
1129 | ASSERT(uthread->t_dtrace_scrpc != 0); | |
1130 | ASSERT(new_pc == uthread->t_dtrace_astpc); | |
1131 | } | |
1132 | } | |
1133 | ||
0a7de745 | 1134 | return 0; |
5ba3f43e A |
1135 | } |
1136 | ||
1137 | int | |
1138 | fasttrap_return_probe(arm_saved_state_t *regs) | |
1139 | { | |
1140 | proc_t *p = current_proc(); | |
1141 | uthread_t uthread = (uthread_t)get_bsdthread_info(current_thread()); | |
1142 | user_addr_t pc = uthread->t_dtrace_pc; | |
1143 | user_addr_t npc = uthread->t_dtrace_npc; | |
1144 | ||
1145 | uthread->t_dtrace_pc = 0; | |
1146 | uthread->t_dtrace_npc = 0; | |
1147 | uthread->t_dtrace_scrpc = 0; | |
1148 | uthread->t_dtrace_astpc = 0; | |
1149 | ||
1150 | /* | |
1151 | * Treat a child created by a call to vfork(2) as if it were its | |
1152 | * parent. We know that there's only one thread of control in such a | |
1153 | * process: this one. | |
1154 | */ | |
1155 | if (p->p_lflag & P_LINVFORK) { | |
1156 | proc_list_lock(); | |
0a7de745 | 1157 | while (p->p_lflag & P_LINVFORK) { |
5ba3f43e | 1158 | p = p->p_pptr; |
0a7de745 | 1159 | } |
5ba3f43e A |
1160 | proc_list_unlock(); |
1161 | } | |
1162 | ||
1163 | /* | |
1164 | * We set rp->r_pc to the address of the traced instruction so | |
1165 | * that it appears to dtrace_probe() that we're on the original | |
1166 | * instruction, and so that the user can't easily detect our | |
1167 | * complex web of lies. dtrace_return_probe() (our caller) | |
1168 | * will correctly set %pc after we return. | |
1169 | */ | |
0a7de745 | 1170 | regs->pc = pc; |
5ba3f43e A |
1171 | |
1172 | fasttrap_return_common(p, regs, pc, npc); | |
1173 | ||
0a7de745 | 1174 | return 0; |
5ba3f43e A |
1175 | } |
1176 | ||
1177 | uint64_t | |
1178 | fasttrap_pid_getarg(void *arg, dtrace_id_t id, void *parg, int argno, | |
0a7de745 | 1179 | int aframes) |
5ba3f43e A |
1180 | { |
1181 | #pragma unused(arg, id, parg, aframes) | |
1182 | arm_saved_state_t* regs = find_user_regs(current_thread()); | |
1183 | ||
1184 | /* First four arguments are in registers */ | |
0a7de745 | 1185 | if (argno < 4) { |
5ba3f43e | 1186 | return regs->r[argno]; |
0a7de745 | 1187 | } |
5ba3f43e A |
1188 | |
1189 | /* Look on the stack for the rest */ | |
1190 | uint32_t value; | |
1191 | uint32_t* sp = (uint32_t*) regs->sp; | |
1192 | DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); | |
0a7de745 | 1193 | value = dtrace_fuword32((user_addr_t) (sp + argno - 4)); |
5ba3f43e A |
1194 | DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT | CPU_DTRACE_BADADDR); |
1195 | ||
1196 | return value; | |
1197 | } | |
1198 | ||
1199 | uint64_t | |
1200 | fasttrap_usdt_getarg(void *arg, dtrace_id_t id, void *parg, int argno, int aframes) | |
1201 | { | |
1202 | #pragma unused(arg, id, parg, argno, aframes) | |
1203 | #if 0 | |
0a7de745 | 1204 | return fasttrap_anarg(ttolwp(curthread)->lwp_regs, 0, argno); |
5ba3f43e A |
1205 | #endif |
1206 | ||
1207 | return 0; | |
1208 | } |