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