xref: /freebsd/sys/cddl/dev/fbt/x86/fbt_isa.c (revision 0b3105a37d7adcadcb720112fed4dc4e8040be99)
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  * Portions Copyright 2006-2008 John Birrell jb@freebsd.org
22  *
23  * $FreeBSD$
24  *
25  */
26 
27 /*
28  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
29  * Use is subject to license terms.
30  */
31 
32 #include <sys/cdefs.h>
33 #include <sys/param.h>
34 
35 #include <sys/dtrace.h>
36 
37 #include "fbt.h"
38 
39 #define	FBT_PUSHL_EBP		0x55
40 #define	FBT_MOVL_ESP_EBP0_V0	0x8b
41 #define	FBT_MOVL_ESP_EBP1_V0	0xec
42 #define	FBT_MOVL_ESP_EBP0_V1	0x89
43 #define	FBT_MOVL_ESP_EBP1_V1	0xe5
44 #define	FBT_REX_RSP_RBP		0x48
45 
46 #define	FBT_POPL_EBP		0x5d
47 #define	FBT_RET			0xc3
48 #define	FBT_RET_IMM16		0xc2
49 #define	FBT_LEAVE		0xc9
50 
51 #ifdef __amd64__
52 #define	FBT_PATCHVAL		0xcc
53 #else
54 #define	FBT_PATCHVAL		0xf0
55 #endif
56 
57 #define	FBT_ENTRY	"entry"
58 #define	FBT_RETURN	"return"
59 
60 int
61 fbt_invop(uintptr_t addr, uintptr_t *stack, uintptr_t rval)
62 {
63 	solaris_cpu_t *cpu = &solaris_cpu[curcpu];
64 	uintptr_t stack0, stack1, stack2, stack3, stack4;
65 	fbt_probe_t *fbt = fbt_probetab[FBT_ADDR2NDX(addr)];
66 
67 	for (; fbt != NULL; fbt = fbt->fbtp_hashnext) {
68 		if ((uintptr_t)fbt->fbtp_patchpoint == addr) {
69 			if (fbt->fbtp_roffset == 0) {
70 				int i = 0;
71 				/*
72 				 * When accessing the arguments on the stack,
73 				 * we must protect against accessing beyond
74 				 * the stack.  We can safely set NOFAULT here
75 				 * -- we know that interrupts are already
76 				 * disabled.
77 				 */
78 				DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
79 				cpu->cpu_dtrace_caller = stack[i++];
80 				stack0 = stack[i++];
81 				stack1 = stack[i++];
82 				stack2 = stack[i++];
83 				stack3 = stack[i++];
84 				stack4 = stack[i++];
85 				DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT |
86 				    CPU_DTRACE_BADADDR);
87 
88 				dtrace_probe(fbt->fbtp_id, stack0, stack1,
89 				    stack2, stack3, stack4);
90 
91 				cpu->cpu_dtrace_caller = 0;
92 			} else {
93 #ifdef __amd64__
94 				/*
95 				 * On amd64, we instrument the ret, not the
96 				 * leave.  We therefore need to set the caller
97 				 * to assure that the top frame of a stack()
98 				 * action is correct.
99 				 */
100 				DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
101 				cpu->cpu_dtrace_caller = stack[0];
102 				DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT |
103 				    CPU_DTRACE_BADADDR);
104 #endif
105 
106 				dtrace_probe(fbt->fbtp_id, fbt->fbtp_roffset,
107 				    rval, 0, 0, 0);
108 				cpu->cpu_dtrace_caller = 0;
109 			}
110 
111 			return (fbt->fbtp_rval);
112 		}
113 	}
114 
115 	return (0);
116 }
117 
118 void
119 fbt_patch_tracepoint(fbt_probe_t *fbt, fbt_patchval_t val)
120 {
121 
122 	*fbt->fbtp_patchpoint = val;
123 }
124 
125 int
126 fbt_provide_module_function(linker_file_t lf, int symindx,
127     linker_symval_t *symval, void *opaque)
128 {
129 	char *modname = opaque;
130 	const char *name = symval->name;
131 	fbt_probe_t *fbt, *retfbt;
132 	int j;
133 	int size;
134 	uint8_t *instr, *limit;
135 
136 	if ((strncmp(name, "dtrace_", 7) == 0 &&
137 	    strncmp(name, "dtrace_safe_", 12) != 0) ||
138 	    strcmp(name, "trap_check") == 0) {
139 		/*
140 		 * Anything beginning with "dtrace_" may be called
141 		 * from probe context unless it explicitly indicates
142 		 * that it won't be called from probe context by
143 		 * using the prefix "dtrace_safe_".
144 		 *
145 		 * Additionally, we avoid instrumenting trap_check() to avoid
146 		 * the possibility of generating a fault in probe context before
147 		 * DTrace's fault handler is called.
148 		 */
149 		return (0);
150 	}
151 
152 	if (name[0] == '_' && name[1] == '_')
153 		return (0);
154 
155 	size = symval->size;
156 
157 	instr = (uint8_t *) symval->value;
158 	limit = (uint8_t *) symval->value + symval->size;
159 
160 #ifdef __amd64__
161 	while (instr < limit) {
162 		if (*instr == FBT_PUSHL_EBP)
163 			break;
164 
165 		if ((size = dtrace_instr_size(instr)) <= 0)
166 			break;
167 
168 		instr += size;
169 	}
170 
171 	if (instr >= limit || *instr != FBT_PUSHL_EBP) {
172 		/*
173 		 * We either don't save the frame pointer in this
174 		 * function, or we ran into some disassembly
175 		 * screw-up.  Either way, we bail.
176 		 */
177 		return (0);
178 	}
179 #else
180 	if (instr[0] != FBT_PUSHL_EBP)
181 		return (0);
182 
183 	if (!(instr[1] == FBT_MOVL_ESP_EBP0_V0 &&
184 	    instr[2] == FBT_MOVL_ESP_EBP1_V0) &&
185 	    !(instr[1] == FBT_MOVL_ESP_EBP0_V1 &&
186 	    instr[2] == FBT_MOVL_ESP_EBP1_V1))
187 		return (0);
188 #endif
189 
190 	fbt = malloc(sizeof (fbt_probe_t), M_FBT, M_WAITOK | M_ZERO);
191 	fbt->fbtp_name = name;
192 	fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
193 	    name, FBT_ENTRY, 3, fbt);
194 	fbt->fbtp_patchpoint = instr;
195 	fbt->fbtp_ctl = lf;
196 	fbt->fbtp_loadcnt = lf->loadcnt;
197 	fbt->fbtp_rval = DTRACE_INVOP_PUSHL_EBP;
198 	fbt->fbtp_savedval = *instr;
199 	fbt->fbtp_patchval = FBT_PATCHVAL;
200 	fbt->fbtp_symindx = symindx;
201 
202 	fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
203 	fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;
204 
205 	lf->fbt_nentries++;
206 
207 	retfbt = NULL;
208 again:
209 	if (instr >= limit)
210 		return (0);
211 
212 	/*
213 	 * If this disassembly fails, then we've likely walked off into
214 	 * a jump table or some other unsuitable area.  Bail out of the
215 	 * disassembly now.
216 	 */
217 	if ((size = dtrace_instr_size(instr)) <= 0)
218 		return (0);
219 
220 #ifdef __amd64__
221 	/*
222 	 * We only instrument "ret" on amd64 -- we don't yet instrument
223 	 * ret imm16, largely because the compiler doesn't seem to
224 	 * (yet) emit them in the kernel...
225 	 */
226 	if (*instr != FBT_RET) {
227 		instr += size;
228 		goto again;
229 	}
230 #else
231 	if (!(size == 1 &&
232 	    (*instr == FBT_POPL_EBP || *instr == FBT_LEAVE) &&
233 	    (*(instr + 1) == FBT_RET ||
234 	    *(instr + 1) == FBT_RET_IMM16))) {
235 		instr += size;
236 		goto again;
237 	}
238 #endif
239 
240 	/*
241 	 * We (desperately) want to avoid erroneously instrumenting a
242 	 * jump table, especially given that our markers are pretty
243 	 * short:  two bytes on x86, and just one byte on amd64.  To
244 	 * determine if we're looking at a true instruction sequence
245 	 * or an inline jump table that happens to contain the same
246 	 * byte sequences, we resort to some heuristic sleeze:  we
247 	 * treat this instruction as being contained within a pointer,
248 	 * and see if that pointer points to within the body of the
249 	 * function.  If it does, we refuse to instrument it.
250 	 */
251 	for (j = 0; j < sizeof (uintptr_t); j++) {
252 		caddr_t check = (caddr_t) instr - j;
253 		uint8_t *ptr;
254 
255 		if (check < symval->value)
256 			break;
257 
258 		if (check + sizeof (caddr_t) > (caddr_t)limit)
259 			continue;
260 
261 		ptr = *(uint8_t **)check;
262 
263 		if (ptr >= (uint8_t *) symval->value && ptr < limit) {
264 			instr += size;
265 			goto again;
266 		}
267 	}
268 
269 	/*
270 	 * We have a winner!
271 	 */
272 	fbt = malloc(sizeof (fbt_probe_t), M_FBT, M_WAITOK | M_ZERO);
273 	fbt->fbtp_name = name;
274 
275 	if (retfbt == NULL) {
276 		fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
277 		    name, FBT_RETURN, 3, fbt);
278 	} else {
279 		retfbt->fbtp_next = fbt;
280 		fbt->fbtp_id = retfbt->fbtp_id;
281 	}
282 
283 	retfbt = fbt;
284 	fbt->fbtp_patchpoint = instr;
285 	fbt->fbtp_ctl = lf;
286 	fbt->fbtp_loadcnt = lf->loadcnt;
287 	fbt->fbtp_symindx = symindx;
288 
289 #ifndef __amd64__
290 	if (*instr == FBT_POPL_EBP) {
291 		fbt->fbtp_rval = DTRACE_INVOP_POPL_EBP;
292 	} else {
293 		ASSERT(*instr == FBT_LEAVE);
294 		fbt->fbtp_rval = DTRACE_INVOP_LEAVE;
295 	}
296 	fbt->fbtp_roffset =
297 	    (uintptr_t)(instr - (uint8_t *) symval->value) + 1;
298 
299 #else
300 	ASSERT(*instr == FBT_RET);
301 	fbt->fbtp_rval = DTRACE_INVOP_RET;
302 	fbt->fbtp_roffset =
303 	    (uintptr_t)(instr - (uint8_t *) symval->value);
304 #endif
305 
306 	fbt->fbtp_savedval = *instr;
307 	fbt->fbtp_patchval = FBT_PATCHVAL;
308 	fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
309 	fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;
310 
311 	lf->fbt_nentries++;
312 
313 	instr += size;
314 	goto again;
315 }
316