Path: blob/main/sys/cddl/dev/dtrace/powerpc/dtrace_isa.c
48375 views
/*1* CDDL HEADER START2*3* The contents of this file are subject to the terms of the4* Common Development and Distribution License, Version 1.0 only5* (the "License"). You may not use this file except in compliance6* with the License.7*8* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE9* or http://www.opensolaris.org/os/licensing.10* See the License for the specific language governing permissions11* and limitations under the License.12*13* When distributing Covered Code, include this CDDL HEADER in each14* file and include the License file at usr/src/OPENSOLARIS.LICENSE.15* If applicable, add the following below this CDDL HEADER, with the16* fields enclosed by brackets "[]" replaced with your own identifying17* information: Portions Copyright [yyyy] [name of copyright owner]18*19* CDDL HEADER END20*21* Portions Copyright 2012,2013 Justin Hibbits <[email protected]>22*/23/*24* Copyright 2005 Sun Microsystems, Inc. All rights reserved.25* Use is subject to license terms.26*/27#include <sys/cdefs.h>2829#include <sys/param.h>30#include <sys/systm.h>31#include <sys/dtrace_impl.h>32#include <sys/kernel.h>33#include <sys/stack.h>34#include <sys/sysent.h>35#include <sys/pcpu.h>3637#include <machine/frame.h>38#include <machine/md_var.h>39#include <machine/psl.h>40#include <machine/reg.h>41#include <machine/stack.h>4243#include <vm/vm.h>44#include <vm/vm_param.h>45#include <vm/pmap.h>4647#include "regset.h"4849/* Offset to the LR Save word (ppc32) */50#define RETURN_OFFSET 451/* Offset to LR Save word (ppc64). CR Save area sits between back chain and LR */52#define RETURN_OFFSET64 165354#ifdef __powerpc64__55#define OFFSET 4 /* Account for the TOC reload slot */56#define FRAME_OFFSET 4857#else58#define OFFSET 059#define FRAME_OFFSET 860#endif6162#define INKERNEL(x) (((x) <= VM_MAX_KERNEL_ADDRESS && \63(x) >= VM_MIN_KERNEL_ADDRESS) || \64(PMAP_HAS_DMAP && (x) >= DMAP_BASE_ADDRESS && \65(x) <= DMAP_MAX_ADDRESS))6667static __inline int68dtrace_sp_inkernel(uintptr_t sp)69{70struct trapframe *frame;71vm_offset_t callpc;7273/* Not within the kernel, or not aligned. */74if (!INKERNEL(sp) || (sp & 0xf) != 0)75return (0);76#ifdef __powerpc64__77callpc = *(vm_offset_t *)(sp + RETURN_OFFSET64);78#else79callpc = *(vm_offset_t *)(sp + RETURN_OFFSET);80#endif81if ((callpc & 3) || (callpc < 0x100))82return (0);8384/*85* trapexit() and asttrapexit() are sentinels86* for kernel stack tracing.87*/88if (callpc + OFFSET == (vm_offset_t) &trapexit ||89callpc + OFFSET == (vm_offset_t) &asttrapexit) {90frame = (struct trapframe *)(sp + FRAME_OFFSET);9192return ((frame->srr1 & PSL_PR) == 0);93}9495return (1);96}9798static __inline void99dtrace_next_sp_pc(uintptr_t sp, uintptr_t *nsp, uintptr_t *pc, uintptr_t *lr)100{101vm_offset_t callpc;102struct trapframe *frame;103104if (lr != 0 && *lr != 0)105callpc = *lr;106else107#ifdef __powerpc64__108callpc = *(vm_offset_t *)(sp + RETURN_OFFSET64);109#else110callpc = *(vm_offset_t *)(sp + RETURN_OFFSET);111#endif112113/*114* trapexit() and asttrapexit() are sentinels115* for kernel stack tracing.116*/117if ((callpc + OFFSET == (vm_offset_t) &trapexit ||118callpc + OFFSET == (vm_offset_t) &asttrapexit)) {119/* Access the trap frame */120frame = (struct trapframe *)(sp + FRAME_OFFSET);121122if (nsp != NULL)123*nsp = frame->fixreg[1];124if (pc != NULL)125*pc = frame->srr0;126if (lr != NULL)127*lr = frame->lr;128return;129}130131if (nsp != NULL)132*nsp = *(uintptr_t *)sp;133if (pc != NULL)134*pc = callpc;135/* lr is only valid for trap frames */136if (lr != NULL)137*lr = 0;138}139140void141dtrace_getpcstack(pc_t *pcstack, int pcstack_limit, int aframes,142uint32_t *intrpc)143{144int depth = 0;145uintptr_t osp, sp, lr = 0;146vm_offset_t callpc;147pc_t caller = (pc_t) solaris_cpu[curcpu].cpu_dtrace_caller;148149osp = PAGE_SIZE;150if (intrpc != 0)151pcstack[depth++] = (pc_t) intrpc;152153aframes++;154155sp = (uintptr_t)__builtin_frame_address(0);156157while (depth < pcstack_limit) {158if (sp <= osp)159break;160161if (!dtrace_sp_inkernel(sp))162break;163osp = sp;164dtrace_next_sp_pc(osp, &sp, &callpc, &lr);165166if (aframes > 0) {167aframes--;168if ((aframes == 0) && (caller != 0)) {169pcstack[depth++] = caller;170}171}172else {173pcstack[depth++] = callpc;174}175}176177for (; depth < pcstack_limit; depth++) {178pcstack[depth] = 0;179}180}181182static int183dtrace_getustack_common(uint64_t *pcstack, int pcstack_limit, uintptr_t pc,184uintptr_t sp)185{186proc_t *p = curproc;187int ret = 0;188189ASSERT(pcstack == NULL || pcstack_limit > 0);190191while (pc != 0) {192ret++;193if (pcstack != NULL) {194*pcstack++ = (uint64_t)pc;195pcstack_limit--;196if (pcstack_limit <= 0)197break;198}199200if (sp == 0)201break;202203if (SV_PROC_FLAG(p, SV_ILP32)) {204pc = dtrace_fuword32((void *)(sp + RETURN_OFFSET));205sp = dtrace_fuword32((void *)sp);206}207else {208pc = dtrace_fuword64((void *)(sp + RETURN_OFFSET64));209sp = dtrace_fuword64((void *)sp);210}211}212213return (ret);214}215216void217dtrace_getupcstack(uint64_t *pcstack, int pcstack_limit)218{219proc_t *p = curproc;220struct trapframe *tf;221uintptr_t pc, sp;222volatile uint16_t *flags =223(volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags;224int n;225226if (*flags & CPU_DTRACE_FAULT)227return;228229if (pcstack_limit <= 0)230return;231232/*233* If there's no user context we still need to zero the stack.234*/235if (p == NULL || (tf = curthread->td_frame) == NULL)236goto zero;237238*pcstack++ = (uint64_t)p->p_pid;239pcstack_limit--;240241if (pcstack_limit <= 0)242return;243244pc = tf->srr0;245sp = tf->fixreg[1];246247if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_ENTRY)) {248/*249* In an entry probe. The frame pointer has not yet been250* pushed (that happens in the function prologue). The251* best approach is to add the current pc as a missing top252* of stack and back the pc up to the caller, which is stored253* at the current stack pointer address since the call254* instruction puts it there right before the branch.255*/256257*pcstack++ = (uint64_t)pc;258pcstack_limit--;259if (pcstack_limit <= 0)260return;261262pc = tf->lr;263}264265n = dtrace_getustack_common(pcstack, pcstack_limit, pc, sp);266ASSERT(n >= 0);267ASSERT(n <= pcstack_limit);268269pcstack += n;270pcstack_limit -= n;271272zero:273while (pcstack_limit-- > 0)274*pcstack++ = 0;275}276277int278dtrace_getustackdepth(void)279{280proc_t *p = curproc;281struct trapframe *tf;282uintptr_t pc, sp;283int n = 0;284285if (p == NULL || (tf = curthread->td_frame) == NULL)286return (0);287288if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_FAULT))289return (-1);290291pc = tf->srr0;292sp = tf->fixreg[1];293294if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_ENTRY)) {295/*296* In an entry probe. The frame pointer has not yet been297* pushed (that happens in the function prologue). The298* best approach is to add the current pc as a missing top299* of stack and back the pc up to the caller, which is stored300* at the current stack pointer address since the call301* instruction puts it there right before the branch.302*/303304if (SV_PROC_FLAG(p, SV_ILP32)) {305pc = dtrace_fuword32((void *) sp);306}307else308pc = dtrace_fuword64((void *) sp);309n++;310}311312n += dtrace_getustack_common(NULL, 0, pc, sp);313314return (n);315}316317void318dtrace_getufpstack(uint64_t *pcstack, uint64_t *fpstack, int pcstack_limit)319{320proc_t *p = curproc;321struct trapframe *tf;322uintptr_t pc, sp;323volatile uint16_t *flags =324(volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags;325#ifdef notyet /* XXX signal stack */326uintptr_t oldcontext;327size_t s1, s2;328#endif329330if (*flags & CPU_DTRACE_FAULT)331return;332333if (pcstack_limit <= 0)334return;335336/*337* If there's no user context we still need to zero the stack.338*/339if (p == NULL || (tf = curthread->td_frame) == NULL)340goto zero;341342*pcstack++ = (uint64_t)p->p_pid;343pcstack_limit--;344345if (pcstack_limit <= 0)346return;347348pc = tf->srr0;349sp = tf->fixreg[1];350351#ifdef notyet /* XXX signal stack */352oldcontext = lwp->lwp_oldcontext;353s1 = sizeof (struct xframe) + 2 * sizeof (long);354s2 = s1 + sizeof (siginfo_t);355#endif356357if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_ENTRY)) {358*pcstack++ = (uint64_t)pc;359*fpstack++ = 0;360pcstack_limit--;361if (pcstack_limit <= 0)362return;363364if (SV_PROC_FLAG(p, SV_ILP32)) {365pc = dtrace_fuword32((void *)sp);366}367else {368pc = dtrace_fuword64((void *)sp);369}370}371372while (pc != 0) {373*pcstack++ = (uint64_t)pc;374*fpstack++ = sp;375pcstack_limit--;376if (pcstack_limit <= 0)377break;378379if (sp == 0)380break;381382#ifdef notyet /* XXX signal stack */383if (oldcontext == sp + s1 || oldcontext == sp + s2) {384ucontext_t *ucp = (ucontext_t *)oldcontext;385greg_t *gregs = ucp->uc_mcontext.gregs;386387sp = dtrace_fulword(&gregs[REG_FP]);388pc = dtrace_fulword(&gregs[REG_PC]);389390oldcontext = dtrace_fulword(&ucp->uc_link);391} else392#endif /* XXX */393{394if (SV_PROC_FLAG(p, SV_ILP32)) {395pc = dtrace_fuword32((void *)(sp + RETURN_OFFSET));396sp = dtrace_fuword32((void *)sp);397}398else {399pc = dtrace_fuword64((void *)(sp + RETURN_OFFSET64));400sp = dtrace_fuword64((void *)sp);401}402}403404/*405* This is totally bogus: if we faulted, we're going to clear406* the fault and break. This is to deal with the apparently407* broken Java stacks on x86.408*/409if (*flags & CPU_DTRACE_FAULT) {410*flags &= ~CPU_DTRACE_FAULT;411break;412}413}414415zero:416while (pcstack_limit-- > 0)417*pcstack++ = 0;418}419420/*ARGSUSED*/421uint64_t422dtrace_getarg(int arg, int aframes)423{424uintptr_t val;425uintptr_t *fp = (uintptr_t *)__builtin_frame_address(0);426uintptr_t *stack;427int i;428429/*430* A total of 8 arguments are passed via registers; any argument with431* index of 7 or lower is therefore in a register.432*/433int inreg = 7;434435for (i = 1; i <= aframes; i++) {436fp = (uintptr_t *)*fp;437438/*439* On ppc32 trapexit() is the immediately following label. On440* ppc64 AIM trapexit() follows a nop.441*/442#ifdef __powerpc64__443if ((long)(fp[2]) + 4 == (long)trapexit) {444#else445if ((long)(fp[1]) == (long)trapexit) {446#endif447/*448* In the case of powerpc, we will use the pointer to the regs449* structure that was pushed when we took the trap. To get this450* structure, we must increment beyond the frame structure. If the451* argument that we're seeking is passed on the stack, we'll pull452* the true stack pointer out of the saved registers and decrement453* our argument by the number of arguments passed in registers; if454* the argument we're seeking is passed in regsiters, we can just455* load it directly.456*/457#ifdef __powerpc64__458struct reg *rp = (struct reg *)((uintptr_t)fp[0] + 48);459#else460struct reg *rp = (struct reg *)((uintptr_t)fp[0] + 8);461#endif462463if (arg <= inreg) {464stack = &rp->fixreg[3];465} else {466stack = (uintptr_t *)(rp->fixreg[1]);467arg -= inreg;468}469goto load;470}471472}473474/*475* We know that we did not come through a trap to get into476* dtrace_probe() -- the provider simply called dtrace_probe()477* directly. As this is the case, we need to shift the argument478* that we're looking for: the probe ID is the first argument to479* dtrace_probe(), so the argument n will actually be found where480* one would expect to find argument (n + 1).481*/482arg++;483484if (arg <= inreg) {485/*486* This shouldn't happen. If the argument is passed in a487* register then it should have been, well, passed in a488* register...489*/490DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP);491return (0);492}493494arg -= (inreg + 1);495stack = fp + 2;496497load:498DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);499val = stack[arg];500DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT);501502return (val);503}504505int506dtrace_getstackdepth(int aframes)507{508int depth = 0;509uintptr_t osp, sp;510vm_offset_t callpc;511512osp = PAGE_SIZE;513sp = (uintptr_t)__builtin_frame_address(0);514for(;;) {515if (sp <= osp)516break;517518if (!dtrace_sp_inkernel(sp))519break;520521depth++;522osp = sp;523dtrace_next_sp_pc(sp, &sp, NULL, NULL);524}525if (depth < aframes)526return (0);527528return (depth - aframes);529}530531ulong_t532dtrace_getreg(struct trapframe *frame, uint_t reg)533{534if (reg < 32)535return (frame->fixreg[reg]);536537switch (reg) {538case 32:539return (frame->lr);540case 33:541return (frame->cr);542case 34:543return (frame->xer);544case 35:545return (frame->ctr);546case 36:547return (frame->srr0);548case 37:549return (frame->srr1);550case 38:551return (frame->exc);552default:553DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP);554return (0);555}556}557558static int559dtrace_copycheck(uintptr_t uaddr, uintptr_t kaddr, size_t size)560{561ASSERT(INKERNEL(kaddr) && kaddr + size >= kaddr);562563if (uaddr + size > VM_MAXUSER_ADDRESS || uaddr + size < uaddr) {564DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);565cpu_core[curcpu].cpuc_dtrace_illval = uaddr;566return (0);567}568569return (1);570}571572void573dtrace_copyin(uintptr_t uaddr, uintptr_t kaddr, size_t size,574volatile uint16_t *flags)575{576if (dtrace_copycheck(uaddr, kaddr, size))577if (copyin((const void *)uaddr, (void *)kaddr, size)) {578DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);579cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;580}581}582583void584dtrace_copyout(uintptr_t kaddr, uintptr_t uaddr, size_t size,585volatile uint16_t *flags)586{587if (dtrace_copycheck(uaddr, kaddr, size)) {588if (copyout((const void *)kaddr, (void *)uaddr, size)) {589DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);590cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;591}592}593}594595void596dtrace_copyinstr(uintptr_t uaddr, uintptr_t kaddr, size_t size,597volatile uint16_t *flags)598{599size_t actual;600int error;601602if (dtrace_copycheck(uaddr, kaddr, size)) {603error = copyinstr((const void *)uaddr, (void *)kaddr,604size, &actual);605606/* ENAMETOOLONG is not a fault condition. */607if (error && error != ENAMETOOLONG) {608DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);609cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;610}611}612}613614/*615* The bulk of this function could be replaced to match dtrace_copyinstr()616* if we ever implement a copyoutstr().617*/618void619dtrace_copyoutstr(uintptr_t kaddr, uintptr_t uaddr, size_t size,620volatile uint16_t *flags)621{622size_t len;623624if (dtrace_copycheck(uaddr, kaddr, size)) {625len = strlen((const char *)kaddr);626if (len > size)627len = size;628629if (copyout((const void *)kaddr, (void *)uaddr, len)) {630DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);631cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;632}633}634}635636uint8_t637dtrace_fuword8(void *uaddr)638{639if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) {640DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);641cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;642return (0);643}644return (fubyte(uaddr));645}646647uint16_t648dtrace_fuword16(void *uaddr)649{650uint16_t ret = 0;651652if (dtrace_copycheck((uintptr_t)uaddr, (uintptr_t)&ret, sizeof(ret))) {653if (copyin((const void *)uaddr, (void *)&ret, sizeof(ret))) {654DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);655cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;656}657}658return ret;659}660661uint32_t662dtrace_fuword32(void *uaddr)663{664if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) {665DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);666cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;667return (0);668}669return (fuword32(uaddr));670}671672uint64_t673dtrace_fuword64(void *uaddr)674{675uint64_t ret = 0;676677if (dtrace_copycheck((uintptr_t)uaddr, (uintptr_t)&ret, sizeof(ret))) {678if (copyin((const void *)uaddr, (void *)&ret, sizeof(ret))) {679DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);680cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;681}682}683return ret;684}685686uintptr_t687dtrace_fulword(void *uaddr)688{689uintptr_t ret = 0;690691if (dtrace_copycheck((uintptr_t)uaddr, (uintptr_t)&ret, sizeof(ret))) {692if (copyin((const void *)uaddr, (void *)&ret, sizeof(ret))) {693DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);694cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;695}696}697return ret;698}699700701