File: [local] / sys / arch / mips64 / mips64 / db_machdep.c (download)
Revision 1.1.1.1 (vendor branch), Tue Mar 4 16:07:34 2008 UTC (16 years, 3 months ago) by nbrk
Branch: OPENBSD_4_2_BASE, MAIN
CVS Tags: jornada-partial-support-wip, HEAD Changes since 1.1: +0 -0 lines
Import of OpenBSD 4.2 release kernel tree with initial code to support
Jornada 720/728, StrongARM 1110-based handheld PC.
At this point kernel roots on NFS and boots into vfs_mountroot() and traps.
What is supported:
- glass console, Jornada framebuffer (jfb) works in 16bpp direct color mode
(needs some palette tweaks for non black/white/blue colors, i think)
- saic, SA11x0 interrupt controller (needs cleanup)
- sacom, SA11x0 UART (supported only as boot console for now)
- SA11x0 GPIO controller fully supported (but can't handle multiple interrupt
handlers on one gpio pin)
- sassp, SSP port on SA11x0 that attaches spibus
- Jornada microcontroller (jmcu) to control kbd, battery, etc throught
the SPI bus (wskbd attaches on jmcu, but not tested)
- tod functions seem work
- initial code for SA-1111 (chip companion) : this is TODO
Next important steps, i think:
- gpio and intc on sa1111
- pcmcia support for sa11x0 (and sa1111 help logic)
- REAL root on nfs when we have PCMCIA support (we may use any of supported pccard NICs)
- root on wd0! (using already supported PCMCIA-ATA)
|
/* $OpenBSD: db_machdep.c,v 1.11 2007/05/03 19:34:00 miod Exp $ */
/*
* Copyright (c) 1998-2003 Opsycon AB (www.opsycon.se)
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <dev/cons.h>
#include <machine/autoconf.h>
#include <machine/db_machdep.h>
#include <machine/cpu.h>
#include <machine/mips_opcode.h>
#include <machine/pte.h>
#include <machine/frame.h>
#include <machine/regnum.h>
#include <ddb/db_sym.h>
#include <ddb/db_extern.h>
#include <ddb/db_access.h>
#include <ddb/db_command.h>
#include <ddb/db_output.h>
#include <ddb/db_variables.h>
#include <ddb/db_interface.h>
#define MIPS_JR_RA 0x03e00008 /* instruction code for jr ra */
extern void trapDump(char *);
u_long MipsEmulateBranch(db_regs_t *, int, int, u_int);
void stacktrace_subr(db_regs_t *, int (*)(const char*, ...));
int kdbpeek(void *);
int64_t kdbpeekd(void *);
short kdbpeekw(void *);
char kdbpeekb(void *);
void kdbpoke(vaddr_t, int);
void kdbpoked(vaddr_t, int64_t);
void kdbpokew(vaddr_t, short);
void kdbpokeb(vaddr_t, char);
int kdb_trap(int, struct trap_frame *);
void db_trap_trace_cmd(db_expr_t, int, db_expr_t, char *);
void db_dump_tlb_cmd(db_expr_t, int, db_expr_t, char *);
int db_active = 0;
db_regs_t ddb_regs;
struct db_variable db_regs[] = {
{ "at", (long *)&ddb_regs.ast, FCN_NULL },
{ "v0", (long *)&ddb_regs.v0, FCN_NULL },
{ "v1", (long *)&ddb_regs.v1, FCN_NULL },
{ "a0", (long *)&ddb_regs.a0, FCN_NULL },
{ "a1", (long *)&ddb_regs.a1, FCN_NULL },
{ "a2", (long *)&ddb_regs.a2, FCN_NULL },
{ "a3", (long *)&ddb_regs.a3, FCN_NULL },
{ "a4", (long *)&ddb_regs.t0, FCN_NULL },
{ "a5", (long *)&ddb_regs.t1, FCN_NULL },
{ "a6", (long *)&ddb_regs.t2, FCN_NULL },
{ "a7", (long *)&ddb_regs.t3, FCN_NULL },
{ "t0", (long *)&ddb_regs.t4, FCN_NULL },
{ "t1", (long *)&ddb_regs.t5, FCN_NULL },
{ "t2", (long *)&ddb_regs.t6, FCN_NULL },
{ "t3", (long *)&ddb_regs.t7, FCN_NULL },
{ "s0", (long *)&ddb_regs.s0, FCN_NULL },
{ "s1", (long *)&ddb_regs.s1, FCN_NULL },
{ "s2", (long *)&ddb_regs.s2, FCN_NULL },
{ "s3", (long *)&ddb_regs.s3, FCN_NULL },
{ "s4", (long *)&ddb_regs.s4, FCN_NULL },
{ "s5", (long *)&ddb_regs.s5, FCN_NULL },
{ "s6", (long *)&ddb_regs.s6, FCN_NULL },
{ "s7", (long *)&ddb_regs.s7, FCN_NULL },
{ "t8", (long *)&ddb_regs.t8, FCN_NULL },
{ "t9", (long *)&ddb_regs.t9, FCN_NULL },
{ "k0", (long *)&ddb_regs.k0, FCN_NULL },
{ "k1", (long *)&ddb_regs.k1, FCN_NULL },
{ "gp", (long *)&ddb_regs.gp, FCN_NULL },
{ "sp", (long *)&ddb_regs.sp, FCN_NULL },
{ "s8", (long *)&ddb_regs.s8, FCN_NULL },
{ "ra", (long *)&ddb_regs.ra, FCN_NULL },
{ "sr", (long *)&ddb_regs.sr, FCN_NULL },
{ "lo", (long *)&ddb_regs.mullo, FCN_NULL },
{ "hi", (long *)&ddb_regs.mulhi, FCN_NULL },
{ "bad", (long *)&ddb_regs.badvaddr,FCN_NULL },
{ "cs", (long *)&ddb_regs.cause, FCN_NULL },
{ "pc", (long *)&ddb_regs.pc, FCN_NULL },
};
struct db_variable *db_eregs = db_regs + sizeof(db_regs)/sizeof(db_regs[0]);
extern label_t *db_recover;
int
kdb_trap(type, fp)
int type;
struct trap_frame *fp;
{
switch(type) {
case T_BREAK: /* breakpoint */
if (db_get_value((fp)->pc, sizeof(int), FALSE) == BREAK_SOVER) {
(fp)->pc += BKPT_SIZE;
}
break;
case -1:
break;
default:
#if 0
if (!db_panic)
return (0);
#endif
if (db_recover != 0) {
db_error("Caught exception in ddb.\n");
/*NOTREACHED*/
}
printf("stoped on non ddb fault\n");
}
bcopy((void *)fp, (void *)&ddb_regs, NUMSAVEREGS * sizeof(register_t));
db_active++;
cnpollc(TRUE);
db_trap(type, 0);
cnpollc(FALSE);
db_active--;
bcopy((void *)&ddb_regs, (void *)fp, NUMSAVEREGS * sizeof(register_t));
return(TRUE);
}
void
db_read_bytes(addr, size, data)
vaddr_t addr;
size_t size;
char *data;
{
while(size >= sizeof(int)) {
*((int *)data)++ = kdbpeek((void *)addr);
addr += sizeof(int);
size -= sizeof(int);
}
if (size > sizeof(short)) {
*((short *)data)++ = kdbpeekw((void *)addr);
addr += sizeof(short);
size -= sizeof(short);
}
if (size) {
*data++ = kdbpeekb((void *)addr);
}
}
void
db_write_bytes(addr, size, data)
vaddr_t addr;
size_t size;
char *data;
{
vaddr_t ptr = addr;
size_t len = size;
while (len >= sizeof(int)) {
kdbpoke(ptr, *((int *)data)++);
ptr += sizeof(int);
len -= sizeof(int);
}
if (len >= sizeof(short)) {
kdbpokew(ptr, *((short *)data)++);
ptr += sizeof(int);
len -= sizeof(int);
}
if (len) {
kdbpokeb(ptr, *data++);
}
if (addr < VM_MIN_KERNEL_ADDRESS) {
Mips_HitSyncDCache(addr, size);
Mips_InvalidateICache(PHYS_TO_KSEG0(addr & 0xffff), size);
}
}
void
db_stack_trace_print(addr, have_addr, count, modif, pr)
db_expr_t addr;
boolean_t have_addr;
db_expr_t count;
char *modif;
int (*pr)(const char *, ...);
{
db_sym_t sym;
db_expr_t diff;
db_addr_t subr;
char *symname;
register_t pc, sp, ra, va;
register_t a0, a1, a2, a3;
unsigned instr, mask;
InstFmt i;
int more, stksize;
extern char edata[];
extern char k_intr[];
extern char k_general[];
extern char idle[];
struct trap_frame *regs = &ddb_regs;
/* get initial values from the exception frame */
sp = regs->sp;
pc = regs->pc;
ra = regs->ra; /* May be a 'leaf' function */
a0 = regs->a0;
a1 = regs->a1;
a2 = regs->a2;
a3 = regs->a3;
/* Jump here when done with a frame, to start a new one */
loop:
/* Jump here after a nonstandard (interrupt handler) frame */
stksize = 0;
/* check for bad SP: could foul up next frame */
if (sp & 3 || (!IS_XKPHYS((vaddr_t)sp) && sp < KSEG0_BASE)) {
(*pr)("SP %p: not in kernel\n", sp);
ra = 0;
subr = 0;
goto done;
}
#if 0
/* Backtraces should contine through interrupts from kernel mode */
if (pc >= (unsigned)MipsKernIntr && pc < (unsigned)MipsUserIntr) {
(*pr)("MipsKernIntr+%x: (%x, %x ,%x) -------\n",
pc-(unsigned)MipsKernIntr, a0, a1, a2);
regs = (struct trap_frame *)(sp + STAND_ARG_SIZE);
a0 = kdbpeek(®s->a0);
a1 = kdbpeek(®s->a1);
a2 = kdbpeek(®s->a2);
a3 = kdbpeek(®s->a3);
pc = kdbpeek(®s->pc); /* exc_pc - pc at time of exception */
ra = kdbpeek(®s->ra); /* ra at time of exception */
sp = kdbpeek(®s->sp);
goto specialframe;
}
#endif
/* check for bad PC */
if (pc & 3 || pc < KSEG0_BASE || pc >= (unsigned)edata) {
(*pr)("PC %p: not in kernel\n", pc);
ra = 0;
goto done;
}
/*
* Dig out the function from the symbol table.
* Watch out for function tail optimizations.
*/
sym = db_search_symbol(pc, DB_STGY_ANY, &diff);
if (sym != DB_SYM_NULL && diff == 0)
sym = db_search_symbol(pc - 4, DB_STGY_ANY, &diff);
db_symbol_values(sym, &symname, 0);
if (sym != DB_SYM_NULL) {
subr = pc - diff;
} else {
subr = 0;
}
/*
* Find the beginning of the current subroutine by scanning backwards
* from the current PC for the end of the previous subroutine.
*/
if (!subr) {
va = pc - sizeof(int);
while ((instr = kdbpeek((int *)va)) != MIPS_JR_RA)
va -= sizeof(int);
va += 2 * sizeof(int); /* skip back over branch & delay slot */
/* skip over nulls which might separate .o files */
while ((instr = kdbpeek((int *)va)) == 0)
va += sizeof(int);
subr = va;
}
/*
* Jump here for locore entry points for which the preceding
* function doesn't end in "j ra"
*/
/* scan forwards to find stack size and any saved registers */
stksize = 0;
more = 3;
mask = 0;
for (va = subr; more; va += sizeof(int),
more = (more == 3) ? 3 : more - 1) {
/* stop if hit our current position */
if (va >= pc)
break;
instr = kdbpeek((int *)va);
i.word = instr;
switch (i.JType.op) {
case OP_SPECIAL:
switch (i.RType.func) {
case OP_JR:
case OP_JALR:
more = 2; /* stop after next instruction */
break;
case OP_SYSCALL:
case OP_BREAK:
more = 1; /* stop now */
};
break;
case OP_BCOND:
case OP_J:
case OP_JAL:
case OP_BEQ:
case OP_BNE:
case OP_BLEZ:
case OP_BGTZ:
more = 2; /* stop after next instruction */
break;
case OP_COP0:
case OP_COP1:
case OP_COP2:
case OP_COP3:
switch (i.RType.rs) {
case OP_BCx:
case OP_BCy:
more = 2; /* stop after next instruction */
};
break;
case OP_SW:
case OP_SD:
/* look for saved registers on the stack */
if (i.IType.rs != 29)
break;
/* only restore the first one */
if (mask & (1 << i.IType.rt))
break;
mask |= (1 << i.IType.rt);
switch (i.IType.rt) {
case 4: /* a0 */
a0 = kdbpeekd((long *)(sp + (short)i.IType.imm));
break;
case 5: /* a1 */
a1 = kdbpeekd((long *)(sp + (short)i.IType.imm));
break;
case 6: /* a2 */
a2 = kdbpeekd((long *)(sp + (short)i.IType.imm));
break;
case 7: /* a3 */
a3 = kdbpeekd((long *)(sp + (short)i.IType.imm));
break;
case 31: /* ra */
ra = kdbpeekd((long *)(sp + (short)i.IType.imm));
break;
}
break;
case OP_ADDI:
case OP_ADDIU:
case OP_DADDI:
case OP_DADDIU:
/* look for stack pointer adjustment */
if (i.IType.rs != 29 || i.IType.rt != 29)
break;
stksize = - ((short)i.IType.imm);
}
}
done:
if (symname == NULL) {
if (subr == (long)idle)
(*pr)("idle ");
else
(*pr)("%p ", subr);
} else {
(*pr)("%s+%p ", symname, diff);
}
(*pr)("(%llx,%llx,%llx,%llx) sp %llx ra %llx, sz %d\n", a0, a1, a2, a3, sp, ra, stksize);
if (subr == (long)k_intr || subr == (long)k_general) {
if (subr == (long)k_intr)
(*pr)("(KERNEL INTERRUPT)\n");
else
(*pr)("(KERNEL TRAP)\n");
sp = *(register_t *)sp;
pc = ((struct trap_frame *)sp)->pc;
ra = ((struct trap_frame *)sp)->ra;
sp = ((struct trap_frame *)sp)->sp; /* last */
goto loop;
}
if (ra) {
if (pc == ra && stksize == 0)
(*pr)("stacktrace: loop!\n");
else {
pc = ra;
sp += stksize;
ra = 0;
goto loop;
}
} else {
if (curproc)
(*pr)("User-level: pid %d\n", curproc->p_pid);
else
(*pr)("User-level: curproc NULL\n");
}
}
/*
* To do a single step ddb needs to know the next address
* that we will get to. It means that we need to find out
* both the address for a branch taken and for not taken, NOT! :-)
* MipsEmulateBranch will do the job to find out _exactly_ which
* address we will end up at so the 'dual bp' method is not
* requiered.
*/
db_addr_t
next_instr_address(db_addr_t pc, boolean_t bd)
{
db_addr_t next;
next = MipsEmulateBranch(&ddb_regs, pc, 0, 0);
return(next);
}
/*
* Decode instruction and figure out type.
*/
int
db_inst_type(ins)
int ins;
{
InstFmt inst;
int ityp = 0;
inst.word = ins;
switch ((int)inst.JType.op) {
case OP_SPECIAL:
switch ((int)inst.RType.func) {
case OP_JR:
ityp = IT_BRANCH;
break;
case OP_JALR:
case OP_SYSCALL:
ityp = IT_CALL;
break;
}
break;
case OP_BCOND:
switch ((int)inst.IType.rt) {
case OP_BLTZ:
case OP_BLTZL:
case OP_BGEZ:
case OP_BGEZL:
ityp = IT_BRANCH;
break;
case OP_BLTZAL:
case OP_BLTZALL:
case OP_BGEZAL:
case OP_BGEZALL:
ityp = IT_CALL;
break;
}
break;
case OP_JAL:
ityp = IT_CALL;
break;
case OP_J:
case OP_BEQ:
case OP_BEQL:
case OP_BNE:
case OP_BNEL:
case OP_BLEZ:
case OP_BLEZL:
case OP_BGTZ:
case OP_BGTZL:
ityp = IT_BRANCH;
break;
case OP_COP1:
switch (inst.RType.rs) {
case OP_BCx:
case OP_BCy:
ityp = IT_BRANCH;
break;
}
break;
case OP_LB:
case OP_LH:
case OP_LW:
case OP_LD:
case OP_LBU:
case OP_LHU:
case OP_LWU:
case OP_LWC1:
ityp = IT_LOAD;
break;
case OP_SB:
case OP_SH:
case OP_SW:
case OP_SD:
case OP_SWC1:
ityp = IT_STORE;
break;
}
return (ityp);
}
/*
* MIPS machine dependent DDB commands.
*/
/*
* Do a trap traceback.
*/
void
db_trap_trace_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *m)
{
trapDump("ddb trap trace");
}
/*
* Dump TLB contents.
*/
void
db_dump_tlb_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *m)
{
int tlbno, last, check, pid;
struct tlb_entry tlb, tlbp;
char *attr[] = {
"WTNA", "WTA ", "UCBL", "CWB ", "RES ", "RES ", "UCNB", "BPAS"
};
pid = -1;
if (m[0] == 'p') {
if (have_addr && addr < 256) {
pid = addr;
tlbno = 0;
count = sys_config.cpu[0].tlbsize;
}
} else if (m[0] == 'c') {
last = sys_config.cpu[0].tlbsize;
for (tlbno = 0; tlbno < last; tlbno++) {
tlb_read(tlbno, &tlb);
for (check = tlbno + 1; check < last; check++) {
tlb_read(check, &tlbp);
if ((tlbp.tlb_hi == tlb.tlb_hi && (tlb.tlb_lo0 & PG_V || tlb.tlb_lo1 & PG_V)) ||
(pfn_to_pad(tlb.tlb_lo0) == pfn_to_pad(tlbp.tlb_lo0) && tlb.tlb_lo0 & PG_V) ||
(pfn_to_pad(tlb.tlb_lo1) == pfn_to_pad(tlbp.tlb_lo1) && tlb.tlb_lo1 & PG_V)) {
printf("MATCH:\n");
db_dump_tlb_cmd(tlbno, 1, 1, "");
db_dump_tlb_cmd(check, 1, 1, "");
}
}
}
return;
} else {
if (have_addr && addr < sys_config.cpu[0].tlbsize) {
tlbno = addr;
}
else {
tlbno = 0;
count = sys_config.cpu[0].tlbsize;
}
}
last = tlbno + count;
for (; tlbno < sys_config.cpu[0].tlbsize && tlbno < last; tlbno++) {
tlb_read(tlbno, &tlb);
if (pid >= 0 && (tlb.tlb_hi & 0xff) != pid)
continue;
if (tlb.tlb_lo0 & PG_V || tlb.tlb_lo1 & PG_V) {
printf("%2d v=%16llx", tlbno, tlb.tlb_hi & (long)~0xff);
printf("/%02x ", tlb.tlb_hi & 0xff);
if (tlb.tlb_lo0 & PG_V) {
printf("%16llx ", pfn_to_pad(tlb.tlb_lo0));
printf("%c", tlb.tlb_lo0 & PG_M ? 'M' : ' ');
printf("%c", tlb.tlb_lo0 & PG_G ? 'G' : ' ');
printf("%s ", attr[(tlb.tlb_lo0 >> 3) & 7]);
} else {
printf("invalid ");
}
if (tlb.tlb_lo1 & PG_V) {
printf("%16llx ", pfn_to_pad(tlb.tlb_lo1));
printf("%c", tlb.tlb_lo1 & PG_M ? 'M' : ' ');
printf("%c", tlb.tlb_lo1 & PG_G ? 'G' : ' ');
printf("%s ", attr[(tlb.tlb_lo1 >> 3) & 7]);
} else {
printf("invalid ");
}
printf(" sz=%x", tlb.tlb_mask);
}
else if (pid < 0) {
printf("%2d v=invalid ", tlbno);
}
printf("\n");
}
}
struct db_command mips_db_command_table[] = {
{ "tlb", db_dump_tlb_cmd, 0, NULL },
{ "trap", db_trap_trace_cmd, 0, NULL },
{ NULL, NULL, 0, NULL }
};
void
db_machine_init()
{
extern char *ssym;
db_machine_commands_install(mips_db_command_table);
if (ssym != NULL) {
ddb_init(); /* Init symbols */
}
}