dtrace_isa.c revision 289786
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 2012,2013 Justin Hibbits <jhibbits@freebsd.org> 23 * 24 * $FreeBSD: stable/10/sys/cddl/dev/dtrace/powerpc/dtrace_isa.c 289786 2015-10-23 07:31:04Z avg $ 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/sysent.h> 37#include <sys/pcpu.h> 38 39#include <machine/frame.h> 40#include <machine/md_var.h> 41#include <machine/reg.h> 42#include <machine/stack.h> 43 44#include <vm/vm.h> 45#include <vm/vm_param.h> 46#include <vm/pmap.h> 47 48#include "regset.h" 49 50/* Offset to the LR Save word (ppc32) */ 51#define RETURN_OFFSET 4 52#define RETURN_OFFSET64 8 53 54#define INKERNEL(x) ((x) <= VM_MAX_KERNEL_ADDRESS && \ 55 (x) >= VM_MIN_KERNEL_ADDRESS) 56 57greg_t 58dtrace_getfp(void) 59{ 60 return (greg_t)__builtin_frame_address(0); 61} 62 63void 64dtrace_getpcstack(pc_t *pcstack, int pcstack_limit, int aframes, 65 uint32_t *intrpc) 66{ 67 int depth = 0; 68 register_t sp; 69 vm_offset_t callpc; 70 pc_t caller = (pc_t) solaris_cpu[curcpu].cpu_dtrace_caller; 71 72 if (intrpc != 0) 73 pcstack[depth++] = (pc_t) intrpc; 74 75 aframes++; 76 77 sp = dtrace_getfp(); 78 79 while (depth < pcstack_limit) { 80 if (!INKERNEL((long) sp)) 81 break; 82 83#ifdef __powerpc64__ 84 callpc = *(uintptr_t *)(sp + RETURN_OFFSET64); 85#else 86 callpc = *(uintptr_t *)(sp + RETURN_OFFSET); 87#endif 88 89 if (!INKERNEL(callpc)) 90 break; 91 92 if (aframes > 0) { 93 aframes--; 94 if ((aframes == 0) && (caller != 0)) { 95 pcstack[depth++] = caller; 96 } 97 } 98 else { 99 pcstack[depth++] = callpc; 100 } 101 102 sp = *(uintptr_t*)sp; 103 } 104 105 for (; depth < pcstack_limit; depth++) { 106 pcstack[depth] = 0; 107 } 108} 109 110static int 111dtrace_getustack_common(uint64_t *pcstack, int pcstack_limit, uintptr_t pc, 112 uintptr_t sp) 113{ 114 proc_t *p = curproc; 115 int ret = 0; 116 117 ASSERT(pcstack == NULL || pcstack_limit > 0); 118 119 while (pc != 0) { 120 ret++; 121 if (pcstack != NULL) { 122 *pcstack++ = (uint64_t)pc; 123 pcstack_limit--; 124 if (pcstack_limit <= 0) 125 break; 126 } 127 128 if (sp == 0) 129 break; 130 131 if (SV_PROC_FLAG(p, SV_ILP32)) { 132 pc = dtrace_fuword32((void *)(sp + RETURN_OFFSET)); 133 sp = dtrace_fuword32((void *)sp); 134 } 135 else { 136 pc = dtrace_fuword64((void *)(sp + RETURN_OFFSET64)); 137 sp = dtrace_fuword64((void *)sp); 138 } 139 } 140 141 return (ret); 142} 143 144void 145dtrace_getupcstack(uint64_t *pcstack, int pcstack_limit) 146{ 147 proc_t *p = curproc; 148 struct trapframe *tf; 149 uintptr_t pc, sp; 150 volatile uint16_t *flags = 151 (volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags; 152 int n; 153 154 if (*flags & CPU_DTRACE_FAULT) 155 return; 156 157 if (pcstack_limit <= 0) 158 return; 159 160 /* 161 * If there's no user context we still need to zero the stack. 162 */ 163 if (p == NULL || (tf = curthread->td_frame) == NULL) 164 goto zero; 165 166 *pcstack++ = (uint64_t)p->p_pid; 167 pcstack_limit--; 168 169 if (pcstack_limit <= 0) 170 return; 171 172 pc = tf->srr0; 173 sp = tf->fixreg[1]; 174 175 if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_ENTRY)) { 176 /* 177 * In an entry probe. The frame pointer has not yet been 178 * pushed (that happens in the function prologue). The 179 * best approach is to add the current pc as a missing top 180 * of stack and back the pc up to the caller, which is stored 181 * at the current stack pointer address since the call 182 * instruction puts it there right before the branch. 183 */ 184 185 *pcstack++ = (uint64_t)pc; 186 pcstack_limit--; 187 if (pcstack_limit <= 0) 188 return; 189 190 pc = tf->lr; 191 } 192 193 n = dtrace_getustack_common(pcstack, pcstack_limit, pc, sp); 194 ASSERT(n >= 0); 195 ASSERT(n <= pcstack_limit); 196 197 pcstack += n; 198 pcstack_limit -= n; 199 200zero: 201 while (pcstack_limit-- > 0) 202 *pcstack++ = 0; 203} 204 205int 206dtrace_getustackdepth(void) 207{ 208 proc_t *p = curproc; 209 struct trapframe *tf; 210 uintptr_t pc, sp; 211 int n = 0; 212 213 if (p == NULL || (tf = curthread->td_frame) == NULL) 214 return (0); 215 216 if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_FAULT)) 217 return (-1); 218 219 pc = tf->srr0; 220 sp = tf->fixreg[1]; 221 222 if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_ENTRY)) { 223 /* 224 * In an entry probe. The frame pointer has not yet been 225 * pushed (that happens in the function prologue). The 226 * best approach is to add the current pc as a missing top 227 * of stack and back the pc up to the caller, which is stored 228 * at the current stack pointer address since the call 229 * instruction puts it there right before the branch. 230 */ 231 232 if (SV_PROC_FLAG(p, SV_ILP32)) { 233 pc = dtrace_fuword32((void *) sp); 234 } 235 else 236 pc = dtrace_fuword64((void *) sp); 237 n++; 238 } 239 240 n += dtrace_getustack_common(NULL, 0, pc, sp); 241 242 return (n); 243} 244 245void 246dtrace_getufpstack(uint64_t *pcstack, uint64_t *fpstack, int pcstack_limit) 247{ 248 proc_t *p = curproc; 249 struct trapframe *tf; 250 uintptr_t pc, sp; 251 volatile uint16_t *flags = 252 (volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags; 253#ifdef notyet /* XXX signal stack */ 254 uintptr_t oldcontext; 255 size_t s1, s2; 256#endif 257 258 if (*flags & CPU_DTRACE_FAULT) 259 return; 260 261 if (pcstack_limit <= 0) 262 return; 263 264 /* 265 * If there's no user context we still need to zero the stack. 266 */ 267 if (p == NULL || (tf = curthread->td_frame) == NULL) 268 goto zero; 269 270 *pcstack++ = (uint64_t)p->p_pid; 271 pcstack_limit--; 272 273 if (pcstack_limit <= 0) 274 return; 275 276 pc = tf->srr0; 277 sp = tf->fixreg[1]; 278 279#ifdef notyet /* XXX signal stack */ 280 oldcontext = lwp->lwp_oldcontext; 281 s1 = sizeof (struct xframe) + 2 * sizeof (long); 282 s2 = s1 + sizeof (siginfo_t); 283#endif 284 285 if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_ENTRY)) { 286 *pcstack++ = (uint64_t)pc; 287 *fpstack++ = 0; 288 pcstack_limit--; 289 if (pcstack_limit <= 0) 290 return; 291 292 if (SV_PROC_FLAG(p, SV_ILP32)) { 293 pc = dtrace_fuword32((void *)sp); 294 } 295 else { 296 pc = dtrace_fuword64((void *)sp); 297 } 298 } 299 300 while (pc != 0) { 301 *pcstack++ = (uint64_t)pc; 302 *fpstack++ = sp; 303 pcstack_limit--; 304 if (pcstack_limit <= 0) 305 break; 306 307 if (sp == 0) 308 break; 309 310#ifdef notyet /* XXX signal stack */ 311 if (oldcontext == sp + s1 || oldcontext == sp + s2) { 312 ucontext_t *ucp = (ucontext_t *)oldcontext; 313 greg_t *gregs = ucp->uc_mcontext.gregs; 314 315 sp = dtrace_fulword(&gregs[REG_FP]); 316 pc = dtrace_fulword(&gregs[REG_PC]); 317 318 oldcontext = dtrace_fulword(&ucp->uc_link); 319 } else 320#endif /* XXX */ 321 { 322 if (SV_PROC_FLAG(p, SV_ILP32)) { 323 pc = dtrace_fuword32((void *)(sp + RETURN_OFFSET)); 324 sp = dtrace_fuword32((void *)sp); 325 } 326 else { 327 pc = dtrace_fuword64((void *)(sp + RETURN_OFFSET64)); 328 sp = dtrace_fuword64((void *)sp); 329 } 330 } 331 332 /* 333 * This is totally bogus: if we faulted, we're going to clear 334 * the fault and break. This is to deal with the apparently 335 * broken Java stacks on x86. 336 */ 337 if (*flags & CPU_DTRACE_FAULT) { 338 *flags &= ~CPU_DTRACE_FAULT; 339 break; 340 } 341 } 342 343zero: 344 while (pcstack_limit-- > 0) 345 *pcstack++ = 0; 346} 347 348/*ARGSUSED*/ 349uint64_t 350dtrace_getarg(int arg, int aframes) 351{ 352 uintptr_t val; 353 uintptr_t *fp = (uintptr_t *)dtrace_getfp(); 354 uintptr_t *stack; 355 int i; 356 357 /* 358 * A total of 8 arguments are passed via registers; any argument with 359 * index of 7 or lower is therefore in a register. 360 */ 361 int inreg = 7; 362 363 for (i = 1; i <= aframes; i++) { 364 fp = (uintptr_t *)*fp; 365 366 /* 367 * On ppc32 AIM, and booke, trapexit() is the immediately following 368 * label. On ppc64 AIM trapexit() follows a nop. 369 */ 370 if (((long)(fp[1]) == (long)trapexit) || 371 (((long)(fp[1]) + 4 == (long)trapexit))) { 372 /* 373 * In the case of powerpc, we will use the pointer to the regs 374 * structure that was pushed when we took the trap. To get this 375 * structure, we must increment beyond the frame structure. If the 376 * argument that we're seeking is passed on the stack, we'll pull 377 * the true stack pointer out of the saved registers and decrement 378 * our argument by the number of arguments passed in registers; if 379 * the argument we're seeking is passed in regsiters, we can just 380 * load it directly. 381 */ 382#ifdef __powerpc64__ 383 struct reg *rp = (struct reg *)((uintptr_t)fp[0] + 48); 384#else 385 struct reg *rp = (struct reg *)((uintptr_t)fp[0] + 8); 386#endif 387 388 if (arg <= inreg) { 389 stack = &rp->fixreg[3]; 390 } else { 391 stack = (uintptr_t *)(rp->fixreg[1]); 392 arg -= inreg; 393 } 394 goto load; 395 } 396 397 } 398 399 /* 400 * We know that we did not come through a trap to get into 401 * dtrace_probe() -- the provider simply called dtrace_probe() 402 * directly. As this is the case, we need to shift the argument 403 * that we're looking for: the probe ID is the first argument to 404 * dtrace_probe(), so the argument n will actually be found where 405 * one would expect to find argument (n + 1). 406 */ 407 arg++; 408 409 if (arg <= inreg) { 410 /* 411 * This shouldn't happen. If the argument is passed in a 412 * register then it should have been, well, passed in a 413 * register... 414 */ 415 DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP); 416 return (0); 417 } 418 419 arg -= (inreg + 1); 420 stack = fp + 2; 421 422load: 423 DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); 424 val = stack[arg]; 425 DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT); 426 427 return (val); 428} 429 430int 431dtrace_getstackdepth(int aframes) 432{ 433 int depth = 0; 434 register_t sp; 435 436 aframes++; 437 sp = dtrace_getfp(); 438 depth++; 439 for(;;) { 440 if (!INKERNEL((long) sp)) 441 break; 442 if (!INKERNEL((long) *(void **)sp)) 443 break; 444 depth++; 445 sp = *(uintptr_t *)sp; 446 } 447 if (depth < aframes) 448 return 0; 449 else 450 return depth - aframes; 451} 452 453ulong_t 454dtrace_getreg(struct trapframe *rp, uint_t reg) 455{ 456 if (reg < 32) 457 return (rp->fixreg[reg]); 458 459 switch (reg) { 460 case 33: 461 return (rp->lr); 462 case 34: 463 return (rp->cr); 464 case 35: 465 return (rp->xer); 466 case 36: 467 return (rp->ctr); 468 case 37: 469 return (rp->srr0); 470 case 38: 471 return (rp->srr1); 472 case 39: 473 return (rp->exc); 474 default: 475 DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP); 476 return (0); 477 } 478} 479 480static int 481dtrace_copycheck(uintptr_t uaddr, uintptr_t kaddr, size_t size) 482{ 483 ASSERT(INKERNEL(kaddr) && kaddr + size >= kaddr); 484 485 if (uaddr + size > VM_MAXUSER_ADDRESS || uaddr + size < uaddr) { 486 DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR); 487 cpu_core[curcpu].cpuc_dtrace_illval = uaddr; 488 return (0); 489 } 490 491 return (1); 492} 493 494void 495dtrace_copyin(uintptr_t uaddr, uintptr_t kaddr, size_t size, 496 volatile uint16_t *flags) 497{ 498 if (dtrace_copycheck(uaddr, kaddr, size)) 499 if (copyin((const void *)uaddr, (void *)kaddr, size)) { 500 DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR); 501 cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr; 502 } 503} 504 505void 506dtrace_copyout(uintptr_t kaddr, uintptr_t uaddr, size_t size, 507 volatile uint16_t *flags) 508{ 509 if (dtrace_copycheck(uaddr, kaddr, size)) { 510 if (copyout((const void *)kaddr, (void *)uaddr, size)) { 511 DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR); 512 cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr; 513 } 514 } 515} 516 517void 518dtrace_copyinstr(uintptr_t uaddr, uintptr_t kaddr, size_t size, 519 volatile uint16_t *flags) 520{ 521 size_t actual; 522 int error; 523 524 if (dtrace_copycheck(uaddr, kaddr, size)) { 525 error = copyinstr((const void *)uaddr, (void *)kaddr, 526 size, &actual); 527 528 /* ENAMETOOLONG is not a fault condition. */ 529 if (error && error != ENAMETOOLONG) { 530 DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR); 531 cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr; 532 } 533 } 534} 535 536/* 537 * The bulk of this function could be replaced to match dtrace_copyinstr() 538 * if we ever implement a copyoutstr(). 539 */ 540void 541dtrace_copyoutstr(uintptr_t kaddr, uintptr_t uaddr, size_t size, 542 volatile uint16_t *flags) 543{ 544 size_t len; 545 546 if (dtrace_copycheck(uaddr, kaddr, size)) { 547 len = strlen((const char *)kaddr); 548 if (len > size) 549 len = size; 550 551 if (copyout((const void *)kaddr, (void *)uaddr, len)) { 552 DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR); 553 cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr; 554 } 555 } 556} 557 558uint8_t 559dtrace_fuword8(void *uaddr) 560{ 561 if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) { 562 DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR); 563 cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr; 564 return (0); 565 } 566 return (fubyte(uaddr)); 567} 568 569uint16_t 570dtrace_fuword16(void *uaddr) 571{ 572 uint16_t ret = 0; 573 574 if (dtrace_copycheck((uintptr_t)uaddr, (uintptr_t)&ret, sizeof(ret))) { 575 if (copyin((const void *)uaddr, (void *)&ret, sizeof(ret))) { 576 DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR); 577 cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr; 578 } 579 } 580 return ret; 581} 582 583uint32_t 584dtrace_fuword32(void *uaddr) 585{ 586 if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) { 587 DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR); 588 cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr; 589 return (0); 590 } 591 return (fuword32(uaddr)); 592} 593 594uint64_t 595dtrace_fuword64(void *uaddr) 596{ 597 uint64_t ret = 0; 598 599 if (dtrace_copycheck((uintptr_t)uaddr, (uintptr_t)&ret, sizeof(ret))) { 600 if (copyin((const void *)uaddr, (void *)&ret, sizeof(ret))) { 601 DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR); 602 cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr; 603 } 604 } 605 return ret; 606} 607 608uintptr_t 609dtrace_fulword(void *uaddr) 610{ 611 uintptr_t ret = 0; 612 613 if (dtrace_copycheck((uintptr_t)uaddr, (uintptr_t)&ret, sizeof(ret))) { 614 if (copyin((const void *)uaddr, (void *)&ret, sizeof(ret))) { 615 DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR); 616 cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr; 617 } 618 } 619 return ret; 620} 621