Commit | Line | Data |
---|---|---|
6f7a3ac3 KB |
1 | /*- |
2 | * Copyright (c) 1991 The Regents of the University of California. | |
3 | * All rights reserved. | |
4 | * | |
ad787160 C |
5 | * This module is believed to contain source code proprietary to AT&T. |
6 | * Use and redistribution is subject to the Berkeley Software License | |
7 | * Agreement and your Software Agreement with AT&T (Western Electric). | |
6f7a3ac3 KB |
8 | */ |
9 | ||
f582ecdc | 10 | #ifndef lint |
ad787160 | 11 | static char sccsid[] = "@(#)kstack.c 5.4 (Berkeley) 4/4/91"; |
6f7a3ac3 | 12 | #endif /* not lint */ |
f582ecdc KB |
13 | |
14 | /* | |
15 | * adb - routines to probe the kernel stack when debugging post-mortem | |
16 | * crash dumps. | |
17 | */ | |
18 | ||
19 | #include "defs.h" | |
20 | #include <ctype.h> | |
21 | #include <machine/pte.h> | |
22 | #include <machine/frame.h> | |
71a2bd59 | 23 | #include <tahoe/tahoe/rpb.h> |
f582ecdc KB |
24 | |
25 | struct pte *sbr; | |
26 | int slr; | |
27 | struct pcb pcb; | |
28 | ||
29 | static caddr_t rpb, erpb; | |
30 | static caddr_t intstack, eintstack; | |
31 | static caddr_t ustack, eustack; | |
32 | ||
33 | char *malloc(); | |
34 | ||
35 | /* | |
36 | * Convert a kernel virtual address to an (off_t) physical offset. | |
37 | */ | |
38 | #define kvtooff(a) ((off_t)(a) & ~KERNBASE) | |
39 | ||
40 | /* | |
41 | * Check if an address is in one of the kernel's stacks: | |
42 | * interrupt stack, rpb stack (during restart sequence), | |
43 | * or u. stack. | |
44 | */ | |
45 | #define within(a, b, e) \ | |
46 | ((addr_t)(a) >= (addr_t)(b) && (addr_t)(a) < (addr_t)(e)) | |
47 | #define kstackaddr(a) \ | |
48 | (within(a, intstack, eintstack) || \ | |
49 | within(a, rpb + sizeof(struct rpb), erpb) || \ | |
50 | within(a, ustack, eustack)) | |
51 | ||
52 | /* | |
53 | * Determine whether we are looking at a kernel core dump, and if so, | |
54 | * set sbr and slr and the current pcb. | |
55 | */ | |
56 | getkcore() { | |
57 | struct nlist *sm, *ss, *mp; | |
58 | ||
59 | if ((sm = lookup("_Sysmap")) == NULL || | |
60 | (ss = lookup("_Syssize")) == NULL || | |
61 | (mp = lookup("_masterpaddr")) == NULL) | |
62 | return (0); /* a.out is not a vmunix */ | |
63 | datmap.m1.b = 0; | |
64 | datmap.m1.e = -1L; | |
65 | /* must set sbr, slr before setpcb() */ | |
66 | sbr = (struct pte *)sm->n_value; | |
67 | slr = ss->n_value; | |
68 | adbprintf("sbr %X slr %X\n", sbr, slr); | |
69 | setpcb((addr_t)mp->n_value); | |
70 | getpcb(); | |
71 | findstackframe(); | |
72 | return (1); | |
73 | } | |
74 | ||
75 | /* | |
76 | * A version of lookup that never returns failure, and which returns | |
77 | * the n_value field of the symbol found. | |
78 | */ | |
79 | static caddr_t | |
80 | xlookup(sym) | |
81 | char *sym; | |
82 | { | |
83 | struct nlist *sp; | |
84 | ||
85 | if ((sp = lookup(sym)) == NULL) { | |
86 | adbprintf("symbol %s not found ... bad kernel core?\n", sym); | |
87 | exit(1); | |
88 | } | |
89 | return ((caddr_t)sp->n_value); | |
90 | } | |
91 | ||
92 | /* | |
93 | * Find the current stack frame when debugging the kernel. | |
94 | * If we're looking at a crash dump and this was not a ``clean'' | |
95 | * crash, then we must search the interrupt stack carefully | |
96 | * looking for a valid frame. | |
97 | */ | |
98 | findstackframe() | |
99 | { | |
100 | register char *cp; | |
101 | register int n; | |
102 | caddr_t addr; | |
103 | struct frame fr; | |
104 | char buf[256]; | |
105 | ||
106 | if (readcore(kvtooff(xlookup("_panicstr")), (caddr_t)&addr, | |
107 | sizeof(addr)) != sizeof(addr) || addr == 0) | |
108 | return; | |
109 | n = readcore(kvtooff(addr), buf, sizeof(buf)); | |
110 | for (cp = buf; --n > 0 && *cp != 0; cp++) | |
111 | if (!isascii(*cp) || (!isprint(*cp) && !isspace(*cp))) | |
112 | *cp = '?'; | |
113 | *cp = '\0'; | |
114 | adbprintf("panic: %s\n", buf); | |
115 | ||
116 | /* | |
117 | * After a panic, look at the top of the rpb stack to find a stack | |
118 | * frame. If this was a clean crash, i.e. one which left the | |
119 | * interrupt and kernel stacks in a reasonable state, then we should | |
120 | * find a pointer to the proper stack frame here (at location | |
121 | * intstack-8). If we don't find a reasonable frame here, then we | |
122 | * must search down through the interrupt stack. | |
123 | */ | |
124 | intstack = xlookup("_intstack"); | |
125 | eintstack = xlookup("_Xdoadump"); /* XXX */ | |
126 | rpb = xlookup("_rsstk"); /* XXX */ | |
127 | erpb = rpb + NBPG - 2 * sizeof(long); | |
128 | ustack = xlookup("_u"); | |
129 | eustack = ustack + ctob(UPAGES); | |
130 | ustack += (int)((struct user *)0)->u_stack; | |
131 | (void) readcore(kvtooff(intstack - 8), (caddr_t)&addr, sizeof(addr)); | |
132 | if (!getframe(addr, &fr) && !checkintstack(&addr, &fr)) { | |
133 | /* search kernel stack? */ | |
134 | prints("can't locate stack frame\n"); | |
135 | return; | |
136 | } | |
137 | /* probably shouldn't clobber pcb, but for now this is easy */ | |
138 | pcb.pcb_fp = (int)addr; | |
139 | pcb.pcb_pc = fr.fr_savpc; | |
140 | } | |
141 | ||
142 | /* | |
143 | * Search interrupt stack for a valid frame. Return 1 if found, | |
144 | * also setting *addr to the kernel address of the frame, and *frame | |
145 | * to the frame at that address. | |
146 | */ | |
147 | checkintstack(addr, frame) | |
148 | caddr_t *addr; | |
149 | struct frame *frame; | |
150 | { | |
151 | register int ssize; | |
152 | register char *stack; | |
153 | ||
154 | ssize = eintstack - intstack; | |
155 | if ((stack = malloc((u_int)ssize)) == NULL) | |
156 | return (0); | |
157 | if (readcore(kvtooff(intstack), stack, ssize) != ssize) { | |
158 | free(stack); | |
159 | return (0); | |
160 | } | |
161 | for (ssize -= sizeof(*frame); ssize >= 0; ssize -= 4) { | |
162 | if (goodframe((struct frame *)&stack[ssize])) { | |
163 | *addr = &intstack[ssize] + FRAMEOFF; | |
164 | *frame = *(struct frame *)&stack[ssize]; | |
165 | free(stack); | |
166 | return (1); | |
167 | } | |
168 | } | |
169 | free(stack); | |
170 | return (0); | |
171 | } | |
172 | ||
173 | /* | |
174 | * Get a stack frame and verify that it looks like | |
175 | * something which might be on a kernel stack. Return 1 if OK. | |
176 | */ | |
177 | getframe(addr, fp) | |
178 | caddr_t addr; | |
179 | struct frame *fp; | |
180 | { | |
181 | off_t off; | |
182 | char *err = NULL; | |
183 | ||
184 | if (!kstackaddr(addr)) | |
185 | return (0); | |
186 | off = vtophys((addr_t)(addr - FRAMEOFF), &err); | |
187 | if (err || readcore(off, (caddr_t)fp, sizeof(*fp)) != sizeof(*fp)) | |
188 | return (0); | |
189 | return (goodframe(fp)); | |
190 | } | |
191 | ||
192 | /* | |
193 | * Check a call frame to see if it's ok as a kernel stack frame. | |
194 | * It should have its parent frame in the kernel stack, and should | |
195 | * return to kernel code. | |
196 | */ | |
197 | goodframe(fr) | |
198 | register struct frame *fr; | |
199 | { | |
200 | ||
201 | return (kstackaddr(fr->fr_savfp) && | |
202 | within(fr->fr_savpc, txtmap.m1.b, txtmap.m1.e)); | |
203 | } |