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