Re: [PATCH v2 3/5] firmware: Add RNMI handler infrastructure

Anup Patel <[email protected]>
Newsgroups org.infradead.lists.opensbi
Message-ID <CAAhSdy3SA390Z_b7WiJ_fxXoJoN0cOeXrqX3+sw_u56L8oJfQQ@mail.gmail.com>
On Sat, May 2, 2026 at 2:46 AM Evgeny Voevodin <[email protected]> wrote:
>
> 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 <[email protected]>

LGTM.

Reviewed-by: Anup Patel <[email protected]>

Regards,
Anup


> ---
>  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 cdb15429..043de7d7 100644
> --- a/firmware/fw_base.S
> +++ b/firmware/fw_base.S
> @@ -529,6 +529,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
> @@ -543,6 +582,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)
> @@ -606,12 +659,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)
> @@ -660,6 +737,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)
> @@ -724,6 +814,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 e65d9877..715df499 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 731a0c98..091a2446 100644
> --- a/include/sbi/sbi_trap.h
> +++ b/include/sbi/sbi_trap.h
> @@ -289,6 +289,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 <sbi/sbi_irqchip.h>
>  #include <sbi/sbi_trap_ldst.h>
>  #include <sbi/sbi_pmu.h>
> +#include <sbi/sbi_platform.h>
>  #include <sbi/sbi_scratch.h>
>  #include <sbi/sbi_sse.h>
>  #include <sbi/sbi_timer.h>
> @@ -375,3 +376,41 @@ trap_done:
>         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;
> +}
> --
> 2.43.0
>

-- 
opensbi mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/opensbi
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.