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