/* $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 */

#ident	"$Header: machdep.c 1.60 90/07/30 $"

/*
 * machdep.c
 *	Machine dependent routines.
 *
 * i386 version.
 */

/* $Log:	machdep.c,v $
 */

#include "sys/signal.h"
#include "sys/types.h"
#include "sys/devsw.h"
#include "sys/param.h"
#include "sys/debug.h"
#include "sys/mutex.h"
#include "sys/systm.h"
#include "sys/user.h"
#include "sys/map.h"
#include "sys/vm.h"
#include "sys/timer.h"
#include "sys/proc.h"
#include "sys/buf.h"
#include "sys/boot.h"
#include "sys/conf.h"
#include "sys/vfs.h"
#include "sys/vnode.h"
#include "sys/ufsmount.h"
#include "sys/file.h"
#include "sys/callo.h"
#include "sys/cmap.h"
#include "sys/acct.h"

#include "sys/cfg.h"
#include "sys/engine.h"
#include "sys/clkarb.h"
#include "sys/clock.h"
#include "sys/slic.h"
#include "sys/slicreg.h"
#include "sys/bic.h"
#include "sys/cmc.h"
#include "sys/bdp.h"
#include "sys/SGSproc.h"
#include "sys/SGSmem.h"

#include "sys/inline.h"
#include "sys/reg.h"
#include "sys/pte.h"
#include "sys/psl.h"
#include "sys/gate.h"
#include "sys/intctl.h"
#include "sys/plocal.h"
#include "sys/mmu.h"
#include "sys/mftpr.h"
#include "sys/hwparam.h"
#include "sys/cmn_err.h"
#include "sys/errno.h"

#ifdef	MODELD
#include "sys/scan.h"
#endif	/* MODELD */

#ifdef DEBUG
extern int sig_debug;	/* sendsig() and sigclean() debug */
#endif
extern	short	upyet;		/* Set when 1st processor is initialized */
extern	bool_t	light_show;	/* twiddling the lights?? */

extern	struct	ctlr_desc *slic_to_config[];

/*
 * setregs()
 *	Machine-dependent register-state setup on exec().
 */

setregs(entry)
	ulong	entry;
{
	/*
	 * Clear floating context in process image.
	 * We don't clear general registers since boot-time uses registers
	 * to pass parameters to /etc/init.
	 */

	disable_fpu();
	u.u_fpusave.fpu_control = FPU_CONTROL_INIT;
	u.u_fpusave.fpu_status = FPU_STATUS_INIT;
	u.u_fpusave.fpu_tag = FPU_TAG_INIT;

	if (u.u_procp->p_fpa)
		disable_fpa();

	/*
	 * Set up entry EIP and deal with "close-on-exec" files.
	 * Set up NULL EBP for debuggers.
	 * Also insure direction-flag is zero (languages assume this).
	 */

	u.u_ar0[EIP] = entry;
	u.u_ar0[EBP] = 0;
	u.u_ar0[FLAGS] &= ~FLAGS_DF;
}

/*
 * sendsig()
 *	Send an interrupt to process (ie, post a signal).
 *
 * Stack is set up to allow sigcode in sigvec library routine to call
 * routine, followed by svc to sigcleanup routine below.  After
 * sigcleanup() resets the signal mask and the stack, it returns to user
 * via the normal system call return using the restored stack.
 * More than usual context must be saved as the users handler may
 * distroy temp registers.
 */
struct sig_frame {
	void			(*sf_ret)();	/* libc return code */
	int			sf_signo;	/* signal number */
	int			sf_arg1;	/* optional arg */
	int			sf_arg2;	/* optional arg */
	int			sf_eax;		/* saved eax */
	int			sf_ecx;		/* saved ecx */
	int			sf_edx;		/* saved edx */
	int			sf_eip;		/* saved eip */
	int			sf_esp;		/* saved esp */
	int			sf_flags;	/* saved flags */
	int			sf_mask;	/* saved sigmask */
	int			sf_sflags;	/* saved signal flags */
	struct	sig_frame	*sf_sigframe;	/* saved sigframe */
};

int ups_state = 0;	/* Current state of UPS */

