Skip to content

Commit acaf6f0

Browse files
committed
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.
1 parent cc4bd3c commit acaf6f0

File tree

1 file changed

+56
-14
lines changed

1 file changed

+56
-14
lines changed

core/exception_vectors.S

Lines changed: 56 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
#define CAUSE_LOADSTORE 3
2424
#define CAUSE_LVL1INT 4
2525

26-
.section .bss
26+
.section .data
2727

2828
/* Stack space for NMI handler
2929

@@ -37,15 +37,26 @@ NMIHandlerStack:
3737
.skip 0x200
3838
.NMIHandlerStackTop:
3939

40+
/* The Load Store exception handler uses a separate stack to store the
41+
* interruptee registers. It does not appear to be practical to use the
42+
* interuptee stack, which must in invalid at some points? This exception is
43+
* synchronouns and the handler does not call back into itself. However it may
44+
* be interrupted by a NMI which in turn may re-enter this exception
45+
* handler. The NMI is responsible for switching the stack pointer to be used by
46+
* this exception handler. Room is allocated for up to 3 stack frames, a base
47+
* and two NMI reentry frames, and each frame is 7 words wide.
48+
*/
49+
#define LoadStoreErrorHandlerStackFrameSize (7 * 4)
50+
4051
.balign 16
52+
.global LoadStoreErrorHandlerStack
4153
LoadStoreErrorHandlerStack:
42-
.word 0 # a0
43-
.word 0 # (unused)
44-
.word 0 # a2
45-
.word 0 # a3
46-
.word 0 # a4
47-
.word 0 # a5
48-
.word 0 # a6
54+
.skip LoadStoreErrorHandlerStackFrameSize * 3
55+
56+
.balign 4
57+
.global LoadStoreErrorHandlerStackPointer
58+
LoadStoreErrorHandlerStackPointer:
59+
.word 0
4960

5061
.balign 4
5162
.global debug_saved_ctx
@@ -137,10 +148,20 @@ DoubleExceptionVector:
137148
LoadStoreErrorHandler:
138149
.type LoadStoreErrorHandler, @function
139150

140-
/* Registers are saved in the address corresponding to their register
141-
* number times 4. This allows a quick and easy mapping later on when
142-
* needing to store the value to a particular register number. */
143-
movi sp, LoadStoreErrorHandlerStack
151+
/* Registers are saved in stack frame offsets corresponding to their
152+
* register number times 4. This allows a quick and easy mapping later
153+
* on when needing to store the value to a particular register
154+
* number.
155+
*
156+
* This handler may be interrupted asynchronously by the NMI. The NMI
157+
* handler is responsible for switching the load/store handler stack
158+
* pointer and that avoids that overhead here. This handler is
159+
* synchronous so the NMI handler can modify and restore the load/store
160+
* stack pointer safely.
161+
*/
162+
movi sp, LoadStoreErrorHandlerStackPointer
163+
l32i sp, sp, 0
164+
144165
s32i a0, sp, 0
145166
s32i a2, sp, 0x08
146167
s32i a3, sp, 0x0c
@@ -537,6 +558,12 @@ call_user_start:
537558
.global call_user_start
538559
.type call_user_start, @function
539560

561+
/* Initialize the load/store error handler stack pointer. There are no
562+
* load/store exceptions before this point. */
563+
movi a2, LoadStoreErrorHandlerStackPointer
564+
movi a3, LoadStoreErrorHandlerStack
565+
s32i a3, a2, 0
566+
540567
movi a2, VecBase
541568
wsr a2, vecbase
542569
call0 sdk_user_start
@@ -553,6 +580,9 @@ NMIExceptionHandler:
553580
.type NMIExceptionHandler, @function
554581

555582
wsr sp, excsave3 # excsave3 holds user stack
583+
584+
/* Load the NMI handler stack pointer which is already offset by -0x40
585+
* to create a frame to store the interruptee state. */
556586
movi sp, .NMIHandlerStackTop - 0x40
557587
s32i a0, sp, 0x00
558588
s32i a2, sp, 0x04
@@ -579,19 +609,31 @@ NMIExceptionHandler:
579609
wsr a0, ps
580610
rsync
581611

582-
/* mark the stack overflow point before we call the actual NMI handler */
612+
/* Mark the stack overflow point before we call the actual NMI handler */
583613
movi a0, NMIHandlerStack
584614
movi a2, NMI_STACK_CANARY
585615
s32i a2, a0, 0x00
586616

617+
/* Switch the load/store error handler stack. */
618+
movi a2, LoadStoreErrorHandlerStackPointer
619+
l32i a3, a2, 0
620+
addi a3, a3, LoadStoreErrorHandlerStackFrameSize
621+
s32i a3, a2, 0
622+
587623
call0 sdk_wDev_ProcessFiq
588624

589-
/* verify we didn't overflow */
625+
/* Verify we didn't overflow */
590626
movi a0, NMIHandlerStack
591627
l32i a3, a0, 0
592628
movi a2, NMI_STACK_CANARY
593629
bne a3, a2, .NMIFatalStackOverflow
594630

631+
/* Restore the load/store error handler stack. */
632+
movi a2, LoadStoreErrorHandlerStackPointer
633+
l32i a3, a2, 0
634+
addi a3, a3, -LoadStoreErrorHandlerStackFrameSize
635+
s32i a3, a2, 0
636+
595637
l32i a0, sp, 0x3c
596638
wsr a0, sar
597639
l32i a0, sp, 0x38

0 commit comments

Comments
 (0)