/*   $OpenBSD: eh_common.S,v 1.61 2013/09/05 20:40:32 miod Exp $	*/
/*
 * Mach Operating System
 * Copyright (c) 1993-1991 Carnegie Mellon University
 * Copyright (c) 1991 OMRON Corporation
 * Copyright (c) 1996 Nivas Madhur
 * Copyright (c) 1998 Steve Murphree, Jr.
 * 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 AND OMRON ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS IS"
 * CONDITION.  CARNEGIE MELLON AND OMRON DISCLAIM 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.
 */

/*
 * NOTICE: This is not a standalone file.  To use it, define the PFSR_SAVE
 * macro (if supporting 88100 processors) and #include this file in your
 * port's eh.S:
 * #include <m88k/m88k/eh_common.S>
 */

/*
 * In the following discussion, references are made to:
 *    MC88100 - RISC MICROPROCESSOR USER'S MANUAL
 * (second edition). Reference in []s refer to section numbers.
 *
 * This discussion assumes that you are at least vaguely familiar with 88100
 * exception handling (chapter 6), the BSD kernel, and that you have a brain
 * (and use it while reading this).
 *
 * I also assume (and hope) that you're not offended by frequent misspellings.
 *
 * Jeffrey Friedl
 * jfriedl@rna.ncl.omron.co.jp
 * December, 1989
 *  -------------------------------------------------------------------
 *
 * EXCEPTIONS, INTERRUPTS, and TRAPS
 * ---------------------------------
 * This is the machine exception handler.
 * In the MC88100, various "conditions" cause an exception, where
 * processing momentarily jumps here to "service" the exception,
 * and then continues where it left off.
 *
 * There are a number of different types of exceptions.
 * For example, exception #6 is the privilege violation exception which
 * is raised when the user tries to execute a supervisor-only instruction.
 *
 * Exception #1 is the interrupt exception, and is raised when an
 * outside device raises the INT line on the CPU.  This happens,
 * for example, when the clock signals that it is time for a context
 * switch, or perhaps the disk drive signaling that some operation
 * is complete.
 *
 * Traps are also exceptions.  Traps are ways for user programs to request
 * kernel operations.  For example, "tcnd eq0, r0, 450" will raise
 * exception 450, the system call exception.
 *
 *
 * SERVICING AN EXCEPTION
 * -----------------------
 * When an exception occurs, each control register is saved in its
 * respective shadow register and execution continues from the
 * appropriate exception handler.  The exception handler must
 *      - save the context from the time of the exception
 *      - service the exception
 *      - restore the context (registers, etc)
 *      - pick up from where the exception occurred.
 *
 * The context is saved on a stack. Actually, in the user_state area
 * in the PCB if the exception happens in user mode.
 *
 * Servicing the exception is usually straightforward and in fact not dealt
 * with very much here.  Usually a C routine is called to handle it.
 * For example, when a privilege exception is raised, the routine that sends
 * an "illegal instruction" signal to the offending process is called.
 *
 * When the exception has been serviced, the context is restored from the
 * stack and execution resumes from where it left off.
 *
 * In more detail:
 *
 * Saving the exception-time context.
 * ---------------------------------
 *     In saving the exception-time context, we copy the shadow and general
 * purpose registers to memory.  Since one exception may occur while
 * servicing another, the memory used to save the exception-time context may
 * not be static (i.e. the same every time).  Thus, memory on a stack is set
 * aside for the exception frame (area where the exception-time context is
 * saved). The same stack is also used when C routines are called (to
 * service the exception).
 *
 *    Each process has a stack in kernel space (called the "kernel stack",
 * short for "process's kernel stack) as well as the user space stack.  When
 * entering the kernel from user space, the kernel stack is unused.  On this
 * stack we save the exception state and (most likely call a C routine to)
 * service the exception.
 *
 * Before servicing an exception, several issues must be addressed.
 *
 * 1) When an interrupt is recognized by the hardware, the data pipeline is
 *    allowed to clear.  However, if one of these data accesses faults (bad
 *    reference, or a reference to a page which needs to be swapped in), that
 *    reference, as well as any others in the pipeline at the time (at most
 *    three total) are left there, to be taken care of by the exception
 *    handler [6.4.1].  This involves swapping in the proper page and
 *    manually doing the appropriate load or store.
 *
 *    The other (at most, two other) data accesses that might have been in
 *    the pipeline must also be manually completed (even though they may not
 *    be at fault [yes, that's a bad pun, thank you]).
 *
 * 2) If any of the (at most three) uncompleted data access in the pipeline
 *    are loads (from memory to a register), then the bit for the destination
 *    register is set in the SSBR.  Since the hardware will never complete
 *    that load (since we do it manually), the hardware will never clear that
 *    SSBR bit.  Thus, we must clear it manually.  If this isn't done, the
 *    system will hang waiting for a bit to clear that will never.
 *
 * 3) If the exception is the privilege violation exception, the bounds
 *    check exception, or the misaligned access exception, the
 *    destination register bit in the SSBR may need to be cleared.
 *
 * 4) If the exception is one of the floating exceptions, then the
 *    destination register for that floating process won't be written,
 *    and the SSBR must be cleared explicitly.
 *
 * 5) The FPU must be enabled (as it is disabled by the exception processing
 *    hardware) and allowed to complete actions in progress. This is
 *    so that it may be used in the servicing of any instruction.
 *    When the FPU is being restarted, operations attempting to complete
 *    may themselves fault (raising another exception).
 *
 * More on Restarting the FPU
 * --------------------------
 *   The manual [section 6.4.3.4] gives only minor mention to this
 * rather complex task.  Before the FPU is restarted all SSBR bits are
 * cleared for actions that the exception handler completes (as mentioned
 * above) so that the SSBR is clear unless there are FPU operations that
 * have not actually been completed (and hence not written to the registers).
 * Also, all control registers (at least all those that we care about) are
 * saved to the stack exception frame before the FPU is restarted (this
 * is important... the reason comes later).
 *
 * The FPU is restarted by doing an rte to a trap-not-taken (the rte
 * actually enables the fpu because we ensure that the EPSR has the
 * FPU-enable bit on; the trap-not-taken ensures anything in the FPU
 * completes by waiting until scoreboard register is clear).
 *
 * At the time the FPU is restarted (the rte to the trap-not-taken) the FPU
 * can write to ANY of the general registers.  Thus, we must make sure that
 * all general registers (r1..r31) are in their pre-exception state so that
 * when saved to the exception frame after the FPU is enabled, they properly
 * reflect any changes made by the FPU in being restarted.
 *
 * Because we can't save the pointer to the exception frame in a general
 * register during the FPU restart (it could get overwritten by the FPU!),
 * we save it in a control register, SR3, during the restart.
 *
 * HOWEVER .....
 *
 * Because other uncompleted actions in the FPU may fault when the FPU is
 * restarted, a new exception may be raised during the restart. This may
 * happen recursively a number of times. Thus, during a restart, ANY register
 * whatsoever may be modified, including control registers.  Because of this
 * we must make sure that the exception handler preserves SR3 throughout
 * servicing an exception so that, if the exception had been raised during
 * an FPU restart, it is returned unmolested when control returns to the FPU
 * restart.
 *
 * Thus: if an exception is from kernel space, we MUST preserve SR3.
 * (if it from user space, no FPU-enable can be in progress and SR3 is
 *  unimportant).
 *
 * Now is a good time to recap SR1..SR3 usage:
 *   SR1 - CPU flags (exception handler flags)
 *   SR2 - generally free
 *   SR3 - free only if the exception is from user mode
 *
 * Once the FPU has been restarted, the general registers are saved to the
 * exception frame.  If the exception is not the interrupt exception,
 * interrupts are enabled and any faulted data accesses (see above) are
 * serviced.  In either case, the exception is then serviced (usually by
 * calling a C routine).  After servicing, any faulted data accesses are
 * serviced (if it had been the interrupt exception).  The context is then
 * restored and control returns to where the exception occurred.
 *
 */

#include "assym.h"

#include <machine/param.h>
#include <machine/asm.h>
#include <machine/trap.h>

/*
 * SR1 - CPU FLAGS REGISTER
 *
 * SR1 contains flags about the current CPU status.
 *
 * The bit FLAG_IGNORE_DATA_EXCEPTION indicates that any data exceptions
 * 	should be ignored (well, at least treated in a special way).
 * The bit FLAG_ENABLING_FPU indicates that the exception handler is
 * 	in the process of enabling the FPU (so that an exception can
 * 	be serviced).  This is needed because enabling the FPU can
 *	cause other exceptions to happen, and the whole system is
 *	in a rather precarious state and so special cautions must
 * 	be taken.
 */
#define FLAG_IGNORE_DATA_EXCEPTION	0
#define FLAG_ENABLING_FPU		1
#define FLAG_FROM_KERNEL		2

/* GENeral REGister OFFset into the E.F. (exception frame) */
#define GENREG_OFF(num)		(EF_R0 + (num) * 4)

/* Invoke a C function with 2 arguments */
#define	CALL(NAME, ARG1, ARG2) \
	or	%r2, %r0, ARG1; \
	bsr.n	_C_LABEL(NAME); \
	 or	%r3, %r0, ARG2

/* Invoke a function and return elsewhere */
/* CAREFUL: needs to have `RET' after the XCALL in memory */
#define	XCALL(NAME, RET) \
	bsr.n	NAME; \
	 addu	%r1, %r1, RET - . - 4

/*
 * Some registers used during the setting up of the new exception frame.
 * Don't choose r1, r30, or r31 for any of them.
 *
 * Also, if any are 'r2' or 'r3', be careful using with CALL above!
 */
#define	FLAGS			%r2
#define	TMP			%r3
#define	TMP2			%r10
#define	TMP3			%r11
#define	SAVE_TMP2(ef)		st	%r10, ef, GENREG_OFF(10)
#define	SAVE_TMP3(ef)		st	%r11, ef, GENREG_OFF(11)
#define	RESTORE_TMP2(ef)	ld	%r10, ef, GENREG_OFF(10)
#define	RESTORE_TMP3(ef)	ld	%r11, ef, GENREG_OFF(11)

/*
 * EF_SR3
 *   A place to save the exception-time SR3 from just after the
 *   time when an exception is raised until just after the FPU
 *   has been restarted.  This does not necessarly conflict with
 *   the general registers (though it can if you're not careful)
 *   and so we can use a spot later used to save a general register.
 */
#define	EF_SR3		GENREG_OFF(5)

	.text
	.align 3

/*
 *
 * #define PREP881x0(NAME, NUM, SSBR_STUFF, FLAG_CHECK)
 *
 * This is the "exception processing preparation" common to all exception
 * processing.  It is used in the following manner:
 *
 *	ASGLOBAL(foo_handler)
 *		PREP881x0("foo", 11, SSBR_Stuff, Precheck_Stuff)
 *		or	%r2, %r0, T_FOO_FAULT
 *		or	%r3, %r0, %r30
 *		XCALL(trapXXX, check_ast)
 *
 * This defines the exception handler for the "foo" exception.
 * The arguments are:
 * NAME
 *	String for debugging (more info later)
 * NUM
 *	The exception number [see the manual, Table 6-1]
 * SSBR_STUFF
 *	If the exception might leave some bits in the SSBR set,
 *	this should indicate how they are cleared.
 * FLAG_PRECHECK
 *	This is for the data access exception only. See it for
 *	more info.
 *
 * What's in between PREP881x0() and check_ast (usually a XCALL)
 * is the actual servicing of the interrupt.  During this time, any
 * register may be used freely as they've all been saved in the
 * exception frame (which is pointed to by r30).
 */

#ifdef M88100
#define PREP88100(NAME, NUM, SSBR_STUFF, FLAG_PRECHECK) \
	xcr	FLAGS, FLAGS, SR1			; \
	FLAG_PRECHECK					  \
	/* the bsr later clobbers r1, so save now */	  \
	stcr	%r1, SR2	/* r1 now free */	; \
	/* set or clear the FLAG_FROM_KERNEL bit */	  \
	ldcr	%r1, EPSR				; \
	bb0.n	PSR_SUPERVISOR_MODE_BIT, %r1, 1f	; \
	 clr	FLAGS, FLAGS, 1<FLAG_FROM_KERNEL>	; \
	set	FLAGS, FLAGS, 1<FLAG_FROM_KERNEL>	; \
	/* get a stack (exception frame) */		  \
