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