首页 > 解决方案 > RISC-V 陷阱处理程序重入陷阱处理程序中的异常

问题描述

在系统初始化期间将 mscratch 设置为陷阱处理程序内存上下文然后执行似乎是一个常见的技巧:

csrrw tp, CSR_MSCRATCH, tp

在机器模式陷阱处理程序的开头交换 tp 和 mscratch。然后在机器模式陷阱处理程序中使用 tp 来访问陷阱处理程序本身所需的内存/状态。在陷阱处理程序结束时,再次执行以恢复指向 mscratch 的内存上下文指​​针和 tp 的原始值。

不过好像有问题。如果由于某种原因在陷阱处理程序本身中发生异常,您将交换 tp 的原始值,该值现在与机器模式陷阱处理程序上下文处于 mscratch 中,因此在处理陷阱时您将使用错误的内存上下文处理程序生成异常。

如果您想检测这种情况,您将需要一个免费寄存器来读取 MPP。

如何处理?

标签: assemblyriscvinterrupt-handling

解决方案


我有一个支持嵌套异常的玩具处理程序。我总是1在那个 csr 中保留一个指向当前运行代码的上下文块的指针scratch——这包括在异常处理程序本身的执行期间。

我的玩具处理程序开始这样的事情:

csrrw t0, scratch, t0   # swap interrupted t0 and its context block pointer
beqz t0, bootUp         # or if it is null then put the base CBP in there...
sw t1, 12(t0)           # save interrupted t1 to its context block
lw t1, 4(t0)            # fetch context block pointer for me
csrrw t1, scratch, t1   # t1 has interrupted t0, CSR scratch points to my context block
sw t1, 8(t0)            # save interrupted t0 to its context block
sw t2, 16(t0)           # save t2

在这段代码之后,t0是指向被中断代码的上下文块的指针,被中断的t0, t1&t2被保存到被中断代码的上下文块中,因此t1可以t2自由使用,而scratchcsr 是指向本次运行的上下文块的指针异常处理程序。

用户代码的上下文块可以存储在任何地方,例如malloc根据需要编辑。在每个用户上下文块中(例如4(t0)上面的 at),我存储了一个指向特殊异常处理程序上下文块数组的指针。

在任何异常处理程序上下文块中(在特殊数组中),每个(在偏移量 4 处)都被预初始化以引用数组中的下一个条目,因此嵌套异常自然会在特殊数组中采用不同的上下文块,就像一个动态嵌套异常处理程序的堆栈;因此,对于该数组中的尽可能多的元素,它可以为许多嵌套异常提供服务。

如果需要,用于异常处理程序的上下文块可以包含其他数据,例如指向全局数据的指针。


处理快速中断可能会保存一些寄存器、运行一些代码、恢复这些寄存器并恢复中断的代码。对于其他一些中断,调度程序可能会选择切换上下文(例如 I/O 就绪),恢复与上次中断不同的线程。由于正确的上下文块已经具有中断线程的部分状态,因此对于完整的上下文切换,只需保存额外的寄存器状态(无需将任何寄存器状态从临时位置转移到正确的上下文块)。


1  除了处理程序开头的 ~4-5 指令窗口。


作为恢复的一部分,对于正在恢复的任何线程,将其 t0 及其上下文块指针恢复到scratchcsr 中。

如果遇到嵌套异常,要恢复的线程只是中断的异常处理程序,而如果用户线程被中断,则由调度程序决定是否恢复最近中断的用户线程(通常不需要完整的上下文切换)或恢复另一个先前挂起的用户线程,该线程可能已被时间片超时或 I/O 系统调用挂起。如果外部中断指示完成的 I/O 操作,则等待该 I/O 的线程可能适合恢复。(系统调用,我正在做 as csrrw zero, %arg, zero,以便在捕获指令中编码系统调用号,并避免使用ecall自 RARS 服务以来)。

在我的玩具系统中,我支持用户级别的多线程,因此一个线程可以通过系统调用请求“操作系统”创建另一个线程。因此,用户线程的上下文块可以“malloc”,而异常处理程序上下文块堆叠在该预初始化数组中。

我的玩具系统在 RARS 下运行,所以是的,它没有多个地址空间的复杂性。


推荐阅读