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: stable/11/sys/cddl/dev/dtrace/arm/dtrace_subr.c 328386 2018-01-25 02:45:21Z pkelsey $ 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: stable/11/sys/cddl/dev/dtrace/arm/dtrace_subr.c 328386 2018-01-25 02:45:21Z pkelsey $"); 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 dtrace_id_t dtrace_probeid_error; 55278529Sgnnextern int (*dtrace_invop_jump_addr)(struct trapframe *); 56279721Sgnnextern void dtrace_getnanotime(struct timespec *tsp); 57278529Sgnn 58298171Smarkjint dtrace_invop(uintptr_t, struct trapframe *, uintptr_t); 59278529Sgnnvoid dtrace_invop_init(void); 60278529Sgnnvoid dtrace_invop_uninit(void); 61278529Sgnn 62278529Sgnntypedef struct dtrace_invop_hdlr { 63298171Smarkj int (*dtih_func)(uintptr_t, struct trapframe *, uintptr_t); 64278529Sgnn struct dtrace_invop_hdlr *dtih_next; 65278529Sgnn} dtrace_invop_hdlr_t; 66278529Sgnn 67278529Sgnndtrace_invop_hdlr_t *dtrace_invop_hdlr; 68278529Sgnn 69278529Sgnnint 70298171Smarkjdtrace_invop(uintptr_t addr, struct trapframe *frame, uintptr_t eax) 71278529Sgnn{ 72278529Sgnn dtrace_invop_hdlr_t *hdlr; 73278529Sgnn int rval; 74278529Sgnn 75278529Sgnn for (hdlr = dtrace_invop_hdlr; hdlr != NULL; hdlr = hdlr->dtih_next) 76298171Smarkj if ((rval = hdlr->dtih_func(addr, frame, eax)) != 0) 77278529Sgnn return (rval); 78278529Sgnn 79278529Sgnn return (0); 80278529Sgnn} 81278529Sgnn 82278529Sgnn 83278529Sgnnvoid 84298171Smarkjdtrace_invop_add(int (*func)(uintptr_t, struct trapframe *, uintptr_t)) 85278529Sgnn{ 86278529Sgnn dtrace_invop_hdlr_t *hdlr; 87278529Sgnn 88278529Sgnn hdlr = kmem_alloc(sizeof (dtrace_invop_hdlr_t), KM_SLEEP); 89278529Sgnn hdlr->dtih_func = func; 90278529Sgnn hdlr->dtih_next = dtrace_invop_hdlr; 91278529Sgnn dtrace_invop_hdlr = hdlr; 92278529Sgnn} 93278529Sgnn 94278529Sgnnvoid 95298171Smarkjdtrace_invop_remove(int (*func)(uintptr_t, struct trapframe *, uintptr_t)) 96278529Sgnn{ 97278529Sgnn dtrace_invop_hdlr_t *hdlr = dtrace_invop_hdlr, *prev = NULL; 98278529Sgnn 99278529Sgnn for (;;) { 100278529Sgnn if (hdlr == NULL) 101278529Sgnn panic("attempt to remove non-existent invop handler"); 102278529Sgnn 103278529Sgnn if (hdlr->dtih_func == func) 104278529Sgnn break; 105278529Sgnn 106278529Sgnn prev = hdlr; 107278529Sgnn hdlr = hdlr->dtih_next; 108278529Sgnn } 109278529Sgnn 110278529Sgnn if (prev == NULL) { 111278529Sgnn ASSERT(dtrace_invop_hdlr == hdlr); 112278529Sgnn dtrace_invop_hdlr = hdlr->dtih_next; 113278529Sgnn } else { 114278529Sgnn ASSERT(dtrace_invop_hdlr != hdlr); 115278529Sgnn prev->dtih_next = hdlr->dtih_next; 116278529Sgnn } 117278529Sgnn 118278529Sgnn kmem_free(hdlr, 0); 119278529Sgnn} 120278529Sgnn 121278529Sgnn 122278529Sgnn/*ARGSUSED*/ 123278529Sgnnvoid 124278529Sgnndtrace_toxic_ranges(void (*func)(uintptr_t base, uintptr_t limit)) 125278529Sgnn{ 126278529Sgnn printf("IMPLEMENT ME: dtrace_toxic_ranges\n"); 127278529Sgnn} 128278529Sgnn 129278529Sgnnvoid 130278529Sgnndtrace_xcall(processorid_t cpu, dtrace_xcall_t func, void *arg) 131278529Sgnn{ 132278529Sgnn cpuset_t cpus; 133278529Sgnn 134278529Sgnn if (cpu == DTRACE_CPUALL) 135278529Sgnn cpus = all_cpus; 136278529Sgnn else 137278529Sgnn CPU_SETOF(cpu, &cpus); 138278529Sgnn 139328386Spkelsey smp_rendezvous_cpus(cpus, smp_no_rendezvous_barrier, func, 140328386Spkelsey smp_no_rendezvous_barrier, arg); 141278529Sgnn} 142278529Sgnn 143278529Sgnnstatic void 144278529Sgnndtrace_sync_func(void) 145278529Sgnn{ 146278529Sgnn} 147278529Sgnn 148278529Sgnnvoid 149278529Sgnndtrace_sync(void) 150278529Sgnn{ 151278529Sgnn dtrace_xcall(DTRACE_CPUALL, (dtrace_xcall_t)dtrace_sync_func, NULL); 152278529Sgnn} 153278529Sgnn 154278529Sgnn/* 155278529Sgnn * DTrace needs a high resolution time function which can 156278529Sgnn * be called from a probe context and guaranteed not to have 157278529Sgnn * instrumented with probes itself. 158278529Sgnn * 159278529Sgnn * Returns nanoseconds since boot. 160278529Sgnn */ 161278529Sgnnuint64_t 162278529Sgnndtrace_gethrtime() 163278529Sgnn{ 164278529Sgnn struct timespec curtime; 165278529Sgnn 166278529Sgnn nanouptime(&curtime); 167278529Sgnn 168278529Sgnn return (curtime.tv_sec * 1000000000UL + curtime.tv_nsec); 169278529Sgnn 170278529Sgnn} 171278529Sgnn 172278529Sgnnuint64_t 173278529Sgnndtrace_gethrestime(void) 174278529Sgnn{ 175279721Sgnn struct timespec current_time; 176278529Sgnn 177279721Sgnn dtrace_getnanotime(¤t_time); 178278529Sgnn 179279721Sgnn return (current_time.tv_sec * 1000000000UL + current_time.tv_nsec); 180278529Sgnn} 181278529Sgnn 182278529Sgnn/* Function to handle DTrace traps during probes. See amd64/amd64/trap.c */ 183278529Sgnnint 184278529Sgnndtrace_trap(struct trapframe *frame, u_int type) 185278529Sgnn{ 186278529Sgnn /* 187278529Sgnn * A trap can occur while DTrace executes a probe. Before 188278529Sgnn * executing the probe, DTrace blocks re-scheduling and sets 189278529Sgnn * a flag in it's per-cpu flags to indicate that it doesn't 190278529Sgnn * want to fault. On returning from the probe, the no-fault 191278529Sgnn * flag is cleared and finally re-scheduling is enabled. 192278529Sgnn * 193278529Sgnn * Check if DTrace has enabled 'no-fault' mode: 194278529Sgnn * 195278529Sgnn */ 196278529Sgnn if ((cpu_core[curcpu].cpuc_dtrace_flags & CPU_DTRACE_NOFAULT) != 0) { 197278529Sgnn /* 198278529Sgnn * There are only a couple of trap types that are expected. 199278529Sgnn * All the rest will be handled in the usual way. 200278529Sgnn */ 201278529Sgnn switch (type) { 202278529Sgnn /* Page fault. */ 203278529Sgnn case FAULT_ALIGN: 204278529Sgnn /* Flag a bad address. */ 205278529Sgnn cpu_core[curcpu].cpuc_dtrace_flags |= CPU_DTRACE_BADADDR; 206278529Sgnn cpu_core[curcpu].cpuc_dtrace_illval = 0; 207278529Sgnn 208278529Sgnn /* 209278529Sgnn * Offset the instruction pointer to the instruction 210278529Sgnn * following the one causing the fault. 211278529Sgnn */ 212278529Sgnn frame->tf_pc += sizeof(int); 213278529Sgnn return (1); 214278529Sgnn default: 215278529Sgnn /* Handle all other traps in the usual way. */ 216278529Sgnn break; 217278529Sgnn } 218278529Sgnn } 219278529Sgnn 220278529Sgnn /* Handle the trap in the usual way. */ 221278529Sgnn return (0); 222278529Sgnn} 223278529Sgnn 224278529Sgnnvoid 225278529Sgnndtrace_probe_error(dtrace_state_t *state, dtrace_epid_t epid, int which, 226278529Sgnn int fault, int fltoffs, uintptr_t illval) 227278529Sgnn{ 228278529Sgnn 229278529Sgnn dtrace_probe(dtrace_probeid_error, (uint64_t)(uintptr_t)state, 230278529Sgnn (uintptr_t)epid, 231278529Sgnn (uintptr_t)which, (uintptr_t)fault, (uintptr_t)fltoffs); 232278529Sgnn} 233278529Sgnn 234278529Sgnnstatic int 235278529Sgnndtrace_invop_start(struct trapframe *frame) 236278529Sgnn{ 237279667Sandrew register_t *r0, *sp; 238279667Sandrew int data, invop, reg, update_sp; 239279667Sandrew 240298171Smarkj invop = dtrace_invop(frame->tf_pc, frame, frame->tf_pc); 241279667Sandrew switch (invop & DTRACE_INVOP_MASK) { 242278529Sgnn case DTRACE_INVOP_PUSHM: 243279667Sandrew sp = (register_t *)frame->tf_svc_sp; 244279667Sandrew r0 = &frame->tf_r0; 245279667Sandrew data = DTRACE_INVOP_DATA(invop); 246279667Sandrew 247279667Sandrew /* 248279667Sandrew * Store the pc, lr, and sp. These have their own 249279667Sandrew * entries in the struct. 250279667Sandrew */ 251279667Sandrew if (data & (1 << BIT_PC)) { 252279667Sandrew sp--; 253279667Sandrew *sp = frame->tf_pc; 254279667Sandrew } 255279667Sandrew if (data & (1 << BIT_LR)) { 256279667Sandrew sp--; 257279667Sandrew *sp = frame->tf_svc_lr; 258279667Sandrew } 259279667Sandrew if (data & (1 << BIT_SP)) { 260279667Sandrew sp--; 261279667Sandrew *sp = frame->tf_svc_sp; 262279667Sandrew } 263279667Sandrew 264279667Sandrew /* Store the general registers */ 265279667Sandrew for (reg = 12; reg >= 0; reg--) { 266279667Sandrew if (data & (1 << reg)) { 267279667Sandrew sp--; 268279667Sandrew *sp = r0[reg]; 269279667Sandrew } 270279667Sandrew } 271279667Sandrew 272279667Sandrew /* Update the stack pointer and program counter to continue */ 273279667Sandrew frame->tf_svc_sp = (register_t)sp; 274279667Sandrew frame->tf_pc += 4; 275278529Sgnn break; 276278529Sgnn case DTRACE_INVOP_POPM: 277279667Sandrew sp = (register_t *)frame->tf_svc_sp; 278279667Sandrew r0 = &frame->tf_r0; 279279667Sandrew data = DTRACE_INVOP_DATA(invop); 280279667Sandrew 281279667Sandrew /* Read the general registers */ 282279667Sandrew for (reg = 0; reg <= 12; reg++) { 283279667Sandrew if (data & (1 << reg)) { 284279667Sandrew r0[reg] = *sp; 285279667Sandrew sp++; 286279667Sandrew } 287279667Sandrew } 288279667Sandrew 289279667Sandrew /* 290279667Sandrew * Set the stack pointer. If we don't update it here we will 291279667Sandrew * need to update it at the end as the instruction would do 292279667Sandrew */ 293279667Sandrew update_sp = 1; 294279667Sandrew if (data & (1 << BIT_SP)) { 295279667Sandrew frame->tf_svc_sp = *sp; 296279667Sandrew *sp++; 297279667Sandrew update_sp = 0; 298279667Sandrew } 299279667Sandrew 300279667Sandrew /* Update the link register, we need to use the correct copy */ 301279667Sandrew if (data & (1 << BIT_LR)) { 302279667Sandrew frame->tf_svc_lr = *sp; 303279667Sandrew *sp++; 304279667Sandrew } 305279667Sandrew /* 306279667Sandrew * And the program counter. If it's not in the list skip over 307279667Sandrew * it when we return so to not hit this again. 308279667Sandrew */ 309279667Sandrew if (data & (1 << BIT_PC)) { 310279667Sandrew frame->tf_pc = *sp; 311279667Sandrew *sp++; 312279667Sandrew } else 313279667Sandrew frame->tf_pc += 4; 314279667Sandrew 315279667Sandrew /* Update the stack pointer if we haven't already done so */ 316279667Sandrew if (update_sp) 317279667Sandrew frame->tf_svc_sp = (register_t)sp; 318278529Sgnn break; 319278529Sgnn case DTRACE_INVOP_B: 320279667Sandrew data = DTRACE_INVOP_DATA(invop) & 0x00ffffff; 321279667Sandrew /* Sign extend the data */ 322279667Sandrew if ((data & (1 << 23)) != 0) 323279667Sandrew data |= 0xff000000; 324279667Sandrew /* The data is the number of 4-byte words to change the pc */ 325279667Sandrew data *= 4; 326279667Sandrew data += 8; 327279667Sandrew frame->tf_pc += data; 328278529Sgnn break; 329278529Sgnn default: 330278529Sgnn return (-1); 331278529Sgnn break; 332278529Sgnn } 333278529Sgnn 334278529Sgnn return (0); 335278529Sgnn} 336278529Sgnn 337278529Sgnnvoid dtrace_invop_init(void) 338278529Sgnn{ 339278529Sgnn dtrace_invop_jump_addr = dtrace_invop_start; 340278529Sgnn} 341278529Sgnn 342278529Sgnnvoid dtrace_invop_uninit(void) 343278529Sgnn{ 344278529Sgnn dtrace_invop_jump_addr = 0; 345278529Sgnn} 346