Path: blob/main/sys/cddl/dev/dtrace/arm/dtrace_subr.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/*23* Copyright 2005 Sun Microsystems, Inc. All rights reserved.24* Use is subject to license terms.25*/2627#include <sys/param.h>28#include <sys/systm.h>29#include <sys/kernel.h>30#include <sys/malloc.h>31#include <sys/kmem.h>32#include <sys/proc.h>33#include <sys/smp.h>34#include <sys/dtrace_impl.h>35#include <sys/dtrace_bsd.h>36#include <cddl/dev/dtrace/dtrace_cddl.h>37#include <machine/armreg.h>38#include <machine/clock.h>39#include <machine/frame.h>40#include <machine/trap.h>41#include <vm/pmap.h>4243#define DELAYBRANCH(x) ((int)(x) < 0)4445#define BIT_PC 1546#define BIT_LR 1447#define BIT_SP 134849extern dtrace_id_t dtrace_probeid_error;50extern int (*dtrace_invop_jump_addr)(struct trapframe *);51extern void dtrace_getnanotime(struct timespec *tsp);52extern void dtrace_getnanouptime(struct timespec *tsp);5354int dtrace_invop(uintptr_t, struct trapframe *, uintptr_t);55void dtrace_invop_init(void);56void dtrace_invop_uninit(void);5758typedef struct dtrace_invop_hdlr {59int (*dtih_func)(uintptr_t, struct trapframe *, uintptr_t);60struct dtrace_invop_hdlr *dtih_next;61} dtrace_invop_hdlr_t;6263dtrace_invop_hdlr_t *dtrace_invop_hdlr;6465int66dtrace_invop(uintptr_t addr, struct trapframe *frame, uintptr_t eax)67{68struct thread *td;69dtrace_invop_hdlr_t *hdlr;70int rval;7172rval = 0;73td = curthread;74td->t_dtrace_trapframe = frame;75for (hdlr = dtrace_invop_hdlr; hdlr != NULL; hdlr = hdlr->dtih_next)76if ((rval = hdlr->dtih_func(addr, frame, eax)) != 0)77break;78td->t_dtrace_trapframe = NULL;79return (rval);80}818283void84dtrace_invop_add(int (*func)(uintptr_t, struct trapframe *, uintptr_t))85{86dtrace_invop_hdlr_t *hdlr;8788hdlr = kmem_alloc(sizeof (dtrace_invop_hdlr_t), KM_SLEEP);89hdlr->dtih_func = func;90hdlr->dtih_next = dtrace_invop_hdlr;91dtrace_invop_hdlr = hdlr;92}9394void95dtrace_invop_remove(int (*func)(uintptr_t, struct trapframe *, uintptr_t))96{97dtrace_invop_hdlr_t *hdlr = dtrace_invop_hdlr, *prev = NULL;9899for (;;) {100if (hdlr == NULL)101panic("attempt to remove non-existent invop handler");102103if (hdlr->dtih_func == func)104break;105106prev = hdlr;107hdlr = hdlr->dtih_next;108}109110if (prev == NULL) {111ASSERT(dtrace_invop_hdlr == hdlr);112dtrace_invop_hdlr = hdlr->dtih_next;113} else {114ASSERT(dtrace_invop_hdlr != hdlr);115prev->dtih_next = hdlr->dtih_next;116}117118kmem_free(hdlr, 0);119}120121122/*ARGSUSED*/123void124dtrace_toxic_ranges(void (*func)(uintptr_t base, uintptr_t limit))125{126127/*128* There are no ranges to exclude that are common to all 32-bit arm129* platforms. This function only needs to exclude ranges "... in130* which it is impossible to recover from such a load after it has been131* attempted." -- i.e., accessing within the range causes some sort132* fault in the system which is not handled by the normal arm133* exception-handling mechanisms. If systems exist where that is the134* case, a method to handle this functionality would have to be added to135* the platform_if interface so that those systems could provide their136* specific toxic range(s).137*/138}139140/*141* DTrace needs a high resolution time function which can142* be called from a probe context and guaranteed not to have143* instrumented with probes itself.144*145* Returns nanoseconds since boot.146*/147uint64_t148dtrace_gethrtime(void)149{150struct timespec curtime;151152dtrace_getnanouptime(&curtime);153154return (curtime.tv_sec * 1000000000UL + curtime.tv_nsec);155156}157158uint64_t159dtrace_gethrestime(void)160{161struct timespec current_time;162163dtrace_getnanotime(¤t_time);164165return (current_time.tv_sec * 1000000000UL + current_time.tv_nsec);166}167168/* Function to handle DTrace traps during probes. See amd64/amd64/trap.c */169int170dtrace_trap(struct trapframe *frame, u_int type)171{172/*173* A trap can occur while DTrace executes a probe. Before174* executing the probe, DTrace blocks re-scheduling and sets175* a flag in its per-cpu flags to indicate that it doesn't176* want to fault. On returning from the probe, the no-fault177* flag is cleared and finally re-scheduling is enabled.178*179* Check if DTrace has enabled 'no-fault' mode:180*181*/182if ((cpu_core[curcpu].cpuc_dtrace_flags & CPU_DTRACE_NOFAULT) != 0) {183/*184* There are only a couple of trap types that are expected.185* All the rest will be handled in the usual way.186*/187switch (type) {188/* Page fault. */189case FAULT_ALIGN:190/* Flag a bad address. */191cpu_core[curcpu].cpuc_dtrace_flags |= CPU_DTRACE_BADADDR;192cpu_core[curcpu].cpuc_dtrace_illval = 0;193194/*195* Offset the instruction pointer to the instruction196* following the one causing the fault.197*/198frame->tf_pc += sizeof(int);199return (1);200default:201/* Handle all other traps in the usual way. */202break;203}204}205206/* Handle the trap in the usual way. */207return (0);208}209210void211dtrace_probe_error(dtrace_state_t *state, dtrace_epid_t epid, int which,212int fault, int fltoffs, uintptr_t illval)213{214215dtrace_probe(dtrace_probeid_error, (uint64_t)(uintptr_t)state,216(uintptr_t)epid,217(uintptr_t)which, (uintptr_t)fault, (uintptr_t)fltoffs);218}219220static int221dtrace_invop_start(struct trapframe *frame)222{223register_t *r0, *sp;224int data, invop, reg, update_sp;225226invop = dtrace_invop(frame->tf_pc, frame, frame->tf_r0);227switch (invop & DTRACE_INVOP_MASK) {228case DTRACE_INVOP_PUSHM:229sp = (register_t *)frame->tf_svc_sp;230r0 = &frame->tf_r0;231data = DTRACE_INVOP_DATA(invop);232233/*234* Store the pc, lr, and sp. These have their own235* entries in the struct.236*/237if (data & (1 << BIT_PC)) {238sp--;239*sp = frame->tf_pc;240}241if (data & (1 << BIT_LR)) {242sp--;243*sp = frame->tf_svc_lr;244}245if (data & (1 << BIT_SP)) {246sp--;247*sp = frame->tf_svc_sp;248}249250/* Store the general registers */251for (reg = 12; reg >= 0; reg--) {252if (data & (1 << reg)) {253sp--;254*sp = r0[reg];255}256}257258/* Update the stack pointer and program counter to continue */259frame->tf_svc_sp = (register_t)sp;260frame->tf_pc += 4;261break;262case DTRACE_INVOP_POPM:263sp = (register_t *)frame->tf_svc_sp;264r0 = &frame->tf_r0;265data = DTRACE_INVOP_DATA(invop);266267/* Read the general registers */268for (reg = 0; reg <= 12; reg++) {269if (data & (1 << reg)) {270r0[reg] = *sp;271sp++;272}273}274275/*276* Set the stack pointer. If we don't update it here we will277* need to update it at the end as the instruction would do278*/279update_sp = 1;280if (data & (1 << BIT_SP)) {281frame->tf_svc_sp = *sp;282*sp++;283update_sp = 0;284}285286/* Update the link register, we need to use the correct copy */287if (data & (1 << BIT_LR)) {288frame->tf_svc_lr = *sp;289*sp++;290}291/*292* And the program counter. If it's not in the list skip over293* it when we return so to not hit this again.294*/295if (data & (1 << BIT_PC)) {296frame->tf_pc = *sp;297*sp++;298} else299frame->tf_pc += 4;300301/* Update the stack pointer if we haven't already done so */302if (update_sp)303frame->tf_svc_sp = (register_t)sp;304break;305case DTRACE_INVOP_B:306data = DTRACE_INVOP_DATA(invop) & 0x00ffffff;307/* Sign extend the data */308if ((data & (1 << 23)) != 0)309data |= 0xff000000;310/* The data is the number of 4-byte words to change the pc */311data *= 4;312data += 8;313frame->tf_pc += data;314break;315default:316return (-1);317break;318}319320return (0);321}322323void dtrace_invop_init(void)324{325dtrace_invop_jump_addr = dtrace_invop_start;326}327328void dtrace_invop_uninit(void)329{330dtrace_invop_jump_addr = 0;331}332333334