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