public inbox for opensbi@lists.infradead.org
 help / color / mirror / Atom feed
From: Nylon Chen <nylon.chen@sifive.com>
To: opensbi@lists.infradead.org
Cc: zong.li@sifive.com, nick.hu@sifive.com,
	samuel.holland@sifive.com, yongxuan.wang@sifive.com,
	anup@brainfault.org, rkrcmar@qti.qualcomm.com,
	atishp@rivosinc.com, Nylon Chen <nylon.chen@sifive.com>
Subject: [PATCH v4 3/3] firmware: fw_base.S: Add common NMI trap handler
Date: Fri, 13 Mar 2026 06:39:41 -0700	[thread overview]
Message-ID: <20260313133941.3012966-4-nylon.chen@sifive.com> (raw)
In-Reply-To: <20260313133941.3012966-1-nylon.chen@sifive.com>

Implement the firmware-level RNMI entry point and complete the RNMI
initialization:

fw_base.S - sbi_rnmi_vector assembly entry:
  Mirrors the existing _trap_handler pattern.  MNSCRATCH holds the
  per-hart scratch pointer (set by rnmi_vector_init), so the entry
  sequence is structurally identical to _trap_handler:

    csrrw sp, MNSCRATCH, sp      ; sp=scratch_ptr, MNSCRATCH=original_sp
    REG_S t0, TMP0(sp)           ; stash t0 in scratch tmp0 slot
    check MNSTATUS.MNPP          ; same bit layout as MSTATUS.MPP

  S/U-mode interrupted:
    frame allocated below scratch area (same as _trap_handler_s_mode)

  M-mode interrupted:
    frame allocated on the existing M-mode stack so no separate RNMI
    stack is required (same as _trap_handler_m_mode)

  After saving all registers, MNEPC/MNSTATUS are saved into the
  standard sbi_trap_regs mepc/mstatus fields so the C handler can
  inspect them with the same accessors used for normal traps.

  The handler is called as:
    sbi_rnmi_trap_handler(regs, mncause)

  On return registers are restored and mnret is executed.  The mnret
  opcode is emitted as a raw .word (0x70200073) because older toolchain
  versions do not recognise the mnemonic.

sbi_trap.c - sbi_rnmi_trap_handler default implementation:
  Prints mnepc, mncause, and mnstatus, then hangs the hart.  This is
  the correct behaviour for an unexpected NMI; a platform that can
  actually handle and resume from an NMI should override this function.

sbi_trap.h - declarations for sbi_rnmi_vector and sbi_rnmi_trap_handler.

sbi_hart.c - complete rnmi_vector_init():
  Adds the platform set_rnmi_trap_vector() call and MNSTATUS.NMIE
  enablement that were deferred from the previous patch.

Co-developed-by: Zong Li <zong.li@sifive.com>
Signed-off-by: Zong Li <zong.li@sifive.com>
Suggested-by: Nick Hu <nick.hu@sifive.com>
Suggested-by: Samuel Holland <samuel.holland@sifive.com>
Signed-off-by: Nylon Chen <nylon.chen@sifive.com>
Signed-off-by: Yong-Xuan Wang <yongxuan.wang@sifive.com>
---
 firmware/fw_base.S     | 141 +++++++++++++++++++++++++++++++++++++++++
 include/sbi/sbi_trap.h |  22 ++++++-
 lib/sbi/sbi_hart.c     |  34 ++++++++--
 lib/sbi/sbi_trap.c     |  17 +++++
 4 files changed, 208 insertions(+), 6 deletions(-)

diff --git a/firmware/fw_base.S b/firmware/fw_base.S
index bce9e226..641f314a 100644
--- a/firmware/fw_base.S
+++ b/firmware/fw_base.S
@@ -643,6 +643,147 @@ memcmp:
 	REG_L	a0, SBI_TRAP_REGS_OFFSET(a0)(a0)
 .endm
 
