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 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 26 * Use is subject to license terms. 27 */ 28#include <sys/cdefs.h> 29 30#include <sys/param.h> 31#include <sys/systm.h> 32#include <sys/kernel.h> 33#include <sys/stack.h> 34#include <sys/pcpu.h> 35 36#include <machine/frame.h> 37#include <machine/md_var.h> 38#include <machine/reg.h> 39 40#include <vm/vm.h> 41#include <vm/vm_param.h> 42#include <vm/pmap.h> 43 44#include <machine/atomic.h> 45#include <machine/db_machdep.h> 46#include <machine/md_var.h> 47#include <machine/stack.h> 48#include <ddb/db_sym.h> 49#include <ddb/ddb.h> 50#include <sys/kdb.h> 51 52#include "regset.h" 53 54/* 55 * Wee need some reasonable default to prevent backtrace code 56 * from wandering too far 57 */ 58#define MAX_FUNCTION_SIZE 0x10000 59#define MAX_PROLOGUE_SIZE 0x100 60#define MAX_USTACK_DEPTH 2048 61 62uint8_t dtrace_fuword8_nocheck(void *); 63uint16_t dtrace_fuword16_nocheck(void *); 64uint32_t dtrace_fuword32_nocheck(void *); 65uint64_t dtrace_fuword64_nocheck(void *); 66 67void 68dtrace_getpcstack(pc_t *pcstack, int pcstack_limit, int aframes, 69 uint32_t *intrpc) 70{ 71 struct unwind_state state; 72 int scp_offset; 73 register_t sp; 74 int depth; 75 76 depth = 0; 77 78 if (intrpc != 0) { 79 pcstack[depth++] = (pc_t) intrpc; 80 } 81 82 aframes++; 83 84 __asm __volatile("mov %0, sp" : "=&r" (sp)); 85 86 state.fp = (uintptr_t)__builtin_frame_address(0); 87 state.sp = sp; 88 state.pc = (uintptr_t)dtrace_getpcstack; 89 90 while (depth < pcstack_limit) { 91 if (!unwind_frame(curthread, &state)) 92 break; 93 if (!INKERNEL(state.pc)) 94 break; 95 96 /* 97 * NB: Unlike some other architectures, we don't need to 98 * explicitly insert cpu_dtrace_caller as it appears in the 99 * normal kernel stack trace rather than a special trap frame. 100 */ 101 if (aframes > 0) { 102 aframes--; 103 } else { 104 pcstack[depth++] = state.pc; 105 } 106 107 } 108 109 for (; depth < pcstack_limit; depth++) { 110 pcstack[depth] = 0; 111 } 112} 113 114static int 115dtrace_getustack_common(uint64_t *pcstack, int pcstack_limit, uintptr_t pc, 116 uintptr_t fp) 117{ 118 volatile uint16_t *flags = 119 (volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags; 120 int ret = 0; 121 uintptr_t oldfp = fp; 122 123 ASSERT(pcstack == NULL || pcstack_limit > 0); 124 125 while (pc != 0) { 126 /* 127 * We limit the number of times we can go around this 128 * loop to account for a circular stack. 129 */ 130 if (ret++ >= MAX_USTACK_DEPTH) { 131 *flags |= CPU_DTRACE_BADSTACK; 132 cpu_core[curcpu].cpuc_dtrace_illval = fp; 133 break; 134 } 135 136 if (pcstack != NULL) { 137 *pcstack++ = (uint64_t)pc; 138 pcstack_limit--; 139 if (pcstack_limit <= 0) 140 break; 141 } 142 143 if (fp == 0) 144 break; 145 146 pc = dtrace_fuword64((void *)(fp + 147 offsetof(struct arm64_frame, f_retaddr))); 148 fp = dtrace_fuword64((void *)fp); 149 150 if (fp == oldfp) { 151 *flags |= CPU_DTRACE_BADSTACK; 152 cpu_core[curcpu].cpuc_dtrace_illval = fp; 153 break; 154 } 155 156 /* 157 * ARM64TODO: 158 * This workaround might not be necessary. It needs to be 159 * revised and removed from all architectures if found 160 * unwanted. Leaving the original x86 comment for reference. 161 * 162 * This is totally bogus: if we faulted, we're going to clear 163 * the fault and break. This is to deal with the apparently 164 * broken Java stacks on x86. 165 */ 166 if (*flags & CPU_DTRACE_FAULT) { 167 *flags &= ~CPU_DTRACE_FAULT; 168 break; 169 } 170 171 oldfp = fp; 172 } 173 174 return (ret); 175} 176 177void 178dtrace_getupcstack(uint64_t *pcstack, int pcstack_limit) 179{ 180 proc_t *p = curproc; 181 struct trapframe *tf; 182 uintptr_t pc, sp, fp; 183 volatile uint16_t *flags = 184 (volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags; 185 int n; 186 187 if (*flags & CPU_DTRACE_FAULT) 188 return; 189 190 if (pcstack_limit <= 0) 191 return; 192 193 /* 194 * If there's no user context we still need to zero the stack. 195 */ 196 if (p == NULL || (tf = curthread->td_frame) == NULL) 197 goto zero; 198 199 *pcstack++ = (uint64_t)p->p_pid; 200 pcstack_limit--; 201 202 if (pcstack_limit <= 0) 203 return; 204 205 pc = tf->tf_elr; 206 sp = tf->tf_sp; 207 fp = tf->tf_x[29]; 208 209 if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_ENTRY)) { 210 /* 211 * In an entry probe. The frame pointer has not yet been 212 * pushed (that happens in the function prologue). The 213 * best approach is to add the current pc as a missing top 214 * of stack and back the pc up to the caller, which is stored 215 * at the current stack pointer address since the call 216 * instruction puts it there right before the branch. 217 */ 218 219 *pcstack++ = (uint64_t)pc; 220 pcstack_limit--; 221 if (pcstack_limit <= 0) 222 return; 223 224 pc = tf->tf_lr; 225 } 226 227 n = dtrace_getustack_common(pcstack, pcstack_limit, pc, fp); 228 ASSERT(n >= 0); 229 ASSERT(n <= pcstack_limit); 230 231 pcstack += n; 232 pcstack_limit -= n; 233 234zero: 235 while (pcstack_limit-- > 0) 236 *pcstack++ = 0; 237} 238 239int 240dtrace_getustackdepth(void) 241{ 242 243 printf("IMPLEMENT ME: %s\n", __func__); 244 245 return (0); 246} 247 248void 249dtrace_getufpstack(uint64_t *pcstack, uint64_t *fpstack, int pcstack_limit) 250{ 251 252 printf("IMPLEMENT ME: %s\n", __func__); 253} 254 255/*ARGSUSED*/ 256uint64_t 257dtrace_getarg(int arg, int aframes) 258{ 259 260 printf("IMPLEMENT ME: %s\n", __func__); 261 262 return (0); 263} 264 265int 266dtrace_getstackdepth(int aframes) 267{ 268 struct unwind_state state; 269 int scp_offset; 270 register_t sp; 271 int depth; 272 bool done; 273 274 depth = 1; 275 done = false; 276 277 __asm __volatile("mov %0, sp" : "=&r" (sp)); 278 279 state.fp = (uintptr_t)__builtin_frame_address(0); 280 state.sp = sp; 281 state.pc = (uintptr_t)dtrace_getstackdepth; 282 283 do { 284 done = !unwind_frame(curthread, &state); 285 if (!INKERNEL(state.pc) || !INKERNEL(state.fp)) 286 break; 287 depth++; 288 } while (!done); 289 290 if (depth < aframes) 291 return (0); 292 else 293 return (depth - aframes); 294} 295 296ulong_t 297dtrace_getreg(struct trapframe *rp, uint_t reg) 298{ 299 300 printf("IMPLEMENT ME: %s\n", __func__); 301 302 return (0); 303} 304 305static int 306dtrace_copycheck(uintptr_t uaddr, uintptr_t kaddr, size_t size) 307{ 308 309 if (uaddr + size > VM_MAXUSER_ADDRESS || uaddr + size < uaddr) { 310 DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR); 311 cpu_core[curcpu].cpuc_dtrace_illval = uaddr; 312 return (0); 313 } 314 315 return (1); 316} 317 318void 319dtrace_copyin(uintptr_t uaddr, uintptr_t kaddr, size_t size, 320 volatile uint16_t *flags) 321{ 322 323 if (dtrace_copycheck(uaddr, kaddr, size)) 324 dtrace_copy(uaddr, kaddr, size); 325} 326 327void 328dtrace_copyout(uintptr_t kaddr, uintptr_t uaddr, size_t size, 329 volatile uint16_t *flags) 330{ 331 332 if (dtrace_copycheck(uaddr, kaddr, size)) 333 dtrace_copy(kaddr, uaddr, size); 334} 335 336void 337dtrace_copyinstr(uintptr_t uaddr, uintptr_t kaddr, size_t size, 338 volatile uint16_t *flags) 339{ 340 341 if (dtrace_copycheck(uaddr, kaddr, size)) 342 dtrace_copystr(uaddr, kaddr, size, flags); 343} 344 345void 346dtrace_copyoutstr(uintptr_t kaddr, uintptr_t uaddr, size_t size, 347 volatile uint16_t *flags) 348{ 349 350 if (dtrace_copycheck(uaddr, kaddr, size)) 351 dtrace_copystr(kaddr, uaddr, size, flags); 352} 353 354uint8_t 355dtrace_fuword8(void *uaddr) 356{ 357 358 if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) { 359 DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR); 360 cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr; 361 return (0); 362 } 363 364 return (dtrace_fuword8_nocheck(uaddr)); 365} 366 367uint16_t 368dtrace_fuword16(void *uaddr) 369{ 370 371 if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) { 372 DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR); 373 cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr; 374 return (0); 375 } 376 377 return (dtrace_fuword16_nocheck(uaddr)); 378} 379 380uint32_t 381dtrace_fuword32(void *uaddr) 382{ 383 384 if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) { 385 DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR); 386 cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr; 387 return (0); 388 } 389 390 return (dtrace_fuword32_nocheck(uaddr)); 391} 392 393uint64_t 394dtrace_fuword64(void *uaddr) 395{ 396 397 if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) { 398 DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR); 399 cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr; 400 return (0); 401 } 402 403 return (dtrace_fuword64_nocheck(uaddr)); 404} 405