All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC 0/1] target/riscv: add RNMI support
@ 2021-03-09  7:29 frank.chang
  2021-03-09  7:29   ` frank.chang
  2021-03-19 13:28   ` Alistair Francis
  0 siblings, 2 replies; 11+ messages in thread
From: frank.chang @ 2021-03-09  7:29 UTC (permalink / raw)
  To: qemu-devel, qemu-riscv; +Cc: Frank Chang

From: Frank Chang <frank.chang@sifive.com>

This patchset add suport of Resumable NMI (RNMI) in RISC-V.

There are four new CSRs and one new instruction added to allow NMI to be
resumable in RISC-V, which are:

=============================================================
  * mnscratch (0x350)
  * mnepc     (0x351)
  * mncause   (0x352)
  * mnstatus  (0x353)
=============================================================
  * mnret: To return from RNMI interrupt/exception handler.
=============================================================

RNMI also has higher priority than any other interrupts or exceptions
and cannot be disable by software.

RNMI may be used to route to other devices such as Bus Error Unit or
Watchdog Timer in the future.

The interrupt/exception trap handler addresses of RNMI are
implementation defined.

The technical proposal of RNMI can be found:
https://lists.riscv.org/g/tech-privileged/message/421

The port is available here:
https://github.com/sifive/qemu/tree/nmi-upstream-v1

To enable RNMI feature, add cpu argument: 'rnmi=true' and specify
RNMI interrupt/exception handler addresses with 'rnmi_irqvec' and
'rnmi_excpvec' arguments, e.g.
    -cpu rv64,rnmi=true,rnmi_irqvec=0x2000,rnmi_excpvec=0x3000

Frank Chang (1):
  target/riscv: add support of RNMI

 target/riscv/cpu.c                            | 40 +++++++++++++
 target/riscv/cpu.h                            | 16 ++++-
 target/riscv/cpu_bits.h                       | 19 ++++++
 target/riscv/cpu_helper.c                     | 47 +++++++++++++--
 target/riscv/csr.c                            | 59 +++++++++++++++++++
 target/riscv/helper.h                         |  1 +
 target/riscv/insn32.decode                    |  3 +
 .../riscv/insn_trans/trans_privileged.c.inc   | 13 ++++
 target/riscv/op_helper.c                      | 31 ++++++++++
 9 files changed, 224 insertions(+), 5 deletions(-)

--
2.17.1



^ permalink raw reply	[flat|nested] 11+ messages in thread

* [RFC 1/1] target/riscv: add support of RNMI
  2021-03-09  7:29 [RFC 0/1] target/riscv: add RNMI support frank.chang
@ 2021-03-09  7:29   ` frank.chang
  2021-03-19 13:28   ` Alistair Francis
  1 sibling, 0 replies; 11+ messages in thread
From: frank.chang @ 2021-03-09  7:29 UTC (permalink / raw)
  To: qemu-devel, qemu-riscv
  Cc: Keith Packard, Sagar Karandikar, Frank Chang, Bastian Koppelmann,
	Richard Henderson, Alistair Francis, Paolo Bonzini,
	Palmer Dabbelt

From: Frank Chang <frank.chang@sifive.com>

Signed-off-by: Frank Chang <frank.chang@sifive.com>
---
 target/riscv/cpu.c                            | 40 +++++++++++++
 target/riscv/cpu.h                            | 16 ++++-
 target/riscv/cpu_bits.h                       | 19 ++++++
 target/riscv/cpu_helper.c                     | 47 +++++++++++++--
 target/riscv/csr.c                            | 59 +++++++++++++++++++
 target/riscv/helper.h                         |  1 +
 target/riscv/insn32.decode                    |  3 +
 .../riscv/insn_trans/trans_privileged.c.inc   | 13 ++++
 target/riscv/op_helper.c                      | 31 ++++++++++
 9 files changed, 224 insertions(+), 5 deletions(-)

diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index ddea8fbeeb3..07ea2105200 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -137,6 +137,14 @@ static void set_feature(CPURISCVState *env, int feature)
     env->features |= (1ULL << feature);
 }
 
+static void set_rnmi_vectors(CPURISCVState *env, int irqvec, int excpvec)
+{
+#ifndef CONFIG_USER_ONLY
+    env->rnmi_irqvec = irqvec;
+    env->rnmi_excpvec = excpvec;
+#endif
+}
+
 static void set_resetvec(CPURISCVState *env, int resetvec)
 {
 #ifndef CONFIG_USER_ONLY
@@ -372,6 +380,23 @@ static void riscv_cpu_disas_set_info(CPUState *s, disassemble_info *info)
     }
 }
 
+#ifndef CONFIG_USER_ONLY
+static void riscv_cpu_set_rnmi(void *opaque, int irq, int level)
+{
+    RISCVCPU *cpu = opaque;
+    CPURISCVState *env = &cpu->env;
+    CPUState *cs = CPU(cpu);
+
+    if (level) {
+        env->nmip |= 1 << irq;
+        cpu_interrupt(cs, CPU_INTERRUPT_RNMI);
+    } else {
+        env->nmip &= ~(1 << irq);
+        cpu_reset_interrupt(cs, CPU_INTERRUPT_RNMI);
+    }
+}
+#endif
+
 static void riscv_cpu_realize(DeviceState *dev, Error **errp)
 {
     CPUState *cs = CPU(dev);
@@ -415,6 +440,16 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp)
 
     set_resetvec(env, cpu->cfg.resetvec);
 