+/* Smrnmi mnret instruction encoding (0x70200073) */
+.macro MNRET
+	.word 0x70200073
+.endm
+
+	.align 4
+	.section .entry, "ax", %progbits
+	.globl sbi_rnmi_vector
+sbi_rnmi_vector:
+	/* Swap SP with MNSCRATCH. After: sp=scratch_ptr, MNSCRATCH=original_sp */
+	csrrw	sp, CSR_MNSCRATCH, sp
+
+	/* Save T0 in scratch tmp0 slot */
+	REG_S	t0, SBI_SCRATCH_TMP0_OFFSET(sp)
+
+	/* Check MNSTATUS.MNPP to determine interrupted privilege mode */
+	csrr	t0, CSR_MNSTATUS
+	srl	t0, t0, MSTATUS_MPP_SHIFT
+	and	t0, t0, PRV_M
+	xori	t0, t0, PRV_M
+	beq	t0, zero, _sbi_rnmi_m_mode
+
+	/* Interrupted S/U-mode: use scratch area as exception stack */
+_sbi_rnmi_s_mode:
+	/* Load original SP from MNSCRATCH into T0 */
+	csrr	t0, CSR_MNSCRATCH
+	/* Restore MNSCRATCH = scratch_ptr for future RNMI entries */
+	csrw	CSR_MNSCRATCH, sp
+	/* Allocate exception frame just below scratch area */
+	addi	sp, sp, -(SBI_TRAP_REGS_SIZE)
+	j	_sbi_rnmi_save_regs
+
+	/* Interrupted M-mode: reuse existing M-mode stack */
+_sbi_rnmi_m_mode:
+	/* Load original SP from MNSCRATCH into T0 */
+	csrr	t0, CSR_MNSCRATCH
+	/* Restore MNSCRATCH = scratch_ptr for future RNMI entries */
+	csrw	CSR_MNSCRATCH, sp
+	/* Allocate exception frame on the existing M-mode stack */
+	addi	sp, t0, -(SBI_TRAP_REGS_SIZE)
+
+_sbi_rnmi_save_regs:
+	/* sp = exception frame, t0 = original SP, MNSCRATCH = scratch_ptr */
+
+	/* Save original SP (from T0) */
+	REG_S	t0, SBI_TRAP_REGS_OFFSET(sp)(sp)
+
+	/* Reload saved T0 from scratch tmp0, then save to exception frame */
+	csrr	t0, CSR_MNSCRATCH
+	REG_L	t0, SBI_SCRATCH_TMP0_OFFSET(t0)
+	REG_S	t0, SBI_TRAP_REGS_OFFSET(t0)(sp)
+
+	/* Save all general registers except SP and T0 (already saved) */
+	REG_S	zero, SBI_TRAP_REGS_OFFSET(zero)(sp)
+	REG_S	ra,   SBI_TRAP_REGS_OFFSET(ra)(sp)
+	REG_S	gp,   SBI_TRAP_REGS_OFFSET(gp)(sp)
+	REG_S	tp,   SBI_TRAP_REGS_OFFSET(tp)(sp)
+	REG_S	t1,   SBI_TRAP_REGS_OFFSET(t1)(sp)
+	REG_S	t2,   SBI_TRAP_REGS_OFFSET(t2)(sp)
+	REG_S	s0,   SBI_TRAP_REGS_OFFSET(s0)(sp)
+	REG_S	s1,   SBI_TRAP_REGS_OFFSET(s1)(sp)
+	REG_S	a0,   SBI_TRAP_REGS_OFFSET(a0)(sp)
+	REG_S	a1,   SBI_TRAP_REGS_OFFSET(a1)(sp)
+	REG_S	a2,   SBI_TRAP_REGS_OFFSET(a2)(sp)
+	REG_S	a3,   SBI_TRAP_REGS_OFFSET(a3)(sp)
+	REG_S	a4,   SBI_TRAP_REGS_OFFSET(a4)(sp)
+	REG_S	a5,   SBI_TRAP_REGS_OFFSET(a5)(sp)
+	REG_S	a6,   SBI_TRAP_REGS_OFFSET(a6)(sp)
+	REG_S	a7,   SBI_TRAP_REGS_OFFSET(a7)(sp)
+	REG_S	s2,   SBI_TRAP_REGS_OFFSET(s2)(sp)
+	REG_S	s3,   SBI_TRAP_REGS_OFFSET(s3)(sp)
+	REG_S	s4,   SBI_TRAP_REGS_OFFSET(s4)(sp)
+	REG_S	s5,   SBI_TRAP_REGS_OFFSET(s5)(sp)
+	REG_S	s6,   SBI_TRAP_REGS_OFFSET(s6)(sp)
+	REG_S	s7,   SBI_TRAP_REGS_OFFSET(s7)(sp)
+	REG_S	s8,   SBI_TRAP_REGS_OFFSET(s8)(sp)
+	REG_S	s9,   SBI_TRAP_REGS_OFFSET(s9)(sp)
+	REG_S	s10,  SBI_TRAP_REGS_OFFSET(s10)(sp)
+	REG_S	s11,  SBI_TRAP_REGS_OFFSET(s11)(sp)
+	REG_S	t3,   SBI_TRAP_REGS_OFFSET(t3)(sp)
+	REG_S	t4,   SBI_TRAP_REGS_OFFSET(t4)(sp)
+	REG_S	t5,   SBI_TRAP_REGS_OFFSET(t5)(sp)
+	REG_S	t6,   SBI_TRAP_REGS_OFFSET(t6)(sp)
+
+	/* Save MNEPC into regs->mepc, MNSTATUS into regs->mstatus */
+	csrr	t0, CSR_MNEPC
+	REG_S	t0, SBI_TRAP_REGS_OFFSET(mepc)(sp)
+	csrr	t0, CSR_MNSTATUS
+	REG_S	t0, SBI_TRAP_REGS_OFFSET(mstatus)(sp)
+
+	/* Call C RNMI handler: a0=regs, a1=mncause */
+	add	a0, sp, zero
+	csrr	a1, CSR_MNCAUSE
+	call	sbi_rnmi_trap_handler
+
+	/* Restore MNEPC and MNSTATUS from exception frame */
+	REG_L	t0, SBI_TRAP_REGS_OFFSET(mepc)(sp)
+	csrw	CSR_MNEPC, t0
+	REG_L	t0, SBI_TRAP_REGS_OFFSET(mstatus)(sp)
+	csrw	CSR_MNSTATUS, t0
+
+	/* Restore all general registers except SP and T0 */
+	REG_L	ra,  SBI_TRAP_REGS_OFFSET(ra)(sp)
+	REG_L	gp,  SBI_TRAP_REGS_OFFSET(gp)(sp)
+	REG_L	tp,  SBI_TRAP_REGS_OFFSET(tp)(sp)
+	REG_L	t1,  SBI_TRAP_REGS_OFFSET(t1)(sp)
+	REG_L	t2,  SBI_TRAP_REGS_OFFSET(t2)(sp)
+	REG_L	s0,  SBI_TRAP_REGS_OFFSET(s0)(sp)
+	REG_L	s1,  SBI_TRAP_REGS_OFFSET(s1)(sp)
+	REG_L	a0,  SBI_TRAP_REGS_OFFSET(a0)(sp)
+	REG_L	a1,  SBI_TRAP_REGS_OFFSET(a1)(sp)
+	REG_L	a2,  SBI_TRAP_REGS_OFFSET(a2)(sp)
+	REG_L	a3,  SBI_TRAP_REGS_OFFSET(a3)(sp)
+	REG_L	a4,  SBI_TRAP_REGS_OFFSET(a4)(sp)
+	REG_L	a5,  SBI_TRAP_REGS_OFFSET(a5)(sp)
+	REG_L	a6,  SBI_TRAP_REGS_OFFSET(a6)(sp)
+	REG_L	a7,  SBI_TRAP_REGS_OFFSET(a7)(sp)
+	REG_L	s2,  SBI_TRAP_REGS_OFFSET(s2)(sp)
+	REG_L	s3,  SBI_TRAP_REGS_OFFSET(s3)(sp)
+	REG_L	s4,  SBI_TRAP_REGS_OFFSET(s4)(sp)
+	REG_L	s5,  SBI_TRAP_REGS_OFFSET(s5)(sp)
+	REG_L	s6,  SBI_TRAP_REGS_OFFSET(s6)(sp)
+	REG_L	s7,  SBI_TRAP_REGS_OFFSET(s7)(sp)
+	REG_L	s8,  SBI_TRAP_REGS_OFFSET(s8)(sp)
+	REG_L	s9,  SBI_TRAP_REGS_OFFSET(s9)(sp)
+	REG_L	s10, SBI_TRAP_REGS_OFFSET(s10)(sp)
+	REG_L	s11, SBI_TRAP_REGS_OFFSET(s11)(sp)
+	REG_L	t3,  SBI_TRAP_REGS_OFFSET(t3)(sp)
+	REG_L	t4,  SBI_TRAP_REGS_OFFSET(t4)(sp)
+	REG_L	t5,  SBI_TRAP_REGS_OFFSET(t5)(sp)
+	REG_L	t6,  SBI_TRAP_REGS_OFFSET(t6)(sp)
+
+	/* Restore T0 */
+	REG_L	t0, SBI_TRAP_REGS_OFFSET(t0)(sp)
+
+	/* Restore SP */
+	REG_L	sp, SBI_TRAP_REGS_OFFSET(sp)(sp)
+
+	/* Return from RNMI */
+	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..6a56e64e 100644
--- a/include/sbi/sbi_trap.h
+++ b/include/sbi/sbi_trap.h
@@ -289,6 +289,24 @@ 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
+/**
+ * Default RNMI trap handler called from sbi_rnmi_vector.
+ *
+ * This is the default handler for Resumable Non-Maskable Interrupts (RNMI).
+ * It prints diagnostic information and hangs the hart. Platforms that can
+ * handle and resume from an NMI should override this function.
+ */
+void __attribute__((weak)) sbi_rnmi_trap_handler(struct sbi_trap_regs *regs,
+						  unsigned long mncause);
+
+#endif /* !__ASSEMBLER__ */
+
+#endif /* __SBI_TRAP_H__ */
diff --git a/lib/sbi/sbi_hart.c b/lib/sbi/sbi_hart.c
index eca6662f..519d29a0 100644
--- a/lib/sbi/sbi_hart.c
+++ b/lib/sbi/sbi_hart.c
@@ -766,21 +766,47 @@ int sbi_hart_init(struct sbi_scratch *scratch, bool cold_boot)
 	return rnmi_vector_init(scratch, cold_boot);
 }
 
