xref: /netbsd-src/sys/arch/amd64/amd64/db_interface.c (revision 91637ac6c6ff410d532a73e2cf2a2fc374b4511c)
1 /*	$NetBSD: db_interface.c,v 1.42 2023/07/31 02:38:16 mrg 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  *	db_interface.c,v 2.4 1991/02/05 17:11:13 mrt (CMU)
29  */
30 
31 /*
32  * Interface to new debugger.
33  */
34 
35 #include <sys/cdefs.h>
36 __KERNEL_RCSID(0, "$NetBSD: db_interface.c,v 1.42 2023/07/31 02:38:16 mrg Exp $");
37 
38 #include "opt_ddb.h"
39 #include "opt_multiprocessor.h"
40 
41 #include "lapic.h"
42 
43 #include <sys/param.h>
44 #include <sys/proc.h>
45 #include <sys/reboot.h>
46 #include <sys/systm.h>
47 #include <sys/atomic.h>
48 #include <sys/cpu.h>
49 
50 #include <dev/cons.h>
51 
52 #include <machine/cpufunc.h>
53 #include <machine/db_machdep.h>
54 #include <machine/cpuvar.h>
55 #if NIOAPIC > 0
56 #include <machine/i82093var.h>
57 #endif
58 #if NLAPIC > 0
59 #include <machine/i82489reg.h>
60 #include <machine/i82489var.h>
61 #endif
62 
63 #include <ddb/db_active.h>
64 #include <ddb/db_sym.h>
65 #include <ddb/db_command.h>
66 #include <ddb/db_extern.h>
67 #include <ddb/db_access.h>
68 #include <ddb/db_output.h>
69 #include <ddb/ddbvar.h>
70 
71 extern const char *const trap_type[];
72 extern int trap_types;
73 
74 int	db_active = 0;
75 #ifdef MULTIPROCESSOR
76 /* ddb_regs defined as a macro */
77 db_regs_t *ddb_regp = NULL;
78 #else
79 db_regs_t ddb_regs;
80 #endif
81 
82 void db_mach_cpu (db_expr_t, bool, db_expr_t, const char *);
83 
84 const struct db_command db_machine_command_table[] = {
85 #ifdef MULTIPROCESSOR
86 	{ DDB_ADD_CMD("cpu",	db_mach_cpu,	0,
87 	  "switch to another cpu", "cpu-no", NULL) },
88 #endif
89 	{ DDB_END_CMD },
90 };
91 
92 void kdbprinttrap(int, int);
93 #ifdef MULTIPROCESSOR
94 extern void ddb_ipi(struct trapframe);
95 static void ddb_suspend(struct trapframe *);
96 #ifndef XENPV
97 int ddb_vec;
98 #endif /* XENPV */
99 static bool ddb_mp_online;
100 #endif
101 
102 #define NOCPU	-1
103 
104 int ddb_cpu = NOCPU;
105 
106 typedef void (vector)(void);
107 extern vector Xintr_ddbipi, Xintr_x2apic_ddbipi;
108 
109 void
db_machine_init(void)110 db_machine_init(void)
111 {
112 
113 #ifdef MULTIPROCESSOR
114 #ifndef XENPV
115 	struct idt_vec *iv = &(cpu_info_primary.ci_idtvec);
116 	vector *handler = &Xintr_ddbipi;
117 	idt_descriptor_t *idt = iv->iv_idt;
118 #if NLAPIC > 0
119 	if (lapic_is_x2apic())
120 		handler = &Xintr_x2apic_ddbipi;
121 #endif
122 	ddb_vec = idt_vec_alloc(iv, 0xf0, 0xff);
123 	KASSERT(ddb_vec > 0);
124 	set_idtgate(&idt[ddb_vec], handler, 1, SDT_SYS386IGT, SEL_KPL,
125 	    GSEL(GCODE_SEL, SEL_KPL));
126 #else
127 	/* Initialised as part of xen_ipi_init() */
128 #endif /* XENPV */
129 #endif
130 }
131 
132 #ifdef MULTIPROCESSOR
133 
134 __cpu_simple_lock_t db_lock;
135 
136 static int
db_suspend_others(void)137 db_suspend_others(void)
138 {
139 	int cpu_me = cpu_number();
140 	int win;
141 
142 #ifndef XENPV
143 	if (ddb_vec == 0)
144 		return 1;
145 #endif /* XENPV */
146 
147 	__cpu_simple_lock(&db_lock);
148 	if (ddb_cpu == NOCPU)
149 		ddb_cpu = cpu_me;
150 	win = (ddb_cpu == cpu_me);
151 	__cpu_simple_unlock(&db_lock);
152 	if (win) {
153 #ifdef XENPV
154 		xen_broadcast_ipi(XEN_IPI_DDB);
155 #else
156 #if NLAPIC > 0
157 		if (ddb_vec != 0)
158 			x86_ipi(ddb_vec, LAPIC_DEST_ALLEXCL,
159 			    LAPIC_DLMODE_FIXED);
160 #endif
161 #endif /* XENPV */
162 	}
163 	ddb_mp_online = x86_mp_online;
164 	x86_mp_online = false;
165 	return win;
166 }
167 
168 static void
db_resume_others(void)169 db_resume_others(void)
170 {
171 	CPU_INFO_ITERATOR cii;
172 	struct cpu_info *ci;
173 
174 	x86_mp_online = ddb_mp_online;
175 	__cpu_simple_lock(&db_lock);
176 	ddb_cpu = NOCPU;
177 	__cpu_simple_unlock(&db_lock);
178 
179 	for (CPU_INFO_FOREACH(cii, ci)) {
180 		if (ci->ci_flags & CPUF_PAUSE)
181 			atomic_and_32(&ci->ci_flags, ~CPUF_PAUSE);
182 	}
183 }
184 
185 #endif
186 
187 /*
188  * Print trap reason.
189  */
190 void
kdbprinttrap(int type,int code)191 kdbprinttrap(int type, int code)
192 {
193 	db_printf("kernel: ");
194 	if (type >= trap_types || type < 0)
195 		db_printf("type %d", type);
196 	else
197 		db_printf("%s", trap_type[type]);
198 	db_printf(" trap, code=%x\n", code);
199 }
200 
201 /*
202  *  kdb_trap - field a TRACE or BPT trap
203  */
204 int
kdb_trap(int type,int code,db_regs_t * regs)205 kdb_trap(int type, int code, db_regs_t *regs)
206 {
207 	int s;
208 #ifdef MULTIPROCESSOR
209 	db_regs_t dbreg;
210 #endif
211 
212 	switch (type) {
213 	case T_NMI:	/* NMI */
214 		printf("NMI ... going to debugger\n");
215 		/*FALLTHROUGH*/
216 	case T_BPTFLT:	/* breakpoint */
217 	case T_TRCTRAP:	/* single_step */
218 	case -1:	/* keyboard interrupt */
219 		break;
220 	default:
221 		if (!db_onpanic && db_recover == 0)
222 			return (0);
223 
224 		kdbprinttrap(type, code);
225 		if (db_recover != 0) {
226 			db_error("Faulted in DDB; continuing...\n");
227 			/*NOTREACHED*/
228 		}
229 	}
230 
231 #ifdef MULTIPROCESSOR
232 	if (!db_suspend_others()) {
233 		ddb_suspend(regs);
234 	} else {
235 	curcpu()->ci_ddb_regs = &dbreg;
236 	ddb_regp = &dbreg;
237 #endif
238 
239 	ddb_regs = *regs;
240 
241 	ddb_regs.tf_cs &= 0xffff;
242 	ddb_regs.tf_ds &= 0xffff;
243 	ddb_regs.tf_es &= 0xffff;
244 	ddb_regs.tf_fs &= 0xffff;
245 	ddb_regs.tf_gs &= 0xffff;
246 	ddb_regs.tf_ss &= 0xffff;
247 
248 	s = splhigh();
249 	db_active++;
250 	cnpollc(true);
251 	db_trap(type, code);
252 	cnpollc(false);
253 	db_active--;
254 	splx(s);
255 #ifdef MULTIPROCESSOR
256 	db_resume_others();
257 	}
258 	/* Restore dbreg because ddb_regp can be changed by db_mach_cpu */
259 	ddb_regp = &dbreg;
260 #endif
261 
262 	*regs = ddb_regs;
263 #ifdef MULTIPROCESSOR
264 	ddb_regp = NULL;
265 #endif
266 
267 	return (1);
268 }
269 
270 void
cpu_Debugger(void)271 cpu_Debugger(void)
272 {
273 	breakpoint();
274 }
275 
276 #ifdef MULTIPROCESSOR
277 
278 /*
279  * Called when we receive a debugger IPI (inter-processor interrupt).
280  * As with trap() in trap.c, this function is called from an assembly
281  * language IDT gate entry routine which prepares a suitable stack frame,
282  * and restores this frame after the exception has been processed. Note
283  * that the effect is as if the arguments were passed call by reference.
284  */
285 
286 void
ddb_ipi(struct trapframe frame)287 ddb_ipi(struct trapframe frame)
288 {
289 
290 	ddb_suspend(&frame);
291 }
292 
293 static void
ddb_suspend(struct trapframe * frame)294 ddb_suspend(struct trapframe *frame)
295 {
296 	volatile struct cpu_info *ci = curcpu();
297 	db_regs_t regs;
298 
299 	regs = *frame;
300 
301 	ci->ci_ddb_regs = &regs;
302 
303 	atomic_or_32(&ci->ci_flags, CPUF_PAUSE);
304 
305 	while (ci->ci_flags & CPUF_PAUSE)
306 		x86_pause();
307 	ci->ci_ddb_regs = 0;
308 	tlbflushg();
309 }
310 
311 
312 extern void cpu_debug_dump(void); /* XXX */
313 
314 void
db_mach_cpu(db_expr_t addr,bool have_addr,db_expr_t count,const char * modif)315 db_mach_cpu(db_expr_t addr, bool have_addr, db_expr_t count, const char *modif)
316 {
317 	struct cpu_info *ci;
318 	if (!have_addr) {
319 		cpu_debug_dump();
320 		return;
321 	}
322 
323 	if (addr < 0) {
324 		db_printf("%ld: CPU out of range\n", addr);
325 		return;
326 	}
327 	ci = cpu_lookup(addr);
328 	if (ci == NULL) {
329 		db_printf("CPU %ld not configured\n", addr);
330 		return;
331 	}
332 	if (ci != curcpu()) {
333 		if (!(ci->ci_flags & CPUF_PAUSE)) {
334 			db_printf("CPU %ld not paused\n", addr);
335 			return;
336 		}
337 	}
338 	if (ci->ci_ddb_regs == 0) {
339 		db_printf("CPU %ld has no saved regs\n", addr);
340 		return;
341 	}
342 	db_printf("using CPU %ld", addr);
343 	ddb_regp = ci->ci_ddb_regs;
344 }
345 
346 #endif
347