Add tests for execution of mret and wfi in U mode

This commit is contained in:
Luke Wren 2022-05-24 22:14:20 +01:00
parent 51750eb81d
commit 64d9f4a111
3 changed files with 135 additions and 0 deletions

View File

@ -20,8 +20,18 @@
asm volatile ("csrw " #csrname ", %0" : : "r" (data)); \
})
#define _set_csr(csrname, data) ({ \
asm volatile ("csrs " #csrname ", %0" : : "r" (data)); \
})
#define _clear_csr(csrname, data) ({ \
asm volatile ("csrc " #csrname ", %0" : : "r" (data)); \
})
// Argument macro expansion layer
#define read_csr(csrname) _read_csr(csrname)
#define write_csr(csrname, data) _write_csr(csrname, data)
#define set_csr(csrname, data) _set_csr(csrname, data)
#define clear_csr(csrname, data) _clear_csr(csrname, data)
#endif

View File

@ -0,0 +1,48 @@
#include "tb_cxxrtl_io.h"
#include "hazard3_csr.h"
// Check that U-mode execution of an mret causes an illegal opcode exception.
/*EXPECTED-OUTPUT***************************************************************
-> exception, mcause = 2, mpp = 0 // should indicate U-mode illegal opcode
Excepting instr: 30200073 // mret
*******************************************************************************/
void __attribute__((naked)) do_mret() {
asm ("mret");
}
int main() {
// Give U mode RWX permission on all of memory.
write_csr(pmpcfg0, 0x1fu);
write_csr(pmpaddr0, -1u);
// Jump to do_mret() in U mode
write_csr(mstatus, read_csr(mstatus) & ~0x1800u);
write_csr(mepc, &do_mret);
asm ("mret");
return 0;
}
void __attribute__((interrupt)) handle_exception() {
uint32_t mcause = read_csr(mcause);
tb_printf("-> exception, mcause = %u, mpp = %u\n", mcause, read_csr(mstatus) >> 11 & 0x3u);
write_csr(mcause, 0);
if (mcause == 3) {
// ebreak -> end of test
tb_exit(0);
}
uint32_t mepc = read_csr(mepc);
if (mepc != (uint32_t)&do_mret) {
tb_printf("Bad mepc: %08x\n", mepc);
tb_exit(-1);
}
tb_printf("Excepting instr: %04x%04x\n", *(uint16_t*)(mepc + 2), *(uint16_t*)mepc);
tb_exit(0);
}

View File

@ -0,0 +1,77 @@
#include "tb_cxxrtl_io.h"
#include "hazard3_csr.h"
// Check that U-mode execution of a wfi causes an illegal opcode exception if
// and only if the mstatus timeout wait bit is set.
/*EXPECTED-OUTPUT***************************************************************
Do WFI with TW=0:
mcause = 11 // = ecall, meaning normal exit. The test also checks mepc.
Do WFI with TW=1:
mstatus = 00200000 // Check TW write is reflected in readback
mcause = 2 // = illegal instruction. The test also checks mepc.
*******************************************************************************/
// Naked so the address can be checked
void __attribute__((naked)) do_wfi() {
asm (
"wfi\n"
"ret\n"
);;
}
void __attribute__((naked)) do_ecall() {
asm ("ecall");
}
// /!\ Unconventional control flow ahead
// Call function in U mode, from M mode. Catch exception, or break back to M
// mode if the function returned normally, and then return.
static inline void umode_call_and_catch(void (*f)(void)) {
clear_csr(mstatus, 0x1800u);
write_csr(mepc, f);
uint32_t mtvec_save = read_csr(mtvec);
asm (
" la ra, 1f\n"
" csrw mtvec, ra\n"
" la ra, do_ecall\n"
" mret\n"
// Note mtvec requires 4-byte alignment.
".p2align 2\n"
"1:\n"
: : : "ra"
);
write_csr(mtvec, mtvec_save);
}
int main() {
// Ensure WFI doesn't block by enabling timer IRQ but leaving IRQs globally disabled.
set_csr(mie, -1u);
// Give U mode RWX permission on all of memory.
write_csr(pmpcfg0, 0x1fu);
write_csr(pmpaddr0, -1u);
tb_puts("Do WFI with TW=0:\n");
umode_call_and_catch(&do_wfi);
tb_printf("mcause = %u\n", read_csr(mcause));
if (read_csr(mepc) != (uint32_t)&do_ecall) {
tb_puts("Non-normal return detected\n");
return -1;
}
tb_puts("Do WFI with TW=1:\n");
set_csr(mstatus, 1u << 21);
tb_printf("mstatus = %08x\n", read_csr(mstatus));
umode_call_and_catch(&do_wfi);
tb_printf("mcause = %u\n", read_csr(mcause));
if (read_csr(mepc) != (uint32_t)&do_wfi) {
tb_puts("mepc doesn't point to wfi\n");
return -1;
}
return 0;
}