From a5fdc4efbf9f79a890fe5d1c144880548b011d90 Mon Sep 17 00:00:00 2001 From: Andrew Waterman Date: Thu, 16 Mar 2023 17:14:24 -0700 Subject: Implement Smrnmi extension We don't model any sources of RNMI, so this is mostly vestigial. --- riscv/csrs.cc | 20 ++++++++++++++++++++ riscv/csrs.h | 7 +++++++ riscv/insns/mnret.h | 15 +++++++++++++++ riscv/isa_parser.cc | 2 ++ riscv/isa_parser.h | 1 + riscv/mmu.h | 1 + riscv/processor.cc | 19 ++++++++++++++++--- riscv/processor.h | 2 ++ riscv/riscv.mk.in | 4 ++++ 9 files changed, 68 insertions(+), 3 deletions(-) create mode 100644 riscv/insns/mnret.h diff --git a/riscv/csrs.cc b/riscv/csrs.cc index 5cadfbe..2e01983 100644 --- a/riscv/csrs.cc +++ b/riscv/csrs.cc @@ -516,6 +516,24 @@ reg_t mstatus_csr_t::compute_mstatus_initial_value() const noexcept { | 0; // initial value for mstatus } +// implement class mnstatus_csr_t +mnstatus_csr_t::mnstatus_csr_t(processor_t* const proc, const reg_t addr): + basic_csr_t(proc, addr, 0) { +} + +bool mnstatus_csr_t::unlogged_write(const reg_t val) noexcept { + // NMIE can be set but not cleared + const reg_t mask = (~read() & MNSTATUS_NMIE) + | (proc->extension_enabled('H') ? MNSTATUS_MNPV : 0) + | MNSTATUS_MNPP; + + const reg_t requested_mnpp = proc->legalize_privilege(get_field(val, MNSTATUS_MNPP)); + const reg_t adjusted_val = set_field(val, MNSTATUS_MNPP, requested_mnpp); + const reg_t new_mnstatus = (read() & ~mask) | (adjusted_val & mask); + + return basic_csr_t::unlogged_write(new_mnstatus); +} + // implement class rv32_low_csr_t rv32_low_csr_t::rv32_low_csr_t(processor_t* const proc, const reg_t addr, csr_t_p orig): csr_t(proc, addr), @@ -654,7 +672,9 @@ bool misa_csr_t::unlogged_write(const reg_t val) noexcept { | (1 << CAUSE_VIRTUAL_INSTRUCTION) | (1 << CAUSE_STORE_GUEST_PAGE_FAULT) ; + state->medeleg->write(state->medeleg->read() & ~hypervisor_exceptions); + if (state->mnstatus) state->mnstatus->write(state->mnstatus->read() & ~MNSTATUS_MNPV); const reg_t new_mstatus = state->mstatus->read() & ~(MSTATUS_GVA | MSTATUS_MPV); state->mstatus->write(new_mstatus); if (state->mstatush) state->mstatush->write(new_mstatus >> 32); // log mstatush change diff --git a/riscv/csrs.h b/riscv/csrs.h index 5dab1fc..65be799 100644 --- a/riscv/csrs.h +++ b/riscv/csrs.h @@ -256,6 +256,13 @@ class mstatus_csr_t final: public base_status_csr_t { typedef std::shared_ptr mstatus_csr_t_p; +class mnstatus_csr_t final: public basic_csr_t { + public: + mnstatus_csr_t(processor_t* const proc, const reg_t addr); + protected: + virtual bool unlogged_write(const reg_t val) noexcept override; +}; + // For RV32 CSRs that are split into two, e.g. mstatus/mstatush // CSRW should only modify the lower half class rv32_low_csr_t: public csr_t { diff --git a/riscv/insns/mnret.h b/riscv/insns/mnret.h new file mode 100644 index 0000000..bc69510 --- /dev/null +++ b/riscv/insns/mnret.h @@ -0,0 +1,15 @@ +require_extension(EXT_SMRNMI); +require_privilege(PRV_M); +set_pc_and_serialize(p->get_state()->mnepc->read()); +reg_t s = STATE.mnstatus->read(); +reg_t prev_prv = get_field(s, MNSTATUS_MNPP); +reg_t prev_virt = get_field(s, MNSTATUS_MNPV); +if (prev_prv != PRV_M) { + reg_t mstatus = STATE.mstatus->read(); + mstatus = set_field(mstatus, MSTATUS_MPRV, 0); + STATE.mstatus->write(mstatus); +} +s = set_field(s, MNSTATUS_NMIE, 1); +STATE.mnstatus->write(s); +p->set_privilege(prev_prv); +p->set_virt(prev_virt); diff --git a/riscv/isa_parser.cc b/riscv/isa_parser.cc index 4fc53f7..caf91ea 100644 --- a/riscv/isa_parser.cc +++ b/riscv/isa_parser.cc @@ -211,6 +211,8 @@ isa_parser_t::isa_parser_t(const char* str, const char *priv) extension_table[EXT_SMEPMP] = true; } else if (ext_str == "smstateen") { extension_table[EXT_SMSTATEEN] = true; + } else if (ext_str == "smrnmi") { + extension_table[EXT_SMRNMI] = true; } else if (ext_str == "sscofpmf") { extension_table[EXT_SSCOFPMF] = true; } else if (ext_str == "svadu") { diff --git a/riscv/isa_parser.h b/riscv/isa_parser.h index a4c0ee5..090a9b5 100644 --- a/riscv/isa_parser.h +++ b/riscv/isa_parser.h @@ -41,6 +41,7 @@ typedef enum { EXT_ZVFHMIN, EXT_SMEPMP, EXT_SMSTATEEN, + EXT_SMRNMI, EXT_SSCOFPMF, EXT_SVADU, EXT_SVNAPOT, diff --git a/riscv/mmu.h b/riscv/mmu.h index df98fe1..ef054cf 100644 --- a/riscv/mmu.h +++ b/riscv/mmu.h @@ -406,6 +406,7 @@ private: inline bool in_mprv() { return proc != nullptr + && !(proc->state.mnstatus && !get_field(proc->state.mnstatus->read(), MNSTATUS_NMIE)) && !proc->state.debug_mode && get_field(proc->state.mstatus->read(), MSTATUS_MPRV); } diff --git a/riscv/processor.cc b/riscv/processor.cc index ad93a42..c05bdac 100644 --- a/riscv/processor.cc +++ b/riscv/processor.cc @@ -494,6 +494,13 @@ void state_t::reset(processor_t* const proc, reg_t max_isa) } } + if (proc->extension_enabled_const(EXT_SMRNMI)) { + csrmap[CSR_MNSCRATCH] = std::make_shared(proc, CSR_MNSCRATCH, 0); + csrmap[CSR_MNEPC] = mnepc = std::make_shared(proc, CSR_MNEPC); + csrmap[CSR_MNCAUSE] = std::make_shared(proc, CSR_MNCAUSE, (reg_t)1 << (xlen - 1)); + csrmap[CSR_MNSTATUS] = mnstatus = std::make_shared(proc, CSR_MNSTATUS); + } + if (proc->extension_enabled_const(EXT_SSTC)) { stimecmp = std::make_shared(proc, CSR_STIMECMP, MIP_STIP); vstimecmp = std::make_shared(proc, CSR_VSTIMECMP, MIP_VSTIP); @@ -660,7 +667,8 @@ void processor_t::take_interrupt(reg_t pending_interrupts) } } - if (!state.debug_mode && enabled_interrupts) { + const bool nmie = !(state.mnstatus && !get_field(state.mnstatus->read(), MNSTATUS_NMIE)); + if (!state.debug_mode && nmie && enabled_interrupts) { // nonstandard interrupts have highest priority if (enabled_interrupts >> (IRQ_M_EXT + 1)) enabled_interrupts = enabled_interrupts >> (IRQ_M_EXT + 1) << (IRQ_M_EXT + 1); @@ -849,8 +857,13 @@ void processor_t::take_trap(trap_t& t, reg_t epc) } else { // Handle the trap in M-mode set_virt(false); - reg_t vector = (state.mtvec->read() & 1) && interrupt ? 4 * bit : 0; - state.pc = (state.mtvec->read() & ~(reg_t)1) + vector; + const reg_t vector = (state.mtvec->read() & 1) && interrupt ? 4 * bit : 0; + const reg_t trap_handler_address = (state.mtvec->read() & ~(reg_t)1) + vector; + // RNMI exception vector is implementation-defined. Since we don't model + // RNMI sources, the feature isn't very useful, so pick an invalid address. + const reg_t rnmi_trap_handler_address = 0; + const bool nmie = !(state.mnstatus && !get_field(state.mnstatus->read(), MNSTATUS_NMIE)); + state.pc = !nmie ? rnmi_trap_handler_address : trap_handler_address; state.mepc->write(epc); state.mcause->write(t.cause()); state.mtval->write(t.get_tval()); diff --git a/riscv/processor.h b/riscv/processor.h index 958c76a..60cfd04 100644 --- a/riscv/processor.h +++ b/riscv/processor.h @@ -99,6 +99,8 @@ struct state_t csr_t_p mideleg; csr_t_p mcounteren; csr_t_p mevent[N_HPMCOUNTERS]; + csr_t_p mnstatus; + csr_t_p mnepc; csr_t_p scounteren; csr_t_p sepc; csr_t_p stval; diff --git a/riscv/riscv.mk.in b/riscv/riscv.mk.in index 8f7ac83..f64711a 100644 --- a/riscv/riscv.mk.in +++ b/riscv/riscv.mk.in @@ -1265,6 +1265,9 @@ riscv_insn_priv = \ sret \ wfi \ +riscv_insn_smrnmi = \ + mnret \ + riscv_insn_svinval = \ sfence_w_inval \ sfence_inval_ir \ @@ -1329,6 +1332,7 @@ riscv_insn_list = \ $(riscv_insn_ext_p) \ $(riscv_insn_priv) \ $(riscv_insn_svinval) \ + $(riscv_insn_smrnmi) \ $(riscv_insn_ext_cmo) \ $(riscv_insn_ext_zicond) \ -- cgit v1.1