diff --git a/test/sim/sw_testcases/mret_mpp_mprv.c b/test/sim/sw_testcases/mret_mpp_mprv.c new file mode 100644 index 0000000..d65404c --- /dev/null +++ b/test/sim/sw_testcases/mret_mpp_mprv.c @@ -0,0 +1,111 @@ +#include "tb_cxxrtl_io.h" +#include "hazard3_csr.h" +#include "pmp.h" + +// Check MPRV is cleared when returning to U mode, but not when returning to M +// mode. Check that mret clears MPP, no matter which mode is returned to. + +/*EXPECTED-OUTPUT*************************************************************** + +Enabling MPRV, with MPP=M +mret to M, check MPRV +mstatus = 00020080 // mprv=1 mpp=U mpie=1 +mret to M, check MPP affects load/store +mcause = 7 // store fault +mstatus = 00021880 // mprv=1 mpp=M mpie=1 +mem[mepc] = c108 +mret to U, check MPRV +mcause = 1 // instr access fault +mstatus = 00000080 // mprv=0 mpp=U mpie=1 +mem[mepc] = 0001 // c.nop + +*******************************************************************************/ + +volatile uint32_t scratch_word; + +#define MPRV (1u << 17) +#define MPP (3u << 11) + +extern void handle_exception(); + +int main() { + // Leave PMP in default state (no U permissions). + scratch_word = 0; + + tb_puts("Enabling MPRV, with MPP=M\n"); + set_csr(mstatus, MPP | MPRV); + + // Check that mret to M does not clear MPRV (also, opportunistically, + // check that mret to M clears MPP) + tb_puts("mret to M, check MPRV\n"); + write_csr(mtvec, (uintptr_t)&handle_exception); + uint32_t mstatus_check; + asm volatile ( + "la %0, 1f\n" + "csrw mepc, %0\n" + "mret\n" + ".p2align 2\n" + "1:\n" + // Note MPP is cleared by the MRET, so must be re-set immediately so + // we can do load/stores again. We read out the current mstatus value + // at the same time we modify it. + "li %0, 0x1800\n" + "csrrs %0, mstatus, %0\n" + : "+r" (mstatus_check) + ); + // MPP should be U. MPRV should still be set. + tb_printf("mstatus = %08x\n", mstatus_check); + // The actual value of MPP is now M again, since we fixed it up. + + // Check that clearing of MPP upon mret immediately affects the + // MPRV-modified load/store privilege level. + tb_puts("mret to M, check MPP affects load/store\n"); + write_csr(mcause, 0); + asm ( + "la a0, 1f\n" + "csrw mepc, a0\n" + "la a0, 2f\n" + "csrw mtvec, a0\n" + "la a0, scratch_word\n" + "mret\n" + ".p2align 2\n" + "1:\n" + // We just executed a return from M mode to M mode, but our MPP was + // cleared in the process, and our MPRV should still be set. + // Therefore our effective load/store privilege is U: + "c.sw a0, (a0)\n" + // Catch the trap we just caused, and restore MPP to M. + ".p2align 2\n" + "2:\n" + "li a0, 0x1800\n" + "csrs mstatus, a0\n" + : : : "a0" + ); + tb_printf("mcause = %u\n", read_csr(mcause)); + tb_printf("mstatus = %08x\n", read_csr(mstatus)); + tb_printf("mem[mepc] = %04x\n", *(uint16_t*)read_csr(mepc)); + + // Check that mret to U (followed by trapping back to M, as we can't + // actually check MPRV from U) *does* clear MPRV + tb_puts("mret to U, check MPRV\n"); + asm ( + "la a0, 1f\n" + "csrw mepc, a0\n" + "csrw mtvec, a0\n" + "li a0, 0x1800\n" + "csrc mstatus, a0\n" + "mret\n" + ".p2align 2\n" + // We execute this address twice, first in U-mode then in M-mode: + "1:\n" + "c.nop\n" + : : : "a0" + ); + tb_printf("mcause = %u\n", read_csr(mcause)); + tb_printf("mstatus = %08x\n", read_csr(mstatus)); + tb_printf("mem[mepc] = %04x\n", *(uint16_t*)read_csr(mepc)); + tb_assert(read_csr(mepc) == read_csr(mtvec), "Should trap to same address that trapped\n"); + + + return 0; +} diff --git a/test/sim/sw_testcases/pmp_mprv.c b/test/sim/sw_testcases/pmp_mprv.c index b8053bb..45bed55 100644 --- a/test/sim/sw_testcases/pmp_mprv.c +++ b/test/sim/sw_testcases/pmp_mprv.c @@ -3,24 +3,21 @@ #include "pmp.h" // Check that PMP enforces U-mode read/write permissions in M-mode, if both -// MPRV=1 and MPP=U. Check that this does not affect execute permissions. -// Check MPRV is cleared when returning to U mode. - -#define MCAUSE_LOAD_FAULT 5 -#define MCAUSE_STORE_FAULT 7 -#define MCAUSE_ECALL_UMODE 8 +// MPRV=1 and MPP=U. +// +// Check that this does not affect execute permissions. /*EXPECTED-OUTPUT*************************************************************** Enabling MPRV, with MPP=M Set MPP=U, then read memory -mcause = 5 // load fault -mstatus = 00021800 // MPRV = 1 MPP = M -mem[mepc] = 4108 // c.lw a0, (a0) +mcause = 5 // load fault +mstatus = 00021800 // mprv=1 mpp=M +mem[mepc] = 4108 // c.lw Set MPP=U, then write memory -mcause = 7 // store fault -mstatus = 00021800 // MPRV = 1 MPP = M -mem[mepc] = c108 // c.sw a0, (a0) +mcause = 7 // store fault +mstatus = 00021800 // mprv=1 mpp=M +mem[mepc] = c108 // c.sw *******************************************************************************/ @@ -30,11 +27,7 @@ volatile uint32_t scratch_word; #define MPP (3u << 11) int main() { - // Give U-mode RW permissions on testbench IO *only* (so any address >= - // 0x800 megabytes). This means we can always print and exit the test. - write_pmpcfg(0, PMPCFG_A_NAPOT << PMPCFG_A_LSB | PMPCFG_R_BITS | PMPCFG_W_BITS); - write_pmpaddr(0, (0x80000000u | 0x3fffffffu) >> 2); - + // Leave PMP in default state (no U permissions). scratch_word = 0; tb_puts("Enabling MPRV, with MPP=M\n"); @@ -52,7 +45,7 @@ int main() { "la a0, 1f\n" "csrw mtvec, a0\n" "la a0, scratch_word\n" - "la a1, 0x1800\n" + "li a1, 0x1800\n" // Note the nop is to check that we have not lost X permissions. The // trap should come from the lw. @@ -76,7 +69,7 @@ int main() { "la a0, 1f\n" "csrw mtvec, a0\n" "la a0, scratch_word\n" - "la a1, 0x1800\n" + "li a1, 0x1800\n" "csrc mstatus, a1\n" "nop\n"