xref: /freebsd/sys/cddl/dev/fbt/x86/fbt_isa.c (revision 3078531de10dcae44b253a35125c949ff4235284)
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 <machine/cpufunc.h>
38 #include <machine/md_var.h>
39 
40 #include "fbt.h"
41 
42 #define	FBT_PUSHL_EBP		0x55
43 #define	FBT_MOVL_ESP_EBP0_V0	0x8b
44 #define	FBT_MOVL_ESP_EBP1_V0	0xec
45 #define	FBT_MOVL_ESP_EBP0_V1	0x89
46 #define	FBT_MOVL_ESP_EBP1_V1	0xe5
47 #define	FBT_REX_RSP_RBP		0x48
48 
49 #define	FBT_POPL_EBP		0x5d
50 #define	FBT_RET			0xc3
51 #define	FBT_RET_IMM16		0xc2
52 #define	FBT_LEAVE		0xc9
53 
54 #ifdef __amd64__
55 #define	FBT_PATCHVAL		0xcc
56 #else
57 #define	FBT_PATCHVAL		0xf0
58 #endif
59 
60 #define	FBT_ENTRY	"entry"
61 #define	FBT_RETURN	"return"
62 
63 int
64 fbt_invop(uintptr_t addr, struct trapframe *frame, uintptr_t scratch __unused)
65 {
66 	solaris_cpu_t *cpu;
67 	uintptr_t *stack;
68 	uintptr_t arg0, arg1, arg2, arg3, arg4, rval;
69 	fbt_probe_t *fbt;
70 	int8_t fbtrval;
71 
72 #ifdef __amd64__
73 	stack = (uintptr_t *)frame->tf_rsp;
74 	rval = frame->tf_rax;
75 #else
76 	/* Skip hardware-saved registers. */
77 	stack = (uintptr_t *)frame->tf_isp + 3;
78 	rval = frame->tf_eax;
79 #endif
80 
81 	cpu = &solaris_cpu[curcpu];
82 	fbt = fbt_probetab[FBT_ADDR2NDX(addr)];
83 	for (; fbt != NULL; fbt = fbt->fbtp_hashnext) {
84 		if ((uintptr_t)fbt->fbtp_patchpoint != addr)
85 			continue;
86 		fbtrval = fbt->fbtp_rval;
87 		for (; fbt != NULL; fbt = fbt->fbtp_tracenext) {
88 			ASSERT(fbt->fbtp_rval == fbtrval);
89 			if (fbt->fbtp_roffset == 0) {
90 #ifdef __amd64__
91 				/* fbt->fbtp_rval == DTRACE_INVOP_PUSHQ_RBP */
92 				DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
93 				cpu->cpu_dtrace_caller = stack[0];
94 				DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT |
95 				    CPU_DTRACE_BADADDR);
96 
97 				arg0 = frame->tf_rdi;
98 				arg1 = frame->tf_rsi;
99 				arg2 = frame->tf_rdx;
100 				arg3 = frame->tf_rcx;
101 				arg4 = frame->tf_r8;
102 #else
103 				int i = 0;
104 
105 				/*
106 				 * When accessing the arguments on the stack,
107 				 * we must protect against accessing beyond
108 				 * the stack.  We can safely set NOFAULT here
109 				 * -- we know that interrupts are already
110 				 * disabled.
111 				 */
112 				DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
113 				cpu->cpu_dtrace_caller = stack[i++];
114 				arg0 = stack[i++];
115 				arg1 = stack[i++];
116 				arg2 = stack[i++];
117 				arg3 = stack[i++];
118 				arg4 = stack[i++];
119 				DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT |
120 				    CPU_DTRACE_BADADDR);
121 #endif
122 
123 				dtrace_probe(fbt->fbtp_id, arg0, arg1,
124 				    arg2, arg3, arg4);
125 
126 				cpu->cpu_dtrace_caller = 0;
127 			} else {
128 #ifdef __amd64__
129 				/*
130 				 * On amd64, we instrument the ret, not the
131 				 * leave.  We therefore need to set the caller
132 				 * to ensure that the top frame of a stack()
133 				 * action is correct.
134 				 */
135 				DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
136 				cpu->cpu_dtrace_caller = stack[0];
137 				DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT |
138 				    CPU_DTRACE_BADADDR);
139 #endif
140 
141 				dtrace_probe(fbt->fbtp_id, fbt->fbtp_roffset,
142 				    rval, 0, 0, 0);
143 				cpu->cpu_dtrace_caller = 0;
144 			}
145 		}
146 		return (fbtrval);
147 	}
148 
149 	return (0);
150 }
151 
152 void
153 fbt_patch_tracepoint(fbt_probe_t *fbt, fbt_patchval_t val)
154 {
155 	register_t intr;
156 	bool old_wp;
157 
158 	intr = intr_disable();
159 	old_wp = disable_wp();
160 	*fbt->fbtp_patchpoint = val;
161 	restore_wp(old_wp);
162 	intr_restore(intr);
163 }
164 
165 int
166 fbt_provide_module_function(linker_file_t lf, int symindx,
167     linker_symval_t *symval, void *opaque)
168 {
169 	char *modname = opaque;
170 	const char *name = symval->name;
171 	fbt_probe_t *fbt, *hash, *retfbt;
172 	int j;
173 	int size;
174 	uint8_t *instr, *limit;
175 
176 	if (fbt_excluded(name))
177 		return (0);
178 
179 	/*
180 	 * trap_check() is a wrapper for DTrace's fault handler, so we don't
181 	 * want to be able to instrument it.
182 	 */
183 	if (strcmp(name, "trap_check") == 0)
184 		return (0);
185 
186 	size = symval->size;
187 
188 	instr = (uint8_t *) symval->value;
189 	limit = (uint8_t *) symval->value + symval->size;
190 
191 #ifdef __amd64__
192 	while (instr < limit) {
193 		if (*instr == FBT_PUSHL_EBP)
194 			break;
195 
196 		if ((size = dtrace_instr_size(instr)) <= 0)
197 			break;
198 
199 		instr += size;
200 	}
201 
202 	if (instr >= limit || *instr != FBT_PUSHL_EBP) {
203 		/*
204 		 * We either don't save the frame pointer in this
205 		 * function, or we ran into some disassembly
206 		 * screw-up.  Either way, we bail.
207 		 */
208 		return (0);
209 	}
210 #else
211 	if (instr[0] != FBT_PUSHL_EBP)
212 		return (0);
213 
214 	if (!(instr[1] == FBT_MOVL_ESP_EBP0_V0 &&
215 	    instr[2] == FBT_MOVL_ESP_EBP1_V0) &&
216 	    !(instr[1] == FBT_MOVL_ESP_EBP0_V1 &&
217 	    instr[2] == FBT_MOVL_ESP_EBP1_V1))
218 		return (0);
219 #endif
220 
221 	fbt = malloc(sizeof (fbt_probe_t), M_FBT, M_WAITOK | M_ZERO);
222 	fbt->fbtp_name = name;
223 	fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
224 	    name, FBT_ENTRY, 3, fbt);
225 	fbt->fbtp_patchpoint = instr;
226 	fbt->fbtp_ctl = lf;
227 	fbt->fbtp_loadcnt = lf->loadcnt;
228 	fbt->fbtp_rval = DTRACE_INVOP_PUSHL_EBP;
229 	fbt->fbtp_savedval = *instr;
230 	fbt->fbtp_patchval = FBT_PATCHVAL;
231 	fbt->fbtp_symindx = symindx;
232 
233 	for (hash = fbt_probetab[FBT_ADDR2NDX(instr)]; hash != NULL;
234 	    hash = hash->fbtp_hashnext) {
235 		if (hash->fbtp_patchpoint == fbt->fbtp_patchpoint) {
236 			fbt->fbtp_tracenext = hash->fbtp_tracenext;
237 			hash->fbtp_tracenext = fbt;
238 			break;
239 		}
240 	}
241 	if (hash == NULL) {
242 		fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
243 		fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;
244 	}
245 
246 	lf->fbt_nentries++;
247 
248 	retfbt = NULL;
249 again:
250 	if (instr >= limit)
251 		return (0);
252 
253 	/*
254 	 * If this disassembly fails, then we've likely walked off into
255 	 * a jump table or some other unsuitable area.  Bail out of the
256 	 * disassembly now.
257 	 */
258 	if ((size = dtrace_instr_size(instr)) <= 0)
259 		return (0);
260 
261 #ifdef __amd64__
262 	/*
263 	 * We only instrument "ret" on amd64 -- we don't yet instrument
264 	 * ret imm16, largely because the compiler doesn't seem to
265 	 * (yet) emit them in the kernel...
266 	 */
267 	if (*instr != FBT_RET) {
268 		instr += size;
269 		goto again;
270 	}
271 #else
272 	if (!(size == 1 &&
273 	    (*instr == FBT_POPL_EBP || *instr == FBT_LEAVE) &&
274 	    (*(instr + 1) == FBT_RET ||
275 	    *(instr + 1) == FBT_RET_IMM16))) {
276 		instr += size;
277 		goto again;
278 	}
279 #endif
280 
281 	/*
282 	 * We (desperately) want to avoid erroneously instrumenting a
283 	 * jump table, especially given that our markers are pretty
284 	 * short:  two bytes on x86, and just one byte on amd64.  To
285 	 * determine if we're looking at a true instruction sequence
286 	 * or an inline jump table that happens to contain the same
287 	 * byte sequences, we resort to some heuristic sleeze:  we
288 	 * treat this instruction as being contained within a pointer,
289 	 * and see if that pointer points to within the body of the
290 	 * function.  If it does, we refuse to instrument it.
291 	 */
292 	for (j = 0; j < sizeof (uintptr_t); j++) {
293 		caddr_t check = (caddr_t) instr - j;
294 		uint8_t *ptr;
295 
296 		if (check < symval->value)
297 			break;
298 
299 		if (check + sizeof (caddr_t) > (caddr_t)limit)
300 			continue;
301 
302 		ptr = *(uint8_t **)check;
303 
304 		if (ptr >= (uint8_t *) symval->value && ptr < limit) {
305 			instr += size;
306 			goto again;
307 		}
308 	}
309 
310 	/*
311 	 * We have a winner!
312 	 */
313 	fbt = malloc(sizeof (fbt_probe_t), M_FBT, M_WAITOK | M_ZERO);
314 	fbt->fbtp_name = name;
315 
316 	if (retfbt == NULL) {
317 		fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
318 		    name, FBT_RETURN, 3, fbt);
319 	} else {
320 		retfbt->fbtp_probenext = fbt;
321 		fbt->fbtp_id = retfbt->fbtp_id;
322 	}
323 
324 	retfbt = fbt;
325 	fbt->fbtp_patchpoint = instr;
326 	fbt->fbtp_ctl = lf;
327 	fbt->fbtp_loadcnt = lf->loadcnt;
328 	fbt->fbtp_symindx = symindx;
329 
330 #ifndef __amd64__
331 	if (*instr == FBT_POPL_EBP) {
332 		fbt->fbtp_rval = DTRACE_INVOP_POPL_EBP;
333 	} else {
334 		ASSERT(*instr == FBT_LEAVE);
335 		fbt->fbtp_rval = DTRACE_INVOP_LEAVE;
336 	}
337 	fbt->fbtp_roffset =
338 	    (uintptr_t)(instr - (uint8_t *) symval->value) + 1;
339 
340 #else
341 	ASSERT(*instr == FBT_RET);
342 	fbt->fbtp_rval = DTRACE_INVOP_RET;
343 	fbt->fbtp_roffset =
344 	    (uintptr_t)(instr - (uint8_t *) symval->value);
345 #endif
346 
347 	fbt->fbtp_savedval = *instr;
348 	fbt->fbtp_patchval = FBT_PATCHVAL;
349 	fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
350 	fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;
351 
352 	lf->fbt_nentries++;
353 
354 	instr += size;
355 	goto again;
356 }
357