Add test to check MPRV/MPP behaviour when executing an MRET
This commit is contained in:
parent
f96a0ffb75
commit
2cfe6aa90e
|
@ -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;
|
||||||
|
}
|
|
@ -3,24 +3,21 @@
|
||||||
#include "pmp.h"
|
#include "pmp.h"
|
||||||
|
|
||||||
// Check that PMP enforces U-mode read/write permissions in M-mode, if both
|
// 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.
|
// MPRV=1 and MPP=U.
|
||||||
// Check MPRV is cleared when returning to U mode.
|
//
|
||||||
|
// Check that this does not affect execute permissions.
|
||||||
#define MCAUSE_LOAD_FAULT 5
|
|
||||||
#define MCAUSE_STORE_FAULT 7
|
|
||||||
#define MCAUSE_ECALL_UMODE 8
|
|
||||||
|
|
||||||
/*EXPECTED-OUTPUT***************************************************************
|
/*EXPECTED-OUTPUT***************************************************************
|
||||||
|
|
||||||
Enabling MPRV, with MPP=M
|
Enabling MPRV, with MPP=M
|
||||||
Set MPP=U, then read memory
|
Set MPP=U, then read memory
|
||||||
mcause = 5 // load fault
|
mcause = 5 // load fault
|
||||||
mstatus = 00021800 // MPRV = 1 MPP = M
|
mstatus = 00021800 // mprv=1 mpp=M
|
||||||
mem[mepc] = 4108 // c.lw a0, (a0)
|
mem[mepc] = 4108 // c.lw
|
||||||
Set MPP=U, then write memory
|
Set MPP=U, then write memory
|
||||||
mcause = 7 // store fault
|
mcause = 7 // store fault
|
||||||
mstatus = 00021800 // MPRV = 1 MPP = M
|
mstatus = 00021800 // mprv=1 mpp=M
|
||||||
mem[mepc] = c108 // c.sw a0, (a0)
|
mem[mepc] = c108 // c.sw
|
||||||
|
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
|
||||||
|
@ -30,11 +27,7 @@ volatile uint32_t scratch_word;
|
||||||
#define MPP (3u << 11)
|
#define MPP (3u << 11)
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
// Give U-mode RW permissions on testbench IO *only* (so any address >=
|
// Leave PMP in default state (no U permissions).
|
||||||
// 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);
|
|
||||||
|
|
||||||
scratch_word = 0;
|
scratch_word = 0;
|
||||||
|
|
||||||
tb_puts("Enabling MPRV, with MPP=M\n");
|
tb_puts("Enabling MPRV, with MPP=M\n");
|
||||||
|
@ -52,7 +45,7 @@ int main() {
|
||||||
"la a0, 1f\n"
|
"la a0, 1f\n"
|
||||||
"csrw mtvec, a0\n"
|
"csrw mtvec, a0\n"
|
||||||
"la a0, scratch_word\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
|
// Note the nop is to check that we have not lost X permissions. The
|
||||||
// trap should come from the lw.
|
// trap should come from the lw.
|
||||||
|
@ -76,7 +69,7 @@ int main() {
|
||||||
"la a0, 1f\n"
|
"la a0, 1f\n"
|
||||||
"csrw mtvec, a0\n"
|
"csrw mtvec, a0\n"
|
||||||
"la a0, scratch_word\n"
|
"la a0, scratch_word\n"
|
||||||
"la a1, 0x1800\n"
|
"li a1, 0x1800\n"
|
||||||
|
|
||||||
"csrc mstatus, a1\n"
|
"csrc mstatus, a1\n"
|
||||||
"nop\n"
|
"nop\n"
|
||||||
|
|
Loading…
Reference in New Issue