RISC-V机器模式异常/中断处理:如何返回原特权模式?
RISC-V机器模式陷阱处理中恢复原特权模式的方法
问题描述
若已将所有异常与中断配置为在machine-mode(机器模式)下处理,machine-mode处理程序在执行mret前,如何知晓需将PRV_U或PRV_M等值写入mstatus.MPP,以从触发陷阱的原特权模式恢复执行?换而言之,当陷阱分别来自User/Supervisor/Hypervisor/Machine模式时,进入machine-mode处理程序后CSR的状态有何差异?
示例代码如下:
uintptr_t exception_handler(uint64_t cause) { const uint64_t hartid = read_csr(mhartid); switch (cause) { // Do stuff to "claim", "handle", and "clear" each exception "cause"... // Maybe it's an ecall? Maybe a page-fault? Doesn't matter... } clear_csr(mstatus, MSTATUS_MPP); set_csr(mstatus, (trek_uint64_t)(???? << 11)); // <-- what goes here!?!! return read_csr(mepc) + 4; // There is an `mret` just after this return. }
解答
核心原理:硬件自动保存原特权模式
当陷阱进入机器模式时,硬件会自动将触发陷阱的原特权模式写入mstatus.MPP(Machine Previous Privilege Mode)字段,同时将原特权模式下的中断使能状态保存到mstatus.MPIE。进入machine-mode处理程序后,mstatus.MPP的值直接对应陷阱来源:
- 陷阱来自User模式:
mstatus.MPP为PRV_U(值为0) - 陷阱来自Supervisor模式:
mstatus.MPP为PRV_S(值为1) - 陷阱来自Hypervisor模式:
mstatus.MPP为PRV_H(值为2) - 陷阱来自Machine模式自身:
mstatus.MPP为PRV_M(值为3)
示例代码的修正方式
你不需要手动猜测原特权模式,直接复用硬件已保存的mstatus.MPP值即可。针对示例代码,正确写法如下:
uintptr_t exception_handler(uint64_t cause) { const uint64_t hartid = read_csr(mhartid); // 先读取硬件保存的原特权模式 uint64_t prev_priv = (read_csr(mstatus) >> 11) & 0x3; switch (cause) { // 处理各类异常/中断逻辑 } clear_csr(mstatus, MSTATUS_MPP); set_csr(mstatus, prev_priv << 11); return read_csr(mepc) + 4; }
如果处理过程中没有修改过mstatus.MPP,甚至可以直接省略clear_csr和set_csr步骤,mret会自动使用硬件初始保存的mstatus.MPP值恢复原模式。
不同来源陷阱的CSR状态差异
除mstatus.MPP外,还有几个CSR能区分陷阱来源:
mcause:陷阱原因寄存器,最高位标识是中断(1)还是异常(0),低几位的具体原因码会因原特权模式不同而区分(比如用户态ecall和机器态ecall的原因码不同)。mepc:保存触发陷阱时的程序计数器,对应原特权模式下的指令地址。mstatus.MPIE:保存原特权模式下的中断使能状态(比如原User模式下的uie是否开启)。
内容的提问来源于stack exchange,提问作者Lance E.T. Compte
相关产品推荐
相关产品推荐

