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 * Portions Copyright 2016 Ruslan Bukin <br@bsdpad.com> 23 * 24 * $FreeBSD$ 25 */ 26 /* 27 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 28 * Use is subject to license terms. 29 */ 30 #include <sys/cdefs.h> 31 32 #include <sys/param.h> 33 #include <sys/systm.h> 34 #include <sys/kernel.h> 35 #include <sys/stack.h> 36 #include <sys/pcpu.h> 37 38 #include <machine/frame.h> 39 #include <machine/md_var.h> 40 41 #include <vm/vm.h> 42 #include <vm/vm_param.h> 43 #include <vm/pmap.h> 44 45 #include <machine/atomic.h> 46 #include <machine/db_machdep.h> 47 #include <machine/md_var.h> 48 #include <machine/stack.h> 49 #include <ddb/db_sym.h> 50 #include <ddb/ddb.h> 51 #include <sys/kdb.h> 52 53 #include "regset.h" 54 55 #define MAX_USTACK_DEPTH 2048 56 57 uint8_t dtrace_fuword8_nocheck(void *); 58 uint16_t dtrace_fuword16_nocheck(void *); 59 uint32_t dtrace_fuword32_nocheck(void *); 60 uint64_t dtrace_fuword64_nocheck(void *); 61 62 void 63 dtrace_getpcstack(pc_t *pcstack, int pcstack_limit, int aframes, 64 uint32_t *intrpc) 65 { 66 struct unwind_state state; 67 uintptr_t caller; 68 register_t sp; 69 int scp_offset; 70 int depth; 71 72 depth = 0; 73 caller = solaris_cpu[curcpu].cpu_dtrace_caller; 74 75 if (intrpc != 0) { 76 pcstack[depth++] = (pc_t)intrpc; 77 } 78 79 /* 80 * Construct the unwind state, starting from this function. This frame, 81 * and 'aframes' others will be skipped. 82 */ 83 __asm __volatile("mv %0, sp" : "=&r" (sp)); 84 85 state.fp = (uintptr_t)__builtin_frame_address(0); 86 state.sp = (uintptr_t)sp; 87 state.pc = (uintptr_t)dtrace_getpcstack; 88 89 while (depth < pcstack_limit) { 90 if (!unwind_frame(curthread, &state)) 91 break; 92 93 if (!INKERNEL(state.pc) || !kstack_contains(curthread, 94 (vm_offset_t)state.fp, sizeof(uintptr_t))) 95 break; 96 97 if (aframes > 0) { 98 aframes--; 99 100 /* 101 * fbt_invop() records the return address at the time 102 * the FBT probe fires. We need to insert this into the 103 * backtrace manually, since the stack frame state at 104 * the time of the probe does not capture it. 105 */ 106 if (aframes == 0 && caller != 0) 107 pcstack[depth++] = caller; 108 } else { 109 pcstack[depth++] = state.pc; 110 } 111 } 112 113 for (; depth < pcstack_limit; depth++) { 114 pcstack[depth] = 0; 115 } 116 } 117 118 static int 119 dtrace_getustack_common(uint64_t *pcstack, int pcstack_limit, uintptr_t pc, 120 uintptr_t fp) 121 { 122 volatile uint16_t *flags; 123 uintptr_t oldfp; 124 int ret; 125 126 oldfp = fp; 127 ret = 0; 128 flags = (volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags; 129 130 ASSERT(pcstack == NULL || pcstack_limit > 0); 131 132 while (pc != 0) { 133 /* 134 * We limit the number of times we can go around this 135 * loop to account for a circular stack. 136 */ 137 if (ret++ >= MAX_USTACK_DEPTH) { 138 *flags |= CPU_DTRACE_BADSTACK; 139 cpu_core[curcpu].cpuc_dtrace_illval = fp; 140 break; 141 } 142 143 if (pcstack != NULL) { 144 *pcstack++ = (uint64_t)pc; 145 pcstack_limit--; 146 if (pcstack_limit <= 0) 147 break; 148 } 149 150 if (fp == 0) 151 break; 152 153 pc = dtrace_fuword64((void *)(fp - 1 * sizeof(uint64_t))); 154 fp = dtrace_fuword64((void *)(fp - 2 * sizeof(uint64_t))); 155 156 if (fp == oldfp) { 157 *flags |= CPU_DTRACE_BADSTACK; 158 cpu_core[curcpu].cpuc_dtrace_illval = fp; 159 break; 160 } 161 oldfp = fp; 162 } 163 164 return (ret); 165 } 166 167 void 168 dtrace_getupcstack(uint64_t *pcstack, int pcstack_limit) 169 { 170 volatile uint16_t *flags; 171 struct trapframe *tf; 172 uintptr_t pc, fp; 173 proc_t *p; 174 int n; 175 176 p = curproc; 177 flags = (volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags; 178 179 if (*flags & CPU_DTRACE_FAULT) 180 return; 181 182 if (pcstack_limit <= 0) 183 return; 184 185 /* 186 * If there's no user context we still need to zero the stack. 187 */ 188 if (p == NULL || (tf = curthread->td_frame) == NULL) 189 goto zero; 190 191 *pcstack++ = (uint64_t)p->p_pid; 192 pcstack_limit--; 193 194 if (pcstack_limit <= 0) 195 return; 196 197 pc = tf->tf_sepc; 198 fp = tf->tf_s[0]; 199 200 if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_ENTRY)) { 201 /* 202 * In an entry probe. The frame pointer has not yet been 203 * pushed (that happens in the function prologue). The 204 * best approach is to add the current pc as a missing top 205 * of stack and back the pc up to the caller, which is stored 206 * at the current stack pointer address since the call 207 * instruction puts it there right before the branch. 208 */ 209 *pcstack++ = (uint64_t)pc; 210 pcstack_limit--; 211 if (pcstack_limit <= 0) 212 return; 213 214 pc = tf->tf_ra; 215 } 216 217 n = dtrace_getustack_common(pcstack, pcstack_limit, pc, fp); 218 ASSERT(n >= 0); 219 ASSERT(n <= pcstack_limit); 220 221 pcstack += n; 222 pcstack_limit -= n; 223 224 zero: 225 while (pcstack_limit-- > 0) 226 *pcstack++ = 0; 227 } 228 229 int 230 dtrace_getustackdepth(void) 231 { 232 struct trapframe *tf; 233 uintptr_t pc, fp; 234 int n = 0; 235 236 if (curproc == NULL || (tf = curthread->td_frame) == NULL) 237 return (0); 238 239 if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_FAULT)) 240 return (-1); 241 242 pc = tf->tf_sepc; 243 fp = tf->tf_s[0]; 244 245 if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_ENTRY)) { 246 /* 247 * In an entry probe. The frame pointer has not yet been 248 * pushed (that happens in the function prologue). The 249 * best approach is to add the current pc as a missing top 250 * of stack and back the pc up to the caller, which is stored 251 * at the current stack pointer address since the call 252 * instruction puts it there right before the branch. 253 */ 254 pc = tf->tf_ra; 255 n++; 256 } 257 258 n += dtrace_getustack_common(NULL, 0, pc, fp); 259 260 return (0); 261 } 262 263 void 264 dtrace_getufpstack(uint64_t *pcstack, uint64_t *fpstack, int pcstack_limit) 265 { 266 267 printf("IMPLEMENT ME: %s\n", __func__); 268 } 269 270 /*ARGSUSED*/ 271 uint64_t 272 dtrace_getarg(int arg, int aframes) 273 { 274 275 printf("IMPLEMENT ME: %s\n", __func__); 276 277 return (0); 278 } 279 280 int 281 dtrace_getstackdepth(int aframes) 282 { 283 struct unwind_state state; 284 int scp_offset; 285 register_t sp; 286 int depth; 287 bool done; 288 289 depth = 1; 290 done = false; 291 292 __asm __volatile("mv %0, sp" : "=&r" (sp)); 293 294 state.fp = (uintptr_t)__builtin_frame_address(0); 295 state.sp = sp; 296 state.pc = (uintptr_t)dtrace_getstackdepth; 297 298 do { 299 done = !unwind_frame(curthread, &state); 300 if (!INKERNEL(state.pc) || !INKERNEL(state.fp)) 301 break; 302 depth++; 303 } while (!done); 304 305 if (depth < aframes) 306 return (0); 307 else 308 return (depth - aframes); 309 } 310 311 ulong_t 312 dtrace_getreg(struct trapframe *rp, uint_t reg) 313 { 314 switch (reg) { 315 case REG_ZERO: 316 return (0); 317 case REG_RA: 318 return (rp->tf_ra); 319 case REG_SP: 320 return (rp->tf_sp); 321 case REG_GP: 322 return (rp->tf_gp); 323 case REG_TP: 324 return (rp->tf_tp); 325 case REG_T0 ... REG_T2: 326 return (rp->tf_t[reg - REG_T0]); 327 case REG_S0 ... REG_S1: 328 return (rp->tf_s[reg - REG_S0]); 329 case REG_A0 ... REG_A7: 330 return (rp->tf_a[reg - REG_A0]); 331 case REG_S2 ... REG_S11: 332 return (rp->tf_s[reg - REG_S2 + 2]); 333 case REG_T3 ... REG_T6: 334 return (rp->tf_t[reg - REG_T3 + 3]); 335 case REG_PC: 336 return (rp->tf_sepc); 337 default: 338 DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP); 339 return (0); 340 } 341 /* NOTREACHED */ 342 } 343 344 static int 345 dtrace_copycheck(uintptr_t uaddr, uintptr_t kaddr, size_t size) 346 { 347 348 if (uaddr + size > VM_MAXUSER_ADDRESS || uaddr + size < uaddr) { 349 DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR); 350 cpu_core[curcpu].cpuc_dtrace_illval = uaddr; 351 return (0); 352 } 353 354 return (1); 355 } 356 357 void 358 dtrace_copyin(uintptr_t uaddr, uintptr_t kaddr, size_t size, 359 volatile uint16_t *flags) 360 { 361 362 if (dtrace_copycheck(uaddr, kaddr, size)) 363 dtrace_copy(uaddr, kaddr, size); 364 } 365 366 void 367 dtrace_copyout(uintptr_t kaddr, uintptr_t uaddr, size_t size, 368 volatile uint16_t *flags) 369 { 370 371 if (dtrace_copycheck(uaddr, kaddr, size)) 372 dtrace_copy(kaddr, uaddr, size); 373 } 374 375 void 376 dtrace_copyinstr(uintptr_t uaddr, uintptr_t kaddr, size_t size, 377 volatile uint16_t *flags) 378 { 379 380 if (dtrace_copycheck(uaddr, kaddr, size)) 381 dtrace_copystr(uaddr, kaddr, size, flags); 382 } 383 384 void 385 dtrace_copyoutstr(uintptr_t kaddr, uintptr_t uaddr, size_t size, 386 volatile uint16_t *flags) 387 { 388 389 if (dtrace_copycheck(uaddr, kaddr, size)) 390 dtrace_copystr(kaddr, uaddr, size, flags); 391 } 392 393 uint8_t 394 dtrace_fuword8(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_fuword8_nocheck(uaddr)); 404 } 405 406 uint16_t 407 dtrace_fuword16(void *uaddr) 408 { 409 410 if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) { 411 DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR); 412 cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr; 413 return (0); 414 } 415 416 return (dtrace_fuword16_nocheck(uaddr)); 417 } 418 419 uint32_t 420 dtrace_fuword32(void *uaddr) 421 { 422 423 if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) { 424 DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR); 425 cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr; 426 return (0); 427 } 428 429 return (dtrace_fuword32_nocheck(uaddr)); 430 } 431 432 uint64_t 433 dtrace_fuword64(void *uaddr) 434 { 435 436 if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) { 437 DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR); 438 cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr; 439 return (0); 440 } 441 442 return (dtrace_fuword64_nocheck(uaddr)); 443 } 444