[PATCH 3/3] firmware: fw_base.S: Add common NMI trap handler
Nylon Chen
nylon.chen at sifive.com
Thu Jan 29 00:13:43 PST 2026
Add rnmi_handler assembly wrapper and sbi_rnmi_trap_handler default
C handler. The default handler prints diagnostics and hangs the hart.
Co-developed-by: Zong Li <zong.li at sifive.com>
Signed-off-by: Zong Li <zong.li at sifive.com>
Suggested-by: Nick Hu <nick.hu at sifive.com>
Suggested-by: Samuel Holland <samuel.holland at sifive.com>
Signed-off-by: Nylon Chen <nylon.chen at sifive.com>
Signed-off-by: Yong-Xuan Wang <yongxuan.wang at sifive.com>
---
firmware/fw_base.S | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++
include/sbi/sbi_trap.h | 12 ++++++++--
lib/sbi/sbi_hart.c | 8 +++++++
lib/sbi/sbi_trap.c | 16 +++++++++++++
4 files changed, 98 insertions(+), 2 deletions(-)
diff --git a/firmware/fw_base.S b/firmware/fw_base.S
index bce9e226..c2b9a5f8 100644
--- a/firmware/fw_base.S
+++ b/firmware/fw_base.S
@@ -643,6 +643,70 @@ memcmp:
REG_L a0, SBI_TRAP_REGS_OFFSET(a0)(a0)
.endm
+.macro RNMI_SAVE_REGS
+ /* Save all general registers except SP (already in MNSCRATCH) */
+ TRAP_SAVE_GENERAL_REGS_EXCEPT_SP_T0
+ /* Also save t0 (TRAP_SAVE_GENERAL_REGS_EXCEPT_SP_T0 doesn't save it) */
+ REG_S t0, SBI_TRAP_REGS_OFFSET(t0)(sp)
+.endm
+
+.macro RNMI_RESTORE_REGS
+ /* Restore all general registers except A0 and T0 */
+ TRAP_RESTORE_GENERAL_REGS_EXCEPT_A0_T0
+ /* Restore A0 and T0 */
+ TRAP_RESTORE_A0_T0
+.endm
+
+/* Smrnmi mnret instruction encoding (0x70200073) */
+.macro MNRET
+ .word 0x70200073
+.endm
+
+ .section .entry, "ax", %progbits
+ .align 4
+ .globl sbi_rnmi_vector
+sbi_rnmi_vector:
+ /* Swap SP with MNSCRATCH */
+ csrrw sp, CSR_MNSCRATCH, sp
+
+ /* Allocate space for full trap registers structure */
+ addi sp, sp, -(SBI_TRAP_REGS_SIZE)
+
+ /* Save all general registers */
+ RNMI_SAVE_REGS
+
+ /* Save original SP (currently in MNSCRATCH) */
+ csrr t0, CSR_MNSCRATCH
+ REG_S t0, SBI_TRAP_REGS_OFFSET(sp)(sp)
+
+ /* Set up arguments for C RNMI handler */
+ add a0, sp, zero
+
+ /* Save RNMI MNEPC/MNSTATUS into regs->mepc/mstatus. */
+ csrr t0, CSR_MNEPC
+ REG_S t0, SBI_TRAP_REGS_OFFSET(mepc)(a0)
+ csrr t0, CSR_MNSTATUS
+ REG_S t0, SBI_TRAP_REGS_OFFSET(mstatus)(a0)
+
+ /* Pass RNMI cause as second argument */
+ csrr a1, CSR_MNCAUSE
+
+ /* Call the default C RNMI handler directly */
+ call sbi_rnmi_trap_handler
+
+ /* Restore RNMI stack pointer for future RNMI entries. */
+ addi t0, sp, SBI_TRAP_REGS_SIZE
+ csrw CSR_MNSCRATCH, t0
+
+ /* Set A0 to point to saved registers for restore macros */
+ add a0, sp, zero
+
+ /* Restore all general registers */
+ RNMI_RESTORE_REGS
+
+ /* Return from RNMI with mnret. */
+ MNRET
+
.section .entry, "ax", %progbits
.align 3
.globl _trap_handler
diff --git a/include/sbi/sbi_trap.h b/include/sbi/sbi_trap.h
index 731a0c98..afd95ad5 100644
--- a/include/sbi/sbi_trap.h
+++ b/include/sbi/sbi_trap.h
@@ -289,6 +289,14 @@ static inline void sbi_trap_set_context(struct sbi_scratch *scratch,
struct sbi_trap_context *sbi_trap_handler(struct sbi_trap_context *tcntx);
-#endif
+/**
+ * Common firmware RNMI trap handler entry in M-mode.
+ *
+ * Platforms program their RNMI trap vector to this address using the
+ * set_rnmi_trap_vector platform operation.
+ */
+void sbi_rnmi_vector(void);
-#endif
+#endif /* !__ASSEMBLER__ */
+
+#endif /* __SBI_TRAP_H__ */
diff --git a/lib/sbi/sbi_hart.c b/lib/sbi/sbi_hart.c
index baebc438..7586ee09 100644
--- a/lib/sbi/sbi_hart.c
+++ b/lib/sbi/sbi_hart.c
@@ -839,6 +839,7 @@ int sbi_rnmi_vector_init(struct sbi_scratch *scratch, bool cold_boot)
{
unsigned long rnmi_sp;
void *rnmi_ctx;
+ int ret;
const struct sbi_platform *plat = sbi_platform_ptr(scratch);
const struct sbi_platform_operations *ops =
plat ? sbi_platform_ops(plat) : NULL;
@@ -871,6 +872,13 @@ int sbi_rnmi_vector_init(struct sbi_scratch *scratch, bool cold_boot)
rnmi_sp = (unsigned long)rnmi_ctx + SBI_TRAP_REGS_SIZE;
csr_write(CSR_MNSCRATCH, rnmi_sp);
+ /* Ask the platform to program the RNMI trap vector. */
+ ret = ops->set_rnmi_trap_vector((uintptr_t)sbi_rnmi_vector, cold_boot);
+ if (ret == SBI_ENODEV)
+ return 0;
+ if (ret)
+ return ret;
+
/* Enable RNMI now that CSR_MNSCRATCH and the trap vector are set up. */
csr_set(CSR_MNSTATUS, MNSTATUS_NMIE);
diff --git a/lib/sbi/sbi_trap.c b/lib/sbi/sbi_trap.c
index f41db4d1..940b83d8 100644
--- a/lib/sbi/sbi_trap.c
+++ b/lib/sbi/sbi_trap.c
@@ -375,3 +375,19 @@ trap_done:
sbi_trap_set_context(scratch, tcntx->prev_context);
return tcntx;
}
+
+/**
+ * Default RNMI trap handler.
+ *
+ * This is the default handler for Resumable Non-Maskable Interrupts (RNMI).
+ * It is invoked directly from the firmware RNMI entry (rnmi_handler)
+ * and prints diagnostic information before hanging the hart.
+ */
+void sbi_rnmi_trap_handler(struct sbi_trap_regs *regs, unsigned long mncause)
+{
+ sbi_printf("mnepc = 0x%016lx\n", regs->mepc);
+ sbi_printf("mncause = 0x%016lx\n", mncause);
+ sbi_printf("mnstatus= 0x%016lx\n", regs->mstatus);
+
+ sbi_hart_hang();
+}
--
2.43.7
More information about the opensbi
mailing list