diff options
author | Ruslan Bukin <br@FreeBSD.org> | 2015-07-01 15:51:11 +0000 |
---|---|---|
committer | Ruslan Bukin <br@FreeBSD.org> | 2015-07-01 15:51:11 +0000 |
commit | b78ee15e9f04ae15c3e1200df974473167524d17 (patch) | |
tree | e6b0ba6abf805eec2a1add71465116f05cc0c76a /sys/cddl/dev/dtrace/aarch64/dtrace_subr.c | |
parent | 8b47cda2f77b115c7bcafdbf41e1412c618996cf (diff) | |
download | src-b78ee15e9f04ae15c3e1200df974473167524d17.tar.gz src-b78ee15e9f04ae15c3e1200df974473167524d17.zip |
First cut of DTrace for AArch64.
Reviewed by: andrew, emaste
Sponsored by: ARM Limited
Differential Revision: https://reviews.freebsd.org/D2738
Notes
Notes:
svn path=/head/; revision=285009
Diffstat (limited to 'sys/cddl/dev/dtrace/aarch64/dtrace_subr.c')
-rw-r--r-- | sys/cddl/dev/dtrace/aarch64/dtrace_subr.c | 311 |
1 files changed, 311 insertions, 0 deletions
diff --git a/sys/cddl/dev/dtrace/aarch64/dtrace_subr.c b/sys/cddl/dev/dtrace/aarch64/dtrace_subr.c new file mode 100644 index 000000000000..9cf5bc4247cd --- /dev/null +++ b/sys/cddl/dev/dtrace/aarch64/dtrace_subr.c @@ -0,0 +1,311 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 + * + * $FreeBSD$ + * + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/types.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/kmem.h> +#include <sys/smp.h> +#include <sys/dtrace_impl.h> +#include <sys/dtrace_bsd.h> +#include <machine/armreg.h> +#include <machine/clock.h> +#include <machine/frame.h> +#include <machine/trap.h> +#include <vm/pmap.h> + +extern uintptr_t dtrace_in_probe_addr; +extern int dtrace_in_probe; +extern dtrace_id_t dtrace_probeid_error; +extern int (*dtrace_invop_jump_addr)(struct trapframe *); +extern void dtrace_getnanotime(struct timespec *tsp); + +int dtrace_invop(uintptr_t, uintptr_t *, uintptr_t); +void dtrace_invop_init(void); +void dtrace_invop_uninit(void); + +typedef struct dtrace_invop_hdlr { + int (*dtih_func)(uintptr_t, uintptr_t *, uintptr_t); + struct dtrace_invop_hdlr *dtih_next; +} dtrace_invop_hdlr_t; + +dtrace_invop_hdlr_t *dtrace_invop_hdlr; + +int +dtrace_invop(uintptr_t addr, uintptr_t *stack, uintptr_t eax) +{ + dtrace_invop_hdlr_t *hdlr; + int rval; + + for (hdlr = dtrace_invop_hdlr; hdlr != NULL; hdlr = hdlr->dtih_next) + if ((rval = hdlr->dtih_func(addr, stack, eax)) != 0) + return (rval); + + return (0); +} + + +void +dtrace_invop_add(int (*func)(uintptr_t, uintptr_t *, uintptr_t)) +{ + dtrace_invop_hdlr_t *hdlr; + + hdlr = kmem_alloc(sizeof (dtrace_invop_hdlr_t), KM_SLEEP); + hdlr->dtih_func = func; + hdlr->dtih_next = dtrace_invop_hdlr; + dtrace_invop_hdlr = hdlr; +} + +void +dtrace_invop_remove(int (*func)(uintptr_t, uintptr_t *, uintptr_t)) +{ + dtrace_invop_hdlr_t *hdlr, *prev; + + hdlr = dtrace_invop_hdlr; + prev = NULL; + + for (;;) { + if (hdlr == NULL) + panic("attempt to remove non-existent invop handler"); + + if (hdlr->dtih_func == func) + break; + + prev = hdlr; + hdlr = hdlr->dtih_next; + } + + if (prev == NULL) { + ASSERT(dtrace_invop_hdlr == hdlr); + dtrace_invop_hdlr = hdlr->dtih_next; + } else { + ASSERT(dtrace_invop_hdlr != hdlr); + prev->dtih_next = hdlr->dtih_next; + } + + kmem_free(hdlr, 0); +} + +/*ARGSUSED*/ +void +dtrace_toxic_ranges(void (*func)(uintptr_t base, uintptr_t limit)) +{ + + printf("IMPLEMENT ME: dtrace_toxic_ranges\n"); +} + +void +dtrace_xcall(processorid_t cpu, dtrace_xcall_t func, void *arg) +{ + cpuset_t cpus; + + if (cpu == DTRACE_CPUALL) + cpus = all_cpus; + else + CPU_SETOF(cpu, &cpus); + + smp_rendezvous_cpus(cpus, smp_no_rendevous_barrier, func, + smp_no_rendevous_barrier, arg); +} + +static void +dtrace_sync_func(void) +{ + +} + +void +dtrace_sync(void) +{ + + dtrace_xcall(DTRACE_CPUALL, (dtrace_xcall_t)dtrace_sync_func, NULL); +} + +/* + * DTrace needs a high resolution time function which can + * be called from a probe context and guaranteed not to have + * instrumented with probes itself. + * + * Returns nanoseconds since boot. + */ +uint64_t +dtrace_gethrtime() +{ + struct timespec curtime; + + nanouptime(&curtime); + + return (curtime.tv_sec * 1000000000UL + curtime.tv_nsec); + +} + +uint64_t +dtrace_gethrestime(void) +{ + struct timespec current_time; + + dtrace_getnanotime(¤t_time); + + return (current_time.tv_sec * 1000000000UL + current_time.tv_nsec); +} + +/* Function to handle DTrace traps during probes. See arm64/arm64/trap.c */ +int +dtrace_trap(struct trapframe *frame, u_int type) +{ + /* + * A trap can occur while DTrace executes a probe. Before + * executing the probe, DTrace blocks re-scheduling and sets + * a flag in it's per-cpu flags to indicate that it doesn't + * want to fault. On returning from the probe, the no-fault + * flag is cleared and finally re-scheduling is enabled. + * + * Check if DTrace has enabled 'no-fault' mode: + * + */ + + if ((cpu_core[curcpu].cpuc_dtrace_flags & CPU_DTRACE_NOFAULT) != 0) { + /* + * There are only a couple of trap types that are expected. + * All the rest will be handled in the usual way. + */ + switch (type) { + case EXCP_DATA_ABORT: + /* Flag a bad address. */ + cpu_core[curcpu].cpuc_dtrace_flags |= CPU_DTRACE_BADADDR; + cpu_core[curcpu].cpuc_dtrace_illval = 0; + + /* + * Offset the instruction pointer to the instruction + * following the one causing the fault. + */ + frame->tf_elr += 4; + return (1); + default: + /* Handle all other traps in the usual way. */ + break; + } + } + + /* Handle the trap in the usual way. */ + return (0); +} + +void +dtrace_probe_error(dtrace_state_t *state, dtrace_epid_t epid, int which, + int fault, int fltoffs, uintptr_t illval) +{ + + dtrace_probe(dtrace_probeid_error, (uint64_t)(uintptr_t)state, + (uintptr_t)epid, + (uintptr_t)which, (uintptr_t)fault, (uintptr_t)fltoffs); +} + +static int +dtrace_invop_start(struct trapframe *frame) +{ + int data, invop, reg, update_sp; + register_t arg1, arg2; + register_t *sp; + int offs; + int tmp; + int i; + + invop = dtrace_invop(frame->tf_elr, (uintptr_t *)frame, frame->tf_elr); + + tmp = (invop & LDP_STP_MASK); + if (tmp == STP_64 || tmp == LDP_64) { + sp = (register_t *)frame->tf_sp; + data = invop; + arg1 = (data >> ARG1_SHIFT) & ARG1_MASK; + arg2 = (data >> ARG2_SHIFT) & ARG2_MASK; + + offs = (data >> OFFSET_SHIFT) & OFFSET_MASK; + + switch (tmp) { + case STP_64: + if (offs >> (OFFSET_SIZE - 1)) + sp -= (~offs & OFFSET_MASK) + 1; + else + sp += (offs); + *(sp + 0) = frame->tf_x[arg1]; + *(sp + 1) = frame->tf_x[arg2]; + break; + case LDP_64: + frame->tf_x[arg1] = *(sp + 0); + frame->tf_x[arg2] = *(sp + 1); + if (offs >> (OFFSET_SIZE - 1)) + sp -= (~offs & OFFSET_MASK) + 1; + else + sp += (offs); + break; + default: + break; + } + + /* Update the stack pointer and program counter to continue */ + frame->tf_sp = (register_t)sp; + frame->tf_elr += INSN_SIZE; + return (0); + } + + if ((invop & B_MASK) == B_INSTR) { + data = (invop & B_DATA_MASK); + /* The data is the number of 4-byte words to change the pc */ + data *= 4; + frame->tf_elr += data; + return (0); + } + + if (invop == RET_INSTR) { + frame->tf_elr = frame->tf_lr; + return (0); + } + + return (-1); +} + +void +dtrace_invop_init(void) +{ + + dtrace_invop_jump_addr = dtrace_invop_start; +} + +void +dtrace_invop_uninit(void) +{ + + dtrace_invop_jump_addr = 0; +} |