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 *)&reg);
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