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