#ifndef lint
static  char sccsid[] = "@(#)machdep.c 1.1 94/10/31";
#endif

/*
 * Copyright (c) 1986 by Sun Microsystems, Inc.
 */

#include <sys/param.h>
#include <sys/errno.h>
#include <sys/vmmac.h>
#include <sun4/buserr.h>
#include <sun4/enable.h>
#include <sun4/mmu.h>
#include <sun4/cpu.h>
#include <sun4/pte.h>
#include <sun4/scb.h>
#include <sun4/psl.h>
#include <sun4/trap.h>
#include <sun4/clock.h>
#include <sun4/intreg.h>
#include "reg.h"
#include "../../debug/debug.h"
#include "../../debug/debugger.h"
#include "asm_linkage.h"

extern int errno;
#define	DVMA	0xfff00000

int istrap = 0;
int scbsyncdone = 0;

int cpudelay = 0;	/* old macros: shift right amount to scale (n << 4) */
int Cpudelay = 11;	/* new macros: count down counter initial value */
int cpu;

int vac_linesize;		/* number of bytes in a cache line */
int vac_pglines;		/* number of cache lines in a page */
int vac_nlines;			/* number of cache lines in cache */

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 */

int clock_type;
static int clock_peek = 0;
unsigned long old_clock0_pte, old_counter_pte; /* save old ptes */
extern char estack[], etext[];

/*
 * Definitions for registers in jmp_buf
 */
#define	JB_PC	0
#define	JB_SP	1

static jmp_buf state;			/* used for temporary games */

/*
 * Startup code after relocation.
 */
startup()
{
	register int i;
	register int pg;
	u_char intreg;
	extern Setpgmap();
	extern char start[], end[], etext[];

#ifndef SAS
	set_clock_ptes();
#endif !SAS
	Setpgmap((caddr_t)INTREG_ADDR,
	    PG_V|PG_KW|PGT_OBIO|btop(OBIO_INTREG_ADDR));

	set_clock_type(); 
	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 += NBPG) {
		pg = Getpgmap(i);
		Setpgmap(i, (pg & ~PG_PROT) | PG_KR);
	}

	cpu = getmachinetype();
	switch (cpu) {
		case SUN4_ARCH:			/* proto 1 */
		case CPU_SUN4_260:
		case CPU_SUN4_110:
	        case CPU_SUN4_330:
		case CPU_SUN4_470:
			break;
		default:
			printf("%s: UNKNOWN MACHINE TYPE IN ID PROM\n", myname);
			printf("%s: DEFAULTING MACHINE TYPE TO SUN4_260\n", 
				myname);
			cpu = CPU_SUN4_260;
			break;
	}
	if (cpu != CPU_SUN4_110)
		cache_on = getenablereg() &  ENA_CACHE;
	if (cache_on || (cpu == CPU_SUN4_110)) {
		cpudelay = 1;
	} else {
		cpudelay = 4;
		vac_init();			/* invalidate entire cache */
	}
}

asm("	.align  4			");
asm("_tcode:				");
asm("	sethi	%hi(_trap), %l3		");
asm("	or	%l3, %lo(_trap), %l3	");
asm("	jmp	%l3			");
asm("	mov	%psr, %l0		");

scbsync()
{
	kadbscbsync();
	scbsyncdone = 1;
}

kadbscbsync()
{
	register struct scb *tbr;
	register int otbr_pg;
	extern trapvec tcodee;
	extern trapvec tcode;
	extern struct scb *gettbr();

	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) {
		/*
		 * Set things up so that we call the debugger.
		 * Use _setjmp()/_longjmp() with some adjustments
		 * to pull this non-local goto off correctly.
		 */
		scbstop = 0;
#ifdef notdef
		(void) _setjmp(state);
		state[JB_PC] = DVEC-8;			/* new pc value */
		state[JB_SP] = *(int *)state[JB_SP];	/* new sp value */
		_longjmp(state, 1);
		/*NOTREACHED*/
#endif
		asm("	t	126 /*TRAPBRKNO*/ -1	");
	}
}

