1 /* $OpenBSD: db_interface.c,v 1.21 2024/02/23 18:19:02 cheloha 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 #include <sys/param.h>
38 #include <sys/proc.h>
39 #include <sys/systm.h> /* just for boothowto */
40
41 #include <uvm/uvm_extern.h>
42
43 #include <arm/db_machdep.h>
44 #include <machine/pmap.h>
45 #include <arm/undefined.h>
46 #include <ddb/db_access.h>
47 #include <ddb/db_command.h>
48 #include <ddb/db_output.h>
49 #include <ddb/db_variables.h>
50 #include <ddb/db_extern.h>
51 #include <ddb/db_interface.h>
52 #include <dev/cons.h>
53
54 static long nil;
55
56 int db_access_und_sp (struct db_variable *, db_expr_t *, int);
57 int db_access_abt_sp (struct db_variable *, db_expr_t *, int);
58 int db_access_irq_sp (struct db_variable *, db_expr_t *, int);
59 u_int db_fetch_reg (int, db_regs_t *);
60
61 int db_trapper (u_int, u_int, trapframe_t *, int, uint32_t);
62
63 struct db_variable db_regs[] = {
64 { "spsr", (long *)&ddb_regs.tf_spsr, FCN_NULL, },
65 { "r0", (long *)&ddb_regs.tf_r0, FCN_NULL, },
66 { "r1", (long *)&ddb_regs.tf_r1, FCN_NULL, },
67 { "r2", (long *)&ddb_regs.tf_r2, FCN_NULL, },
68 { "r3", (long *)&ddb_regs.tf_r3, FCN_NULL, },
69 { "r4", (long *)&ddb_regs.tf_r4, FCN_NULL, },
70 { "r5", (long *)&ddb_regs.tf_r5, FCN_NULL, },
71 { "r6", (long *)&ddb_regs.tf_r6, FCN_NULL, },
72 { "r7", (long *)&ddb_regs.tf_r7, FCN_NULL, },
73 { "r8", (long *)&ddb_regs.tf_r8, FCN_NULL, },
74 { "r9", (long *)&ddb_regs.tf_r9, FCN_NULL, },
75 { "r10", (long *)&ddb_regs.tf_r10, FCN_NULL, },
76 { "r11", (long *)&ddb_regs.tf_r11, FCN_NULL, },
77 { "r12", (long *)&ddb_regs.tf_r12, FCN_NULL, },
78 { "usr_sp", (long *)&ddb_regs.tf_usr_sp, FCN_NULL, },
79 { "usr_lr", (long *)&ddb_regs.tf_usr_lr, FCN_NULL, },
80 { "svc_sp", (long *)&ddb_regs.tf_svc_sp, FCN_NULL, },
81 { "svc_lr", (long *)&ddb_regs.tf_svc_lr, FCN_NULL, },
82 { "pc", (long *)&ddb_regs.tf_pc, FCN_NULL, },
83 { "und_sp", (long *)&nil, db_access_und_sp, },
84 { "abt_sp", (long *)&nil, db_access_abt_sp, },
85 { "irq_sp", (long *)&nil, db_access_irq_sp, },
86 };
87
88 extern label_t *db_recover;
89
90 struct db_variable * db_eregs = db_regs + nitems(db_regs);
91
92 int
db_access_und_sp(struct db_variable * vp,db_expr_t * valp,int rw)93 db_access_und_sp(struct db_variable *vp, db_expr_t *valp, int rw)
94 {
95
96 if (rw == DB_VAR_GET)
97 *valp = get_stackptr(PSR_UND32_MODE);
98 return(0);
99 }
100
101 int
db_access_abt_sp(struct db_variable * vp,db_expr_t * valp,int rw)102 db_access_abt_sp(struct db_variable *vp, db_expr_t *valp, int rw)
103 {
104
105 if (rw == DB_VAR_GET)
106 *valp = get_stackptr(PSR_ABT32_MODE);
107 return(0);
108 }
109
110 int
db_access_irq_sp(struct db_variable * vp,db_expr_t * valp,int rw)111 db_access_irq_sp(struct db_variable *vp, db_expr_t *valp, int rw)
112 {
113
114 if (rw == DB_VAR_GET)
115 *valp = get_stackptr(PSR_IRQ32_MODE);
116 return(0);
117 }
118
119 #ifdef DDB
120 /*
121 * db_ktrap - field a TRACE or BPT trap
122 */
123 int
db_ktrap(int type,db_regs_t * regs)124 db_ktrap(int type, db_regs_t *regs)
125 {
126 int s;
127
128 switch (type) {
129 case T_BREAKPOINT: /* breakpoint */
130 case -1: /* keyboard interrupt */
131 break;
132 default:
133 if (db_recover != 0) {
134 /* This will longjmp back into db_command_loop() */
135 db_error("Faulted in DDB; continuing...\n");
136 /*NOTREACHED*/
137 }
138 }
139
140 /* Should switch to kdb`s own stack here. */
141
142 ddb_regs = *regs;
143
144 s = splhigh();
145 db_active++;
146 cnpollc(1);
147 db_trap(type, 0/*code*/);
148 cnpollc(0);
149 db_active--;
150 splx(s);
151
152 *regs = ddb_regs;
153
154 return (1);
155 }
156 #endif
157
158
159 static int db_validate_address(vaddr_t addr);
160
161 static int
db_validate_address(vaddr_t addr)162 db_validate_address(vaddr_t addr)
163 {
164 struct proc *p = curproc;
165 struct pmap *pmap;
166
167 if (!p || !p->p_vmspace || !p->p_vmspace->vm_map.pmap ||
168 #ifndef ARM32_NEW_VM_LAYOUT
169 addr >= VM_MAXUSER_ADDRESS
170 #else
171 addr >= VM_MIN_KERNEL_ADDRESS
172 #endif
173 )
174 pmap = pmap_kernel();
175 else
176 pmap = p->p_vmspace->vm_map.pmap;
177
178 return (pmap_extract(pmap, addr, NULL) == FALSE);
179 }
180
181 /*
182 * Read bytes from kernel address space for debugger.
183 */
184 void
db_read_bytes(vaddr_t addr,size_t size,void * datap)185 db_read_bytes(vaddr_t addr, size_t size, void *datap)
186 {
187 char *data = datap, *src = (char *)addr;
188
189 if (db_validate_address((u_int)src)) {
190 db_printf("address %p is invalid\n", src);
191 return;
192 }
193
194 if (size == 4 && (addr & 3) == 0 && ((u_int32_t)data & 3) == 0) {
195 *((int*)data) = *((int*)src);
196 return;
197 }
198
199 if (size == 2 && (addr & 1) == 0 && ((u_int32_t)data & 1) == 0) {
200 *((short*)data) = *((short*)src);
201 return;
202 }
203
204 while (size-- > 0) {
205 if (db_validate_address((u_int)src)) {
206 db_printf("address %p is invalid\n", src);
207 return;
208 }
209 *data++ = *src++;
210 }
211 }
212
213 static void
db_write_text(vaddr_t addr,size_t size,char * data)214 db_write_text(vaddr_t addr, size_t size, char *data)
215 {
216 struct pmap *pmap = pmap_kernel();
217 pd_entry_t *pde, oldpde, tmppde;
218 pt_entry_t *pte, oldpte, tmppte;
219 vaddr_t pgva;
220 size_t limit, savesize;
221 char *dst;
222
223 /* XXX: gcc */
224 oldpte = 0;
225
226 if ((savesize = size) == 0)
227 return;
228
229 dst = (char *) addr;
230
231 do {
232 /* Get the PDE of the current VA. */
233 if (pmap_get_pde_pte(pmap, (vaddr_t) dst, &pde, &pte) == FALSE)
234 goto no_mapping;
235 switch ((oldpde = *pde) & L1_TYPE_MASK) {
236 case L1_TYPE_S:
237 pgva = (vaddr_t)dst & L1_S_FRAME;
238 limit = L1_S_SIZE - ((vaddr_t)dst & L1_S_OFFSET);
239
240 tmppde = oldpde | L1_S_PROT(PTE_KERNEL, PROT_WRITE);
241 *pde = tmppde;
242 PTE_SYNC(pde);
243 break;
244
245 case L1_TYPE_C:
246 pgva = (vaddr_t)dst & L2_S_FRAME;
247 limit = L2_S_SIZE - ((vaddr_t)dst & L2_S_OFFSET);
248
249 if (pte == NULL)
250 goto no_mapping;
251 oldpte = *pte;
252 tmppte = oldpte | L2_S_PROT(PTE_KERNEL, PROT_WRITE);
253 *pte = tmppte;
254 PTE_SYNC(pte);
255 break;
256
257 default:
258 no_mapping:
259 printf(" address 0x%08lx not a valid page\n",
260 (vaddr_t) dst);
261 return;
262 }
263 cpu_tlb_flushD_SE(pgva);
264 cpu_cpwait();
265
266 if (limit > size)
267 limit = size;
268 size -= limit;
269
270 /*
271 * Page is now writable. Do as much access as we
272 * can in this page.
273 */
274 for (; limit > 0; limit--)
275 *dst++ = *data++;
276
277 /*
278 * Restore old mapping permissions.
279 */
280 switch (oldpde & L1_TYPE_MASK) {
281 case L1_TYPE_S:
282 *pde = oldpde;
283 PTE_SYNC(pde);
284 break;
285
286 case L1_TYPE_C:
287 *pte = oldpte;
288 PTE_SYNC(pte);
289 break;
290 }
291 cpu_tlb_flushD_SE(pgva);
292 cpu_cpwait();
293
294 } while (size != 0);
295
296 /* Sync the I-cache. */
297 cpu_icache_sync_range(addr, savesize);
298 }
299
300 /*
301 * Write bytes to kernel address space for debugger.
302 */
303 void
db_write_bytes(vaddr_t addr,size_t size,void * datap)304 db_write_bytes(vaddr_t addr, size_t size, void *datap)
305 {
306 extern char etext[];
307 extern char kernel_text[];
308 char *data = datap, *dst;
309 size_t loop;
310
311 /* If any part is in kernel text, use db_write_text() */
312 if (addr >= (vaddr_t) kernel_text && addr < (vaddr_t) etext) {
313 db_write_text(addr, size, data);
314 return;
315 }
316
317 dst = (char *)addr;
318 loop = size;
319 while (loop-- > 0) {
320 if (db_validate_address((u_int)dst)) {
321 db_printf("address %p is invalid\n", dst);
322 return;
323 }
324 *dst++ = *data++;
325 }
326 /* make sure the caches and memory are in sync */
327 cpu_icache_sync_range(addr, size);
328
329 /* In case the current page tables have been modified ... */
330 cpu_tlb_flushID();
331 cpu_cpwait();
332 }
333
334 void
db_enter(void)335 db_enter(void)
336 {
337 asm(".word 0xe7ffffff");
338 }
339
340 const struct db_command db_machine_command_table[] = {
341 { "frame", db_show_frame_cmd, 0, NULL },
342 #ifdef ARM32_DB_COMMANDS
343 ARM32_DB_COMMANDS,
344 #endif
345 { NULL, NULL, 0, NULL }
346 };
347
348 int
db_trapper(u_int addr,u_int inst,trapframe_t * frame,int fault_code,uint32_t fpexc)349 db_trapper(u_int addr, u_int inst, trapframe_t *frame, int fault_code,
350 uint32_t fpexc)
351 {
352
353 if (fault_code == 0) {
354 if ((inst & ~INSN_COND_MASK) == (BKPT_INST & ~INSN_COND_MASK)) {
355 db_ktrap(T_BREAKPOINT, frame);
356 frame->tf_pc += INSN_SIZE;
357 } else
358 db_ktrap(-1, frame);
359 } else
360 return (1);
361 return (0);
362 }
363
364 extern u_int esym;
365 extern u_int end;
366
367 static struct undefined_handler db_uh;
368
369 void
db_machine_init(void)370 db_machine_init(void)
371 {
372 /*
373 * We get called before malloc() is available, so supply a static
374 * struct undefined_handler.
375 */
376 db_uh.uh_handler = db_trapper;
377 install_coproc_handler_static(0, &db_uh);
378 }
379
380 u_int
db_fetch_reg(int reg,db_regs_t * db_regs)381 db_fetch_reg(int reg, db_regs_t *db_regs)
382 {
383
384 switch (reg) {
385 case 0:
386 return (db_regs->tf_r0);
387 case 1:
388 return (db_regs->tf_r1);
389 case 2:
390 return (db_regs->tf_r2);
391 case 3:
392 return (db_regs->tf_r3);
393 case 4:
394 return (db_regs->tf_r4);
395 case 5:
396 return (db_regs->tf_r5);
397 case 6:
398 return (db_regs->tf_r6);
399 case 7:
400 return (db_regs->tf_r7);
401 case 8:
402 return (db_regs->tf_r8);
403 case 9:
404 return (db_regs->tf_r9);
405 case 10:
406 return (db_regs->tf_r10);
407 case 11:
408 return (db_regs->tf_r11);
409 case 12:
410 return (db_regs->tf_r12);
411 case 13:
412 return (db_regs->tf_svc_sp);
413 case 14:
414 return (db_regs->tf_svc_lr);
415 case 15:
416 return (db_regs->tf_pc);
417 default:
418 panic("db_fetch_reg: botch");
419 }
420 }
421
422 vaddr_t
db_branch_taken(u_int insn,vaddr_t pc,db_regs_t * db_regs)423 db_branch_taken(u_int insn, vaddr_t pc, db_regs_t *db_regs)
424 {
425 u_int addr, nregs;
426
427 switch ((insn >> 24) & 0xf) {
428 case 0xa: /* b ... */
429 case 0xb: /* bl ... */
430 addr = ((insn << 2) & 0x03ffffff);
431 if (addr & 0x02000000)
432 addr |= 0xfc000000;
433 return (pc + 8 + addr);
434 case 0x7: /* ldr pc, [pc, reg, lsl #2] */
435 addr = db_fetch_reg(insn & 0xf, db_regs);
436 addr = pc + 8 + (addr << 2);
437 db_read_bytes(addr, 4, (char *)&addr);
438 return (addr);
439 case 0x1: /* mov pc, reg */
440 addr = db_fetch_reg(insn & 0xf, db_regs);
441 return (addr);
442 case 0x8: /* ldmxx reg, {..., pc} */
443 case 0x9:
444 addr = db_fetch_reg((insn >> 16) & 0xf, db_regs);
445 nregs = (insn & 0x5555) + ((insn >> 1) & 0x5555);
446 nregs = (nregs & 0x3333) + ((nregs >> 2) & 0x3333);
447 nregs = (nregs + (nregs >> 4)) & 0x0f0f;
448 nregs = (nregs + (nregs >> 8)) & 0x001f;
449 switch ((insn >> 23) & 0x3) {
450 case 0x0: /* ldmda */
451 addr = addr - 0;
452 break;
453 case 0x1: /* ldmia */
454 addr = addr + 0 + ((nregs - 1) << 2);
455 break;
456 case 0x2: /* ldmdb */
457 addr = addr - 4;
458 break;
459 case 0x3: /* ldmib */
460 addr = addr + 4 + ((nregs - 1) << 2);
461 break;
462 }
463 db_read_bytes(addr, 4, (char *)&addr);
464 return (addr);
465 default:
466 panic("branch_taken: botch");
467 }
468 }
469