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 int 95 fbt_provide_module_function(linker_file_t lf, int symindx, 96 linker_symval_t *symval, void *opaque) 97 { 98 fbt_probe_t *fbt, *retfbt; 99 uint32_t *instr, *limit; 100 const char *name; 101 char *modname; 102 int patchval; 103 int rval; 104 105 modname = opaque; 106 name = symval->name; 107 108 /* Check if function is excluded from instrumentation */ 109 if (fbt_excluded(name)) 110 return (0); 111 112 /* 113 * Some assembly-language exception handlers are not suitable for 114 * instrumentation. 115 */ 116 if (strcmp(name, "cpu_exception_handler") == 0) 117 return (0); 118 if (strcmp(name, "cpu_exception_handler_user") == 0) 119 return (0); 120 if (strcmp(name, "cpu_exception_handler_supervisor") == 0) 121 return (0); 122 if (strcmp(name, "do_trap_supervisor") == 0) 123 return (0); 124 125 instr = (uint32_t *)(symval->value); 126 limit = (uint32_t *)(symval->value + symval->size); 127 128 /* Look for sd operation */ 129 for (; instr < limit; instr++) { 130 /* Look for a non-compressed store of ra to sp */ 131 if (dtrace_instr_sdsp(&instr)) { 132 rval = DTRACE_INVOP_SD; 133 patchval = FBT_PATCHVAL; 134 break; 135 } 136 137 /* Look for a 'C'-compressed store of ra to sp. */ 138 if (dtrace_instr_c_sdsp(&instr)) { 139 rval = DTRACE_INVOP_C_SDSP; 140 patchval = FBT_C_PATCHVAL; 141 break; 142 } 143 } 144 145 if (instr >= limit) 146 return (0); 147 148 fbt = malloc(sizeof (fbt_probe_t), M_FBT, M_WAITOK | M_ZERO); 149 fbt->fbtp_name = name; 150 fbt->fbtp_id = dtrace_probe_create(fbt_id, modname, 151 name, FBT_ENTRY, FBT_AFRAMES, fbt); 152 fbt->fbtp_patchpoint = instr; 153 fbt->fbtp_ctl = lf; 154 fbt->fbtp_loadcnt = lf->loadcnt; 155 fbt->fbtp_savedval = *instr; 156 fbt->fbtp_patchval = patchval; 157 fbt->fbtp_rval = rval; 158 fbt->fbtp_symindx = symindx; 159 160 fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)]; 161 fbt_probetab[FBT_ADDR2NDX(instr)] = fbt; 162 163 lf->fbt_nentries++; 164 165 retfbt = NULL; 166 again: 167 for (; instr < limit; instr++) { 168 /* Look for non-compressed return */ 169 if (dtrace_instr_ret(&instr)) { 170 rval = DTRACE_INVOP_RET; 171 patchval = FBT_PATCHVAL; 172 break; 173 } 174 175 /* Look for 'C'-compressed return */ 176 if (dtrace_instr_c_ret(&instr)) { 177 rval = DTRACE_INVOP_C_RET; 178 patchval = FBT_C_PATCHVAL; 179 break; 180 } 181 } 182 183 if (instr >= limit) 184 return (0); 185 186 /* 187 * We have a winner! 188 */ 189 fbt = malloc(sizeof (fbt_probe_t), M_FBT, M_WAITOK | M_ZERO); 190 fbt->fbtp_name = name; 191 if (retfbt == NULL) { 192 fbt->fbtp_id = dtrace_probe_create(fbt_id, modname, 193 name, FBT_RETURN, FBT_AFRAMES, fbt); 194 } else { 195 retfbt->fbtp_probenext = fbt; 196 fbt->fbtp_id = retfbt->fbtp_id; 197 } 198 retfbt = fbt; 199 200 fbt->fbtp_patchpoint = instr; 201 fbt->fbtp_ctl = lf; 202 fbt->fbtp_loadcnt = lf->loadcnt; 203 fbt->fbtp_symindx = symindx; 204 fbt->fbtp_rval = rval; 205 fbt->fbtp_roffset = (uintptr_t)instr - (uintptr_t)symval->value; 206 fbt->fbtp_savedval = *instr; 207 fbt->fbtp_patchval = patchval; 208 fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)]; 209 fbt_probetab[FBT_ADDR2NDX(instr)] = fbt; 210 211 lf->fbt_nentries++; 212 213 instr++; 214 goto again; 215 } 216