fbt_isa.c revision 270067
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: head/sys/cddl/dev/fbt/x86/fbt_isa.c 270067 2014-08-16 21:42:55Z markj $
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
60int
61fbt_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
119void
120fbt_patch_tracepoint(fbt_probe_t *fbt, fbt_patchval_t val)
121{
122
123	*fbt->fbtp_patchpoint = val;
124}
125
126int
127fbt_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;
209again:
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