move stacktrace_subr() from trap.c into new mips_stacktrace.c so it can be shared between ddb, other mips kernel components (see locore), and an upcoming crash(8) port. remove second copy of kdbpeek() (hidden by old DDB_TRACE option, but they're functionally equivalent.) tested on octeon. Index: conf/files.mips =================================================================== RCS file: /cvsroot/src/sys/arch/mips/conf/files.mips,v retrieving revision 1.78 diff -p -u -u -r1.78 files.mips --- conf/files.mips 9 Aug 2020 08:49:00 -0000 1.78 +++ conf/files.mips 15 Aug 2020 06:20:09 -0000 @@ -46,6 +46,7 @@ file arch/mips/mips/db_trace.c ddb file arch/mips/mips/ipifuncs.c multiprocessor file arch/mips/mips/kgdb_machdep.c kgdb file arch/mips/mips/pmap_machdep.c +file arch/mips/mips/mips_stacktrace.c file uvm/pmap/pmap.c file uvm/pmap/pmap_segtab.c file uvm/pmap/pmap_synci.c Index: include/locore.h =================================================================== RCS file: /cvsroot/src/sys/arch/mips/include/locore.h,v retrieving revision 1.113 diff -p -u -u -r1.113 locore.h --- include/locore.h 31 Jul 2020 08:54:09 -0000 1.113 +++ include/locore.h 15 Aug 2020 06:20:09 -0000 @@ -667,7 +667,6 @@ int32_t kfetch_32(volatile uint32_t *, u /* trap.c */ void netintr(void); -bool kdbpeek(vaddr_t, int *); /* mips_dsp.c */ void dsp_init(void); Index: include/stacktrace.h =================================================================== RCS file: include/stacktrace.h diff -N include/stacktrace.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ include/stacktrace.h 15 Aug 2020 06:20:09 -0000 @@ -0,0 +1,40 @@ +/* $NetBSD$ */ + +/* + * Copyright (c) 2020 Matthew R. Green + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _MIPS_STACKTRACE_H_ +#define _MIPS_STACKTRACE_H_ + +#include + +void stacktrace_subr(mips_reg_t, mips_reg_t, mips_reg_t, mips_reg_t, + vaddr_t, vaddr_t, vaddr_t, vaddr_t, + void (*)(const char*, ...)); + +#endif /* _MIPS_STACKTRACE_H_ */ Index: mips/db_interface.c =================================================================== RCS file: /cvsroot/src/sys/arch/mips/mips/db_interface.c,v retrieving revision 1.88 diff -p -u -u -r1.88 db_interface.c --- mips/db_interface.c 12 Aug 2020 07:37:39 -0000 1.88 +++ mips/db_interface.c 15 Aug 2020 06:20:09 -0000 @@ -94,18 +94,6 @@ paddr_t kvtophys(vaddr_t); CTASSERT(sizeof(ddb_regs) == sizeof(struct reg)); -#ifdef DDB_TRACE -bool -kdbpeek(vaddr_t addr, int *valp) -{ - - if (addr == 0 || (addr & 3)) - return false; - *valp = *(int *)addr; - return true; -} -#endif - #ifndef KGDB int kdb_trap(int type, struct reg *regs) Index: mips/db_trace.c =================================================================== RCS file: /cvsroot/src/sys/arch/mips/mips/db_trace.c,v retrieving revision 1.43 diff -p -u -u -r1.43 db_trace.c --- mips/db_trace.c 6 Jun 2015 22:19:07 -0000 1.43 +++ mips/db_trace.c 15 Aug 2020 06:20:09 -0000 @@ -38,8 +38,10 @@ __KERNEL_RCSID(0, "$NetBSD: db_trace.c,v #include #include +#include #include +#include #include #include @@ -69,11 +71,6 @@ db_sym_t localsym(db_sym_t sym, bool isr */ struct mips_saved_state *db_cur_exc_frame = 0; -/*XXX*/ -void stacktrace_subr(mips_reg_t, mips_reg_t, mips_reg_t, mips_reg_t, - vaddr_t, vaddr_t, vaddr_t, vaddr_t, - void (*)(const char*, ...)); - /* * Stack trace helper. */ Index: mips/mips_stacktrace.c =================================================================== RCS file: mips/mips_stacktrace.c diff -N mips/mips_stacktrace.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ mips/mips_stacktrace.c 15 Aug 2020 06:20:09 -0000 @@ -0,0 +1,503 @@ +/* $NetBSD$ */ + +/* + * Copyright (c) 1988 University of Utah. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * the Systems Programming Group of the University of Utah Computer + * Science Department and Ralph Campbell. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: NetBSD: trap.c,v 1.255 2020/07/13 09:00:40 simonb Exp + * from: Utah Hdr: trap.c 1.32 91/04/06 + * + * @(#)trap.c 8.5 (Berkeley) 1/11/94 + */ + +#include +__KERNEL_RCSID(0, "$NetBSD$"); + +#ifdef _KERNEL_OPT +#include "opt_ddb.h" +#include "opt_kgdb.h" +#endif + +#include +#include +#include + +#include +#include +#include + +#ifdef DDB +#include +#include +#endif + +#ifdef KGDB +#include +#endif + +#ifndef DDB_TRACE + +#if defined(DEBUG) || defined(DDB) || defined(KGDB) || defined(geo) + +extern char start[], edata[], verylocore[]; +#ifdef MIPS1 +extern char mips1_kern_gen_exception[]; +extern char mips1_user_gen_exception[]; +extern char mips1_kern_intr[]; +extern char mips1_user_intr[]; +extern char mips1_systemcall[]; +#endif +#ifdef MIPS3 +extern char mips3_kern_gen_exception[]; +extern char mips3_user_gen_exception[]; +extern char mips3_kern_intr[]; +extern char mips3_user_intr[]; +extern char mips3_systemcall[]; +#endif +#ifdef MIPS32 +extern char mips32_kern_gen_exception[]; +extern char mips32_user_gen_exception[]; +extern char mips32_kern_intr[]; +extern char mips32_user_intr[]; +extern char mips32_systemcall[]; +#endif +#ifdef MIPS32R2 +extern char mips32r2_kern_gen_exception[]; +extern char mips32r2_user_gen_exception[]; +extern char mips32r2_kern_intr[]; +extern char mips32r2_user_intr[]; +extern char mips32r2_systemcall[]; +#endif +#ifdef MIPS64 +extern char mips64_kern_gen_exception[]; +extern char mips64_user_gen_exception[]; +extern char mips64_kern_intr[]; +extern char mips64_user_intr[]; +extern char mips64_systemcall[]; +#endif +#ifdef MIPS64R2 +extern char mips64r2_kern_gen_exception[]; +extern char mips64r2_user_gen_exception[]; +extern char mips64r2_kern_intr[]; +extern char mips64r2_user_intr[]; +extern char mips64r2_systemcall[]; +#endif + +#define MIPS_JR_RA 0x03e00008 /* instruction code for jr ra */ +#define MIPS_JR_K0 0x03400008 /* instruction code for jr k0 */ +#define MIPS_ERET 0x42000018 /* instruction code for eret */ + +int main(void *); /* XXX */ + +/* + * Functions ``special'' enough to print by name + */ +#define Name(_fn) { (void*)_fn, # _fn } +const static struct { void *addr; const char *name;} names[] = { + Name(stacktrace), + Name(stacktrace_subr), + Name(main), + Name(trap), + +#ifdef MIPS1 /* r2000 family (mips-I CPU) */ + Name(mips1_kern_gen_exception), + Name(mips1_user_gen_exception), + Name(mips1_systemcall), + Name(mips1_kern_intr), + Name(mips1_user_intr), +#endif /* MIPS1 */ + +#if defined(MIPS3) /* r4000 family (mips-III CPU) */ + Name(mips3_kern_gen_exception), + Name(mips3_user_gen_exception), + Name(mips3_systemcall), + Name(mips3_kern_intr), + Name(mips3_user_intr), +#endif /* MIPS3 */ + +#if defined(MIPS32) /* MIPS32 family (mips-III CPU) */ + Name(mips32_kern_gen_exception), + Name(mips32_user_gen_exception), + Name(mips32_systemcall), + Name(mips32_kern_intr), + Name(mips32_user_intr), +#endif /* MIPS32 */ + +#if defined(MIPS32R2) /* MIPS32R2 family (mips-III CPU) */ + Name(mips32r2_kern_gen_exception), + Name(mips32r2_user_gen_exception), + Name(mips32r2_systemcall), + Name(mips32r2_kern_intr), + Name(mips32r2_user_intr), +#endif /* MIPS32R2 */ + +#if defined(MIPS64) /* MIPS64 family (mips-III CPU) */ + Name(mips64_kern_gen_exception), + Name(mips64_user_gen_exception), + Name(mips64_systemcall), + Name(mips64_kern_intr), + Name(mips64_user_intr), +#endif /* MIPS64 */ + +#if defined(MIPS64R2) /* MIPS64R2 family (mips-III CPU) */ + Name(mips64r2_kern_gen_exception), + Name(mips64r2_user_gen_exception), + Name(mips64r2_systemcall), + Name(mips64r2_kern_intr), + Name(mips64r2_user_intr), +#endif /* MIPS64R2 */ + + Name(cpu_idle), + Name(cpu_switchto), + {0, 0} +}; + + +bool +static kdbpeek(vaddr_t addr, int *valp) +{ + if (addr & 3) { + printf("kdbpeek: unaligned address %#"PRIxVADDR"\n", addr); + /* We might have been called from DDB, so do not go there. */ + return false; + } else if (addr == 0) { + printf("kdbpeek: NULL\n"); + return false; + } else { + *valp = *(int *)addr; + return true; + } +} + +static mips_reg_t +kdbrpeek(vaddr_t addr, size_t n) +{ + mips_reg_t rc; + + if (addr & (n - 1)) { + printf("kdbrpeek: unaligned address %#"PRIxVADDR"\n", addr); +#if _KERNEL + /* We might have been called from DDB, so do not go there. */ + stacktrace(); +#endif + rc = -1 ; + } else if (addr == 0) { + printf("kdbrpeek: NULL\n"); + rc = 0xdeadfeed; + } else { + if (sizeof(mips_reg_t) == 8 && n == 8) + rc = *(int64_t *)addr; + else + rc = *(int32_t *)addr; + } + return rc; +} + +/* + * Map a function address to a string name, if known; or a hex string. + */ +static const char * +fn_name(vaddr_t addr) +{ + static char buf[17]; + int i = 0; +#ifdef DDB + db_expr_t diff; + db_sym_t sym; + const char *symname; +#endif + +#ifdef DDB + diff = 0; + symname = NULL; + sym = db_search_symbol(addr, DB_STGY_ANY, &diff); + db_symbol_values(sym, &symname, 0); + if (symname && diff == 0) + return (symname); +#endif + for (i = 0; names[i].name; i++) + if (names[i].addr == (void*)addr) + return (names[i].name); + snprintf(buf, sizeof(buf), "%#"PRIxVADDR, addr); + return (buf); +} + +/* + * Do a stack backtrace. + * (*printfn)() prints the output to either the system log, + * the console, or both. + */ +void +stacktrace_subr(mips_reg_t a0, mips_reg_t a1, mips_reg_t a2, mips_reg_t a3, + vaddr_t pc, vaddr_t sp, vaddr_t fp, vaddr_t ra, + void (*printfn)(const char*, ...)) +{ + vaddr_t va, subr; + unsigned instr, mask; + InstFmt i; + int more, stksize; + unsigned int frames = 0; + int foundframesize = 0; + mips_reg_t regs[32] = { + [_R_ZERO] = 0, + [_R_A0] = a0, [_R_A1] = a1, [_R_A2] = a2, [_R_A3] = a3, + [_R_RA] = ra, + }; +#ifdef DDB + db_expr_t diff; + db_sym_t sym; +#endif + +/* Jump here when done with a frame, to start a new one */ +loop: + stksize = 0; + subr = 0; + mask = 1; + if (frames++ > 100) { + (*printfn)("\nstackframe count exceeded\n"); + /* return breaks stackframe-size heuristics with gcc -O2 */ + goto finish; /*XXX*/ + } + + /* check for bad SP: could foul up next frame */ + if ((sp & (sizeof(sp)-1)) || (intptr_t)sp >= 0) { + (*printfn)("SP 0x%x: not in kernel\n", sp); + ra = 0; + subr = 0; + goto done; + } + + /* Check for bad PC */ + if (pc & 3 || (intptr_t)pc >= 0 || (intptr_t)pc >= (intptr_t)edata) { + (*printfn)("PC 0x%x: not in kernel space\n", pc); + ra = 0; + goto done; + } + +#ifdef DDB + /* + * Check the kernel symbol table to see the beginning of + * the current subroutine. + */ + diff = 0; + sym = db_search_symbol(pc, DB_STGY_ANY, &diff); + if (sym != DB_SYM_NULL && diff == 0) { + /* check func(foo) __attribute__((__noreturn__)) case */ + if (!kdbpeek(pc - 2 * sizeof(int), &instr)) + return; + i.word = instr; + if (i.JType.op == OP_JAL) { + sym = db_search_symbol(pc - sizeof(int), + DB_STGY_ANY, &diff); + if (sym != DB_SYM_NULL && diff != 0) + diff += sizeof(int); + } + } + if (sym == DB_SYM_NULL) { + ra = 0; + goto done; + } + va = pc - diff; +#else + /* + * Find the beginning of the current subroutine by scanning backwards + * from the current PC for the end of the previous subroutine. + * + * XXX This won't work well because nowadays gcc is so aggressive + * as to reorder instruction blocks for branch-predict. + * (i.e. 'jr ra' wouldn't indicate the end of subroutine) + */ + va = pc; + do { + va -= sizeof(int); + if (va <= (vaddr_t)verylocore) + goto finish; + if (!kdbpeek(va, &instr)) + return; + if (instr == MIPS_ERET) + goto mips3_eret; + } while (instr != MIPS_JR_RA && instr != MIPS_JR_K0); + /* skip back over branch & delay slot */ + va += sizeof(int); +mips3_eret: + va += sizeof(int); + /* skip over nulls which might separate .o files */ + instr = 0; + while (instr == 0) { + if (!kdbpeek(va, &instr)) + return; + va += sizeof(int); + } +#endif + subr = va; + + /* scan forwards to find stack size and any saved registers */ + stksize = 0; + more = 3; + mask &= 0x40ff0001; /* if s0-s8 are valid, leave then as valid */ + foundframesize = 0; + for (va = subr; more; va += sizeof(int), + more = (more == 3) ? 3 : more - 1) { + /* stop if hit our current position */ + if (va >= pc) + break; + if (!kdbpeek(va, &instr)) + return; + i.word = instr; + switch (i.JType.op) { + case OP_SPECIAL: + switch (i.RType.func) { + case OP_JR: + case OP_JALR: + more = 2; /* stop after next instruction */ + break; + + case OP_ADD: + case OP_ADDU: + case OP_DADD: + case OP_DADDU: + if (!(mask & (1 << i.RType.rd)) + || !(mask & (1 << i.RType.rt))) + break; + if (i.RType.rd != _R_ZERO) + break; + mask |= (1 << i.RType.rs); + regs[i.RType.rs] = regs[i.RType.rt]; + if (i.RType.func >= OP_DADD) + break; + regs[i.RType.rs] = (int32_t)regs[i.RType.rs]; + break; + + case OP_SYSCALL: + case OP_BREAK: + more = 1; /* stop now */ + break; + } + break; + + case OP_REGIMM: + case OP_J: + case OP_JAL: + case OP_BEQ: + case OP_BNE: + case OP_BLEZ: + case OP_BGTZ: + more = 2; /* stop after next instruction */ + break; + + case OP_COP0: + case OP_COP1: + case OP_COP2: + case OP_COP3: + switch (i.RType.rs) { + case OP_BCx: + case OP_BCy: + more = 2; /* stop after next instruction */ + }; + break; + + case OP_SW: +#if !defined(__mips_o32) + case OP_SD: +#endif + { + size_t size = (i.JType.op == OP_SW) ? 4 : 8; + + /* look for saved registers on the stack */ + if (i.IType.rs != _R_SP) + break; + switch (i.IType.rt) { + case _R_A0: /* a0 */ + case _R_A1: /* a1 */ + case _R_A2: /* a2 */ + case _R_A3: /* a3 */ + case _R_S0: /* s0 */ + case _R_S1: /* s1 */ + case _R_S2: /* s2 */ + case _R_S3: /* s3 */ + case _R_S4: /* s4 */ + case _R_S5: /* s5 */ + case _R_S6: /* s6 */ + case _R_S7: /* s7 */ + case _R_S8: /* s8 */ + case _R_RA: /* ra */ + regs[i.IType.rt] = + kdbrpeek(sp + (int16_t)i.IType.imm, size); + mask |= (1 << i.IType.rt); + break; + } + break; + } + + case OP_ADDI: + case OP_ADDIU: +#if !defined(__mips_o32) + case OP_DADDI: + case OP_DADDIU: +#endif + /* look for stack pointer adjustment */ + if (i.IType.rs != _R_SP || i.IType.rt != _R_SP) + break; + /* don't count pops for mcount */ + if (!foundframesize) { + stksize = - ((short)i.IType.imm); + foundframesize = 1; + } + break; + } + } +done: + if (mask & (1 << _R_RA)) + ra = regs[_R_RA]; + (*printfn)("%#"PRIxVADDR": %s+%"PRIxVADDR" (%"PRIxREGISTER",%"PRIxREGISTER",%"PRIxREGISTER",%"PRIxREGISTER") ra %"PRIxVADDR" sz %d\n", + sp, fn_name(subr), pc - subr, + regs[_R_A0], regs[_R_A1], regs[_R_A2], regs[_R_A3], + ra, stksize); + + if (ra) { + if (pc == ra && stksize == 0) + (*printfn)("stacktrace: loop!\n"); + else { + pc = ra; + sp += stksize; + ra = 0; + goto loop; + } + } else { +finish: + (*printfn)("User-level: pid %d.%d\n", + curlwp->l_proc->p_pid, curlwp->l_lid); + } +} + +#endif /* DEBUG || DDB || KGDB || geo */ +#endif /* DDB_TRACE */ Index: mips/trap.c =================================================================== RCS file: /cvsroot/src/sys/arch/mips/mips/trap.c,v retrieving revision 1.255 diff -p -u -u -r1.255 trap.c --- mips/trap.c 13 Jul 2020 09:00:40 -0000 1.255 +++ mips/trap.c 15 Aug 2020 06:20:09 -0000 @@ -753,449 +753,3 @@ mips_singlestep(struct lwp *l) #endif return 0; } - - -#ifndef DDB_TRACE - -#if defined(DEBUG) || defined(DDB) || defined(KGDB) || defined(geo) -mips_reg_t kdbrpeek(vaddr_t, size_t); - -bool -kdbpeek(vaddr_t addr, int *valp) -{ - if (addr & 3) { - printf("kdbpeek: unaligned address %#"PRIxVADDR"\n", addr); - /* We might have been called from DDB, so do not go there. */ - return false; - } else if (addr == 0) { - printf("kdbpeek: NULL\n"); - return false; - } else { - *valp = *(int *)addr; - return true; - } -} - -mips_reg_t -kdbrpeek(vaddr_t addr, size_t n) -{ - mips_reg_t rc; - - if (addr & (n - 1)) { - printf("kdbrpeek: unaligned address %#"PRIxVADDR"\n", addr); - /* We might have been called from DDB, so do not go there. */ - stacktrace(); - rc = -1 ; - } else if (addr == 0) { - printf("kdbrpeek: NULL\n"); - rc = 0xdeadfeed; - } else { - if (sizeof(mips_reg_t) == 8 && n == 8) - rc = *(int64_t *)addr; - else - rc = *(int32_t *)addr; - } - return rc; -} - -extern char start[], edata[], verylocore[]; -#ifdef MIPS1 -extern char mips1_kern_gen_exception[]; -extern char mips1_user_gen_exception[]; -extern char mips1_kern_intr[]; -extern char mips1_user_intr[]; -extern char mips1_systemcall[]; -#endif -#ifdef MIPS3 -extern char mips3_kern_gen_exception[]; -extern char mips3_user_gen_exception[]; -extern char mips3_kern_intr[]; -extern char mips3_user_intr[]; -extern char mips3_systemcall[]; -#endif -#ifdef MIPS32 -extern char mips32_kern_gen_exception[]; -extern char mips32_user_gen_exception[]; -extern char mips32_kern_intr[]; -extern char mips32_user_intr[]; -extern char mips32_systemcall[]; -#endif -#ifdef MIPS32R2 -extern char mips32r2_kern_gen_exception[]; -extern char mips32r2_user_gen_exception[]; -extern char mips32r2_kern_intr[]; -extern char mips32r2_user_intr[]; -extern char mips32r2_systemcall[]; -#endif -#ifdef MIPS64 -extern char mips64_kern_gen_exception[]; -extern char mips64_user_gen_exception[]; -extern char mips64_kern_intr[]; -extern char mips64_user_intr[]; -extern char mips64_systemcall[]; -#endif -#ifdef MIPS64R2 -extern char mips64r2_kern_gen_exception[]; -extern char mips64r2_user_gen_exception[]; -extern char mips64r2_kern_intr[]; -extern char mips64r2_user_intr[]; -extern char mips64r2_systemcall[]; -#endif - -int main(void *); /* XXX */ - -/* - * stack trace code, also useful to DDB one day - */ - -/* forward */ -const char *fn_name(vaddr_t addr); -void stacktrace_subr(mips_reg_t, mips_reg_t, mips_reg_t, mips_reg_t, - vaddr_t, vaddr_t, vaddr_t, vaddr_t, void (*)(const char*, ...)); - -#define MIPS_JR_RA 0x03e00008 /* instruction code for jr ra */ -#define MIPS_JR_K0 0x03400008 /* instruction code for jr k0 */ -#define MIPS_ERET 0x42000018 /* instruction code for eret */ - -/* - * Do a stack backtrace. - * (*printfn)() prints the output to either the system log, - * the console, or both. - */ -void -stacktrace_subr(mips_reg_t a0, mips_reg_t a1, mips_reg_t a2, mips_reg_t a3, - vaddr_t pc, vaddr_t sp, vaddr_t fp, vaddr_t ra, - void (*printfn)(const char*, ...)) -{ - vaddr_t va, subr; - unsigned instr, mask; - InstFmt i; - int more, stksize; - unsigned int frames = 0; - int foundframesize = 0; - mips_reg_t regs[32] = { - [_R_ZERO] = 0, - [_R_A0] = a0, [_R_A1] = a1, [_R_A2] = a2, [_R_A3] = a3, - [_R_RA] = ra, - }; -#ifdef DDB - db_expr_t diff; - db_sym_t sym; -#endif - -/* Jump here when done with a frame, to start a new one */ -loop: - stksize = 0; - subr = 0; - mask = 1; - if (frames++ > 100) { - (*printfn)("\nstackframe count exceeded\n"); - /* return breaks stackframe-size heuristics with gcc -O2 */ - goto finish; /*XXX*/ - } - - /* check for bad SP: could foul up next frame */ - if ((sp & (sizeof(sp)-1)) || (intptr_t)sp >= 0) { - (*printfn)("SP 0x%x: not in kernel\n", sp); - ra = 0; - subr = 0; - goto done; - } - - /* Check for bad PC */ - if (pc & 3 || (intptr_t)pc >= 0 || (intptr_t)pc >= (intptr_t)edata) { - (*printfn)("PC 0x%x: not in kernel space\n", pc); - ra = 0; - goto done; - } - -#ifdef DDB - /* - * Check the kernel symbol table to see the beginning of - * the current subroutine. - */ - diff = 0; - sym = db_search_symbol(pc, DB_STGY_ANY, &diff); - if (sym != DB_SYM_NULL && diff == 0) { - /* check func(foo) __attribute__((__noreturn__)) case */ - if (!kdbpeek(pc - 2 * sizeof(int), &instr)) - return; - i.word = instr; - if (i.JType.op == OP_JAL) { - sym = db_search_symbol(pc - sizeof(int), - DB_STGY_ANY, &diff); - if (sym != DB_SYM_NULL && diff != 0) - diff += sizeof(int); - } - } - if (sym == DB_SYM_NULL) { - ra = 0; - goto done; - } - va = pc - diff; -#else - /* - * Find the beginning of the current subroutine by scanning backwards - * from the current PC for the end of the previous subroutine. - * - * XXX This won't work well because nowadays gcc is so aggressive - * as to reorder instruction blocks for branch-predict. - * (i.e. 'jr ra' wouldn't indicate the end of subroutine) - */ - va = pc; - do { - va -= sizeof(int); - if (va <= (vaddr_t)verylocore) - goto finish; - if (!kdbpeek(va, &instr)) - return; - if (instr == MIPS_ERET) - goto mips3_eret; - } while (instr != MIPS_JR_RA && instr != MIPS_JR_K0); - /* skip back over branch & delay slot */ - va += sizeof(int); -mips3_eret: - va += sizeof(int); - /* skip over nulls which might separate .o files */ - instr = 0; - while (instr == 0) { - if (!kdbpeek(va, &instr)) - return; - va += sizeof(int); - } -#endif - subr = va; - - /* scan forwards to find stack size and any saved registers */ - stksize = 0; - more = 3; - mask &= 0x40ff0001; /* if s0-s8 are valid, leave then as valid */ - foundframesize = 0; - for (va = subr; more; va += sizeof(int), - more = (more == 3) ? 3 : more - 1) { - /* stop if hit our current position */ - if (va >= pc) - break; - if (!kdbpeek(va, &instr)) - return; - i.word = instr; - switch (i.JType.op) { - case OP_SPECIAL: - switch (i.RType.func) { - case OP_JR: - case OP_JALR: - more = 2; /* stop after next instruction */ - break; - - case OP_ADD: - case OP_ADDU: - case OP_DADD: - case OP_DADDU: - if (!(mask & (1 << i.RType.rd)) - || !(mask & (1 << i.RType.rt))) - break; - if (i.RType.rd != _R_ZERO) - break; - mask |= (1 << i.RType.rs); - regs[i.RType.rs] = regs[i.RType.rt]; - if (i.RType.func >= OP_DADD) - break; - regs[i.RType.rs] = (int32_t)regs[i.RType.rs]; - break; - - case OP_SYSCALL: - case OP_BREAK: - more = 1; /* stop now */ - break; - } - break; - - case OP_REGIMM: - case OP_J: - case OP_JAL: - case OP_BEQ: - case OP_BNE: - case OP_BLEZ: - case OP_BGTZ: - more = 2; /* stop after next instruction */ - break; - - case OP_COP0: - case OP_COP1: - case OP_COP2: - case OP_COP3: - switch (i.RType.rs) { - case OP_BCx: - case OP_BCy: - more = 2; /* stop after next instruction */ - }; - break; - - case OP_SW: -#if !defined(__mips_o32) - case OP_SD: -#endif - { - size_t size = (i.JType.op == OP_SW) ? 4 : 8; - - /* look for saved registers on the stack */ - if (i.IType.rs != _R_SP) - break; - switch (i.IType.rt) { - case _R_A0: /* a0 */ - case _R_A1: /* a1 */ - case _R_A2: /* a2 */ - case _R_A3: /* a3 */ - case _R_S0: /* s0 */ - case _R_S1: /* s1 */ - case _R_S2: /* s2 */ - case _R_S3: /* s3 */ - case _R_S4: /* s4 */ - case _R_S5: /* s5 */ - case _R_S6: /* s6 */ - case _R_S7: /* s7 */ - case _R_S8: /* s8 */ - case _R_RA: /* ra */ - regs[i.IType.rt] = - kdbrpeek(sp + (int16_t)i.IType.imm, size); - mask |= (1 << i.IType.rt); - break; - } - break; - } - - case OP_ADDI: - case OP_ADDIU: -#if !defined(__mips_o32) - case OP_DADDI: - case OP_DADDIU: -#endif - /* look for stack pointer adjustment */ - if (i.IType.rs != _R_SP || i.IType.rt != _R_SP) - break; - /* don't count pops for mcount */ - if (!foundframesize) { - stksize = - ((short)i.IType.imm); - foundframesize = 1; - } - break; - } - } -done: - if (mask & (1 << _R_RA)) - ra = regs[_R_RA]; - (*printfn)("%#"PRIxVADDR": %s+%"PRIxVADDR" (%"PRIxREGISTER",%"PRIxREGISTER",%"PRIxREGISTER",%"PRIxREGISTER") ra %"PRIxVADDR" sz %d\n", - sp, fn_name(subr), pc - subr, - regs[_R_A0], regs[_R_A1], regs[_R_A2], regs[_R_A3], - ra, stksize); - - if (ra) { - if (pc == ra && stksize == 0) - (*printfn)("stacktrace: loop!\n"); - else { - pc = ra; - sp += stksize; - ra = 0; - goto loop; - } - } else { -finish: - (*printfn)("User-level: pid %d.%d\n", - curlwp->l_proc->p_pid, curlwp->l_lid); - } -} - -/* - * Functions ``special'' enough to print by name - */ -#define Name(_fn) { (void*)_fn, # _fn } -const static struct { void *addr; const char *name;} names[] = { - Name(stacktrace), - Name(stacktrace_subr), - Name(main), - Name(trap), - -#ifdef MIPS1 /* r2000 family (mips-I CPU) */ - Name(mips1_kern_gen_exception), - Name(mips1_user_gen_exception), - Name(mips1_systemcall), - Name(mips1_kern_intr), - Name(mips1_user_intr), -#endif /* MIPS1 */ - -#if defined(MIPS3) /* r4000 family (mips-III CPU) */ - Name(mips3_kern_gen_exception), - Name(mips3_user_gen_exception), - Name(mips3_systemcall), - Name(mips3_kern_intr), - Name(mips3_user_intr), -#endif /* MIPS3 */ - -#if defined(MIPS32) /* MIPS32 family (mips-III CPU) */ - Name(mips32_kern_gen_exception), - Name(mips32_user_gen_exception), - Name(mips32_systemcall), - Name(mips32_kern_intr), - Name(mips32_user_intr), -#endif /* MIPS32 */ - -#if defined(MIPS32R2) /* MIPS32R2 family (mips-III CPU) */ - Name(mips32r2_kern_gen_exception), - Name(mips32r2_user_gen_exception), - Name(mips32r2_systemcall), - Name(mips32r2_kern_intr), - Name(mips32r2_user_intr), -#endif /* MIPS32R2 */ - -#if defined(MIPS64) /* MIPS64 family (mips-III CPU) */ - Name(mips64_kern_gen_exception), - Name(mips64_user_gen_exception), - Name(mips64_systemcall), - Name(mips64_kern_intr), - Name(mips64_user_intr), -#endif /* MIPS64 */ - -#if defined(MIPS64R2) /* MIPS64R2 family (mips-III CPU) */ - Name(mips64r2_kern_gen_exception), - Name(mips64r2_user_gen_exception), - Name(mips64r2_systemcall), - Name(mips64r2_kern_intr), - Name(mips64r2_user_intr), -#endif /* MIPS64R2 */ - - Name(cpu_idle), - Name(cpu_switchto), - {0, 0} -}; - -/* - * Map a function address to a string name, if known; or a hex string. - */ -const char * -fn_name(vaddr_t addr) -{ - static char buf[17]; - int i = 0; -#ifdef DDB - db_expr_t diff; - db_sym_t sym; - const char *symname; -#endif - -#ifdef DDB - diff = 0; - symname = NULL; - sym = db_search_symbol(addr, DB_STGY_ANY, &diff); - db_symbol_values(sym, &symname, 0); - if (symname && diff == 0) - return (symname); -#endif - for (i = 0; names[i].name; i++) - if (names[i].addr == (void*)addr) - return (names[i].name); - snprintf(buf, sizeof(buf), "%#"PRIxVADDR, addr); - return (buf); -} - -#endif /* DEBUG */ -#endif /* DDB_TRACE */