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 * Portions Copyright 2013 Justin Hibbits jhibbits@freebsd.org
23 * Portions Copyright 2013 Howard Su howardsu@freebsd.org
24 * Portions Copyright 2015 Ruslan Bukin <br@bsdpad.com>
25 *
26 * $FreeBSD$
27 */
28
29 /*
30 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
31 * Use is subject to license terms.
32 */
33
34 #include <sys/cdefs.h>
35 #include <sys/param.h>
36
37 #include <sys/dtrace.h>
38
39 #include "fbt.h"
40
41 #define AARCH64_BRK 0xd4200000
42 #define AARCH64_BRK_IMM16_SHIFT 5
43 #define AARCH64_BRK_IMM16_VAL (0x40d << AARCH64_BRK_IMM16_SHIFT)
44 #define FBT_PATCHVAL (AARCH64_BRK | AARCH64_BRK_IMM16_VAL)
45 #define FBT_ENTRY "entry"
46 #define FBT_RETURN "return"
47 #define FBT_AFRAMES 4
48
49 int
50 fbt_invop(uintptr_t addr, struct trapframe *frame, uintptr_t rval)
51 {
52 solaris_cpu_t *cpu;
53 fbt_probe_t *fbt;
54
55 cpu = &solaris_cpu[curcpu];
56 fbt = fbt_probetab[FBT_ADDR2NDX(addr)];
57
58 for (; fbt != NULL; fbt = fbt->fbtp_hashnext) {
59 if ((uintptr_t)fbt->fbtp_patchpoint != addr)
60 continue;
61
62 cpu->cpu_dtrace_caller = addr;
63
64 if (fbt->fbtp_roffset == 0) {
65 dtrace_probe(fbt->fbtp_id, frame->tf_x[0],
66 frame->tf_x[1], frame->tf_x[2],
67 frame->tf_x[3], frame->tf_x[4]);
68 } else {
69 dtrace_probe(fbt->fbtp_id, fbt->fbtp_roffset, rval,
70 0, 0, 0);
71 }
72 cpu->cpu_dtrace_caller = 0;
73 return (fbt->fbtp_savedval);
74 }
75
76 return (0);
77 }
78
79 void
80 fbt_patch_tracepoint(fbt_probe_t *fbt, fbt_patchval_t val)
81 {
82 vm_offset_t addr;
83
84 if (!arm64_get_writable_addr((vm_offset_t)fbt->fbtp_patchpoint, &addr))
85 panic("%s: Unable to write new instruction", __func__);
86
87 *(fbt_patchval_t *)addr = val;
88 cpu_icache_sync_range((vm_offset_t)fbt->fbtp_patchpoint, 4);
89 }
90
91 int
92 fbt_provide_module_function(linker_file_t lf, int symindx,
93 linker_symval_t *symval, void *opaque)
94 {
95 fbt_probe_t *fbt, *retfbt;
96 uint32_t *target, *start;
97 uint32_t *instr, *limit;
98 const char *name;
99 char *modname;
100 bool found;
101 int offs;
102
103 modname = opaque;
104 name = symval->name;
105
106 /* Check if function is excluded from instrumentation */
107 if (fbt_excluded(name))
108 return (0);
109
110 /*
111 * Instrumenting certain exception handling functions can lead to FBT
112 * recursion, so exclude from instrumentation.
113 */
114 if (strcmp(name, "handle_el1h_sync") == 0 ||
115 strcmp(name, "do_el1h_sync") == 0)
116 return (1);
117
118 instr = (uint32_t *)(symval->value);
119 limit = (uint32_t *)(symval->value + symval->size);
120
121 /*
122 * Ignore any bti instruction at the start of the function
123 * we need to keep it there for any indirect branches calling
124 * the function on Armv8.5+
125 */
126 if ((*instr & BTI_MASK) == BTI_INSTR)
127 instr++;
128
129 /* Look for stp (pre-indexed) operation */
130 found = false;
131 /*
132 * If the first instruction is a nop it's a specially marked
133 * asm function. We only support a nop first as it's not a normal
134 * part of the function prologue.
135 */
136 if (*instr == NOP_INSTR)
137 found = true;
138 if (!found) {
139 for (; instr < limit; instr++) {
140 /*
141 * Some functions start with
142 * "stp xt1, xt2, [xn, <const>]!"
143 */
144 if ((*instr & LDP_STP_MASK) == STP_64) {
145 /*
146 * Assume any other store of this type means we
147 * are past the function prolog.
148 */
149 if (((*instr >> ADDR_SHIFT) & ADDR_MASK) == 31)
150 found = true;
151 break;
152 }
153
154 /*
155 * Some functions start with a "sub sp, sp, <const>"
156 * Sometimes the compiler will have a sub instruction
157 * that is not of the above type so don't stop if we
158 * see one.
159 */
160 if ((*instr & SUB_MASK) == SUB_INSTR &&
161 ((*instr >> SUB_RD_SHIFT) & SUB_R_MASK) == 31 &&
162 ((*instr >> SUB_RN_SHIFT) & SUB_R_MASK) == 31) {
163 found = true;
164 break;
165 }
166 }
167 }
168
169 if (!found)
170 return (0);
171
172 fbt = malloc(sizeof (fbt_probe_t), M_FBT, M_WAITOK | M_ZERO);
173 fbt->fbtp_name = name;
174 fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
175 name, FBT_ENTRY, FBT_AFRAMES, fbt);
176 fbt->fbtp_patchpoint = instr;
177 fbt->fbtp_ctl = lf;
178 fbt->fbtp_loadcnt = lf->loadcnt;
179 fbt->fbtp_savedval = *instr;
180 fbt->fbtp_patchval = FBT_PATCHVAL;
181 if ((*instr & SUB_MASK) == SUB_INSTR)
182 fbt->fbtp_rval = DTRACE_INVOP_SUB;
183 else
184 fbt->fbtp_rval = DTRACE_INVOP_STP;
185 fbt->fbtp_symindx = symindx;
186
187 fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
188 fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;
189
190 lf->fbt_nentries++;
191
192 retfbt = NULL;
193 again:
194 for (; instr < limit; instr++) {
195 if (*instr == RET_INSTR)
196 break;
197 else if ((*instr & B_MASK) == B_INSTR) {
198 offs = (*instr & B_DATA_MASK);
199 offs *= 4;
200 target = (instr + offs);
201 start = (uint32_t *)symval->value;
202 if (target >= limit || target < start)
203 break;
204 }
205 }
206
207 if (instr >= limit)
208 return (0);
209
210 /*
211 * We have a winner!
212 */
213 fbt = malloc(sizeof (fbt_probe_t), M_FBT, M_WAITOK | M_ZERO);
214 fbt->fbtp_name = name;
215 if (retfbt == NULL) {
216 fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
217 name, FBT_RETURN, FBT_AFRAMES, fbt);
218 } else {
219 retfbt->fbtp_probenext = fbt;
220 fbt->fbtp_id = retfbt->fbtp_id;
221 }
222 retfbt = fbt;
223
224 fbt->fbtp_patchpoint = instr;
225 fbt->fbtp_ctl = lf;
226 fbt->fbtp_loadcnt = lf->loadcnt;
227 fbt->fbtp_symindx = symindx;
228 if ((*instr & B_MASK) == B_INSTR)
229 fbt->fbtp_rval = DTRACE_INVOP_B;
230 else
231 fbt->fbtp_rval = DTRACE_INVOP_RET;
232 fbt->fbtp_roffset = (uintptr_t)instr - (uintptr_t)symval->value;
233 fbt->fbtp_savedval = *instr;
234 fbt->fbtp_patchval = FBT_PATCHVAL;
235 fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
236 fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;
237
238 lf->fbt_nentries++;
239
240 instr++;
241 goto again;
242 }
Cache object: f78d7654b815bf2052284c9a570a7e36
|