xref: /netbsd-src/external/cddl/osnet/dev/fbt/aarch64/fbt_isa.c (revision e6c7e151de239c49d2e38720a061ed9d1fa99309)
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 #include <sys/cpu.h>
37 #include <sys/module.h>
38 #include <sys/kmem.h>
39 
40 #include <uvm/uvm_extern.h>
41 
42 #include <dev/mm.h>
43 
44 #include <machine/cpufunc.h>
45 
46 #include <sys/dtrace.h>
47 
48 #include "fbt.h"
49 
50 #define	AARCH64_BRK		0xd4200000
51 #define	AARCH64_BRK_IMM16_SHIFT	5
52 #define	AARCH64_BRK_IMM16_VAL	(0x40d << AARCH64_BRK_IMM16_SHIFT)
53 #define	FBT_PATCHVAL		(AARCH64_BRK | AARCH64_BRK_IMM16_VAL)
54 #define	FBT_ENTRY	"entry"
55 #define	FBT_RETURN	"return"
56 
57 /*
58  * How many artificial frames appear between dtrace_probe and the
59  * interrupted function call?
60  *
61  *	fbt_invop
62  *	dtrace_invop
63  *	dtrace_invop_start
64  *	el1_trap_exit
65  */
66 #define	FBT_AFRAMES	4
67 
68 int
69 fbt_invop(uintptr_t addr, struct trapframe *frame, uintptr_t r0)
70 {
71 	solaris_cpu_t *cpu;
72 	fbt_probe_t *fbt;
73 
74 	cpu = &solaris_cpu[cpu_index(curcpu())];
75 	fbt = fbt_probetab[FBT_ADDR2NDX(addr)];
76 
77 	for (; fbt != NULL; fbt = fbt->fbtp_hashnext) {
78 		if ((uintptr_t)fbt->fbtp_patchpoint == addr) {
79 			cpu->cpu_dtrace_caller = addr;
80 
81 			dtrace_probe(fbt->fbtp_id, frame->tf_regs.r_reg[0],
82 			    frame->tf_regs.r_reg[1], frame->tf_regs.r_reg[2],
83 			    frame->tf_regs.r_reg[3], frame->tf_regs.r_reg[4]);
84 
85 			cpu->cpu_dtrace_caller = 0;
86 			KASSERT(fbt->fbtp_savedval != 0);
87 			return (fbt->fbtp_savedval);
88 		}
89 	}
90 
91 	return (0);
92 }
93 
94 void
95 fbt_patch_tracepoint(fbt_probe_t *fbt, fbt_patchval_t val)
96 {
97 	paddr_t pa;
98 	vaddr_t va;
99 
100 	if (!pmap_extract(pmap_kernel(), (vaddr_t)fbt->fbtp_patchpoint, &pa))
101 		return;
102 	if (!mm_md_direct_mapped_phys(pa, &va))
103 		return;
104 	*(fbt_patchval_t *)va = val;
105 	cpu_icache_sync_range((vm_offset_t)fbt->fbtp_patchpoint, sizeof(val));
106 }
107 
108 #if defined(__FreeBSD__)
109 int
110 fbt_provide_module_function(linker_file_t lf, int symindx,
111     linker_symval_t *symval, void *opaque)
112 #elif defined(__NetBSD__)
113 int
114 fbt_provide_module_cb(const char *name, int symindx, void *value,
115     uint32_t symsize, int type, void *opaque)
116 #else
117 #error unsupported platform
118 #endif
119 {
120 	fbt_probe_t *fbt, *retfbt;
121 	uint32_t *target, *start;
122 	uint32_t *instr, *limit;
123 	int offs;
124 
125 #ifdef __FreeBSD__
126 	char *modname = opaque;
127 	const char *name = symval->name;
128 
129 	instr = (uint32_t *)(symval->value);
130 	limit = (uint32_t *)(symval->value + symval->size);
131 #endif
132 #ifdef __NetBSD__
133 	struct fbt_ksyms_arg *fka = opaque;
134 	modctl_t *mod = fka->fka_mod;
135 	const char *modname = module_name(mod);
136 
137 	/* got a function? */
138 	if (ELF_ST_TYPE(type) != STT_FUNC)
139 		return 0;
140 
141 	instr = (uint32_t *)(value);
142 	limit = (uint32_t *)((uintptr_t)value + symsize);
143 #endif
144 
145 	/* Check if function is excluded from instrumentation */
146 	if (fbt_excluded(name))
147 		return (0);
148 
149 	if (strncmp(name, "_spl", 4) == 0 ||
150 	    strcmp(name, "dosoftints") == 0 ||
151 	    strcmp(name, "nanouptime") == 0 ||
152 	    strncmp(name, "gtmr_", 5) == 0) {
153 		return 0;
154 	}
155 
156 	/* Look for stp (pre-indexed) operation */
157 	for (; instr < limit; instr++) {
158 		if ((*instr & LDP_STP_MASK) == STP_64)
159 			break;
160 	}
161 
162 	if (instr >= limit)
163 		return (0);
164 	KASSERT(*instr != 0);
165 
166 #ifdef __FreeBSD__
167 	fbt = malloc(sizeof (fbt_probe_t), M_FBT, M_WAITOK | M_ZERO);
168 #else
169 	fbt = kmem_zalloc(sizeof (fbt_probe_t), KM_SLEEP);
170 #endif
171 	fbt->fbtp_name = name;
172 	fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
173 	    name, FBT_ENTRY, FBT_AFRAMES, fbt);
174 	fbt->fbtp_patchpoint = instr;
175 #ifdef __FreeBSD__
176 	fbt->fbtp_ctl = lf;
177 	fbt->fbtp_loadcnt = lf->loadcnt;
178 #endif
179 #ifdef __NetBSD__
180 	fbt->fbtp_ctl = mod;
181 #endif
182 	fbt->fbtp_savedval = *instr;
183 	fbt->fbtp_patchval = FBT_PATCHVAL;
184 	fbt->fbtp_symindx = symindx;
185 
186 	fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
187 	fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;
188 
189 #ifdef __FreeBSD__
190 	lf->fbt_nentries++;
191 #endif
192 
193 	retfbt = NULL;
194 again:
195 	for (; instr < limit; instr++) {
196 		if (*instr == RET_INSTR)
197 			break;
198 		else if ((*instr & B_MASK) == B_INSTR) {
199 			offs = (*instr & B_DATA_MASK);
200 			offs *= 4;
201 			target = (instr + offs);
202 #ifdef __FreeBSD__
203 			start = (uint32_t *)symval->value;
204 #else
205 			start = (uint32_t *)value;
206 #endif
207 			if (target >= limit || target < start)
208 				break;
209 		}
210 	}
211 
212 	if (instr >= limit)
213 		return (0);
214 	KASSERT(*instr != 0);
215 
216 	/*
217 	 * We have a winner!
218 	 */
219 #ifdef __FreeBSD__
220 	fbt = malloc(sizeof (fbt_probe_t), M_FBT, M_WAITOK | M_ZERO);
221 #else
222 	fbt = kmem_zalloc(sizeof (fbt_probe_t), KM_SLEEP);
223 #endif
224 	fbt->fbtp_name = name;
225 	if (retfbt == NULL) {
226 		fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
227 		    name, FBT_RETURN, FBT_AFRAMES, fbt);
228 	} else {
229 		retfbt->fbtp_next = fbt;
230 		fbt->fbtp_id = retfbt->fbtp_id;
231 	}
232 	retfbt = fbt;
233 
234 	fbt->fbtp_patchpoint = instr;
235 #ifdef __FreeBSD__
236 	fbt->fbtp_ctl = lf;
237 	fbt->fbtp_loadcnt = lf->loadcnt;
238 #endif
239 #ifdef __NetBSD__
240 	fbt->fbtp_ctl = mod;
241 #endif
242 	fbt->fbtp_savedval = *instr;
243 	fbt->fbtp_patchval = FBT_PATCHVAL;
244 	fbt->fbtp_symindx = symindx;
245 
246 	fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
247 	fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;
248 
249 #ifdef __FreeBSD__
250 	lf->fbt_nentries++;
251 #endif
252 
253 	instr++;
254 	goto again;
255 }
256