arch/risc-v: qemu: don't rewind interrupt stack sp on nested trap

qemu-rv's S-mode/non-SMP setintstack unconditionally reloaded sp to
the top of the per-cpu interrupt stack.  A trap taken while already
running on that stack rewound sp back to the same address, so the
nested trap's frame overwrote the still-live outer trap's frame.

Port the bounds check already used by the canonical setintstack in
riscv_macros.S: only move sp when it is outside the interrupt stack
range.

Other vendor chip.h files have the same unconditional-reload pattern
and are left for a follow-up.

Signed-off-by: liang.huang <liang.huang@houmo.ai>
This commit is contained in:
liang.huang 2026-07-18 11:38:40 +08:00
parent 2f73fe2267
commit 32cf92db24

View file

@ -47,6 +47,45 @@
#ifdef __ASSEMBLY__
#if CONFIG_ARCH_INTERRUPTSTACK > 15
/****************************************************************************
* Name: setintstack_bounds
*
* Description:
* Set sp to high if sp is outside [low, high], the bounds of the
* interrupt stack for the current CPU.
*
****************************************************************************/
.macro setintstack_bounds high, low
/* Check if sp is below the low address of the interrupt stack
* (outside the interrupt stack).
*/
blt sp, \low, 1f
/* Check if sp is above the high address of the interrupt stack
* (outside the interrupt stack)
*/
bgt sp, \high, 1f
/* If sp is within the interrupt stack boundaries, no action is required */
j 2f
1:
/* Set sp to the high address of the interrupt stack (start of the
* interrupt stack)
*/
mv sp, \high
2:
.endm
/****************************************************************************
* Name: setintstack
*
@ -56,7 +95,7 @@
*
****************************************************************************/
#if defined(CONFIG_SMP) && CONFIG_ARCH_INTERRUPTSTACK > 15
#if defined(CONFIG_SMP)
.macro setintstack tmp0, tmp1
up_cpu_index \tmp0
li \tmp1, STACKFRAME_ALIGN_DOWN(CONFIG_ARCH_INTERRUPTSTACK)
@ -76,40 +115,24 @@
sub \tmp1, \tmp0, \tmp1
/* Check if sp is below the low address of the interrupt stack
* (outside the interrupt stack).
*/
blt sp, \tmp1, 1f
/* Check if sp is above the high address of the interrupt stack
* (outside the interrupt stack)
*/
bgt sp, \tmp0, 1f
/* If sp is within the interrupt stack boundaries, no action is required */
j 2f
1:
/* Set sp to the high address of the interrupt stack (start of the
* interrupt stack)
*/
mv sp, \tmp0
2:
setintstack_bounds \tmp0, \tmp1
.endm
#endif /* CONFIG_SMP && CONFIG_ARCH_INTERRUPTSTACK > 15 */
#if CONFIG_ARCH_INTERRUPTSTACK > 15
#if !defined(CONFIG_SMP) && defined(CONFIG_ARCH_USE_S_MODE)
#elif defined(CONFIG_ARCH_USE_S_MODE)
.macro setintstack tmp0, tmp1
csrr \tmp0, CSR_SCRATCH
REGLOAD sp, RISCV_PERCPU_IRQSTACK(\tmp0)
REGLOAD \tmp0, RISCV_PERCPU_IRQSTACK(\tmp0)
/* tmp0 = high address (top) of the interrupt stack for this hart */
li \tmp1, STACKFRAME_ALIGN_DOWN(CONFIG_ARCH_INTERRUPTSTACK)
sub \tmp1, \tmp0, \tmp1
/* tmp1 = low address of the interrupt stack */
setintstack_bounds \tmp0, \tmp1
.endm
#endif /* !defined(CONFIG_SMP) && defined(CONFIG_ARCH_USE_S_MODE) */
#endif /* defined(CONFIG_SMP) */
#endif /* CONFIG_ARCH_INTERRUPTSTACK > 15 */
#endif /* __ASSEMBLY__ */