]>
git.saurik.com Git - apple/xnu.git/blob - bsd/dev/i386/dtrace_subr_x86.c
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
28 * #pragma ident "@(#)dtrace_subr.c 1.13 06/06/12 SMI"
31 #include <sys/dtrace.h>
32 #include <sys/dtrace_glue.h>
33 #include <sys/dtrace_impl.h>
34 #include <sys/fasttrap.h>
37 #include <sys/kauth.h>
38 #include <kern/debug.h>
40 int (*dtrace_pid_probe_ptr
)(x86_saved_state_t
*);
41 int (*dtrace_return_probe_ptr
)(x86_saved_state_t
*);
44 * HACK! There doesn't seem to be an easy way to include trap.h from
47 #define T_INT3 3 /* int 3 instruction */
48 #define T_DTRACE_RET 0x7f /* DTrace pid return */
51 dtrace_user_probe(x86_saved_state_t
*);
54 dtrace_user_probe(x86_saved_state_t
*regs
)
56 x86_saved_state64_t
*regs64
;
57 x86_saved_state32_t
*regs32
;
63 * The only call path into this method is always a user trap.
64 * We don't need to test for user trap, but should assert it.
66 boolean_t user_mode
= TRUE
;
68 if (is_saved_state64(regs
) == TRUE
) {
69 regs64
= saved_state64(regs
);
71 trapno
= regs64
->isf
.trapno
;
72 user_mode
= TRUE
; // By default, because xnu is 32 bit only
75 regs32
= saved_state32(regs
);
76 if (regs32
->cs
& 0x03) user_mode
= TRUE
;
77 trapno
= regs32
->trapno
;
81 struct proc
*p
= current_proc();
83 uthread_t uthread
= (uthread_t
)get_bsdthread_info(current_thread());
84 if (user_mode
/*|| (rp->r_ps & PS_VM)*/) {
86 * DTrace accesses t_cred in probe context. t_cred
87 * must always be either NULL, or point to a valid,
88 * allocated cred structure.
90 kauth_cred_uthread_update(uthread
, p
);
93 if (trapno
== T_DTRACE_RET
) {
94 uint8_t step
= uthread
->t_dtrace_step
;
95 uint8_t ret
= uthread
->t_dtrace_ret
;
96 user_addr_t npc
= uthread
->t_dtrace_npc
;
98 if (uthread
->t_dtrace_ast
) {
99 printf("dtrace_user_probe() should be calling aston()\n");
101 // uthread->t_sig_check = 1;
105 * Clear all user tracing flags.
107 uthread
->t_dtrace_ft
= 0;
110 * If we weren't expecting to take a return probe trap, kill
111 * the process as though it had just executed an unassigned
116 * APPLE NOTE: We're returning KERN_FAILURE, which causes
117 * the generic signal handling code to take over, which will effectively
118 * deliver a EXC_BAD_INSTRUCTION to the user process.
124 * If we hit this trap unrelated to a return probe, we're
125 * just here to reset the AST flag since we deferred a signal
126 * until after we logically single-stepped the instruction we
131 regs64
->isf
.rip
= npc
;
139 * We need to wait until after we've called the
140 * dtrace_return_probe_ptr function pointer to set %pc.
142 rwp
= &CPU
->cpu_ft_lock
;
143 lck_rw_lock_shared(rwp
);
145 if (dtrace_return_probe_ptr
!= NULL
)
146 (void) (*dtrace_return_probe_ptr
)(regs
);
147 lck_rw_unlock_shared(rwp
);
150 regs64
->isf
.rip
= npc
;
156 } else if (trapno
== T_INT3
) {
158 rwp
= &CPU
->cpu_ft_lock
;
161 * The DTrace fasttrap provider uses the breakpoint trap
162 * (int 3). We let DTrace take the first crack at handling
163 * this trap; if it's not a probe that DTrace knowns about,
164 * we call into the trap() routine to handle it like a
165 * breakpoint placed by a conventional debugger.
169 * APPLE NOTE: I believe the purpose of the reader/writers lock
170 * is thus: There are times which dtrace needs to prevent calling
171 * dtrace_pid_probe_ptr(). Sun's original impl grabbed a plain
172 * mutex here. However, that serialized all probe calls, and
173 * destroyed MP behavior. So now they use a RW lock, with probes
174 * as readers, and the top level synchronization as a writer.
176 lck_rw_lock_shared(rwp
);
177 if (dtrace_pid_probe_ptr
!= NULL
&&
178 (*dtrace_pid_probe_ptr
)(regs
) == 0) {
179 lck_rw_unlock_shared(rwp
);
182 lck_rw_unlock_shared(rwp
);
186 * If the instruction that caused the breakpoint trap doesn't
187 * look like an int 3 anymore, it may be that this tracepoint
188 * was removed just after the user thread executed it. In
189 * that case, return to user land to retry the instuction.
191 user_addr_t pc
= (regs64
) ? regs64
->isf
.rip
: (user_addr_t
)regs32
->eip
;
192 if (fuword8(pc
- 1, &instr
) == 0 && instr
!= FASTTRAP_INSTR
) {
207 dtrace_safe_synchronous_signal(void)
210 kthread_t
*t
= curthread
;
211 struct regs
*rp
= lwptoregs(ttolwp(t
));
212 size_t isz
= t
->t_dtrace_npc
- t
->t_dtrace_pc
;
214 ASSERT(t
->t_dtrace_on
);
217 * If we're not in the range of scratch addresses, we're not actually
218 * tracing user instructions so turn off the flags. If the instruction
219 * we copied out caused a synchonous trap, reset the pc back to its
220 * original value and turn off the flags.
222 if (rp
->r_pc
< t
->t_dtrace_scrpc
||
223 rp
->r_pc
> t
->t_dtrace_astpc
+ isz
) {
225 } else if (rp
->r_pc
== t
->t_dtrace_scrpc
||
226 rp
->r_pc
== t
->t_dtrace_astpc
) {
227 rp
->r_pc
= t
->t_dtrace_pc
;
234 dtrace_safe_defer_signal(void)
237 kthread_t
*t
= curthread
;
238 struct regs
*rp
= lwptoregs(ttolwp(t
));
239 size_t isz
= t
->t_dtrace_npc
- t
->t_dtrace_pc
;
241 ASSERT(t
->t_dtrace_on
);
244 * If we're not in the range of scratch addresses, we're not actually
245 * tracing user instructions so turn off the flags.
247 if (rp
->r_pc
< t
->t_dtrace_scrpc
||
248 rp
->r_pc
> t
->t_dtrace_astpc
+ isz
) {
254 * If we've executed the original instruction, but haven't performed
255 * the jmp back to t->t_dtrace_npc or the clean up of any registers
256 * used to emulate %rip-relative instructions in 64-bit mode, do that
257 * here and take the signal right away. We detect this condition by
258 * seeing if the program counter is the range [scrpc + isz, astpc).
260 if (t
->t_dtrace_astpc
- rp
->r_pc
<
261 t
->t_dtrace_astpc
- t
->t_dtrace_scrpc
- isz
) {
264 * If there is a scratch register and we're on the
265 * instruction immediately after the modified instruction,
266 * restore the value of that scratch register.
268 if (t
->t_dtrace_reg
!= 0 &&
269 rp
->r_pc
== t
->t_dtrace_scrpc
+ isz
) {
270 switch (t
->t_dtrace_reg
) {
272 rp
->r_rax
= t
->t_dtrace_regv
;
275 rp
->r_rcx
= t
->t_dtrace_regv
;
278 rp
->r_r8
= t
->t_dtrace_regv
;
281 rp
->r_r9
= t
->t_dtrace_regv
;
286 rp
->r_pc
= t
->t_dtrace_npc
;
292 * Otherwise, make sure we'll return to the kernel after executing
293 * the copied out instruction and defer the signal.
295 if (!t
->t_dtrace_step
) {
296 ASSERT(rp
->r_pc
< t
->t_dtrace_astpc
);
297 rp
->r_pc
+= t
->t_dtrace_astpc
- t
->t_dtrace_scrpc
;
298 t
->t_dtrace_step
= 1;