[PATCH v5 2/5] target/riscv: Handle Smrnmi interrupt and exception.

Tommy Wu posted 5 patches 3 months, 2 weeks ago
There is a newer version of this series
[PATCH v5 2/5] target/riscv: Handle Smrnmi interrupt and exception.
Posted by Tommy Wu 3 months, 2 weeks ago
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>
---
 hw/riscv/riscv_hart.c         | 18 ++++++++
 include/hw/riscv/riscv_hart.h |  4 ++
 target/riscv/cpu.c            | 11 +++++
 target/riscv/cpu.h            |  6 +++
 target/riscv/cpu_bits.h       | 12 ++++++
 target/riscv/cpu_helper.c     | 77 +++++++++++++++++++++++++++++++++--
 6 files changed, 125 insertions(+), 3 deletions(-)

diff --git a/hw/riscv/riscv_hart.c b/hw/riscv/riscv_hart.c
index 613ea2aaa0..b7d73f7a82 100644
--- a/hw/riscv/riscv_hart.c
+++ b/hw/riscv/riscv_hart.c
@@ -33,6 +33,12 @@ static Property riscv_harts_props[] = {
     DEFINE_PROP_STRING("cpu-type", RISCVHartArrayState, cpu_type),
     DEFINE_PROP_UINT64("resetvec", RISCVHartArrayState, resetvec,
                        DEFAULT_RSTVEC),
+    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),
     DEFINE_PROP_END_OF_LIST(),
 };
 
@@ -47,6 +53,18 @@ 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 (s->rnmi_irqvec) {
+            qdev_prop_set_uint64(DEVICE(&s->harts[idx]),
+                                 "rnmi-interrupt-vector",
+                                 s->rnmi_irqvec[idx]);
+        }
+        if (s->rnmi_excpvec) {
+            qdev_prop_set_uint64(DEVICE(&s->harts[idx]),
+                                 "rnmi-exception-vector",
+                                 s->rnmi_excpvec[idx]);
+        }
+    }
     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);
diff --git a/include/hw/riscv/riscv_hart.h b/include/hw/riscv/riscv_hart.h
index 912b4a2682..a6ed73a195 100644
--- a/include/hw/riscv/riscv_hart.h
+++ b/include/hw/riscv/riscv_hart.h
@@ -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;
 };
 
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index a90808a3ba..2f64b3df22 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -1309,6 +1309,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)
@@ -1332,6 +1337,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);
@@ -2681,6 +2688,10 @@ static 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),
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index 1619c3acb6..d8ad04ec31 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -472,6 +472,11 @@ struct CPUArchState {
     uint64_t kvm_timer_state;
     uint64_t kvm_timer_frequency;
 #endif /* CONFIG_KVM */
+
+    /* RNMI */
+    target_ulong rnmip;
+    uint64_t rnmi_irqvec;
+    uint64_t rnmi_excpvec;
 };
 
 /*
@@ -568,6 +573,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 *),
diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
index 32b068f18a..e14b654c35 100644
--- a/target/riscv/cpu_bits.h
+++ b/target/riscv/cpu_bits.h
@@ -662,6 +662,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 */
@@ -711,6 +717,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)
@@ -942,6 +951,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)
diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
index 395a1d9140..f1d464a554 100644
--- a/target/riscv/cpu_helper.c
+++ b/target/riscv/cpu_helper.c
@@ -434,6 +434,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;
@@ -496,7 +508,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);
@@ -619,6 +633,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;
@@ -1654,6 +1692,7 @@ void riscv_cpu_do_interrupt(CPUState *cs)
     bool virt = env->virt_enabled;
     bool write_gva = false;
     uint64_t s;
+    int mode;
 
     /*
      * cs->exception is 32-bits wide unlike mcause which is XLEN-bits wide
@@ -1670,6 +1709,20 @@ void riscv_cpu_do_interrupt(CPUState *cs)
     target_ulong tinst = 0;
     target_ulong htval = 0;
     target_ulong mtval2 = 0;
+    bool nmi_execp = 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);
+        return;
+    }
 
     if (!async) {
         /* set tval to badaddr for traps with address information */