/*
 * Set and/or clear the desired clock bits in the interrupt
 * register.  We have to be extremely careful that we do it
 * in such a manner that we don't get ourselves lost.
 */
set_clk_mode(on, off)
	u_char on, off;
{
	register u_char intreg, dummy;
	register int s;

/* XXX - need SUNRAY support */

#ifndef SAS
	/*
	 * make sure that we are only playing w/ 
	 * clock interrupt register bits
	 */
	on &= (IR_ENA_CLK14 | IR_ENA_CLK10);
	off &= (IR_ENA_CLK14 | IR_ENA_CLK10);

	s = splhi();
	/*
	 * Get a copy of current interrupt register,
	 * turning off any undesired bits (aka `off')
	 */
	intreg = *INTREG & ~(off | IR_ENA_INT);
	*INTREG &= ~IR_ENA_INT;

	/*
	 * Next we turns off the CLK10 and CLK14 bits to clear
	 * the flip-flops, then we disable clock interrupts.
	 * Now we can read the clock's interrupt register
	 * to clear any pending signals there.
	 */
	*INTREG &= ~(IR_ENA_CLK14 | IR_ENA_CLK10);
	if (clock_type == INTERSIL7170) {
	     CLOCK0->clk_cmd = (CLK_CMD_NORMAL & ~CLK_CMD_INTRENA);
	     dummy = CLOCK0->clk_intrreg;			/* clear clock */
	}
	else {
	     dummy = COUNTER->limit10;
	     dummy = COUNTER->limit14;
	}
		  
#ifdef lint
	dummy = dummy;
#endif

	/*
	 * Now we set all the desired bits
	 * in the interrupt register, then
	 * we turn the clock back on and
	 * finally we can enable all interrupts.
	 */
	*INTREG |= (intreg | on);			/* enable flip-flops */
	if (clock_type == INTERSIL7170) {
	     CLOCK0->clk_cmd = CLK_CMD_NORMAL;		/* enable clock intr */
	}
	*INTREG |= IR_ENA_INT;				/* enable interrupts */
	(void)splx(s);
#endif !SAS
}

/*
 * 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*/
	}

/*
	showregs(ondebug_stack ? "unexpected fault" : "unexpected exception",
	    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\n");
		_longjmp(abort_jmp, 1);
		/*NOTREACHED*/
	}

	if (clock_peek) { /* we were peeking for clock type */
		jmp_buf_ptr sav = nofault;

		nofault = NULL;
		_longjmp(sav, 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 call cmd %x %x %x %x\n",
		 trap,getsp(),trappc,trapnpc);
	 cmd();	/* error not resolved, ether debugger */
}
long trap_window[25];