1:	bsr	_ASM_LABEL(m88100_setup_phase_one)	; \
	/* TMP2 now free -- use to set EF_VECTOR */	  \
	or	TMP2, %r0, NUM				; \
	st	TMP2, %r31, EF_VECTOR			; \
	/* Clear any bits in the SSBR (held in TMP) */	  \
	/* SSBR_STUFF may be empty, though.         */	  \
	SSBR_STUFF					  \
	/* call setup_phase_two to restart the FPU  */	  \
	/* and to save all general registers.       */	  \
	bsr	_ASM_LABEL(m88100_setup_phase_two)
#endif

#ifdef M88110
#define PREP88110(NAME, NUM, FLAG_PRECHECK) \
	xcr	FLAGS, FLAGS, SR1			; \
	FLAG_PRECHECK					  \
	/* the bsr later clobbers r1, so save now */	; \
	stcr	%r1, SR2	/* r1 now free */	; \
	/* set or clear the FLAG_FROM_KERNEL bit */	; \
	ldcr	%r1, EPSR 				; \
	clr	FLAGS, FLAGS, 1<FLAG_FROM_KERNEL> 	; \
	bb0	PSR_SUPERVISOR_MODE_BIT, %r1, 1f 	; \
	set	FLAGS, FLAGS, 1<FLAG_FROM_KERNEL>	; \
	/* get a stack and an exception frame */	; \
1:	bsr	_ASM_LABEL(m88110_setup_phase_one)	; \
	/* TMP2 now free -- use to set EF_VECTOR */	; \
	or	TMP2, %r0, NUM				; \
	/* call setup_phase_two to save all general */	; \
	/* registers.                               */	; \
	st	TMP2, %r30, EF_VECTOR			; \
	bsr	_ASM_LABEL(m88110_setup_phase_two)
#endif

/* Some defines for use with PREP88100() */
#define	Clear_SSBR_Dest \
	bsr	_ASM_LABEL(clear_dest_ssbr_bit);
#define	M88100_Data_Precheck \
	bb1.n	FLAG_IGNORE_DATA_EXCEPTION, FLAGS, \
	    _ASM_LABEL(m88100_ignore_data_exception);
#define	M88110_Data_Precheck \
	bb1.n	FLAG_IGNORE_DATA_EXCEPTION, FLAGS, \
	    _ASM_LABEL(m88110_ignore_data_exception);

#ifdef M88100
/*
 * 88100 exception handlers
 */

/* unknown exception handler */
GLOBAL(unknown_handler)
	PREP88100("unknown", 0,,)
	or	%r2, %r0, T_UNKNOWNFLT
	or	%r3, %r0, %r30
	XCALL(_C_LABEL(m88100_trap), _ASM_LABEL(check_ast))

/* interrupt exception handler */
GLOBAL(interrupt_handler)
	PREP88100("interrupt", 1,,)
	or	%r2, %r0, %r30
	XCALL(_C_LABEL(interrupt), _ASM_LABEL(check_ast))

/* instruction access exception handler */
GLOBAL(instruction_access_handler)
	PREP88100("inst", 2,,)
	or	%r2, %r0, T_INSTFLT
	or	%r3, %r0, %r30
	XCALL(_C_LABEL(m88100_trap), _ASM_LABEL(check_ast))

/*
 * data access exception handler --
 *  See badaddr() below for info about Data_Precheck.
 */
GLOBAL(data_exception_handler_bootstrap)
	PREP88100("data", 3,, M88100_Data_Precheck)
	/* No need to call m88100_trap(T_DATAFLT) as PREP will do this for us */
	br	_ASM_LABEL(check_ast)
GLOBAL(data_exception_handler)
	PREP88100("data", 3,,)
	/* No need to call m88100_trap(T_DATAFLT) as PREP will do this for us */
	br	_ASM_LABEL(check_ast)

/* misaligned access exception handler */
GLOBAL(misaligned_handler)
	PREP88100("misalign", 4, Clear_SSBR_Dest,)
	or	%r2, %r0, T_MISALGNFLT
	or	%r3, %r0, %r30
	XCALL(_C_LABEL(m88100_trap), _ASM_LABEL(check_ast))

/* unimplemented opcode exception handler */
GLOBAL(unimplemented_handler)
	PREP88100("unimp", 5,,)
	or	%r2, %r0, T_ILLFLT
	or	%r3, %r0, %r30
	XCALL(_C_LABEL(m88100_trap), _ASM_LABEL(check_ast))

/*
 * Some versions of the chip have a bug whereby false privilege
 * violation exceptions are raised. If the valid bit in the SXIP is clear,
 * it is false.  If so, just return.  The code before PREP handles this....
 */
GLOBAL(privilege_handler)
	stcr	%r1, SR2	/* hold r1 for a moment */
	ldcr	%r1, SXIP	/* look at the sxip... valid bit set? */
	bb1.n	RTE_VALID_BIT, %r1, 1f	/* skip over if a valid exception */
	 ldcr	%r1, SR2	/* restore r1 */
	RTE
1:	PREP88100("privilege", 6, Clear_SSBR_Dest,)
	or	%r2, %r0, T_PRIVINFLT
	or	%r3, %r0, %r30
	XCALL(_C_LABEL(m88100_trap), _ASM_LABEL(check_ast))

/* bounds checking exception handler */
GLOBAL(bounds_handler)
	PREP88100("bounds", 7, Clear_SSBR_Dest,)
	or	%r2, %r0, T_BNDFLT
	or	%r3, %r0, %r30
	XCALL(_C_LABEL(m88100_trap), _ASM_LABEL(check_ast))

/* integer divide-by-zero exception handler */
GLOBAL(divide_handler)
	PREP88100("divide", 8, Clear_SSBR_Dest,)
	or	%r2, %r0, T_ZERODIV
	or	%r3, %r0, %r30
	XCALL(_C_LABEL(m88100_trap), _ASM_LABEL(check_ast))

/* integer overflow exception handler */
GLOBAL(overflow_handler)
	PREP88100("overflow", 9,,)
	or	%r2, %r0, T_OVFFLT
	or	%r3, %r0, %r30
	XCALL(_C_LABEL(m88100_trap), _ASM_LABEL(check_ast))

/* Floating-point precise handler */
#define	FPp_SSBR_STUFF \
	bsr	_ASM_LABEL(clear_FPp_ssbr_bit);
GLOBAL(fp_precise_handler)
	PREP88100("FPU precise", 114, FPp_SSBR_STUFF,)
	or	%r2, %r0, T_FPEPFLT
	or	%r3, %r0, %r30
	XCALL(_C_LABEL(m88100_trap), _ASM_LABEL(check_ast))

/* Floating-point imprecise handler */
#define	FPi_SSBR_STUFF \
	bsr	_ASM_LABEL(clear_FPi_ssbr_bit);
GLOBAL(fp_imprecise_handler)
	PREP88100("FPU imprecise", 115, FPi_SSBR_STUFF,)
	or	%r2, %r0, T_FPEIFLT
	or	%r3, %r0, %r30
	XCALL(_C_LABEL(m88100_trap), _ASM_LABEL(check_ast))

/* trap 450: system calls */
GLOBAL(syscall_handler)
	PREP88100("syscall", 450,,)
	ld	%r2, %r30, GENREG_OFF(13)
	or	%r3, %r0, %r30
	XCALL(_C_LABEL(m88100_syscall), _ASM_LABEL(check_ast))

/* trap 451: cache flush (necessary for trampolines) */
GLOBAL(cache_flush_handler)
	PREP88100("cache_flush", 451,,)
	or	%r2, %r0, %r30
	XCALL(_C_LABEL(cache_flush), _ASM_LABEL(check_ast))

GLOBAL(sigsys)
	PREP88100("sigsys", 501,,)
	or	%r2, %r0, T_SIGSYS
	or	%r3, %r0, %r30
	XCALL(_C_LABEL(m88100_trap), _ASM_LABEL(check_ast))

GLOBAL(stepbpt)
	PREP88100("stepbpt", 504,,)
	or	%r2, %r0, T_STEPBPT
	or	%r3, %r0, %r30
	XCALL(_C_LABEL(m88100_trap), _ASM_LABEL(check_ast))

GLOBAL(userbpt)
	PREP88100("userbpt", 511,,)
	or	%r2, %r0, T_USERBPT
	or	%r3, %r0, %r30
	XCALL(_C_LABEL(m88100_trap), _ASM_LABEL(check_ast))

#ifdef DDB
GLOBAL(break)
	PREP88100("break", 130,,)
	or	%r2, %r0, T_KDB_BREAK
	or	%r3, %r0, %r30
	XCALL(_C_LABEL(m88100_trap), _ASM_LABEL(check_ast))

GLOBAL(trace)
	PREP88100("trace", 131,,)
	or	%r2, %r0, T_KDB_TRACE
	or	%r3, %r0, %r30
	XCALL(_C_LABEL(m88100_trap), _ASM_LABEL(check_ast))

GLOBAL(entry)
	PREP88100("kdb", 132,,)
	or	%r2, %r0, T_KDB_ENTRY
	or	%r3, %r0, %r30
	XCALL(_C_LABEL(m88100_trap), _ASM_LABEL(check_ast))
#endif

/*
 * The error exception and reset exception handler.
 *
 * The error exception is raised when any other non-trap exception is raised
 * while shadowing is off. This is Bad News.
 *
 * The reset exception is raised when the RST signal is asserted (machine
 * is reset), the value of VBR is changed after exceptions are enabled,
 * or when a jmp, br/bsr to addr 0 (accidents do happen :-)
 * To tell the difference, you should check the value of r1 and the valid
 * bit of SXIP.
 * Upon a real reset, VBR is set to zero (0), so code must be at addr 0
 * to handle it!!!
 *
 * The shadow registers are not valid in this case (shadowing was off, if this
 * was an error exception, and may not be on, if this was a reset exception).
 * R1-R31 may be interesting though, so we'll save them.
 *
 * We'll not worry about trashing r26-29 here,
 * since they aren't generally used.
 */
GLOBAL(error_handler)
	br.n	1f
	 or	%r29, %r0, 10
GLOBAL(reset_handler)
	or	%r29, %r0, 0
1:
	or	%r26, %r0,  %r31	/* save old stack */
	or.u	%r31, %r0,  %hi16(_ASM_LABEL(initstack_end))
	or	%r31, %r31, %lo16(_ASM_LABEL(initstack_end))

#ifdef DEBUG
	/* zero the stack, so we'll know what we're lookin' at */
	or.u	%r27, %r0,  %hi16(_ASM_LABEL(initstack))
	or	%r27, %r27, %lo16(_ASM_LABEL(initstack))
1:	cmp	%r28, %r27, %r31
	bb1	ge,   %r28, 2f	/* branch if at the end of the stack */
	st	%r0,  %r0,  %r27
	br.n	1b
	 addu	%r27, %r27, 4	/* bump up */
