Add tests for execution of mret and wfi in U mode
This commit is contained in:
parent
51750eb81d
commit
64d9f4a111
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue