dtrace_isa.c revision 233525
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: head/sys/cddl/dev/dtrace/mips/dtrace_isa.c 233525 2012-03-26 21:47:06Z gonzo $ 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/db_machdep.h> 45#include <machine/md_var.h> 46#include <machine/mips_opcode.h> 47#include <machine/vmparam.h> 48#include <ddb/db_sym.h> 49#include <ddb/ddb.h> 50#include <sys/kdb.h> 51 52#include "regset.h" 53 54#ifdef __mips_n64 55#define MIPS_IS_VALID_KERNELADDR(reg) ((((reg) & 3) == 0) && \ 56 ((vm_offset_t)(reg) >= MIPS_XKPHYS_START)) 57#else 58#define MIPS_IS_VALID_KERNELADDR(reg) ((((reg) & 3) == 0) && \ 59 ((vm_offset_t)(reg) >= MIPS_KSEG0_START)) 60#endif 61 62 63 64/* 65 * Wee need some reasonable default to prevent backtrace code 66 * from wandering too far 67 */ 68#define MAX_FUNCTION_SIZE 0x10000 69#define MAX_PROLOGUE_SIZE 0x100 70 71uint8_t dtrace_fuword8_nocheck(void *); 72uint16_t dtrace_fuword16_nocheck(void *); 73uint32_t dtrace_fuword32_nocheck(void *); 74uint64_t dtrace_fuword64_nocheck(void *); 75 76static int dtrace_next_frame(register_t *pc, register_t *sp, register_t *args, int *valid_args); 77static int dtrace_next_uframe(register_t *pc, register_t *sp, register_t *ra); 78 79void 80dtrace_getpcstack(pc_t *pcstack, int pcstack_limit, int aframes, 81 uint32_t *intrpc) 82{ 83 int depth = 0; 84 vm_offset_t callpc; 85 pc_t caller = (pc_t) solaris_cpu[curcpu].cpu_dtrace_caller; 86 register_t sp, ra, pc; 87 88 if (intrpc != 0) 89 pcstack[depth++] = (pc_t) intrpc; 90 91 aframes++; 92 93 sp = (register_t)(intptr_t)__builtin_frame_address(0); 94 ra = (register_t)(intptr_t)__builtin_return_address(0); 95 96 __asm __volatile( 97 "jal 99f\n" 98 "nop\n" 99 "99:\n" 100 "move %0, $31\n" /* get ra */ 101 "move $31, %1\n" /* restore ra */ 102 : "=r" (pc) 103 : "r" (ra)); 104 105 while (depth < pcstack_limit) { 106 107 callpc = pc; 108 109 if (aframes > 0) { 110 aframes--; 111 if ((aframes == 0) && (caller != 0)) { 112 pcstack[depth++] = caller; 113 } 114 } 115 else { 116 pcstack[depth++] = callpc; 117 } 118 119 if (dtrace_next_frame(&pc, &sp, NULL, NULL) < 0) 120 break; 121 } 122 123 for (; depth < pcstack_limit; depth++) { 124 pcstack[depth] = 0; 125 } 126} 127 128void 129dtrace_getupcstack(uint64_t *pcstack, int pcstack_limit) 130{ 131 proc_t *p = curproc; 132 struct trapframe *tf; 133 register_t sp, ra, pc; 134 volatile uint16_t *flags = 135 (volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags; 136 137 if (*flags & CPU_DTRACE_FAULT) 138 return; 139 140 if (pcstack_limit <= 0) 141 return; 142 143 /* 144 * If there's no user context we still need to zero the stack. 145 */ 146 if (p == NULL || (tf = curthread->td_frame) == NULL) 147 goto zero; 148 149 *pcstack++ = (uint64_t)p->p_pid; 150 pcstack_limit--; 151 152 if (pcstack_limit <= 0) 153 return; 154 155 pc = (uint64_t)tf->pc; 156 sp = (uint64_t)tf->sp; 157 ra = (uint64_t)tf->ra; 158 *pcstack++ = (uint64_t)tf->pc; 159 160 /* 161 * Unwind, and unwind, and unwind 162 */ 163 while (1) { 164 if (dtrace_next_uframe(&pc, &sp, &ra) < 0) 165 break; 166 167 *pcstack++ = pc; 168 pcstack_limit--; 169 170 if (pcstack_limit <= 0) 171 break; 172 } 173 174zero: 175 while (pcstack_limit-- > 0) 176 *pcstack++ = 0; 177} 178 179int 180dtrace_getustackdepth(void) 181{ 182 int n = 0; 183 proc_t *p = curproc; 184 struct trapframe *tf; 185 register_t sp, ra, pc; 186 volatile uint16_t *flags = 187 (volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags; 188 189 if (*flags & CPU_DTRACE_FAULT) 190 return (0); 191 192 if (p == NULL || (tf = curthread->td_frame) == NULL) 193 return (0); 194 195 pc = (uint64_t)tf->pc; 196 sp = (uint64_t)tf->sp; 197 ra = (uint64_t)tf->ra; 198 n++; 199 200 /* 201 * Unwind, and unwind, and unwind 202 */ 203 while (1) { 204 if (dtrace_next_uframe(&pc, &sp, &ra) < 0) 205 break; 206 n++; 207 } 208 209 return (n); 210} 211 212void 213dtrace_getufpstack(uint64_t *pcstack, uint64_t *fpstack, int pcstack_limit) 214{ 215 printf("IMPLEMENT ME: %s\n", __func__); 216} 217 218/*ARGSUSED*/ 219uint64_t 220dtrace_getarg(int arg, int aframes) 221{ 222 int i; 223 register_t sp, ra, pc; 224 /* XXX: Fix this ugly code */ 225 register_t args[8]; 226 int valid[8]; 227 228 sp = (register_t)(intptr_t)__builtin_frame_address(0); 229 ra = (register_t)(intptr_t)__builtin_return_address(0); 230 231 __asm __volatile( 232 "jal 99f\n" 233 "nop\n" 234 "99:\n" 235 "move %0, $31\n" /* get ra */ 236 "move $31, %1\n" /* restore ra */ 237 : "=r" (pc) 238 : "r" (ra)); 239 240 for (i = 0; i <= aframes + 1; i++) { 241 if (dtrace_next_frame(&pc, &sp, args, valid) < 0) { 242 printf("%s: stack ends at frame #%d\n", __func__, i); 243 return (0); 244 } 245 } 246 247 if (arg < 8) { 248 if (valid[arg]) 249 return (args[arg]); 250 else 251 printf("%s: request arg%d is not valid\n", __func__, arg); 252 } 253 254 return (0); 255} 256 257int 258dtrace_getstackdepth(int aframes) 259{ 260 register_t sp, ra, pc; 261 int depth = 0; 262 263 sp = (register_t)(intptr_t)__builtin_frame_address(0); 264 ra = (register_t)(intptr_t)__builtin_return_address(0); 265 266 __asm __volatile( 267 "jal 99f\n" 268 "nop\n" 269 "99:\n" 270 "move %0, $31\n" /* get ra */ 271 "move $31, %1\n" /* restore ra */ 272 : "=r" (pc) 273 : "r" (ra)); 274 275 for (;;) { 276 if (dtrace_next_frame(&pc, &sp, NULL, NULL) < 0) 277 break; 278 depth++; 279 } 280 281 if (depth < aframes) 282 return 0; 283 else 284 return depth - aframes; 285} 286 287ulong_t 288dtrace_getreg(struct trapframe *rp, uint_t reg) 289{ 290 291 return (0); 292} 293 294static int 295dtrace_next_frame(register_t *pc, register_t *sp, 296 register_t *args, int *valid_args) 297{ 298 InstFmt i; 299 /* 300 * Arrays for a0..a3 registers and flags if content 301 * of these registers is valid, e.g. obtained from the stack 302 */ 303 uintptr_t va; 304 unsigned instr, mask; 305 unsigned int frames = 0; 306 int more, stksize; 307 register_t ra = 0; 308 int arg, r; 309 vm_offset_t addr; 310 311 /* 312 * Invalidate arguments values 313 */ 314 if (valid_args) { 315 for (r = 0; r < 8; r++) 316 valid_args[r] = 0; 317 } 318 319 /* Jump here after a nonstandard (interrupt handler) frame */ 320 stksize = 0; 321 if (frames++ > 100) { 322 /* return breaks stackframe-size heuristics with gcc -O2 */ 323 goto error; /* XXX */ 324 } 325 326 /* check for bad SP: could foul up next frame */ 327 if (!MIPS_IS_VALID_KERNELADDR(*sp)) { 328 goto error; 329 } 330 331 /* check for bad PC */ 332 if (!MIPS_IS_VALID_KERNELADDR(*pc)) { 333 goto error; 334 } 335 336 /* 337 * Find the beginning of the current subroutine by scanning 338 * backwards from the current PC for the end of the previous 339 * subroutine. 340 */ 341 va = *pc - sizeof(int); 342 while (1) { 343 instr = kdbpeek((int *)va); 344 345 /* [d]addiu sp,sp,-X */ 346 if (((instr & 0xffff8000) == 0x27bd8000) 347 || ((instr & 0xffff8000) == 0x67bd8000)) 348 break; 349 350 /* jr ra */ 351 if (instr == 0x03e00008) { 352 /* skip over branch-delay slot instruction */ 353 va += 2 * sizeof(int); 354 break; 355 } 356 357 va -= sizeof(int); 358 } 359 360 /* skip over nulls which might separate .o files */ 361 while ((instr = kdbpeek((int *)va)) == 0) 362 va += sizeof(int); 363 364 /* scan forwards to find stack size and any saved registers */ 365 stksize = 0; 366 more = 3; 367 mask = 0; 368 for (; more; va += sizeof(int), 369 more = (more == 3) ? 3 : more - 1) { 370 /* stop if hit our current position */ 371 if (va >= *pc) 372 break; 373 instr = kdbpeek((int *)va); 374 i.word = instr; 375 switch (i.JType.op) { 376 case OP_SPECIAL: 377 switch (i.RType.func) { 378 case OP_JR: 379 case OP_JALR: 380 more = 2; /* stop after next instruction */ 381 break; 382 383 case OP_SYSCALL: 384 case OP_BREAK: 385 more = 1; /* stop now */ 386 }; 387 break; 388 389 case OP_BCOND: 390 case OP_J: 391 case OP_JAL: 392 case OP_BEQ: 393 case OP_BNE: 394 case OP_BLEZ: 395 case OP_BGTZ: 396 more = 2; /* stop after next instruction */ 397 break; 398 399 case OP_COP0: 400 case OP_COP1: 401 case OP_COP2: 402 case OP_COP3: 403 switch (i.RType.rs) { 404 case OP_BCx: 405 case OP_BCy: 406 more = 2; /* stop after next instruction */ 407 }; 408 break; 409 410 case OP_SW: 411 /* look for saved registers on the stack */ 412 if (i.IType.rs != 29) 413 break; 414 /* only restore the first one */ 415 if (mask & (1 << i.IType.rt)) 416 break; 417 mask |= (1 << i.IType.rt); 418 addr = (vm_offset_t)(*sp + (short)i.IType.imm); 419 switch (i.IType.rt) { 420 case 4:/* a0 */ 421 case 5:/* a1 */ 422 case 6:/* a2 */ 423 case 7:/* a3 */ 424#if defined(__mips_n64) || defined(__mips_n32) 425 case 8:/* a4 */ 426 case 9:/* a5 */ 427 case 10:/* a6 */ 428 case 11:/* a7 */ 429#endif 430 arg = i.IType.rt - 4; 431 if (args) 432 args[arg] = kdbpeek((int*)addr); 433 if (valid_args) 434 valid_args[arg] = 1; 435 break; 436 case 31: /* ra */ 437 ra = kdbpeek((int *)addr); 438 } 439 break; 440 441 case OP_SD: 442 /* look for saved registers on the stack */ 443 if (i.IType.rs != 29) 444 break; 445 /* only restore the first one */ 446 if (mask & (1 << i.IType.rt)) 447 break; 448 mask |= (1 << i.IType.rt); 449 addr = (vm_offset_t)(*sp + (short)i.IType.imm); 450 switch (i.IType.rt) { 451 case 4:/* a0 */ 452 case 5:/* a1 */ 453 case 6:/* a2 */ 454 case 7:/* a3 */ 455#if defined(__mips_n64) || defined(__mips_n32) 456 case 8:/* a4 */ 457 case 9:/* a5 */ 458 case 10:/* a6 */ 459 case 11:/* a7 */ 460#endif 461 arg = i.IType.rt - 4; 462 if (args) 463 args[arg] = kdbpeekd((int *)addr); 464 if (valid_args) 465 valid_args[arg] = 1; 466 break; 467 468 case 31: /* ra */ 469 ra = kdbpeekd((int *)addr); 470 } 471 break; 472 473 case OP_ADDI: 474 case OP_ADDIU: 475 case OP_DADDI: 476 case OP_DADDIU: 477 /* look for stack pointer adjustment */ 478 if (i.IType.rs != 29 || i.IType.rt != 29) 479 break; 480 stksize = -((short)i.IType.imm); 481 } 482 } 483 484 if (!MIPS_IS_VALID_KERNELADDR(ra)) 485 return (-1); 486 487 *pc = ra; 488 *sp += stksize; 489 490#if defined(__mips_o32) 491 /* 492 * For MIPS32 fill out arguments 5..8 from the stack 493 */ 494 for (arg = 4; arg < 8; arg++) { 495 addr = (vm_offset_t)(*sp + arg*sizeof(register_t)); 496 if (args) 497 args[arg] = kdbpeekd((int *)addr); 498 if (valid_args) 499 valid_args[arg] = 1; 500 } 501#endif 502 503 return (0); 504error: 505 return (-1); 506} 507 508static int 509dtrace_next_uframe(register_t *pc, register_t *sp, register_t *ra) 510{ 511 int offset, registers_on_stack; 512 uint32_t opcode, mask; 513 register_t function_start; 514 int stksize; 515 InstFmt i; 516 517 volatile uint16_t *flags = 518 (volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags; 519 520 registers_on_stack = 0; 521 mask = 0; 522 function_start = 0; 523 offset = 0; 524 stksize = 0; 525 526 while (offset < MAX_FUNCTION_SIZE) { 527 opcode = dtrace_fuword32((void *)(vm_offset_t)(*pc - offset)); 528 529 if (*flags & CPU_DTRACE_FAULT) 530 goto fault; 531 532 /* [d]addiu sp, sp, -X*/ 533 if (((opcode & 0xffff8000) == 0x27bd8000) 534 || ((opcode & 0xffff8000) == 0x67bd8000)) { 535 function_start = *pc - offset; 536 registers_on_stack = 1; 537 break; 538 } 539 540 /* lui gp, X */ 541 if ((opcode & 0xffff8000) == 0x3c1c0000) { 542 /* 543 * Function might start with this instruction 544 * Keep an eye on "jr ra" and sp correction 545 * with positive value further on 546 */ 547 function_start = *pc - offset; 548 } 549 550 if (function_start) { 551 /* 552 * Stop looking further. Possible end of 553 * function instruction: it means there is no 554 * stack modifications, sp is unchanged 555 */ 556 557 /* [d]addiu sp,sp,X */ 558 if (((opcode & 0xffff8000) == 0x27bd0000) 559 || ((opcode & 0xffff8000) == 0x67bd0000)) 560 break; 561 562 if (opcode == 0x03e00008) 563 break; 564 } 565 566 offset += sizeof(int); 567 } 568 569 if (!function_start) 570 return (-1); 571 572 if (registers_on_stack) { 573 offset = 0; 574 while ((offset < MAX_PROLOGUE_SIZE) 575 && ((function_start + offset) < *pc)) { 576 i.word = 577 dtrace_fuword32((void *)(vm_offset_t)(function_start + offset)); 578 switch (i.JType.op) { 579 case OP_SW: 580 /* look for saved registers on the stack */ 581 if (i.IType.rs != 29) 582 break; 583 /* only restore the first one */ 584 if (mask & (1 << i.IType.rt)) 585 break; 586 mask |= (1 << i.IType.rt); 587 if (i.IType.rt == 31) 588 *ra = dtrace_fuword32((void *)(vm_offset_t)(*sp + (short)i.IType.imm)); 589 break; 590 591 case OP_SD: 592 /* look for saved registers on the stack */ 593 if (i.IType.rs != 29) 594 break; 595 /* only restore the first one */ 596 if (mask & (1 << i.IType.rt)) 597 break; 598 mask |= (1 << i.IType.rt); 599 /* ra */ 600 if (i.IType.rt == 31) 601 *ra = dtrace_fuword64((void *)(vm_offset_t)(*sp + (short)i.IType.imm)); 602 break; 603 604 case OP_ADDI: 605 case OP_ADDIU: 606 case OP_DADDI: 607 case OP_DADDIU: 608 /* look for stack pointer adjustment */ 609 if (i.IType.rs != 29 || i.IType.rt != 29) 610 break; 611 stksize = -((short)i.IType.imm); 612 } 613 614 offset += sizeof(int); 615 616 if (*flags & CPU_DTRACE_FAULT) 617 goto fault; 618 } 619 } 620 621 /* 622 * We reached the end of backtrace 623 */ 624 if (*pc == *ra) 625 return (-1); 626 627 *pc = *ra; 628 *sp += stksize; 629 630 return (0); 631fault: 632 /* 633 * We just got lost in backtrace, no big deal 634 */ 635 *flags &= ~CPU_DTRACE_FAULT; 636 return (-1); 637} 638 639static int 640dtrace_copycheck(uintptr_t uaddr, uintptr_t kaddr, size_t size) 641{ 642 643 if (uaddr + size > VM_MAXUSER_ADDRESS || uaddr + size < uaddr) { 644 DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR); 645 cpu_core[curcpu].cpuc_dtrace_illval = uaddr; 646 return (0); 647 } 648 649 return (1); 650} 651 652void 653dtrace_copyin(uintptr_t uaddr, uintptr_t kaddr, size_t size, 654 volatile uint16_t *flags) 655{ 656 if (dtrace_copycheck(uaddr, kaddr, size)) 657 dtrace_copy(uaddr, kaddr, size); 658} 659 660void 661dtrace_copyout(uintptr_t kaddr, uintptr_t uaddr, size_t size, 662 volatile uint16_t *flags) 663{ 664 if (dtrace_copycheck(uaddr, kaddr, size)) 665 dtrace_copy(kaddr, uaddr, size); 666} 667 668void 669dtrace_copyinstr(uintptr_t uaddr, uintptr_t kaddr, size_t size, 670 volatile uint16_t *flags) 671{ 672 if (dtrace_copycheck(uaddr, kaddr, size)) 673 dtrace_copystr(uaddr, kaddr, size, flags); 674} 675 676void 677dtrace_copyoutstr(uintptr_t kaddr, uintptr_t uaddr, size_t size, 678 volatile uint16_t *flags) 679{ 680 if (dtrace_copycheck(uaddr, kaddr, size)) 681 dtrace_copystr(kaddr, uaddr, size, flags); 682} 683 684uint8_t 685dtrace_fuword8(void *uaddr) 686{ 687 if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) { 688 DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR); 689 cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr; 690 return (0); 691 } 692 return (dtrace_fuword8_nocheck(uaddr)); 693} 694 695uint16_t 696dtrace_fuword16(void *uaddr) 697{ 698 if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) { 699 DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR); 700 cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr; 701 return (0); 702 } 703 return (dtrace_fuword16_nocheck(uaddr)); 704} 705 706uint32_t 707dtrace_fuword32(void *uaddr) 708{ 709 if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) { 710 DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR); 711 cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr; 712 return (0); 713 } 714 return (dtrace_fuword32_nocheck(uaddr)); 715} 716 717uint64_t 718dtrace_fuword64(void *uaddr) 719{ 720 if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) { 721 DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR); 722 cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr; 723 return (0); 724 } 725 return (dtrace_fuword64_nocheck(uaddr)); 726} 727