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 27 /* 28 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 29 * Use is subject to license terms. 30 */ 31 32 #include <sys/cdefs.h> 33 #include <sys/param.h> 34 35 #include <sys/dtrace.h> 36 37 #include <machine/riscvreg.h> 38 #include <machine/encoding.h> 39 40 #include "fbt.h" 41 42 #define FBT_C_PATCHVAL MATCH_C_EBREAK 43 #define FBT_PATCHVAL MATCH_EBREAK 44 #define FBT_AFRAMES 5 45 46 int 47 fbt_invop(uintptr_t addr, struct trapframe *frame, uintptr_t rval) 48 { 49 solaris_cpu_t *cpu; 50 fbt_probe_t *fbt; 51 52 cpu = &solaris_cpu[curcpu]; 53 fbt = fbt_probetab[FBT_ADDR2NDX(addr)]; 54 55 for (; fbt != NULL; fbt = fbt->fbtp_hashnext) { 56 if ((uintptr_t)fbt->fbtp_patchpoint == addr) { 57 cpu->cpu_dtrace_caller = frame->tf_ra - INSN_SIZE; 58 59 if (fbt->fbtp_roffset == 0) { 60 dtrace_probe(fbt->fbtp_id, frame->tf_a[0], 61 frame->tf_a[1], frame->tf_a[2], 62 frame->tf_a[3], frame->tf_a[4]); 63 } else { 64 dtrace_probe(fbt->fbtp_id, fbt->fbtp_roffset, 65 frame->tf_a[0], frame->tf_a[1], 0, 0); 66 } 67 68 cpu->cpu_dtrace_caller = 0; 69 return (fbt->fbtp_savedval); 70 } 71 } 72 73 return (0); 74 } 75 76 void 77 fbt_patch_tracepoint(fbt_probe_t *fbt, fbt_patchval_t val) 78 { 79 80 switch(fbt->fbtp_patchval) { 81 case FBT_C_PATCHVAL: 82 *(uint16_t *)fbt->fbtp_patchpoint = (uint16_t)val; 83 fence_i(); 84 break; 85 case FBT_PATCHVAL: 86 *fbt->fbtp_patchpoint = val; 87 fence_i(); 88 break; 89 }; 90 } 91 92 int 93 fbt_provide_module_function(linker_file_t lf, int symindx, 94 linker_symval_t *symval, void *opaque) 95 { 96 fbt_probe_t *fbt, *retfbt; 97 uint32_t *instr, *limit; 98 const char *name; 99 char *modname; 100 int patchval; 101 int rval; 102 103 modname = opaque; 104 name = symval->name; 105 106 /* Check if function is excluded from instrumentation */ 107 if (fbt_excluded(name)) 108 return (0); 109 110 /* 111 * Some assembly-language exception handlers are not suitable for 112 * instrumentation. 113 */ 114 if (strcmp(name, "cpu_exception_handler") == 0) 115 return (0); 116 if (strcmp(name, "cpu_exception_handler_user") == 0) 117 return (0); 118 if (strcmp(name, "cpu_exception_handler_supervisor") == 0) 119 return (0); 120 if (strcmp(name, "do_trap_supervisor") == 0) 121 return (0); 122 123 instr = (uint32_t *)(symval->value); 124 limit = (uint32_t *)(symval->value + symval->size); 125 126 /* Look for sd operation */ 127 for (; instr < limit; instr++) { 128 /* Look for a non-compressed store of ra to sp */ 129 if (dtrace_instr_sdsp(&instr)) { 130 rval = DTRACE_INVOP_SD; 131 patchval = FBT_PATCHVAL; 132 break; 133 } 134 135 /* Look for a 'C'-compressed store of ra to sp. */ 136 if (dtrace_instr_c_sdsp(&instr)) { 137 rval = DTRACE_INVOP_C_SDSP; 138 patchval = FBT_C_PATCHVAL; 139 break; 140 } 141 } 142 143 if (instr >= limit) 144 return (0); 145 146 fbt = malloc(sizeof (fbt_probe_t), M_FBT, M_WAITOK | M_ZERO); 147 fbt->fbtp_name = name; 148 fbt->fbtp_id = dtrace_probe_create(fbt_id, modname, 149 name, FBT_ENTRY, FBT_AFRAMES, fbt); 150 fbt->fbtp_patchpoint = instr; 151 fbt->fbtp_ctl = lf; 152 fbt->fbtp_loadcnt = lf->loadcnt; 153 fbt->fbtp_savedval = *instr; 154 fbt->fbtp_patchval = patchval; 155 fbt->fbtp_rval = rval; 156 fbt->fbtp_symindx = symindx; 157 158 fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)]; 159 fbt_probetab[FBT_ADDR2NDX(instr)] = fbt; 160 161 lf->fbt_nentries++; 162 163 retfbt = NULL; 164 again: 165 for (; instr < limit; instr++) { 166 /* Look for non-compressed return */ 167 if (dtrace_instr_ret(&instr)) { 168 rval = DTRACE_INVOP_RET; 169 patchval = FBT_PATCHVAL; 170 break; 171 } 172 173 /* Look for 'C'-compressed return */ 174 if (dtrace_instr_c_ret(&instr)) { 175 rval = DTRACE_INVOP_C_RET; 176 patchval = FBT_C_PATCHVAL; 177 break; 178 } 179 } 180 181 if (instr >= limit) 182 return (0); 183 184 /* 185 * We have a winner! 186 */ 187 fbt = malloc(sizeof (fbt_probe_t), M_FBT, M_WAITOK | M_ZERO); 188 fbt->fbtp_name = name; 189 if (retfbt == NULL) { 190 fbt->fbtp_id = dtrace_probe_create(fbt_id, modname, 191 name, FBT_RETURN, FBT_AFRAMES, fbt); 192 } else { 193 retfbt->fbtp_probenext = fbt; 194 fbt->fbtp_id = retfbt->fbtp_id; 195 } 196 retfbt = fbt; 197 198 fbt->fbtp_patchpoint = instr; 199 fbt->fbtp_ctl = lf; 200 fbt->fbtp_loadcnt = lf->loadcnt; 201 fbt->fbtp_symindx = symindx; 202 fbt->fbtp_rval = rval; 203 fbt->fbtp_roffset = (uintptr_t)instr - (uintptr_t)symval->value; 204 fbt->fbtp_savedval = *instr; 205 fbt->fbtp_patchval = patchval; 206 fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)]; 207 fbt_probetab[FBT_ADDR2NDX(instr)] = fbt; 208 209 lf->fbt_nentries++; 210 211 instr++; 212 goto again; 213 } 214