我们知道有些寄存器只能在m模式下设置和访问,如果s模式想要使用某个功能,只能先回到m模式然后再进行相应的设置。OpenSBI定义了s模式和m模式之间功能调用的接口,s模式通过执行“ecall”指令回到m模式使用相关功能,在本章节和下一章节我们将通过类似的方式来学习s模式下如何使用ecall和m模式下如何处理来自s模式的ecall异常。
首先我们将ecall指令封装成宏来使用,如下所示。
#ifndef _ASM_RISCV_ECALL_H #define _ASM_RISCV_ECALL_H #define RISCV_ECALLwhich, arg0, arg1, arg2) { \ register unsigned long a0 asm "a0") = unsigned long)arg0); \ register unsigned long a1 asm "a1") = unsigned long)arg1); \ register unsigned long a2 asm "a2") = unsigned long)arg2); \ register unsigned long a7 asm "a7") = unsigned long)which); \ asm volatile "ecall" \ : "+r" a0) \ : "r" a1), "r" a2), "r" a7) \ : "memory"); \ a0; \ }) #define RISCV_ECALL_0which) RISCV_ECALLwhich, 0, 0, 0) #endif
这个宏的封装方式也是参考了Linux下的“sbi.h”,which表示调用号,按照OpenSBI的规范,调用号是存放在a7寄存器中,其他的参数从a0寄存器开始存放。当然在测试中我们不会去检测a7寄存器的,在实际的OpenSBI代码中,会通过a7寄存器判断是何种ecall调用然后进行不同的处理。在“main”中调用ecall宏发起一个ecall调用,如下所示。
static void mainvoid) { printf"%s %d.\r\n", __func__, __LINE__); supervisor_trap_init); RISCV_ECALL_00); while1); }
在m模式的异常处理中,我们先对s模式的ecall异常不做任何处理,如下所示。
static char *interrupt_cause[] = { "Reserved", "Supervisor software interrupt", "Reserved", "Machine software interrupt", "Reserved", "Supervisor timer interrupt", "Reserved", "Machine timer interrupt", "Reserved", "Supervisor external interrupt", "Reserved", "Machine external interrupt", "Reserved", "Reserved", "Reserved", "Reserved" }; static char *exception_cause[] = { "Instruction address misaligned", "Instruction access fault", "Illegal instruction", "Breakpoint", "Load address misaligned", "Load access fault", "Store/AMO address misaligned", "Store/AMO access fault", "Environment call from U-mode", "Environment call from S-mode", "Reserved", "Environment call from M-mode", "Instruction page fault", "Load page fault", "Reserved", "Store/AMO page fault" };
void machine_trapvoid) { unsigned long cause = mcause_get); unsigned long mepc = mepc_get); unsigned long tval = mtval_get); int is_int = cause & 1l << 63l)) ? 1 : 0; int mcode = cause & 0xff; if mcode >= 16) { printf"%s : %s.\r\n", is_int ? "Interrupt" : "Exception", "Unknown code"); return; } if is_int) { printf"Interrupt : %s.\r\n", interrupt_cause[mcode]); switch mcode) { case M_SOFT_INT: msoftint_clear); break; case M_TIMER_INT: timer_settimer_get) + TIMER_CLK_RATE); // raise a supervisor software interrupt. //sip_setSIP_SSIP); break; } } else { printf"Exception : %s.\r\n", exception_cause[mcode]); switch mcode) { case ILLEGAL_INSTRUCTION: printf"tval = %p\r\n", tval); printf"mepc = %p\r\n", mepc); break; case ECALL_FROM_SMODE: break; } mepc_setmepc + 4); } return; }