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@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
db_alpha_regop(const struct db_variable * vp,db_expr_t * val,int opcode)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
ddb_trap(unsigned long a0,unsigned long a1,unsigned long a2,unsigned long entry,db_regs_t * regs)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
db_read_bytes(vaddr_t addr,register size_t size,register char * data)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
db_write_bytes(vaddr_t addr,register size_t size,register const char * data)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
cpu_Debugger(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
db_mach_cpu(db_expr_t addr,bool have_addr,db_expr_t count,const char * modif)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
db_register_value(db_regs_t * regs,int regno)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
db_inst_call(int ins)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
db_inst_return(int ins)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
db_inst_trap_return(int ins)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
db_inst_branch(int ins)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
db_inst_unconditional_flow_transfer(int ins)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
db_inst_load(int ins)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
db_inst_store(int ins)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
db_branch_taken(int ins,db_addr_t pc,db_regs_t * regs)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
db_alpha_read_saved_reg(unsigned long * regp)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
db_alpha_tf_reg(struct trapframe * tf,unsigned int regno)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
db_alpha_nlist_lookup(db_addr_t addr)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
db_alpha_sym_is_trap(db_addr_t addr)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
db_alpha_sym_is_backstop(db_addr_t addr)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
db_alpha_sym_is_syscall(db_addr_t addr)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 *
db_alpha_trapsym_description(db_addr_t addr)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