diff options
Diffstat (limited to 'riscv/csrs.cc')
-rw-r--r-- | riscv/csrs.cc | 454 |
1 files changed, 409 insertions, 45 deletions
diff --git a/riscv/csrs.cc b/riscv/csrs.cc index 6fdd6a3..3a32712 100644 --- a/riscv/csrs.cc +++ b/riscv/csrs.cc @@ -15,6 +15,8 @@ #include "insn_macros.h" // For CSR_DCSR_V: #include "debug_defines.h" +// For ctz: +#include "arith.h" // STATE macro used by require_privilege() macro: #undef STATE @@ -313,31 +315,31 @@ bool mseccfg_csr_t::get_sseed() const noexcept { } bool mseccfg_csr_t::unlogged_write(const reg_t val) noexcept { - if (proc->n_pmp == 0) - return false; - - // pmpcfg.L is 1 in any rule or entry (including disabled entries) - const bool pmplock_recorded = std::any_of(state->pmpaddr, state->pmpaddr + proc->n_pmp, - [](const pmpaddr_csr_t_p & c) { return c->is_locked(); } ); reg_t new_val = read(); - // When RLB is 0 and pmplock_recorded, RLB is locked to 0. - // Otherwise set the RLB bit according val - if (!(pmplock_recorded && (read() & MSECCFG_RLB) == 0)) { - new_val &= ~MSECCFG_RLB; - new_val |= (val & MSECCFG_RLB); - } + if (proc->n_pmp != 0) { + // pmpcfg.L is 1 in any rule or entry (including disabled entries) + const bool pmplock_recorded = std::any_of(state->pmpaddr, state->pmpaddr + proc->n_pmp, + [](const pmpaddr_csr_t_p & c) { return c->is_locked(); } ); - new_val |= (val & MSECCFG_MMWP); //MMWP is sticky - new_val |= (val & MSECCFG_MML); //MML is sticky + // When RLB is 0 and pmplock_recorded, RLB is locked to 0. + // Otherwise set the RLB bit according val + if (!(pmplock_recorded && (read() & MSECCFG_RLB) == 0)) { + new_val &= ~MSECCFG_RLB; + new_val |= (val & MSECCFG_RLB); + } + + new_val |= (val & MSECCFG_MMWP); //MMWP is sticky + new_val |= (val & MSECCFG_MML); //MML is sticky + + proc->get_mmu()->flush_tlb(); + } if (proc->extension_enabled(EXT_ZKR)) { uint64_t mask = MSECCFG_USEED | MSECCFG_SSEED; new_val = (new_val & ~mask) | (val & mask); } - proc->get_mmu()->flush_tlb(); - if (proc->extension_enabled(EXT_ZICFILP)) { new_val &= ~MSECCFG_MLPE; new_val |= (val & MSECCFG_MLPE); @@ -548,13 +550,16 @@ bool mstatus_csr_t::unlogged_write(const reg_t val) noexcept { | (has_page ? MSTATUS_TVM : 0) | (has_gva ? MSTATUS_GVA : 0) | (has_mpv ? MSTATUS_MPV : 0) + | (proc->extension_enabled(EXT_SMDBLTRP) ? MSTATUS_MDT : 0) | (proc->extension_enabled(EXT_ZICFILP) ? (MSTATUS_SPELP | MSTATUS_MPELP) : 0) | (proc->extension_enabled(EXT_SSDBLTRP) ? SSTATUS_SDT : 0) ; const reg_t requested_mpp = proc->legalize_privilege(get_field(val, MSTATUS_MPP)); const reg_t adjusted_val = set_field(val, MSTATUS_MPP, requested_mpp); - const reg_t new_mstatus = (read() & ~mask) | (adjusted_val & mask); + reg_t new_mstatus = (read() & ~mask) | (adjusted_val & mask); + new_mstatus = (new_mstatus & MSTATUS_MDT) ? (new_mstatus & ~MSTATUS_MIE) : new_mstatus; + new_mstatus = (new_mstatus & MSTATUS_SDT) ? (new_mstatus & ~MSTATUS_SIE) : new_mstatus; maybe_flush_tlb(new_mstatus); this->val = adjust_sd(new_mstatus); return true; @@ -569,6 +574,7 @@ reg_t mstatus_csr_t::compute_mstatus_initial_value() const noexcept { | (proc->extension_enabled_const('U') && (proc->get_const_xlen() != 32) ? set_field((reg_t)0, MSTATUS_UXL, xlen_to_uxl(proc->get_const_xlen())) : 0) | (proc->extension_enabled_const('S') && (proc->get_const_xlen() != 32) ? set_field((reg_t)0, MSTATUS_SXL, xlen_to_uxl(proc->get_const_xlen())) : 0) | (proc->get_mmu()->is_target_big_endian() ? big_endian_bits : 0) + | (proc->extension_enabled(EXT_SMDBLTRP) ? MSTATUS_MDT : 0) | 0; // initial value for mstatus } @@ -581,6 +587,7 @@ 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) + | (proc->extension_enabled(EXT_ZICFILP) ? MNSTATUS_MNPELP : 0) | MNSTATUS_MNPP; const reg_t requested_mnpp = proc->legalize_privilege(get_field(val, MNSTATUS_MNPP)); @@ -634,6 +641,22 @@ reg_t rv32_high_csr_t::written_value() const noexcept { return (orig->written_value() >> 32) & 0xffffffffU; } +aia_rv32_high_csr_t::aia_rv32_high_csr_t(processor_t* const proc, const reg_t addr, csr_t_p orig): + rv32_high_csr_t(proc, addr, orig) { +} + +void aia_rv32_high_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_AIA)) + throw trap_illegal_instruction(insn.bits()); + + if (state->v && !(state->hstateen[0]->read() & HSTATEEN0_AIA)) + throw trap_virtual_instruction(insn.bits()); + } + + rv32_high_csr_t::verify_permissions(insn, write); +} + // implement class sstatus_csr_t sstatus_csr_t::sstatus_csr_t(processor_t* const proc, sstatus_proxy_csr_t_p orig, vsstatus_csr_t_p virt): virtualized_csr_t(proc, orig, virt), @@ -776,8 +799,14 @@ mip_csr_t::mip_csr_t(processor_t* const proc, const reg_t addr): mip_or_mie_csr_t(proc, addr) { } +void mip_csr_t::write_with_mask(const reg_t mask, const reg_t val) noexcept { + if (!(state->mvien->read() & MIP_SEIP) && (mask & MIP_SEIP)) + state->mvip->write_with_mask(MIP_SEIP, val); // mvip.SEIP is an alias of mip.SEIP when mvien.SEIP=0 + mip_or_mie_csr_t::write_with_mask(mask & ~MIP_SEIP, val); +} + reg_t mip_csr_t::read() const noexcept { - return val | state->hvip->basic_csr_t::read(); + return val | state->hvip->basic_csr_t::read() | ((state->mvien->read() & MIP_SEIP) ? 0 : (state->mvip->basic_csr_t::read() & MIP_SEIP)); } void mip_csr_t::backdoor_write_with_mask(const reg_t mask, const reg_t val) noexcept { @@ -859,6 +888,15 @@ mip_proxy_csr_t::mip_proxy_csr_t(processor_t* const proc, const reg_t addr, gene accr(accr) { } +void mip_proxy_csr_t::verify_permissions(insn_t insn, bool write) const { + csr_t::verify_permissions(insn, write); + if (proc->extension_enabled_const(EXT_SSAIA) && proc->extension_enabled('H')) { + if ((state->csrmap[CSR_HVICTL]->read() & HVICTL_VTI) && + proc->extension_enabled('S') && state->v) + throw trap_virtual_instruction(insn.bits()); // VS-mode attempts to access sip when hvictl.VTI=1 + } +} + reg_t mip_proxy_csr_t::read() const noexcept { return accr->ip_read(); } @@ -874,6 +912,15 @@ mie_proxy_csr_t::mie_proxy_csr_t(processor_t* const proc, const reg_t addr, gene accr(accr) { } +void mie_proxy_csr_t::verify_permissions(insn_t insn, bool write) const { + csr_t::verify_permissions(insn, write); + if (proc->extension_enabled_const(EXT_SSAIA) && proc->extension_enabled('H')) { + if ((state->csrmap[CSR_HVICTL]->read() & HVICTL_VTI) && + proc->extension_enabled('S') && state->v) + throw trap_virtual_instruction(insn.bits()); // VS-mode attempts to access sie when hvictl.VTI=1 + } +} + reg_t mie_proxy_csr_t::read() const noexcept { return accr->ie_read(); } @@ -951,6 +998,38 @@ bool medeleg_csr_t::unlogged_write(const reg_t val) noexcept { return basic_csr_t::unlogged_write((read() & ~mask) | (val & mask)); } +sip_csr_t::sip_csr_t(processor_t* const proc, const reg_t addr, generic_int_accessor_t_p accr): + mip_proxy_csr_t(proc, addr, accr) { +} + +reg_t sip_csr_t::read() const noexcept { + const reg_t mask = ~state->mideleg->read() & state->mvien->read(); + return (mip_proxy_csr_t::read() & ~mask) | (state->mvip->read() & mask); +} + +bool sip_csr_t::unlogged_write(const reg_t val) noexcept { + const reg_t mask = ~state->mideleg->read() & state->mvien->read(); + state->mvip->write_with_mask(mask & accr->get_ip_write_mask(), val); + return mip_proxy_csr_t::unlogged_write(val & ~mask); +} + +sie_csr_t::sie_csr_t(processor_t* const proc, const reg_t addr, generic_int_accessor_t_p accr): + mie_proxy_csr_t(proc, addr, accr), + val(0) { +} + +reg_t sie_csr_t::read() const noexcept { + const reg_t mask = ~state->mideleg->read() & state->mvien->read(); + return (mie_proxy_csr_t::read() & ~mask) | (val & mask); +} + +bool sie_csr_t::unlogged_write(const reg_t val) noexcept { + const reg_t mask = ~state->mideleg->read() & state->mvien->read(); + this->val = (this->val & ~mask) | (val & mask); + mie_proxy_csr_t::unlogged_write(val & ~mask); + return true; +} + // implement class masked_csr_t masked_csr_t::masked_csr_t(processor_t* const proc, const reg_t addr, const reg_t mask, const reg_t init): basic_csr_t(proc, addr, init), @@ -1070,6 +1149,7 @@ bool virtualized_satp_csr_t::unlogged_write(const reg_t val) noexcept { wide_counter_csr_t::wide_counter_csr_t(processor_t* const proc, const reg_t addr, smcntrpmf_csr_t_p config_csr): csr_t(proc, addr), val(0), + written(false), config_csr(config_csr) { } @@ -1078,7 +1158,15 @@ reg_t wide_counter_csr_t::read() const noexcept { } void wide_counter_csr_t::bump(const reg_t howmuch) noexcept { - if (is_counting_enabled()) { + if (written) { + // Because writing a CSR serializes the simulator, howmuch should + // reflect exactly one instruction: the explicit CSR write. + // If counting is disabled, though, howmuch will be zero. + assert(howmuch <= 1); + // The ISA mandates that explicit writes to instret take precedence + // over the instret, so simply skip the increment. + written = false; + } else if (is_counting_enabled()) { val += howmuch; // to keep log reasonable size, don't log every bump } // Clear cached value @@ -1086,23 +1174,15 @@ void wide_counter_csr_t::bump(const reg_t howmuch) noexcept { } bool wide_counter_csr_t::unlogged_write(const reg_t val) noexcept { + // Because writing a CSR serializes the simulator and is followed by a + // bump, back-to-back writes with no intervening bump should never occur. + assert(!written); + written = true; + this->val = val; - // The ISA mandates that if an instruction writes instret, the write - // takes precedence over the increment to instret. However, Spike - // unconditionally increments instret after executing an instruction. - // Correct for this artifact by decrementing instret here. - // Ensure that Smctrpmf hasn't disabled counting. - if (is_counting_enabled()) { - this->val--; - } return true; } -reg_t wide_counter_csr_t::written_value() const noexcept { - // Re-adjust for upcoming bump() - return this->val + 1; -} - // Returns true if counting is not inhibited by Smcntrpmf. // Note that minstretcfg / mcyclecfg / mhpmevent* share the same inhibit bits. bool wide_counter_csr_t::is_counting_enabled() const noexcept { @@ -1225,7 +1305,7 @@ hideleg_csr_t::hideleg_csr_t(processor_t* const proc, const reg_t addr, csr_t_p reg_t hideleg_csr_t::read() const noexcept { return masked_csr_t::read() & mideleg->read(); -}; +} hgatp_csr_t::hgatp_csr_t(processor_t* const proc, const reg_t addr): basic_csr_t(proc, addr, 0) { @@ -1340,9 +1420,11 @@ dcsr_csr_t::dcsr_csr_t(processor_t* const proc, const reg_t addr): ebreaku(false), ebreakvs(false), ebreakvu(false), - halt(false), v(false), + mprven(false), cause(0), + ext_cause(0), + cetrig(0), pelp(elp_t::NO_LP_EXPECTED) { } @@ -1354,7 +1436,7 @@ void dcsr_csr_t::verify_permissions(insn_t insn, bool write) const { reg_t dcsr_csr_t::read() const noexcept { reg_t result = 0; - result = set_field(result, DCSR_XDEBUGVER, 1); + result = set_field(result, DCSR_XDEBUGVER, 4); result = set_field(result, DCSR_EBREAKM, ebreakm); result = set_field(result, DCSR_EBREAKS, ebreaks); result = set_field(result, DCSR_EBREAKU, ebreaku); @@ -1363,9 +1445,13 @@ reg_t dcsr_csr_t::read() const noexcept { result = set_field(result, DCSR_STOPCOUNT, 0); result = set_field(result, DCSR_STOPTIME, 0); result = set_field(result, DCSR_CAUSE, cause); + result = set_field(result, DCSR_EXTCAUSE, ext_cause); + if (proc->extension_enabled(EXT_SMDBLTRP)) + result = set_field(result, DCSR_CETRIG, cetrig); result = set_field(result, DCSR_STEP, step); result = set_field(result, DCSR_PRV, prv); result = set_field(result, CSR_DCSR_V, v); + result = set_field(result, DCSR_MPRVEN, mprven); result = set_field(result, DCSR_PELP, pelp); return result; } @@ -1380,14 +1466,17 @@ bool dcsr_csr_t::unlogged_write(const reg_t val) noexcept { ebreakvs = proc->extension_enabled('H') ? get_field(val, CSR_DCSR_EBREAKVS) : false; ebreakvu = proc->extension_enabled('H') ? get_field(val, CSR_DCSR_EBREAKVU) : false; v = proc->extension_enabled('H') ? get_field(val, CSR_DCSR_V) : false; + mprven = get_field(val, CSR_DCSR_MPRVEN); pelp = proc->extension_enabled(EXT_ZICFILP) ? static_cast<elp_t>(get_field(val, DCSR_PELP)) : elp_t::NO_LP_EXPECTED; + cetrig = proc->extension_enabled(EXT_SMDBLTRP) ? get_field(val, DCSR_CETRIG) : false; return true; } -void dcsr_csr_t::update_fields(const uint8_t cause, const reg_t prv, +void dcsr_csr_t::update_fields(const uint8_t cause, uint8_t ext_cause, const reg_t prv, const bool v, const elp_t pelp) noexcept { this->cause = cause; + this->ext_cause = ext_cause; this->prv = prv; this->v = v; this->pelp = pelp; @@ -1400,8 +1489,9 @@ float_csr_t::float_csr_t(processor_t* const proc, const reg_t addr, const reg_t void float_csr_t::verify_permissions(insn_t insn, bool write) const { masked_csr_t::verify_permissions(insn, write); - require_fs; - if (!proc->extension_enabled('F') && !proc->extension_enabled(EXT_ZFINX)) + + if (!((proc->extension_enabled('F') && STATE.sstatus->enabled(SSTATUS_FS)) + || proc->extension_enabled(EXT_ZFINX))) throw trap_illegal_instruction(insn.bits()); if (proc->extension_enabled(EXT_SMSTATEEN) && proc->extension_enabled(EXT_ZFINX)) { @@ -1421,7 +1511,8 @@ void float_csr_t::verify_permissions(insn_t insn, bool write) const { } bool float_csr_t::unlogged_write(const reg_t val) noexcept { - dirty_fp_state; + if (!proc->extension_enabled(EXT_ZFINX)) + dirty_fp_state; return masked_csr_t::unlogged_write(val); } @@ -1631,10 +1722,6 @@ bool stimecmp_csr_t::unlogged_write(const reg_t val) noexcept { return basic_csr_t::unlogged_write(val); } -virtualized_stimecmp_csr_t::virtualized_stimecmp_csr_t(processor_t* const proc, csr_t_p orig, csr_t_p virt): - virtualized_csr_t(proc, orig, virt) { -} - void stimecmp_csr_t::verify_permissions(insn_t insn, bool write) const { if (!(state->menvcfg->read() & MENVCFG_STCE)) { // access to (v)stimecmp with MENVCFG.STCE = 0 @@ -1650,9 +1737,18 @@ void stimecmp_csr_t::verify_permissions(insn_t insn, bool write) const { } basic_csr_t::verify_permissions(insn, write); + + if (proc->extension_enabled_const(EXT_SSAIA) && proc->extension_enabled('H')) { + if ((state->csrmap[CSR_HVICTL]->read() & HVICTL_VTI) && state->v && write) + throw trap_virtual_instruction(insn.bits()); + } +} + +virtualized_with_special_permission_csr_t::virtualized_with_special_permission_csr_t(processor_t* const proc, csr_t_p orig, csr_t_p virt): + virtualized_csr_t(proc, orig, virt) { } -void virtualized_stimecmp_csr_t::verify_permissions(insn_t insn, bool write) const { +void virtualized_with_special_permission_csr_t::verify_permissions(insn_t insn, bool write) const { orig_csr->verify_permissions(insn, write); } @@ -1663,6 +1759,14 @@ 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()); + + if (proc->extension_enabled('H') && + (proc->extension_enabled_const(EXT_SMCDELEG) || proc->extension_enabled(EXT_SSCCFG)) + ) { + if (state->v && (state->menvcfg->read() & MENVCFG_CDE)) { + throw trap_virtual_instruction(insn.bits()); + } + } csr_t::verify_permissions(insn, write); } @@ -1732,10 +1836,74 @@ 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 { + if (proc->extension_enabled(EXT_SMSTATEEN)) { + if ((state->prv < PRV_M) && !(state->mstateen[0]->read() & MSTATEEN0_CSRIND)) + throw trap_illegal_instruction(insn.bits()); + } + // Don't call base verify_permission for VS registers remapped to S-mode if (insn.csr() == address) csr_t::verify_permissions(insn, write); + if (proc->extension_enabled(EXT_SMSTATEEN)) { + if (state->v && !(state->hstateen[0]->read() & HSTATEEN0_CSRIND)) + throw trap_virtual_instruction(insn.bits()); + } + + if (proc->extension_enabled(EXT_SMCDELEG)) { + if (insn.csr() >= CSR_VSIREG && insn.csr() <= CSR_VSIREG6) { + if (!state->v) { + // An attempt to access any vsireg* from M or S mode raises an illegal instruction exception. + throw trap_illegal_instruction(insn.bits()); + } else { + if (state->prv == PRV_S) { + // An attempt from VS-mode to access any vsireg raises an illegal instruction + // exception if menvcfg.CDE = 0, or a virtual instruction exception if menvcfg.CDE = 1 + if ((state->menvcfg->read() & MENVCFG_CDE) != MENVCFG_CDE) { + throw trap_illegal_instruction(insn.bits()); + } else { + throw trap_virtual_instruction(insn.bits()); + } + } else { + throw trap_virtual_instruction(insn.bits()); + } + } + } + if (insn.csr() >= CSR_SIREG && insn.csr() <= CSR_SIREG6) { + // attempts to access any sireg* when menvcfg.CDE = 0; + if ((state->menvcfg->read() & MENVCFG_CDE) != MENVCFG_CDE) { + if (!state->v) { + throw trap_illegal_instruction(insn.bits()); + } else { + if (state->prv == PRV_S) { + // An attempt from VS-mode to access any sireg* causes illegal instruction exception if menvcfg.CDE = 0 + throw trap_illegal_instruction(insn.bits()); + } else { + throw trap_virtual_instruction(insn.bits()); + } + } + } else { + // menvcfg.CDE = 1; + if (state->v) { + // An attempt from VS-mode to access any sireg* causes a virtual instruction exception if menvcfg.CDE = 1 + throw trap_virtual_instruction(insn.bits()); + } + // counter selected by siselect is not delegated to S-mode (the corresponding bit in mcounteren = 0). + auto iselect_addr = iselect->read(); + if (iselect_addr >= SISELECT_SMCDELEG_START && iselect_addr <= SISELECT_SMCDELEG_END) { + reg_t counter_id_offset = iselect_addr - SISELECT_SMCDELEG_START; + if (!(state->mcounteren->read() & (1U << counter_id_offset))) { + if (!state->v) { + throw trap_illegal_instruction(insn.bits()); + } else { + throw trap_virtual_instruction(insn.bits()); + } + } + } + } + } + } + csr_t_p proxy_csr = get_reg(); if (proxy_csr == nullptr) { if (!state->v) { @@ -1797,7 +1965,7 @@ srmcfg_csr_t::srmcfg_csr_t(processor_t* const proc, const reg_t addr, const reg_ masked_csr_t(proc, addr, mask, init) { } -void srmcfg_csr_t::verify_permissions(insn_t insn, bool write) const { +void srmcfg_csr_t::verify_permissions(insn_t insn, bool write UNUSED) const { if (!proc->extension_enabled(EXT_SSQOSID)) throw trap_illegal_instruction(insn.bits()); @@ -1866,3 +2034,199 @@ bool hstatus_csr_t::unlogged_write(const reg_t val) noexcept { proc->get_mmu()->flush_tlb(); return basic_csr_t::unlogged_write(new_hstatus); } + +scntinhibit_csr_t::scntinhibit_csr_t(processor_t* const proc, const reg_t addr, csr_t_p mcountinhibit): + basic_csr_t(proc, addr, mcountinhibit->read()) { +} + +void scntinhibit_csr_t::verify_permissions(insn_t insn, bool write) const { + if (insn.csr() == address) { + csr_t::verify_permissions(insn, write); + } + + if ((state->menvcfg->read() & MENVCFG_CDE) != MENVCFG_CDE) { + throw trap_illegal_instruction(insn.bits()); + } +} + +bool scntinhibit_csr_t::unlogged_write(const reg_t val) noexcept { + state->mcountinhibit->write(state->mcounteren->read() & val); + return true; +} + +reg_t scntinhibit_csr_t::read() const noexcept { + return state->mcounteren->read() & state->mcountinhibit->read(); +} + +mtopi_csr_t::mtopi_csr_t(processor_t* const proc, const reg_t addr): + csr_t(proc, addr) { +} + +reg_t mtopi_csr_t::read() const noexcept { + reg_t enabled_interrupts = state->mip->read() & state->mie->read() & ~state->mideleg->read(); + if (!enabled_interrupts) + return 0; // no enabled pending interrupt to M-mode + + reg_t selected_interrupt = proc->select_an_interrupt_with_default_priority(enabled_interrupts); + reg_t identity = ctz(selected_interrupt); + return set_field((reg_t)1, MTOPI_IID, identity); // IPRIO always 1 if iprio array is RO0 +} + +bool mtopi_csr_t::unlogged_write(const reg_t UNUSED val) noexcept { + return false; +} + +mvip_csr_t::mvip_csr_t(processor_t* const proc, const reg_t addr, const reg_t init): + basic_csr_t(proc, addr, init) { +} + +reg_t mvip_csr_t::read() const noexcept { + const reg_t val = basic_csr_t::read(); + const reg_t mvien = state->mvien->read(); + const reg_t mip = state->mip->read(); + const reg_t menvcfg = state->menvcfg->read(); + return 0 + | (val & MIP_SEIP) + | ((menvcfg & MENVCFG_STCE) ? 0 : (mip & MIP_STIP)) + | (((mvien & MIP_SSIP) ? val : mip) & MIP_SSIP) + ; +} + +bool mvip_csr_t::unlogged_write(const reg_t val) noexcept { + if (!(state->menvcfg->read() & MENVCFG_STCE)) + state->mip->write_with_mask(MIP_STIP, val); // mvip.STIP is an alias of mip.STIP when mip.STIP is writable + if (!(state->mvien->read() & MIP_SSIP)) + state->mip->write_with_mask(MIP_SSIP, val); // mvip.SSIP is an alias of mip.SSIP when mvien.SSIP=0 + + const reg_t new_val = (val & MIP_SEIP) | (((state->mvien->read() & MIP_SSIP) ? val : basic_csr_t::read()) & MIP_SSIP); + return basic_csr_t::unlogged_write(new_val); +} + +void mvip_csr_t::write_with_mask(const reg_t mask, const reg_t val) noexcept { + basic_csr_t::unlogged_write((basic_csr_t::read() & ~mask) | (val & mask)); + log_write(); +} + +nonvirtual_stopi_csr_t::nonvirtual_stopi_csr_t(processor_t* const proc, const reg_t addr): + csr_t(proc, addr) { +} + +void nonvirtual_stopi_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_AIA)) + throw trap_illegal_instruction(insn.bits()); + + if (state->v && !(state->hstateen[0]->read() & HSTATEEN0_AIA)) + throw trap_virtual_instruction(insn.bits()); + } + + csr_t::verify_permissions(insn, write); +} + +reg_t nonvirtual_stopi_csr_t::read() const noexcept { + reg_t enabled_interrupts = state->nonvirtual_sip->read() & state->nonvirtual_sie->read() & ~state->hideleg->read(); + if (!enabled_interrupts) + return 0; // no enabled pending interrupt to S-mode + + reg_t selected_interrupt = proc->select_an_interrupt_with_default_priority(enabled_interrupts); + reg_t identity = ctz(selected_interrupt); + return set_field((reg_t)1, MTOPI_IID, identity); // IPRIO always 1 if iprio array is RO0 +} + +bool nonvirtual_stopi_csr_t::unlogged_write(const reg_t UNUSED val) noexcept { + return false; +} + +inaccessible_csr_t::inaccessible_csr_t(processor_t* const proc, const reg_t addr): + csr_t(proc, addr) { +} + +void inaccessible_csr_t::verify_permissions(insn_t insn, bool write) const { + if (state->v) + throw trap_virtual_instruction(insn.bits()); + else + throw trap_illegal_instruction(insn.bits()); +} + +vstopi_csr_t::vstopi_csr_t(processor_t* const proc, const reg_t addr): + csr_t(proc, addr) { +} + +void vstopi_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_AIA)) + throw trap_illegal_instruction(insn.bits()); + + if (state->v && !(state->hstateen[0]->read() & HSTATEEN0_AIA)) + throw trap_virtual_instruction(insn.bits()); + } + + csr_t::verify_permissions(insn, write); +} + +reg_t vstopi_csr_t::read() const noexcept { + reg_t hvictl = state->hvictl->read(); + bool vti = hvictl & HVICTL_VTI; + reg_t iid = get_field(hvictl, HVICTL_IID); + bool dpr = hvictl & HVICTL_DPR; + bool ipriom = hvictl & HVICTL_IPRIOM; + reg_t iprio = get_field(hvictl, HVICTL_IPRIO); + + reg_t enabled_interrupts = state->mip->read() & state->mie->read() & state->hideleg->read(); + enabled_interrupts >>= 1; // VSSIP -> SSIP, etc + reg_t vgein = get_field(state->hstatus->read(), HSTATUS_VGEIN); + reg_t virtual_sei_priority = (vgein == 0 && iid == IRQ_S_EXT && iprio != 0) ? iprio : 255; // vstopi.IPRIO is 255 for priority number 256 + + reg_t identity, priority; + if (vti) { + if (!(enabled_interrupts & MIP_SEIP) && iid == IRQ_S_EXT) + return 0; + + identity = ((enabled_interrupts & MIP_SEIP) && (iid == IRQ_S_EXT || dpr)) ? IRQ_S_EXT : iid; + priority = (identity == IRQ_S_EXT) ? virtual_sei_priority : ((iprio != 0 || !dpr) ? iprio : 255); + } else { + if (!enabled_interrupts) + return 0; // no enabled pending interrupt to VS-mode + + reg_t selected_interrupt = proc->select_an_interrupt_with_default_priority(enabled_interrupts); + identity = ctz(selected_interrupt); + priority = (identity == IRQ_S_EXT) ? virtual_sei_priority : 255; // vstopi.IPRIO is 255 for interrupt with default priority lower than VSEI + } + return set_field((reg_t)(ipriom ? priority : 1), MTOPI_IID, identity); +} + +bool vstopi_csr_t::unlogged_write(const reg_t UNUSED val) noexcept { + return false; +} + +siselect_csr_t::siselect_csr_t(processor_t* const proc, const reg_t addr, const reg_t init): + basic_csr_t(proc, addr, init) { +} + +void siselect_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()); + } + + basic_csr_t::verify_permissions(insn, write); +} + +aia_csr_t::aia_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 aia_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_AIA)) + throw trap_illegal_instruction(insn.bits()); + + if (state->v && !(state->hstateen[0]->read() & HSTATEEN0_AIA)) + throw trap_virtual_instruction(insn.bits()); + } + + basic_csr_t::verify_permissions(insn, write); +} |