@@ -1835,6 +1835,23 @@ static target_ulong promote_load_fault(target_ulong orig_cause)
/* if no promotion, return original cause */
return orig_cause;
}
+
+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;
+
+ if (cpu_get_fcfien(env)) {
+ env->mnstatus = set_field(env->mnstatus, MNSTATUS_MNPELP, env->elp);
+ }
+
+ riscv_cpu_set_mode(env, PRV_M, false);
+}
+
/*
* Handle Traps
*
@@ -1874,22 +1891,8 @@ void riscv_cpu_do_interrupt(CPUState *cs)
bool nnmi_excep = 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;
-
- if (cpu_get_fcfien(env)) {
- env->mnstatus = set_field(env->mnstatus, MNSTATUS_MNPELP, env->elp);
- }
-
- /* Trapping to M mode, virt is disabled */
- riscv_cpu_set_mode(env, PRV_M, false);
-
+ riscv_do_nmi(env, cause | ((target_ulong)1U << (TARGET_LONG_BITS - 1)),
+ virt);
return;
}
@@ -2101,11 +2104,32 @@ void riscv_cpu_do_interrupt(CPUState *cs)
/* Trapping to M mode, virt is disabled */
virt = false;
}
+ /*
+ * If the hart encounters an exception while executing in M-mode,
+ * with the mnstatus.NMIE bit clear, the program counter is set to
+ * the RNMI exception trap handler address.
+ */
+ nnmi_excep = cpu->cfg.ext_smrnmi &&
+ !get_field(env->mnstatus, MNSTATUS_NMIE) &&
+ !async;
s = env->mstatus;
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 || nnmi_excep) {
+ 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;
mxlen = 16 << riscv_cpu_mxl(env);
env->mcause = cause | ((target_ulong)async << (mxlen - 1));