1/* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 * 22 * $FreeBSD$ 23 * 24 */ 25/* 26 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 27 * Use is subject to license terms. 28 */ 29 30#include <sys/cdefs.h> 31__KERNEL_RCSID(0, "$NetBSD: dtrace_subr.c,v 1.6 2023/04/17 06:57:02 skrll Exp $"); 32 33#include <sys/param.h> 34#include <sys/systm.h> 35#include <sys/types.h> 36#include <sys/kernel.h> 37#include <sys/malloc.h> 38#include <sys/kmem.h> 39#include <sys/xcall.h> 40#include <sys/cpu.h> 41#include <sys/cpuvar.h> 42#include <sys/dtrace_impl.h> 43#include <sys/dtrace_bsd.h> 44#include <machine/cpu.h> 45#include <machine/frame.h> 46#include <machine/vmparam.h> 47#include <uvm/uvm_pglist.h> 48#include <uvm/uvm_prot.h> 49#include <uvm/uvm_pmap.h> 50 51#define CURRENT_CPU cpu_index(curcpu()) 52#define OFFSET_MASK ((1 << OFFSET_SIZE) - 1) 53 54extern dtrace_id_t dtrace_probeid_error; 55extern int (*dtrace_invop_jump_addr)(struct trapframe *); 56extern void dtrace_getnanotime(struct timespec *tsp); 57 58int dtrace_invop(uintptr_t, struct trapframe *, uintptr_t); 59void dtrace_invop_init(void); 60void dtrace_invop_uninit(void); 61 62void dtrace_gethrtime_init(void); 63 64typedef struct dtrace_invop_hdlr { 65 int (*dtih_func)(uintptr_t, struct trapframe *, uintptr_t); 66 struct dtrace_invop_hdlr *dtih_next; 67} dtrace_invop_hdlr_t; 68 69dtrace_invop_hdlr_t *dtrace_invop_hdlr; 70 71int 72dtrace_invop(uintptr_t addr, struct trapframe *frame, uintptr_t r0) 73{ 74 dtrace_invop_hdlr_t *hdlr; 75 int rval; 76 77 for (hdlr = dtrace_invop_hdlr; hdlr != NULL; hdlr = hdlr->dtih_next) 78 if ((rval = hdlr->dtih_func(addr, frame, r0)) != 0) 79 return (rval); 80 81 return (0); 82} 83 84 85void 86dtrace_invop_add(int (*func)(uintptr_t, struct trapframe *, uintptr_t)) 87{ 88 dtrace_invop_hdlr_t *hdlr; 89 90 hdlr = kmem_alloc(sizeof(*hdlr), KM_SLEEP); 91 hdlr->dtih_func = func; 92 hdlr->dtih_next = dtrace_invop_hdlr; 93 dtrace_invop_hdlr = hdlr; 94} 95 96void 97dtrace_invop_remove(int (*func)(uintptr_t, struct trapframe *, uintptr_t)) 98{ 99 dtrace_invop_hdlr_t *hdlr, *prev; 100 101 hdlr = dtrace_invop_hdlr; 102 prev = NULL; 103 104 for (;;) { 105 if (hdlr == NULL) 106 panic("attempt to remove non-existent invop handler"); 107 108 if (hdlr->dtih_func == func) 109 break; 110 111 prev = hdlr; 112 hdlr = hdlr->dtih_next; 113 } 114 115 if (prev == NULL) { 116 ASSERT(dtrace_invop_hdlr == hdlr); 117 dtrace_invop_hdlr = hdlr->dtih_next; 118 } else { 119 ASSERT(dtrace_invop_hdlr != hdlr); 120 prev->dtih_next = hdlr->dtih_next; 121 } 122 123 kmem_free(hdlr, sizeof(*hdlr)); 124} 125 126/*ARGSUSED*/ 127void 128dtrace_toxic_ranges(void (*func)(uintptr_t base, uintptr_t limit)) 129{ 130 131 (*func)(0, (uintptr_t)AARCH64_DIRECTMAP_START); 132 (*func)((uintptr_t)VM_KERNEL_IO_BASE, ~(uintptr_t)0); 133} 134 135static void 136xcall_func(void *arg0, void *arg1) 137{ 138 dtrace_xcall_t func = arg0; 139 140 (*func)(arg1); 141} 142 143void 144dtrace_xcall(processorid_t cpu, dtrace_xcall_t func, void *arg) 145{ 146 uint64_t where; 147 148 if (cpu == DTRACE_CPUALL) { 149 where = xc_broadcast(0, xcall_func, func, arg); 150 } else { 151 struct cpu_info *ci = cpu_lookup(cpu); 152 153 KASSERT(ci != NULL); 154 where = xc_unicast(0, xcall_func, func, arg, ci); 155 } 156 xc_wait(where); 157} 158 159static void 160dtrace_sync_func(void) 161{ 162 163} 164 165void 166dtrace_sync(void) 167{ 168 169 dtrace_xcall(DTRACE_CPUALL, (dtrace_xcall_t)dtrace_sync_func, NULL); 170} 171 172/* 173 * DTrace needs a high resolution time function which can 174 * be called from a probe context and guaranteed not to have 175 * instrumented with probes itself. 176 * 177 * Returns nanoseconds since boot. 178 */ 179uint64_t 180dtrace_gethrtime() 181{ 182 struct timespec curtime; 183 184 nanouptime(&curtime); 185 186 return (curtime.tv_sec * 1000000000UL + curtime.tv_nsec); 187 188} 189 190void 191dtrace_gethrtime_init(void) 192{ 193} 194 195uint64_t 196dtrace_gethrestime(void) 197{ 198 struct timespec current_time; 199 200 dtrace_getnanotime(¤t_time); 201 202 return (current_time.tv_sec * 1000000000UL + current_time.tv_nsec); 203} 204 205/* Function to handle DTrace traps during probes. See arm64/arm64/trap.c */ 206int 207dtrace_trap(struct trapframe *frame, u_int type) 208{ 209 /* 210 * A trap can occur while DTrace executes a probe. Before 211 * executing the probe, DTrace blocks re-scheduling and sets 212 * a flag in its per-cpu flags to indicate that it doesn't 213 * want to fault. On returning from the probe, the no-fault 214 * flag is cleared and finally re-scheduling is enabled. 215 * 216 * Check if DTrace has enabled 'no-fault' mode: 217 * 218 */ 219 220 if ((cpu_core[CURRENT_CPU].cpuc_dtrace_flags & CPU_DTRACE_NOFAULT) != 0) { 221 /* 222 * There are only a couple of trap types that are expected. 223 * All the rest will be handled in the usual way. 224 */ 225 switch (type) { 226 case ESR_EC_DATA_ABT_EL1: 227 /* Flag a bad address. */ 228 cpu_core[CURRENT_CPU].cpuc_dtrace_flags |= CPU_DTRACE_BADADDR; 229 cpu_core[CURRENT_CPU].cpuc_dtrace_illval = 0; 230 231 /* 232 * Offset the instruction pointer to the instruction 233 * following the one causing the fault. 234 */ 235 frame->tf_pc += 4; 236 return (1); 237 default: 238 /* Handle all other traps in the usual way. */ 239 break; 240 } 241 } 242 243 /* Handle the trap in the usual way. */ 244 return (0); 245} 246 247void 248dtrace_probe_error(dtrace_state_t *state, dtrace_epid_t epid, int which, 249 int fault, int fltoffs, uintptr_t illval) 250{ 251 252 dtrace_probe(dtrace_probeid_error, (uint64_t)(uintptr_t)state, 253 (uintptr_t)epid, 254 (uintptr_t)which, (uintptr_t)fault, (uintptr_t)fltoffs); 255} 256 257static int 258dtrace_invop_start(struct trapframe *frame) 259{ 260 int data, invop, reg, update_sp; 261 register_t arg1, arg2; 262 register_t *sp; 263 int offs; 264 int tmp; 265 int i; 266 267 invop = dtrace_invop(frame->tf_pc, frame, frame->tf_regs.r_reg[0]); 268 269 tmp = (invop & LDP_STP_MASK); 270 if (tmp == STP_64 || tmp == LDP_64) { 271 sp = (register_t *)frame->tf_sp; 272 data = invop; 273 arg1 = (data >> ARG1_SHIFT) & ARG1_MASK; 274 arg2 = (data >> ARG2_SHIFT) & ARG2_MASK; 275 276 offs = (data >> OFFSET_SHIFT) & OFFSET_MASK; 277 278 switch (tmp) { 279 case STP_64: 280 if (offs >> (OFFSET_SIZE - 1)) 281 sp -= (~offs & OFFSET_MASK) + 1; 282 else 283 sp += (offs); 284 *(sp + 0) = frame->tf_regs.r_reg[arg1]; 285 *(sp + 1) = frame->tf_regs.r_reg[arg2]; 286 break; 287 case LDP_64: 288 frame->tf_regs.r_reg[arg1] = *(sp + 0); 289 frame->tf_regs.r_reg[arg2] = *(sp + 1); 290 if (offs >> (OFFSET_SIZE - 1)) 291 sp -= (~offs & OFFSET_MASK) + 1; 292 else 293 sp += (offs); 294 break; 295 default: 296 break; 297 } 298 299 /* Update the stack pointer and program counter to continue */ 300 frame->tf_sp = (register_t)sp; 301 frame->tf_pc += INSN_SIZE; 302 return (0); 303 } 304 305 if ((invop & B_MASK) == B_INSTR) { 306 data = (invop & B_DATA_MASK); 307 /* The data is the number of 4-byte words to change the pc */ 308 data *= 4; 309 frame->tf_pc += data; 310 return (0); 311 } 312 313 if (invop == RET_INSTR) { 314 frame->tf_pc = frame->tf_lr; 315 return (0); 316 } 317 318 return (-1); 319} 320 321void 322dtrace_invop_init(void) 323{ 324 325 dtrace_invop_jump_addr = dtrace_invop_start; 326} 327 328void 329dtrace_invop_uninit(void) 330{ 331 332 dtrace_invop_jump_addr = 0; 333} 334