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