[RFC PATCH 00/36] arm64: Add support for FEAT_NMI
Vladimir Murzin
vladimir.murzin at arm.com
Thu Jul 23 05:25:14 PDT 2026
On 7/23/26 04:53, Jinjie Ruan wrote:
>
> 在 2026/7/9 20:12, Vladimir Murzin 写道:
>> FEAT_NMI provides an architected mechanism for supporting non-maskable
>> interrupts (NMIs) and less-masked interrupts (LMIs).
>>
>> Since we already support pseudo-NMIs via priority masking, introducing
>> another flavour of NMI on top of the existing infrastructure could
>> easily become messy, making the code harder to follow and reason
>> about.
>>
>> To avoid that, this series first makes room for the new NMI "tenant"
>> by restructuring the existing exception masking logic.
>>
>> The main idea is to separate the logical view of exception state from
>> its hardware representation. To achieve this, we introduce logical
>> exception contexts that can be mapped onto the corresponding hardware
>> state. This naturally consolidates the hardware-specific handling into
>> a small number of places, while allowing the rest of the code to
>> operate purely in terms of logical exception contexts.
>>
>> Since this restructuring is non-trivial and carries a risk of subtle
>> behavioural changes, the series adds extensive debug checks to verify
>> that the hardware state always matches the expected logical state.
>>
>> With this restructuring in place, wiring FEAT_NMI into the new
>> framework becomes much more manageable.
>>
>> This work would not have been possible without the contributions of
>> Ada Couprie Diaz, Mark Brown, and Lorenzo Pieralisi - please credit
>> them for everything that works well. Any remaining bugs or issues are
>> entirely my own.
>>
>> I'd especially appreciate feedback on the overall approach. Please
>> don't hesitate to bikeshed the naming or other details - improving
>> clarity is one of the main goals of this series. Of course, technical
>> review is more than welcome as well.
>>
>> The series would also benefit from extensive testing on real hardware
>> (without NMI, with pseudo-NMI, and with FEAT_NMI), as most of the
>> testing so far has been done on QEMU and FVP.
>>
>> P.S.
>> I'm aware that Jinjie Ruan has a similar series on the mailing
>> list. I'm very open to collaborating and aligning our efforts if that
>> makes sense.
>>
>> Thanks
>> Vladimir
>>
>> Ada Couprie Diaz (19):
>> arm64: debug: don't mask DAIF for mdscr_write()
>> arm64: hibernate: mask DAIF before restoring hibernated kernel
>> arm64: suspend: rely on daif helpers to handle PMR
>> arm64: irq: introduce a helper for GIC priority initialization
>> arm64: entry: mask DAIF before returning from C EL1 handlers
>> irqchip/gic-v3: make the unmasking of pseudo-NMIs explicit when
>> handling IRQs
>> arm64: irqflags: introduce arm64-specific irqflags type
>> arm64: irqflags: save and use both DAIF and PMR
>> arm64: interrupts: introduce interrupt masking helpers for entry code
>> arm64: entry: replace DAIF helpers with entry helpers
>> arm64: interrupts: introduce generic interrupt masking helpers
>> arm64: replace local_daif helpers
>> arm64: cpuidle: use new helpers to bypass interrupt priority masking
>> arm64: remove daifflags.h
>> arm64: gicv3: remove GIC_PRIO_PSR_I_SET
>> arm64: ptrace: Add PSR_ALLINT_BIT
>> arm64: cpufeature: Detect PE support for FEAT_NMI
>> arm64: nmi: Manage masking for superpriority interrupts
>> arm64: irq: Report FEAT_NMI masking local IRQs
>>
>> Lorenzo Pieralisi (1):
>> irqchip/gic-v3: Implement FEAT_GICv3_NMI support
>>
>> Mark Brown (5):
>> arm64: booting: Document boot requirements for FEAT_NMI
>> arm64: sysreg: Add definitions for immediate versions of MSR ALLINT
>> arm64: idreg: Add an override for FEAT_NMI
>> arm64: nmi: Add handling of superpriority interrupts as NMIs
>> arm64: nmi: Add Kconfig for NMI
>>
>> Vladimir Murzin (11):
>> arm64: ptrace: Remove INIT_PSTATE_EL2
>> arm64: suspend: Initialize PMR on resume
>> arm64: process: Use helper to check exception state
>> arm64: cpufeature: Remove system_has_prio_mask_debugging()
>> arm64: irqflags: Switch to CONFIG_DEBUG_IRQFLAGS
>> arm64: Kconfig: Remove CONFIG_ARM64_DEBUG_PRIORITY_MASKING
>> efi/runtime-wrappers: Permit architectures to override IRQ flags
>> checks
>> arm64/efi: Implement override for IRQ flags checks
>> arm64: suspend: Always initialise PSTATE.ALLINT
>> arm64/efi: Add ALLINT to IRQ flags checks
>> arm64: kprobes: Disable NMIs
>
Hi Jinjie,
> Hi Vladimir,
>
> In the past two years, while debugging the FEAT_NMI code externally, we
> encountered an issue related to the hardware NMI interrupt cancellation.
>
> I'm sharing this below to exchange ideas with you. I've reanalyzed your
> code, and it seems that this issue has been resolved.
>
Thanks for sharing!
> The sequence of issues I understand is as follows:
>
> CPU (IRQs disabled) GICv3 Normal IRQ NMI
> =============== ===== ========== ====
> | | | |
> | | <--- pending -------+ |
> | | (IRQ latched) | |
> | | | |
> | | <--- NMI arrives --------------------+
> | | (ISR_EL1.NMI = 1) | |
> | | | |
> | <--- IRQ exception -------+ | |
> | (enter el1_interrupt()) | | |
> | | | |
> | | <--- NMI withdraw -------------------+
> | | (ISR_EL1.NMI = 0) | |
> | | | |
> | (regs_irqs_disabled()is true) | | |
> | call __el1_nmi()
> | | | |
> | call gic_handle_irq() | | |
> | -> gic_read_nmiar() | | |
> | -> read special 1023 | | |
> | -> safe return | | |
>
>
> Our fix, based on Mark's original version of the code, is as follows:
>
> static void __gic_handle_irq_from_irqson(struct pt_regs *regs)
> {
> bool is_nmi;
> u32 irqnr;
>
> /*
> * We should enter here with interrupts disabled, otherwise we
> may met
> * a race here with FEAT_NMI/FEAT_GICv3_NMI:
> *
> * [interrupt disabled]
> * <- normal interrupt pending, for example
> timer interrupt
> * <- NMI occurs, ISR_EL1.nmi = 1
> * do_el1_interrupt()
> * <- NMI withdraw, ISR_EL1.nmi = 0
> * ISR_EL1.nmi = 0, not an NMI interrupt
> * gic_handle_irq()
> * __gic_handle_irq_from_irqson()
> * irqnr = gic_read_iar() <- Oops, ack and handle an
> normal interrupt
> * in interrupt disabled context!
> *
> * So if we met this case here, just return from the interrupt
> context.
> * Since the interrupt is still pending, we can handle it once the
> * interrupt re-enabled and it'll not be missing.
> */
> if (!interrupts_enabled(regs))
> return;
>
Right. This check now exists higher up the call stack, in
el1_interrupt(). Keeping it here has the unpleasant side effect
of breaking the contract that this function returns in
NOIRQ_CONTEXT.
It seems that preventing execution from reaching this point in
the first place solves all the issues.
Cheers
Vladimir
> irqnr = gic_read_iar();
>
> is_nmi = gic_rpr_is_nmi_prio();
>
> if (is_nmi) {
> nmi_enter();
> __gic_handle_nmi(irqnr, regs);
> nmi_exit();
> }
>
> if (gic_prio_masking_enabled()) {
> gic_pmr_mask_irqs();
> gic_arch_enable_irqs();
> }
>
> ......
>
> }
>
>
> Best regards,
> Jinjie
>
>> Documentation/arch/arm64/booting.rst | 6 +
>> arch/arm/include/asm/arch_gicv3.h | 6 +-
>> arch/arm64/Kconfig | 29 +-
>> arch/arm64/include/asm/arch_gicv3.h | 7 +-
>> arch/arm64/include/asm/assembler.h | 24 +-
>> arch/arm64/include/asm/cpucaps.h | 2 +
>> arch/arm64/include/asm/cpufeature.h | 14 +-
>> arch/arm64/include/asm/cpuidle.h | 14 +-
>> arch/arm64/include/asm/daifflags.h | 144 --------
>> arch/arm64/include/asm/efi.h | 42 ++-
>> arch/arm64/include/asm/entry-common.h | 10 +-
>> .../include/asm/interrupts/common_flags.h | 248 ++++++++++++++
>> arch/arm64/include/asm/interrupts/entry.h | 116 +++++++
>> arch/arm64/include/asm/interrupts/masking.h | 176 ++++++++++
>> arch/arm64/include/asm/irqflags.h | 148 ++++-----
>> arch/arm64/include/asm/kvm_host.h | 1 -
>> arch/arm64/include/asm/mmu_context.h | 1 -
>> arch/arm64/include/asm/ptrace.h | 17 +-
>> arch/arm64/include/asm/sysreg.h | 2 +
>> arch/arm64/include/uapi/asm/ptrace.h | 1 +
>> arch/arm64/kernel/acpi.c | 15 +-
>> arch/arm64/kernel/cpufeature.c | 68 +++-
>> arch/arm64/kernel/debug-monitors.c | 4 -
>> arch/arm64/kernel/entry-common.c | 311 ++++++++++++------
>> arch/arm64/kernel/entry.S | 18 +-
>> arch/arm64/kernel/hibernate.c | 23 +-
>> arch/arm64/kernel/irq.c | 9 +-
>> arch/arm64/kernel/machine_kexec.c | 4 +-
>> arch/arm64/kernel/pi/idreg-override.c | 1 +
>> arch/arm64/kernel/probes/kprobes.c | 24 +-
>> arch/arm64/kernel/process.c | 24 +-
>> arch/arm64/kernel/setup.c | 4 +-
>> arch/arm64/kernel/signal.c | 1 -
>> arch/arm64/kernel/smp.c | 18 +-
>> arch/arm64/kernel/suspend.c | 26 +-
>> arch/arm64/kernel/traps.c | 1 -
>> arch/arm64/kvm/hyp/nvhe/switch.c | 2 +-
>> arch/arm64/kvm/hyp/vgic-v3-sr.c | 7 +-
>> arch/arm64/kvm/hyp/vhe/switch.c | 12 +-
>> arch/arm64/mm/fault.c | 1 -
>> arch/arm64/mm/mmu.c | 7 +-
>> arch/arm64/mm/proc.S | 7 +
>> arch/arm64/tools/cpucaps | 2 +
>> drivers/firmware/efi/runtime-wrappers.c | 32 +-
>> drivers/irqchip/irq-gic-v3.c | 127 +++++--
>> include/linux/irqchip/arm-gic-v3-prio.h | 8 -
>> include/linux/irqchip/arm-gic-v3.h | 4 +
>> 47 files changed, 1247 insertions(+), 521 deletions(-)
>> delete mode 100644 arch/arm64/include/asm/daifflags.h
>> create mode 100644 arch/arm64/include/asm/interrupts/common_flags.h
>> create mode 100644 arch/arm64/include/asm/interrupts/entry.h
>> create mode 100644 arch/arm64/include/asm/interrupts/masking.h
>>
More information about the linux-arm-kernel
mailing list