efi/libstub/x86: Avoid thunking for native firmware calls
authorArd Biesheuvel <ardb@kernel.org>
Tue, 24 Dec 2019 15:10:13 +0000 (16:10 +0100)
committerIngo Molnar <mingo@kernel.org>
Wed, 25 Dec 2019 09:49:20 +0000 (10:49 +0100)
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: Ard 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: Ingo Molnar <mingo@kernel.org>
arch/x86/boot/compressed/Makefile
arch/x86/boot/compressed/eboot.c
arch/x86/boot/compressed/efi_stub_32.S [deleted file]
arch/x86/boot/compressed/efi_stub_64.S [deleted file]
arch/x86/boot/compressed/head_32.S
arch/x86/boot/compressed/head_64.S
arch/x86/include/asm/efi.h
arch/x86/platform/efi/efi_64.c

index 1dac210..56aa5fa 100644 (file)
@@ -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
 
index 2733bc2..36a26d6 100644 (file)
@@ -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
diff --git a/arch/x86/boot/compressed/efi_stub_32.S b/arch/x86/boot/compressed/efi_stub_32.S
deleted file mode 100644 (file)
index ed6c351..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-/* 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
diff --git a/arch/x86/boot/compressed/efi_stub_64.S b/arch/x86/boot/compressed/efi_stub_64.S
deleted file mode 100644 (file)
index 99494df..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-#include <asm/segment.h>
-#include <asm/msr.h>
-#include <asm/processor-flags.h>
-
-#include "../../platform/efi/efi_stub_64.S"
index f2dfd6d..0b03dc7 100644 (file)
@@ -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
 
index ee60b81..ad57ede 100644 (file)
@@ -459,15 +459,6 @@ SYM_FUNC_START(efi_pe_entry)
        leaq    efi64_config(%rip), %rax
        movq    %rax, efi_config(%rip)
 
-       call    1f
-1:     popq    %rbp
-       subq    $1b, %rbp
-
-       /*
-        * Relocate efi_config->call().
-        */
-       addq    %rbp, efi64_config+40(%rip)
-
        movq    %rax, %rdi
        call    make_boot_params
        cmpq    $0,%rax
@@ -475,20 +466,10 @@ SYM_FUNC_START(efi_pe_entry)
        mov     %rax, %rsi
        leaq    startup_32(%rip), %rax
        movl    %eax, BP_code32_start(%rsi)
-       jmp     2f              /* Skip the relocation */
 
 handover_entry:
-       call    1f
-1:     popq    %rbp
-       subq    $1b, %rbp
-
-       /*
-        * Relocate efi_config->call().
-        */
-       movq    efi_config(%rip), %rax
-       addq    %rbp, 40(%rax)
-2:
        movq    efi_config(%rip), %rdi
+       and     $~0xf, %rsp                     /* realign the stack */
        call    efi_main
        movq    %rax,%rsi
        cmpq    $0,%rax
@@ -688,14 +669,12 @@ SYM_DATA_LOCAL(efi_config, .quad 0)
 #ifdef CONFIG_EFI_MIXED
 SYM_DATA_START(efi32_config)
        .fill   5,8,0
-       .quad   efi64_thunk
        .byte   0
 SYM_DATA_END(efi32_config)
 #endif
 
 SYM_DATA_START(efi64_config)
        .fill   5,8,0
-       .quad   efi_call
        .byte   1
 SYM_DATA_END(efi64_config)
 #endif /* CONFIG_EFI_STUB */
index c27323c..001905d 100644 (file)
@@ -152,6 +152,7 @@ struct efi_setup_data {
 extern u64 efi_setup;
 
 #ifdef CONFIG_EFI
+extern efi_status_t efi64_thunk(u32, ...);
 
 static inline bool efi_is_mixed(void)
 {
@@ -205,7 +206,6 @@ struct efi_config {
        u64 runtime_services;
        u64 boot_services;
        u64 text_output;
-       efi_status_t (*call)(unsigned long, ...);
        bool is64;
 } __packed;
 
@@ -235,30 +235,36 @@ static inline bool efi_is_native(void)
                        (unsigned long)(attr), (attr))
 
 #define efi_table_attr(table, attr, instance) ({                       \
-       __typeof__(((table##_t *)0)->attr) __ret;                       \
+       __typeof__(instance->attr) __ret;                               \
        if (efi_is_native()) {                                          \
-               __ret = ((table##_t *)(unsigned long)instance)->attr;   \
+               __ret = instance->attr;                                 \
        } else {                                                        \
-               __ret = (__typeof__(__ret))efi_mixed_mode_cast(         \
-               ((table##_t *)(unsigned long)instance)->mixed_mode.attr);\
+               __ret = (__typeof__(__ret))                             \
+                       efi_mixed_mode_cast(instance->mixed_mode.attr); \
        }                                                               \
        __ret;                                                          \
 })
 
 #define efi_call_proto(protocol, f, instance, ...)                     \
-       __efi_early()->call((unsigned long)                             \
-                               efi_table_attr(protocol, f, instance),  \
-               instance, ##__VA_ARGS__)
+       (efi_is_native()                                                \
+               ? instance->f(instance, ##__VA_ARGS__)                  \
+               : efi64_thunk(instance->mixed_mode.f, instance, ##__VA_ARGS__))
 
 #define efi_call_early(f, ...)                                         \
-       __efi_early()->call((unsigned long)                             \
-                               efi_table_attr(efi_boot_services, f,    \
-               __efi_early()->boot_services), __VA_ARGS__)
+       (efi_is_native()                                                \
+               ? ((efi_boot_services_t *)(unsigned long)               \
+                       __efi_early()->boot_services)->f(__VA_ARGS__)   \
+               : efi64_thunk(((efi_boot_services_t *)(unsigned long)   \
+                       __efi_early()->boot_services)->mixed_mode.f,    \
+                       __VA_ARGS__))
 
 #define efi_call_runtime(f, ...)                                       \
-       __efi_early()->call((unsigned long)                             \
-                               efi_table_attr(efi_runtime_services, f, \
-               __efi_early()->runtime_services), __VA_ARGS__)
+       (efi_is_native()                                                \
+               ? ((efi_runtime_services_t *)(unsigned long)            \
+                       __efi_early()->runtime_services)->f(__VA_ARGS__)\
+               : efi64_thunk(((efi_runtime_services_t *)(unsigned long)\
+                       __efi_early()->runtime_services)->mixed_mode.f, \
+                       __VA_ARGS__))
 
 extern bool efi_reboot_required(void);
 extern bool efi_is_table_address(unsigned long phys_addr);
index 885e50a..03c2ed3 100644 (file)
@@ -635,8 +635,6 @@ void efi_switch_mm(struct mm_struct *mm)
 }
 
 #ifdef CONFIG_EFI_MIXED
-extern efi_status_t efi64_thunk(u32, ...);
-
 static DEFINE_SPINLOCK(efi_runtime_lock);
 
 #define runtime_service32(func)                                                 \