void
sendsig(hand, sig, old_mask)
	void		(*hand)();
	register int	sig;
	sigset_t	old_mask;
{
	register proc_t		*p;
	struct	sig_frame	local;
	struct	sig_frame	*sig_frame;
	struct	sig_frame	*sp;

	if (!u.u_altstack.sas_onstack && (u.u_sa[sig].sa_flags & SA_ALTSTACK)) {
		sp = (struct sig_frame *)u.u_altstack.sas_sp;
		u.u_altstack.sas_onstack = u.u_ar0[ESP];
#ifdef DEBUG
		if (sig_debug)
			printf("sendsig: alt stack at 0x%x\n",sp);
#endif
	} else {
		sp = (struct sig_frame *)u.u_ar0[ESP];
	}
	p = u.u_procp;
#ifdef DEBUG
	if (sig_debug)
		printf("sendsig(0x%x[%d],%d) sp=0x%x\n",p,p-proc,sig,sp);
#endif
	sig_frame = &sp[-1];
	u.u_sigframe = sig_frame;
	/*
	 * grow the stack if it is normal.
	 */
	if (!u.u_altstack.sas_onstack)
		(void) grow((unsigned)sig_frame);
	/*
	 * build a local frame.
	 */
	local.sf_ret = u.u_sigreturn;
	local.sf_eax = u.u_ar0[EAX];
	local.sf_eip = u.u_ar0[EIP];
	local.sf_flags = u.u_ar0[FLAGS];
	local.sf_esp = u.u_ar0[ESP];
	local.sf_ecx = u.u_ar0[ECX];
	local.sf_edx = u.u_ar0[EDX];
	local.sf_signo = sig;
	local.sf_arg1 = (sig == SIGPWR) ? ups_state : u.u_code;
	local.sf_arg2 = u.u_segvcode;
	local.sf_sigframe = u.u_sigframe;
	local.sf_sflags = u.u_sa[sig].sa_flags;

	/*
	 * Special case: user has done a sigpause.  Here the
	 * current mask is not of interest, but rather the
	 * mask from before the sigpause is what we want restored
	 * after the signal processing is completed.
	 */
	if (u.u_oldmask) {
		local.sf_mask = u.u_oldmask;
		local.sf_sflags |= SA_SUSP;
		SIGM_CLR(u.u_oldmask);
	} else
		local.sf_mask = old_mask;
#ifdef DEBUG
	if (sig_debug)
		printf("sendsig: u.u_code=0x%x u.u_segvcode=0x%x u.u_error=%d old eip:0x%x\n",u.u_code,u.u_segvcode,u.u_error,u.u_ar0[EIP]);
#endif
	/*
	 * copy it to user space.
	 */
	if (copyout((char *)&local, (char *)sig_frame,
		sizeof(struct sig_frame)) == -1){
#ifdef DEBUG
		if (sig_debug)
			printf("sendsig: copout failed\n");
#endif
		/*
		 * force an illegal instruction core dump.
		 */
		u.u_sa[SIGILL].sa_handler = SIG_DFL;
		SIGM_DELSET(p->p_sigignore,sigmask(SIGILL));
		SIGM_DELSET(p->p_sigcatch,sigmask(SIGILL));
		SIGM_DELSET(p->p_sigmask,sigmask(SIGILL));
		psignal(p, SIGILL);
		return;
	}
	/*
	 * Modify the users current call frame to go back to
	 * the signal handler.
	 */
	u.u_ar0[EIP] = (int)hand;
	u.u_ar0[ESP] = (int)sig_frame;
	/*
	 * for use by sigcontext.
	 */
	u.u_sigframe = sig_frame;
#ifdef DEBUG
	if (sig_debug)
		printf("sendsig: pc set to 0x%x stack set to 0x%x\n",hand,sig_frame);
#endif
}

/*
 * sigcleanup()
 *	Clean up state after a signal has been handled.
 *
 * Reset signal mask and stack state from context left by sendsig (above).
 *
 * This is called as a normal syscall; thus must arrange to restore
 * EAX and ECX as if they were return values from the syscall.

 */

sigcleanup()
{
	struct	sig_frame	local;
	register proc_t		*p;
	struct	sig_frame	*sp;

	/*
	 * sf_ret has been poped.
	 */
	sp = (struct sig_frame *)(u.u_ar0[ESP]-4);

	p = u.u_procp;

#ifdef DEBUG
	if (sig_debug)
		printf("sigcleanup proc:%d pid:%d sp=0x%x\n",p-proc,p->p_pid,sp);
#endif
	/*
	 * read in the frame.
	 */
	if (copyin((caddr_t)sp, (caddr_t)&local, sizeof(struct sig_frame))) {
		/*
		 * force an illegal instruction core dump.
		 */
		u.u_sa[SIGILL].sa_handler = SIG_DFL;
		SIGM_DELSET(p->p_sigignore, sigmask(SIGILL));
		SIGM_DELSET(p->p_sigcatch, sigmask(SIGILL));
		SIGM_DELSET(p->p_sigmask, sigmask(SIGILL));
		psignal(p, SIGILL);
		return;
	}
#ifdef DEBUG
	if (sig_debug)
		printf("sigcleanup: local.signo = %d new pc:0x%x\n",local.sf_signo,local.sf_eip);
#endif
	/*
	 * CHOLD flag is not set for old style signal().
	 */
	if (!(local.sf_sflags & SA_NOHOLD)) {
		/*
		 * release the signal.
		 * restore mask for sigaction() set.
		 * or undo the current signal for sigset().
		 */
		(void)p_lock(&p->p_state, SPLHI);
		if (local.sf_sflags & SA_SIGSET)
			SIGM_DELSET(p->p_sigmask, sigmask(local.sf_signo));
		else
			p->p_sigmask = local.sf_mask;	
		/*
		 * don't trust user's copy.
		 */
		SIGM_DELSET(p->p_sigmask,CANTMASK);
		v_lock(&p->p_state, SPL0);
	} 

	if (local.sf_sflags & SA_SUSP) {
		/*
		 * Always retore oldmask for routines called while
		 * stopped with sigpause() or sigsuspend().
		 */
		(void)p_lock(&p->p_state, SPLHI);
		p->p_sigmask = local.sf_mask;	
		/*
		 * don't trust user's copy.
		 */
		SIGM_DELSET(p->p_sigmask,CANTMASK);
		v_lock(&p->p_state, SPL0);
	}

	/*
	 * Validate flag register.
	 */

	/* set FLAGS_IOPL and FLAGS_VM in user flags to 0 */
	local.sf_flags &= ~(FLAGS_IOPL | FLAGS_VM);

	/* set interrupt enable flag */
	local.sf_flags |= FLAGS_IF;

	/*
	 * replace current user registers with the old values.
	 * set "syscall return values" to correctly fix r0 and r1
	 */

	u.u_ar0[EAX] = local.sf_eax;
	u.u_ar0[EIP] = local.sf_eip;
	u.u_ar0[FLAGS] = local.sf_flags;
	u.u_ar0[ESP] = local.sf_esp;

	/*
	 * if last call on alternative stack then clear
	 * on_stack flag.
	 */
	if (u.u_ar0[ESP] == u.u_altstack.sas_onstack)
		u.u_altstack.sas_onstack = (int)NULL;
	/*
	 * restore registers for both syscall() and
	 * trap() return paths.
	 */
	u.u_r.r_val1 = local.sf_eax;
	u.u_sigframe = local.sf_sigframe;
	u.u_ar0[EDX] = local.sf_edx;
	u.u_ar0[ECX] = local.sf_ecx;
	u.u_r.r_val2 = local.sf_ecx;
}


