xref: /netbsd-src/sys/arch/amd64/amd64/db_machdep.c (revision 3932dd14904bf18f0623f2144f4b13786df3b31d)
1 /*	$NetBSD: db_machdep.c,v 1.15 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 #include <sys/cdefs.h>
29 __KERNEL_RCSID(0, "$NetBSD: db_machdep.c,v 1.15 2022/12/24 14:47:47 uwe Exp $");
30 
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/proc.h>
34 #include <sys/syscall.h>
35 
36 #include <machine/frame.h>
37 #include <machine/trap.h>
38 #include <machine/intrdefs.h>
39 
40 #include <machine/db_machdep.h>
41 #include <ddb/db_sym.h>
42 #include <ddb/db_access.h>
43 #include <ddb/db_variables.h>
44 #include <ddb/db_output.h>
45 #include <ddb/db_interface.h>
46 #include <ddb/db_user.h>
47 #include <ddb/db_proc.h>
48 #include <ddb/db_command.h>
49 #include <x86/db_machdep.h>
50 
51 #define dbreg(xx) (long *)offsetof(db_regs_t, tf_ ## xx)
52 
53 /*
54  * Machine register set.
55  */
56 const struct db_variable db_regs[] = {
57 	{ "ds",		dbreg(ds),     db_x86_regop, NULL },
58 	{ "es",		dbreg(es),     db_x86_regop, NULL },
59 	{ "fs",		dbreg(fs),     db_x86_regop, NULL },
60 	{ "gs",		dbreg(gs),     db_x86_regop, NULL },
61 	{ "rdi",	dbreg(rdi),    db_x86_regop, NULL },
62 	{ "rsi",	dbreg(rsi),    db_x86_regop, NULL },
63 	{ "rbp",	dbreg(rbp),    db_x86_regop, NULL },
64 	{ "rbx",	dbreg(rbx),    db_x86_regop, NULL },
65 	{ "rdx",	dbreg(rdx),    db_x86_regop, NULL },
66 	{ "rcx",	dbreg(rcx),    db_x86_regop, NULL },
67 	{ "rax",	dbreg(rax),    db_x86_regop, NULL },
68 	{ "r8",		dbreg(r8),     db_x86_regop, NULL },
69 	{ "r9",		dbreg(r9),     db_x86_regop, NULL },
70 	{ "r10",	dbreg(r10),    db_x86_regop, NULL },
71 	{ "r11",	dbreg(r11),    db_x86_regop, NULL },
72 	{ "r12",	dbreg(r12),    db_x86_regop, NULL },
73 	{ "r13",	dbreg(r13),    db_x86_regop, NULL },
74 	{ "r14",	dbreg(r14),    db_x86_regop, NULL },
75 	{ "r15",	dbreg(r15),    db_x86_regop, NULL },
76 	{ "rip",	dbreg(rip),    db_x86_regop, NULL },
77 	{ "cs",		dbreg(cs),     db_x86_regop, NULL },
78 	{ "rflags",	dbreg(rflags), db_x86_regop, NULL },
79 	{ "rsp",	dbreg(rsp),    db_x86_regop, NULL },
80 	{ "ss",		dbreg(ss),     db_x86_regop, NULL },
81 };
82 const struct db_variable * const db_eregs =
83     db_regs + sizeof(db_regs)/sizeof(db_regs[0]);
84 
85 /*
86  * Figure out how many arguments were passed into the frame at "fp".
87  * We can probably figure out how many arguments where passed above
88  * the first 6 (which are in registers), but since we can't
89  * reliably determine the values currently, just return 0.
90  */
91 int
db_numargs(long * retaddrp)92 db_numargs(long *retaddrp)
93 {
94 	return 0;
95 }
96 
97 /*
98  * Figure out the next frame up in the call stack.
99  * For trap(), we print the address of the faulting instruction and
100  *   proceed with the calling frame.  We return the ip that faulted.
101  *   If the trap was caused by jumping through a bogus pointer, then
102  *   the next line in the backtrace will list some random function as
103  *   being called.  It should get the argument list correct, though.
104  *   It might be possible to dig out from the next frame up the name
105  *   of the function that faulted, but that could get hairy.
106  */
107 int
db_nextframe(long ** nextframe,long ** retaddr,long ** arg0,db_addr_t * ip,long * argp,int is_trap,void (* pr)(const char *,...))108 db_nextframe(long **nextframe, long **retaddr, long **arg0, db_addr_t *ip,
109     long *argp, int is_trap, void (*pr)(const char *, ...))
110 {
111 	struct trapframe *tf;
112 	struct x86_64_frame *fp;
113 	struct intrframe *ifp;
114 	int trapno, err, i;
115 	db_expr_t syscallno;
116 
117 	switch (is_trap) {
118 	    case NONE:
119 		*ip = (db_addr_t)
120 			db_get_value((long)*retaddr, 8, false);
121 		fp = (struct x86_64_frame *)
122 			db_get_value((long)*nextframe, 8, false);
123 		if (fp == NULL)
124 			return 0;
125 		*nextframe = (long *)&fp->f_frame;
126 		*retaddr = (long *)&fp->f_retaddr;
127 		*arg0 = (long *)&fp->f_arg0;
128 		break;
129 
130 	    case SYSCALL:
131 		tf = (struct trapframe *)argp;
132 		syscallno = db_get_value((long)&tf->tf_rax, 8, false);
133 		if (syscallno == SYS_syscall || syscallno == SYS___syscall) {
134 			syscallno = db_get_value((long)&tf->tf_rdi, 8, false);
135 			(*pr)("--- syscall (number %"DDB_EXPR_FMT"u"
136 			    " via SYS_syscall) ---\n",
137 			    syscallno);
138 		} else {
139 			(*pr)("--- syscall (number %"DDB_EXPR_FMT"u) ---\n",
140 			    syscallno);
141 		}
142 		return 0;
143 
144 	    case TRAP:
145 	    case INTERRUPT:
146 	    default:
147 
148 		/* The only argument to trap() is the trapframe. */
149 		tf = (struct trapframe *)argp;
150 		switch (is_trap) {
151 		case TRAP:
152 			(*pr)("--- trap (number %"DDB_EXPR_FMT"u) ---\n",
153 				db_get_value((long)&tf->tf_trapno, 8, false));
154 			break;
155 		case INTERRUPT:
156 			(*pr)("--- interrupt ---\n");
157 			break;
158 		}
159 		*ip = (db_addr_t)db_get_value((long)&tf->tf_rip, 8, false);
160 		fp = (struct x86_64_frame *)
161 			db_get_value((long)&tf->tf_rbp, 8, false);
162 		if (fp == NULL)
163 			return 0;
164 		if (((uintptr_t)fp & 7) != 0)
165 			return 0;
166 		*nextframe = (long *)&fp->f_frame;
167 		*retaddr = (long *)&fp->f_retaddr;
168 		*arg0 = (long *)&fp->f_arg0;
169 		break;
170 	}
171 
172 	/*
173 	 * A bit of a hack. Since %rbp may be used in the stub code,
174 	 * walk the stack looking for a valid interrupt frame. Such
175 	 * a frame can be recognized by always having
176 	 * err 0 or IREENT_MAGIC and trapno T_ASTFLT.
177 	 */
178 	int traptype = NONE;
179 	db_sym_t sym = db_frame_info(*nextframe, (db_addr_t)*ip,
180 				     NULL, NULL, &traptype, NULL);
181 	if (sym != DB_SYM_NULL && traptype == INTERRUPT) {
182 		for (i = 0; i < 4; i++) {
183 			ifp = (struct intrframe *)(argp + i);
184 			err = db_get_value((long)&ifp->if_tf.tf_err,
185 			    sizeof(long), false);
186 			trapno = db_get_value((long)&ifp->if_tf.tf_trapno,
187 			    sizeof(long), false);
188 			if ((err == 0 || err == IREENT_MAGIC)
189 			    && trapno == T_ASTFLT) {
190 				*nextframe = (long *)ifp - 1;
191 				break;
192 			}
193 		}
194 		if (i == 4) {
195 			(*pr)("DDB lost frame for ");
196 			db_printsym(*ip, DB_STGY_ANY, pr);
197 			(*pr)(", trying %p\n",argp);
198 			*nextframe = argp;
199 		}
200 	}
201 	return 1;
202 }
203 
204 db_sym_t
db_frame_info(long * frame,db_addr_t callpc,const char ** namep,db_expr_t * offp,int * is_trap,int * nargp)205 db_frame_info(long *frame, db_addr_t callpc, const char **namep,
206     db_expr_t *offp, int *is_trap, int *nargp)
207 {
208 	db_expr_t offset;
209 	db_sym_t sym;
210 	int narg;
211 	const char *name;
212 
213 	sym = db_search_symbol(callpc, DB_STGY_ANY, &offset);
214 	if (sym != DB_SYM_NULL && offset == 0) {
215 		sym = db_search_symbol(callpc - 1, DB_STGY_ANY, &offset);
216 		offset++;
217 	}
218 	db_symbol_values(sym, &name, NULL);
219 	if (sym == DB_SYM_NULL)
220 		return DB_SYM_NULL;
221 
222 	*is_trap = NONE;
223 	narg = 0;
224 
225 	if (INKERNEL((long)frame) && name) {
226 		/*
227 		 * XXX traps should be based off of the Xtrap*
228 		 * locations rather than on trap, since some traps
229 		 * (e.g., npxdna) don't go through trap()
230 		 */
231 		if (!strcmp(name, "trap")) {
232 			*is_trap = TRAP;
233 			narg = 0;
234 		} else if (!strcmp(name, "syscall") ||
235 		    !strcmp(name, "handle_syscall")) {
236 			*is_trap = SYSCALL;
237 			narg = 0;
238 		} else if (name[0] == 'X') {
239 			if (!strncmp(name, "Xintr", 5) ||
240 			    !strncmp(name, "Xhandle", 7) ||
241 			    !strncmp(name, "Xresume", 7) ||
242 			    !strncmp(name, "Xstray", 6) ||
243 			    !strncmp(name, "Xhold", 5) ||
244 			    !strncmp(name, "Xrecurse", 8) ||
245 			    !strcmp(name, "Xdoreti") ||
246 			    !strncmp(name, "Xsoft", 5)) {
247 				*is_trap = INTERRUPT;
248 				narg = 0;
249 			}
250 		}
251 	}
252 
253 	if (offp != NULL)
254 		*offp = offset;
255 	if (nargp != NULL)
256 		*nargp = narg;
257 	if (namep != NULL)
258 		*namep = name;
259 	return sym;
260 }
261