2:	/* stack has been cleared */
#endif

	/* ensure that stack is 8-byte aligned */
	clr	%r31, %r31, 3<0>	/* round down to 8-byte boundary */

	/* create exception frame on stack */
	subu	%r31, %r31, TRAPFRAME_SIZEOF	/* r31 now our E.F. */

	/* save old R31 and other R registers */
	st.d	%r0 , %r31, GENREG_OFF(0)
	st.d	%r2 , %r31, GENREG_OFF(2)
	st.d	%r4 , %r31, GENREG_OFF(4)
	st.d	%r6 , %r31, GENREG_OFF(6)
	st.d	%r8 , %r31, GENREG_OFF(8)
	st.d	%r10, %r31, GENREG_OFF(10)
	st.d	%r12, %r31, GENREG_OFF(12)
	st.d	%r14, %r31, GENREG_OFF(14)
	st.d	%r16, %r31, GENREG_OFF(16)
	st.d	%r18, %r31, GENREG_OFF(18)
	st.d	%r20, %r31, GENREG_OFF(20)
	st.d	%r22, %r31, GENREG_OFF(22)
	st.d	%r24, %r31, GENREG_OFF(24)
	st	%r30, %r31, GENREG_OFF(30)
	st	%r26, %r31, GENREG_OFF(31)

	/* save shadow registers (are OLD if error_handler, though) */
	ldcr	%r10, EPSR
	st	%r10, %r31, EF_EPSR
	ldcr	%r10, SXIP
	st	%r10, %r31, EF_SXIP
	ldcr	%r10, SNIP
	st	%r10, %r31, EF_SNIP
	ldcr	%r10, SR1
	st	%r10, %r31, EF_FLAGS
	ldcr	%r10, SFIP
	st	%r10, %r31, EF_SFIP
	ldcr	%r10, SSBR
	st	%r10, %r31, EF_SSBR
	stcr	%r0,  SSBR	/* won't want shadow bits bothering us later */

	ldcr	%r10, DMT0
	st	%r10, %r31, EF_DMT0
	ldcr	%r11, DMD0
	st	%r11, %r31, EF_DMD0
	ldcr	%r12, DMA0

	st	%r12, %r31, EF_DMA0
	ldcr	%r10, DMT1
	st	%r10, %r31, EF_DMT1
	FLUSH_PIPELINE
	ldcr	%r11, DMD1
	st 	%r11, %r31, EF_DMD1
	ldcr	%r12, DMA1
	st	%r12, %r31, EF_DMA1

	ldcr	%r10, DMT2
	st	%r10, %r31, EF_DMT2
	ldcr	%r11, DMD2
	st	%r11, %r31, EF_DMD2
	ldcr	%r12, DMA2
	st	%r12, %r31, EF_DMA2

	/* shove sr2 into EF_FPLS1 */
	ldcr	%r10, SR2
	st	%r10, %r31, EF_FPLS1

	/* shove sr3 into EF_FPHS2 */
	ldcr	%r10, SR3
	st	%r10, %r31, EF_FPHS2

	/* save error vector */
	st	%r29, %r31, EF_VECTOR

	/*
	 * Cheap way to enable FPU and start shadowing again.
	 */
	ldcr	%r10, PSR
	clr	%r10, %r10, 1<PSR_FPU_DISABLE_BIT>	/* enable the FPU */
	clr	%r10, %r10, 1<PSR_SHADOW_FREEZE_BIT>	/* and shadowing */
	stcr	%r10, PSR
	FLUSH_PIPELINE

	/* put pointer to regs into r30... r31 will become a simple stack */
	or	%r30, %r31, %r0

	subu	%r31, %r31, 0x10	/* make some breathing space */
	st	%r30, %r31, 0x0c	/* store frame pointer on the stack */
#ifdef DDB
	st	%r30, %r31, 0x08	/* store again for the debugger to recognize */
	or.u	%r20, %r0,  %hi16(0x87654321)
	or	%r20, %r20, %lo16(0x87654321)
	st	%r20, %r31, 0x04
	st	%r20, %r31, 0x00
#endif

	bsr.n	_C_LABEL(error_fatal)
	 or	%r2, %r0, %r30

	/* turn interrupts back on */
	ldcr	%r1, PSR
	clr	%r1, %r1, 1<PSR_INTERRUPT_DISABLE_BIT>
	stcr	%r1, PSR
	FLUSH_PIPELINE

1:
	br	1b
	/* NOTREACHED */
#endif	/* M88100 */

/*
 * This is part of baddadr (below).
 */
#ifdef M88100
ASLOCAL(m88100_ignore_data_exception)
	/*
	 * SR1: previous FLAGS reg
	 * SR2: free
	 * SR3: must preserve
	 * FLAGS: CPU status flags
	 */
	xcr	FLAGS, FLAGS, SR1	/* replace SR1, FLAGS */

	/*
	 * For more info, see badaddr() below.
	 *
	 * We just want to jump to "badaddr__return_nonzero" below.
	 *
	 * We don't worry about trashing r2 here because we're
	 * jumping back to the function badaddr() where we're allowed
	 * to blast r2..r9 as we see fit.
	 */

	/* the "+2" below is to set the VALID bit. */
	or.u	%r2, %r0, %hi16(_ASM_LABEL(badaddr__return_nonzero) + 2)
	or	%r2, %r2, %lo16(_ASM_LABEL(badaddr__return_nonzero) + 2)
	stcr	%r2, SNIP	/* Make it the next instruction to execute */
	addu	%r2, %r2, 4
	stcr	%r2, SFIP	/* and the next one after that, too. */
	stcr	%r0, SSBR	/* make the scoreboard happy. */
	RTE
#endif /* M88100 */

#ifdef M88110
/*
 * This is part of baddadr (below).
 */
ASLOCAL(m88110_ignore_data_exception)
	/*
	 * SR1: previous FLAGS reg
	 * SR2: free
	 * SR3: must preserve
	 * FLAGS: CPU status flags
	 */
	xcr	FLAGS, FLAGS, SR1	/* replace SR1, FLAGS */

	/*
	 * For more info, see badaddr() below.
	 *
	 * We just want to jump to "badaddr__return_nonzero" below.
	 *
	 * We don't worry about trashing R2 here because we're
	 * jumping back to the function badaddr() where we're allowed
	 * to blast r2..r9 as we see fit.
	 */

	or.u	%r2, %r0, %hi16(_ASM_LABEL(badaddr__return_nonzero))
	or	%r2, %r2, %lo16(_ASM_LABEL(badaddr__return_nonzero))
	stcr	%r2, EXIP	/* Make it the next instruction to execute */
	stcr	%r0, DSR	/* Clear exception status */
	RTE
#endif /* M88110 */

/*
 * extern int badaddr(vaddr_t addr, int len)
 *
 * Returns true (non-zero) if the given LEN bytes starting at ADDR are
 * not all currently accessible by the kernel.
 *
 * If all LEN bytes starting at ADDR are accessible, zero is returned.
 *
 * Len may be be 1, 2, 4 or 8.
 *
 * This is implemented by setting a special flag in SR1 before trying to access
 * the given address. If a data access exception is raised, the address
 * is inaccessible. The exception handler will notice the special CPU flag
 * and not try to swap the address in. Rather, it will return to
 * "badaddr__return_nonzero" in this routine so that we may return non-zero
 * to the calling routine.
 *
 * If no fault is raised, we continue to where we return zero to the calling
 * routine (after removing the special CPU flag).
 */

GLOBAL(badaddr)
	/*
	 * Disable interrupts ... don't want a context switch while we're
	 * doing this! Also, save the old PSR in R8 to restore later.
	 */
	ldcr	%r8, PSR
	set	%r4, %r8, 1<PSR_INTERRUPT_DISABLE_BIT>
	stcr	%r4, PSR
	FLUSH_PIPELINE

	ldcr	%r5, SR1
	set	%r5, %r5, 1<FLAG_IGNORE_DATA_EXCEPTION>
	/* resetting r5 to SR1 done in the delay slot below. */

	/* make sure the upper 28 bits of the size are zero... */
	ext	%r6, %r3, 0<4>
	bcnd.n	ne0, %r6, _ASM_LABEL(badaddr__return_nonzero)
	 stcr	%r5, SR1

	or.u	%r6, %r0, %hi16(_ASM_LABEL(badaddr_switch))
	or	%r6, %r6, %lo16(_ASM_LABEL(badaddr_switch))
	lda	%r3, %r6[%r3]
	jmp	%r3

ASLOCAL(badaddr_switch)
	br	_ASM_LABEL(badaddr__return_nonzero)
	br	_ASM_LABEL(badaddr__b)
	br	_ASM_LABEL(badaddr__h)
	br	_ASM_LABEL(badaddr__return_nonzero)
	br	_ASM_LABEL(badaddr__w)
	br	_ASM_LABEL(badaddr__return_nonzero)
	br	_ASM_LABEL(badaddr__return_nonzero)
	br	_ASM_LABEL(badaddr__return_nonzero)
	br	_ASM_LABEL(badaddr__d)
	br	_ASM_LABEL(badaddr__return_nonzero)
	br	_ASM_LABEL(badaddr__return_nonzero)
	br	_ASM_LABEL(badaddr__return_nonzero)
	br	_ASM_LABEL(badaddr__return_nonzero)
	br	_ASM_LABEL(badaddr__return_nonzero)
	br	_ASM_LABEL(badaddr__return_nonzero)
	br	_ASM_LABEL(badaddr__return_nonzero)

	/*
	 * The load attemps below will either fault or not. If they fault,
	 * execution will go to data_access_handler, then to
	 * ignore_data_exception, then to badaddr__return_nonzero, which
	 * will return to the calling function.
	 * If there is no fault, execution will just continue as normal.
	 */

ASLOCAL(badaddr__b)
	FLUSH_PIPELINE
	ld.b	%r6, %r2, 0
	FLUSH_PIPELINE
	br.n	_ASM_LABEL(badaddr__return)
	 or	%r2, %r0, %r0

ASLOCAL(badaddr__h)
	/* It's a bad address if it's misaligned. */
	bb1	0, %r2, _ASM_LABEL(badaddr__return_nonzero)

	FLUSH_PIPELINE
	ld.h	%r6, %r2, 0
	FLUSH_PIPELINE
	br.n	_ASM_LABEL(badaddr__return)
	 or	%r2, %r0, %r0

ASLOCAL(badaddr__w)
	/* It's a bad address if it's misaligned. */
	bb1	0, %r2, _ASM_LABEL(badaddr__return_nonzero)
	bb1	1, %r2, _ASM_LABEL(badaddr__return_nonzero)

	FLUSH_PIPELINE
	ld	%r6, %r2, 0
	FLUSH_PIPELINE
	br.n	_ASM_LABEL(badaddr__return)
	 or	%r2, %r0, %r0	/* indicate a zero (address not bad) return.*/

ASLOCAL(badaddr__d)
	/* It's a bad address if it's misaligned. */
	bb1	0, %r2, _ASM_LABEL(badaddr__return_nonzero)
	bb1	1, %r2, _ASM_LABEL(badaddr__return_nonzero)
	bb1	2, %r2, _ASM_LABEL(badaddr__return_nonzero)

	FLUSH_PIPELINE
	ld.d	%r6, %r2, 0
	FLUSH_PIPELINE
	br.n	_ASM_LABEL(badaddr__return)
	 or	%r2, %r0, %r0	/* indicate a zero (address not bad) return.*/

ASLOCAL(badaddr__return_nonzero)
	or	%r2, %r0, 1
	/* FALLTHROUGH */

ASLOCAL(badaddr__return)
	ldcr	%r4, SR1
	clr	%r4, %r4, 1<FLAG_IGNORE_DATA_EXCEPTION>
	stcr	%r4, SR1

	/*
	 * Restore the PSR to what it was before.
	 * The only difference is that we might be enabling interrupts
	 * (which we turned off above).  If interrupts were already off,
	 * we do not want to turn them on now, so we just restore from
	 * where we saved it.
	 */
	stcr	%r8, PSR
	FLUSH_PIPELINE
	jmp	%r1

#ifdef M88100
ASLOCAL(m88100_setup_phase_one)
	/*
	 * SR1: saved copy of exception-time register now holding FLAGS
	 * SR2: saved copy of exception-time r1
	 * SR3: must be preserved .. may be the exception-time stack
	 * r1: return address to calling exception handler
	 * FLAGS: CPU status flags
	 *
	 * immediate goal:
	 *   Decide where we're going to put the exception frame.
	 *   Might be at the end of R31, SR3, or the process pcb.
	 */

	/* Check if we are coming in from a FPU restart exception.
	   If so, the pcb will be in SR3 */
	NOP
	xcr	%r1, %r1, SR2
	NOP
	NOP
	NOP

	bb1	FLAG_ENABLING_FPU, FLAGS, _ASM_LABEL(m88100_use_SR3_pcb)
	/* are we coming in from user mode? If so, pick up process pcb */
	bb0	FLAG_FROM_KERNEL, FLAGS, _ASM_LABEL(m88100_pickup_stack)

	/* Interrupt in kernel mode, not FPU restart */
	/*
	 * SR1: saved copy of exception-time register now holding FLAGS
	 * SR2: return address to the calling exception handler
	 * SR3: must be preserved; may be important for other exceptions
	 * FLAGS: CPU status flags
	 *
	 * immediate goal:
	 *   We're already on the kernel stack, but not having
	 *   needed to use SR3. We can just make room on the
	 *   stack (r31) for our exception frame.
	 */
	subu	%r31, %r31, TRAPFRAME_SIZEOF	/* r31 now our E.F. */
	st	FLAGS,%r31, EF_FLAGS		/* save flags */
	st	%r1,  %r31, GENREG_OFF(1)	/* save prev. r1 (now free)*/

	ldcr	%r1,  SR3			/* save previous SR3 */
	st	%r1,  %r31, EF_SR3

	addu	%r1,  %r31, TRAPFRAME_SIZEOF	/* save previous r31 */
	br.n	_ASM_LABEL(m88100_have_pcb)
	 st 	%r1,  %r31, GENREG_OFF(31)

