target/riscv: Implement Smdbltrp behavior

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 <cleger@rivosinc.com>
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
Message-ID: <20250110125441.3208676-9-cleger@rivosinc.com>
Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
This commit is contained in:
Clément Léger 2025-01-10 13:54:39 +01:00 committed by Alistair Francis
parent f2efb6e793
commit 00af7d5360

View file

@ -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) {