Commit afc4cc71 authored by Ard Biesheuvel's avatar Ard Biesheuvel Committed by Ingo Molnar
Browse files

efi/libstub/x86: Avoid thunking for native firmware calls



We use special wrapper routines to invoke firmware services in the
native case as well as the mixed mode case. For mixed mode, the need
is obvious, but for the native cases, we can simply rely on the
compiler to generate the indirect call, given that GCC now has
support for the MS calling convention (and has had it for quite some
time now). Note that on i386, the decompressor and the EFI stub are not
built with -mregparm=3 like the rest of the i386 kernel, so we can
safely allow the compiler to emit the indirect calls here as well.

So drop all the wrappers and indirection, and switch to either native
calls, or direct calls into the thunk routine for mixed mode.

Signed-off-by: default avatarArd Biesheuvel <ardb@kernel.org>
Cc: Arvind Sankar <nivedita@alum.mit.edu>
Cc: Borislav Petkov <bp@alien8.de>
Cc: James Morse <james.morse@arm.com>
Cc: Matt Fleming <matt@codeblueprint.co.uk>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: linux-efi@vger.kernel.org
Link: https://lkml.kernel.org/r/20191224151025.32482-14-ardb@kernel.org


Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parent 8f24f8c2
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -89,7 +89,7 @@ vmlinux-objs-$(CONFIG_ACPI) += $(obj)/acpi.o

$(obj)/eboot.o: KBUILD_CFLAGS += -fshort-wchar -mno-red-zone

vmlinux-objs-$(CONFIG_EFI_STUB) += $(obj)/eboot.o $(obj)/efi_stub_$(BITS).o \
vmlinux-objs-$(CONFIG_EFI_STUB) += $(obj)/eboot.o \
	$(objtree)/drivers/firmware/efi/libstub/lib.a
vmlinux-objs-$(CONFIG_EFI_MIXED) += $(obj)/efi_thunk_$(BITS).o

+2 −1
Original line number Diff line number Diff line
@@ -44,7 +44,8 @@ BOOT_SERVICES(64);
void efi_char16_printk(efi_system_table_t *table, efi_char16_t *str)
{
	efi_call_proto(efi_simple_text_output_protocol, output_string,
		       efi_early->text_output, str);
		       ((efi_simple_text_output_protocol_t *)(unsigned long)
				efi_early->text_output), str);
}

static efi_status_t
+0 −87
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * EFI call stub for IA32.
 *
 * This stub allows us to make EFI calls in physical mode with interrupts
 * turned off. Note that this implementation is different from the one in
 * arch/x86/platform/efi/efi_stub_32.S because we're _already_ in physical
 * mode at this point.
 */

#include <linux/linkage.h>
#include <asm/page_types.h>

/*
 * efi_call_phys(void *, ...) is a function with variable parameters.
 * All the callers of this function assure that all the parameters are 4-bytes.
 */

/*
 * In gcc calling convention, EBX, ESP, EBP, ESI and EDI are all callee save.
 * So we'd better save all of them at the beginning of this function and restore
 * at the end no matter how many we use, because we can not assure EFI runtime
 * service functions will comply with gcc calling convention, too.
 */

.text
SYM_FUNC_START(efi_call_phys)
	/*
	 * 0. The function can only be called in Linux kernel. So CS has been
	 * set to 0x0010, DS and SS have been set to 0x0018. In EFI, I found
	 * the values of these registers are the same. And, the corresponding
	 * GDT entries are identical. So I will do nothing about segment reg
	 * and GDT, but change GDT base register in prelog and epilog.
	 */

	/*
	 * 1. Because we haven't been relocated by this point we need to
	 * use relative addressing.
	 */
	call	1f
1:	popl	%edx
	subl	$1b, %edx

	/*
	 * 2. Now on the top of stack is the return
	 * address in the caller of efi_call_phys(), then parameter 1,
	 * parameter 2, ..., param n. To make things easy, we save the return
	 * address of efi_call_phys in a global variable.
	 */
	popl	%ecx
	movl	%ecx, saved_return_addr(%edx)
	/* get the function pointer into ECX*/
	popl	%ecx
	movl	%ecx, efi_rt_function_ptr(%edx)

	/*
	 * 3. Call the physical function.
	 */
	call	*%ecx

	/*
	 * 4. Balance the stack. And because EAX contain the return value,
	 * we'd better not clobber it. We need to calculate our address
	 * again because %ecx and %edx are not preserved across EFI function
	 * calls.
	 */
	call	1f
1:	popl	%edx
	subl	$1b, %edx

	movl	efi_rt_function_ptr(%edx), %ecx
	pushl	%ecx

	/*
	 * 10. Push the saved return address onto the stack and return.
	 */
	movl	saved_return_addr(%edx), %ecx
	pushl	%ecx
	ret
SYM_FUNC_END(efi_call_phys)
.previous

.data
saved_return_addr:
	.long 0
efi_rt_function_ptr:
	.long 0
+0 −5
Original line number Diff line number Diff line
#include <asm/segment.h>
#include <asm/msr.h>
#include <asm/processor-flags.h>

#include "../../platform/efi/efi_stub_64.S"
+0 −6
Original line number Diff line number Diff line
@@ -161,9 +161,7 @@ SYM_FUNC_START(efi_pe_entry)
	popl	%ecx
	movl	%ecx, efi32_config+8(%esi)	/* EFI System table pointer */

	/* Relocate efi_config->call() */
	leal	efi32_config(%esi), %eax
	add	%esi, 40(%eax)
	pushl	%eax

	call	make_boot_params
@@ -188,9 +186,7 @@ SYM_FUNC_START(efi32_stub_entry)
	movl	%ecx, efi32_config(%esi)	/* Handle */
	movl	%edx, efi32_config+8(%esi)	/* EFI System table pointer */

	/* Relocate efi_config->call() */
	leal	efi32_config(%esi), %eax
	add	%esi, 40(%eax)
	pushl	%eax
2:
	call	efi_main
@@ -266,8 +262,6 @@ SYM_FUNC_END(.Lrelocated)
	.data
efi32_config:
	.fill 5,8,0
	.long efi_call_phys
	.long 0
	.byte 0
#endif

Loading