ASLOCAL(m88100_use_SR3_pcb)
	/*
	 * SR1: saved copy of exception-time register now holding FLAGS
	 * SR2: return address to the calling exception handler
	 * SR3: must be preserved; exception-time stack pointer
	 * FLAGS: CPU status flags
	 *
	 * immediate goal:
	 *   An exception occurred while enabling the FPU. Since r31
	 *   is the user's r31 while enabling the FPU, we had put
	 *   our pcb pointer into SR3, so make room from
	 *   there for our stack pointer.
	 *   We need to check if SR3 is the old stack pointer or the
	 *   pointer off to the user pcb. If it pointing to the user
	 *   pcb, we need to pick up the kernel stack. Otherwise
	 *   we need to allocate a frame upon it.
	 *   We look at the EPSR to see if it was from user mode
	 *   Unfortunately, we have no registers free at the moment
	 *   But we know register 0 in the pcb frame will always be
	 *   zero, so we can use it as scratch storage.
	 */
	xcr	%r30, %r30, SR3			/* r30 = old exception frame */
	st	%r1,  %r30, GENREG_OFF(0)	/* free up r1 */
	ld	%r1,  %r30, EF_EPSR		/* get back the epsr */
	bb0.n	PSR_SUPERVISOR_MODE_BIT, %r1, 1f/* if user mode */
	 ld	%r1,  %r30, GENREG_OFF(0)	/* restore r1 */
	/* we were in kernel mode - dump frame upon the stack */
	st	%r0,  %r30, GENREG_OFF(0)	/* repair old frame */
	subu	%r30, %r30, TRAPFRAME_SIZEOF	/* r30 now our E.F. */
	st	FLAGS,%r30, EF_FLAGS		/* save flags */
	st	%r1,  %r30, GENREG_OFF(1)	/* save prev r1 (now free) */

	st	%r31, %r30, GENREG_OFF(31)	/* save previous r31 */
	or	%r31, %r0,  %r30		/* make r31 our pointer. */
	addu	%r30, %r30, TRAPFRAME_SIZEOF	/* r30 now has previous SR3 */
	st	%r30, %r31, EF_SR3		/* save previous SR3 */
	br.n	_ASM_LABEL(m88100_have_pcb)
	 xcr	%r30, %r30, SR3			/* restore r30 */
1:
	/* we took an exception while restarting the FPU from user space.
	 * Consequently, we never picked up a stack. Do so now.
	 * R1 is currently free (saved in the exception frame pointed at by
	 * r30) */
	ldcr	%r1,  CPU
	ld	%r1,  %r1,  CI_CURPCB
	addu	%r1,  %r1,  USPACE - TRAPFRAME_SIZEOF
	st	FLAGS,%r1,  EF_FLAGS		/* store flags */
	st	%r31, %r1,  GENREG_OFF(31)	/* store r31 - now free */
	st	%r30, %r1,  EF_SR3		/* store old SR3 (pcb) */
	or	%r31, %r1,  %r0			/* make r31 our exception fp */
	ld	%r1,  %r30, GENREG_OFF(0)	/* restore old r1 */
	st	%r0,  %r30, GENREG_OFF(0)	/* repair that frame */
	st	%r1,  %r31, GENREG_OFF(1)	/* store r1 */
	br.n	_ASM_LABEL(m88100_have_pcb)
	 xcr	%r30, %r30, SR3			/* restore r30 */

ASLOCAL(m88100_pickup_stack)
	/*
	 * SR1: saved copy of exception-time register now holding FLAGS
	 * SR2: return address to the calling exception handler
	 * SR3: free
	 * FLAGS: CPU status flags
	 *
	 * immediate goal:
	 *   Since we're servicing an exception from user mode, we
	 *   know that SR3 is free.  We use it to free up a temp.
	 *   register to be used in getting the process pcb
	 */
	stcr	%r31, SR3	/* save previous r31 */

	/* switch to the process kernel stack. */
	ldcr	%r31, CPU
	ld	%r31, %r31, CI_CURPCB
	addu	%r31, %r31, PCB_USER_STATE	/* point to user save area */

	/*
	 * WARNING! Using pcb->user_state as the exception frame
	 * AND stack pointer, means we can not afford using the stack
	 * until we have saved enough and can go back to the top of the u area,
	 * after the FPU is enabled.
	 */

	st	FLAGS,%r31, EF_FLAGS		/* save flags */
	st	%r1,  %r31, GENREG_OFF(1)	/* save prev. r1 (now free) */
	ldcr	%r1,  SR3			/* save previous r31 */
	st	%r1,  %r31, GENREG_OFF(31)
	/* FALLTHROUGH */

ASLOCAL(m88100_have_pcb)
	/*
	 * SR1: saved copy of exception-time register now holding FLAGS
	 * SR2: return address to the calling exception handler
	 * SR3: free
	 * r1:  free
	 * FLAGS: CPU status flags
	 * r31: our exception frame
	 * Valid in the exception frame:
	 *   Exception-time r1, r31, FLAGS.
	 *   Exception SR3, if appropriate.
	 *
	 * immediate goal:
	 *   Save the shadow registers that need to be saved to
	 *   the exception frame.
	 */
	stcr	TMP, SR3	/* free up TMP, TMP2, TMP3 */
	SAVE_TMP2(%r31)
	SAVE_TMP3(%r31)

	/* save some exception-time registers to the exception frame */
	ldcr	TMP,  EPSR
	st	TMP,  %r31, EF_EPSR
	ldcr	TMP3, SNIP
	st	TMP3, %r31, EF_SNIP
	ldcr	TMP2, SFIP
	st	TMP2, %r31, EF_SFIP
	/* get and store the cpu_info pointer */
	ldcr	TMP,  CPU
	st	TMP,  %r31, EF_CPU

	/*
	 * Save Pbus fault status register from data and inst CMMU.
	 * We can afford calling a function since r1 is safe to use here.
	 */
GLOBAL(pfsr_save)
	PFSR_SAVE
ASLOCAL(pfsr_done)
	
	ldcr	TMP,  SSBR
	ldcr	TMP2, SXIP
	ldcr	TMP3, DMT0
	st	TMP2, %r31, EF_SXIP

/*
 * The above shadow registers are obligatory for any and all
 * exceptions.  Now, if the data access pipeline is not clear,
 * we must save the DMx shadow registers, as well as clear
 * the appropriate SSBR bits for the destination registers of
 * loads or xmems.
 */
	bb0.n	DMT_VALID_BIT, TMP3, 8f
	 st	TMP3, %r31, EF_DMT0

	ldcr	TMP2, DMT1
	ldcr	TMP3, DMT2
	st	TMP2, %r31, EF_DMT1
	st	TMP3, %r31, EF_DMT2

	ldcr	TMP2, DMA0
	ldcr	TMP3, DMA1
	st	TMP2, %r31, EF_DMA0
	st	TMP3, %r31, EF_DMA1

	ldcr	TMP2, DMA2
	ldcr	TMP3, DMD0
	st	TMP2, %r31, EF_DMA2
	st	TMP3, %r31, EF_DMD0

	FLUSH_PIPELINE
	ldcr	TMP2, DMD1
	ldcr	TMP3, DMD2
	st	TMP2, %r31, EF_DMD1
	st	TMP3, %r31, EF_DMD2

	/* make sure an exception in fpu_enable will not see our DMT0 */
	stcr	%r0,   DMT0

	/*
 	 * need to clear "appropriate" bits in the SSBR before
 	 * we restart the FPU
 	 */
	ld	TMP2, %r31, EF_DMT0
	bb1	DMT_LOCK_BIT,  TMP2, 1f
	bb1	DMT_WRITE_BIT, TMP2, 2f
1:
	extu	TMP2, TMP2, DMT_DREG_WIDTH <DMT_DREG_OFFSET>
	set	TMP2, TMP2, 1<5>
	clr	TMP,  TMP,  TMP2
2:
	ldcr	TMP2, DMT1
	bb0	DMT_VALID_BIT, TMP2, 4f
	bb1	DMT_LOCK_BIT,  TMP2, 3f
	bb1	DMT_WRITE_BIT, TMP2, 4f
3:
	extu	TMP2, TMP2, DMT_DREG_WIDTH <DMT_DREG_OFFSET>
	set	TMP2, TMP2, 1<5>
	clr	TMP,  TMP,  TMP2
4:
	ldcr	TMP2, DMT2
	bb0	DMT_VALID_BIT, TMP2, 8f
	bb1	DMT_LOCK_BIT,  TMP2, 5f
	bb1	DMT_WRITE_BIT, TMP2, 8f
	bb1	DMT_DOUBLE_BIT,TMP2, 6f
5:
	extu	TMP2, TMP2, DMT_DREG_WIDTH <DMT_DREG_OFFSET>
	br.n	7f
	 set	TMP2, TMP2, 1<5>	/* single */
6:
	extu	TMP2, TMP2, DMT_DREG_WIDTH <DMT_DREG_OFFSET>
	set	TMP2, TMP2, 1<6>	/* double */
7:
	clr	TMP,  TMP,  TMP2
8:
	/*
	 * SR1: saved copy of exception-time register now holding FLAGS
	 * SR2: return address to the calling exception handler
	 * SR3: saved TMP
	 * r1:  free
	 * TMP: possibly revised SSBR
	 * TMP2: free
	 * TMP3: free
	 * FLAGS: CPU status flags
	 * r31: exception frame
	 * Valid in the exception frame:
	 *   Exception-time r1, r31, FLAGS.
	 *   Exception-time TMP2, TMP3.
	 *   Exception-time epsr, sfip, snip, sxip.
	 *   Dmt0.
	 *   Other data pipeline control registers, if appropriate.
	 *   Exception SR3, if appropriate.
	 */
	ldcr	%r1,  SR2
	jmp	%r1	/* allow the handler to clear more SSBR bits */

ASLOCAL(clear_FPi_ssbr_bit)
	/*
	 * Clear floating point imprecise ssbr bits.
	 * Also, save appropriate FPU control registers to the E.F.
	 *
	 * r1:  return address to calling exception handler
	 * TMP: (possibly) revised ssbr
	 * TMP2: free
	 * TMP3: free
	 */
	fldcr	TMP2, FPSR
	fldcr	TMP3, FPCR
	st	TMP2, %r31, EF_FPSR
	st	TMP3, %r31, EF_FPCR

	fldcr	TMP2, FPECR
	fldcr	TMP3, FPRH
	st	TMP2, %r31, EF_FPECR
	st	TMP3, %r31, EF_FPRH

	fldcr 	TMP2, FPIT
	fldcr	TMP3, FPRL
	st	TMP2, %r31, EF_FPIT
	st	TMP3, %r31, EF_FPRL

	/*
	 * We only need clear the bit in the SSBR for the
	 * 2nd reg of a double result [see section 6.8.5]
	 */
#define FPIT_SIZE_BIT   10
	bb0	FPIT_SIZE_BIT, TMP2, 1f
	extu	TMP2, TMP2, 5<0>	/* get the reg. */
	set	TMP2, TMP2, 1<6>	/* set width */
	clr	TMP,  TMP,  TMP2
1:
	jmp	%r1


ASLOCAL(clear_FPp_ssbr_bit)
	/*
	 * Clear floating point precise ssbr bits.
	 * Also, save appropriate FPU control registers to the E.F.
	 *
	 * r1:  return address to calling exception handler
	 * TMP: (possibly) revised ssbr
	 * TMP2: free
	 * TMP3: free
	 */
	fldcr	TMP2, FPSR
	fldcr	TMP3, FPCR
	st	TMP2, %r31, EF_FPSR
	st	TMP3, %r31, EF_FPCR

	fldcr	TMP3, FPECR
	st	TMP3, %r31, EF_FPECR
	fldcr	TMP2, FPHS1
	fldcr	TMP3, FPHS2
	st	TMP2, %r31, EF_FPHS1
	st	TMP3, %r31, EF_FPHS2

	fldcr	TMP2, FPLS1
	fldcr	TMP3, FPLS2
	st	TMP2, %r31, EF_FPLS1
	st	TMP3, %r31, EF_FPLS2

	fldcr	TMP2, FPPT
	st	TMP2, %r31, EF_FPPT

#define FPPT_SIZE_BIT   5
	bb1.n	FPPT_SIZE_BIT, TMP2, 2f
	 extu	TMP3, TMP2, 5<0>	/* get FP operation dest reg */
	br.n	3f
	 set	TMP3, TMP3, 1<5>	/* size=1 - clear one bit for float */
2:
	set	TMP3, TMP3, 1<6>	/* size=2 - clear two bits for double */
