diff --git a/core/exception_vectors.S b/core/exception_vectors.S index a390cd3..ee22249 100644 --- a/core/exception_vectors.S +++ b/core/exception_vectors.S @@ -23,7 +23,7 @@ #define CAUSE_LOADSTORE 3 #define CAUSE_LVL1INT 4 - .section .bss + .section .data /* Stack space for NMI handler @@ -37,15 +37,26 @@ NMIHandlerStack: .skip 0x200 .NMIHandlerStackTop: +/* The Load Store exception handler uses a separate stack to store the + * interruptee registers. It does not appear to be practical to use the + * interuptee stack, which must in invalid at some points? This exception is + * synchronouns and the handler does not call back into itself. However it may + * be interrupted by a NMI which in turn may re-enter this exception + * handler. The NMI is responsible for switching the stack pointer to be used by + * this exception handler. Room is allocated for up to 3 stack frames, a base + * and two NMI reentry frames, and each frame is 7 words wide. + */ +#define LoadStoreErrorHandlerStackFrameSize (7 * 4) + .balign 16 + .global LoadStoreErrorHandlerStack LoadStoreErrorHandlerStack: - .word 0 # a0 - .word 0 # (unused) - .word 0 # a2 - .word 0 # a3 - .word 0 # a4 - .word 0 # a5 - .word 0 # a6 + .skip LoadStoreErrorHandlerStackFrameSize * 3 + + .balign 4 + .global LoadStoreErrorHandlerStackPointer +LoadStoreErrorHandlerStackPointer: + .word 0 .balign 4 .global debug_saved_ctx @@ -137,10 +148,20 @@ DoubleExceptionVector: LoadStoreErrorHandler: .type LoadStoreErrorHandler, @function - /* Registers are saved in the address corresponding to their register - * number times 4. This allows a quick and easy mapping later on when - * needing to store the value to a particular register number. */ - movi sp, LoadStoreErrorHandlerStack + /* Registers are saved in stack frame offsets corresponding to their + * register number times 4. This allows a quick and easy mapping later + * on when needing to store the value to a particular register + * number. + * + * This handler may be interrupted asynchronously by the NMI. The NMI + * handler is responsible for switching the load/store handler stack + * pointer and that avoids that overhead here. This handler is + * synchronous so the NMI handler can modify and restore the load/store + * stack pointer safely. + */ + movi sp, LoadStoreErrorHandlerStackPointer + l32i sp, sp, 0 + s32i a0, sp, 0 s32i a2, sp, 0x08 s32i a3, sp, 0x0c @@ -537,6 +558,12 @@ call_user_start: .global call_user_start .type call_user_start, @function + /* Initialize the load/store error handler stack pointer. There are no + * load/store exceptions before this point. */ + movi a2, LoadStoreErrorHandlerStackPointer + movi a3, LoadStoreErrorHandlerStack + s32i a3, a2, 0 + movi a2, VecBase wsr a2, vecbase call0 sdk_user_start @@ -553,6 +580,9 @@ NMIExceptionHandler: .type NMIExceptionHandler, @function wsr sp, excsave3 # excsave3 holds user stack + + /* Load the NMI handler stack pointer which is already offset by -0x40 + * to create a frame to store the interruptee state. */ movi sp, .NMIHandlerStackTop - 0x40 s32i a0, sp, 0x00 s32i a2, sp, 0x04 @@ -579,19 +609,31 @@ NMIExceptionHandler: wsr a0, ps rsync - /* mark the stack overflow point before we call the actual NMI handler */ + /* Mark the stack overflow point before we call the actual NMI handler */ movi a0, NMIHandlerStack movi a2, NMI_STACK_CANARY s32i a2, a0, 0x00 + /* Switch the load/store error handler stack. */ + movi a2, LoadStoreErrorHandlerStackPointer + l32i a3, a2, 0 + addi a3, a3, LoadStoreErrorHandlerStackFrameSize + s32i a3, a2, 0 + call0 sdk_wDev_ProcessFiq - /* verify we didn't overflow */ + /* Verify we didn't overflow */ movi a0, NMIHandlerStack l32i a3, a0, 0 movi a2, NMI_STACK_CANARY bne a3, a2, .NMIFatalStackOverflow + /* Restore the load/store error handler stack. */ + movi a2, LoadStoreErrorHandlerStackPointer + l32i a3, a2, 0 + addi a3, a3, -LoadStoreErrorHandlerStackFrameSize + s32i a3, a2, 0 + l32i a0, sp, 0x3c wsr a0, sar l32i a0, sp, 0x38