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:
parent
d9c5f2eaa2
commit
8513e5de22
1 changed files with 78 additions and 34 deletions
|
@ -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,13 +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 (5 * 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
|
||||||
|
|
||||||
.balign 4
|
.balign 4
|
||||||
.global debug_saved_ctx
|
.global debug_saved_ctx
|
||||||
|
@ -131,10 +144,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
|
||||||
|
@ -407,6 +430,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
|
||||||
|
@ -422,7 +451,10 @@ call_user_start:
|
||||||
NMIExceptionHandler:
|
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
|
||||||
|
@ -449,39 +481,51 @@ 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
|
||||||
|
|
||||||
l32i a0, sp, 0x3c
|
/* Restore the load/store error handler stack. */
|
||||||
wsr a0, sar
|
movi a2, LoadStoreErrorHandlerStackPointer
|
||||||
l32i a0, sp, 0x38
|
l32i a3, a2, 0
|
||||||
wsr a0, excvaddr
|
addi a3, a3, -LoadStoreErrorHandlerStackFrameSize
|
||||||
l32i a0, sp, 0x34
|
s32i a3, a2, 0
|
||||||
wsr a0, excsave1
|
|
||||||
l32i a0, sp, 0x30
|
l32i a0, sp, 0x3c
|
||||||
wsr a0, exccause
|
wsr a0, sar
|
||||||
l32i a0, sp, 0x2c
|
l32i a0, sp, 0x38
|
||||||
wsr a0, epc1
|
wsr a0, excvaddr
|
||||||
l32i a11, sp, 0x28
|
l32i a0, sp, 0x34
|
||||||
l32i a10, sp, 0x24
|
wsr a0, excsave1
|
||||||
l32i a9, sp, 0x20
|
l32i a0, sp, 0x30
|
||||||
l32i a8, sp, 0x1c
|
wsr a0, exccause
|
||||||
l32i a7, sp, 0x18
|
l32i a0, sp, 0x2c
|
||||||
l32i a6, sp, 0x14
|
wsr a0, epc1
|
||||||
l32i a5, sp, 0x10
|
l32i a11, sp, 0x28
|
||||||
l32i a4, sp, 0x0c
|
l32i a10, sp, 0x24
|
||||||
l32i a3, sp, 0x08
|
l32i a9, sp, 0x20
|
||||||
movi a0, 0x33 # Reset PS
|
l32i a8, sp, 0x1c
|
||||||
|
l32i a7, sp, 0x18
|
||||||
|
l32i a6, sp, 0x14
|
||||||
|
l32i a5, sp, 0x10
|
||||||
|
l32i a4, sp, 0x0c
|
||||||
|
l32i a3, sp, 0x08
|
||||||
|
movi a0, 0x33 # Reset PS
|
||||||
wsr a0, ps
|
wsr a0, ps
|
||||||
rsync
|
rsync
|
||||||
/* set dport nmi status to 1 (wDev_ProcessFiq clears bit 0 and verifies it
|
/* set dport nmi status to 1 (wDev_ProcessFiq clears bit 0 and verifies it
|
||||||
|
|
Loading…
Reference in a new issue