NetBSD/sys/arch/arm/arm32/db_interface.c
thorpej aee5994fce Use abstract names for the protection and PTE type bits in
L1 and L2 descriptors.  This will allow us to support different
PTE layouts that enable the use of extensions on different
processor models.
2002-04-09 19:37:14 +00:00

466 lines
10 KiB
C

/* $NetBSD: db_interface.c,v 1.19 2002/04/09 19:37:16 thorpej Exp $ */
/*
* Copyright (c) 1996 Scott K. Stevens
*
* Mach Operating System
* Copyright (c) 1991,1990 Carnegie Mellon University
* All Rights Reserved.
*
* Permission to use, copy, modify and distribute this software and its
* documentation is hereby granted, provided that both the copyright
* notice and this permission notice appear in all copies of the
* software, derivative works or modified versions, and any portions
* thereof, and that both notices appear in supporting documentation.
*
* CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
* CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
* ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
*
* Carnegie Mellon requests users of this software to return to
*
* Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
* School of Computer Science
* Carnegie Mellon University
* Pittsburgh PA 15213-3890
*
* any improvements or extensions that they make and grant Carnegie the
* rights to redistribute these changes.
*
* From: db_interface.c,v 2.4 1991/02/05 17:11:13 mrt (CMU)
*/
/*
* Interface to new debugger.
*/
#include "opt_ddb.h"
#include <sys/param.h>
#include <sys/proc.h>
#include <sys/reboot.h>
#include <sys/systm.h> /* just for boothowto */
#include <sys/exec.h>
#include <uvm/uvm_extern.h>
#include <arm/arm32/db_machdep.h>
#include <arm/arm32/katelib.h>
#include <arm/undefined.h>
#include <ddb/db_access.h>
#include <ddb/db_command.h>
#include <ddb/db_output.h>
#include <ddb/db_variables.h>
#include <ddb/db_sym.h>
#include <ddb/db_extern.h>
#include <ddb/db_interface.h>
#include <dev/cons.h>
static int nil;
int db_access_und_sp __P((const struct db_variable *, db_expr_t *, int));
int db_access_abt_sp __P((const struct db_variable *, db_expr_t *, int));
int db_access_irq_sp __P((const struct db_variable *, db_expr_t *, int));
u_int db_fetch_reg __P((int, db_regs_t *));
int db_trapper __P((u_int, u_int, trapframe_t *, int));
const struct db_variable db_regs[] = {
{ "spsr", (long *)&DDB_REGS->tf_spsr, FCN_NULL, },
{ "r0", (long *)&DDB_REGS->tf_r0, FCN_NULL, },
{ "r1", (long *)&DDB_REGS->tf_r1, FCN_NULL, },
{ "r2", (long *)&DDB_REGS->tf_r2, FCN_NULL, },
{ "r3", (long *)&DDB_REGS->tf_r3, FCN_NULL, },
{ "r4", (long *)&DDB_REGS->tf_r4, FCN_NULL, },
{ "r5", (long *)&DDB_REGS->tf_r5, FCN_NULL, },
{ "r6", (long *)&DDB_REGS->tf_r6, FCN_NULL, },
{ "r7", (long *)&DDB_REGS->tf_r7, FCN_NULL, },
{ "r8", (long *)&DDB_REGS->tf_r8, FCN_NULL, },
{ "r9", (long *)&DDB_REGS->tf_r9, FCN_NULL, },
{ "r10", (long *)&DDB_REGS->tf_r10, FCN_NULL, },
{ "r11", (long *)&DDB_REGS->tf_r11, FCN_NULL, },
{ "r12", (long *)&DDB_REGS->tf_r12, FCN_NULL, },
{ "usr_sp", (long *)&DDB_REGS->tf_usr_sp, FCN_NULL, },
{ "usr_lr", (long *)&DDB_REGS->tf_usr_lr, FCN_NULL, },
{ "svc_sp", (long *)&DDB_REGS->tf_svc_sp, FCN_NULL, },
{ "svc_lr", (long *)&DDB_REGS->tf_svc_lr, FCN_NULL, },
{ "pc", (long *)&DDB_REGS->tf_pc, FCN_NULL, },
{ "und_sp", (long *)&nil, db_access_und_sp, },
{ "abt_sp", (long *)&nil, db_access_abt_sp, },
{ "irq_sp", (long *)&nil, db_access_irq_sp, },
};
const struct db_variable * const db_eregs = db_regs + sizeof(db_regs)/sizeof(db_regs[0]);
extern label_t *db_recover;
int db_active = 0;
int
db_access_und_sp(const struct db_variable *vp, db_expr_t *valp, int rw)
{
if (rw == DB_VAR_GET)
*valp = get_stackptr(PSR_UND32_MODE);
return(0);
}
int
db_access_abt_sp(const struct db_variable *vp, db_expr_t *valp, int rw)
{
if (rw == DB_VAR_GET)
*valp = get_stackptr(PSR_ABT32_MODE);
return(0);
}
int
db_access_irq_sp(const struct db_variable *vp, db_expr_t *valp, int rw)
{
if (rw == DB_VAR_GET)
*valp = get_stackptr(PSR_IRQ32_MODE);
return(0);
}
/*
* kdb_trap - field a TRACE or BPT trap
*/
int
kdb_trap(int type, db_regs_t *regs)
{
int s;
switch (type) {
case T_BREAKPOINT: /* breakpoint */
case -1: /* keyboard interrupt */
break;
default:
db_printf("kernel: trap");
if (db_recover != 0) {
db_error("Faulted in DDB; continuing...\n");
/*NOTREACHED*/
}
}
/* Should switch to kdb`s own stack here. */
ddb_regs = *regs;
s = splhigh();
db_active++;
cnpollc(TRUE);
db_trap(type, 0/*code*/);
cnpollc(FALSE);
db_active--;
splx(s);
*regs = ddb_regs;
return (1);
}
static int
db_validate_address(vaddr_t addr)
{
struct proc *p = curproc;
struct pmap *pmap;
if (!p || !p->p_vmspace || !p->p_vmspace->vm_map.pmap)
pmap = pmap_kernel();
else
pmap = p->p_vmspace->vm_map.pmap;
return (pmap_extract(pmap, addr, NULL) == FALSE);
}
/*
* Read bytes from kernel address space for debugger.
*/
void
db_read_bytes(addr, size, data)
vm_offset_t addr;
size_t size;
char *data;
{
char *src;
src = (char *)addr;
while (size-- > 0) {
if (db_validate_address((u_int)src)) {
db_printf("address %p is invalid\n", src);
return;
}
*data++ = *src++;
}
}
static void
db_write_text(vaddr_t addr, size_t size, char *data)
{
struct pmap *pmap = pmap_kernel();
pd_entry_t *pde, oldpde, tmppde;
pt_entry_t *pte, oldpte, tmppte;
vaddr_t pgva;
size_t limit, savesize;
char *dst;
if ((savesize = size) == 0)
return;
dst = (char *) addr;
do {
/* Get the PDE of the current VA. */
pde = pmap_pde(pmap, (vaddr_t) dst);
switch ((oldpde = *pde) & L1_TYPE_MASK) {
case L1_TYPE_S:
pgva = (vaddr_t)dst & L1_S_FRAME;
limit = L1_S_SIZE - ((vaddr_t)dst & L1_S_OFFSET);
tmppde = oldpde | L1_S_PROT_W;
*pde = tmppde;
break;
case L1_TYPE_C:
pgva = (vaddr_t)dst & L2_S_FRAME;
limit = L2_S_SIZE - ((vaddr_t)dst & L2_S_OFFSET);
pte = vtopte(pgva);
oldpte = *pte;
tmppte = oldpte | L2_S_PROT_W;
*pte = tmppte;
break;
default:
printf(" address 0x%08lx not a valid page\n",
(vaddr_t) dst);
return;
}
cpu_tlb_flushD_SE(pgva);
cpu_cpwait();
if (limit > size)
limit = size;
size -= limit;
/*
* Page is now writable. Do as much access as we
* can in this page.
*/
for (; limit > 0; limit--)
*dst++ = *data++;
/*
* Restore old mapping permissions.
*/
switch (oldpde & L1_TYPE_MASK) {
case L1_TYPE_S:
*pde = oldpde;
break;
case L1_TYPE_C:
*pte = oldpte;
break;
}
cpu_tlb_flushD_SE(pgva);
cpu_cpwait();
} while (size != 0);
/* Sync the I-cache. */
cpu_icache_sync_range(addr, savesize);
}
/*
* Write bytes to kernel address space for debugger.
*/
void
db_write_bytes(vaddr_t addr, size_t size, char *data)
{
extern char etext[];
char *dst;
size_t loop;
/* If any part is in kernel text, use db_write_text() */
if (addr >= KERNEL_TEXT_BASE && addr < (vaddr_t) etext) {
db_write_text(addr, size, data);
return;
}
dst = (char *)addr;
loop = size;
while (loop-- > 0) {
if (db_validate_address((u_int)dst)) {
db_printf("address %p is invalid\n", dst);
return;
}
*dst++ = *data++;
}
/* make sure the caches and memory are in sync */
cpu_icache_sync_range(addr, size);
/* In case the current page tables have been modified ... */
cpu_tlb_flushID();
cpu_cpwait();
}
void
cpu_Debugger(void)
{
asm(".word 0xe7ffffff");
}
const struct db_command db_machine_command_table[] = {
{ "frame", db_show_frame_cmd, 0, NULL },
{ "panic", db_show_panic_cmd, 0, NULL },
#ifdef ARM32_DB_COMMANDS
ARM32_DB_COMMANDS,
#endif
{ NULL, NULL, 0, NULL }
};
int
db_trapper(u_int addr, u_int inst, trapframe_t *frame, int fault_code)
{
if (fault_code == 0) {
if ((inst & ~INSN_COND_MASK) == (BKPT_INST & ~INSN_COND_MASK))
kdb_trap(T_BREAKPOINT, frame);
else
kdb_trap(-1, frame);
} else
return (1);
return (0);
}
extern u_int esym;
extern u_int end;
static struct undefined_handler db_uh;
void
db_machine_init(void)
{
#ifndef __ELF__
struct exec *kernexec = (struct exec *)KERNEL_TEXT_BASE;
int len;
/*
* The boot loader currently loads the kernel with the a.out
* header still attached.
*/
if (kernexec->a_syms == 0) {
printf("ddb: No symbol table\n");
} else {
/* cover the symbols themselves (what is the int for?? XXX) */
esym = (int)&end + kernexec->a_syms + sizeof(int);
/*
* and the string table. (int containing size of string
* table is included in string table size).
*/
len = *((u_int *)esym);
esym += (len + (sizeof(u_int) - 1)) & ~(sizeof(u_int) - 1);
}
#endif
/*
* We get called before malloc() is available, so supply a static
* struct undefined_handler.
*/
db_uh.uh_handler = db_trapper;
install_coproc_handler_static(0, &db_uh);
}
u_int
db_fetch_reg(int reg, db_regs_t *db_regs)
{
switch (reg) {
case 0:
return (db_regs->tf_r0);
case 1:
return (db_regs->tf_r1);
case 2:
return (db_regs->tf_r2);
case 3:
return (db_regs->tf_r3);
case 4:
return (db_regs->tf_r4);
case 5:
return (db_regs->tf_r5);
case 6:
return (db_regs->tf_r6);
case 7:
return (db_regs->tf_r7);
case 8:
return (db_regs->tf_r8);
case 9:
return (db_regs->tf_r9);
case 10:
return (db_regs->tf_r10);
case 11:
return (db_regs->tf_r11);
case 12:
return (db_regs->tf_r12);
case 13:
return (db_regs->tf_svc_sp);
case 14:
return (db_regs->tf_svc_lr);
case 15:
return (db_regs->tf_pc);
default:
panic("db_fetch_reg: botch");
}
}
u_int
branch_taken(u_int insn, u_int pc, db_regs_t *db_regs)
{
u_int addr, nregs;
switch ((insn >> 24) & 0xf) {
case 0xa: /* b ... */
case 0xb: /* bl ... */
addr = ((insn << 2) & 0x03ffffff);
if (addr & 0x02000000)
addr |= 0xfc000000;
return (pc + 8 + addr);
case 0x7: /* ldr pc, [pc, reg, lsl #2] */
addr = db_fetch_reg(insn & 0xf, db_regs);
addr = pc + 8 + (addr << 2);
db_read_bytes(addr, 4, (char *)&addr);
return (addr);
case 0x1: /* mov pc, reg */
addr = db_fetch_reg(insn & 0xf, db_regs);
return (addr);
case 0x8: /* ldmxx reg, {..., pc} */
case 0x9:
addr = db_fetch_reg((insn >> 16) & 0xf, db_regs);
nregs = (insn & 0x5555) + ((insn >> 1) & 0x5555);
nregs = (nregs & 0x3333) + ((nregs >> 2) & 0x3333);
nregs = (nregs + (nregs >> 4)) & 0x0f0f;
nregs = (nregs + (nregs >> 8)) & 0x001f;
switch ((insn >> 23) & 0x3) {
case 0x0: /* ldmda */
addr = addr - 0;
break;
case 0x1: /* ldmia */
addr = addr + 0 + ((nregs - 1) << 2);
break;
case 0x2: /* ldmdb */
addr = addr - 4;
break;
case 0x3: /* ldmib */
addr = addr + 4 + ((nregs - 1) << 2);
break;
}
db_read_bytes(addr, 4, (char *)&addr);
return (addr);
default:
panic("branch_taken: botch");
}
}