@@ -1755,8 +1808,20 @@ 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 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.
+     */
+    nmi_execp = cpu->cfg.ext_smrnmi &&
+                !get_field(env->mnstatus, MNSTATUS_NMIE) &&
+                !async &&
+                mode == PRV_M;
+
+    if (mode == PRV_S) {
         /* handle the trap in S-mode */
         if (riscv_has_ext(env, RVH)) {
             uint64_t hdeleg = async ? env->hideleg : env->hedeleg;
@@ -1834,6 +1899,12 @@ void riscv_cpu_do_interrupt(CPUState *cs)
         env->mtinst = tinst;
         env->pc = (env->mtvec >> 2 << 2) +
                   ((async && (env->mtvec & 3) == 1) ? cause * 4 : 0);
+        if (cpu->cfg.ext_smrnmi && nmi_execp) {
+            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);
     }
 
-- 
2.39.3


RE: [PATCH v5 2/5] target/riscv: Handle Smrnmi interrupt and exception.
Posted by Alvin Che-Chia Chang(張哲嘉) 3 months, 1 week ago
Hi Tommy,

> -----Original Message-----
> From: qemu-riscv-bounces+alvinga=andestech.com@nongnu.org
> <qemu-riscv-bounces+alvinga=andestech.com@nongnu.org> On Behalf Of
> Tommy Wu
> Sent: Friday, August 9, 2024 4:12 PM
> To: qemu-devel@nongnu.org; qemu-riscv@nongnu.org
> Cc: frank.chang@sifive.com; palmer@dabbelt.com; alistair.francis@wdc.com;
> alistair23@gmail.com; bin.meng@windriver.com; liweiwei@iscas.ac.cn;
> dbarboza@ventanamicro.com; Tommy Wu <tommy.wu@sifive.com>
> Subject: [PATCH v5 2/5] target/riscv: Handle Smrnmi interrupt and exception.
>
> [EXTERNAL MAIL]
>
> 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>
> ---
>  hw/riscv/riscv_hart.c         | 18 ++++++++
>  include/hw/riscv/riscv_hart.h |  4 ++
>  target/riscv/cpu.c            | 11 +++++
>  target/riscv/cpu.h            |  6 +++
>  target/riscv/cpu_bits.h       | 12 ++++++
>  target/riscv/cpu_helper.c     | 77
> +++++++++++++++++++++++++++++++++--
>  6 files changed, 125 insertions(+), 3 deletions(-)
>
> diff --git a/hw/riscv/riscv_hart.c b/hw/riscv/riscv_hart.c index
> 613ea2aaa0..b7d73f7a82 100644
> --- a/hw/riscv/riscv_hart.c
> +++ b/hw/riscv/riscv_hart.c
> @@ -33,6 +33,12 @@ static Property riscv_harts_props[] = {
>      DEFINE_PROP_STRING("cpu-type", RISCVHartArrayState, cpu_type),
>      DEFINE_PROP_UINT64("resetvec", RISCVHartArrayState, resetvec,
>                         DEFAULT_RSTVEC),
> +    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),
>      DEFINE_PROP_END_OF_LIST(),
>  };
>
> @@ -47,6 +53,18 @@ 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 (s->rnmi_irqvec) {
> +            qdev_prop_set_uint64(DEVICE(&s->harts[idx]),
> +                                 "rnmi-interrupt-vector",
> +                                 s->rnmi_irqvec[idx]);
> +        }
> +        if (s->rnmi_excpvec) {
> +            qdev_prop_set_uint64(DEVICE(&s->harts[idx]),
> +                                 "rnmi-exception-vector",
> +                                 s->rnmi_excpvec[idx]);
> +        }
> +    }
>      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); diff --git
> a/include/hw/riscv/riscv_hart.h b/include/hw/riscv/riscv_hart.h index
> 912b4a2682..a6ed73a195 100644
> --- a/include/hw/riscv/riscv_hart.h
> +++ b/include/hw/riscv/riscv_hart.h
> @@ -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;
>  };
>
> diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index
> a90808a3ba..2f64b3df22 100644
> --- a/target/riscv/cpu.c
> +++ b/target/riscv/cpu.c
> @@ -1309,6 +1309,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) @@ -1332,6 +1337,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); @@
> -2681,6 +2688,10 @@ static 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), diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index
> 1619c3acb6..d8ad04ec31 100644
> --- a/target/riscv/cpu.h
> +++ b/target/riscv/cpu.h
> @@ -472,6 +472,11 @@ struct CPUArchState {
>      uint64_t kvm_timer_state;
>      uint64_t kvm_timer_frequency;
>  #endif /* CONFIG_KVM */
> +
> +    /* RNMI */
> +    target_ulong rnmip;
> +    uint64_t rnmi_irqvec;
> +    uint64_t rnmi_excpvec;
>  };
>
>  /*
> @@ -568,6 +573,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 *), diff --git
> a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index
> 32b068f18a..e14b654c35 100644
> --- a/target/riscv/cpu_bits.h
> +++ b/target/riscv/cpu_bits.h
> @@ -662,6 +662,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 */ @@ -711,6 +717,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)
> @@ -942,6 +951,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)
> diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index
> 395a1d9140..f1d464a554 100644
> --- a/target/riscv/cpu_helper.c
> +++ b/target/riscv/cpu_helper.c
> @@ -434,6 +434,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;
> @@ -496,7 +508,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); @@ -619,6
> +633,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;
> @@ -1654,6 +1692,7 @@ void riscv_cpu_do_interrupt(CPUState *cs)
>      bool virt = env->virt_enabled;
>      bool write_gva = false;
>      uint64_t s;
> +    int mode;
>
>      /*
>       * cs->exception is 32-bits wide unlike mcause which is XLEN-bits wide
> @@ -1670,6 +1709,20 @@ void riscv_cpu_do_interrupt(CPUState *cs)
>      target_ulong tinst = 0;
>      target_ulong htval = 0;
>      target_ulong mtval2 = 0;
> +    bool nmi_execp = 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);
> +        return;
> +    }
>
>      if (!async) {
>          /* set tval to badaddr for traps with address information */ @@
> -1755,8 +1808,20 @@ 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 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.
> +     */
> +    nmi_execp = cpu->cfg.ext_smrnmi &&
> +                !get_field(env->mnstatus, MNSTATUS_NMIE) &&
> +                !async &&
> +                mode == PRV_M;
> +
> +    if (mode == PRV_S) {
>          /* handle the trap in S-mode */
>          if (riscv_has_ext(env, RVH)) {
>              uint64_t hdeleg = async ? env->hideleg : env->hedeleg; @@
> -1834,6 +1899,12 @@ void riscv_cpu_do_interrupt(CPUState *cs)
>          env->mtinst = tinst;
>          env->pc = (env->mtvec >> 2 << 2) +
>                    ((async && (env->mtvec & 3) == 1) ? cause * 4 : 0);
Seems above two lines could be removed, since you set env->pc again in the following if-else.

> +        if (cpu->cfg.ext_smrnmi && nmi_execp) {
> +            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);
>      }
>
> --
> 2.39.3
>

BRs,
Alvin Chang

CONFIDENTIALITY NOTICE:

This e-mail (and its attachments) may contain confidential and legally privileged information or information protected from disclosure. If you are not the intended recipient, you are hereby notified that any disclosure, copying, distribution, or use of the information contained herein is strictly prohibited. In this case, please immediately notify the sender by return e-mail, delete the message (and any accompanying documents) and destroy all printed hard copies. Thank you for your cooperation.

Copyright ANDES TECHNOLOGY CORPORATION - All Rights Reserved.
Re: [PATCH v5 2/5] target/riscv: Handle Smrnmi interrupt and exception.
Posted by Tommy Wu 2 months, 3 weeks ago
On Thu, Aug 15, 2024 at 9:40 AM Alvin Che-Chia Chang(張哲嘉)
<alvinga@andestech.com> wrote:
>
> Hi Tommy,
>
> > -----Original Message-----
> > From: qemu-riscv-bounces+alvinga=andestech.com@nongnu.org
> > <qemu-riscv-bounces+alvinga=andestech.com@nongnu.org> On Behalf Of
> > Tommy Wu
> > Sent: Friday, August 9, 2024 4:12 PM
> > To: qemu-devel@nongnu.org; qemu-riscv@nongnu.org
> > Cc: frank.chang@sifive.com; palmer@dabbelt.com; alistair.francis@wdc.com;
> > alistair23@gmail.com; bin.meng@windriver.com; liweiwei@iscas.ac.cn;
> > dbarboza@ventanamicro.com; Tommy Wu <tommy.wu@sifive.com>
> > Subject: [PATCH v5 2/5] target/riscv: Handle Smrnmi interrupt and exception.
> >
> > [EXTERNAL MAIL]
> >
> > 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>
> > ---
> >  hw/riscv/riscv_hart.c         | 18 ++++++++
> >  include/hw/riscv/riscv_hart.h |  4 ++
> >  target/riscv/cpu.c            | 11 +++++
> >  target/riscv/cpu.h            |  6 +++
> >  target/riscv/cpu_bits.h       | 12 ++++++
> >  target/riscv/cpu_helper.c     | 77
> > +++++++++++++++++++++++++++++++++--
> >  6 files changed, 125 insertions(+), 3 deletions(-)
> >
> > diff --git a/hw/riscv/riscv_hart.c b/hw/riscv/riscv_hart.c index
> > 613ea2aaa0..b7d73f7a82 100644
> > --- a/hw/riscv/riscv_hart.c
> > +++ b/hw/riscv/riscv_hart.c
> > @@ -33,6 +33,12 @@ static Property riscv_harts_props[] = {
> >      DEFINE_PROP_STRING("cpu-type", RISCVHartArrayState, cpu_type),
> >      DEFINE_PROP_UINT64("resetvec", RISCVHartArrayState, resetvec,
> >                         DEFAULT_RSTVEC),
> > +    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),
> >      DEFINE_PROP_END_OF_LIST(),
> >  };
> >
> > @@ -47,6 +53,18 @@ 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 (s->rnmi_irqvec) {
> > +            qdev_prop_set_uint64(DEVICE(&s->harts[idx]),
> > +                                 "rnmi-interrupt-vector",
> > +                                 s->rnmi_irqvec[idx]);
> > +        }
> > +        if (s->rnmi_excpvec) {
> > +            qdev_prop_set_uint64(DEVICE(&s->harts[idx]),
> > +                                 "rnmi-exception-vector",
> > +                                 s->rnmi_excpvec[idx]);
> > +        }
> > +    }
> >      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); diff --git
> > a/include/hw/riscv/riscv_hart.h b/include/hw/riscv/riscv_hart.h index
> > 912b4a2682..a6ed73a195 100644
> > --- a/include/hw/riscv/riscv_hart.h
> > +++ b/include/hw/riscv/riscv_hart.h
> > @@ -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;
> >  };
> >
> > diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index
> > a90808a3ba..2f64b3df22 100644
> > --- a/target/riscv/cpu.c
> > +++ b/target/riscv/cpu.c
> > @@ -1309,6 +1309,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) @@ -1332,6 +1337,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); @@
> > -2681,6 +2688,10 @@ static 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), diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index
> > 1619c3acb6..d8ad04ec31 100644
> > --- a/target/riscv/cpu.h
> > +++ b/target/riscv/cpu.h
> > @@ -472,6 +472,11 @@ struct CPUArchState {
> >      uint64_t kvm_timer_state;
> >      uint64_t kvm_timer_frequency;
> >  #endif /* CONFIG_KVM */
> > +
> > +    /* RNMI */
> > +    target_ulong rnmip;
> > +    uint64_t rnmi_irqvec;
> > +    uint64_t rnmi_excpvec;
> >  };
> >
> >  /*
> > @@ -568,6 +573,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 *), diff --git
> > a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index
> > 32b068f18a..e14b654c35 100644
> > --- a/target/riscv/cpu_bits.h
> > +++ b/target/riscv/cpu_bits.h
> > @@ -662,6 +662,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 */ @@ -711,6 +717,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)
> > @@ -942,6 +951,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)
> > diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index
> > 395a1d9140..f1d464a554 100644
> > --- a/target/riscv/cpu_helper.c
> > +++ b/target/riscv/cpu_helper.c
> > @@ -434,6 +434,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;
> > @@ -496,7 +508,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); @@ -619,6
> > +633,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;
> > @@ -1654,6 +1692,7 @@ void riscv_cpu_do_interrupt(CPUState *cs)
> >      bool virt = env->virt_enabled;
> >      bool write_gva = false;
> >      uint64_t s;
> > +    int mode;
> >
> >      /*
> >       * cs->exception is 32-bits wide unlike mcause which is XLEN-bits wide
> > @@ -1670,6 +1709,20 @@ void riscv_cpu_do_interrupt(CPUState *cs)
> >      target_ulong tinst = 0;
> >      target_ulong htval = 0;
> >      target_ulong mtval2 = 0;
> > +    bool nmi_execp = 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);
> > +        return;
> > +    }
> >
> >      if (!async) {
> >          /* set tval to badaddr for traps with address information */ @@
> > -1755,8 +1808,20 @@ 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 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.
> > +     */
> > +    nmi_execp = cpu->cfg.ext_smrnmi &&
> > +                !get_field(env->mnstatus, MNSTATUS_NMIE) &&
> > +                !async &&
> > +                mode == PRV_M;
> > +
> > +    if (mode == PRV_S) {
> >          /* handle the trap in S-mode */
> >          if (riscv_has_ext(env, RVH)) {
> >              uint64_t hdeleg = async ? env->hideleg : env->hedeleg; @@
> > -1834,6 +1899,12 @@ void riscv_cpu_do_interrupt(CPUState *cs)
> >          env->mtinst = tinst;
> >          env->pc = (env->mtvec >> 2 << 2) +
> >                    ((async && (env->mtvec & 3) == 1) ? cause * 4 : 0);
> Seems above two lines could be removed, since you set env->pc again in the following if-else.
>

Thank you! I forgot to delete the redundant code.
I'll delete that in the v6 patchset.

Regards,
Tommy Wu

> > +        if (cpu->cfg.ext_smrnmi && nmi_execp) {
> > +            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);
> >      }
> >
> > --
> > 2.39.3
> >
>
> BRs,
> Alvin Chang
>
> CONFIDENTIALITY NOTICE:
>
> This e-mail (and its attachments) may contain confidential and legally privileged information or information protected from disclosure. If you are not the intended recipient, you are hereby notified that any disclosure, copying, distribution, or use of the information contained herein is strictly prohibited. In this case, please immediately notify the sender by return e-mail, delete the message (and any accompanying documents) and destroy all printed hard copies. Thank you for your cooperation.
>
> Copyright ANDES TECHNOLOGY CORPORATION - All Rights Reserved.