From 930423a66f5d786ee4423a45ec2b4963199e6d45 Mon Sep 17 00:00:00 2001 From: Evgeny Voevodin Date: Thu, 7 May 2026 18:08:02 +0000 Subject: [PATCH 1/7] include: sbi_scratch: Add tmp1 scratch space for RNMI context saving RNMI handlers use MNSCRATCH instead of MSCRATCH and need separate scratch space from regular trap handling. Add tmp1 for RNMI context while tmp0 remains for regular traps. Signed-off-by: Evgeny Voevodin Reviewed-by: Anup Patel --- firmware/fw_base.S | 3 ++- include/sbi/sbi_scratch.h | 11 ++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/firmware/fw_base.S b/firmware/fw_base.S index 2498797c..95750556 100644 --- a/firmware/fw_base.S +++ b/firmware/fw_base.S @@ -234,9 +234,10 @@ _scratch_init: /* Store hartid-to-scratch function address in scratch space */ lla a4, _hartid_to_scratch REG_S a4, SBI_SCRATCH_HARTID_TO_SCRATCH_OFFSET(tp) - /* Clear trap_context and tmp0 in scratch space */ + /* Clear trap_context, tmp0 and tmp1 in scratch space */ REG_S zero, SBI_SCRATCH_TRAP_CONTEXT_OFFSET(tp) REG_S zero, SBI_SCRATCH_TMP0_OFFSET(tp) + REG_S zero, SBI_SCRATCH_TMP1_OFFSET(tp) /* Store firmware options in scratch space */ MOV_3R s0, a0, s1, a1, s2, a2 #ifdef FW_OPTIONS diff --git a/include/sbi/sbi_scratch.h b/include/sbi/sbi_scratch.h index f1b4155d..2d375d9c 100644 --- a/include/sbi/sbi_scratch.h +++ b/include/sbi/sbi_scratch.h @@ -40,12 +40,14 @@ #define SBI_SCRATCH_TRAP_CONTEXT_OFFSET (11 * __SIZEOF_POINTER__) /** Offset of tmp0 member in sbi_scratch */ #define SBI_SCRATCH_TMP0_OFFSET (12 * __SIZEOF_POINTER__) +/** Offset of tmp1 member in sbi_scratch */ +#define SBI_SCRATCH_TMP1_OFFSET (13 * __SIZEOF_POINTER__) /** Offset of options member in sbi_scratch */ -#define SBI_SCRATCH_OPTIONS_OFFSET (13 * __SIZEOF_POINTER__) +#define SBI_SCRATCH_OPTIONS_OFFSET (14 * __SIZEOF_POINTER__) /** Offset of hartindex member in sbi_scratch */ -#define SBI_SCRATCH_HARTINDEX_OFFSET (14 * __SIZEOF_POINTER__) +#define SBI_SCRATCH_HARTINDEX_OFFSET (15 * __SIZEOF_POINTER__) /** Offset of extra space in sbi_scratch */ -#define SBI_SCRATCH_EXTRA_SPACE_OFFSET (15 * __SIZEOF_POINTER__) +#define SBI_SCRATCH_EXTRA_SPACE_OFFSET (16 * __SIZEOF_POINTER__) /** Maximum size of sbi_scratch (4KB) */ #define SBI_SCRATCH_SIZE (0x1000) @@ -83,6 +85,8 @@ struct sbi_scratch { unsigned long trap_context; /** Temporary storage */ unsigned long tmp0; + /** Temporary storage */ + unsigned long tmp1; /** Options for OpenSBI library */ unsigned long options; /** Index of the hart */ @@ -106,6 +110,7 @@ assert_member_offset(struct sbi_scratch, platform_addr, SBI_SCRATCH_PLATFORM_ADD assert_member_offset(struct sbi_scratch, hartid_to_scratch, SBI_SCRATCH_HARTID_TO_SCRATCH_OFFSET); assert_member_offset(struct sbi_scratch, trap_context, SBI_SCRATCH_TRAP_CONTEXT_OFFSET); assert_member_offset(struct sbi_scratch, tmp0, SBI_SCRATCH_TMP0_OFFSET); +assert_member_offset(struct sbi_scratch, tmp1, SBI_SCRATCH_TMP1_OFFSET); assert_member_offset(struct sbi_scratch, options, SBI_SCRATCH_OPTIONS_OFFSET); assert_member_offset(struct sbi_scratch, hartindex, SBI_SCRATCH_HARTINDEX_OFFSET); From 8150783bc2ebbfc7c5fe6805a221c6315a42c0be Mon Sep 17 00:00:00 2001 From: Evgeny Voevodin Date: Thu, 7 May 2026 18:08:03 +0000 Subject: [PATCH 2/7] lib: sbi: Add Smrnmi extension macros for registers and bits Add CSR definitions (MNSCRATCH, MNSTATUS, MNEPC, MNCAUSE) and bit definitions (MNSTATUS_NMIE, MNSTATUS_MNPV, MNSTATUS_MNPP). Also add SBI_HART_EXT_SMRNMI to the hart extension enumeration. Signed-off-by: Evgeny Voevodin Reviewed-by: Anup Patel Reviewed-by: Nylon Chen --- include/sbi/riscv_encoding.h | 10 ++++++++++ include/sbi/sbi_hart.h | 2 ++ lib/sbi/sbi_hart.c | 1 + 3 files changed, 13 insertions(+) diff --git a/include/sbi/riscv_encoding.h b/include/sbi/riscv_encoding.h index 229c9a6f..ef1b3fdd 100644 --- a/include/sbi/riscv_encoding.h +++ b/include/sbi/riscv_encoding.h @@ -215,6 +215,10 @@ #endif +#define MNSTATUS_NMIE (_UL(0x8)) +#define MNSTATUS_MNPV (_UL(0x80)) +#define MNSTATUS_MNPP (_UL(0x1800)) + #define MHPMEVENT_SSCOF_MASK _ULL(0xFF00000000000000) #if __riscv_xlen > 32 @@ -806,6 +810,12 @@ #define CSR_VTYPE 0xc21 #define CSR_VLENB 0xc22 +/* Smrnmi extension registers */ +#define CSR_MNSCRATCH 0x740 +#define CSR_MNEPC 0x741 +#define CSR_MNCAUSE 0x742 +#define CSR_MNSTATUS 0x744 + /* ===== Trap/Exception Causes ===== */ #define CAUSE_MISALIGNED_FETCH 0x0 diff --git a/include/sbi/sbi_hart.h b/include/sbi/sbi_hart.h index 4aae1c50..03de5414 100644 --- a/include/sbi/sbi_hart.h +++ b/include/sbi/sbi_hart.h @@ -83,6 +83,8 @@ enum sbi_hart_extensions { SBI_HART_EXT_SSQOSID, /** HART has Ssstateen extension **/ SBI_HART_EXT_SSSTATEEN, + /** Hart has Smrnmi extension */ + SBI_HART_EXT_SMRNMI, /** Maximum index of Hart extension */ SBI_HART_EXT_MAX, diff --git a/lib/sbi/sbi_hart.c b/lib/sbi/sbi_hart.c index 3d28a804..a2512bae 100644 --- a/lib/sbi/sbi_hart.c +++ b/lib/sbi/sbi_hart.c @@ -737,6 +737,7 @@ const struct sbi_hart_ext_data sbi_hart_ext[] = { __SBI_HART_EXT_DATA(ssctr, SBI_HART_EXT_SSCTR), __SBI_HART_EXT_DATA(ssqosid, SBI_HART_EXT_SSQOSID), __SBI_HART_EXT_DATA(ssstateen, SBI_HART_EXT_SSSTATEEN), + __SBI_HART_EXT_DATA(smrnmi, SBI_HART_EXT_SMRNMI), }; _Static_assert(SBI_HART_EXT_MAX == array_size(sbi_hart_ext), From f4f051c8b5eca57c4cb86cbe68bfe87eb638a699 Mon Sep 17 00:00:00 2001 From: Evgeny Voevodin Date: Thu, 7 May 2026 18:08:04 +0000 Subject: [PATCH 3/7] firmware: Add RNMI handler infrastructure Implement basic Resumable NMI (RNMI) handler support for the RISC-V Smrnmi extension. The new _trap_rnmi_handler assembly entry point saves context using the Smrnmi MN* CSRs (MNSCRATCH, MNEPC, MNSTATUS, MNCAUSE) and returns via mnret. It dispatches to sbi_trap_rnmi_handler(), which optionally calls a platform-specific ops->rnmi_handler callback for actual NMI processing. If no platform handler is registered or it fails, the event is reported as an unhandled NMI. The RNMI handler reuses the generic trap context structure but stores MN* CSR values (MNEPC, MNSTATUS, MNCAUSE) into the corresponding generic fields (mepc, mstatus, cause) for compatibility with existing trap infrastructure. Signed-off-by: Evgeny Voevodin Reviewed-by: Anup Patel --- firmware/fw_base.S | 121 +++++++++++++++++++++++++++++++++++++ include/sbi/sbi_platform.h | 4 ++ include/sbi/sbi_trap.h | 2 + lib/sbi/sbi_trap.c | 39 ++++++++++++ 4 files changed, 166 insertions(+) diff --git a/firmware/fw_base.S b/firmware/fw_base.S index 95750556..2c6af024 100644 --- a/firmware/fw_base.S +++ b/firmware/fw_base.S @@ -505,6 +505,45 @@ memcmp: csrrw tp, CSR_MSCRATCH, tp .endm +.macro TRAP_SAVE_AND_SETUP_SP_T0_NMI + /* Swap TP and MNSCRATCH (for RNMI) */ + csrrw tp, CSR_MNSCRATCH, tp + + /* Save T0 in scratch space */ + REG_S t0, SBI_SCRATCH_TMP1_OFFSET(tp) + + /* + * Set T0 to appropriate exception stack + * + * Came_From_M_Mode = ((MNSTATUS.MNPP < PRV_M) ? 1 : 0) - 1; + * Exception_Stack = TP ^ (Came_From_M_Mode & (SP ^ TP)) + */ + csrr t0, CSR_MNSTATUS + srl t0, t0, 11 /* MNPP is at bits 11-12 */ + and t0, t0, PRV_M + slti t0, t0, PRV_M + add t0, t0, -1 + xor sp, sp, tp + and t0, t0, sp + xor sp, sp, tp + xor t0, tp, t0 + + /* Save original SP on exception stack */ + REG_S sp, (SBI_TRAP_REGS_OFFSET(sp) - SBI_TRAP_CONTEXT_SIZE)(t0) + + /* Set SP to exception stack and make room for trap context */ + add sp, t0, -(SBI_TRAP_CONTEXT_SIZE) + + /* Restore T0 from scratch space */ + REG_L t0, SBI_SCRATCH_TMP1_OFFSET(tp) + + /* Save T0 on stack */ + REG_S t0, SBI_TRAP_REGS_OFFSET(t0)(sp) + + /* Swap TP and MNSCRATCH */ + csrrw tp, CSR_MNSCRATCH, tp +.endm + .macro TRAP_SAVE_MEPC_MSTATUS have_mstatush /* Save MEPC and MSTATUS CSRs */ csrr t0, CSR_MEPC @@ -519,6 +558,20 @@ memcmp: .endif .endm +.macro TRAP_SAVE_MNEPC_MNSTATUS have_mstatush + /* + * Save MNEPC and MNSTATUS CSRs (for RNMI) + * Note: Trap context structure has generic field names (mepc, mstatus), + * we store MN* CSR values into these same structure fields. + */ + 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) + /* MNSTATUSH doesn't exist in SMRNMI spec */ + REG_S zero, SBI_TRAP_REGS_OFFSET(mstatusH)(sp) +.endm + .macro TRAP_SAVE_GENERAL_REGS_EXCEPT_SP_T0 /* Save all general regisers except SP and T0 */ REG_S zero, SBI_TRAP_REGS_OFFSET(zero)(sp) @@ -582,12 +635,36 @@ memcmp: CLEAR_MDT t0 .endm +.macro TRAP_SAVE_NMI_INFO + /* + * Save NMI trap info (MNCAUSE, no MNTVAL in spec) + * Note: Trap info structure has generic field names (cause, tval, etc.), + * we store MN* CSR values into these same structure fields. + */ + csrr t0, CSR_MNCAUSE + REG_S t0, (SBI_TRAP_REGS_SIZE + SBI_TRAP_INFO_OFFSET(cause))(sp) + /* MNTVAL doesn't exist in SMRNMI spec */ + REG_S zero, (SBI_TRAP_REGS_SIZE + SBI_TRAP_INFO_OFFSET(tval))(sp) + REG_S zero, (SBI_TRAP_REGS_SIZE + SBI_TRAP_INFO_OFFSET(tval2))(sp) + REG_S zero, (SBI_TRAP_REGS_SIZE + SBI_TRAP_INFO_OFFSET(tinst))(sp) + REG_S zero, (SBI_TRAP_REGS_SIZE + SBI_TRAP_INFO_OFFSET(gva))(sp) + + /* We are ready to take another trap, clear MDT */ + CLEAR_MDT t0 +.endm + .macro TRAP_CALL_C_ROUTINE /* Call C routine */ add a0, sp, zero call sbi_trap_handler .endm +.macro TRAP_CALL_C_RNMI_ROUTINE + /* Call C routine */ + add a0, sp, zero + call sbi_trap_rnmi_handler +.endm + .macro TRAP_RESTORE_GENERAL_REGS_EXCEPT_A0_T0 /* Restore all general regisers except A0 and T0 */ REG_L ra, SBI_TRAP_REGS_OFFSET(ra)(a0) @@ -636,6 +713,19 @@ memcmp: csrw CSR_MEPC, t0 .endm +.macro TRAP_RESTORE_MNEPC_MNSTATUS + /* + * Restore MNSTATUS and MNEPC CSRs (for RNMI) + * Note: Load from generic structure fields (mstatus, mepc) and + * restore to NMI-specific CSRs (MNSTATUS, MNEPC). + * No MNSTATUSH in SMRNMI spec. + */ + REG_L t0, SBI_TRAP_REGS_OFFSET(mstatus)(a0) + csrw CSR_MNSTATUS, t0 + REG_L t0, SBI_TRAP_REGS_OFFSET(mepc)(a0) + csrw CSR_MNEPC, t0 +.endm + .macro TRAP_RESTORE_A0_T0 /* Restore T0 */ REG_L t0, SBI_TRAP_REGS_OFFSET(t0)(a0) @@ -700,6 +790,37 @@ _trap_handler_hyp: mret + .section .entry, "ax", %progbits + .align 3 + .globl _trap_rnmi_handler +_trap_rnmi_handler: + /* + * NMI interrupt handler using MN* CSRs + * + * Context detection via MNPP (previous privilege mode): + * - If MNPP < M-mode: use exception stack (TP) + * - If MNPP == M-mode: use current stack (SP) + * This handles nested interrupt cases. + */ + TRAP_SAVE_AND_SETUP_SP_T0_NMI + + TRAP_SAVE_MNEPC_MNSTATUS 0 + + TRAP_SAVE_GENERAL_REGS_EXCEPT_SP_T0 + + TRAP_SAVE_NMI_INFO + + TRAP_CALL_C_RNMI_ROUTINE + + TRAP_RESTORE_GENERAL_REGS_EXCEPT_A0_T0 + + TRAP_RESTORE_MNEPC_MNSTATUS + + TRAP_RESTORE_A0_T0 + + /* mnret - return from NMI (SMRNMI extension) */ + .word 0x70200073 + .section .entry, "ax", %progbits .align 3 .globl _reset_regs diff --git a/include/sbi/sbi_platform.h b/include/sbi/sbi_platform.h index c6d30080..8adc7dda 100644 --- a/include/sbi/sbi_platform.h +++ b/include/sbi/sbi_platform.h @@ -149,6 +149,10 @@ struct sbi_platform_operations { unsigned long log2len); /** platform specific pmp disable on current HART */ void (*pmp_disable)(unsigned int n); + + /** platform specific Smrnmi NMI handler. + * Returns SBI_SUCCESS on success, error code if NMI cannot be handled. */ + int (*rnmi_handler)(struct sbi_trap_context *tcntx); }; /** Platform default per-HART stack size for exception/interrupt handling */ diff --git a/include/sbi/sbi_trap.h b/include/sbi/sbi_trap.h index 5eec4dab..2aaa4cbd 100644 --- a/include/sbi/sbi_trap.h +++ b/include/sbi/sbi_trap.h @@ -269,6 +269,8 @@ static inline void sbi_trap_set_context(struct sbi_scratch *scratch, struct sbi_trap_context *sbi_trap_handler(struct sbi_trap_context *tcntx); +struct sbi_trap_context *sbi_trap_rnmi_handler(struct sbi_trap_context *tcntx); + #endif #endif diff --git a/lib/sbi/sbi_trap.c b/lib/sbi/sbi_trap.c index f41db4d1..1e55b885 100644 --- a/lib/sbi/sbi_trap.c +++ b/lib/sbi/sbi_trap.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -375,3 +376,41 @@ struct sbi_trap_context *sbi_trap_handler(struct sbi_trap_context *tcntx) sbi_trap_set_context(scratch, tcntx->prev_context); return tcntx; } + +/** + * Default Resumable NMI (RNMI) handler + * + * This function is called from the _trap_rnmi_handler assembly code. + * It provides a simple wrapper that calls the platform-specific + * NMI handler if registered. If no handler is registered, it prints + * diagnostic information and hangs, similar to unhandled traps. + * + * Note: The trap context stores NMI CSR values (MNCAUSE, MNEPC, MNSTATUS) + * in the generic trap context fields (cause, mepc, mstatus). + * + * @param tcntx Pointer to trap context (saved on stack) + * @return Same trap context pointer (needed for restore macros) + */ +struct sbi_trap_context *sbi_trap_rnmi_handler(struct sbi_trap_context *tcntx) +{ + int rc; + const struct sbi_platform *plat = sbi_platform_thishart_ptr(); + const struct sbi_platform_operations *ops = sbi_platform_ops(plat); + + /* Call platform-specific NMI handler if registered */ + if (ops && ops->rnmi_handler) { + rc = ops->rnmi_handler(tcntx); + if (rc) { + /* Platform handler failed to handle NMI */ + sbi_trap_error("platform NMI handler failed", rc, tcntx); + } + return tcntx; + } + + /* No platform handler - treat as unhandled NMI */ + sbi_trap_error("unhandled NMI (no platform rnmi_handler)", + SBI_ENOTSUPP, tcntx); + + /* Never returns */ + return tcntx; +} From 28e3c2fd6caafadc83a5f39f1af4059e257a15e6 Mon Sep 17 00:00:00 2001 From: Evgeny Voevodin Date: Thu, 7 May 2026 18:08:05 +0000 Subject: [PATCH 4/7] lib: sbi: hart: Move device tree features detection before trap-based checks Smrnmi detection and enablement in the following commits will happen before any trap-based mechanism. As it relies on device tree, move sbi_platform_extensions_init() to the beginning of hart_detect_features(). Signed-off-by: Evgeny Voevodin Reviewed-by: Anup Patel --- lib/sbi/sbi_hart.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/lib/sbi/sbi_hart.c b/lib/sbi/sbi_hart.c index a2512bae..23eba504 100644 --- a/lib/sbi/sbi_hart.c +++ b/lib/sbi/sbi_hart.c @@ -898,6 +898,19 @@ static int hart_detect_features(struct sbi_scratch *scratch) hfeatures->mhpm_mask = 0; hfeatures->priv_version = SBI_HART_PRIV_VER_UNKNOWN; + /* + * Parse device tree extensions early, before any trap-based checks. + * Needed to detect Smrnmi and install NMI handlers before CSR probes + * that may trigger traps. + */ + rc = sbi_platform_extensions_init(sbi_platform_thishart_ptr(), + hfeatures); + if (rc) + return rc; + + /* Validate DT-claimed extensions against actual hardware */ + hart_ext_validate(hfeatures); + #define __check_hpm_csr(__csr, __mask) \ oldval = csr_read_allowed(__csr, &trap); \ if (!trap.cause) { \ @@ -1052,15 +1065,6 @@ static int hart_detect_features(struct sbi_scratch *scratch) #undef __check_csr_existence - /* Let platform populate extensions */ - rc = sbi_platform_extensions_init(sbi_platform_thishart_ptr(), - hfeatures); - if (rc) - return rc; - - /* Validate DT-claimed extensions against actual hardware */ - hart_ext_validate(hfeatures); - /* Zicntr should only be detected using traps */ __sbi_hart_update_extension(hfeatures, SBI_HART_EXT_ZICNTR, sbi_hart_has_csr(scratch, SBI_HART_CSR_CYCLE) && From ebd6efd9adaf3432adddf6f4fc79cce8a0b29f73 Mon Sep 17 00:00:00 2001 From: Evgeny Voevodin Date: Thu, 7 May 2026 18:08:07 +0000 Subject: [PATCH 5/7] lib: sbi: hart: Detect and enable Smrnmi before trap-based feature detection The location of the RNMI/E trap vectors in the Smrnmi extension is implementation-defined, so platforms with vendor-specific NMI vector mechanisms must install the firmware's NMI entry points themselves. Add an smrnmi_handlers_init() callback to sbi_platform_operations that receives the firmware entry points and lets platform code install them at the hardware-specific vector locations. Two pointers are passed: - _trap_rnmi_handler: the dedicated RNMI entry point that saves context using the Smrnmi MN* CSRs and returns via mnret. - _trap_handler: the regular M-mode trap entry since RNME is taken as a regular M-mode trap with NMIE=0. When Smrnmi is present, install the platform's NMI vectors via the new callback, initialize MNSCRATCH with the per-hart scratch pointer, and set MNSTATUS.NMIE. Smrnmi-enabled platforms must register smrnmi_handlers_init; if the extension is detected but no callback is registered, sbi_panic() is called since enabling NMIs without handlers in place would route subsequent traps into nowhere. Signed-off-by: Evgeny Voevodin Reviewed-by: Anup Patel --- include/sbi/sbi_platform.h | 4 ++++ lib/sbi/sbi_hart.c | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/include/sbi/sbi_platform.h b/include/sbi/sbi_platform.h index 8adc7dda..bd551604 100644 --- a/include/sbi/sbi_platform.h +++ b/include/sbi/sbi_platform.h @@ -150,6 +150,10 @@ struct sbi_platform_operations { /** platform specific pmp disable on current HART */ void (*pmp_disable)(unsigned int n); + /** platform specific Smrnmi handlers init on current HART */ + void (*smrnmi_handlers_init)(void (*rnmi_handler)(void), + void (*rnme_handler)(void)); + /** platform specific Smrnmi NMI handler. * Returns SBI_SUCCESS on success, error code if NMI cannot be handled. */ int (*rnmi_handler)(struct sbi_trap_context *tcntx); diff --git a/lib/sbi/sbi_hart.c b/lib/sbi/sbi_hart.c index 23eba504..52211f88 100644 --- a/lib/sbi/sbi_hart.c +++ b/lib/sbi/sbi_hart.c @@ -911,6 +911,26 @@ static int hart_detect_features(struct sbi_scratch *scratch) /* Validate DT-claimed extensions against actual hardware */ hart_ext_validate(hfeatures); + if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SMRNMI)) { + const struct sbi_platform *plat = sbi_platform_thishart_ptr(); + const struct sbi_platform_operations *ops = sbi_platform_ops(plat); + extern void _trap_rnmi_handler(void); + extern void _trap_handler(void); + + if (!ops || !ops->smrnmi_handlers_init) + sbi_panic("Smrnmi detected, but platform lacks smrnmi_handlers_init callback\n"); + + /* Reuse _trap_handler for the RNME slot since RNME is taken + * as a regular M-mode trap with NMIE=0. */ + ops->smrnmi_handlers_init(_trap_rnmi_handler, _trap_handler); + + /* Initialize MNSCRATCH for the RNMI handler */ + csr_write(CSR_MNSCRATCH, scratch); + + /* Enable NMIs */ + csr_set(CSR_MNSTATUS, MNSTATUS_NMIE); + } + #define __check_hpm_csr(__csr, __mask) \ oldval = csr_read_allowed(__csr, &trap); \ if (!trap.cause) { \ From 5bb1a21101fad0aa618f3bdb48903afb98e03c27 Mon Sep 17 00:00:00 2001 From: Chen Pei Date: Thu, 6 Aug 2026 21:03:00 +0800 Subject: [PATCH 6/7] platform: xuantie: Add RNMI handler support framework Add the xuantie platform callbacks required by the common Smrnmi (Resumable NMI) infrastructure: smrnmi_handlers_init to program the hardware NMI vector, and rnmi_handler to process the NMI when it fires. xuantie vectors RNMI through a single indirectly-accessed rnmi_addr_base register (selected via the miselect/mireg CSR indirect window). NMI is vectored to rnmi_addr_base and double-trap to rnmi_addr_base + 2K, which requires the RNMI asm entry to be 4K-aligned with the double-trap entry 2K above it. That asm layout is not in place yet, so smrnmi_handlers_init is left as a TODO placeholder documenting the intended programming; the NMI source handling in rnmi_handler is likewise a placeholder. The callbacks are registered into generic_platform_ops when the QUIRK_XUANTIE_RNMI quirk is present, matched via the "xuantie,rnmi" compatible. Signed-off-by: Chen Pei --- .../generic/include/xuantie/xuantie_quirk.h | 1 + .../generic/include/xuantie/xuantie_rnmi.h | 33 +++++++ platform/generic/xuantie/objects.mk | 2 +- platform/generic/xuantie/xuantie_dummy.c | 13 ++- platform/generic/xuantie/xuantie_rnmi.c | 93 +++++++++++++++++++ 5 files changed, 140 insertions(+), 2 deletions(-) create mode 100644 platform/generic/include/xuantie/xuantie_rnmi.h create mode 100644 platform/generic/xuantie/xuantie_rnmi.c diff --git a/platform/generic/include/xuantie/xuantie_quirk.h b/platform/generic/include/xuantie/xuantie_quirk.h index e6466d26..2546cf33 100644 --- a/platform/generic/include/xuantie/xuantie_quirk.h +++ b/platform/generic/include/xuantie/xuantie_quirk.h @@ -9,6 +9,7 @@ #define QUIRK_XUANTIE_LINK BIT(1) #define QUIRK_XUANTIE_PMP_EXT BIT(2) #define QUIRK_XUANTIE_PMU BIT(3) +#define QUIRK_XUANTIE_RNMI BIT(4) struct xuantie_generic_quirks { u32 quirk; diff --git a/platform/generic/include/xuantie/xuantie_rnmi.h b/platform/generic/include/xuantie/xuantie_rnmi.h new file mode 100644 index 00000000..291477c0 --- /dev/null +++ b/platform/generic/include/xuantie/xuantie_rnmi.h @@ -0,0 +1,33 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef __RISCV_XUANTIE_SMRNMI_H__ +#define __RISCV_XUANTIE_SMRNMI_H__ + +struct sbi_trap_context; + +/* + * Install the RNMI/RNME assembly entry points into the xuantie hardware. + * + * @rnmi_handler: asm entry for resumable NMI (_trap_rnmi_handler) + * @rnme_handler: asm entry for RNME, taken as a regular M-mode trap + * with NMIE=0 (_trap_handler) + * + * Called from sbi_hart.c when Smrnmi is detected. The way the NMI vector + * address is programmed is implementation defined by the xuantie hardware. + */ +void xuantie_smrnmi_handlers_init(void (*rnmi_handler)(void), + void (*rnme_handler)(void)); + +/* + * C handler invoked when an RNMI fires. The trap context carries the NMI + * CSR values: tcntx->trap.cause = MNCAUSE, tcntx->regs.mepc = MNEPC, + * tcntx->regs.mstatus = MNSTATUS. + * + * Returns 0 on success, or a negative SBI error code if the NMI could not + * be handled. + */ +int xuantie_rnmi_handler(struct sbi_trap_context *tcntx); + +#endif /* __RISCV_XUANTIE_SMRNMI_H__ */ diff --git a/platform/generic/xuantie/objects.mk b/platform/generic/xuantie/objects.mk index f512ed24..2af0f52d 100644 --- a/platform/generic/xuantie/objects.mk +++ b/platform/generic/xuantie/objects.mk @@ -3,4 +3,4 @@ # carray-platform_override_modules-$(CONFIG_PLATFORM_XUANTIE) += xuantie_dummy -platform-objs-$(CONFIG_PLATFORM_XUANTIE) += xuantie/xuantie_dummy.o xuantie/xuantie_pmc.o xuantie/xuantie_link.o xuantie/xuantie_pmp_ext.o xuantie/xuantie_pmu.o +platform-objs-$(CONFIG_PLATFORM_XUANTIE) += xuantie/xuantie_dummy.o xuantie/xuantie_pmc.o xuantie/xuantie_link.o xuantie/xuantie_pmp_ext.o xuantie/xuantie_pmu.o xuantie/xuantie_rnmi.o diff --git a/platform/generic/xuantie/xuantie_dummy.c b/platform/generic/xuantie/xuantie_dummy.c index 528ffb3b..6035c211 100644 --- a/platform/generic/xuantie/xuantie_dummy.c +++ b/platform/generic/xuantie/xuantie_dummy.c @@ -15,6 +15,7 @@ #include #include #include +#include static u32 gquirk = 0; @@ -55,12 +56,17 @@ static int xuantie_dummy_platform_init(const void *fdt, int nodeoff, generic_platform_ops.early_init = xuantie_early_init; generic_platform_ops.final_init = xuantie_final_init; + if (gquirk & QUIRK_XUANTIE_RNMI) { + generic_platform_ops.smrnmi_handlers_init = xuantie_smrnmi_handlers_init; + generic_platform_ops.rnmi_handler = xuantie_rnmi_handler; + } + return 0; } static const struct xuantie_generic_quirks xuantie_quirks = { .quirk = QUIRK_XUANTIE_PMC | QUIRK_XUANTIE_LINK | QUIRK_XUANTIE_PMP_EXT | - QUIRK_XUANTIE_PMU, + QUIRK_XUANTIE_PMU | QUIRK_XUANTIE_RNMI, }; static const struct xuantie_generic_quirks xuantie_pmc_quirks = { @@ -79,11 +85,16 @@ static const struct xuantie_generic_quirks xuantie_pmu_quirks = { .quirk = QUIRK_XUANTIE_PMU, }; +static const struct xuantie_generic_quirks xuantie_rnmi_quirks = { + .quirk = QUIRK_XUANTIE_RNMI, +}; + static const struct fdt_match xuantie_dummy_match[] = { { .compatible = "xuantie,dummy", .data = &xuantie_quirks }, { .compatible = "xuantie,pmc", .data = &xuantie_pmc_quirks }, { .compatible = "xuantie,link", .data = &xuantie_link_quirks }, { .compatible = "xuantie,pmu", .data = &xuantie_pmu_quirks }, + { .compatible = "xuantie,rnmi", .data = &xuantie_rnmi_quirks }, { .compatible = "riscv-virtio", .data = &xuantie_pmp_ext_quirks }, // qemu debug { }, }; diff --git a/platform/generic/xuantie/xuantie_rnmi.c b/platform/generic/xuantie/xuantie_rnmi.c new file mode 100644 index 00000000..49c2e6c4 --- /dev/null +++ b/platform/generic/xuantie/xuantie_rnmi.c @@ -0,0 +1,93 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * xuantie Smrnmi (Resumable NMI) support framework. + * + * The Smrnmi core infrastructure (asm entry _trap_rnmi_handler and the C + * dispatcher sbi_trap_rnmi_handler) lives in the common OpenSBI code. This + * module provides the two xuantie platform callbacks: + * + * - xuantie_smrnmi_handlers_init(): program the hardware NMI vector so the + * CPU jumps to the RNMI/RNME asm entry points. + * - xuantie_rnmi_handler(): handle the NMI when it fires. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* + * xuantie RNMI vectoring is driven by a single indirectly-accessed + * rnmi_addr_base register: + * - selected through the CSR indirect window (miselect/mireg) with the + * vendor selector below; + * - 4K-aligned, low 12 bits hardwired to 0; + * - NMI is vectored to rnmi_addr_base; + * - double trap is vectored to rnmi_addr_base + 2K. + */ +#define XUANTIE_ISEL_RNMI_ADDR_BASE _UL(0x8000000000000E01) +#define XUANTIE_RNMI_ADDR_ALIGN_MASK (~0xFFFUL) +#define XUANTIE_RNMI_DTRAP_OFFSET _UL(0x800) + +void xuantie_smrnmi_handlers_init(void (*rnmi_handler)(void), + void (*rnme_handler)(void)) +{ + struct sbi_scratch *scratch = sbi_scratch_thishart_ptr(); + unsigned long base = (unsigned long)rnmi_handler; + + if (!sbi_hart_has_extension(scratch, SBI_HART_EXT_SMCSRIND) || + !sbi_hart_has_extension(scratch, SBI_HART_EXT_SMRNMI)) + return; + + /* + * Validate the handler layout for debugging. The hardware vectors NMI + * to rnmi_addr_base and double-trap to rnmi_addr_base + 2K, so the + * RNMI entry must be 4K-aligned and the double-trap (RNME) entry must + * sit exactly 2K above it. + */ + if (base & ~XUANTIE_RNMI_ADDR_ALIGN_MASK) + sbi_printf("xuantie: RNMI handler %p not 4K-aligned\n", + rnmi_handler); + if ((unsigned long)rnme_handler != + (base & XUANTIE_RNMI_ADDR_ALIGN_MASK) + XUANTIE_RNMI_DTRAP_OFFSET) + sbi_printf("xuantie: RNME handler %p != RNMI base + 2K\n", + rnme_handler); + + /* + * TODO: program the xuantie rnmi_addr_base register. + * + * The required 4K-aligned asm layout (RNMI entry at base, double-trap + * at base + 2K) is not in place yet. Once it is, program the base: + * + * csr_write(CSR_MISELECT, XUANTIE_ISEL_RNMI_ADDR_BASE); + * csr_write(CSR_MIREG, base & XUANTIE_RNMI_ADDR_ALIGN_MASK); + */ +} + +int xuantie_rnmi_handler(struct sbi_trap_context *tcntx) +{ + /* + * TODO: Handle the xuantie NMI source. + * + * The NMI CSR values are available in the trap context: + * tcntx->trap.cause = MNCAUSE (NMI cause) + * tcntx->regs.mepc = MNEPC (PC interrupted by the NMI) + * tcntx->regs.mstatus = MNSTATUS (MNPP/MNPV/NMIE of interrupted ctx) + * + * Typical steps: + * 1. Read the NMI source / cause from the xuantie hardware. + * 2. Acknowledge / clear the NMI status so it does not re-fire. + * 3. Log or take the required recovery action. + */ + sbi_printf("xuantie: RNMI cause=0x%lx mepc=0x%lx\n", + tcntx->trap.cause, tcntx->regs.mepc); + + return 0; +} From 3acb1117e4ad9e9d4e0396f0e64c93fce6b352ea Mon Sep 17 00:00:00 2001 From: Chen Pei Date: Fri, 7 Aug 2026 17:27:28 +0800 Subject: [PATCH 7/7] firmware: Lay out RNMI/RNME handlers for xuantie NMI vectoring xuantie vectors RNMI through rnmi_addr_base (low 12 bits hardwired to 0) and derives the double-trap (RNME) entry from base + 2K. Align _trap_rnmi_handler to 4K and place a dedicated _trap_rnme_handler exactly 2K above it, so a platform can program rnmi_addr_base with the RNMI base and have both vectors land on the correct entries. _trap_rnme_handler reuses the generic trap handler since RNME is taken as a regular M-mode trap with NMIE=0. sbi_hart.c now passes it as the RNME handler to smrnmi_handlers_init. The rnmi_addr_base programming itself is still a TODO in xuantie_smrnmi_handlers_init; the layout-validation prints added earlier confirm this arrangement (RNMI at a 4K base, RNME at base + 2K). Signed-off-by: Chen Pei --- firmware/fw_base.S | 19 ++++++++++++++++++- lib/sbi/sbi_hart.c | 12 ++++++++---- platform/generic/xuantie/xuantie_rnmi.c | 11 ++--------- 3 files changed, 28 insertions(+), 14 deletions(-) diff --git a/firmware/fw_base.S b/firmware/fw_base.S index 2c6af024..6670a92e 100644 --- a/firmware/fw_base.S +++ b/firmware/fw_base.S @@ -791,7 +791,14 @@ _trap_handler_hyp: mret .section .entry, "ax", %progbits - .align 3 + /* + * RNMI entry must be 4K-aligned: platforms such as xuantie program + * this address into an NMI-vector base register whose low 12 bits are + * hardwired to 0, and derive the double-trap (RNME) entry from + * base + 2K. Keep _trap_rnmi_handler within 2K so that + * _trap_rnme_handler lands exactly at base + 0x800. + */ + .align 12 .globl _trap_rnmi_handler _trap_rnmi_handler: /* @@ -821,6 +828,16 @@ _trap_rnmi_handler: /* mnret - return from NMI (SMRNMI extension) */ .word 0x70200073 + /* + * RNME / double-trap entry, fixed at _trap_rnmi_handler + 2K. + * RNME is taken as a regular M-mode trap with NMIE=0, so reuse the + * generic trap handler. + */ + .align 11 + .globl _trap_rnme_handler +_trap_rnme_handler: + j _trap_handler + .section .entry, "ax", %progbits .align 3 .globl _reset_regs diff --git a/lib/sbi/sbi_hart.c b/lib/sbi/sbi_hart.c index 52211f88..c16d489f 100644 --- a/lib/sbi/sbi_hart.c +++ b/lib/sbi/sbi_hart.c @@ -915,14 +915,18 @@ static int hart_detect_features(struct sbi_scratch *scratch) const struct sbi_platform *plat = sbi_platform_thishart_ptr(); const struct sbi_platform_operations *ops = sbi_platform_ops(plat); extern void _trap_rnmi_handler(void); - extern void _trap_handler(void); + extern void _trap_rnme_handler(void); if (!ops || !ops->smrnmi_handlers_init) sbi_panic("Smrnmi detected, but platform lacks smrnmi_handlers_init callback\n"); - /* Reuse _trap_handler for the RNME slot since RNME is taken - * as a regular M-mode trap with NMIE=0. */ - ops->smrnmi_handlers_init(_trap_rnmi_handler, _trap_handler); + /* + * _trap_rnme_handler is placed exactly 2K above + * _trap_rnmi_handler in fw_base.S; RNME is taken as a regular + * M-mode trap with NMIE=0. Platforms (e.g. xuantie) that derive + * the double-trap vector from the NMI base rely on this layout. + */ + ops->smrnmi_handlers_init(_trap_rnmi_handler, _trap_rnme_handler); /* Initialize MNSCRATCH for the RNMI handler */ csr_write(CSR_MNSCRATCH, scratch); diff --git a/platform/generic/xuantie/xuantie_rnmi.c b/platform/generic/xuantie/xuantie_rnmi.c index 49c2e6c4..67d8ccc8 100644 --- a/platform/generic/xuantie/xuantie_rnmi.c +++ b/platform/generic/xuantie/xuantie_rnmi.c @@ -60,15 +60,8 @@ void xuantie_smrnmi_handlers_init(void (*rnmi_handler)(void), sbi_printf("xuantie: RNME handler %p != RNMI base + 2K\n", rnme_handler); - /* - * TODO: program the xuantie rnmi_addr_base register. - * - * The required 4K-aligned asm layout (RNMI entry at base, double-trap - * at base + 2K) is not in place yet. Once it is, program the base: - * - * csr_write(CSR_MISELECT, XUANTIE_ISEL_RNMI_ADDR_BASE); - * csr_write(CSR_MIREG, base & XUANTIE_RNMI_ADDR_ALIGN_MASK); - */ + csr_write(CSR_MISELECT, XUANTIE_ISEL_RNMI_ADDR_BASE); + csr_write(CSR_MIREG, base & XUANTIE_RNMI_ADDR_ALIGN_MASK); } int xuantie_rnmi_handler(struct sbi_trap_context *tcntx)