1 /* $NetBSD: mips_stacktrace.c,v 1.9 2021/04/06 13:11:22 simonb Exp $ */ 2 3 /* 4 * Copyright (c) 1988 University of Utah. 5 * Copyright (c) 1992, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * This code is derived from software contributed to Berkeley by 9 * the Systems Programming Group of the University of Utah Computer 10 * Science Department and Ralph Campbell. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 3. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 * 36 * from: NetBSD: trap.c,v 1.255 2020/07/13 09:00:40 simonb Exp 37 * from: Utah Hdr: trap.c 1.32 91/04/06 38 * 39 * @(#)trap.c 8.5 (Berkeley) 1/11/94 40 */ 41 42 #include <sys/cdefs.h> 43 __KERNEL_RCSID(0, "$NetBSD: mips_stacktrace.c,v 1.9 2021/04/06 13:11:22 simonb Exp $"); 44 45 #ifdef _KERNEL_OPT 46 #include "opt_ddb.h" 47 #include "opt_kgdb.h" 48 #endif 49 50 #include <sys/param.h> 51 #include <sys/systm.h> 52 #include <sys/proc.h> 53 54 #include <mips/locore.h> 55 #include <mips/mips_opcode.h> 56 #include <mips/regnum.h> 57 #include <mips/stacktrace.h> 58 59 #if defined(_KMEMUSER) && !defined(DDB) 60 #define DDB 1 61 #endif 62 63 #ifdef DDB 64 #include <machine/db_machdep.h> 65 #include <ddb/db_sym.h> 66 #include <ddb/db_user.h> 67 #include <ddb/db_access.h> 68 #endif 69 70 #ifdef KGDB 71 #include <sys/kgdb.h> 72 #endif 73 74 #ifndef DDB_TRACE 75 76 #if defined(DEBUG) || defined(DDB) || defined(KGDB) || defined(geo) 77 78 extern char start[], edata[], verylocore[]; 79 #ifdef MIPS1 80 extern char mips1_kern_gen_exception[]; 81 extern char mips1_user_gen_exception[]; 82 extern char mips1_kern_intr[]; 83 extern char mips1_user_intr[]; 84 extern char mips1_systemcall[]; 85 #endif 86 #ifdef MIPS3 87 extern char mips3_kern_gen_exception[]; 88 extern char mips3_user_gen_exception[]; 89 extern char mips3_kern_intr[]; 90 extern char mips3_user_intr[]; 91 extern char mips3_systemcall[]; 92 #endif 93 #ifdef MIPS32 94 extern char mips32_kern_gen_exception[]; 95 extern char mips32_user_gen_exception[]; 96 extern char mips32_kern_intr[]; 97 extern char mips32_user_intr[]; 98 extern char mips32_systemcall[]; 99 #endif 100 #ifdef MIPS32R2 101 extern char mips32r2_kern_gen_exception[]; 102 extern char mips32r2_user_gen_exception[]; 103 extern char mips32r2_kern_intr[]; 104 extern char mips32r2_user_intr[]; 105 extern char mips32r2_systemcall[]; 106 #endif 107 #ifdef MIPS64 108 extern char mips64_kern_gen_exception[]; 109 extern char mips64_user_gen_exception[]; 110 extern char mips64_kern_intr[]; 111 extern char mips64_user_intr[]; 112 extern char mips64_systemcall[]; 113 #endif 114 #ifdef MIPS64R2 115 extern char mips64r2_kern_gen_exception[]; 116 extern char mips64r2_user_gen_exception[]; 117 extern char mips64r2_kern_intr[]; 118 extern char mips64r2_user_intr[]; 119 extern char mips64r2_systemcall[]; 120 #endif 121 122 #define MIPS_JR_RA 0x03e00008 /* instruction code for jr ra */ 123 #define MIPS_JR_K0 0x03400008 /* instruction code for jr k0 */ 124 #define MIPS_ERET 0x42000018 /* instruction code for eret */ 125 126 int main(void *); /* XXX */ 127 128 /* 129 * Functions ``special'' enough to print by name 130 */ 131 #define Name(_fn) { (void*)_fn, # _fn } 132 static const struct { void *addr; const char *name;} names[] = { 133 #ifdef _KERNEL 134 Name(stacktrace), 135 Name(stacktrace_subr), 136 Name(main), 137 Name(trap), 138 139 #ifdef MIPS1 /* r2000 family (mips-I CPU) */ 140 Name(mips1_kern_gen_exception), 141 Name(mips1_user_gen_exception), 142 Name(mips1_systemcall), 143 Name(mips1_kern_intr), 144 Name(mips1_user_intr), 145 #endif /* MIPS1 */ 146 147 #if defined(MIPS3) /* r4000 family (mips-III CPU) */ 148 Name(mips3_kern_gen_exception), 149 Name(mips3_user_gen_exception), 150 Name(mips3_systemcall), 151 Name(mips3_kern_intr), 152 Name(mips3_user_intr), 153 #endif /* MIPS3 */ 154 155 #if defined(MIPS32) /* MIPS32 family (mips-III CPU) */ 156 Name(mips32_kern_gen_exception), 157 Name(mips32_user_gen_exception), 158 Name(mips32_systemcall), 159 Name(mips32_kern_intr), 160 Name(mips32_user_intr), 161 #endif /* MIPS32 */ 162 163 #if defined(MIPS32R2) /* MIPS32R2 family (mips-III CPU) */ 164 Name(mips32r2_kern_gen_exception), 165 Name(mips32r2_user_gen_exception), 166 Name(mips32r2_systemcall), 167 Name(mips32r2_kern_intr), 168 Name(mips32r2_user_intr), 169 #endif /* MIPS32R2 */ 170 171 #if defined(MIPS64) /* MIPS64 family (mips-III CPU) */ 172 Name(mips64_kern_gen_exception), 173 Name(mips64_user_gen_exception), 174 Name(mips64_systemcall), 175 Name(mips64_kern_intr), 176 Name(mips64_user_intr), 177 #endif /* MIPS64 */ 178 179 #if defined(MIPS64R2) /* MIPS64R2 family (mips-III CPU) */ 180 Name(mips64r2_kern_gen_exception), 181 Name(mips64r2_user_gen_exception), 182 Name(mips64r2_systemcall), 183 Name(mips64r2_kern_intr), 184 Name(mips64r2_user_intr), 185 #endif /* MIPS64R2 */ 186 187 Name(cpu_idle), 188 Name(cpu_switchto), 189 #endif /* _KERNEL */ 190 {0, 0} 191 }; 192 193 194 bool 195 kdbpeek(vaddr_t addr, unsigned *valp) 196 { 197 if (addr & 3) { 198 printf("kdbpeek: unaligned address %#"PRIxVADDR"\n", addr); 199 /* We might have been called from DDB, so do not go there. */ 200 return false; 201 } else if (addr == 0) { 202 printf("kdbpeek: NULL\n"); 203 return false; 204 } else { 205 #if _KERNEL 206 *valp = *(unsigned *)addr; 207 #else 208 db_read_bytes((db_addr_t)addr, sizeof(unsigned), (char *)valp); 209 #endif 210 return true; 211 } 212 } 213 214 mips_reg_t 215 kdbrpeek(vaddr_t addr, size_t n) 216 { 217 mips_reg_t rc = 0; 218 219 if (addr & (n - 1)) { 220 printf("kdbrpeek: unaligned address %#"PRIxVADDR"\n", addr); 221 #if _KERNEL 222 /* We might have been called from DDB, so do not go there. */ 223 stacktrace(); 224 #endif 225 rc = -1; 226 } else if (addr == 0) { 227 printf("kdbrpeek: NULL\n"); 228 rc = 0xdeadfeed; 229 } else { 230 if (sizeof(mips_reg_t) == 8 && n == 8) 231 #if _KERNEL 232 rc = *(int64_t *)addr; 233 else 234 rc = *(int32_t *)addr; 235 #else 236 db_read_bytes((db_addr_t)addr, sizeof(int64_t), (char *)&rc); 237 else 238 db_read_bytes((db_addr_t)addr, sizeof(int32_t), (char *)&rc); 239 #endif 240 } 241 return rc; 242 } 243 244 /* 245 * Map a function address to a string name, if known; or a hex string. 246 */ 247 static const char * 248 fn_name(vaddr_t addr) 249 { 250 static char buf[17]; 251 int i = 0; 252 #ifdef DDB 253 db_expr_t diff; 254 db_sym_t sym; 255 const char *symname; 256 #endif 257 258 #ifdef DDB 259 diff = 0; 260 symname = NULL; 261 sym = db_search_symbol(addr, DB_STGY_ANY, &diff); 262 db_symbol_values(sym, &symname, 0); 263 if (symname && diff == 0) 264 return (symname); 265 #endif 266 for (i = 0; names[i].name; i++) 267 if (names[i].addr == (void*)addr) 268 return (names[i].name); 269 snprintf(buf, sizeof(buf), "%#"PRIxVADDR, addr); 270 return (buf); 271 } 272 273 /* 274 * Do a stack backtrace. 275 * (*printfn)() prints the output to either the system log, 276 * the console, or both. 277 */ 278 void 279 stacktrace_subr(mips_reg_t a0, mips_reg_t a1, mips_reg_t a2, mips_reg_t a3, 280 vaddr_t pc, vaddr_t sp, vaddr_t fp, vaddr_t ra, 281 void (*printfn)(const char*, ...)) 282 { 283 vaddr_t va, subr; 284 unsigned instr, mask; 285 InstFmt i; 286 int more, stksize; 287 unsigned int frames = 0; 288 int foundframesize = 0; 289 mips_reg_t regs[32] = { 290 [_R_ZERO] = 0, 291 [_R_A0] = a0, [_R_A1] = a1, [_R_A2] = a2, [_R_A3] = a3, 292 [_R_RA] = ra, 293 }; 294 295 /* Jump here when done with a frame, to start a new one */ 296 loop: 297 stksize = 0; 298 subr = 0; 299 mask = 1; 300 if (frames++ > 100) { 301 (*printfn)("\nstackframe count exceeded\n"); 302 /* return breaks stackframe-size heuristics with gcc -O2 */ 303 goto finish; /*XXX*/ 304 } 305 306 /* check for bad SP: could foul up next frame */ 307 if ((sp & (sizeof(sp)-1)) || (intptr_t)sp >= 0) { 308 (*printfn)("SP 0x%x: not in kernel\n", sp); 309 ra = 0; 310 subr = 0; 311 goto done; 312 } 313 314 /* Check for bad PC */ 315 if (pc & 3 || (intptr_t)pc >= 0 || (intptr_t)pc >= (intptr_t)edata) { 316 (*printfn)("PC 0x%x: not in kernel space\n", pc); 317 ra = 0; 318 goto done; 319 } 320 321 #if defined(DDB) && defined(_KERNEL) 322 if (ksyms_available()) { 323 db_expr_t diff; 324 db_sym_t sym; 325 326 /* 327 * Check the kernel symbol table to see the beginning of 328 * the current subroutine. 329 */ 330 diff = 0; 331 sym = db_search_symbol(pc, DB_STGY_ANY, &diff); 332 if (sym != DB_SYM_NULL && diff == 0) { 333 /* check func(foo) __attribute__((__noreturn__)) case */ 334 if (!kdbpeek(pc - 2 * sizeof(unsigned), &instr)) 335 return; 336 i.word = instr; 337 if (i.JType.op == OP_JAL) { 338 sym = db_search_symbol(pc - sizeof(int), 339 DB_STGY_ANY, &diff); 340 if (sym != DB_SYM_NULL && diff != 0) 341 diff += sizeof(int); 342 } 343 } 344 if (sym == DB_SYM_NULL) { 345 ra = 0; 346 goto done; 347 } 348 va = pc - diff; 349 } else { 350 #endif /* DDB && _KERNEL */ 351 /* 352 * Find the beginning of the current subroutine by 353 * scanning backwards from the current PC for the end 354 * of the previous subroutine. 355 * 356 * XXX This won't work well because nowadays gcc is so 357 * aggressive as to reorder instruction blocks for 358 * branch-predict. (i.e. 'jr ra' wouldn't indicate 359 * the end of subroutine) 360 */ 361 va = pc; 362 do { 363 va -= sizeof(int); 364 #ifdef _KERNEL /* XXX crash */ 365 if (va <= (vaddr_t)verylocore) 366 goto finish; 367 #endif 368 if (!kdbpeek(va, &instr)) 369 return; 370 if (instr == MIPS_ERET) 371 goto mips3_eret; 372 } while (instr != MIPS_JR_RA && instr != MIPS_JR_K0); 373 /* skip back over branch & delay slot */ 374 va += sizeof(int); 375 mips3_eret: 376 va += sizeof(int); 377 /* skip over nulls which might separate .o files */ 378 instr = 0; 379 while (instr == 0) { 380 if (!kdbpeek(va, &instr)) 381 return; 382 va += sizeof(int); 383 } 384 #if defined(DDB) && defined(_KERNEL) 385 } 386 #endif /* DDB && _KERNEL */ 387 subr = va; 388 389 /* scan forwards to find stack size and any saved registers */ 390 stksize = 0; 391 more = 3; 392 mask &= 0x40ff0001; /* if s0-s8 are valid, leave then as valid */ 393 foundframesize = 0; 394 for (va = subr; more; va += sizeof(int), 395 more = (more == 3) ? 3 : more - 1) { 396 /* stop if hit our current position */ 397 if (va >= pc) 398 break; 399 if (!kdbpeek(va, &instr)) 400 return; 401 i.word = instr; 402 switch (i.JType.op) { 403 case OP_SPECIAL: 404 switch (i.RType.func) { 405 case OP_JR: 406 case OP_JALR: 407 more = 2; /* stop after next instruction */ 408 break; 409 410 case OP_ADD: 411 case OP_ADDU: 412 case OP_DADD: 413 case OP_DADDU: 414 if (!(mask & (1 << i.RType.rd)) 415 || !(mask & (1 << i.RType.rt))) 416 break; 417 if (i.RType.rd != _R_ZERO) 418 break; 419 mask |= (1 << i.RType.rs); 420 regs[i.RType.rs] = regs[i.RType.rt]; 421 if (i.RType.func >= OP_DADD) 422 break; 423 regs[i.RType.rs] = (int32_t)regs[i.RType.rs]; 424 break; 425 426 case OP_SYSCALL: 427 case OP_BREAK: 428 more = 1; /* stop now */ 429 break; 430 } 431 break; 432 433 case OP_REGIMM: 434 case OP_J: 435 case OP_JAL: 436 case OP_BEQ: 437 case OP_BNE: 438 case OP_BLEZ: 439 case OP_BGTZ: 440 more = 2; /* stop after next instruction */ 441 break; 442 443 case OP_COP0: 444 case OP_COP1: 445 case OP_COP2: 446 case OP_COP3: 447 switch (i.RType.rs) { 448 case OP_BCx: 449 case OP_BCy: 450 more = 2; /* stop after next instruction */ 451 }; 452 break; 453 454 case OP_SW: 455 #if !defined(__mips_o32) 456 case OP_SD: 457 #endif 458 { 459 size_t size = (i.JType.op == OP_SW) ? 4 : 8; 460 461 /* look for saved registers on the stack */ 462 if (i.IType.rs != _R_SP) 463 break; 464 switch (i.IType.rt) { 465 case _R_A0: /* a0 */ 466 case _R_A1: /* a1 */ 467 case _R_A2: /* a2 */ 468 case _R_A3: /* a3 */ 469 case _R_S0: /* s0 */ 470 case _R_S1: /* s1 */ 471 case _R_S2: /* s2 */ 472 case _R_S3: /* s3 */ 473 case _R_S4: /* s4 */ 474 case _R_S5: /* s5 */ 475 case _R_S6: /* s6 */ 476 case _R_S7: /* s7 */ 477 case _R_S8: /* s8 */ 478 case _R_RA: /* ra */ 479 regs[i.IType.rt] = 480 kdbrpeek(sp + (int16_t)i.IType.imm, size); 481 mask |= (1 << i.IType.rt); 482 break; 483 } 484 break; 485 } 486 487 case OP_ADDI: 488 case OP_ADDIU: 489 #if !defined(__mips_o32) 490 case OP_DADDI: 491 case OP_DADDIU: 492 #endif 493 /* look for stack pointer adjustment */ 494 if (i.IType.rs != _R_SP || i.IType.rt != _R_SP) 495 break; 496 /* don't count pops for mcount */ 497 if (!foundframesize) { 498 stksize = - ((short)i.IType.imm); 499 foundframesize = 1; 500 } 501 break; 502 } 503 } 504 done: 505 if (mask & (1 << _R_RA)) 506 ra = regs[_R_RA]; 507 (*printfn)("%#"PRIxVADDR": %s+%#"PRIxVADDR" (%#"PRIxREGISTER"," 508 "%#"PRIxREGISTER",%#"PRIxREGISTER",%#"PRIxREGISTER") " 509 "ra %#"PRIxVADDR" sz %d\n", 510 sp, fn_name(subr), pc - subr, 511 regs[_R_A0], regs[_R_A1], regs[_R_A2], regs[_R_A3], 512 ra, stksize); 513 514 if (ra) { 515 if (pc == ra && stksize == 0) 516 (*printfn)("stacktrace: loop!\n"); 517 else { 518 pc = ra; 519 sp += stksize; 520 ra = 0; 521 goto loop; 522 } 523 } else { 524 finish: 525 #ifdef _KERNEL 526 (*printfn)("User-level: pid %d.%d\n", 527 curlwp->l_proc->p_pid, curlwp->l_lid); 528 #else 529 (*printfn)("User-level: FIXME\n"); 530 #endif 531 } 532 } 533 534 #endif /* DEBUG || DDB || KGDB || geo */ 535 #endif /* DDB_TRACE */ 536