Commit b0dc553c authored by Andy Lutomirski's avatar Andy Lutomirski Committed by Borislav Petkov
Browse files

x86/fpu: Make the EFI FPU calling convention explicit



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: default avatarAndy Lutomirski <luto@kernel.org>
Signed-off-by: default avatarBorislav Petkov <bp@suse.de>
Link: https://lkml.kernel.org/r/25d392fff64680e0f4bb8cf0b1003314dc29eafe.1611205691.git.luto@kernel.org
parent 6ee1d745
Loading
Loading
Loading
Loading
+20 −4
Original line number Diff line number Diff line
@@ -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
+2 −2
Original line number Diff line number Diff line
@@ -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);