sigcontext()
{
	struct a {
		int	request;
		caddr_t	addr;
	} *uap = (struct a *)u.u_ap;
	int	data;
	caddr_t	addr;

	if (u.u_sigframe == 0) {
		u.u_error = EINVAL;
		return;
	}
	switch( uap->request) {
	case SF_SIGNO:
		addr = (caddr_t)(&u.u_sigframe->sf_signo);
		break;
	case SF_TRAPCODE:
	case SF_SYSCODE:
	case SF_CODE:
		addr = (caddr_t)(&u.u_sigframe->sf_arg1);
		break;
	case SF_CODE_DATA:
	case SF_SEGVCODE:
		addr = (caddr_t)(&u.u_sigframe->sf_arg2);
		break;
	case SF_PC:
		addr = (caddr_t)(&u.u_sigframe->sf_eip);
		break;
	case SF_SP:
		addr = (caddr_t)(&u.u_sigframe->sf_esp);
		break;
	case SF_R0:
		addr = (caddr_t)(&u.u_sigframe->sf_eax);
		break;
	case SF_SET_PC:
		data = (int)uap->addr;
		addr = (caddr_t)(&u.u_sigframe->sf_eip);
		(void) suword(addr, data);
		return;
	default:
		u.u_error = EINVAL;
		return;
	}
#ifdef DEBUG
	if (sig_debug) {
		printf("request addr is 0x%x sigframe is 0x%x\n",
						addr, u.u_sigframe);
	}
#endif
	if (copyin(addr, (caddr_t)&data, sizeof(data))) {
		u.u_error = EFAULT;
		return;
	} 
#ifdef DEBUG
	if (sig_debug) {
		printf("data is 0x%x uses address is 0x%x\n",
						data, uap->addr);
	}
#endif
	/*
	 * u_code is encoded thus
	 * for a systemcall | tab  | zero | index | zero |
	 * for a trap       | zero | trap | zero  | zero |
	 * (trap is always 0x20|trap type)
	 * which can be modified by signals to
	 *                  |      |      |       | code |
	 */
	switch( uap->request) {
	case SF_TRAPCODE:
		if (data & 0x00ff0000)
			data = (data&0x00ff0000)>>16;
		else
			data = -1;
		(void) suword(uap->addr, data);
		break;
	case SF_SYSCODE:
		if (data & 0x00ff0000)
			data = -1;
		else
			data = data>>8;
		(void) suword(uap->addr, data);
		break;
	case SF_CODE:
		data &= 0xff;		/* only 16 bits */
		if (!data)
			data = -1;
		(void) suword(uap->addr, data);
		break;
	default:
		(void) suword(uap->addr, data);
		break;
	}
}


/*
 * start_engine()
 *	Start another processor by "unpausing" it.
 *
 * Called by tmp_ctl TMP_ONLINE command.
 *
 * The semaphore tmp_onoff is assumed to be held by the caller.
 * This semaphore guarantees that only one on/off line transaction
 * occurs at a time.  No real need to single-thread these on SGS,
 * but doesn't hurt and provides some basic sanity (who knows, maybe
 * there is some hidden reason! ;-)
 */

