Message ID | 20210309072925.4314-2-frank.chang@sifive.com |
---|---|
State | New |
Headers | show |
Series | target/riscv: add RNMI support | expand |
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 > >
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 > > > > >
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);