/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* #pragma ident "@(#)sdt.c 1.9 08/07/01 SMI" */ #ifdef KERNEL #ifndef _KERNEL #define _KERNEL /* Solaris vs. Darwin */ #endif #endif #define MACH__POSIX_C_SOURCE_PRIVATE 1 /* pulls in suitable savearea from mach/ppc/thread_status.h */ #include #include #include #include #include #include #include #include extern sdt_probe_t **sdt_probetab; #if defined(__i386__) /*ARGSUSED*/ int sdt_invop(uintptr_t addr, uintptr_t *stack, uintptr_t eax) { #pragma unused(eax) uintptr_t stack0 = 0, stack1 = 0, stack2 = 0, stack3 = 0, stack4 = 0; sdt_probe_t *sdt = sdt_probetab[SDT_ADDR2NDX(addr)]; for (; sdt != NULL; sdt = sdt->sdp_hashnext) { if ((uintptr_t)sdt->sdp_patchpoint == addr) { uintptr_t *stacktop; if (CPU_ON_INTR(CPU)) stacktop = (uintptr_t *)dtrace_get_cpu_int_stack_top(); else stacktop = (uintptr_t *)(dtrace_get_kernel_stack(current_thread()) + kernel_stack_size); if (stack <= stacktop) stack0 = *stack++; if (stack <= stacktop) stack1 = *stack++; if (stack <= stacktop) stack2 = *stack++; if (stack <= stacktop) stack3 = *stack++; if (stack <= stacktop) stack4 = *stack++; dtrace_probe(sdt->sdp_id, stack0, stack1, stack2, stack3, stack4); return (DTRACE_INVOP_NOP); } } return (0); } #elif defined(__x86_64__) /*ARGSUSED*/ int sdt_invop(uintptr_t addr, uintptr_t *stack, uintptr_t eax) { #pragma unused(eax) sdt_probe_t *sdt = sdt_probetab[SDT_ADDR2NDX(addr)]; for (; sdt != NULL; sdt = sdt->sdp_hashnext) { if ((uintptr_t)sdt->sdp_patchpoint == addr) { x86_saved_state64_t *regs = (x86_saved_state64_t *)stack; dtrace_probe(sdt->sdp_id, regs->rdi, regs->rsi, regs->rdx, regs->rcx, regs->r8); return (DTRACE_INVOP_NOP); } } return (0); } #else #error Unknown arch #endif struct frame { struct frame *backchain; uintptr_t retaddr; }; /*ARGSUSED*/ uint64_t sdt_getarg(void *arg, dtrace_id_t id, void *parg, int argno, int aframes) { #pragma unused(arg, id, parg) uint64_t val; struct frame *fp = (struct frame *)__builtin_frame_address(0); uintptr_t *stack; uintptr_t pc; int i; #if defined(__x86_64__) /* * A total of 6 arguments are passed via registers; any argument with * index of 5 or lower is therefore in a register. */ int inreg = 5; #endif for (i = 1; i <= aframes; i++) { fp = fp->backchain; pc = fp->retaddr; if (dtrace_invop_callsite_pre != NULL && pc > (uintptr_t)dtrace_invop_callsite_pre && pc <= (uintptr_t)dtrace_invop_callsite_post) { #if defined(__i386__) /* * If we pass through the invalid op handler, we will * use the pointer that it passed to the stack as the * second argument to dtrace_invop() as the pointer to * the frame we're hunting for. */ stack = (uintptr_t *)&fp[1]; /* Find marshalled arguments */ fp = (struct frame *)stack[1]; /* Grab *second* argument */ stack = (uintptr_t *)&fp[0]; /* Find marshalled arguments */ #elif defined(__x86_64__) /* * In the case of x86_64, we will use the pointer to the * save area structure that was pushed when we took the * trap. To get this structure, we must increment * beyond the frame structure. If the * argument that we're seeking is passed on the stack, * we'll pull the true stack pointer out of the saved * registers and decrement our argument by the number * of arguments passed in registers; if the argument * we're seeking is passed in regsiters, we can just * load it directly. */ /* fp points to frame of dtrace_invop() activation. */ fp = fp->backchain; /* to fbt_perfcallback() activation. */ fp = fp->backchain; /* to kernel_trap() activation. */ fp = fp->backchain; /* to trap_from_kernel() activation. */ x86_saved_state_t *tagged_regs = (x86_saved_state_t *)&fp[1]; x86_saved_state64_t *saved_state = saved_state64(tagged_regs); if (argno <= inreg) { stack = (uintptr_t *)&saved_state->rdi; } else { fp = (struct frame *)(saved_state->isf.rsp); stack = (uintptr_t *)&fp[0]; /* Find marshalled arguments */ argno -= (inreg +1); } #else #error Unknown arch #endif goto load; } } /* * We know that we did not come through a trap to get into * dtrace_probe() -- We arrive here when the provider has * called dtrace_probe() directly. * The probe ID is the first argument to dtrace_probe(). * We must advance beyond that to get the argX. */ argno++; /* Advance past probeID */ #if defined(__x86_64__) if (argno <= inreg) { /* * This shouldn't happen. If the argument is passed in a * register then it should have been, well, passed in a * register... */ DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP); return (0); } argno -= (inreg + 1); #endif stack = (uintptr_t *)&fp[1]; /* Find marshalled arguments */ load: DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); /* dtrace_probe arguments arg0 ... arg4 are 64bits wide */ val = (uint64_t)(*(((uintptr_t *)stack) + argno)); DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT); return (val); }