From ddb6e1993b5c30b543b48c2148dc003ece5f356d Mon Sep 17 00:00:00 2001 From: Jonathan Jonsson Date: Tue, 14 Jan 2025 14:13:47 +0100 Subject: [PATCH 1/3] Fix issues with smcsrind not considering stateen, throw illegal on missing proxy csr and check for csr privilege in sscsring_reg_csr_t since mireg uses the same class. Add stateen checks for scontext/hcontext Fix for issue: 1893 --- riscv/csr_init.cc | 14 +++--- riscv/csrs.cc | 118 ++++++++++++++++++++++++++++++++++++++++++---- riscv/csrs.h | 25 ++++++++++ 3 files changed, 142 insertions(+), 15 deletions(-) diff --git a/riscv/csr_init.cc b/riscv/csr_init.cc index a03d188d4c..02e87c56c5 100644 --- a/riscv/csr_init.cc +++ b/riscv/csr_init.cc @@ -215,9 +215,9 @@ void state_t::csr_init(processor_t* const proc, reg_t max_isa) add_csr(CSR_TINFO, std::make_shared(proc, CSR_TINFO, 0)); } unsigned scontext_length = (xlen == 32 ? 16 : 32); // debug spec suggests 16-bit for RV32 and 32-bit for RV64 - add_supervisor_csr(CSR_SCONTEXT, scontext = std::make_shared(proc, CSR_SCONTEXT, (reg_t(1) << scontext_length) - 1, 0)); + add_supervisor_csr(CSR_SCONTEXT, scontext = std::make_shared(proc, CSR_SCONTEXT, (reg_t(1) << scontext_length) - 1, 0)); unsigned hcontext_length = (xlen == 32 ? 6 : 13) + (proc->extension_enabled('H') ? 1 : 0); // debug spec suggest 7-bit (6-bit) for RV32 and 14-bit (13-bit) for RV64 with (without) H extension - auto hcontext = std::make_shared(proc, CSR_HCONTEXT, (reg_t(1) << hcontext_length) - 1, 0); + auto hcontext = std::make_shared(proc, CSR_HCONTEXT, (reg_t(1) << hcontext_length) - 1, 0); add_hypervisor_csr(CSR_HCONTEXT, hcontext); add_csr(CSR_MCONTEXT, mcontext = std::make_shared(proc, CSR_MCONTEXT, hcontext)); add_csr(CSR_MSECCFG, mseccfg = std::make_shared(proc, CSR_MSECCFG)); @@ -284,7 +284,8 @@ void state_t::csr_init(processor_t* const proc, reg_t max_isa) const reg_t sstateen0_mask = (proc->extension_enabled(EXT_ZFINX) ? SSTATEEN0_FCSR : 0) | (proc->extension_enabled(EXT_ZCMT) ? SSTATEEN0_JVT : 0) | SSTATEEN0_CS; - const reg_t hstateen0_mask = sstateen0_mask | HSTATEEN0_SENVCFG | HSTATEEN_SSTATEEN; + const reg_t hstateen0_mask = sstateen0_mask | HSTATEEN0_SENVCFG | HSTATEEN_SSTATEEN | + (proc->extension_enabled(EXT_SSCSRIND) ? HSTATEEN0_CSRIND : 0); const reg_t mstateen0_mask = hstateen0_mask | (proc->extension_enabled(EXT_SSQOSID) ? MSTATEEN0_PRIV114 : 0); for (int i = 0; i < 4; i++) { const reg_t mstateen_mask = i == 0 ? mstateen0_mask : MSTATEEN_HSTATEEN; @@ -349,11 +350,12 @@ void state_t::csr_init(processor_t* const proc, reg_t max_isa) } if (proc->extension_enabled_const(EXT_SSCSRIND)) { - csr_t_p vsiselect = std::make_shared(proc, CSR_VSISELECT, 0); + auto vsiselect = std::make_shared(proc, CSR_VSISELECT, 0); add_hypervisor_csr(CSR_VSISELECT, vsiselect); - csr_t_p siselect = std::make_shared(proc, CSR_SISELECT, 0); - add_supervisor_csr(CSR_SISELECT, std::make_shared(proc, siselect, vsiselect)); + auto siselect = std::make_shared(proc, CSR_SISELECT, 0); + // Correct virtualized type? + add_supervisor_csr(CSR_SISELECT, std::make_shared(proc, siselect, vsiselect)); const reg_t vsireg_csrs[] = { CSR_VSIREG, CSR_VSIREG2, CSR_VSIREG3, CSR_VSIREG4, CSR_VSIREG5, CSR_VSIREG6 }; const reg_t sireg_csrs[] = { CSR_SIREG, CSR_SIREG2, CSR_SIREG3, CSR_SIREG4, CSR_SIREG5, CSR_SIREG6 }; diff --git a/riscv/csrs.cc b/riscv/csrs.cc index 2267f7f47d..06f9fa8418 100644 --- a/riscv/csrs.cc +++ b/riscv/csrs.cc @@ -1731,11 +1731,29 @@ virtualized_indirect_csr_t::virtualized_indirect_csr_t(processor_t* const proc, } void virtualized_indirect_csr_t::verify_permissions(insn_t insn, bool write) const { - virtualized_csr_t::verify_permissions(insn, write); if (state->v) virt_csr->verify_permissions(insn, write); else orig_csr->verify_permissions(insn, write); + virtualized_csr_t::verify_permissions(insn, write); +} + +virtualized_select_indirect_csr_t::virtualized_select_indirect_csr_t(processor_t *const proc, + csr_t_p orig, + csr_t_p virt) + : virtualized_csr_t(proc, orig, virt) {} + +void virtualized_select_indirect_csr_t::verify_permissions(insn_t insn, + bool write) const { + if (proc->extension_enabled(EXT_SMSTATEEN)) { + if ((state->prv < PRV_M) && + !(state->mstateen[0]->read() & MSTATEEN0_CSRIND)) + throw trap_illegal_instruction(insn.bits()); + + if (state->v && !(state->hstateen[0]->read() & HSTATEEN0_CSRIND)) + throw trap_virtual_instruction(insn.bits()); + } + virtualized_csr_t::verify_permissions(insn, write); } sscsrind_reg_csr_t::sscsrind_reg_csr_t(processor_t* const proc, const reg_t addr, csr_t_p iselect) : @@ -1744,19 +1762,43 @@ sscsrind_reg_csr_t::sscsrind_reg_csr_t(processor_t* const proc, const reg_t addr } void sscsrind_reg_csr_t::verify_permissions(insn_t insn, bool write) const { - // Don't call base verify_permission for VS registers remapped to S-mode - if (insn.csr() == address) - csr_t::verify_permissions(insn, write); + const auto csr_priv = get_field(insn.csr(), 0x300); + const bool is_vsi = csr_priv == PRV_HS; + // csr_priv checked due to mireg using the same class + if (csr_priv < PRV_M && state->prv < PRV_M){ + // The CSRIND bit in mstateen0 controls access to the siselect, sireg*, vsiselect, and the vsireg* + // Stateen takes precedence over general sscsrind rules + if (proc->extension_enabled(EXT_SMSTATEEN)) { + const bool m_csrind = state->mstateen[0]->read() & MSTATEEN0_CSRIND; + const bool h_csrind = state->hstateen[0]->read() & HSTATEEN0_CSRIND; + if (!m_csrind) + throw trap_illegal_instruction(insn.bits()); + + if (state->v && !h_csrind) + throw trap_virtual_instruction(insn.bits()); + } + } + + // A virtual instruction exception is raised for attempts from VS-mode or VU-mode to directly access + // vsiselect or vsireg*, or attempts from VU-mode to access siselect or sireg*. + if (state->v and csr_priv < PRV_M){ + if (is_vsi) + throw trap_virtual_instruction(insn.bits()); + else if (state->prv == PRV_U) + throw trap_virtual_instruction(insn.bits()); + } csr_t_p proxy_csr = get_reg(); if (proxy_csr == nullptr) { - if (!state->v) { - throw trap_illegal_instruction(insn.bits()); - } else { - throw trap_virtual_instruction(insn.bits()); - } + // The spec recomends raising illegal if the proxy csr is not implemented. + throw trap_illegal_instruction(insn.bits()); } proxy_csr->verify_permissions(insn, write); + + // Don't call base verify_permission for VS registers remapped to S-mode + if (insn.csr() == address) + csr_t::verify_permissions(insn, write); + } @@ -1777,6 +1819,36 @@ bool sscsrind_reg_csr_t::unlogged_write(const reg_t val) noexcept { } // Returns the actual CSR that maps to value in *siselect or nullptr if no mapping exists +sscsrind_select_csr_t::sscsrind_select_csr_t(processor_t *const proc, const reg_t addr, + const reg_t init) + : basic_csr_t(proc, addr, init) {} + +void sscsrind_select_csr_t::verify_permissions(insn_t insn, bool write) const { + const auto csr_priv = get_field(insn.csr(), 0x300); + const bool is_vsi = csr_priv == PRV_HS; + // The CSRIND bit in mstateen0 controls access to the siselect, sireg*, vsiselect, and the vsireg* + if (proc->extension_enabled(EXT_SMSTATEEN) && state->prv < PRV_M) { + const bool m_csrind = state->mstateen[0]->read() & MSTATEEN0_CSRIND; + const bool h_csrind = state->hstateen[0]->read() & HSTATEEN0_CSRIND; + if (!m_csrind) + throw trap_illegal_instruction(insn.bits()); + + if (state->v && !h_csrind) + throw trap_virtual_instruction(insn.bits()); + } + // A virtual instruction exception is raised for attempts from VS-mode or VU-mode to directly access + // vsiselect or vsireg*, or attempts from VU-mode to access siselect or sireg*. + if (state->v){ + if (is_vsi){ + throw trap_virtual_instruction(insn.bits()); + } else if (state->prv == PRV_U) + throw trap_virtual_instruction(insn.bits()); + } + basic_csr_t::verify_permissions(insn, write); +}; + +// Returns the actual CSR that maps to value in *siselect or nullptr if no +// mapping exists csr_t_p sscsrind_reg_csr_t::get_reg() const noexcept { auto proxy = ireg_proxy; auto isel = iselect->read(); @@ -1878,3 +1950,31 @@ bool hstatus_csr_t::unlogged_write(const reg_t val) noexcept { proc->get_mmu()->flush_tlb(); return basic_csr_t::unlogged_write(new_hstatus); } + +scontext_csr_t::scontext_csr_t(processor_t* const proc, const reg_t addr, const reg_t mask, const reg_t init) : +masked_csr_t(proc, addr, mask, init){}; +void scontext_csr_t::verify_permissions(insn_t insn, bool write) const { + if (proc->extension_enabled(EXT_SMSTATEEN)) { + if ((state->prv < PRV_M) && + !(state->mstateen[0]->read() & MSTATEEN0_HCONTEXT)) + throw trap_illegal_instruction(insn.bits()); + + if (state->v && !(state->hstateen[0]->read() & HSTATEEN0_SCONTEXT)) + throw trap_virtual_instruction(insn.bits()); + } + masked_csr_t::verify_permissions(insn, write); + +} + +hcontext_csr_t::hcontext_csr_t(processor_t* const proc, const reg_t addr, const reg_t mask, const reg_t init) : +masked_csr_t(proc, addr, mask, init){}; +void hcontext_csr_t::verify_permissions(insn_t insn, bool write) const { + if (proc->extension_enabled(EXT_SMSTATEEN)) { + if ((state->prv < PRV_M) && + !(state->mstateen[0]->read() & MSTATEEN0_HCONTEXT)) + throw trap_illegal_instruction(insn.bits()); + + } + masked_csr_t::verify_permissions(insn, write); + +} diff --git a/riscv/csrs.h b/riscv/csrs.h index 278bdb3713..20a4399534 100644 --- a/riscv/csrs.h +++ b/riscv/csrs.h @@ -849,6 +849,19 @@ class sscsrind_reg_csr_t : public csr_t { csr_t_p get_reg() const noexcept; }; +class sscsrind_select_csr_t: public basic_csr_t { + public: + sscsrind_select_csr_t(processor_t* const proc, const reg_t addr, const reg_t init); + protected: + virtual void verify_permissions(insn_t insn, bool write) const override; +}; + +class virtualized_select_indirect_csr_t: public virtualized_csr_t { + public: + virtualized_select_indirect_csr_t(processor_t* const proc, csr_t_p orig, csr_t_p virt); + virtual void verify_permissions(insn_t insn, bool write) const override; +}; + // smcntrpmf_csr_t caches the previous state of the CSR in case a CSRW instruction // modifies the state that should not be immediately visible to bump() class smcntrpmf_csr_t : public masked_csr_t { @@ -899,4 +912,16 @@ class hstatus_csr_t final: public basic_csr_t { protected: virtual bool unlogged_write(const reg_t val) noexcept override; }; + +class scontext_csr_t: public masked_csr_t { + public: + scontext_csr_t(processor_t* const proc, const reg_t addr, const reg_t mask, const reg_t init); + virtual void verify_permissions(insn_t insn, bool write) const override; +}; + +class hcontext_csr_t: public masked_csr_t { + public: + hcontext_csr_t(processor_t* const proc, const reg_t addr, const reg_t mask, const reg_t init); + virtual void verify_permissions(insn_t insn, bool write) const override; +}; #endif From bc7461b4838ba6ec584f0376cfd2119780f06d0e Mon Sep 17 00:00:00 2001 From: Jonathan Jonsson Date: Tue, 28 Jan 2025 09:13:30 +0100 Subject: [PATCH 2/3] Add const csr hedelegh and masks for stateen.priv113/stateen.context --- riscv/csr_init.cc | 7 +++++-- riscv/csrs.cc | 15 +++++++++++++++ riscv/csrs.h | 6 ++++++ riscv/encoding.h | 9 +++++++-- 4 files changed, 33 insertions(+), 4 deletions(-) diff --git a/riscv/csr_init.cc b/riscv/csr_init.cc index 02e87c56c5..5b59ed42c4 100644 --- a/riscv/csr_init.cc +++ b/riscv/csr_init.cc @@ -179,6 +179,7 @@ void state_t::csr_init(processor_t* const proc, reg_t max_isa) (1 << CAUSE_SOFTWARE_CHECK_FAULT) | (1 << CAUSE_HARDWARE_ERROR_FAULT); add_hypervisor_csr(CSR_HEDELEG, hedeleg = std::make_shared(proc, CSR_HEDELEG, hedeleg_mask, 0)); + add_hypervisor_csr(CSR_HEDELEGH, std::make_shared(proc, CSR_HEDELEGH, 0)); add_hypervisor_csr(CSR_HCOUNTEREN, hcounteren = std::make_shared(proc, CSR_HCOUNTEREN, counteren_mask, 0)); htimedelta = std::make_shared(proc, CSR_HTIMEDELTA, 0); if (xlen == 32) { @@ -285,8 +286,10 @@ void state_t::csr_init(processor_t* const proc, reg_t max_isa) (proc->extension_enabled(EXT_ZCMT) ? SSTATEEN0_JVT : 0) | SSTATEEN0_CS; const reg_t hstateen0_mask = sstateen0_mask | HSTATEEN0_SENVCFG | HSTATEEN_SSTATEEN | - (proc->extension_enabled(EXT_SSCSRIND) ? HSTATEEN0_CSRIND : 0); - const reg_t mstateen0_mask = hstateen0_mask | (proc->extension_enabled(EXT_SSQOSID) ? MSTATEEN0_PRIV114 : 0); + (proc->extension_enabled(EXT_SSCSRIND) ? HSTATEEN0_CSRIND : 0) | + (proc->get_cfg().trigger_count > 0 ? HSTATEEN0_SCONTEXT : 0); + const reg_t mstateen0_mask = hstateen0_mask | MSTATEEN0_PRIV113 | + (proc->extension_enabled(EXT_SSQOSID) ? MSTATEEN0_PRIV114 : 0); for (int i = 0; i < 4; i++) { const reg_t mstateen_mask = i == 0 ? mstateen0_mask : MSTATEEN_HSTATEEN; mstateen[i] = std::make_shared(proc, CSR_MSTATEEN0 + i, mstateen_mask, 0); diff --git a/riscv/csrs.cc b/riscv/csrs.cc index 06f9fa8418..afcf2304a7 100644 --- a/riscv/csrs.cc +++ b/riscv/csrs.cc @@ -1978,3 +1978,18 @@ void hcontext_csr_t::verify_permissions(insn_t insn, bool write) const { masked_csr_t::verify_permissions(insn, write); } + +hedelegh_csr_t::hedelegh_csr_t(processor_t* const proc, const reg_t addr, const reg_t init) : +const_csr_t(proc, addr, init){}; +void hedelegh_csr_t::verify_permissions(insn_t insn, bool write) const { + if (proc->get_const_xlen() != 32) + throw trap_illegal_instruction(insn.bits()); + if (proc->extension_enabled(EXT_SMSTATEEN)) { + if ((state->prv < PRV_M) && + !(state->mstateen[0]->read() & MSTATEEN0_PRIV113)) + throw trap_illegal_instruction(insn.bits()); + + } + const_csr_t::verify_permissions(insn, write); + +} diff --git a/riscv/csrs.h b/riscv/csrs.h index 20a4399534..058e56b741 100644 --- a/riscv/csrs.h +++ b/riscv/csrs.h @@ -924,4 +924,10 @@ class hcontext_csr_t: public masked_csr_t { hcontext_csr_t(processor_t* const proc, const reg_t addr, const reg_t mask, const reg_t init); virtual void verify_permissions(insn_t insn, bool write) const override; }; + +class hedelegh_csr_t: public const_csr_t { + public: + hedelegh_csr_t(processor_t* const proc, const reg_t addr, const reg_t init); + virtual void verify_permissions(insn_t insn, bool write) const override; +}; #endif diff --git a/riscv/encoding.h b/riscv/encoding.h index dcd4e248ed..4411ac2506 100644 --- a/riscv/encoding.h +++ b/riscv/encoding.h @@ -4,7 +4,7 @@ /* * This file is auto-generated by running 'make' in - * https://github.com/riscv/riscv-opcodes (47862ce) + * https://github.com/riscv/riscv-opcodes (9f70bcd) */ #ifndef RISCV_CSR_ENCODING_H @@ -81,8 +81,9 @@ #define USTATUS_UPIE 0x00000010 #define MNSTATUS_NMIE 0x00000008 -#define MNSTATUS_MNPP 0x00001800 #define MNSTATUS_MNPV 0x00000080 +#define MNSTATUS_MNPELP 0x00000200 +#define MNSTATUS_MNPP 0x00001800 #define DCSR_XDEBUGVER (15U<<28) #define DCSR_EXTCAUSE (7<<24) @@ -194,6 +195,7 @@ #define MSTATEEN0_FCSR 0x00000002 #define MSTATEEN0_JVT 0x00000004 #define MSTATEEN0_CTR 0x0040000000000000 +#define MSTATEEN0_PRIV113 0x0100000000000000 #define MSTATEEN0_PRIV114 0x0080000000000000 #define MSTATEEN0_HCONTEXT 0x0200000000000000 #define MSTATEEN0_AIA 0x0800000000000000 @@ -202,6 +204,7 @@ #define MSTATEEN_HSTATEEN 0x8000000000000000 #define MSTATEEN0H_CTR 0x00400000 +#define MSTATEEN0H_PRIV113 0x01000000 #define MSTATEEN0H_PRIV114 0x00800000 #define MSTATEEN0H_HCONTEXT 0x02000000 #define MSTATEEN0H_AIA 0x08000000 @@ -2768,6 +2771,7 @@ #define CSR_VSIEH 0x214 #define CSR_VSIPH 0x254 #define CSR_VSTIMECMPH 0x25d +#define CSR_HEDELEGH 0x612 #define CSR_HTIMEDELTAH 0x615 #define CSR_HIDELEGH 0x613 #define CSR_HVIENH 0x618 @@ -4313,6 +4317,7 @@ DECLARE_CSR(stimecmph, CSR_STIMECMPH) DECLARE_CSR(vsieh, CSR_VSIEH) DECLARE_CSR(vsiph, CSR_VSIPH) DECLARE_CSR(vstimecmph, CSR_VSTIMECMPH) +DECLARE_CSR(hedelegh, CSR_HEDELEGH) DECLARE_CSR(htimedeltah, CSR_HTIMEDELTAH) DECLARE_CSR(hidelegh, CSR_HIDELEGH) DECLARE_CSR(hvienh, CSR_HVIENH) From a55b6f7e5d03776bf7c476c45114f5b3c7acfdc6 Mon Sep 17 00:00:00 2001 From: Jonathan Jonsson Date: Fri, 7 Feb 2025 14:46:06 +0100 Subject: [PATCH 3/3] Add SMCDELEG extension and update encoding.h --- disasm/isa_parser.cc | 2 + riscv/csr_init.cc | 88 ++++++++++++++++++++++++++++++++++++++------ riscv/csrs.cc | 88 ++++++++++++++++++++++++++++++++++++++++++++ riscv/csrs.h | 27 ++++++++++++++ riscv/encoding.h | 4 +- riscv/isa_parser.h | 1 + riscv/processor.h | 2 + 7 files changed, 199 insertions(+), 13 deletions(-) diff --git a/disasm/isa_parser.cc b/disasm/isa_parser.cc index 69c3e34bc2..53acbba9d7 100644 --- a/disasm/isa_parser.cc +++ b/disasm/isa_parser.cc @@ -326,6 +326,8 @@ isa_parser_t::isa_parser_t(const char* str, const char *priv) extension_table[EXT_SSCSRIND] = true; } else if (ext_str == "smcntrpmf") { extension_table[EXT_SMCNTRPMF] = true; + } else if (ext_str == "smcdeleg") { + extension_table[EXT_SMCDELEG] = true; } else if (ext_str == "zimop") { extension_table[EXT_ZIMOP] = true; } else if (ext_str == "zcmop") { diff --git a/riscv/csr_init.cc b/riscv/csr_init.cc index 5b59ed42c4..5057b84b29 100644 --- a/riscv/csr_init.cc +++ b/riscv/csr_init.cc @@ -1,5 +1,6 @@ -#include "processor.h" #include "debug_defines.h" +#include "processor.h" +// clang-format off void state_t::add_csr(reg_t addr, const csr_t_p& csr) { @@ -35,11 +36,11 @@ void state_t::csr_init(processor_t* const proc, reg_t max_isa) const reg_t minstretcfg_mask = !proc->extension_enabled_const(EXT_SMCNTRPMF) ? 0 : MHPMEVENT_MINH | MHPMEVENT_SINH | MHPMEVENT_UINH | MHPMEVENT_VSINH | MHPMEVENT_VUINH; - auto minstretcfg = std::make_shared(proc, CSR_MINSTRETCFG, minstretcfg_mask, 0); - auto mcyclecfg = std::make_shared(proc, CSR_MCYCLECFG, minstretcfg_mask, 0); + auto _minstretcfg = std::make_shared(proc, CSR_MINSTRETCFG, minstretcfg_mask, 0); + auto _mcyclecfg = std::make_shared(proc, CSR_MCYCLECFG, minstretcfg_mask, 0); - minstret = std::make_shared(proc, CSR_MINSTRET, minstretcfg); - mcycle = std::make_shared(proc, CSR_MCYCLE, mcyclecfg); + minstret = std::make_shared(proc, CSR_MINSTRET, _minstretcfg); + mcycle = std::make_shared(proc, CSR_MCYCLE, _mcyclecfg); time = std::make_shared(proc, CSR_TIME); if (proc->extension_enabled_const(EXT_ZICNTR)) { add_csr(CSR_INSTRET, std::make_shared(proc, CSR_INSTRET, minstret)); @@ -251,7 +252,8 @@ void state_t::csr_init(processor_t* const proc, reg_t max_isa) (proc->extension_enabled(EXT_SSTC) ? MENVCFG_STCE : 0) | (proc->extension_enabled(EXT_ZICFILP) ? MENVCFG_LPE : 0) | (proc->extension_enabled(EXT_ZICFISS) ? MENVCFG_SSE : 0) | - (proc->extension_enabled(EXT_SSDBLTRP) ? MENVCFG_DTE : 0); + (proc->extension_enabled(EXT_SSDBLTRP) ? MENVCFG_DTE : 0) | + (proc->extension_enabled(EXT_SMCDELEG) ? MENVCFG_CDE : 0); menvcfg = std::make_shared(proc, CSR_MENVCFG, menvcfg_mask, 0); if (xlen == 32) { add_user_csr(CSR_MENVCFG, std::make_shared(proc, CSR_MENVCFG, menvcfg)); @@ -373,16 +375,78 @@ void state_t::csr_init(processor_t* const proc, reg_t max_isa) if (proc->extension_enabled_const(EXT_SMCNTRPMF)) { if (xlen == 32) { - add_csr(CSR_MCYCLECFG, std::make_shared(proc, CSR_MCYCLECFG, mcyclecfg)); - add_csr(CSR_MCYCLECFGH, std::make_shared(proc, CSR_MCYCLECFGH, mcyclecfg)); - add_csr(CSR_MINSTRETCFG, std::make_shared(proc, CSR_MINSTRETCFG, minstretcfg)); - add_csr(CSR_MINSTRETCFGH, std::make_shared(proc, CSR_MINSTRETCFGH, minstretcfg)); + add_csr(CSR_MCYCLECFG, mcyclecfg = std::make_shared(proc, CSR_MCYCLECFG, _mcyclecfg)); + add_csr(CSR_MCYCLECFGH, std::make_shared(proc, CSR_MCYCLECFGH, _mcyclecfg)); + add_csr(CSR_MINSTRETCFG, minstretcfg = std::make_shared(proc, CSR_MINSTRETCFG, _minstretcfg)); + add_csr(CSR_MINSTRETCFGH, std::make_shared(proc, CSR_MINSTRETCFGH, _minstretcfg)); } else { - add_csr(CSR_MCYCLECFG, mcyclecfg); - add_csr(CSR_MINSTRETCFG, minstretcfg); + add_csr(CSR_MCYCLECFG, mcyclecfg = _mcyclecfg); + add_csr(CSR_MINSTRETCFG, minstretcfg = _minstretcfg); } } const reg_t srmcfg_mask = SRMCFG_MCID | SRMCFG_RCID; add_const_ext_csr(EXT_SSQOSID, CSR_SRMCFG, std::make_shared(proc, CSR_SRMCFG, srmcfg_mask, 0)); + + if (proc->extension_enabled_const(EXT_SMCDELEG) && proc->extension_enabled_const(EXT_SSCSRIND)) { + add_supervisor_csr(CSR_SCOUNTINHIBIT, std::make_shared(proc, CSR_SCOUNTINHIBIT)); + auto sireg = std::dynamic_pointer_cast(csrmap.at(CSR_SIREG)); + auto sireg2 = std::dynamic_pointer_cast(csrmap.at(CSR_SIREG2)); + auto sireg3 = std::dynamic_pointer_cast(csrmap.at(CSR_SIREG3)); + auto sireg4 = std::dynamic_pointer_cast(csrmap.at(CSR_SIREG4)); + auto sireg5 = std::dynamic_pointer_cast(csrmap.at(CSR_SIREG5)); + auto sireg6 = std::dynamic_pointer_cast(csrmap.at(CSR_SIREG6)); + auto add_proxy = [&proc, this](std::shared_ptr ireg, + reg_t select, + reg_t csr_proxy) { + assert(ireg); + const auto exists = csrmap.find(csr_proxy) != csrmap.end(); + const auto dummy = std::make_shared(proc, 0xdeafbeef, 0); + + auto smc_obj_orig = std::make_shared( + proc, ireg->address, select, exists ? csrmap.at(csr_proxy) : dummy , !exists); + auto orig_csr = std::dynamic_pointer_cast(ireg->get_orig_csr()); + assert(orig_csr); + orig_csr->add_ireg_proxy(select, smc_obj_orig); + + auto smc_obj_virt = std::make_shared( + proc, ireg->address + 0x100, select, exists ? csrmap.at(csr_proxy) : dummy, !exists); + auto virt_csr = std::dynamic_pointer_cast(ireg->get_virt_csr()); + assert(virt_csr); + virt_csr->add_ireg_proxy(select, smc_obj_virt); + }; + + // We need to add all CSRs to be able to throw virtual on them, otherwise when csrind + // looks for the select value it won't find any csr and thus throw illegal. + // The spec states that an access to any sireg* in VS mode should raise virtual if menvcfg.cde = 1. + // Most likely the motivation for this is to not leak information about which extensions the + // underlying machine has implemented. + add_proxy(sireg, SISELECT_SMCDELEG_START, CSR_MCYCLE); + add_proxy(sireg, SISELECT_SMCDELEG_UNUSED, 0xDEADBEEF); + add_proxy(sireg, SISELECT_SMCDELEG_INSTRET, CSR_MINSTRET); + + add_proxy(sireg2, SISELECT_SMCDELEG_START, CSR_MCYCLECFG); + add_proxy(sireg2, SISELECT_SMCDELEG_UNUSED, 0xDEADBEEF); + add_proxy(sireg2, SISELECT_SMCDELEG_INSTRETCFG, CSR_MINSTRETCFG); + + add_proxy(sireg4, SISELECT_SMCDELEG_START, CSR_MCYCLEH); + add_proxy(sireg4, SISELECT_SMCDELEG_UNUSED, 0xDEADBEEF); + add_proxy(sireg4, SISELECT_SMCDELEG_INSTRET, CSR_MINSTRETH); + + add_proxy(sireg5, SISELECT_SMCDELEG_START, CSR_MCYCLECFGH); + add_proxy(sireg5, SISELECT_SMCDELEG_UNUSED, 0xDEADBEEF); + add_proxy(sireg5, SISELECT_SMCDELEG_INSTRETCFG, CSR_MINSTRETCFGH); + + // Dummies... + for (auto i = 0; i < 32; i++){ + add_proxy(sireg3, SISELECT_SMCDELEG_START+i, 0xDEADBEEF); + add_proxy(sireg6, SISELECT_SMCDELEG_START+i, 0xDEADBEEF); + } + for (auto cnt = 0; cnt < 29; cnt++) { + add_proxy(sireg, SISELECT_SMCDELEG_HPMCOUNTER_3 + cnt, CSR_HPMCOUNTER3 + cnt); + add_proxy(sireg2, SISELECT_SMCDELEG_HPMEVENT_3 + cnt, CSR_MHPMEVENT3 + cnt); + add_proxy(sireg4, SISELECT_SMCDELEG_HPMCOUNTER_3 + cnt, CSR_HPMCOUNTER3H + cnt); + add_proxy(sireg5, SISELECT_SMCDELEG_HPMEVENT_3 + cnt, CSR_MHPMEVENT3H + cnt); + } + } } diff --git a/riscv/csrs.cc b/riscv/csrs.cc index afcf2304a7..193b8d24db 100644 --- a/riscv/csrs.cc +++ b/riscv/csrs.cc @@ -1,4 +1,5 @@ // See LICENSE for license details. +// clang-format off // For std::any_of #include @@ -1675,7 +1676,11 @@ scountovf_csr_t::scountovf_csr_t(processor_t* const proc, const reg_t addr): void scountovf_csr_t::verify_permissions(insn_t insn, bool write) const { if (!proc->extension_enabled(EXT_SSCOFPMF)) throw trap_illegal_instruction(insn.bits()); + csr_t::verify_permissions(insn, write); + + if (proc->extension_enabled_const(EXT_SMCDELEG) && state->menvcfg->read() & MENVCFG_CDE && state->v) + throw trap_virtual_instruction(insn.bits()); } reg_t scountovf_csr_t::read() const noexcept { @@ -1951,6 +1956,89 @@ bool hstatus_csr_t::unlogged_write(const reg_t val) noexcept { return basic_csr_t::unlogged_write(new_hstatus); } +smcdeleg_indir_csr_t::smcdeleg_indir_csr_t(processor_t* const proc, const reg_t addr, const reg_t select, const csr_t_p csr, bool missing) : + csr_t(proc,addr), addr(addr), select(select), orig_csr(csr), missing(missing){ + assert (select >= SISELECT_SMCDELEG_START and select <= SISELECT_SMCDELEG_END); +} + +bool smcdeleg_indir_csr_t::unlogged_write(const reg_t val) noexcept { + reg_t write_val = 0; + if (addr == CSR_SIREG2 || addr == CSR_SIREG5){ + write_val = (orig_csr->read() & MHPMEVENT_MINH) | (val & ~MHPMEVENT_MINH); + } else { + write_val = val; + } + return orig_csr->unlogged_write(write_val); +} + +reg_t smcdeleg_indir_csr_t::read() const noexcept { + // MINH masking + if (addr == CSR_SIREG2 || addr == CSR_SIREG5){ + return orig_csr->read() & ~MHPMEVENT_MINH; + } + return orig_csr->read(); +} +void +smcdeleg_indir_csr_t::verify_permissions(insn_t insn, bool write) const{ + bool is_vsi = get_field(addr, 0x300) == PRV_HS; + auto counter_offset = select - SISELECT_SMCDELEG_START; + auto counter_enabled = (state->mcounteren->read() >> counter_offset) & 1; + const bool cde = state->menvcfg->read() & MENVCFG_CDE; + + assert(counter_offset >= 0); + + // Counter must be active and mencfg.cde must be set (when access comes from M or HS) + if ((state->prv >= PRV_S && !state->v) && (missing || !cde || !counter_enabled)){ + throw trap_illegal_instruction(insn.bits()); + } + + if (is_vsi){ + if (state->prv >= PRV_S && !state->v){ + throw trap_illegal_instruction(insn.bits()); + } + else if (state->prv == PRV_S and state->v){ + if (cde){ + throw trap_virtual_instruction(insn.bits()); + } else { + throw trap_illegal_instruction(insn.bits()); + } + } + } else { // Sireg* + // An attempt from VS-mode to access any sireg* (really vsireg*) raises an illegal instruction + // exception if menvcfg.CDE = 0, or a virtual instruction exception if menvcfg.CDE = 1 + if (state->v && state->prv == PRV_S){ + assert(missing); + if (cde) + throw trap_virtual_instruction(insn.bits()); + else + throw trap_illegal_instruction(insn.bits()); + } + } +} + +scountinhibit_csr_t::scountinhibit_csr_t(processor_t* const proc, const reg_t addr) : csr_t(proc, addr){} +void scountinhibit_csr_t::verify_permissions(insn_t insn, bool write) const { + // menvcfg.cde can only be set if smcdeleg is present + if (!(state->menvcfg->read() & MENVCFG_CDE)) + throw trap_illegal_instruction(insn.bits()); + + csr_t::verify_permissions(insn, write); + + if (state->v) + throw trap_virtual_instruction(insn.bits()); +} + +reg_t scountinhibit_csr_t::read() const noexcept { + const auto mask = state->mcounteren->read(); + return state->mcountinhibit->read() & mask; +} + +bool scountinhibit_csr_t::unlogged_write(const reg_t val) noexcept { + const auto masked_val = state->mcounteren->read() & val; + state->mcountinhibit->write(val); + return false; +} + scontext_csr_t::scontext_csr_t(processor_t* const proc, const reg_t addr, const reg_t mask, const reg_t init) : masked_csr_t(proc, addr, mask, init){}; void scontext_csr_t::verify_permissions(insn_t insn, bool write) const { diff --git a/riscv/csrs.h b/riscv/csrs.h index 058e56b741..16e2f01fa1 100644 --- a/riscv/csrs.h +++ b/riscv/csrs.h @@ -1,6 +1,7 @@ // See LICENSE for license details. #ifndef _RISCV_CSRS_H #define _RISCV_CSRS_H +// clang-format off #include "common.h" #include "encoding.h" @@ -70,6 +71,7 @@ class csr_t { // For access to written_value() and unlogged_write(): friend class rv32_high_csr_t; friend class rv32_low_csr_t; + friend class smcdeleg_indir_csr_t; }; typedef std::shared_ptr csr_t_p; @@ -832,6 +834,8 @@ class virtualized_indirect_csr_t: public virtualized_csr_t { public: virtualized_indirect_csr_t(processor_t* const proc, csr_t_p orig, csr_t_p virt); virtual void verify_permissions(insn_t insn, bool write) const override; + auto get_orig_csr(){return virtualized_csr_t::orig_csr;} + auto get_virt_csr(){return virtualized_csr_t::virt_csr;} }; class sscsrind_reg_csr_t : public csr_t { @@ -913,6 +917,29 @@ class hstatus_csr_t final: public basic_csr_t { virtual bool unlogged_write(const reg_t val) noexcept override; }; +class smcdeleg_indir_csr_t : public csr_t { + public: + smcdeleg_indir_csr_t(processor_t* const proc, const reg_t addr, const reg_t select, const csr_t_p csr, bool missing); + protected: + virtual bool unlogged_write(const reg_t val) noexcept; + virtual void verify_permissions(insn_t insn, bool write) const; + virtual reg_t read() const noexcept; +protected: + reg_t addr; + reg_t select; + csr_t_p orig_csr; + bool missing; +}; + +class scountinhibit_csr_t: public csr_t { + public: + scountinhibit_csr_t(processor_t* const proc, const reg_t addr); + virtual void verify_permissions(insn_t insn, bool write) const override; + virtual reg_t read() const noexcept override; + protected: + virtual bool unlogged_write(const reg_t val) noexcept override; +}; + class scontext_csr_t: public masked_csr_t { public: scontext_csr_t(processor_t* const proc, const reg_t addr, const reg_t mask, const reg_t init); diff --git a/riscv/encoding.h b/riscv/encoding.h index 4411ac2506..f2c993c006 100644 --- a/riscv/encoding.h +++ b/riscv/encoding.h @@ -4,7 +4,7 @@ /* * This file is auto-generated by running 'make' in - * https://github.com/riscv/riscv-opcodes (9f70bcd) + * https://github.com/riscv/riscv-opcodes (106c942) */ #ifndef RISCV_CSR_ENCODING_H @@ -182,11 +182,13 @@ #define MENVCFG_CBZE 0x00000080 #define MENVCFG_PMM 0x0000000300000000 #define MENVCFG_DTE 0x0800000000000000 +#define MENVCFG_CDE 0x1000000000000000 #define MENVCFG_ADUE 0x2000000000000000 #define MENVCFG_PBMTE 0x4000000000000000 #define MENVCFG_STCE 0x8000000000000000 #define MENVCFGH_DTE 0x08000000 +#define MENVCFGH_CDE 0x10000000 #define MENVCFGH_ADUE 0x20000000 #define MENVCFGH_PBMTE 0x40000000 #define MENVCFGH_STCE 0x80000000 diff --git a/riscv/isa_parser.h b/riscv/isa_parser.h index 90e1ec1650..1f1765b8ad 100644 --- a/riscv/isa_parser.h +++ b/riscv/isa_parser.h @@ -75,6 +75,7 @@ typedef enum { EXT_SMCSRIND, EXT_SSCSRIND, EXT_SMCNTRPMF, + EXT_SMCDELEG, EXT_ZIMOP, EXT_ZCMOP, EXT_ZALASR, diff --git a/riscv/processor.h b/riscv/processor.h index 4f22cbdee5..6d2bd8594d 100644 --- a/riscv/processor.h +++ b/riscv/processor.h @@ -172,6 +172,8 @@ struct state_t csr_t_p ssp; + csr_t_p minstretcfg, mcyclecfg; + bool serialized; // whether timer CSRs are in a well-defined state // When true, execute a single instruction and then enter debug mode. This