1b78ee15eSRuslan Bukin /* 2b78ee15eSRuslan Bukin * CDDL HEADER START 3b78ee15eSRuslan Bukin * 4b78ee15eSRuslan Bukin * The contents of this file are subject to the terms of the 5b78ee15eSRuslan Bukin * Common Development and Distribution License (the "License"). 6b78ee15eSRuslan Bukin * You may not use this file except in compliance with the License. 7b78ee15eSRuslan Bukin * 8b78ee15eSRuslan Bukin * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9b78ee15eSRuslan Bukin * or http://www.opensolaris.org/os/licensing. 10b78ee15eSRuslan Bukin * See the License for the specific language governing permissions 11b78ee15eSRuslan Bukin * and limitations under the License. 12b78ee15eSRuslan Bukin * 13b78ee15eSRuslan Bukin * When distributing Covered Code, include this CDDL HEADER in each 14b78ee15eSRuslan Bukin * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15b78ee15eSRuslan Bukin * If applicable, add the following below this CDDL HEADER, with the 16b78ee15eSRuslan Bukin * fields enclosed by brackets "[]" replaced with your own identifying 17b78ee15eSRuslan Bukin * information: Portions Copyright [yyyy] [name of copyright owner] 18b78ee15eSRuslan Bukin * 19b78ee15eSRuslan Bukin * CDDL HEADER END 20b78ee15eSRuslan Bukin * 21b78ee15eSRuslan Bukin * Portions Copyright 2006-2008 John Birrell jb@freebsd.org 22b78ee15eSRuslan Bukin * Portions Copyright 2013 Justin Hibbits jhibbits@freebsd.org 23b78ee15eSRuslan Bukin * Portions Copyright 2013 Howard Su howardsu@freebsd.org 24b78ee15eSRuslan Bukin * Portions Copyright 2015 Ruslan Bukin <br@bsdpad.com> 25b78ee15eSRuslan Bukin */ 26b78ee15eSRuslan Bukin 27b78ee15eSRuslan Bukin /* 28b78ee15eSRuslan Bukin * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 29b78ee15eSRuslan Bukin * Use is subject to license terms. 30b78ee15eSRuslan Bukin */ 31b78ee15eSRuslan Bukin 32b78ee15eSRuslan Bukin #include <sys/param.h> 33b78ee15eSRuslan Bukin 34b78ee15eSRuslan Bukin #include <sys/dtrace.h> 35b78ee15eSRuslan Bukin 36b78ee15eSRuslan Bukin #include "fbt.h" 37b78ee15eSRuslan Bukin 38bab7781eSChristos Margiolis #define FBT_PATCHVAL DTRACE_PATCHVAL 39599fb1d1SRobert Watson #define FBT_AFRAMES 4 40b78ee15eSRuslan Bukin 41b78ee15eSRuslan Bukin int 426c280659SMark Johnston fbt_invop(uintptr_t addr, struct trapframe *frame, uintptr_t rval) 43b78ee15eSRuslan Bukin { 44b78ee15eSRuslan Bukin solaris_cpu_t *cpu; 45b78ee15eSRuslan Bukin fbt_probe_t *fbt; 46b78ee15eSRuslan Bukin 47b78ee15eSRuslan Bukin cpu = &solaris_cpu[curcpu]; 48b78ee15eSRuslan Bukin fbt = fbt_probetab[FBT_ADDR2NDX(addr)]; 49b78ee15eSRuslan Bukin 50b78ee15eSRuslan Bukin for (; fbt != NULL; fbt = fbt->fbtp_hashnext) { 51e3ccf4f9SAndrew Turner if ((uintptr_t)fbt->fbtp_patchpoint != addr) 52e3ccf4f9SAndrew Turner continue; 53e3ccf4f9SAndrew Turner 54b78ee15eSRuslan Bukin cpu->cpu_dtrace_caller = addr; 55b78ee15eSRuslan Bukin 56e3ccf4f9SAndrew Turner if (fbt->fbtp_roffset == 0) { 57b78ee15eSRuslan Bukin dtrace_probe(fbt->fbtp_id, frame->tf_x[0], 58b78ee15eSRuslan Bukin frame->tf_x[1], frame->tf_x[2], 59b78ee15eSRuslan Bukin frame->tf_x[3], frame->tf_x[4]); 60e3ccf4f9SAndrew Turner } else { 61e3ccf4f9SAndrew Turner dtrace_probe(fbt->fbtp_id, fbt->fbtp_roffset, rval, 62e3ccf4f9SAndrew Turner 0, 0, 0); 63e3ccf4f9SAndrew Turner } 64b78ee15eSRuslan Bukin cpu->cpu_dtrace_caller = 0; 65b78ee15eSRuslan Bukin return (fbt->fbtp_savedval); 66b78ee15eSRuslan Bukin } 67b78ee15eSRuslan Bukin 68b78ee15eSRuslan Bukin return (0); 69b78ee15eSRuslan Bukin } 70b78ee15eSRuslan Bukin 71b78ee15eSRuslan Bukin void 72b78ee15eSRuslan Bukin fbt_patch_tracepoint(fbt_probe_t *fbt, fbt_patchval_t val) 73b78ee15eSRuslan Bukin { 74e48770deSJohn Baldwin void *addr; 75b78ee15eSRuslan Bukin 76e48770deSJohn Baldwin if (!arm64_get_writable_addr(fbt->fbtp_patchpoint, &addr)) 773d2533f5SAndrew Turner panic("%s: Unable to write new instruction", __func__); 783d2533f5SAndrew Turner 793d2533f5SAndrew Turner *(fbt_patchval_t *)addr = val; 801e3f42b6SJohn Baldwin cpu_icache_sync_range(fbt->fbtp_patchpoint, 4); 81b78ee15eSRuslan Bukin } 82b78ee15eSRuslan Bukin 83b78ee15eSRuslan Bukin int 84b78ee15eSRuslan Bukin fbt_provide_module_function(linker_file_t lf, int symindx, 85b78ee15eSRuslan Bukin linker_symval_t *symval, void *opaque) 86b78ee15eSRuslan Bukin { 87b78ee15eSRuslan Bukin fbt_probe_t *fbt, *retfbt; 88b78ee15eSRuslan Bukin uint32_t *target, *start; 89b78ee15eSRuslan Bukin uint32_t *instr, *limit; 90b78ee15eSRuslan Bukin const char *name; 91b78ee15eSRuslan Bukin char *modname; 92b78ee15eSRuslan Bukin int offs; 93b78ee15eSRuslan Bukin 94b78ee15eSRuslan Bukin modname = opaque; 95b78ee15eSRuslan Bukin name = symval->name; 96b78ee15eSRuslan Bukin 97b78ee15eSRuslan Bukin /* Check if function is excluded from instrumentation */ 98b78ee15eSRuslan Bukin if (fbt_excluded(name)) 99b78ee15eSRuslan Bukin return (0); 100b78ee15eSRuslan Bukin 10130b68ecdSRobert Watson /* 10230b68ecdSRobert Watson * Instrumenting certain exception handling functions can lead to FBT 10330b68ecdSRobert Watson * recursion, so exclude from instrumentation. 10430b68ecdSRobert Watson */ 10530b68ecdSRobert Watson if (strcmp(name, "handle_el1h_sync") == 0 || 10630b68ecdSRobert Watson strcmp(name, "do_el1h_sync") == 0) 10730b68ecdSRobert Watson return (1); 10830b68ecdSRobert Watson 109b78ee15eSRuslan Bukin instr = (uint32_t *)(symval->value); 110b78ee15eSRuslan Bukin limit = (uint32_t *)(symval->value + symval->size); 111b78ee15eSRuslan Bukin 112b5876847SAndrew Turner /* 113b5876847SAndrew Turner * Ignore any bti instruction at the start of the function 114b5876847SAndrew Turner * we need to keep it there for any indirect branches calling 115b5876847SAndrew Turner * the function on Armv8.5+ 116b5876847SAndrew Turner */ 117b5876847SAndrew Turner if ((*instr & BTI_MASK) == BTI_INSTR) 118b5876847SAndrew Turner instr++; 119b5876847SAndrew Turner 12028d94520SAndrew Turner /* 12128d94520SAndrew Turner * If the first instruction is a nop it's a specially marked 12228d94520SAndrew Turner * asm function. We only support a nop first as it's not a normal 12328d94520SAndrew Turner * part of the function prologue. 12428d94520SAndrew Turner */ 12528d94520SAndrew Turner if (*instr == NOP_INSTR) 126b4db386fSIgor Ostapenko goto found; 127b4db386fSIgor Ostapenko 128b4db386fSIgor Ostapenko /* Look for stp (pre-indexed) or sub operation */ 129b4db386fSIgor Ostapenko for (; instr < limit; instr++) { 13028d94520SAndrew Turner /* 131980746e5SChristos Margiolis * Functions start with "stp xt1, xt2, [xn, <const>]!" or 132980746e5SChristos Margiolis * "sub sp, sp, <const>". 133980746e5SChristos Margiolis * 134980746e5SChristos Margiolis * Sometimes the compiler will have a sub instruction that is 135980746e5SChristos Margiolis * not of the above type so don't stop if we see one. 13628d94520SAndrew Turner */ 137d0df1a2dSAndrew Turner if ((*instr & LDP_STP_MASK) == STP_64) { 138d0df1a2dSAndrew Turner /* 139980746e5SChristos Margiolis * Assume any other store of this type means we are 140980746e5SChristos Margiolis * past the function prologue. 141d0df1a2dSAndrew Turner */ 142d0df1a2dSAndrew Turner if (((*instr >> ADDR_SHIFT) & ADDR_MASK) == 31) 143b4db386fSIgor Ostapenko break; 144980746e5SChristos Margiolis } else if ((*instr & SUB_MASK) == SUB_INSTR && 145c00ec4daSAndrew Turner ((*instr >> SUB_RD_SHIFT) & SUB_R_MASK) == 31 && 146980746e5SChristos Margiolis ((*instr >> SUB_RN_SHIFT) & SUB_R_MASK) == 31) 147b4db386fSIgor Ostapenko break; 14828d94520SAndrew Turner } 149b4db386fSIgor Ostapenko found: 150b4db386fSIgor Ostapenko if (instr >= limit) 151b78ee15eSRuslan Bukin return (0); 152b78ee15eSRuslan Bukin 153b78ee15eSRuslan Bukin fbt = malloc(sizeof (fbt_probe_t), M_FBT, M_WAITOK | M_ZERO); 154b78ee15eSRuslan Bukin fbt->fbtp_name = name; 155b78ee15eSRuslan Bukin fbt->fbtp_id = dtrace_probe_create(fbt_id, modname, 156599fb1d1SRobert Watson name, FBT_ENTRY, FBT_AFRAMES, fbt); 157b78ee15eSRuslan Bukin fbt->fbtp_patchpoint = instr; 158b78ee15eSRuslan Bukin fbt->fbtp_ctl = lf; 159b78ee15eSRuslan Bukin fbt->fbtp_loadcnt = lf->loadcnt; 160b78ee15eSRuslan Bukin fbt->fbtp_savedval = *instr; 161b78ee15eSRuslan Bukin fbt->fbtp_patchval = FBT_PATCHVAL; 162c00ec4daSAndrew Turner if ((*instr & SUB_MASK) == SUB_INSTR) 163c00ec4daSAndrew Turner fbt->fbtp_rval = DTRACE_INVOP_SUB; 164c00ec4daSAndrew Turner else 165d0df1a2dSAndrew Turner fbt->fbtp_rval = DTRACE_INVOP_STP; 166b78ee15eSRuslan Bukin fbt->fbtp_symindx = symindx; 167b78ee15eSRuslan Bukin 168b78ee15eSRuslan Bukin fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)]; 169b78ee15eSRuslan Bukin fbt_probetab[FBT_ADDR2NDX(instr)] = fbt; 170b78ee15eSRuslan Bukin 171b78ee15eSRuslan Bukin lf->fbt_nentries++; 172b78ee15eSRuslan Bukin 173b78ee15eSRuslan Bukin retfbt = NULL; 174b78ee15eSRuslan Bukin again: 175b78ee15eSRuslan Bukin for (; instr < limit; instr++) { 176b78ee15eSRuslan Bukin if (*instr == RET_INSTR) 177b78ee15eSRuslan Bukin break; 178b78ee15eSRuslan Bukin else if ((*instr & B_MASK) == B_INSTR) { 179b78ee15eSRuslan Bukin offs = (*instr & B_DATA_MASK); 180*4da070ceSMark Johnston target = instr + offs; 181b78ee15eSRuslan Bukin start = (uint32_t *)symval->value; 182b78ee15eSRuslan Bukin if (target >= limit || target < start) 183b78ee15eSRuslan Bukin break; 184b78ee15eSRuslan Bukin } 185b78ee15eSRuslan Bukin } 186b78ee15eSRuslan Bukin 187b78ee15eSRuslan Bukin if (instr >= limit) 188b78ee15eSRuslan Bukin return (0); 189b78ee15eSRuslan Bukin 190b78ee15eSRuslan Bukin /* 191b78ee15eSRuslan Bukin * We have a winner! 192b78ee15eSRuslan Bukin */ 193b78ee15eSRuslan Bukin fbt = malloc(sizeof (fbt_probe_t), M_FBT, M_WAITOK | M_ZERO); 194b78ee15eSRuslan Bukin fbt->fbtp_name = name; 195b78ee15eSRuslan Bukin if (retfbt == NULL) { 196b78ee15eSRuslan Bukin fbt->fbtp_id = dtrace_probe_create(fbt_id, modname, 197599fb1d1SRobert Watson name, FBT_RETURN, FBT_AFRAMES, fbt); 198b78ee15eSRuslan Bukin } else { 199c208cb99SMark Johnston retfbt->fbtp_probenext = fbt; 200b78ee15eSRuslan Bukin fbt->fbtp_id = retfbt->fbtp_id; 201b78ee15eSRuslan Bukin } 202b78ee15eSRuslan Bukin retfbt = fbt; 203b78ee15eSRuslan Bukin 204b78ee15eSRuslan Bukin fbt->fbtp_patchpoint = instr; 205b78ee15eSRuslan Bukin fbt->fbtp_ctl = lf; 206b78ee15eSRuslan Bukin fbt->fbtp_loadcnt = lf->loadcnt; 207b78ee15eSRuslan Bukin fbt->fbtp_symindx = symindx; 208b78ee15eSRuslan Bukin if ((*instr & B_MASK) == B_INSTR) 209b78ee15eSRuslan Bukin fbt->fbtp_rval = DTRACE_INVOP_B; 210b78ee15eSRuslan Bukin else 211b78ee15eSRuslan Bukin fbt->fbtp_rval = DTRACE_INVOP_RET; 212e3ccf4f9SAndrew Turner fbt->fbtp_roffset = (uintptr_t)instr - (uintptr_t)symval->value; 213b78ee15eSRuslan Bukin fbt->fbtp_savedval = *instr; 214b78ee15eSRuslan Bukin fbt->fbtp_patchval = FBT_PATCHVAL; 215b78ee15eSRuslan Bukin fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)]; 216b78ee15eSRuslan Bukin fbt_probetab[FBT_ADDR2NDX(instr)] = fbt; 217b78ee15eSRuslan Bukin 218b78ee15eSRuslan Bukin lf->fbt_nentries++; 219b78ee15eSRuslan Bukin 220b78ee15eSRuslan Bukin instr++; 221b78ee15eSRuslan Bukin goto again; 222b78ee15eSRuslan Bukin } 223