OSDN Git Service

x86/fpu: Make the EFI FPU calling convention explicit
authorAndy Lutomirski <luto@kernel.org>
Thu, 21 Jan 2021 05:09:50 +0000 (21:09 -0800)
committerBorislav Petkov <bp@suse.de>
Fri, 29 Jan 2021 11:22:15 +0000 (12:22 +0100)
EFI uses kernel_fpu_begin() to conform to the UEFI calling convention.
This specifically requires initializing FCW (FPU Control Word), whereas
no sane 64-bit kernel code should use legacy 387 operations that
reference FCW.

This should allow to safely change the default semantics of
kernel_fpu_begin() to stop initializing FCW on 64-bit kernels.

 [ bp: Massage commit message a little. ]

Signed-off-by: Andy Lutomirski <luto@kernel.org>
Signed-off-by: Borislav Petkov <bp@suse.de>
Link: https://lkml.kernel.org/r/25d392fff64680e0f4bb8cf0b1003314dc29eafe.1611205691.git.luto@kernel.org
arch/x86/include/asm/efi.h
arch/x86/platform/efi/efi_64.c

index c98f783..c81e68f 100644 (file)
@@ -68,17 +68,33 @@ extern unsigned long efi_fw_vendor, efi_config_table;
                #f " called with too many arguments (" #p ">" #n ")");  \
 })
 
+static inline void efi_fpu_begin(void)
+{
+       /*
+        * The UEFI calling convention (UEFI spec 2.3.2 and 2.3.4) requires
+        * that FCW and MXCSR (64-bit) must be initialized prior to calling
+        * UEFI code.  (Oddly the spec does not require that the FPU stack
+        * be empty.)
+        */
+       kernel_fpu_begin_mask(KFPU_387 | KFPU_MXCSR);
+}
+
+static inline void efi_fpu_end(void)
+{
+       kernel_fpu_end();
+}
+
 #ifdef CONFIG_X86_32
 #define arch_efi_call_virt_setup()                                     \
 ({                                                                     \
-       kernel_fpu_begin();                                             \
+       efi_fpu_begin();                                                \
        firmware_restrict_branch_speculation_start();                   \
 })
 
 #define arch_efi_call_virt_teardown()                                  \
 ({                                                                     \
        firmware_restrict_branch_speculation_end();                     \
-       kernel_fpu_end();                                               \
+       efi_fpu_end();                                                  \
 })
 
 #define arch_efi_call_virt(p, f, args...)      p->f(args)
@@ -107,7 +123,7 @@ struct efi_scratch {
 #define arch_efi_call_virt_setup()                                     \
 ({                                                                     \
        efi_sync_low_kernel_mappings();                                 \
-       kernel_fpu_begin();                                             \
+       efi_fpu_begin();                                                \
        firmware_restrict_branch_speculation_start();                   \
        efi_switch_mm(&efi_mm);                                         \
 })
@@ -119,7 +135,7 @@ struct efi_scratch {
 ({                                                                     \
        efi_switch_mm(efi_scratch.prev_mm);                             \
        firmware_restrict_branch_speculation_end();                     \
-       kernel_fpu_end();                                               \
+       efi_fpu_end();                                                  \
 })
 
 #ifdef CONFIG_KASAN
index e1e8d4e..cf7b3bf 100644 (file)
@@ -850,7 +850,7 @@ efi_set_virtual_address_map(unsigned long memory_map_size,
                                                         virtual_map);
        efi_switch_mm(&efi_mm);
 
-       kernel_fpu_begin();
+       efi_fpu_begin();
 
        /* Disable interrupts around EFI calls: */
        local_irq_save(flags);
@@ -859,7 +859,7 @@ efi_set_virtual_address_map(unsigned long memory_map_size,
                          descriptor_version, virtual_map);
        local_irq_restore(flags);
 
-       kernel_fpu_end();
+       efi_fpu_end();
 
        /* grab the virtually remapped EFI runtime services table pointer */
        efi.runtime = READ_ONCE(systab->runtime);