[BACK]Return to eh_common.S CVS log [TXT][DIR] Up to [local] / sys / arch / m88k / m88k

File: [local] / sys / arch / m88k / m88k / eh_common.S (download)

Revision 1.1, Tue Mar 4 16:07:10 2008 UTC (16 years, 2 months ago) by nbrk
Branch point for: MAIN

Initial revision

/*   $OpenBSD: eh_common.S,v 1.24 2007/05/18 16:35:54 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 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 MACH 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

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

/* 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	st	r10, r31, GENREG_OFF(10)
#define	SAVE_TMP3	st	r11, r31, GENREG_OFF(11)
#define	RESTORE_TMP2	ld	r10, r31, GENREG_OFF(10)
#define	RESTORE_TMP3	ld	r11, r31, 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.
 *
 * EF_FLAGS
 *   This is just the old EF_MODE. "EF_MODE" isn't a very good name.
 */
#define	EF_SR3		(EF_R0 + 5)
#define	EF_FLAGS	EF_MODE

	text
	align 8

#ifdef M88110
#define SAVE_CTX \
	stcr	r31, SRX			; \
	or.u	r31, r0,  hi16(_ASM_LABEL(save_frame))	; \
	or	r31, r31, lo16(_ASM_LABEL(save_frame))	; \
	/* 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.d	r26, r31, GENREG_OFF(26)	; \
	st.d	r28, r31, GENREG_OFF(28)	; \
	st	r30, r31, GENREG_OFF(30)	; \
	ldcr	r1,  SRX			; \
	st	r1,  r31, GENREG_OFF(31)	; \
	ldcr	r1,  EPSR			; \
	ldcr	r2,  EXIP			; \
	ldcr	r3,  ENIP			; \
	st	r1,  r31, REG_OFF(EF_EPSR)	; \
	st	r2,  r31, REG_OFF(EF_EXIP)	; \
	st	r3,  r31, REG_OFF(EF_ENIP)	; \
	ldcr	r1,  DSR			; \
	ldcr	r2,  DLAR			; \
	ldcr	r3,  DPAR			; \
	st	r1,  r31, REG_OFF(EF_DSR)	; \
	st	r2,  r31, REG_OFF(EF_DLAR)	; \
	st	r3,  r31, REG_OFF(EF_DPAR)	; \
	ldcr	r1,  ISR			; \
	ldcr	r2,  ILAR			; \
	ldcr	r3,  IPAR			; \
	st	r1,  r31, REG_OFF(EF_ISR)	; \
	st	r2,  r31, REG_OFF(EF_ILAR)	; \
	st	r3,  r31, REG_OFF(EF_IPAR)	; \
	ldcr	r1, DSAP			; \
	ldcr	r2, DUAP			; \
	st	r1,  r31, REG_OFF(EF_DSAP)	; \
	st	r2,  r31, REG_OFF(EF_DUAP)	; \
	ldcr	r1, ISAP			; \
	ldcr	r2, IUAP			; \
	st	r1,  r31, REG_OFF(EF_ISAP)	; \
	st	r2,  r31, REG_OFF(EF_IUAP)	; \
	/* Restore r1, r2, r3, and r31 */	; \
	ld	r1 , r31, GENREG_OFF(1)		; \
	ld	r2 , r31, GENREG_OFF(2)		; \
	ld	r3 , r31, GENREG_OFF(3)		; \
	ld	r31, r31, GENREG_OFF(31)	;
#endif

/*
 *
 * #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, REG_OFF(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) \
	SAVE_CTX					  \
	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(m88110_setup_phase_one)	; \
	/* TMP2 now free -- use to set EF_VECTOR */	; \
	or	TMP2, r0, NUM				; \
	st	TMP2, r31, REG_OFF(EF_VECTOR)		; \
	/* call setup_phase_two to restart the FPU  */	; \
	/* and to save all general registers.       */	; \
	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, T_INT
	or	r3, r0, r30
	XCALL(_C_LABEL(m88100_trap), _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)
	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)

