控制流(Control Flow)和 Trap
RISC-V Trap 处理中涉及的寄存器
1.mtvec
(Machine Trap-Vector Base-Address)
- 异常向量表的基址
mtvec
分为两个部分Base
:trap入口函数的基地址 (由于是保证4字节对齐,所以后面两个bit可以用于别的功能)Mode
:分为Direct和VectoredDirect
:所有的异常,pc都是直接指向这个Base地址.Vectored
:BASE只是这个数组的基地址,然后根据cause去决定数组的下标.
2.mepc
(Machine Exception Program Counter)
- 当trap发生时,pc会变成异常处理代码的地址,同时hart会设置
mepc
为当前指令或者下一条指令的地址
,当需要退出trap的时候,执行mret
指令,可以将mepc赋值给pc,从而回到原本的执行流.
3.mcause
(Machine Cause)
- trap的原因.
- trap时,hart会设置该寄存器来通知trap的原因(
scause
不足以存下中断所有的必须信息。例如缺页异常,就会将stval
设置成需要访问但是不在内存中的地址,以便于操作系统将这个地址所在的页面加载进来。)
4.mtval
(Machine Trap Value)
- 指定这个mcause的时候,可能信息还不够,
mtval
传递补充的信息
.
5.mstatus
(Machine Status)
- 描述状态信息
异常发生时,权限级别总是往高了跳,不会往下跳。 实际上,中断的时候,总是跳往
M
模式下.
这里我们要学的是,主要分成三组信息:
-
xIE
(x=M/S/U) <IE:Interupt Enabled:中断使能位>中断使能位1:开中断
中断使能位0:关中断
-
xPIE
(x=M/S/U) <PIE:Previous Interupt Enabled:保存上一个的xIE的值>在陷入trap的时候,会先将当前的中断使能位保存到PIE中,trap执行结束后,将PIE恢复到IE.
-
xPP
(x=M/S) <PP:Previous Privilege:保存trap发生前的权限级别值>
6.mscratch
(当前控制流的context的指针)
RISC-V Trap 处理流程
Trap初始化
:比如设置trap入口基地址Trap的上半部分
:是在硬件内部发生的,是硬件的一套处理逻辑。硬件的控制处理完了,才会跳到软件部分——即Trap的下半部分Trap的下半部分
:是我们写的处理trap的一套软件逻辑。从Trap返回
.
1.Trap初始化
设置mtvec寄存器
:
extern void trap_vector(void);
void trap_init()
{
/*
* set the trap-vector base-address for machine-mode
*/
w_mtvec((reg_t)trap_vector);
}
trap_vector的代码
:
trap_vector:
# save context(registers).
csrrw t6, mscratch, t6 # swap t6 and mscratch
reg_save t6
csrw mscratch, t6
# call the C trap handler in trap.c
csrr a0, mepc
csrr a1, mcause
call trap_handler
# trap_handler的返回值是正常控制流的PC值
# trap_handler will return the return address via a0.
csrw mepc, a0
# restore context(registers).
csrr t6, mscratch
reg_restore t6
# return to whatever we were doing before trap.
mret
2.Top Half
Trap时,Hart会自动执行
如下:
-
MPIE = MIE ,MIE = 0 (即禁止中断)[
这个非常重要!!!!
] -
设置mepc为正常执行流的指令地址,同时PC被设置为mtvec
注意
:- 对于Exception,mepc指向异常的指令.
- 对于Interrupt,mepc指向下一条指令.
-
设置
mcause
和mtval
-
将trap发生之前的权限模式保存在
mstatus
的MPP中,再把hart权限模式更改为M.
3.Bottom Half
4.退出trap
Mret
指令会做这些事情:
- 恢复Hart的权限级别为MPP
- 恢复Hart的MIE为MPIE,MPIE=1
- pc=mepc