Home | History | Annotate | Line # | Download | only in i386
      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