/* 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	r3, r0, r30
	XCALL(_ASM_LABEL(m88100_Xfp_precise), _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	r3, r0, r30
	XCALL(_ASM_LABEL(Xfp_imprecise), _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(intstack_end))
	or	r31, r31, lo16(_ASM_LABEL(intstack_end))

#ifdef DEBUG
	/* zero the stack, so we'll know what we're lookin' at */
	or.u	r27, r0,  hi16(_ASM_LABEL(intstack))
	or	r27, r27, lo16(_ASM_LABEL(intstack))
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, SIZEOF_EF	/* 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, REG_OFF(EF_EPSR)
	ldcr	r10, SXIP
	st	r10, r31, REG_OFF(EF_SXIP)
	ldcr	r10, SNIP
	st	r10, r31, REG_OFF(EF_SNIP)
	ldcr	r10, SR1
	st	r10, r31, REG_OFF(EF_MODE)
	ldcr	r10, SFIP
	st	r10, r31, REG_OFF(EF_SFIP)
	ldcr	r10, SSBR
	st	r10, r31, REG_OFF(EF_SSBR)
	stcr	r0,  SSBR	/* won't want shadow bits bothering us later */

	ldcr	r10, DMT0
	st	r10, r31, REG_OFF(EF_DMT0)
	ldcr	r11, DMD0
	st	r11, r31, REG_OFF(EF_DMD0)
	ldcr	r12, DMA0

	st	r12, r31, REG_OFF(EF_DMA0)
	ldcr	r10, DMT1
	st	r10, r31, REG_OFF(EF_DMT1)
	FLUSH_PIPELINE
	ldcr	r11, DMD1
	st 	r11, r31, REG_OFF(EF_DMD1)
	ldcr	r12, DMA1
	st	r12, r31, REG_OFF(EF_DMA1)

	ldcr	r10, DMT2
	st	r10, r31, REG_OFF(EF_DMT2)
	ldcr	r11, DMD2
	st	r11, r31, REG_OFF(EF_DMD2)
	ldcr	r12, DMA2
	st	r12, r31, REG_OFF(EF_DMA2)

	/* shove sr2 into EF_FPLS1 */
	ldcr	r10, SR2
	st	r10, r31, REG_OFF(EF_FPLS1)

	/* shove sr3 into EF_FPHS2 */
	ldcr	r10, SR3
	st	r10, r31, REG_OFF(EF_FPHS2)

	/* save error vector */
	st	r29, r31, REG_OFF(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 boolean_t badaddr(unsigned addr, unsigned 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, or 4.
 *
 * 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. */

	/*
	 * If it's a word we're doing, do that here. Otherwise,
	 * see if it's a halfword.....
	 */
	sub	r6,  r3, 4
	bcnd.n	ne0, r6, _ASM_LABEL(badaddr__maybe_halfword)
	 stcr	r5,  SR1
	FLUSH_PIPELINE

	/*
	 * 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)
	/*
	 * The next line will either fault or not. If it faults, execution
	 * will go to:  data_access_handler (see above)
	 * and then to: ignore_data_exception (see above)
	 * and then to: badaddr__return_nonzero (see below)
	 * which will return to the calling function.
	 *
	 * If there is no fault, execution just continues as normal.
	 */
	ld	r5, r2, 0
	FLUSH_PIPELINE
	br.n	_ASM_LABEL(badaddr__return)
	 or	r2, r0, r0	/* indicate a zero (address not bad) return.*/

ASLOCAL(badaddr__maybe_halfword)
	/* More or less like the code for checking a word above */
	sub	r6, r3, 2
	bcnd	ne0, r6, _ASM_LABEL(badaddr__maybe_byte)

	/* it's bad if it's misaligned */
	bb1	0, r2, _ASM_LABEL(badaddr__return_nonzero)

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

ASLOCAL(badaddr__maybe_byte)
#ifdef DEBUG
	/* More or less like the code for checking a word above */
	sub	r6, r3, 1
	bcnd	ne0, r6, _ASM_LABEL(badaddr__unknown_size)
#endif
	FLUSH_PIPELINE
	ld.b	r5, r2, 0
	FLUSH_PIPELINE
	br.n	_ASM_LABEL(badaddr__return)
	 or	r2, r0, r0
ASLOCAL(badaddr__unknown_size)
#ifdef	DEBUG
	data
1: 	string "bad length (%d) to badaddr() from 0x%x\000"
	text
	or.u	r2, r0, hi16(1b)
	or	r2, r2, lo16(1b)
	bsr.n	_C_LABEL(panic)
	 or	r4, r0, r1
	/*NOTREACHED*/
#endif

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,  SIZEOF_EF		/* r31 now our E.F. */
	st	FLAGS,r31,  REG_OFF(EF_FLAGS)	/* save flags */
	st	r1,   r31,  GENREG_OFF(1)	/* save prev. r1 (now free)*/

	ldcr	r1,   SR3			/* save previous SR3 */
	st	r1,   r31,  REG_OFF(EF_SR3)

	addu	r1,   r31,  SIZEOF_EF		/* 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,  REG_OFF(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,  SIZEOF_EF		/* r30 now our E.F. */
	st	FLAGS,r30,  REG_OFF(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,  SIZEOF_EF		/* r30 now has previous SR3 */
	st	r30,  r31,  REG_OFF(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,   USIZE - SIZEOF_EF
	st	FLAGS,r1,   REG_OFF(EF_FLAGS)	/* store flags */
	st	r31,  r1,   GENREG_OFF(31)	/* store r31 - now free */
	st	r30,  r1,   REG_OFF(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,  REG_OFF(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
	SAVE_TMP3

	/* save some exception-time registers to the exception frame */
	ldcr	TMP,  EPSR
	st	TMP,  r31,  REG_OFF(EF_EPSR)
	ldcr	TMP3, SNIP
	st	TMP3, r31,  REG_OFF(EF_SNIP)
	ldcr	TMP2, SFIP
	st	TMP2, r31,  REG_OFF(EF_SFIP)
	/* get and store the cpu_info pointer */
	ldcr	TMP,  CPU
	st	TMP,  r31,  REG_OFF(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,  REG_OFF(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,  REG_OFF(EF_DMT0)

	ldcr	TMP2, DMT1
	ldcr	TMP3, DMT2
	st	TMP2, r31,  REG_OFF(EF_DMT1)
	st	TMP3, r31,  REG_OFF(EF_DMT2)

	ldcr	TMP2, DMA0
	ldcr	TMP3, DMA1
	st	TMP2, r31,  REG_OFF(EF_DMA0)
	st	TMP3, r31,  REG_OFF(EF_DMA1)

	ldcr	TMP2, DMA2
	ldcr	TMP3, DMD0
	st	TMP2, r31,  REG_OFF(EF_DMA2)
	st	TMP3, r31,  REG_OFF(EF_DMD0)

	FLUSH_PIPELINE
	ldcr	TMP2, DMD1
	ldcr	TMP3, DMD2
	st	TMP2, r31,  REG_OFF(EF_DMD1)
	st	TMP3, r31,  REG_OFF(EF_DMD2)

/*
 * need to clear "appropriate" bits in the SSBR before
 * we restart the FPU
 */

	ldcr	TMP2, DMT0
	bb0.n	DMT_VALID_BIT, TMP2, 8f
	/* make sure an exception in fpu_enable will not see our DMT0 */
	 stcr	r0,   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 espr, 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 floatingpoint-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,  REG_OFF(EF_FPSR)
	st	TMP3, r31,  REG_OFF(EF_FPCR)

	fldcr	TMP2, FPECR
	fldcr	TMP3, FPRH
	st	TMP2, r31,  REG_OFF(EF_FPECR)
	st	TMP3, r31,  REG_OFF(EF_FPRH)

	fldcr 	TMP2, FPIT
	fldcr	TMP3, FPRL
	st	TMP2, r31,  REG_OFF(EF_FPIT)
	st	TMP3, r31,  REG_OFF(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 pont 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,  REG_OFF(EF_FPSR)
	st	TMP3, r31,  REG_OFF(EF_FPCR)

	fldcr	TMP3, FPECR
	st	TMP3, r31,  REG_OFF(EF_FPECR)
	fldcr	TMP2, FPHS1
	fldcr	TMP3, FPHS2
	st	TMP2, r31,  REG_OFF(EF_FPHS1)
	st	TMP3, r31,  REG_OFF(EF_FPHS2)

	fldcr	TMP2, FPLS1
	fldcr	TMP3, FPLS2
	st	TMP2, r31,  REG_OFF(EF_FPLS1)
	st	TMP3, r31,  REG_OFF(EF_FPLS2)

	fldcr	TMP2, FPPT
	st	TMP2, r31,  REG_OFF(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 bit 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 espr, 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 resart the FPU.
	 */

	stcr	TMP,  SSBR	/* done with SSBR, TMP now free */
	RESTORE_TMP2	/* done with extra temp regs */
	RESTORE_TMP3	/* 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,  REG_OFF(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 espr, 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,  REG_OFF(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)

	/* get and save IPL */
	bsr.n	_C_LABEL(getipl)
	 st	r29,  r31,  GENREG_OFF(29)
	st	r2, r31, REG_OFF(EF_MASK)

	/*
	 * 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 espr, 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	r2,   r31,  REG_OFF(EF_EPSR)
	bb1	PSR_SUPERVISOR_MODE_BIT, r2, 1f	/* if in kernel mode */

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

	/*
	 * 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	r2,   r30,  REG_OFF(EF_VECTOR)
	bcnd.n	eq0,  r2,   8f			/* error exception */
	 ld	r14,  r30,  REG_OFF(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.n	eq,   r3,   8f
#endif

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

	/* service any outstanding data pipeline stuff */
	ld	r3,   r30,  REG_OFF(EF_DMT0)
	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, T_INT
	or	r3, r0, r30
	XCALL(_C_LABEL(m88110_trap), _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)
	PREP88110("data", 3, M88110_Data_Precheck)
	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_fp_precise_handler)
	PREP88110("FPU precise", 114,)
	or	r3, r0, r30
	XCALL(_ASM_LABEL(m88110_Xfp_precise), _ASM_LABEL(check_ast))

/* MVME197 non-maskable interrupt handler (ABORT button) */
GLOBAL(m88110_nonmaskable)
	PREP88110("NMI", 11,)
	or	r2, r0, T_NON_MASK
	or	r3, r0, r30
	XCALL(_C_LABEL(m88110_trap), _ASM_LABEL(check_ast))

/* 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)
	br.n	1f
	 or	r29, r0, 10
GLOBAL(m88110_reset_handler)
	or	r29, r0, 0
1:
	or	r26, r0, r31	/* save old stack */
	or.u	r31, r0,  hi16(_ASM_LABEL(intstack_end))
	or	r31, r31, lo16(_ASM_LABEL(intstack_end))

#ifdef DEBUG
	/* zero the stack, so we'll know what we're lookin' at */
	or.u	r27, r0,  hi16(_ASM_LABEL(intstack))
	or	r27, r27, lo16(_ASM_LABEL(intstack))
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, SIZEOF_EF		/* 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, REG_OFF(EF_VECTOR)
	cmp	r29, r29, 0	/* is it the reset exception? */
	bb1.n	ne,  r29, 1f	/* if not, skip */

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

1:
	/* retrieve saved shadow registers for error_handler */
	or.u	r30, r0,  hi16(_ASM_LABEL(save_frame))
	or	r30, r30, lo16(_ASM_LABEL(save_frame))
	ld	r10, r30, REG_OFF(EF_EPSR)
	st	r10, r31, REG_OFF(EF_EPSR)
	ld	r10, r30, REG_OFF(EF_EXIP)
	st	r10, r31, REG_OFF(EF_EXIP)
	ld	r10, r30, REG_OFF(EF_ENIP)
	st	r10, r31, REG_OFF(EF_ENIP)
	ld	r10, r30, REG_OFF(EF_DSR)
	st	r10, r31, REG_OFF(EF_DSR)
	ld	r10, r30, REG_OFF(EF_DLAR)
	st	r10, r31, REG_OFF(EF_DLAR)
	ld	r10, r30, REG_OFF(EF_DPAR)
	st	r10, r31, REG_OFF(EF_DPAR)
	ld	r10, r30, REG_OFF(EF_ISR)
	st	r10, r31, REG_OFF(EF_ISR)
	ld	r10, r30, REG_OFF(EF_ILAR)
	st	r10, r31, REG_OFF(EF_ILAR)
	ld	r10, r30, REG_OFF(EF_IPAR)
	st	r10, r31, REG_OFF(EF_IPAR)
	ld	r10, r30, REG_OFF(EF_ISAP)
	st	r10, r31, REG_OFF(EF_ISAP)
	ld	r10, r30, REG_OFF(EF_DSAP)
	st	r10, r31, REG_OFF(EF_DSAP)
	ld	r10, r30, REG_OFF(EF_IUAP)
	st	r10, r31, REG_OFF(EF_IUAP)
	ld	r10, r30, REG_OFF(EF_DUAP)
	st	r10, r31, REG_OFF(EF_DUAP)
	ldcr	r10, SR1
	st	r10, r31, REG_OFF(EF_MODE)
2:
	/* shove sr2 into EF_FPLS1 */
	ldcr	r10, SR2
	st	r10, r31, REG_OFF(EF_FPLS1)

	/* shove sr3 into EF_FPHS2 */
	ldcr	r10, SR3
	st	r10, r31, REG_OFF(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

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

ASLOCAL(m88110_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(m88110_use_SR3_pcb)
	/* are we coming in from user mode? If so, pick up process pcb */
	bb0	FLAG_FROM_KERNEL, FLAGS, _ASM_LABEL(m88110_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,  SIZEOF_EF		/* r31 now our E.F. */
	st	FLAGS,r31,  REG_OFF(EF_FLAGS)	/* save flags */
	st	r1,   r31,  GENREG_OFF(1)	/* save prev. r1 (now free) */

	ldcr	r1,   SR3			/* save previous SR3 */
	st	r1,   r31,  REG_OFF(EF_SR3)

	addu	r1,   r31,  SIZEOF_EF		/* save previous r31 */
	br.n	_ASM_LABEL(m88110_have_pcb)
	 st	r1,   r31,  GENREG_OFF(31)

ASLOCAL(m88110_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.
	 */
#if 1
	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

	or.u	TMP,  r0,   hi16(_C_LABEL(m88110_error_handler))
	or	TMP,  TMP,  lo16(_C_LABEL(m88110_error_handler))
	stcr	TMP,  EXIP

	RTE
#else
	/* Testing!!! */
	xcr	r30,  r30,  SR3			/* r30 = old exception frame */
	st	r1,   r30,  GENREG_OFF(0)	/* free up r1 */
	ld	r1,   r30,  REG_OFF(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,  SIZEOF_EF		/* r30 now our E.F. */
	st	FLAGS,r30,  REG_OFF(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,  SIZEOF_EF		/* r30 now has previous SR3 */
	st	r30,  r31,  REG_OFF(EF_SR3)	/* save previous SR3 */
	br.n	_ASM_LABEL(m88110_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,   USIZE - SIZEOF_EF
	st	FLAGS,r1,   REG_OFF(EF_FLAGS)	/* store flags */
	st	r31,  r1,   GENREG_OFF(31)	/* store r31 - now free */
	st	r30,  r1,   REG_OFF(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(m88110_have_pcb)
	 xcr	r30,  r30,  SR3			/* restore r30 */
#endif

ASLOCAL(m88110_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 temporary
	 *   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,  REG_OFF(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(m88110_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
	SAVE_TMP3

	/* save some exception-time registers to the exception frame */
	ldcr	TMP,  EPSR
	st	TMP,  r31,  REG_OFF(EF_EPSR)
	ldcr	TMP2, EXIP
	ldcr	TMP3, ENIP
	st	TMP2, r31,  REG_OFF(EF_EXIP)
	st	TMP3, r31,  REG_OFF(EF_ENIP)

	/* get and store the cpu_info pointer */
	ldcr	TMP,  CPU
	st	TMP,  r31,  REG_OFF(EF_CPU)

	/*
	 * Save fault status registers from CMMU.
	 */
	ldcr	TMP,  ISR
	ldcr	TMP2, ILAR
	ldcr	TMP3, IPAR
	st	TMP,  r31,  REG_OFF(EF_ISR)
	st	TMP2, r31,  REG_OFF(EF_ILAR)
	st	TMP3, r31,  REG_OFF(EF_IPAR)
	ldcr	TMP,  ISAP
	ldcr	TMP2, IUAP
	st	TMP,  r31,  REG_OFF(EF_ISAP)
	st	TMP2, r31,  REG_OFF(EF_IUAP)
	ldcr	TMP,  DSR
	ldcr	TMP2, DLAR
	ldcr	TMP3, DPAR
	st	TMP,  r31,  REG_OFF(EF_DSR)
	st	TMP2, r31,  REG_OFF(EF_DLAR)
	st	TMP3, r31,  REG_OFF(EF_DPAR)
	ldcr	TMP,  DSAP
	ldcr	TMP2, DUAP
	st	TMP,  r31,  REG_OFF(EF_DSAP)
	st	TMP2, r31,  REG_OFF(EF_DUAP)

	stcr	r0, ISR
	stcr	r0, DSR

	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
	 * r31: our exception frame
	 * Valid in the exception frame:
	 *   Exception-time r1, r31, FLAGS.
	 *   Exception-time TMP2, TMP3.
	 *   Exception-time espr, enip, exip.
	 *   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 resart the FPU.
	 */

	RESTORE_TMP2	/* done with extra temp regs */
	RESTORE_TMP3	/* done with extra temp regs */

	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

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

	set	FLAGS, FLAGS, 1<FLAG_ENABLING_FPU>
	xcr	FLAGS, FLAGS, SR1
	st	r1,   r31,  REG_OFF(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 espr, sfip, enip, exip.
	 *   Exception number (EF_VECTOR).
	 *   Dmt0
	 *   Other data pipeline control registers, if appropriate.
	 *   FPU control registers, if appropriate.
	 *   Exception SR3, if appropriate.
	 * Held temporarily 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 "m88110_fpu_enable" to enable the FPU. */

ASLOCAL(m88110_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 */
	ld	TMP,  r31,  REG_OFF(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)

	/* get and save IPL */
	bsr.n	_C_LABEL(getipl)
	 st	r29,  r31,  GENREG_OFF(29)
	st	r2, r31, REG_OFF(EF_MASK)

	/*
	 * 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 espr, enip, exip.
	 *   Exception number (EF_VECTOR).
	 *   DSR
	 *   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	r2,   r31,  REG_OFF(EF_EPSR)
	bb1	PSR_SUPERVISOR_MODE_BIT, r2, 1f	/* if in kernel mode */

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

	/*
	 * 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 again for the debugger */
#endif

	ld	r2,   r30,  REG_OFF(EF_VECTOR)
	bcnd.n	eq0,  r2,   8f
	 ld	r14,  r30,  REG_OFF(EF_RET)	/* load return value XXX!!! */
	cmp	r3,   r2,   1	/* is an interrupt? */
	bb1.n	eq,   r3,   8f
	 cmp	r3,   r2,   11	/* or NMI? */
	bb1.n	eq,   r3,   8f

#ifdef DDB
	 cmp	r3,   r2,   130	/* DDB break exception */
	bb1.n	eq,   r3,   8f

	 cmp	r3,   r2,   132	/* DDB entry exception */
	bb1.n	eq,   r3,   8f
#endif

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

8:
	jmp	r14		/* loaded above */

	data
	.align 8		/* needs align 8 for ld.d/st.d */
ASLOCAL(save_frame)
	space SIZEOF_EF
#endif	/* M88110 */

/*
 * 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 load pc from the switchframe and
 * jump there (the pc will usually be proc_do_uret).
 */

ENTRY(proc_trampoline)
#ifdef MULTIPROCESSOR
	bsr	_C_LABEL(proc_trampoline_mp)
#endif
	ld	r1, r31, 0		/* load func */
	ld	r2, r31, 4 		/* load proc pointer */
	jsr.n	r1
	 addu	r31, r31, 8		/* release ksigframe */
	ld	r1,  r31, 0		/* load pc */
	ld	r2,  r31, 4		/* & proc pointer from switch frame */
	jsr.n	r1
	 addu	r31, r31, 8

#define	FPTR	r14


ASLOCAL(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 dosoftint() to process soft
	 * interrupts.
	 * If returning to userland, look for ASTs.
	 */
	/* do not service soft interrupts if interrupts were disabled... */
	ld	r2, FPTR, REG_OFF(EF_EPSR)
	bb1	PSR_INTERRUPT_DISABLE_BIT, r2, _ASM_LABEL(no_softint)
	/* ...or we were not at spl0 */
	ld	r2, FPTR, REG_OFF(EF_MASK)
	bcnd	ne0, r2, _ASM_LABEL(no_softint)

	/* do an inline spl0() */
	bsr.n	_C_LABEL(setipl)
	 or	r2, r0, IPL_SOFTCLOCK
	bsr	_C_LABEL(dosoftint)
	bsr.n	_C_LABEL(setipl)
	 or	r2, r0, IPL_NONE

ASLOCAL(no_softint)
	/* do not service AST if not returning to user mode */
	ld	r2, FPTR, REG_OFF(EF_EPSR)
	bb1	PSR_SUPERVISOR_MODE_BIT, r2, _ASM_LABEL(no_ast)

	ldcr	r2,  CPU
	ld	r3,  r2, CI_CURPROC
	bcnd	eq0, r3, _ASM_LABEL(no_ast)	/* no AST if no process! */
	ld	r2,  r3, P_ASTPENDING
	bcnd.n	eq0, r2, _ASM_LABEL(no_ast)	/* .n safe since the first
						   instruction of CALL() is
						   safe in a delay slot. */
	/*
	 * trap(AST,...) will service ast's.
	 */
	 or	r3, r0, FPTR
	or	r2, r0, T_ASTFLT
#if defined(M88110) && defined(M88100)
	or.u	r4, r0, hi16(_C_LABEL(cputyp))
	ld	r5, r4, lo16(_C_LABEL(cputyp))
	cmp	r4, r5, CPU_88110
	bb0	eq, r4, 2f
#endif
#if defined(M88110)
	XCALL(_C_LABEL(m88110_trap), _ASM_LABEL(no_ast))
#endif
#if defined(M88110) && defined(M88100)
2:
#endif
#ifdef M88100
	XCALL(_C_LABEL(m88100_trap), _ASM_LABEL(no_ast))
#endif

/*
 * void proc_do_uret(struct proc *p)
 *
 * This is called as proc_do_uret(proc) from proc_trampoline(). This function
 * loads FPTR with a pointer to the trap frame for the given proc and continues
 * near the end of check_ast, bypassing soft interrupts and AST checks, to
 * load all the registers and do an RTE.
 */

ENTRY(proc_do_uret)
	ld	FPTR, r2,   P_ADDR		/* p->p_addr */
	addu	FPTR, FPTR, PCB_USER_STATE	/* p->p_addr.u_pcb.user_state */
	/* FALLTHROUGH */

ASLOCAL(no_ast)
	/* 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, REG_OFF(EF_MASK)	/* get pre-exception ipl */

	/*
	 * 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*/
#ifdef M88110
#ifdef M88100
	or.u	r1, r0, hi16(_C_LABEL(cputyp))
	ld	r30, r1, lo16(_C_LABEL(cputyp))
	cmp	r1, r30, CPU_88110
	bb1	ne, r1, 1f
#endif
	ld	r30, r31, REG_OFF(EF_ENIP)
	ld	r1,  r31, REG_OFF(EF_EXIP)
	stcr	r30, ENIP
	stcr	r1,  EXIP
#ifdef M88100
	br	2f
1:
#endif
#endif
#ifdef M88100
	/*
	 * 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, REG_OFF(EF_SNIP)
	ld	r1,  r31, REG_OFF(EF_SFIP)
	stcr	r30, SNIP
	stcr	r1,  SFIP
2:
#endif
	ld	r30, r31, REG_OFF(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