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 2016-2018 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 <machine/riscvreg.h>
40#include <machine/encoding.h>
41
42#include "fbt.h"
43
44#define	FBT_C_PATCHVAL		MATCH_C_EBREAK
45#define	FBT_PATCHVAL		MATCH_EBREAK
46#define	FBT_ENTRY		"entry"
47#define	FBT_RETURN		"return"
48
49int
50fbt_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			cpu->cpu_dtrace_caller = addr;
61
62			if (fbt->fbtp_roffset == 0) {
63				dtrace_probe(fbt->fbtp_id, frame->tf_a[0],
64				    frame->tf_a[1], frame->tf_a[2],
65				    frame->tf_a[3], frame->tf_a[4]);
66			} else {
67				dtrace_probe(fbt->fbtp_id, fbt->fbtp_roffset,
68				    frame->tf_a[0], frame->tf_a[1], 0, 0);
69			}
70
71			cpu->cpu_dtrace_caller = 0;
72			return (fbt->fbtp_savedval);
73		}
74	}
75
76	return (0);
77}
78
79void
80fbt_patch_tracepoint(fbt_probe_t *fbt, fbt_patchval_t val)
81{
82
83	switch(fbt->fbtp_patchval) {
84	case FBT_C_PATCHVAL:
85		*(uint16_t *)fbt->fbtp_patchpoint = (uint16_t)val;
86		fence_i();
87		break;
88	case FBT_PATCHVAL:
89		*fbt->fbtp_patchpoint = val;
90		fence_i();
91		break;
92	};
93}
94
95static int
96match_opcode(uint32_t insn, int match, int mask)
97{
98
99	if (((insn ^ match) & mask) == 0)
100		return (1);
101
102	return (0);
103}
104
105static int
106check_c_ret(uint32_t **instr)
107{
108	uint16_t *instr1;
109	int i;
110
111	for (i = 0; i < 2; i++) {
112		instr1 = (uint16_t *)(*instr) + i;
113		if (match_opcode(*instr1, (MATCH_C_JR | (X_RA << RD_SHIFT)),
114		    (MASK_C_JR | RD_MASK))) {
115			*instr = (uint32_t *)instr1;
116			return (1);
117		}
118	}
119
120	return (0);
121}
122
123static int
124check_c_sdsp(uint32_t **instr)
125{
126	uint16_t *instr1;
127	int i;
128
129	for (i = 0; i < 2; i++) {
130		instr1 = (uint16_t *)(*instr) + i;
131		if (match_opcode(*instr1, (MATCH_C_SDSP | RS2_C_RA),
132		    (MASK_C_SDSP | RS2_C_MASK))) {
133			*instr = (uint32_t *)instr1;
134			return (1);
135		}
136	}
137
138	return (0);
139}
140
141int
142fbt_provide_module_function(linker_file_t lf, int symindx,
143    linker_symval_t *symval, void *opaque)
144{
145	fbt_probe_t *fbt, *retfbt;
146	uint32_t *instr, *limit;
147	const char *name;
148	char *modname;
149	int patchval;
150	int rval;
151
152	modname = opaque;
153	name = symval->name;
154
155	/* Check if function is excluded from instrumentation */
156	if (fbt_excluded(name))
157		return (0);
158
159	/*
160	 * Some assembly-language exception handlers are not suitable for
161	 * instrumentation.
162	 */
163	if (strcmp(name, "cpu_exception_handler") == 0)
164		return (0);
165	if (strcmp(name, "cpu_exception_handler_user") == 0)
166		return (0);
167	if (strcmp(name, "cpu_exception_handler_supervisor") == 0)
168		return (0);
169	if (strcmp(name, "do_trap_supervisor") == 0)
170		return (0);
171
172	instr = (uint32_t *)(symval->value);
173	limit = (uint32_t *)(symval->value + symval->size);
174
175	/* Look for sd operation */
176	for (; instr < limit; instr++) {
177		/* Look for a non-compressed store of ra to sp */
178		if (match_opcode(*instr, (MATCH_SD | RS2_RA | RS1_SP),
179		    (MASK_SD | RS2_MASK | RS1_MASK))) {
180			rval = DTRACE_INVOP_SD;
181			patchval = FBT_PATCHVAL;
182			break;
183		}
184
185		/* Look for a 'C'-compressed store of ra to sp. */
186		if (check_c_sdsp(&instr)) {
187			rval = DTRACE_INVOP_C_SDSP;
188			patchval = FBT_C_PATCHVAL;
189			break;
190		}
191	}
192
193	if (instr >= limit)
194		return (0);
195
196	fbt = malloc(sizeof (fbt_probe_t), M_FBT, M_WAITOK | M_ZERO);
197	fbt->fbtp_name = name;
198	fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
199	    name, FBT_ENTRY, 3, fbt);
200	fbt->fbtp_patchpoint = instr;
201	fbt->fbtp_ctl = lf;
202	fbt->fbtp_loadcnt = lf->loadcnt;
203	fbt->fbtp_savedval = *instr;
204	fbt->fbtp_patchval = patchval;
205	fbt->fbtp_rval = rval;
206	fbt->fbtp_symindx = symindx;
207
208	fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
209	fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;
210
211	lf->fbt_nentries++;
212
213	retfbt = NULL;
214again:
215	for (; instr < limit; instr++) {
216		/* Look for non-compressed return */
217		if (match_opcode(*instr, (MATCH_JALR | (X_RA << RS1_SHIFT)),
218		    (MASK_JALR | RD_MASK | RS1_MASK | IMM_MASK))) {
219			rval = DTRACE_INVOP_RET;
220			patchval = FBT_PATCHVAL;
221			break;
222		}
223
224		/* Look for 'C'-compressed return */
225		if (check_c_ret(&instr)) {
226			rval = DTRACE_INVOP_C_RET;
227			patchval = FBT_C_PATCHVAL;
228			break;
229		}
230	}
231
232	if (instr >= limit)
233		return (0);
234
235	/*
236	 * We have a winner!
237	 */
238	fbt = malloc(sizeof (fbt_probe_t), M_FBT, M_WAITOK | M_ZERO);
239	fbt->fbtp_name = name;
240	if (retfbt == NULL) {
241		fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
242		    name, FBT_RETURN, 3, fbt);
243	} else {
244		retfbt->fbtp_probenext = fbt;
245		fbt->fbtp_id = retfbt->fbtp_id;
246	}
247	retfbt = fbt;
248
249	fbt->fbtp_patchpoint = instr;
250	fbt->fbtp_ctl = lf;
251	fbt->fbtp_loadcnt = lf->loadcnt;
252	fbt->fbtp_symindx = symindx;
253	fbt->fbtp_rval = rval;
254	fbt->fbtp_roffset = (uintptr_t)instr - (uintptr_t)symval->value;
255	fbt->fbtp_savedval = *instr;
256	fbt->fbtp_patchval = patchval;
257	fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
258	fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;
259
260	lf->fbt_nentries++;
261
262	instr++;
263	goto again;
264}
265