]>
Commit | Line | Data |
---|---|---|
2d21ac55 A |
1 | /* |
2 | * Copyright (c) 2007 Apple Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * This file contains Original Code and/or Modifications of Original Code | |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. The rights granted to you under the License | |
10 | * may not be used to create, or enable the creation or redistribution of, | |
11 | * unlawful or unlicensed copies of an Apple operating system, or to | |
12 | * circumvent, violate, or enable the circumvention or violation of, any | |
13 | * terms of an Apple operating system software license agreement. | |
14 | * | |
15 | * Please obtain a copy of the License at | |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. | |
17 | * | |
18 | * The Original Code and all software distributed under the License are | |
19 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
22 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
23 | * Please see the License for the specific language governing rights and | |
24 | * limitations under the License. | |
25 | * | |
26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ | |
27 | */ | |
28 | ||
29 | /* | |
30 | * CDDL HEADER START | |
31 | * | |
32 | * The contents of this file are subject to the terms of the | |
33 | * Common Development and Distribution License (the "License"). | |
34 | * You may not use this file except in compliance with the License. | |
35 | * | |
36 | * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE | |
37 | * or http://www.opensolaris.org/os/licensing. | |
38 | * See the License for the specific language governing permissions | |
39 | * and limitations under the License. | |
40 | * | |
41 | * When distributing Covered Code, include this CDDL HEADER in each | |
42 | * file and include the License file at usr/src/OPENSOLARIS.LICENSE. | |
43 | * If applicable, add the following below this CDDL HEADER, with the | |
44 | * fields enclosed by brackets "[]" replaced with your own identifying | |
45 | * information: Portions Copyright [yyyy] [name of copyright owner] | |
46 | * | |
47 | * CDDL HEADER END | |
48 | */ | |
49 | ||
50 | /* | |
b0d623f7 | 51 | * Copyright 2008 Sun Microsystems, Inc. All rights reserved. |
2d21ac55 A |
52 | * Use is subject to license terms. |
53 | */ | |
54 | ||
55 | /* | |
b0d623f7 | 56 | * #pragma ident "@(#)fasttrap_isa.c 1.27 08/04/09 SMI" |
2d21ac55 A |
57 | */ |
58 | ||
59 | #ifdef KERNEL | |
60 | #ifndef _KERNEL | |
61 | #define _KERNEL /* Solaris vs. Darwin */ | |
62 | #endif | |
63 | #endif | |
64 | ||
65 | #define MACH__POSIX_C_SOURCE_PRIVATE 1 /* pulls in suitable savearea from mach/ppc/thread_status.h */ | |
66 | #include <sys/fasttrap_isa.h> | |
67 | #include <sys/fasttrap_impl.h> | |
68 | #include <sys/dtrace.h> | |
69 | #include <sys/dtrace_impl.h> | |
70 | #include <sys/dtrace_ptss.h> | |
71 | #include <kern/debug.h> | |
72 | #include <ppc/decodePPC.h> | |
73 | #include <kern/task.h> | |
74 | #include <mach/vm_param.h> | |
75 | #include <mach/mach_vm.h> | |
76 | #include <mach/task.h> | |
77 | #include <vm/pmap.h> | |
78 | #include <vm/vm_map.h> /* All the bits we care about are guarded by MACH_KERNEL_PRIVATE :-( */ | |
cf7d32b8 | 79 | extern dtrace_id_t dtrace_probeid_error; |
2d21ac55 | 80 | |
b0d623f7 A |
81 | /* Solaris proc_t is the struct. Darwin's proc_t is a pointer to it. */ |
82 | #define proc_t struct proc /* Steer clear of the Darwin typedef for proc_t */ | |
2d21ac55 A |
83 | |
84 | static int32_t branchtaken(int32_t bo, int32_t bi, ppc_saved_state_t *sv); | |
85 | static int32_t dtrace_decode_ppc(uint32_t inst); | |
86 | int patchInst(task_t task, addr64_t vaddr, uint32_t inst); | |
87 | kern_return_t dtrace_user_probe(ppc_saved_state_t *sv); | |
88 | ||
89 | /* | |
90 | * Lossless User-Land Tracing on PPC | |
91 | * --------------------------------- | |
92 | * | |
93 | * PPC uses a different technique to emulate user-land instruction replaces by a probe | |
94 | * trap than x86. | |
95 | * | |
96 | * Like x86, it will emulate all forms of branch instructions. We will not attempt | |
97 | * to emulate any instruction that we know will cause an interruption or exception | |
98 | * (system call, trap, privileged instruction, instruction that uses a privileged | |
99 | * register). | |
100 | * | |
101 | * NOTE: I am thinking that we should punish tight loopers, e.g., branch-to-dot. | |
102 | * Depending upon clock resolution and how fast we can process these guys, it is | |
103 | * possible that its quantum will never decrease. Maybe we could just manually | |
104 | * end the guy's quantum and let the next guy go... | |
105 | * | |
106 | * When fasttrap_tracepoint_init is called, we fetch the instruction and decode it. | |
107 | * If we don't recognize it or find it is a "banned" instruction, we return -1, | |
108 | * telling our caller to forget it. Otherwise we save the instruction image and | |
109 | * enough of the decode to quickly handle it at probe time. We cram it into | |
110 | * the fasttrap_machtp_t structure. | |
111 | * | |
112 | * When the probe hits, we verify that the PC is still a probe point and if not, | |
113 | * we bail. Otherwise we have a bit more to do. | |
114 | * | |
115 | * If DTFTP_ENTRY is set, we have an entry probe and need to call dtrace_probe. | |
116 | * | |
117 | * If DTFTP_IS_ENABLED is set, all we need to do is to return a 1. | |
118 | * | |
119 | * If ftp_argmap is NULL, we call dtrace_probe | |
120 | * | |
121 | * Otherwise, we figure out what the arguments are and pass them to dtrace_probe | |
122 | * | |
123 | * Next, we need to set up to emulate the probed instruction and here is where we are | |
124 | * the most different than the x86 code. | |
125 | * | |
126 | * Like x86, we first check to see if the instruction is any form of branch. If so, | |
127 | * we emulate it completely within the kernel and are done. | |
128 | * | |
129 | * If it is anything else, we build a code stream within the kernel to execute the | |
130 | * instruction. Note that this is very different from x86 which build the code in | |
131 | * userland. | |
132 | * | |
133 | * The generated stream needs to be executed within the kernel's code space but with | |
134 | * the user address space and registers. Because PPC allows different translation modes | |
135 | * for instruction fetch and data fetch, this is not too difficult. | |
136 | * | |
137 | * There are two kinds streams needed: execute and continue, and execute and return, | |
138 | * which are used for entry/offset and exit probes respectivily. | |
139 | * | |
140 | * The probe code will copy the instruction image into the current user savearea (which | |
141 | * also contains the complete user state register context). A flag that requests either | |
142 | * execute/continue or execute/return is also set in the savearea. | |
143 | * | |
144 | * We now exit the dtrace code and the marked context makes its way back to the point | |
145 | * where it will be dispatched on the processor. | |
146 | * | |
147 | * The exception return code will start to restore the user context, including registers | |
148 | * and address space. However, before dispatching the user, it will notice that the | |
149 | * emulate flags are set. At this point the code will build a code stream | |
150 | * in an area in the per_proc that consists of | |
151 | * the original instruction followed by a trap instruction. It will set the new MSR (in | |
152 | * SRR1) to have address translation enable for data, translation disabled for instruction | |
153 | * fetches, interruptions disabled, and supervisor state. | |
154 | * | |
155 | * The new PC and MSR are loaded via a RFID and the generated stream is executed. If a | |
156 | * synchronous fault occurs, it is either handled (PTE miss, FPU or vector unavailable), | |
157 | * emulated (alignment or denorm), or passed on to the user. | |
158 | * | |
159 | * Assuming the emulated instruction completes, the trap will execute. When that happens, | |
160 | * low-level trap handler will check its flags. If the trap corresponds to an | |
161 | * execute/continue stream, the trap handler will adjust the PC and complete the | |
162 | * transition into user space. | |
163 | * | |
164 | * If the trap corresponds to an execute/return stream, the handler will generate | |
165 | * a T_DTRACE_RET exception and let the trap handler pass it along to dtrace_user_probe. | |
166 | * | |
167 | */ | |
168 | ||
169 | ||
170 | static uint64_t | |
171 | fasttrap_anarg(ppc_saved_state_t *sv, int function_entry, int argno) | |
172 | { | |
173 | #pragma unused(function_entry) | |
174 | uint32_t farg; | |
175 | uint64_t value; | |
176 | ||
177 | /* The first 8 arguments (argno 0-7) are in registers */ | |
178 | if (argno < 8) { | |
179 | value = (&sv->save_r3)[argno]; | |
180 | } else { | |
181 | if (sv->save_srr1 & 0x8000000000000000ULL) { | |
182 | /* 64-bit */ | |
183 | /* Grab argument >= 8 from stack */ | |
184 | fasttrap_fuword64_noerr(sv->save_r1 + 48 + ((argno)* sizeof(uint64_t)), &value); | |
185 | } else { | |
186 | /* 32-bit */ | |
187 | /* Grab argument >= 8 from stack */ | |
188 | fasttrap_fuword32_noerr(sv->save_r1 + 24 + ((argno) * sizeof(uint32_t)), &farg); | |
189 | value = (uint64_t)farg; | |
190 | } | |
191 | } | |
192 | ||
193 | return (value); | |
194 | } | |
195 | ||
196 | /*ARGSUSED*/ | |
197 | int | |
198 | fasttrap_tracepoint_init(proc_t *p, fasttrap_tracepoint_t *tp, user_addr_t pc, | |
199 | fasttrap_probe_type_t type) | |
200 | { | |
201 | #pragma unused(type) | |
202 | ||
203 | uint32_t instr, testr1, testr2, testr3; | |
204 | user_addr_t targpc; | |
205 | int32_t target, optype; | |
206 | ||
207 | /* | |
208 | * Read the instruction at the given address out of the process's | |
209 | * address space. We don't have to worry about a debugger | |
210 | * changing this instruction before we overwrite it with our trap | |
211 | * instruction since P_PR_LOCK is set. Since instructions can span | |
212 | * pages, we potentially read the instruction in two parts. If the | |
213 | * second part fails, we just zero out that part of the instruction. | |
214 | */ | |
215 | /* | |
216 | * APPLE NOTE: Of course, we do not have a P_PR_LOCK, so this is racey... | |
217 | */ | |
218 | ||
219 | if (uread(p, &instr, 4, pc) != 0) return (-1); /* Grab instruction, return suddenly if read fails... */ | |
220 | ||
221 | optype = dtrace_decode_ppc(instr); /* See if we have an instruction we can probe */ | |
222 | ||
223 | tp->ftt_instr = instr; /* Save the instruction image */ | |
224 | testr1 = tp->ftt_bo = (uint8_t)((instr >> (31 - 10)) & 0x1F); /* Extract branch options */ | |
225 | testr2 = tp->ftt_bi = (uint8_t)((instr >> (31 - 15)) & 0x1F); /* Extract condition register bit */ | |
226 | testr3 = (instr >> (31 - 20)) & 0x1F; /* Get that last register */ | |
227 | tp->ftt_flgs = (uint8_t)(instr & 3); /* Set the absolute address and link flags */ | |
228 | ||
229 | switch(optype) { /* Do instruction specific decode */ | |
230 | ||
231 | case diCMN: /* Common instruction */ | |
232 | tp->ftt_type = ftmtCommon; /* Mark as common instruction */ | |
233 | break; | |
234 | ||
235 | case diINV: /* Invalid */ | |
236 | case diTRP: /* Trap */ | |
237 | case diSC: /* System Call */ | |
238 | case diRFI: /* Return from interrupt */ | |
239 | case diPRV: /* Priviliged instruction */ | |
240 | return (-1); /* We will not emulate these... */ | |
241 | break; | |
242 | ||
243 | case diB: /* Branch */ | |
244 | tp->ftt_type = ftmtB; /* Mark as branch instruction */ | |
245 | target = instr & 0x03FFFFFC; /* Extract address or offset */ | |
246 | if(target & 0x02000000) target |= 0xFC000000; /* Sign extend */ | |
247 | tp->ftt_trgt = target; /* Trim back down and save */ | |
248 | ||
249 | targpc = (user_addr_t)((int64_t)target); /* Generate a target address, hopefully we sign extend... */ | |
250 | if(!(tp->ftt_flgs & ftmtAbs)) { /* Are we dealing with an offset here? */ | |
251 | targpc = targpc + pc; /* Apply offset to get target address */ | |
252 | } | |
253 | ||
254 | if(targpc == pc) return -1; /* Branching to self is a sin and is forbidden... */ | |
255 | break; | |
256 | ||
257 | case diBC: /* Branch conditional */ | |
258 | tp->ftt_type = ftmtBC; /* Mark as branch conditional */ | |
259 | target = instr & 0x0000FFFC; /* Extract address or offset */ | |
260 | if(target & 0x00008000) target |= 0xFFFF0000; /* Sign extend */ | |
261 | tp->ftt_trgt = target; /* Trim back down and save */ | |
262 | ||
263 | targpc = (user_addr_t)((int64_t)target); /* Generate a target address, hopefully we sign extend... */ | |
264 | if(!(tp->ftt_flgs & ftmtAbs)) { /* Are we dealing with an offset here? */ | |
265 | targpc = targpc + pc; /* Apply offset to get target address */ | |
266 | } | |
267 | ||
268 | if(targpc == pc) return -1; /* Branching to self is a sin and is forbidden... */ | |
269 | break; | |
270 | ||
271 | case diBLR: /* Branch conditional to link register */ | |
272 | tp->ftt_type = ftmtBLR; /* Mark as branch conditional to link register */ | |
273 | break; | |
274 | ||
275 | case diBCTR: /* Branch conditional to count register */ | |
276 | tp->ftt_type = ftmtBCTR; /* Mark as branch conditional to count register */ | |
277 | break; | |
278 | ||
279 | case diOR: /* OR */ | |
280 | if((instr >> 26) == 24) { /* Is this the ORI nop? */ | |
281 | if((testr1 == testr2) && ((instr & 0x0000FFFF) == 0)) tp->ftt_type = ftmtNOP; /* Remember if this is a NOP instruction */ | |
282 | else tp->ftt_type = ftmtCommon; /* Otherwise it is a common ORI instruction */ | |
283 | } | |
284 | else if((testr1 == testr2) && (testr1 == testr3)) tp->ftt_type = ftmtNOP; /* If all three registers are the same, this is a NOP */ | |
285 | else tp->ftt_type = ftmtCommon; /* Otherwise it is a common OR instruction */ | |
286 | ||
287 | break; | |
288 | ||
289 | default: | |
290 | panic("fasttrap_tracepoint_init: invalid branch decode, inst = %08X, optype = %d\n", instr, optype); | |
291 | break; | |
292 | ||
293 | } | |
294 | ||
295 | return (0); | |
296 | } | |
297 | ||
298 | int | |
299 | fasttrap_tracepoint_install(proc_t *p, fasttrap_tracepoint_t *tp) | |
300 | { | |
301 | return patchInst(p->task, tp->ftt_pc, FASTTRAP_INSTR); /* Patch the instruction and flush it */ | |
302 | } | |
303 | ||
304 | extern void dbgTrace(uint32_t, uint32_t, uint32_t, uint32_t, uint32_t); | |
305 | ||
306 | int | |
307 | fasttrap_tracepoint_remove(proc_t *p, fasttrap_tracepoint_t *tp) | |
308 | { | |
309 | uint32_t instr; | |
310 | ||
311 | /* | |
312 | * Distinguish between read or write failures and a changed | |
313 | * instruction. | |
314 | */ | |
315 | if (uread(p, &instr, 4, tp->ftt_pc) != 0) return (0); /* Get the instruction, but exit if not mapped */ | |
316 | ||
317 | // dbgTrace(0x99999999, (uint32_t)tp->ftt_pc, tp->ftt_instr, instr, 0); /* (TRACE/DEBUG) */ | |
318 | ||
319 | if (instr != FASTTRAP_INSTR) return (0); /* Did someone change it? If so, just leave */ | |
320 | ||
321 | return patchInst(p->task, tp->ftt_pc, tp->ftt_instr); /* Patch the old instruction back in and flush it */ | |
322 | } | |
323 | ||
324 | static void | |
325 | fasttrap_return_common(ppc_saved_state_t *sv, user_addr_t pc, pid_t pid, user_addr_t new_pc) | |
326 | { | |
327 | ||
328 | fasttrap_tracepoint_t *tp; | |
329 | fasttrap_bucket_t *bucket; | |
330 | fasttrap_id_t *id; | |
331 | lck_mtx_t *pid_mtx; | |
332 | ||
333 | pid_mtx = &cpu_core[CPU->cpu_id].cpuc_pid_lock; | |
334 | lck_mtx_lock(pid_mtx); | |
335 | bucket = &fasttrap_tpoints.fth_table[FASTTRAP_TPOINTS_INDEX(pid, pc)]; | |
336 | ||
337 | for (tp = bucket->ftb_data; tp != NULL; tp = tp->ftt_next) { | |
338 | if (pid == tp->ftt_pid && pc == tp->ftt_pc && | |
b0d623f7 | 339 | tp->ftt_proc->ftpc_acount != 0) |
2d21ac55 A |
340 | break; |
341 | } | |
342 | ||
343 | /* | |
344 | * Don't sweat it if we can't find the tracepoint again. Unlike | |
345 | * when we're in fasttrap_pid_probe(), finding the tracepoint here | |
346 | * is not essential to the correct execution of the process. | |
347 | */ | |
348 | if (tp == NULL) { | |
349 | lck_mtx_unlock(pid_mtx); | |
350 | return; | |
351 | } | |
352 | ||
353 | for (id = tp->ftt_retids; id != NULL; id = id->fti_next) { | |
354 | /* | |
355 | * If there's a branch that could act as a return site, we | |
356 | * need to trace it, and check here if the program counter is | |
357 | * external to the function. | |
358 | */ | |
359 | if((new_pc - id->fti_probe->ftp_faddr) < id->fti_probe->ftp_fsize) /* Is target within the function? */ | |
360 | continue; /* Yeah, skip this one... */ | |
361 | ||
362 | DTRACE_CPUFLAG_SET(CPU_DTRACE_USTACK_FP); | |
cf7d32b8 A |
363 | if (ISSET(current_proc()->p_lflag, P_LNOATTACH)) { |
364 | dtrace_probe(dtrace_probeid_error, 0 /* state */, | |
365 | id->fti_probe->ftp_id, 1 /* ndx */, -1 /* offset */, | |
366 | DTRACEFLT_UPRIV); | |
367 | } else { | |
368 | dtrace_probe(id->fti_probe->ftp_id, | |
369 | pc - id->fti_probe->ftp_faddr, | |
370 | sv->save_r3, sv->save_r4, 0, 0); | |
371 | } | |
2d21ac55 A |
372 | DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_USTACK_FP); |
373 | } | |
374 | ||
375 | lck_mtx_unlock(pid_mtx); | |
376 | } | |
377 | ||
378 | static void | |
379 | fasttrap_usdt_args(fasttrap_probe_t *probe, ppc_saved_state_t *sv, int argc, | |
380 | uint64_t *argv) | |
381 | { | |
382 | int i, x, cap = MIN(argc, probe->ftp_nargs); | |
383 | uint32_t farg; | |
384 | ||
385 | for (i = 0; i < cap; i++) { | |
386 | x = probe->ftp_argmap[i]; | |
387 | ||
388 | if (x <= 8) { /* Is this argument in a register? */ | |
389 | argv[i] = (&sv->save_r0)[x]; | |
390 | } else { | |
391 | if(sv->save_srr1 & 0x8000000000000000ULL) { /* Are we running in 64-bit? */ | |
392 | fasttrap_fuword64_noerr(sv->save_r1 + 48 + (x * sizeof(uint64_t)), &argv[i]); /* Grab argument > 8 from stack */ | |
393 | } | |
394 | else { | |
395 | fasttrap_fuword32_noerr(sv->save_r1 + 24 + (x * sizeof(uint32_t)), &farg); /* Grab argument > 8 from stack */ | |
396 | argv[i] = (uint64_t)farg; /* Convert to 64-bit */ | |
397 | } | |
398 | } | |
399 | } | |
400 | ||
401 | for (; i < argc; i++) { | |
402 | argv[i] = 0; | |
403 | } | |
404 | } | |
405 | ||
406 | int | |
407 | fasttrap_pid_probe(ppc_saved_state_t *sv) | |
408 | { | |
409 | proc_t *p = current_proc(); | |
410 | fasttrap_bucket_t *bucket; | |
411 | lck_mtx_t *pid_mtx; | |
412 | fasttrap_tracepoint_t *tp, tp_local; | |
413 | pid_t pid; | |
414 | dtrace_icookie_t cookie; | |
415 | uint_t is_enabled = 0; | |
416 | user_addr_t new_pc = 0; | |
417 | user_addr_t pc; | |
418 | user_addr_t addrmask; | |
419 | ||
420 | pc = sv->save_srr0; /* Remember the PC for later */ | |
421 | if(sv->save_srr1 & 0x8000000000000000ULL) addrmask = 0xFFFFFFFFFFFFFFFFULL; /* Set 64-bit addressing if enabled */ | |
422 | else addrmask = 0x00000000FFFFFFFFULL; /* Otherwise set 32-bit */ | |
423 | ||
424 | uthread_t uthread = (uthread_t)get_bsdthread_info(current_thread()); | |
425 | ||
426 | /* | |
427 | * Clear all user tracing flags. | |
428 | */ | |
429 | uthread->t_dtrace_ft = 0; | |
430 | ||
431 | /* | |
432 | * Treat a child created by a call to vfork(2) as if it were its | |
433 | * parent. We know that there's only one thread of control in such a | |
434 | * process: this one. | |
435 | */ | |
436 | /* | |
437 | * APPLE NOTE: Terry says: "You need to hold the process locks (currently: kernel funnel) for this traversal" | |
438 | * FIXME: How do we assert this? | |
439 | */ | |
440 | while (p->p_lflag & P_LINVFORK) p = p->p_pptr; /* Search the end */ | |
441 | ||
442 | pid = p->p_pid; | |
443 | pid_mtx = &cpu_core[CPU->cpu_id].cpuc_pid_lock; | |
444 | lck_mtx_lock(pid_mtx); | |
445 | bucket = &fasttrap_tpoints.fth_table[FASTTRAP_TPOINTS_INDEX(pid, sv->save_srr0)]; /* Get the bucket that corresponds to out PC */ | |
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 && (sv->save_srr0 == tp->ftt_pc) && | |
b0d623f7 | 452 | tp->ftt_proc->ftpc_acount != 0) |
2d21ac55 A |
453 | break; |
454 | } | |
455 | ||
456 | /* | |
457 | * If we couldn't find a matching tracepoint, either a tracepoint has | |
458 | * been inserted without using the pid<pid> ioctl interface (see | |
459 | * fasttrap_ioctl), or somehow we have mislaid this tracepoint. | |
460 | */ | |
461 | if (tp == NULL) { | |
462 | lck_mtx_unlock(pid_mtx); | |
463 | return (-1); | |
464 | } | |
465 | ||
466 | if (tp->ftt_ids != NULL) { | |
467 | fasttrap_id_t *id; | |
468 | ||
469 | for (id = tp->ftt_ids; id != NULL; id = id->fti_next) { | |
470 | fasttrap_probe_t *probe = id->fti_probe; | |
471 | ||
cf7d32b8 A |
472 | if (ISSET(current_proc()->p_lflag, P_LNOATTACH)) { |
473 | dtrace_probe(dtrace_probeid_error, 0 /* state */, | |
474 | id->fti_probe->ftp_id, 1 /* ndx */, -1 /* offset */, | |
475 | DTRACEFLT_UPRIV); | |
476 | } else if (id->fti_ptype == DTFTP_ENTRY) { | |
2d21ac55 A |
477 | /* |
478 | * We note that this was an entry | |
479 | * probe to help ustack() find the | |
480 | * first caller. | |
481 | */ | |
482 | cookie = dtrace_interrupt_disable(); | |
483 | DTRACE_CPUFLAG_SET(CPU_DTRACE_USTACK_FP | CPU_DTRACE_ENTRY); | |
484 | dtrace_probe(probe->ftp_id, sv->save_r3, sv->save_r4, /* Call the main probe routine with the first 5 args */ | |
485 | sv->save_r5, sv->save_r6, sv->save_r7); | |
486 | DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_USTACK_FP | CPU_DTRACE_ENTRY); | |
487 | dtrace_interrupt_enable(cookie); | |
488 | ||
489 | } else if (id->fti_ptype == DTFTP_IS_ENABLED) { | |
490 | /* | |
491 | * Note that in this case, we don't | |
492 | * call dtrace_probe() since it's only | |
493 | * an artificial probe meant to change | |
494 | * the flow of control so that it | |
495 | * encounters the true probe. | |
496 | */ | |
497 | is_enabled = 1; | |
498 | ||
499 | } else if (probe->ftp_argmap == NULL) { | |
500 | DTRACE_CPUFLAG_SET(CPU_DTRACE_USTACK_FP); | |
501 | dtrace_probe(probe->ftp_id, sv->save_r3, sv->save_r4, /* Call the main probe routine with the first 5 args */ | |
502 | sv->save_r5, sv->save_r6, sv->save_r7); | |
503 | DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_USTACK_FP); | |
504 | ||
505 | } else { | |
506 | uint64_t t[5]; | |
507 | ||
508 | fasttrap_usdt_args(probe, sv, 5, t); /* Grab 5 arguments */ | |
509 | ||
510 | DTRACE_CPUFLAG_SET(CPU_DTRACE_USTACK_FP); | |
511 | dtrace_probe(probe->ftp_id, t[0], t[1], | |
512 | t[2], t[3], t[4]); | |
513 | DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_USTACK_FP); | |
514 | } | |
515 | ||
516 | /* APPLE NOTE: Oneshot probes get one and only one chance... */ | |
517 | if (probe->ftp_prov->ftp_provider_type == DTFTP_PROVIDER_ONESHOT) { | |
518 | fasttrap_tracepoint_remove(p, tp); | |
519 | } | |
520 | } | |
521 | } | |
522 | ||
523 | /* | |
524 | * We're about to do a bunch of work so we cache a local copy of | |
525 | * the tracepoint to emulate the instruction, and then find the | |
526 | * tracepoint again later if we need to light up any return probes. | |
527 | */ | |
528 | tp_local = *tp; | |
529 | lck_mtx_unlock(pid_mtx); | |
530 | tp = &tp_local; | |
531 | ||
532 | /* | |
533 | * If there's an is-enabled probe connected to this tracepoint it | |
534 | * means that there was a 'xor r3,r3,r3' | |
535 | * instruction that was placed there by DTrace when the binary was | |
536 | * linked. As this probe is, in fact, enabled, we need to stuff 1 | |
537 | * into R3. Accordingly, we can bypass all the instruction | |
538 | * emulation logic since we know the inevitable result. It's possible | |
539 | * that a user could construct a scenario where the 'is-enabled' | |
540 | * probe was on some other instruction, but that would be a rather | |
541 | * exotic way to shoot oneself in the foot. | |
542 | */ | |
543 | if (is_enabled) { | |
544 | sv->save_r3 = 1; /* Set condition to true */ | |
545 | new_pc = (sv->save_srr0 + 4) & addrmask; /* Just fall through to the next instruction */ | |
546 | goto done; | |
547 | } | |
548 | ||
549 | /* | |
550 | * We emulate certain types of instructions to ensure correctness | |
551 | * (in the case of position dependent instructions) or optimize | |
552 | * common cases. The rest we execute in the kernel, but with | |
553 | * most of the user's context active. | |
554 | */ | |
555 | switch (tp->ftt_type) { | |
556 | ||
557 | case ftmtNOP: /* NOP */ | |
558 | new_pc = (sv->save_srr0 + 4) & addrmask; /* Just fall through to the next instruction */ | |
559 | break; | |
560 | ||
561 | case ftmtB: /* Plain unconditional branch */ | |
562 | new_pc = (user_addr_t)((int64_t)tp->ftt_trgt); /* Assume target is absolute address for the moment */ | |
563 | if(!(tp->ftt_flgs & ftmtAbs)) new_pc = (new_pc + sv->save_srr0) & addrmask; /* We don't have absolute address, use as offset from instruction address */ | |
564 | ||
565 | if(tp->ftt_flgs & ftmtLink) sv->save_lr = (sv->save_srr0 + 4) & addrmask; /* Set the LR to the next instruction if needed */ | |
566 | break; | |
567 | ||
568 | case ftmtBC: /* Conditional PC relative or absolute branch */ | |
569 | new_pc = (user_addr_t)((int64_t)tp->ftt_trgt); /* Assume target is absolute address for the moment */ | |
570 | if(!(tp->ftt_flgs & ftmtAbs)) new_pc = new_pc + sv->save_srr0; /* We don't have absolute address, use as offset from instruction address */ | |
571 | ||
572 | if(tp->ftt_flgs & ftmtLink) sv->save_lr = (sv->save_srr0 + 4) & addrmask; /* Set the LR to the next instruction if needed */ | |
573 | if(!branchtaken(tp->ftt_bo, tp->ftt_bi, sv)) new_pc = (sv->save_srr0 + 4) & addrmask; /* If branch was not taken, set PC to next address */ | |
574 | break; | |
575 | ||
576 | case ftmtBLR: /* Conditional branch to LR */ | |
577 | new_pc = sv->save_lr; /* Branch target comes from the LR */ | |
578 | ||
579 | if(tp->ftt_flgs & ftmtLink) sv->save_lr = (sv->save_srr0 + 4) & addrmask; /* Set the LR to the next instruction if needed */ | |
580 | if(!branchtaken(tp->ftt_bo, tp->ftt_bi, sv)) new_pc = (sv->save_srr0 + 4) & addrmask; /* If branch was not taken, set PC to next address */ | |
581 | break; | |
582 | ||
583 | case ftmtBCTR: /* Conditional branch to CTR */ | |
584 | new_pc = sv->save_ctr; /* Branch target comes from the CTR */ | |
585 | ||
586 | if(tp->ftt_flgs & ftmtLink) sv->save_lr = (sv->save_srr0 + 4) & addrmask; /* Set the LR to the next instruction if needed */ | |
587 | if(!branchtaken(tp->ftt_bo, tp->ftt_bi, sv)) new_pc = (sv->save_srr0 + 4) & addrmask; /* If branch was not taken, set PC to next address */ | |
588 | break; | |
589 | ||
590 | case ftmtCommon: /* Common, non-in-kernel emulated instruction */ | |
591 | sv->save_instr[0] = 1; /* We only have one instruction to inject */ | |
592 | sv->save_instr[1] = tp->ftt_instr; /* Set the instruction */ | |
593 | sv->save_hdr.save_flags = sv->save_hdr.save_flags | SAVinject; /* Tell low-level exception return to inject the instruction */ | |
594 | uthread->t_dtrace_step = 1; /* Let it be known that a trace return is imminent */ | |
595 | return 0; /* Go and don't dome back until you are done... */ | |
596 | ||
597 | default: | |
598 | panic("fasttrap_pid_probe: invalid ftt_type = %08X\n", tp->ftt_type); /* Huh, wha happened? */ | |
599 | break; | |
600 | } | |
601 | ||
602 | ||
603 | done: | |
604 | ||
605 | /* | |
606 | * If there were no return probes when we first found the tracepoint, | |
607 | * we should feel no obligation to honor any return probes that were | |
608 | * subsequently enabled -- they'll just have to wait until the next | |
609 | * time around. | |
610 | */ | |
611 | sv->save_srr0 = new_pc; /* Set the new PC */ | |
612 | if (tp->ftt_retids != NULL) fasttrap_return_common(sv, pc, pid, new_pc); | |
613 | ||
614 | return (0); | |
615 | } | |
616 | ||
617 | ||
618 | int | |
619 | fasttrap_return_probe(ppc_saved_state_t *sv) | |
620 | { | |
621 | ||
622 | user_addr_t pc, npc; | |
623 | ||
624 | proc_t *p = current_proc(); | |
625 | ||
626 | ||
627 | /* | |
628 | * Treat a child created by a call to vfork(2) as if it were its | |
629 | * parent. We know that there's only one thread of control in such a | |
630 | * process: this one. | |
631 | */ | |
632 | /* | |
633 | * APPLE NOTE: Terry says: "You need to hold the process locks (currently: kernel funnel) for this traversal" | |
634 | * How do we assert this? | |
635 | */ | |
636 | while (p->p_lflag & P_LINVFORK) { | |
637 | p = p->p_pptr; | |
638 | } | |
639 | ||
640 | pc = sv->save_srr0; /* Get the PC of the probed instruction */ | |
641 | npc = pc + 4; /* Get next PC */ | |
642 | if(!(sv->save_srr1 & 0x8000000000000000ULL)) npc &= 0x00000000FFFFFFFF; /* Wrap new PC if running 32-bit */ | |
643 | fasttrap_return_common(sv, pc, p->p_pid, npc); | |
644 | ||
645 | return (0); | |
646 | } | |
647 | ||
648 | uint64_t | |
649 | fasttrap_pid_getarg(void *arg, dtrace_id_t id, void *parg, int argno, | |
650 | int aframes) | |
651 | { | |
652 | #pragma unused(arg, id, parg, aframes) | |
653 | return (fasttrap_anarg((ppc_saved_state_t *)find_user_regs(current_thread()), 1, argno)); | |
654 | } | |
655 | ||
656 | uint64_t | |
657 | fasttrap_usdt_getarg(void *arg, dtrace_id_t id, void *parg, int argno, | |
658 | int aframes) | |
659 | { | |
660 | #pragma unused(arg, id, parg, aframes) | |
661 | return (fasttrap_anarg((ppc_saved_state_t *)find_user_regs(current_thread()), 0, argno)); | |
662 | } | |
663 | ||
664 | ||
665 | static int32_t branchtaken(int32_t bo, int32_t bi, ppc_saved_state_t *sv) { | |
666 | int32_t bcond, czero, crmatch; | |
667 | uint64_t ctr; | |
668 | ||
669 | if((bo & 0x14) == 0x14) return 1; /* If this is a branch always, exit with true... */ | |
670 | ||
671 | czero = 0; /* Assume that we have not just decremented the CTR to 0 */ | |
672 | ||
673 | if(!(bo & 4)) { /* Skip the next bit if we do NOT muck with the CTR */ | |
674 | ctr = sv->save_ctr = sv->save_ctr - 1; /* Decrement the CTR */ | |
675 | if(!(sv->save_srr1 & 0x8000000000000000ULL)) ctr &= 0x00000000FFFFFFFF; /* Only look at the bottom 32 bits if 32-bit mode */ | |
676 | czero = (ctr == 0); /* Remember if we just hit zero */ | |
677 | } | |
678 | ||
679 | bcond = (bo >> 3); /* If 1, branch if CR flag is 1. If 0, branch if 0 */ | |
680 | crmatch = bo >> 4; /* If bo[0] is set, do not check CR flag */ | |
681 | crmatch = crmatch | (((sv->save_cr >> (31 - bi)) ^ bcond) ^ 1); /* Low bit is now set if CR flag matches or CR is not checked. Other bits are trash. */ | |
682 | ||
683 | // dbgTrace(0x77777777, bo, bi, sv->save_cr, ((czero | crmatch) & 1)); /* (TRACE/DEBUG) */ | |
684 | ||
685 | return ((czero | crmatch) & 1); /* Return 1 if branch taken, 0 if not... */ | |
686 | } | |
687 | ||
688 | static int32_t dtrace_decode_ppc(uint32_t inst) { | |
689 | ||
690 | int32_t curdcd, lastmask, newmask, spr, bit, bito, word; | |
691 | uint16_t xop = 0; | |
692 | dcdtab *dcd; | |
693 | ||
694 | curdcd = inst >> 26; /* Isolate major op code to start decode */ | |
695 | lastmask = 99; /* Always force a new xop at the start */ | |
696 | ||
697 | while(1) { /* Loop until we find instruction or fail */ | |
698 | dcd = &insts[curdcd]; /* Point to the current decode table entry */ | |
699 | if(dcd->dcdFlgs & dcdJump) { /* Should we jump to a new spot in the decode table? */ | |
700 | curdcd = dcd->dcdMatch; /* Jump */ | |
701 | continue; | |
702 | } | |
703 | ||
704 | newmask = dcd->dcdFlgs & dcdMask; /* Isolate the mask index */ | |
705 | if(lastmask != newmask) { /* Are we changing masks? */ | |
706 | if(!newmask) break; /* If the mask is 0, we match everything and succeed... (note: lastmask can never be 0) */ | |
707 | xop = inst & masktab[newmask]; /* Clear all extra bits to make match */ | |
708 | lastmask = newmask; /* Remember */ | |
709 | } | |
710 | ||
711 | if(xop == dcd->dcdMatch) break; /* We found our guy! */ | |
712 | ||
713 | if(!(dcd->dcdFlgs & dcdStep)) { /* No stepping, we failed */ | |
714 | dcd = &dcdfail; /* Point to a failure entry */ | |
715 | break; /* Leave... */ | |
716 | } | |
717 | ||
718 | curdcd = curdcd + 1; /* Step to the next decode entry */ | |
719 | } | |
720 | ||
721 | if(dcd->dcdType != diSPR) return (int32_t)(dcd->dcdType); /* Return what we found */ | |
722 | ||
723 | spr = (inst >> (31 - 20)) & 0x3FF; /* Get the source */ | |
724 | spr = ((spr << 5) & 0x3E0) | ((spr >> 5) & 0x1F); /* Flip to right order */ | |
725 | ||
726 | word = spr >> 5; /* Get word index into table */ | |
727 | bito = spr & 0x1F; /* Get bit offset into entry */ | |
728 | bit = 0x80000000 >> bito; /* Position bit for a test */ | |
729 | ||
730 | if(!(sprtbl[word] & bit)) return (diINV); /* Bogus SPR so whole instruction is invalid... */ | |
731 | ||
732 | if(spr & 0x10) return (diPRV); /* This is a priviliged SPR so instruction is priviliged... */ | |
733 | return (diCMN); /* Just a common SPR so instruction is the same... */ | |
734 | } |