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> Reviewed-by: Alistair Francis <alistair.fran...@wdc.com> --- target/riscv/cpu_helper.c | 57 ++++++++++++++++++++++++++++----------- 1 file changed, 41 insertions(+), 16 deletions(-) diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 539ba327e7..e1dfc4ecbf 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -1938,6 +1938,24 @@ 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); + } + + /* Trapping to M mode, virt is disabled */ + riscv_cpu_set_mode(env, PRV_M, false); +} + /* * Handle Traps * @@ -1977,22 +1995,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 << (mxlen - 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 << (mxlen - 1)), + env->virt_enabled); return; } @@ -2204,11 +2208,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; env->mcause = cause | ((target_ulong)async << (mxlen - 1)); if (smode_double_trap) { -- 2.47.1