riscv 开发之S模式ecall调用

我们知道有些寄存器只能在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;
}

 

Published by

风君子

独自遨游何稽首 揭天掀地慰生平