+/* Forward declaration for the RNMI vector entry point in fw_base.S */
+extern void sbi_rnmi_vector(void);
+
 static int rnmi_vector_init(struct sbi_scratch *scratch, bool cold_boot)
 {
+	int ret;
+	const struct sbi_platform *plat = sbi_platform_ptr(scratch);
+	const struct sbi_platform_operations *ops =
+		plat ? sbi_platform_ops(plat) : NULL;
+
 	/* If the hart does not support Smrnmi then nothing to do. */
 	if (!sbi_hart_has_extension(scratch, SBI_HART_EXT_SMRNMI))
 		return 0;
 
+	/* Platform MUST provide RNMI vector hook if hart supports Smrnmi. */
+	if (!ops || !ops->set_rnmi_trap_vector) {
+		sbi_printf("%s: platform does not support Smrnmi\n", __func__);
+		return SBI_ENOTSUPP;
+	}
+
 	/*
 	 * Set MNSCRATCH to the scratch pointer (same role as MSCRATCH).
-	 * The RNMI handler (to be added in the next patch) will check
-	 * MNSTATUS.MNPP to determine whether to use the scratch area
-	 * (S/U-mode interrupted) or reuse the existing M-mode stack
-	 * (M-mode interrupted).
+	 * The RNMI handler swaps SP/MNSCRATCH on entry, detects the
+	 * interrupted mode via MNSTATUS.MNPP, and then either reuses the
+	 * existing M-mode stack (M-mode interrupted) or uses the scratch
+	 * area as a stack base (S/U-mode interrupted) -- mirroring what
+	 * _trap_handler already does for nested M-mode traps.
 	 */
 	csr_write(CSR_MNSCRATCH, (unsigned long)scratch);
 
+	/* Ask the platform to program the RNMI trap vector. */
+	ret = ops->set_rnmi_trap_vector((unsigned long)sbi_rnmi_vector,
+					cold_boot);
+	if (ret == SBI_ENODEV)
+		return 0;
+	if (ret)
+		return ret;
+
+	/* Enable RNMI now that MNSCRATCH and the trap vector are set up. */
+	csr_set(CSR_MNSTATUS, MNSTATUS_NMIE);
+
 	return 0;
 }
 
