/* $OpenBSD: eh_common.S,v 1.66 2024/06/05 19:22:04 miod Exp $ */ /* * Mach Operating System * Copyright (c) 1993-1991 Carnegie Mellon University * Copyright (c) 1991 OMRON Corporation * Copyright (c) 1996 Nivas Madhur * Copyright (c) 1998 Steve Murphree, Jr. * All Rights Reserved. * * Permission to use, copy, modify and distribute this software and its * documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON AND OMRON ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS IS" * CONDITION. CARNEGIE MELLON AND OMRON DISCLAIM ANY LIABILITY OF ANY KIND * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. */ /* * NOTICE: This is not a standalone file. To use it, define the PFSR_SAVE * macro (if supporting 88100 processors) and #include this file in your * port's eh.S: * #include */ /* * In the following discussion, references are made to: * MC88100 - RISC MICROPROCESSOR USER'S MANUAL * (second edition). Reference in []s refer to section numbers. * * This discussion assumes that you are at least vaguely familiar with 88100 * exception handling (chapter 6), the BSD kernel, and that you have a brain * (and use it while reading this). * * I also assume (and hope) that you're not offended by frequent misspellings. * * Jeffrey Friedl * jfriedl@rna.ncl.omron.co.jp * December, 1989 * ------------------------------------------------------------------- * * EXCEPTIONS, INTERRUPTS, and TRAPS * --------------------------------- * This is the machine exception handler. * In the MC88100, various "conditions" cause an exception, where * processing momentarily jumps here to "service" the exception, * and then continues where it left off. * * There are a number of different types of exceptions. * For example, exception #6 is the privilege violation exception which * is raised when the user tries to execute a supervisor-only instruction. * * Exception #1 is the interrupt exception, and is raised when an * outside device raises the INT line on the CPU. This happens, * for example, when the clock signals that it is time for a context * switch, or perhaps the disk drive signaling that some operation * is complete. * * Traps are also exceptions. Traps are ways for user programs to request * kernel operations. For example, "tcnd eq0, r0, 450" will raise * exception 450, the system call exception. * * * SERVICING AN EXCEPTION * ----------------------- * When an exception occurs, each control register is saved in its * respective shadow register and execution continues from the * appropriate exception handler. The exception handler must * - save the context from the time of the exception * - service the exception * - restore the context (registers, etc) * - pick up from where the exception occurred. * * The context is saved on a stack. Actually, in the user_state area * in the PCB if the exception happens in user mode. * * Servicing the exception is usually straightforward and in fact not dealt * with very much here. Usually a C routine is called to handle it. * For example, when a privilege exception is raised, the routine that sends * an "illegal instruction" signal to the offending process is called. * * When the exception has been serviced, the context is restored from the * stack and execution resumes from where it left off. * * In more detail: * * Saving the exception-time context. * --------------------------------- * In saving the exception-time context, we copy the shadow and general * purpose registers to memory. Since one exception may occur while * servicing another, the memory used to save the exception-time context may * not be static (i.e. the same every time). Thus, memory on a stack is set * aside for the exception frame (area where the exception-time context is * saved). The same stack is also used when C routines are called (to * service the exception). * * Each process has a stack in kernel space (called the "kernel stack", * short for "process's kernel stack) as well as the user space stack. When * entering the kernel from user space, the kernel stack is unused. On this * stack we save the exception state and (most likely call a C routine to) * service the exception. * * Before servicing an exception, several issues must be addressed. * * 1) When an interrupt is recognized by the hardware, the data pipeline is * allowed to clear. However, if one of these data accesses faults (bad * reference, or a reference to a page which needs to be swapped in), that * reference, as well as any others in the pipeline at the time (at most * three total) are left there, to be taken care of by the exception * handler [6.4.1]. This involves swapping in the proper page and * manually doing the appropriate load or store. * * The other (at most, two other) data accesses that might have been in * the pipeline must also be manually completed (even though they may not * be at fault [yes, that's a bad pun, thank you]). * * 2) If any of the (at most three) uncompleted data access in the pipeline * are loads (from memory to a register), then the bit for the destination * register is set in the SSBR. Since the hardware will never complete * that load (since we do it manually), the hardware will never clear that * SSBR bit. Thus, we must clear it manually. If this isn't done, the * system will hang waiting for a bit to clear that will never. * * 3) If the exception is the privilege violation exception, the bounds * check exception, or the misaligned access exception, the * destination register bit in the SSBR may need to be cleared. * * 4) If the exception is one of the floating exceptions, then the * destination register for that floating process won't be written, * and the SSBR must be cleared explicitly. * * 5) The FPU must be enabled (as it is disabled by the exception processing * hardware) and allowed to complete actions in progress. This is * so that it may be used in the servicing of any instruction. * When the FPU is being restarted, operations attempting to complete * may themselves fault (raising another exception). * * More on Restarting the FPU * -------------------------- * The manual [section 6.4.3.4] gives only minor mention to this * rather complex task. Before the FPU is restarted all SSBR bits are * cleared for actions that the exception handler completes (as mentioned * above) so that the SSBR is clear unless there are FPU operations that * have not actually been completed (and hence not written to the registers). * Also, all control registers (at least all those that we care about) are * saved to the stack exception frame before the FPU is restarted (this * is important... the reason comes later). * * The FPU is restarted by doing an rte to a trap-not-taken (the rte * actually enables the fpu because we ensure that the EPSR has the * FPU-enable bit on; the trap-not-taken ensures anything in the FPU * completes by waiting until scoreboard register is clear). * * At the time the FPU is restarted (the rte to the trap-not-taken) the FPU * can write to ANY of the general registers. Thus, we must make sure that * all general registers (r1..r31) are in their pre-exception state so that * when saved to the exception frame after the FPU is enabled, they properly * reflect any changes made by the FPU in being restarted. * * Because we can't save the pointer to the exception frame in a general * register during the FPU restart (it could get overwritten by the FPU!), * we save it in a control register, SR3, during the restart. * * HOWEVER ..... * * Because other uncompleted actions in the FPU may fault when the FPU is * restarted, a new exception may be raised during the restart. This may * happen recursively a number of times. Thus, during a restart, ANY register * whatsoever may be modified, including control registers. Because of this * we must make sure that the exception handler preserves SR3 throughout * servicing an exception so that, if the exception had been raised during * an FPU restart, it is returned unmolested when control returns to the FPU * restart. * * Thus: if an exception is from kernel space, we MUST preserve SR3. * (if it from user space, no FPU-enable can be in progress and SR3 is * unimportant). * * Now is a good time to recap SR1..SR3 usage: * SR1 - CPU flags (exception handler flags) * SR2 - generally free * SR3 - free only if the exception is from user mode * * Once the FPU has been restarted, the general registers are saved to the * exception frame. If the exception is not the interrupt exception, * interrupts are enabled and any faulted data accesses (see above) are * serviced. In either case, the exception is then serviced (usually by * calling a C routine). After servicing, any faulted data accesses are * serviced (if it had been the interrupt exception). The context is then * restored and control returns to where the exception occurred. * */ #include "assym.h" #include #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 /* GENeral REGister OFFset into the E.F. (exception frame) */ #define GENREG_OFF(num) (EF_R0 + (num) * 4) /* Invoke a C function with 2 arguments */ #define CALL(NAME, ARG1, ARG2) \ or %r2, %r0, ARG1; \ bsr.n NAME; \ or %r3, %r0, ARG2 /* Invoke a function and return elsewhere */ /* CAREFUL: needs to have `RET' after the XCALL in memory */ #define XCALL(NAME, RET) \ bsr.n NAME; \ addu %r1, %r1, RET - . - 4 /* * Some registers used during the setting up of the new exception frame. * Don't choose r1, r30, or r31 for any of them. * * Also, if any are 'r2' or 'r3', be careful using with CALL above! */ #define FLAGS %r2 #define TMP %r3 #define TMP2 %r10 #define TMP3 %r11 #define SAVE_TMP2(ef) st %r10, ef, GENREG_OFF(10) #define SAVE_TMP3(ef) st %r11, ef, GENREG_OFF(11) #define RESTORE_TMP2(ef) ld %r10, ef, GENREG_OFF(10) #define RESTORE_TMP3(ef) ld %r11, ef, GENREG_OFF(11) /* * EF_SR3 * A place to save the exception-time SR3 from just after the * time when an exception is raised until just after the FPU * has been restarted. This does not necessarily conflict with * the general registers (though it can if you're not careful) * and so we can use a spot later used to save a general register. */ #define EF_SR3 GENREG_OFF(5) .text .align 3 /* * * #define PREP881x0(NAME, NUM, SSBR_STUFF, FLAG_CHECK) * * This is the "exception processing preparation" common to all exception * processing. It is used in the following manner: * * ASGLOBAL(foo_handler) * PREP881x0("foo", 11, SSBR_Stuff, Precheck_Stuff) * or %r2, %r0, T_FOO_FAULT * or %r3, %r0, %r30 * XCALL(trapXXX, check_ast) * * This defines the exception handler for the "foo" exception. * The arguments are: * NAME * String for debugging (more info later) * NUM * The exception number [see the manual, Table 6-1] * SSBR_STUFF * If the exception might leave some bits in the SSBR set, * this should indicate how they are cleared. * FLAG_PRECHECK * This is for the data access exception only. See it for * more info. * * What's in between PREP881x0() and check_ast (usually a XCALL) * is the actual servicing of the interrupt. During this time, any * register may be used freely as they've all been saved in the * exception frame (which is pointed to by r30). */ #ifdef M88100 #define PREP88100(NAME, NUM, SSBR_STUFF, FLAG_PRECHECK) \ xcr FLAGS, FLAGS, SR1 ; \ FLAG_PRECHECK \ /* the bsr later clobbers r1, so save now */ \ stcr %r1, SR2 /* r1 now free */ ; \ /* set or clear the FLAG_FROM_KERNEL bit */ \ ldcr %r1, EPSR ; \ bb0.n PSR_SUPERVISOR_MODE_BIT, %r1, 1f ; \ clr FLAGS, FLAGS, 1 ; \ set FLAGS, FLAGS, 1 ; \ /* get a stack (exception frame) */ \ 1: bsr m88100_setup_phase_one ; \ /* TMP2 now free -- use to set EF_VECTOR */ \ or TMP2, %r0, NUM ; \ st TMP2, %r31, EF_VECTOR ; \ /* Clear any bits in the SSBR (held in TMP) */ \ /* SSBR_STUFF may be empty, though. */ \ SSBR_STUFF \ /* call setup_phase_two to restart the FPU */ \ /* and to save all general registers. */ \ bsr m88100_setup_phase_two #endif #ifdef M88110 #define PREP88110(NAME, NUM, FLAG_PRECHECK) \ xcr FLAGS, FLAGS, SR1 ; \ FLAG_PRECHECK \ /* the bsr later clobbers r1, so save now */ ; \ stcr %r1, SR2 /* r1 now free */ ; \ /* set or clear the FLAG_FROM_KERNEL bit */ ; \ ldcr %r1, EPSR ; \ clr FLAGS, FLAGS, 1 ; \ bb0 PSR_SUPERVISOR_MODE_BIT, %r1, 1f ; \ set FLAGS, FLAGS, 1 ; \ /* get a stack and an exception frame */ ; \ 1: bsr m88110_setup_phase_one ; \ /* TMP2 now free -- use to set EF_VECTOR */ ; \ or TMP2, %r0, NUM ; \ /* call setup_phase_two to save all general */ ; \ /* registers. */ ; \ st TMP2, %r30, EF_VECTOR ; \ bsr m88110_setup_phase_two #endif /* Some defines for use with PREP88100() */ #define Clear_SSBR_Dest \ bsr clear_dest_ssbr_bit; #define M88100_Data_Precheck \ bb1.n FLAG_IGNORE_DATA_EXCEPTION, FLAGS, \ m88100_ignore_data_exception; #define M88110_Data_Precheck \ bb1.n FLAG_IGNORE_DATA_EXCEPTION, FLAGS, \ 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(m88100_trap, check_ast) /* interrupt exception handler */ GLOBAL(interrupt_handler) PREP88100("interrupt", 1,,) or %r2, %r0, %r30 XCALL(interrupt, check_ast) /* instruction access exception handler */ GLOBAL(instruction_access_handler) PREP88100("inst", 2,,) or %r2, %r0, T_INSTFLT or %r3, %r0, %r30 XCALL(m88100_trap, check_ast) /* * data access exception handler -- * See badaddr() below for info about Data_Precheck. */ GLOBAL(data_exception_handler_bootstrap) PREP88100("data", 3,, M88100_Data_Precheck) /* No need to call m88100_trap(T_DATAFLT) as PREP will do this for us */ br check_ast GLOBAL(data_exception_handler) PREP88100("data", 3,,) /* No need to call m88100_trap(T_DATAFLT) as PREP will do this for us */ br 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(m88100_trap, check_ast) /* unimplemented opcode exception handler */ GLOBAL(unimplemented_handler) PREP88100("unimp", 5,,) or %r2, %r0, T_ILLFLT or %r3, %r0, %r30 XCALL(m88100_trap, 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(m88100_trap, 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(m88100_trap, 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(m88100_trap, check_ast) /* integer overflow exception handler */ GLOBAL(overflow_handler) PREP88100("overflow", 9,,) or %r2, %r0, T_OVFFLT or %r3, %r0, %r30 XCALL(m88100_trap, check_ast) /* Floating-point precise handler */ #define FPp_SSBR_STUFF \ bsr clear_FPp_ssbr_bit; GLOBAL(fp_precise_handler) PREP88100("FPU precise", 114, FPp_SSBR_STUFF,) or %r2, %r0, T_FPEPFLT or %r3, %r0, %r30 XCALL(m88100_trap, check_ast) /* Floating-point imprecise handler */ #define FPi_SSBR_STUFF \ bsr clear_FPi_ssbr_bit; GLOBAL(fp_imprecise_handler) PREP88100("FPU imprecise", 115, FPi_SSBR_STUFF,) or %r2, %r0, T_FPEIFLT or %r3, %r0, %r30 XCALL(m88100_trap, check_ast) /* trap 450: system calls */ GLOBAL(syscall_handler) PREP88100("syscall", 450,,) ld %r2, %r30, GENREG_OFF(13) or %r3, %r0, %r30 XCALL(m88100_syscall, check_ast) /* trap 451: cache flush (necessary for trampolines) */ GLOBAL(cache_flush_handler) PREP88100("cache_flush", 451,,) or %r2, %r0, %r30 XCALL(cache_flush, check_ast) GLOBAL(sigsys) PREP88100("sigsys", 501,,) or %r2, %r0, T_SIGSYS or %r3, %r0, %r30 XCALL(m88100_trap, check_ast) GLOBAL(stepbpt) PREP88100("stepbpt", 504,,) or %r2, %r0, T_STEPBPT or %r3, %r0, %r30 XCALL(m88100_trap, check_ast) GLOBAL(userbpt) PREP88100("userbpt", 511,,) or %r2, %r0, T_USERBPT or %r3, %r0, %r30 XCALL(m88100_trap, check_ast) #ifdef DDB GLOBAL(break) PREP88100("break", 130,,) or %r2, %r0, T_KDB_BREAK or %r3, %r0, %r30 XCALL(m88100_trap, check_ast) GLOBAL(trace) PREP88100("trace", 131,,) or %r2, %r0, T_KDB_TRACE or %r3, %r0, %r30 XCALL(m88100_trap, check_ast) GLOBAL(entry) PREP88100("kdb", 132,,) or %r2, %r0, T_KDB_ENTRY or %r3, %r0, %r30 XCALL(m88100_trap, 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(initstack_end) or %r31, %r31, %lo16(initstack_end) #ifdef DEBUG /* zero the stack, so we'll know what we're lookin' at */ or.u %r27, %r0, %hi16(initstack) or %r27, %r27, %lo16(initstack) 1: cmp %r28, %r27, %r31 bb1 ge, %r28, 2f /* branch if at the end of the stack */ st %r0, %r0, %r27 br.n 1b addu %r27, %r27, 4 /* bump up */ 2: /* stack has been cleared */ #endif /* ensure that stack is 8-byte aligned */ clr %r31, %r31, 3<0> /* round down to 8-byte boundary */ /* create exception frame on stack */ subu %r31, %r31, TRAPFRAME_SIZEOF /* r31 now our E.F. */ /* save old R31 and other R registers */ st.d %r0 , %r31, GENREG_OFF(0) st.d %r2 , %r31, GENREG_OFF(2) st.d %r4 , %r31, GENREG_OFF(4) st.d %r6 , %r31, GENREG_OFF(6) st.d %r8 , %r31, GENREG_OFF(8) st.d %r10, %r31, GENREG_OFF(10) st.d %r12, %r31, GENREG_OFF(12) st.d %r14, %r31, GENREG_OFF(14) st.d %r16, %r31, GENREG_OFF(16) st.d %r18, %r31, GENREG_OFF(18) st.d %r20, %r31, GENREG_OFF(20) st.d %r22, %r31, GENREG_OFF(22) st.d %r24, %r31, GENREG_OFF(24) st %r30, %r31, GENREG_OFF(30) st %r26, %r31, GENREG_OFF(31) /* save shadow registers (are OLD if error_handler, though) */ ldcr %r10, EPSR st %r10, %r31, EF_EPSR ldcr %r10, SXIP st %r10, %r31, EF_SXIP ldcr %r10, SNIP st %r10, %r31, EF_SNIP ldcr %r10, SR1 st %r10, %r31, EF_FLAGS ldcr %r10, SFIP st %r10, %r31, EF_SFIP ldcr %r10, SSBR st %r10, %r31, EF_SSBR stcr %r0, SSBR /* won't want shadow bits bothering us later */ ldcr %r10, DMT0 st %r10, %r31, EF_DMT0 ldcr %r11, DMD0 st %r11, %r31, EF_DMD0 ldcr %r12, DMA0 st %r12, %r31, EF_DMA0 ldcr %r10, DMT1 st %r10, %r31, EF_DMT1 FLUSH_PIPELINE ldcr %r11, DMD1 st %r11, %r31, EF_DMD1 ldcr %r12, DMA1 st %r12, %r31, EF_DMA1 ldcr %r10, DMT2 st %r10, %r31, EF_DMT2 ldcr %r11, DMD2 st %r11, %r31, EF_DMD2 ldcr %r12, DMA2 st %r12, %r31, EF_DMA2 /* shove sr2 into EF_FPLS1 */ ldcr %r10, SR2 st %r10, %r31, EF_FPLS1 /* shove sr3 into EF_FPHS2 */ ldcr %r10, SR3 st %r10, %r31, EF_FPHS2 /* save error vector */ st %r29, %r31, EF_VECTOR /* * Cheap way to enable FPU and start shadowing again. */ ldcr %r10, PSR clr %r10, %r10, 1 /* 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 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(badaddr__return_nonzero + 2) or %r2, %r2, %lo16(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(badaddr__return_nonzero) or %r2, %r2, %lo16(badaddr__return_nonzero) stcr %r2, EXIP /* Make it the next instruction to execute */ stcr %r0, DSR /* Clear exception status */ RTE #endif /* M88110 */ /* * extern int badaddr(vaddr_t addr, int len) * * Returns true (non-zero) if the given LEN bytes starting at ADDR are * not all currently accessible by the kernel. * * If all LEN bytes starting at ADDR are accessible, zero is returned. * * Len may be be 1, 2, 4 or 8. * * This is implemented by setting a special flag in SR1 before trying to access * the given address. If a data access exception is raised, the address * is inaccessible. The exception handler will notice the special CPU flag * and not try to swap the address in. Rather, it will return to * "badaddr__return_nonzero" in this routine so that we may return non-zero * to the calling routine. * * If no fault is raised, we continue to where we return zero to the calling * routine (after removing the special CPU flag). */ GLOBAL(badaddr) /* * Disable interrupts ... don't want a context switch while we're * doing this! Also, save the old PSR in R8 to restore later. */ ldcr %r8, PSR set %r4, %r8, 1 stcr %r4, PSR FLUSH_PIPELINE ldcr %r5, SR1 set %r5, %r5, 1 /* resetting r5 to SR1 done in the delay slot below. */ /* make sure the upper 28 bits of the size are zero... */ ext %r6, %r3, 0<4> bcnd.n ne0, %r6, badaddr__return_nonzero stcr %r5, SR1 or.u %r6, %r0, %hi16(badaddr_switch) or %r6, %r6, %lo16(badaddr_switch) lda %r3, %r6[%r3] jmp %r3 ASLOCAL(badaddr_switch) br badaddr__return_nonzero br badaddr__b br badaddr__h br badaddr__return_nonzero br badaddr__w br badaddr__return_nonzero br badaddr__return_nonzero br badaddr__return_nonzero br badaddr__d br badaddr__return_nonzero br badaddr__return_nonzero br badaddr__return_nonzero br badaddr__return_nonzero br badaddr__return_nonzero br badaddr__return_nonzero br badaddr__return_nonzero /* * The load attempts below will either fault or not. If they fault, * execution will go to data_access_handler, then to * ignore_data_exception, then to badaddr__return_nonzero, which * will return to the calling function. * If there is no fault, execution will just continue as normal. */ ASLOCAL(badaddr__b) FLUSH_PIPELINE ld.b %r6, %r2, 0 FLUSH_PIPELINE br.n badaddr__return or %r2, %r0, %r0 ASLOCAL(badaddr__h) /* It's a bad address if it's misaligned. */ bb1 0, %r2, badaddr__return_nonzero FLUSH_PIPELINE ld.h %r6, %r2, 0 FLUSH_PIPELINE br.n badaddr__return or %r2, %r0, %r0 ASLOCAL(badaddr__w) /* It's a bad address if it's misaligned. */ bb1 0, %r2, badaddr__return_nonzero bb1 1, %r2, badaddr__return_nonzero FLUSH_PIPELINE ld %r6, %r2, 0 FLUSH_PIPELINE br.n badaddr__return or %r2, %r0, %r0 /* indicate a zero (address not bad) return.*/ ASLOCAL(badaddr__d) /* It's a bad address if it's misaligned. */ bb1 0, %r2, badaddr__return_nonzero bb1 1, %r2, badaddr__return_nonzero bb1 2, %r2, badaddr__return_nonzero FLUSH_PIPELINE ld.d %r6, %r2, 0 FLUSH_PIPELINE br.n badaddr__return or %r2, %r0, %r0 /* indicate a zero (address not bad) return.*/ ASLOCAL(badaddr__return_nonzero) or %r2, %r0, 1 /* FALLTHROUGH */ ASLOCAL(badaddr__return) ldcr %r4, SR1 clr %r4, %r4, 1 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, m88100_use_SR3_pcb /* are we coming in from user mode? If so, pick up process pcb */ bb0 FLAG_FROM_KERNEL, FLAGS, m88100_pickup_stack /* Interrupt in kernel mode, not FPU restart */ /* * SR1: saved copy of exception-time register now holding FLAGS * SR2: return address to the calling exception handler * SR3: must be preserved; may be important for other exceptions * FLAGS: CPU status flags * * immediate goal: * We're already on the kernel stack, but not having * needed to use SR3. We can just make room on the * stack (r31) for our exception frame. */ subu %r31, %r31, TRAPFRAME_SIZEOF /* r31 now our E.F. */ st FLAGS,%r31, EF_FLAGS /* save flags */ st %r1, %r31, GENREG_OFF(1) /* save prev. r1 (now free)*/ ldcr %r1, SR3 /* save previous SR3 */ st %r1, %r31, EF_SR3 addu %r1, %r31, TRAPFRAME_SIZEOF /* save previous r31 */ br.n m88100_have_pcb st %r1, %r31, GENREG_OFF(31) ASLOCAL(m88100_use_SR3_pcb) /* * SR1: saved copy of exception-time register now holding FLAGS * SR2: return address to the calling exception handler * SR3: must be preserved; exception-time stack pointer * FLAGS: CPU status flags * * immediate goal: * An exception occurred while enabling the FPU. Since r31 * is the user's r31 while enabling the FPU, we had put * our pcb pointer into SR3, so make room from * there for our stack pointer. * We need to check if SR3 is the old stack pointer or the * pointer off to the user pcb. If it pointing to the user * pcb, we need to pick up the kernel stack. Otherwise * we need to allocate a frame upon it. * We look at the EPSR to see if it was from user mode * Unfortunately, we have no registers free at the moment * But we know register 0 in the pcb frame will always be * zero, so we can use it as scratch storage. */ xcr %r30, %r30, SR3 /* r30 = old exception frame */ st %r1, %r30, GENREG_OFF(0) /* free up r1 */ ld %r1, %r30, EF_EPSR /* get back the epsr */ bb0.n PSR_SUPERVISOR_MODE_BIT, %r1, 1f/* if user mode */ ld %r1, %r30, GENREG_OFF(0) /* restore r1 */ /* we were in kernel mode - dump frame upon the stack */ st %r0, %r30, GENREG_OFF(0) /* repair old frame */ subu %r30, %r30, TRAPFRAME_SIZEOF /* r30 now our E.F. */ st FLAGS,%r30, EF_FLAGS /* save flags */ st %r1, %r30, GENREG_OFF(1) /* save prev r1 (now free) */ st %r31, %r30, GENREG_OFF(31) /* save previous r31 */ or %r31, %r0, %r30 /* make r31 our pointer. */ addu %r30, %r30, TRAPFRAME_SIZEOF /* r30 now has previous SR3 */ st %r30, %r31, EF_SR3 /* save previous SR3 */ br.n m88100_have_pcb xcr %r30, %r30, SR3 /* restore r30 */ 1: /* we took an exception while restarting the FPU from user space. * Consequently, we never picked up a stack. Do so now. * R1 is currently free (saved in the exception frame pointed at by * r30) */ ldcr %r1, CPU ld %r1, %r1, CI_CURPCB addu %r1, %r1, USPACE - TRAPFRAME_SIZEOF st FLAGS,%r1, EF_FLAGS /* store flags */ st %r31, %r1, GENREG_OFF(31) /* store r31 - now free */ st %r30, %r1, EF_SR3 /* store old SR3 (pcb) */ or %r31, %r1, %r0 /* make r31 our exception fp */ ld %r1, %r30, GENREG_OFF(0) /* restore old r1 */ st %r0, %r30, GENREG_OFF(0) /* repair that frame */ st %r1, %r31, GENREG_OFF(1) /* store r1 */ br.n m88100_have_pcb xcr %r30, %r30, SR3 /* restore r30 */ ASLOCAL(m88100_pickup_stack) /* * SR1: saved copy of exception-time register now holding FLAGS * SR2: return address to the calling exception handler * SR3: free * FLAGS: CPU status flags * * immediate goal: * Since we're servicing an exception from user mode, we * know that SR3 is free. We use it to free up a temp. * register to be used in getting the process pcb */ stcr %r31, SR3 /* save previous r31 */ /* switch to the process kernel stack. */ ldcr %r31, CPU ld %r31, %r31, CI_CURPCB addu %r31, %r31, PCB_USER_STATE /* point to user save area */ /* * WARNING! Using pcb->user_state as the exception frame * AND stack pointer, means we can not afford using the stack * until we have saved enough and can go back to the top of the u area, * after the FPU is enabled. */ st FLAGS,%r31, EF_FLAGS /* save flags */ st %r1, %r31, GENREG_OFF(1) /* save prev. r1 (now free) */ ldcr %r1, SR3 /* save previous r31 */ st %r1, %r31, GENREG_OFF(31) /* FALLTHROUGH */ ASLOCAL(m88100_have_pcb) /* * SR1: saved copy of exception-time register now holding FLAGS * SR2: return address to the calling exception handler * SR3: free * r1: free * FLAGS: CPU status flags * r31: our exception frame * Valid in the exception frame: * Exception-time r1, r31, FLAGS. * Exception SR3, if appropriate. * * immediate goal: * Save the shadow registers that need to be saved to * the exception frame. */ stcr TMP, SR3 /* free up TMP, TMP2, TMP3 */ SAVE_TMP2(%r31) SAVE_TMP3(%r31) /* save some exception-time registers to the exception frame */ ldcr TMP, EPSR st TMP, %r31, EF_EPSR ldcr TMP3, SNIP st TMP3, %r31, EF_SNIP ldcr TMP2, SFIP st TMP2, %r31, EF_SFIP /* get and store the cpu_info pointer */ ldcr TMP, CPU st TMP, %r31, EF_CPU /* * Save Pbus fault status register from data and inst CMMU. * We can afford calling a function since r1 is safe to use here. */ GLOBAL(pfsr_save) PFSR_SAVE ASLOCAL(pfsr_done) ldcr TMP, SSBR ldcr TMP2, SXIP ldcr TMP3, DMT0 st TMP2, %r31, EF_SXIP /* * The above shadow registers are obligatory for any and all * exceptions. Now, if the data access pipeline is not clear, * we must save the DMx shadow registers, as well as clear * the appropriate SSBR bits for the destination registers of * loads or xmems. */ bb0.n DMT_VALID_BIT, TMP3, 8f st TMP3, %r31, EF_DMT0 ldcr TMP2, DMT1 ldcr TMP3, DMT2 st TMP2, %r31, EF_DMT1 st TMP3, %r31, EF_DMT2 ldcr TMP2, DMA0 ldcr TMP3, DMA1 st TMP2, %r31, EF_DMA0 st TMP3, %r31, EF_DMA1 ldcr TMP2, DMA2 ldcr TMP3, DMD0 st TMP2, %r31, EF_DMA2 st TMP3, %r31, EF_DMD0 FLUSH_PIPELINE ldcr TMP2, DMD1 ldcr TMP3, DMD2 st TMP2, %r31, EF_DMD1 st TMP3, %r31, EF_DMD2 /* make sure an exception in fpu_enable will not see our DMT0 */ stcr %r0, DMT0 /* * need to clear "appropriate" bits in the SSBR before * we restart the FPU */ ld TMP2, %r31, EF_DMT0 bb1 DMT_LOCK_BIT, TMP2, 1f bb1 DMT_WRITE_BIT, TMP2, 2f 1: extu TMP2, TMP2, DMT_DREG_WIDTH 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 epsr, sfip, snip, sxip. * Dmt0. * Other data pipeline control registers, if appropriate. * Exception SR3, if appropriate. */ ldcr %r1, SR2 jmp %r1 /* allow the handler to clear more SSBR bits */ ASLOCAL(clear_FPi_ssbr_bit) /* * Clear floating point imprecise ssbr bits. * Also, save appropriate FPU control registers to the E.F. * * r1: return address to calling exception handler * TMP: (possibly) revised ssbr * TMP2: free * TMP3: free */ fldcr TMP2, FPSR fldcr TMP3, FPCR st TMP2, %r31, EF_FPSR st TMP3, %r31, EF_FPCR fldcr TMP2, FPECR fldcr TMP3, FPRH st TMP2, %r31, EF_FPECR st TMP3, %r31, EF_FPRH fldcr TMP2, FPIT fldcr TMP3, FPRL st TMP2, %r31, EF_FPIT st TMP3, %r31, EF_FPRL /* * We only need clear the bit in the SSBR for the * 2nd reg of a double result [see section 6.8.5] */ #define FPIT_SIZE_BIT 10 bb0 FPIT_SIZE_BIT, TMP2, 1f extu TMP2, TMP2, 5<0> /* get the reg. */ set TMP2, TMP2, 1<6> /* set width */ clr TMP, TMP, TMP2 1: jmp %r1 ASLOCAL(clear_FPp_ssbr_bit) /* * Clear floating point precise ssbr bits. * Also, save appropriate FPU control registers to the E.F. * * r1: return address to calling exception handler * TMP: (possibly) revised ssbr * TMP2: free * TMP3: free */ fldcr TMP2, FPSR fldcr TMP3, FPCR st TMP2, %r31, EF_FPSR st TMP3, %r31, EF_FPCR fldcr TMP3, FPECR st TMP3, %r31, EF_FPECR fldcr TMP2, FPHS1 fldcr TMP3, FPHS2 st TMP2, %r31, EF_FPHS1 st TMP3, %r31, EF_FPHS2 fldcr TMP2, FPLS1 fldcr TMP3, FPLS2 st TMP2, %r31, EF_FPLS1 st TMP3, %r31, EF_FPLS2 fldcr TMP2, FPPT st TMP2, %r31, EF_FPPT #define FPPT_SIZE_BIT 5 bb1.n FPPT_SIZE_BIT, TMP2, 2f extu TMP3, TMP2, 5<0> /* get FP operation dest reg */ br.n 3f set TMP3, TMP3, 1<5> /* size=1 - clear one bit for float */ 2: set TMP3, TMP3, 1<6> /* size=2 - clear two bits for double */ 3: jmp.n %r1 clr TMP, TMP, TMP3 /* clear bit(s) in ssbr. */ ASLOCAL(clear_dest_ssbr_bit) /* * There are various cases where an exception can leave the * destination register's bit in the SB set. * Examples: * misaligned or privilege exception on a LD or XMEM * DIV or DIVU by zero. * * I think that if the instruction is LD.D, then two bits must * be cleared. * * Even though there are a number of instructions/exception * combinations that could fire this code up, it's only required * to be run for the above cases. However, I don't think it'll * ever be a problem to run this in other cases (ST instructions, * for example), so I don't bother checking. If we had to check * for every possible instruction, this code would be much larger. * * The only checking, then, is to see if it's a LD.D or not. * * At the moment.... * r1: return address to calling exception handler * TMP: (possibly) revised ssbr * TMP2: free * TMP3: free */ ldcr TMP3, EPSR /* going to check: user or system memory? */ ldcr TMP2, SXIP /* get the instruction's address */ bb1.n PSR_SUPERVISOR_MODE_BIT, TMP3, 2f clr TMP2, TMP2, 2<0> /* get rid of valid and error bits. */ /* user space load here */ #if ERRATA__XXX_USR NOP ld.usr TMP2, TMP2, %r0 /* get the instruction itself */ NOP NOP NOP br 3f #else br.n 3f ld.usr TMP2, TMP2, %r0 /* get the instruction itself */ #endif 2: /* system space load here */ ld TMP2, TMP2, %r0 /* get the instruction itself */ 3: /* now we have the instruction..... */ /* * Now see if it's a double load * There are three forms of double load [IMM16, scaled, unscaled], * which can be checked by matching against two templates: * -- 77776666555544443333222211110000 -- * if (((instruction & 11111100000000000000000000000000) == * 00010000000000000000000000000000) || * ((instruction & 11111100000000001111110011100000) == * 11110100000000000001000000000000)) * { * It's a load double, so * clear two SSBR bits. * } else { * It's not a load double. * Must be a load single, xmem, or st * Thus, clear one SSBR bit. * } */ /* check the first pattern for ld.d */ extu TMP3, TMP2, 16<16> /* get the upper 16 bits */ mask TMP3, TMP3, 0xfc00 /* apply the mask */ cmp TMP3, TMP3, 0x1000 /* if equal, it's a load double */ bb1 eq, TMP3, 2f /* still could be -- check the second pattern for ld.d */ /* look at the upper 16 bits first */ extu TMP3, TMP2, 16<16> /* get the upper 16 bits */ mask TMP3, TMP3, 0xfc00 /* apply the mask */ cmp TMP3, TMP3, 0xf400 /* if equal, might be a load double */ bb1 ne, TMP3, 1f /* not equal, must be single */ /* now look at the lower 16 bits */ extu TMP3, TMP2, 16<0> /* get the lower 16 bits */ mask TMP3, TMP3, 0xfce0 /* apply the mask */ cmp TMP3, TMP3, 0x1000 /* if equal, it's a load double */ bb1 eq, TMP3, 2f 1: /* misaligned single */ extu TMP2, TMP2, 5<21> /* get the destination register */ br.n 3f set TMP2, TMP2, 1<5> /* set size=1 */ 2: /* misaligned double */ extu TMP2, TMP2, 5<21> /* get the destination register */ set TMP2, TMP2, 1<6> /* set size=2 -- clear two bits */ 3: jmp.n %r1 clr TMP, TMP, TMP2 /* clear bit(s) in ssbr. */ ASLOCAL(m88100_setup_phase_two) /* * SR1: saved copy of exception-time register now holding FLAGS * SR2: free * SR3: saved TMP * r1: return address to calling exception handler * TMP: possibly revised SSBR * TMP2: free * TMP3: free * FLAGS: CPU status flags * r31: our exception frame * Valid in the exception frame: * Exception-time r1, r31, FLAGS. * Exception-time TMP2, TMP3. * Exception-time epsr, sfip, snip, sxip. * Exception number (EF_VECTOR). * Dmt0 * Other data pipeline control registers, if appropriate. * FPU control registers, if appropriate. * Exception SR3, if appropriate. * * immediate goal: * restore the system to the exception-time state (except * SR3 will be OUR stack pointer) so that we may restart the FPU. */ stcr TMP, SSBR /* done with SSBR, TMP now free */ RESTORE_TMP2(%r31) /* done with extra temp regs */ RESTORE_TMP3(%r31) /* done with extra temp regs */ /* Get the current PSR and modify for the rte to enable the FPU */ ldcr TMP, PSR clr TMP, TMP, 1 /* 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(m88100_fpu_enable + 2) or TMP, TMP, %lo16(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, EF_RET /* save the return address */ ld %r1, %r31, GENREG_OFF(1) /* get original r1 */ xcr TMP, %r31, SR3 /* TMP now restored. R31 now saved in SR3 */ ld %r31, %r31, GENREG_OFF(31) /* get original r31 */ /* * SR1: CPU flags * SR2: free * SR3: pointer to our exception frame (our stack pointer) * r1 through r31: original exception-time values * * Valid in the exception frame: * Exception-time FLAGS. * Exception-time epsr, sfip, snip, sxip. * Exception number (EF_VECTOR). * Dmt0 * Other data pipeline control registers, if appropriate. * FPU control registers, if appropriate. * Exception SR3, if appropriate. * Held 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 "m88100_fpu_enable" to enable the FPU. */ ASLOCAL(m88100_fpu_enable) FLUSH_PIPELINE xcr TMP, TMP, SR3 /* get E.F. pointer */ st %r30, TMP, GENREG_OFF(30) /* save previous r30, r31 */ st %r31, TMP, GENREG_OFF(31) /* save previous r30, r31 */ or %r31, TMP, %r0 /* transfer E.F. pointer to r31 */ ld TMP, %r31, EF_SR3 /* get previous SR3 */ /* make sure that the FLAG_ENABLING_FPU bit is off */ xcr FLAGS,FLAGS,SR1 clr FLAGS,FLAGS,1 xcr FLAGS,FLAGS,SR1 xcr TMP, TMP, SR3 /* replace TMP, SR3 */ /* now save all regs to the exception frame. */ st %r0, %r31, GENREG_OFF(0) st %r1, %r31, GENREG_OFF(1) st %r2, %r31, GENREG_OFF(2) st %r3, %r31, GENREG_OFF(3) st %r4, %r31, GENREG_OFF(4) st %r5, %r31, GENREG_OFF(5) st %r6, %r31, GENREG_OFF(6) st %r7, %r31, GENREG_OFF(7) st %r8, %r31, GENREG_OFF(8) st %r9, %r31, GENREG_OFF(9) st %r10, %r31, GENREG_OFF(10) st %r11, %r31, GENREG_OFF(11) st %r12, %r31, GENREG_OFF(12) st %r13, %r31, GENREG_OFF(13) st %r14, %r31, GENREG_OFF(14) st %r15, %r31, GENREG_OFF(15) st %r16, %r31, GENREG_OFF(16) st %r17, %r31, GENREG_OFF(17) st %r18, %r31, GENREG_OFF(18) st %r19, %r31, GENREG_OFF(19) st %r20, %r31, GENREG_OFF(20) st %r21, %r31, GENREG_OFF(21) st %r22, %r31, GENREG_OFF(22) st %r23, %r31, GENREG_OFF(23) st %r24, %r31, GENREG_OFF(24) st %r25, %r31, GENREG_OFF(25) st %r26, %r31, GENREG_OFF(26) st %r27, %r31, GENREG_OFF(27) st %r28, %r31, GENREG_OFF(28) st %r29, %r31, GENREG_OFF(29) /* * SR1: free * SR2: free * SR3: previous exception-time SR3 * r1: return address to the calling exception handler * r2 through r30: free * r31: our exception frame * * Valid in the exception frame: * Exception-time r0 through r31. * Exception-time FLAGS. * Exception-time epsr, sfip, snip, sxip. * Exception number (EF_VECTOR). * Dmt0 * Other data pipeline control registers, if appropriate. * FPU control registers, if appropriate. * Exception SR3, if appropriate. * * immediate goal: * Pick up a stack if we came in from user mode. * Put a copy of the exception frame pointer into r30 * Bump the stack a doubleword and write the exception frame pointer. * If not an interrupt exception, turn on interrupts and service any * outstanding data access exceptions. * Return to calling exception handler to service the exception. */ /* * If it's not the interrupt exception, enable interrupts and * take care of any data access exceptions...... */ or %r30, %r0, %r31 /* get a copy of the e.f. pointer */ ld %r6, %r31, EF_EPSR bb1 PSR_SUPERVISOR_MODE_BIT, %r6, 1f /* if in kernel mode */ ldcr %r31, CPU ld %r31, %r31, CI_CURPCB addu %r31, %r31, USPACE /* point at proper end */ 1: /* get and save IPL */ bsr getipl st %r2, %r30, EF_MASK /* * here - r30 holds a pointer to the exception frame. * r31 is a pointer to the kernel stack/interrupt stack. */ subu %r31, %r31, 8 /* make some breathing space */ st %r30, %r31, 0 /* store frame pointer on the stack */ #ifdef DDB st %r30, %r31, 4 /* store it for the debugger to recognize */ #endif ld %r6, %r30, EF_EPSR ld %r2, %r30, EF_VECTOR bcnd.n eq0, %r2, 8f /* error exception */ ld %r14, %r30, EF_RET /* * Do not process possible data exceptions here if this is an interrupt. * Instead, the interrupt handler will take care of this by itself. */ cmp %r3, %r2, 1 /* is an interrupt? */ bb1.n eq, %r3, 8f /* skip if so */ #ifdef DDB cmp %r3, %r2, 130 /* DDB break exception */ bb1.n eq, %r3, 8f cmp %r3, %r2, 132 /* DDB entry exception */ bb1 eq, %r3, 8f #else NOP #endif /* turn interrupts back on unless they were not enabled when the trap occurred */ bb1.n PSR_INTERRUPT_DISABLE_BIT, %r6, 7f ld %r3, %r30, EF_DMT0 ldcr %r2, PSR clr %r2, %r2, 1 stcr %r2, PSR FLUSH_PIPELINE 7: /* service any outstanding data pipeline stuff */ bb0 DMT_VALID_BIT, %r3, 8f /* * r30 can be clobbered by calls. So stuff its value into a preserved * register, say r15. R14 is in use (see return_to_... below). */ or %r15, %r0, %r30 CALL(m88100_trap, T_DATAFLT, %r15) or %r30, %r0, %r15 8: jmp %r14 /* loaded above */ #endif /* M88100 */ #ifdef M88110 /* * 88110 exception handlers */ /* unknown exception handler */ GLOBAL(m88110_unknown_handler) PREP88110("unknown", 0,) or %r2, %r0, T_UNKNOWNFLT or %r3, %r0, %r30 XCALL(m88110_trap, check_ast) /* interrupt exception handler */ GLOBAL(m88110_interrupt_handler) PREP88110("interrupt", 1,) or %r2, %r0, %r30 XCALL(interrupt, check_ast) /* instruction access exception handler */ GLOBAL(m88110_instruction_access_handler) PREP88110("inst", 2,) or %r2, %r0, T_INSTFLT or %r3, %r0, %r30 XCALL(m88110_trap, check_ast) /* * data access exception handler -- * See badaddr() below for info about Data_Precheck. */ GLOBAL(m88110_data_exception_handler_bootstrap) PREP88110("data", 3, M88110_Data_Precheck) or %r2, %r0, T_DATAFLT or %r3, %r0, %r30 XCALL(m88110_trap, check_ast) GLOBAL(m88110_data_exception_handler) PREP88110("data", 3,) or %r2, %r0, T_DATAFLT or %r3, %r0, %r30 XCALL(m88110_trap, check_ast) /* misaligned access exception handler */ GLOBAL(m88110_misaligned_handler) PREP88110("misalign", 4,) or %r2, %r0, T_MISALGNFLT or %r3, %r0, %r30 XCALL(m88110_trap, check_ast) /* unimplemented opcode exception handler */ GLOBAL(m88110_unimplemented_handler) PREP88110("unimp", 5,) or %r2, %r0, T_ILLFLT or %r3, %r0, %r30 XCALL(m88110_trap, check_ast) /* privilege exception handler */ GLOBAL(m88110_privilege_handler) PREP88110("privilege", 6,) or %r2, %r0, T_PRIVINFLT or %r3, %r0, %r30 XCALL(m88110_trap, check_ast) /* bounds checking exception handler */ GLOBAL(m88110_bounds_handler) PREP88110("bounds", 7,) or %r2, %r0, T_BNDFLT or %r3, %r0, %r30 XCALL(m88110_trap, 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(m88110_trap, check_ast) /* integer overflow exception handler */ GLOBAL(m88110_overflow_handler) PREP88110("overflow", 9,) or %r2, %r0, T_OVFFLT or %r3, %r0, %r30 XCALL(m88110_trap, check_ast) /* Floating-point precise handler */ GLOBAL(m88110_fpu_handler) PREP88110("FPU", 114,) or %r2, %r0, T_FPEPFLT or %r3, %r0, %r30 XCALL(m88110_trap, check_ast) /* non-maskable interrupt handler (IPIs, ABORT button) */ GLOBAL(m88110_nonmaskable) PREP88110("NMI", 11,) or %r2, %r0, %r30 XCALL(nmi, nmi_return) /* software walk data MMU read miss handler */ GLOBAL(m88110_data_read_miss) PREP88110("88110 data read miss", 12,) or %r2, %r0, T_110_DRM or %r3, %r0, %r30 XCALL(m88110_trap, 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(m88110_trap, 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(m88110_trap, check_ast) /* trap 450: system calls */ GLOBAL(m88110_syscall_handler) PREP88110("syscall", 450,) ld %r2, %r30, GENREG_OFF(13) or %r3, %r0, %r30 XCALL(m88110_syscall, check_ast) /* trap 451: cache flush (necessary for trampolines) */ GLOBAL(m88110_cache_flush_handler) PREP88110("cache_flush", 451,) or %r2, %r0, %r30 XCALL(cache_flush, check_ast) GLOBAL(m88110_sigsys) PREP88110("sigsys", 501,) or %r2, %r0, T_SIGSYS or %r3, %r0, %r30 XCALL(m88110_trap, check_ast) GLOBAL(m88110_stepbpt) PREP88110("stepbpt", 504,) or %r2, %r0, T_STEPBPT or %r3, %r0, %r30 XCALL(m88110_trap, check_ast) GLOBAL(m88110_userbpt) PREP88110("userbpt", 511,) or %r2, %r0, T_USERBPT or %r3, %r0, %r30 XCALL(m88110_trap, check_ast) #ifdef DDB GLOBAL(m88110_break) PREP88110("break", 130,) or %r2, %r0, T_KDB_BREAK or %r3, %r0, %r30 XCALL(m88110_trap, check_ast) GLOBAL(m88110_trace) PREP88110("trace", 131,) or %r2, %r0, T_KDB_TRACE or %r3, %r0, %r30 XCALL(m88110_trap, check_ast) GLOBAL(m88110_entry) PREP88110("kdb", 132,) or %r2, %r0, T_KDB_ENTRY or %r3, %r0, %r30 XCALL(m88110_trap, check_ast) #endif /* * The error exception and reset exception handler. * * The error exception is raised when any other non-trap exception is raised * while shadowing is off. This is Bad News. * * The reset exception is raised when the RST signal is asserted (machine * is reset), the value of VBR is changed after exceptions are enabled, * or when a jmp, br/bsr to addr 0 (accidents do happen :-) * Upon a real reset, VBR is set to zero (0), so code must be at addr 0 * to handle it!!! * * The shadow registers are not valid in this case (shadowing was off, if this * was an error exception, and may not be on, if this was a reset exception). * R1-R31 may be interesting though, so we'll save them. * * We'll not worry about trashing r26-29 here, * since they aren't generally used. */ GLOBAL(m88110_error_handler) or %r29, %r0, 10 br 1f GLOBAL(m88110_reset_handler) or %r29, %r0, 0 1: or %r26, %r0, %r31 /* save old stack */ or.u %r31, %r0, %hi16(initstack_end) or %r31, %r31, %lo16(initstack_end) #ifdef DEBUG /* zero the stack, so we'll know what we're lookin' at */ or.u %r27, %r0, %hi16(initstack) or %r27, %r27, %lo16(initstack) 1: cmp %r28, %r27, %r31 bb1 ge, %r28, 2f /* branch if at the end of the stack */ st %r0, %r0, %r27 addu %r27, %r27, 4 /* bump up */ br 1b 2: /* stack has been cleared */ #endif /* ensure that stack is 8-byte aligned */ clr %r31, %r31, 3<0> /* round down to 8-byte boundary */ /* create exception frame on stack */ subu %r31, %r31, TRAPFRAME_SIZEOF /* r31 now our E.F. */ /* save old R31 and other R registers */ st.d %r0, %r31, GENREG_OFF(0) st.d %r2, %r31, GENREG_OFF(2) st.d %r4, %r31, GENREG_OFF(4) st.d %r6, %r31, GENREG_OFF(6) st.d %r8, %r31, GENREG_OFF(8) st.d %r10, %r31, GENREG_OFF(10) st.d %r12, %r31, GENREG_OFF(12) st.d %r14, %r31, GENREG_OFF(14) st.d %r16, %r31, GENREG_OFF(16) st.d %r18, %r31, GENREG_OFF(18) st.d %r20, %r31, GENREG_OFF(20) st.d %r22, %r31, GENREG_OFF(22) st.d %r24, %r31, GENREG_OFF(24) st %r30, %r31, GENREG_OFF(30) st %r26, %r31, GENREG_OFF(31) /* vector is put in SRO (either 0 or 10 at this point) */ st %r29, %r31, EF_VECTOR /* save shadow registers (are OLD if error_handler, though) */ ldcr %r10, EPSR st %r10, %r31, EF_EPSR ldcr %r10, EXIP st %r10, %r31, EF_EXIP ldcr %r10, ENIP st %r10, %r31, EF_ENIP ldcr %r10, DSR st %r10, %r31, EF_DSR ldcr %r10, DLAR st %r10, %r31, EF_DLAR ldcr %r10, DPAR st %r10, %r31, EF_DPAR ldcr %r10, ISR st %r10, %r31, EF_ISR ldcr %r10, ILAR st %r10, %r31, EF_ILAR ldcr %r10, IPAR st %r10, %r31, EF_IPAR ldcr %r10, SR1 st %r10, %r31, EF_FLAGS /* shove sr2 into EF_FPLS1 */ ldcr %r10, SR2 st %r10, %r31, EF_FPLS1 /* shove sr3 into EF_FPHS2 */ ldcr %r10, SR3 st %r10, %r31, EF_FPHS2 /* * Cheap way to enable FPU and start shadowing again. */ ldcr %r10, PSR clr %r10, %r10, 1 /* 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 or %r2, %r0, %r30 bsr error_fatal /* turn interrupts back on */ ldcr %r1, PSR clr %r1, %r1, 1 stcr %r1, PSR FLUSH_PIPELINE 1: br 1b /* NOTREACHED */ /* * 88110 exception handling setup * * This is much simpler than for 88100, because all exception are * precise. Therefore, when reenabling shadowing, we do not risk * getting new exceptions caught by the execution pipeline and not * reported yet. * * However, as soon as shadow freezing is over, we can receive a * non-maskable interrupt at any time. The code below will cope with * this, as long as the stack pointer (r31) is valid in the kernel * all the time shadowing is enabled. * * Thus, unlike the 88100 code, we setup both the exception frame * (in r30) and the exception stack (in r31) as early as possible. */ ASLOCAL(m88110_setup_phase_one) /* * SR1: saved copy of exception-time register now holding FLAGS * SR2: saved copy of exception-time r1 * SR3: free * r1: return address to calling exception handler * FLAGS: CPU status flags * * immediate goal: * Find out where to put the exception frame, and which * stack to use. */ NOP xcr %r1, %r1, SR2 /* recover exception-time r1 */ NOP NOP NOP /* * If we were in the kernel when the exception occurred, we have * a valid stack. Keep using it, and build the frame on it. * * Note that if this exception is an NMI, we might be currently * switching processes, and curpcb and curproc won't match. It * is nevertheless safe to use curpcb, since nmi are processed * with interrupts disabled, and we won't check for AST or soft * interrupts before returning to the cpu_switchto kernel code. */ bb1 FLAG_FROM_KERNEL, FLAGS, m88110_kernel_stack /* * Otherwise, this is an exception in user mode, we'll use the PCB * for the exception frame and the top of the PCB as the stack. */ /* compute frame address: in PCB */ stcr %r30, SR3 /* save r30, now free */ ldcr %r30, CPU ld %r30, %r30, CI_CURPCB addu %r30, %r30, PCB_USER_STATE /* point to user save area */ /* save a few registers before we lose them*/ st %r1, %r30, GENREG_OFF(1) /* save prev. r1 (now free)*/ ldcr %r1, SR3 /* save previous r30 */ st %r31, %r30, GENREG_OFF(31) st %r1, %r30, GENREG_OFF(30) /* compute stack address: top of U area */ ldcr %r1, CPU ld %r31, %r1, CI_CURPCB addu %r31, %r31, USPACE br m88110_have_stack ASLOCAL(m88110_kernel_stack) /* Exception in kernel mode */ /* compute stack and frame address: allocate them on current stack */ subu %r31, %r31, TRAPFRAME_SIZEOF /* r31 now our E.F. */ /* save a few registers before we lose them */ st %r1, %r31, GENREG_OFF(1) /* save prev. r1 (now free) */ addu %r1, %r31, TRAPFRAME_SIZEOF /* save previous r31 */ st %r30, %r31, GENREG_OFF(30) st %r1, %r31, GENREG_OFF(31) /* frame = stack */ or %r30, %r31, %r0 /* FALLTHROUGH */ ASLOCAL(m88110_have_stack) /* * SR1: saved copy of exception-time register now holding FLAGS * SR2: return address to the calling exception handler * SR3: free * r1: free * FLAGS: CPU status flags * r30: incomplete exception frame * r31: exception stack * Valid in the exception frame: * Exception-time r1, r30, r31, FLAGS. */ stcr TMP, SR3 /* free up TMP, TMP2, TMP3 */ SAVE_TMP2(%r30) SAVE_TMP3(%r30) /* save some exception-time registers to the exception frame */ st FLAGS,%r30, EF_FLAGS /* save flags */ ldcr TMP, EPSR st TMP, %r30, EF_EPSR ldcr TMP2, EXIP ldcr TMP3, ENIP st TMP2, %r30, EF_EXIP st TMP3, %r30, EF_ENIP /* get and store the cpu_info pointer */ ldcr TMP, CPU st TMP, %r30, EF_CPU /* * Save and clear fault status registers. */ ldcr TMP, ISR st TMP, %r30, EF_ISR bcnd eq0, TMP, 1f ldcr TMP2, ILAR ldcr TMP3, IPAR st TMP2, %r30, EF_ILAR st TMP3, %r30, EF_IPAR ldcr TMP, ISAP ldcr TMP2, IUAP st TMP, %r30, EF_ISAP st TMP2, %r30, EF_IUAP stcr %r0, ISR 1: ldcr TMP, DSR st TMP, %r30, EF_DSR bcnd eq0, TMP, 1f ldcr TMP2, DLAR ldcr TMP3, DPAR st TMP2, %r30, EF_DLAR st TMP3, %r30, EF_DPAR ldcr TMP, DSAP ldcr TMP2, DUAP st TMP, %r30, EF_DSAP st TMP2, %r30, EF_DUAP stcr %r0, DSR 1: ldcr %r1, SR2 jmp %r1 ASLOCAL(m88110_setup_phase_two) /* * SR1: saved copy of exception-time register now holding FLAGS * SR2: free * SR3: saved TMP * r1: return address to calling exception handler * TMP2: free * TMP3: free * FLAGS: CPU status flags * r30: incomplete exception frame * r31: exception stack * Valid in the exception frame: * Exception-time r1, r30, r31, FLAGS. * Exception-time TMP2, TMP3. * Exception-time epsr, enip, exip. * Exception number (EF_VECTOR). * * immediate goal: * restore the system to the exception-time state. */ RESTORE_TMP2(%r30) /* done with extra temp regs */ RESTORE_TMP3(%r30) /* done with extra temp regs */ ldcr TMP, PSR clr TMP, TMP, 1 /* enable shadowing */ stcr TMP, EPSR or.u TMP, %r0, %hi16(m88110_shadow_enable) or TMP, TMP, %lo16(m88110_shadow_enable) stcr TMP, EXIP xcr FLAGS,FLAGS,SR1 st %r1, %r30, EF_RET /* save the return address */ xcr TMP, %r31, SR3 /* TMP now restored. R31 now saved in SR3 */ /* * SR1: CPU flags * SR2: free * SR3: pointer to our exception frame (our stack pointer) * r2 through r29: original exception-time values * r30: exception frame * r31: exception stack * * Valid in the exception frame: * Exception-time r1, r30, r31, FLAGS. * Exception-time epsr, sfip, enip, exip. * Exception number (EF_VECTOR). * DSR/ISR, fault registers, if appropriate. * Held temporarily in the exception frame: * Return address to the calling exception handler. * * immediate goal: * Do an RTE to unfreeze the shadow registers. * Another exception (NMI) may be raised in this. */ RTE /* jumps to "m88110_shadow_enable" */ ASLOCAL(m88110_shadow_enable) FLUSH_PIPELINE /* XXX necessary? */ /* now save all missing regs to the exception frame. */ st %r0, %r30, GENREG_OFF(0) st %r2, %r30, GENREG_OFF(2) st %r3, %r30, GENREG_OFF(3) st %r4, %r30, GENREG_OFF(4) st %r5, %r30, GENREG_OFF(5) st %r6, %r30, GENREG_OFF(6) st %r7, %r30, GENREG_OFF(7) st %r8, %r30, GENREG_OFF(8) st %r9, %r30, GENREG_OFF(9) st %r10, %r30, GENREG_OFF(10) st %r11, %r30, GENREG_OFF(11) st %r12, %r30, GENREG_OFF(12) st %r13, %r30, GENREG_OFF(13) st %r14, %r30, GENREG_OFF(14) st %r15, %r30, GENREG_OFF(15) st %r16, %r30, GENREG_OFF(16) st %r17, %r30, GENREG_OFF(17) st %r18, %r30, GENREG_OFF(18) st %r19, %r30, GENREG_OFF(19) st %r20, %r30, GENREG_OFF(20) st %r21, %r30, GENREG_OFF(21) st %r22, %r30, GENREG_OFF(22) st %r23, %r30, GENREG_OFF(23) st %r24, %r30, GENREG_OFF(24) st %r25, %r30, GENREG_OFF(25) st %r26, %r30, GENREG_OFF(26) st %r27, %r30, GENREG_OFF(27) st %r28, %r30, GENREG_OFF(28) st %r29, %r30, GENREG_OFF(29) /* * SR1: free * SR2: free * SR3: free * r1 through r29: free * r30: exception frame * r31: exception stack * * Valid in the exception frame: * Exception-time r0 through r31. * Exception-time FLAGS. * Exception-time epsr, enip, exip. * Exception number (EF_VECTOR). * Return value (EF_RET). * DSR/ISR, fault registers, if appropriate. * * immediate goal: * Bump the stack a doubleword and write the exception frame pointer. * If not an interrupt exception or an NMI, turn on interrupts. * Return to calling exception handler to service the exception. */ /* get and save IPL */ bsr getipl st %r2, %r30, EF_MASK subu %r31, %r31, 8 /* make some breathing space */ st %r30, %r31, 0 /* store frame pointer on the stack */ #ifdef DDB st %r30, %r31, 4 /* store it again for the debugger */ #endif ld %r6, %r30, EF_EPSR ld %r14, %r30, EF_RET /* don't turn interrupts back on unless they were enabled when the trap occurred */ bb1 PSR_INTERRUPT_DISABLE_BIT, %r6, 8f ld %r2, %r30, EF_VECTOR bcnd eq0, %r2, 8f cmp %r3, %r2, 1 /* is this an interrupt? */ bb1 eq, %r3, 8f cmp %r3, %r2, 11 /* or NMI? */ bb1 eq, %r3, 8f #ifdef DDB cmp %r3, %r2, 130 /* DDB break exception */ bb1 eq, %r3, 8f cmp %r3, %r2, 132 /* DDB entry exception */ bb1 eq, %r3, 8f #endif ldcr %r2, PSR clr %r2, %r2, 1 stcr %r2, PSR FLUSH_PIPELINE 8: jmp %r14 /* loaded above */ #endif /* M88110 */ .text #define FPTR %r14 ASGLOBAL(check_ast) /* * Regs r1-r30 are free. r31 is pointing at the word * on the kernel stack where our pointer to the exception frame * it stored. Reload it now. * * We load it into r14 since it is preserved across function * calls, and we may have to call some routines from within here. * * Control is transferred here from obvious places in this file. */ ld FPTR, %r31, 0 /* grab exception frame pointer */ /* * If the saved ipl is 0, then call spl0() to process soft * interrupts. And if returning to userland, look for ASTs. */ /* do not service AST and soft interrupts if interrupts were disabled */ ld %r2, FPTR, EF_EPSR bb1 PSR_INTERRUPT_DISABLE_BIT, %r2, ast_done /* ...or we were not at spl0 */ ld %r2, FPTR, EF_MASK bcnd ne0, %r2, ast_done #ifdef MULTIPROCESSOR /* * Check for IPI soft interrupt */ ldcr %r3, CPU or %r15, %r0, %r0 addu %r3, %r3, CI_SOFTIPI_CB xmem %r15, %r3, %r0 bcnd eq0, %r15, do_softint bsr.n setipl or %r2, %r0, IPL_NONE jsr %r15 ASLOCAL(do_softint) #endif /* * Process soft interrupts, if any. */ bsr spl0 /* do not service AST if not returning to user mode */ ld %r2, FPTR, EF_EPSR bb1 PSR_SUPERVISOR_MODE_BIT, %r2, ast_done 1: ldcr %r2, CPU ld %r3, %r2, CI_CURPROC ld %r2, %r3, P_ASTPENDING bcnd eq0, %r2, ast_done bsr.n ast or %r2, %r0, FPTR br 1b /* * void proc_trampoline() * * When a process setup by cpu_fork() resumes, it will find itself in * proc_trampoline, with r14 and r15 referring to a function and its * argument. proc_trampoline will call the function, and return to userland. */ ENTRY(proc_trampoline) bsr proc_trampoline_mi jsr.n %r14 or %r2, %r15, 0 /* arg */ /* * Load FPTR with a pointer to the trap frame for the current proc and * continue near the end of check_ast, bypassing soft interrupts and AST * checks, to load all the registers and do an RTE. */ ldcr %r3, CPU ld %r2, %r3, CI_CURPROC ld FPTR, %r2, P_ADDR /* p->p_addr */ addu FPTR, FPTR, PCB_USER_STATE /* p->p_addr.u_pcb.user_state */ /* FALLTHROUGH */ ASGLOBAL(ast_done) /* disable interrupts */ ldcr %r1, PSR set %r1, %r1, 1 stcr %r1, PSR FLUSH_PIPELINE /* now ready to return....*/ bsr.n setipl ld %r2, FPTR, EF_MASK /* get pre-exception ipl */ #if defined(M88100) && defined(M88110) ldcr %r2, PID extu %r3, %r2, 8<8> bcnd ne0, %r3, m88110_user_rte /* FALLTHROUGH */ #endif #ifdef M88100 ASGLOBAL(m88100_user_rte) /* * Transfer the frame pointer to r31, since we no longer need a stack. * No page faults here, and interrupts are disabled. */ or %r31, %r0, FPTR /* restore r1 later */ ld %r2, %r31, GENREG_OFF(2) ld %r3, %r31, GENREG_OFF(3) ld %r4, %r31, GENREG_OFF(4) ld %r5, %r31, GENREG_OFF(5) ld %r6, %r31, GENREG_OFF(6) ld %r7, %r31, GENREG_OFF(7) ld %r8, %r31, GENREG_OFF(8) ld %r9, %r31, GENREG_OFF(9) ld %r10, %r31, GENREG_OFF(10) ld %r11, %r31, GENREG_OFF(11) ld %r12, %r31, GENREG_OFF(12) ld %r13, %r31, GENREG_OFF(13) ld %r14, %r31, GENREG_OFF(14) ld %r15, %r31, GENREG_OFF(15) ld %r16, %r31, GENREG_OFF(16) ld %r17, %r31, GENREG_OFF(17) ld %r18, %r31, GENREG_OFF(18) ld %r19, %r31, GENREG_OFF(19) ld %r20, %r31, GENREG_OFF(20) ld %r21, %r31, GENREG_OFF(21) ld %r22, %r31, GENREG_OFF(22) ld %r23, %r31, GENREG_OFF(23) ld %r24, %r31, GENREG_OFF(24) ld %r25, %r31, GENREG_OFF(25) ld %r26, %r31, GENREG_OFF(26) ld %r27, %r31, GENREG_OFF(27) ld %r28, %r31, GENREG_OFF(28) ld %r29, %r31, GENREG_OFF(29) /* restore r1, r30, r31 later */ /* disable shadowing */ ldcr %r1, PSR set %r1, %r1, 1 stcr %r1, PSR FLUSH_PIPELINE /* reload the control regs*/ /* * RTE will cause execution to continue first with the * instruction pointed to by the NIP and then the FIP; * it is not necessary to restore XIP. */ stcr %r0, SSBR ld %r30, %r31, EF_SNIP ld %r1, %r31, EF_SFIP stcr %r30, SNIP stcr %r1, SFIP ld %r30, %r31, EF_EPSR stcr %r30, EPSR /* Now restore r1, r30, and r31 */ ld %r1, %r31, GENREG_OFF(1) ld %r30, %r31, GENREG_OFF(30) ld %r31, %r31, GENREG_OFF(31) RTE #endif #ifdef M88110 ASGLOBAL(m88110_user_rte) /* * Disable shadowing. This used to be done after all the registers * from the E.F. have been restored, but on 88110 we may receive * an NMI anytime, unless shadowing is frozen, and we rely on r31 * being valid. */ ldcr %r1, PSR set %r1, %r1, 1 stcr %r1, PSR FLUSH_PIPELINE ASLOCAL(m88110_restore) /* * Transfer the frame pointer to r31, since we no longer need a stack. * No page faults here, and interrupts are disabled. */ or %r31, %r0, FPTR /* restore r1 later */ ld %r2, %r31, GENREG_OFF(2) ld %r3, %r31, GENREG_OFF(3) ld %r4, %r31, GENREG_OFF(4) ld %r5, %r31, GENREG_OFF(5) ld %r6, %r31, GENREG_OFF(6) ld %r7, %r31, GENREG_OFF(7) ld %r8, %r31, GENREG_OFF(8) ld %r9, %r31, GENREG_OFF(9) ld %r10, %r31, GENREG_OFF(10) ld %r11, %r31, GENREG_OFF(11) ld %r12, %r31, GENREG_OFF(12) ld %r13, %r31, GENREG_OFF(13) ld %r14, %r31, GENREG_OFF(14) ld %r15, %r31, GENREG_OFF(15) ld %r16, %r31, GENREG_OFF(16) ld %r17, %r31, GENREG_OFF(17) ld %r18, %r31, GENREG_OFF(18) ld %r19, %r31, GENREG_OFF(19) ld %r20, %r31, GENREG_OFF(20) ld %r21, %r31, GENREG_OFF(21) ld %r22, %r31, GENREG_OFF(22) ld %r23, %r31, GENREG_OFF(23) ld %r24, %r31, GENREG_OFF(24) ld %r25, %r31, GENREG_OFF(25) ld %r26, %r31, GENREG_OFF(26) ld %r27, %r31, GENREG_OFF(27) ld %r28, %r31, GENREG_OFF(28) ld %r29, %r31, GENREG_OFF(29) /* restore r1, r30, r31 later */ /* reload the control regs*/ ld %r30, %r31, EF_ENIP ld %r1, %r31, EF_EXIP stcr %r30, ENIP stcr %r1, EXIP ld %r30, %r31, EF_EPSR stcr %r30, EPSR /* Now restore r1, r30, and r31 */ ld %r1, %r31, GENREG_OFF(1) ld %r30, %r31, GENREG_OFF(30) ld %r31, %r31, GENREG_OFF(31) /* * 88110 errata #18 (fixed in revision 5 cpus): * ``An rte to user mode where the logical address of the rte * instruction is within two cache lines of the logical address * in the exip can cause the wrong instruction to be returned * to if the rte instruction misses in the icache.'' * * Since we run with interrupts disabled at this point, all we * need is make sure that the rte will not appear as the first * instruction of a cache line. */ br 1f .align 6 /* 32 bytes might not be enough */ 1: NOP RTE /* * NMI return here after processing. * We then decide whether to check for AST and soft interrupts, * or not. */ ASLOCAL(nmi_return) bcnd ne0, %r2, check_ast ld FPTR, %r31, 0 /* grab exception frame pointer */ /* * Disable interrupts and shadowing. The latter used to be done * after all the registers from the E.F. have been restored, but * on 88110 we may receive an NMI anytime, unless shadowing is frozen, * and we rely on r31 being valid. */ ldcr %r1, PSR set %r1, %r1, 1 set %r1, %r1, 1 stcr %r1, PSR FLUSH_PIPELINE /* now ready to return....*/ bsr.n setipl ld %r2, FPTR, EF_MASK /* get pre-exception ipl */ #ifdef MULTIPROCESSOR /* * Reenable NMIs if necessary. */ or %r2, FPTR, %r0 bsr nmi_wrapup #endif br m88110_restore #endif