start_engine(eng)
	register struct engine *eng;
{
	spl_t	s;

#ifdef	notyet				/* SGS VLSI doesn't support flush yet */
	unchar	bic_slic;
	unchar	chan_ctl;

	/*
	 * Re-enable the appropriate BIC channel (this is left disabled
	 * by an offline).  This is a NOP on early rev BIC's.
	 *
	 * Note that only one SLIC on the processor board talks to the BIC.
	 */

	bic_slic = BIC_SLIC(eng->e_slicaddr,
			slic_to_config[eng->e_slicaddr]->cd_flags);
	chan_ctl = (eng->e_slicaddr & 0x01) ? BIC_CDIAGCTL1 : BIC_CDIAGCTL0;

	wrSubslave(bic_slic, PROC_BIC, chan_ctl, 
		(unchar) (rdSubslave(bic_slic, PROC_BIC, chan_ctl) & ~BICCDC_DISABLE));
#endif	/* notyet */ /* SGS VLSI doesn't support flush yet */

	/*
	 * Un-hold the processor, turn on the LED, and *don't* reset.
	 * Also enable NMI's: it's ok for 1st online (don't expect any NMI
	 * sources) and subsequent online's need NMI's enabled here since they
	 * don't execute localinit() to enable NMI's.  This gives small risk
	 * of strange crash if NMI is asserted on 1st online (since processor
	 * is an 8086 at this time); if a problem, need to keep state in
	 * e_flags whether the processor has ever been online'd before, and
	 * initialize PROC_CTL differently here 1st time vs subsequent times.
	 */

	if (light_show && fp_lights) {
		s = splhi();
		FP_LIGHTON(eng - engine);
		splx(s);
	}

#ifdef	MODELD
	if (eng->e_flags&E_SGS2)
		unhold_proc((int)eng->e_slicaddr);
	else
#endif	/* MODELD */
	wrslave(eng->e_slicaddr, PROC_CTL,
		PROC_CTL_NO_SSTEP | PROC_CTL_NO_HOLD | PROC_CTL_NO_RESET);
}

/*
 * halt_engine()
 *	Halt processor via pause and reset.
 *
 * Turn off processor light.
 * Done implicitly via reset on B8K.
 * If fp_lights then done explicitly.
 *
 * Called by tmp_ctl with TMP_OFFLINE command to shutdown a processor.
 */

#ifdef	notyet				/* SGS VLSI doesn't support flush yet */
static	struct	proc_cmcs {
	unchar	pc_subaddr;		/* sub-slave address of CMC */
	uint	pc_diag_flag;		/* flags -- all zero if CMC in use */
}	proc_cmcs[] = {
	{ PROC_CMC_0, CFG_SP_CMC0|CFG_SP_DRAM_0|CFG_SP_TRAM_0|CFG_SP_SRAM_0 },
	{ PROC_CMC_1, CFG_SP_CMC1|CFG_SP_DRAM_1|CFG_SP_TRAM_1|CFG_SP_SRAM_1 },
};
#endif	/* notyet */ /* SGS VLSI doesn't support flush yet */

halt_engine(eng)
	register struct engine *eng;
{
	spl_t	s;


#ifdef	notyet				/* SGS VLSI doesn't support flush yet */
	register struct ctlr_desc *cd = slic_to_config[eng->e_slicaddr];
	register struct proc_cmcs *pc;
	register int	i;
	unchar		bic_slic;
	unchar		chan_ctl;
	unchar		cmc_mode;
#endif	/* notyet */ /* SGS VLSI doesn't support flush yet */
	unchar		slicid = eng->e_slicaddr;

	/*
	 * HOLD the processor, but don't reset it (also turn OFF led).
	 * Wait for processor to be HELD.
	 */

#ifdef	MODELD
	if (eng->e_flags&E_SGS2) {
		/*
		 * No way for a remote processor to turn off the LED.
		 */
		hold_proc(slicid);
	} else {
		wrslave(slicid, PROC_CTL,
				PROC_CTL_LED_OFF | PROC_CTL_NO_NMI |
				PROC_CTL_NO_SSTEP | PROC_CTL_NO_RESET);
		while (rdslave(slicid, PROC_STAT) & PROC_STAT_NO_HOLDA)
			continue;
	}
#else	/* !MODELD */
	wrslave(slicid, PROC_CTL,
			PROC_CTL_LED_OFF | PROC_CTL_NO_NMI |
			PROC_CTL_NO_SSTEP | PROC_CTL_NO_RESET);

	while (rdslave(slicid, PROC_STAT) & PROC_STAT_NO_HOLDA)
		continue;
#endif	/* MODELD */

#ifdef	notyet				/* SGS VLSI doesn't support flush yet */
	/*
	 * NOTE: the flush algorithm is probably WRONG!  Verify/fix
	 * when VLSI does support the flush function.
	 */
	/*
	 * Flush the processor's cache.
	 *
	 * For each cache set, if it passed all power-up diagnostics then
	 * tell the CMC it's a "master", flush it, make it a "slave" again.
	 */

	for (pc = proc_cmcs, i = 0; i < cd->cd_p_nsets; i++, pc++) {

		/*
		 * If any diagnostic flag is on, this cache set wasn't in use.
		 */

		if (eng->e_diag_flag & pc->pc_diag_flag)
			continue;

		/*
		 * Make the CMC the "master" and start the flush.
		 */

		cmc_mode = rdSubslave(slicid, pc->pc_subaddr, CMC_MODE);
		wrSubslave(slicid, pc->pc_subaddr, CMC_MODE,
				cmc_mode & ~(CMCM_SLAVE | CMCM_DISA_FLUSH));

		/*
		 * Wait for flush to finish.
		 */

		while (rdSubslave(slicid, pc->pc_subaddr, CMC_STATUS) & CMCS_FLUSH)
			continue;

		/*
		 * Make the CMC a "slave" again.
		 */

		wrSubslave(slicid, pc->pc_subaddr, CMC_MODE, cmc_mode);
	}

	/*
	 * Isolate the processor from the bus by turning off the appropriate
	 * BIC channel.  This is a NOP on early rev BIC's.
	 *
	 * Note that only one SLIC on the processor board talks to the BIC.
	 */

	bic_slic = BIC_SLIC(slicid, cd->cd_flags);
	chan_ctl = (slicid & 0x01) ? BIC_CDIAGCTL1 : BIC_CDIAGCTL0;

	wrSubslave(bic_slic, PROC_BIC, chan_ctl, 
		(unchar) (rdSubslave(bic_slic, PROC_BIC, chan_ctl) | BICCDC_DISABLE));
#endif	/* notyet */ /* SGS VLSI doesn't support flush yet */

	if (light_show && fp_lights) {
		s = splhi();
		FP_LIGHTOFF(eng - engine);
		splx(s);
	}
}

