10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 52179Sahl * Common Development and Distribution License (the "License"). 62179Sahl * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 212179Sahl 220Sstevel@tonic-gate /* 233446Smrj * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 240Sstevel@tonic-gate * Use is subject to license terms. 250Sstevel@tonic-gate */ 260Sstevel@tonic-gate 270Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 280Sstevel@tonic-gate 290Sstevel@tonic-gate #include <sys/dtrace.h> 300Sstevel@tonic-gate #include <sys/fasttrap.h> 310Sstevel@tonic-gate #include <sys/x_call.h> 320Sstevel@tonic-gate #include <sys/cmn_err.h> 330Sstevel@tonic-gate #include <sys/trap.h> 340Sstevel@tonic-gate #include <sys/psw.h> 350Sstevel@tonic-gate #include <sys/privregs.h> 360Sstevel@tonic-gate #include <sys/machsystm.h> 370Sstevel@tonic-gate #include <vm/seg_kmem.h> 380Sstevel@tonic-gate 390Sstevel@tonic-gate typedef struct dtrace_invop_hdlr { 400Sstevel@tonic-gate int (*dtih_func)(uintptr_t, uintptr_t *, uintptr_t); 410Sstevel@tonic-gate struct dtrace_invop_hdlr *dtih_next; 420Sstevel@tonic-gate } dtrace_invop_hdlr_t; 430Sstevel@tonic-gate 440Sstevel@tonic-gate dtrace_invop_hdlr_t *dtrace_invop_hdlr; 450Sstevel@tonic-gate 460Sstevel@tonic-gate int 470Sstevel@tonic-gate dtrace_invop(uintptr_t addr, uintptr_t *stack, uintptr_t eax) 480Sstevel@tonic-gate { 490Sstevel@tonic-gate dtrace_invop_hdlr_t *hdlr; 500Sstevel@tonic-gate int rval; 510Sstevel@tonic-gate 520Sstevel@tonic-gate for (hdlr = dtrace_invop_hdlr; hdlr != NULL; hdlr = hdlr->dtih_next) { 530Sstevel@tonic-gate if ((rval = hdlr->dtih_func(addr, stack, eax)) != 0) 540Sstevel@tonic-gate return (rval); 550Sstevel@tonic-gate } 560Sstevel@tonic-gate 570Sstevel@tonic-gate return (0); 580Sstevel@tonic-gate } 590Sstevel@tonic-gate 600Sstevel@tonic-gate void 610Sstevel@tonic-gate dtrace_invop_add(int (*func)(uintptr_t, uintptr_t *, uintptr_t)) 620Sstevel@tonic-gate { 630Sstevel@tonic-gate dtrace_invop_hdlr_t *hdlr; 640Sstevel@tonic-gate 650Sstevel@tonic-gate hdlr = kmem_alloc(sizeof (dtrace_invop_hdlr_t), KM_SLEEP); 660Sstevel@tonic-gate hdlr->dtih_func = func; 670Sstevel@tonic-gate hdlr->dtih_next = dtrace_invop_hdlr; 680Sstevel@tonic-gate dtrace_invop_hdlr = hdlr; 690Sstevel@tonic-gate } 700Sstevel@tonic-gate 710Sstevel@tonic-gate void 720Sstevel@tonic-gate dtrace_invop_remove(int (*func)(uintptr_t, uintptr_t *, uintptr_t)) 730Sstevel@tonic-gate { 740Sstevel@tonic-gate dtrace_invop_hdlr_t *hdlr = dtrace_invop_hdlr, *prev = NULL; 750Sstevel@tonic-gate 760Sstevel@tonic-gate for (;;) { 770Sstevel@tonic-gate if (hdlr == NULL) 780Sstevel@tonic-gate panic("attempt to remove non-existent invop handler"); 790Sstevel@tonic-gate 800Sstevel@tonic-gate if (hdlr->dtih_func == func) 810Sstevel@tonic-gate break; 820Sstevel@tonic-gate 830Sstevel@tonic-gate prev = hdlr; 840Sstevel@tonic-gate hdlr = hdlr->dtih_next; 850Sstevel@tonic-gate } 860Sstevel@tonic-gate 870Sstevel@tonic-gate if (prev == NULL) { 880Sstevel@tonic-gate ASSERT(dtrace_invop_hdlr == hdlr); 890Sstevel@tonic-gate dtrace_invop_hdlr = hdlr->dtih_next; 900Sstevel@tonic-gate } else { 910Sstevel@tonic-gate ASSERT(dtrace_invop_hdlr != hdlr); 920Sstevel@tonic-gate prev->dtih_next = hdlr->dtih_next; 930Sstevel@tonic-gate } 940Sstevel@tonic-gate 950Sstevel@tonic-gate kmem_free(hdlr, sizeof (dtrace_invop_hdlr_t)); 960Sstevel@tonic-gate } 970Sstevel@tonic-gate 980Sstevel@tonic-gate int 990Sstevel@tonic-gate dtrace_getipl(void) 1000Sstevel@tonic-gate { 1010Sstevel@tonic-gate return (CPU->cpu_pri); 1020Sstevel@tonic-gate } 1030Sstevel@tonic-gate 1040Sstevel@tonic-gate /*ARGSUSED*/ 1050Sstevel@tonic-gate void 1060Sstevel@tonic-gate dtrace_toxic_ranges(void (*func)(uintptr_t base, uintptr_t limit)) 1070Sstevel@tonic-gate { 1080Sstevel@tonic-gate #ifdef __amd64 1090Sstevel@tonic-gate extern uintptr_t toxic_addr; 1100Sstevel@tonic-gate extern size_t toxic_size; 1110Sstevel@tonic-gate 1120Sstevel@tonic-gate (*func)(0, _userlimit); 1130Sstevel@tonic-gate 1140Sstevel@tonic-gate if (hole_end > hole_start) 1150Sstevel@tonic-gate (*func)(hole_start, hole_end); 1160Sstevel@tonic-gate (*func)(toxic_addr, toxic_addr + toxic_size); 1170Sstevel@tonic-gate #else 1180Sstevel@tonic-gate extern void *device_arena_contains(void *, size_t, size_t *); 1190Sstevel@tonic-gate caddr_t vaddr; 1200Sstevel@tonic-gate size_t len; 1210Sstevel@tonic-gate 1220Sstevel@tonic-gate for (vaddr = (caddr_t)kernelbase; vaddr < (caddr_t)KERNEL_TEXT; 1230Sstevel@tonic-gate vaddr += len) { 1240Sstevel@tonic-gate len = (caddr_t)KERNEL_TEXT - vaddr; 1250Sstevel@tonic-gate vaddr = device_arena_contains(vaddr, len, &len); 1260Sstevel@tonic-gate if (vaddr == NULL) 1270Sstevel@tonic-gate break; 1280Sstevel@tonic-gate (*func)((uintptr_t)vaddr, (uintptr_t)vaddr + len); 1290Sstevel@tonic-gate } 1300Sstevel@tonic-gate #endif 1310Sstevel@tonic-gate (*func)(0, _userlimit); 1320Sstevel@tonic-gate } 1330Sstevel@tonic-gate 1340Sstevel@tonic-gate static int 1350Sstevel@tonic-gate dtrace_xcall_func(dtrace_xcall_t func, void *arg) 1360Sstevel@tonic-gate { 1370Sstevel@tonic-gate (*func)(arg); 1380Sstevel@tonic-gate 1390Sstevel@tonic-gate return (0); 1400Sstevel@tonic-gate } 1410Sstevel@tonic-gate 1420Sstevel@tonic-gate /*ARGSUSED*/ 1430Sstevel@tonic-gate void 1440Sstevel@tonic-gate dtrace_xcall(processorid_t cpu, dtrace_xcall_t func, void *arg) 1450Sstevel@tonic-gate { 1460Sstevel@tonic-gate cpuset_t set; 1470Sstevel@tonic-gate 1480Sstevel@tonic-gate CPUSET_ZERO(set); 1490Sstevel@tonic-gate 1500Sstevel@tonic-gate if (cpu == DTRACE_CPUALL) { 1510Sstevel@tonic-gate CPUSET_ALL(set); 1520Sstevel@tonic-gate } else { 1530Sstevel@tonic-gate CPUSET_ADD(set, cpu); 1540Sstevel@tonic-gate } 1550Sstevel@tonic-gate 1560Sstevel@tonic-gate kpreempt_disable(); 1570Sstevel@tonic-gate xc_sync((xc_arg_t)func, (xc_arg_t)arg, 0, X_CALL_HIPRI, set, 1580Sstevel@tonic-gate (xc_func_t)dtrace_xcall_func); 1590Sstevel@tonic-gate kpreempt_enable(); 1600Sstevel@tonic-gate } 1610Sstevel@tonic-gate 1620Sstevel@tonic-gate void 1630Sstevel@tonic-gate dtrace_sync_func(void) 1640Sstevel@tonic-gate {} 1650Sstevel@tonic-gate 1660Sstevel@tonic-gate void 1670Sstevel@tonic-gate dtrace_sync(void) 1680Sstevel@tonic-gate { 1690Sstevel@tonic-gate dtrace_xcall(DTRACE_CPUALL, (dtrace_xcall_t)dtrace_sync_func, NULL); 1700Sstevel@tonic-gate } 1710Sstevel@tonic-gate 1720Sstevel@tonic-gate int (*dtrace_pid_probe_ptr)(struct regs *); 1730Sstevel@tonic-gate int (*dtrace_return_probe_ptr)(struct regs *); 1740Sstevel@tonic-gate 1750Sstevel@tonic-gate void 1760Sstevel@tonic-gate dtrace_user_probe(struct regs *rp, caddr_t addr, processorid_t cpuid) 1770Sstevel@tonic-gate { 1780Sstevel@tonic-gate krwlock_t *rwp; 1790Sstevel@tonic-gate proc_t *p = curproc; 1800Sstevel@tonic-gate extern void trap(struct regs *, caddr_t, processorid_t); 1810Sstevel@tonic-gate 1820Sstevel@tonic-gate if (USERMODE(rp->r_cs) || (rp->r_ps & PS_VM)) { 1830Sstevel@tonic-gate if (curthread->t_cred != p->p_cred) { 1840Sstevel@tonic-gate cred_t *oldcred = curthread->t_cred; 1850Sstevel@tonic-gate /* 1860Sstevel@tonic-gate * DTrace accesses t_cred in probe context. t_cred 1870Sstevel@tonic-gate * must always be either NULL, or point to a valid, 1880Sstevel@tonic-gate * allocated cred structure. 1890Sstevel@tonic-gate */ 1900Sstevel@tonic-gate curthread->t_cred = crgetcred(); 1910Sstevel@tonic-gate crfree(oldcred); 1920Sstevel@tonic-gate } 1930Sstevel@tonic-gate } 1940Sstevel@tonic-gate 1950Sstevel@tonic-gate if (rp->r_trapno == T_DTRACE_RET) { 1960Sstevel@tonic-gate uint8_t step = curthread->t_dtrace_step; 1970Sstevel@tonic-gate uint8_t ret = curthread->t_dtrace_ret; 1980Sstevel@tonic-gate uintptr_t npc = curthread->t_dtrace_npc; 1990Sstevel@tonic-gate 2000Sstevel@tonic-gate if (curthread->t_dtrace_ast) { 2010Sstevel@tonic-gate aston(curthread); 2020Sstevel@tonic-gate curthread->t_sig_check = 1; 2030Sstevel@tonic-gate } 2040Sstevel@tonic-gate 2050Sstevel@tonic-gate /* 2060Sstevel@tonic-gate * Clear all user tracing flags. 2070Sstevel@tonic-gate */ 2080Sstevel@tonic-gate curthread->t_dtrace_ft = 0; 2090Sstevel@tonic-gate 2100Sstevel@tonic-gate /* 2110Sstevel@tonic-gate * If we weren't expecting to take a return probe trap, kill 2120Sstevel@tonic-gate * the process as though it had just executed an unassigned 2130Sstevel@tonic-gate * trap instruction. 2140Sstevel@tonic-gate */ 2150Sstevel@tonic-gate if (step == 0) { 2160Sstevel@tonic-gate tsignal(curthread, SIGILL); 2170Sstevel@tonic-gate return; 2180Sstevel@tonic-gate } 2190Sstevel@tonic-gate 2200Sstevel@tonic-gate /* 2210Sstevel@tonic-gate * If we hit this trap unrelated to a return probe, we're 2220Sstevel@tonic-gate * just here to reset the AST flag since we deferred a signal 2230Sstevel@tonic-gate * until after we logically single-stepped the instruction we 2240Sstevel@tonic-gate * copied out. 2250Sstevel@tonic-gate */ 2260Sstevel@tonic-gate if (ret == 0) { 2270Sstevel@tonic-gate rp->r_pc = npc; 2280Sstevel@tonic-gate return; 2290Sstevel@tonic-gate } 2300Sstevel@tonic-gate 2310Sstevel@tonic-gate /* 2320Sstevel@tonic-gate * We need to wait until after we've called the 2330Sstevel@tonic-gate * dtrace_return_probe_ptr function pointer to set %pc. 2340Sstevel@tonic-gate */ 2350Sstevel@tonic-gate rwp = &CPU->cpu_ft_lock; 2360Sstevel@tonic-gate rw_enter(rwp, RW_READER); 2370Sstevel@tonic-gate if (dtrace_return_probe_ptr != NULL) 2380Sstevel@tonic-gate (void) (*dtrace_return_probe_ptr)(rp); 2390Sstevel@tonic-gate rw_exit(rwp); 2400Sstevel@tonic-gate rp->r_pc = npc; 2410Sstevel@tonic-gate 2420Sstevel@tonic-gate } else if (rp->r_trapno == T_BPTFLT) { 243*3939Ssethg uint8_t instr, instr2; 244*3939Ssethg caddr_t linearpc; 2450Sstevel@tonic-gate rwp = &CPU->cpu_ft_lock; 2460Sstevel@tonic-gate 2470Sstevel@tonic-gate /* 2480Sstevel@tonic-gate * The DTrace fasttrap provider uses the breakpoint trap 2490Sstevel@tonic-gate * (int 3). We let DTrace take the first crack at handling 2500Sstevel@tonic-gate * this trap; if it's not a probe that DTrace knowns about, 2510Sstevel@tonic-gate * we call into the trap() routine to handle it like a 2520Sstevel@tonic-gate * breakpoint placed by a conventional debugger. 2530Sstevel@tonic-gate */ 2540Sstevel@tonic-gate rw_enter(rwp, RW_READER); 2550Sstevel@tonic-gate if (dtrace_pid_probe_ptr != NULL && 2560Sstevel@tonic-gate (*dtrace_pid_probe_ptr)(rp) == 0) { 2570Sstevel@tonic-gate rw_exit(rwp); 2580Sstevel@tonic-gate return; 2590Sstevel@tonic-gate } 2600Sstevel@tonic-gate rw_exit(rwp); 2610Sstevel@tonic-gate 262*3939Ssethg if (dtrace_linear_pc(rp, p, &linearpc) != 0) { 263*3939Ssethg trap(rp, addr, cpuid); 264*3939Ssethg return; 265*3939Ssethg } 266*3939Ssethg 2670Sstevel@tonic-gate /* 2680Sstevel@tonic-gate * If the instruction that caused the breakpoint trap doesn't 2690Sstevel@tonic-gate * look like an int 3 anymore, it may be that this tracepoint 2700Sstevel@tonic-gate * was removed just after the user thread executed it. In 2710Sstevel@tonic-gate * that case, return to user land to retry the instuction. 272*3939Ssethg * Note that we assume the length of the instruction to retry 273*3939Ssethg * is 1 byte because that's the length of FASTTRAP_INSTR. 274*3939Ssethg * We check for r_pc > 0 and > 2 so that we don't have to 275*3939Ssethg * deal with segment wraparound. 2760Sstevel@tonic-gate */ 277*3939Ssethg if (rp->r_pc > 0 && fuword8(linearpc - 1, &instr) == 0 && 278*3939Ssethg instr != FASTTRAP_INSTR && 279*3939Ssethg (instr != 3 || (rp->r_pc >= 2 && 280*3939Ssethg (fuword8(linearpc - 2, &instr2) != 0 || instr2 != 0xCD)))) { 2810Sstevel@tonic-gate rp->r_pc--; 2820Sstevel@tonic-gate return; 2830Sstevel@tonic-gate } 2840Sstevel@tonic-gate 2850Sstevel@tonic-gate trap(rp, addr, cpuid); 2860Sstevel@tonic-gate 2870Sstevel@tonic-gate } else { 2880Sstevel@tonic-gate trap(rp, addr, cpuid); 2890Sstevel@tonic-gate } 2900Sstevel@tonic-gate } 2910Sstevel@tonic-gate 2920Sstevel@tonic-gate void 2930Sstevel@tonic-gate dtrace_safe_synchronous_signal(void) 2940Sstevel@tonic-gate { 2950Sstevel@tonic-gate kthread_t *t = curthread; 2960Sstevel@tonic-gate struct regs *rp = lwptoregs(ttolwp(t)); 2970Sstevel@tonic-gate size_t isz = t->t_dtrace_npc - t->t_dtrace_pc; 2980Sstevel@tonic-gate 2990Sstevel@tonic-gate ASSERT(t->t_dtrace_on); 3000Sstevel@tonic-gate 3010Sstevel@tonic-gate /* 3020Sstevel@tonic-gate * If we're not in the range of scratch addresses, we're not actually 3030Sstevel@tonic-gate * tracing user instructions so turn off the flags. If the instruction 3040Sstevel@tonic-gate * we copied out caused a synchonous trap, reset the pc back to its 3050Sstevel@tonic-gate * original value and turn off the flags. 3060Sstevel@tonic-gate */ 3070Sstevel@tonic-gate if (rp->r_pc < t->t_dtrace_scrpc || 3080Sstevel@tonic-gate rp->r_pc > t->t_dtrace_astpc + isz) { 3090Sstevel@tonic-gate t->t_dtrace_ft = 0; 3100Sstevel@tonic-gate } else if (rp->r_pc == t->t_dtrace_scrpc || 3110Sstevel@tonic-gate rp->r_pc == t->t_dtrace_astpc) { 3120Sstevel@tonic-gate rp->r_pc = t->t_dtrace_pc; 3130Sstevel@tonic-gate t->t_dtrace_ft = 0; 3140Sstevel@tonic-gate } 3150Sstevel@tonic-gate } 3160Sstevel@tonic-gate 3170Sstevel@tonic-gate int 3180Sstevel@tonic-gate dtrace_safe_defer_signal(void) 3190Sstevel@tonic-gate { 3200Sstevel@tonic-gate kthread_t *t = curthread; 3210Sstevel@tonic-gate struct regs *rp = lwptoregs(ttolwp(t)); 3220Sstevel@tonic-gate size_t isz = t->t_dtrace_npc - t->t_dtrace_pc; 3230Sstevel@tonic-gate 3240Sstevel@tonic-gate ASSERT(t->t_dtrace_on); 3250Sstevel@tonic-gate 3260Sstevel@tonic-gate /* 3270Sstevel@tonic-gate * If we're not in the range of scratch addresses, we're not actually 3280Sstevel@tonic-gate * tracing user instructions so turn off the flags. 3290Sstevel@tonic-gate */ 3300Sstevel@tonic-gate if (rp->r_pc < t->t_dtrace_scrpc || 3310Sstevel@tonic-gate rp->r_pc > t->t_dtrace_astpc + isz) { 3320Sstevel@tonic-gate t->t_dtrace_ft = 0; 3330Sstevel@tonic-gate return (0); 3340Sstevel@tonic-gate } 3350Sstevel@tonic-gate 3360Sstevel@tonic-gate /* 3370Sstevel@tonic-gate * If we've executed the original instruction, but haven't performed 3380Sstevel@tonic-gate * the jmp back to t->t_dtrace_npc or the clean up of any registers 3390Sstevel@tonic-gate * used to emulate %rip-relative instructions in 64-bit mode, do that 3400Sstevel@tonic-gate * here and take the signal right away. We detect this condition by 3410Sstevel@tonic-gate * seeing if the program counter is the range [scrpc + isz, astpc). 3420Sstevel@tonic-gate */ 3430Sstevel@tonic-gate if (t->t_dtrace_astpc - rp->r_pc < 3440Sstevel@tonic-gate t->t_dtrace_astpc - t->t_dtrace_scrpc - isz) { 3450Sstevel@tonic-gate #ifdef __amd64 3460Sstevel@tonic-gate /* 3470Sstevel@tonic-gate * If there is a scratch register and we're on the 3480Sstevel@tonic-gate * instruction immediately after the modified instruction, 3490Sstevel@tonic-gate * restore the value of that scratch register. 3500Sstevel@tonic-gate */ 3510Sstevel@tonic-gate if (t->t_dtrace_reg != 0 && 3520Sstevel@tonic-gate rp->r_pc == t->t_dtrace_scrpc + isz) { 3530Sstevel@tonic-gate switch (t->t_dtrace_reg) { 3540Sstevel@tonic-gate case REG_RAX: 3550Sstevel@tonic-gate rp->r_rax = t->t_dtrace_regv; 3560Sstevel@tonic-gate break; 3570Sstevel@tonic-gate case REG_RCX: 3580Sstevel@tonic-gate rp->r_rcx = t->t_dtrace_regv; 3590Sstevel@tonic-gate break; 3600Sstevel@tonic-gate case REG_R8: 3610Sstevel@tonic-gate rp->r_r8 = t->t_dtrace_regv; 3620Sstevel@tonic-gate break; 3630Sstevel@tonic-gate case REG_R9: 3640Sstevel@tonic-gate rp->r_r9 = t->t_dtrace_regv; 3650Sstevel@tonic-gate break; 3660Sstevel@tonic-gate } 3670Sstevel@tonic-gate } 3680Sstevel@tonic-gate #endif 3690Sstevel@tonic-gate rp->r_pc = t->t_dtrace_npc; 3700Sstevel@tonic-gate t->t_dtrace_ft = 0; 3710Sstevel@tonic-gate return (0); 3720Sstevel@tonic-gate } 3730Sstevel@tonic-gate 3740Sstevel@tonic-gate /* 3750Sstevel@tonic-gate * Otherwise, make sure we'll return to the kernel after executing 3760Sstevel@tonic-gate * the copied out instruction and defer the signal. 3770Sstevel@tonic-gate */ 3780Sstevel@tonic-gate if (!t->t_dtrace_step) { 3790Sstevel@tonic-gate ASSERT(rp->r_pc < t->t_dtrace_astpc); 3800Sstevel@tonic-gate rp->r_pc += t->t_dtrace_astpc - t->t_dtrace_scrpc; 3810Sstevel@tonic-gate t->t_dtrace_step = 1; 3820Sstevel@tonic-gate } 3830Sstevel@tonic-gate 3840Sstevel@tonic-gate t->t_dtrace_ast = 1; 3850Sstevel@tonic-gate 3860Sstevel@tonic-gate return (1); 3870Sstevel@tonic-gate } 3883446Smrj 3893446Smrj /* 3903446Smrj * Additional artificial frames for the machine type. For i86pc, we're already 3913446Smrj * accounted for, so return 0. 3923446Smrj */ 3933446Smrj int 3943446Smrj dtrace_mach_aframes(void) 3953446Smrj { 3963446Smrj return (0); 3973446Smrj } 398