]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/cddl/dev/fbt/riscv/fbt_isa.c
dtrace: fix fbt return probes on RISC-V
[FreeBSD/FreeBSD.git] / sys / cddl / dev / fbt / riscv / fbt_isa.c
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
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                         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
79 void
80 fbt_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
95 static int
96 match_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
105 static int
106 check_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
123 static int
124 check_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
141 int
142 fbt_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         instr = (uint32_t *)(symval->value);
160         limit = (uint32_t *)(symval->value + symval->size);
161
162         /* Look for sd operation */
163         for (; instr < limit; instr++) {
164                 /* Look for a non-compressed store of ra to sp */
165                 if (match_opcode(*instr, (MATCH_SD | RS2_RA | RS1_SP),
166                     (MASK_SD | RS2_MASK | RS1_MASK))) {
167                         rval = DTRACE_INVOP_SD;
168                         patchval = FBT_PATCHVAL;
169                         break;
170                 }
171
172                 /* Look for a 'C'-compressed store of ra to sp. */
173                 if (check_c_sdsp(&instr)) {
174                         rval = DTRACE_INVOP_C_SDSP;
175                         patchval = FBT_C_PATCHVAL;
176                         break;
177                 }
178         }
179
180         if (instr >= limit)
181                 return (0);
182
183         fbt = malloc(sizeof (fbt_probe_t), M_FBT, M_WAITOK | M_ZERO);
184         fbt->fbtp_name = name;
185         fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
186             name, FBT_ENTRY, 3, fbt);
187         fbt->fbtp_patchpoint = instr;
188         fbt->fbtp_ctl = lf;
189         fbt->fbtp_loadcnt = lf->loadcnt;
190         fbt->fbtp_savedval = *instr;
191         fbt->fbtp_patchval = patchval;
192         fbt->fbtp_rval = rval;
193         fbt->fbtp_symindx = symindx;
194
195         fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
196         fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;
197
198         lf->fbt_nentries++;
199
200         retfbt = NULL;
201 again:
202         for (; instr < limit; instr++) {
203                 /* Look for non-compressed return */
204                 if (match_opcode(*instr, (MATCH_JALR | (X_RA << RS1_SHIFT)),
205                     (MASK_JALR | RD_MASK | RS1_MASK | IMM_MASK))) {
206                         rval = DTRACE_INVOP_RET;
207                         patchval = FBT_PATCHVAL;
208                         break;
209                 }
210
211                 /* Look for 'C'-compressed return */
212                 if (check_c_ret(&instr)) {
213                         rval = DTRACE_INVOP_C_RET;
214                         patchval = FBT_C_PATCHVAL;
215                         break;
216                 }
217         }
218
219         if (instr >= limit)
220                 return (0);
221
222         /*
223          * We have a winner!
224          */
225         fbt = malloc(sizeof (fbt_probe_t), M_FBT, M_WAITOK | M_ZERO);
226         fbt->fbtp_name = name;
227         if (retfbt == NULL) {
228                 fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
229                     name, FBT_RETURN, 3, fbt);
230         } else {
231                 retfbt->fbtp_probenext = fbt;
232                 fbt->fbtp_id = retfbt->fbtp_id;
233         }
234         retfbt = fbt;
235
236         fbt->fbtp_patchpoint = instr;
237         fbt->fbtp_ctl = lf;
238         fbt->fbtp_loadcnt = lf->loadcnt;
239         fbt->fbtp_symindx = symindx;
240         fbt->fbtp_rval = rval;
241         fbt->fbtp_roffset = (uintptr_t)instr - (uintptr_t)symval->value;
242         fbt->fbtp_savedval = *instr;
243         fbt->fbtp_patchval = patchval;
244         fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
245         fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;
246
247         lf->fbt_nentries++;
248
249         instr++;
250         goto again;
251 }