#define	TOP_KSTK	((int *)(VA_UAREA - 1))
#define	BOT_KSTK	((int *)(VA_UBLOCK ))
#define	POPECX		0x59			/* pops a single argument */
#define	ADDLESP		0xc483			/* addl $xxx, %esp */
/*
 * Use these variables to bound stack when !upyet
 *	they're initialized in printstack.
 */
int *bottom_bp, *top_bp;

#ifndef KLINT
/*VARARGS*/
printstack(dummy)
{
	int	*i =  &dummy;

	printf("Cpu registers:\n");
	printf(" eax=%x ebx=%x ecx=%x edx=%x\n", i[7], i[4], i[6], i[5]);
	printf(" esi=%x edi=%x ebp=%x esp=%x\n", i[1], i[0], i[15], &i[18]);
	/*
	 * for bounding stack when !upyet
	 */
	bottom_bp = (int *)(&dummy)[-2];
	top_bp = (int *)((((int)bottom_bp + UPAGES*NBPG) & ~CLOFSET) - sizeof(struct user));
	hexdumpstack(dummy);
	printf("Stack @ 0x%x\n",
		(((int*)VA_PT)[btop(VA_UBLOCK+KSOFF)] & PG_PFNUM)
		+ ((int)&dummy - VA_UBLOCK));
	printstacktrace((int *) ((&dummy)[-2]));
}

/*ARGSUSED*/
hexdumpstack(dummy)
{
	int	*a = (int *)(((&dummy)[-2]) &~ 0xf);
	char	s1[10], s2[10], s3[10], s4[10];

	printf("Hex dump of panic stack:\n");
	/*
	 * stop dump when address a is below stack top, and
	 * while not on a page boundary.
	 */
	while (a < TOP_KSTK) {
		if (!upyet && (a >= top_bp))
			break;
		hexstr(s1, a[0]);
		hexstr(s2, a[1]);
		hexstr(s3, a[2]);
		hexstr(s4, a[3]);
		printf("%x: %s%x %s%x %s%x %s%x\n", a,
			s1, a[0], s2, a[1],
			s3, a[2], s4, a[3]);
		/*
		 * If we have wrapped around the top, stop now
		 */
		if (a + 4 < a)
			break;
		a += 4;
	}
}
#endif /*KLINT*/

hexstr(s, n)
	char	*s;
	int	n;
{
	int	i = 0;
	uint	mask = 0xf0000000;

	bcopy("       ", s, 8);
	while (mask && (n & mask) == 0) {
		i++;
		mask >>= 4;
	}
	s[i] = '\0';
}

#ifndef KLINT
/*ARGSUSED*/
printstacktrace(bp)
	register int *bp;			/* current frame pointer */
{
	int	*newbp = (int *)(bp[0]);	/* next frame pointer */
	int	*oldpc = (int *)(bp[1]);	/* pc this frame returns to */
	int	*oldap = &bp[2];		/* point to first argument */
	int	 nargs;				/* number of arguments passed */
	extern	int etext;

	if ((oldpc < &etext) && ((*oldpc & 0xffff) == ADDLESP))
		nargs = ((*oldpc>>16) & 0xff) / 4;
	else if ((oldpc < &etext) && ((*oldpc & 0xff) == POPECX))
		nargs = 1;
	else
		nargs = 5;	/* default for OS */
		
	printf("@ 0x%x call(", oldpc);
	while (nargs) {
		printf("0x%x", *oldap++);
		if (--nargs)
			printf(", ");
	}
	printf(")\n");


	if (!upyet && (newbp < top_bp) && (newbp > bp) && (newbp > bottom_bp))
		printstacktrace(newbp);
	else if ((newbp < TOP_KSTK) && (newbp > bp) && (newbp > BOT_KSTK))
		printstacktrace(newbp);
}
#endif /*KLINT*/

/*
 * pause_self()
 *	Stop processor via pause self.
 *
 * Typically called from from NMI handler when a panic is in progress.
 * There is no return.
 *
 * Caller must call with SPLHI.
 */

