dtrace_subr.c revision 279667
1278529Sgnn/* 2278529Sgnn * CDDL HEADER START 3278529Sgnn * 4278529Sgnn * The contents of this file are subject to the terms of the 5278529Sgnn * Common Development and Distribution License, Version 1.0 only 6278529Sgnn * (the "License"). You may not use this file except in compliance 7278529Sgnn * with the License. 8278529Sgnn * 9278529Sgnn * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10278529Sgnn * or http://www.opensolaris.org/os/licensing. 11278529Sgnn * See the License for the specific language governing permissions 12278529Sgnn * and limitations under the License. 13278529Sgnn * 14278529Sgnn * When distributing Covered Code, include this CDDL HEADER in each 15278529Sgnn * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16278529Sgnn * If applicable, add the following below this CDDL HEADER, with the 17278529Sgnn * fields enclosed by brackets "[]" replaced with your own identifying 18278529Sgnn * information: Portions Copyright [yyyy] [name of copyright owner] 19278529Sgnn * 20278529Sgnn * CDDL HEADER END 21278529Sgnn * 22278529Sgnn * $FreeBSD: head/sys/cddl/dev/dtrace/arm/dtrace_subr.c 279667 2015-03-05 17:55:31Z andrew $ 23278529Sgnn * 24278529Sgnn */ 25278529Sgnn/* 26278529Sgnn * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 27278529Sgnn * Use is subject to license terms. 28278529Sgnn */ 29278529Sgnn 30278529Sgnn#include <sys/cdefs.h> 31278529Sgnn__FBSDID("$FreeBSD: head/sys/cddl/dev/dtrace/arm/dtrace_subr.c 279667 2015-03-05 17:55:31Z andrew $"); 32278529Sgnn 33278529Sgnn#include <sys/param.h> 34278529Sgnn#include <sys/systm.h> 35278529Sgnn#include <sys/types.h> 36278529Sgnn#include <sys/kernel.h> 37278529Sgnn#include <sys/malloc.h> 38278529Sgnn#include <sys/kmem.h> 39278529Sgnn#include <sys/smp.h> 40278529Sgnn#include <sys/dtrace_impl.h> 41278529Sgnn#include <sys/dtrace_bsd.h> 42278529Sgnn#include <machine/armreg.h> 43278529Sgnn#include <machine/clock.h> 44278529Sgnn#include <machine/frame.h> 45278529Sgnn#include <machine/trap.h> 46278529Sgnn#include <vm/pmap.h> 47278529Sgnn 48278529Sgnn#define DELAYBRANCH(x) ((int)(x) < 0) 49279667Sandrew 50279667Sandrew#define BIT_PC 15 51279667Sandrew#define BIT_LR 14 52279667Sandrew#define BIT_SP 13 53279667Sandrew 54278529Sgnnextern uintptr_t dtrace_in_probe_addr; 55278529Sgnnextern int dtrace_in_probe; 56278529Sgnnextern dtrace_id_t dtrace_probeid_error; 57278529Sgnnextern int (*dtrace_invop_jump_addr)(struct trapframe *); 58278529Sgnn 59278529Sgnnint dtrace_invop(uintptr_t, uintptr_t *, uintptr_t); 60278529Sgnnvoid dtrace_invop_init(void); 61278529Sgnnvoid dtrace_invop_uninit(void); 62278529Sgnn 63278529Sgnntypedef struct dtrace_invop_hdlr { 64278529Sgnn int (*dtih_func)(uintptr_t, uintptr_t *, uintptr_t); 65278529Sgnn struct dtrace_invop_hdlr *dtih_next; 66278529Sgnn} dtrace_invop_hdlr_t; 67278529Sgnn 68278529Sgnndtrace_invop_hdlr_t *dtrace_invop_hdlr; 69278529Sgnn 70278529Sgnnint 71278529Sgnndtrace_invop(uintptr_t addr, uintptr_t *stack, uintptr_t eax) 72278529Sgnn{ 73278529Sgnn dtrace_invop_hdlr_t *hdlr; 74278529Sgnn int rval; 75278529Sgnn 76278529Sgnn for (hdlr = dtrace_invop_hdlr; hdlr != NULL; hdlr = hdlr->dtih_next) 77278529Sgnn if ((rval = hdlr->dtih_func(addr, stack, eax)) != 0) 78278529Sgnn return (rval); 79278529Sgnn 80278529Sgnn return (0); 81278529Sgnn} 82278529Sgnn 83278529Sgnn 84278529Sgnnvoid 85278529Sgnndtrace_invop_add(int (*func)(uintptr_t, uintptr_t *, uintptr_t)) 86278529Sgnn{ 87278529Sgnn dtrace_invop_hdlr_t *hdlr; 88278529Sgnn 89278529Sgnn hdlr = kmem_alloc(sizeof (dtrace_invop_hdlr_t), KM_SLEEP); 90278529Sgnn hdlr->dtih_func = func; 91278529Sgnn hdlr->dtih_next = dtrace_invop_hdlr; 92278529Sgnn dtrace_invop_hdlr = hdlr; 93278529Sgnn} 94278529Sgnn 95278529Sgnnvoid 96278529Sgnndtrace_invop_remove(int (*func)(uintptr_t, uintptr_t *, uintptr_t)) 97278529Sgnn{ 98278529Sgnn dtrace_invop_hdlr_t *hdlr = dtrace_invop_hdlr, *prev = NULL; 99278529Sgnn 100278529Sgnn for (;;) { 101278529Sgnn if (hdlr == NULL) 102278529Sgnn panic("attempt to remove non-existent invop handler"); 103278529Sgnn 104278529Sgnn if (hdlr->dtih_func == func) 105278529Sgnn break; 106278529Sgnn 107278529Sgnn prev = hdlr; 108278529Sgnn hdlr = hdlr->dtih_next; 109278529Sgnn } 110278529Sgnn 111278529Sgnn if (prev == NULL) { 112278529Sgnn ASSERT(dtrace_invop_hdlr == hdlr); 113278529Sgnn dtrace_invop_hdlr = hdlr->dtih_next; 114278529Sgnn } else { 115278529Sgnn ASSERT(dtrace_invop_hdlr != hdlr); 116278529Sgnn prev->dtih_next = hdlr->dtih_next; 117278529Sgnn } 118278529Sgnn 119278529Sgnn kmem_free(hdlr, 0); 120278529Sgnn} 121278529Sgnn 122278529Sgnn 123278529Sgnn/*ARGSUSED*/ 124278529Sgnnvoid 125278529Sgnndtrace_toxic_ranges(void (*func)(uintptr_t base, uintptr_t limit)) 126278529Sgnn{ 127278529Sgnn printf("IMPLEMENT ME: dtrace_toxic_ranges\n"); 128278529Sgnn} 129278529Sgnn 130278529Sgnnvoid 131278529Sgnndtrace_xcall(processorid_t cpu, dtrace_xcall_t func, void *arg) 132278529Sgnn{ 133278529Sgnn cpuset_t cpus; 134278529Sgnn 135278529Sgnn if (cpu == DTRACE_CPUALL) 136278529Sgnn cpus = all_cpus; 137278529Sgnn else 138278529Sgnn CPU_SETOF(cpu, &cpus); 139278529Sgnn 140278529Sgnn smp_rendezvous_cpus(cpus, smp_no_rendevous_barrier, func, 141278529Sgnn smp_no_rendevous_barrier, arg); 142278529Sgnn} 143278529Sgnn 144278529Sgnnstatic void 145278529Sgnndtrace_sync_func(void) 146278529Sgnn{ 147278529Sgnn} 148278529Sgnn 149278529Sgnnvoid 150278529Sgnndtrace_sync(void) 151278529Sgnn{ 152278529Sgnn dtrace_xcall(DTRACE_CPUALL, (dtrace_xcall_t)dtrace_sync_func, NULL); 153278529Sgnn} 154278529Sgnn 155278529Sgnn/* 156278529Sgnn * DTrace needs a high resolution time function which can 157278529Sgnn * be called from a probe context and guaranteed not to have 158278529Sgnn * instrumented with probes itself. 159278529Sgnn * 160278529Sgnn * Returns nanoseconds since boot. 161278529Sgnn */ 162278529Sgnnuint64_t 163278529Sgnndtrace_gethrtime() 164278529Sgnn{ 165278529Sgnn struct timespec curtime; 166278529Sgnn 167278529Sgnn nanouptime(&curtime); 168278529Sgnn 169278529Sgnn return (curtime.tv_sec * 1000000000UL + curtime.tv_nsec); 170278529Sgnn 171278529Sgnn} 172278529Sgnn 173278529Sgnnuint64_t 174278529Sgnndtrace_gethrestime(void) 175278529Sgnn{ 176278529Sgnn struct timespec curtime; 177278529Sgnn 178278529Sgnn getnanotime(&curtime); 179278529Sgnn 180278529Sgnn return (curtime.tv_sec * 1000000000UL + curtime.tv_nsec); 181278529Sgnn} 182278529Sgnn 183278529Sgnn/* Function to handle DTrace traps during probes. See amd64/amd64/trap.c */ 184278529Sgnnint 185278529Sgnndtrace_trap(struct trapframe *frame, u_int type) 186278529Sgnn{ 187278529Sgnn /* 188278529Sgnn * A trap can occur while DTrace executes a probe. Before 189278529Sgnn * executing the probe, DTrace blocks re-scheduling and sets 190278529Sgnn * a flag in it's per-cpu flags to indicate that it doesn't 191278529Sgnn * want to fault. On returning from the probe, the no-fault 192278529Sgnn * flag is cleared and finally re-scheduling is enabled. 193278529Sgnn * 194278529Sgnn * Check if DTrace has enabled 'no-fault' mode: 195278529Sgnn * 196278529Sgnn */ 197278529Sgnn if ((cpu_core[curcpu].cpuc_dtrace_flags & CPU_DTRACE_NOFAULT) != 0) { 198278529Sgnn /* 199278529Sgnn * There are only a couple of trap types that are expected. 200278529Sgnn * All the rest will be handled in the usual way. 201278529Sgnn */ 202278529Sgnn switch (type) { 203278529Sgnn /* Page fault. */ 204278529Sgnn case FAULT_ALIGN: 205278529Sgnn /* Flag a bad address. */ 206278529Sgnn cpu_core[curcpu].cpuc_dtrace_flags |= CPU_DTRACE_BADADDR; 207278529Sgnn cpu_core[curcpu].cpuc_dtrace_illval = 0; 208278529Sgnn 209278529Sgnn /* 210278529Sgnn * Offset the instruction pointer to the instruction 211278529Sgnn * following the one causing the fault. 212278529Sgnn */ 213278529Sgnn frame->tf_pc += sizeof(int); 214278529Sgnn return (1); 215278529Sgnn default: 216278529Sgnn /* Handle all other traps in the usual way. */ 217278529Sgnn break; 218278529Sgnn } 219278529Sgnn } 220278529Sgnn 221278529Sgnn /* Handle the trap in the usual way. */ 222278529Sgnn return (0); 223278529Sgnn} 224278529Sgnn 225278529Sgnnvoid 226278529Sgnndtrace_probe_error(dtrace_state_t *state, dtrace_epid_t epid, int which, 227278529Sgnn int fault, int fltoffs, uintptr_t illval) 228278529Sgnn{ 229278529Sgnn 230278529Sgnn dtrace_probe(dtrace_probeid_error, (uint64_t)(uintptr_t)state, 231278529Sgnn (uintptr_t)epid, 232278529Sgnn (uintptr_t)which, (uintptr_t)fault, (uintptr_t)fltoffs); 233278529Sgnn} 234278529Sgnn 235278529Sgnnstatic int 236278529Sgnndtrace_invop_start(struct trapframe *frame) 237278529Sgnn{ 238279667Sandrew register_t *r0, *sp; 239279667Sandrew int data, invop, reg, update_sp; 240279667Sandrew 241279667Sandrew invop = dtrace_invop(frame->tf_pc, (uintptr_t *)frame, frame->tf_pc); 242279667Sandrew switch (invop & DTRACE_INVOP_MASK) { 243278529Sgnn case DTRACE_INVOP_PUSHM: 244279667Sandrew sp = (register_t *)frame->tf_svc_sp; 245279667Sandrew r0 = &frame->tf_r0; 246279667Sandrew data = DTRACE_INVOP_DATA(invop); 247279667Sandrew 248279667Sandrew /* 249279667Sandrew * Store the pc, lr, and sp. These have their own 250279667Sandrew * entries in the struct. 251279667Sandrew */ 252279667Sandrew if (data & (1 << BIT_PC)) { 253279667Sandrew sp--; 254279667Sandrew *sp = frame->tf_pc; 255279667Sandrew } 256279667Sandrew if (data & (1 << BIT_LR)) { 257279667Sandrew sp--; 258279667Sandrew *sp = frame->tf_svc_lr; 259279667Sandrew } 260279667Sandrew if (data & (1 << BIT_SP)) { 261279667Sandrew sp--; 262279667Sandrew *sp = frame->tf_svc_sp; 263279667Sandrew } 264279667Sandrew 265279667Sandrew /* Store the general registers */ 266279667Sandrew for (reg = 12; reg >= 0; reg--) { 267279667Sandrew if (data & (1 << reg)) { 268279667Sandrew sp--; 269279667Sandrew *sp = r0[reg]; 270279667Sandrew } 271279667Sandrew } 272279667Sandrew 273279667Sandrew /* Update the stack pointer and program counter to continue */ 274279667Sandrew frame->tf_svc_sp = (register_t)sp; 275279667Sandrew frame->tf_pc += 4; 276278529Sgnn break; 277278529Sgnn case DTRACE_INVOP_POPM: 278279667Sandrew sp = (register_t *)frame->tf_svc_sp; 279279667Sandrew r0 = &frame->tf_r0; 280279667Sandrew data = DTRACE_INVOP_DATA(invop); 281279667Sandrew 282279667Sandrew /* Read the general registers */ 283279667Sandrew for (reg = 0; reg <= 12; reg++) { 284279667Sandrew if (data & (1 << reg)) { 285279667Sandrew r0[reg] = *sp; 286279667Sandrew sp++; 287279667Sandrew } 288279667Sandrew } 289279667Sandrew 290279667Sandrew /* 291279667Sandrew * Set the stack pointer. If we don't update it here we will 292279667Sandrew * need to update it at the end as the instruction would do 293279667Sandrew */ 294279667Sandrew update_sp = 1; 295279667Sandrew if (data & (1 << BIT_SP)) { 296279667Sandrew frame->tf_svc_sp = *sp; 297279667Sandrew *sp++; 298279667Sandrew update_sp = 0; 299279667Sandrew } 300279667Sandrew 301279667Sandrew /* Update the link register, we need to use the correct copy */ 302279667Sandrew if (data & (1 << BIT_LR)) { 303279667Sandrew frame->tf_svc_lr = *sp; 304279667Sandrew *sp++; 305279667Sandrew } 306279667Sandrew /* 307279667Sandrew * And the program counter. If it's not in the list skip over 308279667Sandrew * it when we return so to not hit this again. 309279667Sandrew */ 310279667Sandrew if (data & (1 << BIT_PC)) { 311279667Sandrew frame->tf_pc = *sp; 312279667Sandrew *sp++; 313279667Sandrew } else 314279667Sandrew frame->tf_pc += 4; 315279667Sandrew 316279667Sandrew /* Update the stack pointer if we haven't already done so */ 317279667Sandrew if (update_sp) 318279667Sandrew frame->tf_svc_sp = (register_t)sp; 319278529Sgnn break; 320278529Sgnn case DTRACE_INVOP_B: 321279667Sandrew data = DTRACE_INVOP_DATA(invop) & 0x00ffffff; 322279667Sandrew /* Sign extend the data */ 323279667Sandrew if ((data & (1 << 23)) != 0) 324279667Sandrew data |= 0xff000000; 325279667Sandrew /* The data is the number of 4-byte words to change the pc */ 326279667Sandrew data *= 4; 327279667Sandrew data += 8; 328279667Sandrew frame->tf_pc += data; 329278529Sgnn break; 330278529Sgnn default: 331278529Sgnn return (-1); 332278529Sgnn break; 333278529Sgnn } 334278529Sgnn 335278529Sgnn return (0); 336278529Sgnn} 337278529Sgnn 338278529Sgnnvoid dtrace_invop_init(void) 339278529Sgnn{ 340278529Sgnn dtrace_invop_jump_addr = dtrace_invop_start; 341278529Sgnn} 342278529Sgnn 343278529Sgnnvoid dtrace_invop_uninit(void) 344278529Sgnn{ 345278529Sgnn dtrace_invop_jump_addr = 0; 346278529Sgnn} 347