Add test for MPRV vs PMP

This commit is contained in:
Luke Wren 2022-05-29 19:06:04 +01:00
parent 71eff7649d
commit f96a0ffb75
1 changed files with 94 additions and 0 deletions

View File

@ -0,0 +1,94 @@
#include "tb_cxxrtl_io.h"
#include "hazard3_csr.h"
#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
/*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)
Set MPP=U, then write memory
mcause = 7 // store fault
mstatus = 00021800 // MPRV = 1 MPP = M
mem[mepc] = c108 // c.sw a0, (a0)
*******************************************************************************/
volatile uint32_t scratch_word;
#define MPRV (1u << 17)
#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);
scratch_word = 0;
tb_puts("Enabling MPRV, with MPP=M\n");
set_csr(mstatus, MPP | MPRV);
// Effective privilege is still M mode, so we can do whatever we want. Go
// nuts. Read and write some memory.
scratch_word = 0x12345678u;
tb_assert(scratch_word == 0x12345678u, "Bad readback somehow\n");
write_csr(mcause, 0);
write_csr(mepc, 0);
tb_puts("Set MPP=U, then read memory\n");
asm (
"la a0, 1f\n"
"csrw mtvec, a0\n"
"la a0, scratch_word\n"
"la a1, 0x1800\n"
// Note the nop is to check that we have not lost X permissions. The
// trap should come from the lw.
"csrc mstatus, a1\n"
"nop\n"
"c.lw a0, (a0)\n"
".p2align 2\n"
"1:"
: : : "a0", "a1"
);
// We should have instantly trapped, and set MPP back to M upon trapping,
// restoring read/write permissions.
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));
// Same trick but writes. We should get a different mcause value.
tb_puts("Set MPP=U, then write memory\n");
asm (
"la a0, 1f\n"
"csrw mtvec, a0\n"
"la a0, scratch_word\n"
"la a1, 0x1800\n"
"csrc mstatus, a1\n"
"nop\n"
"c.sw a0, (a0)\n"
".p2align 2\n"
"1:"
: : : "a0", "a1"
);
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));
return 0;
}