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 * 23266b4a78SMark Johnston */ 24266b4a78SMark Johnston 25266b4a78SMark Johnston /* 26266b4a78SMark Johnston * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 27266b4a78SMark Johnston * Use is subject to license terms. 28266b4a78SMark Johnston */ 29266b4a78SMark Johnston 30266b4a78SMark Johnston #include <sys/param.h> 31266b4a78SMark Johnston 32266b4a78SMark Johnston #include <sys/dtrace.h> 33266b4a78SMark Johnston 341aa8a926SMark Johnston #include <machine/cpufunc.h> 357a79ce2eSMark Johnston #include <machine/md_var.h> 361aa8a926SMark Johnston 37266b4a78SMark Johnston #include "fbt.h" 38266b4a78SMark Johnston 39266b4a78SMark Johnston #define FBT_PUSHL_EBP 0x55 40266b4a78SMark Johnston #define FBT_MOVL_ESP_EBP0_V0 0x8b 41266b4a78SMark Johnston #define FBT_MOVL_ESP_EBP1_V0 0xec 42266b4a78SMark Johnston #define FBT_MOVL_ESP_EBP0_V1 0x89 43266b4a78SMark Johnston #define FBT_MOVL_ESP_EBP1_V1 0xe5 44266b4a78SMark Johnston #define FBT_REX_RSP_RBP 0x48 45266b4a78SMark Johnston 46266b4a78SMark Johnston #define FBT_POPL_EBP 0x5d 47266b4a78SMark Johnston #define FBT_RET 0xc3 48266b4a78SMark Johnston #define FBT_RET_IMM16 0xc2 49266b4a78SMark Johnston #define FBT_LEAVE 0xc9 50266b4a78SMark Johnston 51266b4a78SMark Johnston #ifdef __amd64__ 52266b4a78SMark Johnston #define FBT_PATCHVAL 0xcc 53266b4a78SMark Johnston #else 54266b4a78SMark Johnston #define FBT_PATCHVAL 0xf0 55266b4a78SMark Johnston #endif 56266b4a78SMark Johnston 57*82283cadSMark Johnston #define FBT_AFRAMES 3 5890951695SChuck Silvers 59266b4a78SMark Johnston int 60a7aa3d4dSMark Johnston fbt_invop(uintptr_t addr, struct trapframe *frame, uintptr_t scratch __unused) 61266b4a78SMark Johnston { 626c280659SMark Johnston solaris_cpu_t *cpu; 636c280659SMark Johnston uintptr_t *stack; 64a7aa3d4dSMark Johnston uintptr_t arg0, arg1, arg2, arg3, arg4, rval; 656c280659SMark Johnston fbt_probe_t *fbt; 66c208cb99SMark Johnston int8_t fbtrval; 67266b4a78SMark Johnston 686c280659SMark Johnston #ifdef __amd64__ 696c280659SMark Johnston stack = (uintptr_t *)frame->tf_rsp; 70a7aa3d4dSMark Johnston rval = frame->tf_rax; 716c280659SMark Johnston #else 726c280659SMark Johnston /* Skip hardware-saved registers. */ 736c280659SMark Johnston stack = (uintptr_t *)frame->tf_isp + 3; 74a7aa3d4dSMark Johnston rval = frame->tf_eax; 756c280659SMark Johnston #endif 766c280659SMark Johnston 776c280659SMark Johnston cpu = &solaris_cpu[curcpu]; 786c280659SMark Johnston fbt = fbt_probetab[FBT_ADDR2NDX(addr)]; 79266b4a78SMark Johnston for (; fbt != NULL; fbt = fbt->fbtp_hashnext) { 80c208cb99SMark Johnston if ((uintptr_t)fbt->fbtp_patchpoint != addr) 81c208cb99SMark Johnston continue; 82c208cb99SMark Johnston fbtrval = fbt->fbtp_rval; 830e69c959SMark Johnston 840e69c959SMark Johnston /* 850e69c959SMark Johnston * Report the address of the breakpoint for the benefit 860e69c959SMark Johnston * of consumers fetching register values with regs[]. 870e69c959SMark Johnston */ 8860013d9cSMark Johnston #ifdef __i386__ 8960013d9cSMark Johnston frame->tf_eip--; 9060013d9cSMark Johnston #else 910e69c959SMark Johnston frame->tf_rip--; 9260013d9cSMark Johnston #endif 93c208cb99SMark Johnston for (; fbt != NULL; fbt = fbt->fbtp_tracenext) { 94c208cb99SMark Johnston ASSERT(fbt->fbtp_rval == fbtrval); 95266b4a78SMark Johnston if (fbt->fbtp_roffset == 0) { 966c280659SMark Johnston #ifdef __amd64__ 976c280659SMark Johnston /* fbt->fbtp_rval == DTRACE_INVOP_PUSHQ_RBP */ 986c280659SMark Johnston DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); 996c280659SMark Johnston cpu->cpu_dtrace_caller = stack[0]; 1006c280659SMark Johnston DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT | 1016c280659SMark Johnston CPU_DTRACE_BADADDR); 1026c280659SMark Johnston 1036c280659SMark Johnston arg0 = frame->tf_rdi; 1046c280659SMark Johnston arg1 = frame->tf_rsi; 1056c280659SMark Johnston arg2 = frame->tf_rdx; 1066c280659SMark Johnston arg3 = frame->tf_rcx; 1076c280659SMark Johnston arg4 = frame->tf_r8; 1086c280659SMark Johnston #else 109266b4a78SMark Johnston int i = 0; 1106c280659SMark Johnston 111266b4a78SMark Johnston /* 112266b4a78SMark Johnston * When accessing the arguments on the stack, 113266b4a78SMark Johnston * we must protect against accessing beyond 114266b4a78SMark Johnston * the stack. We can safely set NOFAULT here 115266b4a78SMark Johnston * -- we know that interrupts are already 116266b4a78SMark Johnston * disabled. 117266b4a78SMark Johnston */ 118266b4a78SMark Johnston DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); 119266b4a78SMark Johnston cpu->cpu_dtrace_caller = stack[i++]; 1206c280659SMark Johnston arg0 = stack[i++]; 1216c280659SMark Johnston arg1 = stack[i++]; 1226c280659SMark Johnston arg2 = stack[i++]; 1236c280659SMark Johnston arg3 = stack[i++]; 1246c280659SMark Johnston arg4 = stack[i++]; 125266b4a78SMark Johnston DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT | 126266b4a78SMark Johnston CPU_DTRACE_BADADDR); 1276c280659SMark Johnston #endif 128266b4a78SMark Johnston 1296c280659SMark Johnston dtrace_probe(fbt->fbtp_id, arg0, arg1, 1306c280659SMark Johnston arg2, arg3, arg4); 131266b4a78SMark Johnston 132266b4a78SMark Johnston cpu->cpu_dtrace_caller = 0; 133266b4a78SMark Johnston } else { 134266b4a78SMark Johnston #ifdef __amd64__ 135266b4a78SMark Johnston /* 136266b4a78SMark Johnston * On amd64, we instrument the ret, not the 137266b4a78SMark Johnston * leave. We therefore need to set the caller 1386c280659SMark Johnston * to ensure that the top frame of a stack() 139266b4a78SMark Johnston * action is correct. 140266b4a78SMark Johnston */ 141266b4a78SMark Johnston DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); 142266b4a78SMark Johnston cpu->cpu_dtrace_caller = stack[0]; 143266b4a78SMark Johnston DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT | 144266b4a78SMark Johnston CPU_DTRACE_BADADDR); 145266b4a78SMark Johnston #endif 146266b4a78SMark Johnston 147266b4a78SMark Johnston dtrace_probe(fbt->fbtp_id, fbt->fbtp_roffset, 148266b4a78SMark Johnston rval, 0, 0, 0); 149266b4a78SMark Johnston cpu->cpu_dtrace_caller = 0; 150266b4a78SMark Johnston } 151266b4a78SMark Johnston } 1520e69c959SMark Johnston /* Advance to the instruction following the breakpoint. */ 15360013d9cSMark Johnston #ifdef __i386__ 15460013d9cSMark Johnston frame->tf_eip++; 15560013d9cSMark Johnston #else 1560e69c959SMark Johnston frame->tf_rip++; 15760013d9cSMark Johnston #endif 158c208cb99SMark Johnston return (fbtrval); 159266b4a78SMark Johnston } 160266b4a78SMark Johnston 161266b4a78SMark Johnston return (0); 162266b4a78SMark Johnston } 163266b4a78SMark Johnston 164266b4a78SMark Johnston void 165266b4a78SMark Johnston fbt_patch_tracepoint(fbt_probe_t *fbt, fbt_patchval_t val) 166266b4a78SMark Johnston { 1671de56ac7SMark Johnston register_t intr; 1687a79ce2eSMark Johnston bool old_wp; 169266b4a78SMark Johnston 1701de56ac7SMark Johnston intr = intr_disable(); 1717a79ce2eSMark Johnston old_wp = disable_wp(); 172266b4a78SMark Johnston *fbt->fbtp_patchpoint = val; 1737a79ce2eSMark Johnston restore_wp(old_wp); 1741de56ac7SMark Johnston intr_restore(intr); 175266b4a78SMark Johnston } 176266b4a78SMark Johnston 177266b4a78SMark Johnston int 178266b4a78SMark Johnston fbt_provide_module_function(linker_file_t lf, int symindx, 179266b4a78SMark Johnston linker_symval_t *symval, void *opaque) 180266b4a78SMark Johnston { 181266b4a78SMark Johnston char *modname = opaque; 182266b4a78SMark Johnston const char *name = symval->name; 183c208cb99SMark Johnston fbt_probe_t *fbt, *hash, *retfbt; 184266b4a78SMark Johnston int j; 185266b4a78SMark Johnston int size; 186266b4a78SMark Johnston uint8_t *instr, *limit; 187266b4a78SMark Johnston 1888bb9b7f1SMark Johnston if (fbt_excluded(name)) 189266b4a78SMark Johnston return (0); 1908bb9b7f1SMark Johnston 1918bb9b7f1SMark Johnston /* 1928bb9b7f1SMark Johnston * trap_check() is a wrapper for DTrace's fault handler, so we don't 1938bb9b7f1SMark Johnston * want to be able to instrument it. 1948bb9b7f1SMark Johnston */ 1958bb9b7f1SMark Johnston if (strcmp(name, "trap_check") == 0) 1968bb9b7f1SMark Johnston return (0); 197266b4a78SMark Johnston 198266b4a78SMark Johnston size = symval->size; 199266b4a78SMark Johnston 200266b4a78SMark Johnston instr = (uint8_t *) symval->value; 201266b4a78SMark Johnston limit = (uint8_t *) symval->value + symval->size; 202266b4a78SMark Johnston 203266b4a78SMark Johnston #ifdef __amd64__ 204266b4a78SMark Johnston while (instr < limit) { 205266b4a78SMark Johnston if (*instr == FBT_PUSHL_EBP) 206266b4a78SMark Johnston break; 207266b4a78SMark Johnston 208266b4a78SMark Johnston if ((size = dtrace_instr_size(instr)) <= 0) 209266b4a78SMark Johnston break; 210266b4a78SMark Johnston 211266b4a78SMark Johnston instr += size; 212266b4a78SMark Johnston } 213266b4a78SMark Johnston 214266b4a78SMark Johnston if (instr >= limit || *instr != FBT_PUSHL_EBP) { 215266b4a78SMark Johnston /* 216266b4a78SMark Johnston * We either don't save the frame pointer in this 217266b4a78SMark Johnston * function, or we ran into some disassembly 218266b4a78SMark Johnston * screw-up. Either way, we bail. 219266b4a78SMark Johnston */ 220266b4a78SMark Johnston return (0); 221266b4a78SMark Johnston } 222266b4a78SMark Johnston #else 223266b4a78SMark Johnston if (instr[0] != FBT_PUSHL_EBP) 224266b4a78SMark Johnston return (0); 225266b4a78SMark Johnston 226266b4a78SMark Johnston if (!(instr[1] == FBT_MOVL_ESP_EBP0_V0 && 227266b4a78SMark Johnston instr[2] == FBT_MOVL_ESP_EBP1_V0) && 228266b4a78SMark Johnston !(instr[1] == FBT_MOVL_ESP_EBP0_V1 && 229266b4a78SMark Johnston instr[2] == FBT_MOVL_ESP_EBP1_V1)) 230266b4a78SMark Johnston return (0); 231266b4a78SMark Johnston #endif 232266b4a78SMark Johnston 233266b4a78SMark Johnston fbt = malloc(sizeof (fbt_probe_t), M_FBT, M_WAITOK | M_ZERO); 234266b4a78SMark Johnston fbt->fbtp_name = name; 235266b4a78SMark Johnston fbt->fbtp_id = dtrace_probe_create(fbt_id, modname, 23690951695SChuck Silvers name, FBT_ENTRY, FBT_AFRAMES, fbt); 237266b4a78SMark Johnston fbt->fbtp_patchpoint = instr; 238266b4a78SMark Johnston fbt->fbtp_ctl = lf; 239266b4a78SMark Johnston fbt->fbtp_loadcnt = lf->loadcnt; 240266b4a78SMark Johnston fbt->fbtp_rval = DTRACE_INVOP_PUSHL_EBP; 241266b4a78SMark Johnston fbt->fbtp_savedval = *instr; 242266b4a78SMark Johnston fbt->fbtp_patchval = FBT_PATCHVAL; 243266b4a78SMark Johnston fbt->fbtp_symindx = symindx; 244266b4a78SMark Johnston 245c208cb99SMark Johnston for (hash = fbt_probetab[FBT_ADDR2NDX(instr)]; hash != NULL; 246c208cb99SMark Johnston hash = hash->fbtp_hashnext) { 247c208cb99SMark Johnston if (hash->fbtp_patchpoint == fbt->fbtp_patchpoint) { 248c208cb99SMark Johnston fbt->fbtp_tracenext = hash->fbtp_tracenext; 249c208cb99SMark Johnston hash->fbtp_tracenext = fbt; 250c208cb99SMark Johnston break; 251c208cb99SMark Johnston } 252c208cb99SMark Johnston } 253c208cb99SMark Johnston if (hash == NULL) { 254266b4a78SMark Johnston fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)]; 255266b4a78SMark Johnston fbt_probetab[FBT_ADDR2NDX(instr)] = fbt; 256c208cb99SMark Johnston } 257266b4a78SMark Johnston 258266b4a78SMark Johnston lf->fbt_nentries++; 259266b4a78SMark Johnston 260266b4a78SMark Johnston retfbt = NULL; 261266b4a78SMark Johnston again: 262266b4a78SMark Johnston if (instr >= limit) 263266b4a78SMark Johnston return (0); 264266b4a78SMark Johnston 265266b4a78SMark Johnston /* 266266b4a78SMark Johnston * If this disassembly fails, then we've likely walked off into 267266b4a78SMark Johnston * a jump table or some other unsuitable area. Bail out of the 268266b4a78SMark Johnston * disassembly now. 269266b4a78SMark Johnston */ 270266b4a78SMark Johnston if ((size = dtrace_instr_size(instr)) <= 0) 271266b4a78SMark Johnston return (0); 272266b4a78SMark Johnston 273266b4a78SMark Johnston #ifdef __amd64__ 274266b4a78SMark Johnston /* 275266b4a78SMark Johnston * We only instrument "ret" on amd64 -- we don't yet instrument 276266b4a78SMark Johnston * ret imm16, largely because the compiler doesn't seem to 277266b4a78SMark Johnston * (yet) emit them in the kernel... 278266b4a78SMark Johnston */ 279266b4a78SMark Johnston if (*instr != FBT_RET) { 280266b4a78SMark Johnston instr += size; 281266b4a78SMark Johnston goto again; 282266b4a78SMark Johnston } 283266b4a78SMark Johnston #else 284266b4a78SMark Johnston if (!(size == 1 && 285266b4a78SMark Johnston (*instr == FBT_POPL_EBP || *instr == FBT_LEAVE) && 286266b4a78SMark Johnston (*(instr + 1) == FBT_RET || 287266b4a78SMark Johnston *(instr + 1) == FBT_RET_IMM16))) { 288266b4a78SMark Johnston instr += size; 289266b4a78SMark Johnston goto again; 290266b4a78SMark Johnston } 291266b4a78SMark Johnston #endif 292266b4a78SMark Johnston 293266b4a78SMark Johnston /* 294266b4a78SMark Johnston * We (desperately) want to avoid erroneously instrumenting a 295266b4a78SMark Johnston * jump table, especially given that our markers are pretty 296266b4a78SMark Johnston * short: two bytes on x86, and just one byte on amd64. To 297266b4a78SMark Johnston * determine if we're looking at a true instruction sequence 298266b4a78SMark Johnston * or an inline jump table that happens to contain the same 299266b4a78SMark Johnston * byte sequences, we resort to some heuristic sleeze: we 300266b4a78SMark Johnston * treat this instruction as being contained within a pointer, 301266b4a78SMark Johnston * and see if that pointer points to within the body of the 302266b4a78SMark Johnston * function. If it does, we refuse to instrument it. 303266b4a78SMark Johnston */ 304266b4a78SMark Johnston for (j = 0; j < sizeof (uintptr_t); j++) { 305266b4a78SMark Johnston caddr_t check = (caddr_t) instr - j; 306266b4a78SMark Johnston uint8_t *ptr; 307266b4a78SMark Johnston 308266b4a78SMark Johnston if (check < symval->value) 309266b4a78SMark Johnston break; 310266b4a78SMark Johnston 311266b4a78SMark Johnston if (check + sizeof (caddr_t) > (caddr_t)limit) 312266b4a78SMark Johnston continue; 313266b4a78SMark Johnston 314266b4a78SMark Johnston ptr = *(uint8_t **)check; 315266b4a78SMark Johnston 316266b4a78SMark Johnston if (ptr >= (uint8_t *) symval->value && ptr < limit) { 317266b4a78SMark Johnston instr += size; 318266b4a78SMark Johnston goto again; 319266b4a78SMark Johnston } 320266b4a78SMark Johnston } 321266b4a78SMark Johnston 322266b4a78SMark Johnston /* 323266b4a78SMark Johnston * We have a winner! 324266b4a78SMark Johnston */ 325266b4a78SMark Johnston fbt = malloc(sizeof (fbt_probe_t), M_FBT, M_WAITOK | M_ZERO); 326266b4a78SMark Johnston fbt->fbtp_name = name; 327266b4a78SMark Johnston 328266b4a78SMark Johnston if (retfbt == NULL) { 329266b4a78SMark Johnston fbt->fbtp_id = dtrace_probe_create(fbt_id, modname, 33090951695SChuck Silvers name, FBT_RETURN, FBT_AFRAMES, fbt); 331266b4a78SMark Johnston } else { 332c208cb99SMark Johnston retfbt->fbtp_probenext = fbt; 333266b4a78SMark Johnston fbt->fbtp_id = retfbt->fbtp_id; 334266b4a78SMark Johnston } 335266b4a78SMark Johnston 336266b4a78SMark Johnston retfbt = fbt; 337266b4a78SMark Johnston fbt->fbtp_patchpoint = instr; 338266b4a78SMark Johnston fbt->fbtp_ctl = lf; 339266b4a78SMark Johnston fbt->fbtp_loadcnt = lf->loadcnt; 340266b4a78SMark Johnston fbt->fbtp_symindx = symindx; 341266b4a78SMark Johnston 342266b4a78SMark Johnston #ifndef __amd64__ 343266b4a78SMark Johnston if (*instr == FBT_POPL_EBP) { 344266b4a78SMark Johnston fbt->fbtp_rval = DTRACE_INVOP_POPL_EBP; 345266b4a78SMark Johnston } else { 346266b4a78SMark Johnston ASSERT(*instr == FBT_LEAVE); 347266b4a78SMark Johnston fbt->fbtp_rval = DTRACE_INVOP_LEAVE; 348266b4a78SMark Johnston } 349266b4a78SMark Johnston fbt->fbtp_roffset = 350266b4a78SMark Johnston (uintptr_t)(instr - (uint8_t *) symval->value) + 1; 351266b4a78SMark Johnston 352266b4a78SMark Johnston #else 353266b4a78SMark Johnston ASSERT(*instr == FBT_RET); 354266b4a78SMark Johnston fbt->fbtp_rval = DTRACE_INVOP_RET; 355266b4a78SMark Johnston fbt->fbtp_roffset = 356266b4a78SMark Johnston (uintptr_t)(instr - (uint8_t *) symval->value); 357266b4a78SMark Johnston #endif 358266b4a78SMark Johnston 359266b4a78SMark Johnston fbt->fbtp_savedval = *instr; 360266b4a78SMark Johnston fbt->fbtp_patchval = FBT_PATCHVAL; 361266b4a78SMark Johnston fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)]; 362266b4a78SMark Johnston fbt_probetab[FBT_ADDR2NDX(instr)] = fbt; 363266b4a78SMark Johnston 364266b4a78SMark Johnston lf->fbt_nentries++; 365266b4a78SMark Johnston 366266b4a78SMark Johnston instr += size; 367266b4a78SMark Johnston goto again; 368266b4a78SMark Johnston } 369