我們知道有些寄存器只能在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_ECALL(which, 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_0(which) RISCV_ECALL(which, 0, 0, 0) #endif
這個宏的封裝方式也是參考了Linux下的“sbi.h”,which表示調用號,按照OpenSBI的規范,調用號是存放在a7寄存器中,其他的參數從a0寄存器開始存放。當然在測試中我們不會去檢測a7寄存器的,在實際的OpenSBI代碼中,會通過a7寄存器判斷是何種ecall調用然后進行不同的處理。在“main”中調用ecall宏發起一個ecall調用,如下所示。
static void main(void) { printf("%s %d.\r\n", __func__, __LINE__); supervisor_trap_init(); RISCV_ECALL_0(0); while(1); }
在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_trap(void) { 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_set(timer_get() + TIMER_CLK_RATE); // raise a supervisor software interrupt. //sip_set(SIP_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_set(mepc + 4); } return; }