看了一下 opensbi 里,_trap_handler 的实现,里面的主体就是:
_trap_handler:
TRAP_SAVE_AND_SETUP_SP_T0
TRAP_SAVE_MEPC_MSTATUS 0
TRAP_SAVE_GENERAL_REGS_EXCEPT_SP_T0
TRAP_SAVE_INFO 0 0
TRAP_CALL_C_ROUTINE
TRAP_RESTORE_GENERAL_REGS_EXCEPT_A0_T0
TRAP_RESTORE_MEPC_MSTATUS 0
TRAP_RESTORE_A0_T0
mret
仔细看了这些汇编的实现,尤其是 TRAP_SAVE_AND_SETUP_SP_T0 的实现,
.macro TRAP_SAVE_AND_SETUP_SP_T0
/* Swap TP and MSCRATCH */
csrrw tp, CSR_MSCRATCH, tp
/* Save T0 in scratch space */
REG_S t0, SBI_SCRATCH_TMP0_OFFSET(tp)
/*
* Set T0 to appropriate exception stack
*
* Came_From_M_Mode = ((MSTATUS.MPP < PRV_M) ? 1 : 0) - 1;
* Exception_Stack = TP ^ (Came_From_M_Mode & (SP ^ TP))
*
* Came_From_M_Mode = 0 ==> Exception_Stack = TP
* Came_From_M_Mode = -1 ==> Exception_Stack = SP
*/
csrr t0, CSR_MSTATUS
srl t0, t0, MSTATUS_MPP_SHIFT
and t0, t0, PRV_M
slti t0, t0, PRV_M
add t0, t0, -1
xor sp, sp, tp
and t0, t0, sp
xor sp, sp, tp
xor t0, tp, t0
/* Save original SP on exception stack */
REG_S sp, (SBI_TRAP_REGS_OFFSET(sp) - SBI_TRAP_CONTEXT_SIZE)(t0)
/* Set SP to exception stack and make room for trap context */
add sp, t0, -(SBI_TRAP_CONTEXT_SIZE)
/* Restore T0 from scratch space */
REG_L t0, SBI_SCRATCH_TMP0_OFFSET(tp)
/* Save T0 on stack */
REG_S t0, SBI_TRAP_REGS_OFFSET(t0)(sp)
/* Swap TP and MSCRATCH */
csrrw tp, CSR_MSCRATCH, tp
.endm
说白了,这段就是一个逻辑: 如果异常从 M 模式过来,那么栈就使用 tp ,也就是 scratch 空间; 如果异常从 S 模式过来,那么栈就使用 sp ,也就是 s 模式的栈空间。
但我看了这个实现,如果进入异常后(此时处于 M ),再进入异常的话,这个栈空间就没有沿着 tp 继续增长,而是覆盖了 上一次的 tp 栈空间了。 PS:其实这里不只是异常,因为源码是使用的是 direct 模式,不是 vector 模式,所以任何 trap 包括中断和异常 都是有这个问题的。
所以现在有这几个问题:
TRAP_SAVE_AND_SETUP_SP_T0
的实现,是不是就是没有考虑到异常里面进异常时的,栈空间的不停增长?TRAP_SAVE_AND_SETUP_SP_T0
的实现逻辑是,如果异常从 S 过来,就直接用 S 模式的栈,这是什么惯用做法吗?![]() |
1
NealLason 2 天前
先不纠结栈,先考虑什么场景下会出现异常里面进异常呢?
我的理解,只有 OpenSBI 自身造成的异常才会导致重复进入异常吧,OpenSBI 自身运行在最高特权级别 M 模式,如果自身没有 bug ,不应出现自身造成异常的情况 |
![]() |
2
amiwrong123 OP |
![]() |
3
NealLason 2 天前
@amiwrong123 只要是进 trap handler 的时候就重新设置 sp ,那就肯定不能支持中断嵌套哎,不然之前中断栈里的上下文就被破坏了
|