xref: /openbsd-src/sys/arch/arm64/arm64/db_interface.c (revision 28d09237077354eff565f9f3ef991865a249ce6c)
1 /*	$OpenBSD: db_interface.c,v 1.16 2024/05/22 05:51:49 jsg Exp $	*/
2 /*	$NetBSD: db_interface.c,v 1.34 2003/10/26 23:11:15 chris Exp $	*/
3 
4 /*
5  * Copyright (c) 1996 Scott K. Stevens
6  *
7  * Mach Operating System
8  * Copyright (c) 1991,1990 Carnegie Mellon University
9  * All Rights Reserved.
10  *
11  * Permission to use, copy, modify and distribute this software and its
12  * documentation is hereby granted, provided that both the copyright
13  * notice and this permission notice appear in all copies of the
14  * software, derivative works or modified versions, and any portions
15  * thereof, and that both notices appear in supporting documentation.
16  *
17  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
18  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
19  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
20  *
21  * Carnegie Mellon requests users of this software to return to
22  *
23  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
24  *  School of Computer Science
25  *  Carnegie Mellon University
26  *  Pittsburgh PA 15213-3890
27  *
28  * any improvements or extensions that they make and grant Carnegie the
29  * rights to redistribute these changes.
30  *
31  *	From: db_interface.c,v 2.4 1991/02/05 17:11:13 mrt (CMU)
32  */
33 
34 /*
35  * Interface to new debugger.
36  */
37 
38 #include <sys/param.h>
39 #include <sys/proc.h>
40 #include <sys/reboot.h>
41 #include <sys/systm.h>
42 #include <sys/mutex.h>
43 
44 #include <uvm/uvm_extern.h>
45 
46 #include <dev/cons.h>
47 
48 #include <machine/cpufunc.h>
49 #include <machine/db_machdep.h>
50 #include <machine/pmap.h>
51 
52 #include <ddb/db_sym.h>
53 #include <ddb/db_command.h>
54 #include <ddb/db_extern.h>
55 #include <ddb/db_output.h>
56 #include <ddb/db_run.h>
57 #include <ddb/db_variables.h>
58 
59 int db_trapper (vaddr_t, u_int, trapframe_t *, int);
60 
61 struct db_variable db_regs[] = {
62 	{ "x0", (long *)&DDB_REGS->tf_x[0], FCN_NULL, },
63 	{ "x1", (long *)&DDB_REGS->tf_x[1], FCN_NULL, },
64 	{ "x2", (long *)&DDB_REGS->tf_x[2], FCN_NULL, },
65 	{ "x3", (long *)&DDB_REGS->tf_x[3], FCN_NULL, },
66 	{ "x4", (long *)&DDB_REGS->tf_x[4], FCN_NULL, },
67 	{ "x5", (long *)&DDB_REGS->tf_x[5], FCN_NULL, },
68 	{ "x6", (long *)&DDB_REGS->tf_x[6], FCN_NULL, },
69 	{ "x7", (long *)&DDB_REGS->tf_x[7], FCN_NULL, },
70 	{ "x8", (long *)&DDB_REGS->tf_x[8], FCN_NULL, },
71 	{ "x9", (long *)&DDB_REGS->tf_x[9], FCN_NULL, },
72 	{ "x10", (long *)&DDB_REGS->tf_x[10], FCN_NULL, },
73 	{ "x11", (long *)&DDB_REGS->tf_x[11], FCN_NULL, },
74 	{ "x12", (long *)&DDB_REGS->tf_x[12], FCN_NULL, },
75 	{ "x13", (long *)&DDB_REGS->tf_x[13], FCN_NULL, },
76 	{ "x14", (long *)&DDB_REGS->tf_x[14], FCN_NULL, },
77 	{ "x15", (long *)&DDB_REGS->tf_x[15], FCN_NULL, },
78 	{ "x16", (long *)&DDB_REGS->tf_x[16], FCN_NULL, },
79 	{ "x17", (long *)&DDB_REGS->tf_x[17], FCN_NULL, },
80 	{ "x18", (long *)&DDB_REGS->tf_x[18], FCN_NULL, },
81 	{ "x19", (long *)&DDB_REGS->tf_x[19], FCN_NULL, },
82 	{ "x20", (long *)&DDB_REGS->tf_x[20], FCN_NULL, },
83 	{ "x21", (long *)&DDB_REGS->tf_x[21], FCN_NULL, },
84 	{ "x22", (long *)&DDB_REGS->tf_x[22], FCN_NULL, },
85 	{ "x23", (long *)&DDB_REGS->tf_x[23], FCN_NULL, },
86 	{ "x24", (long *)&DDB_REGS->tf_x[24], FCN_NULL, },
87 	{ "x25", (long *)&DDB_REGS->tf_x[25], FCN_NULL, },
88 	{ "x26", (long *)&DDB_REGS->tf_x[26], FCN_NULL, },
89 	{ "x27", (long *)&DDB_REGS->tf_x[27], FCN_NULL, },
90 	{ "x28", (long *)&DDB_REGS->tf_x[28], FCN_NULL, },
91 	{ "x29", (long *)&DDB_REGS->tf_x[29], FCN_NULL, },
92 	{ "x30", (long *)&DDB_REGS->tf_x[30], FCN_NULL, },
93 	{ "sp", (long *)&DDB_REGS->tf_sp, FCN_NULL, },
94 	{ "spsr", (long *)&DDB_REGS->tf_spsr, FCN_NULL, },
95 	{ "elr", (long *)&DDB_REGS->tf_elr, FCN_NULL, },
96 	{ "lr", (long *)&DDB_REGS->tf_lr, FCN_NULL, },
97 };
98 
99 #ifdef MULTIPROCESSOR
100 struct db_mutex ddb_mp_mutex = DB_MUTEX_INITIALIZER;
101 volatile int ddb_state = DDB_STATE_NOT_RUNNING;
102 volatile cpuid_t ddb_active_cpu;
103 int		 db_switch_cpu;
104 long             db_switch_to_cpu;
105 
106 void db_cpuinfo_cmd(db_expr_t, int, db_expr_t, char *);
107 void db_startproc_cmd(db_expr_t, int, db_expr_t, char *);
108 void db_stopproc_cmd(db_expr_t, int, db_expr_t, char *);
109 void db_ddbproc_cmd(db_expr_t, int, db_expr_t, char *);
110 void db_stopcpu(int cpu);
111 void db_startcpu(int cpu);
112 int db_enter_ddb(void);
113 #endif
114 
115 extern label_t       *db_recover;
116 
117 struct db_variable * db_eregs = db_regs + nitems(db_regs);
118 
119 #ifdef DDB
120 /*
121  *  kdb_trap - field a TRACE or BPT trap
122  */
123 int
kdb_trap(int type,db_regs_t * regs)124 kdb_trap(int type, db_regs_t *regs)
125 {
126 	int s;
127 
128 #ifdef MULTIPROCESSOR
129 	db_mtx_enter(&ddb_mp_mutex);
130 	if (ddb_state == DDB_STATE_EXITING)
131 		ddb_state = DDB_STATE_NOT_RUNNING;
132 	db_mtx_leave(&ddb_mp_mutex);
133 	while (db_enter_ddb()) {
134 #endif
135 
136 	switch (type) {
137 	case T_BREAKPOINT:	/* breakpoint */
138 	case -1:		/* keyboard interrupt */
139 		break;
140 	default:
141 		if (db_recover != 0) {
142 			/* This will longjmp back into db_command_loop() */
143 			db_error("Faulted in DDB; continuing...\n");
144 			/*NOTREACHED*/
145 		}
146 	}
147 
148 	/* Should switch to kdb`s own stack here. */
149 
150 	ddb_regs = *regs;
151 
152 	s = splhigh();
153 	db_active++;
154 	cnpollc(1);
155 	db_trap(type, 0/*code*/);
156 	cnpollc(0);
157 	db_active--;
158 	splx(s);
159 
160 	*regs = ddb_regs;
161 
162 #ifdef MULTIPROCESSOR
163 		if (!db_switch_cpu)
164 			ddb_state = DDB_STATE_EXITING;
165 	}
166 #endif
167 	return (1);
168 }
169 #endif
170 
171 #define INKERNEL(va)	(((vaddr_t)(va)) & (1ULL << 63))
172 
173 static int db_validate_address(vaddr_t addr);
174 
175 static int
db_validate_address(vaddr_t addr)176 db_validate_address(vaddr_t addr)
177 {
178 	struct proc *p = curproc;
179 	struct pmap *pmap;
180 
181 	if (!p || !p->p_vmspace || !p->p_vmspace->vm_map.pmap ||
182 	    INKERNEL(addr))
183 		pmap = pmap_kernel();
184 	else
185 		pmap = p->p_vmspace->vm_map.pmap;
186 
187 	return (pmap_extract(pmap, addr, NULL) == FALSE);
188 }
189 
190 /*
191  * Read bytes from kernel address space for debugger.
192  */
193 void
db_read_bytes(vaddr_t addr,size_t size,void * datap)194 db_read_bytes(vaddr_t addr, size_t size, void *datap)
195 {
196 	char *data = datap, *src = (char *)addr;
197 
198 	if (db_validate_address((vaddr_t)src)) {
199 		db_printf("address %p is invalid\n", src);
200 		return;
201 	}
202 
203 	if (size == 8 && (addr & 7) == 0 && ((vaddr_t)data & 7) == 0) {
204 		*((uint64_t*)data) = *((uint64_t*)src);
205 		return;
206 	}
207 
208 	if (size == 4 && (addr & 3) == 0 && ((vaddr_t)data & 3) == 0) {
209 		*((int*)data) = *((int*)src);
210 		return;
211 	}
212 
213 	if (size == 2 && (addr & 1) == 0 && ((vaddr_t)data & 1) == 0) {
214 		*((short*)data) = *((short*)src);
215 		return;
216 	}
217 
218 	while (size-- > 0) {
219 		if (db_validate_address((vaddr_t)src)) {
220 			db_printf("address %p is invalid\n", src);
221 			return;
222 		}
223 		*data++ = *src++;
224 	}
225 }
226 
227 /*
228  * Write bytes somewhere in the kernel text.  Make the text
229  * pages writable temporarily.
230  */
231 static void
db_write_text(vaddr_t addr,size_t size,char * data)232 db_write_text(vaddr_t addr, size_t size, char *data)
233 {
234 	vaddr_t pgva;
235 	size_t limit;
236 	char *dst;
237 
238 	if (size == 0)
239 		return;
240 
241 	dst = (char *)addr;
242 
243 	do {
244 		/*
245 		 * Get the VA for the page.
246 		 */
247 		pgva = trunc_page((vaddr_t)dst);
248 
249 		/*
250 		 * Compute number of bytes that can be written
251 		 * with this mapping and subtract it from the
252 		 * total size.
253 		 */
254 		limit = PAGE_SIZE - ((vaddr_t)dst & PGOFSET);
255 		if (limit > size)
256 			limit = size;
257 		size -= limit;
258 
259 		pmap_page_rw(pmap_kernel(), pgva);
260 
261 		for (; limit > 0; limit--)
262 			*dst++ = *data++;
263 
264 		/* Restore protection */
265 		pmap_page_ro(pmap_kernel(), pgva, PROT_READ|PROT_EXEC);
266 
267 	} while (size != 0);
268 }
269 
270 /*
271  * Write bytes to kernel address space for debugger.
272  */
273 void
db_write_bytes(vaddr_t addr,size_t size,void * datap)274 db_write_bytes(vaddr_t addr, size_t size, void *datap)
275 {
276 	extern char etext[];
277 	char *data = datap, *dst;
278 	size_t loop;
279 
280 	/* If any part is in kernel text, use db_write_text() */
281 	if (addr >= KERNBASE && addr < (vaddr_t)&etext) {
282 		db_write_text(addr, size, data);
283 		return;
284 	}
285 
286 	dst = (char *)addr;
287 	loop = size;
288 	while (loop-- > 0) {
289 		if (db_validate_address((vaddr_t)dst)) {
290 			db_printf("address %p is invalid\n", dst);
291 			return;
292 		}
293 		*dst++ = *data++;
294 	}
295 	/* make sure the caches and memory are in sync */
296 	cpu_icache_sync_range(addr, size);
297 
298 	/* In case the current page tables have been modified ... */
299 	cpu_tlb_flush();
300 }
301 
302 void
db_enter(void)303 db_enter(void)
304 {
305 	asm("brk 0");
306 }
307 
308 #ifdef MULTIPROCESSOR
309 void
db_cpuinfo_cmd(db_expr_t addr,int have_addr,db_expr_t count,char * modif)310 db_cpuinfo_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif)
311 {
312 	int i;
313 
314 	for (i = 0; i < MAXCPUS; i++) {
315 		if (cpu_info[i] != NULL) {
316 			db_printf("%c%4d: ", (i == cpu_number()) ? '*' : ' ',
317 			    CPU_INFO_UNIT(cpu_info[i]));
318 			switch(cpu_info[i]->ci_ddb_paused) {
319 			case CI_DDB_RUNNING:
320 				db_printf("running\n");
321 				break;
322 			case CI_DDB_SHOULDSTOP:
323 				db_printf("stopping\n");
324 				break;
325 			case CI_DDB_STOPPED:
326 				db_printf("stopped\n");
327 				break;
328 			case CI_DDB_ENTERDDB:
329 				db_printf("entering ddb\n");
330 				break;
331 			case CI_DDB_INDDB:
332 				db_printf("ddb\n");
333 				break;
334 			default:
335 				db_printf("? (%d)\n",
336 				    cpu_info[i]->ci_ddb_paused);
337 				break;
338 			}
339 		}
340 	}
341 }
342 
343 void
db_startproc_cmd(db_expr_t addr,int have_addr,db_expr_t count,char * modif)344 db_startproc_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif)
345 {
346 	int i;
347 
348 	if (have_addr) {
349 		if (addr >= 0 && addr < MAXCPUS &&
350 		    cpu_info[addr] != NULL && addr != cpu_number())
351 			db_startcpu(addr);
352 		else
353 			db_printf("Invalid cpu %d\n", (int)addr);
354 	} else {
355 		for (i = 0; i < MAXCPUS; i++) {
356 			if (cpu_info[i] != NULL && i != cpu_number())
357 				db_startcpu(i);
358 		}
359 	}
360 }
361 
362 void
db_stopproc_cmd(db_expr_t addr,int have_addr,db_expr_t count,char * modif)363 db_stopproc_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif)
364 {
365 	int i;
366 
367 	if (have_addr) {
368 		if (addr >= 0 && addr < MAXCPUS &&
369 		    cpu_info[addr] != NULL && addr != cpu_number())
370 			db_stopcpu(addr);
371 		else
372 			db_printf("Invalid cpu %d\n", (int)addr);
373 	} else {
374 		for (i = 0; i < MAXCPUS; i++) {
375 			if (cpu_info[i] != NULL && i != cpu_number())
376 				db_stopcpu(i);
377 		}
378 	}
379 }
380 
381 void
db_ddbproc_cmd(db_expr_t addr,int have_addr,db_expr_t count,char * modif)382 db_ddbproc_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif)
383 {
384 	if (have_addr) {
385 		if (addr >= 0 && addr < MAXCPUS &&
386 		    cpu_info[addr] != NULL && addr != cpu_number()) {
387 			db_stopcpu(addr);
388 			db_switch_to_cpu = addr;
389 			db_switch_cpu = 1;
390 			db_cmd_loop_done = 1;
391 		} else {
392 			db_printf("Invalid cpu %d\n", (int)addr);
393 		}
394 	} else {
395 		db_printf("CPU not specified\n");
396 	}
397 }
398 
399 int
db_enter_ddb(void)400 db_enter_ddb(void)
401 {
402 	int i;
403 
404 	db_mtx_enter(&ddb_mp_mutex);
405 
406 	/* If we are first in, grab ddb and stop all other CPUs */
407 	if (ddb_state == DDB_STATE_NOT_RUNNING) {
408 		ddb_active_cpu = cpu_number();
409 		ddb_state = DDB_STATE_RUNNING;
410 		curcpu()->ci_ddb_paused = CI_DDB_INDDB;
411 		db_mtx_leave(&ddb_mp_mutex);
412 		for (i = 0; i < MAXCPUS; i++) {
413 			if (cpu_info[i] != NULL && i != cpu_number() &&
414 			    cpu_info[i]->ci_ddb_paused != CI_DDB_STOPPED) {
415 				cpu_info[i]->ci_ddb_paused = CI_DDB_SHOULDSTOP;
416 				arm_send_ipi(cpu_info[i], ARM_IPI_DDB);
417 			}
418 		}
419 		return (1);
420 	}
421 
422 	/* Leaving ddb completely.  Start all other CPUs and return 0 */
423 	if (ddb_active_cpu == cpu_number() && ddb_state == DDB_STATE_EXITING) {
424 		for (i = 0; i < MAXCPUS; i++) {
425 			if (cpu_info[i] != NULL) {
426 				cpu_info[i]->ci_ddb_paused = CI_DDB_RUNNING;
427 			}
428 		}
429 		db_mtx_leave(&ddb_mp_mutex);
430 		return (0);
431 	}
432 
433 	/* We're switching to another CPU.  db_ddbproc_cmd() has made sure
434 	 * it is waiting for ddb, we just have to set ddb_active_cpu. */
435 	if (ddb_active_cpu == cpu_number() && db_switch_cpu) {
436 		curcpu()->ci_ddb_paused = CI_DDB_SHOULDSTOP;
437 		db_switch_cpu = 0;
438 		ddb_active_cpu = db_switch_to_cpu;
439 		cpu_info[db_switch_to_cpu]->ci_ddb_paused = CI_DDB_ENTERDDB;
440 	}
441 
442 	/* Wait until we should enter ddb or resume */
443 	while (ddb_active_cpu != cpu_number() &&
444 	    curcpu()->ci_ddb_paused != CI_DDB_RUNNING) {
445 		if (curcpu()->ci_ddb_paused == CI_DDB_SHOULDSTOP)
446 			curcpu()->ci_ddb_paused = CI_DDB_STOPPED;
447 		db_mtx_leave(&ddb_mp_mutex);
448 
449 		/* Busy wait without locking, we'll confirm with lock later */
450 		while (ddb_active_cpu != cpu_number() &&
451 		    curcpu()->ci_ddb_paused != CI_DDB_RUNNING)
452 			CPU_BUSY_CYCLE();
453 
454 		db_mtx_enter(&ddb_mp_mutex);
455 	}
456 
457 	/* Either enter ddb or exit */
458 	if (ddb_active_cpu == cpu_number() && ddb_state == DDB_STATE_RUNNING) {
459 		curcpu()->ci_ddb_paused = CI_DDB_INDDB;
460 		db_mtx_leave(&ddb_mp_mutex);
461 		return (1);
462 	} else {
463 		db_mtx_leave(&ddb_mp_mutex);
464 		return (0);
465 	}
466 }
467 
468 void
db_startcpu(int cpu)469 db_startcpu(int cpu)
470 {
471 	if (cpu != cpu_number() && cpu_info[cpu] != NULL) {
472 		db_mtx_enter(&ddb_mp_mutex);
473 		cpu_info[cpu]->ci_ddb_paused = CI_DDB_RUNNING;
474 		db_mtx_leave(&ddb_mp_mutex);
475 	}
476 }
477 
478 void
db_stopcpu(int cpu)479 db_stopcpu(int cpu)
480 {
481 	db_mtx_enter(&ddb_mp_mutex);
482 	if (cpu != cpu_number() && cpu_info[cpu] != NULL &&
483 	    cpu_info[cpu]->ci_ddb_paused != CI_DDB_STOPPED) {
484 		cpu_info[cpu]->ci_ddb_paused = CI_DDB_SHOULDSTOP;
485 		db_mtx_leave(&ddb_mp_mutex);
486 		arm_send_ipi(cpu_info[cpu], ARM_IPI_DDB);
487 	} else {
488 		db_mtx_leave(&ddb_mp_mutex);
489 	}
490 }
491 #endif
492 
493 const struct db_command db_machine_command_table[] = {
494 #ifdef MULTIPROCESSOR
495 	{ "cpuinfo",    db_cpuinfo_cmd,         0,      NULL },
496 	{ "startcpu",   db_startproc_cmd,       0,      NULL },
497 	{ "stopcpu",    db_stopproc_cmd,        0,      NULL },
498 	{ "ddbcpu",     db_ddbproc_cmd,         0,      NULL },
499 #endif
500 	{ NULL, 	NULL, 			0,	NULL }
501 };
502 
503 int
db_trapper(vaddr_t addr,u_int inst,trapframe_t * frame,int fault_code)504 db_trapper(vaddr_t addr, u_int inst, trapframe_t *frame, int fault_code)
505 {
506 
507 	if (fault_code == EXCP_BRK) {
508 		kdb_trap(T_BREAKPOINT, frame);
509 		frame->tf_elr += INSN_SIZE;
510 	} else
511 		kdb_trap(-1, frame);
512 	return (0);
513 }
514 
515 extern vaddr_t esym;
516 extern vaddr_t end;
517 
518 void
db_machine_init(void)519 db_machine_init(void)
520 {
521 #ifdef MULTIPROCESSOR
522 	int i;
523 
524 	for (i = 0; i < MAXCPUS; i++) {
525 		if (cpu_info[i] != NULL)
526 			cpu_info[i]->ci_ddb_paused = CI_DDB_RUNNING;
527 	}
528 #endif
529 }
530 
531 vaddr_t
db_branch_taken(u_int insn,vaddr_t pc,db_regs_t * db_regs)532 db_branch_taken(u_int insn, vaddr_t pc, db_regs_t *db_regs)
533 {
534 	/* implement */
535 	return pc + 4;
536 }
537