summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--firmware/fw_base.S121
-rw-r--r--include/sbi/sbi_platform.h4
-rw-r--r--include/sbi/sbi_trap.h2
-rw-r--r--lib/sbi/sbi_trap.c39
4 files changed, 166 insertions, 0 deletions
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)
@@ -726,6 +816,37 @@ _trap_handler_hyp:
.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
_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;
+}