915 lines
20 KiB
C
915 lines
20 KiB
C
#ident "@(#)machdep.c 1.1 92/07/30"
|
|
|
|
/*
|
|
* Copyright (c) 1986 by Sun Microsystems, Inc.
|
|
*/
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/errno.h>
|
|
#include <sys/vmmac.h>
|
|
#include <sun/openprom.h>
|
|
#include <machine/buserr.h>
|
|
#include <machine/enable.h>
|
|
#include <machine/mmu.h>
|
|
#include <machine/cpu.h>
|
|
#include <machine/pte.h>
|
|
#include <machine/scb.h>
|
|
#include <machine/psl.h>
|
|
#include <machine/trap.h>
|
|
#include <machine/clock.h>
|
|
#include <machine/intreg.h>
|
|
#include <machine/eeprom.h>
|
|
#include <machine/asm_linkage.h>
|
|
#include <machine/reg.h>
|
|
#include <machine/frame.h>
|
|
#include "allregs.h"
|
|
#include <debug/debug.h>
|
|
#include <debug/debugger.h>
|
|
|
|
extern int errno;
|
|
extern char *malloc();
|
|
|
|
int istrap = 0;
|
|
int scbsyncdone = 0;
|
|
|
|
/*
|
|
* The next group of variables and routines handle the
|
|
* Open Boot Prom devinfo or property information.
|
|
*
|
|
* These machine-dependent quantities are set from the prom properties.
|
|
* For the time being, set these to "large, safe" values.
|
|
* XXX - does this all want to be packaged in a header file?
|
|
*/
|
|
extern void fiximp();
|
|
extern int Cpudelay;
|
|
extern int nwindows;
|
|
extern u_int npmgrps;
|
|
extern int vac_size;
|
|
extern int vac_linesize;
|
|
extern int segmask;
|
|
extern int mips_off, mips_on; /* approx. MIPS with cache off/on */
|
|
/*
|
|
* properties tchotchkes
|
|
*/
|
|
#define GETPROPLEN prom_getproplen
|
|
#define GETPROP prom_getprop
|
|
#define NEXT prom_nextnode
|
|
#define CHILD prom_childnode
|
|
extern int getprop();
|
|
|
|
/*
|
|
* Open proms give us romp as a variable
|
|
*/
|
|
struct sunromvec *romp = (struct sunromvec *)0xFFE81000;
|
|
|
|
int fake_bpt; /* place for a fake breakpoint at startup */
|
|
jmp_buf debugregs; /* context for debugger */
|
|
jmp_buf mainregs; /* context for debuggee */
|
|
jmp_buf_ptr curregs; /* pointer to saved context for each process */
|
|
int cache_on; /* cache is being used */
|
|
struct allregs regsave; /* temp save area--align to double */
|
|
struct scb *mon_tbr, *our_tbr; /* storage for %tbr's */
|
|
|
|
int use_kern_tbr = 0;
|
|
|
|
extern char start[], estack[], etext[], edata[], end[];
|
|
extern int exit();
|
|
extern struct scb *gettbr();
|
|
|
|
/*
|
|
* Definitions for registers in jmp_buf
|
|
*/
|
|
#define JB_PC 0
|
|
#define JB_SP 1
|
|
|
|
#define CALL(func) (*(int (*)())((int)(func) - (int)start + (int)real))
|
|
#define RELOC(adr) ((adr) - (char *)start + real)
|
|
extern setcontext(), getsegmap(), setsegmap(), getpgmap(), setpgmap();
|
|
extern printf();
|
|
void Setpgmap();
|
|
|
|
/*
|
|
* This routine is called before we are relocated to the correct address,
|
|
* so everything is done via funny macros when dealing w/ globals.
|
|
* Also, we have a candidate for "worst abuse of preprocessor" award:
|
|
* MONSTART and MONEND are actually defined using romp, but _romp
|
|
* isn't set yet. Call our sunromvec argument "romp" so that the global
|
|
* variable is hidden in this function and the correct value is used.
|
|
*/
|
|
early_startup(real, romp)
|
|
register char *real;
|
|
register struct sunromvec *romp;
|
|
{
|
|
register u_int cnt, pg, i;
|
|
register int *from, *to;
|
|
register int pm, lopm;
|
|
register int lastpmsav, lastpgsav;
|
|
register struct memlist *pmem;
|
|
caddr_t vstart;
|
|
int too_big = 0;
|
|
u_int npmgrps; /* yes, this hides the global too */
|
|
|
|
(void) CALL(setcontext)(0);
|
|
/*
|
|
* We need to know npmgrps so we can compute PMGRP_INVALID.
|
|
* We'll do this again later for the real globals.
|
|
* Alas! We can't just call getprop(), because the global romp
|
|
* isn't even mapped in yet. We want to do this:
|
|
* if ((int)CALL(getprop)(0, "mmu-npmg", &npmgrps) <= 0)
|
|
* npmgrps = NPMGRPS_60;
|
|
* but we have to do this:
|
|
*/
|
|
if ((*romp->op_config_ops->devr_getproplen)
|
|
(0, RELOC("mmu-npmg")) == sizeof (int))
|
|
{
|
|
(*romp->op_config_ops->devr_getprop)
|
|
(0, RELOC("mmu-npmg"), &npmgrps);
|
|
} else
|
|
npmgrps = NPMGRPS_60;
|
|
|
|
/*
|
|
* Find the top of physical memory and take as many pages as we need.
|
|
* We assume the physmem chunks are in ascending order and the last
|
|
* chunk is big enough to hold us. (It actually doesn't matter if
|
|
* the pages are in order; we handle that gracefully.)
|
|
*/
|
|
cnt = mmu_btopr(end - start);
|
|
if (romp->op_romvec_version > 0) {
|
|
vstart = (caddr_t)(*romp->op2_alloc)((int)start,
|
|
mmu_ptob(cnt));
|
|
if ((u_int)vstart != (u_int)start)
|
|
too_big = 1;
|
|
} else {
|
|
pmem = *romp->v_availmemory;
|
|
while (pmem->next != (struct memlist *)NULL)
|
|
pmem = pmem->next;
|
|
pg = mmu_btopr(pmem->address + pmem->size) - cnt;
|
|
if (mmu_ptob(cnt) > pmem->size) {
|
|
too_big = 1;
|
|
} else {
|
|
too_big = 0;
|
|
pmem->size -= mmu_ptob(cnt);
|
|
}
|
|
|
|
/*
|
|
* Look for the lowest pmeg in use in the monitor
|
|
* and "DVMA" space
|
|
*/
|
|
lopm = PMGRP_INVALID;
|
|
for (i = MONSTART; i != 0; i += PMGRPSIZE)
|
|
if (((pm = CALL(getsegmap)(i)) < lopm) && pm)
|
|
lopm = pm;
|
|
|
|
/*
|
|
* Adjust down from there to get our starting pmeg
|
|
* and save copies of last pmeg and page used.
|
|
*/
|
|
lopm -= ((unsigned)(end - start) + PMGRPOFFSET) >> PMGRPSHIFT;
|
|
lastpmsav = lopm;
|
|
lastpgsav = pg;
|
|
|
|
for (i = (int)start; i < (int)end; i += MMU_PAGESIZE) {
|
|
if (CALL(getsegmap)(i) == PMGRP_INVALID) {
|
|
register u_int j = i & ~PMGRPOFFSET;
|
|
u_int last = j + NPMENTPERPMGRP * MMU_PAGESIZE;
|
|
|
|
(void) CALL(setsegmap)(i, lopm++);
|
|
while (j < last) {
|
|
(void) CALL(setpgmap)(j, 0);
|
|
j += MMU_PAGESIZE;
|
|
}
|
|
}
|
|
(void) CALL(setpgmap)(i, PG_V | PG_KW | PG_NC | pg++);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Copy program up to correct address
|
|
*/
|
|
for (to = (int *)start, from = (int *)real; to < (int *)edata; )
|
|
*to++ = *from++;
|
|
for (to = (int *)edata; to < (int *)end; ++to)
|
|
*to = 0;
|
|
|
|
/*
|
|
* Now we can reference global variables,
|
|
* save page count and monitor's nmi routine address.
|
|
* Don't need this information on OBP.
|
|
*/
|
|
if (romp->op_romvec_version > 0) {
|
|
lastpg = 0;
|
|
lastpm = 0;
|
|
pagesused = 0;
|
|
mon_tbr = gettbr();
|
|
if (too_big) {
|
|
CALL(printf)(
|
|
RELOC("kadb: size %x exceeded available memory\n"),
|
|
mmu_ptob(cnt));
|
|
_exit(1);
|
|
}
|
|
} else {
|
|
lastpg = lastpgsav;
|
|
lastpm = lastpmsav;
|
|
pagesused = cnt;
|
|
mon_tbr = gettbr();
|
|
if (too_big) {
|
|
CALL(printf)(
|
|
RELOC(
|
|
"kadb: size %x exceeded available memory %x\n"),
|
|
mmu_ptob(cnt), pmem->size);
|
|
_exit(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Startup code after relocation.
|
|
*/
|
|
startup()
|
|
{
|
|
register int i;
|
|
register int pg;
|
|
u_char intreg;
|
|
register int vaddr, pmeg;
|
|
register int cpu;
|
|
extern void prom_init();
|
|
|
|
|
|
prom_init("Kadb");
|
|
|
|
/*
|
|
* Set our implementation parameters from the prom properties.
|
|
*/
|
|
fiximp();
|
|
|
|
if (prom_getversion() > 0) {
|
|
vaddr = (int)(*romp->op2_map)(COUNTER_ADDR, OBIO,
|
|
OBIO_COUNTER_ADDR, MMU_PAGESIZE);
|
|
vaddr = (int)(*romp->op2_map)(INTREG_ADDR, OBIO,
|
|
OBIO_INTREG_ADDR, MMU_PAGESIZE);
|
|
} else {
|
|
/*
|
|
* We need to get a pmeg for the devices we are mapping in.
|
|
* Assume the devices lie within the same segment.
|
|
*/
|
|
vaddr = EEPROM_ADDR & COUNTER_ADDR & INTREG_ADDR;
|
|
pmeg = getsegmap(vaddr);
|
|
if (pmeg == PMGRP_INVALID) {
|
|
setsegmap(vaddr, --lastpm);
|
|
}
|
|
/*
|
|
* Now map! Remember: on a sun4c, the clock is in the eeprom.
|
|
*/
|
|
Setpgmap((caddr_t)EEPROM_ADDR,
|
|
PG_V|PG_KW|PGT_OBIO|btop(OBIO_EEPROM_ADDR));
|
|
Setpgmap((caddr_t)COUNTER_ADDR,
|
|
PG_V|PG_KW|PGT_OBIO|btop(OBIO_COUNTER_ADDR));
|
|
Setpgmap((caddr_t)INTREG_ADDR,
|
|
PG_V|PG_KW|PGT_OBIO|btop(OBIO_INTREG_ADDR));
|
|
}
|
|
set_clk_mode(IR_ENA_CLK14, 0);
|
|
|
|
/*
|
|
* Fix up old scb.
|
|
*/
|
|
kadbscbsync();
|
|
spl13(); /* we can take nmi's now */
|
|
|
|
/*
|
|
* Now make text (and dvec) read only,
|
|
* this also sets a stack redzone
|
|
*/
|
|
for (i = (int)start; i < (int)etext; i += MMU_PAGESIZE) {
|
|
pg = getpgmap(i);
|
|
Setpgmap(i, (pg & ~PG_PROT) | PG_KR);
|
|
}
|
|
|
|
cache_on = getenablereg() & ENA_CACHE;
|
|
if (cache_on) {
|
|
setdelay(mips_on);
|
|
} else {
|
|
setdelay(mips_off);
|
|
vac_init(); /* invalidate entire cache */
|
|
}
|
|
}
|
|
|
|
scbsync()
|
|
{
|
|
kadbscbsync();
|
|
scbsyncdone = 1;
|
|
}
|
|
|
|
kadbscbsync()
|
|
{
|
|
register struct scb *tbr;
|
|
register int otbr_pg;
|
|
extern trapvec tcode;
|
|
|
|
tbr = gettbr();
|
|
otbr_pg = getpgmap(tbr);
|
|
Setpgmap(tbr, (otbr_pg & ~PG_PROT) | PG_KW);
|
|
|
|
tbr->user_trap[TRAPBRKNO-1] = tcode;
|
|
tbr->user_trap[TRAPBRKNO] = tcode;
|
|
Setpgmap(tbr, otbr_pg);
|
|
if (scbstop) {
|
|
/*
|
|
* We're running interactively. Trap into the debugger
|
|
* so the user can look around before continuing.
|
|
* We use trap TRAPBRKNO-1: "enter debugger"
|
|
*/
|
|
scbstop = 0;
|
|
asm_trap(TRAPBRKNO-1);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Sys_trap trap handlers.
|
|
*/
|
|
|
|
/*
|
|
* level15 (memory error) interrupt.
|
|
*/
|
|
level15()
|
|
{
|
|
/*
|
|
* For now, the memory error regs are not mapped into the debugger,
|
|
* so we just print a message.
|
|
*/
|
|
printf("memory error\n");
|
|
}
|
|
|
|
/*
|
|
* Miscellanous fault error handler
|
|
*/
|
|
fault(trap, trappc, trapnpc)
|
|
register int trap;
|
|
register int trappc;
|
|
register int trapnpc;
|
|
{
|
|
register int ondebug_stack;
|
|
register u_int *pc;
|
|
register u_int realpc;
|
|
|
|
ondebug_stack = (getsp() > (int)etext && getsp() < (int)estack);
|
|
if (trap == T_DATA_FAULT && nofault && ondebug_stack) {
|
|
jmp_buf_ptr sav = nofault;
|
|
|
|
nofault = NULL;
|
|
_longjmp(sav, 1);
|
|
/*NOTREACHED*/
|
|
}
|
|
traceback(getsp());
|
|
/*
|
|
* If we are on the debugger stack and
|
|
* abort_jmp is set, do a longjmp to it.
|
|
*/
|
|
if (abort_jmp && ondebug_stack) {
|
|
printf("abort jump: trap %x sp %x pc %x npc %x\n",
|
|
trap, getsp(), trappc, trapnpc);
|
|
printf("etext %x estack %x edata %x nofault %x\n",
|
|
etext, estack, edata, nofault);
|
|
_longjmp(abort_jmp, 1);
|
|
/*NOTREACHED*/
|
|
}
|
|
|
|
/*
|
|
* Ok, the user faulted while not in the
|
|
* debugger. Enter the main cmd loop
|
|
* so that the user can look around...
|
|
*/
|
|
/*
|
|
* There is a problem here since we really need to tell cmd()
|
|
* the current registers. We would like to call cmd() in locore
|
|
* but the interface is not really set up to handle this (yet?)
|
|
*/
|
|
|
|
printf("fault and calling cmd: trap %x sp %x pc %x npc %x\n",
|
|
trap, getsp(), trappc, trapnpc);
|
|
cmd(); /* error not resolved, enter debugger */
|
|
}
|
|
|
|
long trap_window[25];
|
|
static jmp_buf_ptr saved_jb;
|
|
static jmp_buf jb;
|
|
extern int debugging;
|
|
|
|
|
|
/*
|
|
* Peekc is so named to avoid a naming conflict
|
|
* with adb which has a variable named peekc
|
|
*/
|
|
int
|
|
Peekc(addr)
|
|
char *addr;
|
|
{
|
|
u_char val;
|
|
|
|
saved_jb = nofault;
|
|
nofault = jb;
|
|
errno = 0;
|
|
if (!_setjmp(jb)) {
|
|
val = *addr;
|
|
/* if we get here, it worked */
|
|
nofault = saved_jb;
|
|
return ((int)val);
|
|
}
|
|
/* a fault occured */
|
|
nofault = saved_jb;
|
|
errno = EFAULT;
|
|
return (-1);
|
|
}
|
|
|
|
short
|
|
peek(addr)
|
|
short *addr;
|
|
{
|
|
short val;
|
|
|
|
saved_jb = nofault;
|
|
nofault = jb;
|
|
errno = 0;
|
|
if (!_setjmp(jb)) {
|
|
val = *addr;
|
|
/* if we get here, it worked */
|
|
nofault = saved_jb;
|
|
return (val);
|
|
}
|
|
/* a fault occured */
|
|
nofault = saved_jb;
|
|
errno = EFAULT;
|
|
return (-1);
|
|
}
|
|
long
|
|
peekl(addr)
|
|
long *addr;
|
|
{
|
|
long val;
|
|
|
|
saved_jb = nofault;
|
|
nofault = jb;
|
|
errno = 0;
|
|
if (!_setjmp(jb)) {
|
|
val = *addr;
|
|
/* if we get here, it worked */
|
|
nofault = saved_jb;
|
|
return (val);
|
|
}
|
|
/* a fault occured */
|
|
nofault = saved_jb;
|
|
errno = EFAULT;
|
|
return (-1);
|
|
}
|
|
|
|
int
|
|
pokec(addr, val)
|
|
char *addr;
|
|
char val;
|
|
{
|
|
|
|
saved_jb = nofault;
|
|
nofault = jb;
|
|
errno = 0;
|
|
if (!_setjmp(jb)) {
|
|
*addr = val;
|
|
/* if we get here, it worked */
|
|
nofault = saved_jb;
|
|
return (0);
|
|
}
|
|
/* a fault occured */
|
|
nofault = saved_jb;
|
|
errno = EFAULT;
|
|
return (-1);
|
|
}
|
|
|
|
int
|
|
pokel(addr, val)
|
|
long *addr;
|
|
long val;
|
|
{
|
|
|
|
saved_jb = nofault;
|
|
nofault = jb;
|
|
errno = 0;
|
|
if (!_setjmp(jb)) {
|
|
*addr = val;
|
|
/* if we get here, it worked */
|
|
nofault = saved_jb;
|
|
return (0);
|
|
}
|
|
/* a fault occured */
|
|
nofault = saved_jb;
|
|
errno = EFAULT;
|
|
return (-1);
|
|
}
|
|
|
|
poketext(addr, val)
|
|
int *addr;
|
|
int val;
|
|
{
|
|
int pg = 0;
|
|
|
|
pg = getpgmap((int)addr);
|
|
if ((pg & PG_V) == 0) {
|
|
if (debugging > 2)
|
|
printf("poketext: invalid page map %X at %X\n",
|
|
pg, addr);
|
|
goto err;
|
|
}
|
|
if ((pg & PGT_MASK) != PGT_OBMEM) {
|
|
if (debugging > 2)
|
|
printf("poketext: incorrect page type %X at %X\n",
|
|
pg, addr);
|
|
goto err;
|
|
}
|
|
|
|
vac_pageflush(addr);
|
|
if (btop(addr + sizeof (int) - 1) != btop(addr))
|
|
vac_pageflush(addr + sizeof (int) - 1);
|
|
|
|
if ((pg & PG_PROT) == PG_KR)
|
|
Setpgmap(addr, (pg & ~PG_PROT) | PG_KW);
|
|
else if ((pg & PG_PROT) == PG_URKR)
|
|
Setpgmap(addr, (pg & ~PG_PROT) | PG_UW);
|
|
/* otherwise it is already writeable */
|
|
*addr = val; /* should be prepared to catch a fault here? */
|
|
/*
|
|
* Reset to page map to previous entry,
|
|
* but mark as modified
|
|
*/
|
|
vac_pageflush(addr);
|
|
if (btop(addr + sizeof (int) - 1) != btop(addr))
|
|
vac_pageflush(addr + sizeof (int) - 1);
|
|
Setpgmap(addr, pg | PG_M);
|
|
errno = 0;
|
|
return (0);
|
|
|
|
err:
|
|
errno = EFAULT;
|
|
return (-1);
|
|
}
|
|
|
|
scopy(from, to, count)
|
|
register char *from;
|
|
register char *to;
|
|
register int count;
|
|
{
|
|
register int val;
|
|
|
|
for (; count > 0; count--) {
|
|
if ((val = Peekc(from++)) == -1)
|
|
goto err;
|
|
if (pokec(to++, val) == -1)
|
|
goto err;
|
|
}
|
|
return (0);
|
|
err:
|
|
errno = EFAULT;
|
|
return (-1);
|
|
}
|
|
|
|
/*
|
|
* Setup a new context to run at routine using stack whose
|
|
* top (end) is at sp. Assumes that the current context
|
|
* is to be initialized for mainregs and new context is
|
|
* to be set up in debugregs.
|
|
*/
|
|
spawn(sp, routine)
|
|
char *sp;
|
|
func_t routine;
|
|
{
|
|
char *fp;
|
|
int res;
|
|
|
|
if (curregs != 0) {
|
|
printf("bad call to spawn\n");
|
|
exit(1);
|
|
}
|
|
if ((res = _setjmp(mainregs)) == 0) {
|
|
/*
|
|
* Setup top (null) window.
|
|
*/
|
|
sp -= WINDOWSIZE;
|
|
((struct rwindow *)sp)->rw_rtn = 0;
|
|
((struct rwindow *)sp)->rw_fp = 0;
|
|
/*
|
|
* Setup window for routine with return to exit.
|
|
*/
|
|
fp = sp;
|
|
sp -= WINDOWSIZE;
|
|
((struct rwindow *)sp)->rw_rtn = (int)exit - 8;
|
|
((struct rwindow *)sp)->rw_fp = (int)fp;
|
|
/*
|
|
* Setup new return window with routine return value.
|
|
*/
|
|
fp = sp;
|
|
sp -= WINDOWSIZE;
|
|
((struct rwindow *)sp)->rw_rtn = (int)routine - 8;
|
|
((struct rwindow *)sp)->rw_fp = (int)fp;
|
|
/* copy entire jump buffer to debugregs */
|
|
bcopy((caddr_t)mainregs, (caddr_t)debugregs, sizeof (jmp_buf));
|
|
debugregs[JB_SP] = (int)sp; /* set sp */
|
|
curregs = debugregs;
|
|
regsave.r_npc = (int)&fake_bpt;
|
|
_longjmp(debugregs, 1); /* jump to new context */
|
|
/*NOTREACHED*/
|
|
}
|
|
|
|
}
|
|
|
|
doswitch()
|
|
{
|
|
int res;
|
|
|
|
if ((res = _setjmp(curregs)) == 0) {
|
|
/*
|
|
* Switch curregs to other descriptor
|
|
*/
|
|
if (curregs == mainregs) {
|
|
curregs = debugregs;
|
|
} else /* curregs == debugregs */ {
|
|
curregs = mainregs;
|
|
}
|
|
_longjmp(curregs, 1);
|
|
/*NOTREACHED*/
|
|
}
|
|
/*
|
|
* else continue on in new context
|
|
*/
|
|
}
|
|
|
|
/*
|
|
* Main interpreter command loop.
|
|
*/
|
|
cmd()
|
|
{
|
|
int resetclk = 0;
|
|
u_char intreg;
|
|
int addr, t;
|
|
int i;
|
|
u_char interreg;
|
|
int s;
|
|
|
|
dorun = 0;
|
|
i = 0;
|
|
|
|
/*
|
|
* See if the sp says that we are already on the debugger stack
|
|
*/
|
|
reg = (struct regs *)®save;
|
|
addr = getsp();
|
|
if (addr > (int)etext && addr < (int)estack) {
|
|
printf("Already in debugger!\n");
|
|
return;
|
|
}
|
|
cache_on = getenablereg() & ENA_CACHE;
|
|
if (cache_on)
|
|
setdelay(mips_on);
|
|
else
|
|
setdelay(mips_off);
|
|
do {
|
|
doswitch();
|
|
if (dorun == 0)
|
|
printf("cmd: nothing to do\n");
|
|
} while (dorun == 0);
|
|
/* we don't need to splx since we are returning to the caller */
|
|
/* and will reset his/her state */
|
|
}
|
|
|
|
/*
|
|
* Return an available physical page number
|
|
*/
|
|
int
|
|
getapage()
|
|
{
|
|
register struct memlist *pmem;
|
|
register caddr_t addr;
|
|
|
|
/*
|
|
* Find the top of physical memory and take the top pages.
|
|
* We assume the physmem chunks are in ascending order and
|
|
* the last chunk is non-zero.
|
|
*/
|
|
pmem = *romp->v_availmemory;
|
|
|
|
for( ; pmem->next != (struct memlist *)NULL; pmem = pmem->next)
|
|
;
|
|
if (pmem->size < MMU_PAGESIZE) {
|
|
printf("panic: getapage: last physmem chunk too small\n");
|
|
_exit();
|
|
}
|
|
pmem->size -= MMU_PAGESIZE;
|
|
return (mmu_btop(pmem->address + pmem->size));
|
|
}
|
|
|
|
/*
|
|
* Call into the monitor (hopefully)
|
|
*/
|
|
montrap(funcptr)
|
|
int (*funcptr)();
|
|
{
|
|
our_tbr = gettbr();
|
|
if (use_kern_tbr) {
|
|
settbr(regsave.r_tbr);
|
|
} else
|
|
settbr(mon_tbr);
|
|
/*
|
|
* Must do the following because libprom calls
|
|
* montrap to "enter" and "exit_to" the prom.
|
|
* Since kadb has this montrap function we
|
|
* are calling through here. Thus, if an argument
|
|
* is passed do as libprom's routine does, else
|
|
* do as before.
|
|
*/
|
|
if (funcptr)
|
|
(*funcptr)();
|
|
else
|
|
(*romp->op_exit)();
|
|
settbr(our_tbr);
|
|
}
|
|
|
|
/*
|
|
* set delay constant for usec_delay()
|
|
* delay ~= (usecs * (Cpudelay * 2 + 3) + 8) / mips
|
|
* => Cpudelay = ceil((mips - 3) / 2)
|
|
*/
|
|
static
|
|
setdelay(mips)
|
|
int mips;
|
|
{
|
|
extern int Cpudelay;
|
|
|
|
Cpudelay = 0;
|
|
if (mips > 3)
|
|
Cpudelay = (mips - 2) >> 1;
|
|
}
|
|
|
|
/*
|
|
* Set the pme for address v using the software pte given.
|
|
* Setpgmap() automatically turns on the ``no cache'' bit
|
|
* for all mappings between DEBUGSTART and DEBUGEND.
|
|
*/
|
|
void
|
|
Setpgmap(v, pte)
|
|
caddr_t v;
|
|
int pte;
|
|
{
|
|
if (v >= (caddr_t)DEBUGSTART && v <= (caddr_t)DEBUGEND)
|
|
pte |= PG_NC;
|
|
setpgmap(v, pte);
|
|
}
|
|
|
|
traceback(sp)
|
|
caddr_t sp;
|
|
{
|
|
register u_int tospage;
|
|
register struct frame *fp;
|
|
static int done = 0;
|
|
|
|
#ifdef PARTIAL_ALIGN
|
|
if (partial_align? ((int)sp & 0x3): ((int)sp & 0x7)) {
|
|
#else
|
|
if ((int)sp & (STACK_ALIGN-1)) {
|
|
#endif PARTIAL_ALIGN
|
|
printf("traceback: misaligned sp = %x\n", sp);
|
|
return;
|
|
}
|
|
flush_windows();
|
|
tospage = (u_int)btoc(sp);
|
|
fp = (struct frame *)sp;
|
|
printf("Begin traceback... sp = %x\n", sp);
|
|
while (btoc((u_int)fp) == tospage) {
|
|
if (fp == fp->fr_savfp) {
|
|
printf("FP loop at %x", fp);
|
|
break;
|
|
}
|
|
printf("Called from %x, fp=%x, args=%x %x %x %x %x %x\n",
|
|
fp->fr_savpc, fp->fr_savfp,
|
|
fp->fr_arg[0], fp->fr_arg[1], fp->fr_arg[2],
|
|
fp->fr_arg[3], fp->fr_arg[4], fp->fr_arg[5]);
|
|
#ifdef notdef
|
|
printf("\tl0-l7: %x, %x, %x, %x, %x, %x, %x, %x\n",
|
|
fp->fr_local[0], fp->fr_local[1],
|
|
fp->fr_local[2], fp->fr_local[3],
|
|
fp->fr_local[4], fp->fr_local[5],
|
|
fp->fr_local[6], fp->fr_local[7]);
|
|
#endif
|
|
fp = fp->fr_savfp;
|
|
if (fp == 0)
|
|
break;
|
|
}
|
|
printf("End traceback...\n");
|
|
}
|
|
|
|
our_die_routine(retaddr)
|
|
register caddr_t retaddr;
|
|
{
|
|
#ifdef NOTYET
|
|
(*romp->op_chain)(0, 0, retaddr+8);
|
|
/* NOTREACHED */
|
|
#endif
|
|
;
|
|
}
|
|
|
|
struct memlist *
|
|
getmemlist(name, prop)
|
|
char *name, *prop;
|
|
{
|
|
int nodeid;
|
|
int i, j, k, chunks;
|
|
u_int propsize;
|
|
struct dev_reg *rp;
|
|
struct memlist *pmem;
|
|
static caddr_t bufp = (caddr_t)0;
|
|
static u_int left;
|
|
|
|
if (bufp == (caddr_t) 0) {
|
|
bufp = (caddr_t)(*romp->op2_alloc)((caddr_t)0, PAGESIZE);
|
|
left = PAGESIZE;
|
|
}
|
|
|
|
/*
|
|
* First find the right node.
|
|
*/
|
|
nodeid = search(NEXT(0), name, bufp);
|
|
if (nodeid == 0)
|
|
return ((struct memlist *)0);
|
|
|
|
/*
|
|
* Allocate memlist elements, one per dev_reg struct.
|
|
*/
|
|
propsize = (u_int)GETPROPLEN(nodeid, prop);
|
|
chunks = propsize / sizeof (struct dev_reg);
|
|
if (chunks == 0)
|
|
return ((struct memlist *)0);
|
|
if (left < (chunks * sizeof (struct memlist))) {
|
|
printf("memlists too big");
|
|
return ((struct memlist *)0);
|
|
}
|
|
pmem = (struct memlist *)bufp;
|
|
bzero((caddr_t)pmem, (u_int) chunks * sizeof (struct memlist));
|
|
left -= (u_int) chunks * sizeof (struct memlist);
|
|
bufp += (u_int) chunks * sizeof (struct memlist);
|
|
if (left < propsize) {
|
|
printf("buffer too small");
|
|
return ((struct memlist *)0);
|
|
}
|
|
/*
|
|
* Fill in memlist chunks.
|
|
*/
|
|
rp = (struct dev_reg *)bufp;
|
|
bzero((caddr_t)rp, propsize);
|
|
left -= propsize;
|
|
bufp += propsize;
|
|
|
|
(void)GETPROP(nodeid, prop, rp);
|
|
for (i = 0; i < chunks; ++i) {
|
|
if (rp[i].reg_bustype != OBMEM)
|
|
continue;
|
|
|
|
/*
|
|
* Insert the new element into the array of list nodes
|
|
* so that the array remains sorted in ascending order.
|
|
*/
|
|
|
|
/* Find the first element with a larger address */
|
|
for (j = 0; j < i; j++)
|
|
if (pmem[j].address > (u_int)rp[i].reg_addr)
|
|
break;
|
|
|
|
/* Copy following elements up to make room for the new one */
|
|
for (k = i; k > j; --k)
|
|
pmem[k] = pmem[k-1];
|
|
|
|
/* Insert the new element */
|
|
pmem[j].address = (u_int)rp[i].reg_addr;
|
|
pmem[j].size = rp[i].reg_size;
|
|
}
|
|
for (i = 1; i < chunks; ++i) {
|
|
if (pmem[i].address)
|
|
pmem[i-1].next = &pmem[i];
|
|
}
|
|
return (pmem);
|
|
}
|
|
|
|
search(node, name, buf)
|
|
int node;
|
|
char *name;
|
|
char *buf;
|
|
{
|
|
int id;
|
|
|
|
*buf = '\0';
|
|
(void)GETPROP(node, "name", buf);
|
|
if (strcmp(buf, name) == 0) {
|
|
return (node);
|
|
}
|
|
id = NEXT(node);
|
|
if (id && (id = search(id, name, buf)))
|
|
return (id);
|
|
id = CHILD(node);
|
|
if (id && (id = search(id, name, buf)))
|
|
return (id);
|
|
return (0);
|
|
}
|