/*- * Copyright (c) 1991 The Regents of the University of California. * All rights reserved. * * This module is believed to contain source code proprietary to AT&T. * Use and redistribution is subject to the Berkeley Software License * Agreement and your Software Agreement with AT&T (Western Electric). */ #ifndef lint static char sccsid[] = "@(#)kstack.c 5.3 (Berkeley) 4/11/91"; #endif /* not lint */ /* * adb - routines to probe the kernel stack when debugging post-mortem * crash dumps. */ #include "defs.h" #include <ctype.h> #include <machine/pte.h> #include <machine/frame.h> #include <vax/vax/rpb.h> struct pte *sbr; int slr; struct pcb pcb; static caddr_t rpb; static caddr_t scb; static caddr_t intstack, eintstack; static caddr_t ustack, eustack; char *malloc(); /* * Convert a kernel virtual address to an (off_t) physical offset. */ #define kvtooff(a) ((off_t)(a) & ~KERNBASE) /* * Check if an address is in one of the kernel's stacks: * interrupt stack, rpb stack (during restart sequence), * or u. stack. */ #define within(a, b, e) \ ((addr_t)(a) >= (addr_t)(b) && (addr_t)(a) < (addr_t)(e)) #define kstackaddr(a) \ (within(a, intstack, eintstack) || \ within(a, rpb + sizeof(struct rpb), scb) || \ within(a, ustack, eustack)) /* * Determine whether we are looking at a kernel core dump, and if so, * set sbr and slr and the current pcb. */ getkcore() { struct nlist *sm, *ss, *mp; if ((sm = lookup("_Sysmap")) == NULL || (ss = lookup("_Syssize")) == NULL || (mp = lookup("_masterpaddr")) == NULL) return (0); /* a.out is not a vmunix */ datmap.m1.b = 0; datmap.m1.e = -1L; /* must set sbr, slr before setpcb() */ sbr = (struct pte *)sm->n_value; slr = ss->n_value; adbprintf("sbr %X slr %X\n", sbr, slr); setpcb((addr_t)mp->n_value); getpcb(); findstackframe(); return (1); } /* * A version of lookup that never returns failure, and which returns * the n_value field of the symbol found. */ static caddr_t xlookup(sym) char *sym; { struct nlist *sp; if ((sp = lookup(sym)) == NULL) { adbprintf("symbol %s not found ... bad kernel core?\n", sym); exit(1); } return ((caddr_t)sp->n_value); } /* * Find the current stack frame when debugging the kernel. * If we're looking at a crash dump and this was not a ``clean'' * crash, then we must search the interrupt stack carefully * looking for a valid frame. */ findstackframe() { register char *cp; register int n; caddr_t addr; struct frame fr; char buf[256]; if (readcore(kvtooff(xlookup("_panicstr")), (caddr_t)&addr, sizeof(addr)) != sizeof(addr) || addr == 0) return; n = readcore(kvtooff(addr), buf, sizeof(buf)); for (cp = buf; --n > 0 && *cp != 0; cp++) if (!isascii(*cp) || (!isprint(*cp) && !isspace(*cp))) *cp = '?'; *cp = '\0'; adbprintf("panic: %s\n", buf); /* * After a panic, look at the top of the rpb stack to find a stack * frame. If this was a clean crash, i.e. one which left the * interrupt and kernel stacks in a reasonable state, then we should * find a pointer to the proper stack frame here (at location scb-4). * If we don't find a reasonable frame here, then we must search down * through the interrupt stack. */ intstack = xlookup("_intstack"); eintstack = xlookup("_doadump"); /* XXX */ rpb = xlookup("_rpb"); scb = xlookup("_scb"); ustack = xlookup("_u"); eustack = ustack + ctob(UPAGES); ustack += (int)((struct user *)0)->u_stack; (void) readcore(kvtooff(scb - 4), (caddr_t)&addr, sizeof(addr)); if (!getframe(addr, &fr) && !checkintstack(&addr, &fr)) { /* search kernel stack? */ prints("can't locate stack frame\n"); return; } /* probably shouldn't clobber pcb, but for now this is easy */ pcb.pcb_fp = (int)addr; pcb.pcb_pc = fr.fr_savpc; pcb.pcb_ap = (int)addr + sizeof(fr) + fr.fr_spa; for (n = fr.fr_mask; n != 0; n >>= 1) if (n & 1) pcb.pcb_ap += 4; } /* * Search interrupt stack for a valid frame. Return 1 if found, * also setting *addr to the kernel address of the frame, and *frame * to the frame at that address. */ checkintstack(addr, frame) caddr_t *addr; struct frame *frame; { register int ssize; register char *stack; ssize = eintstack - intstack; if ((stack = malloc((u_int)ssize)) == NULL) return (0); if (readcore(kvtooff(intstack), stack, ssize) != ssize) { free(stack); return (0); } for (ssize -= sizeof(*frame); ssize >= 0; ssize -= 4) { if (goodframe((struct frame *)&stack[ssize])) { *addr = &intstack[ssize]; *frame = *(struct frame *)&stack[ssize]; free(stack); return (1); } } free(stack); return (0); } /* * Get a stack frame and verify that it looks like * something which might be on a kernel stack. Return 1 if OK. */ getframe(addr, fp) caddr_t addr; struct frame *fp; { off_t off; char *err = NULL; if (!kstackaddr(addr)) return (0); off = vtophys((addr_t)addr, &err); if (err || readcore(off, (caddr_t)fp, sizeof(*fp)) != sizeof(*fp)) return (0); return (goodframe(fp)); } /* * Check a call frame to see if it's ok as a kernel stack frame. * It should be a calls, should have its parent within the kernel stack, * and should return to kernel code. */ goodframe(fr) register struct frame *fr; { return (fr->fr_handler == 0 && fr->fr_s && kstackaddr(fr->fr_savap) && kstackaddr(fr->fr_savfp) && within(fr->fr_savpc, txtmap.m1.b, txtmap.m1.e)); }