1 /* $NetBSD: db_machdep.c,v 1.10 2022/12/24 14:47:47 uwe Exp $ */
2
3 /*
4 * Mach Operating System
5 * Copyright (c) 1991,1990 Carnegie Mellon University
6 * All Rights Reserved.
7 *
8 * Permission to use, copy, modify and distribute this software and its
9 * documentation is hereby granted, provided that both the copyright
10 * notice and this permission notice appear in all copies of the
11 * software, derivative works or modified versions, and any portions
12 * thereof, and that both notices appear in supporting documentation.
13 *
14 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
15 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
16 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
17 *
18 * Carnegie Mellon requests users of this software to return to
19 *
20 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
21 * School of Computer Science
22 * Carnegie Mellon University
23 * Pittsburgh PA 15213-3890
24 *
25 * any improvements or extensions that they make and grant Carnegie the
26 * rights to redistribute these changes.
27 */
28
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: db_machdep.c,v 1.10 2022/12/24 14:47:47 uwe Exp $");
31
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/proc.h>
35
36 #ifndef _KERNEL
37 #include <stdbool.h>
38 #endif
39
40 #include <machine/frame.h>
41 #include <machine/trap.h>
42 #include <machine/intrdefs.h>
43 #include <machine/cpu.h>
44
45 #include <uvm/uvm_prot.h>
46 /* We need to include both for ddb and crash(8). */
47 #include <uvm/uvm_pmap.h>
48 #include <machine/pmap.h>
49
50 #include <machine/db_machdep.h>
51 #include <ddb/db_sym.h>
52 #include <ddb/db_access.h>
53 #include <ddb/db_variables.h>
54 #include <ddb/db_output.h>
55 #include <ddb/db_interface.h>
56 #include <ddb/db_user.h>
57 #include <ddb/db_proc.h>
58 #include <ddb/db_command.h>
59 #include <ddb/db_cpu.h>
60 #include <x86/db_machdep.h>
61
62 #define dbreg(xx) (long *)offsetof(db_regs_t, tf_ ## xx)
63
64 /*
65 * Machine register set.
66 */
67 const struct db_variable db_regs[] = {
68 { "ds", dbreg(ds), db_x86_regop, NULL },
69 { "es", dbreg(es), db_x86_regop, NULL },
70 { "fs", dbreg(fs), db_x86_regop, NULL },
71 { "gs", dbreg(gs), db_x86_regop, NULL },
72 { "edi", dbreg(edi), db_x86_regop, NULL },
73 { "esi", dbreg(esi), db_x86_regop, NULL },
74 { "ebp", dbreg(ebp), db_x86_regop, NULL },
75 { "ebx", dbreg(ebx), db_x86_regop, NULL },
76 { "edx", dbreg(edx), db_x86_regop, NULL },
77 { "ecx", dbreg(ecx), db_x86_regop, NULL },
78 { "eax", dbreg(eax), db_x86_regop, NULL },
79 { "eip", dbreg(eip), db_x86_regop, NULL },
80 { "cs", dbreg(cs), db_x86_regop, NULL },
81 { "eflags", dbreg(eflags), db_x86_regop, NULL },
82 { "esp", dbreg(esp), db_x86_regop, NULL },
83 { "ss", dbreg(ss), db_x86_regop, NULL },
84 };
85 const struct db_variable * const db_eregs =
86 db_regs + sizeof(db_regs)/sizeof(db_regs[0]);
87
88 /*
89 * Figure out how many arguments were passed into the frame at "fp".
90 */
91 int
db_numargs(long * retaddrp)92 db_numargs(long *retaddrp)
93 {
94 int *argp;
95 int inst;
96 int args;
97 extern char etext[];
98
99 argp = (int *)db_get_value((int)retaddrp, 4, false);
100 if (argp < (int *)VM_MIN_KERNEL_ADDRESS || argp > (int *)etext) {
101 args = 10;
102 } else {
103 inst = db_get_value((int)argp, 4, false);
104 if ((inst & 0xff) == 0x59) /* popl %ecx */
105 args = 1;
106 else if ((inst & 0xffff) == 0xc483) /* addl %n, %esp */
107 args = ((inst >> 16) & 0xff) / 4;
108 else
109 args = 10;
110 }
111 return (args);
112 }
113
114 /*
115 * Figure out the next frame up in the call stack.
116 * For trap(), we print the address of the faulting instruction and
117 * proceed with the calling frame. We return the ip that faulted.
118 * If the trap was caused by jumping through a bogus pointer, then
119 * the next line in the backtrace will list some random function as
120 * being called. It should get the argument list correct, though.
121 * It might be possible to dig out from the next frame up the name
122 * of the function that faulted, but that could get hairy.
123 */
124 int
db_nextframe(long ** nextframe,long ** retaddr,long ** arg0,db_addr_t * ip,long * argp,int is_trap,void (* pr)(const char *,...))125 db_nextframe(long **nextframe, long **retaddr, long **arg0, db_addr_t *ip,
126 long *argp, int is_trap, void (*pr)(const char *, ...))
127 {
128 static struct trapframe tf;
129 static struct i386tss tss;
130 struct i386_frame *fp;
131 uintptr_t ptr;
132
133 switch (is_trap) {
134 case NONE:
135 *ip = (db_addr_t)
136 db_get_value((int)*retaddr, 4, false);
137 fp = (struct i386_frame *)
138 db_get_value((int)*nextframe, 4, false);
139 if (fp == NULL)
140 return 0;
141 *nextframe = (long *)&fp->f_frame;
142 *retaddr = (long *)&fp->f_retaddr;
143 *arg0 = (long *)&fp->f_arg0;
144 break;
145
146 case TRAP_TSS:
147 case INTERRUPT_TSS:
148 ptr = db_get_value((int)argp, 4, false);
149 db_read_bytes((db_addr_t)ptr, sizeof(tss), (char *)&tss);
150 *ip = tss.__tss_eip;
151 fp = (struct i386_frame *)tss.tss_ebp;
152 if (fp == NULL)
153 return 0;
154 *nextframe = (long *)&fp->f_frame;
155 *retaddr = (long *)&fp->f_retaddr;
156 *arg0 = (long *)&fp->f_arg0;
157 if (is_trap == INTERRUPT_TSS)
158 (*pr)("--- interrupt via task gate ---\n");
159 else
160 (*pr)("--- trap via task gate ---\n");
161 break;
162
163 case TRAP:
164 case SYSCALL:
165 case INTERRUPT:
166 case SOFTINTR:
167 default:
168 /* The only argument to trap() or syscall() is the trapframe. */
169 switch (is_trap) {
170 case TRAP:
171 ptr = db_get_value((int)argp, 4, false);
172 db_read_bytes((db_addr_t)ptr, sizeof(tf), (char *)&tf);
173 (*pr)("--- trap (number %d) ---\n", tf.tf_trapno);
174 break;
175 case SYSCALL:
176 ptr = db_get_value((int)argp, 4, false);
177 db_read_bytes((db_addr_t)ptr, sizeof(tf), (char *)&tf);
178 (*pr)("--- syscall (number %d) ---\n", tf.tf_eax);
179 break;
180 case INTERRUPT:
181 (*pr)("--- interrupt ---\n");
182 /*
183 * see the "XXX -1 here is a hack" comment below.
184 */
185 db_read_bytes((db_addr_t)argp, sizeof(tf), (char *)&tf);
186 break;
187 case SOFTINTR:
188 (*pr)("--- softint ---\n");
189 tf.tf_eip = 0;
190 tf.tf_ebp = 0;
191 break;
192 }
193 *ip = (db_addr_t)tf.tf_eip;
194 fp = (struct i386_frame *)tf.tf_ebp;
195 if (fp == NULL)
196 return 0;
197 *nextframe = (long *)&fp->f_frame;
198 *retaddr = (long *)&fp->f_retaddr;
199 *arg0 = (long *)&fp->f_arg0;
200 break;
201 }
202
203 /*
204 * A bit of a hack. Since %ebp may be used in the stub code,
205 * walk the stack looking for a valid interrupt frame. Such
206 * a frame can be recognized by always having
207 * err 0 or IREENT_MAGIC and trapno T_ASTFLT.
208 */
209 int traptype = NONE;
210 db_sym_t sym = db_frame_info(*nextframe, (db_addr_t)*ip,
211 NULL, NULL, &traptype, NULL);
212 if (sym != DB_SYM_NULL && traptype == INTERRUPT) {
213 struct intrframe *ifp;
214 int trapno;
215 int err;
216
217 /*
218 * 2nd argument of interrupt handlers is a pointer to intrframe.
219 */
220 ifp = (struct intrframe *)
221 db_get_value((db_addr_t)(argp + 1), sizeof(ifp), false);
222 /*
223 * check if it's a valid intrframe.
224 */
225 err = db_get_value((db_addr_t)&ifp->__if_err,
226 sizeof(ifp->__if_err), false);
227 trapno = db_get_value((db_addr_t)&ifp->__if_trapno,
228 sizeof(ifp->__if_trapno), false);
229 if ((err == 0 || err == IREENT_MAGIC) && trapno == T_ASTFLT) {
230 /*
231 * found seemingly valid intrframe.
232 *
233 * XXX -1 here is a hack.
234 * for the next frame, we will be called with
235 * argp = *nextframe + 2. (long *)if - 1 + 2 = &tf.
236 */
237 *nextframe = (long *)ifp - 1;
238 } else {
239 (*pr)("DDB lost frame for ");
240 db_printsym(*ip, DB_STGY_ANY, pr);
241 (*pr)(", trying %p\n",argp);
242 *nextframe = argp;
243 }
244 }
245 return 1;
246 }
247
248 db_sym_t
db_frame_info(long * frame,db_addr_t callpc,const char ** namep,db_expr_t * offp,int * is_trap,int * nargp)249 db_frame_info(long *frame, db_addr_t callpc, const char **namep,
250 db_expr_t *offp, int *is_trap, int *nargp)
251 {
252 db_expr_t offset;
253 db_sym_t sym;
254 int narg;
255 const char *name;
256
257 sym = db_search_symbol(callpc, DB_STGY_ANY, &offset);
258 if (sym != DB_SYM_NULL && offset == 0) {
259 sym = db_search_symbol(callpc - 1, DB_STGY_ANY, &offset);
260 offset++;
261 }
262 db_symbol_values(sym, &name, NULL);
263 if (sym == DB_SYM_NULL)
264 return DB_SYM_NULL;
265
266 *is_trap = NONE;
267 narg = MAXNARG;
268
269 if (INKERNEL((int)frame) && name) {
270 /*
271 * XXX traps should be based off of the Xtrap*
272 * locations rather than on trap, since some traps
273 * (e.g., npxdna) don't go through trap()
274 */
275 if (!strcmp(name, "trap_tss")) {
276 *is_trap = TRAP_TSS;
277 narg = 0;
278 } else if (!strcmp(name, "trap")) {
279 *is_trap = TRAP;
280 narg = 0;
281 } else if (!strcmp(name, "syscall")) {
282 *is_trap = SYSCALL;
283 narg = 0;
284 } else if (name[0] == 'X') {
285 if (!strncmp(name, "Xintr", 5) ||
286 !strncmp(name, "Xresume", 7) ||
287 !strncmp(name, "Xstray", 6) ||
288 !strncmp(name, "Xhold", 5) ||
289 !strncmp(name, "Xrecurse", 8) ||
290 !strcmp(name, "Xdoreti")) {
291 *is_trap = INTERRUPT;
292 narg = 0;
293 } else if (!strcmp(name, "Xsoftintr")) {
294 *is_trap = SOFTINTR;
295 narg = 0;
296 } else if (!strncmp(name, "Xtss_", 5)) {
297 *is_trap = INTERRUPT_TSS;
298 narg = 0;
299 }
300 }
301 }
302
303 if (offp != NULL)
304 *offp = offset;
305 if (nargp != NULL)
306 *nargp = narg;
307 if (namep != NULL)
308 *namep = name;
309 return sym;
310 }
311
312 bool
db_intrstack_p(const void * vp)313 db_intrstack_p(const void *vp)
314 {
315 struct cpu_info *ci;
316 const char *cp;
317
318 for (ci = db_cpu_first(); ci != NULL; ci = db_cpu_next(ci)) {
319 db_read_bytes((db_addr_t)&ci->ci_intrstack, sizeof(cp),
320 (char *)&cp);
321 if (cp == NULL) {
322 continue;
323 }
324 if ((cp - INTRSTACKSIZE + 4) <= (const char *)vp &&
325 (const char *)vp <= cp) {
326 return true;
327 }
328 }
329 return false;
330 }
331