+    if (cpu->cfg.rnmi) {
+        set_feature(env, RISCV_FEATURE_RNMI);
+        set_rnmi_vectors(env, cpu->cfg.rnmi_irqvec, cpu->cfg.rnmi_excpvec);
+#ifndef CONFIG_USER_ONLY
+        env->nmie = true;
+        qdev_init_gpio_in_named(DEVICE(cpu), riscv_cpu_set_rnmi,
+                                "rnmi", TARGET_LONG_BITS);
+#endif
+    }
+
     /* If only XLEN is set for misa, then set misa from properties */
     if (env->misa == RV32 || env->misa == RV64) {
         /* Do some ISA extension error checking */
@@ -554,6 +589,11 @@ static Property riscv_cpu_properties[] = {
     DEFINE_PROP_BOOL("mmu", RISCVCPU, cfg.mmu, true),
     DEFINE_PROP_BOOL("pmp", RISCVCPU, cfg.pmp, true),
     DEFINE_PROP_UINT64("resetvec", RISCVCPU, cfg.resetvec, DEFAULT_RSTVEC),
+    DEFINE_PROP_BOOL("rnmi", RISCVCPU, cfg.rnmi, false),
+    DEFINE_PROP_UINT64("rnmi_irqvec", RISCVCPU, cfg.rnmi_irqvec,
+                       DEFAULT_RNMI_IRQVEC),
+    DEFINE_PROP_UINT64("rnmi_excpvec", RISCVCPU, cfg.rnmi_excpvec,
+                       DEFAULT_RNMI_EXCPVEC),
     DEFINE_PROP_END_OF_LIST(),
 };
 
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index 0edb2826a27..b9aa403dfec 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -80,7 +80,8 @@
 enum {
     RISCV_FEATURE_MMU,
     RISCV_FEATURE_PMP,
-    RISCV_FEATURE_MISA
+    RISCV_FEATURE_MISA,
+    RISCV_FEATURE_RNMI,
 };
 
 #define PRIV_VERSION_1_10_0 0x00011000
@@ -178,6 +179,16 @@ struct CPURISCVState {
     target_ulong mcause;
     target_ulong mtval;  /* since: priv-1.10.0 */
 
+    /* NMI */
+    target_ulong mnscratch;
+    target_ulong mnepc;
+    target_ulong mncause; /* mncause without bit XLEN-1 set to 1 */
+    target_ulong mnstatus;
+    bool nmie;
+    target_ulong nmip;
+    target_ulong rnmi_irqvec;
+    target_ulong rnmi_excpvec;
+
     /* Hypervisor CSRs */
     target_ulong hstatus;
     target_ulong hedeleg;
@@ -300,6 +311,9 @@ struct RISCVCPU {
         bool mmu;
         bool pmp;
         uint64_t resetvec;
+        bool rnmi;
+        uint64_t rnmi_irqvec;
+        uint64_t rnmi_excpvec;
     } cfg;
 };
 
diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
index caf45992070..94ab76c66b8 100644
--- a/target/riscv/cpu_bits.h
+++ b/target/riscv/cpu_bits.h
@@ -166,6 +166,12 @@
 #define CSR_MTVAL           0x343
 #define CSR_MIP             0x344
 
+/* NMI */
+#define CSR_MNSCRATCH       0x350
+#define CSR_MNEPC           0x351
+#define CSR_MNCAUSE         0x352
+#define CSR_MNSTATUS        0x353
+
 /* Legacy Machine Trap Handling (priv v1.9.1) */
 #define CSR_MBADADDR        0x343
 
@@ -526,6 +532,12 @@
 /* Default Reset Vector adress */
 #define DEFAULT_RSTVEC      0x1000
 
+/* Default RNMI Interrupt Vector address */
+#define DEFAULT_RNMI_IRQVEC     0x1000
+
+/* Default RNMI Exception Vector address */
+#define DEFAULT_RNMI_EXCPVEC    0x1000
+
 /* Exception causes */
 #define EXCP_NONE                                -1 /* sentinel value */
 #define RISCV_EXCP_INST_ADDR_MIS                 0x0
@@ -552,6 +564,9 @@
 #define RISCV_EXCP_INT_FLAG                0x80000000
 #define RISCV_EXCP_INT_MASK                0x7fffffff
 
+/* RNMI mnstatus CSR mask */
+#define MNSTATUS_MPP                       MSTATUS_MPP
+
 /* Interrupt causes */
 #define IRQ_U_SOFT                         0
 #define IRQ_S_SOFT                         1
@@ -592,4 +607,8 @@
 #define MIE_UTIE                           (1 << IRQ_U_TIMER)
 #define MIE_SSIE                           (1 << IRQ_S_SOFT)
 #define MIE_USIE                           (1 << IRQ_U_SOFT)
+
+/* RISC-V-specific interrupt pending bits */
+#define CPU_INTERRUPT_RNMI                 CPU_INTERRUPT_TGT_EXT_0
+
 #endif
diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
index 2f43939fb6d..f331c4b7032 100644
--- a/target/riscv/cpu_helper.c
+++ b/target/riscv/cpu_helper.c
@@ -38,6 +38,19 @@ int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch)
 #ifndef CONFIG_USER_ONLY
 static int riscv_cpu_local_irq_pending(CPURISCVState *env)
 {
+    if (riscv_feature(env, RISCV_FEATURE_RNMI)) {
+        /* Priority: RNMI > Other interrupt. */
+        if (env->nmip && env->nmie) {
+            return ctz64(env->nmip); /* since non-zero */
+        } else if (!env->nmie) {
+            /*
+             * We are already in RNMI handler,
+             * other interrupts cannot preempt.
+             */
+            return EXCP_NONE;
+        }
+    }
+
     target_ulong irqs;
 
     target_ulong mstatus_mie = get_field(env->mstatus, MSTATUS_MIE);
@@ -80,7 +93,8 @@ static int riscv_cpu_local_irq_pending(CPURISCVState *env)
 bool riscv_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
 {
 #if !defined(CONFIG_USER_ONLY)
-    if (interrupt_request & CPU_INTERRUPT_HARD) {
+    if (interrupt_request &
+            (CPU_INTERRUPT_HARD | CPU_INTERRUPT_RNMI)) {
         RISCVCPU *cpu = RISCV_CPU(cs);
         CPURISCVState *env = &cpu->env;
         int interruptno = riscv_cpu_local_irq_pending(env);
@@ -847,6 +861,23 @@ void riscv_cpu_do_interrupt(CPUState *cs)
     target_ulong tval = 0;
     target_ulong htval = 0;
     target_ulong mtval2 = 0;
+    target_ulong nextpc;
+    bool nmi_execp = false;
+
+    if (riscv_feature(env, RISCV_FEATURE_RNMI)) {
+        nmi_execp = !env->nmie && !async;
+
+        if (env->nmip && async) {
+            env->nmie = false;
+            env->mnstatus = set_field(env->mnstatus, MSTATUS_MPP,
+                                      env->priv);
+            env->mncause = cause;
+            env->mnepc = env->pc;
+            env->pc = env->rnmi_irqvec;
+            riscv_cpu_set_mode(env, PRV_M);
+            goto handled;
+        }
+    }
 
     if  (cause == RISCV_EXCP_SEMIHOST) {
         if (env->priv >= PRV_S) {
@@ -905,7 +936,7 @@ 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 &&
+    if (env->priv <= PRV_S && !nmi_execp &&
             cause < TARGET_LONG_BITS && ((deleg >> cause) & 1)) {
         /* handle the trap in S-mode */
         if (riscv_has_ext(env, RVH)) {
@@ -1005,8 +1036,15 @@ void riscv_cpu_do_interrupt(CPUState *cs)
         env->mepc = env->pc;
         env->mbadaddr = tval;
         env->mtval2 = mtval2;
-        env->pc = (env->mtvec >> 2 << 2) +
-            ((async && (env->mtvec & 3) == 1) ? cause * 4 : 0);
+
+        if (nmi_execp) {
+            nextpc = env->rnmi_excpvec;
+        } else {
+            nextpc = (env->mtvec >> 2 << 2) +
+                ((async && (env->mtvec & 3) == 1) ? cause * 4 : 0);
+        }
+        env->pc = nextpc;
+
         riscv_cpu_set_mode(env, PRV_M);
     }
 
@@ -1016,6 +1054,7 @@ void riscv_cpu_do_interrupt(CPUState *cs)
      * RISC-V ISA Specification.
      */
 
+handled:
 #endif
     cs->exception_index = EXCP_NONE; /* mark handled to qemu */
 }
diff --git a/target/riscv/csr.c b/target/riscv/csr.c
index fd2e6363f39..f67c50327ec 100644
--- a/target/riscv/csr.c
+++ b/target/riscv/csr.c
@@ -188,6 +188,11 @@ static int pmp(CPURISCVState *env, int csrno)
 {
     return -!riscv_feature(env, RISCV_FEATURE_PMP);
 }
+
+static int nmi(CPURISCVState *env, int csrno)
+{
+    return -!riscv_feature(env, RISCV_FEATURE_RNMI);
+}
 #endif
 
 /* User Floating-Point CSRs */
@@ -712,6 +717,54 @@ static int write_mbadaddr(CPURISCVState *env, int csrno, target_ulong val)
     return 0;
 }
 
+static int read_mnscratch(CPURISCVState *env, int csrno, target_ulong *val)
+{
+    *val = env->mnscratch;
+    return 0;
+}
+
+static int write_mnscratch(CPURISCVState *env, int csrno, target_ulong val)
+{
+    env->mnscratch = val;
+    return 0;
+}
+
+static int read_mnepc(CPURISCVState *env, int csrno, target_ulong *val)
+{
+    *val = env->mnepc;
+    return 0;
+}
+
+static int write_mnepc(CPURISCVState *env, int csrno, target_ulong val)
+{
+    env->mnepc = val;
+    return 0;
+}
+
+static int read_mncause(CPURISCVState *env, int csrno, target_ulong *val)
+{
+    *val = env->mncause;
+    return 0;
+}
+
+static int write_mncause(CPURISCVState *env, int csrno, target_ulong val)
+{
+    env->mncause = val;
+    return 0;
+}
+
+static int read_mnstatus(CPURISCVState *env, int csrno, target_ulong *val)
+{
+    *val = env->mnstatus;
+    return 0;
+}
+
+static int write_mnstatus(CPURISCVState *env, int csrno, target_ulong val)
+{
+    env->mnstatus = val & MNSTATUS_MPP;
+    return 0;
+}
+
 static int rmw_mip(CPURISCVState *env, int csrno, target_ulong *ret_value,
                    target_ulong new_value, target_ulong write_mask)
 {
@@ -1427,6 +1480,12 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
     [CSR_MBADADDR] = { "mbadaddr", any,  read_mbadaddr, write_mbadaddr },
     [CSR_MIP]      = { "mip",      any,  NULL,    NULL, rmw_mip        },
 
+    /* NMI */
+    [CSR_MNSCRATCH] = { "mnscratch", nmi, read_mnscratch, write_mnscratch },
+    [CSR_MNEPC]     = { "mnepc",     nmi, read_mnepc,     write_mnepc     },
+    [CSR_MNCAUSE]   = { "mncause",   nmi, read_mncause,   write_mncause   },
+    [CSR_MNSTATUS]  = { "mnstatus",  nmi, read_mnstatus,  write_mnstatus  },
+
     /* Supervisor Trap Setup */
     [CSR_SSTATUS]    = { "sstatus",    smode, read_sstatus,    write_sstatus    },
     [CSR_SIE]        = { "sie",        smode, read_sie,        write_sie        },
diff --git a/target/riscv/helper.h b/target/riscv/helper.h
index e3f3f41e891..0914d777d6d 100644
--- a/target/riscv/helper.h
+++ b/target/riscv/helper.h
@@ -65,6 +65,7 @@ DEF_HELPER_4(csrrc, tl, env, tl, tl, tl)
 #ifndef CONFIG_USER_ONLY
 DEF_HELPER_2(sret, tl, env, tl)
 DEF_HELPER_2(mret, tl, env, tl)
+DEF_HELPER_2(mnret, tl, env, tl)
 DEF_HELPER_1(wfi, void, env)
 DEF_HELPER_1(tlb_flush, void, env)
 #endif
diff --git a/target/riscv/insn32.decode b/target/riscv/insn32.decode
index 84080dd18ca..557f3394276 100644
--- a/target/riscv/insn32.decode
+++ b/target/riscv/insn32.decode
@@ -97,6 +97,9 @@ wfi         0001000    00101 00000 000 00000 1110011
 sfence_vma  0001001    ..... ..... 000 00000 1110011 @sfence_vma
 sfence_vm   0001000    00100 ..... 000 00000 1110011 @sfence_vm
 
+# *** NMI ***
+mnret       0111000    00010 00000 000 00000 1110011
+
 # *** RV32I Base Instruction Set ***
 lui      ....................       ..... 0110111 @u
 auipc    ....................       ..... 0010111 @u
diff --git a/target/riscv/insn_trans/trans_privileged.c.inc b/target/riscv/insn_trans/trans_privileged.c.inc
index 32312be2024..63c49dfe6fb 100644
--- a/target/riscv/insn_trans/trans_privileged.c.inc
+++ b/target/riscv/insn_trans/trans_privileged.c.inc
@@ -106,6 +106,19 @@ static bool trans_mret(DisasContext *ctx, arg_mret *a)
 #endif
 }
 
+static bool trans_mnret(DisasContext *ctx, arg_mnret *a)
+{
+#ifndef CONFIG_USER_ONLY
+    tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next);
+    gen_helper_mnret(cpu_pc, cpu_env, cpu_pc);
+    exit_tb(ctx); /* no chaining */
+    ctx->base.is_jmp = DISAS_NORETURN;
+    return true;
+#else
+    return false;
+#endif
+}
+
 static bool trans_wfi(DisasContext *ctx, arg_wfi *a)
 {
 #ifndef CONFIG_USER_ONLY
diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c
index 1eddcb94de7..b9601776153 100644
--- a/target/riscv/op_helper.c
+++ b/target/riscv/op_helper.c
@@ -175,6 +175,37 @@ target_ulong helper_mret(CPURISCVState *env, target_ulong cpu_pc_deb)
     return retpc;
 }
 
+target_ulong helper_mnret(CPURISCVState *env, target_ulong cpu_pc_deb)
+{
+    if (!riscv_feature(env, RISCV_FEATURE_RNMI)) {
+        /* RNMI feature is not presented. */
+        riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
+    }
+
+    if (!(env->priv >= PRV_M)) {
+        riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
+    }
+
+    /* Get return PC from mnepc CSR. */
+    target_ulong retpc = env->mnepc;
+    if (!riscv_has_ext(env, RVC) && (retpc & 0x3)) {
+        riscv_raise_exception(env, RISCV_EXCP_INST_ADDR_MIS, GETPC());
+    }
+
+    /* Get previous privilege level from mnstatus CSR. */
+    target_ulong prev_priv = get_field(env->mnstatus, MNSTATUS_MPP);
+
+    if (!pmp_get_num_rules(env) && (prev_priv != PRV_M)) {
+        riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
+    }
+
+    riscv_cpu_set_mode(env, prev_priv);
+
+    env->nmie = true;
+
+    return retpc;
+}
+
 void helper_wfi(CPURISCVState *env)
 {
     CPUState *cs = env_cpu(env);
-- 
2.17.1



^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [RFC 1/1] target/riscv: add support of RNMI
@ 2021-03-09  7:29   ` frank.chang
  0 siblings, 0 replies; 11+ messages in thread
From: frank.chang @ 2021-03-09  7:29 UTC (permalink / raw)
  To: qemu-devel, qemu-riscv
  Cc: Frank Chang, Palmer Dabbelt, Alistair Francis, Sagar Karandikar,
	Bastian Koppelmann, Richard Henderson, Keith Packard,
	Paolo Bonzini

From: Frank Chang <frank.chang@sifive.com>

Signed-off-by: Frank Chang <frank.chang@sifive.com>
---
 target/riscv/cpu.c                            | 40 +++++++++++++
 target/riscv/cpu.h                            | 16 ++++-
 target/riscv/cpu_bits.h                       | 19 ++++++
 target/riscv/cpu_helper.c                     | 47 +++++++++++++--
 target/riscv/csr.c                            | 59 +++++++++++++++++++
 target/riscv/helper.h                         |  1 +
 target/riscv/insn32.decode                    |  3 +
 .../riscv/insn_trans/trans_privileged.c.inc   | 13 ++++
 target/riscv/op_helper.c                      | 31 ++++++++++
 9 files changed, 224 insertions(+), 5 deletions(-)

diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index ddea8fbeeb3..07ea2105200 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -137,6 +137,14 @@ static void set_feature(CPURISCVState *env, int feature)
     env->features |= (1ULL << feature);
 }
 
+static void set_rnmi_vectors(CPURISCVState *env, int irqvec, int excpvec)
+{
+#ifndef CONFIG_USER_ONLY
+    env->rnmi_irqvec = irqvec;
+    env->rnmi_excpvec = excpvec;
+#endif
+}
+
 static void set_resetvec(CPURISCVState *env, int resetvec)
 {
 #ifndef CONFIG_USER_ONLY
@@ -372,6 +380,23 @@ static void riscv_cpu_disas_set_info(CPUState *s, disassemble_info *info)
     }
 }
 
+#ifndef CONFIG_USER_ONLY
+static void riscv_cpu_set_rnmi(void *opaque, int irq, int level)
+{
+    RISCVCPU *cpu = opaque;
+    CPURISCVState *env = &cpu->env;
+    CPUState *cs = CPU(cpu);
+
+    if (level) {
+        env->nmip |= 1 << irq;
+        cpu_interrupt(cs, CPU_INTERRUPT_RNMI);
+    } else {
+        env->nmip &= ~(1 << irq);
+        cpu_reset_interrupt(cs, CPU_INTERRUPT_RNMI);
+    }
+}
+#endif
+
 static void riscv_cpu_realize(DeviceState *dev, Error **errp)
 {
     CPUState *cs = CPU(dev);
@@ -415,6 +440,16 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp)
 
     set_resetvec(env, cpu->cfg.resetvec);
 
+    if (cpu->cfg.rnmi) {
+        set_feature(env, RISCV_FEATURE_RNMI);
+        set_rnmi_vectors(env, cpu->cfg.rnmi_irqvec, cpu->cfg.rnmi_excpvec);
+#ifndef CONFIG_USER_ONLY
+        env->nmie = true;
+        qdev_init_gpio_in_named(DEVICE(cpu), riscv_cpu_set_rnmi,
+                                "rnmi", TARGET_LONG_BITS);
+#endif
+    }
+
     /* If only XLEN is set for misa, then set misa from properties */
     if (env->misa == RV32 || env->misa == RV64) {
         /* Do some ISA extension error checking */
@@ -554,6 +589,11 @@ static Property riscv_cpu_properties[] = {
     DEFINE_PROP_BOOL("mmu", RISCVCPU, cfg.mmu, true),
     DEFINE_PROP_BOOL("pmp", RISCVCPU, cfg.pmp, true),
     DEFINE_PROP_UINT64("resetvec", RISCVCPU, cfg.resetvec, DEFAULT_RSTVEC),
+    DEFINE_PROP_BOOL("rnmi", RISCVCPU, cfg.rnmi, false),
+    DEFINE_PROP_UINT64("rnmi_irqvec", RISCVCPU, cfg.rnmi_irqvec,
+                       DEFAULT_RNMI_IRQVEC),
+    DEFINE_PROP_UINT64("rnmi_excpvec", RISCVCPU, cfg.rnmi_excpvec,
+                       DEFAULT_RNMI_EXCPVEC),
     DEFINE_PROP_END_OF_LIST(),
 };
 
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index 0edb2826a27..b9aa403dfec 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -80,7 +80,8 @@
 enum {
     RISCV_FEATURE_MMU,
     RISCV_FEATURE_PMP,
-    RISCV_FEATURE_MISA
+    RISCV_FEATURE_MISA,
+    RISCV_FEATURE_RNMI,
 };
 
 #define PRIV_VERSION_1_10_0 0x00011000
@@ -178,6 +179,16 @@ struct CPURISCVState {
     target_ulong mcause;
     target_ulong mtval;  /* since: priv-1.10.0 */
 
+    /* NMI */
+    target_ulong mnscratch;
+    target_ulong mnepc;
+    target_ulong mncause; /* mncause without bit XLEN-1 set to 1 */
+    target_ulong mnstatus;
+    bool nmie;
+    target_ulong nmip;
+    target_ulong rnmi_irqvec;
+    target_ulong rnmi_excpvec;
+
     /* Hypervisor CSRs */
     target_ulong hstatus;
     target_ulong hedeleg;
@@ -300,6 +311,9 @@ struct RISCVCPU {
         bool mmu;
         bool pmp;
         uint64_t resetvec;
+        bool rnmi;
+        uint64_t rnmi_irqvec;
+        uint64_t rnmi_excpvec;
     } cfg;
 };
 
diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
index caf45992070..94ab76c66b8 100644
--- a/target/riscv/cpu_bits.h
+++ b/target/riscv/cpu_bits.h
@@ -166,6 +166,12 @@
 #define CSR_MTVAL           0x343
 #define CSR_MIP             0x344
 
+/* NMI */
+#define CSR_MNSCRATCH       0x350
+#define CSR_MNEPC           0x351
+#define CSR_MNCAUSE         0x352
+#define CSR_MNSTATUS        0x353
+
 /* Legacy Machine Trap Handling (priv v1.9.1) */
 #define CSR_MBADADDR        0x343
 
@@ -526,6 +532,12 @@
 /* Default Reset Vector adress */
 #define DEFAULT_RSTVEC      0x1000
 
+/* Default RNMI Interrupt Vector address */
+#define DEFAULT_RNMI_IRQVEC     0x1000
+
+/* Default RNMI Exception Vector address */
+#define DEFAULT_RNMI_EXCPVEC    0x1000
+
 /* Exception causes */
 #define EXCP_NONE                                -1 /* sentinel value */
 #define RISCV_EXCP_INST_ADDR_MIS                 0x0
@@ -552,6 +564,9 @@
 #define RISCV_EXCP_INT_FLAG                0x80000000
 #define RISCV_EXCP_INT_MASK                0x7fffffff
 
+/* RNMI mnstatus CSR mask */
+#define MNSTATUS_MPP                       MSTATUS_MPP
+
 /* Interrupt causes */
 #define IRQ_U_SOFT                         0
 #define IRQ_S_SOFT                         1
@@ -592,4 +607,8 @@
 #define MIE_UTIE                           (1 << IRQ_U_TIMER)
 #define MIE_SSIE                           (1 << IRQ_S_SOFT)
 #define MIE_USIE                           (1 << IRQ_U_SOFT)
+
+/* RISC-V-specific interrupt pending bits */
+#define CPU_INTERRUPT_RNMI                 CPU_INTERRUPT_TGT_EXT_0
+
 #endif
diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
index 2f43939fb6d..f331c4b7032 100644
--- a/target/riscv/cpu_helper.c
+++ b/target/riscv/cpu_helper.c
@@ -38,6 +38,19 @@ int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch)
 #ifndef CONFIG_USER_ONLY
 static int riscv_cpu_local_irq_pending(CPURISCVState *env)
 {
+    if (riscv_feature(env, RISCV_FEATURE_RNMI)) {
+        /* Priority: RNMI > Other interrupt. */
+        if (env->nmip && env->nmie) {
+            return ctz64(env->nmip); /* since non-zero */
+        } else if (!env->nmie) {
+            /*
+             * We are already in RNMI handler,
+             * other interrupts cannot preempt.
+             */
+            return EXCP_NONE;
+        }
+    }
+
     target_ulong irqs;
 
     target_ulong mstatus_mie = get_field(env->mstatus, MSTATUS_MIE);
@@ -80,7 +93,8 @@ static int riscv_cpu_local_irq_pending(CPURISCVState *env)
 bool riscv_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
 {
 #if !defined(CONFIG_USER_ONLY)
-    if (interrupt_request & CPU_INTERRUPT_HARD) {
+    if (interrupt_request &
+            (CPU_INTERRUPT_HARD | CPU_INTERRUPT_RNMI)) {
         RISCVCPU *cpu = RISCV_CPU(cs);
         CPURISCVState *env = &cpu->env;
         int interruptno = riscv_cpu_local_irq_pending(env);
@@ -847,6 +861,23 @@ void riscv_cpu_do_interrupt(CPUState *cs)
     target_ulong tval = 0;
     target_ulong htval = 0;
     target_ulong mtval2 = 0;
+    target_ulong nextpc;
+    bool nmi_execp = false;
+
+    if (riscv_feature(env, RISCV_FEATURE_RNMI)) {
+        nmi_execp = !env->nmie && !async;
+
+        if (env->nmip && async) {
+            env->nmie = false;
+            env->mnstatus = set_field(env->mnstatus, MSTATUS_MPP,
+                                      env->priv);
+            env->mncause = cause;
+            env->mnepc = env->pc;
+            env->pc = env->rnmi_irqvec;
+            riscv_cpu_set_mode(env, PRV_M);
+            goto handled;
+        }
+    }
 
     if  (cause == RISCV_EXCP_SEMIHOST) {
         if (env->priv >= PRV_S) {
@@ -905,7 +936,7 @@ 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 &&
+    if (env->priv <= PRV_S && !nmi_execp &&
             cause < TARGET_LONG_BITS && ((deleg >> cause) & 1)) {
         /* handle the trap in S-mode */
         if (riscv_has_ext(env, RVH)) {
@@ -1005,8 +1036,15 @@ void riscv_cpu_do_interrupt(CPUState *cs)
         env->mepc = env->pc;
         env->mbadaddr = tval;
         env->mtval2 = mtval2;
-        env->pc = (env->mtvec >> 2 << 2) +
-            ((async && (env->mtvec & 3) == 1) ? cause * 4 : 0);
+
+        if (nmi_execp) {
+            nextpc = env->rnmi_excpvec;
+        } else {
+            nextpc = (env->mtvec >> 2 << 2) +
+                ((async && (env->mtvec & 3) == 1) ? cause * 4 : 0);
+        }
+        env->pc = nextpc;
+
         riscv_cpu_set_mode(env, PRV_M);
     }
 
@@ -1016,6 +1054,7 @@ void riscv_cpu_do_interrupt(CPUState *cs)
      * RISC-V ISA Specification.
      */
 
+handled:
 #endif
     cs->exception_index = EXCP_NONE; /* mark handled to qemu */
 }
diff --git a/target/riscv/csr.c b/target/riscv/csr.c
index fd2e6363f39..f67c50327ec 100644
--- a/target/riscv/csr.c
+++ b/target/riscv/csr.c
@@ -188,6 +188,11 @@ static int pmp(CPURISCVState *env, int csrno)
 {
     return -!riscv_feature(env, RISCV_FEATURE_PMP);
 }
+
+static int nmi(CPURISCVState *env, int csrno)
+{
+    return -!riscv_feature(env, RISCV_FEATURE_RNMI);
+}
 #endif
 
 /* User Floating-Point CSRs */
@@ -712,6 +717,54 @@ static int write_mbadaddr(CPURISCVState *env, int csrno, target_ulong val)
     return 0;
 }
 
+static int read_mnscratch(CPURISCVState *env, int csrno, target_ulong *val)
+{
+    *val = env->mnscratch;
+    return 0;
+}
+
+static int write_mnscratch(CPURISCVState *env, int csrno, target_ulong val)
+{
+    env->mnscratch = val;
+    return 0;
+}
+
+static int read_mnepc(CPURISCVState *env, int csrno, target_ulong *val)
+{
+    *val = env->mnepc;
+    return 0;
+}
+
+static int write_mnepc(CPURISCVState *env, int csrno, target_ulong val)
+{
+    env->mnepc = val;
+    return 0;
+}
+
+static int read_mncause(CPURISCVState *env, int csrno, target_ulong *val)
+{
+    *val = env->mncause;
+    return 0;
+}
+
+static int write_mncause(CPURISCVState *env, int csrno, target_ulong val)
+{
+    env->mncause = val;
+    return 0;
+}
+
+static int read_mnstatus(CPURISCVState *env, int csrno, target_ulong *val)
+{
+    *val = env->mnstatus;
+    return 0;
+}
+
+static int write_mnstatus(CPURISCVState *env, int csrno, target_ulong val)
+{
+    env->mnstatus = val & MNSTATUS_MPP;
+    return 0;
+}
+
 static int rmw_mip(CPURISCVState *env, int csrno, target_ulong *ret_value,
                    target_ulong new_value, target_ulong write_mask)
 {
@@ -1427,6 +1480,12 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
     [CSR_MBADADDR] = { "mbadaddr", any,  read_mbadaddr, write_mbadaddr },
     [CSR_MIP]      = { "mip",      any,  NULL,    NULL, rmw_mip        },
 
+    /* NMI */
+    [CSR_MNSCRATCH] = { "mnscratch", nmi, read_mnscratch, write_mnscratch },
+    [CSR_MNEPC]     = { "mnepc",     nmi, read_mnepc,     write_mnepc     },
+    [CSR_MNCAUSE]   = { "mncause",   nmi, read_mncause,   write_mncause   },
+    [CSR_MNSTATUS]  = { "mnstatus",  nmi, read_mnstatus,  write_mnstatus  },
+
     /* Supervisor Trap Setup */
     [CSR_SSTATUS]    = { "sstatus",    smode, read_sstatus,    write_sstatus    },
     [CSR_SIE]        = { "sie",        smode, read_sie,        write_sie        },
diff --git a/target/riscv/helper.h b/target/riscv/helper.h
index e3f3f41e891..0914d777d6d 100644
--- a/target/riscv/helper.h
+++ b/target/riscv/helper.h
@@ -65,6 +65,7 @@ DEF_HELPER_4(csrrc, tl, env, tl, tl, tl)
 #ifndef CONFIG_USER_ONLY
 DEF_HELPER_2(sret, tl, env, tl)
 DEF_HELPER_2(mret, tl, env, tl)
+DEF_HELPER_2(mnret, tl, env, tl)
 DEF_HELPER_1(wfi, void, env)
 DEF_HELPER_1(tlb_flush, void, env)
 #endif
diff --git a/target/riscv/insn32.decode b/target/riscv/insn32.decode
index 84080dd18ca..557f3394276 100644
--- a/target/riscv/insn32.decode
+++ b/target/riscv/insn32.decode
@@ -97,6 +97,9 @@ wfi         0001000    00101 00000 000 00000 1110011
 sfence_vma  0001001    ..... ..... 000 00000 1110011 @sfence_vma
 sfence_vm   0001000    00100 ..... 000 00000 1110011 @sfence_vm
 
+# *** NMI ***
+mnret       0111000    00010 00000 000 00000 1110011
+
 # *** RV32I Base Instruction Set ***
 lui      ....................       ..... 0110111 @u
 auipc    ....................       ..... 0010111 @u
diff --git a/target/riscv/insn_trans/trans_privileged.c.inc b/target/riscv/insn_trans/trans_privileged.c.inc
index 32312be2024..63c49dfe6fb 100644
--- a/target/riscv/insn_trans/trans_privileged.c.inc
+++ b/target/riscv/insn_trans/trans_privileged.c.inc
@@ -106,6 +106,19 @@ static bool trans_mret(DisasContext *ctx, arg_mret *a)
 #endif
 }
 
+static bool trans_mnret(DisasContext *ctx, arg_mnret *a)
+{
+#ifndef CONFIG_USER_ONLY
+    tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next);
+    gen_helper_mnret(cpu_pc, cpu_env, cpu_pc);
+    exit_tb(ctx); /* no chaining */
+    ctx->base.is_jmp = DISAS_NORETURN;
+    return true;
+#else
+    return false;
+#endif
+}
+
 static bool trans_wfi(DisasContext *ctx, arg_wfi *a)
 {
 #ifndef CONFIG_USER_ONLY
diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c
index 1eddcb94de7..b9601776153 100644
--- a/target/riscv/op_helper.c
+++ b/target/riscv/op_helper.c
@@ -175,6 +175,37 @@ target_ulong helper_mret(CPURISCVState *env, target_ulong cpu_pc_deb)
     return retpc;
 }
 
+target_ulong helper_mnret(CPURISCVState *env, target_ulong cpu_pc_deb)
+{
+    if (!riscv_feature(env, RISCV_FEATURE_RNMI)) {
+        /* RNMI feature is not presented. */
+        riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
+    }
+
+    if (!(env->priv >= PRV_M)) {
+        riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
+    }
+
+    /* Get return PC from mnepc CSR. */
+    target_ulong retpc = env->mnepc;
+    if (!riscv_has_ext(env, RVC) && (retpc & 0x3)) {
+        riscv_raise_exception(env, RISCV_EXCP_INST_ADDR_MIS, GETPC());
+    }
+
+    /* Get previous privilege level from mnstatus CSR. */
+    target_ulong prev_priv = get_field(env->mnstatus, MNSTATUS_MPP);
+
+    if (!pmp_get_num_rules(env) && (prev_priv != PRV_M)) {
+        riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
+    }
+
+    riscv_cpu_set_mode(env, prev_priv);
+
+    env->nmie = true;
+
+    return retpc;
+}
+
 void helper_wfi(CPURISCVState *env)
 {
     CPUState *cs = env_cpu(env);
-- 
2.17.1



^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [RFC 1/1] target/riscv: add support of RNMI
  2021-03-09  7:29   ` frank.chang
@ 2021-03-19 13:27     ` Alistair Francis
  -1 siblings, 0 replies; 11+ messages in thread
From: Alistair Francis @ 2021-03-19 13:27 UTC (permalink / raw)
  To: Frank Chang
  Cc: Keith Packard, open list:RISC-V, Sagar Karandikar,
	Bastian Koppelmann, Richard Henderson,
	qemu-devel@nongnu.org Developers, Alistair Francis,
	Paolo Bonzini, Palmer Dabbelt

On Tue, Mar 9, 2021 at 2:30 AM <frank.chang@sifive.com> wrote:
>
> From: Frank Chang <frank.chang@sifive.com>
>
> Signed-off-by: Frank Chang <frank.chang@sifive.com>

I had a quick look and this looks fine. I haven't compared it to the
spec yet though.

When you send the patch series do you mind splitting it up a bit more?
It just makes it easier to review.

Alistair

> ---
>  target/riscv/cpu.c                            | 40 +++++++++++++
>  target/riscv/cpu.h                            | 16 ++++-
>  target/riscv/cpu_bits.h                       | 19 ++++++
>  target/riscv/cpu_helper.c                     | 47 +++++++++++++--
>  target/riscv/csr.c                            | 59 +++++++++++++++++++
>  target/riscv/helper.h                         |  1 +
>  target/riscv/insn32.decode                    |  3 +
>  .../riscv/insn_trans/trans_privileged.c.inc   | 13 ++++
>  target/riscv/op_helper.c                      | 31 ++++++++++
>  9 files changed, 224 insertions(+), 5 deletions(-)
>
> diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
> index ddea8fbeeb3..07ea2105200 100644
> --- a/target/riscv/cpu.c
> +++ b/target/riscv/cpu.c
> @@ -137,6 +137,14 @@ static void set_feature(CPURISCVState *env, int feature)
>      env->features |= (1ULL << feature);
>  }
>
> +static void set_rnmi_vectors(CPURISCVState *env, int irqvec, int excpvec)
> +{
> +#ifndef CONFIG_USER_ONLY
> +    env->rnmi_irqvec = irqvec;
> +    env->rnmi_excpvec = excpvec;
> +#endif
> +}
> +
>  static void set_resetvec(CPURISCVState *env, int resetvec)
>  {
>  #ifndef CONFIG_USER_ONLY
> @@ -372,6 +380,23 @@ static void riscv_cpu_disas_set_info(CPUState *s, disassemble_info *info)
>      }
>  }
>
> +#ifndef CONFIG_USER_ONLY
> +static void riscv_cpu_set_rnmi(void *opaque, int irq, int level)
> +{
> +    RISCVCPU *cpu = opaque;
> +    CPURISCVState *env = &cpu->env;
> +    CPUState *cs = CPU(cpu);
> +
> +    if (level) {
> +        env->nmip |= 1 << irq;
> +        cpu_interrupt(cs, CPU_INTERRUPT_RNMI);
> +    } else {
> +        env->nmip &= ~(1 << irq);
> +        cpu_reset_interrupt(cs, CPU_INTERRUPT_RNMI);
> +    }
> +}
> +#endif
> +
>  static void riscv_cpu_realize(DeviceState *dev, Error **errp)
>  {
>      CPUState *cs = CPU(dev);
> @@ -415,6 +440,16 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp)
>
>      set_resetvec(env, cpu->cfg.resetvec);
>
> +    if (cpu->cfg.rnmi) {
> +        set_feature(env, RISCV_FEATURE_RNMI);
> +        set_rnmi_vectors(env, cpu->cfg.rnmi_irqvec, cpu->cfg.rnmi_excpvec);
> +#ifndef CONFIG_USER_ONLY
> +        env->nmie = true;
> +        qdev_init_gpio_in_named(DEVICE(cpu), riscv_cpu_set_rnmi,
> +                                "rnmi", TARGET_LONG_BITS);
> +#endif
> +    }
> +
>      /* If only XLEN is set for misa, then set misa from properties */
>      if (env->misa == RV32 || env->misa == RV64) {
>          /* Do some ISA extension error checking */
> @@ -554,6 +589,11 @@ static Property riscv_cpu_properties[] = {
>      DEFINE_PROP_BOOL("mmu", RISCVCPU, cfg.mmu, true),
>      DEFINE_PROP_BOOL("pmp", RISCVCPU, cfg.pmp, true),
>      DEFINE_PROP_UINT64("resetvec", RISCVCPU, cfg.resetvec, DEFAULT_RSTVEC),
> +    DEFINE_PROP_BOOL("rnmi", RISCVCPU, cfg.rnmi, false),
> +    DEFINE_PROP_UINT64("rnmi_irqvec", RISCVCPU, cfg.rnmi_irqvec,
> +                       DEFAULT_RNMI_IRQVEC),
> +    DEFINE_PROP_UINT64("rnmi_excpvec", RISCVCPU, cfg.rnmi_excpvec,
> +                       DEFAULT_RNMI_EXCPVEC),
>      DEFINE_PROP_END_OF_LIST(),
>  };
>
> diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
> index 0edb2826a27..b9aa403dfec 100644
> --- a/target/riscv/cpu.h
> +++ b/target/riscv/cpu.h
> @@ -80,7 +80,8 @@
>  enum {
>      RISCV_FEATURE_MMU,
>      RISCV_FEATURE_PMP,
> -    RISCV_FEATURE_MISA
> +    RISCV_FEATURE_MISA,
> +    RISCV_FEATURE_RNMI,
>  };
>
>  #define PRIV_VERSION_1_10_0 0x00011000
> @@ -178,6 +179,16 @@ struct CPURISCVState {
>      target_ulong mcause;
>      target_ulong mtval;  /* since: priv-1.10.0 */
>
> +    /* NMI */
> +    target_ulong mnscratch;
> +    target_ulong mnepc;
> +    target_ulong mncause; /* mncause without bit XLEN-1 set to 1 */
> +    target_ulong mnstatus;
> +    bool nmie;
> +    target_ulong nmip;
> +    target_ulong rnmi_irqvec;
> +    target_ulong rnmi_excpvec;
> +
>      /* Hypervisor CSRs */
>      target_ulong hstatus;
>      target_ulong hedeleg;
> @@ -300,6 +311,9 @@ struct RISCVCPU {
>          bool mmu;
>          bool pmp;
>          uint64_t resetvec;
> +        bool rnmi;
> +        uint64_t rnmi_irqvec;
> +        uint64_t rnmi_excpvec;
>      } cfg;
>  };
>
> diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
> index caf45992070..94ab76c66b8 100644
> --- a/target/riscv/cpu_bits.h
> +++ b/target/riscv/cpu_bits.h
> @@ -166,6 +166,12 @@
>  #define CSR_MTVAL           0x343
>  #define CSR_MIP             0x344
>
> +/* NMI */
> +#define CSR_MNSCRATCH       0x350
> +#define CSR_MNEPC           0x351
> +#define CSR_MNCAUSE         0x352
> +#define CSR_MNSTATUS        0x353
> +
>  /* Legacy Machine Trap Handling (priv v1.9.1) */
>  #define CSR_MBADADDR        0x343
>
> @@ -526,6 +532,12 @@
>  /* Default Reset Vector adress */
>  #define DEFAULT_RSTVEC      0x1000
>
> +/* Default RNMI Interrupt Vector address */
> +#define DEFAULT_RNMI_IRQVEC     0x1000
> +
> +/* Default RNMI Exception Vector address */
> +#define DEFAULT_RNMI_EXCPVEC    0x1000
> +
>  /* Exception causes */
>  #define EXCP_NONE                                -1 /* sentinel value */
>  #define RISCV_EXCP_INST_ADDR_MIS                 0x0
> @@ -552,6 +564,9 @@
>  #define RISCV_EXCP_INT_FLAG                0x80000000
>  #define RISCV_EXCP_INT_MASK                0x7fffffff
>
> +/* RNMI mnstatus CSR mask */
> +#define MNSTATUS_MPP                       MSTATUS_MPP
> +
>  /* Interrupt causes */
>  #define IRQ_U_SOFT                         0
>  #define IRQ_S_SOFT                         1
> @@ -592,4 +607,8 @@
>  #define MIE_UTIE                           (1 << IRQ_U_TIMER)
>  #define MIE_SSIE                           (1 << IRQ_S_SOFT)
>  #define MIE_USIE                           (1 << IRQ_U_SOFT)
> +
> +/* RISC-V-specific interrupt pending bits */
> +#define CPU_INTERRUPT_RNMI                 CPU_INTERRUPT_TGT_EXT_0
> +
>  #endif
> diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
> index 2f43939fb6d..f331c4b7032 100644
> --- a/target/riscv/cpu_helper.c
> +++ b/target/riscv/cpu_helper.c
> @@ -38,6 +38,19 @@ int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch)
>  #ifndef CONFIG_USER_ONLY
>  static int riscv_cpu_local_irq_pending(CPURISCVState *env)
>  {
> +    if (riscv_feature(env, RISCV_FEATURE_RNMI)) {
> +        /* Priority: RNMI > Other interrupt. */
> +        if (env->nmip && env->nmie) {
> +            return ctz64(env->nmip); /* since non-zero */
> +        } else if (!env->nmie) {
> +            /*
> +             * We are already in RNMI handler,
> +             * other interrupts cannot preempt.
> +             */
> +            return EXCP_NONE;
> +        }
> +    }
> +
>      target_ulong irqs;
>
>      target_ulong mstatus_mie = get_field(env->mstatus, MSTATUS_MIE);
> @@ -80,7 +93,8 @@ static int riscv_cpu_local_irq_pending(CPURISCVState *env)
>  bool riscv_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
>  {
>  #if !defined(CONFIG_USER_ONLY)
> -    if (interrupt_request & CPU_INTERRUPT_HARD) {
> +    if (interrupt_request &
> +            (CPU_INTERRUPT_HARD | CPU_INTERRUPT_RNMI)) {
>          RISCVCPU *cpu = RISCV_CPU(cs);
>          CPURISCVState *env = &cpu->env;
>          int interruptno = riscv_cpu_local_irq_pending(env);
> @@ -847,6 +861,23 @@ void riscv_cpu_do_interrupt(CPUState *cs)
>      target_ulong tval = 0;
>      target_ulong htval = 0;
>      target_ulong mtval2 = 0;
> +    target_ulong nextpc;
> +    bool nmi_execp = false;
> +
> +    if (riscv_feature(env, RISCV_FEATURE_RNMI)) {
> +        nmi_execp = !env->nmie && !async;
> +
> +        if (env->nmip && async) {
> +            env->nmie = false;
> +            env->mnstatus = set_field(env->mnstatus, MSTATUS_MPP,
> +                                      env->priv);
> +            env->mncause = cause;
> +            env->mnepc = env->pc;
> +            env->pc = env->rnmi_irqvec;
> +            riscv_cpu_set_mode(env, PRV_M);
> +            goto handled;
> +        }
> +    }
>
>      if  (cause == RISCV_EXCP_SEMIHOST) {
>          if (env->priv >= PRV_S) {
> @@ -905,7 +936,7 @@ 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 &&
> +    if (env->priv <= PRV_S && !nmi_execp &&
>              cause < TARGET_LONG_BITS && ((deleg >> cause) & 1)) {
>          /* handle the trap in S-mode */
>          if (riscv_has_ext(env, RVH)) {
> @@ -1005,8 +1036,15 @@ void riscv_cpu_do_interrupt(CPUState *cs)
>          env->mepc = env->pc;
>          env->mbadaddr = tval;
>          env->mtval2 = mtval2;
> -        env->pc = (env->mtvec >> 2 << 2) +
> -            ((async && (env->mtvec & 3) == 1) ? cause * 4 : 0);
> +
> +        if (nmi_execp) {
> +            nextpc = env->rnmi_excpvec;
> +        } else {
> +            nextpc = (env->mtvec >> 2 << 2) +
> +                ((async && (env->mtvec & 3) == 1) ? cause * 4 : 0);
> +        }
> +        env->pc = nextpc;
> +
>          riscv_cpu_set_mode(env, PRV_M);
>      }
>
> @@ -1016,6 +1054,7 @@ void riscv_cpu_do_interrupt(CPUState *cs)
>       * RISC-V ISA Specification.
>       */
>
> +handled:
>  #endif
>      cs->exception_index = EXCP_NONE; /* mark handled to qemu */
>  }
> diff --git a/target/riscv/csr.c b/target/riscv/csr.c
> index fd2e6363f39..f67c50327ec 100644
> --- a/target/riscv/csr.c
> +++ b/target/riscv/csr.c
> @@ -188,6 +188,11 @@ static int pmp(CPURISCVState *env, int csrno)
>  {
>      return -!riscv_feature(env, RISCV_FEATURE_PMP);
>  }
> +
> +static int nmi(CPURISCVState *env, int csrno)
> +{
> +    return -!riscv_feature(env, RISCV_FEATURE_RNMI);
> +}
>  #endif
>
>  /* User Floating-Point CSRs */
> @@ -712,6 +717,54 @@ static int write_mbadaddr(CPURISCVState *env, int csrno, target_ulong val)
>      return 0;
>  }
>
> +static int read_mnscratch(CPURISCVState *env, int csrno, target_ulong *val)
> +{
> +    *val = env->mnscratch;
> +    return 0;
> +}
> +
> +static int write_mnscratch(CPURISCVState *env, int csrno, target_ulong val)
> +{
> +    env->mnscratch = val;
> +    return 0;
> +}
> +
> +static int read_mnepc(CPURISCVState *env, int csrno, target_ulong *val)
> +{
> +    *val = env->mnepc;
> +    return 0;
> +}
> +
> +static int write_mnepc(CPURISCVState *env, int csrno, target_ulong val)
> +{
> +    env->mnepc = val;
> +    return 0;
> +}
> +
> +static int read_mncause(CPURISCVState *env, int csrno, target_ulong *val)
> +{
> +    *val = env->mncause;
> +    return 0;
> +}
> +
> +static int write_mncause(CPURISCVState *env, int csrno, target_ulong val)
> +{
> +    env->mncause = val;
> +    return 0;
> +}
> +
> +static int read_mnstatus(CPURISCVState *env, int csrno, target_ulong *val)
> +{
> +    *val = env->mnstatus;
> +    return 0;
> +}
> +
> +static int write_mnstatus(CPURISCVState *env, int csrno, target_ulong val)
> +{
> +    env->mnstatus = val & MNSTATUS_MPP;
> +    return 0;
> +}
> +
>  static int rmw_mip(CPURISCVState *env, int csrno, target_ulong *ret_value,
>                     target_ulong new_value, target_ulong write_mask)
>  {
> @@ -1427,6 +1480,12 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
>      [CSR_MBADADDR] = { "mbadaddr", any,  read_mbadaddr, write_mbadaddr },
>      [CSR_MIP]      = { "mip",      any,  NULL,    NULL, rmw_mip        },
>
> +    /* NMI */
> +    [CSR_MNSCRATCH] = { "mnscratch", nmi, read_mnscratch, write_mnscratch },
> +    [CSR_MNEPC]     = { "mnepc",     nmi, read_mnepc,     write_mnepc     },
> +    [CSR_MNCAUSE]   = { "mncause",   nmi, read_mncause,   write_mncause   },
> +    [CSR_MNSTATUS]  = { "mnstatus",  nmi, read_mnstatus,  write_mnstatus  },
> +
>      /* Supervisor Trap Setup */
>      [CSR_SSTATUS]    = { "sstatus",    smode, read_sstatus,    write_sstatus    },
>      [CSR_SIE]        = { "sie",        smode, read_sie,        write_sie        },
> diff --git a/target/riscv/helper.h b/target/riscv/helper.h
> index e3f3f41e891..0914d777d6d 100644
> --- a/target/riscv/helper.h
> +++ b/target/riscv/helper.h
> @@ -65,6 +65,7 @@ DEF_HELPER_4(csrrc, tl, env, tl, tl, tl)
>  #ifndef CONFIG_USER_ONLY
>  DEF_HELPER_2(sret, tl, env, tl)
>  DEF_HELPER_2(mret, tl, env, tl)
> +DEF_HELPER_2(mnret, tl, env, tl)
>  DEF_HELPER_1(wfi, void, env)
>  DEF_HELPER_1(tlb_flush, void, env)
>  #endif
> diff --git a/target/riscv/insn32.decode b/target/riscv/insn32.decode
> index 84080dd18ca..557f3394276 100644
> --- a/target/riscv/insn32.decode
> +++ b/target/riscv/insn32.decode
> @@ -97,6 +97,9 @@ wfi         0001000    00101 00000 000 00000 1110011
>  sfence_vma  0001001    ..... ..... 000 00000 1110011 @sfence_vma
>  sfence_vm   0001000    00100 ..... 000 00000 1110011 @sfence_vm
>
> +# *** NMI ***
> +mnret       0111000    00010 00000 000 00000 1110011
> +
>  # *** RV32I Base Instruction Set ***
>  lui      ....................       ..... 0110111 @u
>  auipc    ....................       ..... 0010111 @u
> diff --git a/target/riscv/insn_trans/trans_privileged.c.inc b/target/riscv/insn_trans/trans_privileged.c.inc
> index 32312be2024..63c49dfe6fb 100644
> --- a/target/riscv/insn_trans/trans_privileged.c.inc
> +++ b/target/riscv/insn_trans/trans_privileged.c.inc
> @@ -106,6 +106,19 @@ static bool trans_mret(DisasContext *ctx, arg_mret *a)
>  #endif
>  }
>
> +static bool trans_mnret(DisasContext *ctx, arg_mnret *a)
> +{
> +#ifndef CONFIG_USER_ONLY
> +    tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next);
> +    gen_helper_mnret(cpu_pc, cpu_env, cpu_pc);
> +    exit_tb(ctx); /* no chaining */
> +    ctx->base.is_jmp = DISAS_NORETURN;
> +    return true;
> +#else
> +    return false;
> +#endif
> +}
> +
>  static bool trans_wfi(DisasContext *ctx, arg_wfi *a)
>  {
>  #ifndef CONFIG_USER_ONLY
> diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c
> index 1eddcb94de7..b9601776153 100644
> --- a/target/riscv/op_helper.c
> +++ b/target/riscv/op_helper.c
> @@ -175,6 +175,37 @@ target_ulong helper_mret(CPURISCVState *env, target_ulong cpu_pc_deb)
>      return retpc;
>  }
>
> +target_ulong helper_mnret(CPURISCVState *env, target_ulong cpu_pc_deb)
> +{
> +    if (!riscv_feature(env, RISCV_FEATURE_RNMI)) {
> +        /* RNMI feature is not presented. */
> +        riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
> +    }
> +
> +    if (!(env->priv >= PRV_M)) {
> +        riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
> +    }
> +
> +    /* Get return PC from mnepc CSR. */
> +    target_ulong retpc = env->mnepc;
> +    if (!riscv_has_ext(env, RVC) && (retpc & 0x3)) {
> +        riscv_raise_exception(env, RISCV_EXCP_INST_ADDR_MIS, GETPC());
> +    }
> +
> +    /* Get previous privilege level from mnstatus CSR. */
> +    target_ulong prev_priv = get_field(env->mnstatus, MNSTATUS_MPP);
> +
> +    if (!pmp_get_num_rules(env) && (prev_priv != PRV_M)) {
> +        riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
> +    }
> +
> +    riscv_cpu_set_mode(env, prev_priv);
> +
> +    env->nmie = true;
> +
> +    return retpc;
> +}
> +
>  void helper_wfi(CPURISCVState *env)
>  {
>      CPUState *cs = env_cpu(env);
> --
> 2.17.1
>
>


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [RFC 1/1] target/riscv: add support of RNMI
@ 2021-03-19 13:27     ` Alistair Francis
  0 siblings, 0 replies; 11+ messages in thread
From: Alistair Francis @ 2021-03-19 13:27 UTC (permalink / raw)
  To: Frank Chang
  Cc: qemu-devel@nongnu.org Developers, open list:RISC-V,
	Keith Packard, Sagar Karandikar, Bastian Koppelmann,
	Richard Henderson, Alistair Francis, Paolo Bonzini,
	Palmer Dabbelt

On Tue, Mar 9, 2021 at 2:30 AM <frank.chang@sifive.com> wrote:
>
> From: Frank Chang <frank.chang@sifive.com>
>
> Signed-off-by: Frank Chang <frank.chang@sifive.com>

I had a quick look and this looks fine. I haven't compared it to the
spec yet though.

When you send the patch series do you mind splitting it up a bit more?
It just makes it easier to review.

Alistair

> ---
>  target/riscv/cpu.c                            | 40 +++++++++++++
>  target/riscv/cpu.h                            | 16 ++++-
>  target/riscv/cpu_bits.h                       | 19 ++++++
>  target/riscv/cpu_helper.c                     | 47 +++++++++++++--
>  target/riscv/csr.c                            | 59 +++++++++++++++++++
>  target/riscv/helper.h                         |  1 +
>  target/riscv/insn32.decode                    |  3 +
>  .../riscv/insn_trans/trans_privileged.c.inc   | 13 ++++
>  target/riscv/op_helper.c                      | 31 ++++++++++
>  9 files changed, 224 insertions(+), 5 deletions(-)
>
> diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
> index ddea8fbeeb3..07ea2105200 100644
> --- a/target/riscv/cpu.c
> +++ b/target/riscv/cpu.c
> @@ -137,6 +137,14 @@ static void set_feature(CPURISCVState *env, int feature)
>      env->features |= (1ULL << feature);
>  }
>
> +static void set_rnmi_vectors(CPURISCVState *env, int irqvec, int excpvec)
> +{
> +#ifndef CONFIG_USER_ONLY
> +    env->rnmi_irqvec = irqvec;
> +    env->rnmi_excpvec = excpvec;
> +#endif
> +}
> +
>  static void set_resetvec(CPURISCVState *env, int resetvec)
>  {
>  #ifndef CONFIG_USER_ONLY
> @@ -372,6 +380,23 @@ static void riscv_cpu_disas_set_info(CPUState *s, disassemble_info *info)
>      }
>  }
>
> +#ifndef CONFIG_USER_ONLY
> +static void riscv_cpu_set_rnmi(void *opaque, int irq, int level)
> +{
> +    RISCVCPU *cpu = opaque;
> +    CPURISCVState *env = &cpu->env;
> +    CPUState *cs = CPU(cpu);
> +
> +    if (level) {
> +        env->nmip |= 1 << irq;
> +        cpu_interrupt(cs, CPU_INTERRUPT_RNMI);
> +    } else {
> +        env->nmip &= ~(1 << irq);
> +        cpu_reset_interrupt(cs, CPU_INTERRUPT_RNMI);
> +    }
> +}
> +#endif
> +
>  static void riscv_cpu_realize(DeviceState *dev, Error **errp)
>  {
>      CPUState *cs = CPU(dev);
> @@ -415,6 +440,16 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp)
>
>      set_resetvec(env, cpu->cfg.resetvec);
>
> +    if (cpu->cfg.rnmi) {
> +        set_feature(env, RISCV_FEATURE_RNMI);
> +        set_rnmi_vectors(env, cpu->cfg.rnmi_irqvec, cpu->cfg.rnmi_excpvec);
> +#ifndef CONFIG_USER_ONLY
> +        env->nmie = true;
> +        qdev_init_gpio_in_named(DEVICE(cpu), riscv_cpu_set_rnmi,
> +                                "rnmi", TARGET_LONG_BITS);
> +#endif
> +    }
> +
>      /* If only XLEN is set for misa, then set misa from properties */
>      if (env->misa == RV32 || env->misa == RV64) {
>          /* Do some ISA extension error checking */
> @@ -554,6 +589,11 @@ static Property riscv_cpu_properties[] = {
>      DEFINE_PROP_BOOL("mmu", RISCVCPU, cfg.mmu, true),
>      DEFINE_PROP_BOOL("pmp", RISCVCPU, cfg.pmp, true),
>      DEFINE_PROP_UINT64("resetvec", RISCVCPU, cfg.resetvec, DEFAULT_RSTVEC),
> +    DEFINE_PROP_BOOL("rnmi", RISCVCPU, cfg.rnmi, false),
> +    DEFINE_PROP_UINT64("rnmi_irqvec", RISCVCPU, cfg.rnmi_irqvec,
> +                       DEFAULT_RNMI_IRQVEC),
> +    DEFINE_PROP_UINT64("rnmi_excpvec", RISCVCPU, cfg.rnmi_excpvec,
> +                       DEFAULT_RNMI_EXCPVEC),
>      DEFINE_PROP_END_OF_LIST(),
>  };
>
> diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
> index 0edb2826a27..b9aa403dfec 100644
> --- a/target/riscv/cpu.h
> +++ b/target/riscv/cpu.h
> @@ -80,7 +80,8 @@
>  enum {
>      RISCV_FEATURE_MMU,
>      RISCV_FEATURE_PMP,
> -    RISCV_FEATURE_MISA
> +    RISCV_FEATURE_MISA,
> +    RISCV_FEATURE_RNMI,
>  };
>
>  #define PRIV_VERSION_1_10_0 0x00011000
> @@ -178,6 +179,16 @@ struct CPURISCVState {
>      target_ulong mcause;
>      target_ulong mtval;  /* since: priv-1.10.0 */
>
> +    /* NMI */
> +    target_ulong mnscratch;
> +    target_ulong mnepc;
> +    target_ulong mncause; /* mncause without bit XLEN-1 set to 1 */
> +    target_ulong mnstatus;
> +    bool nmie;
> +    target_ulong nmip;
> +    target_ulong rnmi_irqvec;
> +    target_ulong rnmi_excpvec;
> +
>      /* Hypervisor CSRs */
>      target_ulong hstatus;
>      target_ulong hedeleg;
> @@ -300,6 +311,9 @@ struct RISCVCPU {
>          bool mmu;
>          bool pmp;
>          uint64_t resetvec;
> +        bool rnmi;
> +        uint64_t rnmi_irqvec;
> +        uint64_t rnmi_excpvec;
>      } cfg;
>  };
>
> diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
> index caf45992070..94ab76c66b8 100644
> --- a/target/riscv/cpu_bits.h
> +++ b/target/riscv/cpu_bits.h
> @@ -166,6 +166,12 @@
>  #define CSR_MTVAL           0x343
>  #define CSR_MIP             0x344
>
> +/* NMI */
> +#define CSR_MNSCRATCH       0x350
> +#define CSR_MNEPC           0x351
> +#define CSR_MNCAUSE         0x352
> +#define CSR_MNSTATUS        0x353
> +
>  /* Legacy Machine Trap Handling (priv v1.9.1) */
>  #define CSR_MBADADDR        0x343
>
> @@ -526,6 +532,12 @@
>  /* Default Reset Vector adress */
>  #define DEFAULT_RSTVEC      0x1000
>
> +/* Default RNMI Interrupt Vector address */
> +#define DEFAULT_RNMI_IRQVEC     0x1000
> +
> +/* Default RNMI Exception Vector address */
> +#define DEFAULT_RNMI_EXCPVEC    0x1000
> +
>  /* Exception causes */
>  #define EXCP_NONE                                -1 /* sentinel value */
>  #define RISCV_EXCP_INST_ADDR_MIS                 0x0
> @@ -552,6 +564,9 @@
>  #define RISCV_EXCP_INT_FLAG                0x80000000
>  #define RISCV_EXCP_INT_MASK                0x7fffffff
>
> +/* RNMI mnstatus CSR mask */
> +#define MNSTATUS_MPP                       MSTATUS_MPP
> +
>  /* Interrupt causes */
>  #define IRQ_U_SOFT                         0
>  #define IRQ_S_SOFT                         1
> @@ -592,4 +607,8 @@
>  #define MIE_UTIE                           (1 << IRQ_U_TIMER)
>  #define MIE_SSIE                           (1 << IRQ_S_SOFT)
>  #define MIE_USIE                           (1 << IRQ_U_SOFT)
> +
> +/* RISC-V-specific interrupt pending bits */
> +#define CPU_INTERRUPT_RNMI                 CPU_INTERRUPT_TGT_EXT_0
> +
>  #endif
> diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
> index 2f43939fb6d..f331c4b7032 100644
> --- a/target/riscv/cpu_helper.c
> +++ b/target/riscv/cpu_helper.c
> @@ -38,6 +38,19 @@ int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch)
>  #ifndef CONFIG_USER_ONLY
>  static int riscv_cpu_local_irq_pending(CPURISCVState *env)
>  {
> +    if (riscv_feature(env, RISCV_FEATURE_RNMI)) {
> +        /* Priority: RNMI > Other interrupt. */
> +        if (env->nmip && env->nmie) {
> +            return ctz64(env->nmip); /* since non-zero */
> +        } else if (!env->nmie) {
> +            /*
> +             * We are already in RNMI handler,
> +             * other interrupts cannot preempt.
> +             */
> +            return EXCP_NONE;
> +        }
> +    }
> +
>      target_ulong irqs;
>
>      target_ulong mstatus_mie = get_field(env->mstatus, MSTATUS_MIE);
> @@ -80,7 +93,8 @@ static int riscv_cpu_local_irq_pending(CPURISCVState *env)
>  bool riscv_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
>  {
>  #if !defined(CONFIG_USER_ONLY)
> -    if (interrupt_request & CPU_INTERRUPT_HARD) {
> +    if (interrupt_request &
> +            (CPU_INTERRUPT_HARD | CPU_INTERRUPT_RNMI)) {
>          RISCVCPU *cpu = RISCV_CPU(cs);
>          CPURISCVState *env = &cpu->env;
>          int interruptno = riscv_cpu_local_irq_pending(env);
> @@ -847,6 +861,23 @@ void riscv_cpu_do_interrupt(CPUState *cs)
>      target_ulong tval = 0;
>      target_ulong htval = 0;
>      target_ulong mtval2 = 0;
> +    target_ulong nextpc;
> +    bool nmi_execp = false;
> +
> +    if (riscv_feature(env, RISCV_FEATURE_RNMI)) {
> +        nmi_execp = !env->nmie && !async;
> +
> +        if (env->nmip && async) {
> +            env->nmie = false;
> +            env->mnstatus = set_field(env->mnstatus, MSTATUS_MPP,
> +                                      env->priv);
> +            env->mncause = cause;
> +            env->mnepc = env->pc;
> +            env->pc = env->rnmi_irqvec;
> +            riscv_cpu_set_mode(env, PRV_M);
> +            goto handled;
> +        }
> +    }
>
>      if  (cause == RISCV_EXCP_SEMIHOST) {
>          if (env->priv >= PRV_S) {
> @@ -905,7 +936,7 @@ 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 &&
> +    if (env->priv <= PRV_S && !nmi_execp &&
>              cause < TARGET_LONG_BITS && ((deleg >> cause) & 1)) {
>          /* handle the trap in S-mode */
>          if (riscv_has_ext(env, RVH)) {
> @@ -1005,8 +1036,15 @@ void riscv_cpu_do_interrupt(CPUState *cs)
>          env->mepc = env->pc;
>          env->mbadaddr = tval;
>          env->mtval2 = mtval2;
> -        env->pc = (env->mtvec >> 2 << 2) +
> -            ((async && (env->mtvec & 3) == 1) ? cause * 4 : 0);
> +
> +        if (nmi_execp) {
> +            nextpc = env->rnmi_excpvec;
> +        } else {
> +            nextpc = (env->mtvec >> 2 << 2) +
> +                ((async && (env->mtvec & 3) == 1) ? cause * 4 : 0);
> +        }
> +        env->pc = nextpc;
> +
>          riscv_cpu_set_mode(env, PRV_M);
>      }
>
> @@ -1016,6 +1054,7 @@ void riscv_cpu_do_interrupt(CPUState *cs)
>       * RISC-V ISA Specification.
>       */
>
> +handled:
>  #endif
>      cs->exception_index = EXCP_NONE; /* mark handled to qemu */
>  }
> diff --git a/target/riscv/csr.c b/target/riscv/csr.c
> index fd2e6363f39..f67c50327ec 100644
> --- a/target/riscv/csr.c
> +++ b/target/riscv/csr.c
> @@ -188,6 +188,11 @@ static int pmp(CPURISCVState *env, int csrno)
>  {
>      return -!riscv_feature(env, RISCV_FEATURE_PMP);
>  }
> +
> +static int nmi(CPURISCVState *env, int csrno)
> +{
> +    return -!riscv_feature(env, RISCV_FEATURE_RNMI);
> +}
>  #endif
>
>  /* User Floating-Point CSRs */
> @@ -712,6 +717,54 @@ static int write_mbadaddr(CPURISCVState *env, int csrno, target_ulong val)
>      return 0;
>  }
>
> +static int read_mnscratch(CPURISCVState *env, int csrno, target_ulong *val)
> +{
> +    *val = env->mnscratch;
> +    return 0;
> +}
> +
> +static int write_mnscratch(CPURISCVState *env, int csrno, target_ulong val)
> +{
> +    env->mnscratch = val;
> +    return 0;
> +}
> +
> +static int read_mnepc(CPURISCVState *env, int csrno, target_ulong *val)
> +{
> +    *val = env->mnepc;
> +    return 0;
> +}
> +
> +static int write_mnepc(CPURISCVState *env, int csrno, target_ulong val)
> +{
> +    env->mnepc = val;
> +    return 0;
> +}
> +
> +static int read_mncause(CPURISCVState *env, int csrno, target_ulong *val)
> +{
> +    *val = env->mncause;
> +    return 0;
> +}
> +
> +static int write_mncause(CPURISCVState *env, int csrno, target_ulong val)
> +{
> +    env->mncause = val;
> +    return 0;
> +}
> +
> +static int read_mnstatus(CPURISCVState *env, int csrno, target_ulong *val)
> +{
> +    *val = env->mnstatus;
> +    return 0;
> +}
> +
> +static int write_mnstatus(CPURISCVState *env, int csrno, target_ulong val)
> +{
> +    env->mnstatus = val & MNSTATUS_MPP;
> +    return 0;
> +}
> +
>  static int rmw_mip(CPURISCVState *env, int csrno, target_ulong *ret_value,
>                     target_ulong new_value, target_ulong write_mask)
>  {
> @@ -1427,6 +1480,12 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
>      [CSR_MBADADDR] = { "mbadaddr", any,  read_mbadaddr, write_mbadaddr },
>      [CSR_MIP]      = { "mip",      any,  NULL,    NULL, rmw_mip        },
>
> +    /* NMI */
> +    [CSR_MNSCRATCH] = { "mnscratch", nmi, read_mnscratch, write_mnscratch },
> +    [CSR_MNEPC]     = { "mnepc",     nmi, read_mnepc,     write_mnepc     },
> +    [CSR_MNCAUSE]   = { "mncause",   nmi, read_mncause,   write_mncause   },
> +    [CSR_MNSTATUS]  = { "mnstatus",  nmi, read_mnstatus,  write_mnstatus  },
> +
>      /* Supervisor Trap Setup */
>      [CSR_SSTATUS]    = { "sstatus",    smode, read_sstatus,    write_sstatus    },
>      [CSR_SIE]        = { "sie",        smode, read_sie,        write_sie        },
> diff --git a/target/riscv/helper.h b/target/riscv/helper.h
> index e3f3f41e891..0914d777d6d 100644
> --- a/target/riscv/helper.h
> +++ b/target/riscv/helper.h
> @@ -65,6 +65,7 @@ DEF_HELPER_4(csrrc, tl, env, tl, tl, tl)
>  #ifndef CONFIG_USER_ONLY
>  DEF_HELPER_2(sret, tl, env, tl)
>  DEF_HELPER_2(mret, tl, env, tl)
> +DEF_HELPER_2(mnret, tl, env, tl)
>  DEF_HELPER_1(wfi, void, env)
>  DEF_HELPER_1(tlb_flush, void, env)
>  #endif
> diff --git a/target/riscv/insn32.decode b/target/riscv/insn32.decode
> index 84080dd18ca..557f3394276 100644
> --- a/target/riscv/insn32.decode
> +++ b/target/riscv/insn32.decode
> @@ -97,6 +97,9 @@ wfi         0001000    00101 00000 000 00000 1110011
>  sfence_vma  0001001    ..... ..... 000 00000 1110011 @sfence_vma
>  sfence_vm   0001000    00100 ..... 000 00000 1110011 @sfence_vm
>
> +# *** NMI ***
> +mnret       0111000    00010 00000 000 00000 1110011
> +
>  # *** RV32I Base Instruction Set ***
>  lui      ....................       ..... 0110111 @u
>  auipc    ....................       ..... 0010111 @u
> diff --git a/target/riscv/insn_trans/trans_privileged.c.inc b/target/riscv/insn_trans/trans_privileged.c.inc
> index 32312be2024..63c49dfe6fb 100644
> --- a/target/riscv/insn_trans/trans_privileged.c.inc
> +++ b/target/riscv/insn_trans/trans_privileged.c.inc
> @@ -106,6 +106,19 @@ static bool trans_mret(DisasContext *ctx, arg_mret *a)
>  #endif
>  }
>
> +static bool trans_mnret(DisasContext *ctx, arg_mnret *a)
> +{
> +#ifndef CONFIG_USER_ONLY
> +    tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next);
> +    gen_helper_mnret(cpu_pc, cpu_env, cpu_pc);
> +    exit_tb(ctx); /* no chaining */
> +    ctx->base.is_jmp = DISAS_NORETURN;
> +    return true;
> +#else
> +    return false;
> +#endif
> +}
> +
>  static bool trans_wfi(DisasContext *ctx, arg_wfi *a)
>  {
>  #ifndef CONFIG_USER_ONLY
> diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c
> index 1eddcb94de7..b9601776153 100644
> --- a/target/riscv/op_helper.c
> +++ b/target/riscv/op_helper.c
> @@ -175,6 +175,37 @@ target_ulong helper_mret(CPURISCVState *env, target_ulong cpu_pc_deb)
>      return retpc;
>  }
>
> +target_ulong helper_mnret(CPURISCVState *env, target_ulong cpu_pc_deb)
> +{
> +    if (!riscv_feature(env, RISCV_FEATURE_RNMI)) {
> +        /* RNMI feature is not presented. */
> +        riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
> +    }
> +
> +    if (!(env->priv >= PRV_M)) {
> +        riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
> +    }
> +
> +    /* Get return PC from mnepc CSR. */
> +    target_ulong retpc = env->mnepc;
> +    if (!riscv_has_ext(env, RVC) && (retpc & 0x3)) {
> +        riscv_raise_exception(env, RISCV_EXCP_INST_ADDR_MIS, GETPC());
> +    }
> +
> +    /* Get previous privilege level from mnstatus CSR. */
> +    target_ulong prev_priv = get_field(env->mnstatus, MNSTATUS_MPP);
> +
> +    if (!pmp_get_num_rules(env) && (prev_priv != PRV_M)) {
> +        riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
> +    }
> +
> +    riscv_cpu_set_mode(env, prev_priv);
> +
> +    env->nmie = true;
> +
> +    return retpc;
> +}
> +
>  void helper_wfi(CPURISCVState *env)
>  {
>      CPUState *cs = env_cpu(env);
> --
> 2.17.1
>
>


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [RFC 0/1] target/riscv: add RNMI support
  2021-03-09  7:29 [RFC 0/1] target/riscv: add RNMI support frank.chang
@ 2021-03-19 13:28   ` Alistair Francis
  2021-03-19 13:28   ` Alistair Francis
  1 sibling, 0 replies; 11+ messages in thread
From: Alistair Francis @ 2021-03-19 13:28 UTC (permalink / raw)
  To: Frank Chang; +Cc: open list:RISC-V, qemu-devel@nongnu.org Developers

On Tue, Mar 9, 2021 at 2:31 AM <frank.chang@sifive.com> wrote:
>
> From: Frank Chang <frank.chang@sifive.com>
>
> This patchset add suport of Resumable NMI (RNMI) in RISC-V.
>
> There are four new CSRs and one new instruction added to allow NMI to be
> resumable in RISC-V, which are:
>
> =============================================================
>   * mnscratch (0x350)
>   * mnepc     (0x351)
>   * mncause   (0x352)
>   * mnstatus  (0x353)
> =============================================================
>   * mnret: To return from RNMI interrupt/exception handler.
> =============================================================
>
> RNMI also has higher priority than any other interrupts or exceptions
> and cannot be disable by software.
>
> RNMI may be used to route to other devices such as Bus Error Unit or
> Watchdog Timer in the future.
>
> The interrupt/exception trap handler addresses of RNMI are
> implementation defined.
>
> The technical proposal of RNMI can be found:
> https://lists.riscv.org/g/tech-privileged/message/421
>
> The port is available here:
> https://github.com/sifive/qemu/tree/nmi-upstream-v1
>
> To enable RNMI feature, add cpu argument: 'rnmi=true' and specify
> RNMI interrupt/exception handler addresses with 'rnmi_irqvec' and
> 'rnmi_excpvec' arguments, e.g.
>     -cpu rv64,rnmi=true,rnmi_irqvec=0x2000,rnmi_excpvec=0x3000

Can you include details about the software you are using and how I can
test this?

Alistair

>
> Frank Chang (1):
>   target/riscv: add support of RNMI
>
>  target/riscv/cpu.c                            | 40 +++++++++++++
>  target/riscv/cpu.h                            | 16 ++++-
>  target/riscv/cpu_bits.h                       | 19 ++++++
>  target/riscv/cpu_helper.c                     | 47 +++++++++++++--
>  target/riscv/csr.c                            | 59 +++++++++++++++++++
>  target/riscv/helper.h                         |  1 +
>  target/riscv/insn32.decode                    |  3 +
>  .../riscv/insn_trans/trans_privileged.c.inc   | 13 ++++
>  target/riscv/op_helper.c                      | 31 ++++++++++
>  9 files changed, 224 insertions(+), 5 deletions(-)
>
> --
> 2.17.1
>
>


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [RFC 0/1] target/riscv: add RNMI support
@ 2021-03-19 13:28   ` Alistair Francis
  0 siblings, 0 replies; 11+ messages in thread
From: Alistair Francis @ 2021-03-19 13:28 UTC (permalink / raw)
  To: Frank Chang; +Cc: qemu-devel@nongnu.org Developers, open list:RISC-V

On Tue, Mar 9, 2021 at 2:31 AM <frank.chang@sifive.com> wrote:
>
> From: Frank Chang <frank.chang@sifive.com>
>
> This patchset add suport of Resumable NMI (RNMI) in RISC-V.
>
> There are four new CSRs and one new instruction added to allow NMI to be
> resumable in RISC-V, which are:
>
> =============================================================
>   * mnscratch (0x350)
>   * mnepc     (0x351)
>   * mncause   (0x352)
>   * mnstatus  (0x353)
> =============================================================
>   * mnret: To return from RNMI interrupt/exception handler.
> =============================================================
>
> RNMI also has higher priority than any other interrupts or exceptions
> and cannot be disable by software.
>
> RNMI may be used to route to other devices such as Bus Error Unit or
> Watchdog Timer in the future.
>
> The interrupt/exception trap handler addresses of RNMI are
> implementation defined.
>
> The technical proposal of RNMI can be found:
> https://lists.riscv.org/g/tech-privileged/message/421
>
> The port is available here:
> https://github.com/sifive/qemu/tree/nmi-upstream-v1
>
> To enable RNMI feature, add cpu argument: 'rnmi=true' and specify
> RNMI interrupt/exception handler addresses with 'rnmi_irqvec' and
> 'rnmi_excpvec' arguments, e.g.
>     -cpu rv64,rnmi=true,rnmi_irqvec=0x2000,rnmi_excpvec=0x3000

Can you include details about the software you are using and how I can
test this?

Alistair

>
> Frank Chang (1):
>   target/riscv: add support of RNMI
>
>  target/riscv/cpu.c                            | 40 +++++++++++++
>  target/riscv/cpu.h                            | 16 ++++-
>  target/riscv/cpu_bits.h                       | 19 ++++++
>  target/riscv/cpu_helper.c                     | 47 +++++++++++++--
>  target/riscv/csr.c                            | 59 +++++++++++++++++++
>  target/riscv/helper.h                         |  1 +
>  target/riscv/insn32.decode                    |  3 +
>  .../riscv/insn_trans/trans_privileged.c.inc   | 13 ++++
>  target/riscv/op_helper.c                      | 31 ++++++++++
>  9 files changed, 224 insertions(+), 5 deletions(-)
>
> --
> 2.17.1
>
>


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [RFC 0/1] target/riscv: add RNMI support
  2021-03-19 13:28   ` Alistair Francis
@ 2021-03-22  2:03     ` Frank Chang
  -1 siblings, 0 replies; 11+ messages in thread
From: Frank Chang @ 2021-03-22  2:03 UTC (permalink / raw)
  To: Alistair Francis; +Cc: open list:RISC-V, qemu-devel@nongnu.org Developers

[-- Attachment #1: Type: text/plain, Size: 2443 bytes --]

On Fri, Mar 19, 2021 at 9:30 PM Alistair Francis <alistair23@gmail.com>
wrote:

> On Tue, Mar 9, 2021 at 2:31 AM <frank.chang@sifive.com> wrote:
> >
> > From: Frank Chang <frank.chang@sifive.com>
> >
> > This patchset add suport of Resumable NMI (RNMI) in RISC-V.
> >
> > There are four new CSRs and one new instruction added to allow NMI to be
> > resumable in RISC-V, which are:
> >
> > =============================================================
> >   * mnscratch (0x350)
> >   * mnepc     (0x351)
> >   * mncause   (0x352)
> >   * mnstatus  (0x353)
> > =============================================================
> >   * mnret: To return from RNMI interrupt/exception handler.
> > =============================================================
> >
> > RNMI also has higher priority than any other interrupts or exceptions
> > and cannot be disable by software.
> >
> > RNMI may be used to route to other devices such as Bus Error Unit or
> > Watchdog Timer in the future.
> >
> > The interrupt/exception trap handler addresses of RNMI are
> > implementation defined.
> >
> > The technical proposal of RNMI can be found:
> > https://lists.riscv.org/g/tech-privileged/message/421
> >
> > The port is available here:
> > https://github.com/sifive/qemu/tree/nmi-upstream-v1
> >
> > To enable RNMI feature, add cpu argument: 'rnmi=true' and specify
> > RNMI interrupt/exception handler addresses with 'rnmi_irqvec' and
> > 'rnmi_excpvec' arguments, e.g.
> >     -cpu rv64,rnmi=true,rnmi_irqvec=0x2000,rnmi_excpvec=0x3000
>
> Can you include details about the software you are using and how I can
> test this?
>
> Alistair
>

Thanks, I'll include that in my next version patchset.

Frank Chang


>
> >
> > Frank Chang (1):
> >   target/riscv: add support of RNMI
> >
> >  target/riscv/cpu.c                            | 40 +++++++++++++
> >  target/riscv/cpu.h                            | 16 ++++-
> >  target/riscv/cpu_bits.h                       | 19 ++++++
> >  target/riscv/cpu_helper.c                     | 47 +++++++++++++--
> >  target/riscv/csr.c                            | 59 +++++++++++++++++++
> >  target/riscv/helper.h                         |  1 +
> >  target/riscv/insn32.decode                    |  3 +
> >  .../riscv/insn_trans/trans_privileged.c.inc   | 13 ++++
> >  target/riscv/op_helper.c                      | 31 ++++++++++
> >  9 files changed, 224 insertions(+), 5 deletions(-)
> >
> > --
> > 2.17.1
> >
> >
>

[-- Attachment #2: Type: text/html, Size: 3739 bytes --]

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [RFC 0/1] target/riscv: add RNMI support
@ 2021-03-22  2:03     ` Frank Chang
  0 siblings, 0 replies; 11+ messages in thread
From: Frank Chang @ 2021-03-22  2:03 UTC (permalink / raw)
  To: Alistair Francis; +Cc: qemu-devel@nongnu.org Developers, open list:RISC-V

[-- Attachment #1: Type: text/plain, Size: 2443 bytes --]

On Fri, Mar 19, 2021 at 9:30 PM Alistair Francis <alistair23@gmail.com>
wrote:

> On Tue, Mar 9, 2021 at 2:31 AM <frank.chang@sifive.com> wrote:
> >
> > From: Frank Chang <frank.chang@sifive.com>
> >
> > This patchset add suport of Resumable NMI (RNMI) in RISC-V.
> >
> > There are four new CSRs and one new instruction added to allow NMI to be
> > resumable in RISC-V, which are:
> >
> > =============================================================
> >   * mnscratch (0x350)
> >   * mnepc     (0x351)
> >   * mncause   (0x352)
> >   * mnstatus  (0x353)
> > =============================================================
> >   * mnret: To return from RNMI interrupt/exception handler.
> > =============================================================
> >
> > RNMI also has higher priority than any other interrupts or exceptions
> > and cannot be disable by software.
> >
> > RNMI may be used to route to other devices such as Bus Error Unit or
> > Watchdog Timer in the future.
> >
> > The interrupt/exception trap handler addresses of RNMI are
> > implementation defined.
> >
> > The technical proposal of RNMI can be found:
> > https://lists.riscv.org/g/tech-privileged/message/421
> >
> > The port is available here:
> > https://github.com/sifive/qemu/tree/nmi-upstream-v1
> >
> > To enable RNMI feature, add cpu argument: 'rnmi=true' and specify
> > RNMI interrupt/exception handler addresses with 'rnmi_irqvec' and
> > 'rnmi_excpvec' arguments, e.g.
> >     -cpu rv64,rnmi=true,rnmi_irqvec=0x2000,rnmi_excpvec=0x3000
>
> Can you include details about the software you are using and how I can
> test this?
>
> Alistair
>

Thanks, I'll include that in my next version patchset.

Frank Chang


>
> >
> > Frank Chang (1):
> >   target/riscv: add support of RNMI
> >
> >  target/riscv/cpu.c                            | 40 +++++++++++++
> >  target/riscv/cpu.h                            | 16 ++++-
> >  target/riscv/cpu_bits.h                       | 19 ++++++
> >  target/riscv/cpu_helper.c                     | 47 +++++++++++++--
> >  target/riscv/csr.c                            | 59 +++++++++++++++++++
> >  target/riscv/helper.h                         |  1 +
> >  target/riscv/insn32.decode                    |  3 +
> >  .../riscv/insn_trans/trans_privileged.c.inc   | 13 ++++
> >  target/riscv/op_helper.c                      | 31 ++++++++++
> >  9 files changed, 224 insertions(+), 5 deletions(-)
> >
> > --
> > 2.17.1
> >
> >
>

[-- Attachment #2: Type: text/html, Size: 3739 bytes --]

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [RFC 1/1] target/riscv: add support of RNMI
  2021-03-19 13:27     ` Alistair Francis
@ 2021-03-22  2:04       ` Frank Chang
  -1 siblings, 0 replies; 11+ messages in thread
From: Frank Chang @ 2021-03-22  2:04 UTC (permalink / raw)
  To: Alistair Francis
  Cc: Keith Packard, open list:RISC-V, Sagar Karandikar,
	Bastian Koppelmann, Richard Henderson,
	qemu-devel@nongnu.org Developers, Alistair Francis,
	Paolo Bonzini, Palmer Dabbelt

[-- Attachment #1: Type: text/plain, Size: 16626 bytes --]

On Fri, Mar 19, 2021 at 9:29 PM Alistair Francis <alistair23@gmail.com>
wrote:

> On Tue, Mar 9, 2021 at 2:30 AM <frank.chang@sifive.com> wrote:
> >
> > From: Frank Chang <frank.chang@sifive.com>
> >
> > Signed-off-by: Frank Chang <frank.chang@sifive.com>
>
> I had a quick look and this looks fine. I haven't compared it to the
> spec yet though.
>
> When you send the patch series do you mind splitting it up a bit more?
> It just makes it easier to review.
>
> Alistair
>

Sure, will do that.

Thanks,
Frank Chang


>
> > ---
> >  target/riscv/cpu.c                            | 40 +++++++++++++
> >  target/riscv/cpu.h                            | 16 ++++-
> >  target/riscv/cpu_bits.h                       | 19 ++++++
> >  target/riscv/cpu_helper.c                     | 47 +++++++++++++--
> >  target/riscv/csr.c                            | 59 +++++++++++++++++++
> >  target/riscv/helper.h                         |  1 +
> >  target/riscv/insn32.decode                    |  3 +
> >  .../riscv/insn_trans/trans_privileged.c.inc   | 13 ++++
> >  target/riscv/op_helper.c                      | 31 ++++++++++
> >  9 files changed, 224 insertions(+), 5 deletions(-)
> >
> > diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
> > index ddea8fbeeb3..07ea2105200 100644
> > --- a/target/riscv/cpu.c
> > +++ b/target/riscv/cpu.c
> > @@ -137,6 +137,14 @@ static void set_feature(CPURISCVState *env, int
> feature)
> >      env->features |= (1ULL << feature);
> >  }
> >
> > +static void set_rnmi_vectors(CPURISCVState *env, int irqvec, int
> excpvec)
> > +{
> > +#ifndef CONFIG_USER_ONLY
> > +    env->rnmi_irqvec = irqvec;
> > +    env->rnmi_excpvec = excpvec;
> > +#endif
> > +}
> > +
> >  static void set_resetvec(CPURISCVState *env, int resetvec)
> >  {
> >  #ifndef CONFIG_USER_ONLY
> > @@ -372,6 +380,23 @@ static void riscv_cpu_disas_set_info(CPUState *s,
> disassemble_info *info)
> >      }
> >  }
> >
> > +#ifndef CONFIG_USER_ONLY
> > +static void riscv_cpu_set_rnmi(void *opaque, int irq, int level)
> > +{
> > +    RISCVCPU *cpu = opaque;
> > +    CPURISCVState *env = &cpu->env;
> > +    CPUState *cs = CPU(cpu);
> > +
> > +    if (level) {
> > +        env->nmip |= 1 << irq;
> > +        cpu_interrupt(cs, CPU_INTERRUPT_RNMI);
> > +    } else {
> > +        env->nmip &= ~(1 << irq);
> > +        cpu_reset_interrupt(cs, CPU_INTERRUPT_RNMI);
> > +    }
> > +}
> > +#endif
> > +
> >  static void riscv_cpu_realize(DeviceState *dev, Error **errp)
> >  {
> >      CPUState *cs = CPU(dev);
> > @@ -415,6 +440,16 @@ static void riscv_cpu_realize(DeviceState *dev,
> Error **errp)
> >
> >      set_resetvec(env, cpu->cfg.resetvec);
> >
> > +    if (cpu->cfg.rnmi) {
> > +        set_feature(env, RISCV_FEATURE_RNMI);
> > +        set_rnmi_vectors(env, cpu->cfg.rnmi_irqvec,
> cpu->cfg.rnmi_excpvec);
> > +#ifndef CONFIG_USER_ONLY
> > +        env->nmie = true;
> > +        qdev_init_gpio_in_named(DEVICE(cpu), riscv_cpu_set_rnmi,
> > +                                "rnmi", TARGET_LONG_BITS);
> > +#endif
> > +    }
> > +
> >      /* If only XLEN is set for misa, then set misa from properties */
> >      if (env->misa == RV32 || env->misa == RV64) {
> >          /* Do some ISA extension error checking */
> > @@ -554,6 +589,11 @@ static Property riscv_cpu_properties[] = {
> >      DEFINE_PROP_BOOL("mmu", RISCVCPU, cfg.mmu, true),
> >      DEFINE_PROP_BOOL("pmp", RISCVCPU, cfg.pmp, true),
> >      DEFINE_PROP_UINT64("resetvec", RISCVCPU, cfg.resetvec,
> DEFAULT_RSTVEC),
> > +    DEFINE_PROP_BOOL("rnmi", RISCVCPU, cfg.rnmi, false),
> > +    DEFINE_PROP_UINT64("rnmi_irqvec", RISCVCPU, cfg.rnmi_irqvec,
> > +                       DEFAULT_RNMI_IRQVEC),
> > +    DEFINE_PROP_UINT64("rnmi_excpvec", RISCVCPU, cfg.rnmi_excpvec,
> > +                       DEFAULT_RNMI_EXCPVEC),
> >      DEFINE_PROP_END_OF_LIST(),
> >  };
> >
> > diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
> > index 0edb2826a27..b9aa403dfec 100644
> > --- a/target/riscv/cpu.h
> > +++ b/target/riscv/cpu.h
> > @@ -80,7 +80,8 @@
> >  enum {
> >      RISCV_FEATURE_MMU,
> >      RISCV_FEATURE_PMP,
> > -    RISCV_FEATURE_MISA
> > +    RISCV_FEATURE_MISA,
> > +    RISCV_FEATURE_RNMI,
> >  };
> >
> >  #define PRIV_VERSION_1_10_0 0x00011000
> > @@ -178,6 +179,16 @@ struct CPURISCVState {
> >      target_ulong mcause;
> >      target_ulong mtval;  /* since: priv-1.10.0 */
> >
> > +    /* NMI */
> > +    target_ulong mnscratch;
> > +    target_ulong mnepc;
> > +    target_ulong mncause; /* mncause without bit XLEN-1 set to 1 */
> > +    target_ulong mnstatus;
> > +    bool nmie;
> > +    target_ulong nmip;
> > +    target_ulong rnmi_irqvec;
> > +    target_ulong rnmi_excpvec;
> > +
> >      /* Hypervisor CSRs */
> >      target_ulong hstatus;
> >      target_ulong hedeleg;
> > @@ -300,6 +311,9 @@ struct RISCVCPU {
> >          bool mmu;
> >          bool pmp;
> >          uint64_t resetvec;
> > +        bool rnmi;
> > +        uint64_t rnmi_irqvec;
> > +        uint64_t rnmi_excpvec;
> >      } cfg;
> >  };
> >
> > diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
> > index caf45992070..94ab76c66b8 100644
> > --- a/target/riscv/cpu_bits.h
> > +++ b/target/riscv/cpu_bits.h
> > @@ -166,6 +166,12 @@
> >  #define CSR_MTVAL           0x343
> >  #define CSR_MIP             0x344
> >
> > +/* NMI */
> > +#define CSR_MNSCRATCH       0x350
> > +#define CSR_MNEPC           0x351
> > +#define CSR_MNCAUSE         0x352
> > +#define CSR_MNSTATUS        0x353
> > +
> >  /* Legacy Machine Trap Handling (priv v1.9.1) */
> >  #define CSR_MBADADDR        0x343
> >
> > @@ -526,6 +532,12 @@
> >  /* Default Reset Vector adress */
> >  #define DEFAULT_RSTVEC      0x1000
> >
> > +/* Default RNMI Interrupt Vector address */
> > +#define DEFAULT_RNMI_IRQVEC     0x1000
> > +
> > +/* Default RNMI Exception Vector address */
> > +#define DEFAULT_RNMI_EXCPVEC    0x1000
> > +
> >  /* Exception causes */
> >  #define EXCP_NONE                                -1 /* sentinel value */
> >  #define RISCV_EXCP_INST_ADDR_MIS                 0x0
> > @@ -552,6 +564,9 @@
> >  #define RISCV_EXCP_INT_FLAG                0x80000000
> >  #define RISCV_EXCP_INT_MASK                0x7fffffff
> >
> > +/* RNMI mnstatus CSR mask */
> > +#define MNSTATUS_MPP                       MSTATUS_MPP
> > +
> >  /* Interrupt causes */
> >  #define IRQ_U_SOFT                         0
> >  #define IRQ_S_SOFT                         1
> > @@ -592,4 +607,8 @@
> >  #define MIE_UTIE                           (1 << IRQ_U_TIMER)
> >  #define MIE_SSIE                           (1 << IRQ_S_SOFT)
> >  #define MIE_USIE                           (1 << IRQ_U_SOFT)
> > +
> > +/* RISC-V-specific interrupt pending bits */
> > +#define CPU_INTERRUPT_RNMI                 CPU_INTERRUPT_TGT_EXT_0
> > +
> >  #endif
> > diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
> > index 2f43939fb6d..f331c4b7032 100644
> > --- a/target/riscv/cpu_helper.c
> > +++ b/target/riscv/cpu_helper.c
> > @@ -38,6 +38,19 @@ int riscv_cpu_mmu_index(CPURISCVState *env, bool
> ifetch)
> >  #ifndef CONFIG_USER_ONLY
> >  static int riscv_cpu_local_irq_pending(CPURISCVState *env)
> >  {
> > +    if (riscv_feature(env, RISCV_FEATURE_RNMI)) {
> > +        /* Priority: RNMI > Other interrupt. */
> > +        if (env->nmip && env->nmie) {
> > +            return ctz64(env->nmip); /* since non-zero */
> > +        } else if (!env->nmie) {
> > +            /*
> > +             * We are already in RNMI handler,
> > +             * other interrupts cannot preempt.
> > +             */
> > +            return EXCP_NONE;
> > +        }
> > +    }
> > +
> >      target_ulong irqs;
> >
> >      target_ulong mstatus_mie = get_field(env->mstatus, MSTATUS_MIE);
> > @@ -80,7 +93,8 @@ static int riscv_cpu_local_irq_pending(CPURISCVState
> *env)
> >  bool riscv_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
> >  {
> >  #if !defined(CONFIG_USER_ONLY)
> > -    if (interrupt_request & CPU_INTERRUPT_HARD) {
> > +    if (interrupt_request &
> > +            (CPU_INTERRUPT_HARD | CPU_INTERRUPT_RNMI)) {
> >          RISCVCPU *cpu = RISCV_CPU(cs);
> >          CPURISCVState *env = &cpu->env;
> >          int interruptno = riscv_cpu_local_irq_pending(env);
> > @@ -847,6 +861,23 @@ void riscv_cpu_do_interrupt(CPUState *cs)
> >      target_ulong tval = 0;
> >      target_ulong htval = 0;
> >      target_ulong mtval2 = 0;
> > +    target_ulong nextpc;
> > +    bool nmi_execp = false;
> > +
> > +    if (riscv_feature(env, RISCV_FEATURE_RNMI)) {
> > +        nmi_execp = !env->nmie && !async;
> > +
> > +        if (env->nmip && async) {
> > +            env->nmie = false;
> > +            env->mnstatus = set_field(env->mnstatus, MSTATUS_MPP,
> > +                                      env->priv);
> > +            env->mncause = cause;
> > +            env->mnepc = env->pc;
> > +            env->pc = env->rnmi_irqvec;
> > +            riscv_cpu_set_mode(env, PRV_M);
> > +            goto handled;
> > +        }
> > +    }
> >
> >      if  (cause == RISCV_EXCP_SEMIHOST) {
> >          if (env->priv >= PRV_S) {
> > @@ -905,7 +936,7 @@ 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 &&
> > +    if (env->priv <= PRV_S && !nmi_execp &&
> >              cause < TARGET_LONG_BITS && ((deleg >> cause) & 1)) {
> >          /* handle the trap in S-mode */
> >          if (riscv_has_ext(env, RVH)) {
> > @@ -1005,8 +1036,15 @@ void riscv_cpu_do_interrupt(CPUState *cs)
> >          env->mepc = env->pc;
> >          env->mbadaddr = tval;
> >          env->mtval2 = mtval2;
> > -        env->pc = (env->mtvec >> 2 << 2) +
> > -            ((async && (env->mtvec & 3) == 1) ? cause * 4 : 0);
> > +
> > +        if (nmi_execp) {
> > +            nextpc = env->rnmi_excpvec;
> > +        } else {
> > +            nextpc = (env->mtvec >> 2 << 2) +
> > +                ((async && (env->mtvec & 3) == 1) ? cause * 4 : 0);
> > +        }
> > +        env->pc = nextpc;
> > +
> >          riscv_cpu_set_mode(env, PRV_M);
> >      }
> >
> > @@ -1016,6 +1054,7 @@ void riscv_cpu_do_interrupt(CPUState *cs)
> >       * RISC-V ISA Specification.
> >       */
> >
> > +handled:
> >  #endif
> >      cs->exception_index = EXCP_NONE; /* mark handled to qemu */
> >  }
> > diff --git a/target/riscv/csr.c b/target/riscv/csr.c
> > index fd2e6363f39..f67c50327ec 100644
> > --- a/target/riscv/csr.c
> > +++ b/target/riscv/csr.c
> > @@ -188,6 +188,11 @@ static int pmp(CPURISCVState *env, int csrno)
> >  {
> >      return -!riscv_feature(env, RISCV_FEATURE_PMP);
> >  }
> > +
> > +static int nmi(CPURISCVState *env, int csrno)
> > +{
> > +    return -!riscv_feature(env, RISCV_FEATURE_RNMI);
> > +}
> >  #endif
> >
> >  /* User Floating-Point CSRs */
> > @@ -712,6 +717,54 @@ static int write_mbadaddr(CPURISCVState *env, int
> csrno, target_ulong val)
> >      return 0;
> >  }
> >
> > +static int read_mnscratch(CPURISCVState *env, int csrno, target_ulong
> *val)
> > +{
> > +    *val = env->mnscratch;
> > +    return 0;
> > +}
> > +
> > +static int write_mnscratch(CPURISCVState *env, int csrno, target_ulong
> val)
> > +{
> > +    env->mnscratch = val;
> > +    return 0;
> > +}
> > +
> > +static int read_mnepc(CPURISCVState *env, int csrno, target_ulong *val)
> > +{
> > +    *val = env->mnepc;
> > +    return 0;
> > +}
> > +
> > +static int write_mnepc(CPURISCVState *env, int csrno, target_ulong val)
> > +{
> > +    env->mnepc = val;
> > +    return 0;
> > +}
> > +
> > +static int read_mncause(CPURISCVState *env, int csrno, target_ulong
> *val)
> > +{
> > +    *val = env->mncause;
> > +    return 0;
> > +}
> > +
> > +static int write_mncause(CPURISCVState *env, int csrno, target_ulong
> val)
> > +{
> > +    env->mncause = val;
> > +    return 0;
> > +}
> > +
> > +static int read_mnstatus(CPURISCVState *env, int csrno, target_ulong
> *val)
> > +{
> > +    *val = env->mnstatus;
> > +    return 0;
> > +}
> > +
> > +static int write_mnstatus(CPURISCVState *env, int csrno, target_ulong
> val)
> > +{
> > +    env->mnstatus = val & MNSTATUS_MPP;
> > +    return 0;
> > +}
> > +
> >  static int rmw_mip(CPURISCVState *env, int csrno, target_ulong
> *ret_value,
> >                     target_ulong new_value, target_ulong write_mask)
> >  {
> > @@ -1427,6 +1480,12 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
> >      [CSR_MBADADDR] = { "mbadaddr", any,  read_mbadaddr, write_mbadaddr
> },
> >      [CSR_MIP]      = { "mip",      any,  NULL,    NULL, rmw_mip
> },
> >
> > +    /* NMI */
> > +    [CSR_MNSCRATCH] = { "mnscratch", nmi, read_mnscratch,
> write_mnscratch },
> > +    [CSR_MNEPC]     = { "mnepc",     nmi, read_mnepc,     write_mnepc
>    },
> > +    [CSR_MNCAUSE]   = { "mncause",   nmi, read_mncause,
>  write_mncause   },
> > +    [CSR_MNSTATUS]  = { "mnstatus",  nmi, read_mnstatus,
> write_mnstatus  },
> > +
> >      /* Supervisor Trap Setup */
> >      [CSR_SSTATUS]    = { "sstatus",    smode, read_sstatus,
> write_sstatus    },
> >      [CSR_SIE]        = { "sie",        smode, read_sie,
> write_sie        },
> > diff --git a/target/riscv/helper.h b/target/riscv/helper.h
> > index e3f3f41e891..0914d777d6d 100644
> > --- a/target/riscv/helper.h
> > +++ b/target/riscv/helper.h
> > @@ -65,6 +65,7 @@ DEF_HELPER_4(csrrc, tl, env, tl, tl, tl)
> >  #ifndef CONFIG_USER_ONLY
> >  DEF_HELPER_2(sret, tl, env, tl)
> >  DEF_HELPER_2(mret, tl, env, tl)
> > +DEF_HELPER_2(mnret, tl, env, tl)
> >  DEF_HELPER_1(wfi, void, env)
> >  DEF_HELPER_1(tlb_flush, void, env)
> >  #endif
> > diff --git a/target/riscv/insn32.decode b/target/riscv/insn32.decode
> > index 84080dd18ca..557f3394276 100644
> > --- a/target/riscv/insn32.decode
> > +++ b/target/riscv/insn32.decode
> > @@ -97,6 +97,9 @@ wfi         0001000    00101 00000 000 00000 1110011
> >  sfence_vma  0001001    ..... ..... 000 00000 1110011 @sfence_vma
> >  sfence_vm   0001000    00100 ..... 000 00000 1110011 @sfence_vm
> >
> > +# *** NMI ***
> > +mnret       0111000    00010 00000 000 00000 1110011
> > +
> >  # *** RV32I Base Instruction Set ***
> >  lui      ....................       ..... 0110111 @u
> >  auipc    ....................       ..... 0010111 @u
> > diff --git a/target/riscv/insn_trans/trans_privileged.c.inc
> b/target/riscv/insn_trans/trans_privileged.c.inc
> > index 32312be2024..63c49dfe6fb 100644
> > --- a/target/riscv/insn_trans/trans_privileged.c.inc
> > +++ b/target/riscv/insn_trans/trans_privileged.c.inc
> > @@ -106,6 +106,19 @@ static bool trans_mret(DisasContext *ctx, arg_mret
> *a)
> >  #endif
> >  }
> >
> > +static bool trans_mnret(DisasContext *ctx, arg_mnret *a)
> > +{
> > +#ifndef CONFIG_USER_ONLY
> > +    tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next);
> > +    gen_helper_mnret(cpu_pc, cpu_env, cpu_pc);
> > +    exit_tb(ctx); /* no chaining */
> > +    ctx->base.is_jmp = DISAS_NORETURN;
> > +    return true;
> > +#else
> > +    return false;
> > +#endif
> > +}
> > +
> >  static bool trans_wfi(DisasContext *ctx, arg_wfi *a)
> >  {
> >  #ifndef CONFIG_USER_ONLY
> > diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c
> > index 1eddcb94de7..b9601776153 100644
> > --- a/target/riscv/op_helper.c
> > +++ b/target/riscv/op_helper.c
> > @@ -175,6 +175,37 @@ target_ulong helper_mret(CPURISCVState *env,
> target_ulong cpu_pc_deb)
> >      return retpc;
> >  }
> >
> > +target_ulong helper_mnret(CPURISCVState *env, target_ulong cpu_pc_deb)
> > +{
> > +    if (!riscv_feature(env, RISCV_FEATURE_RNMI)) {
> > +        /* RNMI feature is not presented. */
> > +        riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
> > +    }
> > +
> > +    if (!(env->priv >= PRV_M)) {
> > +        riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
> > +    }
> > +
> > +    /* Get return PC from mnepc CSR. */
> > +    target_ulong retpc = env->mnepc;
> > +    if (!riscv_has_ext(env, RVC) && (retpc & 0x3)) {
> > +        riscv_raise_exception(env, RISCV_EXCP_INST_ADDR_MIS, GETPC());
> > +    }
> > +
> > +    /* Get previous privilege level from mnstatus CSR. */
> > +    target_ulong prev_priv = get_field(env->mnstatus, MNSTATUS_MPP);
> > +
> > +    if (!pmp_get_num_rules(env) && (prev_priv != PRV_M)) {
> > +        riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
> > +    }
> > +
> > +    riscv_cpu_set_mode(env, prev_priv);
> > +
> > +    env->nmie = true;
> > +
> > +    return retpc;
> > +}
> > +
> >  void helper_wfi(CPURISCVState *env)
> >  {
> >      CPUState *cs = env_cpu(env);
> > --
> > 2.17.1
> >
> >
>

[-- Attachment #2: Type: text/html, Size: 21646 bytes --]

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [RFC 1/1] target/riscv: add support of RNMI
@ 2021-03-22  2:04       ` Frank Chang
  0 siblings, 0 replies; 11+ messages in thread
From: Frank Chang @ 2021-03-22  2:04 UTC (permalink / raw)
  To: Alistair Francis
  Cc: qemu-devel@nongnu.org Developers, open list:RISC-V,
	Keith Packard, Sagar Karandikar, Bastian Koppelmann,
	Richard Henderson, Alistair Francis, Paolo Bonzini,
	Palmer Dabbelt

[-- Attachment #1: Type: text/plain, Size: 16626 bytes --]

On Fri, Mar 19, 2021 at 9:29 PM Alistair Francis <alistair23@gmail.com>
wrote:

> On Tue, Mar 9, 2021 at 2:30 AM <frank.chang@sifive.com> wrote:
> >
> > From: Frank Chang <frank.chang@sifive.com>
> >
> > Signed-off-by: Frank Chang <frank.chang@sifive.com>
>
> I had a quick look and this looks fine. I haven't compared it to the
> spec yet though.
>
> When you send the patch series do you mind splitting it up a bit more?
> It just makes it easier to review.
>
> Alistair
>

Sure, will do that.

Thanks,
Frank Chang


>
> > ---
> >  target/riscv/cpu.c                            | 40 +++++++++++++
> >  target/riscv/cpu.h                            | 16 ++++-
> >  target/riscv/cpu_bits.h                       | 19 ++++++
> >  target/riscv/cpu_helper.c                     | 47 +++++++++++++--
> >  target/riscv/csr.c                            | 59 +++++++++++++++++++
> >  target/riscv/helper.h                         |  1 +
> >  target/riscv/insn32.decode                    |  3 +
> >  .../riscv/insn_trans/trans_privileged.c.inc   | 13 ++++
> >  target/riscv/op_helper.c                      | 31 ++++++++++
> >  9 files changed, 224 insertions(+), 5 deletions(-)
> >
> > diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
> > index ddea8fbeeb3..07ea2105200 100644
> > --- a/target/riscv/cpu.c
> > +++ b/target/riscv/cpu.c
> > @@ -137,6 +137,14 @@ static void set_feature(CPURISCVState *env, int
> feature)
> >      env->features |= (1ULL << feature);
> >  }
> >
> > +static void set_rnmi_vectors(CPURISCVState *env, int irqvec, int
> excpvec)
> > +{
> > +#ifndef CONFIG_USER_ONLY
> > +    env->rnmi_irqvec = irqvec;
> > +    env->rnmi_excpvec = excpvec;
> > +#endif
> > +}
> > +
> >  static void set_resetvec(CPURISCVState *env, int resetvec)
> >  {
> >  #ifndef CONFIG_USER_ONLY
> > @@ -372,6 +380,23 @@ static void riscv_cpu_disas_set_info(CPUState *s,
> disassemble_info *info)
> >      }
> >  }
> >
> > +#ifndef CONFIG_USER_ONLY
> > +static void riscv_cpu_set_rnmi(void *opaque, int irq, int level)
> > +{
> > +    RISCVCPU *cpu = opaque;
> > +    CPURISCVState *env = &cpu->env;
> > +    CPUState *cs = CPU(cpu);
> > +
> > +    if (level) {
> > +        env->nmip |= 1 << irq;
> > +        cpu_interrupt(cs, CPU_INTERRUPT_RNMI);
> > +    } else {
> > +        env->nmip &= ~(1 << irq);
> > +        cpu_reset_interrupt(cs, CPU_INTERRUPT_RNMI);
> > +    }
> > +}
> > +#endif
> > +
> >  static void riscv_cpu_realize(DeviceState *dev, Error **errp)
> >  {
> >      CPUState *cs = CPU(dev);
> > @@ -415,6 +440,16 @@ static void riscv_cpu_realize(DeviceState *dev,
> Error **errp)
> >
> >      set_resetvec(env, cpu->cfg.resetvec);
> >
> > +    if (cpu->cfg.rnmi) {
> > +        set_feature(env, RISCV_FEATURE_RNMI);
> > +        set_rnmi_vectors(env, cpu->cfg.rnmi_irqvec,
> cpu->cfg.rnmi_excpvec);
> > +#ifndef CONFIG_USER_ONLY
> > +        env->nmie = true;
> > +        qdev_init_gpio_in_named(DEVICE(cpu), riscv_cpu_set_rnmi,
> > +                                "rnmi", TARGET_LONG_BITS);
> > +#endif
> > +    }
> > +
> >      /* If only XLEN is set for misa, then set misa from properties */
> >      if (env->misa == RV32 || env->misa == RV64) {
> >          /* Do some ISA extension error checking */
> > @@ -554,6 +589,11 @@ static Property riscv_cpu_properties[] = {
> >      DEFINE_PROP_BOOL("mmu", RISCVCPU, cfg.mmu, true),
> >      DEFINE_PROP_BOOL("pmp", RISCVCPU, cfg.pmp, true),
> >      DEFINE_PROP_UINT64("resetvec", RISCVCPU, cfg.resetvec,
> DEFAULT_RSTVEC),
> > +    DEFINE_PROP_BOOL("rnmi", RISCVCPU, cfg.rnmi, false),
> > +    DEFINE_PROP_UINT64("rnmi_irqvec", RISCVCPU, cfg.rnmi_irqvec,
> > +                       DEFAULT_RNMI_IRQVEC),
> > +    DEFINE_PROP_UINT64("rnmi_excpvec", RISCVCPU, cfg.rnmi_excpvec,
> > +                       DEFAULT_RNMI_EXCPVEC),
> >      DEFINE_PROP_END_OF_LIST(),
> >  };
> >
> > diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
> > index 0edb2826a27..b9aa403dfec 100644
> > --- a/target/riscv/cpu.h
> > +++ b/target/riscv/cpu.h
> > @@ -80,7 +80,8 @@
> >  enum {
> >      RISCV_FEATURE_MMU,
> >      RISCV_FEATURE_PMP,
> > -    RISCV_FEATURE_MISA
> > +    RISCV_FEATURE_MISA,
> > +    RISCV_FEATURE_RNMI,
> >  };
> >
> >  #define PRIV_VERSION_1_10_0 0x00011000
> > @@ -178,6 +179,16 @@ struct CPURISCVState {
> >      target_ulong mcause;
> >      target_ulong mtval;  /* since: priv-1.10.0 */
> >
> > +    /* NMI */
> > +    target_ulong mnscratch;
> > +    target_ulong mnepc;
> > +    target_ulong mncause; /* mncause without bit XLEN-1 set to 1 */
> > +    target_ulong mnstatus;
> > +    bool nmie;
> > +    target_ulong nmip;
> > +    target_ulong rnmi_irqvec;
> > +    target_ulong rnmi_excpvec;
> > +
> >      /* Hypervisor CSRs */
> >      target_ulong hstatus;
> >      target_ulong hedeleg;
> > @@ -300,6 +311,9 @@ struct RISCVCPU {
> >          bool mmu;
> >          bool pmp;
> >          uint64_t resetvec;
> > +        bool rnmi;
> > +        uint64_t rnmi_irqvec;
> > +        uint64_t rnmi_excpvec;
> >      } cfg;
> >  };
> >
> > diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
> > index caf45992070..94ab76c66b8 100644
> > --- a/target/riscv/cpu_bits.h
> > +++ b/target/riscv/cpu_bits.h
> > @@ -166,6 +166,12 @@
> >  #define CSR_MTVAL           0x343
> >  #define CSR_MIP             0x344
> >
> > +/* NMI */
> > +#define CSR_MNSCRATCH       0x350
> > +#define CSR_MNEPC           0x351
> > +#define CSR_MNCAUSE         0x352
> > +#define CSR_MNSTATUS        0x353
> > +
> >  /* Legacy Machine Trap Handling (priv v1.9.1) */
> >  #define CSR_MBADADDR        0x343
> >
> > @@ -526,6 +532,12 @@
> >  /* Default Reset Vector adress */
> >  #define DEFAULT_RSTVEC      0x1000
> >
> > +/* Default RNMI Interrupt Vector address */
> > +#define DEFAULT_RNMI_IRQVEC     0x1000
> > +
> > +/* Default RNMI Exception Vector address */
> > +#define DEFAULT_RNMI_EXCPVEC    0x1000
> > +
> >  /* Exception causes */
> >  #define EXCP_NONE                                -1 /* sentinel value */
> >  #define RISCV_EXCP_INST_ADDR_MIS                 0x0
> > @@ -552,6 +564,9 @@
> >  #define RISCV_EXCP_INT_FLAG                0x80000000
> >  #define RISCV_EXCP_INT_MASK                0x7fffffff
> >
> > +/* RNMI mnstatus CSR mask */
> > +#define MNSTATUS_MPP                       MSTATUS_MPP
> > +
> >  /* Interrupt causes */
> >  #define IRQ_U_SOFT                         0
> >  #define IRQ_S_SOFT                         1
> > @@ -592,4 +607,8 @@
> >  #define MIE_UTIE                           (1 << IRQ_U_TIMER)
> >  #define MIE_SSIE                           (1 << IRQ_S_SOFT)
> >  #define MIE_USIE                           (1 << IRQ_U_SOFT)
> > +
> > +/* RISC-V-specific interrupt pending bits */
> > +#define CPU_INTERRUPT_RNMI                 CPU_INTERRUPT_TGT_EXT_0
> > +
> >  #endif
> > diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
> > index 2f43939fb6d..f331c4b7032 100644
> > --- a/target/riscv/cpu_helper.c
> > +++ b/target/riscv/cpu_helper.c
> > @@ -38,6 +38,19 @@ int riscv_cpu_mmu_index(CPURISCVState *env, bool
> ifetch)
> >  #ifndef CONFIG_USER_ONLY
> >  static int riscv_cpu_local_irq_pending(CPURISCVState *env)
> >  {
> > +    if (riscv_feature(env, RISCV_FEATURE_RNMI)) {
> > +        /* Priority: RNMI > Other interrupt. */
> > +        if (env->nmip && env->nmie) {
> > +            return ctz64(env->nmip); /* since non-zero */
> > +        } else if (!env->nmie) {
> > +            /*
> > +             * We are already in RNMI handler,
> > +             * other interrupts cannot preempt.
> > +             */
> > +            return EXCP_NONE;
> > +        }
> > +    }
> > +
> >      target_ulong irqs;
> >
> >      target_ulong mstatus_mie = get_field(env->mstatus, MSTATUS_MIE);
> > @@ -80,7 +93,8 @@ static int riscv_cpu_local_irq_pending(CPURISCVState
> *env)
> >  bool riscv_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
> >  {
> >  #if !defined(CONFIG_USER_ONLY)
> > -    if (interrupt_request & CPU_INTERRUPT_HARD) {
> > +    if (interrupt_request &
> > +            (CPU_INTERRUPT_HARD | CPU_INTERRUPT_RNMI)) {
> >          RISCVCPU *cpu = RISCV_CPU(cs);
> >          CPURISCVState *env = &cpu->env;
> >          int interruptno = riscv_cpu_local_irq_pending(env);
> > @@ -847,6 +861,23 @@ void riscv_cpu_do_interrupt(CPUState *cs)
> >      target_ulong tval = 0;
> >      target_ulong htval = 0;
> >      target_ulong mtval2 = 0;
> > +    target_ulong nextpc;
> > +    bool nmi_execp = false;
> > +
> > +    if (riscv_feature(env, RISCV_FEATURE_RNMI)) {
> > +        nmi_execp = !env->nmie && !async;
> > +
> > +        if (env->nmip && async) {
> > +            env->nmie = false;
> > +            env->mnstatus = set_field(env->mnstatus, MSTATUS_MPP,
> > +                                      env->priv);
> > +            env->mncause = cause;
> > +            env->mnepc = env->pc;
> > +            env->pc = env->rnmi_irqvec;
> > +            riscv_cpu_set_mode(env, PRV_M);
> > +            goto handled;
> > +        }
> > +    }
> >
> >      if  (cause == RISCV_EXCP_SEMIHOST) {
> >          if (env->priv >= PRV_S) {
> > @@ -905,7 +936,7 @@ 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 &&
> > +    if (env->priv <= PRV_S && !nmi_execp &&
> >              cause < TARGET_LONG_BITS && ((deleg >> cause) & 1)) {
> >          /* handle the trap in S-mode */
> >          if (riscv_has_ext(env, RVH)) {
> > @@ -1005,8 +1036,15 @@ void riscv_cpu_do_interrupt(CPUState *cs)
> >          env->mepc = env->pc;
> >          env->mbadaddr = tval;
> >          env->mtval2 = mtval2;
> > -        env->pc = (env->mtvec >> 2 << 2) +
> > -            ((async && (env->mtvec & 3) == 1) ? cause * 4 : 0);
> > +
> > +        if (nmi_execp) {
> > +            nextpc = env->rnmi_excpvec;
> > +        } else {
> > +            nextpc = (env->mtvec >> 2 << 2) +
> > +                ((async && (env->mtvec & 3) == 1) ? cause * 4 : 0);
> > +        }
> > +        env->pc = nextpc;
> > +
> >          riscv_cpu_set_mode(env, PRV_M);
> >      }
> >
> > @@ -1016,6 +1054,7 @@ void riscv_cpu_do_interrupt(CPUState *cs)
> >       * RISC-V ISA Specification.
> >       */
> >
> > +handled:
> >  #endif
> >      cs->exception_index = EXCP_NONE; /* mark handled to qemu */
> >  }
> > diff --git a/target/riscv/csr.c b/target/riscv/csr.c
> > index fd2e6363f39..f67c50327ec 100644
> > --- a/target/riscv/csr.c
> > +++ b/target/riscv/csr.c
> > @@ -188,6 +188,11 @@ static int pmp(CPURISCVState *env, int csrno)
> >  {
> >      return -!riscv_feature(env, RISCV_FEATURE_PMP);
> >  }
> > +
> > +static int nmi(CPURISCVState *env, int csrno)
> > +{
> > +    return -!riscv_feature(env, RISCV_FEATURE_RNMI);
> > +}
> >  #endif
> >
> >  /* User Floating-Point CSRs */
> > @@ -712,6 +717,54 @@ static int write_mbadaddr(CPURISCVState *env, int
> csrno, target_ulong val)
> >      return 0;
> >  }
> >
> > +static int read_mnscratch(CPURISCVState *env, int csrno, target_ulong
> *val)
> > +{
> > +    *val = env->mnscratch;
> > +    return 0;
> > +}
> > +
> > +static int write_mnscratch(CPURISCVState *env, int csrno, target_ulong
> val)
> > +{
> > +    env->mnscratch = val;
> > +    return 0;
> > +}
> > +
> > +static int read_mnepc(CPURISCVState *env, int csrno, target_ulong *val)
> > +{
> > +    *val = env->mnepc;
> > +    return 0;
> > +}
> > +
> > +static int write_mnepc(CPURISCVState *env, int csrno, target_ulong val)
> > +{
> > +    env->mnepc = val;
> > +    return 0;
> > +}
> > +
> > +static int read_mncause(CPURISCVState *env, int csrno, target_ulong
> *val)
> > +{
> > +    *val = env->mncause;
> > +    return 0;
> > +}
> > +
> > +static int write_mncause(CPURISCVState *env, int csrno, target_ulong
> val)
> > +{
> > +    env->mncause = val;
> > +    return 0;
> > +}
> > +
> > +static int read_mnstatus(CPURISCVState *env, int csrno, target_ulong
> *val)
> > +{
> > +    *val = env->mnstatus;
> > +    return 0;
> > +}
> > +
> > +static int write_mnstatus(CPURISCVState *env, int csrno, target_ulong
> val)
> > +{
> > +    env->mnstatus = val & MNSTATUS_MPP;
> > +    return 0;
> > +}
> > +
> >  static int rmw_mip(CPURISCVState *env, int csrno, target_ulong
> *ret_value,
> >                     target_ulong new_value, target_ulong write_mask)
> >  {
> > @@ -1427,6 +1480,12 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
> >      [CSR_MBADADDR] = { "mbadaddr", any,  read_mbadaddr, write_mbadaddr
> },
> >      [CSR_MIP]      = { "mip",      any,  NULL,    NULL, rmw_mip
> },
> >
> > +    /* NMI */
> > +    [CSR_MNSCRATCH] = { "mnscratch", nmi, read_mnscratch,
> write_mnscratch },
> > +    [CSR_MNEPC]     = { "mnepc",     nmi, read_mnepc,     write_mnepc
>    },
> > +    [CSR_MNCAUSE]   = { "mncause",   nmi, read_mncause,
>  write_mncause   },
> > +    [CSR_MNSTATUS]  = { "mnstatus",  nmi, read_mnstatus,
> write_mnstatus  },
> > +
> >      /* Supervisor Trap Setup */
> >      [CSR_SSTATUS]    = { "sstatus",    smode, read_sstatus,
> write_sstatus    },
> >      [CSR_SIE]        = { "sie",        smode, read_sie,
> write_sie        },
> > diff --git a/target/riscv/helper.h b/target/riscv/helper.h
> > index e3f3f41e891..0914d777d6d 100644
> > --- a/target/riscv/helper.h
> > +++ b/target/riscv/helper.h
> > @@ -65,6 +65,7 @@ DEF_HELPER_4(csrrc, tl, env, tl, tl, tl)
> >  #ifndef CONFIG_USER_ONLY
> >  DEF_HELPER_2(sret, tl, env, tl)
> >  DEF_HELPER_2(mret, tl, env, tl)
> > +DEF_HELPER_2(mnret, tl, env, tl)
> >  DEF_HELPER_1(wfi, void, env)
> >  DEF_HELPER_1(tlb_flush, void, env)
> >  #endif
> > diff --git a/target/riscv/insn32.decode b/target/riscv/insn32.decode
> > index 84080dd18ca..557f3394276 100644
> > --- a/target/riscv/insn32.decode
> > +++ b/target/riscv/insn32.decode
> > @@ -97,6 +97,9 @@ wfi         0001000    00101 00000 000 00000 1110011
> >  sfence_vma  0001001    ..... ..... 000 00000 1110011 @sfence_vma
> >  sfence_vm   0001000    00100 ..... 000 00000 1110011 @sfence_vm
> >
> > +# *** NMI ***
> > +mnret       0111000    00010 00000 000 00000 1110011
> > +
> >  # *** RV32I Base Instruction Set ***
> >  lui      ....................       ..... 0110111 @u
> >  auipc    ....................       ..... 0010111 @u
> > diff --git a/target/riscv/insn_trans/trans_privileged.c.inc
> b/target/riscv/insn_trans/trans_privileged.c.inc
> > index 32312be2024..63c49dfe6fb 100644
> > --- a/target/riscv/insn_trans/trans_privileged.c.inc
> > +++ b/target/riscv/insn_trans/trans_privileged.c.inc
> > @@ -106,6 +106,19 @@ static bool trans_mret(DisasContext *ctx, arg_mret
> *a)
> >  #endif
> >  }
> >
> > +static bool trans_mnret(DisasContext *ctx, arg_mnret *a)
> > +{
> > +#ifndef CONFIG_USER_ONLY
> > +    tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next);
> > +    gen_helper_mnret(cpu_pc, cpu_env, cpu_pc);
> > +    exit_tb(ctx); /* no chaining */
> > +    ctx->base.is_jmp = DISAS_NORETURN;
> > +    return true;
> > +#else
> > +    return false;
> > +#endif
> > +}
> > +
> >  static bool trans_wfi(DisasContext *ctx, arg_wfi *a)
> >  {
> >  #ifndef CONFIG_USER_ONLY
> > diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c
> > index 1eddcb94de7..b9601776153 100644
> > --- a/target/riscv/op_helper.c
> > +++ b/target/riscv/op_helper.c
> > @@ -175,6 +175,37 @@ target_ulong helper_mret(CPURISCVState *env,
> target_ulong cpu_pc_deb)
> >      return retpc;
> >  }
> >
> > +target_ulong helper_mnret(CPURISCVState *env, target_ulong cpu_pc_deb)
> > +{
> > +    if (!riscv_feature(env, RISCV_FEATURE_RNMI)) {
> > +        /* RNMI feature is not presented. */
> > +        riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
> > +    }
> > +
> > +    if (!(env->priv >= PRV_M)) {
> > +        riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
> > +    }
> > +
> > +    /* Get return PC from mnepc CSR. */
> > +    target_ulong retpc = env->mnepc;
> > +    if (!riscv_has_ext(env, RVC) && (retpc & 0x3)) {
> > +        riscv_raise_exception(env, RISCV_EXCP_INST_ADDR_MIS, GETPC());
> > +    }
> > +
> > +    /* Get previous privilege level from mnstatus CSR. */
> > +    target_ulong prev_priv = get_field(env->mnstatus, MNSTATUS_MPP);
> > +
> > +    if (!pmp_get_num_rules(env) && (prev_priv != PRV_M)) {
> > +        riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
> > +    }
> > +
> > +    riscv_cpu_set_mode(env, prev_priv);
> > +
> > +    env->nmie = true;
> > +
> > +    return retpc;
> > +}
> > +
> >  void helper_wfi(CPURISCVState *env)
> >  {
> >      CPUState *cs = env_cpu(env);
> > --
> > 2.17.1
> >
> >
>

[-- Attachment #2: Type: text/html, Size: 21646 bytes --]

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2021-03-22  2:06 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-03-09  7:29 [RFC 0/1] target/riscv: add RNMI support frank.chang
2021-03-09  7:29 ` [RFC 1/1] target/riscv: add support of RNMI frank.chang
2021-03-09  7:29   ` frank.chang
2021-03-19 13:27   ` Alistair Francis
2021-03-19 13:27     ` Alistair Francis
2021-03-22  2:04     ` Frank Chang
2021-03-22  2:04       ` Frank Chang
2021-03-19 13:28 ` [RFC 0/1] target/riscv: add RNMI support Alistair Francis
2021-03-19 13:28   ` Alistair Francis
2021-03-22  2:03   ` Frank Chang
2021-03-22  2:03     ` Frank Chang

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.