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