1266b4a78SMark Johnston /* 2266b4a78SMark Johnston * CDDL HEADER START 3266b4a78SMark Johnston * 4266b4a78SMark Johnston * The contents of this file are subject to the terms of the 5266b4a78SMark Johnston * Common Development and Distribution License (the "License"). 6266b4a78SMark Johnston * You may not use this file except in compliance with the License. 7266b4a78SMark Johnston * 8266b4a78SMark Johnston * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9266b4a78SMark Johnston * or http://www.opensolaris.org/os/licensing. 10266b4a78SMark Johnston * See the License for the specific language governing permissions 11266b4a78SMark Johnston * and limitations under the License. 12266b4a78SMark Johnston * 13266b4a78SMark Johnston * When distributing Covered Code, include this CDDL HEADER in each 14266b4a78SMark Johnston * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15266b4a78SMark Johnston * If applicable, add the following below this CDDL HEADER, with the 16266b4a78SMark Johnston * fields enclosed by brackets "[]" replaced with your own identifying 17266b4a78SMark Johnston * information: Portions Copyright [yyyy] [name of copyright owner] 18266b4a78SMark Johnston * 19266b4a78SMark Johnston * CDDL HEADER END 20266b4a78SMark Johnston * 21266b4a78SMark Johnston * Portions Copyright 2006-2008 John Birrell jb@freebsd.org 22266b4a78SMark Johnston * Portions Copyright 2013 Justin Hibbits jhibbits@freebsd.org 23266b4a78SMark Johnston * 24266b4a78SMark Johnston * $FreeBSD$ 25266b4a78SMark Johnston * 26266b4a78SMark Johnston */ 27266b4a78SMark Johnston 28266b4a78SMark Johnston /* 29266b4a78SMark Johnston * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 30266b4a78SMark Johnston * Use is subject to license terms. 31266b4a78SMark Johnston */ 32266b4a78SMark Johnston 33266b4a78SMark Johnston #include <sys/cdefs.h> 34266b4a78SMark Johnston #include <sys/param.h> 35266b4a78SMark Johnston #include <sys/dtrace.h> 36266b4a78SMark Johnston #include <machine/md_var.h> 37266b4a78SMark Johnston 38266b4a78SMark Johnston #include "fbt.h" 39266b4a78SMark Johnston 40679ea094SJustin Hibbits #define FBT_PATCHVAL 0x7ffff808 41266b4a78SMark Johnston #define FBT_MFLR_R0 0x7c0802a6 42266b4a78SMark Johnston #define FBT_MTLR_R0 0x7c0803a6 43266b4a78SMark Johnston #define FBT_BLR 0x4e800020 44266b4a78SMark Johnston #define FBT_BCTR 0x4e800030 45266b4a78SMark Johnston #define FBT_BRANCH 0x48000000 46266b4a78SMark Johnston #define FBT_BR_MASK 0x03fffffc 47266b4a78SMark Johnston #define FBT_IS_JUMP(instr) ((instr & ~FBT_BR_MASK) == FBT_BRANCH) 48266b4a78SMark Johnston 49266b4a78SMark Johnston #define FBT_ENTRY "entry" 50266b4a78SMark Johnston #define FBT_RETURN "return" 5124d5dfb1SJustin Hibbits #define FBT_AFRAMES 7 52266b4a78SMark Johnston 53266b4a78SMark Johnston int 546c280659SMark Johnston fbt_invop(uintptr_t addr, struct trapframe *frame, uintptr_t rval) 55266b4a78SMark Johnston { 56266b4a78SMark Johnston solaris_cpu_t *cpu = &solaris_cpu[curcpu]; 57266b4a78SMark Johnston fbt_probe_t *fbt = fbt_probetab[FBT_ADDR2NDX(addr)]; 58266b4a78SMark Johnston uintptr_t tmp; 59266b4a78SMark Johnston 60266b4a78SMark Johnston for (; fbt != NULL; fbt = fbt->fbtp_hashnext) { 61266b4a78SMark Johnston if ((uintptr_t)fbt->fbtp_patchpoint == addr) { 62266b4a78SMark Johnston if (fbt->fbtp_roffset == 0) { 63266b4a78SMark Johnston cpu->cpu_dtrace_caller = addr; 64266b4a78SMark Johnston 65266b4a78SMark Johnston dtrace_probe(fbt->fbtp_id, frame->fixreg[3], 66266b4a78SMark Johnston frame->fixreg[4], frame->fixreg[5], 67266b4a78SMark Johnston frame->fixreg[6], frame->fixreg[7]); 68266b4a78SMark Johnston 69266b4a78SMark Johnston cpu->cpu_dtrace_caller = 0; 70266b4a78SMark Johnston } else { 71266b4a78SMark Johnston 72266b4a78SMark Johnston dtrace_probe(fbt->fbtp_id, fbt->fbtp_roffset, 73266b4a78SMark Johnston rval, 0, 0, 0); 74266b4a78SMark Johnston /* 75266b4a78SMark Johnston * The caller doesn't have the fbt item, so 76266b4a78SMark Johnston * fixup tail calls here. 77266b4a78SMark Johnston */ 78266b4a78SMark Johnston if (fbt->fbtp_rval == DTRACE_INVOP_JUMP) { 79266b4a78SMark Johnston frame->srr0 = (uintptr_t)fbt->fbtp_patchpoint; 80266b4a78SMark Johnston tmp = fbt->fbtp_savedval & FBT_BR_MASK; 81266b4a78SMark Johnston /* Sign extend. */ 82266b4a78SMark Johnston if (tmp & 0x02000000) 83266b4a78SMark Johnston #ifdef __powerpc64__ 84266b4a78SMark Johnston tmp |= 0xfffffffffc000000ULL; 85266b4a78SMark Johnston #else 86266b4a78SMark Johnston tmp |= 0xfc000000UL; 87266b4a78SMark Johnston #endif 88266b4a78SMark Johnston frame->srr0 += tmp; 89266b4a78SMark Johnston } 90266b4a78SMark Johnston cpu->cpu_dtrace_caller = 0; 91266b4a78SMark Johnston } 92266b4a78SMark Johnston 93266b4a78SMark Johnston return (fbt->fbtp_rval); 94266b4a78SMark Johnston } 95266b4a78SMark Johnston } 96266b4a78SMark Johnston 97266b4a78SMark Johnston return (0); 98266b4a78SMark Johnston } 99266b4a78SMark Johnston 100266b4a78SMark Johnston void 101266b4a78SMark Johnston fbt_patch_tracepoint(fbt_probe_t *fbt, fbt_patchval_t val) 102266b4a78SMark Johnston { 103266b4a78SMark Johnston 104266b4a78SMark Johnston *fbt->fbtp_patchpoint = val; 105266b4a78SMark Johnston __syncicache(fbt->fbtp_patchpoint, 4); 106266b4a78SMark Johnston } 107266b4a78SMark Johnston 108266b4a78SMark Johnston int 109266b4a78SMark Johnston fbt_provide_module_function(linker_file_t lf, int symindx, 110266b4a78SMark Johnston linker_symval_t *symval, void *opaque) 111266b4a78SMark Johnston { 112266b4a78SMark Johnston char *modname = opaque; 113266b4a78SMark Johnston const char *name = symval->name; 114266b4a78SMark Johnston fbt_probe_t *fbt, *retfbt; 115266b4a78SMark Johnston int j; 116266b4a78SMark Johnston uint32_t *instr, *limit; 117266b4a78SMark Johnston 11824d5dfb1SJustin Hibbits #ifdef __powerpc64__ 11924d5dfb1SJustin Hibbits /* 12024d5dfb1SJustin Hibbits * PowerPC64 uses '.' prefixes on symbol names, ignore it, but only 12124d5dfb1SJustin Hibbits * allow symbols with the '.' prefix, so that we don't get the function 12224d5dfb1SJustin Hibbits * descriptor instead. 12324d5dfb1SJustin Hibbits */ 124266b4a78SMark Johnston if (name[0] == '.') 125266b4a78SMark Johnston name++; 12624d5dfb1SJustin Hibbits else 12724d5dfb1SJustin Hibbits return (0); 12824d5dfb1SJustin Hibbits #endif 129266b4a78SMark Johnston 1300440a7f5SJustin Hibbits if (fbt_excluded(name)) 131266b4a78SMark Johnston return (0); 132266b4a78SMark Johnston 133266b4a78SMark Johnston instr = (uint32_t *) symval->value; 134266b4a78SMark Johnston limit = (uint32_t *) (symval->value + symval->size); 135266b4a78SMark Johnston 136266b4a78SMark Johnston for (; instr < limit; instr++) 137266b4a78SMark Johnston if (*instr == FBT_MFLR_R0) 138266b4a78SMark Johnston break; 139266b4a78SMark Johnston 140266b4a78SMark Johnston if (*instr != FBT_MFLR_R0) 141266b4a78SMark Johnston return (0); 142266b4a78SMark Johnston 143266b4a78SMark Johnston fbt = malloc(sizeof (fbt_probe_t), M_FBT, M_WAITOK | M_ZERO); 144266b4a78SMark Johnston fbt->fbtp_name = name; 145266b4a78SMark Johnston fbt->fbtp_id = dtrace_probe_create(fbt_id, modname, 14624d5dfb1SJustin Hibbits name, FBT_ENTRY, FBT_AFRAMES, fbt); 147266b4a78SMark Johnston fbt->fbtp_patchpoint = instr; 148266b4a78SMark Johnston fbt->fbtp_ctl = lf; 149266b4a78SMark Johnston fbt->fbtp_loadcnt = lf->loadcnt; 150266b4a78SMark Johnston fbt->fbtp_savedval = *instr; 151266b4a78SMark Johnston fbt->fbtp_patchval = FBT_PATCHVAL; 152266b4a78SMark Johnston fbt->fbtp_rval = DTRACE_INVOP_MFLR_R0; 153266b4a78SMark Johnston fbt->fbtp_symindx = symindx; 154266b4a78SMark Johnston 155266b4a78SMark Johnston fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)]; 156266b4a78SMark Johnston fbt_probetab[FBT_ADDR2NDX(instr)] = fbt; 157266b4a78SMark Johnston 158266b4a78SMark Johnston lf->fbt_nentries++; 159266b4a78SMark Johnston 160266b4a78SMark Johnston retfbt = NULL; 161266b4a78SMark Johnston again: 162266b4a78SMark Johnston if (instr >= limit) 163266b4a78SMark Johnston return (0); 164266b4a78SMark Johnston 165266b4a78SMark Johnston /* 166266b4a78SMark Johnston * We (desperately) want to avoid erroneously instrumenting a 167266b4a78SMark Johnston * jump table. To determine if we're looking at a true instruction 168266b4a78SMark Johnston * sequence or an inline jump table that happens to contain the same 169266b4a78SMark Johnston * byte sequences, we resort to some heuristic sleeze: we treat this 170266b4a78SMark Johnston * instruction as being contained within a pointer, and see if that 171266b4a78SMark Johnston * pointer points to within the body of the function. If it does, we 172266b4a78SMark Johnston * refuse to instrument it. 173266b4a78SMark Johnston */ 174266b4a78SMark Johnston { 175266b4a78SMark Johnston uint32_t *ptr; 176266b4a78SMark Johnston 177266b4a78SMark Johnston ptr = *(uint32_t **)instr; 178266b4a78SMark Johnston 179266b4a78SMark Johnston if (ptr >= (uint32_t *) symval->value && ptr < limit) { 180266b4a78SMark Johnston instr++; 181266b4a78SMark Johnston goto again; 182266b4a78SMark Johnston } 183266b4a78SMark Johnston } 184266b4a78SMark Johnston 185266b4a78SMark Johnston if (*instr != FBT_MTLR_R0) { 186266b4a78SMark Johnston instr++; 187266b4a78SMark Johnston goto again; 188266b4a78SMark Johnston } 189266b4a78SMark Johnston 190266b4a78SMark Johnston instr++; 191266b4a78SMark Johnston 192266b4a78SMark Johnston for (j = 0; j < 12 && instr < limit; j++, instr++) { 193266b4a78SMark Johnston if ((*instr == FBT_BCTR) || (*instr == FBT_BLR) || 194266b4a78SMark Johnston FBT_IS_JUMP(*instr)) 195266b4a78SMark Johnston break; 196266b4a78SMark Johnston } 197266b4a78SMark Johnston 198266b4a78SMark Johnston if (!(*instr == FBT_BCTR || *instr == FBT_BLR || FBT_IS_JUMP(*instr))) 199266b4a78SMark Johnston goto again; 200266b4a78SMark Johnston 201266b4a78SMark Johnston /* 202266b4a78SMark Johnston * We have a winner! 203266b4a78SMark Johnston */ 204266b4a78SMark Johnston fbt = malloc(sizeof (fbt_probe_t), M_FBT, M_WAITOK | M_ZERO); 205266b4a78SMark Johnston fbt->fbtp_name = name; 206266b4a78SMark Johnston 207266b4a78SMark Johnston if (retfbt == NULL) { 208266b4a78SMark Johnston fbt->fbtp_id = dtrace_probe_create(fbt_id, modname, 20924d5dfb1SJustin Hibbits name, FBT_RETURN, FBT_AFRAMES, fbt); 210266b4a78SMark Johnston } else { 211c208cb99SMark Johnston retfbt->fbtp_probenext = fbt; 212266b4a78SMark Johnston fbt->fbtp_id = retfbt->fbtp_id; 213266b4a78SMark Johnston } 214266b4a78SMark Johnston 215266b4a78SMark Johnston retfbt = fbt; 216266b4a78SMark Johnston fbt->fbtp_patchpoint = instr; 217266b4a78SMark Johnston fbt->fbtp_ctl = lf; 218266b4a78SMark Johnston fbt->fbtp_loadcnt = lf->loadcnt; 219266b4a78SMark Johnston fbt->fbtp_symindx = symindx; 220266b4a78SMark Johnston 221266b4a78SMark Johnston if (*instr == FBT_BCTR) 222266b4a78SMark Johnston fbt->fbtp_rval = DTRACE_INVOP_BCTR; 223266b4a78SMark Johnston else if (*instr == FBT_BLR) 224*cfebc0faSJustin Hibbits fbt->fbtp_rval = DTRACE_INVOP_BLR; 225266b4a78SMark Johnston else 226266b4a78SMark Johnston fbt->fbtp_rval = DTRACE_INVOP_JUMP; 227266b4a78SMark Johnston 22824d5dfb1SJustin Hibbits fbt->fbtp_roffset = 22924d5dfb1SJustin Hibbits (uintptr_t)((uint8_t *)instr - (uint8_t *)symval->value); 23024d5dfb1SJustin Hibbits 231266b4a78SMark Johnston fbt->fbtp_savedval = *instr; 232266b4a78SMark Johnston fbt->fbtp_patchval = FBT_PATCHVAL; 233266b4a78SMark Johnston fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)]; 234266b4a78SMark Johnston fbt_probetab[FBT_ADDR2NDX(instr)] = fbt; 235266b4a78SMark Johnston 236266b4a78SMark Johnston lf->fbt_nentries++; 237266b4a78SMark Johnston 238266b4a78SMark Johnston instr += 4; 239266b4a78SMark Johnston goto again; 240266b4a78SMark Johnston } 241