1*e7f0873cSskrll /* $NetBSD: db_interface.c,v 1.65 2023/08/02 14:36:39 skrll Exp $ */
28655276eSmatt
38655276eSmatt /*
48655276eSmatt * Copyright (c) 1996 Scott K. Stevens
58655276eSmatt *
68655276eSmatt * Mach Operating System
78655276eSmatt * Copyright (c) 1991,1990 Carnegie Mellon University
88655276eSmatt * All Rights Reserved.
98655276eSmatt *
108655276eSmatt * Permission to use, copy, modify and distribute this software and its
118655276eSmatt * documentation is hereby granted, provided that both the copyright
128655276eSmatt * notice and this permission notice appear in all copies of the
138655276eSmatt * software, derivative works or modified versions, and any portions
148655276eSmatt * thereof, and that both notices appear in supporting documentation.
158655276eSmatt *
168655276eSmatt * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
178655276eSmatt * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
188655276eSmatt * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
198655276eSmatt *
208655276eSmatt * Carnegie Mellon requests users of this software to return to
218655276eSmatt *
228655276eSmatt * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
238655276eSmatt * School of Computer Science
248655276eSmatt * Carnegie Mellon University
258655276eSmatt * Pittsburgh PA 15213-3890
268655276eSmatt *
278655276eSmatt * any improvements or extensions that they make and grant Carnegie the
288655276eSmatt * rights to redistribute these changes.
298655276eSmatt *
308655276eSmatt * From: db_interface.c,v 2.4 1991/02/05 17:11:13 mrt (CMU)
318655276eSmatt */
328655276eSmatt
338655276eSmatt /*
348655276eSmatt * Interface to new debugger.
358655276eSmatt */
3608716eaeSlukem
3708716eaeSlukem #include <sys/cdefs.h>
38*e7f0873cSskrll __KERNEL_RCSID(0, "$NetBSD: db_interface.c,v 1.65 2023/08/02 14:36:39 skrll Exp $");
3908716eaeSlukem
408655276eSmatt #include "opt_ddb.h"
41a2f6e1f0Sbriggs #include "opt_kgdb.h"
42cd0fc78fSskrll #include "opt_multiprocessor.h"
438655276eSmatt
448655276eSmatt #include <sys/param.h>
4579ef8ec5Sskrll
4679ef8ec5Sskrll #include <sys/atomic.h>
4779ef8ec5Sskrll #include <sys/exec.h>
4879ef8ec5Sskrll #include <sys/intr.h>
498655276eSmatt #include <sys/proc.h>
508655276eSmatt #include <sys/reboot.h>
518655276eSmatt #include <sys/systm.h> /* just for boothowto */
528655276eSmatt
538655276eSmatt #include <uvm/uvm_extern.h>
548655276eSmatt
55e3a3a9f5Schris #include <arm/arm32/db_machdep.h>
56fc019be5Sthorpej #include <arm/undefined.h>
57dbfa10e5Sriastradh
58e3a3a9f5Schris #include <ddb/db_access.h>
59dbfa10e5Sriastradh #include <ddb/db_active.h>
608655276eSmatt #include <ddb/db_command.h>
618655276eSmatt #include <ddb/db_output.h>
628655276eSmatt #include <ddb/db_variables.h>
638655276eSmatt #include <ddb/db_sym.h>
648655276eSmatt #include <ddb/db_extern.h>
658655276eSmatt #include <ddb/db_interface.h>
66dbfa10e5Sriastradh
678655276eSmatt #include <dev/cons.h>
688655276eSmatt
69a2f6e1f0Sbriggs #if defined(KGDB) || !defined(DDB)
70a2f6e1f0Sbriggs #define db_printf printf
71a2f6e1f0Sbriggs #endif
72a2f6e1f0Sbriggs
7302cdf4d2Sdsl u_int db_fetch_reg(int, db_regs_t *);
748655276eSmatt
7502cdf4d2Sdsl int db_trapper(u_int, u_int, trapframe_t *, int);
76e3a3a9f5Schris
778655276eSmatt int db_active = 0;
78027821c5Sskrll db_regs_t ddb_regs; /* register state */
79027821c5Sskrll db_regs_t *ddb_regp;
80027821c5Sskrll
81a9d8178cSmatt #ifdef MULTIPROCESSOR
82a9d8178cSmatt volatile struct cpu_info *db_onproc;
83a9d8178cSmatt volatile struct cpu_info *db_newcpu;
84a9d8178cSmatt #endif
85a9d8178cSmatt
86a9d8178cSmatt
878655276eSmatt
888655276eSmatt
89a2f6e1f0Sbriggs #ifdef DDB
908655276eSmatt /*
918655276eSmatt * kdb_trap - field a TRACE or BPT trap
928655276eSmatt */
938655276eSmatt int
kdb_trap(int type,db_regs_t * regs)94e422b995Sthorpej kdb_trap(int type, db_regs_t *regs)
958655276eSmatt {
96a9d8178cSmatt struct cpu_info * const ci = curcpu();
97027821c5Sskrll db_regs_t dbreg;
988655276eSmatt int s;
998655276eSmatt
1008655276eSmatt switch (type) {
1018655276eSmatt case T_BREAKPOINT: /* breakpoint */
1028655276eSmatt case -1: /* keyboard interrupt */
1038655276eSmatt break;
104a9d8178cSmatt #ifdef MULTIPROCESSOR
105a9d8178cSmatt case -2:
106a9d8178cSmatt /*
107a9d8178cSmatt * We called to enter ddb from another process but by the time
108a9d8178cSmatt * we got here, no one was in ddb. So ignore the request.
109a9d8178cSmatt */
110a9d8178cSmatt if (db_onproc == NULL)
111a9d8178cSmatt return 1;
112a9d8178cSmatt break;
113a9d8178cSmatt #endif
1148655276eSmatt default:
1158655276eSmatt if (db_recover != 0) {
11688f63e28Sthorpej /* This will longjmp back into db_command_loop() */
1178655276eSmatt db_error("Faulted in DDB; continuing...\n");
1188655276eSmatt /*NOTREACHED*/
1198655276eSmatt }
1208655276eSmatt }
1218655276eSmatt
1228655276eSmatt /* Should switch to kdb`s own stack here. */
1238655276eSmatt
124a9d8178cSmatt #ifdef MULTIPROCESSOR
125a9d8178cSmatt const bool is_mp_p = ncpu > 1;
126a9d8178cSmatt if (is_mp_p) {
127a9d8178cSmatt /*
128a9d8178cSmatt * Try to take ownership of DDB. If we do, tell all other
129a9d8178cSmatt * CPUs to enter DDB too.
130a9d8178cSmatt */
131a9d8178cSmatt if (atomic_cas_ptr(&db_onproc, NULL, ci) == NULL) {
132a9d8178cSmatt intr_ipi_send(NULL, IPI_DDB);
133a9d8178cSmatt }
134a9d8178cSmatt }
135a9d8178cSmatt for (;;) {
136a9d8178cSmatt if (is_mp_p) {
137a9d8178cSmatt /*
138a9d8178cSmatt * While we aren't the master, wait until the master
139a9d8178cSmatt * gives control to us or exits. If it exited, we
14042afdf9bSskrll * just exit too. Otherwise this cpu will enter DDB.
141a9d8178cSmatt */
142a9d8178cSmatt membar_consumer();
143a9d8178cSmatt while (db_onproc != ci) {
144a9d8178cSmatt if (db_onproc == NULL)
145a9d8178cSmatt return 1;
146a9d8178cSmatt #ifdef _ARM_ARCH_6
147a9d8178cSmatt __asm __volatile("wfe");
148a9d8178cSmatt membar_consumer();
149a9d8178cSmatt #endif
150a9d8178cSmatt if (db_onproc == ci) {
151a9d8178cSmatt printf("%s: switching to %s\n",
152a9d8178cSmatt __func__, ci->ci_cpuname);
153a9d8178cSmatt }
154a9d8178cSmatt }
155a9d8178cSmatt }
156a9d8178cSmatt #endif
1578655276eSmatt
1588655276eSmatt s = splhigh();
159027821c5Sskrll ci->ci_ddb_regs = &dbreg;
160027821c5Sskrll ddb_regp = &dbreg;
161027821c5Sskrll ddb_regs = *regs;
162027821c5Sskrll
163a9d8178cSmatt atomic_inc_32(&db_active);
164ab87c666Sthorpej cnpollc(true);
1658655276eSmatt db_trap(type, 0/*code*/);
166ab87c666Sthorpej cnpollc(false);
167a9d8178cSmatt atomic_dec_32(&db_active);
168027821c5Sskrll
169a9d8178cSmatt ci->ci_ddb_regs = NULL;
170027821c5Sskrll ddb_regp = &dbreg;
171027821c5Sskrll *regs = ddb_regs;
1728655276eSmatt splx(s);
1738655276eSmatt
174a9d8178cSmatt #ifdef MULTIPROCESSOR
175a9d8178cSmatt if (is_mp_p && db_newcpu != NULL) {
176a9d8178cSmatt db_onproc = db_newcpu;
177a9d8178cSmatt db_newcpu = NULL;
17869120ac1Sskrll dsb(ishst);
17969120ac1Sskrll sev();
180a9d8178cSmatt continue;
181a9d8178cSmatt }
182a9d8178cSmatt break;
183a9d8178cSmatt }
184a9d8178cSmatt
185a9d8178cSmatt if (is_mp_p) {
186a9d8178cSmatt /*
187a9d8178cSmatt * We are exiting DDB so there is noone onproc. Tell
188a9d8178cSmatt * the other CPUs to exit.
189a9d8178cSmatt */
190a9d8178cSmatt db_onproc = NULL;
19169120ac1Sskrll dsb(ishst);
19269120ac1Sskrll sev();
193a9d8178cSmatt }
194a9d8178cSmatt #endif
1958655276eSmatt
196d777316dSskrll return 1;
1978655276eSmatt }
198a2f6e1f0Sbriggs #endif
1998655276eSmatt
200a2f6e1f0Sbriggs int
db_validate_address(vaddr_t addr)201e422b995Sthorpej db_validate_address(vaddr_t addr)
2028655276eSmatt {
2038655276eSmatt struct proc *p = curproc;
20479543e3eSthorpej struct pmap *pmap;
2058655276eSmatt
20641a1932eSscw if (!p || !p->p_vmspace || !p->p_vmspace->vm_map.pmap ||
207c8bed530Sthorpej addr >= VM_MIN_KERNEL_ADDRESS
208c8bed530Sthorpej )
20979543e3eSthorpej pmap = pmap_kernel();
2108655276eSmatt else
21179543e3eSthorpej pmap = p->p_vmspace->vm_map.pmap;
2128655276eSmatt
213df27e1efSskrll return pmap_extract(pmap, addr, NULL) == false;
2148655276eSmatt }
2158655276eSmatt
2168655276eSmatt /*
2178655276eSmatt * Read bytes from kernel address space for debugger.
2188655276eSmatt */
2198655276eSmatt void
db_read_bytes(vaddr_t addr,size_t size,char * data)220454af1c0Sdsl db_read_bytes(vaddr_t addr, size_t size, char *data)
2218655276eSmatt {
22284f125b7Sscw char *src = (char *)addr;
2238655276eSmatt
22484f125b7Sscw if (db_validate_address((u_int)src)) {
22584f125b7Sscw db_printf("address %p is invalid\n", src);
22684f125b7Sscw return;
22784f125b7Sscw }
22884f125b7Sscw
22984f125b7Sscw if (size == 4 && (addr & 3) == 0 && ((uintptr_t)data & 3) == 0) {
23084f125b7Sscw *((int*)data) = *((int*)src);
23184f125b7Sscw return;
23284f125b7Sscw }
23384f125b7Sscw
23484f125b7Sscw if (size == 2 && (addr & 1) == 0 && ((uintptr_t)data & 1) == 0) {
23584f125b7Sscw *((short*)data) = *((short*)src);
23684f125b7Sscw return;
23784f125b7Sscw }
23879543e3eSthorpej
23979543e3eSthorpej while (size-- > 0) {
2408655276eSmatt if (db_validate_address((u_int)src)) {
2418655276eSmatt db_printf("address %p is invalid\n", src);
2428655276eSmatt return;
2438655276eSmatt }
2448655276eSmatt *data++ = *src++;
2458655276eSmatt }
2468655276eSmatt }
2478655276eSmatt
2488655276eSmatt static void
db_write_text(vaddr_t addr,size_t size,const char * data)249fd4f2844Suwe db_write_text(vaddr_t addr, size_t size, const char *data)
2508655276eSmatt {
2518655276eSmatt
252ba2539a9Schs ktext_write((void *)addr, data, size);
2538655276eSmatt }
2548655276eSmatt
2558655276eSmatt /*
2568655276eSmatt * Write bytes to kernel address space for debugger.
2578655276eSmatt */
2588655276eSmatt void
db_write_bytes(vaddr_t addr,size_t size,const char * data)259fd4f2844Suwe db_write_bytes(vaddr_t addr, size_t size, const char *data)
2608655276eSmatt {
26178fac054Sthorpej extern char kernel_text[];
2628655276eSmatt extern char etext[];
2638655276eSmatt char *dst;
26479543e3eSthorpej size_t loop;
2658655276eSmatt
266e422b995Sthorpej /* If any part is in kernel text, use db_write_text() */
26778fac054Sthorpej if (addr >= (vaddr_t) kernel_text && addr < (vaddr_t) etext) {
268e422b995Sthorpej db_write_text(addr, size, data);
269e422b995Sthorpej return;
270e422b995Sthorpej }
271e422b995Sthorpej
2728655276eSmatt dst = (char *)addr;
27384f125b7Sscw if (db_validate_address((u_int)dst)) {
27484f125b7Sscw db_printf("address %p is invalid\n", dst);
27584f125b7Sscw return;
27684f125b7Sscw }
27784f125b7Sscw
27884f125b7Sscw if (size == 4 && (addr & 3) == 0 && ((uintptr_t)data & 3) == 0)
279fd4f2844Suwe *((int *)dst) = *((const int *)data);
28084f125b7Sscw else
28184f125b7Sscw if (size == 2 && (addr & 1) == 0 && ((uintptr_t)data & 1) == 0)
282fd4f2844Suwe *((short *)dst) = *((const short *)data);
28384f125b7Sscw else {
2848655276eSmatt loop = size;
28579543e3eSthorpej while (loop-- > 0) {
2868655276eSmatt if (db_validate_address((u_int)dst)) {
2878655276eSmatt db_printf("address %p is invalid\n", dst);
2888655276eSmatt return;
2898655276eSmatt }
290e422b995Sthorpej *dst++ = *data++;
2918655276eSmatt }
29284f125b7Sscw }
29384f125b7Sscw
2948655276eSmatt /* make sure the caches and memory are in sync */
2954e990d9cSthorpej cpu_icache_sync_range(addr, size);
2968655276eSmatt
2978655276eSmatt /* In case the current page tables have been modified ... */
2988655276eSmatt cpu_tlb_flushID();
299940aa6cbSthorpej cpu_cpwait();
3008655276eSmatt }
3018655276eSmatt
3027ba6ee4cSbsh #ifdef DDB
3038655276eSmatt void
cpu_Debugger(void)304e422b995Sthorpej cpu_Debugger(void)
3058655276eSmatt {
30604c25313Srin #ifdef _ARM_ARCH_BE8
307877241d5Srin __asm(".word 0xffffffe7");
30804c25313Srin #else
30904c25313Srin __asm(".word 0xe7ffffff");
310877241d5Srin #endif
3118655276eSmatt }
3128655276eSmatt
3138655276eSmatt int
db_trapper(u_int addr,u_int inst,trapframe_t * frame,int fault_code)314e422b995Sthorpej db_trapper(u_int addr, u_int inst, trapframe_t *frame, int fault_code)
3158655276eSmatt {
316e422b995Sthorpej
3178655276eSmatt if (fault_code == 0) {
3188655276eSmatt if ((inst & ~INSN_COND_MASK) == (BKPT_INST & ~INSN_COND_MASK))
3198655276eSmatt kdb_trap(T_BREAKPOINT, frame);
3208655276eSmatt else
3218655276eSmatt kdb_trap(-1, frame);
3228655276eSmatt } else
323d777316dSskrll return 1;
324d777316dSskrll return 0;
3258655276eSmatt }
3268655276eSmatt
3278655276eSmatt extern u_int esym;
3288655276eSmatt extern u_int end;
3298655276eSmatt
330ce5529eaSbjh21 static struct undefined_handler db_uh;
331ce5529eaSbjh21
3328655276eSmatt void
db_machine_init(void)333e422b995Sthorpej db_machine_init(void)
3348655276eSmatt {
3358655276eSmatt
336ce5529eaSbjh21 /*
337ce5529eaSbjh21 * We get called before malloc() is available, so supply a static
338ce5529eaSbjh21 * struct undefined_handler.
339ce5529eaSbjh21 */
340ce5529eaSbjh21 db_uh.uh_handler = db_trapper;
341b0a3a430Srearnsha install_coproc_handler_static(CORE_UNKNOWN_HANDLER, &db_uh);
3428655276eSmatt }
343a2f6e1f0Sbriggs #endif
3448655276eSmatt
3458655276eSmatt u_int
db_fetch_reg(int reg,db_regs_t * regs)346172a6238She db_fetch_reg(int reg, db_regs_t *regs)
3478655276eSmatt {
3488655276eSmatt
3498655276eSmatt switch (reg) {
3508655276eSmatt case 0:
351d777316dSskrll return regs->tf_r0;
3528655276eSmatt case 1:
353d777316dSskrll return regs->tf_r1;
3548655276eSmatt case 2:
355d777316dSskrll return regs->tf_r2;
3568655276eSmatt case 3:
357d777316dSskrll return regs->tf_r3;
3588655276eSmatt case 4:
359d777316dSskrll return regs->tf_r4;
3608655276eSmatt case 5:
361d777316dSskrll return regs->tf_r5;
3628655276eSmatt case 6:
363d777316dSskrll return regs->tf_r6;
3648655276eSmatt case 7:
365d777316dSskrll return regs->tf_r7;
3668655276eSmatt case 8:
367d777316dSskrll return regs->tf_r8;
3688655276eSmatt case 9:
369d777316dSskrll return regs->tf_r9;
3708655276eSmatt case 10:
371d777316dSskrll return regs->tf_r10;
3728655276eSmatt case 11:
373d777316dSskrll return regs->tf_r11;
3748655276eSmatt case 12:
375d777316dSskrll return regs->tf_r12;
3768655276eSmatt case 13:
377d777316dSskrll return regs->tf_svc_sp;
3788655276eSmatt case 14:
379d777316dSskrll return regs->tf_svc_lr;
3808655276eSmatt case 15:
381d777316dSskrll return regs->tf_pc;
3828655276eSmatt default:
3838655276eSmatt panic("db_fetch_reg: botch");
3848655276eSmatt }
3858655276eSmatt }
3868655276eSmatt
3878655276eSmatt u_int
branch_taken(u_int insn,u_int pc,db_regs_t * regs)388172a6238She branch_taken(u_int insn, u_int pc, db_regs_t *regs)
3898655276eSmatt {
3908655276eSmatt u_int addr, nregs;
3918655276eSmatt
3928655276eSmatt switch ((insn >> 24) & 0xf) {
3938655276eSmatt case 0xa: /* b ... */
3948655276eSmatt case 0xb: /* bl ... */
3958655276eSmatt addr = ((insn << 2) & 0x03ffffff);
3968655276eSmatt if (addr & 0x02000000)
3978655276eSmatt addr |= 0xfc000000;
398d777316dSskrll return pc + 8 + addr;
3998655276eSmatt case 0x7: /* ldr pc, [pc, reg, lsl #2] */
400172a6238She addr = db_fetch_reg(insn & 0xf, regs);
4018655276eSmatt addr = pc + 8 + (addr << 2);
4028655276eSmatt db_read_bytes(addr, 4, (char *)&addr);
403d777316dSskrll return addr;
40489753885Schristos case 0x5: /* ldr pc, [reg] */
40589753885Schristos addr = db_fetch_reg((insn >> 16) & 0xf, regs);
40689753885Schristos db_read_bytes(addr, 4, (char *)&addr);
407d777316dSskrll return addr;
4088655276eSmatt case 0x1: /* mov pc, reg */
409172a6238She addr = db_fetch_reg(insn & 0xf, regs);
410d777316dSskrll return addr;
4118655276eSmatt case 0x8: /* ldmxx reg, {..., pc} */
4128655276eSmatt case 0x9:
413172a6238She addr = db_fetch_reg((insn >> 16) & 0xf, regs);
4148655276eSmatt nregs = (insn & 0x5555) + ((insn >> 1) & 0x5555);
4158655276eSmatt nregs = (nregs & 0x3333) + ((nregs >> 2) & 0x3333);
4168655276eSmatt nregs = (nregs + (nregs >> 4)) & 0x0f0f;
4178655276eSmatt nregs = (nregs + (nregs >> 8)) & 0x001f;
4188655276eSmatt switch ((insn >> 23) & 0x3) {
4198655276eSmatt case 0x0: /* ldmda */
4208655276eSmatt addr = addr - 0;
4218655276eSmatt break;
4228655276eSmatt case 0x1: /* ldmia */
4238655276eSmatt addr = addr + 0 + ((nregs - 1) << 2);
4248655276eSmatt break;
4258655276eSmatt case 0x2: /* ldmdb */
4268655276eSmatt addr = addr - 4;
4278655276eSmatt break;
4288655276eSmatt case 0x3: /* ldmib */
4298655276eSmatt addr = addr + 4 + ((nregs - 1) << 2);
4308655276eSmatt break;
4318655276eSmatt }
4328655276eSmatt db_read_bytes(addr, 4, (char *)&addr);
433d777316dSskrll return addr;
4348655276eSmatt default:
4358655276eSmatt panic("branch_taken: botch");
4368655276eSmatt }
4378655276eSmatt }
438