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