When the Smsdbltrp ISA extension is enabled, MSTATUS.MDT bit is enabled
at reset and set upon trap. If a trap happens while MSTATUS.MDT is
already set, it will trigger an abort or an NMI is the Smrnmi extension
is available.
Signed-off-by: Clément Léger <cleger@rivosinc.com>
---
target/riscv/cpu.c | 3 +++
target/riscv/cpu_helper.c | 35 ++++++++++++++++++++++++++---------
2 files changed, 29 insertions(+), 9 deletions(-)
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index 75433a4359..f9b2ce4463 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -967,6 +967,9 @@ static void riscv_cpu_reset_hold(Object *obj, ResetType type)
env->mstatus_hs = set_field(env->mstatus_hs,
MSTATUS64_UXL, env->misa_mxl);
}
+ if (riscv_cpu_cfg(env)->ext_smdbltrp) {
+ env->mstatus = set_field(env->mstatus, MSTATUS_MDT, 1);
+ }
}
env->mcause = 0;
env->miclaim = MIP_SGEIP;
diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
index 69da3c3384..5a30d1b8a8 100644
--- a/target/riscv/cpu_helper.c
+++ b/target/riscv/cpu_helper.c
@@ -1697,6 +1697,17 @@ static target_ulong riscv_transformed_insn(CPURISCVState *env,
return xinsn;
}
+static void riscv_do_nmi(CPURISCVState *env, target_ulong cause, bool virt)
+{
+ env->mnstatus = set_field(env->mnstatus, MNSTATUS_NMIE, false);
+ env->mnstatus = set_field(env->mnstatus, MNSTATUS_MNPV, virt);
+ env->mnstatus = set_field(env->mnstatus, MNSTATUS_MNPP, env->priv);
+ env->mncause = cause;
+ env->mnepc = env->pc;
+ env->pc = env->rnmi_irqvec;
+ riscv_cpu_set_mode(env, PRV_M, virt);
+}
+
/*
* Handle Traps
*
@@ -1733,15 +1744,8 @@ void riscv_cpu_do_interrupt(CPUState *cs)
bool nmi_execp = false;
if (cpu->cfg.ext_smrnmi && env->rnmip && async) {
- env->mnstatus = set_field(env->mnstatus, MNSTATUS_NMIE, false);
- env->mnstatus = set_field(env->mnstatus, MNSTATUS_MNPV,
- env->virt_enabled);
- env->mnstatus = set_field(env->mnstatus, MNSTATUS_MNPP,
- env->priv);
- env->mncause = cause | ((target_ulong)1U << (TARGET_LONG_BITS - 1));
- env->mnepc = env->pc;
- env->pc = env->rnmi_irqvec;
- riscv_cpu_set_mode(env, PRV_M, virt);
+ riscv_do_nmi(env, cause | ((target_ulong)1U << (TARGET_LONG_BITS - 1)),
+ virt);
return;
}
@@ -1937,6 +1941,19 @@ void riscv_cpu_do_interrupt(CPUState *cs)
s = set_field(s, MSTATUS_MPIE, get_field(s, MSTATUS_MIE));
s = set_field(s, MSTATUS_MPP, env->priv);
s = set_field(s, MSTATUS_MIE, 0);
+ if (cpu->cfg.ext_smdbltrp) {
+ if (env->mstatus & MSTATUS_MDT) {
+ assert(env->priv == PRV_M);
+ if (!cpu->cfg.ext_smrnmi || nmi_execp) {
+ cpu_abort(CPU(cpu), "M-mode double trap\n");
+ } else {
+ riscv_do_nmi(env, cause, false);
+ return;
+ }
+ }
+
+ s = set_field(s, MSTATUS_MDT, 1);
+ }
env->mstatus = s;
env->mcause = cause | ~(((target_ulong)-1) >> async);
if (smode_double_trap) {
--
2.45.2