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