Hazard3/test/sim/sw_testcases/mret_mpp_mprv.c

112 lines
3.2 KiB
C

#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;
}