1/*-
2 * Copyright (c) 2001 Jake Burkholder.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD$");
29
30#include <sys/param.h>
31#include <sys/systm.h>
32#include <sys/kdb.h>
33#include <sys/proc.h>
34#include <sys/stack.h>
35#include <sys/sysent.h>
36
37#include <vm/vm.h>
38#include <vm/vm_page.h>
39#include <vm/vm_map.h>
40
41#include <machine/cpu.h>
42#include <machine/pcb.h>
43#include <machine/stack.h>
44#include <machine/trap.h>
45#include <machine/vmparam.h>
46
47#include <ddb/ddb.h>
48#include <ddb/db_access.h>
49#include <ddb/db_sym.h>
50#include <ddb/db_variables.h>
51#include <ddb/db_watch.h>
52
53static db_varfcn_t db_frame;
54
55#define	DB_OFFSET(x)	(db_expr_t *)offsetof(struct trapframe, x)
56struct	db_variable db_regs[] = {
57	{ "g0",		DB_OFFSET(tf_global[0]),	db_frame },
58	{ "g1",		DB_OFFSET(tf_global[1]),	db_frame },
59	{ "g2",		DB_OFFSET(tf_global[2]),	db_frame },
60	{ "g3",		DB_OFFSET(tf_global[3]),	db_frame },
61	{ "g4",		DB_OFFSET(tf_global[4]),	db_frame },
62	{ "g5",		DB_OFFSET(tf_global[5]),	db_frame },
63	{ "g6",		DB_OFFSET(tf_global[6]),	db_frame },
64	{ "g7",		DB_OFFSET(tf_global[7]),	db_frame },
65	{ "i0",		DB_OFFSET(tf_out[0]),		db_frame },
66	{ "i1",		DB_OFFSET(tf_out[1]),		db_frame },
67	{ "i2",		DB_OFFSET(tf_out[2]),		db_frame },
68	{ "i3",		DB_OFFSET(tf_out[3]),		db_frame },
69	{ "i4",		DB_OFFSET(tf_out[4]),		db_frame },
70	{ "i5",		DB_OFFSET(tf_out[5]),		db_frame },
71	{ "i6",		DB_OFFSET(tf_out[6]),		db_frame },
72	{ "i7",		DB_OFFSET(tf_out[7]),		db_frame },
73	{ "tnpc",	DB_OFFSET(tf_tnpc),		db_frame },
74	{ "tpc",	DB_OFFSET(tf_tpc),		db_frame },
75	{ "tstate",	DB_OFFSET(tf_tstate),		db_frame },
76};
77struct db_variable *db_eregs = db_regs + nitems(db_regs);
78
79static int
80db_frame(struct db_variable *vp, db_expr_t *valuep, int op)
81{
82	uint64_t *reg;
83
84	if (kdb_frame == NULL)
85		return (0);
86	reg = (uint64_t*)((uintptr_t)kdb_frame + (uintptr_t)vp->valuep);
87	if (op == DB_VAR_GET)
88		*valuep = *reg;
89	else
90		*reg = *valuep;
91	return (1);
92}
93
94/*
95 * User stack trace (debugging aid).
96 */
97static void
98db_utrace(struct thread *td, struct trapframe *tf, int count)
99{
100	struct pcb *pcb;
101	db_addr_t sp, rsp, o7, pc;
102	int i, found;
103
104	pcb = td->td_pcb;
105	sp = db_get_value((db_addr_t)&tf->tf_sp, sizeof(tf->tf_sp), FALSE);
106	o7 = db_get_value((db_addr_t)&tf->tf_out[7], sizeof(tf->tf_out[7]),
107	    FALSE);
108	pc = db_get_value((db_addr_t)&tf->tf_tpc, sizeof(tf->tf_tpc), FALSE);
109	db_printf("user trace: trap %%o7=%#lx\n", o7);
110	while (count-- && sp != 0 && !db_pager_quit) {
111		db_printf("pc %#lx, sp %#lx\n", pc, sp);
112		/* First, check whether the frame is in the pcb. */
113		found = 0;
114		for (i = 0; i < pcb->pcb_nsaved; i++) {
115			if (pcb->pcb_rwsp[i] == sp) {
116				found = 1;
117				sp = pcb->pcb_rw[i].rw_in[6];
118				pc = pcb->pcb_rw[i].rw_in[7];
119				break;
120			}
121		}
122		if (!found) {
123			rsp = sp + SPOFF;
124			sp = 0;
125			if (copyin((void *)(rsp + offsetof(struct frame, fr_fp)),
126			    &sp, sizeof(sp)) != 0 ||
127			    copyin((void *)(rsp + offsetof(struct frame, fr_pc)),
128			    &pc, sizeof(pc)) != 0)
129				break;
130		}
131	}
132	db_printf("done\n");
133}
134
135static int
136db_print_trap(struct thread *td, struct trapframe *tf, int count)
137{
138	struct proc *p;
139	const char *symname;
140	c_db_sym_t sym;
141	db_expr_t diff;
142	db_addr_t func;
143	db_addr_t tpc;
144	u_long type;
145	u_long sfar;
146	u_long sfsr;
147	u_long tar;
148	u_long level;
149	u_long pil;
150	u_long code;
151	u_long o7;
152	int user;
153
154	p = td->td_proc;
155	type = db_get_value((db_addr_t)&tf->tf_type,
156	    sizeof(tf->tf_type), FALSE);
157	db_printf("-- %s", trap_msg[type & ~T_KERNEL]);
158	switch (type & ~T_KERNEL) {
159	case T_DATA_PROTECTION:
160		tar = (u_long)db_get_value((db_addr_t)&tf->tf_tar,
161		    sizeof(tf->tf_tar), FALSE);
162		db_printf(" tar=%#lx", tar);
163		/* fall through */
164	case T_DATA_EXCEPTION:
165	case T_INSTRUCTION_EXCEPTION:
166	case T_MEM_ADDRESS_NOT_ALIGNED:
167		sfar = (u_long)db_get_value((db_addr_t)&tf->tf_sfar,
168		    sizeof(tf->tf_sfar), FALSE);
169		sfsr = (u_long)db_get_value((db_addr_t)&tf->tf_sfsr,
170		    sizeof(tf->tf_sfsr), FALSE);
171		db_printf(" sfar=%#lx sfsr=%#lx", sfar, sfsr);
172		break;
173	case T_DATA_MISS:
174	case T_INSTRUCTION_MISS:
175		tar = (u_long)db_get_value((db_addr_t)&tf->tf_tar,
176		    sizeof(tf->tf_tar), FALSE);
177		db_printf(" tar=%#lx", tar);
178		break;
179	case T_SYSCALL:
180		code = db_get_value((db_addr_t)&tf->tf_global[1],
181		    sizeof(tf->tf_global[1]), FALSE);
182		db_printf(" (%ld", code);
183		if (code >= 0 && code < p->p_sysent->sv_size) {
184			func = (db_addr_t)p->p_sysent->sv_table[code].sy_call;
185			sym = db_search_symbol(func, DB_STGY_ANY, &diff);
186			if (sym != DB_SYM_NULL && diff == 0) {
187				db_symbol_values(sym, &symname, NULL);
188				db_printf(", %s, %s", p->p_sysent->sv_name,
189				    symname);
190			}
191			db_printf(")");
192		}
193		break;
194	case T_INTERRUPT:
195		level = (u_long)db_get_value((db_addr_t)&tf->tf_level,
196		    sizeof(tf->tf_level), FALSE);
197		pil = (u_long)db_get_value((db_addr_t)&tf->tf_pil,
198		    sizeof(tf->tf_pil), FALSE);
199		db_printf(" level=%#lx pil=%#lx", level, pil);
200		break;
201	default:
202		break;
203	}
204	o7 = (u_long)db_get_value((db_addr_t)&tf->tf_out[7],
205	    sizeof(tf->tf_out[7]), FALSE);
206	db_printf(" %%o7=%#lx --\n", o7);
207	user = (type & T_KERNEL) == 0;
208	if (user) {
209		tpc = db_get_value((db_addr_t)&tf->tf_tpc,
210		    sizeof(tf->tf_tpc), FALSE);
211		db_printf("userland() at ");
212		db_printsym(tpc, DB_STGY_PROC);
213		db_printf("\n");
214		db_utrace(td, tf, count);
215	}
216	return (user);
217}
218
219static int
220db_backtrace(struct thread *td, struct frame *fp, int count)
221{
222	struct trapframe *tf;
223	const char *name;
224	c_db_sym_t sym;
225	db_expr_t offset;
226	db_expr_t value;
227	db_addr_t npc;
228	db_addr_t pc;
229	int trap;
230	int user;
231
232	if (count == -1)
233		count = 1024;
234
235	trap = 0;
236	user = 0;
237	npc = 0;
238	while (count-- && !user && !db_pager_quit) {
239		pc = (db_addr_t)db_get_value((db_addr_t)&fp->fr_pc,
240		    sizeof(fp->fr_pc), FALSE);
241		if (trap) {
242			pc = npc;
243			trap = 0;
244		}
245		if (!INKERNEL((vm_offset_t)pc))
246			break;
247		sym = db_search_symbol(pc, DB_STGY_ANY, &offset);
248		if (sym == C_DB_SYM_NULL) {
249			value = 0;
250			name = NULL;
251		} else
252			db_symbol_values(sym, &name, &value);
253		if (name == NULL)
254			name = "(null)";
255		fp = (struct frame *)(db_get_value((db_addr_t)&fp->fr_fp,
256		   sizeof(fp->fr_fp), FALSE) + SPOFF);
257		if ((value > (u_long)tl_trap_begin &&
258		    value < (u_long)tl_trap_end) ||
259		    (value > (u_long)tl_text_begin &&
260		    value < (u_long)tl_text_end)) {
261			tf = (struct trapframe *)(fp + 1);
262			npc = db_get_value((db_addr_t)&tf->tf_tpc,
263			    sizeof(tf->tf_tpc), FALSE);
264			user = db_print_trap(td, tf, count);
265			trap = 1;
266		} else {
267			db_printf("%s() at ", name);
268			db_printsym(pc, DB_STGY_PROC);
269			db_printf("\n");
270		}
271	}
272	return (0);
273}
274
275void
276db_trace_self(void)
277{
278
279	db_backtrace(curthread,
280	    (struct frame *)__builtin_frame_address(1), -1);
281}
282
283int
284db_trace_thread(struct thread *td, int count)
285{
286	struct pcb *ctx;
287
288	ctx = kdb_thr_ctx(td);
289	return (db_backtrace(td,
290	    (struct frame *)(ctx->pcb_sp + SPOFF), count));
291}
292