1 /* $NetBSD: db_interface.c,v 1.89 2024/09/18 22:29:39 rin 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.89 2024/09/18 22:29:39 rin Exp $"); 37 38 #include "opt_ddb.h" 39 #include "opt_multiprocessor.h" 40 41 #include "ioapic.h" 42 #include "lapic.h" 43 44 #include <sys/param.h> 45 #include <sys/proc.h> 46 #include <sys/reboot.h> 47 #include <sys/systm.h> 48 #include <sys/atomic.h> 49 #include <sys/cpu.h> 50 51 #include <dev/cons.h> 52 53 #include <machine/cpufunc.h> 54 #include <machine/db_machdep.h> 55 #include <machine/cpuvar.h> 56 #if NIOAPIC > 0 57 #include <machine/i82093var.h> 58 #endif 59 #if NLAPIC > 0 60 #include <machine/i82489reg.h> 61 #include <machine/i82489var.h> 62 #endif 63 64 #include <ddb/db_active.h> 65 #include <ddb/db_sym.h> 66 #include <ddb/db_command.h> 67 #include <ddb/db_extern.h> 68 #include <ddb/db_access.h> 69 #include <ddb/db_output.h> 70 #include <ddb/ddbvar.h> 71 72 extern const char *const trap_type[]; 73 extern int trap_types; 74 75 int db_active = 0; 76 #ifdef MULTIPROCESSOR 77 /* ddb_regs defined as a macro */ 78 db_regs_t *ddb_regp = NULL; 79 #else 80 db_regs_t ddb_regs; 81 #endif 82 83 void db_mach_cpu (db_expr_t, bool, db_expr_t, const char *); 84 85 const struct db_command db_machine_command_table[] = { 86 #ifdef MULTIPROCESSOR 87 { DDB_ADD_CMD("cpu", db_mach_cpu, 0, 88 "switch to another cpu", "cpu-no", NULL) }, 89 #endif 90 { DDB_END_CMD }, 91 }; 92 93 void kdbprinttrap(int, int); 94 #ifdef MULTIPROCESSOR 95 extern void ddb_ipi(struct trapframe); 96 extern void ddb_ipi_tss(struct i386tss *); 97 static void ddb_suspend(struct trapframe *); 98 #ifndef XENPV 99 int ddb_vec; 100 #endif /* XENPV */ 101 static bool ddb_mp_online; 102 #endif 103 104 #define NOCPU -1 105 106 int ddb_cpu = NOCPU; 107 108 typedef void (vector)(void); 109 extern vector Xintr_ddbipi, Xintr_x2apic_ddbipi; 110 111 void 112 db_machine_init(void) 113 { 114 115 #ifdef MULTIPROCESSOR 116 #ifndef XENPV 117 vector *handler = &Xintr_ddbipi; 118 struct idt_vec *iv; 119 120 iv = &(cpu_info_primary.ci_idtvec); 121 #if NLAPIC > 0 122 if (lapic_is_x2apic()) 123 handler = &Xintr_x2apic_ddbipi; 124 #endif 125 ddb_vec = idt_vec_alloc(iv, 0xf0, 0xff); 126 KASSERT(ddb_vec > 0); 127 idt_vec_set(iv, ddb_vec, handler); 128 #else 129 /* Initialised as part of xen_ipi_init() */ 130 #endif /* XENPV */ 131 #endif 132 } 133 134 #ifdef MULTIPROCESSOR 135 136 __cpu_simple_lock_t db_lock; 137 138 static int 139 db_suspend_others(void) 140 { 141 int cpu_me = cpu_number(); 142 int win; 143 144 #ifndef XENPV 145 if (ddb_vec == 0) 146 return 1; 147 #endif /* XENPV */ 148 149 __cpu_simple_lock(&db_lock); 150 if (ddb_cpu == NOCPU) 151 ddb_cpu = cpu_me; 152 win = (ddb_cpu == cpu_me); 153 __cpu_simple_unlock(&db_lock); 154 if (win) { 155 #ifdef XENPV 156 xen_broadcast_ipi(XEN_IPI_DDB); 157 #else 158 #if NLAPIC > 0 159 x86_ipi(ddb_vec, LAPIC_DEST_ALLEXCL, 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 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 191 kdbprinttrap(int type, int code) 192 { 193 db_printf("kernel: %s trap ", (type & T_USER) ? "user" : "supervisor"); 194 type &= ~T_USER; 195 if (type >= trap_types || type < 0) 196 db_printf("type %d", type); 197 else 198 db_printf("%s", trap_type[type]); 199 db_printf(", code=%x\n", code); 200 } 201 202 /* 203 * kdb_trap - field a TRACE or BPT trap 204 */ 205 int 206 kdb_trap(int type, int code, db_regs_t *regs) 207 { 208 int s, flags; 209 #ifdef MULTIPROCESSOR 210 db_regs_t dbreg; 211 #endif 212 213 flags = regs->tf_err & TC_FLAGMASK; 214 regs->tf_err &= ~TC_FLAGMASK; 215 216 switch (type) { 217 case T_NMI: /* NMI */ 218 printf("NMI ... going to debugger\n"); 219 /*FALLTHROUGH*/ 220 case T_BPTFLT: /* breakpoint */ 221 case T_TRCTRAP: /* single_step */ 222 case -1: /* keyboard interrupt */ 223 break; 224 default: 225 if (!db_onpanic && db_recover == 0) 226 return (0); 227 228 kdbprinttrap(type, code); 229 if (db_recover != 0) { 230 db_error("Faulted in DDB; continuing...\n"); 231 /*NOTREACHED*/ 232 } 233 } 234 235 #ifdef MULTIPROCESSOR 236 if (!db_suspend_others()) { 237 ddb_suspend(regs); 238 } else { 239 curcpu()->ci_ddb_regs = &dbreg; 240 ddb_regp = &dbreg; 241 #endif 242 /* XXX Should switch to kdb's own stack here. */ 243 ddb_regs = *regs; 244 if (!(flags & TC_TSS) && KERNELMODE(regs->tf_cs)) { 245 /* 246 * Kernel mode - esp and ss not saved 247 */ 248 ddb_regs.tf_esp = (int)®s->tf_esp; /* kernel stack pointer */ 249 ddb_regs.tf_ss = x86_getss(); 250 } 251 252 ddb_regs.tf_cs &= 0xffff; 253 ddb_regs.tf_ds &= 0xffff; 254 ddb_regs.tf_es &= 0xffff; 255 ddb_regs.tf_fs &= 0xffff; 256 ddb_regs.tf_gs &= 0xffff; 257 ddb_regs.tf_ss &= 0xffff; 258 259 s = splhigh(); 260 db_active++; 261 cnpollc(true); 262 db_trap(type, code); 263 cnpollc(false); 264 db_active--; 265 splx(s); 266 #ifdef MULTIPROCESSOR 267 db_resume_others(); 268 } 269 /* Restore dbreg because ddb_regp can be changed by db_mach_cpu */ 270 ddb_regp = &dbreg; 271 #endif 272 273 regs->tf_gs = ddb_regs.tf_gs; 274 regs->tf_fs = ddb_regs.tf_fs; 275 regs->tf_es = ddb_regs.tf_es; 276 regs->tf_ds = ddb_regs.tf_ds; 277 regs->tf_edi = ddb_regs.tf_edi; 278 regs->tf_esi = ddb_regs.tf_esi; 279 regs->tf_ebp = ddb_regs.tf_ebp; 280 regs->tf_ebx = ddb_regs.tf_ebx; 281 regs->tf_edx = ddb_regs.tf_edx; 282 regs->tf_ecx = ddb_regs.tf_ecx; 283 regs->tf_eax = ddb_regs.tf_eax; 284 regs->tf_eip = ddb_regs.tf_eip; 285 regs->tf_cs = ddb_regs.tf_cs; 286 regs->tf_eflags = ddb_regs.tf_eflags; 287 if (!(flags & TC_TSS) && !KERNELMODE(regs->tf_cs)) { 288 /* ring transit - saved esp and ss valid */ 289 regs->tf_esp = ddb_regs.tf_esp; 290 regs->tf_ss = ddb_regs.tf_ss; 291 } 292 293 #ifdef MULTIPROCESSOR 294 ddb_regp = NULL; 295 #endif 296 297 return (1); 298 } 299 300 void 301 cpu_Debugger(void) 302 { 303 breakpoint(); 304 } 305 306 #ifdef MULTIPROCESSOR 307 308 /* 309 * Called when we receive a debugger IPI (inter-processor interrupt). 310 * As with trap() in trap.c, this function is called from an assembly 311 * language IDT gate entry routine which prepares a suitable stack frame, 312 * and restores this frame after the exception has been processed. Note 313 * that the effect is as if the arguments were passed call by reference. 314 */ 315 void 316 ddb_ipi(struct trapframe frame) 317 { 318 319 ddb_suspend(&frame); 320 } 321 322 void 323 ddb_ipi_tss(struct i386tss *tss) 324 { 325 struct trapframe tf; 326 327 tf.tf_gs = tss->tss_gs; 328 tf.tf_fs = tss->tss_fs; 329 tf.tf_es = tss->__tss_es; 330 tf.tf_ds = tss->__tss_ds; 331 tf.tf_edi = tss->__tss_edi; 332 tf.tf_esi = tss->__tss_esi; 333 tf.tf_ebp = tss->tss_ebp; 334 tf.tf_ebx = tss->__tss_ebx; 335 tf.tf_edx = tss->__tss_edx; 336 tf.tf_ecx = tss->__tss_ecx; 337 tf.tf_eax = tss->__tss_eax; 338 tf.tf_trapno = 0; 339 tf.tf_err = TC_TSS; 340 tf.tf_eip = tss->__tss_eip; 341 tf.tf_cs = tss->__tss_cs; 342 tf.tf_eflags = tss->__tss_eflags; 343 tf.tf_esp = tss->tss_esp; 344 tf.tf_ss = tss->__tss_ss; 345 346 ddb_suspend(&tf); 347 } 348 349 static void 350 ddb_suspend(struct trapframe *frame) 351 { 352 volatile struct cpu_info *ci = curcpu(); 353 db_regs_t regs; 354 int flags; 355 356 regs = *frame; 357 flags = regs.tf_err & TC_FLAGMASK; 358 regs.tf_err &= ~TC_FLAGMASK; 359 if (!(flags & TC_TSS) && KERNELMODE(regs.tf_cs)) { 360 /* 361 * Kernel mode - esp and ss not saved 362 */ 363 regs.tf_esp = (int)&frame->tf_esp; /* kernel stack pointer */ 364 regs.tf_ss = x86_getss(); 365 } 366 367 ci->ci_ddb_regs = ®s; 368 369 atomic_or_32(&ci->ci_flags, CPUF_PAUSE); 370 371 while (ci->ci_flags & CPUF_PAUSE) 372 x86_pause(); 373 ci->ci_ddb_regs = 0; 374 tlbflushg(); 375 } 376 377 378 extern void cpu_debug_dump(void); /* XXX */ 379 380 void 381 db_mach_cpu(db_expr_t addr, bool have_addr, db_expr_t count, const char *modif) 382 { 383 struct cpu_info *ci; 384 if (!have_addr) { 385 cpu_debug_dump(); 386 return; 387 } 388 389 if (addr < 0) { 390 db_printf("%ld: CPU out of range\n", addr); 391 return; 392 } 393 ci = cpu_lookup(addr); 394 if (ci == NULL) { 395 db_printf("CPU %ld not configured\n", addr); 396 return; 397 } 398 if (ci != curcpu()) { 399 if (!(ci->ci_flags & CPUF_PAUSE)) { 400 db_printf("CPU %ld not paused\n", addr); 401 return; 402 } 403 } 404 if (ci->ci_ddb_regs == 0) { 405 db_printf("CPU %ld has no saved regs\n", addr); 406 return; 407 } 408 db_printf("using CPU %ld", addr); 409 ddb_regp = ci->ci_ddb_regs; 410 } 411 412 #endif 413