Skip to content

Commit a7079c0

Browse files
committed
[ot] target/riscv: Add support for Non-Maskable Interrupts
Adds support for Non-Maskable Interrupts (NMIs) to the RISC-V QEMU CPU implementation. NMIs are defined by the RISC-V specification to be independent to regular interrupts, used only for hardware error conditions and causing an immediate jump to an implementation-defined NMI vector running in M-mode, regardless of the values of e.g. MIE/MIP. The spec states that "the values written to mcause on an NMI are implementation-defined"; in Ibex we always use an mcause of 31 (and a corresponding mtvec offset of 0x7C to determine the jump address for the NMI handler). To aid a more generic implementation, this commit introduces a generic `nmi_cause` CPU environment value that can be used alongside the NMI functionality to allow implementations to change the mcause as they see fit. Signed-off-by: Alex Jones <[email protected]>
1 parent b3e8936 commit a7079c0

File tree

5 files changed

+30
-2
lines changed

5 files changed

+30
-2
lines changed

target/riscv/cpu.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -954,7 +954,9 @@ bool riscv_cpu_has_work(CPUState *cs)
954954
return riscv_cpu_all_pending(env) != 0 ||
955955
riscv_cpu_sirq_pending(env) != RISCV_EXCP_NONE ||
956956
riscv_cpu_vsirq_pending(env) != RISCV_EXCP_NONE ||
957-
env->debug_cs;
957+
env->debug_cs ||
958+
env->pending_nmi ||
959+
env->processing_nmi;
958960
#else
959961
return true;
960962
#endif
@@ -1403,6 +1405,11 @@ static void riscv_cpu_set_irq(void *opaque, int irq, int level)
14031405
BOOL_TO_MASK(level | env->software_seip));
14041406
}
14051407
break;
1408+
case IRQ_NMI:
1409+
/* NMIs do not need to update mip */
1410+
env->pending_nmi = true;
1411+
riscv_cpu_interrupt(env);
1412+
break;
14061413
default:
14071414
/* Handle platform / custom local interrupts */
14081415
riscv_cpu_update_mip(env, 1ULL << irq, BOOL_TO_MASK(level));

target/riscv/cpu.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,13 @@ struct CPUArchState {
447447
uint64_t dmhaltvec; /* Address of halt handler */
448448
uint64_t dmexcpvec; /* Address of exception handler */
449449

450+
/*
451+
* NMI support
452+
*/
453+
bool pending_nmi; /* True if an NMI is pending, and must be handled */
454+
bool processing_nmi; /* True if currently in an NMI handler */
455+
unsigned nmi_cause; /* Reason for entering NMI */
456+
450457
/*
451458
* CSRs for PointerMasking extension
452459
*/

target/riscv/cpu_bits.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -731,6 +731,7 @@ typedef enum RISCVException {
731731
#define IRQ_M_EXT 11
732732
#define IRQ_S_GEXT 12
733733
#define IRQ_PMU_OVF 13
734+
#define IRQ_NMI 14
734735
#define IRQ_LOCAL_MAX 64
735736
/* -1 is due to bit zero of hgeip and hgeie being ROZ. */
736737
#define IRQ_LOCAL_GUEST_MAX (TARGET_LONG_BITS - 1)

target/riscv/cpu_helper.c

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,18 @@ bool riscv_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
504504
if (unlikely(env->debug_dm &&
505505
(env->debugger || get_field(env->dcsr, DCSR_STEP)))) {
506506
return false;
507+
} else if (unlikely(env->pending_nmi && !env->processing_nmi)) {
508+
/*
509+
* NMIs are handled in the same way as machine traps, but have an
510+
* implementation-defined cause, and must be signalled via
511+
* additional flags, since they are implementation-specific, and
512+
* not visible through the mip register.
513+
*/
514+
cs->exception_index = RISCV_EXCP_INT_FLAG | env->nmi_cause;
515+
env->pending_nmi = false;
516+
env->processing_nmi = true;
517+
riscv_cpu_do_interrupt(cs);
518+
return true;
507519
}
508520
int interruptno = riscv_cpu_local_irq_pending(env);
509521
if (interruptno >= 0) {
@@ -653,7 +665,7 @@ void riscv_cpu_interrupt(CPURISCVState *env)
653665

654666
vstip = env->vstime_irq ? MIP_VSTIP : 0;
655667

656-
if (env->mip | vsgein | vstip | irqf) {
668+
if (env->mip | vsgein | vstip | irqf | env->pending_nmi) {
657669
cpu_interrupt(cs, CPU_INTERRUPT_HARD);
658670
} else {
659671
cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);

target/riscv/op_helper.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,7 @@ target_ulong helper_mret(CPURISCVState *env)
343343
mstatus = set_field(mstatus, MSTATUS_MPRV, 0);
344344
}
345345
env->mstatus = mstatus;
346+
env->processing_nmi = false;
346347

347348
if (riscv_has_ext(env, RVH) && prev_virt) {
348349
riscv_cpu_swap_hypervisor_regs(env);

0 commit comments

Comments
 (0)