/* $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 */ /* * 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 #include #include /* * 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 ; \ set FLAGS, FLAGS, 1 ; \ /* 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 ; \ set FLAGS, FLAGS, 1 ; \ /* 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 /* enable the FPU */ clr r10, r10, 1 /* 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 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 stcr r4, PSR FLUSH_PIPELINE ldcr r5, SR1 set r5, r5, 1 /* 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 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 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 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 br.n 7f set TMP2, TMP2, 1<5> /* single */ 6: extu TMP2, TMP2, DMT_DREG_WIDTH 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 /* enable the FPU */ clr TMP, TMP, 1 /* 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 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 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 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 /* enable the FPU */ clr r10, r10, 1 /* 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 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 /* enable the FPU */ clr TMP, TMP, 1 /* 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 /* enable the FPU */ clr TMP, TMP, 1 /* 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 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 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 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 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 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