Load/store exception handler: handle re-entry via a NMI.

The NMI can asynchronously interrupt the load/store exception handler. This
does occur frequently as the NMI handler code does invoke load/store
exceptions, and the load/store exception handler is heavly used. This was
corrupting the load/store exception handler saved state and thus randomly
corrupting registers a0 to a6 of the interruptee.
This commit is contained in:
Our Air Quality 2018-06-17 12:25:22 +10:00
parent cc4bd3c58f
commit acaf6f0799

View file

@ -23,7 +23,7 @@
#define CAUSE_LOADSTORE 3 #define CAUSE_LOADSTORE 3
#define CAUSE_LVL1INT 4 #define CAUSE_LVL1INT 4
.section .bss .section .data
/* Stack space for NMI handler /* Stack space for NMI handler
@ -37,15 +37,26 @@ NMIHandlerStack:
.skip 0x200 .skip 0x200
.NMIHandlerStackTop: .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 .balign 16
.global LoadStoreErrorHandlerStack
LoadStoreErrorHandlerStack: LoadStoreErrorHandlerStack:
.word 0 # a0 .skip LoadStoreErrorHandlerStackFrameSize * 3
.word 0 # (unused)
.word 0 # a2 .balign 4
.word 0 # a3 .global LoadStoreErrorHandlerStackPointer
.word 0 # a4 LoadStoreErrorHandlerStackPointer:
.word 0 # a5 .word 0
.word 0 # a6
.balign 4 .balign 4
.global debug_saved_ctx .global debug_saved_ctx
@ -137,10 +148,20 @@ DoubleExceptionVector:
LoadStoreErrorHandler: LoadStoreErrorHandler:
.type LoadStoreErrorHandler, @function .type LoadStoreErrorHandler, @function
/* Registers are saved in the address corresponding to their register /* Registers are saved in stack frame offsets corresponding to their
* number times 4. This allows a quick and easy mapping later on when * register number times 4. This allows a quick and easy mapping later
* needing to store the value to a particular register number. */ * on when needing to store the value to a particular register
movi sp, LoadStoreErrorHandlerStack * 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 a0, sp, 0
s32i a2, sp, 0x08 s32i a2, sp, 0x08
s32i a3, sp, 0x0c s32i a3, sp, 0x0c
@ -537,6 +558,12 @@ call_user_start:
.global call_user_start .global call_user_start
.type call_user_start, @function .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 movi a2, VecBase
wsr a2, vecbase wsr a2, vecbase
call0 sdk_user_start call0 sdk_user_start
@ -553,6 +580,9 @@ NMIExceptionHandler:
.type NMIExceptionHandler, @function .type NMIExceptionHandler, @function
wsr sp, excsave3 # excsave3 holds user stack 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 movi sp, .NMIHandlerStackTop - 0x40
s32i a0, sp, 0x00 s32i a0, sp, 0x00
s32i a2, sp, 0x04 s32i a2, sp, 0x04
@ -579,19 +609,31 @@ NMIExceptionHandler:
wsr a0, ps wsr a0, ps
rsync 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 a0, NMIHandlerStack
movi a2, NMI_STACK_CANARY movi a2, NMI_STACK_CANARY
s32i a2, a0, 0x00 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 call0 sdk_wDev_ProcessFiq
/* verify we didn't overflow */ /* Verify we didn't overflow */
movi a0, NMIHandlerStack movi a0, NMIHandlerStack
l32i a3, a0, 0 l32i a3, a0, 0
movi a2, NMI_STACK_CANARY movi a2, NMI_STACK_CANARY
bne a3, a2, .NMIFatalStackOverflow 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 l32i a0, sp, 0x3c
wsr a0, sar wsr a0, sar
l32i a0, sp, 0x38 l32i a0, sp, 0x38