dtrace_isa.c revision 233409
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 233409 2012-03-24 05:14:37Z 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 return (0); 491error: 492 return (-1); 493} 494 495static int 496dtrace_next_uframe(register_t *pc, register_t *sp, register_t *ra) 497{ 498 int offset, registers_on_stack; 499 uint32_t opcode, mask; 500 register_t function_start; 501 int stksize; 502 InstFmt i; 503 504 registers_on_stack = 0; 505 mask = 0; 506 function_start = 0; 507 offset = 0; 508 stksize = 0; 509 510 while (offset < MAX_FUNCTION_SIZE) { 511 opcode = dtrace_fuword32((void *)(vm_offset_t)(*pc - offset)); 512 513 /* [d]addiu sp, sp, -X*/ 514 if (((opcode & 0xffff8000) == 0x27bd8000) 515 || ((opcode & 0xffff8000) == 0x67bd8000)) { 516 function_start = *pc - offset; 517 registers_on_stack = 1; 518 break; 519 } 520 521 /* lui gp, X */ 522 if ((opcode & 0xffff8000) == 0x3c1c0000) { 523 /* 524 * Function might start with this instruction 525 * Keep an eye on "jr ra" and sp correction 526 * with positive value further on 527 */ 528 function_start = *pc - offset; 529 } 530 531 if (function_start) { 532 /* 533 * Stop looking further. Possible end of 534 * function instruction: it means there is no 535 * stack modifications, sp is unchanged 536 */ 537 538 /* [d]addiu sp,sp,X */ 539 if (((opcode & 0xffff8000) == 0x27bd0000) 540 || ((opcode & 0xffff8000) == 0x67bd0000)) 541 break; 542 543 if (opcode == 0x03e00008) 544 break; 545 } 546 547 offset += sizeof(int); 548 } 549 550 if (!function_start) 551 return (-1); 552 553 if (registers_on_stack) { 554 offset = 0; 555 while ((offset < MAX_PROLOGUE_SIZE) 556 && ((function_start + offset) < *pc)) { 557 i.word = 558 dtrace_fuword32((void *)(vm_offset_t)(function_start + offset)); 559 switch (i.JType.op) { 560 case OP_SW: 561 /* look for saved registers on the stack */ 562 if (i.IType.rs != 29) 563 break; 564 /* only restore the first one */ 565 if (mask & (1 << i.IType.rt)) 566 break; 567 mask |= (1 << i.IType.rt); 568 if (i.IType.rt == 31) 569 *ra = dtrace_fuword32((void *)(vm_offset_t)(*sp + (short)i.IType.imm)); 570 break; 571 572 case OP_SD: 573 /* look for saved registers on the stack */ 574 if (i.IType.rs != 29) 575 break; 576 /* only restore the first one */ 577 if (mask & (1 << i.IType.rt)) 578 break; 579 mask |= (1 << i.IType.rt); 580 /* ra */ 581 if (i.IType.rt == 31) 582 *ra = dtrace_fuword64((void *)(vm_offset_t)(*sp + (short)i.IType.imm)); 583 break; 584 585 case OP_ADDI: 586 case OP_ADDIU: 587 case OP_DADDI: 588 case OP_DADDIU: 589 /* look for stack pointer adjustment */ 590 if (i.IType.rs != 29 || i.IType.rt != 29) 591 break; 592 stksize = -((short)i.IType.imm); 593 } 594 595 offset += sizeof(int); 596 } 597 } 598 599 /* 600 * We reached the end of backtrace 601 */ 602 if (*pc == *ra) 603 return (-1); 604 605 *pc = *ra; 606 *sp += stksize; 607 608 return (0); 609} 610 611static int 612dtrace_copycheck(uintptr_t uaddr, uintptr_t kaddr, size_t size) 613{ 614 615 if (uaddr + size > VM_MAXUSER_ADDRESS || uaddr + size < uaddr) { 616 DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR); 617 cpu_core[curcpu].cpuc_dtrace_illval = uaddr; 618 return (0); 619 } 620 621 return (1); 622} 623 624void 625dtrace_copyin(uintptr_t uaddr, uintptr_t kaddr, size_t size, 626 volatile uint16_t *flags) 627{ 628 if (dtrace_copycheck(uaddr, kaddr, size)) 629 dtrace_copy(uaddr, kaddr, size); 630} 631 632void 633dtrace_copyout(uintptr_t kaddr, uintptr_t uaddr, size_t size, 634 volatile uint16_t *flags) 635{ 636 if (dtrace_copycheck(uaddr, kaddr, size)) 637 dtrace_copy(kaddr, uaddr, size); 638} 639 640void 641dtrace_copyinstr(uintptr_t uaddr, uintptr_t kaddr, size_t size, 642 volatile uint16_t *flags) 643{ 644 if (dtrace_copycheck(uaddr, kaddr, size)) 645 dtrace_copystr(uaddr, kaddr, size, flags); 646} 647 648void 649dtrace_copyoutstr(uintptr_t kaddr, uintptr_t uaddr, size_t size, 650 volatile uint16_t *flags) 651{ 652 if (dtrace_copycheck(uaddr, kaddr, size)) 653 dtrace_copystr(kaddr, uaddr, size, flags); 654} 655 656uint8_t 657dtrace_fuword8(void *uaddr) 658{ 659 if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) { 660 DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR); 661 cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr; 662 return (0); 663 } 664 return (dtrace_fuword8_nocheck(uaddr)); 665} 666 667uint16_t 668dtrace_fuword16(void *uaddr) 669{ 670 if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) { 671 DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR); 672 cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr; 673 return (0); 674 } 675 return (dtrace_fuword16_nocheck(uaddr)); 676} 677 678uint32_t 679dtrace_fuword32(void *uaddr) 680{ 681 if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) { 682 DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR); 683 cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr; 684 return (0); 685 } 686 return (dtrace_fuword32_nocheck(uaddr)); 687} 688 689uint64_t 690dtrace_fuword64(void *uaddr) 691{ 692 if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) { 693 DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR); 694 cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr; 695 return (0); 696 } 697 return (dtrace_fuword64_nocheck(uaddr)); 698} 699