循序渐进,学习开发一个RISC-V 上的操作系统

第 10 章 Trap 

控制流(Control Flow)和 Trap

循序渐进,学习开发一个RISC-V 上的操作系统_第1张图片

RISC-V Trap 处理中涉及的寄存器 (Machine模式下)

循序渐进,学习开发一个RISC-V 上的操作系统_第2张图片

 循序渐进,学习开发一个RISC-V 上的操作系统_第3张图片

 循序渐进,学习开发一个RISC-V 上的操作系统_第4张图片

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方式适用于中断发生频繁,要求处理速度快的情况。

循序渐进,学习开发一个RISC-V 上的操作系统_第5张图片

 循序渐进,学习开发一个RISC-V 上的操作系统_第6张图片mcause 表示trap的种类

  • Interrupt 0表示异常 1表示中断
  • Exception Code 表示具体的种类(查表)

循序渐进,学习开发一个RISC-V 上的操作系统_第7张图片

 mstatus 用来描述状态信息

  • xIE控制全局中断使能
  • xPIE保存trap之前的xIE的状态
  • xPP保存trap发生之前的权限级别 注意只有MPP 和 SPP 因为trap都是低权限(U/S) 向高权限跳转(M/S)没有trap会跳到User模式

循序渐进,学习开发一个RISC-V 上的操作系统_第8张图片

循序渐进,学习开发一个RISC-V 上的操作系统_第9张图片

RISC-V Trap 处理流程

1、Trap初始化

循序渐进,学习开发一个RISC-V 上的操作系统_第10张图片

 将trap_vector (即trap处理函数)写进mtvec寄存器中,准备跳转时用

2、Trap的Top Half

循序渐进,学习开发一个RISC-V 上的操作系统_第11张图片

 这部分由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

循序渐进,学习开发一个RISC-V 上的操作系统_第12张图片

 经过前两步操作,最终跳转到trap_vector来执行。

  • 首先需要保存上下文信息。 然后将mepc 和macuse当成两个函数参数 调用trap_handler
  • trap_handler 运行完之后,恢复上下文信息,再调用MRET 返回Trap之前的状态。
  • trap_handler 返回的值存在a0寄存器中 是之前mepc的值(异常)或者是mepc + 4(中断),然后将a0的值再次赋值给mepc ,相当于对mepc进行更新了 ,后面mret指令需要使用mepc的值。

循序渐进,学习开发一个RISC-V 上的操作系统_第13张图片

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 才可以在返回时跳转到触发中断的下一条指令。

运行结果:

循序渐进,学习开发一个RISC-V 上的操作系统_第14张图片

 在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");
}

你可能感兴趣的:(学习,risc-v)