x86/boot: Ignore NMIs during very early boot
authorJun'ichi Nomura <junichi.nomura@nec.com>
Wed, 29 Nov 2023 20:44:49 +0000 (15:44 -0500)
committerIngo Molnar <mingo@kernel.org>
Thu, 30 Nov 2023 08:55:40 +0000 (09:55 +0100)
When there are two racing NMIs on x86, the first NMI invokes NMI handler and
the 2nd NMI is latched until IRET is executed.

If panic on NMI and panic kexec are enabled, the first NMI triggers
panic and starts booting the next kernel via kexec. Note that the 2nd
NMI is still latched. During the early boot of the next kernel, once
an IRET is executed as a result of a page fault, then the 2nd NMI is
unlatched and invokes the NMI handler.

However, NMI handler is not set up at the early stage of boot, which
results in a boot failure.

Avoid such problems by setting up a NOP handler for early NMIs.

[ mingo: Refined the changelog. ]

Signed-off-by: Jun'ichi Nomura <junichi.nomura@nec.com>
Signed-off-by: Derek Barbosa <debarbos@redhat.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Cc: Kees Cook <keescook@chromium.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Paul E. McKenney <paulmck@kernel.org>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Peter Zijlstra <peterz@infradead.org>
arch/x86/boot/compressed/ident_map_64.c
arch/x86/boot/compressed/idt_64.c
arch/x86/boot/compressed/idt_handlers_64.S
arch/x86/boot/compressed/misc.h

index 473ba59b82a8153be7b7907e9cfb540cf3f1e72e..d040080d7edbd6ba53b9556102ac732a4f90e8e3 100644 (file)
@@ -386,3 +386,8 @@ void do_boot_page_fault(struct pt_regs *regs, unsigned long error_code)
         */
        kernel_add_identity_map(address, end);
 }
+
+void do_boot_nmi_trap(struct pt_regs *regs, unsigned long error_code)
+{
+       /* Empty handler to ignore NMI during early boot */
+}
index 3cdf94b4145674c547a6604875fb4c26590e562a..d100284bbef47b65657a4f43e11dfed5fa6d01a1 100644 (file)
@@ -61,6 +61,7 @@ void load_stage2_idt(void)
        boot_idt_desc.address = (unsigned long)boot_idt;
 
        set_idt_entry(X86_TRAP_PF, boot_page_fault);
+       set_idt_entry(X86_TRAP_NMI, boot_nmi_trap);
 
 #ifdef CONFIG_AMD_MEM_ENCRYPT
        /*
index 22890e199f5b44c7bc8f617f266e4364917c8efc..4d03c8562f637d2eb473f23d24e7dae61d730cb2 100644 (file)
@@ -70,6 +70,7 @@ SYM_FUNC_END(\name)
        .code64
 
 EXCEPTION_HANDLER      boot_page_fault do_boot_page_fault error_code=1
+EXCEPTION_HANDLER      boot_nmi_trap do_boot_nmi_trap error_code=0
 
 #ifdef CONFIG_AMD_MEM_ENCRYPT
 EXCEPTION_HANDLER      boot_stage1_vc do_vc_no_ghcb            error_code=1
index c0d502bd871616663f4de30c3250f0ae9d243ec1..bc2f0f17fb90ec35f6e694ad38971e02a80c5fae 100644 (file)
@@ -196,6 +196,7 @@ static inline void cleanup_exception_handling(void) { }
 
 /* IDT Entry Points */
 void boot_page_fault(void);
+void boot_nmi_trap(void);
 void boot_stage1_vc(void);
 void boot_stage2_vc(void);