"Smrnmi": Resumable Non-Maskable Interrupts

Part II Linux boot: optional Vol. II (Privileged) pp. 90–92 · ~4 min read

  • smrnmi

Smrnmi gives non-maskable interrupts their own epc, cause and status registers plus their own return instruction — so an NMI that lands mid-handler no longer destroys the context it interrupted.

Why this matters

NMIs report what you cannot schedule — ECC errors, bus faults, watchdogs — and by definition they arrive mid-handler. With one save set the only honest response is a reset; with two, you log it and resume.

A base-architecture NMI (ch. II·3) clobbers mepc and mcause — land one inside a trap handler and the interrupted context is gone. Smrnmi gives NMIs their own save set and return instruction, making hardware-error handling resumable.

The parallel state: mnscratch (0x740), mnepc (0x741 — same WARL /bit-masking rules as mepc, including for MNRET’s implicit read), mncause (0x742 — MSB set + cause code for interrupts, or MSB clear + exception code when delivering an Smdbltrp double trap), and mnstatus:

mnstatus (0x744) — Figure 9

Reserved3113MNPP1211Reserved10MNPELP9Reserved8MNPV7Reserved64NMIE3Reserved20
WPRI/RO WLRL WARL RW
Click a field for semantics, reset state, and the minimal-implementation note.

Operation

RNMI lifecycle
What happens
Signalrnmi inputs to the hart: higher priority than every interrupt/exception, immune to mstatus.MIE
Entrymnepc ← PC, mncause ← type, MNPP/MNPV ← context, NMIE ← 0; jump to the implementation-defined RNMI interrupt handler in M-mode
Fault inside the windowException in M-mode with NMIE=0 → normal actions but PC ← the separate RNMI EXCEPTION handler address
ReturnMNRET (M-only): PC ← mnepc, mode ← MNPP/MNPV, NMIE ← 1; MPRV ← 0 if leaving M; ELP ← yLPE ∧ MNPELP (Zicfilp)
Non-interactionMRET/SRET neither depend on nor modify NMIE; WFI treats NMIE as a global enable (i.e., unaffected by it)
Dotted-underlined cells have explanations — click one.
If you remember only three things
  • The parallel save set is the whole idea: mnepc, mncause, mnstatus and mnscratch plus MNRET, leaving mepc/mcause untouched — which is exactly what makes an RNMI resumable.
  • NMIE resets to 0 and is set-only. Software enables RNMIs once its stacks exist and can never mask them again; non-maskability survives because the bit only moves one way.
  • There is exactly one RNMI context, never nested. While NMIE=0, M-mode exceptions divert to the separate RNMI exception vector and MPRV effects are suppressed.

Hardware Designer Notes

Think of Smrnmi as a two-entry shadow of your trap unit: four CSRs, two vectors, one return instruction. It becomes load-bearing when you add RAS features (ECC machine-check reporting) or Smdbltrp — for a v1 Linux core it’s an optional stub with mnstatus.NMIE the only bit DV must remember resets to zero.

Minimal Linux-boot hart MUST

  • Give rnmi inputs priority over everything, unmaskable by MIE
  • Reset NMIE to 0 and make it set-only from software
  • Redirect M-mode exceptions to the RNMI exception vector while NMIE=0
  • Suppress MPRV effects while NMIE=0

MAY simplify / trap-and-emulate

  • Point the RNMI exception vector at mtvec
  • Report all RNMI causes as 0 (undistinguished) in mncause
  • Skip Smrnmi entirely on a first Linux core — base NMIs (or none) are still conformant

Check yourself — resumable NMIs

1.Why can't a base-architecture NMI generally resume the interrupted work?

2.Software wants to briefly mask RNMIs around a critical section. Can it?

3.An exception occurs in M-mode while mnstatus.NMIE=0 (inside the RNMI handler). What happens?

4.What does MNRET restore and set?

4 questions