3:
	jmp.n	%r1
	 clr	TMP,  TMP,  TMP3	/* clear bit(s) in ssbr. */


ASLOCAL(clear_dest_ssbr_bit)
	/*
	 * There are various cases where an exception can leave the
	 * destination register's bit in the SB set.
	 * Examples:
	 *   misaligned or privilege exception on a LD or XMEM
	 *   DIV or DIVU by zero.
	 *
	 * I think that if the instruction is LD.D, then two bits must
	 * be cleared.
	 *
	 * Even though there are a number of instructions/exception
	 * combinations that could fire this code up, it's only required
	 * to be run for the above cases.  However, I don't think it'll
	 * ever be a problem to run this in other cases (ST instructions,
	 * for example), so I don't bother checking.  If we had to check
	 * for every possible instruction, this code would be much larger.
	 *
	 * The only checking, then, is to see if it's a LD.D or not.
	 *
	 * At the moment....
	 *  r1:  return address to calling exception handler
	 *  TMP: (possibly) revised ssbr
	 *  TMP2: free
	 *  TMP3: free
	 */

	ldcr	TMP3, EPSR	/* going to check: user or system memory? */
	ldcr	TMP2, SXIP	/* get the instruction's address */
	bb1.n	PSR_SUPERVISOR_MODE_BIT, TMP3, 2f
	 clr	TMP2, TMP2, 2<0>	/* get rid of valid and error bits. */

	/* user space load here */
#if ERRATA__XXX_USR
	NOP
	ld.usr	TMP2, TMP2, %r0	/* get the instruction itself */
	NOP
	NOP
	NOP
	br    3f
#else
	br.n  3f
	 ld.usr	TMP2, TMP2, %r0	/* get the instruction itself */
#endif

2:	/* system space load here */
	ld	TMP2, TMP2, %r0	/* get the instruction itself */

3:	/* now we have the instruction..... */
	/*
	 * Now see if it's a double load
	 * There are three forms of double load [IMM16, scaled, unscaled],
	 * which can be checked by matching against two templates:
	 *          -- 77776666555544443333222211110000 --
	 *   if (((instruction & 11111100000000000000000000000000) ==
	 *             00010000000000000000000000000000) ;;
	 *       ((instruction & 11111100000000001111110011100000) ==
	 *             11110100000000000001000000000000))
	 *   {
	 *      It's a load double, so
	 *      clear two SSBR bits.
	 *   } else {
	 *      It's not a load double.
	 *      Must be a load single, xmem, or st
	 *      Thus, clear one SSBR bit.
	 *   }
	 */
	/* check the first pattern for ld.d */
	extu	TMP3, TMP2, 16<16>	/* get the upper 16 bits */
	mask	TMP3, TMP3, 0xfc00	/* apply the mask */
	cmp	TMP3, TMP3, 0x1000	/* if equal, it's a load double */
	bb1	eq,   TMP3, 2f

	/* still could be -- check the second pattern for ld.d */
	/* look at the upper 16 bits first */
	extu	TMP3, TMP2, 16<16>	/* get the upper 16 bits */
	mask	TMP3, TMP3, 0xfc00	/* apply the mask */
	cmp	TMP3, TMP3, 0xf400	/* if equal, might be a load double */
	bb1	ne,   TMP3, 1f		/* not equal, must be single */

	/* now look at the lower 16 bits */
	extu	TMP3, TMP2, 16<0>	/* get the lower 16 bits */
	mask	TMP3, TMP3, 0xfce0	/* apply the mask */
	cmp	TMP3, TMP3, 0x1000	/* if equal, it's a load double */
	bb1	eq,   TMP3, 2f

1:	/* misaligned single */
	extu	TMP2, TMP2, 5<21>	/* get the destination register */
	br.n	3f
	 set	TMP2, TMP2, 1<5>	/* set size=1 */

2:	/* misaligned double */
	extu	TMP2, TMP2, 5<21>	/* get the destination register */
	set	TMP2, TMP2, 1<6>	/* set size=2 -- clear two bits */
3:
	jmp.n	%r1
	 clr	TMP,  TMP,  TMP2	/* clear bit(s) in ssbr. */

ASLOCAL(m88100_setup_phase_two)
	/*
	 * SR1: saved copy of exception-time register now holding FLAGS
	 * SR2: free
	 * SR3: saved TMP
	 * r1:  return address to calling exception handler
	 * TMP: possibly revised SSBR
	 * TMP2: free
	 * TMP3: free
	 * FLAGS: CPU status flags
	 * r31: our exception frame
	 *    Valid in the exception frame:
	 *   Exception-time r1, r31, FLAGS.
	 *   Exception-time TMP2, TMP3.
	 *   Exception-time epsr, sfip, snip, sxip.
	 *   Exception number (EF_VECTOR).
	 *   Dmt0
	 *   Other data pipeline control registers, if appropriate.
	 *   FPU control registers, if appropriate.
	 *   Exception SR3, if appropriate.
	 *
	 * immediate goal:
	 *   restore the system to the exception-time state (except
	 *   SR3 will be OUR stack pointer) so that we may restart the FPU.
	 */

	stcr	TMP,  SSBR	/* done with SSBR, TMP now free */
	RESTORE_TMP2(%r31)	/* done with extra temp regs */
	RESTORE_TMP3(%r31)	/* done with extra temp regs */

	/* Get the current PSR and modify for the rte to enable the FPU */
	ldcr	TMP,  PSR
	clr	TMP,  TMP, 1<PSR_FPU_DISABLE_BIT>	/* enable the FPU */
	clr	TMP,  TMP, 1<PSR_SHADOW_FREEZE_BIT>	/* and shadowing */
	stcr	TMP,  EPSR

	/* the "+2" below is to set the VALID_BIT */
	or.u	TMP,  %r0, %hi16(_ASM_LABEL(m88100_fpu_enable) + 2)
	or	TMP,  TMP, %lo16(_ASM_LABEL(m88100_fpu_enable) + 2)
	stcr	TMP,  SNIP
	addu	TMP,  TMP, 4
	stcr	TMP,  SFIP

	set	FLAGS, FLAGS, 1<FLAG_ENABLING_FPU>
	xcr	FLAGS, FLAGS, SR1
	st	%r1,   %r31,  EF_RET		/* save the return address */
	ld	%r1,   %r31,  GENREG_OFF(1)	/* get original r1 */

	xcr	TMP,   %r31,  SR3	/* TMP now restored. R31 now saved in SR3 */
	ld	%r31,  %r31,  GENREG_OFF(31)	/* get original r31 */

	/*
	 * SR1: CPU flags
	 * SR2: free
	 * SR3: pointer to our exception frame (our stack pointer)
	 * r1 through r31: original exception-time values
	 *
	 * Valid in the exception frame:
	 *   Exception-time FLAGS.
	 *   Exception-time epsr, sfip, snip, sxip.
	 *   Exception number (EF_VECTOR).
	 *   Dmt0
	 *   Other data pipeline control registers, if appropriate.
	 *   FPU control registers, if appropriate.
	 *   Exception SR3, if appropriate.
	 *   Held temporarly in the exception frame:
	 *   Return address to the calling exception handler.
	 *
	 * immediate goal:
	 *   Do an RTE to restart the fpu and jump to "fpu_enable"
	 *   Another exception (or exceptions) may be raised in
	 *   this, which is why FLAG_ENABLING_FPU is set in SR1.
	 */

	RTE	/* jumps to "m88100_fpu_enable" to enable the FPU. */

ASLOCAL(m88100_fpu_enable)
	FLUSH_PIPELINE
	xcr	TMP,  TMP,  SR3			/* get E.F. pointer */
	st	%r30, TMP,  GENREG_OFF(30)	/* save previous r30, r31 */
	st	%r31, TMP,  GENREG_OFF(31)	/* save previous r30, r31 */
	or	%r31, TMP,  %r0			/* transfer E.F. pointer to r31 */
	ld	TMP,  %r31, EF_SR3		/* get previous SR3 */

	/* make sure that the FLAG_ENABLING_FPU bit is off */
	xcr	FLAGS,FLAGS,SR1
	clr	FLAGS,FLAGS,1<FLAG_ENABLING_FPU>
	xcr	FLAGS,FLAGS,SR1

	xcr	TMP,  TMP,  SR3	/* replace TMP, SR3 */

	/* now save all regs to the exception frame. */
	st	%r0,  %r31, GENREG_OFF(0)
	st	%r1,  %r31, GENREG_OFF(1)
	st	%r2,  %r31, GENREG_OFF(2)
	st	%r3,  %r31, GENREG_OFF(3)
	st	%r4,  %r31, GENREG_OFF(4)
	st	%r5,  %r31, GENREG_OFF(5)
	st	%r6,  %r31, GENREG_OFF(6)
	st	%r7,  %r31, GENREG_OFF(7)
	st	%r8,  %r31, GENREG_OFF(8)
	st	%r9,  %r31, GENREG_OFF(9)
	st	%r10, %r31, GENREG_OFF(10)
	st	%r11, %r31, GENREG_OFF(11)
	st	%r12, %r31, GENREG_OFF(12)
	st	%r13, %r31, GENREG_OFF(13)
	st	%r14, %r31, GENREG_OFF(14)
	st	%r15, %r31, GENREG_OFF(15)
	st	%r16, %r31, GENREG_OFF(16)
	st	%r17, %r31, GENREG_OFF(17)
	st	%r18, %r31, GENREG_OFF(18)
	st	%r19, %r31, GENREG_OFF(19)
	st	%r20, %r31, GENREG_OFF(20)
	st	%r21, %r31, GENREG_OFF(21)
	st	%r22, %r31, GENREG_OFF(22)
	st	%r23, %r31, GENREG_OFF(23)
	st	%r24, %r31, GENREG_OFF(24)
	st	%r25, %r31, GENREG_OFF(25)
	st	%r26, %r31, GENREG_OFF(26)
	st	%r27, %r31, GENREG_OFF(27)
	st	%r28, %r31, GENREG_OFF(28)
	st	%r29, %r31, GENREG_OFF(29)

	/*
	 * SR1: free
	 * SR2: free
	 * SR3: previous exception-time SR3
	 * r1: return address to the calling exception handler
	 * r2 through r30: free
	 * r31: our exception frame
	 *
	 * Valid in the exception frame:
	 *   Exception-time r0 through r31.
	 *   Exception-time FLAGS.
	 *   Exception-time epsr, sfip, snip, sxip.
	 *   Exception number (EF_VECTOR).
	 *   Dmt0
	 *   Other data pipeline control registers, if appropriate.
	 *   FPU control registers, if appropriate.
	 *   Exception SR3, if appropriate.
	 *
	 * immediate goal:
	 *   Pick up a stack if we came in from user mode.
	 *   Put a copy of the exception frame pointer into r30
	 *   Bump the stack a doubleword and write the exception frame pointer.
	 *   If not an interrupt exception, turn on interrupts and service any
	 *     outstanding data access exceptions.
	 *   Return to calling exception handler to service the exception.
	 */

	/*
	 * If it's not the interrupt exception, enable interrupts and
	 * take care of any data access exceptions......
	 */
	or	%r30, %r0,  %r31	/* get a copy of the e.f. pointer */
	ld	%r6,  %r31, EF_EPSR
	bb1	PSR_SUPERVISOR_MODE_BIT, %r6, 1f	/* if in kernel mode */

	ldcr	%r31, CPU
	ld	%r31, %r31, CI_CURPCB
	addu	%r31, %r31, USPACE	/* point at proper end */
1:

	/* get and save IPL */
	bsr	_C_LABEL(getipl)
	st	%r2, %r30, EF_MASK

	/*
	 * here - r30 holds a pointer to the exception frame.
	 * r31 is a pointer to the kernel stack/interrupt stack.
	 */
	subu	%r31, %r31, 8	/* make some breathing space */
	st	%r30, %r31, 0	/* store frame pointer on the stack */
#ifdef DDB
	st	%r30, %r31, 4	/* store it for the debugger to recognize */
#endif

	ld	%r6,  %r30, EF_EPSR
	ld	%r2,  %r30, EF_VECTOR
	bcnd.n	eq0,  %r2,  8f			/* error exception */
	 ld	%r14, %r30, EF_RET

	/*
	 * Do not process possible data exceptions here if this is an interrupt.
	 * Instead, the interrupt handler will take care of this by itself.
	 */
	cmp	%r3,  %r2,  1			/* is an interrupt? */
	bb1.n	eq,   %r3,  8f			/* skip if so */

