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 = ®s;
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