xref: /netbsd-src/external/cddl/osnet/dev/fbt/aarch64/fbt_isa.c (revision d89e5c8159cd8a8c5f2573f68e9aef8fea8372e4)
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