Illegal instruction mret - mret指令返回异常


lesson7 

qemu-system-riscv64 --version
QEMU emulator version 6.1.0
Copyright (c) 2003-2021 Fabrice Bellard and the QEMU Project developers

执行mret  抛异常 Illegal instruction

qemu-system-riscv64 -machine virt -bios none -kernel kernelimage -m 128M -smp 1 -nographic
start 58.
Exception : Illegal instruction.
tval = 0x0000000000000000
mepc = 0x00000000800000f2
static void machine_switchto_supervisor(void)
{
    // set M Previous Privilege mode to Supervisor, for mret.
    unsigned long x = mstatus_get();
    x &= ~MSTATUS_MPP_MASK;x |= MSTATUS_MPP_S;
    mstatus_set(x);

    // set M Exception Program Counter to main, for mret.
    // requires gcc -mcmodel=medany
    mepc_set((unsigned long)main);

    // disable paging for now.
    satp_set(0);

    // delegate interrupts and exceptions to supervisor mode.
    medeleg_set(0xb109);
    mideleg_set(0x222);

    printf("%s %d.\r\n", __func__, __LINE__);
    // switch to supervisor mode and jump to main().
    asm volatile("mret");  //抛异常
}

改成

static void machine_switchto_supervisor(void)
{
    // set M Previous Privilege mode to Supervisor, for mret.
    unsigned long x = mstatus_get();
    x &= ~MSTATUS_MPP_MASK;
    x |= MSTATUS_MPP_M;
 
    mstatus_set(x);

    // set M Exception Program Counter to main, for mret.
    // requires gcc -mcmodel=medany
    mepc_set((unsigned long)main);

    // disable paging for now.
    satp_set(0);

    // delegate interrupts and exceptions to supervisor mode.
    medeleg_set(0xb109);
    mideleg_set(0x222);

    printf("%s %d.\r\n", __func__, __LINE__);
    // switch to supervisor mode and jump to main().
    asm volatile("mret");
}

The issue turned out to be RISC-V's Physical Memory Protection (PMP). QEMU will raise an illegal instruction exception when executing an MRET instruction if no PMP rules have been defined. Adding a PMP entry resolved the issue.

在硬件设计里,PMP (Phsical Memory Protection) 是可选项,但在大部分地方我们都可以见到它的身影。PMP 检查一般用于 hart 在监管者模式或用户模式下的所有访问;或者在 mstatus.MPRV = 1 时的 load 和 store 等情况。一旦触发 PMP 保护,RISC-V 要求产生精确中断并处理。

PMP 允许机器模式指定用户模式下可以访问的内存地址。PMP entry 由一个 8-bit 的 PMP 配置寄存器和一个 32/64 位长的 PMP 地址寄存器组成。整个 PMP 包括若干个(通常为 8 到 16 组)PMP entry 。配置寄存器可以配置读、写和执行权限,地址寄存器用来划定界限。

下两图显示了 PMP 地址寄存器和配置寄存器的布局。pmpxxcfg 表示了 PMP 配置寄存器,pmpxxaddr 表示了 PMP 地址寄存器。

当处于用户模式的处理器尝试 load 或 store 操作时,将地址和所有的 PMP 地址寄存器比较。如果地 址大于等于 PMP 地址 i,但小于 PMP 地址 i+1,则 PMP i+1 的配置寄存器决定该访问是否可以继续,如果不能将会引发访问异常。

R,W,X 位分别指示了 PMP 入口允许读、写、执行权限。A 域解释了 PMP 寄存器的编码情况。

void setup_pmp(void)
{
  // Set up a PMP to permit access to all of memory.
  // Ignore the illegal-instruction trap if PMPs aren't supported.
  uintptr_t pmpc = PMP_NAPOT | PMP_R | PMP_W | PMP_X;
  asm volatile ("la t0, 1f\n\t"
                "csrrw t0, mtvec, t0\n\t"
                "csrw pmpaddr0, %1\n\t"
                "csrw pmpcfg0, %0\n\t"
                ".align 2\n\t"
                "1: csrw mtvec, t0"
                : : "r" (pmpc), "r" (-1UL) : "t0");
}

设置setup_pmp之后,异常解除

void setup_pmp(void)
{
  // Set up a PMP to permit access to all of memory.
  // Ignore the illegal-instruction trap if PMPs aren't supported.
  unsigned long pmpc = PMP_NAPOT | PMP_R | PMP_W | PMP_X;
  asm volatile ("la t0, 1f\n\t"
                "csrrw t0, mtvec, t0\n\t"
                "csrw pmpaddr0, %1\n\t"
                "csrw pmpcfg0, %0\n\t"
                ".align 2\n\t"
                "1: csrw mtvec, t0"
                : : "r" (pmpc), "r" (-1UL) : "t0");
}
static void machine_switchto_supervisor(void)
{
    setup_pmp();
    // set M Previous Privilege mode to Supervisor, for mret.
    unsigned long x = mstatus_get();
    x &= ~MSTATUS_MPP_MASK;
    //x |= MSTATUS_MPP_M;
    x |= MSTATUS_MPP_S;
    mstatus_set(x);

    // set M Exception Program Counter to main, for mret.
    // requires gcc -mcmodel=medany
    mepc_set((unsigned long)main);

    // disable paging for now.
    satp_set(0);

    // delegate interrupts and exceptions to supervisor mode.
    medeleg_set(0xb109);
    mideleg_set(0x222);

    printf("%s %d.\r\n", __func__, __LINE__);
    // switch to supervisor mode and jump to main().
    asm volatile("mret");
}
qemu-system-riscv64 -machine virt -bios none -kernel kernelimage -m 128M -smp 1 -nographic
start 74.
machine_switchto_supervisor 67.
main 28.

相关