aboutsummaryrefslogtreecommitdiff
path: root/riscv/csrs.cc
diff options
context:
space:
mode:
Diffstat (limited to 'riscv/csrs.cc')
-rw-r--r--riscv/csrs.cc304
1 files changed, 279 insertions, 25 deletions
diff --git a/riscv/csrs.cc b/riscv/csrs.cc
index 1873f7e..49717e5 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(); } );
+
+ // 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
+ 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);
@@ -639,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),
@@ -781,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 {
@@ -864,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();
}
@@ -879,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();
}
@@ -956,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),
@@ -1645,10 +1719,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
@@ -1664,9 +1734,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());
+ }
}
-void virtualized_stimecmp_csr_t::verify_permissions(insn_t insn, bool write) const {
+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_with_special_permission_csr_t::verify_permissions(insn_t insn, bool write) const {
orig_csr->verify_permissions(insn, write);
}
@@ -1754,14 +1833,16 @@ 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->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());
}
@@ -1973,3 +2054,176 @@ bool scntinhibit_csr_t::unlogged_write(const reg_t val) noexcept {
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);
+}