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 (at) 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 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 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 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 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