showregs(str, locregs)
	register char *str;
	register struct allregs *locregs;
{
/* 	printf("g1-g7: %x, %x, %x, %x, %x, %x, %x\n",
	     rp->r_g1, rp->r_g2, rp->r_g3,
	     rp->r_g4, rp->r_g5, rp->r_g6, rp->r_g7);
printf("trap_window: wim=%x\n", trap_window[24]);
printf("o0-o7: %x, %x, %x, %x, %x, %x, %x, %x\n",
trap_window[0], trap_window[1], trap_window[2], trap_window[3],
trap_window[4], trap_window[5], trap_window[6], trap_window[7]);
printf("l0-l7: %x, %x, %x, %x, %x, %x, %x, %x\n",
trap_window[8], trap_window[9], trap_window[10], trap_window[11],
trap_window[12], trap_window[13], trap_window[14], trap_window[15]);
printf("i0-i7: %x, %x, %x, %x, %x, %x, %x, %x\n",
trap_window[16], trap_window[17], trap_window[18], trap_window[19],
trap_window[20], trap_window[21], trap_window[22], trap_window[23]);

#ifndef SAS
	DELAY(2000000);
#endif
	(void) splx(s);
 */
}

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, i;
	
	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;
	}

	if ((cpu != CPU_SUN4_110) && !(pg&PG_NC)){
	        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
	 */
	if ((cpu != CPU_SUN4_110) && !(pg&PG_NC)){
	        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;

	if (curregs != 0) {
		printf("bad call to spawn\n");
		exit(1);
	}
	if (!_setjmp(mainregs)) {
		/*
		 * 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()
{

	if (!_setjmp(curregs)) {
		/*
		 * 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;

	/*
	 * See if the sp says that we are already on the debugger stack
	 */
	reg = (struct regs *)&regsave;
	addr = getsp();
	if (addr > (int)etext && addr < (int)estack) {
		printf("Already in debugger!\n");
		return;
	}
	if (cpu == CPU_SUN4_260)
		cache_on = getenablereg() &  ENA_CACHE;
	if (cache_on || (cpu == CPU_SUN4_110))
		cpudelay = 1;
	else
		cpudelay = 4;
	/*
	 * Set up for monitor's input routines to work.  This
	 * requires that we call the monitor's nmi handler on level14
	 * clock interrupts and that interrupts and level14 clock
	 * interrupt are enabled.
	 */
	if ((*INTREG & (IR_ENA_INT | IR_ENA_CLK14)) !=
	    (IR_ENA_INT | IR_ENA_CLK14)) {
		resetclk = 1;
		interreg = *INTREG;
		set_clk_mode(IR_ENA_CLK14, 0);
	}
	s = spl13();
	do {
		doswitch();
		if (dorun == 0)
			printf("cmd: nothing to do\n");
	} while (dorun == 0);
        if (resetclk) {
		set_clk_mode(0, IR_ENA_CLK14);
		*INTREG = interreg;
	}
	/* we don't need to splx since we are returning to the caller */
	/* and will reset his/her state */
}

void
vac_init()
{
	if (cpu == CPU_SUN4_470) {
		vac_linesize = VAC_LINESIZE_SUNRAY;
	} else {
		vac_linesize = VAC_LINESIZE_SUNRISE;
	}
	vac_nlines = VAC_SIZE / vac_linesize;
	vac_pglines = PAGESIZE / vac_linesize;
	vac_tagsinit();
}



set_clock_type() { 

	clock_peek = 1;
	if (Peekc((char *)&CLOCK0->clk_hsec) != -1) {
		clock_type = INTERSIL7170;	/* put back other  old pte */
		Setpgmap((caddr_t)COUNTER_ADDR, old_counter_pte);
	}
	else {
		clock_type = MOSTEK48T02;	/* put back other old pte */
		Setpgmap((caddr_t)CLOCK0_ADDR, old_clock0_pte);
	}
	clock_peek = 0;
}

/*
 * Return an available physical page number
 */
int
getapage()
{
	extern int lastpg;

	return (--lastpg);
}
 
#ifndef SAS
set_clock_ptes() {

     caddr_t i;
     int segm;

     /* copy old ptes, put the unused page pte back to what it was */

     old_clock0_pte = Getpgmap((caddr_t)CLOCK0_ADDR);
     old_counter_pte = Getpgmap((caddr_t)COUNTER_ADDR);
	
     i = (caddr_t) CLOCK0_ADDR;
     
     /* KLUDGE
	The last seg may not be mapped in by eporom. Only the sun4s
	with intel ethernet chip have the last smeg mapped in.
	For example, sun4_260 has the last smg mapped in while
	sun4_330 does not (because 4_330 uses LANCE ethernet chip).
	*/
     if ((segm = Getsegmap(i)) == (npmgrps-1)) {
	  segm = --lastpm;
	  Setsegmap((caddr_t)CLOCK0_ADDR, segm);
     }

     Setpgmap((caddr_t)CLOCK0_ADDR,			/* intersil clock */
	      PG_V|PG_KW|PGT_OBIO|btop(OBIO_CLOCK_ADDR));
     
     Setpgmap((caddr_t)COUNTER_ADDR,			/* mostek clock */
	      PG_V|PG_KW|PGT_OBIO|btop(OBIO_COUNTER_ADDR));
   }
#endif

/*
 * Call into the monitor (hopefully)
 */
montrap()
{
	(*romp->v_exit_to_mon)();
}