#ifdef DDB
	 cmp	%r3,  %r2,  130			/* DDB break exception */
	bb1.n	eq,   %r3,  8f
	 cmp	%r3,  %r2,  132			/* DDB entry exception */
	bb1	eq,   %r3,  8f
#else
	 NOP
#endif

	/* turn interrupts back on unless they were not enabled when the
	   trap occured */
	bb1.n	PSR_INTERRUPT_DISABLE_BIT, %r6, 7f
	 ld	%r3,  %r30, EF_DMT0

	ldcr	%r2,  PSR
	clr	%r2,  %r2,  1<PSR_INTERRUPT_DISABLE_BIT>
	stcr	%r2,  PSR
	FLUSH_PIPELINE
7:
	/* service any outstanding data pipeline stuff */
	bb0	DMT_VALID_BIT, %r3, 8f

	/*
	 * r30 can be clobbered by calls. So stuff its value into a preserved
	 * register, say r15. R14 is in use (see return_to_... below).
	 */
	or	%r15, %r0,  %r30
	CALL(m88100_trap, T_DATAFLT, %r15)
	or	%r30, %r0,  %r15

8:
	jmp	%r14	/* loaded above */
#endif /* M88100 */

#ifdef M88110
/*
 * 88110 exception handlers
 */

/* unknown exception handler */
GLOBAL(m88110_unknown_handler)
	PREP88110("unknown", 0,)
	or	%r2, %r0, T_UNKNOWNFLT
	or	%r3, %r0, %r30
	XCALL(_C_LABEL(m88110_trap), _ASM_LABEL(check_ast))

/* interrupt exception handler */
GLOBAL(m88110_interrupt_handler)
	PREP88110("interrupt", 1,)
	or	%r2, %r0, %r30
	XCALL(_C_LABEL(interrupt), _ASM_LABEL(check_ast))

/* instruction access exception handler */
GLOBAL(m88110_instruction_access_handler)
	PREP88110("inst", 2,)
	or	%r2, %r0, T_INSTFLT
	or	%r3, %r0, %r30
	XCALL(_C_LABEL(m88110_trap), _ASM_LABEL(check_ast))
/*
 * data access exception handler --
 *  See badaddr() below for info about Data_Precheck.
 */
GLOBAL(m88110_data_exception_handler_bootstrap)
	PREP88110("data", 3, M88110_Data_Precheck)
	or	%r2, %r0, T_DATAFLT
	or	%r3, %r0, %r30
	XCALL(_C_LABEL(m88110_trap), _ASM_LABEL(check_ast))
GLOBAL(m88110_data_exception_handler)
	PREP88110("data", 3,)
	or	%r2, %r0, T_DATAFLT
	or	%r3, %r0, %r30
	XCALL(_C_LABEL(m88110_trap), _ASM_LABEL(check_ast))

/* misaligned access exception handler */
GLOBAL(m88110_misaligned_handler)
	PREP88110("misalign", 4,)
	or	%r2, %r0, T_MISALGNFLT
	or	%r3, %r0, %r30
	XCALL(_C_LABEL(m88110_trap), _ASM_LABEL(check_ast))

/* unimplemented opcode exception handler */
GLOBAL(m88110_unimplemented_handler)
	PREP88110("unimp", 5,)
	or	%r2, %r0, T_ILLFLT
	or	%r3, %r0, %r30
	XCALL(_C_LABEL(m88110_trap), _ASM_LABEL(check_ast))

/* privilege exception handler */
GLOBAL(m88110_privilege_handler)
	PREP88110("privilege", 6,)
	or	%r2, %r0, T_PRIVINFLT
	or	%r3, %r0, %r30
	XCALL(_C_LABEL(m88110_trap), _ASM_LABEL(check_ast))

/* bounds checking exception handler */
GLOBAL(m88110_bounds_handler)
	PREP88110("bounds", 7,)
	or	%r2, %r0, T_BNDFLT
	or	%r3, %r0, %r30
	XCALL(_C_LABEL(m88110_trap), _ASM_LABEL(check_ast))

/* integer divide-by-zero exception handler */
GLOBAL(m88110_divide_handler)
	PREP88110("divide", 8,)
	or	%r2, %r0, T_ZERODIV
	or	%r3, %r0, %r30
	XCALL(_C_LABEL(m88110_trap), _ASM_LABEL(check_ast))

/* integer overflow exception handler */
GLOBAL(m88110_overflow_handler)
	PREP88110("overflow", 9,)
	or	%r2, %r0, T_OVFFLT
	or	%r3, %r0, %r30
	XCALL(_C_LABEL(m88110_trap), _ASM_LABEL(check_ast))

/* Floating-point precise handler */
GLOBAL(m88110_fpu_handler)
	PREP88110("FPU", 114,)
	or	%r2, %r0, T_FPEPFLT
	or	%r3, %r0, %r30
	XCALL(_C_LABEL(m88110_trap), _ASM_LABEL(check_ast))

/* non-maskable interrupt handler (IPIs, ABORT button) */
GLOBAL(m88110_nonmaskable)
	PREP88110("NMI", 11,)
	or	%r2, %r0, %r30
	XCALL(_C_LABEL(nmi), _ASM_LABEL(nmi_return))

/* software walk data MMU read miss handler */
GLOBAL(m88110_data_read_miss)
	PREP88110("88110 data read miss", 12,)
	or	%r2, %r0, T_110_DRM
	or	%r3, %r0, %r30
	XCALL(_C_LABEL(m88110_trap), _ASM_LABEL(check_ast))

/* software walk data MMU write miss handler */
GLOBAL(m88110_data_write_miss)
	PREP88110("88110 data write miss", 13,)
	or	%r2, %r0, T_110_DWM
	or	%r3, %r0, %r30
	XCALL(_C_LABEL(m88110_trap), _ASM_LABEL(check_ast))

/* software walk inst MMU ATC miss handler */
GLOBAL(m88110_inst_atc_miss)
	PREP88110("88110 inst ATC miss", 14,)
	or	%r2, %r0, T_110_IAM
	or	%r3, %r0, %r30
	XCALL(_C_LABEL(m88110_trap), _ASM_LABEL(check_ast))

/* trap 450: system calls */
GLOBAL(m88110_syscall_handler)
	PREP88110("syscall", 450,)
	ld	%r2, %r30, GENREG_OFF(13)
	or	%r3, %r0, %r30
	XCALL(_C_LABEL(m88110_syscall), _ASM_LABEL(check_ast))

/* trap 451: cache flush (necessary for trampolines) */
GLOBAL(m88110_cache_flush_handler)
	PREP88110("cache_flush", 451,)
	or	%r2, %r0, %r30
	XCALL(_C_LABEL(cache_flush), _ASM_LABEL(check_ast))

GLOBAL(m88110_sigsys)
	PREP88110("sigsys", 501,)
	or	%r2, %r0, T_SIGSYS
	or	%r3, %r0, %r30
	XCALL(_C_LABEL(m88110_trap), _ASM_LABEL(check_ast))

GLOBAL(m88110_stepbpt)
	PREP88110("stepbpt", 504,)
	or	%r2, %r0, T_STEPBPT
	or	%r3, %r0, %r30
	XCALL(_C_LABEL(m88110_trap), _ASM_LABEL(check_ast))

GLOBAL(m88110_userbpt)
	PREP88110("userbpt", 511,)
	or	%r2, %r0, T_USERBPT
	or	%r3, %r0, %r30
	XCALL(_C_LABEL(m88110_trap), _ASM_LABEL(check_ast))

#ifdef DDB
GLOBAL(m88110_break)
	PREP88110("break", 130,)
	or	%r2, %r0, T_KDB_BREAK
	or	%r3, %r0, %r30
	XCALL(_C_LABEL(m88110_trap), _ASM_LABEL(check_ast))

GLOBAL(m88110_trace)
	PREP88110("trace", 131,)
	or	%r2, %r0, T_KDB_TRACE
	or	%r3, %r0, %r30
	XCALL(_C_LABEL(m88110_trap), _ASM_LABEL(check_ast))

GLOBAL(m88110_entry)
	PREP88110("kdb", 132,)
	or	%r2, %r0, T_KDB_ENTRY
	or	%r3, %r0, %r30
	XCALL(_C_LABEL(m88110_trap), _ASM_LABEL(check_ast))
#endif

/*
 * The error exception and reset exception handler.
 *
 * The error exception is raised when any other non-trap exception is raised
 * while shadowing is off. This is Bad News.
 *
 * The reset exception is raised when the RST signal is asserted (machine
 * is reset), the value of VBR is changed after exceptions are enabled,
 * or when a jmp, br/bsr to addr 0 (accidents do happen :-)
 * Upon a real reset, VBR is set to zero (0), so code must be at addr 0
 * to handle it!!!
 *
 * The shadow registers are not valid in this case (shadowing was off, if this
 * was an error exception, and may not be on, if this was a reset exception).
 * R1-R31 may be interesting though, so we'll save them.
 *
 * We'll not worry about trashing r26-29 here,
 * since they aren't generally used.
 */
GLOBAL(m88110_error_handler)
	or	%r29, %r0, 10
	br	1f
GLOBAL(m88110_reset_handler)
	or	%r29, %r0, 0
1:
	or	%r26, %r0,  %r31	/* save old stack */
	or.u	%r31, %r0,  %hi16(_ASM_LABEL(initstack_end))
	or	%r31, %r31, %lo16(_ASM_LABEL(initstack_end))

#ifdef DEBUG
	/* zero the stack, so we'll know what we're lookin' at */
	or.u	%r27, %r0,  %hi16(_ASM_LABEL(initstack))
	or	%r27, %r27, %lo16(_ASM_LABEL(initstack))
1:	cmp	%r28, %r27, %r31
	bb1	ge,   %r28, 2f	/* branch if at the end of the stack */
	st	%r0,  %r0,  %r27
	addu	%r27, %r27, 4	/* bump up */
	br	1b
2:	/* stack has been cleared */
#endif

	/* ensure that stack is 8-byte aligned */
	clr	%r31, %r31, 3<0>	/* round down to 8-byte boundary */

	/* create exception frame on stack */
	subu	%r31, %r31, TRAPFRAME_SIZEOF	/* r31 now our E.F. */

	/* save old R31 and other R registers */
	st.d	%r0,  %r31, GENREG_OFF(0)
	st.d	%r2,  %r31, GENREG_OFF(2)
	st.d	%r4,  %r31, GENREG_OFF(4)
	st.d	%r6,  %r31, GENREG_OFF(6)
	st.d	%r8,  %r31, GENREG_OFF(8)
	st.d	%r10, %r31, GENREG_OFF(10)
	st.d	%r12, %r31, GENREG_OFF(12)
	st.d	%r14, %r31, GENREG_OFF(14)
	st.d	%r16, %r31, GENREG_OFF(16)
	st.d	%r18, %r31, GENREG_OFF(18)
	st.d	%r20, %r31, GENREG_OFF(20)
	st.d	%r22, %r31, GENREG_OFF(22)
	st.d	%r24, %r31, GENREG_OFF(24)
	st	%r30, %r31, GENREG_OFF(30)
	st	%r26, %r31, GENREG_OFF(31)

	/* vector is put in SRO (either 0 or 10 at this point) */
	st	%r29, %r31, EF_VECTOR

	/* save shadow registers (are OLD if error_handler, though) */
	ldcr	%r10, EPSR
	st	%r10, %r31, EF_EPSR
	ldcr	%r10, EXIP
	st	%r10, %r31, EF_EXIP
	ldcr	%r10, ENIP
	st	%r10, %r31, EF_ENIP
	ldcr	%r10, DSR
	st	%r10, %r31, EF_DSR
	ldcr	%r10, DLAR
	st	%r10, %r31, EF_DLAR
	ldcr	%r10, DPAR
	st	%r10, %r31, EF_DPAR
	ldcr	%r10, ISR
	st	%r10, %r31, EF_ISR
	ldcr	%r10, ILAR
	st	%r10, %r31, EF_ILAR
	ldcr	%r10, IPAR
	st	%r10, %r31, EF_IPAR
	ldcr	%r10, SR1
	st	%r10, %r31, EF_FLAGS

	/* shove sr2 into EF_FPLS1 */
	ldcr	%r10, SR2
	st	%r10, %r31, EF_FPLS1

	/* shove sr3 into EF_FPHS2 */
	ldcr	%r10, SR3
	st	%r10, %r31, EF_FPHS2

	/*
	 * Cheap way to enable FPU and start shadowing again.
	 */
	ldcr	%r10, PSR
	clr	%r10, %r10, 1<PSR_FPU_DISABLE_BIT>	/* enable the FPU */
	clr	%r10, %r10, 1<PSR_SHADOW_FREEZE_BIT>	/* and shadowing */
	stcr	%r10, PSR
	FLUSH_PIPELINE

	/* put pointer to regs into r30... r31 will become a simple stack */
	or	%r30, %r31, %r0

	subu	%r31, %r31, 0x10	/* make some breathing space */
	st	%r30, %r31, 0x0c	/* store frame pointer on the stack */
