diff --git a/firmware/fw_base.S b/firmware/fw_base.S index 2498797c..6670a92e 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 @@ -504,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 @@ -518,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) @@ -581,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) @@ -635,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) @@ -699,6 +790,54 @@ _trap_handler_hyp: mret + .section .entry, "ax", %progbits + /* + * 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: + /* + * 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 + + /* + * 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/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/include/sbi/sbi_platform.h b/include/sbi/sbi_platform.h index c6d30080..bd551604 100644 --- a/include/sbi/sbi_platform.h +++ b/include/sbi/sbi_platform.h @@ -149,6 +149,14 @@ struct sbi_platform_operations { unsigned long log2len); /** 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); }; /** Platform default per-HART stack size for exception/interrupt handling */ 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); 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_hart.c b/lib/sbi/sbi_hart.c index 3d28a804..c16d489f 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), @@ -897,6 +898,43 @@ 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); + + 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_rnme_handler(void); + + if (!ops || !ops->smrnmi_handlers_init) + sbi_panic("Smrnmi detected, but platform lacks smrnmi_handlers_init callback\n"); + + /* + * _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); + + /* Enable NMIs */ + csr_set(CSR_MNSTATUS, MNSTATUS_NMIE); + } + #define __check_hpm_csr(__csr, __mask) \ oldval = csr_read_allowed(__csr, &trap); \ if (!trap.cause) { \ @@ -1051,15 +1089,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) && 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; +} 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..67d8ccc8 --- /dev/null +++ b/platform/generic/xuantie/xuantie_rnmi.c @@ -0,0 +1,86 @@ +/* + * 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); + + 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; +}