1 /* $NetBSD: db_interface.c,v 1.98 2023/10/26 10:41:03 andvar 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 * From: 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.98 2023/10/26 10:41:03 andvar Exp $");
37
38 #ifdef _KERNEL_OPT
39 #include "opt_ddb.h"
40 #include "opt_kgdb.h"
41 #include "opt_multiprocessor.h"
42 #endif
43
44 #include <sys/param.h>
45 #include <sys/proc.h>
46 #include <sys/cpu.h>
47 #include <sys/reboot.h>
48 #include <sys/systm.h>
49 #include <sys/lwp.h>
50
51 #include <dev/cons.h>
52
53 #include <uvm/uvm.h>
54
55 #include <machine/db_machdep.h>
56 #include <machine/locore.h>
57
58 #include <ddb/db_access.h>
59 #include <ddb/db_active.h>
60 #include <ddb/ddbvar.h>
61
62 #if defined(DDB) || defined(_KMEMUSER)
63 #include <ddb/db_user.h>
64 #include <ddb/db_command.h>
65 #include <ddb/db_sym.h>
66 #include <ddb/db_variables.h>
67 #include <ddb/db_extern.h>
68 #include <ddb/db_output.h>
69 #include <ddb/db_interface.h>
70 #endif
71 #ifdef KGDB
72 #include <ddb/db_interface.h>
73 #endif
74
75 #include <machine/instr.h>
76 #if defined(_KERNEL)
77 #include <machine/promlib.h>
78 #endif
79 #include <machine/ctlreg.h>
80 #include <machine/pmap.h>
81
82 #if defined(_KERNEL)
83 #include <sparc/sparc/asm.h>
84
85 #include "fb.h"
86
87 /*
88 * Read bytes from kernel address space for debugger.
89 */
90 void
db_read_bytes(vaddr_t addr,size_t size,char * data)91 db_read_bytes(vaddr_t addr, size_t size, char *data)
92 {
93 char *src;
94
95 src = (char *)addr;
96 while (size-- > 0)
97 *data++ = *src++;
98 }
99
100 /*
101 * Write bytes to kernel address space for debugger.
102 */
103 void
db_write_bytes(vaddr_t addr,size_t size,const char * data)104 db_write_bytes(vaddr_t addr, size_t size, const char *data)
105 {
106 char *dst;
107
108 dst = (char *)addr;
109 while (size-- > 0) {
110 if ((dst >= (char *)VM_MIN_KERNEL_ADDRESS) && (dst < etext))
111 pmap_writetext(dst, *data);
112 else
113 *dst = *data;
114 dst++, data++;
115 }
116
117 }
118 #endif
119
120 #if defined(DDB)
121
122 /*
123 * Data and functions used by DDB only.
124 */
125
126 void
cpu_Debugger(void)127 cpu_Debugger(void)
128 {
129 __asm("ta 0x81");
130 sparc_noop(); /* Force this function to allocate a stack frame */
131 }
132
133 #endif /* DDB */
134
135 #if defined(DDB) || defined(_KMEMUSER)
136
137 int db_active = 0;
138
139 #ifdef _KERNEL
140 void kdb_kbd_trap(struct trapframe *);
141 void db_prom_cmd(db_expr_t, bool, db_expr_t, const char *);
142 void db_page_cmd(db_expr_t, bool, db_expr_t, const char *);
143 void db_proc_cmd(db_expr_t, bool, db_expr_t, const char *);
144 void db_dump_pcb(db_expr_t, bool, db_expr_t, const char *);
145 #endif
146 #ifdef MULTIPROCESSOR
147 void db_cpu_cmd(db_expr_t, bool, db_expr_t, const char *);
148 void db_xcall_cmd(db_expr_t, bool, db_expr_t, const char *);
149 #endif
150
151 #ifdef _KERNEL
152 /*
153 * Received keyboard interrupt sequence.
154 */
155 void
kdb_kbd_trap(struct trapframe * tf)156 kdb_kbd_trap(struct trapframe *tf)
157 {
158 if (db_active == 0 && (boothowto & RB_KDB)) {
159 printf("\n\nkernel: keyboard interrupt\n");
160 kdb_trap(-1, tf);
161 }
162 }
163 #endif
164
165 /* struct cpu_info of CPU being investigated */
166 struct cpu_info *ddb_cpuinfo;
167
168 #ifdef MULTIPROCESSOR
169
170 #define NOCPU -1
171
172 static int db_suspend_others(void);
173 static void db_resume_others(void);
174 void ddb_suspend(struct trapframe *);
175
176 /* from cpu.c */
177 void mp_pause_cpus_ddb(void);
178 void mp_resume_cpus_ddb(void);
179
180 __cpu_simple_lock_t db_lock;
181 int ddb_cpu = NOCPU;
182
183 static int
db_suspend_others(void)184 db_suspend_others(void)
185 {
186 int cpu_me = cpu_number();
187 int win;
188
189 __cpu_simple_lock(&db_lock);
190 if (ddb_cpu == NOCPU)
191 ddb_cpu = cpu_me;
192 win = (ddb_cpu == cpu_me);
193 __cpu_simple_unlock(&db_lock);
194
195 if (win)
196 mp_pause_cpus_ddb();
197
198 return win;
199 }
200
201 static void
db_resume_others(void)202 db_resume_others(void)
203 {
204
205 mp_resume_cpus_ddb();
206
207 __cpu_simple_lock(&db_lock);
208 ddb_cpu = NOCPU;
209 __cpu_simple_unlock(&db_lock);
210 }
211
212 void
ddb_suspend(struct trapframe * tf)213 ddb_suspend(struct trapframe *tf)
214 {
215 volatile db_regs_t dbregs;
216
217 /* Initialise local dbregs storage from trap frame */
218 dbregs.db_tf = *tf;
219 dbregs.db_fr = *(struct frame *)tf->tf_out[6];
220
221 cpuinfo.ci_ddb_regs = &dbregs;
222 while (cpuinfo.flags & CPUFLG_PAUSED) /*void*/;
223 cpuinfo.ci_ddb_regs = NULL;
224 }
225 #endif /* MULTIPROCESSOR */
226
227 #if defined(DDB) || defined(KGDB)
228 /*
229 * kdb_trap - field a TRACE or BPT trap
230 */
231 int
kdb_trap(int type,struct trapframe * tf)232 kdb_trap(int type, struct trapframe *tf)
233 {
234 db_regs_t dbregs;
235 int s;
236
237 #if NFB > 0
238 fb_unblank();
239 #endif
240
241 switch (type) {
242 case T_BREAKPOINT: /* breakpoint */
243 case -1: /* keyboard interrupt */
244 break;
245 default:
246 if (!db_onpanic && db_recover==0)
247 return (0);
248
249 printf("kernel: %s trap\n", trap_type[type & 0xff]);
250 if (db_recover != 0) {
251 db_error("Faulted in DDB; continuing...\n");
252 /*NOTREACHED*/
253 }
254 }
255
256 #ifdef MULTIPROCESSOR
257 if (!db_suspend_others()) {
258 ddb_suspend(tf);
259 return 1;
260 }
261 #endif
262 /* Initialise local dbregs storage from trap frame */
263 dbregs.db_tf = *tf;
264 dbregs.db_fr = *(struct frame *)tf->tf_out[6];
265
266 /* Setup current CPU & reg pointers */
267 ddb_cpuinfo = curcpu();
268 curcpu()->ci_ddb_regs = ddb_regp = &dbregs;
269
270 /* Should switch to kdb`s own stack here. */
271
272 s = splhigh();
273 db_active++;
274 cnpollc(true);
275 db_trap(type, 0/*code*/);
276 cnpollc(false);
277 db_active--;
278 splx(s);
279
280 /* Update trap frame from local dbregs storage */
281 *(struct frame *)tf->tf_out[6] = dbregs.db_fr;
282 *tf = dbregs.db_tf;
283 curcpu()->ci_ddb_regs = ddb_regp = 0;
284 ddb_cpuinfo = NULL;
285
286 #ifdef MULTIPROCESSOR
287 db_resume_others();
288 #endif
289
290 return (1);
291 }
292 #endif /* DDB || KGDB */
293
294 #ifdef _KERNEL
295 void
db_proc_cmd(db_expr_t addr,bool have_addr,db_expr_t count,const char * modif)296 db_proc_cmd(db_expr_t addr, bool have_addr, db_expr_t count, const char *modif)
297 {
298 struct lwp *l;
299 struct proc *p;
300
301 l = curlwp;
302 if (have_addr)
303 l = (struct lwp *) addr;
304
305 if (l == NULL) {
306 db_printf("no current process\n");
307 return;
308 }
309
310 p = l->l_proc;
311
312 db_printf("LWP %p: ", l);
313 db_printf("PID:%d.%d CPU:%d stat:%d vmspace:%p", p->p_pid,
314 l->l_lid, l->l_cpu->ci_cpuid, l->l_stat, p->p_vmspace);
315 if (!P_ZOMBIE(p))
316 db_printf(" ctx: %p cpuset %x",
317 p->p_vmspace->vm_map.pmap->pm_ctx,
318 p->p_vmspace->vm_map.pmap->pm_cpuset);
319 db_printf("\npmap:%p wchan:%p pri:%d epri:%d\n",
320 p->p_vmspace->vm_map.pmap,
321 l->l_wchan, l->l_priority, lwp_eprio(l));
322 db_printf("maxsaddr:%p ssiz:%d pg or %llxB\n",
323 p->p_vmspace->vm_maxsaddr, p->p_vmspace->vm_ssize,
324 (unsigned long long)ctob(p->p_vmspace->vm_ssize));
325 db_printf("profile timer: %lld sec %ld nsec\n",
326 p->p_stats->p_timer[ITIMER_PROF].it_value.tv_sec,
327 p->p_stats->p_timer[ITIMER_PROF].it_value.tv_nsec);
328 db_printf("pcb: %p\n", lwp_getpcb(l));
329 return;
330 }
331
332 void
db_dump_pcb(db_expr_t addr,bool have_addr,db_expr_t count,const char * modif)333 db_dump_pcb(db_expr_t addr, bool have_addr, db_expr_t count, const char *modif)
334 {
335 struct pcb *pcb;
336 char bits[64];
337 int i;
338
339 if (have_addr)
340 pcb = (struct pcb *) addr;
341 else
342 pcb = curcpu()->curpcb;
343
344 snprintb(bits, sizeof(bits), PSR_BITS, pcb->pcb_psr);
345 db_printf("pcb@%p sp:%p pc:%p psr:%s onfault:%p\nfull windows:\n",
346 pcb, (void *)(long)pcb->pcb_sp, (void *)(long)pcb->pcb_pc,
347 bits, (void *)pcb->pcb_onfault);
348
349 for (i=0; i<pcb->pcb_nsaved; i++) {
350 db_printf("win %d: at %llx local, in\n", i,
351 (unsigned long long)pcb->pcb_rw[i+1].rw_in[6]);
352 db_printf("%16llx %16llx %16llx %16llx\n",
353 (unsigned long long)pcb->pcb_rw[i].rw_local[0],
354 (unsigned long long)pcb->pcb_rw[i].rw_local[1],
355 (unsigned long long)pcb->pcb_rw[i].rw_local[2],
356 (unsigned long long)pcb->pcb_rw[i].rw_local[3]);
357 db_printf("%16llx %16llx %16llx %16llx\n",
358 (unsigned long long)pcb->pcb_rw[i].rw_local[4],
359 (unsigned long long)pcb->pcb_rw[i].rw_local[5],
360 (unsigned long long)pcb->pcb_rw[i].rw_local[6],
361 (unsigned long long)pcb->pcb_rw[i].rw_local[7]);
362 db_printf("%16llx %16llx %16llx %16llx\n",
363 (unsigned long long)pcb->pcb_rw[i].rw_in[0],
364 (unsigned long long)pcb->pcb_rw[i].rw_in[1],
365 (unsigned long long)pcb->pcb_rw[i].rw_in[2],
366 (unsigned long long)pcb->pcb_rw[i].rw_in[3]);
367 db_printf("%16llx %16llx %16llx %16llx\n",
368 (unsigned long long)pcb->pcb_rw[i].rw_in[4],
369 (unsigned long long)pcb->pcb_rw[i].rw_in[5],
370 (unsigned long long)pcb->pcb_rw[i].rw_in[6],
371 (unsigned long long)pcb->pcb_rw[i].rw_in[7]);
372 }
373 }
374
375 void
db_prom_cmd(db_expr_t addr,bool have_addr,db_expr_t count,const char * modif)376 db_prom_cmd(db_expr_t addr, bool have_addr, db_expr_t count, const char *modif)
377 {
378
379 prom_abort();
380 }
381
382 void
db_page_cmd(db_expr_t addr,bool have_addr,db_expr_t count,const char * modif)383 db_page_cmd(db_expr_t addr, bool have_addr, db_expr_t count, const char *modif)
384 {
385
386 if (!have_addr) {
387 db_printf("Need paddr for page\n");
388 return;
389 }
390
391 db_printf("pa %llx pg %p\n", (unsigned long long)addr,
392 PHYS_TO_VM_PAGE(addr));
393 }
394 #endif /* _KERNEL */
395
396 #if defined(MULTIPROCESSOR)
397
398 void
db_cpu_cmd(db_expr_t addr,bool have_addr,db_expr_t count,const char * modif)399 db_cpu_cmd(db_expr_t addr, bool have_addr, db_expr_t count, const char *modif)
400 {
401 struct cpu_info *ci;
402 if (!have_addr) {
403 cpu_debug_dump();
404 return;
405 }
406
407 if ((addr < 0) || (addr >= sparc_ncpus)) {
408 db_printf("%ld: CPU out of range\n", addr);
409 return;
410 }
411 ci = cpus[addr];
412 if (ci == NULL) {
413 db_printf("CPU %ld not configured\n", addr);
414 return;
415 }
416 if (ci != curcpu()) {
417 if (!(ci->flags & CPUFLG_PAUSED)) {
418 db_printf("CPU %ld not paused\n", addr);
419 return;
420 }
421 }
422 if (ci->ci_ddb_regs == 0) {
423 db_printf("CPU %ld has no saved regs\n", addr);
424 return;
425 }
426 db_printf("using CPU %ld", addr);
427 ddb_regp = __UNVOLATILE(ci->ci_ddb_regs);
428 ddb_cpuinfo = ci;
429 }
430
431 void
db_xcall_cmd(db_expr_t addr,bool have_addr,db_expr_t count,const char * modif)432 db_xcall_cmd(db_expr_t addr, bool have_addr, db_expr_t count, const char *modif)
433 {
434 cpu_xcall_dump();
435 }
436
437 #endif /* MULTIPROCESSOR */
438
439 const struct db_command db_machine_command_table[] = {
440 #ifdef _KERNEL
441 { DDB_ADD_CMD("prom", db_prom_cmd, 0,
442 "Enter the Sun PROM monitor.",NULL,NULL) },
443 { DDB_ADD_CMD("page", db_page_cmd, 0,
444 "Display the address of a struct vm_page given a physical address",
445 "pa", " pa:\tphysical address to look up") },
446 { DDB_ADD_CMD("proc", db_proc_cmd, 0,
447 "Display some information about an LWP",
448 "[addr]"," addr:\tstruct lwp address (curlwp otherwise)") },
449 { DDB_ADD_CMD("pcb", db_dump_pcb, 0,
450 "Display information about a struct pcb",
451 "[address]",
452 " address:\tthe struct pcb to print (curpcb otherwise)") },
453 #endif
454 #ifdef MULTIPROCESSOR
455 { DDB_ADD_CMD("cpu", db_cpu_cmd, 0,
456 "switch to another cpu's registers", "cpu-no", NULL) },
457 { DDB_ADD_CMD("xcall", db_xcall_cmd, 0,
458 "show xcall information on all cpus", NULL, NULL) },
459 #endif
460 { DDB_END_CMD },
461 };
462 #endif /* DDB || _KMEMUSER */
463
464 /*
465 * support for SOFTWARE_SSTEP:
466 * return the next pc if the given branch is taken.
467 *
468 * note: in the case of conditional branches with annul,
469 * this actually returns the next pc in the "not taken" path,
470 * but in that case next_instr_address() will return the
471 * next pc in the "taken" path. so even tho the breakpoints
472 * are backwards, everything will still work, and the logic is
473 * much simpler this way.
474 */
475 db_addr_t
db_branch_taken(int inst,db_addr_t pc,db_regs_t * regs)476 db_branch_taken(int inst, db_addr_t pc, db_regs_t *regs)
477 {
478 union instr insn;
479 db_addr_t npc = ddb_regp->db_tf.tf_npc;
480
481 insn.i_int = inst;
482
483 /*
484 * if this is not an annulled conditional branch, the next pc is "npc".
485 */
486
487 if (insn.i_any.i_op != IOP_OP2 || insn.i_branch.i_annul != 1)
488 return npc;
489
490 switch (insn.i_op2.i_op2) {
491 case IOP2_Bicc:
492 case IOP2_FBfcc:
493 case IOP2_BPcc:
494 case IOP2_FBPfcc:
495 case IOP2_CBccc:
496 /* branch on some condition-code */
497 switch (insn.i_branch.i_cond)
498 {
499 case Icc_A: /* always */
500 return pc + ((inst << 10) >> 8);
501
502 default: /* all other conditions */
503 return npc + 4;
504 }
505
506 case IOP2_BPr:
507 /* branch on register, always conditional */
508 return npc + 4;
509
510 default:
511 /* not a branch */
512 #ifdef _KERNEL
513 panic("branch_taken() on non-branch");
514 #else
515 printf("branch_taken() on non-branch\n");
516 return 0;
517 #endif
518 }
519 }
520
521 bool
db_inst_branch(int inst)522 db_inst_branch(int inst)
523 {
524 union instr insn;
525
526 insn.i_int = inst;
527
528 if (insn.i_any.i_op != IOP_OP2)
529 return false;
530
531 switch (insn.i_op2.i_op2) {
532 case IOP2_BPcc:
533 case IOP2_Bicc:
534 case IOP2_BPr:
535 case IOP2_FBPfcc:
536 case IOP2_FBfcc:
537 case IOP2_CBccc:
538 return true;
539
540 default:
541 return false;
542 }
543 }
544
545
546 bool
db_inst_call(int inst)547 db_inst_call(int inst)
548 {
549 union instr insn;
550
551 insn.i_int = inst;
552
553 switch (insn.i_any.i_op) {
554 case IOP_CALL:
555 return true;
556
557 case IOP_reg:
558 return (insn.i_op3.i_op3 == IOP3_JMPL) && !db_inst_return(inst);
559
560 default:
561 return false;
562 }
563 }
564
565
566 bool
db_inst_unconditional_flow_transfer(int inst)567 db_inst_unconditional_flow_transfer(int inst)
568 {
569 union instr insn;
570
571 insn.i_int = inst;
572
573 if (db_inst_call(inst))
574 return true;
575
576 if (insn.i_any.i_op != IOP_OP2)
577 return false;
578
579 switch (insn.i_op2.i_op2)
580 {
581 case IOP2_BPcc:
582 case IOP2_Bicc:
583 case IOP2_FBPfcc:
584 case IOP2_FBfcc:
585 case IOP2_CBccc:
586 return insn.i_branch.i_cond == Icc_A;
587
588 default:
589 return false;
590 }
591 }
592
593
594 bool
db_inst_return(int inst)595 db_inst_return(int inst)
596 {
597
598 return (inst == I_JMPLri(I_G0, I_O7, 8) || /* ret */
599 inst == I_JMPLri(I_G0, I_I7, 8)); /* retl */
600 }
601
602 bool
db_inst_trap_return(int inst)603 db_inst_trap_return(int inst)
604 {
605 union instr insn;
606
607 insn.i_int = inst;
608
609 return (insn.i_any.i_op == IOP_reg &&
610 insn.i_op3.i_op3 == IOP3_RETT);
611 }
612
613
614 int
db_inst_load(int inst)615 db_inst_load(int inst)
616 {
617 union instr insn;
618
619 insn.i_int = inst;
620
621 if (insn.i_any.i_op != IOP_mem)
622 return 0;
623
624 switch (insn.i_op3.i_op3) {
625 case IOP3_LD:
626 case IOP3_LDUB:
627 case IOP3_LDUH:
628 case IOP3_LDD:
629 case IOP3_LDSB:
630 case IOP3_LDSH:
631 case IOP3_LDSTUB:
632 case IOP3_SWAP:
633 case IOP3_LDA:
634 case IOP3_LDUBA:
635 case IOP3_LDUHA:
636 case IOP3_LDDA:
637 case IOP3_LDSBA:
638 case IOP3_LDSHA:
639 case IOP3_LDSTUBA:
640 case IOP3_SWAPA:
641 case IOP3_LDF:
642 case IOP3_LDFSR:
643 case IOP3_LDDF:
644 case IOP3_LFC:
645 case IOP3_LDCSR:
646 case IOP3_LDDC:
647 return 1;
648
649 default:
650 return 0;
651 }
652 }
653
654 int
db_inst_store(int inst)655 db_inst_store(int inst)
656 {
657 union instr insn;
658
659 insn.i_int = inst;
660
661 if (insn.i_any.i_op != IOP_mem)
662 return 0;
663
664 switch (insn.i_op3.i_op3) {
665 case IOP3_ST:
666 case IOP3_STB:
667 case IOP3_STH:
668 case IOP3_STD:
669 case IOP3_LDSTUB:
670 case IOP3_SWAP:
671 case IOP3_STA:
672 case IOP3_STBA:
673 case IOP3_STHA:
674 case IOP3_STDA:
675 case IOP3_LDSTUBA:
676 case IOP3_SWAPA:
677 case IOP3_STF:
678 case IOP3_STFSR:
679 case IOP3_STDFQ:
680 case IOP3_STDF:
681 case IOP3_STC:
682 case IOP3_STCSR:
683 case IOP3_STDCQ:
684 case IOP3_STDC:
685 return 1;
686
687 default:
688 return 0;
689 }
690 }
691