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 * 24 */ 25 26 /* 27 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 28 * Use is subject to license terms. 29 */ 30 31 #include <sys/cdefs.h> 32 #include <sys/param.h> 33 #include <sys/dtrace.h> 34 #include <machine/md_var.h> 35 36 #include "fbt.h" 37 38 #define FBT_PATCHVAL 0x7ffff808 39 #define FBT_MFLR_R0 0x7c0802a6 40 #define FBT_MTLR_R0 0x7c0803a6 41 #define FBT_BLR 0x4e800020 42 #define FBT_BCTR 0x4e800030 43 #define FBT_BRANCH 0x48000000 44 #define FBT_BR_MASK 0x03fffffc 45 #define FBT_IS_JUMP(instr) ((instr & ~FBT_BR_MASK) == FBT_BRANCH) 46 47 #define FBT_AFRAMES 5 48 49 int 50 fbt_invop(uintptr_t addr, struct trapframe *frame, uintptr_t rval) 51 { 52 solaris_cpu_t *cpu = &solaris_cpu[curcpu]; 53 fbt_probe_t *fbt = fbt_probetab[FBT_ADDR2NDX(addr)]; 54 uintptr_t tmp; 55 56 for (; fbt != NULL; fbt = fbt->fbtp_hashnext) { 57 if ((uintptr_t)fbt->fbtp_patchpoint == addr) { 58 if (fbt->fbtp_roffset == 0) { 59 cpu->cpu_dtrace_caller = addr; 60 61 dtrace_probe(fbt->fbtp_id, frame->fixreg[3], 62 frame->fixreg[4], frame->fixreg[5], 63 frame->fixreg[6], frame->fixreg[7]); 64 65 cpu->cpu_dtrace_caller = 0; 66 } else { 67 68 dtrace_probe(fbt->fbtp_id, fbt->fbtp_roffset, 69 rval, 0, 0, 0); 70 /* 71 * The caller doesn't have the fbt item, so 72 * fixup tail calls here. 73 */ 74 if (fbt->fbtp_rval == DTRACE_INVOP_JUMP) { 75 frame->srr0 = (uintptr_t)fbt->fbtp_patchpoint; 76 tmp = fbt->fbtp_savedval & FBT_BR_MASK; 77 /* Sign extend. */ 78 if (tmp & 0x02000000) 79 #ifdef __powerpc64__ 80 tmp |= 0xfffffffffc000000ULL; 81 #else 82 tmp |= 0xfc000000UL; 83 #endif 84 frame->srr0 += tmp; 85 } 86 cpu->cpu_dtrace_caller = 0; 87 } 88 89 return (fbt->fbtp_rval); 90 } 91 } 92 93 return (0); 94 } 95 96 void 97 fbt_patch_tracepoint(fbt_probe_t *fbt, fbt_patchval_t val) 98 { 99 100 *fbt->fbtp_patchpoint = val; 101 __syncicache(fbt->fbtp_patchpoint, 4); 102 } 103 104 int 105 fbt_provide_module_function(linker_file_t lf, int symindx, 106 linker_symval_t *symval, void *opaque) 107 { 108 char *modname = opaque; 109 const char *name = symval->name; 110 fbt_probe_t *fbt, *retfbt; 111 int j; 112 uint32_t *instr, *limit; 113 114 #ifdef __powerpc64__ 115 #if !defined(_CALL_ELF) || _CALL_ELF == 1 116 /* 117 * PowerPC64 uses '.' prefixes on symbol names, ignore it, but only 118 * allow symbols with the '.' prefix, so that we don't get the function 119 * descriptor instead. 120 */ 121 if (name[0] == '.') 122 name++; 123 else 124 return (0); 125 #endif 126 #endif 127 128 if (fbt_excluded(name)) 129 return (0); 130 131 instr = (uint32_t *) symval->value; 132 limit = (uint32_t *) (symval->value + symval->size); 133 134 for (; instr < limit; instr++) 135 if (*instr == FBT_MFLR_R0) 136 break; 137 138 if (*instr != FBT_MFLR_R0) 139 return (0); 140 141 fbt = malloc(sizeof (fbt_probe_t), M_FBT, M_WAITOK | M_ZERO); 142 fbt->fbtp_name = name; 143 fbt->fbtp_id = dtrace_probe_create(fbt_id, modname, 144 name, FBT_ENTRY, FBT_AFRAMES, fbt); 145 fbt->fbtp_patchpoint = instr; 146 fbt->fbtp_ctl = lf; 147 fbt->fbtp_loadcnt = lf->loadcnt; 148 fbt->fbtp_savedval = *instr; 149 fbt->fbtp_patchval = FBT_PATCHVAL; 150 fbt->fbtp_rval = DTRACE_INVOP_MFLR_R0; 151 fbt->fbtp_symindx = symindx; 152 153 fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)]; 154 fbt_probetab[FBT_ADDR2NDX(instr)] = fbt; 155 156 lf->fbt_nentries++; 157 158 retfbt = NULL; 159 again: 160 if (instr >= limit) 161 return (0); 162 163 /* 164 * We (desperately) want to avoid erroneously instrumenting a 165 * jump table. To determine if we're looking at a true instruction 166 * sequence or an inline jump table that happens to contain the same 167 * byte sequences, we resort to some heuristic sleeze: we treat this 168 * instruction as being contained within a pointer, and see if that 169 * pointer points to within the body of the function. If it does, we 170 * refuse to instrument it. 171 */ 172 { 173 uint32_t *ptr; 174 175 ptr = *(uint32_t **)instr; 176 177 if (ptr >= (uint32_t *) symval->value && ptr < limit) { 178 instr++; 179 goto again; 180 } 181 } 182 183 if (*instr != FBT_MTLR_R0) { 184 instr++; 185 goto again; 186 } 187 188 instr++; 189 190 for (j = 0; j < 12 && instr < limit; j++, instr++) { 191 if ((*instr == FBT_BCTR) || (*instr == FBT_BLR) || 192 FBT_IS_JUMP(*instr)) 193 break; 194 } 195 196 if (!(*instr == FBT_BCTR || *instr == FBT_BLR || FBT_IS_JUMP(*instr))) 197 goto again; 198 199 /* 200 * We have a winner! 201 */ 202 fbt = malloc(sizeof (fbt_probe_t), M_FBT, M_WAITOK | M_ZERO); 203 fbt->fbtp_name = name; 204 205 if (retfbt == NULL) { 206 fbt->fbtp_id = dtrace_probe_create(fbt_id, modname, 207 name, FBT_RETURN, FBT_AFRAMES, fbt); 208 } else { 209 retfbt->fbtp_probenext = fbt; 210 fbt->fbtp_id = retfbt->fbtp_id; 211 } 212 213 retfbt = fbt; 214 fbt->fbtp_patchpoint = instr; 215 fbt->fbtp_ctl = lf; 216 fbt->fbtp_loadcnt = lf->loadcnt; 217 fbt->fbtp_symindx = symindx; 218 219 if (*instr == FBT_BCTR) 220 fbt->fbtp_rval = DTRACE_INVOP_BCTR; 221 else if (*instr == FBT_BLR) 222 fbt->fbtp_rval = DTRACE_INVOP_BLR; 223 else 224 fbt->fbtp_rval = DTRACE_INVOP_JUMP; 225 226 fbt->fbtp_roffset = 227 (uintptr_t)((uint8_t *)instr - (uint8_t *)symval->value); 228 229 fbt->fbtp_savedval = *instr; 230 fbt->fbtp_patchval = FBT_PATCHVAL; 231 fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)]; 232 fbt_probetab[FBT_ADDR2NDX(instr)] = fbt; 233 234 lf->fbt_nentries++; 235 236 instr += 4; 237 goto again; 238 } 239