]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
x86/kexec: Pass bitmask for CR4 bits to preserve from kernel C code
authorDavid Woodhouse <dwmw@amazon.co.uk>
Mon, 17 Mar 2025 11:35:12 +0000 (11:35 +0000)
committerDavid Woodhouse <dwmw@amazon.co.uk>
Mon, 17 Mar 2025 11:35:12 +0000 (11:35 +0000)
The relocate_kernel() function masks out all but PAE and LA57 bits from
the CR4 register... and also the MCE bit for TDX guests, where disabling
MCE may not be permitted.

The conditional disabling of CR4_MCE is currently implemented by the use
of ALTERNATIVE() in the assembler code.

In order to allow a future patch to move relocate_kernel() to a data
section and avoid objtool having opinions about it, eliminate the use
of ALTERNATIVE() by passing the bitmask in from C code.

Suggested-by: Josh Poimboeuf <jpoimboe@kernel.org>
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
arch/x86/include/asm/kexec.h
arch/x86/kernel/machine_kexec_64.c
arch/x86/kernel/relocate_kernel_64.S

index 5081d0b9e2901c2e333e4d227e68f18bd7c0d8ae..bd9fc22a6be2ee8375d9b11e866edd3c66c75ae3 100644 (file)
@@ -65,6 +65,7 @@ extern gate_desc kexec_debug_idt[];
 extern unsigned char kexec_debug_exc_vectors[];
 extern uint16_t kexec_debug_8250_port;
 extern unsigned long kexec_debug_8250_mmio32;
+extern uint32_t kexec_preserve_cr4_bits;
 #endif
 
 /*
index 7abc7aa0261b2e4bdfcaec5636e57010ca071ef2..016862d2b544b08343a31aca956e6e89be82df8d 100644 (file)
@@ -353,6 +353,22 @@ int machine_kexec_prepare(struct kimage *image)
        kexec_va_control_page = (unsigned long)control_page;
        kexec_pa_table_page = (unsigned long)__pa(image->arch.pgd);
 
+       /*
+        * The relocate_kernel assembly code sets CR4 to a subset of the bits
+        * which were set during kernel runtime, including only:
+        *  - physical address extension (which is always set in kernel)
+        *  - 5-level paging (if it's enabled)
+        *  - Machine check exception on TDX guests
+        *
+        * Clearing MCE may not be allowed in TDX guests, but it *should* be
+        * cleared in the general case. Because of the conditional nature of
+        * that, pass the set of bits in from the kernel for relocate_kernel
+        * to do a simple 'andl' with them.
+        */
+       kexec_preserve_cr4_bits = X86_CR4_PAE | X86_CR4_LA57;
+       if (cpu_feature_enabled(X86_FEATURE_TDX_GUEST))
+               kexec_preserve_cr4_bits |= X86_CR4_MCE;
+
        if (image->type == KEXEC_TYPE_DEFAULT)
                kexec_pa_swap_page = page_to_pfn(image->swap_page) << PAGE_SHIFT;
 
index 4f8b7d31802560152220cbd064db7580dbd671e6..576b7bbdd55eb1b1fb0b15571e5f9610b97af94e 100644 (file)
@@ -41,6 +41,7 @@ SYM_DATA(kexec_pa_swap_page, .quad 0)
 SYM_DATA_LOCAL(pa_backup_pages_map, .quad 0)
 SYM_DATA(kexec_debug_8250_mmio32, .quad 0)
 SYM_DATA(kexec_debug_8250_port, .word 0)
+SYM_DATA(kexec_preserve_cr4_bits, .long 0)
 
        .balign 16
 SYM_DATA_START_LOCAL(kexec_debug_gdt)
@@ -183,17 +184,12 @@ SYM_CODE_START_LOCAL_NOALIGN(identity_mapped)
        movq    %rax, %cr0
 
        /*
-        * Set cr4 to a known state:
-        *  - physical address extension enabled
-        *  - 5-level paging, if it was enabled before
-        *  - Machine check exception on TDX guest, if it was enabled before.
-        *    Clearing MCE might not be allowed in TDX guests, depending on setup.
+        * Set CR4 to a known state, using the bitmask which was set in
+        * machine_kexec_prepare().
         *
         * Use R13 that contains the original CR4 value, read in relocate_kernel().
-        * PAE is always set in the original CR4.
         */
-       andl    $(X86_CR4_PAE | X86_CR4_LA57), %r13d
-       ALTERNATIVE "", __stringify(orl $X86_CR4_MCE, %r13d), X86_FEATURE_TDX_GUEST
+       andl    kexec_preserve_cr4_bits(%rip), %r13d
        movq    %r13, %cr4
 
        /* Flush the TLB (needed?) */