#ifdef DDB
	st	%r30, %r31, 0x08	/* store again for the debugger to recognize */
	or.u	%r20,  %r0, %hi16(0x87654321)
	or	%r20, %r20, %lo16(0x87654321)
	st	%r20, %r31, 0x04
	st	%r20, %r31, 0x00
#endif

	or	%r2, %r0, %r30
	bsr	_C_LABEL(error_fatal)

	/* turn interrupts back on */
	ldcr	%r1, PSR
	clr	%r1, %r1, 1<PSR_INTERRUPT_DISABLE_BIT>
	stcr	%r1, PSR
	FLUSH_PIPELINE

1:
	br	1b
	/* NOTREACHED */

/*
 * 88110 exception handling setup
 *
 * This is much simpler than for 88100, because all exception are
 * precise. Therefore, when reenabling shadowing, we do not risk
 * getting new exceptions caught by the execution pipeline and not
 * reported yet.
 *
 * However, as soon as shadow freezing is over, we can receive a
 * non-maskable interrupt at any time. The code below will cope with
 * this, as long as the stack pointer (r31) is valid in the kernel
 * all the time shadowing is enabled.
 *
 * Thus, unlike the 88100 code, we setup both the exception frame
 * (in r30) and the exception stack (in r31) as early as possible.
 */

ASLOCAL(m88110_setup_phase_one)
	/*
	 * SR1: saved copy of exception-time register now holding FLAGS
	 * SR2: saved copy of exception-time r1
	 * SR3: free
	 * r1: return address to calling exception handler
	 * FLAGS: CPU status flags
	 *
	 * immediate goal:
	 *   Find out where to put the exception frame, and which
	 *   stack to use.
	 */

	NOP
	xcr	%r1,   %r1,   SR2	/* recover exception-time r1 */
	NOP
	NOP
	NOP

	/*
	 * If we were in the kernel when the exception occured, we have
	 * a valid stack. Keep using it, and build the frame on it.
	 *
	 * Note that if this exception is an NMI, we might be currently
	 * switching processes, and curpcb and curproc won't match. It
	 * is nevertheless safe to use curpcb, since nmi are processed
	 * with interrupts disabled, and we won't check for AST or soft
	 * interrupts before returning to the cpu_switchto kernel code.
	 */
	bb1	FLAG_FROM_KERNEL, FLAGS, _ASM_LABEL(m88110_kernel_stack)

	/*
	 * Otherwise, this is an exception in user mode, we'll use the PCB
	 * for the exception frame and the top of the PCB as the stack.
	 */

	/* compute frame address: in PCB */
	stcr	%r30, SR3			/* save r30, now free */
	ldcr	%r30, CPU
	ld	%r30, %r30, CI_CURPCB
	addu	%r30, %r30, PCB_USER_STATE	/* point to user save area */

	/* save a few registers before we lose them*/
	st	%r1,  %r30, GENREG_OFF(1)	/* save prev. r1 (now free)*/
	ldcr	%r1,  SR3			/* save previous r30 */
	st	%r31, %r30, GENREG_OFF(31)
	st	%r1,  %r30, GENREG_OFF(30)

	/* compute stack address: top of U area */
	ldcr	%r1,  CPU
	ld	%r31, %r1,  CI_CURPCB
	addu	%r31, %r31, USPACE

	br	_ASM_LABEL(m88110_have_stack)

ASLOCAL(m88110_kernel_stack)		/* Exception in kernel mode */

	/* compute stack and frame address: allocate them on current stack */
	subu	%r31, %r31, TRAPFRAME_SIZEOF	/* r31 now our E.F. */

	/* save a few registers before we lose them */
	st	%r1,  %r31, GENREG_OFF(1)	/* save prev. r1 (now free) */
	addu	%r1,  %r31, TRAPFRAME_SIZEOF	/* save previous r31 */
	st	%r30, %r31, GENREG_OFF(30)
	st	%r1,  %r31, GENREG_OFF(31)

	/* frame = stack */
	or	%r30, %r31, %r0

	/* FALLTHROUGH */

ASLOCAL(m88110_have_stack)
	/*
	 * SR1: saved copy of exception-time register now holding FLAGS
	 * SR2: return address to the calling exception handler
	 * SR3: free
	 * r1:  free
	 * FLAGS: CPU status flags
	 * r30: incomplete exception frame
	 * r31: exception stack
	 * Valid in the exception frame:
	 *   Exception-time r1, r30, r31, FLAGS.
	 */

	stcr	TMP, SR3	/* free up TMP, TMP2, TMP3 */
	SAVE_TMP2(%r30)
	SAVE_TMP3(%r30)

	/* save some exception-time registers to the exception frame */
	st	FLAGS,%r30, EF_FLAGS		/* save flags */

	ldcr	TMP,  EPSR
	st	TMP,  %r30, EF_EPSR
	ldcr	TMP2, EXIP
	ldcr	TMP3, ENIP
	st	TMP2, %r30, EF_EXIP
	st	TMP3, %r30, EF_ENIP

	/* get and store the cpu_info pointer */
	ldcr	TMP,  CPU
	st	TMP,  %r30, EF_CPU

	/*
	 * Save and clear fault status registers.
	 */
	ldcr	TMP,  ISR
	st	TMP,  %r30, EF_ISR
	bcnd	eq0,  TMP,  1f
	ldcr	TMP2, ILAR
	ldcr	TMP3, IPAR
	st	TMP2, %r30, EF_ILAR
	st	TMP3, %r30, EF_IPAR
	ldcr	TMP,  ISAP
	ldcr	TMP2, IUAP
	st	TMP,  %r30, EF_ISAP
	st	TMP2, %r30, EF_IUAP
	stcr	%r0,  ISR
1:
	ldcr	TMP,  DSR
	st	TMP,  %r30,  EF_DSR
	bcnd	eq0,  TMP,  1f
	ldcr	TMP2, DLAR
	ldcr	TMP3, DPAR
	st	TMP2, %r30,  EF_DLAR
	st	TMP3, %r30,  EF_DPAR
	ldcr	TMP,  DSAP
	ldcr	TMP2, DUAP
	st	TMP,  %r30,  EF_DSAP
	st	TMP2, %r30,  EF_DUAP
	stcr	%r0,  DSR
1:
	ldcr	%r1,  SR2
	jmp	%r1

ASLOCAL(m88110_setup_phase_two)
	/*
	 * SR1: saved copy of exception-time register now holding FLAGS
	 * SR2: free
	 * SR3: saved TMP
	 * r1:  return address to calling exception handler
	 * TMP2: free
	 * TMP3: free
	 * FLAGS: CPU status flags
	 * r30: incomplete exception frame
	 * r31: exception stack
	 * Valid in the exception frame:
	 *   Exception-time r1, r30, r31, FLAGS.
	 *   Exception-time TMP2, TMP3.
	 *   Exception-time epsr, enip, exip.
	 *   Exception number (EF_VECTOR).
	 *
	 * immediate goal:
	 *   restore the system to the exception-time state.
	 */

	RESTORE_TMP2(%r30)	/* done with extra temp regs */
	RESTORE_TMP3(%r30)	/* done with extra temp regs */

	ldcr	TMP,  PSR
	clr	TMP,  TMP,  1<PSR_SHADOW_FREEZE_BIT>	/* enable shadowing */
	stcr	TMP,  EPSR

	or.u	TMP,  %r0,  %hi16(_ASM_LABEL(m88110_shadow_enable))
	or	TMP,  TMP,  %lo16(_ASM_LABEL(m88110_shadow_enable))
	stcr	TMP,  EXIP

	xcr	FLAGS,FLAGS,SR1
	st	%r1,  %r30, EF_RET		/* save the return address */

	xcr	TMP,  %r31, SR3	/* TMP now restored. R31 now saved in SR3 */

	/*
	 * SR1: CPU flags
	 * SR2: free
	 * SR3: pointer to our exception frame (our stack pointer)
	 * r2 through r29: original exception-time values
	 * r30: exception frame
	 * r31: exception stack
	 *
	 * Valid in the exception frame:
	 *   Exception-time r1, r30, r31, FLAGS.
	 *   Exception-time epsr, sfip, enip, exip.
	 *   Exception number (EF_VECTOR).
	 *   DSR/ISR, fault registers, if appropriate.
	 * Held temporarily in the exception frame:
	 *   Return address to the calling exception handler.
	 *
	 * immediate goal:
	 *   Do an RTE to unfreeze the shadow registers.
	 *   Another exception (NMI) may be raised in this.
	 */
	RTE	/* jumps to "m88110_shadow_enable" */

ASLOCAL(m88110_shadow_enable)
	FLUSH_PIPELINE				/* XXX necessary? */

	/* now save all missing regs to the exception frame. */
	st	%r0,  %r30, GENREG_OFF(0)
	st	%r2,  %r30, GENREG_OFF(2)
	st	%r3,  %r30, GENREG_OFF(3)
	st	%r4,  %r30, GENREG_OFF(4)
	st	%r5,  %r30, GENREG_OFF(5)
	st	%r6,  %r30, GENREG_OFF(6)
	st	%r7,  %r30, GENREG_OFF(7)
	st	%r8,  %r30, GENREG_OFF(8)
	st	%r9,  %r30, GENREG_OFF(9)
	st	%r10, %r30, GENREG_OFF(10)
	st	%r11, %r30, GENREG_OFF(11)
	st	%r12, %r30, GENREG_OFF(12)
	st	%r13, %r30, GENREG_OFF(13)
	st	%r14, %r30, GENREG_OFF(14)
	st	%r15, %r30, GENREG_OFF(15)
	st	%r16, %r30, GENREG_OFF(16)
	st	%r17, %r30, GENREG_OFF(17)
	st	%r18, %r30, GENREG_OFF(18)
	st	%r19, %r30, GENREG_OFF(19)
	st	%r20, %r30, GENREG_OFF(20)
	st	%r21, %r30, GENREG_OFF(21)
	st	%r22, %r30, GENREG_OFF(22)
	st	%r23, %r30, GENREG_OFF(23)
	st	%r24, %r30, GENREG_OFF(24)
	st	%r25, %r30, GENREG_OFF(25)
	st	%r26, %r30, GENREG_OFF(26)
	st	%r27, %r30, GENREG_OFF(27)
	st	%r28, %r30, GENREG_OFF(28)
	st	%r29, %r30, GENREG_OFF(29)

	/*
	 * SR1: free
	 * SR2: free
	 * SR3: free
	 * r1 through r29: free
	 * r30: exception frame
	 * r31: exception stack
	 *
	 * Valid in the exception frame:
	 *   Exception-time r0 through r31.
	 *   Exception-time FLAGS.
	 *   Exception-time epsr, enip, exip.
	 *   Exception number (EF_VECTOR).
	 *   Return value (EF_RET).
	 *   DSR/ISR, fault registers, if appropriate.
	 *
	 * immediate goal:
	 *   Bump the stack a doubleword and write the exception frame pointer.
	 *   If not an interrupt exception or an NMI, turn on interrupts.
	 *   Return to calling exception handler to service the exception.
	 */

	/* get and save IPL */
	bsr	_C_LABEL(getipl)
	st	%r2,  %r30, EF_MASK

	subu	%r31, %r31, 8	/* make some breathing space */
	st	%r30, %r31, 0	/* store frame pointer on the stack */
#ifdef DDB
	st	%r30, %r31, 4	/* store it again for the debugger */
#endif

	ld	%r6,  %r30, EF_EPSR
	ld	%r14, %r30, EF_RET

	/* don't turn interrupts back on unless they were enabled when the
	   trap occured */
	bb1	PSR_INTERRUPT_DISABLE_BIT, %r6, 8f

	ld	%r2,  %r30, EF_VECTOR
	bcnd	eq0,  %r2,  8f
	cmp	%r3,  %r2,  1		/* is this an interrupt? */
	bb1	eq,   %r3,  8f
	cmp	%r3,  %r2,  11		/* or NMI? */
	bb1	eq,   %r3,  8f

