diff --git a/test/sim/common/hazard3_csr.h b/test/sim/common/hazard3_csr.h index 3ab5daa..3005c86 100644 --- a/test/sim/common/hazard3_csr.h +++ b/test/sim/common/hazard3_csr.h @@ -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 diff --git a/test/sim/sw_testcases/umode_mret.c b/test/sim/sw_testcases/umode_mret.c new file mode 100644 index 0000000..4b885d1 --- /dev/null +++ b/test/sim/sw_testcases/umode_mret.c @@ -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); +} diff --git a/test/sim/sw_testcases/umode_wfi.c b/test/sim/sw_testcases/umode_wfi.c new file mode 100644 index 0000000..683696b --- /dev/null +++ b/test/sim/sw_testcases/umode_wfi.c @@ -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; +}