From f033cde8749c32137ac7fd521edd835254f0f7c9 Mon Sep 17 00:00:00 2001 From: Luke Wren Date: Tue, 24 May 2022 17:30:24 +0100 Subject: [PATCH] Add test for readability of CSRs in U mode. Fix readback value of mstatus.mpp --- hdl/hazard3_csr.v | 32 +- test/sim/sw_testcases/csr_readable_umode.c | 348 +++++++++++++++++++++ 2 files changed, 364 insertions(+), 16 deletions(-) create mode 100644 test/sim/sw_testcases/csr_readable_umode.c diff --git a/hdl/hazard3_csr.v b/hdl/hazard3_csr.v index e1c94fb..0a026ee 100644 --- a/hdl/hazard3_csr.v +++ b/hdl/hazard3_csr.v @@ -495,22 +495,22 @@ always @ (*) begin MSTATUS: if (CSR_M_MANDATORY || CSR_M_TRAP) begin decode_match = match_mrw; rdata = { - 1'b0, // Never any dirty state besides GPRs - 8'd0, // (WPRI) - 1'b0, // TSR (Trap SRET), tied 0 if no S mode. - 1'b0, // TW (Timeout Wait), tied 0 if only M mode. - 1'b0, // TVM (trap virtual memory), tied 0 if no S mode. - 1'b0, // MXR (Make eXecutable Readable), tied 0 if not S mode. - 1'b0, // SUM, tied 0, we have no S or U mode - mstatus_mprv, // MPRV (modify privilege) - 4'd0, // XS, FS always "off" (no extension state to clear!) - {2{m_mode}}, // MPP (M-mode previous privilege), only M and U supported - 2'd0, // (WPRI) - 1'b0, // SPP, tied 0 if S mode not supported - mstatus_mpie, // Previous interrupt enable - 3'd0, // No S, U - mstatus_mie, // Interrupt enable - 3'd0 // No S, U + 1'b0, // Never any dirty state besides GPRs + 8'd0, // (WPRI) + 1'b0, // TSR (Trap SRET), tied 0 if no S mode. + 1'b0, // TW (Timeout Wait), tied 0 if only M mode. + 1'b0, // TVM (trap virtual memory), tied 0 if no S mode. + 1'b0, // MXR (Make eXecutable Readable), tied 0 if not S mode. + 1'b0, // SUM, tied 0, we have no S or U mode + mstatus_mprv, // MPRV (modify privilege) + 4'd0, // XS, FS always "off" (no extension state to clear!) + {2{mstatus_mpp}}, // MPP (M-mode previous privilege), only M and U supported + 2'd0, // (WPRI) + 1'b0, // SPP, tied 0 if S mode not supported + mstatus_mpie, // Previous interrupt enable + 3'd0, // No S, U + mstatus_mie, // Interrupt enable + 3'd0 // No S, U }; end diff --git a/test/sim/sw_testcases/csr_readable_umode.c b/test/sim/sw_testcases/csr_readable_umode.c new file mode 100644 index 0000000..b4319f8 --- /dev/null +++ b/test/sim/sw_testcases/csr_readable_umode.c @@ -0,0 +1,348 @@ +#include "tb_cxxrtl_io.h" +#include "hazard3_csr.h" + +// Check all implemented M-mode and D-mode CSRs are unreadable in U mode. +// Check that an M-mode trap taken from U mode is able to access the M-mode +// trap CSRs. + +// These are new (priv-1.12) and may not be recognised by the toolchain: +#define mconfigptr 0xf15 +#define mstatush 0x310 + +// Exceptions here are: medeleg, mideleg, tdata1, dcsr, dpc, dscratch1, +// dscratch0, dmdata0 (custom). medeleg/mideleg are just a couple of +// unimplemented registers sprinkled in for a sanity check, and the remainder +// are D-mode registers. +// +// Note we permit reads but not writes to tselect, to work around a logic +// error in openocd. Planning to implement triggers at some point, so this +// oddity will vanish. + +/*EXPECTED-OUTPUT*************************************************************** +-> exception, mcause = 2, mpp = 0 // mvendorid +CSR was f11 +-> exception, mcause = 2, mpp = 0 // marchid +CSR was f12 +-> exception, mcause = 2, mpp = 0 // mimpid +CSR was f13 +-> exception, mcause = 2, mpp = 0 // mhartid +CSR was f14 +-> exception, mcause = 2, mpp = 0 // mconfigptr +CSR was f15 +-> exception, mcause = 2, mpp = 0 // misa +CSR was 301 +-> exception, mcause = 2, mpp = 0 // mstatus +CSR was 300 +-> exception, mcause = 2, mpp = 0 // mstatush +CSR was 310 +-> exception, mcause = 2, mpp = 0 // mie +CSR was 304 +-> exception, mcause = 2, mpp = 0 // mip +CSR was 344 +-> exception, mcause = 2, mpp = 0 // mtvec +CSR was 305 +-> exception, mcause = 2, mpp = 0 // mscratch +CSR was 340 +-> exception, mcause = 2, mpp = 0 // mepc +CSR was 341 +-> exception, mcause = 2, mpp = 0 // mcause +CSR was 342 +-> exception, mcause = 2, mpp = 0 // mtval +CSR was 343 +-> exception, mcause = 2, mpp = 0 // mcounteren +CSR was 306 +-> exception, mcause = 2, mpp = 0 // mcycle +CSR was b00 +-> exception, mcause = 2, mpp = 0 // mcycleh +CSR was b80 +-> exception, mcause = 2, mpp = 0 // minstret +CSR was b02 +-> exception, mcause = 2, mpp = 0 // minstreth +CSR was b82 +-> exception, mcause = 2, mpp = 0 // mphmcounter3 +CSR was b03 +-> exception, mcause = 2, mpp = 0 // ... +CSR was b04 +-> exception, mcause = 2, mpp = 0 +CSR was b05 +-> exception, mcause = 2, mpp = 0 +CSR was b06 +-> exception, mcause = 2, mpp = 0 +CSR was b07 +-> exception, mcause = 2, mpp = 0 +CSR was b08 +-> exception, mcause = 2, mpp = 0 +CSR was b09 +-> exception, mcause = 2, mpp = 0 +CSR was b0a +-> exception, mcause = 2, mpp = 0 +CSR was b0b +-> exception, mcause = 2, mpp = 0 +CSR was b0c +-> exception, mcause = 2, mpp = 0 +CSR was b0d +-> exception, mcause = 2, mpp = 0 +CSR was b0e +-> exception, mcause = 2, mpp = 0 +CSR was b0f +-> exception, mcause = 2, mpp = 0 +CSR was b10 +-> exception, mcause = 2, mpp = 0 +CSR was b11 +-> exception, mcause = 2, mpp = 0 +CSR was b12 +-> exception, mcause = 2, mpp = 0 +CSR was b13 +-> exception, mcause = 2, mpp = 0 +CSR was b14 +-> exception, mcause = 2, mpp = 0 +CSR was b15 +-> exception, mcause = 2, mpp = 0 +CSR was b16 +-> exception, mcause = 2, mpp = 0 +CSR was b17 +-> exception, mcause = 2, mpp = 0 +CSR was b18 +-> exception, mcause = 2, mpp = 0 +CSR was b19 +-> exception, mcause = 2, mpp = 0 +CSR was b1a +-> exception, mcause = 2, mpp = 0 +CSR was b1b +-> exception, mcause = 2, mpp = 0 +CSR was b1c +-> exception, mcause = 2, mpp = 0 +CSR was b1d +-> exception, mcause = 2, mpp = 0 +CSR was b1e +-> exception, mcause = 2, mpp = 0 // ... mhpmcounter31 +CSR was b1f +-> exception, mcause = 2, mpp = 0 // mhpmcounter3h +CSR was b83 +-> exception, mcause = 2, mpp = 0 // ... +CSR was b84 +-> exception, mcause = 2, mpp = 0 +CSR was b85 +-> exception, mcause = 2, mpp = 0 +CSR was b86 +-> exception, mcause = 2, mpp = 0 +CSR was b87 +-> exception, mcause = 2, mpp = 0 +CSR was b88 +-> exception, mcause = 2, mpp = 0 +CSR was b89 +-> exception, mcause = 2, mpp = 0 +CSR was b8a +-> exception, mcause = 2, mpp = 0 +CSR was b8b +-> exception, mcause = 2, mpp = 0 +CSR was b8c +-> exception, mcause = 2, mpp = 0 +CSR was b8d +-> exception, mcause = 2, mpp = 0 +CSR was b8e +-> exception, mcause = 2, mpp = 0 +CSR was b8f +-> exception, mcause = 2, mpp = 0 +CSR was b90 +-> exception, mcause = 2, mpp = 0 +CSR was b91 +-> exception, mcause = 2, mpp = 0 +CSR was b92 +-> exception, mcause = 2, mpp = 0 +CSR was b93 +-> exception, mcause = 2, mpp = 0 +CSR was b94 +-> exception, mcause = 2, mpp = 0 +CSR was b95 +-> exception, mcause = 2, mpp = 0 +CSR was b96 +-> exception, mcause = 2, mpp = 0 +CSR was b97 +-> exception, mcause = 2, mpp = 0 +CSR was b98 +-> exception, mcause = 2, mpp = 0 +CSR was b99 +-> exception, mcause = 2, mpp = 0 +CSR was b9a +-> exception, mcause = 2, mpp = 0 +CSR was b9b +-> exception, mcause = 2, mpp = 0 +CSR was b9c +-> exception, mcause = 2, mpp = 0 +CSR was b9d +-> exception, mcause = 2, mpp = 0 +CSR was b9e +-> exception, mcause = 2, mpp = 0 // ... mhpmcounter31h +CSR was b9f // followed by U-mode counters, which shouldn't trap... +-> exception, mcause = 2, mpp = 0 // mcountinhibit +CSR was 320 +-> exception, mcause = 2, mpp = 0 // mhpmevent3 +CSR was 323 +-> exception, mcause = 2, mpp = 0 // tselect +CSR was 7a0 +-> exception, mcause = 2, mpp = 0 // tdata1 +CSR was 7a1 +-> exception, mcause = 2, mpp = 0 // dcsr +CSR was 7b0 +-> exception, mcause = 2, mpp = 0 // dpc +CSR was 7b1 +-> exception, mcause = 2, mpp = 0 // dscratch0 +CSR was 7b2 +-> exception, mcause = 2, mpp = 0 // dscratch1 +CSR was 7b3 +-> exception, mcause = 2, mpp = 0 // hazard3 dmdata0 +CSR was bff +-> exception, mcause = 2, mpp = 0 // hazard3 meie0 +CSR was be0 +-> exception, mcause = 2, mpp = 0 // hazard3 meip0 +CSR was fe0 +-> exception, mcause = 2, mpp = 0 // hazard3 mlei +CSR was fe4 +-> exception, mcause = 3, mpp = 0 // This is the ebreak that ends the test + +*******************************************************************************/ + +// This function is run in U mode. It returns to a trampoline that ebreaks to M mode. +void read_all_csrs() { + (void)read_csr(mvendorid); + (void)read_csr(marchid); + (void)read_csr(mimpid); + (void)read_csr(mhartid); + (void)read_csr(mconfigptr); + (void)read_csr(misa); + + (void)read_csr(mstatus); + (void)read_csr(mstatush); + (void)read_csr(mie); + (void)read_csr(mip); + (void)read_csr(mtvec); + (void)read_csr(mscratch); + (void)read_csr(mepc); + (void)read_csr(mcause); + (void)read_csr(mtval); + (void)read_csr(mcounteren); + + (void)read_csr(mcycle); + (void)read_csr(mcycleh); + (void)read_csr(minstret); + (void)read_csr(minstreth); + + (void)read_csr(mhpmcounter3); + (void)read_csr(mhpmcounter4); + (void)read_csr(mhpmcounter5); + (void)read_csr(mhpmcounter6); + (void)read_csr(mhpmcounter7); + (void)read_csr(mhpmcounter8); + (void)read_csr(mhpmcounter9); + (void)read_csr(mhpmcounter10); + (void)read_csr(mhpmcounter11); + (void)read_csr(mhpmcounter12); + (void)read_csr(mhpmcounter13); + (void)read_csr(mhpmcounter14); + (void)read_csr(mhpmcounter15); + (void)read_csr(mhpmcounter16); + (void)read_csr(mhpmcounter17); + (void)read_csr(mhpmcounter18); + (void)read_csr(mhpmcounter19); + (void)read_csr(mhpmcounter20); + (void)read_csr(mhpmcounter21); + (void)read_csr(mhpmcounter22); + (void)read_csr(mhpmcounter23); + (void)read_csr(mhpmcounter24); + (void)read_csr(mhpmcounter25); + (void)read_csr(mhpmcounter26); + (void)read_csr(mhpmcounter27); + (void)read_csr(mhpmcounter28); + (void)read_csr(mhpmcounter29); + (void)read_csr(mhpmcounter30); + (void)read_csr(mhpmcounter31); + + (void)read_csr(mhpmcounter3h); + (void)read_csr(mhpmcounter4h); + (void)read_csr(mhpmcounter5h); + (void)read_csr(mhpmcounter6h); + (void)read_csr(mhpmcounter7h); + (void)read_csr(mhpmcounter8h); + (void)read_csr(mhpmcounter9h); + (void)read_csr(mhpmcounter10h); + (void)read_csr(mhpmcounter11h); + (void)read_csr(mhpmcounter12h); + (void)read_csr(mhpmcounter13h); + (void)read_csr(mhpmcounter14h); + (void)read_csr(mhpmcounter15h); + (void)read_csr(mhpmcounter16h); + (void)read_csr(mhpmcounter17h); + (void)read_csr(mhpmcounter18h); + (void)read_csr(mhpmcounter19h); + (void)read_csr(mhpmcounter20h); + (void)read_csr(mhpmcounter21h); + (void)read_csr(mhpmcounter22h); + (void)read_csr(mhpmcounter23h); + (void)read_csr(mhpmcounter24h); + (void)read_csr(mhpmcounter25h); + (void)read_csr(mhpmcounter26h); + (void)read_csr(mhpmcounter27h); + (void)read_csr(mhpmcounter28h); + (void)read_csr(mhpmcounter29h); + (void)read_csr(mhpmcounter30h); + (void)read_csr(mhpmcounter31h); + + (void)read_csr(cycle); + (void)read_csr(cycleh); + (void)read_csr(instret); + (void)read_csr(instreth); + + (void)read_csr(mcountinhibit); + (void)read_csr(mhpmevent3); + (void)read_csr(tselect); + (void)read_csr(tdata1); + (void)read_csr(dcsr); + (void)read_csr(dpc); + (void)read_csr(dscratch0); + (void)read_csr(dscratch1); + (void)read_csr(hazard3_csr_dmdata0); + (void)read_csr(hazard3_csr_meie0); + (void)read_csr(hazard3_csr_meip0); + (void)read_csr(hazard3_csr_mlei); +} + +void __attribute__((naked)) ebreak_trampoline() { + asm ("ebreak"); +} + +int main() { + // Make counters accessible to U mode + write_csr(mcounteren, -1u); + + // Enter function in U mode, return via ebreak trampoline + write_csr(mstatus, read_csr(mstatus) & ~0x1800u); + write_csr(mepc, &read_all_csrs); + asm ( + "la ra, ebreak_trampoline\n" + "mret\n" + ); + + 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 ((*(uint16_t*)mepc & 0x3) == 0x3) { + tb_printf("CSR was %03x\n", *(uint16_t*)(mepc + 2) >> 4); + mepc += 4; + } + else { + tb_puts("Exception on 16-bit instruction?!\n"); + tb_exit(-1); + } + write_csr(mepc, mepc); +}