第 10 章 Trap
控制流(Control Flow)和 Trap
RISC-V Trap 处理中涉及的寄存器 (Machine模式下)
mtvec 保存异常发生时处理器需要跳转的地址
- BASE trap入口函数的基地址 长度为寄存器长度-2(读地址时,最后两位就用00填充,达到4字节对齐的效果)
- MODE 0表示direct方式,trap发生之后,PC直接跳转到BASE指定的地址处;
- MODE 1表示Vectored方式,trap发生后,PC = BASE + 4 * cause (cause表示中断的原因),Vectored相当于一个索引表,每个索引项都是一个j指令,跳转至相应的处理函数。PC先直接定位到相应的索引项,再执行j指令跳转到处理函数。
- Vector方式适用于中断发生频繁,要求处理速度快的情况。
mcause 表示trap的种类
- Interrupt 0表示异常 1表示中断
- Exception Code 表示具体的种类(查表)
mstatus 用来描述状态信息
- xIE控制全局中断使能
- xPIE保存trap之前的xIE的状态
- xPP保存trap发生之前的权限级别 注意只有MPP 和 SPP 因为trap都是低权限(U/S) 向高权限跳转(M/S)没有trap会跳到User模式
RISC-V Trap 处理流程
1、Trap初始化
将trap_vector (即trap处理函数)写进mtvec寄存器中,准备跳转时用
2、Trap的Top Half
这部分由hart自动执行,本项目中讨论的是Machine模式下发生的Trap
因此将mstatus的MIE值赋值给MPIE中,清楚MIE标志位(关中断使能)
设置mepc的值,异常时设置为当前PC值,中断时设置为触发中断指令的下一条指令的PC
同时将PC设置为mtvec(在trap初始化时设置为了trap_vector)
根据trap的种类设置mcause 并根据有需要设置mtval(补充信息)
把trap之前的权限模式保存在mstatus中的MPP中,再把hart权限模式改为M (在任何Level下触发Trap,首先切换到Machine模式)
3、Trap的Bottom Half
经过前两步操作,最终跳转到trap_vector来执行。
- 首先需要保存上下文信息。 然后将mepc 和macuse当成两个函数参数 调用trap_handler
- trap_handler 运行完之后,恢复上下文信息,再调用MRET 返回Trap之前的状态。
- trap_handler 返回的值存在a0寄存器中 是之前mepc的值(异常)或者是mepc + 4(中断),然后将a0的值再次赋值给mepc ,相当于对mepc进行更新了 ,后面mret指令需要使用mepc的值。
reg_t trap_handler(reg_t epc, reg_t cause)
{
reg_t return_pc = epc;
reg_t cause_code = cause & 0xfff;
if (cause & 0x80000000) {
/* Asynchronous trap - interrupt */
switch (cause_code) {
case 3:
uart_puts("software interruption!\n");
break;
case 7:
uart_puts("timer interruption!\n");
break;
case 11:
uart_puts("external interruption!\n");
break;
default:
uart_puts("unknown async exception!\n");
break;
}
} else {
/* Synchronous trap - exception */
printf("Sync exceptions!, code = %d\n", cause_code);
panic("OOPS! What can I do!");
//return_pc += 4;
}
return return_pc;
}
传进来的两个参数分别是mepc 和 mcause, mcause的低12位表示了trap的具体类型,最高位表示中断或异常。这里给出了中断类型中3,7,11的“处理方式”。
如果是中断的话,这个处理函数的return_pc应该要+4 才可以在返回时跳转到触发中断的下一条指令。
运行结果:
在trap_test中触发的trap类型是内存访问异常。*(int *)0x00000000 = 100; 0x00000000不在可访 问内存范围中。可访问内存从0x80000000开始。
void trap_test()
{
/*
* Synchronous exception code = 7
* Store/AMO access fault
*/
*(int *)0x00000000 = 100;
/*
* Synchronous exception code = 5
* Load access fault
*/
//int a = *(int *)0x00000000;
uart_puts("Yeah! I'm return back from trap!\n");
}