Path: blob/main/sys/cddl/dev/dtrace/amd64/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/*22* Copyright 2005 Sun Microsystems, Inc. All rights reserved.23* Use is subject to license terms.24*/25#include <sys/cdefs.h>2627#include <sys/param.h>28#include <sys/systm.h>29#include <sys/dtrace_impl.h>30#include <sys/kernel.h>31#include <sys/msan.h>32#include <sys/stack.h>33#include <sys/pcpu.h>3435#include <cddl/dev/dtrace/dtrace_cddl.h>3637#include <machine/frame.h>38#include <machine/md_var.h>39#include <machine/stack.h>40#include <x86/ifunc.h>4142#include <vm/vm.h>43#include <vm/vm_param.h>44#include <vm/pmap.h>4546#include "regset.h"4748uint8_t dtrace_fuword8_nocheck(void *);49uint16_t dtrace_fuword16_nocheck(void *);50uint32_t dtrace_fuword32_nocheck(void *);51uint64_t dtrace_fuword64_nocheck(void *);5253int dtrace_ustackdepth_max = 2048;5455void56dtrace_getpcstack(pc_t *pcstack, int pcstack_limit, int aframes,57uint32_t *intrpc)58{59struct thread *td;60int depth = 0;61register_t rbp;62struct amd64_frame *frame;63vm_offset_t callpc;64pc_t caller = (pc_t) solaris_cpu[curcpu].cpu_dtrace_caller;6566if (intrpc != 0)67pcstack[depth++] = (pc_t) intrpc;6869aframes++;7071__asm __volatile("movq %%rbp,%0" : "=r" (rbp));7273frame = (struct amd64_frame *)rbp;74td = curthread;75while (depth < pcstack_limit) {76kmsan_mark(frame, sizeof(*frame), KMSAN_STATE_INITED);7778if (!kstack_contains(curthread, (vm_offset_t)frame,79sizeof(*frame)))80break;8182callpc = frame->f_retaddr;8384if (!INKERNEL(callpc))85break;8687if (aframes > 0) {88aframes--;89if ((aframes == 0) && (caller != 0)) {90pcstack[depth++] = caller;91}92} else {93pcstack[depth++] = callpc;94}9596if ((vm_offset_t)frame->f_frame <= (vm_offset_t)frame)97break;98frame = frame->f_frame;99}100101for (; depth < pcstack_limit; depth++) {102pcstack[depth] = 0;103}104kmsan_check(pcstack, pcstack_limit * sizeof(*pcstack), "dtrace");105}106107static int108dtrace_getustack_common(uint64_t *pcstack, int pcstack_limit, uintptr_t pc,109uintptr_t sp)110{111uintptr_t oldsp;112volatile uint16_t *flags =113(volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags;114int ret = 0;115116ASSERT(pcstack == NULL || pcstack_limit > 0);117ASSERT(dtrace_ustackdepth_max > 0);118119while (pc != 0) {120/*121* We limit the number of times we can go around this122* loop to account for a circular stack.123*/124if (ret++ >= dtrace_ustackdepth_max) {125*flags |= CPU_DTRACE_BADSTACK;126cpu_core[curcpu].cpuc_dtrace_illval = sp;127break;128}129130if (pcstack != NULL) {131*pcstack++ = (uint64_t)pc;132pcstack_limit--;133if (pcstack_limit <= 0)134break;135}136137if (sp == 0)138break;139140oldsp = sp;141142pc = dtrace_fuword64((void *)(sp +143offsetof(struct amd64_frame, f_retaddr)));144sp = dtrace_fuword64((void *)sp);145146if (sp == oldsp) {147*flags |= CPU_DTRACE_BADSTACK;148cpu_core[curcpu].cpuc_dtrace_illval = sp;149break;150}151152/*153* This is totally bogus: if we faulted, we're going to clear154* the fault and break. This is to deal with the apparently155* broken Java stacks on x86.156*/157if (*flags & CPU_DTRACE_FAULT) {158*flags &= ~CPU_DTRACE_FAULT;159break;160}161}162163return (ret);164}165166void167dtrace_getupcstack(uint64_t *pcstack, int pcstack_limit)168{169proc_t *p = curproc;170struct trapframe *tf;171uintptr_t pc, sp, fp;172volatile uint16_t *flags =173(volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags;174int n;175176if (*flags & CPU_DTRACE_FAULT)177return;178179if (pcstack_limit <= 0)180return;181182/*183* If there's no user context we still need to zero the stack.184*/185if (p == NULL || (tf = curthread->td_frame) == NULL)186goto zero;187188*pcstack++ = (uint64_t)p->p_pid;189pcstack_limit--;190191if (pcstack_limit <= 0)192return;193194pc = tf->tf_rip;195fp = tf->tf_rbp;196sp = tf->tf_rsp;197198if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_ENTRY)) {199/*200* In an entry probe. The frame pointer has not yet been201* pushed (that happens in the function prologue). The202* best approach is to add the current pc as a missing top203* of stack and back the pc up to the caller, which is stored204* at the current stack pointer address since the call205* instruction puts it there right before the branch.206*/207208*pcstack++ = (uint64_t)pc;209pcstack_limit--;210if (pcstack_limit <= 0)211return;212213pc = dtrace_fuword64((void *) sp);214}215216n = dtrace_getustack_common(pcstack, pcstack_limit, pc, fp);217ASSERT(n >= 0);218ASSERT(n <= pcstack_limit);219220pcstack += n;221pcstack_limit -= n;222223zero:224while (pcstack_limit-- > 0)225*pcstack++ = 0;226}227228int229dtrace_getustackdepth(void)230{231proc_t *p = curproc;232struct trapframe *tf;233uintptr_t pc, fp, sp;234int n = 0;235236if (p == NULL || (tf = curthread->td_frame) == NULL)237return (0);238239if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_FAULT))240return (-1);241242pc = tf->tf_rip;243fp = tf->tf_rbp;244sp = tf->tf_rsp;245246if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_ENTRY)) {247/*248* In an entry probe. The frame pointer has not yet been249* pushed (that happens in the function prologue). The250* best approach is to add the current pc as a missing top251* of stack and back the pc up to the caller, which is stored252* at the current stack pointer address since the call253* instruction puts it there right before the branch.254*/255256pc = dtrace_fuword64((void *) sp);257n++;258}259260n += dtrace_getustack_common(NULL, 0, pc, fp);261262return (n);263}264265void266dtrace_getufpstack(uint64_t *pcstack, uint64_t *fpstack, int pcstack_limit)267{268proc_t *p = curproc;269struct trapframe *tf;270uintptr_t pc, sp, fp;271volatile uint16_t *flags =272(volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags;273#ifdef notyet /* XXX signal stack */274uintptr_t oldcontext;275size_t s1, s2;276#endif277278if (*flags & CPU_DTRACE_FAULT)279return;280281if (pcstack_limit <= 0)282return;283284/*285* If there's no user context we still need to zero the stack.286*/287if (p == NULL || (tf = curthread->td_frame) == NULL)288goto zero;289290*pcstack++ = (uint64_t)p->p_pid;291pcstack_limit--;292293if (pcstack_limit <= 0)294return;295296pc = tf->tf_rip;297sp = tf->tf_rsp;298fp = tf->tf_rbp;299300#ifdef notyet /* XXX signal stack */301oldcontext = lwp->lwp_oldcontext;302s1 = sizeof (struct xframe) + 2 * sizeof (long);303s2 = s1 + sizeof (siginfo_t);304#endif305306if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_ENTRY)) {307*pcstack++ = (uint64_t)pc;308*fpstack++ = 0;309pcstack_limit--;310if (pcstack_limit <= 0)311return;312313pc = dtrace_fuword64((void *)sp);314}315316while (pc != 0) {317*pcstack++ = (uint64_t)pc;318*fpstack++ = fp;319pcstack_limit--;320if (pcstack_limit <= 0)321break;322323if (fp == 0)324break;325326#ifdef notyet /* XXX signal stack */327if (oldcontext == sp + s1 || oldcontext == sp + s2) {328ucontext_t *ucp = (ucontext_t *)oldcontext;329greg_t *gregs = ucp->uc_mcontext.gregs;330331sp = dtrace_fulword(&gregs[REG_FP]);332pc = dtrace_fulword(&gregs[REG_PC]);333334oldcontext = dtrace_fulword(&ucp->uc_link);335} else336#endif /* XXX */337{338pc = dtrace_fuword64((void *)(fp +339offsetof(struct amd64_frame, f_retaddr)));340fp = dtrace_fuword64((void *)fp);341}342343/*344* This is totally bogus: if we faulted, we're going to clear345* the fault and break. This is to deal with the apparently346* broken Java stacks on x86.347*/348if (*flags & CPU_DTRACE_FAULT) {349*flags &= ~CPU_DTRACE_FAULT;350break;351}352}353354zero:355while (pcstack_limit-- > 0)356*pcstack++ = 0;357}358359/*ARGSUSED*/360uint64_t361dtrace_getarg(int arg, int aframes)362{363struct thread *td;364uintptr_t val;365struct amd64_frame *fp = (struct amd64_frame *)dtrace_getfp();366uintptr_t *stack;367int i;368369/*370* A total of 6 arguments are passed via registers; any argument with371* index of 5 or lower is therefore in a register.372*/373int inreg = 5;374375/*376* Did we arrive here via dtrace_invop()? We can simply fetch arguments377* from the trap frame if so.378*/379td = curthread;380if (td->t_dtrace_trapframe != NULL) {381struct trapframe *tf = td->t_dtrace_trapframe;382383if (arg <= inreg) {384switch (arg) {385case 0:386return (tf->tf_rdi);387case 1:388return (tf->tf_rsi);389case 2:390return (tf->tf_rdx);391case 3:392return (tf->tf_rcx);393case 4:394return (tf->tf_r8);395case 5:396return (tf->tf_r9);397}398}399400arg -= inreg;401stack = (uintptr_t *)tf->tf_rsp;402goto load;403}404405for (i = 1; i <= aframes; i++) {406kmsan_mark(fp, sizeof(*fp), KMSAN_STATE_INITED);407fp = fp->f_frame;408}409410/*411* We know that we did not come through a trap to get into412* dtrace_probe() -- the provider simply called dtrace_probe()413* directly. As this is the case, we need to shift the argument414* that we're looking for: the probe ID is the first argument to415* dtrace_probe(), so the argument n will actually be found where416* one would expect to find argument (n + 1).417*/418arg++;419420if (arg <= inreg) {421/*422* This shouldn't happen. If the argument is passed in a423* register then it should have been, well, passed in a424* register...425*/426DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP);427return (0);428}429430arg -= (inreg + 1);431stack = (uintptr_t *)&fp[1];432433load:434DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);435val = stack[arg];436DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT);437438kmsan_mark(&val, sizeof(val), KMSAN_STATE_INITED);439440return (val);441}442443int444dtrace_getstackdepth(int aframes)445{446int depth = 0;447struct amd64_frame *frame;448vm_offset_t rbp;449450aframes++;451rbp = dtrace_getfp();452frame = (struct amd64_frame *)rbp;453depth++;454for (;;) {455kmsan_mark(frame, sizeof(*frame), KMSAN_STATE_INITED);456457if (!kstack_contains(curthread, (vm_offset_t)frame,458sizeof(*frame)))459break;460461depth++;462if (frame->f_frame <= frame)463break;464frame = frame->f_frame;465}466if (depth < aframes)467return 0;468else469return depth - aframes;470}471472ulong_t473dtrace_getreg(struct trapframe *frame, uint_t reg)474{475/* This table is dependent on reg.d. */476int regmap[] = {477REG_GS, /* 0 GS */478REG_FS, /* 1 FS */479REG_ES, /* 2 ES */480REG_DS, /* 3 DS */481REG_RDI, /* 4 EDI */482REG_RSI, /* 5 ESI */483REG_RBP, /* 6 EBP, REG_FP */484REG_RSP, /* 7 ESP */485REG_RBX, /* 8 EBX, REG_R1 */486REG_RDX, /* 9 EDX */487REG_RCX, /* 10 ECX */488REG_RAX, /* 11 EAX, REG_R0 */489REG_TRAPNO, /* 12 TRAPNO */490REG_ERR, /* 13 ERR */491REG_RIP, /* 14 EIP, REG_PC */492REG_CS, /* 15 CS */493REG_RFL, /* 16 EFL, REG_PS */494REG_RSP, /* 17 UESP, REG_SP */495REG_SS /* 18 SS */496};497498if (reg <= GS) {499if (reg >= sizeof (regmap) / sizeof (int)) {500DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP);501return (0);502}503504reg = regmap[reg];505} else {506/* This is dependent on reg.d. */507reg -= GS + 1;508}509510switch (reg) {511case REG_RDI:512return (frame->tf_rdi);513case REG_RSI:514return (frame->tf_rsi);515case REG_RDX:516return (frame->tf_rdx);517case REG_RCX:518return (frame->tf_rcx);519case REG_R8:520return (frame->tf_r8);521case REG_R9:522return (frame->tf_r9);523case REG_RAX:524return (frame->tf_rax);525case REG_RBX:526return (frame->tf_rbx);527case REG_RBP:528return (frame->tf_rbp);529case REG_R10:530return (frame->tf_r10);531case REG_R11:532return (frame->tf_r11);533case REG_R12:534return (frame->tf_r12);535case REG_R13:536return (frame->tf_r13);537case REG_R14:538return (frame->tf_r14);539case REG_R15:540return (frame->tf_r15);541case REG_DS:542return (frame->tf_ds);543case REG_ES:544return (frame->tf_es);545case REG_FS:546return (frame->tf_fs);547case REG_GS:548return (frame->tf_gs);549case REG_TRAPNO:550return (frame->tf_trapno);551case REG_ERR:552return (frame->tf_err);553case REG_RIP:554return (frame->tf_rip);555case REG_CS:556return (frame->tf_cs);557case REG_SS:558return (frame->tf_ss);559case REG_RFL:560return (frame->tf_rflags);561case REG_RSP:562return (frame->tf_rsp);563default:564DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP);565return (0);566}567}568569static int570dtrace_copycheck(uintptr_t uaddr, uintptr_t kaddr, size_t size)571{572ASSERT(INKERNEL(kaddr) && kaddr + size >= kaddr);573574if (uaddr + size > VM_MAXUSER_ADDRESS || uaddr + size < uaddr) {575DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);576cpu_core[curcpu].cpuc_dtrace_illval = uaddr;577return (0);578}579580return (1);581}582583void584dtrace_copyin(uintptr_t uaddr, uintptr_t kaddr, size_t size,585volatile uint16_t *flags)586{587if (dtrace_copycheck(uaddr, kaddr, size)) {588dtrace_copy(uaddr, kaddr, size);589kmsan_mark((void *)kaddr, size, KMSAN_STATE_INITED);590}591}592593void594dtrace_copyout(uintptr_t kaddr, uintptr_t uaddr, size_t size,595volatile uint16_t *flags)596{597if (dtrace_copycheck(uaddr, kaddr, size)) {598kmsan_check((void *)kaddr, size, "dtrace_copyout");599dtrace_copy(kaddr, uaddr, size);600}601}602603void604dtrace_copyinstr(uintptr_t uaddr, uintptr_t kaddr, size_t size,605volatile uint16_t *flags)606{607if (dtrace_copycheck(uaddr, kaddr, size)) {608dtrace_copystr(uaddr, kaddr, size, flags);609kmsan_mark((void *)kaddr, size, KMSAN_STATE_INITED);610}611}612613void614dtrace_copyoutstr(uintptr_t kaddr, uintptr_t uaddr, size_t size,615volatile uint16_t *flags)616{617if (dtrace_copycheck(uaddr, kaddr, size)) {618kmsan_check((void *)kaddr, size, "dtrace_copyoutstr");619dtrace_copystr(kaddr, uaddr, size, flags);620}621}622623uint8_t624dtrace_fuword8(void *uaddr)625{626uint8_t val;627628if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) {629DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);630cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;631return (0);632}633val = dtrace_fuword8_nocheck(uaddr);634kmsan_mark(&val, sizeof(val), KMSAN_STATE_INITED);635return (val);636}637638uint16_t639dtrace_fuword16(void *uaddr)640{641uint16_t val;642643if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) {644DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);645cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;646return (0);647}648val = dtrace_fuword16_nocheck(uaddr);649kmsan_mark(&val, sizeof(val), KMSAN_STATE_INITED);650return (val);651}652653uint32_t654dtrace_fuword32(void *uaddr)655{656uint32_t val;657658if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) {659DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);660cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;661return (0);662}663val = dtrace_fuword32_nocheck(uaddr);664kmsan_mark(&val, sizeof(val), KMSAN_STATE_INITED);665return (val);666}667668uint64_t669dtrace_fuword64(void *uaddr)670{671uint64_t val;672673if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) {674DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);675cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;676return (0);677}678val = dtrace_fuword64_nocheck(uaddr);679kmsan_mark(&val, sizeof(val), KMSAN_STATE_INITED);680return (val);681}682683/*684* ifunc resolvers for SMAP support685*/686void dtrace_copy_nosmap(uintptr_t, uintptr_t, size_t);687void dtrace_copy_smap(uintptr_t, uintptr_t, size_t);688DEFINE_IFUNC(, void, dtrace_copy, (uintptr_t, uintptr_t, size_t))689{690691return ((cpu_stdext_feature & CPUID_STDEXT_SMAP) != 0 ?692dtrace_copy_smap : dtrace_copy_nosmap);693}694695void dtrace_copystr_nosmap(uintptr_t, uintptr_t, size_t, volatile uint16_t *);696void dtrace_copystr_smap(uintptr_t, uintptr_t, size_t, volatile uint16_t *);697DEFINE_IFUNC(, void, dtrace_copystr, (uintptr_t, uintptr_t, size_t,698volatile uint16_t *))699{700701return ((cpu_stdext_feature & CPUID_STDEXT_SMAP) != 0 ?702dtrace_copystr_smap : dtrace_copystr_nosmap);703}704705uintptr_t dtrace_fulword_nosmap(void *);706uintptr_t dtrace_fulword_smap(void *);707DEFINE_IFUNC(, uintptr_t, dtrace_fulword, (void *))708{709710return ((cpu_stdext_feature & CPUID_STDEXT_SMAP) != 0 ?711dtrace_fulword_smap : dtrace_fulword_nosmap);712}713714uint8_t dtrace_fuword8_nocheck_nosmap(void *);715uint8_t dtrace_fuword8_nocheck_smap(void *);716DEFINE_IFUNC(, uint8_t, dtrace_fuword8_nocheck, (void *))717{718719return ((cpu_stdext_feature & CPUID_STDEXT_SMAP) != 0 ?720dtrace_fuword8_nocheck_smap : dtrace_fuword8_nocheck_nosmap);721}722723uint16_t dtrace_fuword16_nocheck_nosmap(void *);724uint16_t dtrace_fuword16_nocheck_smap(void *);725DEFINE_IFUNC(, uint16_t, dtrace_fuword16_nocheck, (void *))726{727728return ((cpu_stdext_feature & CPUID_STDEXT_SMAP) != 0 ?729dtrace_fuword16_nocheck_smap : dtrace_fuword16_nocheck_nosmap);730}731732uint32_t dtrace_fuword32_nocheck_nosmap(void *);733uint32_t dtrace_fuword32_nocheck_smap(void *);734DEFINE_IFUNC(, uint32_t, dtrace_fuword32_nocheck, (void *))735{736737return ((cpu_stdext_feature & CPUID_STDEXT_SMAP) != 0 ?738dtrace_fuword32_nocheck_smap : dtrace_fuword32_nocheck_nosmap);739}740741uint64_t dtrace_fuword64_nocheck_nosmap(void *);742uint64_t dtrace_fuword64_nocheck_smap(void *);743DEFINE_IFUNC(, uint64_t, dtrace_fuword64_nocheck, (void *))744{745746return ((cpu_stdext_feature & CPUID_STDEXT_SMAP) != 0 ?747dtrace_fuword64_nocheck_smap : dtrace_fuword64_nocheck_nosmap);748}749750751