diff --git a/riscv/csr_init.cc b/riscv/csr_init.cc index a03d188d4..02e87c56c 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 2267f7f47..06f9fa841 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 278bdb371..20a439953 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