* [PATCH v5 0/5] target/riscv: Add Smrnmi support.
@ 2024-08-09 8:12 Tommy Wu
2024-08-09 8:12 ` [PATCH v5 1/5] target/riscv: Add `ext_smrnmi` in the RISCVCPUConfig Tommy Wu
` (4 more replies)
0 siblings, 5 replies; 13+ messages in thread
From: Tommy Wu @ 2024-08-09 8:12 UTC (permalink / raw)
To: qemu-devel, qemu-riscv
Cc: frank.chang, palmer, alistair.francis, alistair23, bin.meng,
liweiwei, dbarboza, Tommy Wu
This patchset added support for Smrnmi Extension 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 (0x740)
* mnepc (0x741)
* mncause (0x742)
* mnstatus (0x744)
=============================================================
* mnret: To return from RNMI interrupt/exception handler.
=============================================================
RNMI also has higher priority than any other interrupts or exceptions
and cannot be disabled 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.
If anyone wants to test the patches, we can use the customized OpenSBI[1],
and the customized QEMU[2].
We implemented a PoC RNMI trap handler in the customized OpenSBI.
In the customized QEMU, we use the Smrnmi patches and the patch from
Damien Hedde[3]. The patch from Damien Hedde can be used to inject
the RNMI signal with the qmp command.
[1] https://github.com/TommyWu-fdgkhdkgh/opensbi/tree/dev/twu/master
[2] https://github.com/TommyWu-fdgkhdkgh/qemu/tree/dev/twu/master
[3] https://lists.gnu.org/archive/html/qemu-devel/2019-06/msg06232.html
Test commands :
$ ./build/qemu-system-riscv64 -M virt -cpu rv64,smrnmi=true,
rnmi-interrupt-vector={Offset of the RNMI handler in the customized
OpenSBI.} -m 4G -smp 2 -serial mon:stdio -serial null -nographic
-bios fw_jump.elf -kernel Image -initrd rootfs.cpio
-qmp unix:/tmp/qmp-sock,server,wait=off
Use qmp command to inject the RNMI interrupt.
$ ./scripts/qmp/qmp-shell /tmp/qmp-sock
(QEMU) gpio-set path=/machine/soc0/harts[0] gpio=riscv.cpu.rnmi
number=0 value=true
(QEMU) gpio-set path=/machine/soc0/harts[0] gpio=riscv.cpu.rnmi
number=0 value=false
Changelog:
v5
* Move the patch that adds the Smrnmi extension to the last patch.
( Thank Alistair for the suggestion. )
* Implement an M-mode software PoC for this with implemented handlers.
( Thank Andrew Jones for the suggestion. )
* Add a commit message to all patches of the series.
( Thank Andrew Jones for the suggestion. )
v4
* Fix some coding style issues.
( Thank Daniel for the suggestions. )
v3
* Update to the newest version of Smrnmi extension specification.
v2
* split up the series into more commits for convenience of review.
* add missing rnmi_irqvec and rnmi_excpvec properties to riscv_harts.
Tommy Wu (5):
target/riscv: Add `ext_smrnmi` in the RISCVCPUConfig.
target/riscv: Handle Smrnmi interrupt and exception.
target/riscv: Add Smrnmi CSRs.
target/riscv: Add Smrnmi mnret instruction.
target/riscv: Add Smrnmi cpu extension.
hw/riscv/riscv_hart.c | 18 ++++
include/hw/riscv/riscv_hart.h | 4 +
target/riscv/cpu.c | 18 ++++
target/riscv/cpu.h | 10 +++
target/riscv/cpu_bits.h | 23 ++++++
target/riscv/cpu_cfg.h | 1 +
target/riscv/cpu_helper.c | 77 ++++++++++++++++-
target/riscv/csr.c | 82 +++++++++++++++++++
target/riscv/helper.h | 1 +
target/riscv/insn32.decode | 3 +
.../riscv/insn_trans/trans_privileged.c.inc | 12 +++
target/riscv/op_helper.c | 46 +++++++++++
12 files changed, 292 insertions(+), 3 deletions(-)
--
2.39.3
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v5 1/5] target/riscv: Add `ext_smrnmi` in the RISCVCPUConfig.
2024-08-09 8:12 [PATCH v5 0/5] target/riscv: Add Smrnmi support Tommy Wu
@ 2024-08-09 8:12 ` Tommy Wu
2024-08-19 3:40 ` Alistair Francis
2024-08-09 8:12 ` [PATCH v5 2/5] target/riscv: Handle Smrnmi interrupt and exception Tommy Wu
` (3 subsequent siblings)
4 siblings, 1 reply; 13+ messages in thread
From: Tommy Wu @ 2024-08-09 8:12 UTC (permalink / raw)
To: qemu-devel, qemu-riscv
Cc: frank.chang, palmer, alistair.francis, alistair23, bin.meng,
liweiwei, dbarboza, Tommy Wu
The boolean variable `ext_smrnmi` is used to determine whether the
Smrnmi extension exists.
Signed-off-by: Frank Chang <frank.chang@sifive.com>
Signed-off-by: Tommy Wu <tommy.wu@sifive.com>
---
target/riscv/cpu_cfg.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h
index 8b272fb826..ae2a945b5f 100644
--- a/target/riscv/cpu_cfg.h
+++ b/target/riscv/cpu_cfg.h
@@ -125,6 +125,7 @@ struct RISCVCPUConfig {
bool ext_ssaia;
bool ext_sscofpmf;
bool ext_smepmp;
+ bool ext_smrnmi;
bool rvv_ta_all_1s;
bool rvv_ma_all_1s;
--
2.39.3
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v5 2/5] target/riscv: Handle Smrnmi interrupt and exception.
2024-08-09 8:12 [PATCH v5 0/5] target/riscv: Add Smrnmi support Tommy Wu
2024-08-09 8:12 ` [PATCH v5 1/5] target/riscv: Add `ext_smrnmi` in the RISCVCPUConfig Tommy Wu
@ 2024-08-09 8:12 ` Tommy Wu
2024-08-15 1:40 ` Alvin Che-Chia Chang(張哲嘉)
2024-08-09 8:12 ` [PATCH v5 3/5] target/riscv: Add Smrnmi CSRs Tommy Wu
` (2 subsequent siblings)
4 siblings, 1 reply; 13+ messages in thread
From: Tommy Wu @ 2024-08-09 8:12 UTC (permalink / raw)
To: qemu-devel, qemu-riscv
Cc: frank.chang, palmer, alistair.francis, alistair23, bin.meng,
liweiwei, dbarboza, Tommy Wu
Because the RNMI interrupt trap handler address is implementation defined.
We add the `rnmi-interrupt-vector` and `rnmi-exception-vector` as the property
of the harts. It’s very easy for users to set the address based on their
expectation. This patch also adds the functionality to handle the RNMI signals.
Signed-off-by: Frank Chang <frank.chang@sifive.com>
Signed-off-by: Tommy Wu <tommy.wu@sifive.com>
---
hw/riscv/riscv_hart.c | 18 ++++++++
include/hw/riscv/riscv_hart.h | 4 ++
target/riscv/cpu.c | 11 +++++
target/riscv/cpu.h | 6 +++
target/riscv/cpu_bits.h | 12 ++++++
target/riscv/cpu_helper.c | 77 +++++++++++++++++++++++++++++++++--
6 files changed, 125 insertions(+), 3 deletions(-)
diff --git a/hw/riscv/riscv_hart.c b/hw/riscv/riscv_hart.c
index 613ea2aaa0..b7d73f7a82 100644
--- a/hw/riscv/riscv_hart.c
+++ b/hw/riscv/riscv_hart.c
@@ -33,6 +33,12 @@ static Property riscv_harts_props[] = {
DEFINE_PROP_STRING("cpu-type", RISCVHartArrayState, cpu_type),
DEFINE_PROP_UINT64("resetvec", RISCVHartArrayState, resetvec,
DEFAULT_RSTVEC),
+ DEFINE_PROP_ARRAY("rnmi-interrupt-vector", RISCVHartArrayState,
+ num_rnmi_irqvec, rnmi_irqvec, qdev_prop_uint64,
+ uint64_t),
+ DEFINE_PROP_ARRAY("rnmi-exception-vector", RISCVHartArrayState,
+ num_rnmi_excpvec, rnmi_excpvec, qdev_prop_uint64,
+ uint64_t),
DEFINE_PROP_END_OF_LIST(),
};
@@ -47,6 +53,18 @@ static bool riscv_hart_realize(RISCVHartArrayState *s, int idx,
{
object_initialize_child(OBJECT(s), "harts[*]", &s->harts[idx], cpu_type);
qdev_prop_set_uint64(DEVICE(&s->harts[idx]), "resetvec", s->resetvec);
+ if (s->harts[idx].cfg.ext_smrnmi) {
+ if (s->rnmi_irqvec) {
+ qdev_prop_set_uint64(DEVICE(&s->harts[idx]),
+ "rnmi-interrupt-vector",
+ s->rnmi_irqvec[idx]);
+ }
+ if (s->rnmi_excpvec) {
+ qdev_prop_set_uint64(DEVICE(&s->harts[idx]),
+ "rnmi-exception-vector",
+ s->rnmi_excpvec[idx]);
+ }
+ }
s->harts[idx].env.mhartid = s->hartid_base + idx;
qemu_register_reset(riscv_harts_cpu_reset, &s->harts[idx]);
return qdev_realize(DEVICE(&s->harts[idx]), NULL, errp);
diff --git a/include/hw/riscv/riscv_hart.h b/include/hw/riscv/riscv_hart.h
index 912b4a2682..a6ed73a195 100644
--- a/include/hw/riscv/riscv_hart.h
+++ b/include/hw/riscv/riscv_hart.h
@@ -38,6 +38,10 @@ struct RISCVHartArrayState {
uint32_t hartid_base;
char *cpu_type;
uint64_t resetvec;
+ uint32_t num_rnmi_irqvec;
+ uint64_t *rnmi_irqvec;
+ uint32_t num_rnmi_excpvec;
+ uint64_t *rnmi_excpvec;
RISCVCPU *harts;
};
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index a90808a3ba..2f64b3df22 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -1309,6 +1309,11 @@ static void riscv_cpu_set_irq(void *opaque, int irq, int level)
g_assert_not_reached();
}
}
+
+static void riscv_cpu_set_nmi(void *opaque, int irq, int level)
+{
+ riscv_cpu_set_rnmi(RISCV_CPU(opaque), irq, level);
+}
#endif /* CONFIG_USER_ONLY */
static bool riscv_cpu_is_dynamic(Object *cpu_obj)
@@ -1332,6 +1337,8 @@ static void riscv_cpu_init(Object *obj)
#ifndef CONFIG_USER_ONLY
qdev_init_gpio_in(DEVICE(obj), riscv_cpu_set_irq,
IRQ_LOCAL_MAX + IRQ_LOCAL_GUEST_MAX);
+ qdev_init_gpio_in_named(DEVICE(cpu), riscv_cpu_set_nmi,
+ "riscv.cpu.rnmi", RNMI_MAX);
#endif /* CONFIG_USER_ONLY */
general_user_opts = g_hash_table_new(g_str_hash, g_str_equal);
@@ -2681,6 +2688,10 @@ static Property riscv_cpu_properties[] = {
#ifndef CONFIG_USER_ONLY
DEFINE_PROP_UINT64("resetvec", RISCVCPU, env.resetvec, DEFAULT_RSTVEC),
+ DEFINE_PROP_UINT64("rnmi-interrupt-vector", RISCVCPU, env.rnmi_irqvec,
+ DEFAULT_RNMI_IRQVEC),
+ DEFINE_PROP_UINT64("rnmi-exception-vector", RISCVCPU, env.rnmi_excpvec,
+ DEFAULT_RNMI_EXCPVEC),
#endif
DEFINE_PROP_BOOL("short-isa-string", RISCVCPU, cfg.short_isa_string, false),
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index 1619c3acb6..d8ad04ec31 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -472,6 +472,11 @@ struct CPUArchState {
uint64_t kvm_timer_state;
uint64_t kvm_timer_frequency;
#endif /* CONFIG_KVM */
+
+ /* RNMI */
+ target_ulong rnmip;
+ uint64_t rnmi_irqvec;
+ uint64_t rnmi_excpvec;
};
/*
@@ -568,6 +573,7 @@ void riscv_cpu_swap_hypervisor_regs(CPURISCVState *env);
int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint64_t interrupts);
uint64_t riscv_cpu_update_mip(CPURISCVState *env, uint64_t mask,
uint64_t value);
+void riscv_cpu_set_rnmi(RISCVCPU *cpu, uint32_t irq, bool level);
void riscv_cpu_interrupt(CPURISCVState *env);
#define BOOL_TO_MASK(x) (-!!(x)) /* helper for riscv_cpu_update_mip value */
void riscv_cpu_set_rdtime_fn(CPURISCVState *env, uint64_t (*fn)(void *),
diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
index 32b068f18a..e14b654c35 100644
--- a/target/riscv/cpu_bits.h
+++ b/target/riscv/cpu_bits.h
@@ -662,6 +662,12 @@ typedef enum {
/* Default Reset Vector address */
#define DEFAULT_RSTVEC 0x1000
+/* Default RNMI Interrupt Vector address */
+#define DEFAULT_RNMI_IRQVEC 0x0
+
+/* Default RNMI Exception Vector address */
+#define DEFAULT_RNMI_EXCPVEC 0x0
+
/* Exception causes */
typedef enum RISCVException {
RISCV_EXCP_NONE = -1, /* sentinel value */
@@ -711,6 +717,9 @@ typedef enum RISCVException {
/* -1 is due to bit zero of hgeip and hgeie being ROZ. */
#define IRQ_LOCAL_GUEST_MAX (TARGET_LONG_BITS - 1)
+/* RNMI causes */
+#define RNMI_MAX 16
+
/* mip masks */
#define MIP_USIP (1 << IRQ_U_SOFT)
#define MIP_SSIP (1 << IRQ_S_SOFT)
@@ -942,6 +951,9 @@ typedef enum RISCVException {
#define MHPMEVENT_IDX_MASK 0xFFFFF
#define MHPMEVENT_SSCOF_RESVD 16
+/* RISC-V-specific interrupt pending bits. */
+#define CPU_INTERRUPT_RNMI CPU_INTERRUPT_TGT_EXT_0
+
/* JVT CSR bits */
#define JVT_MODE 0x3F
#define JVT_BASE (~0x3F)
diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
index 395a1d9140..f1d464a554 100644
--- a/target/riscv/cpu_helper.c
+++ b/target/riscv/cpu_helper.c
@@ -434,6 +434,18 @@ static int riscv_cpu_local_irq_pending(CPURISCVState *env)
uint64_t vsbits, irq_delegated;
int virq;
+ /* Priority: RNMI > Other interrupt. */
+ if (riscv_cpu_cfg(env)->ext_smrnmi) {
+ /* If mnstatus.NMIE == 0, all interrupts are disabled. */
+ if (!get_field(env->mnstatus, MNSTATUS_NMIE)) {
+ return RISCV_EXCP_NONE;
+ }
+
+ if (env->rnmip) {
+ return ctz64(env->rnmip); /* since non-zero */
+ }
+ }
+
/* Determine interrupt enable state of all privilege modes */
if (env->virt_enabled) {
mie = 1;
@@ -496,7 +508,9 @@ static int riscv_cpu_local_irq_pending(CPURISCVState *env)
bool riscv_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
{
- if (interrupt_request & CPU_INTERRUPT_HARD) {
+ uint32_t mask = CPU_INTERRUPT_HARD | CPU_INTERRUPT_RNMI;
+
+ if (interrupt_request & mask) {
RISCVCPU *cpu = RISCV_CPU(cs);
CPURISCVState *env = &cpu->env;
int interruptno = riscv_cpu_local_irq_pending(env);
@@ -619,6 +633,30 @@ void riscv_cpu_set_geilen(CPURISCVState *env, target_ulong geilen)
env->geilen = geilen;
}
+void riscv_cpu_set_rnmi(RISCVCPU *cpu, uint32_t irq, bool level)
+{
+ CPURISCVState *env = &cpu->env;
+ CPUState *cs = CPU(cpu);
+ bool release_lock = false;
+
+ if (!bql_locked()) {
+ release_lock = true;
+ bql_lock();
+ }
+
+ if (level) {
+ env->rnmip |= 1 << irq;
+ cpu_interrupt(cs, CPU_INTERRUPT_RNMI);
+ } else {
+ env->rnmip &= ~(1 << irq);
+ cpu_reset_interrupt(cs, CPU_INTERRUPT_RNMI);
+ }
+
+ if (release_lock) {
+ bql_unlock();
+ }
+}
+
int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint64_t interrupts)
{
CPURISCVState *env = &cpu->env;
@@ -1654,6 +1692,7 @@ void riscv_cpu_do_interrupt(CPUState *cs)
bool virt = env->virt_enabled;
bool write_gva = false;
uint64_t s;
+ int mode;
/*
* cs->exception is 32-bits wide unlike mcause which is XLEN-bits wide
@@ -1670,6 +1709,20 @@ void riscv_cpu_do_interrupt(CPUState *cs)
target_ulong tinst = 0;
target_ulong htval = 0;
target_ulong mtval2 = 0;
+ bool nmi_execp = false;
+
+ if (cpu->cfg.ext_smrnmi && env->rnmip && async) {
+ env->mnstatus = set_field(env->mnstatus, MNSTATUS_NMIE, false);
+ env->mnstatus = set_field(env->mnstatus, MNSTATUS_MNPV,
+ env->virt_enabled);
+ env->mnstatus = set_field(env->mnstatus, MNSTATUS_MNPP,
+ env->priv);
+ env->mncause = cause | ((target_ulong)1U << (TARGET_LONG_BITS - 1));
+ env->mnepc = env->pc;
+ env->pc = env->rnmi_irqvec;
+ riscv_cpu_set_mode(env, PRV_M, virt);
+ return;
+ }
if (!async) {
/* set tval to badaddr for traps with address information */
@@ -1755,8 +1808,20 @@ void riscv_cpu_do_interrupt(CPUState *cs)
__func__, env->mhartid, async, cause, env->pc, tval,
riscv_cpu_get_trap_name(cause, async));
- if (env->priv <= PRV_S && cause < 64 &&
- (((deleg >> cause) & 1) || s_injected || vs_injected)) {
+ mode = env->priv <= PRV_S && cause < 64 &&
+ (((deleg >> cause) & 1) || s_injected || vs_injected) ? PRV_S : PRV_M;
+
+ /*
+ * If the hart encounters an exception while executing in M-mode,
+ * with the mnstatus.NMIE bit clear, the program counter is set to
+ * the RNMI exception trap handler address.
+ */
+ nmi_execp = cpu->cfg.ext_smrnmi &&
+ !get_field(env->mnstatus, MNSTATUS_NMIE) &&
+ !async &&
+ mode == PRV_M;
+
+ if (mode == PRV_S) {
/* handle the trap in S-mode */
if (riscv_has_ext(env, RVH)) {
uint64_t hdeleg = async ? env->hideleg : env->hedeleg;
@@ -1834,6 +1899,12 @@ void riscv_cpu_do_interrupt(CPUState *cs)
env->mtinst = tinst;
env->pc = (env->mtvec >> 2 << 2) +
((async && (env->mtvec & 3) == 1) ? cause * 4 : 0);
+ if (cpu->cfg.ext_smrnmi && nmi_execp) {
+ env->pc = env->rnmi_excpvec;
+ } else {
+ env->pc = (env->mtvec >> 2 << 2) +
+ ((async && (env->mtvec & 3) == 1) ? cause * 4 : 0);
+ }
riscv_cpu_set_mode(env, PRV_M, virt);
}
--
2.39.3
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v5 3/5] target/riscv: Add Smrnmi CSRs.
2024-08-09 8:12 [PATCH v5 0/5] target/riscv: Add Smrnmi support Tommy Wu
2024-08-09 8:12 ` [PATCH v5 1/5] target/riscv: Add `ext_smrnmi` in the RISCVCPUConfig Tommy Wu
2024-08-09 8:12 ` [PATCH v5 2/5] target/riscv: Handle Smrnmi interrupt and exception Tommy Wu
@ 2024-08-09 8:12 ` Tommy Wu
2024-08-09 8:12 ` [PATCH v5 4/5] target/riscv: Add Smrnmi mnret instruction Tommy Wu
2024-08-09 8:12 ` [PATCH v5 5/5] target/riscv: Add Smrnmi cpu extension Tommy Wu
4 siblings, 0 replies; 13+ messages in thread
From: Tommy Wu @ 2024-08-09 8:12 UTC (permalink / raw)
To: qemu-devel, qemu-riscv
Cc: frank.chang, palmer, alistair.francis, alistair23, bin.meng,
liweiwei, dbarboza, Tommy Wu
The Smrnmi extension adds the `MNSCRATCH`, `MNEPC`, `MNCAUSE`,
`MNSTATUS` CSRs.
Signed-off-by: Frank Chang <frank.chang@sifive.com>
Signed-off-by: Tommy Wu <tommy.wu@sifive.com>
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
---
target/riscv/cpu.c | 5 +++
target/riscv/cpu.h | 4 ++
target/riscv/cpu_bits.h | 11 ++++++
target/riscv/csr.c | 82 +++++++++++++++++++++++++++++++++++++++++
4 files changed, 102 insertions(+)
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index 2f64b3df22..98e6940e93 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -1024,6 +1024,11 @@ static void riscv_cpu_reset_hold(Object *obj, ResetType type)
riscv_trigger_reset_hold(env);
}
+ if (cpu->cfg.ext_smrnmi) {
+ env->rnmip = 0;
+ env->mnstatus = set_field(env->mnstatus, MNSTATUS_NMIE, false);
+ }
+
if (kvm_enabled()) {
kvm_riscv_reset_vcpu(cpu);
}
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index d8ad04ec31..a84e719d3f 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -474,6 +474,10 @@ struct CPUArchState {
#endif /* CONFIG_KVM */
/* RNMI */
+ target_ulong mnscratch;
+ target_ulong mnepc;
+ target_ulong mncause; /* mncause without bit XLEN-1 set to 1 */
+ target_ulong mnstatus;
target_ulong rnmip;
uint64_t rnmi_irqvec;
uint64_t rnmi_excpvec;
diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
index e14b654c35..da1723496c 100644
--- a/target/riscv/cpu_bits.h
+++ b/target/riscv/cpu_bits.h
@@ -350,6 +350,12 @@
#define CSR_PMPADDR14 0x3be
#define CSR_PMPADDR15 0x3bf
+/* RNMI */
+#define CSR_MNSCRATCH 0x740
+#define CSR_MNEPC 0x741
+#define CSR_MNCAUSE 0x742
+#define CSR_MNSTATUS 0x744
+
/* Debug/Trace Registers (shared with Debug Mode) */
#define CSR_TSELECT 0x7a0
#define CSR_TDATA1 0x7a1
@@ -627,6 +633,11 @@ typedef enum {
#define SATP64_ASID 0x0FFFF00000000000ULL
#define SATP64_PPN 0x00000FFFFFFFFFFFULL
+/* RNMI mnstatus CSR mask */
+#define MNSTATUS_NMIE 0x00000008
+#define MNSTATUS_MNPV 0x00000080
+#define MNSTATUS_MNPP 0x00001800
+
/* VM modes (satp.mode) privileged ISA 1.10 */
#define VM_1_10_MBARE 0
#define VM_1_10_SV32 1
diff --git a/target/riscv/csr.c b/target/riscv/csr.c
index ea3560342c..e5de72453c 100644
--- a/target/riscv/csr.c
+++ b/target/riscv/csr.c
@@ -578,6 +578,17 @@ static RISCVException debug(CPURISCVState *env, int csrno)
return RISCV_EXCP_ILLEGAL_INST;
}
+
+static RISCVException rnmi(CPURISCVState *env, int csrno)
+{
+ RISCVCPU *cpu = env_archcpu(env);
+
+ if (cpu->cfg.ext_smrnmi) {
+ return RISCV_EXCP_NONE;
+ }
+
+ return RISCV_EXCP_ILLEGAL_INST;
+}
#endif
static RISCVException seed(CPURISCVState *env, int csrno)
@@ -4566,6 +4577,67 @@ static RISCVException write_upmbase(CPURISCVState *env, int csrno,
return RISCV_EXCP_NONE;
}
+static RISCVException read_mnscratch(CPURISCVState *env, int csrno,
+ target_ulong *val)
+{
+ *val = env->mnscratch;
+ return RISCV_EXCP_NONE;
+}
+
+static int write_mnscratch(CPURISCVState *env, int csrno, target_ulong val)
+{
+ env->mnscratch = val;
+ return RISCV_EXCP_NONE;
+}
+
+static int read_mnepc(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ *val = env->mnepc;
+ return RISCV_EXCP_NONE;
+}
+
+static int write_mnepc(CPURISCVState *env, int csrno, target_ulong val)
+{
+ env->mnepc = val;
+ return RISCV_EXCP_NONE;
+}
+
+static int read_mncause(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ *val = env->mncause;
+ return RISCV_EXCP_NONE;
+}
+
+static int write_mncause(CPURISCVState *env, int csrno, target_ulong val)
+{
+ env->mncause = val;
+ return RISCV_EXCP_NONE;
+}
+
+static int read_mnstatus(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ *val = env->mnstatus;
+ return RISCV_EXCP_NONE;
+}
+
+static int write_mnstatus(CPURISCVState *env, int csrno, target_ulong val)
+{
+ target_ulong mask = (MNSTATUS_NMIE | MNSTATUS_MNPP);
+
+ if (riscv_has_ext(env, RVH)) {
+ /* Flush tlb on mnstatus fields that affect VM. */
+ if ((val ^ env->mnstatus) & MNSTATUS_MNPV) {
+ tlb_flush(env_cpu(env));
+ }
+
+ mask |= MNSTATUS_MNPV;
+ }
+
+ /* mnstatus.mnie can only be cleared by hardware. */
+ env->mnstatus = (env->mnstatus & MNSTATUS_NMIE) | (val & mask);
+ return RISCV_EXCP_NONE;
+}
+
#endif
/* Crypto Extension */
@@ -5070,6 +5142,16 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
write_sstateen_1_3,
.min_priv_ver = PRIV_VERSION_1_12_0 },
+ /* RNMI */
+ [CSR_MNSCRATCH] = { "mnscratch", rnmi, read_mnscratch, write_mnscratch,
+ .min_priv_ver = PRIV_VERSION_1_12_0 },
+ [CSR_MNEPC] = { "mnepc", rnmi, read_mnepc, write_mnepc,
+ .min_priv_ver = PRIV_VERSION_1_12_0 },
+ [CSR_MNCAUSE] = { "mncause", rnmi, read_mncause, write_mncause,
+ .min_priv_ver = PRIV_VERSION_1_12_0 },
+ [CSR_MNSTATUS] = { "mnstatus", rnmi, read_mnstatus, write_mnstatus,
+ .min_priv_ver = PRIV_VERSION_1_12_0 },
+
/* Supervisor Trap Setup */
[CSR_SSTATUS] = { "sstatus", smode, read_sstatus, write_sstatus,
NULL, read_sstatus_i128 },
--
2.39.3
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v5 4/5] target/riscv: Add Smrnmi mnret instruction.
2024-08-09 8:12 [PATCH v5 0/5] target/riscv: Add Smrnmi support Tommy Wu
` (2 preceding siblings ...)
2024-08-09 8:12 ` [PATCH v5 3/5] target/riscv: Add Smrnmi CSRs Tommy Wu
@ 2024-08-09 8:12 ` Tommy Wu
2024-08-19 3:49 ` Alistair Francis
2024-08-09 8:12 ` [PATCH v5 5/5] target/riscv: Add Smrnmi cpu extension Tommy Wu
4 siblings, 1 reply; 13+ messages in thread
From: Tommy Wu @ 2024-08-09 8:12 UTC (permalink / raw)
To: qemu-devel, qemu-riscv
Cc: frank.chang, palmer, alistair.francis, alistair23, bin.meng,
liweiwei, dbarboza, Tommy Wu
This patch adds a new instruction `mnret`. `mnret` is an M-mode-only
instruction that uses the values in `mnepc` and `mnstatus` to return to the
program counter, privilege mode, and virtualization mode of the
interrupted context.
Signed-off-by: Frank Chang <frank.chang@sifive.com>
Signed-off-by: Tommy Wu <tommy.wu@sifive.com>
---
target/riscv/helper.h | 1 +
target/riscv/insn32.decode | 3 ++
.../riscv/insn_trans/trans_privileged.c.inc | 12 +++++
target/riscv/op_helper.c | 46 +++++++++++++++++++
4 files changed, 62 insertions(+)
diff --git a/target/riscv/helper.h b/target/riscv/helper.h
index 451261ce5a..16ea240d26 100644
--- a/target/riscv/helper.h
+++ b/target/riscv/helper.h
@@ -131,6 +131,7 @@ DEF_HELPER_6(csrrw_i128, tl, env, int, tl, tl, tl, tl)
#ifndef CONFIG_USER_ONLY
DEF_HELPER_1(sret, tl, env)
DEF_HELPER_1(mret, tl, env)
+DEF_HELPER_1(mnret, tl, env)
DEF_HELPER_1(wfi, void, env)
DEF_HELPER_1(wrs_nto, void, env)
DEF_HELPER_1(tlb_flush, void, env)
diff --git a/target/riscv/insn32.decode b/target/riscv/insn32.decode
index c45b8fa1d8..d320631e8c 100644
--- a/target/riscv/insn32.decode
+++ b/target/riscv/insn32.decode
@@ -121,6 +121,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 bc5263a4e0..06bc20dda4 100644
--- a/target/riscv/insn_trans/trans_privileged.c.inc
+++ b/target/riscv/insn_trans/trans_privileged.c.inc
@@ -106,6 +106,18 @@ static bool trans_mret(DisasContext *ctx, arg_mret *a)
#endif
}
+static bool trans_mnret(DisasContext *ctx, arg_mnret *a)
+{
+#ifndef CONFIG_USER_ONLY
+ gen_helper_mnret(cpu_pc, tcg_env);
+ tcg_gen_exit_tb(NULL, 0); /* 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 25a5263573..3e26392e65 100644
--- a/target/riscv/op_helper.c
+++ b/target/riscv/op_helper.c
@@ -353,6 +353,52 @@ target_ulong helper_mret(CPURISCVState *env)
return retpc;
}
+target_ulong helper_mnret(CPURISCVState *env)
+{
+ if (!riscv_cpu_cfg(env)->ext_smrnmi) {
+ /* 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_MNPP);
+
+ if (riscv_cpu_cfg(env)->pmp &&
+ !pmp_get_num_rules(env) && (prev_priv != PRV_M)) {
+ riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
+ }
+
+ target_ulong prev_virt = get_field(env->mnstatus, MNSTATUS_MNPV) &&
+ (prev_priv != PRV_M);
+ env->mnstatus = set_field(env->mnstatus, MNSTATUS_NMIE, true);
+
+ /*
+ * If MNRET changes the privilege mode to a mode
+ * less privileged than M, it also sets mstatus.MPRV to 0.
+ */
+ if (prev_priv < PRV_M) {
+ env->mstatus = set_field(env->mstatus, MSTATUS_MPRV, false);
+ }
+
+ if (riscv_has_ext(env, RVH) && prev_virt) {
+ riscv_cpu_swap_hypervisor_regs(env);
+ }
+
+ riscv_cpu_set_mode(env, prev_priv, prev_virt);
+
+ return retpc;
+}
+
void helper_wfi(CPURISCVState *env)
{
CPUState *cs = env_cpu(env);
--
2.39.3
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v5 5/5] target/riscv: Add Smrnmi cpu extension.
2024-08-09 8:12 [PATCH v5 0/5] target/riscv: Add Smrnmi support Tommy Wu
` (3 preceding siblings ...)
2024-08-09 8:12 ` [PATCH v5 4/5] target/riscv: Add Smrnmi mnret instruction Tommy Wu
@ 2024-08-09 8:12 ` Tommy Wu
4 siblings, 0 replies; 13+ messages in thread
From: Tommy Wu @ 2024-08-09 8:12 UTC (permalink / raw)
To: qemu-devel, qemu-riscv
Cc: frank.chang, palmer, alistair.francis, alistair23, bin.meng,
liweiwei, dbarboza, Tommy Wu
This adds the properties for ISA extension Smrnmi.
Signed-off-by: Frank Chang <frank.chang@sifive.com>
Signed-off-by: Tommy Wu <tommy.wu@sifive.com>
---
target/riscv/cpu.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index 98e6940e93..7ee7b9c4ee 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -184,6 +184,7 @@ const RISCVIsaExtData isa_edata_arr[] = {
ISA_EXT_DATA_ENTRY(smaia, PRIV_VERSION_1_12_0, ext_smaia),
ISA_EXT_DATA_ENTRY(smcntrpmf, PRIV_VERSION_1_12_0, ext_smcntrpmf),
ISA_EXT_DATA_ENTRY(smepmp, PRIV_VERSION_1_12_0, ext_smepmp),
+ ISA_EXT_DATA_ENTRY(smrnmi, PRIV_VERSION_1_12_0, ext_smrnmi),
ISA_EXT_DATA_ENTRY(smstateen, PRIV_VERSION_1_12_0, ext_smstateen),
ISA_EXT_DATA_ENTRY(ssaia, PRIV_VERSION_1_12_0, ext_ssaia),
ISA_EXT_DATA_ENTRY(ssccptr, PRIV_VERSION_1_11_0, has_priv_1_11),
@@ -1514,6 +1515,7 @@ const RISCVCPUMultiExtConfig riscv_cpu_extensions[] = {
MULTI_EXT_CFG_BOOL("smaia", ext_smaia, false),
MULTI_EXT_CFG_BOOL("smepmp", ext_smepmp, false),
+ MULTI_EXT_CFG_BOOL("smrnmi", ext_smrnmi, false),
MULTI_EXT_CFG_BOOL("smstateen", ext_smstateen, false),
MULTI_EXT_CFG_BOOL("ssaia", ext_ssaia, false),
MULTI_EXT_CFG_BOOL("svade", ext_svade, false),
--
2.39.3
^ permalink raw reply related [flat|nested] 13+ messages in thread
* RE: [PATCH v5 2/5] target/riscv: Handle Smrnmi interrupt and exception.
2024-08-09 8:12 ` [PATCH v5 2/5] target/riscv: Handle Smrnmi interrupt and exception Tommy Wu
@ 2024-08-15 1:40 ` Alvin Che-Chia Chang(張哲嘉)
2024-08-29 15:17 ` Tommy Wu
0 siblings, 1 reply; 13+ messages in thread
From: Alvin Che-Chia Chang(張哲嘉) @ 2024-08-15 1:40 UTC (permalink / raw)
To: Tommy Wu, qemu-devel@nongnu.org, qemu-riscv@nongnu.org
Cc: frank.chang@sifive.com, palmer@dabbelt.com,
alistair.francis@wdc.com, alistair23@gmail.com,
bin.meng@windriver.com, liweiwei@iscas.ac.cn,
dbarboza@ventanamicro.com
Hi Tommy,
> -----Original Message-----
> From: qemu-riscv-bounces+alvinga=andestech.com@nongnu.org
> <qemu-riscv-bounces+alvinga=andestech.com@nongnu.org> On Behalf Of
> Tommy Wu
> Sent: Friday, August 9, 2024 4:12 PM
> To: qemu-devel@nongnu.org; qemu-riscv@nongnu.org
> Cc: frank.chang@sifive.com; palmer@dabbelt.com; alistair.francis@wdc.com;
> alistair23@gmail.com; bin.meng@windriver.com; liweiwei@iscas.ac.cn;
> dbarboza@ventanamicro.com; Tommy Wu <tommy.wu@sifive.com>
> Subject: [PATCH v5 2/5] target/riscv: Handle Smrnmi interrupt and exception.
>
> [EXTERNAL MAIL]
>
> Because the RNMI interrupt trap handler address is implementation defined.
> We add the `rnmi-interrupt-vector` and `rnmi-exception-vector` as the
> property of the harts. It’s very easy for users to set the address based on their
> expectation. This patch also adds the functionality to handle the RNMI signals.
>
> Signed-off-by: Frank Chang <frank.chang@sifive.com>
> Signed-off-by: Tommy Wu <tommy.wu@sifive.com>
> ---
> hw/riscv/riscv_hart.c | 18 ++++++++
> include/hw/riscv/riscv_hart.h | 4 ++
> target/riscv/cpu.c | 11 +++++
> target/riscv/cpu.h | 6 +++
> target/riscv/cpu_bits.h | 12 ++++++
> target/riscv/cpu_helper.c | 77
> +++++++++++++++++++++++++++++++++--
> 6 files changed, 125 insertions(+), 3 deletions(-)
>
> diff --git a/hw/riscv/riscv_hart.c b/hw/riscv/riscv_hart.c index
> 613ea2aaa0..b7d73f7a82 100644
> --- a/hw/riscv/riscv_hart.c
> +++ b/hw/riscv/riscv_hart.c
> @@ -33,6 +33,12 @@ static Property riscv_harts_props[] = {
> DEFINE_PROP_STRING("cpu-type", RISCVHartArrayState, cpu_type),
> DEFINE_PROP_UINT64("resetvec", RISCVHartArrayState, resetvec,
> DEFAULT_RSTVEC),
> + DEFINE_PROP_ARRAY("rnmi-interrupt-vector", RISCVHartArrayState,
> + num_rnmi_irqvec, rnmi_irqvec,
> qdev_prop_uint64,
> + uint64_t),
> + DEFINE_PROP_ARRAY("rnmi-exception-vector", RISCVHartArrayState,
> + num_rnmi_excpvec, rnmi_excpvec,
> qdev_prop_uint64,
> + uint64_t),
> DEFINE_PROP_END_OF_LIST(),
> };
>
> @@ -47,6 +53,18 @@ static bool riscv_hart_realize(RISCVHartArrayState *s,
> int idx, {
> object_initialize_child(OBJECT(s), "harts[*]", &s->harts[idx], cpu_type);
> qdev_prop_set_uint64(DEVICE(&s->harts[idx]), "resetvec", s->resetvec);
> + if (s->harts[idx].cfg.ext_smrnmi) {
> + if (s->rnmi_irqvec) {
> + qdev_prop_set_uint64(DEVICE(&s->harts[idx]),
> + "rnmi-interrupt-vector",
> + s->rnmi_irqvec[idx]);
> + }
> + if (s->rnmi_excpvec) {
> + qdev_prop_set_uint64(DEVICE(&s->harts[idx]),
> + "rnmi-exception-vector",
> + s->rnmi_excpvec[idx]);
> + }
> + }
> s->harts[idx].env.mhartid = s->hartid_base + idx;
> qemu_register_reset(riscv_harts_cpu_reset, &s->harts[idx]);
> return qdev_realize(DEVICE(&s->harts[idx]), NULL, errp); diff --git
> a/include/hw/riscv/riscv_hart.h b/include/hw/riscv/riscv_hart.h index
> 912b4a2682..a6ed73a195 100644
> --- a/include/hw/riscv/riscv_hart.h
> +++ b/include/hw/riscv/riscv_hart.h
> @@ -38,6 +38,10 @@ struct RISCVHartArrayState {
> uint32_t hartid_base;
> char *cpu_type;
> uint64_t resetvec;
> + uint32_t num_rnmi_irqvec;
> + uint64_t *rnmi_irqvec;
> + uint32_t num_rnmi_excpvec;
> + uint64_t *rnmi_excpvec;
> RISCVCPU *harts;
> };
>
> diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index
> a90808a3ba..2f64b3df22 100644
> --- a/target/riscv/cpu.c
> +++ b/target/riscv/cpu.c
> @@ -1309,6 +1309,11 @@ static void riscv_cpu_set_irq(void *opaque, int irq,
> int level)
> g_assert_not_reached();
> }
> }
> +
> +static void riscv_cpu_set_nmi(void *opaque, int irq, int level) {
> + riscv_cpu_set_rnmi(RISCV_CPU(opaque), irq, level); }
> #endif /* CONFIG_USER_ONLY */
>
> static bool riscv_cpu_is_dynamic(Object *cpu_obj) @@ -1332,6 +1337,8 @@
> static void riscv_cpu_init(Object *obj) #ifndef CONFIG_USER_ONLY
> qdev_init_gpio_in(DEVICE(obj), riscv_cpu_set_irq,
> IRQ_LOCAL_MAX + IRQ_LOCAL_GUEST_MAX);
> + qdev_init_gpio_in_named(DEVICE(cpu), riscv_cpu_set_nmi,
> + "riscv.cpu.rnmi", RNMI_MAX);
> #endif /* CONFIG_USER_ONLY */
>
> general_user_opts = g_hash_table_new(g_str_hash, g_str_equal); @@
> -2681,6 +2688,10 @@ static Property riscv_cpu_properties[] = {
>
> #ifndef CONFIG_USER_ONLY
> DEFINE_PROP_UINT64("resetvec", RISCVCPU, env.resetvec,
> DEFAULT_RSTVEC),
> + DEFINE_PROP_UINT64("rnmi-interrupt-vector", RISCVCPU,
> env.rnmi_irqvec,
> + DEFAULT_RNMI_IRQVEC),
> + DEFINE_PROP_UINT64("rnmi-exception-vector", RISCVCPU,
> env.rnmi_excpvec,
> + DEFAULT_RNMI_EXCPVEC),
> #endif
>
> DEFINE_PROP_BOOL("short-isa-string", RISCVCPU, cfg.short_isa_string,
> false), diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index
> 1619c3acb6..d8ad04ec31 100644
> --- a/target/riscv/cpu.h
> +++ b/target/riscv/cpu.h
> @@ -472,6 +472,11 @@ struct CPUArchState {
> uint64_t kvm_timer_state;
> uint64_t kvm_timer_frequency;
> #endif /* CONFIG_KVM */
> +
> + /* RNMI */
> + target_ulong rnmip;
> + uint64_t rnmi_irqvec;
> + uint64_t rnmi_excpvec;
> };
>
> /*
> @@ -568,6 +573,7 @@ void riscv_cpu_swap_hypervisor_regs(CPURISCVState
> *env); int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint64_t interrupts);
> uint64_t riscv_cpu_update_mip(CPURISCVState *env, uint64_t mask,
> uint64_t value);
> +void riscv_cpu_set_rnmi(RISCVCPU *cpu, uint32_t irq, bool level);
> void riscv_cpu_interrupt(CPURISCVState *env); #define BOOL_TO_MASK(x)
> (-!!(x)) /* helper for riscv_cpu_update_mip value */ void
> riscv_cpu_set_rdtime_fn(CPURISCVState *env, uint64_t (*fn)(void *), diff --git
> a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index
> 32b068f18a..e14b654c35 100644
> --- a/target/riscv/cpu_bits.h
> +++ b/target/riscv/cpu_bits.h
> @@ -662,6 +662,12 @@ typedef enum {
> /* Default Reset Vector address */
> #define DEFAULT_RSTVEC 0x1000
>
> +/* Default RNMI Interrupt Vector address */
> +#define DEFAULT_RNMI_IRQVEC 0x0
> +
> +/* Default RNMI Exception Vector address */
> +#define DEFAULT_RNMI_EXCPVEC 0x0
> +
> /* Exception causes */
> typedef enum RISCVException {
> RISCV_EXCP_NONE = -1, /* sentinel value */ @@ -711,6 +717,9 @@
> typedef enum RISCVException {
> /* -1 is due to bit zero of hgeip and hgeie being ROZ. */
> #define IRQ_LOCAL_GUEST_MAX (TARGET_LONG_BITS -
> 1)
>
> +/* RNMI causes */
> +#define RNMI_MAX 16
> +
> /* mip masks */
> #define MIP_USIP (1 << IRQ_U_SOFT)
> #define MIP_SSIP (1 << IRQ_S_SOFT)
> @@ -942,6 +951,9 @@ typedef enum RISCVException {
> #define MHPMEVENT_IDX_MASK 0xFFFFF
> #define MHPMEVENT_SSCOF_RESVD 16
>
> +/* RISC-V-specific interrupt pending bits. */
> +#define CPU_INTERRUPT_RNMI
> CPU_INTERRUPT_TGT_EXT_0
> +
> /* JVT CSR bits */
> #define JVT_MODE 0x3F
> #define JVT_BASE (~0x3F)
> diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index
> 395a1d9140..f1d464a554 100644
> --- a/target/riscv/cpu_helper.c
> +++ b/target/riscv/cpu_helper.c
> @@ -434,6 +434,18 @@ static int riscv_cpu_local_irq_pending(CPURISCVState
> *env)
> uint64_t vsbits, irq_delegated;
> int virq;
>
> + /* Priority: RNMI > Other interrupt. */
> + if (riscv_cpu_cfg(env)->ext_smrnmi) {
> + /* If mnstatus.NMIE == 0, all interrupts are disabled. */
> + if (!get_field(env->mnstatus, MNSTATUS_NMIE)) {
> + return RISCV_EXCP_NONE;
> + }
> +
> + if (env->rnmip) {
> + return ctz64(env->rnmip); /* since non-zero */
> + }
> + }
> +
> /* Determine interrupt enable state of all privilege modes */
> if (env->virt_enabled) {
> mie = 1;
> @@ -496,7 +508,9 @@ static int riscv_cpu_local_irq_pending(CPURISCVState
> *env)
>
> bool riscv_cpu_exec_interrupt(CPUState *cs, int interrupt_request) {
> - if (interrupt_request & CPU_INTERRUPT_HARD) {
> + uint32_t mask = CPU_INTERRUPT_HARD | CPU_INTERRUPT_RNMI;
> +
> + if (interrupt_request & mask) {
> RISCVCPU *cpu = RISCV_CPU(cs);
> CPURISCVState *env = &cpu->env;
> int interruptno = riscv_cpu_local_irq_pending(env); @@ -619,6
> +633,30 @@ void riscv_cpu_set_geilen(CPURISCVState *env, target_ulong
> geilen)
> env->geilen = geilen;
> }
>
> +void riscv_cpu_set_rnmi(RISCVCPU *cpu, uint32_t irq, bool level) {
> + CPURISCVState *env = &cpu->env;
> + CPUState *cs = CPU(cpu);
> + bool release_lock = false;
> +
> + if (!bql_locked()) {
> + release_lock = true;
> + bql_lock();
> + }
> +
> + if (level) {
> + env->rnmip |= 1 << irq;
> + cpu_interrupt(cs, CPU_INTERRUPT_RNMI);
> + } else {
> + env->rnmip &= ~(1 << irq);
> + cpu_reset_interrupt(cs, CPU_INTERRUPT_RNMI);
> + }
> +
> + if (release_lock) {
> + bql_unlock();
> + }
> +}
> +
> int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint64_t interrupts) {
> CPURISCVState *env = &cpu->env;
> @@ -1654,6 +1692,7 @@ void riscv_cpu_do_interrupt(CPUState *cs)
> bool virt = env->virt_enabled;
> bool write_gva = false;
> uint64_t s;
> + int mode;
>
> /*
> * cs->exception is 32-bits wide unlike mcause which is XLEN-bits wide
> @@ -1670,6 +1709,20 @@ void riscv_cpu_do_interrupt(CPUState *cs)
> target_ulong tinst = 0;
> target_ulong htval = 0;
> target_ulong mtval2 = 0;
> + bool nmi_execp = false;
> +
> + if (cpu->cfg.ext_smrnmi && env->rnmip && async) {
> + env->mnstatus = set_field(env->mnstatus, MNSTATUS_NMIE, false);
> + env->mnstatus = set_field(env->mnstatus, MNSTATUS_MNPV,
> + env->virt_enabled);
> + env->mnstatus = set_field(env->mnstatus, MNSTATUS_MNPP,
> + env->priv);
> + env->mncause = cause | ((target_ulong)1U << (TARGET_LONG_BITS
> - 1));
> + env->mnepc = env->pc;
> + env->pc = env->rnmi_irqvec;
> + riscv_cpu_set_mode(env, PRV_M, virt);
> + return;
> + }
>
> if (!async) {
> /* set tval to badaddr for traps with address information */ @@
> -1755,8 +1808,20 @@ void riscv_cpu_do_interrupt(CPUState *cs)
> __func__, env->mhartid, async, cause, env->pc, tval,
> riscv_cpu_get_trap_name(cause, async));
>
> - if (env->priv <= PRV_S && cause < 64 &&
> - (((deleg >> cause) & 1) || s_injected || vs_injected)) {
> + mode = env->priv <= PRV_S && cause < 64 &&
> + (((deleg >> cause) & 1) || s_injected || vs_injected) ? PRV_S :
> + PRV_M;
> +
> + /*
> + * If the hart encounters an exception while executing in M-mode,
> + * with the mnstatus.NMIE bit clear, the program counter is set to
> + * the RNMI exception trap handler address.
> + */
> + nmi_execp = cpu->cfg.ext_smrnmi &&
> + !get_field(env->mnstatus, MNSTATUS_NMIE) &&
> + !async &&
> + mode == PRV_M;
> +
> + if (mode == PRV_S) {
> /* handle the trap in S-mode */
> if (riscv_has_ext(env, RVH)) {
> uint64_t hdeleg = async ? env->hideleg : env->hedeleg; @@
> -1834,6 +1899,12 @@ void riscv_cpu_do_interrupt(CPUState *cs)
> env->mtinst = tinst;
> env->pc = (env->mtvec >> 2 << 2) +
> ((async && (env->mtvec & 3) == 1) ? cause * 4 : 0);
Seems above two lines could be removed, since you set env->pc again in the following if-else.
> + if (cpu->cfg.ext_smrnmi && nmi_execp) {
> + env->pc = env->rnmi_excpvec;
> + } else {
> + env->pc = (env->mtvec >> 2 << 2) +
> + ((async && (env->mtvec & 3) == 1) ? cause * 4 :
> 0);
> + }
> riscv_cpu_set_mode(env, PRV_M, virt);
> }
>
> --
> 2.39.3
>
BRs,
Alvin Chang
CONFIDENTIALITY NOTICE:
This e-mail (and its attachments) may contain confidential and legally privileged information or information protected from disclosure. If you are not the intended recipient, you are hereby notified that any disclosure, copying, distribution, or use of the information contained herein is strictly prohibited. In this case, please immediately notify the sender by return e-mail, delete the message (and any accompanying documents) and destroy all printed hard copies. Thank you for your cooperation.
Copyright ANDES TECHNOLOGY CORPORATION - All Rights Reserved.
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v5 1/5] target/riscv: Add `ext_smrnmi` in the RISCVCPUConfig.
2024-08-09 8:12 ` [PATCH v5 1/5] target/riscv: Add `ext_smrnmi` in the RISCVCPUConfig Tommy Wu
@ 2024-08-19 3:40 ` Alistair Francis
0 siblings, 0 replies; 13+ messages in thread
From: Alistair Francis @ 2024-08-19 3:40 UTC (permalink / raw)
To: Tommy Wu
Cc: qemu-devel, qemu-riscv, frank.chang, palmer, alistair.francis,
bin.meng, liweiwei, dbarboza
On Fri, Aug 9, 2024 at 6:12 PM Tommy Wu <tommy.wu@sifive.com> wrote:
>
> The boolean variable `ext_smrnmi` is used to determine whether the
> Smrnmi extension exists.
>
> Signed-off-by: Frank Chang <frank.chang@sifive.com>
> Signed-off-by: Tommy Wu <tommy.wu@sifive.com>
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
Alistair
> ---
> target/riscv/cpu_cfg.h | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h
> index 8b272fb826..ae2a945b5f 100644
> --- a/target/riscv/cpu_cfg.h
> +++ b/target/riscv/cpu_cfg.h
> @@ -125,6 +125,7 @@ struct RISCVCPUConfig {
> bool ext_ssaia;
> bool ext_sscofpmf;
> bool ext_smepmp;
> + bool ext_smrnmi;
> bool rvv_ta_all_1s;
> bool rvv_ma_all_1s;
>
> --
> 2.39.3
>
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v5 4/5] target/riscv: Add Smrnmi mnret instruction.
2024-08-09 8:12 ` [PATCH v5 4/5] target/riscv: Add Smrnmi mnret instruction Tommy Wu
@ 2024-08-19 3:49 ` Alistair Francis
2024-08-29 16:12 ` Tommy Wu
0 siblings, 1 reply; 13+ messages in thread
From: Alistair Francis @ 2024-08-19 3:49 UTC (permalink / raw)
To: Tommy Wu
Cc: qemu-devel, qemu-riscv, frank.chang, palmer, alistair.francis,
bin.meng, liweiwei, dbarboza
On Fri, Aug 9, 2024 at 6:12 PM Tommy Wu <tommy.wu@sifive.com> wrote:
>
> This patch adds a new instruction `mnret`. `mnret` is an M-mode-only
> instruction that uses the values in `mnepc` and `mnstatus` to return to the
> program counter, privilege mode, and virtualization mode of the
> interrupted context.
>
> Signed-off-by: Frank Chang <frank.chang@sifive.com>
> Signed-off-by: Tommy Wu <tommy.wu@sifive.com>
> ---
> target/riscv/helper.h | 1 +
> target/riscv/insn32.decode | 3 ++
> .../riscv/insn_trans/trans_privileged.c.inc | 12 +++++
> target/riscv/op_helper.c | 46 +++++++++++++++++++
> 4 files changed, 62 insertions(+)
>
> diff --git a/target/riscv/helper.h b/target/riscv/helper.h
> index 451261ce5a..16ea240d26 100644
> --- a/target/riscv/helper.h
> +++ b/target/riscv/helper.h
> @@ -131,6 +131,7 @@ DEF_HELPER_6(csrrw_i128, tl, env, int, tl, tl, tl, tl)
> #ifndef CONFIG_USER_ONLY
> DEF_HELPER_1(sret, tl, env)
> DEF_HELPER_1(mret, tl, env)
> +DEF_HELPER_1(mnret, tl, env)
> DEF_HELPER_1(wfi, void, env)
> DEF_HELPER_1(wrs_nto, void, env)
> DEF_HELPER_1(tlb_flush, void, env)
> diff --git a/target/riscv/insn32.decode b/target/riscv/insn32.decode
> index c45b8fa1d8..d320631e8c 100644
> --- a/target/riscv/insn32.decode
> +++ b/target/riscv/insn32.decode
> @@ -121,6 +121,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 bc5263a4e0..06bc20dda4 100644
> --- a/target/riscv/insn_trans/trans_privileged.c.inc
> +++ b/target/riscv/insn_trans/trans_privileged.c.inc
> @@ -106,6 +106,18 @@ static bool trans_mret(DisasContext *ctx, arg_mret *a)
> #endif
> }
>
> +static bool trans_mnret(DisasContext *ctx, arg_mnret *a)
> +{
> +#ifndef CONFIG_USER_ONLY
> + gen_helper_mnret(cpu_pc, tcg_env);
> + tcg_gen_exit_tb(NULL, 0); /* 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 25a5263573..3e26392e65 100644
> --- a/target/riscv/op_helper.c
> +++ b/target/riscv/op_helper.c
> @@ -353,6 +353,52 @@ target_ulong helper_mret(CPURISCVState *env)
> return retpc;
> }
>
> +target_ulong helper_mnret(CPURISCVState *env)
> +{
> + if (!riscv_cpu_cfg(env)->ext_smrnmi) {
> + /* RNMI feature is not presented. */
> + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
> + }
> +
> + if (!(env->priv >= PRV_M)) {
This should just be (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)) {
What is the purpose of this check? The low bits should be zero,
unrelated to the compressed instructions
> + 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_MNPP);
> +
> + if (riscv_cpu_cfg(env)->pmp &&
> + !pmp_get_num_rules(env) && (prev_priv != PRV_M)) {
I don't see this mentioned in:
https://github.com/riscv/riscv-isa-manual/blob/a4382e9c8e285360a88d8056c1253e1525552393/src/rnmi.adoc
Alistair
> + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
> + }
> +
> + target_ulong prev_virt = get_field(env->mnstatus, MNSTATUS_MNPV) &&
> + (prev_priv != PRV_M);
> + env->mnstatus = set_field(env->mnstatus, MNSTATUS_NMIE, true);
> +
> + /*
> + * If MNRET changes the privilege mode to a mode
> + * less privileged than M, it also sets mstatus.MPRV to 0.
> + */
> + if (prev_priv < PRV_M) {
> + env->mstatus = set_field(env->mstatus, MSTATUS_MPRV, false);
> + }
> +
> + if (riscv_has_ext(env, RVH) && prev_virt) {
> + riscv_cpu_swap_hypervisor_regs(env);
> + }
> +
> + riscv_cpu_set_mode(env, prev_priv, prev_virt);
> +
> + return retpc;
> +}
> +
> void helper_wfi(CPURISCVState *env)
> {
> CPUState *cs = env_cpu(env);
> --
> 2.39.3
>
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v5 2/5] target/riscv: Handle Smrnmi interrupt and exception.
2024-08-15 1:40 ` Alvin Che-Chia Chang(張哲嘉)
@ 2024-08-29 15:17 ` Tommy Wu
0 siblings, 0 replies; 13+ messages in thread
From: Tommy Wu @ 2024-08-29 15:17 UTC (permalink / raw)
To: Alvin Che-Chia Chang(張哲嘉)
Cc: qemu-devel@nongnu.org, qemu-riscv@nongnu.org,
frank.chang@sifive.com, palmer@dabbelt.com,
alistair.francis@wdc.com, alistair23@gmail.com,
bin.meng@windriver.com, liweiwei@iscas.ac.cn,
dbarboza@ventanamicro.com
On Thu, Aug 15, 2024 at 9:40 AM Alvin Che-Chia Chang(張哲嘉)
<alvinga@andestech.com> wrote:
>
> Hi Tommy,
>
> > -----Original Message-----
> > From: qemu-riscv-bounces+alvinga=andestech.com@nongnu.org
> > <qemu-riscv-bounces+alvinga=andestech.com@nongnu.org> On Behalf Of
> > Tommy Wu
> > Sent: Friday, August 9, 2024 4:12 PM
> > To: qemu-devel@nongnu.org; qemu-riscv@nongnu.org
> > Cc: frank.chang@sifive.com; palmer@dabbelt.com; alistair.francis@wdc.com;
> > alistair23@gmail.com; bin.meng@windriver.com; liweiwei@iscas.ac.cn;
> > dbarboza@ventanamicro.com; Tommy Wu <tommy.wu@sifive.com>
> > Subject: [PATCH v5 2/5] target/riscv: Handle Smrnmi interrupt and exception.
> >
> > [EXTERNAL MAIL]
> >
> > Because the RNMI interrupt trap handler address is implementation defined.
> > We add the `rnmi-interrupt-vector` and `rnmi-exception-vector` as the
> > property of the harts. It’s very easy for users to set the address based on their
> > expectation. This patch also adds the functionality to handle the RNMI signals.
> >
> > Signed-off-by: Frank Chang <frank.chang@sifive.com>
> > Signed-off-by: Tommy Wu <tommy.wu@sifive.com>
> > ---
> > hw/riscv/riscv_hart.c | 18 ++++++++
> > include/hw/riscv/riscv_hart.h | 4 ++
> > target/riscv/cpu.c | 11 +++++
> > target/riscv/cpu.h | 6 +++
> > target/riscv/cpu_bits.h | 12 ++++++
> > target/riscv/cpu_helper.c | 77
> > +++++++++++++++++++++++++++++++++--
> > 6 files changed, 125 insertions(+), 3 deletions(-)
> >
> > diff --git a/hw/riscv/riscv_hart.c b/hw/riscv/riscv_hart.c index
> > 613ea2aaa0..b7d73f7a82 100644
> > --- a/hw/riscv/riscv_hart.c
> > +++ b/hw/riscv/riscv_hart.c
> > @@ -33,6 +33,12 @@ static Property riscv_harts_props[] = {
> > DEFINE_PROP_STRING("cpu-type", RISCVHartArrayState, cpu_type),
> > DEFINE_PROP_UINT64("resetvec", RISCVHartArrayState, resetvec,
> > DEFAULT_RSTVEC),
> > + DEFINE_PROP_ARRAY("rnmi-interrupt-vector", RISCVHartArrayState,
> > + num_rnmi_irqvec, rnmi_irqvec,
> > qdev_prop_uint64,
> > + uint64_t),
> > + DEFINE_PROP_ARRAY("rnmi-exception-vector", RISCVHartArrayState,
> > + num_rnmi_excpvec, rnmi_excpvec,
> > qdev_prop_uint64,
> > + uint64_t),
> > DEFINE_PROP_END_OF_LIST(),
> > };
> >
> > @@ -47,6 +53,18 @@ static bool riscv_hart_realize(RISCVHartArrayState *s,
> > int idx, {
> > object_initialize_child(OBJECT(s), "harts[*]", &s->harts[idx], cpu_type);
> > qdev_prop_set_uint64(DEVICE(&s->harts[idx]), "resetvec", s->resetvec);
> > + if (s->harts[idx].cfg.ext_smrnmi) {
> > + if (s->rnmi_irqvec) {
> > + qdev_prop_set_uint64(DEVICE(&s->harts[idx]),
> > + "rnmi-interrupt-vector",
> > + s->rnmi_irqvec[idx]);
> > + }
> > + if (s->rnmi_excpvec) {
> > + qdev_prop_set_uint64(DEVICE(&s->harts[idx]),
> > + "rnmi-exception-vector",
> > + s->rnmi_excpvec[idx]);
> > + }
> > + }
> > s->harts[idx].env.mhartid = s->hartid_base + idx;
> > qemu_register_reset(riscv_harts_cpu_reset, &s->harts[idx]);
> > return qdev_realize(DEVICE(&s->harts[idx]), NULL, errp); diff --git
> > a/include/hw/riscv/riscv_hart.h b/include/hw/riscv/riscv_hart.h index
> > 912b4a2682..a6ed73a195 100644
> > --- a/include/hw/riscv/riscv_hart.h
> > +++ b/include/hw/riscv/riscv_hart.h
> > @@ -38,6 +38,10 @@ struct RISCVHartArrayState {
> > uint32_t hartid_base;
> > char *cpu_type;
> > uint64_t resetvec;
> > + uint32_t num_rnmi_irqvec;
> > + uint64_t *rnmi_irqvec;
> > + uint32_t num_rnmi_excpvec;
> > + uint64_t *rnmi_excpvec;
> > RISCVCPU *harts;
> > };
> >
> > diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index
> > a90808a3ba..2f64b3df22 100644
> > --- a/target/riscv/cpu.c
> > +++ b/target/riscv/cpu.c
> > @@ -1309,6 +1309,11 @@ static void riscv_cpu_set_irq(void *opaque, int irq,
> > int level)
> > g_assert_not_reached();
> > }
> > }
> > +
> > +static void riscv_cpu_set_nmi(void *opaque, int irq, int level) {
> > + riscv_cpu_set_rnmi(RISCV_CPU(opaque), irq, level); }
> > #endif /* CONFIG_USER_ONLY */
> >
> > static bool riscv_cpu_is_dynamic(Object *cpu_obj) @@ -1332,6 +1337,8 @@
> > static void riscv_cpu_init(Object *obj) #ifndef CONFIG_USER_ONLY
> > qdev_init_gpio_in(DEVICE(obj), riscv_cpu_set_irq,
> > IRQ_LOCAL_MAX + IRQ_LOCAL_GUEST_MAX);
> > + qdev_init_gpio_in_named(DEVICE(cpu), riscv_cpu_set_nmi,
> > + "riscv.cpu.rnmi", RNMI_MAX);
> > #endif /* CONFIG_USER_ONLY */
> >
> > general_user_opts = g_hash_table_new(g_str_hash, g_str_equal); @@
> > -2681,6 +2688,10 @@ static Property riscv_cpu_properties[] = {
> >
> > #ifndef CONFIG_USER_ONLY
> > DEFINE_PROP_UINT64("resetvec", RISCVCPU, env.resetvec,
> > DEFAULT_RSTVEC),
> > + DEFINE_PROP_UINT64("rnmi-interrupt-vector", RISCVCPU,
> > env.rnmi_irqvec,
> > + DEFAULT_RNMI_IRQVEC),
> > + DEFINE_PROP_UINT64("rnmi-exception-vector", RISCVCPU,
> > env.rnmi_excpvec,
> > + DEFAULT_RNMI_EXCPVEC),
> > #endif
> >
> > DEFINE_PROP_BOOL("short-isa-string", RISCVCPU, cfg.short_isa_string,
> > false), diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index
> > 1619c3acb6..d8ad04ec31 100644
> > --- a/target/riscv/cpu.h
> > +++ b/target/riscv/cpu.h
> > @@ -472,6 +472,11 @@ struct CPUArchState {
> > uint64_t kvm_timer_state;
> > uint64_t kvm_timer_frequency;
> > #endif /* CONFIG_KVM */
> > +
> > + /* RNMI */
> > + target_ulong rnmip;
> > + uint64_t rnmi_irqvec;
> > + uint64_t rnmi_excpvec;
> > };
> >
> > /*
> > @@ -568,6 +573,7 @@ void riscv_cpu_swap_hypervisor_regs(CPURISCVState
> > *env); int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint64_t interrupts);
> > uint64_t riscv_cpu_update_mip(CPURISCVState *env, uint64_t mask,
> > uint64_t value);
> > +void riscv_cpu_set_rnmi(RISCVCPU *cpu, uint32_t irq, bool level);
> > void riscv_cpu_interrupt(CPURISCVState *env); #define BOOL_TO_MASK(x)
> > (-!!(x)) /* helper for riscv_cpu_update_mip value */ void
> > riscv_cpu_set_rdtime_fn(CPURISCVState *env, uint64_t (*fn)(void *), diff --git
> > a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index
> > 32b068f18a..e14b654c35 100644
> > --- a/target/riscv/cpu_bits.h
> > +++ b/target/riscv/cpu_bits.h
> > @@ -662,6 +662,12 @@ typedef enum {
> > /* Default Reset Vector address */
> > #define DEFAULT_RSTVEC 0x1000
> >
> > +/* Default RNMI Interrupt Vector address */
> > +#define DEFAULT_RNMI_IRQVEC 0x0
> > +
> > +/* Default RNMI Exception Vector address */
> > +#define DEFAULT_RNMI_EXCPVEC 0x0
> > +
> > /* Exception causes */
> > typedef enum RISCVException {
> > RISCV_EXCP_NONE = -1, /* sentinel value */ @@ -711,6 +717,9 @@
> > typedef enum RISCVException {
> > /* -1 is due to bit zero of hgeip and hgeie being ROZ. */
> > #define IRQ_LOCAL_GUEST_MAX (TARGET_LONG_BITS -
> > 1)
> >
> > +/* RNMI causes */
> > +#define RNMI_MAX 16
> > +
> > /* mip masks */
> > #define MIP_USIP (1 << IRQ_U_SOFT)
> > #define MIP_SSIP (1 << IRQ_S_SOFT)
> > @@ -942,6 +951,9 @@ typedef enum RISCVException {
> > #define MHPMEVENT_IDX_MASK 0xFFFFF
> > #define MHPMEVENT_SSCOF_RESVD 16
> >
> > +/* RISC-V-specific interrupt pending bits. */
> > +#define CPU_INTERRUPT_RNMI
> > CPU_INTERRUPT_TGT_EXT_0
> > +
> > /* JVT CSR bits */
> > #define JVT_MODE 0x3F
> > #define JVT_BASE (~0x3F)
> > diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index
> > 395a1d9140..f1d464a554 100644
> > --- a/target/riscv/cpu_helper.c
> > +++ b/target/riscv/cpu_helper.c
> > @@ -434,6 +434,18 @@ static int riscv_cpu_local_irq_pending(CPURISCVState
> > *env)
> > uint64_t vsbits, irq_delegated;
> > int virq;
> >
> > + /* Priority: RNMI > Other interrupt. */
> > + if (riscv_cpu_cfg(env)->ext_smrnmi) {
> > + /* If mnstatus.NMIE == 0, all interrupts are disabled. */
> > + if (!get_field(env->mnstatus, MNSTATUS_NMIE)) {
> > + return RISCV_EXCP_NONE;
> > + }
> > +
> > + if (env->rnmip) {
> > + return ctz64(env->rnmip); /* since non-zero */
> > + }
> > + }
> > +
> > /* Determine interrupt enable state of all privilege modes */
> > if (env->virt_enabled) {
> > mie = 1;
> > @@ -496,7 +508,9 @@ static int riscv_cpu_local_irq_pending(CPURISCVState
> > *env)
> >
> > bool riscv_cpu_exec_interrupt(CPUState *cs, int interrupt_request) {
> > - if (interrupt_request & CPU_INTERRUPT_HARD) {
> > + uint32_t mask = CPU_INTERRUPT_HARD | CPU_INTERRUPT_RNMI;
> > +
> > + if (interrupt_request & mask) {
> > RISCVCPU *cpu = RISCV_CPU(cs);
> > CPURISCVState *env = &cpu->env;
> > int interruptno = riscv_cpu_local_irq_pending(env); @@ -619,6
> > +633,30 @@ void riscv_cpu_set_geilen(CPURISCVState *env, target_ulong
> > geilen)
> > env->geilen = geilen;
> > }
> >
> > +void riscv_cpu_set_rnmi(RISCVCPU *cpu, uint32_t irq, bool level) {
> > + CPURISCVState *env = &cpu->env;
> > + CPUState *cs = CPU(cpu);
> > + bool release_lock = false;
> > +
> > + if (!bql_locked()) {
> > + release_lock = true;
> > + bql_lock();
> > + }
> > +
> > + if (level) {
> > + env->rnmip |= 1 << irq;
> > + cpu_interrupt(cs, CPU_INTERRUPT_RNMI);
> > + } else {
> > + env->rnmip &= ~(1 << irq);
> > + cpu_reset_interrupt(cs, CPU_INTERRUPT_RNMI);
> > + }
> > +
> > + if (release_lock) {
> > + bql_unlock();
> > + }
> > +}
> > +
> > int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint64_t interrupts) {
> > CPURISCVState *env = &cpu->env;
> > @@ -1654,6 +1692,7 @@ void riscv_cpu_do_interrupt(CPUState *cs)
> > bool virt = env->virt_enabled;
> > bool write_gva = false;
> > uint64_t s;
> > + int mode;
> >
> > /*
> > * cs->exception is 32-bits wide unlike mcause which is XLEN-bits wide
> > @@ -1670,6 +1709,20 @@ void riscv_cpu_do_interrupt(CPUState *cs)
> > target_ulong tinst = 0;
> > target_ulong htval = 0;
> > target_ulong mtval2 = 0;
> > + bool nmi_execp = false;
> > +
> > + if (cpu->cfg.ext_smrnmi && env->rnmip && async) {
> > + env->mnstatus = set_field(env->mnstatus, MNSTATUS_NMIE, false);
> > + env->mnstatus = set_field(env->mnstatus, MNSTATUS_MNPV,
> > + env->virt_enabled);
> > + env->mnstatus = set_field(env->mnstatus, MNSTATUS_MNPP,
> > + env->priv);
> > + env->mncause = cause | ((target_ulong)1U << (TARGET_LONG_BITS
> > - 1));
> > + env->mnepc = env->pc;
> > + env->pc = env->rnmi_irqvec;
> > + riscv_cpu_set_mode(env, PRV_M, virt);
> > + return;
> > + }
> >
> > if (!async) {
> > /* set tval to badaddr for traps with address information */ @@
> > -1755,8 +1808,20 @@ void riscv_cpu_do_interrupt(CPUState *cs)
> > __func__, env->mhartid, async, cause, env->pc, tval,
> > riscv_cpu_get_trap_name(cause, async));
> >
> > - if (env->priv <= PRV_S && cause < 64 &&
> > - (((deleg >> cause) & 1) || s_injected || vs_injected)) {
> > + mode = env->priv <= PRV_S && cause < 64 &&
> > + (((deleg >> cause) & 1) || s_injected || vs_injected) ? PRV_S :
> > + PRV_M;
> > +
> > + /*
> > + * If the hart encounters an exception while executing in M-mode,
> > + * with the mnstatus.NMIE bit clear, the program counter is set to
> > + * the RNMI exception trap handler address.
> > + */
> > + nmi_execp = cpu->cfg.ext_smrnmi &&
> > + !get_field(env->mnstatus, MNSTATUS_NMIE) &&
> > + !async &&
> > + mode == PRV_M;
> > +
> > + if (mode == PRV_S) {
> > /* handle the trap in S-mode */
> > if (riscv_has_ext(env, RVH)) {
> > uint64_t hdeleg = async ? env->hideleg : env->hedeleg; @@
> > -1834,6 +1899,12 @@ void riscv_cpu_do_interrupt(CPUState *cs)
> > env->mtinst = tinst;
> > env->pc = (env->mtvec >> 2 << 2) +
> > ((async && (env->mtvec & 3) == 1) ? cause * 4 : 0);
> Seems above two lines could be removed, since you set env->pc again in the following if-else.
>
Thank you! I forgot to delete the redundant code.
I'll delete that in the v6 patchset.
Regards,
Tommy Wu
> > + if (cpu->cfg.ext_smrnmi && nmi_execp) {
> > + env->pc = env->rnmi_excpvec;
> > + } else {
> > + env->pc = (env->mtvec >> 2 << 2) +
> > + ((async && (env->mtvec & 3) == 1) ? cause * 4 :
> > 0);
> > + }
> > riscv_cpu_set_mode(env, PRV_M, virt);
> > }
> >
> > --
> > 2.39.3
> >
>
> BRs,
> Alvin Chang
>
> CONFIDENTIALITY NOTICE:
>
> This e-mail (and its attachments) may contain confidential and legally privileged information or information protected from disclosure. If you are not the intended recipient, you are hereby notified that any disclosure, copying, distribution, or use of the information contained herein is strictly prohibited. In this case, please immediately notify the sender by return e-mail, delete the message (and any accompanying documents) and destroy all printed hard copies. Thank you for your cooperation.
>
> Copyright ANDES TECHNOLOGY CORPORATION - All Rights Reserved.
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v5 4/5] target/riscv: Add Smrnmi mnret instruction.
2024-08-19 3:49 ` Alistair Francis
@ 2024-08-29 16:12 ` Tommy Wu
2024-08-29 23:42 ` Alistair Francis
0 siblings, 1 reply; 13+ messages in thread
From: Tommy Wu @ 2024-08-29 16:12 UTC (permalink / raw)
To: Alistair Francis
Cc: qemu-devel, qemu-riscv, frank.chang, palmer, alistair.francis,
bin.meng, liweiwei, dbarboza
On Mon, Aug 19, 2024 at 11:49 AM Alistair Francis <alistair23@gmail.com> wrote:
>
> On Fri, Aug 9, 2024 at 6:12 PM Tommy Wu <tommy.wu@sifive.com> wrote:
> >
> > This patch adds a new instruction `mnret`. `mnret` is an M-mode-only
> > instruction that uses the values in `mnepc` and `mnstatus` to return to the
> > program counter, privilege mode, and virtualization mode of the
> > interrupted context.
> >
> > Signed-off-by: Frank Chang <frank.chang@sifive.com>
> > Signed-off-by: Tommy Wu <tommy.wu@sifive.com>
> > ---
> > target/riscv/helper.h | 1 +
> > target/riscv/insn32.decode | 3 ++
> > .../riscv/insn_trans/trans_privileged.c.inc | 12 +++++
> > target/riscv/op_helper.c | 46 +++++++++++++++++++
> > 4 files changed, 62 insertions(+)
> >
> > diff --git a/target/riscv/helper.h b/target/riscv/helper.h
> > index 451261ce5a..16ea240d26 100644
> > --- a/target/riscv/helper.h
> > +++ b/target/riscv/helper.h
> > @@ -131,6 +131,7 @@ DEF_HELPER_6(csrrw_i128, tl, env, int, tl, tl, tl, tl)
> > #ifndef CONFIG_USER_ONLY
> > DEF_HELPER_1(sret, tl, env)
> > DEF_HELPER_1(mret, tl, env)
> > +DEF_HELPER_1(mnret, tl, env)
> > DEF_HELPER_1(wfi, void, env)
> > DEF_HELPER_1(wrs_nto, void, env)
> > DEF_HELPER_1(tlb_flush, void, env)
> > diff --git a/target/riscv/insn32.decode b/target/riscv/insn32.decode
> > index c45b8fa1d8..d320631e8c 100644
> > --- a/target/riscv/insn32.decode
> > +++ b/target/riscv/insn32.decode
> > @@ -121,6 +121,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 bc5263a4e0..06bc20dda4 100644
> > --- a/target/riscv/insn_trans/trans_privileged.c.inc
> > +++ b/target/riscv/insn_trans/trans_privileged.c.inc
> > @@ -106,6 +106,18 @@ static bool trans_mret(DisasContext *ctx, arg_mret *a)
> > #endif
> > }
> >
> > +static bool trans_mnret(DisasContext *ctx, arg_mnret *a)
> > +{
> > +#ifndef CONFIG_USER_ONLY
> > + gen_helper_mnret(cpu_pc, tcg_env);
> > + tcg_gen_exit_tb(NULL, 0); /* 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 25a5263573..3e26392e65 100644
> > --- a/target/riscv/op_helper.c
> > +++ b/target/riscv/op_helper.c
> > @@ -353,6 +353,52 @@ target_ulong helper_mret(CPURISCVState *env)
> > return retpc;
> > }
> >
> > +target_ulong helper_mnret(CPURISCVState *env)
> > +{
> > + if (!riscv_cpu_cfg(env)->ext_smrnmi) {
> > + /* RNMI feature is not presented. */
> > + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
> > + }
> > +
> > + if (!(env->priv >= PRV_M)) {
>
> This should just be (env->priv != PRV_M)
>
Thanks for the suggestion. You’re right.
The reason that we write the code is that we want to do the same
checks that `helper_mret` does.[1]
Maybe we can send another patchset to fix both `helper_mret` and `helper_mnret`.
[1] https://github.com/qemu/qemu/commit/0c3e702aca76ca6ebf2aac4451870efc9d52a7a3
> > + 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)) {
>
> What is the purpose of this check? The low bits should be zero,
> unrelated to the compressed instructions
>
This is also the same check that `helper_mret` does.[1]
Maybe we can send another patchset to fix both `helper_mret` and `helper_mnret`.
[1] https://github.com/qemu/qemu/commit/0c3e702aca76ca6ebf2aac4451870efc9d52a7a3
> > + 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_MNPP);
> > +
> > + if (riscv_cpu_cfg(env)->pmp &&
> > + !pmp_get_num_rules(env) && (prev_priv != PRV_M)) {
>
> I don't see this mentioned in:
>
> https://github.com/riscv/riscv-isa-manual/blob/a4382e9c8e285360a88d8056c1253e1525552393/src/rnmi.adoc
>
> Alistair
>
This is also the same check that `helper_mret` does.[2]
It seems that this part is mentioned in the `riscv-privileged`
specification, `Priority and Matching Logic` section.
[2] https://github.com/qemu/qemu/commit/0fbb5d2d3c9ded9fbd3f6f993974cc5e88e28912
Thanks for all the suggestions and code review.
Best Regards,
Tommy Wu.
> > + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
> > + }
> > +
> > + target_ulong prev_virt = get_field(env->mnstatus, MNSTATUS_MNPV) &&
> > + (prev_priv != PRV_M);
> > + env->mnstatus = set_field(env->mnstatus, MNSTATUS_NMIE, true);
> > +
> > + /*
> > + * If MNRET changes the privilege mode to a mode
> > + * less privileged than M, it also sets mstatus.MPRV to 0.
> > + */
> > + if (prev_priv < PRV_M) {
> > + env->mstatus = set_field(env->mstatus, MSTATUS_MPRV, false);
> > + }
> > +
> > + if (riscv_has_ext(env, RVH) && prev_virt) {
> > + riscv_cpu_swap_hypervisor_regs(env);
> > + }
> > +
> > + riscv_cpu_set_mode(env, prev_priv, prev_virt);
> > +
> > + return retpc;
> > +}
> > +
> > void helper_wfi(CPURISCVState *env)
> > {
> > CPUState *cs = env_cpu(env);
> > --
> > 2.39.3
> >
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v5 4/5] target/riscv: Add Smrnmi mnret instruction.
2024-08-29 16:12 ` Tommy Wu
@ 2024-08-29 23:42 ` Alistair Francis
2024-09-02 3:15 ` Tommy Wu
0 siblings, 1 reply; 13+ messages in thread
From: Alistair Francis @ 2024-08-29 23:42 UTC (permalink / raw)
To: Tommy Wu
Cc: qemu-devel, qemu-riscv, frank.chang, palmer, alistair.francis,
bin.meng, liweiwei, dbarboza
On Fri, Aug 30, 2024 at 2:12 AM Tommy Wu <tommy.wu@sifive.com> wrote:
>
> On Mon, Aug 19, 2024 at 11:49 AM Alistair Francis <alistair23@gmail.com> wrote:
> >
> > On Fri, Aug 9, 2024 at 6:12 PM Tommy Wu <tommy.wu@sifive.com> wrote:
> > >
> > > This patch adds a new instruction `mnret`. `mnret` is an M-mode-only
> > > instruction that uses the values in `mnepc` and `mnstatus` to return to the
> > > program counter, privilege mode, and virtualization mode of the
> > > interrupted context.
> > >
> > > Signed-off-by: Frank Chang <frank.chang@sifive.com>
> > > Signed-off-by: Tommy Wu <tommy.wu@sifive.com>
> > > ---
> > > target/riscv/helper.h | 1 +
> > > target/riscv/insn32.decode | 3 ++
> > > .../riscv/insn_trans/trans_privileged.c.inc | 12 +++++
> > > target/riscv/op_helper.c | 46 +++++++++++++++++++
> > > 4 files changed, 62 insertions(+)
> > >
> > > diff --git a/target/riscv/helper.h b/target/riscv/helper.h
> > > index 451261ce5a..16ea240d26 100644
> > > --- a/target/riscv/helper.h
> > > +++ b/target/riscv/helper.h
> > > @@ -131,6 +131,7 @@ DEF_HELPER_6(csrrw_i128, tl, env, int, tl, tl, tl, tl)
> > > #ifndef CONFIG_USER_ONLY
> > > DEF_HELPER_1(sret, tl, env)
> > > DEF_HELPER_1(mret, tl, env)
> > > +DEF_HELPER_1(mnret, tl, env)
> > > DEF_HELPER_1(wfi, void, env)
> > > DEF_HELPER_1(wrs_nto, void, env)
> > > DEF_HELPER_1(tlb_flush, void, env)
> > > diff --git a/target/riscv/insn32.decode b/target/riscv/insn32.decode
> > > index c45b8fa1d8..d320631e8c 100644
> > > --- a/target/riscv/insn32.decode
> > > +++ b/target/riscv/insn32.decode
> > > @@ -121,6 +121,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 bc5263a4e0..06bc20dda4 100644
> > > --- a/target/riscv/insn_trans/trans_privileged.c.inc
> > > +++ b/target/riscv/insn_trans/trans_privileged.c.inc
> > > @@ -106,6 +106,18 @@ static bool trans_mret(DisasContext *ctx, arg_mret *a)
> > > #endif
> > > }
> > >
> > > +static bool trans_mnret(DisasContext *ctx, arg_mnret *a)
> > > +{
> > > +#ifndef CONFIG_USER_ONLY
> > > + gen_helper_mnret(cpu_pc, tcg_env);
> > > + tcg_gen_exit_tb(NULL, 0); /* 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 25a5263573..3e26392e65 100644
> > > --- a/target/riscv/op_helper.c
> > > +++ b/target/riscv/op_helper.c
> > > @@ -353,6 +353,52 @@ target_ulong helper_mret(CPURISCVState *env)
> > > return retpc;
> > > }
> > >
> > > +target_ulong helper_mnret(CPURISCVState *env)
> > > +{
> > > + if (!riscv_cpu_cfg(env)->ext_smrnmi) {
> > > + /* RNMI feature is not presented. */
> > > + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
> > > + }
> > > +
> > > + if (!(env->priv >= PRV_M)) {
> >
> > This should just be (env->priv != PRV_M)
> >
>
> Thanks for the suggestion. You’re right.
> The reason that we write the code is that we want to do the same
> checks that `helper_mret` does.[1]
>
> Maybe we can send another patchset to fix both `helper_mret` and `helper_mnret`.
>
> [1] https://github.com/qemu/qemu/commit/0c3e702aca76ca6ebf2aac4451870efc9d52a7a3
>
> > > + 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)) {
> >
> > What is the purpose of this check? The low bits should be zero,
> > unrelated to the compressed instructions
> >
>
> This is also the same check that `helper_mret` does.[1]
> Maybe we can send another patchset to fix both `helper_mret` and `helper_mnret`.
Ah, ok. It's probably worth splitting the shared helper_mret() code
into a helper function then
Alistair
>
> [1] https://github.com/qemu/qemu/commit/0c3e702aca76ca6ebf2aac4451870efc9d52a7a3
>
> > > + 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_MNPP);
> > > +
> > > + if (riscv_cpu_cfg(env)->pmp &&
> > > + !pmp_get_num_rules(env) && (prev_priv != PRV_M)) {
> >
> > I don't see this mentioned in:
> >
> > https://github.com/riscv/riscv-isa-manual/blob/a4382e9c8e285360a88d8056c1253e1525552393/src/rnmi.adoc
> >
> > Alistair
> >
>
> This is also the same check that `helper_mret` does.[2]
> It seems that this part is mentioned in the `riscv-privileged`
> specification, `Priority and Matching Logic` section.
>
> [2] https://github.com/qemu/qemu/commit/0fbb5d2d3c9ded9fbd3f6f993974cc5e88e28912
>
> Thanks for all the suggestions and code review.
>
> Best Regards,
> Tommy Wu.
>
> > > + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
> > > + }
> > > +
> > > + target_ulong prev_virt = get_field(env->mnstatus, MNSTATUS_MNPV) &&
> > > + (prev_priv != PRV_M);
> > > + env->mnstatus = set_field(env->mnstatus, MNSTATUS_NMIE, true);
> > > +
> > > + /*
> > > + * If MNRET changes the privilege mode to a mode
> > > + * less privileged than M, it also sets mstatus.MPRV to 0.
> > > + */
> > > + if (prev_priv < PRV_M) {
> > > + env->mstatus = set_field(env->mstatus, MSTATUS_MPRV, false);
> > > + }
> > > +
> > > + if (riscv_has_ext(env, RVH) && prev_virt) {
> > > + riscv_cpu_swap_hypervisor_regs(env);
> > > + }
> > > +
> > > + riscv_cpu_set_mode(env, prev_priv, prev_virt);
> > > +
> > > + return retpc;
> > > +}
> > > +
> > > void helper_wfi(CPURISCVState *env)
> > > {
> > > CPUState *cs = env_cpu(env);
> > > --
> > > 2.39.3
> > >
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v5 4/5] target/riscv: Add Smrnmi mnret instruction.
2024-08-29 23:42 ` Alistair Francis
@ 2024-09-02 3:15 ` Tommy Wu
0 siblings, 0 replies; 13+ messages in thread
From: Tommy Wu @ 2024-09-02 3:15 UTC (permalink / raw)
To: Alistair Francis
Cc: qemu-devel, qemu-riscv, frank.chang, palmer, alistair.francis,
bin.meng, liweiwei, dbarboza
On Fri, Aug 30, 2024 at 7:42 AM Alistair Francis <alistair23@gmail.com> wrote:
>
> On Fri, Aug 30, 2024 at 2:12 AM Tommy Wu <tommy.wu@sifive.com> wrote:
> >
> > On Mon, Aug 19, 2024 at 11:49 AM Alistair Francis <alistair23@gmail.com> wrote:
> > >
> > > On Fri, Aug 9, 2024 at 6:12 PM Tommy Wu <tommy.wu@sifive.com> wrote:
> > > >
> > > > This patch adds a new instruction `mnret`. `mnret` is an M-mode-only
> > > > instruction that uses the values in `mnepc` and `mnstatus` to return to the
> > > > program counter, privilege mode, and virtualization mode of the
> > > > interrupted context.
> > > >
> > > > Signed-off-by: Frank Chang <frank.chang@sifive.com>
> > > > Signed-off-by: Tommy Wu <tommy.wu@sifive.com>
> > > > ---
> > > > target/riscv/helper.h | 1 +
> > > > target/riscv/insn32.decode | 3 ++
> > > > .../riscv/insn_trans/trans_privileged.c.inc | 12 +++++
> > > > target/riscv/op_helper.c | 46 +++++++++++++++++++
> > > > 4 files changed, 62 insertions(+)
> > > >
> > > > diff --git a/target/riscv/helper.h b/target/riscv/helper.h
> > > > index 451261ce5a..16ea240d26 100644
> > > > --- a/target/riscv/helper.h
> > > > +++ b/target/riscv/helper.h
> > > > @@ -131,6 +131,7 @@ DEF_HELPER_6(csrrw_i128, tl, env, int, tl, tl, tl, tl)
> > > > #ifndef CONFIG_USER_ONLY
> > > > DEF_HELPER_1(sret, tl, env)
> > > > DEF_HELPER_1(mret, tl, env)
> > > > +DEF_HELPER_1(mnret, tl, env)
> > > > DEF_HELPER_1(wfi, void, env)
> > > > DEF_HELPER_1(wrs_nto, void, env)
> > > > DEF_HELPER_1(tlb_flush, void, env)
> > > > diff --git a/target/riscv/insn32.decode b/target/riscv/insn32.decode
> > > > index c45b8fa1d8..d320631e8c 100644
> > > > --- a/target/riscv/insn32.decode
> > > > +++ b/target/riscv/insn32.decode
> > > > @@ -121,6 +121,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 bc5263a4e0..06bc20dda4 100644
> > > > --- a/target/riscv/insn_trans/trans_privileged.c.inc
> > > > +++ b/target/riscv/insn_trans/trans_privileged.c.inc
> > > > @@ -106,6 +106,18 @@ static bool trans_mret(DisasContext *ctx, arg_mret *a)
> > > > #endif
> > > > }
> > > >
> > > > +static bool trans_mnret(DisasContext *ctx, arg_mnret *a)
> > > > +{
> > > > +#ifndef CONFIG_USER_ONLY
> > > > + gen_helper_mnret(cpu_pc, tcg_env);
> > > > + tcg_gen_exit_tb(NULL, 0); /* 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 25a5263573..3e26392e65 100644
> > > > --- a/target/riscv/op_helper.c
> > > > +++ b/target/riscv/op_helper.c
> > > > @@ -353,6 +353,52 @@ target_ulong helper_mret(CPURISCVState *env)
> > > > return retpc;
> > > > }
> > > >
> > > > +target_ulong helper_mnret(CPURISCVState *env)
> > > > +{
> > > > + if (!riscv_cpu_cfg(env)->ext_smrnmi) {
> > > > + /* RNMI feature is not presented. */
> > > > + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
> > > > + }
> > > > +
> > > > + if (!(env->priv >= PRV_M)) {
> > >
> > > This should just be (env->priv != PRV_M)
> > >
> >
> > Thanks for the suggestion. You’re right.
> > The reason that we write the code is that we want to do the same
> > checks that `helper_mret` does.[1]
> >
> > Maybe we can send another patchset to fix both `helper_mret` and `helper_mnret`.
> >
> > [1] https://github.com/qemu/qemu/commit/0c3e702aca76ca6ebf2aac4451870efc9d52a7a3
> >
> > > > + 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)) {
> > >
> > > What is the purpose of this check? The low bits should be zero,
> > > unrelated to the compressed instructions
> > >
> >
> > This is also the same check that `helper_mret` does.[1]
> > Maybe we can send another patchset to fix both `helper_mret` and `helper_mnret`.
>
> Ah, ok. It's probably worth splitting the shared helper_mret() code
> into a helper function then
>
> Alistair
>
Thanks for the suggestion, I'll split the shared code into
a helper function in the v6 patchset.
Best Regards,
Tommy Wu
> >
> > [1] https://github.com/qemu/qemu/commit/0c3e702aca76ca6ebf2aac4451870efc9d52a7a3
> >
> > > > + 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_MNPP);
> > > > +
> > > > + if (riscv_cpu_cfg(env)->pmp &&
> > > > + !pmp_get_num_rules(env) && (prev_priv != PRV_M)) {
> > >
> > > I don't see this mentioned in:
> > >
> > > https://github.com/riscv/riscv-isa-manual/blob/a4382e9c8e285360a88d8056c1253e1525552393/src/rnmi.adoc
> > >
> > > Alistair
> > >
> >
> > This is also the same check that `helper_mret` does.[2]
> > It seems that this part is mentioned in the `riscv-privileged`
> > specification, `Priority and Matching Logic` section.
> >
> > [2] https://github.com/qemu/qemu/commit/0fbb5d2d3c9ded9fbd3f6f993974cc5e88e28912
> >
> > Thanks for all the suggestions and code review.
> >
> > Best Regards,
> > Tommy Wu.
> >
> > > > + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
> > > > + }
> > > > +
> > > > + target_ulong prev_virt = get_field(env->mnstatus, MNSTATUS_MNPV) &&
> > > > + (prev_priv != PRV_M);
> > > > + env->mnstatus = set_field(env->mnstatus, MNSTATUS_NMIE, true);
> > > > +
> > > > + /*
> > > > + * If MNRET changes the privilege mode to a mode
> > > > + * less privileged than M, it also sets mstatus.MPRV to 0.
> > > > + */
> > > > + if (prev_priv < PRV_M) {
> > > > + env->mstatus = set_field(env->mstatus, MSTATUS_MPRV, false);
> > > > + }
> > > > +
> > > > + if (riscv_has_ext(env, RVH) && prev_virt) {
> > > > + riscv_cpu_swap_hypervisor_regs(env);
> > > > + }
> > > > +
> > > > + riscv_cpu_set_mode(env, prev_priv, prev_virt);
> > > > +
> > > > + return retpc;
> > > > +}
> > > > +
> > > > void helper_wfi(CPURISCVState *env)
> > > > {
> > > > CPUState *cs = env_cpu(env);
> > > > --
> > > > 2.39.3
> > > >
^ permalink raw reply [flat|nested] 13+ messages in thread
end of thread, other threads:[~2024-09-02 3:16 UTC | newest]
Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-08-09 8:12 [PATCH v5 0/5] target/riscv: Add Smrnmi support Tommy Wu
2024-08-09 8:12 ` [PATCH v5 1/5] target/riscv: Add `ext_smrnmi` in the RISCVCPUConfig Tommy Wu
2024-08-19 3:40 ` Alistair Francis
2024-08-09 8:12 ` [PATCH v5 2/5] target/riscv: Handle Smrnmi interrupt and exception Tommy Wu
2024-08-15 1:40 ` Alvin Che-Chia Chang(張哲嘉)
2024-08-29 15:17 ` Tommy Wu
2024-08-09 8:12 ` [PATCH v5 3/5] target/riscv: Add Smrnmi CSRs Tommy Wu
2024-08-09 8:12 ` [PATCH v5 4/5] target/riscv: Add Smrnmi mnret instruction Tommy Wu
2024-08-19 3:49 ` Alistair Francis
2024-08-29 16:12 ` Tommy Wu
2024-08-29 23:42 ` Alistair Francis
2024-09-02 3:15 ` Tommy Wu
2024-08-09 8:12 ` [PATCH v5 5/5] target/riscv: Add Smrnmi cpu extension Tommy Wu
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).