pause_self()
{
#ifdef lint
	extern unsigned READ_PTROOT();
#endif /*lint*/

	/*
	 * Save all registers.  Save "panic" sp in l.panicsp.
	 * Also save page-table in use by this processor when the
	 * system went down.
	 */

	l.panicsp = (caddr_t) saveregs();
	l.panic_pt = READ_PTROOT();
	if (l.usingfpu)
		save_fpu();
#ifdef FPA
	/* if have fpa, not idle and using it */
#ifdef	EMULA_FPA
	if (l.fpa && !l.noproc && u.u_procp->p_fpa == FPA_HW)
#else	/* !EMULA_FPA */
	if (l.fpa && !l.noproc && u.u_procp->p_fpa)
#endif	/* EMULA_FPA */
		save_fpa();
#endif	/* FPA  */

	/*
	 * Turn off light show if enabled.
	 */

	if (light_show) {
		if (fp_lights)
			FP_LIGHTOFF(l.me);
		*(int *) VA_LED = 0;
	}

	/*
	 * Pause myself!
	 */

	l.eng->e_flags |= E_PAUSED;

	/*
	 * HOLD the processor, but don't reset it (also turn OFF led).
	 * Wait for processor to be HELD.  The while() will not actually
	 * ever finish. Call lwrslave and lrdslave so we don't go away
	 * with slic_mutex held.
	 *
	 * In this case, *don't* isolate the processor from the bus, since
	 * may want to take a dump.  Since pausing self, not clear we could
	 * isolate self from bus.
	 */
#ifdef	DEBUG
	/*
	 * Flush cache to insure consistent memory image for a dump.
	 */
	flush_cache(); 
#endif	/* DEBUG */

#ifdef	MODELD
	if (l.eng->e_flags&E_SGS2) {
		hold_proc(l.eng->e_slicaddr);
	} else {
		lwrslave(l.eng->e_slicaddr, PROC_CTL,
				PROC_CTL_LED_OFF | PROC_CTL_NO_NMI |
				PROC_CTL_NO_SSTEP | PROC_CTL_NO_RESET);
		while (lrdslave(l.eng->e_slicaddr, PROC_STAT) & PROC_STAT_NO_HOLDA)
			continue;
	}
#else	/* !MODELD */
	lwrslave(l.eng->e_slicaddr, PROC_CTL,
			PROC_CTL_LED_OFF | PROC_CTL_NO_NMI |
			PROC_CTL_NO_SSTEP | PROC_CTL_NO_RESET);

	while (lrdslave(l.eng->e_slicaddr, PROC_STAT) & PROC_STAT_NO_HOLDA)
		continue;
#endif	/* MODELD */
}

/*
 * donmi()
 *	Report reason for NMI (decode errors, etc) and stop the machine.
 */

