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