RISCV-LEARN

Trap

1 思路

前面实现了协作式多任务,但是现在的操作系统往往采用的是抢占式多任务,这意味着操作系统会剥夺某个任务对hart的使用权,这涉及到interrupt。在RISC-V中外部的interrupt和内部的exception总称为trap,两者在处理逻辑上区别不大,下面是大致过程。

在实验四的基础上,通过在任务里面查看mtvec寄存器的值,会发现为0,若添加*(int *)0x00000000 = 1;指令则会发生exception,系统会执行玩初始化和top half部分,但是当要跳转到由mtvec指向的地址时会出错,因为这里没有给mtvec赋值,即初始化部分为空,只执行了top half,故而在bottom half阶段会出错。

所以接下来就是实现初始化和bottom half,即定义trap_vector函数,并赋值给mtvec。

2 实现

2-1 初始化

void trap_init()
{
    asm volatile("csrw mtvec, %[trap_vector]" : : [trap_vector] "r"(trap_vector));
}

其实就是把tarp_vector的地址写给mtvec寄存器。

2-2 trap_vector

.balign 4
trap_vector:
    csrrw t6, mscratch, t6
    write_ctx_from_reg t6
    csrr t5, mscratch
    STORE t5, 30*REG_SIZE(t6)

    csrw mscratch, t6

    csrr a0, mepc
    csrr a1, mcause
    call trap_handler

    csrw mepc, a0

    csrr t6, mscratch
    write_reg_from_ctx t6

    mret

因为mtvec寄存器中的BASE字段是[31:2],故trap_vector函数的地址必须是4B对齐;mtvec寄存器的后两位记录了该trap_vector函数是Direct还是Vectored的mode,前者表示所有trap的处理都会将pc设置为BASE,后者表示发生interrupt时pc被设置为BASE+mcause*4。

.h文件既支持C语言中的#define宏定义,还支持汇编语言中的.macro.end宏定义,故而这里把上下文的保存和恢复放在了.h文件中,但是这个头文件只能被汇编文件include,不能被C语言文件include,否则编译器会报错。另外,s和S文件都支持.include,只有S支持#include。

trap_handler有两个参数,第一个是mepc,第二个是mcause;保存上下文即保存发生trap的任务的上下文,恢复上下文也是指恢复发生trap的任务的上下文,即同一个任务,故mscratch在trap_handler前后指向的是同一个内存地址中的上下文。

2-3 trap_handler

reg_t trap_handler(reg_t mepc, reg_t mcause)
{
    reg_t return_pc = mepc;
    reg_t cause_code = mcause & MCAUSE_MASK_ECODE;
    if (mcause & MCAUSE_MASK_INTERRUPT)
    {
        printf("Interruption Happened, Exception Code : %ld\n", cause_code);
        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("Unkonwn Interruption\n");
            break;
        }
    }
    else
    {
        printf("Exception Happened, Exception Code: %ld\n", cause_code);
        panic("What can I do");
    }
    return return_pc;
}

一些说明: