xref: /xnu-11215.41.3/bsd/dev/i386/sdt_x86.c (revision 33de042d024d46de5ff4e89f2471de6608e37fa4)
1 /*
2  * CDDL HEADER START
3  *
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.
7  *
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.
12  *
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]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <kern/cpu_data.h>
27 #include <kern/thread.h>
28 #include <mach/thread_status.h>
29 #include <mach/vm_param.h>
30 
31 #include <sys/dtrace.h>
32 #include <sys/dtrace_impl.h>
33 
34 #include <sys/dtrace_glue.h>
35 
36 #include <sys/sdt_impl.h>
37 
38 extern sdt_probe_t      **sdt_probetab;
39 
40 /*ARGSUSED*/
41 int
sdt_invop(uintptr_t addr,uintptr_t * stack,uintptr_t eax)42 sdt_invop(uintptr_t addr, uintptr_t *stack, uintptr_t eax)
43 {
44 #pragma unused(eax)
45 	sdt_probe_t *sdt = sdt_probetab[SDT_ADDR2NDX(addr)];
46 
47 	for (; sdt != NULL; sdt = sdt->sdp_hashnext) {
48 		if ((uintptr_t)sdt->sdp_patchpoint == addr) {
49 			x86_saved_state64_t *regs = (x86_saved_state64_t *)stack;
50 
51 			dtrace_probe(sdt->sdp_id, regs->rdi, regs->rsi, regs->rdx, regs->rcx, regs->r8);
52 
53 			return DTRACE_INVOP_NOP;
54 		}
55 	}
56 
57 	return 0;
58 }
59 
60 
61 struct frame {
62 	struct frame *backchain;
63 	uintptr_t retaddr;
64 };
65 
66 /*ARGSUSED*/
67 uint64_t
sdt_getarg(void * arg,dtrace_id_t id,void * parg,int argno,int aframes)68 sdt_getarg(void *arg, dtrace_id_t id, void *parg, int argno, int aframes)
69 {
70 #pragma unused(arg, id, parg)
71 	uint64_t val;
72 	struct frame *fp = (struct frame *)__builtin_frame_address(0);
73 	uintptr_t *stack;
74 	uintptr_t pc;
75 	int i;
76 
77 	/*
78 	 * A total of 6 arguments are passed via registers; any argument with
79 	 * index of 5 or lower is therefore in a register.
80 	 */
81 	int inreg = 5;
82 
83 	for (i = 1; i <= aframes; i++) {
84 		fp = fp->backchain;
85 		pc = fp->retaddr;
86 
87 		if (dtrace_invop_callsite_pre != NULL
88 		    && pc > (uintptr_t)dtrace_invop_callsite_pre
89 		    && pc <= (uintptr_t)dtrace_invop_callsite_post) {
90 			/*
91 			 * In the case of x86_64, we will use the pointer to the
92 			 * save area structure that was pushed when we took the
93 			 * trap.  To get this structure, we must increment
94 			 * beyond the frame structure. If the
95 			 * argument that we're seeking is passed on the stack,
96 			 * we'll pull the true stack pointer out of the saved
97 			 * registers and decrement our argument by the number
98 			 * of arguments passed in registers; if the argument
99 			 * we're seeking is passed in regsiters, we can just
100 			 * load it directly.
101 			 */
102 
103 			/* fp points to frame of dtrace_invop() activation. */
104 			fp = fp->backchain; /* to fbt_perfcallback() activation. */
105 			fp = fp->backchain; /* to kernel_trap() activation. */
106 			fp = fp->backchain; /* to trap_from_kernel() activation. */
107 
108 			x86_saved_state_t   *tagged_regs = (x86_saved_state_t *)&fp[1];
109 			x86_saved_state64_t *saved_state = saved_state64(tagged_regs);
110 
111 			if (argno <= inreg) {
112 				stack = (uintptr_t *)(void*)&saved_state->rdi;
113 			} else {
114 				fp = (struct frame *)(saved_state->isf.rsp);
115 				stack = (uintptr_t *)&fp[0]; /* Find marshalled
116 				                              *  arguments */
117 				argno -= (inreg + 1);
118 			}
119 			goto load;
120 		}
121 	}
122 
123 	/*
124 	 * We know that we did not come through a trap to get into
125 	 * dtrace_probe() --  We arrive here when the provider has
126 	 * called dtrace_probe() directly.
127 	 * The probe ID is the first argument to dtrace_probe().
128 	 * We must advance beyond that to get the argX.
129 	 */
130 	argno++; /* Advance past probeID */
131 
132 	if (argno <= inreg) {
133 		/*
134 		 * This shouldn't happen.  If the argument is passed in a
135 		 * register then it should have been, well, passed in a
136 		 * register...
137 		 */
138 		DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP);
139 		return 0;
140 	}
141 
142 	argno -= (inreg + 1);
143 	stack = (uintptr_t *)&fp[1]; /* Find marshalled arguments */
144 
145 load:
146 	DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
147 	/* dtrace_probe arguments arg0 ... arg4 are 64bits wide */
148 	val = (uint64_t)(*(((uintptr_t *)stack) + argno));
149 	DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT);
150 
151 	return val;
152 }
153