xref: /netbsd-src/sys/arch/arm/arm32/db_interface.c (revision e7f0873ce54b17abd47ea68a94545128b48d44f3)
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