Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 140 additions & 1 deletion firmware/fw_base.S
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
10 changes: 10 additions & 0 deletions include/sbi/riscv_encoding.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions include/sbi/sbi_hart.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
8 changes: 8 additions & 0 deletions include/sbi/sbi_platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
11 changes: 8 additions & 3 deletions include/sbi/sbi_scratch.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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 */
Expand All @@ -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);

Expand Down
2 changes: 2 additions & 0 deletions include/sbi/sbi_trap.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
47 changes: 38 additions & 9 deletions lib/sbi/sbi_hart.c
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down Expand Up @@ -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) { \
Expand Down Expand Up @@ -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) &&
Expand Down
Loading
Loading