diff options
Diffstat (limited to 'riscv/csrs.cc')
-rw-r--r-- | riscv/csrs.cc | 519 |
1 files changed, 393 insertions, 126 deletions
diff --git a/riscv/csrs.cc b/riscv/csrs.cc index 98edacf..93b0bae 100644 --- a/riscv/csrs.cc +++ b/riscv/csrs.cc @@ -1,5 +1,8 @@ // See LICENSE for license details. +// For std::any_of +#include <algorithm> + #include "csrs.h" // For processor_t: #include "processor.h" @@ -15,7 +18,6 @@ #undef STATE #define STATE (*state) - // implement class csr_t csr_t::csr_t(processor_t* const proc, const reg_t addr): proc(proc), @@ -44,7 +46,6 @@ void csr_t::verify_permissions(insn_t insn, bool write) const { } } - csr_t::~csr_t() { } @@ -59,7 +60,7 @@ void csr_t::log_write() const noexcept { log_special_write(address, written_value()); } -void csr_t::log_special_write(const reg_t address, const reg_t val) const noexcept { +void csr_t::log_special_write(const reg_t UNUSED address, const reg_t UNUSED val) const noexcept { #if defined(RISCV_ENABLE_COMMITLOG) proc->get_state()->log_reg_write[((address) << 4) | 4] = {val, 0}; #endif @@ -80,7 +81,6 @@ bool basic_csr_t::unlogged_write(const reg_t val) noexcept { return true; } - // implement class pmpaddr_csr_t pmpaddr_csr_t::pmpaddr_csr_t(processor_t* const proc, const reg_t addr): csr_t(proc, addr), @@ -89,7 +89,6 @@ pmpaddr_csr_t::pmpaddr_csr_t(processor_t* const proc, const reg_t addr): pmpidx(address - CSR_PMPADDR0) { } - void pmpaddr_csr_t::verify_permissions(insn_t insn, bool write) const { csr_t::verify_permissions(insn, write); // If n_pmp is zero, that means pmp is not implemented hence raise @@ -100,14 +99,12 @@ void pmpaddr_csr_t::verify_permissions(insn_t insn, bool write) const { throw trap_illegal_instruction(insn.bits()); } - reg_t pmpaddr_csr_t::read() const noexcept { if ((cfg & PMP_A) >= PMP_NAPOT) return val | (~proc->pmp_tor_mask() >> 1); return val & proc->pmp_tor_mask(); } - bool pmpaddr_csr_t::unlogged_write(const reg_t val) noexcept { // If no PMPs are configured, disallow access to all. Otherwise, // allow access to all, but unimplemented ones are hardwired to @@ -117,7 +114,9 @@ bool pmpaddr_csr_t::unlogged_write(const reg_t val) noexcept { if (proc->n_pmp == 0) return false; - bool locked = cfg & PMP_L; + const bool lock_bypass = state->mseccfg->get_rlb(); + const bool locked = !lock_bypass && (cfg & PMP_L); + if (pmpidx < proc->n_pmp && !locked && !next_locked_and_tor()) { this->val = val & ((reg_t(1) << (MAX_PADDR_BITS - PMP_SHIFT)) - 1); } @@ -129,30 +128,27 @@ bool pmpaddr_csr_t::unlogged_write(const reg_t val) noexcept { bool pmpaddr_csr_t::next_locked_and_tor() const noexcept { if (pmpidx+1 >= state->max_pmp) return false; // this is the last entry - bool next_locked = state->pmpaddr[pmpidx+1]->cfg & PMP_L; - bool next_tor = (state->pmpaddr[pmpidx+1]->cfg & PMP_A) == PMP_TOR; + const bool lock_bypass = state->mseccfg->get_rlb(); + const bool next_locked = !lock_bypass && (state->pmpaddr[pmpidx+1]->cfg & PMP_L); + const bool next_tor = (state->pmpaddr[pmpidx+1]->cfg & PMP_A) == PMP_TOR; return next_locked && next_tor; } - reg_t pmpaddr_csr_t::tor_paddr() const noexcept { return (val & proc->pmp_tor_mask()) << PMP_SHIFT; } - reg_t pmpaddr_csr_t::tor_base_paddr() const noexcept { if (pmpidx == 0) return 0; // entry 0 always uses 0 as base return state->pmpaddr[pmpidx-1]->tor_paddr(); } - reg_t pmpaddr_csr_t::napot_mask() const noexcept { bool is_na4 = (cfg & PMP_A) == PMP_NA4; reg_t mask = (val << 1) | (!is_na4) | ~proc->pmp_tor_mask(); return ~(mask & ~(mask + 1)) << PMP_SHIFT; } - bool pmpaddr_csr_t::match4(reg_t addr) const noexcept { if ((cfg & PMP_A) == 0) return false; bool is_tor = (cfg & PMP_A) == PMP_TOR; @@ -161,7 +157,6 @@ bool pmpaddr_csr_t::match4(reg_t addr) const noexcept { return ((addr ^ tor_paddr()) & napot_mask()) == 0; } - bool pmpaddr_csr_t::subset_match(reg_t addr, reg_t len) const noexcept { if ((addr | len) & (len - 1)) abort(); @@ -184,21 +179,55 @@ bool pmpaddr_csr_t::subset_match(reg_t addr, reg_t len) const noexcept { return !(is_tor ? tor_homogeneous : napot_homogeneous); } - bool pmpaddr_csr_t::access_ok(access_type type, reg_t mode) const noexcept { - return - (mode == PRV_M && !(cfg & PMP_L)) || - (type == LOAD && (cfg & PMP_R)) || - (type == STORE && (cfg & PMP_W)) || - (type == FETCH && (cfg & PMP_X)); + const bool cfgx = cfg & PMP_X; + const bool cfgw = cfg & PMP_W; + const bool cfgr = cfg & PMP_R; + const bool cfgl = cfg & PMP_L; + + const bool prvm = mode == PRV_M; + + const bool typer = type == LOAD; + const bool typex = type == FETCH; + const bool typew = type == STORE; + const bool normal_rwx = (typer && cfgr) || (typew && cfgw) || (typex && cfgx); + const bool mseccfg_mml = state->mseccfg->get_mml(); + + if (mseccfg_mml) { + if (cfgx && cfgw && cfgr && cfgl) { + // Locked Shared data region: Read only on both M and S/U mode. + return typer; + } else { + const bool mml_shared_region = !cfgr && cfgw; + const bool mml_chk_normal = (prvm == cfgl) && normal_rwx; + const bool mml_chk_shared = + (!cfgl && cfgx && (typer || typew)) || + (!cfgl && !cfgx && (typer || (typew && prvm))) || + (cfgl && typex) || + (cfgl && typer && cfgx && prvm); + return mml_shared_region ? mml_chk_shared : mml_chk_normal; + } + } else { + const bool m_bypass = (prvm && !cfgl); + return m_bypass || normal_rwx; + } } - // implement class pmpcfg_csr_t pmpcfg_csr_t::pmpcfg_csr_t(processor_t* const proc, const reg_t addr): csr_t(proc, addr) { } +void pmpcfg_csr_t::verify_permissions(insn_t insn, bool write) const { + csr_t::verify_permissions(insn, write); + // If n_pmp is zero, that means pmp is not implemented hence raise + // trap if it tries to access the csr. I would prefer to implement + // this by not instantiating any pmpcfg_csr_t for these regs, but + // n_pmp can change after reset() is run. + if (proc->n_pmp == 0) + throw trap_illegal_instruction(insn.bits()); +} + reg_t pmpcfg_csr_t::read() const noexcept { reg_t cfg_res = 0; for (size_t i0 = (address - CSR_PMPCFG0) * 4, i = i0; i < i0 + proc->get_xlen() / 8 && i < state->max_pmp; i++) @@ -211,14 +240,35 @@ bool pmpcfg_csr_t::unlogged_write(const reg_t val) noexcept { return false; bool write_success = false; + const bool rlb = state->mseccfg->get_rlb(); + const bool mml = state->mseccfg->get_mml(); for (size_t i0 = (address - CSR_PMPCFG0) * 4, i = i0; i < i0 + proc->get_xlen() / 8; i++) { if (i < proc->n_pmp) { - if (!(state->pmpaddr[i]->cfg & PMP_L)) { + const bool locked = (state->pmpaddr[i]->cfg & PMP_L); + if (rlb || !locked) { uint8_t cfg = (val >> (8 * (i - i0))) & (PMP_R | PMP_W | PMP_X | PMP_A | PMP_L); - cfg &= ~PMP_W | ((cfg & PMP_R) ? PMP_W : 0); // Disallow R=0 W=1 + // Drop R=0 W=1 when MML = 0 + // Remove the restriction when MML = 1 + if (!mml) { + cfg &= ~PMP_W | ((cfg & PMP_R) ? PMP_W : 0); + } + // Disallow A=NA4 when granularity > 4 if (proc->lg_pmp_granularity != PMP_SHIFT && (cfg & PMP_A) == PMP_NA4) - cfg |= PMP_NAPOT; // Disallow A=NA4 when granularity > 4 - state->pmpaddr[i]->cfg = cfg; + cfg |= PMP_NAPOT; + /* + * Adding a rule with executable privileges that either is M-mode-only or a locked Shared-Region + * is not possible and such pmpcfg writes are ignored, leaving pmpcfg unchanged. + * This restriction can be temporarily lifted e.g. during the boot process, by setting mseccfg.RLB. + */ + const bool cfgx = cfg & PMP_X; + const bool cfgw = cfg & PMP_W; + const bool cfgr = cfg & PMP_R; + if (rlb || !(mml && ((cfg & PMP_L) // M-mode-only or a locked Shared-Region + && !(cfgx && cfgw && cfgr) // RWX = 111 is allowed + && (cfgx || (cfgw && !cfgr)) // X=1 or RW=01 is not allowed + ))) { + state->pmpaddr[i]->cfg = cfg; + } } write_success = true; } @@ -227,6 +277,52 @@ bool pmpcfg_csr_t::unlogged_write(const reg_t val) noexcept { return write_success; } +// implement class mseccfg_csr_t +mseccfg_csr_t::mseccfg_csr_t(processor_t* const proc, const reg_t addr): + basic_csr_t(proc, addr, 0) { +} + +void mseccfg_csr_t::verify_permissions(insn_t insn, bool write) const { + basic_csr_t::verify_permissions(insn, write); + if (!proc->extension_enabled(EXT_SMEPMP)) + throw trap_illegal_instruction(insn.bits()); +} + +bool mseccfg_csr_t::get_mml() const noexcept { + return (read() & MSECCFG_MML); +} + +bool mseccfg_csr_t::get_mmwp() const noexcept { + return (read() & MSECCFG_MMWP); +} + +bool mseccfg_csr_t::get_rlb() const noexcept { + return (read() & MSECCFG_RLB); +} + +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); + } + + new_val |= (val & MSECCFG_MMWP); //MMWP is sticky + new_val |= (val & MSECCFG_MML); //MML is sticky + + proc->get_mmu()->flush_tlb(); + + return basic_csr_t::unlogged_write(new_val); +} // implement class virtualized_csr_t virtualized_csr_t::virtualized_csr_t(processor_t* const proc, csr_t_p orig, csr_t_p virt): @@ -235,7 +331,6 @@ virtualized_csr_t::virtualized_csr_t(processor_t* const proc, csr_t_p orig, csr_ virt_csr(virt) { } - reg_t virtualized_csr_t::read() const noexcept { return readvirt(state->v); } @@ -252,49 +347,41 @@ bool virtualized_csr_t::unlogged_write(const reg_t val) noexcept { return false; // virt_csr or orig_csr has already logged } - // implement class epc_csr_t epc_csr_t::epc_csr_t(processor_t* const proc, const reg_t addr): csr_t(proc, addr), val(0) { } - reg_t epc_csr_t::read() const noexcept { return val & proc->pc_alignment_mask(); } - bool epc_csr_t::unlogged_write(const reg_t val) noexcept { this->val = val & ~(reg_t)1; return true; } - // implement class tvec_csr_t tvec_csr_t::tvec_csr_t(processor_t* const proc, const reg_t addr): csr_t(proc, addr), val(0) { } - reg_t tvec_csr_t::read() const noexcept { return val; } - bool tvec_csr_t::unlogged_write(const reg_t val) noexcept { this->val = val & ~(reg_t)2; return true; } - // implement class cause_csr_t cause_csr_t::cause_csr_t(processor_t* const proc, const reg_t addr): basic_csr_t(proc, addr, 0) { } - reg_t cause_csr_t::read() const noexcept { reg_t val = basic_csr_t::read(); // When reading, the interrupt bit needs to adjust to xlen. Spike does @@ -305,7 +392,6 @@ reg_t cause_csr_t::read() const noexcept { return val; } - // implement class base_status_csr_t base_status_csr_t::base_status_csr_t(processor_t* const proc, const reg_t addr): csr_t(proc, addr), @@ -315,12 +401,11 @@ base_status_csr_t::base_status_csr_t(processor_t* const proc, const reg_t addr): | (proc->get_const_xlen() == 32 ? SSTATUS32_SD : SSTATUS64_SD)) { } - reg_t base_status_csr_t::compute_sstatus_write_mask() const noexcept { // If a configuration has FS bits, they will always be accessible no // matter the state of misa. - const bool has_fs = proc->extension_enabled('S') || proc->extension_enabled('F') - || proc->extension_enabled('V'); + const bool has_fs = (proc->extension_enabled('S') || proc->extension_enabled('F') + || proc->extension_enabled('V')) && !proc->extension_enabled(EXT_ZFINX); const bool has_vs = proc->extension_enabled('V'); return 0 | (proc->extension_enabled('S') ? (SSTATUS_SIE | SSTATUS_SPIE | SSTATUS_SPP) : 0) @@ -331,7 +416,6 @@ reg_t base_status_csr_t::compute_sstatus_write_mask() const noexcept { ; } - reg_t base_status_csr_t::adjust_sd(const reg_t val) const noexcept { // This uses get_const_xlen() instead of get_xlen() not only because // the variable is static, so it's only called once, but also @@ -347,7 +431,6 @@ reg_t base_status_csr_t::adjust_sd(const reg_t val) const noexcept { return val & ~sd_bit; } - void base_status_csr_t::maybe_flush_tlb(const reg_t newval) noexcept { if ((newval ^ read()) & (MSTATUS_MPP | MSTATUS_MPRV @@ -356,7 +439,6 @@ void base_status_csr_t::maybe_flush_tlb(const reg_t newval) noexcept { proc->get_mmu()->flush_tlb(); } - namespace { int xlen_to_uxl(int xlen) { if (xlen == 32) @@ -367,7 +449,6 @@ namespace { } } - // implement class vsstatus_csr_t vsstatus_csr_t::vsstatus_csr_t(processor_t* const proc, const reg_t addr): base_status_csr_t(proc, addr), @@ -381,7 +462,6 @@ bool vsstatus_csr_t::unlogged_write(const reg_t val) noexcept { return true; } - // implement class sstatus_proxy_csr_t sstatus_proxy_csr_t::sstatus_proxy_csr_t(processor_t* const proc, const reg_t addr, mstatus_csr_t_p mstatus): base_status_csr_t(proc, addr), @@ -391,32 +471,27 @@ sstatus_proxy_csr_t::sstatus_proxy_csr_t(processor_t* const proc, const reg_t ad bool sstatus_proxy_csr_t::unlogged_write(const reg_t val) noexcept { const reg_t new_mstatus = (mstatus->read() & ~sstatus_write_mask) | (val & sstatus_write_mask); + // On RV32 this will only log the low 32 bits, so make sure we're + // not modifying anything in the upper 32 bits. + assert((sstatus_write_mask & 0xffffffffU) == sstatus_write_mask); + mstatus->write(new_mstatus); return false; // avoid double logging: already logged by mstatus->write() } - // implement class mstatus_csr_t mstatus_csr_t::mstatus_csr_t(processor_t* const proc, const reg_t addr): base_status_csr_t(proc, addr), - val(0 - | (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) - -#ifdef RISCV_ENABLE_DUAL_ENDIAN - | (proc->get_mmu()->is_target_big_endian() ? MSTATUS_UBE | MSTATUS_SBE | MSTATUS_MBE : 0) -#endif - | 0 // initial value for mstatus - ) { + val(compute_mstatus_initial_value()) { } - bool mstatus_csr_t::unlogged_write(const reg_t val) noexcept { - const bool has_mpv = proc->extension_enabled('S') && proc->extension_enabled('H'); + const bool has_mpv = proc->extension_enabled('H'); const bool has_gva = has_mpv; const reg_t mask = sstatus_write_mask - | MSTATUS_MIE | MSTATUS_MPIE | MSTATUS_MPRV + | MSTATUS_MIE | MSTATUS_MPIE + | (proc->extension_enabled('U') ? MSTATUS_MPRV : 0) | MSTATUS_MPP | MSTATUS_TW | (proc->extension_enabled('S') ? MSTATUS_TSR : 0) | (has_page ? MSTATUS_TVM : 0) @@ -431,19 +506,59 @@ bool mstatus_csr_t::unlogged_write(const reg_t val) noexcept { return true; } -// implement class mstatush_csr_t -mstatush_csr_t::mstatush_csr_t(processor_t* const proc, const reg_t addr, mstatus_csr_t_p mstatus): +reg_t mstatus_csr_t::compute_mstatus_initial_value() const noexcept { + const reg_t big_endian_bits = (proc->extension_enabled_const('U') ? MSTATUS_UBE : 0) + | (proc->extension_enabled_const('S') ? MSTATUS_SBE : 0) + | MSTATUS_MBE; + return 0 + | (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) + | 0; // initial value for mstatus +} + +// 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), + orig(orig) { +} + +reg_t rv32_low_csr_t::read() const noexcept { + return orig->read() & 0xffffffffU; +} + +void rv32_low_csr_t::verify_permissions(insn_t insn, bool write) const { + orig->verify_permissions(insn, write); +} + +bool rv32_low_csr_t::unlogged_write(const reg_t val) noexcept { + return orig->unlogged_write((orig->written_value() >> 32 << 32) | (val & 0xffffffffU)); +} + +reg_t rv32_low_csr_t::written_value() const noexcept { + return orig->written_value() & 0xffffffffU; +} + +// implement class rv32_high_csr_t +rv32_high_csr_t::rv32_high_csr_t(processor_t* const proc, const reg_t addr, csr_t_p orig): csr_t(proc, addr), - mstatus(mstatus), - mask(MSTATUSH_MPV | MSTATUSH_GVA | MSTATUSH_SBE | MSTATUSH_MBE) { + orig(orig) { +} + +reg_t rv32_high_csr_t::read() const noexcept { + return (orig->read() >> 32) & 0xffffffffU; } -reg_t mstatush_csr_t::read() const noexcept { - return (mstatus->read() >> 32) & mask; +void rv32_high_csr_t::verify_permissions(insn_t insn, bool write) const { + orig->verify_permissions(insn, write); } -bool mstatush_csr_t::unlogged_write(const reg_t val) noexcept { - return mstatus->unlogged_write((mstatus->written_value() & ~(mask << 32)) | ((val & mask) << 32)); +bool rv32_high_csr_t::unlogged_write(const reg_t val) noexcept { + return orig->unlogged_write((orig->written_value() << 32 >> 32) | ((val & 0xffffffffU) << 32)); +} + +reg_t rv32_high_csr_t::written_value() const noexcept { + return (orig->written_value() >> 32) & 0xffffffffU; } // implement class sstatus_csr_t @@ -483,7 +598,6 @@ bool sstatus_csr_t::enabled(const reg_t which) { return false; } - // implement class misa_csr_t misa_csr_t::misa_csr_t(processor_t* const proc, const reg_t addr, const reg_t max_isa): basic_csr_t(proc, addr, max_isa), @@ -501,7 +615,7 @@ misa_csr_t::misa_csr_t(processor_t* const proc, const reg_t addr, const reg_t ma ) { } -const reg_t misa_csr_t::dependency(const reg_t val, const char feature, const char depends_on) const noexcept { +reg_t misa_csr_t::dependency(const reg_t val, const char feature, const char depends_on) const noexcept { return (val & (1L << (depends_on - 'A'))) ? val : (val & ~(1L << (feature - 'A'))); } @@ -530,7 +644,9 @@ bool misa_csr_t::unlogged_write(const reg_t val) noexcept { | (1 << CAUSE_STORE_GUEST_PAGE_FAULT) ; state->medeleg->write(state->medeleg->read() & ~hypervisor_exceptions); - state->mstatus->write(state->mstatus->read() & ~(MSTATUS_GVA | MSTATUS_MPV)); + 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 state->mie->write_with_mask(MIP_HS_MASK, 0); // also takes care of hie, sie state->mip->write_with_mask(MIP_HS_MASK, 0); // also takes care of hip, sip, hvip state->hstatus->write(0); @@ -544,7 +660,6 @@ bool misa_csr_t::extension_enabled_const(unsigned char ext) const noexcept { return extension_enabled(ext); } - // implement class mip_or_mie_csr_t mip_or_mie_csr_t::mip_or_mie_csr_t(processor_t* const proc, const reg_t addr): csr_t(proc, addr), @@ -565,7 +680,6 @@ bool mip_or_mie_csr_t::unlogged_write(const reg_t val) noexcept { return false; // avoid double logging: already logged by write_with_mask() } - mip_csr_t::mip_csr_t(processor_t* const proc, const reg_t addr): mip_or_mie_csr_t(proc, addr) { } @@ -575,7 +689,9 @@ void mip_csr_t::backdoor_write_with_mask(const reg_t mask, const reg_t val) noex } reg_t mip_csr_t::write_mask() const noexcept { - const reg_t supervisor_ints = proc->extension_enabled('S') ? MIP_SSIP | MIP_STIP | MIP_SEIP : 0; + // MIP_STIP is writable unless SSTC exists and STCE is set in MENVCFG + const reg_t supervisor_ints = proc->extension_enabled('S') ? MIP_SSIP | ((state->menvcfg->read() & MENVCFG_STCE) ? 0 : MIP_STIP) | MIP_SEIP : 0; + const reg_t lscof_int = proc->extension_enabled(EXT_SSCOFPMF) ? MIP_LCOFIP : 0; const reg_t vssip_int = proc->extension_enabled('H') ? MIP_VSSIP : 0; const reg_t hypervisor_ints = proc->extension_enabled('H') ? MIP_HS_MASK : 0; // We must mask off sgeip, vstip, and vseip. All three of these @@ -583,26 +699,24 @@ reg_t mip_csr_t::write_mask() const noexcept { // * sgeip is read-only -- write hgeip instead // * vseip is read-only -- write hvip instead // * vstip is read-only -- write hvip instead - return (supervisor_ints | hypervisor_ints) & - (MIP_SEIP | MIP_SSIP | MIP_STIP | vssip_int); + return (supervisor_ints | hypervisor_ints | lscof_int) & + (MIP_SEIP | MIP_SSIP | MIP_STIP | MIP_LCOFIP | vssip_int); } - mie_csr_t::mie_csr_t(processor_t* const proc, const reg_t addr): mip_or_mie_csr_t(proc, addr) { } - reg_t mie_csr_t::write_mask() const noexcept { const reg_t supervisor_ints = proc->extension_enabled('S') ? MIP_SSIP | MIP_STIP | MIP_SEIP : 0; + const reg_t lscof_int = proc->extension_enabled(EXT_SSCOFPMF) ? MIP_LCOFIP : 0; const reg_t hypervisor_ints = proc->extension_enabled('H') ? MIP_HS_MASK : 0; const reg_t coprocessor_ints = (reg_t)proc->any_custom_extensions() << IRQ_COP; - const reg_t delegable_ints = supervisor_ints | coprocessor_ints; + const reg_t delegable_ints = supervisor_ints | coprocessor_ints | lscof_int; const reg_t all_ints = delegable_ints | hypervisor_ints | MIP_MSIP | MIP_MTIP | MIP_MEIP; return all_ints; } - // implement class generic_int_accessor_t generic_int_accessor_t::generic_int_accessor_t(state_t* const state, const reg_t read_mask, @@ -643,7 +757,6 @@ reg_t generic_int_accessor_t::deleg_mask() const { return hideleg_mask & mideleg_mask; } - // implement class mip_proxy_csr_t mip_proxy_csr_t::mip_proxy_csr_t(processor_t* const proc, const reg_t addr, generic_int_accessor_t_p accr): csr_t(proc, addr), @@ -674,7 +787,6 @@ bool mie_proxy_csr_t::unlogged_write(const reg_t val) noexcept { return false; // accr has already logged } - // implement class mideleg_csr_t mideleg_csr_t::mideleg_csr_t(processor_t* const proc, const reg_t addr): basic_csr_t(proc, addr, 0) { @@ -696,13 +808,13 @@ void mideleg_csr_t::verify_permissions(insn_t insn, bool write) const { bool mideleg_csr_t::unlogged_write(const reg_t val) noexcept { const reg_t supervisor_ints = proc->extension_enabled('S') ? MIP_SSIP | MIP_STIP | MIP_SEIP : 0; + const reg_t lscof_int = proc->extension_enabled(EXT_SSCOFPMF) ? MIP_LCOFIP : 0; const reg_t coprocessor_ints = (reg_t)proc->any_custom_extensions() << IRQ_COP; - const reg_t delegable_ints = supervisor_ints | coprocessor_ints; + const reg_t delegable_ints = supervisor_ints | coprocessor_ints | lscof_int; return basic_csr_t::unlogged_write(val & delegable_ints); } - // implement class medeleg_csr_t medeleg_csr_t::medeleg_csr_t(processor_t* const proc, const reg_t addr): basic_csr_t(proc, addr, 0), @@ -724,7 +836,13 @@ void medeleg_csr_t::verify_permissions(insn_t insn, bool write) const { bool medeleg_csr_t::unlogged_write(const reg_t val) noexcept { const reg_t mask = 0 | (1 << CAUSE_MISALIGNED_FETCH) + | (1 << CAUSE_FETCH_ACCESS) + | (1 << CAUSE_ILLEGAL_INSTRUCTION) | (1 << CAUSE_BREAKPOINT) + | (1 << CAUSE_MISALIGNED_LOAD) + | (1 << CAUSE_LOAD_ACCESS) + | (1 << CAUSE_MISALIGNED_STORE) + | (1 << CAUSE_STORE_ACCESS) | (1 << CAUSE_USER_ECALL) | (1 << CAUSE_SUPERVISOR_ECALL) | (1 << CAUSE_FETCH_PAGE_FAULT) @@ -735,7 +853,6 @@ bool medeleg_csr_t::unlogged_write(const reg_t val) noexcept { return basic_csr_t::unlogged_write((read() & ~mask) | (val & mask)); } - // 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), @@ -746,13 +863,17 @@ bool masked_csr_t::unlogged_write(const reg_t val) noexcept { return basic_csr_t::unlogged_write((read() & ~mask) | (val & mask)); } +// implement class henvcfg_csr_t +henvcfg_csr_t::henvcfg_csr_t(processor_t* const proc, const reg_t addr, const reg_t mask, const reg_t init, csr_t_p menvcfg): + masked_csr_t(proc, addr, mask, init), + menvcfg(menvcfg) { +} // implement class base_atp_csr_t and family base_atp_csr_t::base_atp_csr_t(processor_t* const proc, const reg_t addr): basic_csr_t(proc, addr, 0) { } - bool base_atp_csr_t::unlogged_write(const reg_t val) noexcept { const reg_t newval = proc->supports_impl(IMPL_MMU) ? compute_new_satp(val) : 0; if (newval != read()) @@ -798,7 +919,7 @@ satp_csr_t::satp_csr_t(processor_t* const proc, const reg_t addr): void satp_csr_t::verify_permissions(insn_t insn, bool write) const { base_atp_csr_t::verify_permissions(insn, write); if (get_field(state->mstatus->read(), MSTATUS_TVM)) - require(state->prv >= PRV_M); + require(state->prv == PRV_M); } virtualized_satp_csr_t::virtualized_satp_csr_t(processor_t* const proc, satp_csr_t_p orig, csr_t_p virt): @@ -826,7 +947,6 @@ bool virtualized_satp_csr_t::unlogged_write(const reg_t val) noexcept { return virtualized_csr_t::unlogged_write(newval); } - // implement class wide_counter_csr_t wide_counter_csr_t::wide_counter_csr_t(processor_t* const proc, const reg_t addr): csr_t(proc, addr), @@ -842,10 +962,7 @@ void wide_counter_csr_t::bump(const reg_t howmuch) noexcept { } bool wide_counter_csr_t::unlogged_write(const reg_t val) noexcept { - if (proc->get_xlen() == 32) - this->val = (this->val >> 32 << 32) | (val & 0xffffffffU); - else - this->val = val; + 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. @@ -859,29 +976,30 @@ reg_t wide_counter_csr_t::written_value() const noexcept { return this->val + 1; } -void wide_counter_csr_t::write_upper_half(const reg_t val) noexcept { - this->val = (val << 32) | (this->val << 32 >> 32); - this->val--; // See comment above. - // Log upper half only. - log_special_write(address + (CSR_MINSTRETH - CSR_MINSTRET), written_value() >> 32); -} - - -counter_top_csr_t::counter_top_csr_t(processor_t* const proc, const reg_t addr, wide_counter_csr_t_p parent): +// implement class time_counter_csr_t +time_counter_csr_t::time_counter_csr_t(processor_t* const proc, const reg_t addr): csr_t(proc, addr), - parent(parent) { + shadow_val(0) { } -reg_t counter_top_csr_t::read() const noexcept { - return parent->read() >> 32; +reg_t time_counter_csr_t::read() const noexcept { + // reading the time CSR in VS or VU mode returns the sum of the contents of + // htimedelta and the actual value of time. + if (state->v) + return shadow_val + state->htimedelta->read(); + else + return shadow_val; } -bool counter_top_csr_t::unlogged_write(const reg_t val) noexcept { - parent->write_upper_half(val); - return true; +void time_counter_csr_t::sync(const reg_t val) noexcept { + shadow_val = val; + if (proc->extension_enabled(EXT_SSTC)) { + const reg_t mip_val = (shadow_val >= state->stimecmp->read() ? MIP_STIP : 0) | + (shadow_val + state->htimedelta->read() >= state->vstimecmp->read() ? MIP_VSTIP : 0); + state->mip->backdoor_write_with_mask(MIP_STIP | MIP_VSTIP, mip_val); + } } - proxy_csr_t::proxy_csr_t(processor_t* const proc, const reg_t addr, csr_t_p delegate): csr_t(proc, addr), delegate(delegate) { @@ -896,7 +1014,6 @@ bool proxy_csr_t::unlogged_write(const reg_t val) noexcept { return false; } - const_csr_t::const_csr_t(processor_t* const proc, const reg_t addr, reg_t val): csr_t(proc, addr), val(val) { @@ -906,11 +1023,10 @@ reg_t const_csr_t::read() const noexcept { return val; } -bool const_csr_t::unlogged_write(const reg_t val) noexcept { +bool const_csr_t::unlogged_write(const reg_t UNUSED val) noexcept { return false; } - counter_proxy_csr_t::counter_proxy_csr_t(processor_t* const proc, const reg_t addr, csr_t_p delegate): proxy_csr_t(proc, addr, delegate) { } @@ -920,11 +1036,13 @@ bool counter_proxy_csr_t::myenable(csr_t_p counteren) const noexcept { } void counter_proxy_csr_t::verify_permissions(insn_t insn, bool write) const { + proxy_csr_t::verify_permissions(insn, write); + const bool mctr_ok = (state->prv < PRV_M) ? myenable(state->mcounteren) : true; const bool hctr_ok = state->v ? myenable(state->hcounteren) : true; const bool sctr_ok = (proc->extension_enabled('S') && state->prv < PRV_S) ? myenable(state->scounteren) : true; - if (write || !mctr_ok) + if (!mctr_ok) throw trap_illegal_instruction(insn.bits()); if (!hctr_ok) throw trap_virtual_instruction(insn.bits()); @@ -936,7 +1054,6 @@ void counter_proxy_csr_t::verify_permissions(insn_t insn, bool write) const { } } - hypervisor_csr_t::hypervisor_csr_t(processor_t* const proc, const reg_t addr): basic_csr_t(proc, addr, 0) { } @@ -947,7 +1064,6 @@ void hypervisor_csr_t::verify_permissions(insn_t insn, bool write) const { throw trap_illegal_instruction(insn.bits()); } - hideleg_csr_t::hideleg_csr_t(processor_t* const proc, const reg_t addr, csr_t_p mideleg): masked_csr_t(proc, addr, MIP_VS_MASK, 0), mideleg(mideleg) { @@ -957,7 +1073,6 @@ 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) { } @@ -975,7 +1090,7 @@ bool hgatp_csr_t::unlogged_write(const reg_t val) noexcept { if (proc->get_const_xlen() == 32) { mask = HGATP32_PPN | HGATP32_MODE | - proc->supports_impl(IMPL_MMU_VMID) ? HGATP32_VMID : 0; + (proc->supports_impl(IMPL_MMU_VMID) ? HGATP32_VMID : 0); } else { mask = (HGATP64_PPN & ((reg_t(1) << (MAX_PADDR_BITS - PGSHIFT)) - 1)) | (proc->supports_impl(IMPL_MMU_VMID) ? HGATP64_VMID : 0); @@ -990,7 +1105,6 @@ bool hgatp_csr_t::unlogged_write(const reg_t val) noexcept { return basic_csr_t::unlogged_write((read() & ~mask) | (val & mask)); } - tselect_csr_t::tselect_csr_t(processor_t* const proc, const reg_t addr): basic_csr_t(proc, addr, 0) { } @@ -999,7 +1113,6 @@ bool tselect_csr_t::unlogged_write(const reg_t val) noexcept { return basic_csr_t::unlogged_write((val < proc->TM.count()) ? val : read()); } - tdata1_csr_t::tdata1_csr_t(processor_t* const proc, const reg_t addr): csr_t(proc, addr) { } @@ -1012,7 +1125,6 @@ bool tdata1_csr_t::unlogged_write(const reg_t val) noexcept { return proc->TM.tdata1_write(proc, state->tselect->read(), val); } - tdata2_csr_t::tdata2_csr_t(processor_t* const proc, const reg_t addr): csr_t(proc, addr) { } @@ -1025,7 +1137,6 @@ bool tdata2_csr_t::unlogged_write(const reg_t val) noexcept { return proc->TM.tdata2_write(proc, state->tselect->read(), val); } - debug_mode_csr_t::debug_mode_csr_t(processor_t* const proc, const reg_t addr): basic_csr_t(proc, addr, 0) { } @@ -1046,7 +1157,6 @@ void dpc_csr_t::verify_permissions(insn_t insn, bool write) const { throw trap_illegal_instruction(insn.bits()); } - dcsr_csr_t::dcsr_csr_t(processor_t* const proc, const reg_t addr): csr_t(proc, addr), prv(0), @@ -1098,16 +1208,30 @@ void dcsr_csr_t::write_cause_and_prv(uint8_t cause, reg_t prv) noexcept { log_write(); } - float_csr_t::float_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 float_csr_t::verify_permissions(insn_t insn, bool write) const { masked_csr_t::verify_permissions(insn, write); - require_fp; - if (!proc->extension_enabled('F')) + require_fs; + if (!proc->extension_enabled('F') && !proc->extension_enabled(EXT_ZFINX)) throw trap_illegal_instruction(insn.bits()); + + if (proc->extension_enabled(EXT_SMSTATEEN) && proc->extension_enabled(EXT_ZFINX)) { + if ((state->prv < PRV_M) && !(state->mstateen[0]->read() & MSTATEEN0_FCSR)) + throw trap_illegal_instruction(insn.bits()); + + if (state->v && !(state->hstateen[0]->read() & HSTATEEN0_FCSR)) + throw trap_virtual_instruction(insn.bits()); + + if ((proc->extension_enabled('S') && state->prv < PRV_S) && !(state->sstateen[0]->read() & SSTATEEN0_FCSR)) { + if (state->v) + throw trap_virtual_instruction(insn.bits()); + else + throw trap_illegal_instruction(insn.bits()); + } + } } bool float_csr_t::unlogged_write(const reg_t val) noexcept { @@ -1115,7 +1239,6 @@ bool float_csr_t::unlogged_write(const reg_t val) noexcept { return masked_csr_t::unlogged_write(val); } - composite_csr_t::composite_csr_t(processor_t* const proc, const reg_t addr, csr_t_p upper_csr, csr_t_p lower_csr, const unsigned upper_lsb): csr_t(proc, addr), upper_csr(upper_csr), @@ -1139,7 +1262,6 @@ bool composite_csr_t::unlogged_write(const reg_t val) noexcept { return false; // logging is done only by the underlying CSRs } - seed_csr_t::seed_csr_t(processor_t* const proc, const reg_t addr): csr_t(proc, addr) { } @@ -1161,8 +1283,6 @@ bool seed_csr_t::unlogged_write(const reg_t val) noexcept { return true; } - - vector_csr_t::vector_csr_t(processor_t* const proc, const reg_t addr, const reg_t mask, const reg_t init): basic_csr_t(proc, addr, init), mask(mask) { @@ -1187,7 +1307,6 @@ bool vector_csr_t::unlogged_write(const reg_t val) noexcept { return basic_csr_t::unlogged_write(val & mask); } - vxsat_csr_t::vxsat_csr_t(processor_t* const proc, const reg_t addr): masked_csr_t(proc, addr, /*mask*/ 1, /*init*/ 0) { } @@ -1203,3 +1322,151 @@ bool vxsat_csr_t::unlogged_write(const reg_t val) noexcept { dirty_vs_state; return masked_csr_t::unlogged_write(val); } + +// implement class hstateen_csr_t +hstateen_csr_t::hstateen_csr_t(processor_t* const proc, const reg_t addr, const reg_t mask, + const reg_t init, uint8_t index): + masked_csr_t(proc, addr, mask, init), + index(index) { +} + +reg_t hstateen_csr_t::read() const noexcept { + // For every bit in an mstateen CSR that is zero (whether read-only zero or set to zero), + // the same bit appears as read-only zero in the matching hstateen and sstateen CSRs + return masked_csr_t::read() & state->mstateen[index]->read(); +} + +bool hstateen_csr_t::unlogged_write(const reg_t val) noexcept { + // For every bit in an mstateen CSR that is zero (whether read-only zero or set to zero), + // the same bit appears as read-only zero in the matching hstateen and sstateen CSRs + return masked_csr_t::unlogged_write(val & state->mstateen[index]->read()); +} + +void hstateen_csr_t::verify_permissions(insn_t insn, bool write) const { + if ((state->prv < PRV_M) && !(state->mstateen[index]->read() & MSTATEEN_HSTATEEN)) + throw trap_illegal_instruction(insn.bits()); + masked_csr_t::verify_permissions(insn, write); +} + +// implement class sstateen_csr_t +sstateen_csr_t::sstateen_csr_t(processor_t* const proc, const reg_t addr, const reg_t mask, + const reg_t init, uint8_t index): + hstateen_csr_t(proc, addr, mask, init, index) { +} + +reg_t sstateen_csr_t::read() const noexcept { + // For every bit in an mstateen CSR that is zero (whether read-only zero or set to zero), + // the same bit appears as read-only zero in the matching hstateen and sstateen CSRs + // For every bit in an hstateen CSR that is zero (whether read-only zero or set to zero), + // the same bit appears as read-only zero in sstateen when accessed in VS-mode + if (state->v) + return hstateen_csr_t::read() & state->hstateen[index]->read(); + else + return hstateen_csr_t::read(); +} + +bool sstateen_csr_t::unlogged_write(const reg_t val) noexcept { + // For every bit in an mstateen CSR that is zero (whether read-only zero or set to zero), + // the same bit appears as read-only zero in the matching hstateen and sstateen CSRs + // For every bit in an hstateen CSR that is zero (whether read-only zero or set to zero), + // the same bit appears as read-only zero in sstateen when accessed in VS-mode + if (state->v) + return hstateen_csr_t::unlogged_write(val & state->hstateen[index]->read()); + else + return hstateen_csr_t::unlogged_write(val); +} + +void sstateen_csr_t::verify_permissions(insn_t insn, bool write) const { + hstateen_csr_t::verify_permissions(insn, write); + + if (state->v && !(state->hstateen[index]->read() & HSTATEEN_SSTATEEN)) + throw trap_virtual_instruction(insn.bits()); +} + +// implement class senvcfg_csr_t +senvcfg_csr_t::senvcfg_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 senvcfg_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_HENVCFG)) + throw trap_illegal_instruction(insn.bits()); + + if (state->v && !(state->hstateen[0]->read() & HSTATEEN0_SENVCFG)) + throw trap_virtual_instruction(insn.bits()); + } + + masked_csr_t::verify_permissions(insn, write); +} + +void henvcfg_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_HENVCFG)) + throw trap_illegal_instruction(insn.bits()); + } + + masked_csr_t::verify_permissions(insn, write); +} + +stimecmp_csr_t::stimecmp_csr_t(processor_t* const proc, const reg_t addr, const reg_t imask): + basic_csr_t(proc, addr, 0), intr_mask(imask) { +} + +bool stimecmp_csr_t::unlogged_write(const reg_t val) noexcept { + state->mip->backdoor_write_with_mask(intr_mask, state->time->read() >= val ? intr_mask : 0); + 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 virtualized_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 + if (state->prv < PRV_M) + throw trap_illegal_instruction(insn.bits()); + } + + state->time_proxy->verify_permissions(insn, false); + + if (state->v && !(state->henvcfg->read() & HENVCFG_STCE)) { + // access to vstimecmp with MENVCFG.STCE = 1 and HENVCFG.STCE = 0 when V = 1 + throw trap_virtual_instruction(insn.bits()); + } + + virtualized_csr_t::verify_permissions(insn, write); +} + +scountovf_csr_t::scountovf_csr_t(processor_t* const proc, const reg_t addr): + csr_t(proc, 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); +} + +reg_t scountovf_csr_t::read() const noexcept { + reg_t val = 0; + for (reg_t i = 3; i <= 31; ++i) { + bool of = state->mevent[i - 3]->read() & MHPMEVENT_OF; + val |= of << i; + } + + /* In M and S modes, scountovf bit X is readable when mcounteren bit X is set, */ + /* and otherwise reads as zero. Similarly, in VS mode, scountovf bit X is readable */ + /* when mcounteren bit X and hcounteren bit X are both set, and otherwise reads as zero. */ + val &= state->mcounteren->read(); + if (state->v) + val &= state->hcounteren->read(); + return val; +} + +bool scountovf_csr_t::unlogged_write(const reg_t UNUSED val) noexcept { + /* this function is unused */ + return false; +} |