target/riscv: Handle Smrnmi interrupt and exception

Because the RNMI interrupt trap handler address is implementation defined.
We add the 'rnmi-interrupt-vector' and 'rnmi-exception-vector' as the property
of the harts. It’s very easy for users to set the address based on their
expectation. This patch also adds the functionality to handle the RNMI signals.

Signed-off-by: Frank Chang <frank.chang@sifive.com>
Signed-off-by: Tommy Wu <tommy.wu@sifive.com>
Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com>
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
Message-ID: <20250106054336.1878291-4-frank.chang@sifive.com>
Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
This commit is contained in:
Tommy Wu 2025-01-06 13:43:33 +08:00 committed by Alistair Francis
parent 5db557f82b
commit c1149f69ab
6 changed files with 152 additions and 7 deletions

View file

@ -554,6 +554,18 @@ static int riscv_cpu_local_irq_pending(CPURISCVState *env)
uint64_t vsbits, irq_delegated;
int virq;
/* Priority: RNMI > Other interrupt. */
if (riscv_cpu_cfg(env)->ext_smrnmi) {
/* If mnstatus.NMIE == 0, all interrupts are disabled. */
if (!get_field(env->mnstatus, MNSTATUS_NMIE)) {
return RISCV_EXCP_NONE;
}
if (env->rnmip) {
return ctz64(env->rnmip); /* since non-zero */
}
}
/* Determine interrupt enable state of all privilege modes */
if (env->virt_enabled) {
mie = 1;
@ -616,7 +628,9 @@ static int riscv_cpu_local_irq_pending(CPURISCVState *env)
bool riscv_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
{
if (interrupt_request & CPU_INTERRUPT_HARD) {
uint32_t mask = CPU_INTERRUPT_HARD | CPU_INTERRUPT_RNMI;
if (interrupt_request & mask) {
RISCVCPU *cpu = RISCV_CPU(cs);
CPURISCVState *env = &cpu->env;
int interruptno = riscv_cpu_local_irq_pending(env);
@ -748,6 +762,30 @@ void riscv_cpu_set_geilen(CPURISCVState *env, target_ulong geilen)
env->geilen = geilen;
}
void riscv_cpu_set_rnmi(RISCVCPU *cpu, uint32_t irq, bool level)
{
CPURISCVState *env = &cpu->env;
CPUState *cs = CPU(cpu);
bool release_lock = false;
if (!bql_locked()) {
release_lock = true;
bql_lock();
}
if (level) {
env->rnmip |= 1 << irq;
cpu_interrupt(cs, CPU_INTERRUPT_RNMI);
} else {
env->rnmip &= ~(1 << irq);
cpu_reset_interrupt(cs, CPU_INTERRUPT_RNMI);
}
if (release_lock) {
bql_unlock();
}
}
int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint64_t interrupts)
{
CPURISCVState *env = &cpu->env;
@ -1897,6 +1935,7 @@ void riscv_cpu_do_interrupt(CPUState *cs)
bool write_gva = false;
bool always_storeamo = (env->excp_uw2 & RISCV_UW2_ALWAYS_STORE_AMO);
uint64_t s;
int mode;
/*
* cs->exception is 32-bits wide unlike mcause which is XLEN-bits wide
@ -1914,7 +1953,24 @@ void riscv_cpu_do_interrupt(CPUState *cs)
target_ulong htval = 0;
target_ulong mtval2 = 0;
int sxlen = 0;
int mxlen = 0;
int mxlen = 16 << riscv_cpu_mxl(env);
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;
/* Trapping to M mode, virt is disabled */
riscv_cpu_set_mode(env, PRV_M, false);
return;
}
if (!async) {
/* set tval to badaddr for traps with address information */
@ -2008,8 +2064,10 @@ void riscv_cpu_do_interrupt(CPUState *cs)
__func__, env->mhartid, async, cause, env->pc, tval,
riscv_cpu_get_trap_name(cause, async));
if (env->priv <= PRV_S && cause < 64 &&
(((deleg >> cause) & 1) || s_injected || vs_injected)) {
mode = env->priv <= PRV_S && cause < 64 &&
(((deleg >> cause) & 1) || s_injected || vs_injected) ? PRV_S : PRV_M;
if (mode == PRV_S) {
/* handle the trap in S-mode */
/* save elp status */
if (cpu_get_fcfien(env)) {
@ -2064,6 +2122,14 @@ void riscv_cpu_do_interrupt(CPUState *cs)
((async && (env->stvec & 3) == 1) ? cause * 4 : 0);
riscv_cpu_set_mode(env, PRV_S, virt);
} else {
/*
* If the hart encounters an exception while executing in M-mode
* with the mnstatus.NMIE bit clear, the exception is an RNMI exception.
*/
nnmi_excep = cpu->cfg.ext_smrnmi &&
!get_field(env->mnstatus, MNSTATUS_NMIE) &&
!async;
/* handle the trap in M-mode */
/* save elp status */
if (cpu_get_fcfien(env)) {
@ -2091,14 +2157,22 @@ void riscv_cpu_do_interrupt(CPUState *cs)
s = set_field(s, MSTATUS_MPP, env->priv);
s = set_field(s, MSTATUS_MIE, 0);
env->mstatus = s;
mxlen = 16 << riscv_cpu_mxl(env);
env->mcause = cause | ((target_ulong)async << (mxlen - 1));
env->mepc = env->pc;
env->mtval = tval;
env->mtval2 = mtval2;
env->mtinst = tinst;
env->pc = (env->mtvec >> 2 << 2) +
((async && (env->mtvec & 3) == 1) ? cause * 4 : 0);
/*
* For RNMI exception, program counter is set to the RNMI exception
* trap handler address.
*/
if (nnmi_excep) {
env->pc = env->rnmi_excpvec;
} else {
env->pc = (env->mtvec >> 2 << 2) +
((async && (env->mtvec & 3) == 1) ? cause * 4 : 0);
}
riscv_cpu_set_mode(env, PRV_M, virt);
}