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