donmi()
{
#ifdef	MODELD
	register int sgs2 = l.eng->e_flags&E_SGS2;
#endif	/* MODELD */
	unchar 	slicid = va_slic->sl_procid;
	unchar 	regval;
	unchar 	bic_slic;
	unchar 	accerr_reg;

	/*
	 * Currently, NMIs are presented to the processor in these situations:
	 *
	 *	- SLIC NMI (sent by software)
	 *	- Access error from access to reserved processor LOCAL address
	 *	- Access error:
	 *		- Bus Timeout
	 *		- ECC Uncorrectable error
	 *		- Processor fatal error (SGS only)
	 *	- Cache Parity error (these hold the processor & freeze the bus)
	 *
	 * In all cases, the processor pauses itself.
	 */

	/*
	 * Disable processor acceptence of NMI's. Other interrupts
	 * turned off by caller (currently only trap()), ie donmi
	 * must be called at splhi and cli.
	 */
#ifdef	MODELD
	if (sgs2)
		disable_snmi(slicid);
	else
#endif	/* MODELD */
	wrslave(slicid, PROC_CTL, PROC_CTL_NO_NMI | PROC_CTL_NO_SSTEP | 
				  PROC_CTL_NO_HOLD | PROC_CTL_NO_RESET);

#ifdef	MODELD
	if (sgs2) {
		register int flag = accerr_flag(slicid);

		/*
		 * First check for SLIC NMI or local reserved location access.
		 */
		if (va_slic->sl_nmiint == PAUSESELF)
			pause_self();

		if (va_slic->sl_nmiint == 0) {
			cmn_err(CE_PANIC, "RESERVED LOCATION ACCESS ERROR");
			/*
			 *+ The processor tried to access a reserved address that
			 *+ caused a Non-Maskable Interrupt to be generated.
			 */
			/*NOTREACHED*/
		}

		/*
		 * Not a board-local source (cache parity errors freeze the bus,
		 * so we're not executing if that happened).  Thus, it should be
		 * a bus access error, stored in per-channel BIC access error register.
		 *
		 * Assumes BIC access error register has identical bit assignments
		 * as 1st gen (032) processor board access error register, since
		 * access_error() is coded this way.
		 *
		 * Since system needs to be "up" enough to have filled out
		 * slic_to_config[]; just panic if not yet up far enough.
		 */

		if (slic_to_config[slicid] == NULL) {
			cmn_err(CE_PANIC, "ACCESS ERROR");
			/*
			 *+ The processor has discovered a bus access error
			 *+ that occurred early in the boot sequence.
			 *+ The information needed to map the error
			 *+ information to the source of the access error 
			 *+ hasn't been initialized yet.
			 */
			/*NOTREACHED*/
		}

		if (anyaccerr(flag)) {
			if (upyet) {
				cmn_err(CE_WARN,"Processor %d (slic %d) Access Error\n",
					l.me, slicid);
				/*
				 *+ A bus access error occurred, generating a
				*+ Non-Maskable Interrupt.
			 	*/
			} else {
				cmn_err(CE_WARN,"Processor ?? (slic %d) Access Error\n",
					slicid);
				/*
				 *+ A bus access error occurred, generating a
				*+ Non-Maskable Interrupt.
			 	*/
			}
			sgs2_access_error(flag);
		}
	} else {
#endif	/* MODELD */
	/*
	 * First check for SLIC NMI or local reserved location access.
	 */

	regval = rdslave(slicid, PROC_FLT);

	if ((regval & PROC_FLT_SLIC_NMI) == 0) {	/* SLIC NMI */
		if (va_slic->sl_nmiint == PAUSESELF)
			pause_self();
		cmn_err(CE_PANIC, "SLIC NMI");
		/*
		 *+ A Non-Maskable Interrupt was received from the SLIC chip.
		 */
		/*NOTREACHED*/
	}

	if ((regval & PROC_FLT_ACC_ERR) == 0) {		/* Reserved Space */
		cmn_err(CE_PANIC, "RESERVED LOCATION ACCESS ERROR");
		/*
		 *+ The processor tried to access a reserved address that
		 *+ caused a Non-Maskable Interrupt to be generated.
		 */
		/*NOTREACHED*/
	}

	/*
	 * Not a board-local source (cache parity errors freeze the bus,
	 * so we're not executing if that happened).  Thus, it should be
	 * a bus access error, stored in per-channel BIC access error register.
	 *
	 * Assumes BIC access error register has identical bit assignments
	 * as 1st gen (032) processor board access error register, since
	 * access_error() is coded this way.
	 *
	 * Since system needs to be "up" enough to have filled out
	 * slic_to_config[]; just panic if not yet up far enough.
	 */

	if (slic_to_config[slicid] == NULL) {
		cmn_err(CE_PANIC, "ACCESS ERROR");
		/*
		 *+ The processor has discovered a bus access error
		 *+ that occurred early in the boot sequence.
		 *+ The information needed to map the error
		 *+ information to the source of the access error 
		 *+ hasn't been initialized yet.
		 */
		/*NOTREACHED*/
	}
	bic_slic = BIC_SLIC(slicid, slic_to_config[slicid]->cd_flags);
	accerr_reg = (slicid & 0x01) ? BIC_ACCERR1 : BIC_ACCERR0;

	regval = rdSubslave(bic_slic, PROC_BIC, accerr_reg);

	if ((regval & BICAE_OCC) == 0) {
		if (upyet) {
			cmn_err(CE_WARN,"Processor %d (slic %d) Access Error\n",
				l.me, slicid);
			/*
			 *+ A bus access error occurred, generating a
			*+ Non-Maskable Interrupt.
		 	*/
		} else {
			cmn_err(CE_WARN,"Processor ?? (slic %d) Access Error\n",
				slicid);
			/*
			 *+ A bus access error occurred, generating a
			*+ Non-Maskable Interrupt.
		 	*/
		}
		access_error(regval);
	}
#ifdef	MODELD
	}
#endif

	/*
	 * All else failed ==> just panic.
	 *
	 * When/if handle cache parity errors somehow (unfreeze bus and
	 * un-hold processors), should do something here.  For now, system
	 * is *gone* if these happen.
	 */

	cmn_err(CE_PANIC, "NMI");
	/*
	 *+ A Non-Maskable Interrupt was generated by an unknown source.
	 */
	/*NOTREACHED*/
}

#ifdef	MODELD
/*
 * Clear the nmi error, returning true if the nmi was the result of
 * a bus timeout.
 */
clearnmi()
{
	unchar	my_slic = va_slic->sl_procid;
	int	timeout = 0;
#if 0
	int	sgs2 = l.eng->e_flags&E_SGS2;
#else
	extern int starttype;
	int	sgs2 = starttype == SLB_SGS2PROCBOARD;
#endif

	if (sgs2) {
		int flag = accerr_flag(my_slic);

		clraccerr(my_slic);	/* Takes it out of the BIC */
		reset_snmi(my_slic);	/* ... and the SIC */
		enable_snmi(my_slic);
		timeout = istimeout(flag);
	} else {
		extern	struct	ctlr_desc *slic_to_config[];
		unchar	bic_slic = BIC_SLIC(my_slic, slic_to_config[my_slic]->cd_flags);
		unchar	accerr_reg = (my_slic & 0x01) ? BIC_ACCERR1 : BIC_ACCERR0;
		unchar	regval = rdSubslave(bic_slic, PROC_BIC, accerr_reg);

		/*
		 * Writing anything to the BIC's access-error register clears it.
		 */
		wrSubslave(bic_slic, PROC_BIC, accerr_reg, 0xbb);
		timeout = ((~regval)&SLB_ATMSK) == SLB_AETIMOUT;
	}
	return(timeout);
}
#endif	/* MODELD */