#ifdef DDB
	cmp	%r3,  %r2,  130		/* DDB break exception */
	bb1	eq,   %r3,  8f
	cmp	%r3,  %r2,  132		/* DDB entry exception */
	bb1	eq,   %r3,  8f
#endif

	ldcr	%r2,  PSR
	clr	%r2,  %r2,  1<PSR_INTERRUPT_DISABLE_BIT>
	stcr	%r2,  PSR
	FLUSH_PIPELINE

8:
	jmp	%r14		/* loaded above */
#endif	/* M88110 */

	.text

#define	FPTR	%r14

ASGLOBAL(check_ast)
	/*
	 * Regs r1-r30 are free. r31 is pointing at the word
	 * on the kernel stack where our pointer to the exception frame
	 * it stored. Reload it now.
	 *
	 * We load it into r14 since it is preserved across function
	 * calls, and we may have to call some routines from within here.
	 *
	 * Control is transferred here from obvious places in this file.
	 */
	ld	FPTR, %r31, 0	/* grab exception frame pointer */

	/*
	 * If the saved ipl is 0, then call spl0() to process soft
	 * interrupts. And if returning to userland, look for ASTs.
	 */

	/* do not service AST and soft interrupts if interrupts were disabled */
	ld	%r2, FPTR, EF_EPSR
	bb1	PSR_INTERRUPT_DISABLE_BIT, %r2, _ASM_LABEL(ast_done)
	/* ...or we were not at spl0 */
	ld	%r2, FPTR, EF_MASK
	bcnd	ne0, %r2, _ASM_LABEL(ast_done)

#ifdef MULTIPROCESSOR
	/*
	 * Check for IPI soft interrupt
	 */
	ldcr	%r3,  CPU
	or	%r15, %r0,  %r0
	addu	%r3,  %r3,  CI_SOFTIPI_CB
	xmem	%r15, %r3,  %r0
	bcnd	eq0,  %r15, _ASM_LABEL(do_softint)

	bsr.n	_C_LABEL(setipl)
	 or	%r2,  %r0,  IPL_NONE

	jsr	%r15
ASLOCAL(do_softint)
#endif

	/*
	 * Process soft interrupts, if any.
	 */
	bsr	_C_LABEL(spl0)

	/* do not service AST if not returning to user mode */
	ld	%r2, FPTR, EF_EPSR
	bb1	PSR_SUPERVISOR_MODE_BIT, %r2, _ASM_LABEL(ast_done)
1:
	ldcr	%r2, CPU
	ld	%r3, %r2, CI_CURPROC
	ld	%r2, %r3, P_ASTPENDING
	bcnd	eq0, %r2, _ASM_LABEL(ast_done)

	bsr.n	_C_LABEL(ast)
	 or	%r2, %r0, FPTR

	br	1b

/*
 * void proc_trampoline(void (*func)(void *), void *proc)
 *
 * When a process setup by cpu_fork() resumes, it will find itself in
 * proc_trampoline, with r31 pointing to a ksigframe. proc_trampoline will
 * load func and proc values from ksigframe, call the function, and on return
 * pop off the ksigframe. Then, it will return to userland.
 */

ENTRY(proc_trampoline)
#ifdef MULTIPROCESSOR
	bsr	_C_LABEL(proc_trampoline_mp)
#endif

	bsr.n	_C_LABEL(setipl)	/* setipl(IPL_NONE) */
	 or	%r2,  %r0,  %r0

	ld	%r1,  %r31, 0		/* load func */
	ld	%r2,  %r31, 4 		/* load arg */
	jsr.n	%r1
	 addu	%r31, %r31, 8		/* release ksigframe */

/*
 * Load FPTR with a pointer to the trap frame for the current proc and
 * continue near the end of check_ast, bypassing soft interrupts and AST
 * checks, to load all the registers and do an RTE.
 */

	ldcr	%r3,  CPU
	ld	%r2,  %r3,  CI_CURPROC

	ld	FPTR, %r2,   P_ADDR		/* p->p_addr */
	addu	FPTR, FPTR, PCB_USER_STATE	/* p->p_addr.u_pcb.user_state */

	/* FALLTHROUGH */

ASGLOBAL(ast_done)
	/* disable interrupts */
	ldcr	%r1, PSR
	set	%r1, %r1, 1<PSR_INTERRUPT_DISABLE_BIT>
	stcr	%r1, PSR
	FLUSH_PIPELINE

	/* now ready to return....*/
	bsr.n	_C_LABEL(setipl)
	 ld	%r2, FPTR, EF_MASK		/* get pre-exception ipl */

#if defined(M88100) && defined(M88110)
	ldcr	%r2, PID
	extu	%r3, %r2, 8<8>
	bcnd	ne0, %r3, _ASM_LABEL(m88110_user_rte)
	/* FALLTHROUGH */
#endif

#ifdef M88100
ASGLOBAL(m88100_user_rte)
	/*
	 * Transfer the frame pointer to r31, since we no longer need a stack.
	 * No page faults here, and interrupts are disabled.
	 */
	or	%r31, %r0,  FPTR

	/* restore r1 later */
	ld	%r2,  %r31, GENREG_OFF(2)
	ld	%r3,  %r31, GENREG_OFF(3)
	ld	%r4,  %r31, GENREG_OFF(4)
	ld	%r5,  %r31, GENREG_OFF(5)
	ld	%r6,  %r31, GENREG_OFF(6)
	ld	%r7,  %r31, GENREG_OFF(7)
	ld	%r8,  %r31, GENREG_OFF(8)
	ld	%r9,  %r31, GENREG_OFF(9)
	ld	%r10, %r31, GENREG_OFF(10)
	ld	%r11, %r31, GENREG_OFF(11)
	ld	%r12, %r31, GENREG_OFF(12)
	ld	%r13, %r31, GENREG_OFF(13)
	ld	%r14, %r31, GENREG_OFF(14)
	ld	%r15, %r31, GENREG_OFF(15)
	ld	%r16, %r31, GENREG_OFF(16)
	ld	%r17, %r31, GENREG_OFF(17)
	ld	%r18, %r31, GENREG_OFF(18)
	ld	%r19, %r31, GENREG_OFF(19)
	ld	%r20, %r31, GENREG_OFF(20)
	ld	%r21, %r31, GENREG_OFF(21)
	ld	%r22, %r31, GENREG_OFF(22)
	ld	%r23, %r31, GENREG_OFF(23)
	ld	%r24, %r31, GENREG_OFF(24)
	ld	%r25, %r31, GENREG_OFF(25)
	ld	%r26, %r31, GENREG_OFF(26)
	ld	%r27, %r31, GENREG_OFF(27)
	ld	%r28, %r31, GENREG_OFF(28)
	ld	%r29, %r31, GENREG_OFF(29)
	/* restore r1, r30, r31 later */

	/* disable shadowing */
	ldcr	%r1, PSR
	set	%r1, %r1, 1<PSR_SHADOW_FREEZE_BIT>
	stcr	%r1, PSR
	FLUSH_PIPELINE

	/* reload the control regs*/
	/*
	 * RTE will cause execution to continue first with the
	 * instruction pointed to by the NIP and then the FIP;
	 * it is not necessary to restore XIP.
	 */
	stcr	%r0,  SSBR
	ld	%r30, %r31, EF_SNIP
	ld	%r1,  %r31, EF_SFIP
	stcr	%r30, SNIP
	stcr	%r1,  SFIP

	ld	%r30, %r31, EF_EPSR
	stcr	%r30, EPSR

	/* Now restore r1, r30, and r31 */
	ld	%r1,  %r31, GENREG_OFF(1)
	ld	%r30, %r31, GENREG_OFF(30)
	ld	%r31, %r31, GENREG_OFF(31)

	RTE
#endif

#ifdef M88110
ASGLOBAL(m88110_user_rte)
	/*
	 * Disable shadowing. This used to be done after all the registers
	 * from the E.F. have been restored, but on 88110 we may receive
	 * an NMI anytime, unless shadowing is frozen, and we rely on r31
	 * being valid.
	 */
	ldcr	%r1, PSR
	set	%r1, %r1, 1<PSR_SHADOW_FREEZE_BIT>
	stcr	%r1, PSR
	FLUSH_PIPELINE

ASLOCAL(m88110_restore)
	/*
	 * Transfer the frame pointer to r31, since we no longer need a stack.
	 * No page faults here, and interrupts are disabled.
	 */
	or	%r31, %r0,  FPTR
	/* restore r1 later */
	ld	%r2,  %r31, GENREG_OFF(2)
	ld	%r3,  %r31, GENREG_OFF(3)
	ld	%r4,  %r31, GENREG_OFF(4)
	ld	%r5,  %r31, GENREG_OFF(5)
	ld	%r6,  %r31, GENREG_OFF(6)
	ld	%r7,  %r31, GENREG_OFF(7)
	ld	%r8,  %r31, GENREG_OFF(8)
	ld	%r9,  %r31, GENREG_OFF(9)
	ld	%r10, %r31, GENREG_OFF(10)
	ld	%r11, %r31, GENREG_OFF(11)
	ld	%r12, %r31, GENREG_OFF(12)
	ld	%r13, %r31, GENREG_OFF(13)
	ld	%r14, %r31, GENREG_OFF(14)
	ld	%r15, %r31, GENREG_OFF(15)
	ld	%r16, %r31, GENREG_OFF(16)
	ld	%r17, %r31, GENREG_OFF(17)
	ld	%r18, %r31, GENREG_OFF(18)
	ld	%r19, %r31, GENREG_OFF(19)
	ld	%r20, %r31, GENREG_OFF(20)
	ld	%r21, %r31, GENREG_OFF(21)
	ld	%r22, %r31, GENREG_OFF(22)
	ld	%r23, %r31, GENREG_OFF(23)
	ld	%r24, %r31, GENREG_OFF(24)
	ld	%r25, %r31, GENREG_OFF(25)
	ld	%r26, %r31, GENREG_OFF(26)
	ld	%r27, %r31, GENREG_OFF(27)
	ld	%r28, %r31, GENREG_OFF(28)
	ld	%r29, %r31, GENREG_OFF(29)
	/* restore r1, r30, r31 later */

	/* reload the control regs*/
	ld	%r30, %r31, EF_ENIP
	ld	%r1,  %r31, EF_EXIP
	stcr	%r30, ENIP
	stcr	%r1,  EXIP

	ld	%r30, %r31, EF_EPSR
	stcr	%r30, EPSR

	/* Now restore r1, r30, and r31 */
	ld	%r1,  %r31, GENREG_OFF(1)
	ld	%r30, %r31, GENREG_OFF(30)
	ld	%r31, %r31, GENREG_OFF(31)

	/*
	 * 88110 errata #18 (fixed in revision 5 cpus):
	 * ``An rte to user mode where the logical address of the rte
	 *   instruction is within two cache lines of the logical address
	 *   in the exip can cause the wrong instruction to be returned
	 *   to if the rte instruction misses in the icache.''
	 *
	 * Since we run with interrupts disabled at this point, all we
	 * need is make sure that the rte will not appear as the first
	 * instruction of a cache line.
	 */
	br	1f

	.align 6	/* 32 bytes might not be enough */
1:
	NOP
	RTE

	/*
	 * NMI return here after processing.
	 * We then decide whether to check for AST and soft interrupts,
	 * or not.
	 */
ASLOCAL(nmi_return)
	bcnd	ne0, %r2, _ASM_LABEL(check_ast)

	ld	FPTR, %r31, 0	/* grab exception frame pointer */

	/*
	 * Disable interrupts and shadowing. The latter used to be done
	 * after all the registers from the E.F. have been restored, but
	 * on 88110 we may receive an NMI anytime, unless shadowing is frozen,
	 * and we rely on r31 being valid.
	 */
	ldcr	%r1, PSR
	set	%r1, %r1, 1<PSR_INTERRUPT_DISABLE_BIT>
	set	%r1, %r1, 1<PSR_SHADOW_FREEZE_BIT>
	stcr	%r1, PSR
	FLUSH_PIPELINE

	/* now ready to return....*/
	bsr.n	_C_LABEL(setipl)
	 ld	%r2, FPTR, EF_MASK		/* get pre-exception ipl */

#ifdef MULTIPROCESSOR
	/*
	 * Reenable NMIs if necessary.
	 */
	or	%r2,   FPTR,  %r0
	bsr	_C_LABEL(nmi_wrapup)
#endif

	br	_ASM_LABEL(m88110_restore)
#endif
