dtrace_isa.c revision 233525
1233409Sgonzo/* 2233409Sgonzo * CDDL HEADER START 3233409Sgonzo * 4233409Sgonzo * The contents of this file are subject to the terms of the 5233409Sgonzo * Common Development and Distribution License, Version 1.0 only 6233409Sgonzo * (the "License"). You may not use this file except in compliance 7233409Sgonzo * with the License. 8233409Sgonzo * 9233409Sgonzo * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10233409Sgonzo * or http://www.opensolaris.org/os/licensing. 11233409Sgonzo * See the License for the specific language governing permissions 12233409Sgonzo * and limitations under the License. 13233409Sgonzo * 14233409Sgonzo * When distributing Covered Code, include this CDDL HEADER in each 15233409Sgonzo * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16233409Sgonzo * If applicable, add the following below this CDDL HEADER, with the 17233409Sgonzo * fields enclosed by brackets "[]" replaced with your own identifying 18233409Sgonzo * information: Portions Copyright [yyyy] [name of copyright owner] 19233409Sgonzo * 20233409Sgonzo * CDDL HEADER END 21233409Sgonzo * 22233409Sgonzo * $FreeBSD: head/sys/cddl/dev/dtrace/mips/dtrace_isa.c 233525 2012-03-26 21:47:06Z gonzo $ 23233409Sgonzo */ 24233409Sgonzo/* 25233409Sgonzo * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 26233409Sgonzo * Use is subject to license terms. 27233409Sgonzo */ 28233409Sgonzo#include <sys/cdefs.h> 29233409Sgonzo 30233409Sgonzo#include <sys/param.h> 31233409Sgonzo#include <sys/systm.h> 32233409Sgonzo#include <sys/kernel.h> 33233409Sgonzo#include <sys/stack.h> 34233409Sgonzo#include <sys/pcpu.h> 35233409Sgonzo 36233409Sgonzo#include <machine/frame.h> 37233409Sgonzo#include <machine/md_var.h> 38233409Sgonzo#include <machine/reg.h> 39233409Sgonzo 40233409Sgonzo#include <vm/vm.h> 41233409Sgonzo#include <vm/vm_param.h> 42233409Sgonzo#include <vm/pmap.h> 43233409Sgonzo 44233409Sgonzo#include <machine/db_machdep.h> 45233409Sgonzo#include <machine/md_var.h> 46233409Sgonzo#include <machine/mips_opcode.h> 47233409Sgonzo#include <machine/vmparam.h> 48233409Sgonzo#include <ddb/db_sym.h> 49233409Sgonzo#include <ddb/ddb.h> 50233409Sgonzo#include <sys/kdb.h> 51233409Sgonzo 52233409Sgonzo#include "regset.h" 53233409Sgonzo 54233409Sgonzo#ifdef __mips_n64 55233409Sgonzo#define MIPS_IS_VALID_KERNELADDR(reg) ((((reg) & 3) == 0) && \ 56233409Sgonzo ((vm_offset_t)(reg) >= MIPS_XKPHYS_START)) 57233409Sgonzo#else 58233409Sgonzo#define MIPS_IS_VALID_KERNELADDR(reg) ((((reg) & 3) == 0) && \ 59233409Sgonzo ((vm_offset_t)(reg) >= MIPS_KSEG0_START)) 60233409Sgonzo#endif 61233409Sgonzo 62233409Sgonzo 63233409Sgonzo 64233409Sgonzo/* 65233409Sgonzo * Wee need some reasonable default to prevent backtrace code 66233409Sgonzo * from wandering too far 67233409Sgonzo */ 68233409Sgonzo#define MAX_FUNCTION_SIZE 0x10000 69233409Sgonzo#define MAX_PROLOGUE_SIZE 0x100 70233409Sgonzo 71233409Sgonzouint8_t dtrace_fuword8_nocheck(void *); 72233409Sgonzouint16_t dtrace_fuword16_nocheck(void *); 73233409Sgonzouint32_t dtrace_fuword32_nocheck(void *); 74233409Sgonzouint64_t dtrace_fuword64_nocheck(void *); 75233409Sgonzo 76233409Sgonzostatic int dtrace_next_frame(register_t *pc, register_t *sp, register_t *args, int *valid_args); 77233409Sgonzostatic int dtrace_next_uframe(register_t *pc, register_t *sp, register_t *ra); 78233409Sgonzo 79233409Sgonzovoid 80233409Sgonzodtrace_getpcstack(pc_t *pcstack, int pcstack_limit, int aframes, 81233409Sgonzo uint32_t *intrpc) 82233409Sgonzo{ 83233409Sgonzo int depth = 0; 84233409Sgonzo vm_offset_t callpc; 85233409Sgonzo pc_t caller = (pc_t) solaris_cpu[curcpu].cpu_dtrace_caller; 86233409Sgonzo register_t sp, ra, pc; 87233409Sgonzo 88233409Sgonzo if (intrpc != 0) 89233409Sgonzo pcstack[depth++] = (pc_t) intrpc; 90233409Sgonzo 91233409Sgonzo aframes++; 92233409Sgonzo 93233409Sgonzo sp = (register_t)(intptr_t)__builtin_frame_address(0); 94233409Sgonzo ra = (register_t)(intptr_t)__builtin_return_address(0); 95233409Sgonzo 96233409Sgonzo __asm __volatile( 97233409Sgonzo "jal 99f\n" 98233409Sgonzo "nop\n" 99233409Sgonzo "99:\n" 100233409Sgonzo "move %0, $31\n" /* get ra */ 101233409Sgonzo "move $31, %1\n" /* restore ra */ 102233409Sgonzo : "=r" (pc) 103233409Sgonzo : "r" (ra)); 104233409Sgonzo 105233409Sgonzo while (depth < pcstack_limit) { 106233409Sgonzo 107233409Sgonzo callpc = pc; 108233409Sgonzo 109233409Sgonzo if (aframes > 0) { 110233409Sgonzo aframes--; 111233409Sgonzo if ((aframes == 0) && (caller != 0)) { 112233409Sgonzo pcstack[depth++] = caller; 113233409Sgonzo } 114233409Sgonzo } 115233409Sgonzo else { 116233409Sgonzo pcstack[depth++] = callpc; 117233409Sgonzo } 118233409Sgonzo 119233409Sgonzo if (dtrace_next_frame(&pc, &sp, NULL, NULL) < 0) 120233409Sgonzo break; 121233409Sgonzo } 122233409Sgonzo 123233409Sgonzo for (; depth < pcstack_limit; depth++) { 124233409Sgonzo pcstack[depth] = 0; 125233409Sgonzo } 126233409Sgonzo} 127233409Sgonzo 128233409Sgonzovoid 129233409Sgonzodtrace_getupcstack(uint64_t *pcstack, int pcstack_limit) 130233409Sgonzo{ 131233409Sgonzo proc_t *p = curproc; 132233409Sgonzo struct trapframe *tf; 133233409Sgonzo register_t sp, ra, pc; 134233409Sgonzo volatile uint16_t *flags = 135233409Sgonzo (volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags; 136233409Sgonzo 137233409Sgonzo if (*flags & CPU_DTRACE_FAULT) 138233409Sgonzo return; 139233409Sgonzo 140233409Sgonzo if (pcstack_limit <= 0) 141233409Sgonzo return; 142233409Sgonzo 143233409Sgonzo /* 144233409Sgonzo * If there's no user context we still need to zero the stack. 145233409Sgonzo */ 146233409Sgonzo if (p == NULL || (tf = curthread->td_frame) == NULL) 147233409Sgonzo goto zero; 148233409Sgonzo 149233409Sgonzo *pcstack++ = (uint64_t)p->p_pid; 150233409Sgonzo pcstack_limit--; 151233409Sgonzo 152233409Sgonzo if (pcstack_limit <= 0) 153233409Sgonzo return; 154233409Sgonzo 155233409Sgonzo pc = (uint64_t)tf->pc; 156233409Sgonzo sp = (uint64_t)tf->sp; 157233409Sgonzo ra = (uint64_t)tf->ra; 158233409Sgonzo *pcstack++ = (uint64_t)tf->pc; 159233409Sgonzo 160233409Sgonzo /* 161233409Sgonzo * Unwind, and unwind, and unwind 162233409Sgonzo */ 163233409Sgonzo while (1) { 164233409Sgonzo if (dtrace_next_uframe(&pc, &sp, &ra) < 0) 165233409Sgonzo break; 166233409Sgonzo 167233409Sgonzo *pcstack++ = pc; 168233409Sgonzo pcstack_limit--; 169233409Sgonzo 170233409Sgonzo if (pcstack_limit <= 0) 171233409Sgonzo break; 172233409Sgonzo } 173233409Sgonzo 174233409Sgonzozero: 175233409Sgonzo while (pcstack_limit-- > 0) 176233409Sgonzo *pcstack++ = 0; 177233409Sgonzo} 178233409Sgonzo 179233409Sgonzoint 180233409Sgonzodtrace_getustackdepth(void) 181233409Sgonzo{ 182233409Sgonzo int n = 0; 183233409Sgonzo proc_t *p = curproc; 184233409Sgonzo struct trapframe *tf; 185233409Sgonzo register_t sp, ra, pc; 186233409Sgonzo volatile uint16_t *flags = 187233409Sgonzo (volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags; 188233409Sgonzo 189233409Sgonzo if (*flags & CPU_DTRACE_FAULT) 190233409Sgonzo return (0); 191233409Sgonzo 192233409Sgonzo if (p == NULL || (tf = curthread->td_frame) == NULL) 193233409Sgonzo return (0); 194233409Sgonzo 195233409Sgonzo pc = (uint64_t)tf->pc; 196233409Sgonzo sp = (uint64_t)tf->sp; 197233409Sgonzo ra = (uint64_t)tf->ra; 198233409Sgonzo n++; 199233409Sgonzo 200233409Sgonzo /* 201233409Sgonzo * Unwind, and unwind, and unwind 202233409Sgonzo */ 203233409Sgonzo while (1) { 204233409Sgonzo if (dtrace_next_uframe(&pc, &sp, &ra) < 0) 205233409Sgonzo break; 206233409Sgonzo n++; 207233409Sgonzo } 208233409Sgonzo 209233409Sgonzo return (n); 210233409Sgonzo} 211233409Sgonzo 212233409Sgonzovoid 213233409Sgonzodtrace_getufpstack(uint64_t *pcstack, uint64_t *fpstack, int pcstack_limit) 214233409Sgonzo{ 215233409Sgonzo printf("IMPLEMENT ME: %s\n", __func__); 216233409Sgonzo} 217233409Sgonzo 218233409Sgonzo/*ARGSUSED*/ 219233409Sgonzouint64_t 220233409Sgonzodtrace_getarg(int arg, int aframes) 221233409Sgonzo{ 222233409Sgonzo int i; 223233409Sgonzo register_t sp, ra, pc; 224233409Sgonzo /* XXX: Fix this ugly code */ 225233409Sgonzo register_t args[8]; 226233409Sgonzo int valid[8]; 227233409Sgonzo 228233409Sgonzo sp = (register_t)(intptr_t)__builtin_frame_address(0); 229233409Sgonzo ra = (register_t)(intptr_t)__builtin_return_address(0); 230233409Sgonzo 231233409Sgonzo __asm __volatile( 232233409Sgonzo "jal 99f\n" 233233409Sgonzo "nop\n" 234233409Sgonzo "99:\n" 235233409Sgonzo "move %0, $31\n" /* get ra */ 236233409Sgonzo "move $31, %1\n" /* restore ra */ 237233409Sgonzo : "=r" (pc) 238233409Sgonzo : "r" (ra)); 239233409Sgonzo 240233409Sgonzo for (i = 0; i <= aframes + 1; i++) { 241233409Sgonzo if (dtrace_next_frame(&pc, &sp, args, valid) < 0) { 242233409Sgonzo printf("%s: stack ends at frame #%d\n", __func__, i); 243233409Sgonzo return (0); 244233409Sgonzo } 245233409Sgonzo } 246233409Sgonzo 247233409Sgonzo if (arg < 8) { 248233409Sgonzo if (valid[arg]) 249233409Sgonzo return (args[arg]); 250233409Sgonzo else 251233409Sgonzo printf("%s: request arg%d is not valid\n", __func__, arg); 252233409Sgonzo } 253233409Sgonzo 254233409Sgonzo return (0); 255233409Sgonzo} 256233409Sgonzo 257233409Sgonzoint 258233409Sgonzodtrace_getstackdepth(int aframes) 259233409Sgonzo{ 260233409Sgonzo register_t sp, ra, pc; 261233409Sgonzo int depth = 0; 262233409Sgonzo 263233409Sgonzo sp = (register_t)(intptr_t)__builtin_frame_address(0); 264233409Sgonzo ra = (register_t)(intptr_t)__builtin_return_address(0); 265233409Sgonzo 266233409Sgonzo __asm __volatile( 267233409Sgonzo "jal 99f\n" 268233409Sgonzo "nop\n" 269233409Sgonzo "99:\n" 270233409Sgonzo "move %0, $31\n" /* get ra */ 271233409Sgonzo "move $31, %1\n" /* restore ra */ 272233409Sgonzo : "=r" (pc) 273233409Sgonzo : "r" (ra)); 274233409Sgonzo 275233409Sgonzo for (;;) { 276233409Sgonzo if (dtrace_next_frame(&pc, &sp, NULL, NULL) < 0) 277233409Sgonzo break; 278233409Sgonzo depth++; 279233409Sgonzo } 280233409Sgonzo 281233409Sgonzo if (depth < aframes) 282233409Sgonzo return 0; 283233409Sgonzo else 284233409Sgonzo return depth - aframes; 285233409Sgonzo} 286233409Sgonzo 287233409Sgonzoulong_t 288233409Sgonzodtrace_getreg(struct trapframe *rp, uint_t reg) 289233409Sgonzo{ 290233409Sgonzo 291233409Sgonzo return (0); 292233409Sgonzo} 293233409Sgonzo 294233409Sgonzostatic int 295233409Sgonzodtrace_next_frame(register_t *pc, register_t *sp, 296233409Sgonzo register_t *args, int *valid_args) 297233409Sgonzo{ 298233409Sgonzo InstFmt i; 299233409Sgonzo /* 300233409Sgonzo * Arrays for a0..a3 registers and flags if content 301233409Sgonzo * of these registers is valid, e.g. obtained from the stack 302233409Sgonzo */ 303233409Sgonzo uintptr_t va; 304233409Sgonzo unsigned instr, mask; 305233409Sgonzo unsigned int frames = 0; 306233409Sgonzo int more, stksize; 307233409Sgonzo register_t ra = 0; 308233409Sgonzo int arg, r; 309233409Sgonzo vm_offset_t addr; 310233409Sgonzo 311233409Sgonzo /* 312233409Sgonzo * Invalidate arguments values 313233409Sgonzo */ 314233409Sgonzo if (valid_args) { 315233409Sgonzo for (r = 0; r < 8; r++) 316233409Sgonzo valid_args[r] = 0; 317233409Sgonzo } 318233409Sgonzo 319233409Sgonzo /* Jump here after a nonstandard (interrupt handler) frame */ 320233409Sgonzo stksize = 0; 321233409Sgonzo if (frames++ > 100) { 322233409Sgonzo /* return breaks stackframe-size heuristics with gcc -O2 */ 323233409Sgonzo goto error; /* XXX */ 324233409Sgonzo } 325233409Sgonzo 326233409Sgonzo /* check for bad SP: could foul up next frame */ 327233409Sgonzo if (!MIPS_IS_VALID_KERNELADDR(*sp)) { 328233409Sgonzo goto error; 329233409Sgonzo } 330233409Sgonzo 331233409Sgonzo /* check for bad PC */ 332233409Sgonzo if (!MIPS_IS_VALID_KERNELADDR(*pc)) { 333233409Sgonzo goto error; 334233409Sgonzo } 335233409Sgonzo 336233409Sgonzo /* 337233409Sgonzo * Find the beginning of the current subroutine by scanning 338233409Sgonzo * backwards from the current PC for the end of the previous 339233409Sgonzo * subroutine. 340233409Sgonzo */ 341233409Sgonzo va = *pc - sizeof(int); 342233409Sgonzo while (1) { 343233409Sgonzo instr = kdbpeek((int *)va); 344233409Sgonzo 345233409Sgonzo /* [d]addiu sp,sp,-X */ 346233409Sgonzo if (((instr & 0xffff8000) == 0x27bd8000) 347233409Sgonzo || ((instr & 0xffff8000) == 0x67bd8000)) 348233409Sgonzo break; 349233409Sgonzo 350233409Sgonzo /* jr ra */ 351233409Sgonzo if (instr == 0x03e00008) { 352233409Sgonzo /* skip over branch-delay slot instruction */ 353233409Sgonzo va += 2 * sizeof(int); 354233409Sgonzo break; 355233409Sgonzo } 356233409Sgonzo 357233409Sgonzo va -= sizeof(int); 358233409Sgonzo } 359233409Sgonzo 360233409Sgonzo /* skip over nulls which might separate .o files */ 361233409Sgonzo while ((instr = kdbpeek((int *)va)) == 0) 362233409Sgonzo va += sizeof(int); 363233409Sgonzo 364233409Sgonzo /* scan forwards to find stack size and any saved registers */ 365233409Sgonzo stksize = 0; 366233409Sgonzo more = 3; 367233409Sgonzo mask = 0; 368233409Sgonzo for (; more; va += sizeof(int), 369233409Sgonzo more = (more == 3) ? 3 : more - 1) { 370233409Sgonzo /* stop if hit our current position */ 371233409Sgonzo if (va >= *pc) 372233409Sgonzo break; 373233409Sgonzo instr = kdbpeek((int *)va); 374233409Sgonzo i.word = instr; 375233409Sgonzo switch (i.JType.op) { 376233409Sgonzo case OP_SPECIAL: 377233409Sgonzo switch (i.RType.func) { 378233409Sgonzo case OP_JR: 379233409Sgonzo case OP_JALR: 380233409Sgonzo more = 2; /* stop after next instruction */ 381233409Sgonzo break; 382233409Sgonzo 383233409Sgonzo case OP_SYSCALL: 384233409Sgonzo case OP_BREAK: 385233409Sgonzo more = 1; /* stop now */ 386233409Sgonzo }; 387233409Sgonzo break; 388233409Sgonzo 389233409Sgonzo case OP_BCOND: 390233409Sgonzo case OP_J: 391233409Sgonzo case OP_JAL: 392233409Sgonzo case OP_BEQ: 393233409Sgonzo case OP_BNE: 394233409Sgonzo case OP_BLEZ: 395233409Sgonzo case OP_BGTZ: 396233409Sgonzo more = 2; /* stop after next instruction */ 397233409Sgonzo break; 398233409Sgonzo 399233409Sgonzo case OP_COP0: 400233409Sgonzo case OP_COP1: 401233409Sgonzo case OP_COP2: 402233409Sgonzo case OP_COP3: 403233409Sgonzo switch (i.RType.rs) { 404233409Sgonzo case OP_BCx: 405233409Sgonzo case OP_BCy: 406233409Sgonzo more = 2; /* stop after next instruction */ 407233409Sgonzo }; 408233409Sgonzo break; 409233409Sgonzo 410233409Sgonzo case OP_SW: 411233409Sgonzo /* look for saved registers on the stack */ 412233409Sgonzo if (i.IType.rs != 29) 413233409Sgonzo break; 414233409Sgonzo /* only restore the first one */ 415233409Sgonzo if (mask & (1 << i.IType.rt)) 416233409Sgonzo break; 417233409Sgonzo mask |= (1 << i.IType.rt); 418233409Sgonzo addr = (vm_offset_t)(*sp + (short)i.IType.imm); 419233409Sgonzo switch (i.IType.rt) { 420233409Sgonzo case 4:/* a0 */ 421233409Sgonzo case 5:/* a1 */ 422233409Sgonzo case 6:/* a2 */ 423233409Sgonzo case 7:/* a3 */ 424233409Sgonzo#if defined(__mips_n64) || defined(__mips_n32) 425233409Sgonzo case 8:/* a4 */ 426233409Sgonzo case 9:/* a5 */ 427233409Sgonzo case 10:/* a6 */ 428233409Sgonzo case 11:/* a7 */ 429233409Sgonzo#endif 430233409Sgonzo arg = i.IType.rt - 4; 431233409Sgonzo if (args) 432233409Sgonzo args[arg] = kdbpeek((int*)addr); 433233409Sgonzo if (valid_args) 434233409Sgonzo valid_args[arg] = 1; 435233409Sgonzo break; 436233409Sgonzo case 31: /* ra */ 437233409Sgonzo ra = kdbpeek((int *)addr); 438233409Sgonzo } 439233409Sgonzo break; 440233409Sgonzo 441233409Sgonzo case OP_SD: 442233409Sgonzo /* look for saved registers on the stack */ 443233409Sgonzo if (i.IType.rs != 29) 444233409Sgonzo break; 445233409Sgonzo /* only restore the first one */ 446233409Sgonzo if (mask & (1 << i.IType.rt)) 447233409Sgonzo break; 448233409Sgonzo mask |= (1 << i.IType.rt); 449233409Sgonzo addr = (vm_offset_t)(*sp + (short)i.IType.imm); 450233409Sgonzo switch (i.IType.rt) { 451233409Sgonzo case 4:/* a0 */ 452233409Sgonzo case 5:/* a1 */ 453233409Sgonzo case 6:/* a2 */ 454233409Sgonzo case 7:/* a3 */ 455233409Sgonzo#if defined(__mips_n64) || defined(__mips_n32) 456233409Sgonzo case 8:/* a4 */ 457233409Sgonzo case 9:/* a5 */ 458233409Sgonzo case 10:/* a6 */ 459233409Sgonzo case 11:/* a7 */ 460233409Sgonzo#endif 461233409Sgonzo arg = i.IType.rt - 4; 462233409Sgonzo if (args) 463233409Sgonzo args[arg] = kdbpeekd((int *)addr); 464233409Sgonzo if (valid_args) 465233409Sgonzo valid_args[arg] = 1; 466233409Sgonzo break; 467233409Sgonzo 468233409Sgonzo case 31: /* ra */ 469233409Sgonzo ra = kdbpeekd((int *)addr); 470233409Sgonzo } 471233409Sgonzo break; 472233409Sgonzo 473233409Sgonzo case OP_ADDI: 474233409Sgonzo case OP_ADDIU: 475233409Sgonzo case OP_DADDI: 476233409Sgonzo case OP_DADDIU: 477233409Sgonzo /* look for stack pointer adjustment */ 478233409Sgonzo if (i.IType.rs != 29 || i.IType.rt != 29) 479233409Sgonzo break; 480233409Sgonzo stksize = -((short)i.IType.imm); 481233409Sgonzo } 482233409Sgonzo } 483233409Sgonzo 484233409Sgonzo if (!MIPS_IS_VALID_KERNELADDR(ra)) 485233409Sgonzo return (-1); 486233409Sgonzo 487233409Sgonzo *pc = ra; 488233409Sgonzo *sp += stksize; 489233409Sgonzo 490233525Sgonzo#if defined(__mips_o32) 491233525Sgonzo /* 492233525Sgonzo * For MIPS32 fill out arguments 5..8 from the stack 493233525Sgonzo */ 494233525Sgonzo for (arg = 4; arg < 8; arg++) { 495233525Sgonzo addr = (vm_offset_t)(*sp + arg*sizeof(register_t)); 496233525Sgonzo if (args) 497233525Sgonzo args[arg] = kdbpeekd((int *)addr); 498233525Sgonzo if (valid_args) 499233525Sgonzo valid_args[arg] = 1; 500233525Sgonzo } 501233525Sgonzo#endif 502233525Sgonzo 503233409Sgonzo return (0); 504233409Sgonzoerror: 505233409Sgonzo return (-1); 506233409Sgonzo} 507233409Sgonzo 508233409Sgonzostatic int 509233409Sgonzodtrace_next_uframe(register_t *pc, register_t *sp, register_t *ra) 510233409Sgonzo{ 511233409Sgonzo int offset, registers_on_stack; 512233409Sgonzo uint32_t opcode, mask; 513233409Sgonzo register_t function_start; 514233409Sgonzo int stksize; 515233409Sgonzo InstFmt i; 516233409Sgonzo 517233525Sgonzo volatile uint16_t *flags = 518233525Sgonzo (volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags; 519233525Sgonzo 520233409Sgonzo registers_on_stack = 0; 521233409Sgonzo mask = 0; 522233409Sgonzo function_start = 0; 523233409Sgonzo offset = 0; 524233409Sgonzo stksize = 0; 525233409Sgonzo 526233409Sgonzo while (offset < MAX_FUNCTION_SIZE) { 527233409Sgonzo opcode = dtrace_fuword32((void *)(vm_offset_t)(*pc - offset)); 528233409Sgonzo 529233525Sgonzo if (*flags & CPU_DTRACE_FAULT) 530233525Sgonzo goto fault; 531233525Sgonzo 532233409Sgonzo /* [d]addiu sp, sp, -X*/ 533233409Sgonzo if (((opcode & 0xffff8000) == 0x27bd8000) 534233409Sgonzo || ((opcode & 0xffff8000) == 0x67bd8000)) { 535233409Sgonzo function_start = *pc - offset; 536233409Sgonzo registers_on_stack = 1; 537233409Sgonzo break; 538233409Sgonzo } 539233409Sgonzo 540233409Sgonzo /* lui gp, X */ 541233409Sgonzo if ((opcode & 0xffff8000) == 0x3c1c0000) { 542233409Sgonzo /* 543233409Sgonzo * Function might start with this instruction 544233409Sgonzo * Keep an eye on "jr ra" and sp correction 545233409Sgonzo * with positive value further on 546233409Sgonzo */ 547233409Sgonzo function_start = *pc - offset; 548233409Sgonzo } 549233409Sgonzo 550233409Sgonzo if (function_start) { 551233409Sgonzo /* 552233409Sgonzo * Stop looking further. Possible end of 553233409Sgonzo * function instruction: it means there is no 554233409Sgonzo * stack modifications, sp is unchanged 555233409Sgonzo */ 556233409Sgonzo 557233409Sgonzo /* [d]addiu sp,sp,X */ 558233409Sgonzo if (((opcode & 0xffff8000) == 0x27bd0000) 559233409Sgonzo || ((opcode & 0xffff8000) == 0x67bd0000)) 560233409Sgonzo break; 561233409Sgonzo 562233409Sgonzo if (opcode == 0x03e00008) 563233409Sgonzo break; 564233409Sgonzo } 565233409Sgonzo 566233409Sgonzo offset += sizeof(int); 567233409Sgonzo } 568233409Sgonzo 569233409Sgonzo if (!function_start) 570233409Sgonzo return (-1); 571233409Sgonzo 572233409Sgonzo if (registers_on_stack) { 573233409Sgonzo offset = 0; 574233409Sgonzo while ((offset < MAX_PROLOGUE_SIZE) 575233409Sgonzo && ((function_start + offset) < *pc)) { 576233409Sgonzo i.word = 577233409Sgonzo dtrace_fuword32((void *)(vm_offset_t)(function_start + offset)); 578233409Sgonzo switch (i.JType.op) { 579233409Sgonzo case OP_SW: 580233409Sgonzo /* look for saved registers on the stack */ 581233409Sgonzo if (i.IType.rs != 29) 582233409Sgonzo break; 583233409Sgonzo /* only restore the first one */ 584233409Sgonzo if (mask & (1 << i.IType.rt)) 585233409Sgonzo break; 586233409Sgonzo mask |= (1 << i.IType.rt); 587233409Sgonzo if (i.IType.rt == 31) 588233409Sgonzo *ra = dtrace_fuword32((void *)(vm_offset_t)(*sp + (short)i.IType.imm)); 589233409Sgonzo break; 590233409Sgonzo 591233409Sgonzo case OP_SD: 592233409Sgonzo /* look for saved registers on the stack */ 593233409Sgonzo if (i.IType.rs != 29) 594233409Sgonzo break; 595233409Sgonzo /* only restore the first one */ 596233409Sgonzo if (mask & (1 << i.IType.rt)) 597233409Sgonzo break; 598233409Sgonzo mask |= (1 << i.IType.rt); 599233409Sgonzo /* ra */ 600233409Sgonzo if (i.IType.rt == 31) 601233409Sgonzo *ra = dtrace_fuword64((void *)(vm_offset_t)(*sp + (short)i.IType.imm)); 602233409Sgonzo break; 603233409Sgonzo 604233409Sgonzo case OP_ADDI: 605233409Sgonzo case OP_ADDIU: 606233409Sgonzo case OP_DADDI: 607233409Sgonzo case OP_DADDIU: 608233409Sgonzo /* look for stack pointer adjustment */ 609233409Sgonzo if (i.IType.rs != 29 || i.IType.rt != 29) 610233409Sgonzo break; 611233409Sgonzo stksize = -((short)i.IType.imm); 612233409Sgonzo } 613233409Sgonzo 614233409Sgonzo offset += sizeof(int); 615233525Sgonzo 616233525Sgonzo if (*flags & CPU_DTRACE_FAULT) 617233525Sgonzo goto fault; 618233409Sgonzo } 619233409Sgonzo } 620233409Sgonzo 621233409Sgonzo /* 622233409Sgonzo * We reached the end of backtrace 623233409Sgonzo */ 624233409Sgonzo if (*pc == *ra) 625233409Sgonzo return (-1); 626233409Sgonzo 627233409Sgonzo *pc = *ra; 628233409Sgonzo *sp += stksize; 629233409Sgonzo 630233409Sgonzo return (0); 631233525Sgonzofault: 632233525Sgonzo /* 633233525Sgonzo * We just got lost in backtrace, no big deal 634233525Sgonzo */ 635233525Sgonzo *flags &= ~CPU_DTRACE_FAULT; 636233525Sgonzo return (-1); 637233409Sgonzo} 638233409Sgonzo 639233409Sgonzostatic int 640233409Sgonzodtrace_copycheck(uintptr_t uaddr, uintptr_t kaddr, size_t size) 641233409Sgonzo{ 642233409Sgonzo 643233409Sgonzo if (uaddr + size > VM_MAXUSER_ADDRESS || uaddr + size < uaddr) { 644233409Sgonzo DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR); 645233409Sgonzo cpu_core[curcpu].cpuc_dtrace_illval = uaddr; 646233409Sgonzo return (0); 647233409Sgonzo } 648233409Sgonzo 649233409Sgonzo return (1); 650233409Sgonzo} 651233409Sgonzo 652233409Sgonzovoid 653233409Sgonzodtrace_copyin(uintptr_t uaddr, uintptr_t kaddr, size_t size, 654233409Sgonzo volatile uint16_t *flags) 655233409Sgonzo{ 656233409Sgonzo if (dtrace_copycheck(uaddr, kaddr, size)) 657233409Sgonzo dtrace_copy(uaddr, kaddr, size); 658233409Sgonzo} 659233409Sgonzo 660233409Sgonzovoid 661233409Sgonzodtrace_copyout(uintptr_t kaddr, uintptr_t uaddr, size_t size, 662233409Sgonzo volatile uint16_t *flags) 663233409Sgonzo{ 664233409Sgonzo if (dtrace_copycheck(uaddr, kaddr, size)) 665233409Sgonzo dtrace_copy(kaddr, uaddr, size); 666233409Sgonzo} 667233409Sgonzo 668233409Sgonzovoid 669233409Sgonzodtrace_copyinstr(uintptr_t uaddr, uintptr_t kaddr, size_t size, 670233409Sgonzo volatile uint16_t *flags) 671233409Sgonzo{ 672233409Sgonzo if (dtrace_copycheck(uaddr, kaddr, size)) 673233409Sgonzo dtrace_copystr(uaddr, kaddr, size, flags); 674233409Sgonzo} 675233409Sgonzo 676233409Sgonzovoid 677233409Sgonzodtrace_copyoutstr(uintptr_t kaddr, uintptr_t uaddr, size_t size, 678233409Sgonzo volatile uint16_t *flags) 679233409Sgonzo{ 680233409Sgonzo if (dtrace_copycheck(uaddr, kaddr, size)) 681233409Sgonzo dtrace_copystr(kaddr, uaddr, size, flags); 682233409Sgonzo} 683233409Sgonzo 684233409Sgonzouint8_t 685233409Sgonzodtrace_fuword8(void *uaddr) 686233409Sgonzo{ 687233409Sgonzo if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) { 688233409Sgonzo DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR); 689233409Sgonzo cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr; 690233409Sgonzo return (0); 691233409Sgonzo } 692233409Sgonzo return (dtrace_fuword8_nocheck(uaddr)); 693233409Sgonzo} 694233409Sgonzo 695233409Sgonzouint16_t 696233409Sgonzodtrace_fuword16(void *uaddr) 697233409Sgonzo{ 698233409Sgonzo if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) { 699233409Sgonzo DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR); 700233409Sgonzo cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr; 701233409Sgonzo return (0); 702233409Sgonzo } 703233409Sgonzo return (dtrace_fuword16_nocheck(uaddr)); 704233409Sgonzo} 705233409Sgonzo 706233409Sgonzouint32_t 707233409Sgonzodtrace_fuword32(void *uaddr) 708233409Sgonzo{ 709233409Sgonzo if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) { 710233409Sgonzo DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR); 711233409Sgonzo cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr; 712233409Sgonzo return (0); 713233409Sgonzo } 714233409Sgonzo return (dtrace_fuword32_nocheck(uaddr)); 715233409Sgonzo} 716233409Sgonzo 717233409Sgonzouint64_t 718233409Sgonzodtrace_fuword64(void *uaddr) 719233409Sgonzo{ 720233409Sgonzo if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) { 721233409Sgonzo DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR); 722233409Sgonzo cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr; 723233409Sgonzo return (0); 724233409Sgonzo } 725233409Sgonzo return (dtrace_fuword64_nocheck(uaddr)); 726233409Sgonzo} 727