1 /* $NetBSD: db_interface.c,v 1.46 2026/11/10 03:43:25 thorpej Exp $ */ 2 3 /* 4 * Mach Operating System 5 * Copyright (c) 1992,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 (at) 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 * Parts of this file are derived from Mach 3: 33 * 34 * File: alpha_instruction.c 35 * Author: Alessandro Forin, Carnegie Mellon University 36 * Date: 6/92 37 */ 38 39 /* 40 * Interface to DDB. 41 * 42 * Modified for NetBSD/alpha by: 43 * 44 * Christopher G. Demetriou, Carnegie Mellon University 45 * 46 * Jason R. Thorpe, Numerical Aerospace Simulation Facility, 47 * NASA Ames Research Center 48 */ 49 50 #ifdef _KERNEL_OPT 51 #include "opt_ddb.h" 52 #include "opt_multiprocessor.h" 53 #endif 54 55 #include <sys/cdefs.h> /* RCS ID & Copyright macro defns */ 56 57 __KERNEL_RCSID(0, "$NetBSD: db_interface.c,v 1.46 2026/11/10 03:43:25 thorpej Exp $"); 58 59 #include <sys/param.h> 60 #include <sys/proc.h> 61 #include <sys/reboot.h> 62 #include <sys/systm.h> 63 64 #include <dev/cons.h> 65 66 #include <machine/alpha.h> 67 #include <machine/db_machdep.h> 68 #include <machine/pal.h> 69 #include <machine/prom.h> 70 71 #include <machine/alpha_instruction.h> 72 73 #include <ddb/db_user.h> 74 #include <ddb/db_sym.h> 75 #include <ddb/db_command.h> 76 #include <ddb/db_extern.h> 77 #include <ddb/db_access.h> 78 #include <ddb/db_active.h> 79 #include <ddb/db_output.h> 80 #include <ddb/db_variables.h> 81 #include <ddb/db_interface.h> 82 83 #if 0 84 extern char *trap_type[]; 85 extern int trap_types; 86 #endif 87 88 db_regs_t *ddb_regp; 89 90 #if defined(MULTIPROCESSOR) 91 void db_mach_cpu(db_expr_t, bool, db_expr_t, const char *); 92 #endif 93 94 const struct db_command db_machine_command_table[] = { 95 #if defined(MULTIPROCESSOR) 96 { DDB_ADD_CMD("cpu", db_mach_cpu, 0, 97 "switch to another cpu", "cpu-no", NULL) }, 98 #endif 99 { DDB_END_CMD }, 100 }; 101 102 static int db_alpha_regop(const struct db_variable *, db_expr_t *, int); 103 104 #define dbreg(xx) ((long *)(xx)) 105 106 #define DBREG(n, r) \ 107 { .name = __STRING(n), \ 108 .valuep = ((long *)(r)), \ 109 .fcn = db_alpha_regop, \ 110 .modif = NULL, } 111 112 const struct db_variable db_regs[] = { 113 DBREG(v0, FRAME_V0), 114 DBREG(t0, FRAME_T0), 115 DBREG(t1, FRAME_T1), 116 DBREG(t2, FRAME_T2), 117 DBREG(t3, FRAME_T3), 118 DBREG(t4, FRAME_T4), 119 DBREG(t5, FRAME_T5), 120 DBREG(t6, FRAME_T6), 121 DBREG(t7, FRAME_T7), 122 DBREG(s0, FRAME_S0), 123 DBREG(s1, FRAME_S1), 124 DBREG(s2, FRAME_S2), 125 DBREG(s3, FRAME_S3), 126 DBREG(s4, FRAME_S4), 127 DBREG(s5, FRAME_S5), 128 DBREG(s6, FRAME_S6), 129 DBREG(a0, FRAME_A0), 130 DBREG(a1, FRAME_A1), 131 DBREG(a2, FRAME_A2), 132 DBREG(a3, FRAME_A3), 133 DBREG(a4, FRAME_A4), 134 DBREG(a5, FRAME_A5), 135 DBREG(t8, FRAME_T8), 136 DBREG(t9, FRAME_T9), 137 DBREG(t10, FRAME_T10), 138 DBREG(t11, FRAME_T11), 139 DBREG(ra, FRAME_RA), 140 DBREG(t12, FRAME_T12), 141 DBREG(at, FRAME_AT), 142 DBREG(gp, FRAME_GP), 143 DBREG(sp, FRAME_SP), 144 DBREG(pc, FRAME_PC), 145 DBREG(ps, FRAME_PS), 146 DBREG(ai, FRAME_T11), 147 DBREG(pv, FRAME_T12), 148 }; 149 const struct db_variable * const db_eregs = db_regs + sizeof(db_regs)/sizeof(db_regs[0]); 150 151 #undef DBREG 152 153 static int 154 db_alpha_regop(const struct db_variable *vp, db_expr_t *val, int opcode) 155 { 156 unsigned long *tfaddr; 157 unsigned long zeroval = 0; 158 struct trapframe *f = NULL; 159 160 #ifdef _KERNEL /* XXX ?? */ 161 if (vp->modif != NULL && *vp->modif == 'u') { 162 if (curlwp != NULL) 163 f = curlwp->l_md.md_tf; 164 } else 165 #endif /* _KERNEL */ 166 f = DDB_REGS; 167 tfaddr = f == NULL ? &zeroval : &f->tf_regs[(u_long)vp->valuep]; 168 switch (opcode) { 169 case DB_VAR_GET: 170 *val = *tfaddr; 171 break; 172 173 case DB_VAR_SET: 174 *tfaddr = *val; 175 break; 176 177 default: 178 #ifdef _KERNEL 179 panic("db_alpha_regop: unknown op %d", opcode); 180 #endif 181 break; 182 } 183 184 return (0); 185 } 186 187 #ifdef _KERNEL 188 /* 189 * ddb_trap - field a kernel trap 190 */ 191 int 192 ddb_trap(unsigned long a0, unsigned long a1, unsigned long a2, unsigned long entry, db_regs_t *regs) 193 { 194 struct cpu_info *ci = curcpu(); 195 unsigned long psl; 196 197 if (entry != ALPHA_KENTRY_IF || 198 (a0 != ALPHA_IF_CODE_BPT && a0 != ALPHA_IF_CODE_BUGCHK)) { 199 if (db_recover != 0) { 200 /* This will longjmp back into db_command_loop() */ 201 db_error("Caught exception in ddb.\n"); 202 /* NOTREACHED */ 203 } 204 205 /* 206 * Tell caller "We did NOT handle the trap." 207 * Caller should panic, or whatever. 208 */ 209 return (0); 210 } 211 212 /* 213 * alpha_debug() switches us to the debugger stack. 214 */ 215 216 /* Our register state is simply the trapframe. */ 217 ddb_regp = ci->ci_db_regs = regs; 218 219 /* 220 * Use SWPIPL directly; we want to avoid processing 221 * software interrupts when we go back. Soft ints 222 * will be caught later, so not to worry. 223 */ 224 psl = alpha_pal_swpipl(ALPHA_PSL_IPL_HIGH); 225 226 db_active++; 227 cnpollc(true); /* Set polling mode, unblank video */ 228 229 db_trap(entry, a0); /* Where the work happens */ 230 231 cnpollc(false); /* Resume interrupt mode */ 232 db_active--; 233 234 alpha_pal_swpipl(psl); 235 236 ddb_regp = ci->ci_db_regs = NULL; 237 238 /* 239 * Tell caller "We HAVE handled the trap." 240 */ 241 return (1); 242 } 243 244 /* 245 * Read bytes from kernel address space for debugger. 246 */ 247 void 248 db_read_bytes(vaddr_t addr, register size_t size, register char *data) 249 { 250 register char *src; 251 252 src = (char *)addr; 253 while (size-- > 0) 254 *data++ = *src++; 255 } 256 257 /* 258 * Write bytes to kernel address space for debugger. 259 */ 260 void 261 db_write_bytes(vaddr_t addr, register size_t size, register const char *data) 262 { 263 register char *dst; 264 265 dst = (char *)addr; 266 while (size-- > 0) 267 *dst++ = *data++; 268 alpha_pal_imb(); 269 } 270 271 void 272 cpu_Debugger(void) 273 { 274 275 __asm volatile("call_pal 0x81"); /* bugchk */ 276 } 277 #endif /* _KERNEL */ 278 279 /* 280 * Alpha-specific ddb commands: 281 * 282 * cpu tell DDB to use register state from the 283 * CPU specified (MULTIPROCESSOR) 284 */ 285 286 #if defined(MULTIPROCESSOR) 287 void 288 db_mach_cpu(db_expr_t addr, bool have_addr, db_expr_t count, const char * modif) 289 { 290 struct cpu_info *ci; 291 292 if (!have_addr) { 293 cpu_debug_dump(); 294 return; 295 } 296 297 if (addr < 0 || addr >= ALPHA_MAXPROCS) { 298 db_printf("CPU %ld out of range\n", addr); 299 return; 300 } 301 302 ci = cpu_info[addr]; 303 if (ci == NULL) { 304 db_printf("CPU %ld is not configured\n", addr); 305 return; 306 } 307 308 if (ci != curcpu()) { 309 if ((ci->ci_flags & CPUF_PAUSED) == 0) { 310 db_printf("CPU %ld not paused\n", addr); 311 return; 312 } 313 } 314 315 if (ci->ci_db_regs == NULL) { 316 db_printf("CPU %ld has no register state\n", addr); 317 return; 318 } 319 320 db_printf("Using CPU %ld\n", addr); 321 ddb_regp = ci->ci_db_regs; 322 } 323 #endif /* MULTIPROCESSOR */ 324 325 /* 326 * Map Alpha register numbers to trapframe/db_regs_t offsets. 327 */ 328 static int reg_to_frame[32] = { 329 FRAME_V0, 330 FRAME_T0, 331 FRAME_T1, 332 FRAME_T2, 333 FRAME_T3, 334 FRAME_T4, 335 FRAME_T5, 336 FRAME_T6, 337 FRAME_T7, 338 339 FRAME_S0, 340 FRAME_S1, 341 FRAME_S2, 342 FRAME_S3, 343 FRAME_S4, 344 FRAME_S5, 345 FRAME_S6, 346 347 FRAME_A0, 348 FRAME_A1, 349 FRAME_A2, 350 FRAME_A3, 351 FRAME_A4, 352 FRAME_A5, 353 354 FRAME_T8, 355 FRAME_T9, 356 FRAME_T10, 357 FRAME_T11, 358 FRAME_RA, 359 FRAME_T12, 360 FRAME_AT, 361 FRAME_GP, 362 FRAME_SP, 363 -1, /* zero */ 364 }; 365 366 u_long 367 db_register_value(db_regs_t *regs, int regno) 368 { 369 370 if (regno > 31 || regno < 0) { 371 db_printf(" **** STRANGE REGISTER NUMBER %d **** ", regno); 372 return (0); 373 } 374 375 if (regno == 31) 376 return (0); 377 378 return (regs->tf_regs[reg_to_frame[regno]]); 379 } 380 381 #ifdef _KERNEL 382 /* 383 * Support functions for software single-step. 384 */ 385 386 bool 387 db_inst_call(int ins) 388 { 389 alpha_instruction insn; 390 391 insn.bits = ins; 392 return ((insn.branch_format.opcode == op_bsr) || 393 ((insn.jump_format.opcode == op_j) && 394 (insn.jump_format.action & 1))); 395 } 396 397 bool 398 db_inst_return(int ins) 399 { 400 alpha_instruction insn; 401 402 insn.bits = ins; 403 return ((insn.jump_format.opcode == op_j) && 404 (insn.jump_format.action == op_ret)); 405 } 406 407 bool 408 db_inst_trap_return(int ins) 409 { 410 alpha_instruction insn; 411 412 insn.bits = ins; 413 return ((insn.pal_format.opcode == op_pal) && 414 (insn.pal_format.function == PAL_OSF1_rti)); 415 } 416 417 bool 418 db_inst_branch(int ins) 419 { 420 alpha_instruction insn; 421 422 insn.bits = ins; 423 switch (insn.branch_format.opcode) { 424 case op_j: 425 case op_br: 426 case op_fbeq: 427 case op_fblt: 428 case op_fble: 429 case op_fbne: 430 case op_fbge: 431 case op_fbgt: 432 case op_blbc: 433 case op_beq: 434 case op_blt: 435 case op_ble: 436 case op_blbs: 437 case op_bne: 438 case op_bge: 439 case op_bgt: 440 return (true); 441 } 442 443 return (false); 444 } 445 446 bool 447 db_inst_unconditional_flow_transfer(int ins) 448 { 449 alpha_instruction insn; 450 451 insn.bits = ins; 452 switch (insn.branch_format.opcode) { 453 case op_j: 454 case op_br: 455 return (true); 456 457 case op_pal: 458 switch (insn.pal_format.function) { 459 case PAL_OSF1_retsys: 460 case PAL_OSF1_rti: 461 case PAL_OSF1_callsys: 462 return (true); 463 } 464 } 465 466 return (false); 467 } 468 469 #if 0 470 bool 471 db_inst_spill(int ins, int regn) 472 { 473 alpha_instruction insn; 474 475 insn.bits = ins; 476 return ((insn.mem_format.opcode == op_stq) && 477 (insn.mem_format.rd == regn)); 478 } 479 #endif 480 481 bool 482 db_inst_load(int ins) 483 { 484 alpha_instruction insn; 485 486 insn.bits = ins; 487 488 /* Loads. */ 489 if (insn.mem_format.opcode == op_ldbu || 490 insn.mem_format.opcode == op_ldq_u || 491 insn.mem_format.opcode == op_ldwu) 492 return (true); 493 if ((insn.mem_format.opcode >= op_ldf) && 494 (insn.mem_format.opcode <= op_ldt)) 495 return (true); 496 if ((insn.mem_format.opcode >= op_ldl) && 497 (insn.mem_format.opcode <= op_ldq_l)) 498 return (true); 499 500 /* Prefetches. */ 501 if (insn.mem_format.opcode == op_special) { 502 /* Note: MB is treated as a store. */ 503 if ((insn.mem_format.displacement == (short)op_fetch) || 504 (insn.mem_format.displacement == (short)op_fetch_m)) 505 return (true); 506 } 507 508 return (false); 509 } 510 511 bool 512 db_inst_store(int ins) 513 { 514 alpha_instruction insn; 515 516 insn.bits = ins; 517 518 /* Stores. */ 519 if (insn.mem_format.opcode == op_stw || 520 insn.mem_format.opcode == op_stb || 521 insn.mem_format.opcode == op_stq_u) 522 return (true); 523 if ((insn.mem_format.opcode >= op_stf) && 524 (insn.mem_format.opcode <= op_stt)) 525 return (true); 526 if ((insn.mem_format.opcode >= op_stl) && 527 (insn.mem_format.opcode <= op_stq_c)) 528 return (true); 529 530 /* Barriers. */ 531 if (insn.mem_format.opcode == op_special) { 532 if (insn.mem_format.displacement == op_mb) 533 return (true); 534 } 535 536 return (false); 537 } 538 539 db_addr_t 540 db_branch_taken(int ins, db_addr_t pc, db_regs_t *regs) 541 { 542 long signed_immediate; 543 alpha_instruction insn; 544 db_addr_t newpc; 545 546 insn.bits = ins; 547 switch (insn.branch_format.opcode) { 548 /* 549 * Jump format: target PC is (contents of instruction's "RB") & ~3. 550 */ 551 case op_j: 552 newpc = db_register_value(regs, insn.jump_format.rb) & ~3; 553 break; 554 555 /* 556 * Branch format: target PC is 557 * (new PC) + (4 * sign-ext(displacement)). 558 */ 559 case op_br: 560 case op_fbeq: 561 case op_fblt: 562 case op_fble: 563 case op_bsr: 564 case op_fbne: 565 case op_fbge: 566 case op_fbgt: 567 case op_blbc: 568 case op_beq: 569 case op_blt: 570 case op_ble: 571 case op_blbs: 572 case op_bne: 573 case op_bge: 574 case op_bgt: 575 signed_immediate = insn.branch_format.displacement; 576 newpc = (pc + 4) + (signed_immediate << 2); 577 break; 578 579 default: 580 printf("DDB: db_inst_branch_taken on non-branch!\n"); 581 newpc = pc; /* XXX */ 582 } 583 584 return (newpc); 585 } 586 #endif /* _KERNEL */ 587 588 unsigned long 589 db_alpha_read_saved_reg(unsigned long *regp) 590 { 591 unsigned long reg; 592 593 db_read_bytes((db_addr_t)regp, sizeof(reg), (char *)®); 594 return reg; 595 } 596 597 unsigned long 598 db_alpha_tf_reg(struct trapframe *tf, unsigned int regno) 599 { 600 return db_alpha_read_saved_reg(&tf->tf_regs[regno]); 601 } 602 603 /* 604 * Alpha special symbol handling. 605 */ 606 db_alpha_nlist db_alpha_nl[] = { 607 DB_ALPHA_SYM(SYM_XentArith, XentArith), 608 DB_ALPHA_SYM(SYM_XentIF, XentIF), 609 DB_ALPHA_SYM(SYM_XentInt, XentInt), 610 DB_ALPHA_SYM(SYM_XentMM, XentMM), 611 DB_ALPHA_SYM(SYM_XentSys, XentSys), 612 DB_ALPHA_SYM(SYM_XentUna, XentUna), 613 DB_ALPHA_SYM(SYM_XentRestart, XentRestart), 614 DB_ALPHA_SYM(SYM_exception_return, exception_return), 615 DB_ALPHA_SYM(SYM_alpha_kthread_backstop, alpha_kthread_backstop), 616 #ifndef _KERNEL 617 DB_ALPHA_SYM(SYM_dumppcb, dumppcb), 618 #endif /* _KERNEL */ 619 DB_ALPHA_SYM_EOL 620 }; 621 622 static int 623 db_alpha_nlist_lookup(db_addr_t addr) 624 { 625 int i; 626 627 for (i = 0; i < SYM___eol; i++) { 628 if (db_alpha_nl[i].n_value == addr) { 629 return i; 630 } 631 } 632 return -1; 633 } 634 635 bool 636 db_alpha_sym_is_trap(db_addr_t addr) 637 { 638 int i = db_alpha_nlist_lookup(addr); 639 return i >= SYM_XentArith && i <= SYM_exception_return; 640 } 641 642 bool 643 db_alpha_sym_is_backstop(db_addr_t addr) 644 { 645 return db_alpha_nlist_lookup(addr) == SYM_alpha_kthread_backstop; 646 } 647 648 bool 649 db_alpha_sym_is_syscall(db_addr_t addr) 650 { 651 return db_alpha_nlist_lookup(addr) == SYM_XentSys; 652 } 653 654 const char * 655 db_alpha_trapsym_description(db_addr_t addr) 656 { 657 static const char * const trap_descriptions[] = { 658 [SYM_XentArith] = "arithmetic trap", 659 [SYM_XentIF] = "instruction fault", 660 [SYM_XentInt] = "interrupt", 661 [SYM_XentMM] = "memory management fault", 662 [SYM_XentSys] = "syscall", 663 [SYM_XentUna] = "unaligned access fault", 664 [SYM_XentRestart] = "console restart", 665 [SYM_exception_return] = "(exception return)", 666 }; 667 668 int i = db_alpha_nlist_lookup(addr); 669 if (i >= SYM_XentArith && i <= SYM_exception_return) { 670 return trap_descriptions[i]; 671 } 672 return "??? trap ???"; 673 } 674