1 /* $NetBSD: fbt_isa.c,v 1.8 2023/05/09 21:29:07 riastradh Exp $ */
2
3 /*
4 * CDDL HEADER START
5 *
6 * The contents of this file are subject to the terms of the
7 * Common Development and Distribution License (the "License").
8 * You may not use this file except in compliance with the License.
9 *
10 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
11 * or http://www.opensolaris.org/os/licensing.
12 * See the License for the specific language governing permissions
13 * and limitations under the License.
14 *
15 * When distributing Covered Code, include this CDDL HEADER in each
16 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
17 * If applicable, add the following below this CDDL HEADER, with the
18 * fields enclosed by brackets "[]" replaced with your own identifying
19 * information: Portions Copyright [yyyy] [name of copyright owner]
20 *
21 * CDDL HEADER END
22 *
23 * Portions Copyright 2006-2008 John Birrell jb@freebsd.org
24 * Portions Copyright 2013 Justin Hibbits jhibbits@freebsd.org
25 * Portions Copyright 2013 Howard Su howardsu@freebsd.org
26 * Portions Copyright 2015 Ruslan Bukin <br@bsdpad.com>
27 *
28 * $FreeBSD$
29 */
30
31 /*
32 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
33 * Use is subject to license terms.
34 */
35
36 #include <sys/cdefs.h>
37 #include <sys/param.h>
38 #include <sys/cpu.h>
39 #include <sys/module.h>
40 #include <sys/kmem.h>
41
42 #include <uvm/uvm_extern.h>
43
44 #include <dev/mm.h>
45
46 #include <machine/cpufunc.h>
47
48 #include <sys/dtrace.h>
49
50 #include "fbt.h"
51
52 #define AARCH64_BRK 0xd4200000
53 #define AARCH64_BRK_IMM16_SHIFT 5
54 #define AARCH64_BRK_IMM16_VAL (0x40d << AARCH64_BRK_IMM16_SHIFT)
55 #define FBT_PATCHVAL (AARCH64_BRK | AARCH64_BRK_IMM16_VAL)
56 #define FBT_ENTRY "entry"
57 #define FBT_RETURN "return"
58
59 /*
60 * How many artificial frames appear between dtrace_probe and the
61 * interrupted function call?
62 *
63 * fbt_invop
64 * dtrace_invop
65 * dtrace_invop_start
66 * el1_trap_exit
67 */
68 #define FBT_AFRAMES 4
69
70 int
fbt_invop(uintptr_t addr,struct trapframe * frame,uintptr_t r0)71 fbt_invop(uintptr_t addr, struct trapframe *frame, uintptr_t r0)
72 {
73 solaris_cpu_t *cpu;
74 fbt_probe_t *fbt;
75
76 cpu = &solaris_cpu[cpu_index(curcpu())];
77 fbt = fbt_probetab[FBT_ADDR2NDX(addr)];
78
79 for (; fbt != NULL; fbt = fbt->fbtp_hashnext) {
80 if ((uintptr_t)fbt->fbtp_patchpoint == addr) {
81 cpu->cpu_dtrace_caller = addr;
82
83 dtrace_probe(fbt->fbtp_id, frame->tf_regs.r_reg[0],
84 frame->tf_regs.r_reg[1], frame->tf_regs.r_reg[2],
85 frame->tf_regs.r_reg[3], frame->tf_regs.r_reg[4]);
86
87 cpu->cpu_dtrace_caller = 0;
88 KASSERT(fbt->fbtp_savedval != 0);
89 return (fbt->fbtp_savedval);
90 }
91 }
92
93 return (0);
94 }
95
96 void
fbt_patch_tracepoint(fbt_probe_t * fbt,fbt_patchval_t val)97 fbt_patch_tracepoint(fbt_probe_t *fbt, fbt_patchval_t val)
98 {
99 paddr_t pa;
100 vaddr_t va;
101
102 if (!pmap_extract(pmap_kernel(), (vaddr_t)fbt->fbtp_patchpoint, &pa))
103 return;
104 if (!mm_md_direct_mapped_phys(pa, &va))
105 return;
106 *(fbt_patchval_t *)va = htole32(val);
107 cpu_icache_sync_range((vm_offset_t)fbt->fbtp_patchpoint, sizeof(val));
108 }
109
110 #if defined(__FreeBSD__)
111 int
fbt_provide_module_function(linker_file_t lf,int symindx,linker_symval_t * symval,void * opaque)112 fbt_provide_module_function(linker_file_t lf, int symindx,
113 linker_symval_t *symval, void *opaque)
114 #elif defined(__NetBSD__)
115 int
116 fbt_provide_module_cb(const char *name, int symindx, void *value,
117 uint32_t symsize, int type, void *opaque)
118 #else
119 #error unsupported platform
120 #endif
121 {
122 fbt_probe_t *fbt, *retfbt;
123 uint32_t *target, *start;
124 uint32_t *instr, *limit;
125 int offs;
126
127 #ifdef __FreeBSD__
128 char *modname = opaque;
129 const char *name = symval->name;
130
131 instr = (uint32_t *)(symval->value);
132 limit = (uint32_t *)(symval->value + symval->size);
133 #endif
134 #ifdef __NetBSD__
135 struct fbt_ksyms_arg *fka = opaque;
136 modctl_t *mod = fka->fka_mod;
137 const char *modname = module_name(mod);
138
139 /* got a function? */
140 if (ELF_ST_TYPE(type) != STT_FUNC)
141 return 0;
142
143 instr = (uint32_t *)(value);
144 limit = (uint32_t *)((uintptr_t)value + symsize);
145 #endif
146
147 /* Check if function is excluded from instrumentation */
148 if (fbt_excluded(name))
149 return (0);
150
151 if (strncmp(name, "_spl", 4) == 0 ||
152 strcmp(name, "dosoftints") == 0 ||
153 strcmp(name, "nanouptime") == 0 ||
154 strncmp(name, "gtmr_", 5) == 0) {
155 return 0;
156 }
157
158 /* Look for stp (pre-indexed) operation */
159 for (; instr < limit; instr++) {
160 if ((le32toh(*instr) & LDP_STP_MASK) == STP_64)
161 break;
162 }
163
164 if (instr >= limit)
165 return (0);
166 KASSERT(*instr != 0);
167
168 #ifdef __FreeBSD__
169 fbt = malloc(sizeof (fbt_probe_t), M_FBT, M_WAITOK | M_ZERO);
170 #else
171 fbt = kmem_zalloc(sizeof (fbt_probe_t), KM_SLEEP);
172 #endif
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 #ifdef __FreeBSD__
178 fbt->fbtp_ctl = lf;
179 fbt->fbtp_loadcnt = lf->loadcnt;
180 #endif
181 #ifdef __NetBSD__
182 fbt->fbtp_ctl = mod;
183 #endif
184 fbt->fbtp_savedval = le32toh(*instr);
185 fbt->fbtp_patchval = FBT_PATCHVAL;
186 fbt->fbtp_symindx = symindx;
187
188 fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
189 fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;
190
191 #ifdef __FreeBSD__
192 lf->fbt_nentries++;
193 #endif
194
195 retfbt = NULL;
196 again:
197 for (; instr < limit; instr++) {
198 if (le32toh(*instr) == RET_INSTR)
199 break;
200 else if ((le32toh(*instr) & B_MASK) == B_INSTR) {
201 offs = (le32toh(*instr) & B_DATA_MASK);
202 offs *= 4;
203 target = (instr + offs);
204 #ifdef __FreeBSD__
205 start = (uint32_t *)symval->value;
206 #else
207 start = (uint32_t *)value;
208 #endif
209 if (target >= limit || target < start)
210 break;
211 }
212 }
213
214 if (instr >= limit)
215 return (0);
216 KASSERT(*instr != 0);
217
218 /*
219 * We have a winner!
220 */
221 #ifdef __FreeBSD__
222 fbt = malloc(sizeof (fbt_probe_t), M_FBT, M_WAITOK | M_ZERO);
223 #else
224 fbt = kmem_zalloc(sizeof (fbt_probe_t), KM_SLEEP);
225 #endif
226 fbt->fbtp_name = name;
227 if (retfbt == NULL) {
228 fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
229 name, FBT_RETURN, FBT_AFRAMES, fbt);
230 } else {
231 retfbt->fbtp_next = fbt;
232 fbt->fbtp_id = retfbt->fbtp_id;
233 }
234 retfbt = fbt;
235
236 fbt->fbtp_patchpoint = instr;
237 #ifdef __FreeBSD__
238 fbt->fbtp_ctl = lf;
239 fbt->fbtp_loadcnt = lf->loadcnt;
240 #endif
241 #ifdef __NetBSD__
242 fbt->fbtp_ctl = mod;
243 #endif
244 fbt->fbtp_savedval = le32toh(*instr);
245 fbt->fbtp_patchval = FBT_PATCHVAL;
246 fbt->fbtp_symindx = symindx;
247
248 fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
249 fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;
250
251 #ifdef __FreeBSD__
252 lf->fbt_nentries++;
253 #endif
254
255 instr++;
256 goto again;
257 }
258