Path: blob/main/sys/cddl/dev/dtrace/riscv/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 2016 Ruslan Bukin <[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/pcpu.h>3536#include <machine/frame.h>37#include <machine/md_var.h>38#include <machine/encoding.h>39#include <machine/riscvreg.h>4041#include <vm/vm.h>42#include <vm/vm_param.h>43#include <vm/pmap.h>4445#include <machine/atomic.h>46#include <machine/db_machdep.h>47#include <machine/md_var.h>48#include <machine/stack.h>49#include <ddb/db_sym.h>50#include <ddb/ddb.h>51#include <sys/kdb.h>5253#include "regset.h"5455#define MAX_USTACK_DEPTH 20485657uint8_t dtrace_fuword8_nocheck(void *);58uint16_t dtrace_fuword16_nocheck(void *);59uint32_t dtrace_fuword32_nocheck(void *);60uint64_t dtrace_fuword64_nocheck(void *);6162int dtrace_match_opcode(uint32_t, int, int);63int dtrace_instr_sdsp(uint32_t **);64int dtrace_instr_ret(uint32_t **);65int dtrace_instr_c_sdsp(uint32_t **);66int dtrace_instr_c_ret(uint32_t **);6768void69dtrace_getpcstack(pc_t *pcstack, int pcstack_limit, int aframes,70uint32_t *intrpc)71{72struct unwind_state state;73uintptr_t caller;74register_t sp;75int scp_offset;76int depth;7778depth = 0;79caller = solaris_cpu[curcpu].cpu_dtrace_caller;8081if (intrpc != 0) {82pcstack[depth++] = (pc_t)intrpc;83}8485/*86* Construct the unwind state, starting from this function. This frame,87* and 'aframes' others will be skipped.88*/89__asm __volatile("mv %0, sp" : "=&r" (sp));9091state.fp = (uintptr_t)__builtin_frame_address(0);92state.sp = (uintptr_t)sp;93state.pc = (uintptr_t)dtrace_getpcstack;9495while (depth < pcstack_limit) {96if (!unwind_frame(curthread, &state))97break;9899if (!INKERNEL(state.pc) || !kstack_contains(curthread,100(vm_offset_t)state.fp, sizeof(uintptr_t)))101break;102103if (aframes > 0) {104aframes--;105106/*107* fbt_invop() records the return address at the time108* the FBT probe fires. We need to insert this into the109* backtrace manually, since the stack frame state at110* the time of the probe does not capture it.111*/112if (aframes == 0 && caller != 0)113pcstack[depth++] = caller;114} else {115pcstack[depth++] = state.pc;116}117}118119for (; depth < pcstack_limit; depth++) {120pcstack[depth] = 0;121}122}123124static int125dtrace_getustack_common(uint64_t *pcstack, int pcstack_limit, uintptr_t pc,126uintptr_t fp)127{128volatile uint16_t *flags;129uintptr_t oldfp;130int ret;131132oldfp = fp;133ret = 0;134flags = (volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags;135136ASSERT(pcstack == NULL || pcstack_limit > 0);137138while (pc != 0) {139/*140* We limit the number of times we can go around this141* loop to account for a circular stack.142*/143if (ret++ >= MAX_USTACK_DEPTH) {144*flags |= CPU_DTRACE_BADSTACK;145cpu_core[curcpu].cpuc_dtrace_illval = fp;146break;147}148149if (pcstack != NULL) {150*pcstack++ = (uint64_t)pc;151pcstack_limit--;152if (pcstack_limit <= 0)153break;154}155156if (fp == 0)157break;158159pc = dtrace_fuword64((void *)(fp - 1 * sizeof(uint64_t)));160fp = dtrace_fuword64((void *)(fp - 2 * sizeof(uint64_t)));161162if (fp == oldfp) {163*flags |= CPU_DTRACE_BADSTACK;164cpu_core[curcpu].cpuc_dtrace_illval = fp;165break;166}167oldfp = fp;168}169170return (ret);171}172173void174dtrace_getupcstack(uint64_t *pcstack, int pcstack_limit)175{176volatile uint16_t *flags;177struct trapframe *tf;178uintptr_t pc, fp;179proc_t *p;180int n;181182p = curproc;183flags = (volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags;184185if (*flags & CPU_DTRACE_FAULT)186return;187188if (pcstack_limit <= 0)189return;190191/*192* If there's no user context we still need to zero the stack.193*/194if (p == NULL || (tf = curthread->td_frame) == NULL)195goto zero;196197*pcstack++ = (uint64_t)p->p_pid;198pcstack_limit--;199200if (pcstack_limit <= 0)201return;202203pc = tf->tf_sepc;204fp = tf->tf_s[0];205206if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_ENTRY)) {207/*208* In an entry probe. The frame pointer has not yet been209* pushed (that happens in the function prologue). The210* best approach is to add the current pc as a missing top211* of stack and back the pc up to the caller, which is stored212* at the current stack pointer address since the call213* instruction puts it there right before the branch.214*/215*pcstack++ = (uint64_t)pc;216pcstack_limit--;217if (pcstack_limit <= 0)218return;219220pc = tf->tf_ra;221}222223n = dtrace_getustack_common(pcstack, pcstack_limit, pc, fp);224ASSERT(n >= 0);225ASSERT(n <= pcstack_limit);226227pcstack += n;228pcstack_limit -= n;229230zero:231while (pcstack_limit-- > 0)232*pcstack++ = 0;233}234235int236dtrace_getustackdepth(void)237{238struct trapframe *tf;239uintptr_t pc, fp;240int n = 0;241242if (curproc == NULL || (tf = curthread->td_frame) == NULL)243return (0);244245if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_FAULT))246return (-1);247248pc = tf->tf_sepc;249fp = tf->tf_s[0];250251if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_ENTRY)) {252/*253* In an entry probe. The frame pointer has not yet been254* pushed (that happens in the function prologue). The255* best approach is to add the current pc as a missing top256* of stack and back the pc up to the caller, which is stored257* at the current stack pointer address since the call258* instruction puts it there right before the branch.259*/260pc = tf->tf_ra;261n++;262}263264n += dtrace_getustack_common(NULL, 0, pc, fp);265266return (0);267}268269void270dtrace_getufpstack(uint64_t *pcstack, uint64_t *fpstack, int pcstack_limit)271{272273printf("IMPLEMENT ME: %s\n", __func__);274}275276/*ARGSUSED*/277uint64_t278dtrace_getarg(int arg, int aframes)279{280281printf("IMPLEMENT ME: %s\n", __func__);282283return (0);284}285286int287dtrace_getstackdepth(int aframes)288{289struct unwind_state state;290int scp_offset;291register_t sp;292int depth;293bool done;294295depth = 1;296done = false;297298__asm __volatile("mv %0, sp" : "=&r" (sp));299300state.fp = (uintptr_t)__builtin_frame_address(0);301state.sp = sp;302state.pc = (uintptr_t)dtrace_getstackdepth;303304do {305done = !unwind_frame(curthread, &state);306if (!INKERNEL(state.pc) || !INKERNEL(state.fp))307break;308depth++;309} while (!done);310311if (depth < aframes)312return (0);313else314return (depth - aframes);315}316317ulong_t318dtrace_getreg(struct trapframe *frame, uint_t reg)319{320switch (reg) {321case REG_ZERO:322return (0);323case REG_RA:324return (frame->tf_ra);325case REG_SP:326return (frame->tf_sp);327case REG_GP:328return (frame->tf_gp);329case REG_TP:330return (frame->tf_tp);331case REG_T0 ... REG_T2:332return (frame->tf_t[reg - REG_T0]);333case REG_S0 ... REG_S1:334return (frame->tf_s[reg - REG_S0]);335case REG_A0 ... REG_A7:336return (frame->tf_a[reg - REG_A0]);337case REG_S2 ... REG_S11:338return (frame->tf_s[reg - REG_S2 + 2]);339case REG_T3 ... REG_T6:340return (frame->tf_t[reg - REG_T3 + 3]);341case REG_PC:342return (frame->tf_sepc);343default:344DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP);345return (0);346}347/* NOTREACHED */348}349350static int351dtrace_copycheck(uintptr_t uaddr, uintptr_t kaddr, size_t size)352{353354if (uaddr + size > VM_MAXUSER_ADDRESS || uaddr + size < uaddr) {355DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);356cpu_core[curcpu].cpuc_dtrace_illval = uaddr;357return (0);358}359360return (1);361}362363void364dtrace_copyin(uintptr_t uaddr, uintptr_t kaddr, size_t size,365volatile uint16_t *flags)366{367368if (dtrace_copycheck(uaddr, kaddr, size))369dtrace_copy(uaddr, kaddr, size);370}371372void373dtrace_copyout(uintptr_t kaddr, uintptr_t uaddr, size_t size,374volatile uint16_t *flags)375{376377if (dtrace_copycheck(uaddr, kaddr, size))378dtrace_copy(kaddr, uaddr, size);379}380381void382dtrace_copyinstr(uintptr_t uaddr, uintptr_t kaddr, size_t size,383volatile uint16_t *flags)384{385386if (dtrace_copycheck(uaddr, kaddr, size))387dtrace_copystr(uaddr, kaddr, size, flags);388}389390void391dtrace_copyoutstr(uintptr_t kaddr, uintptr_t uaddr, size_t size,392volatile uint16_t *flags)393{394395if (dtrace_copycheck(uaddr, kaddr, size))396dtrace_copystr(kaddr, uaddr, size, flags);397}398399uint8_t400dtrace_fuword8(void *uaddr)401{402403if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) {404DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);405cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;406return (0);407}408409return (dtrace_fuword8_nocheck(uaddr));410}411412uint16_t413dtrace_fuword16(void *uaddr)414{415416if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) {417DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);418cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;419return (0);420}421422return (dtrace_fuword16_nocheck(uaddr));423}424425uint32_t426dtrace_fuword32(void *uaddr)427{428429if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) {430DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);431cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;432return (0);433}434435return (dtrace_fuword32_nocheck(uaddr));436}437438uint64_t439dtrace_fuword64(void *uaddr)440{441442if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) {443DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);444cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;445return (0);446}447448return (dtrace_fuword64_nocheck(uaddr));449}450451int452dtrace_match_opcode(uint32_t insn, int match, int mask)453{454if (((insn ^ match) & mask) == 0)455return (1);456457return (0);458}459460int461dtrace_instr_sdsp(uint32_t **instr)462{463if (dtrace_match_opcode(**instr, (MATCH_SD | RS2_RA | RS1_SP),464(MASK_SD | RS2_MASK | RS1_MASK)))465return (1);466467return (0);468}469470int471dtrace_instr_c_sdsp(uint32_t **instr)472{473uint16_t *instr1;474int i;475476for (i = 0; i < 2; i++) {477instr1 = (uint16_t *)(*instr) + i;478if (dtrace_match_opcode(*instr1, (MATCH_C_SDSP | RS2_C_RA),479(MASK_C_SDSP | RS2_C_MASK))) {480*instr = (uint32_t *)instr1;481return (1);482}483}484485return (0);486}487488int489dtrace_instr_ret(uint32_t **instr)490{491if (dtrace_match_opcode(**instr, (MATCH_JALR | (X_RA << RS1_SHIFT)),492(MASK_JALR | RD_MASK | RS1_MASK | IMM_MASK)))493return (1);494495return (0);496}497498int499dtrace_instr_c_ret(uint32_t **instr)500{501uint16_t *instr1;502int i;503504for (i = 0; i < 2; i++) {505instr1 = (uint16_t *)(*instr) + i;506if (dtrace_match_opcode(*instr1,507(MATCH_C_JR | (X_RA << RD_SHIFT)), (MASK_C_JR | RD_MASK))) {508*instr = (uint32_t *)instr1;509return (1);510}511}512513return (0);514}515516517