mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-03 07:43:54 -06:00
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:
parent
5db557f82b
commit
c1149f69ab
6 changed files with 152 additions and 7 deletions
|
@ -26,6 +26,7 @@
|
|||
#include "target/riscv/cpu.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/riscv/riscv_hart.h"
|
||||
#include "qemu/error-report.h"
|
||||
|
||||
static const Property riscv_harts_props[] = {
|
||||
DEFINE_PROP_UINT32("num-harts", RISCVHartArrayState, num_harts, 1),
|
||||
|
@ -33,6 +34,23 @@ static const Property riscv_harts_props[] = {
|
|||
DEFINE_PROP_STRING("cpu-type", RISCVHartArrayState, cpu_type),
|
||||
DEFINE_PROP_UINT64("resetvec", RISCVHartArrayState, resetvec,
|
||||
DEFAULT_RSTVEC),
|
||||
|
||||
/*
|
||||
* Smrnmi implementation-defined interrupt and exception trap handlers.
|
||||
*
|
||||
* When an RNMI interrupt is detected, the hart then enters M-mode and
|
||||
* jumps to the address defined by "rnmi-interrupt-vector".
|
||||
*
|
||||
* When the hart encounters an exception while executing in M-mode with
|
||||
* the mnstatus.NMIE bit clear, the hart then jumps to the address
|
||||
* defined by "rnmi-exception-vector".
|
||||
*/
|
||||
DEFINE_PROP_ARRAY("rnmi-interrupt-vector", RISCVHartArrayState,
|
||||
num_rnmi_irqvec, rnmi_irqvec, qdev_prop_uint64,
|
||||
uint64_t),
|
||||
DEFINE_PROP_ARRAY("rnmi-exception-vector", RISCVHartArrayState,
|
||||
num_rnmi_excpvec, rnmi_excpvec, qdev_prop_uint64,
|
||||
uint64_t),
|
||||
};
|
||||
|
||||
static void riscv_harts_cpu_reset(void *opaque)
|
||||
|
@ -46,6 +64,29 @@ static bool riscv_hart_realize(RISCVHartArrayState *s, int idx,
|
|||
{
|
||||
object_initialize_child(OBJECT(s), "harts[*]", &s->harts[idx], cpu_type);
|
||||
qdev_prop_set_uint64(DEVICE(&s->harts[idx]), "resetvec", s->resetvec);
|
||||
|
||||
if (s->harts[idx].cfg.ext_smrnmi) {
|
||||
if (idx < s->num_rnmi_irqvec) {
|
||||
qdev_prop_set_uint64(DEVICE(&s->harts[idx]),
|
||||
"rnmi-interrupt-vector", s->rnmi_irqvec[idx]);
|
||||
}
|
||||
|
||||
if (idx < s->num_rnmi_excpvec) {
|
||||
qdev_prop_set_uint64(DEVICE(&s->harts[idx]),
|
||||
"rnmi-exception-vector", s->rnmi_excpvec[idx]);
|
||||
}
|
||||
} else {
|
||||
if (s->num_rnmi_irqvec > 0) {
|
||||
warn_report_once("rnmi-interrupt-vector property is ignored "
|
||||
"because Smrnmi extension is not enabled.");
|
||||
}
|
||||
|
||||
if (s->num_rnmi_excpvec > 0) {
|
||||
warn_report_once("rnmi-exception-vector property is ignored "
|
||||
"because Smrnmi extension is not enabled.");
|
||||
}
|
||||
}
|
||||
|
||||
s->harts[idx].env.mhartid = s->hartid_base + idx;
|
||||
qemu_register_reset(riscv_harts_cpu_reset, &s->harts[idx]);
|
||||
return qdev_realize(DEVICE(&s->harts[idx]), NULL, errp);
|
||||
|
|
|
@ -38,6 +38,10 @@ struct RISCVHartArrayState {
|
|||
uint32_t hartid_base;
|
||||
char *cpu_type;
|
||||
uint64_t resetvec;
|
||||
uint32_t num_rnmi_irqvec;
|
||||
uint64_t *rnmi_irqvec;
|
||||
uint32_t num_rnmi_excpvec;
|
||||
uint64_t *rnmi_excpvec;
|
||||
RISCVCPU *harts;
|
||||
};
|
||||
|
||||
|
|
|
@ -1412,6 +1412,11 @@ static void riscv_cpu_set_irq(void *opaque, int irq, int level)
|
|||
g_assert_not_reached();
|
||||
}
|
||||
}
|
||||
|
||||
static void riscv_cpu_set_nmi(void *opaque, int irq, int level)
|
||||
{
|
||||
riscv_cpu_set_rnmi(RISCV_CPU(opaque), irq, level);
|
||||
}
|
||||
#endif /* CONFIG_USER_ONLY */
|
||||
|
||||
static bool riscv_cpu_is_dynamic(Object *cpu_obj)
|
||||
|
@ -1435,6 +1440,8 @@ static void riscv_cpu_init(Object *obj)
|
|||
#ifndef CONFIG_USER_ONLY
|
||||
qdev_init_gpio_in(DEVICE(obj), riscv_cpu_set_irq,
|
||||
IRQ_LOCAL_MAX + IRQ_LOCAL_GUEST_MAX);
|
||||
qdev_init_gpio_in_named(DEVICE(cpu), riscv_cpu_set_nmi,
|
||||
"riscv.cpu.rnmi", RNMI_MAX);
|
||||
#endif /* CONFIG_USER_ONLY */
|
||||
|
||||
general_user_opts = g_hash_table_new(g_str_hash, g_str_equal);
|
||||
|
@ -2793,6 +2800,10 @@ static const Property riscv_cpu_properties[] = {
|
|||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
DEFINE_PROP_UINT64("resetvec", RISCVCPU, env.resetvec, DEFAULT_RSTVEC),
|
||||
DEFINE_PROP_UINT64("rnmi-interrupt-vector", RISCVCPU, env.rnmi_irqvec,
|
||||
DEFAULT_RNMI_IRQVEC),
|
||||
DEFINE_PROP_UINT64("rnmi-exception-vector", RISCVCPU, env.rnmi_excpvec,
|
||||
DEFAULT_RNMI_EXCPVEC),
|
||||
#endif
|
||||
|
||||
DEFINE_PROP_BOOL("short-isa-string", RISCVCPU, cfg.short_isa_string, false),
|
||||
|
|
|
@ -487,6 +487,8 @@ struct CPUArchState {
|
|||
target_ulong mncause; /* mncause without bit XLEN-1 set to 1 */
|
||||
target_ulong mnstatus;
|
||||
target_ulong rnmip;
|
||||
uint64_t rnmi_irqvec;
|
||||
uint64_t rnmi_excpvec;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -585,6 +587,7 @@ void riscv_cpu_swap_hypervisor_regs(CPURISCVState *env);
|
|||
int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint64_t interrupts);
|
||||
uint64_t riscv_cpu_update_mip(CPURISCVState *env, uint64_t mask,
|
||||
uint64_t value);
|
||||
void riscv_cpu_set_rnmi(RISCVCPU *cpu, uint32_t irq, bool level);
|
||||
void riscv_cpu_interrupt(CPURISCVState *env);
|
||||
#define BOOL_TO_MASK(x) (-!!(x)) /* helper for riscv_cpu_update_mip value */
|
||||
void riscv_cpu_set_rdtime_fn(CPURISCVState *env, uint64_t (*fn)(void *),
|
||||
|
|
|
@ -650,6 +650,12 @@ typedef enum {
|
|||
/* Default Reset Vector address */
|
||||
#define DEFAULT_RSTVEC 0x1000
|
||||
|
||||
/* Default RNMI Interrupt Vector address */
|
||||
#define DEFAULT_RNMI_IRQVEC 0x0
|
||||
|
||||
/* Default RNMI Exception Vector address */
|
||||
#define DEFAULT_RNMI_EXCPVEC 0x0
|
||||
|
||||
/* Exception causes */
|
||||
typedef enum RISCVException {
|
||||
RISCV_EXCP_NONE = -1, /* sentinel value */
|
||||
|
@ -704,6 +710,9 @@ typedef enum RISCVException {
|
|||
/* -1 is due to bit zero of hgeip and hgeie being ROZ. */
|
||||
#define IRQ_LOCAL_GUEST_MAX (TARGET_LONG_BITS - 1)
|
||||
|
||||
/* RNMI causes */
|
||||
#define RNMI_MAX 16
|
||||
|
||||
/* mip masks */
|
||||
#define MIP_USIP (1 << IRQ_U_SOFT)
|
||||
#define MIP_SSIP (1 << IRQ_S_SOFT)
|
||||
|
@ -889,6 +898,9 @@ typedef enum RISCVException {
|
|||
#define MHPMEVENT_IDX_MASK 0xFFFFF
|
||||
#define MHPMEVENT_SSCOF_RESVD 16
|
||||
|
||||
/* RISC-V-specific interrupt pending bits. */
|
||||
#define CPU_INTERRUPT_RNMI CPU_INTERRUPT_TGT_EXT_0
|
||||
|
||||
/* JVT CSR bits */
|
||||
#define JVT_MODE 0x3F
|
||||
#define JVT_BASE (~0x3F)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue