Path: blob/main/sys/cddl/dev/dtrace/powerpc/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/clock.h>38#include <machine/frame.h>39#include <machine/trap.h>40#include <vm/pmap.h>4142#define DELAYBRANCH(x) ((int)(x) < 0)4344extern dtrace_id_t dtrace_probeid_error;45extern int (*dtrace_invop_jump_addr)(struct trapframe *);4647extern void dtrace_getnanotime(struct timespec *tsp);4849int dtrace_invop(uintptr_t, struct trapframe *, uintptr_t);50void dtrace_invop_init(void);51void dtrace_invop_uninit(void);5253typedef struct dtrace_invop_hdlr {54int (*dtih_func)(uintptr_t, struct trapframe *, uintptr_t);55struct dtrace_invop_hdlr *dtih_next;56} dtrace_invop_hdlr_t;5758dtrace_invop_hdlr_t *dtrace_invop_hdlr;5960int61dtrace_invop(uintptr_t addr, struct trapframe *frame, uintptr_t arg0)62{63struct thread *td;64dtrace_invop_hdlr_t *hdlr;65int rval;6667rval = 0;68td = curthread;69td->t_dtrace_trapframe = frame;70for (hdlr = dtrace_invop_hdlr; hdlr != NULL; hdlr = hdlr->dtih_next)71if ((rval = hdlr->dtih_func(addr, frame, arg0)) != 0)72break;73td->t_dtrace_trapframe = NULL;74return (rval);75}7677void78dtrace_invop_add(int (*func)(uintptr_t, struct trapframe *, uintptr_t))79{80dtrace_invop_hdlr_t *hdlr;8182hdlr = kmem_alloc(sizeof (dtrace_invop_hdlr_t), KM_SLEEP);83hdlr->dtih_func = func;84hdlr->dtih_next = dtrace_invop_hdlr;85dtrace_invop_hdlr = hdlr;86}8788void89dtrace_invop_remove(int (*func)(uintptr_t, struct trapframe *, uintptr_t))90{91dtrace_invop_hdlr_t *hdlr = dtrace_invop_hdlr, *prev = NULL;9293for (;;) {94if (hdlr == NULL)95panic("attempt to remove non-existent invop handler");9697if (hdlr->dtih_func == func)98break;99100prev = hdlr;101hdlr = hdlr->dtih_next;102}103104if (prev == NULL) {105ASSERT(dtrace_invop_hdlr == hdlr);106dtrace_invop_hdlr = hdlr->dtih_next;107} else {108ASSERT(dtrace_invop_hdlr != hdlr);109prev->dtih_next = hdlr->dtih_next;110}111112kmem_free(hdlr, 0);113}114115116/*ARGSUSED*/117void118dtrace_toxic_ranges(void (*func)(uintptr_t base, uintptr_t limit))119{120/*121* No toxic regions?122*/123}124125static int64_t tgt_cpu_tsc;126static int64_t hst_cpu_tsc;127static int64_t timebase_skew[MAXCPU];128static uint64_t nsec_scale;129130/* See below for the explanation of this macro. */131/* This is taken from the amd64 dtrace_subr, to provide a synchronized timer132* between multiple processors in dtrace. Since PowerPC Timebases can be much133* lower than x86, the scale shift is 26 instead of 28, allowing for a 15.63MHz134* timebase.135*/136#define SCALE_SHIFT 26137138static void139dtrace_gethrtime_init_cpu(void *arg)140{141uintptr_t cpu = (uintptr_t) arg;142143if (cpu == curcpu)144tgt_cpu_tsc = mftb();145else146hst_cpu_tsc = mftb();147}148149static void150dtrace_gethrtime_init(void *arg)151{152struct pcpu *pc;153uint64_t tb_f;154cpuset_t map;155int i;156157tb_f = cpu_tickrate();158159/*160* The following line checks that nsec_scale calculated below161* doesn't overflow 32-bit unsigned integer, so that it can multiply162* another 32-bit integer without overflowing 64-bit.163* Thus minimum supported Timebase frequency is 15.63MHz.164*/165KASSERT(tb_f > (NANOSEC >> (32 - SCALE_SHIFT)), ("Timebase frequency is too low"));166167/*168* We scale up NANOSEC/tb_f ratio to preserve as much precision169* as possible.170* 2^26 factor was chosen quite arbitrarily from practical171* considerations:172* - it supports TSC frequencies as low as 15.63MHz (see above);173*/174nsec_scale = ((uint64_t)NANOSEC << SCALE_SHIFT) / tb_f;175176/* The current CPU is the reference one. */177sched_pin();178timebase_skew[curcpu] = 0;179CPU_FOREACH(i) {180if (i == curcpu)181continue;182183pc = pcpu_find(i);184CPU_SETOF(PCPU_GET(cpuid), &map);185CPU_SET(pc->pc_cpuid, &map);186187smp_rendezvous_cpus(map, NULL,188dtrace_gethrtime_init_cpu,189smp_no_rendezvous_barrier, (void *)(uintptr_t) i);190191timebase_skew[i] = tgt_cpu_tsc - hst_cpu_tsc;192}193sched_unpin();194}195#ifdef EARLY_AP_STARTUP196SYSINIT(dtrace_gethrtime_init, SI_SUB_DTRACE, SI_ORDER_ANY,197dtrace_gethrtime_init, NULL);198#else199SYSINIT(dtrace_gethrtime_init, SI_SUB_SMP, SI_ORDER_ANY, dtrace_gethrtime_init,200NULL);201#endif202203/*204* DTrace needs a high resolution time function which can205* be called from a probe context and guaranteed not to have206* instrumented with probes itself.207*208* Returns nanoseconds since boot.209*/210uint64_t211dtrace_gethrtime(void)212{213uint64_t timebase;214uint32_t lo;215uint32_t hi;216217/*218* We split timebase value into lower and higher 32-bit halves and separately219* scale them with nsec_scale, then we scale them down by 2^28220* (see nsec_scale calculations) taking into account 32-bit shift of221* the higher half and finally add.222*/223timebase = mftb() - timebase_skew[curcpu];224lo = timebase;225hi = timebase >> 32;226return (((lo * nsec_scale) >> SCALE_SHIFT) +227((hi * nsec_scale) << (32 - SCALE_SHIFT)));228}229230uint64_t231dtrace_gethrestime(void)232{233struct timespec curtime;234235dtrace_getnanotime(&curtime);236237return (curtime.tv_sec * 1000000000UL + curtime.tv_nsec);238}239240/* Function to handle DTrace traps during probes. See powerpc/powerpc/trap.c */241int242dtrace_trap(struct trapframe *frame, u_int type)243{244uint16_t nofault;245246/*247* A trap can occur while DTrace executes a probe. Before248* executing the probe, DTrace blocks re-scheduling and sets249* a flag in its per-cpu flags to indicate that it doesn't250* want to fault. On returning from the probe, the no-fault251* flag is cleared and finally re-scheduling is enabled.252*253* Check if DTrace has enabled 'no-fault' mode:254*/255sched_pin();256nofault = cpu_core[curcpu].cpuc_dtrace_flags & CPU_DTRACE_NOFAULT;257sched_unpin();258if (nofault) {259KASSERT((frame->srr1 & PSL_EE) == 0, ("interrupts enabled"));260/*261* There are only a couple of trap types that are expected.262* All the rest will be handled in the usual way.263*/264switch (type) {265/* Page fault. */266case EXC_DSI:267case EXC_DSE:268/* Flag a bad address. */269cpu_core[curcpu].cpuc_dtrace_flags |= CPU_DTRACE_BADADDR;270cpu_core[curcpu].cpuc_dtrace_illval = frame->dar;271272/*273* Offset the instruction pointer to the instruction274* following the one causing the fault.275*/276frame->srr0 += sizeof(int);277return (1);278case EXC_ISI:279case EXC_ISE:280/* Flag a bad address. */281cpu_core[curcpu].cpuc_dtrace_flags |= CPU_DTRACE_BADADDR;282cpu_core[curcpu].cpuc_dtrace_illval = frame->srr0;283284/*285* Offset the instruction pointer to the instruction286* following the one causing the fault.287*/288frame->srr0 += sizeof(int);289return (1);290default:291/* Handle all other traps in the usual way. */292break;293}294}295296/* Handle the trap in the usual way. */297return (0);298}299300void301dtrace_probe_error(dtrace_state_t *state, dtrace_epid_t epid, int which,302int fault, int fltoffs, uintptr_t illval)303{304305dtrace_probe(dtrace_probeid_error, (uint64_t)(uintptr_t)state,306(uintptr_t)epid,307(uintptr_t)which, (uintptr_t)fault, (uintptr_t)fltoffs);308}309310static int311dtrace_invop_start(struct trapframe *frame)312{313314switch (dtrace_invop(frame->srr0, frame, frame->fixreg[3])) {315case DTRACE_INVOP_JUMP:316break;317case DTRACE_INVOP_BCTR:318frame->srr0 = frame->ctr;319break;320case DTRACE_INVOP_BLR:321frame->srr0 = frame->lr;322break;323case DTRACE_INVOP_MFLR_R0:324frame->fixreg[0] = frame->lr;325frame->srr0 = frame->srr0 + 4;326break;327default:328return (-1);329}330return (0);331}332333void dtrace_invop_init(void)334{335dtrace_invop_jump_addr = dtrace_invop_start;336}337338void dtrace_invop_uninit(void)339{340dtrace_invop_jump_addr = 0;341}342343344