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