aboutsummaryrefslogtreecommitdiff
path: root/target/riscv/csr.c
diff options
context:
space:
mode:
authorRajnesh Kanwal <rkanwal@rivosinc.com>2023-10-16 12:17:35 +0100
committerAlistair Francis <alistair.francis@wdc.com>2023-11-07 11:02:17 +1000
commit1697837ed98cf56a6f65edd06128151f83b99403 (patch)
tree19c70275348fb836c8ec1d0d13c9facc8db9231b /target/riscv/csr.c
parent1ebad505f3d5108513bf150b901344caceb3a7c1 (diff)
downloadqemu-1697837ed98cf56a6f65edd06128151f83b99403.zip
qemu-1697837ed98cf56a6f65edd06128151f83b99403.tar.gz
qemu-1697837ed98cf56a6f65edd06128151f83b99403.tar.bz2
target/riscv: Add M-mode virtual interrupt and IRQ filtering support.
This change adds support for inserting virtual interrupts from M-mode into S-mode using mvien and mvip csrs. IRQ filtering is a use case of this change, i-e M-mode can stop delegating an interrupt to S-mode and instead enable it in MIE and receive those interrupts in M-mode and then selectively inject the interrupt using mvien and mvip. Also, the spec doesn't mandate the interrupt to be actually supported in hardware. Which allows M-mode to assert virtual interrupts to S-mode that have no connection to any real interrupt events. This is defined as part of the AIA specification [0], "5.3 Interrupt filtering and virtual interrupts for supervisor level". [0]: https://github.com/riscv/riscv-aia/releases/download/1.0/riscv-interrupts-1.0.pdf Signed-off-by: Rajnesh Kanwal <rkanwal@rivosinc.com> Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Message-ID: <20231016111736.28721-6-rkanwal@rivosinc.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
Diffstat (limited to 'target/riscv/csr.c')
-rw-r--r--target/riscv/csr.c279
1 files changed, 251 insertions, 28 deletions
diff --git a/target/riscv/csr.c b/target/riscv/csr.c
index 4847b47..645f30f 100644
--- a/target/riscv/csr.c
+++ b/target/riscv/csr.c
@@ -1117,21 +1117,16 @@ static RISCVException write_stimecmph(CPURISCVState *env, int csrno,
return RISCV_EXCP_NONE;
}
-/* Machine constants */
-
-#define M_MODE_INTERRUPTS ((uint64_t)(MIP_MSIP | MIP_MTIP | MIP_MEIP))
-#define S_MODE_INTERRUPTS ((uint64_t)(MIP_SSIP | MIP_STIP | MIP_SEIP | \
- MIP_LCOFIP))
-#define VS_MODE_INTERRUPTS ((uint64_t)(MIP_VSSIP | MIP_VSTIP | MIP_VSEIP))
-#define HS_MODE_INTERRUPTS ((uint64_t)(MIP_SGEIP | VS_MODE_INTERRUPTS))
-
#define VSTOPI_NUM_SRCS 5
-static const uint64_t delegable_ints = S_MODE_INTERRUPTS |
- VS_MODE_INTERRUPTS;
-static const uint64_t vs_delegable_ints = VS_MODE_INTERRUPTS;
+#define LOCAL_INTERRUPTS (~0x1FFF)
+
+static const uint64_t delegable_ints =
+ S_MODE_INTERRUPTS | VS_MODE_INTERRUPTS | MIP_LCOFIP;
+static const uint64_t vs_delegable_ints =
+ (VS_MODE_INTERRUPTS | LOCAL_INTERRUPTS) & ~MIP_LCOFIP;
static const uint64_t all_ints = M_MODE_INTERRUPTS | S_MODE_INTERRUPTS |
- HS_MODE_INTERRUPTS;
+ HS_MODE_INTERRUPTS | LOCAL_INTERRUPTS;
#define DELEGABLE_EXCPS ((1ULL << (RISCV_EXCP_INST_ADDR_MIS)) | \
(1ULL << (RISCV_EXCP_INST_ACCESS_FAULT)) | \
(1ULL << (RISCV_EXCP_ILLEGAL_INST)) | \
@@ -1162,12 +1157,30 @@ static const target_ulong vs_delegable_excps = DELEGABLE_EXCPS &
static const target_ulong sstatus_v1_10_mask = SSTATUS_SIE | SSTATUS_SPIE |
SSTATUS_UIE | SSTATUS_UPIE | SSTATUS_SPP | SSTATUS_FS | SSTATUS_XS |
SSTATUS_SUM | SSTATUS_MXR | SSTATUS_VS;
-static const target_ulong sip_writable_mask = SIP_SSIP | MIP_USIP | MIP_UEIP |
- SIP_LCOFIP;
+
+/*
+ * Spec allows for bits 13:63 to be either read-only or writable.
+ * So far we have interrupt LCOFIP in that region which is writable.
+ *
+ * Also, spec allows to inject virtual interrupts in this region even
+ * without any hardware interrupts for that interrupt number.
+ *
+ * For now interrupt in 13:63 region are all kept writable. 13 being
+ * LCOFIP and 14:63 being virtual only. Change this in future if we
+ * introduce more interrupts that are not writable.
+ */
+
+/* Bit STIP can be an alias of mip.STIP that's why it's writable in mvip. */
+static const target_ulong mvip_writable_mask = MIP_SSIP | MIP_STIP | MIP_SEIP |
+ LOCAL_INTERRUPTS;
+static const target_ulong mvien_writable_mask = MIP_SSIP | MIP_SEIP |
+ LOCAL_INTERRUPTS;
+
+static const target_ulong sip_writable_mask = SIP_SSIP | LOCAL_INTERRUPTS;
static const target_ulong hip_writable_mask = MIP_VSSIP;
static const target_ulong hvip_writable_mask = MIP_VSSIP | MIP_VSTIP |
- MIP_VSEIP;
-static const target_ulong vsip_writable_mask = MIP_VSSIP;
+ MIP_VSEIP | LOCAL_INTERRUPTS;
+static const target_ulong vsip_writable_mask = MIP_VSSIP | LOCAL_INTERRUPTS;
const bool valid_vm_1_10_32[16] = {
[VM_1_10_MBARE] = true,
@@ -1562,6 +1575,52 @@ static RISCVException rmw_mieh(CPURISCVState *env, int csrno,
return ret;
}
+static RISCVException rmw_mvien64(CPURISCVState *env, int csrno,
+ uint64_t *ret_val,
+ uint64_t new_val, uint64_t wr_mask)
+{
+ uint64_t mask = wr_mask & mvien_writable_mask;
+
+ if (ret_val) {
+ *ret_val = env->mvien;
+ }
+
+ env->mvien = (env->mvien & ~mask) | (new_val & mask);
+
+ return RISCV_EXCP_NONE;
+}
+
+static RISCVException rmw_mvien(CPURISCVState *env, int csrno,
+ target_ulong *ret_val,
+ target_ulong new_val, target_ulong wr_mask)
+{
+ uint64_t rval;
+ RISCVException ret;
+
+ ret = rmw_mvien64(env, csrno, &rval, new_val, wr_mask);
+ if (ret_val) {
+ *ret_val = rval;
+ }
+
+ return ret;
+}
+
+static RISCVException rmw_mvienh(CPURISCVState *env, int csrno,
+ target_ulong *ret_val,
+ target_ulong new_val, target_ulong wr_mask)
+{
+ uint64_t rval;
+ RISCVException ret;
+
+ ret = rmw_mvien64(env, csrno, &rval,
+ ((uint64_t)new_val) << 32, ((uint64_t)wr_mask) << 32);
+ if (ret_val) {
+ *ret_val = rval >> 32;
+ }
+
+ return ret;
+}
+
static int read_mtopi(CPURISCVState *env, int csrno, target_ulong *val)
{
int irq;
@@ -1703,6 +1762,11 @@ static int rmw_xireg(CPURISCVState *env, int csrno, target_ulong *val,
priv = PRV_M;
break;
case CSR_SIREG:
+ if (env->priv == PRV_S && env->mvien & MIP_SEIP &&
+ env->siselect >= ISELECT_IMSIC_EIDELIVERY &&
+ env->siselect <= ISELECT_IMSIC_EIE63) {
+ goto done;
+ }
iprio = env->siprio;
isel = env->siselect;
priv = PRV_S;
@@ -1769,6 +1833,9 @@ static int rmw_xtopei(CPURISCVState *env, int csrno, target_ulong *val,
priv = PRV_M;
break;
case CSR_STOPEI:
+ if (env->mvien & MIP_SEIP && env->priv == PRV_S) {
+ goto done;
+ }
priv = PRV_S;
break;
case CSR_VSTOPEI:
@@ -2360,6 +2427,143 @@ static RISCVException rmw_miph(CPURISCVState *env, int csrno,
return ret;
}
+/*
+ * The function is written for two use-cases:
+ * 1- To access mvip csr as is for m-mode access.
+ * 2- To access sip as a combination of mip and mvip for s-mode.
+ *
+ * Both report bits 1, 5, 9 and 13:63 but with the exception of
+ * STIP being read-only zero in case of mvip when sstc extension
+ * is present.
+ * Also, sip needs to be read-only zero when both mideleg[i] and
+ * mvien[i] are zero but mvip needs to be an alias of mip.
+ */
+static RISCVException rmw_mvip64(CPURISCVState *env, int csrno,
+ uint64_t *ret_val,
+ uint64_t new_val, uint64_t wr_mask)
+{
+ RISCVCPU *cpu = env_archcpu(env);
+ target_ulong ret_mip = 0;
+ RISCVException ret;
+ uint64_t old_mvip;
+
+ /*
+ * mideleg[i] mvien[i]
+ * 0 0 No delegation. mvip[i] is alias of mip[i].
+ * 0 1 mvip[i] becomes source of interrupt, mip bypassed.
+ * 1 X mip[i] is source of interrupt and mvip[i] aliases
+ * mip[i].
+ *
+ * So alias condition would be for bits:
+ * ((S_MODE_INTERRUPTS | LOCAL_INTERRUPTS) & (mideleg | ~mvien)) |
+ * (!sstc & MIP_STIP)
+ *
+ * Non-alias condition will be for bits:
+ * (S_MODE_INTERRUPTS | LOCAL_INTERRUPTS) & (~mideleg & mvien)
+ *
+ * alias_mask denotes the bits that come from mip nalias_mask denotes bits
+ * that come from hvip.
+ */
+ uint64_t alias_mask = ((S_MODE_INTERRUPTS | LOCAL_INTERRUPTS) &
+ (env->mideleg | ~env->mvien)) | MIP_STIP;
+ uint64_t nalias_mask = (S_MODE_INTERRUPTS | LOCAL_INTERRUPTS) &
+ (~env->mideleg & env->mvien);
+ uint64_t wr_mask_mvip;
+ uint64_t wr_mask_mip;
+
+ /*
+ * mideleg[i] mvien[i]
+ * 0 0 sip[i] read-only zero.
+ * 0 1 sip[i] alias of mvip[i].
+ * 1 X sip[i] alias of mip[i].
+ *
+ * Both alias and non-alias mask remain same for sip except for bits
+ * which are zero in both mideleg and mvien.
+ */
+ if (csrno == CSR_SIP) {
+ /* Remove bits that are zero in both mideleg and mvien. */
+ alias_mask &= (env->mideleg | env->mvien);
+ nalias_mask &= (env->mideleg | env->mvien);
+ }
+
+ /*
+ * If sstc is present, mvip.STIP is not an alias of mip.STIP so clear
+ * that our in mip returned value.
+ */
+ if (cpu->cfg.ext_sstc && (env->priv == PRV_M) &&
+ get_field(env->menvcfg, MENVCFG_STCE)) {
+ alias_mask &= ~MIP_STIP;
+ }
+
+ wr_mask_mip = wr_mask & alias_mask & mvip_writable_mask;
+ wr_mask_mvip = wr_mask & nalias_mask & mvip_writable_mask;
+
+ /*
+ * For bits set in alias_mask, mvip needs to be alias of mip, so forward
+ * this to rmw_mip.
+ */
+ ret = rmw_mip(env, CSR_MIP, &ret_mip, new_val, wr_mask_mip);
+ if (ret != RISCV_EXCP_NONE) {
+ return ret;
+ }
+
+ old_mvip = env->mvip;
+
+ /*
+ * Write to mvip. Update only non-alias bits. Alias bits were updated
+ * in mip in rmw_mip above.
+ */
+ if (wr_mask_mvip) {
+ env->mvip = (env->mvip & ~wr_mask_mvip) | (new_val & wr_mask_mvip);
+
+ /*
+ * Given mvip is separate source from mip, we need to trigger interrupt
+ * from here separately. Normally this happen from riscv_cpu_update_mip.
+ */
+ riscv_cpu_interrupt(env);
+ }
+
+ if (ret_val) {
+ ret_mip &= alias_mask;
+ old_mvip &= nalias_mask;
+
+ *ret_val = old_mvip | ret_mip;
+ }
+
+ return RISCV_EXCP_NONE;
+}
+
+static RISCVException rmw_mvip(CPURISCVState *env, int csrno,
+ target_ulong *ret_val,
+ target_ulong new_val, target_ulong wr_mask)
+{
+ uint64_t rval;
+ RISCVException ret;
+
+ ret = rmw_mvip64(env, csrno, &rval, new_val, wr_mask);
+ if (ret_val) {
+ *ret_val = rval;
+ }
+
+ return ret;
+}
+
+static RISCVException rmw_mviph(CPURISCVState *env, int csrno,
+ target_ulong *ret_val,
+ target_ulong new_val, target_ulong wr_mask)
+{
+ uint64_t rval;
+ RISCVException ret;
+
+ ret = rmw_mvip64(env, csrno, &rval,
+ ((uint64_t)new_val) << 32, ((uint64_t)wr_mask) << 32);
+ if (ret_val) {
+ *ret_val = rval >> 32;
+ }
+
+ return ret;
+}
+
/* Supervisor Trap Setup */
static RISCVException read_sstatus_i128(CPURISCVState *env, int csrno,
Int128 *val)
@@ -2454,20 +2658,37 @@ static RISCVException rmw_sie64(CPURISCVState *env, int csrno,
uint64_t *ret_val,
uint64_t new_val, uint64_t wr_mask)
{
+ uint64_t nalias_mask = (S_MODE_INTERRUPTS | LOCAL_INTERRUPTS) &
+ (~env->mideleg & env->mvien);
+ uint64_t alias_mask = (S_MODE_INTERRUPTS | LOCAL_INTERRUPTS) & env->mideleg;
+ uint64_t sie_mask = wr_mask & nalias_mask;
RISCVException ret;
- uint64_t mask = env->mideleg & S_MODE_INTERRUPTS;
+ /*
+ * mideleg[i] mvien[i]
+ * 0 0 sie[i] read-only zero.
+ * 0 1 sie[i] is a separate writable bit.
+ * 1 X sie[i] alias of mie[i].
+ *
+ * Both alias and non-alias mask remain same for sip except for bits
+ * which are zero in both mideleg and mvien.
+ */
if (env->virt_enabled) {
if (env->hvictl & HVICTL_VTI) {
return RISCV_EXCP_VIRT_INSTRUCTION_FAULT;
}
ret = rmw_vsie64(env, CSR_VSIE, ret_val, new_val, wr_mask);
+ if (ret_val) {
+ *ret_val &= alias_mask;
+ }
} else {
- ret = rmw_mie64(env, csrno, ret_val, new_val, wr_mask & mask);
- }
+ ret = rmw_mie64(env, csrno, ret_val, new_val, wr_mask & alias_mask);
+ if (ret_val) {
+ *ret_val &= alias_mask;
+ *ret_val |= env->sie & nalias_mask;
+ }
- if (ret_val) {
- *ret_val &= mask;
+ env->sie = (env->sie & ~sie_mask) | (new_val & sie_mask);
}
return ret;
@@ -2665,7 +2886,7 @@ static RISCVException rmw_sip64(CPURISCVState *env, int csrno,
uint64_t new_val, uint64_t wr_mask)
{
RISCVException ret;
- uint64_t mask = env->mideleg & sip_writable_mask;
+ uint64_t mask = (env->mideleg | env->mvien) & sip_writable_mask;
if (env->virt_enabled) {
if (env->hvictl & HVICTL_VTI) {
@@ -2673,11 +2894,12 @@ static RISCVException rmw_sip64(CPURISCVState *env, int csrno,
}
ret = rmw_vsip64(env, CSR_VSIP, ret_val, new_val, wr_mask);
} else {
- ret = rmw_mip64(env, csrno, ret_val, new_val, wr_mask & mask);
+ ret = rmw_mvip64(env, csrno, ret_val, new_val, wr_mask & mask);
}
if (ret_val) {
- *ret_val &= env->mideleg & S_MODE_INTERRUPTS;
+ *ret_val &= (env->mideleg | env->mvien) &
+ (S_MODE_INTERRUPTS | LOCAL_INTERRUPTS);
}
return ret;
@@ -2842,6 +3064,7 @@ static int read_vstopi(CPURISCVState *env, int csrno, target_ulong *val)
*val = (iid & TOPI_IID_MASK) << TOPI_IID_SHIFT;
*val |= iprio;
+
return RISCV_EXCP_NONE;
}
@@ -4165,14 +4388,14 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
[CSR_MTOPI] = { "mtopi", aia_any, read_mtopi },
/* Virtual Interrupts for Supervisor Level (AIA) */
- [CSR_MVIEN] = { "mvien", aia_any, read_zero, write_ignore },
- [CSR_MVIP] = { "mvip", aia_any, read_zero, write_ignore },
+ [CSR_MVIEN] = { "mvien", aia_any, NULL, NULL, rmw_mvien },
+ [CSR_MVIP] = { "mvip", aia_any, NULL, NULL, rmw_mvip },
/* Machine-Level High-Half CSRs (AIA) */
[CSR_MIDELEGH] = { "midelegh", aia_any32, NULL, NULL, rmw_midelegh },
[CSR_MIEH] = { "mieh", aia_any32, NULL, NULL, rmw_mieh },
- [CSR_MVIENH] = { "mvienh", aia_any32, read_zero, write_ignore },
- [CSR_MVIPH] = { "mviph", aia_any32, read_zero, write_ignore },
+ [CSR_MVIENH] = { "mvienh", aia_any32, NULL, NULL, rmw_mvienh },
+ [CSR_MVIPH] = { "mviph", aia_any32, NULL, NULL, rmw_mviph },
[CSR_MIPH] = { "miph", aia_any32, NULL, NULL, rmw_miph },
/* Execution environment configuration */