aboutsummaryrefslogtreecommitdiff
path: root/riscv/csrs.cc
diff options
context:
space:
mode:
Diffstat (limited to 'riscv/csrs.cc')
-rw-r--r--riscv/csrs.cc519
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;
+}