/*
 * swt_fpu_pgxbug()
 *	See about backing out of FPU page-cross chip bug.
 *
 * i386 parts B1 and younger can lock up if the first byte of an FPU
 * instruction is in the last 8 bytes of a page, the last byte of the
 * page is an ESC instruction, interveaning bytes are all legit prefixes,
 * and reference to the next page causes a page fault.  Interrupts break the
 * lockup, but it locks up again unless the following page is made valid.
 * See B1 Errata sheet (12/17/86) bug # 17.
 *
 * hardclock() tested that this processor may suffer from the bug and the
 * pc is near the end of page.  hardclock() can't do the "fix" since it
 * might fault, and don't want to fault (and block) at raised SPL.
 *
 * Since fubyte() is relatively cheap, just fetch from the following page;
 * this is sufficient to work around the problem, and cheaper than trying to
 * fully test for the problem (would use fubyte() or equivalent to test
 * for the ESC at end of page).
 */

swt_fpu_pgxbug()
{
	(void) fubyte((caddr_t) u.u_ar0[PC] + NBPG);
}


#ifdef	DEBUG		/*XXX to EOF */
/*
 * flush_cache()
 *	Read enough memory locations to insure cache is flushed (no dirty
 *	cache blocks).
 */
#define	START_CACHE_FLUSH	((int *)CD_LOC)
#define	END_CACHE_FLUSH		((int *)((int)CD_LOC + 128*1024))
flush_cache()
{
	register int *p;

	for (p = START_CACHE_FLUSH; p < END_CACHE_FLUSH; p++)
		flush_ref(*p);
}
flush_ref(x)
{
#ifdef	lint
	lint_ref_int(x);
#endif	/* lint  */
}
#endif	/* DEBUG */ /*XXX to EOF */


dump_SGShw_regs()
{
	dump_SGSproc_regs();
}

/*
 * For each SGS processor in the system, print it's
 *	status and fault registers, selected BDP LO and BDP HI registers,
 *	and selected BIC registers.
 */
#ifndef KLINT
dump_SGSproc_regs()
{
	register struct ctlr_toc *toc;
	register struct	ctlr_desc *cd;
	register int i, unit;

	/*
	 * Load pointer to table of contents
	 * for SGS procesor boards in the system.
	 */
	toc = &CD_LOC->c_toc[SLB_SGSPROCBOARD];		/* SGS processors */

	/*
	 * Locate the array of descriptors for SGS memory boards.
	 *	search this array and print out information
	 *	on each configured memory board
	 */
	cd = &CD_LOC->c_ctlrs[toc->ct_start];
	unit = 0;
	for (i = 0; i < toc->ct_count; i++, cd++) {
		if (cd->cd_diag_flag & (CFG_FAIL|CFG_DECONF)) {
			continue;
		}
		printf("processor %d ", unit);
		unit++;
		printf("stat 0x%x flt 0x%x\n", 
			rdslave(cd->cd_slic, PROC_STAT),
			rdslave(cd->cd_slic, PROC_FLT));
	}
}
#endif /*KLINT*/


/*
 * Return a number to use in spin loops that takes into account
 * both the cpu rate and the mip rating.
 * If the machine is not yet up, then use cpurate otherwise
 * use l.cpu_speed which will then be defined.
 */

calc_delay(x)
	unsigned int	x;
{
	extern int	cpurate;

	if (!upyet) {
		ASSERT_DEBUG(cpurate,"cpurate not set");
		return (x*cpurate);
	} else {
		ASSERT_DEBUG(l.cpu_speed,"l.cpu_speed not set");
		return (x*l.cpu_speed);
	}
}

#ifdef	MODELD
/*
 * hold_proc(slicid)
 * 
 *	Called to assert the FORCE HOLD bit of the External Control register
 *	of the SIC of the processor indicated by slicid.  The indicated 
 *	processor will not return until it has been "unheld()".
 *
 *	Assumes that the target processor is an SGS2 (i486).  The caller
 *	assures this.
 *	
 *	Caller must call with SPLHI.  No locks (or gates) can be held by 
 *	the target processor since it is about to go away (remember the
 *	target processor may be the caller).
 */
hold_proc(slicid)
unchar	slicid;
{
	unchar	regval;

	/*
	 * This mix of bits represents the state of the bits for "normal"
	 * operation, except that we have now set EXT_CTL1_FORCE_HOLD
	 * to cause ourselves to be held.
	 */
	regval = (EXT_CTL1_NO_486_PARITY_ERR | EXT_CTL1_NO_RESET_SLIC_ERR |
		  EXT_CTL1_NO_BUS_SCAN | EXT_CTL1_NORMAL_OSC | 
		  EXT_CTL1_NO_START_INVAL| EXT_CTL1_FORCE_HOLD);

	/*
	 * If the target processor is also the caller then let's
	 * be sure to use lwrslave/lrdslave instead of wrslave/rdslave.
	 * Otherwise, we would go away while holding the slic_mutex lock,
	 * causing all other processors to block.
	 */
	if (l.eng->e_slicaddr == slicid) {
		lwrslave(slicid, EXT_CTL1, regval);
		while (lrdslave(slicid, EXT_CTL0) & EXT_CTL0_NO_HOLD_ACK) 
			continue;
	} else {
		wrslave(slicid, EXT_CTL1, regval);
		while (rdslave(slicid, EXT_CTL0) & EXT_CTL0_NO_HOLD_ACK) 
			continue;
	}
}
#endif	/* MODELD */