diff --git a/lib/sbi/sbi_trap.c b/lib/sbi/sbi_trap.c
index f41db4d1..38be122a 100644
--- a/lib/sbi/sbi_trap.c
+++ b/lib/sbi/sbi_trap.c
@@ -375,3 +375,20 @@ 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 __attribute__((weak)) sbi_rnmi_trap_handler(struct sbi_trap_regs *regs,
+						  unsigned long mncause)
+{
+	sbi_printf("mnepc    = 0x%" PRILX "\n", regs->mepc);
+	sbi_printf("mncause  = 0x%" PRILX "\n", mncause);
+	sbi_printf("mnstatus = 0x%" PRILX "\n", regs->mstatus);
+
+	sbi_hart_hang();
+}
-- 
2.43.7


-- 
opensbi mailing list
opensbi@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/opensbi

  parent reply	other threads:[~2026-03-13 13:40 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-03-13 13:39 [PATCH v4 0/3] Add support for RISC-V Smrnmi extension Nylon Chen
2026-03-13 13:39 ` [PATCH v4 1/3] lib: sbi: Add Smrnmi extension detection Nylon Chen
2026-03-13 13:39 ` [PATCH v4 2/3] lib: sbi: Enable Smrnmi extension handler Nylon Chen
2026-03-13 13:39 ` Nylon Chen [this message]
2026-03-13 16:03 ` [PATCH v4 0/3] Add support for RISC-V Smrnmi extension Nylon Chen

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260313133941.3012966-4-nylon.chen@sifive.com \
    --to=nylon.chen@sifive.com \
    --cc=anup@brainfault.org \
    --cc=atishp@rivosinc.com \
    --cc=nick.hu@sifive.com \
    --cc=opensbi@lists.infradead.org \
    --cc=rkrcmar@qti.qualcomm.com \
    --cc=samuel.holland@sifive.com \
    --cc=yongxuan.wang@sifive.com \
    --cc=zong.li@sifive.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox