When the Smsdbltrp ISA extension is enabled, 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 <cle...@rivosinc.com> --- target/riscv/cpu_helper.c | 52 +++++++++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 18 deletions(-) diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 623a3abbf7..8825572d5e 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -1703,6 +1703,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, false); +} + /* * Handle Traps * @@ -1741,15 +1752,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; - riscv_cpu_set_mode(env, PRV_M, virt); + riscv_do_nmi(env, cause | ((target_ulong)1U << (TARGET_LONG_BITS - 1)), + virt); return; } @@ -1932,11 +1936,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)); @@ -1950,15 +1975,6 @@ void riscv_cpu_do_interrupt(CPUState *cs) env->mtval = tval; env->mtinst = tinst; - /* - * 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; - if (nnmi_excep) { env->pc = env->rnmi_excpvec; } else { -- 2.45.2