Skip to content

Commit e31ae60

Browse files
andyrossnashif
authored andcommitted
arch/xtensa: Fix nested interrupt entry
The "cross stack call" mechanism has intermediate states where the stack frames are not valid for our own interrupt entry code, which causes corruption if an interrupt races at exactly the right time. Leave interrupts masked until just before the call. The fix is midly complicated by the fact that we RELY on nested window exception frames to spill registers from the interruptee, so have to do the masking with PS.INTLEVEL, which requires a register to save its contents, which we don't have since everything needs to happen in one 4-register window. But thankfully our Zephyr-reserved EPS register is guaranteed to be available through this process. Fixes #57009 Signed-off-by: Andy Ross <[email protected]>
1 parent c626bac commit e31ae60

File tree

1 file changed

+23
-10
lines changed

1 file changed

+23
-10
lines changed

arch/xtensa/include/xtensa-asm2-s.h

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#ifndef ZEPHYR_ARCH_XTENSA_INCLUDE_XTENSA_ASM2_S_H
88
#define ZEPHYR_ARCH_XTENSA_INCLUDE_XTENSA_ASM2_S_H
99

10+
#include <zsr.h>
1011
#include "xtensa-asm2-context.h"
1112

1213
#include <offsets.h>
@@ -242,11 +243,14 @@
242243
* should point to a stored pointer which points to one BSA below the
243244
* interrupted/old stack) in A1, a handler function in A2, and a "new"
244245
* stack pointer (i.e. a pointer to the word ABOVE the allocated stack
245-
* area) in A3. On return A0/1 will be unchanged, A2 has the return
246-
* value of the called function, and A3 is clobbered. A4-A15 become
247-
* part of called frames and MUST NOT BE IN USE by the code that
248-
* expands this macro. The called function gets the context save
249-
* handle in A1 as it's first argument.
246+
* area) in A3. Exceptions should be enabled via PS.EXCM, but
247+
* PS.INTLEVEL must (!) be set such that no nested interrupts can
248+
* arrive (we restore the natural INTLEVEL from the value in ZSR_EPS
249+
* just before entering the call). On return A0/1 will be unchanged,
250+
* A2 has the return value of the called function, and A3 is
251+
* clobbered. A4-A15 become part of called frames and MUST NOT BE IN
252+
* USE by the code that expands this macro. The called function gets
253+
* the context save handle in A1 as it's first argument.
250254
*/
251255
.macro CROSS_STACK_CALL
252256
mov a6, a3 /* place "new sp" in the next frame's A2 */
@@ -266,10 +270,13 @@
266270
.align 4
267271
_xstack_call0_\@:
268272
/* We want an ENTRY to set a bit in windowstart and do the
269-
* rotation, but we want our own SP
273+
* rotation, but we want our own SP. After that, we are
274+
* running in a valid frame, so re-enable interrupts.
270275
*/
271276
entry a1, 16
272277
mov a1, a2
278+
rsr.ZSR_EPS a2
279+
wsr.PS a2
273280
call4 _xstack_call1_\@
274281
mov a2, a6 /* copy return value */
275282
retw
@@ -330,13 +337,19 @@ _xstack_returned_\@:
330337
wsr.PS a0
331338
_not_l1:
332339

333-
/* Unmask EXCM bit so C code can spill/fill in window
334-
* exceptions. Note interrupts are already fully masked by
335-
* INTLEVEL, so this is safe.
340+
/* Setting up the cross stack call below has states where the
341+
* resulting frames are invalid/non-reentrant, so we can't
342+
* allow nested interrupts. But we do need EXCM unmasked, as
343+
* we use CALL/ENTRY instructions in the process and need to
344+
* handle exceptions to spill caller/interruptee frames. Use
345+
* PS.INTLEVEL at maximum to mask all interrupts and stash the
346+
* current value in our designated EPS register (which is
347+
* guaranteed unused across the call)
336348
*/
337-
rsr.PS a0
349+
rsil a0, 0xf
338350
movi a3, ~(PS_EXCM_MASK)
339351
and a0, a0, a3
352+
wsr.ZSR_EPS a0
340353
wsr.PS a0
341354
rsync
342355

0 commit comments

Comments
 (0)