efi: Add 'runtime' pointer to struct efi
authorArd Biesheuvel <ardb@kernel.org>
Tue, 21 Jan 2020 08:44:43 +0000 (09:44 +0100)
committerArd Biesheuvel <ardb@kernel.org>
Sun, 23 Feb 2020 20:59:42 +0000 (21:59 +0100)
Instead of going through the EFI system table each time, just copy the
runtime services table pointer into struct efi directly. This is the
last use of the system table pointer in struct efi, allowing us to
drop it in a future patch, along with a fair amount of quirky handling
of the translated address.

Note that usually, the runtime services pointer changes value during
the call to SetVirtualAddressMap(), so grab the updated value as soon
as that call returns. (Mixed mode uses a 1:1 mapping, and kexec boot
enters with the updated address in the system table, so in those cases,
we don't need to do anything here)

Tested-by: Tony Luck <tony.luck@intel.com> # arch/ia64
Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
arch/x86/include/asm/efi.h
arch/x86/kernel/asm-offsets_32.c
arch/x86/platform/efi/efi.c
arch/x86/platform/efi/efi_32.c
arch/x86/platform/efi/efi_64.c
arch/x86/platform/efi/efi_stub_32.S
drivers/firmware/efi/arm-init.c
drivers/firmware/efi/runtime-wrappers.c
include/linux/efi.h

index 78fc28da2e29e486a9614e6cb55ba50f799497c5..0de57151c7326dfe37ccec38689a4edb54ba7386 100644 (file)
@@ -218,7 +218,8 @@ extern void efi_thunk_runtime_setup(void);
 efi_status_t efi_set_virtual_address_map(unsigned long memory_map_size,
                                         unsigned long descriptor_size,
                                         u32 descriptor_version,
-                                        efi_memory_desc_t *virtual_map);
+                                        efi_memory_desc_t *virtual_map,
+                                        unsigned long systab_phys);
 
 /* arch specific definitions used by the stub code */
 
index 82826f2275cce92ccb5941c98717962c38e8b61c..2b4256ebe86ecfd0f9ccf610018709001d14bf62 100644 (file)
@@ -3,6 +3,8 @@
 # error "Please do not build this file directly, build asm-offsets.c instead"
 #endif
 
+#include <linux/efi.h>
+
 #include <asm/ucontext.h>
 
 #define __SYSCALL_I386(nr, sym, qual) [nr] = 1,
@@ -64,4 +66,7 @@ void foo(void)
        BLANK();
        DEFINE(__NR_syscall_max, sizeof(syscalls) - 1);
        DEFINE(NR_syscalls, sizeof(syscalls));
+
+       BLANK();
+       DEFINE(EFI_svam, offsetof(efi_runtime_services_t, set_virtual_address_map));
 }
index e4ee9a37254aad997c412653b430467f2a90b8d5..6bd8aae235d28a5c10168bf5bd53ed952fe02665 100644 (file)
@@ -55,8 +55,8 @@
 #include <asm/uv/uv.h>
 
 static efi_system_table_t efi_systab __initdata;
-static u64 efi_systab_phys __initdata;
 
+static unsigned long efi_systab_phys __initdata;
 static unsigned long prop_phys = EFI_INVALID_TABLE_ADDR;
 static unsigned long uga_phys = EFI_INVALID_TABLE_ADDR;
 static unsigned long efi_runtime, efi_nr_tables;
@@ -335,7 +335,7 @@ void __init efi_print_memmap(void)
        }
 }
 
-static int __init efi_systab_init(u64 phys)
+static int __init efi_systab_init(unsigned long phys)
 {
        int size = efi_enabled(EFI_64BIT) ? sizeof(efi_system_table_64_t)
                                          : sizeof(efi_system_table_32_t);
@@ -949,7 +949,8 @@ static void __init __efi_enter_virtual_mode(void)
        status = efi_set_virtual_address_map(efi.memmap.desc_size * count,
                                             efi.memmap.desc_size,
                                             efi.memmap.desc_version,
-                                            (efi_memory_desc_t *)pa);
+                                            (efi_memory_desc_t *)pa,
+                                            efi_systab_phys);
        if (status != EFI_SUCCESS) {
                pr_err("Unable to switch EFI into virtual mode (status=%lx)!\n",
                       status);
@@ -983,6 +984,8 @@ void __init efi_enter_virtual_mode(void)
        if (efi_enabled(EFI_PARAVIRT))
                return;
 
+       efi.runtime = (efi_runtime_services_t *)efi_runtime;
+
        if (efi_setup)
                kexec_enter_virtual_mode();
        else
index 081d466002c96c4f061f54b22ca4032876108fbe..c049c432745d2491a04ce11c5249fbb8fca9a69f 100644 (file)
@@ -66,14 +66,16 @@ void __init efi_map_region(efi_memory_desc_t *md)
 void __init efi_map_region_fixed(efi_memory_desc_t *md) {}
 void __init parse_efi_setup(u64 phys_addr, u32 data_len) {}
 
-efi_status_t efi_call_svam(efi_set_virtual_address_map_t *__efiapi *,
-                          u32, u32, u32, void *);
+efi_status_t efi_call_svam(efi_runtime_services_t * const *,
+                          u32, u32, u32, void *, u32);
 
 efi_status_t __init efi_set_virtual_address_map(unsigned long memory_map_size,
                                                unsigned long descriptor_size,
                                                u32 descriptor_version,
-                                               efi_memory_desc_t *virtual_map)
+                                               efi_memory_desc_t *virtual_map,
+                                               unsigned long systab_phys)
 {
+       const efi_system_table_t *systab = (efi_system_table_t *)systab_phys;
        struct desc_ptr gdt_descr;
        efi_status_t status;
        unsigned long flags;
@@ -90,9 +92,10 @@ efi_status_t __init efi_set_virtual_address_map(unsigned long memory_map_size,
 
        /* Disable interrupts around EFI calls: */
        local_irq_save(flags);
-       status = efi_call_svam(&efi.systab->runtime->set_virtual_address_map,
+       status = efi_call_svam(&systab->runtime,
                               memory_map_size, descriptor_size,
-                              descriptor_version, virtual_map);
+                              descriptor_version, virtual_map,
+                              __pa(&efi.runtime));
        local_irq_restore(flags);
 
        load_fixmap_gdt(0);
index fa8506e76bbeba3eb7b2efc75ff96767d41f6b6b..f78f7da666fbb6abb133a3e5f3f403b8832cc667 100644 (file)
@@ -500,12 +500,9 @@ static DEFINE_SPINLOCK(efi_runtime_lock);
  */
 #define __efi_thunk(func, ...)                                         \
 ({                                                                     \
-       efi_runtime_services_32_t *__rt;                                \
        unsigned short __ds, __es;                                      \
        efi_status_t ____s;                                             \
                                                                        \
-       __rt = (void *)(unsigned long)efi.systab->mixed_mode.runtime;   \
-                                                                       \
        savesegment(ds, __ds);                                          \
        savesegment(es, __es);                                          \
                                                                        \
@@ -513,7 +510,7 @@ static DEFINE_SPINLOCK(efi_runtime_lock);
        loadsegment(ds, __KERNEL_DS);                                   \
        loadsegment(es, __KERNEL_DS);                                   \
                                                                        \
-       ____s = efi64_thunk(__rt->func, __VA_ARGS__);                   \
+       ____s = efi64_thunk(efi.runtime->mixed_mode.func, __VA_ARGS__); \
                                                                        \
        loadsegment(ds, __ds);                                          \
        loadsegment(es, __es);                                          \
@@ -886,8 +883,10 @@ efi_status_t __init __no_sanitize_address
 efi_set_virtual_address_map(unsigned long memory_map_size,
                            unsigned long descriptor_size,
                            u32 descriptor_version,
-                           efi_memory_desc_t *virtual_map)
+                           efi_memory_desc_t *virtual_map,
+                           unsigned long systab_phys)
 {
+       const efi_system_table_t *systab = (efi_system_table_t *)systab_phys;
        efi_status_t status;
        unsigned long flags;
        pgd_t *save_pgd = NULL;
@@ -910,13 +909,16 @@ efi_set_virtual_address_map(unsigned long memory_map_size,
 
        /* Disable interrupts around EFI calls: */
        local_irq_save(flags);
-       status = efi_call(efi.systab->runtime->set_virtual_address_map,
+       status = efi_call(efi.runtime->set_virtual_address_map,
                          memory_map_size, descriptor_size,
                          descriptor_version, virtual_map);
        local_irq_restore(flags);
 
        kernel_fpu_end();
 
+       /* grab the virtually remapped EFI runtime services table pointer */
+       efi.runtime = READ_ONCE(systab->runtime);
+
        if (save_pgd)
                efi_uv1_memmap_phys_epilog(save_pgd);
        else
index 75c46e7a809f38ca96bfe5a9ebc473b7c87b5124..09237236fb25403225435ca3f41c3ede39c23233 100644 (file)
@@ -8,14 +8,20 @@
 
 #include <linux/linkage.h>
 #include <linux/init.h>
+#include <asm/asm-offsets.h>
 #include <asm/page_types.h>
 
        __INIT
 SYM_FUNC_START(efi_call_svam)
-       push    8(%esp)
-       push    8(%esp)
+       push    %ebp
+       movl    %esp, %ebp
+       push    %ebx
+
+       push    16(%esp)
+       push    16(%esp)
        push    %ecx
        push    %edx
+       movl    %eax, %ebx              // &systab_phys->runtime
 
        /*
         * Switch to the flat mapped alias of this routine, by jumping to the
@@ -35,15 +41,20 @@ SYM_FUNC_START(efi_call_svam)
        subl    $__PAGE_OFFSET, %esp
 
        /* call the EFI routine */
-       call    *(%eax)
+       movl    (%eax), %eax
+       call    *EFI_svam(%eax)
 
-       /* convert ESP back to a kernel VA, and pop the outgoing args */
-       addl    $__PAGE_OFFSET + 16, %esp
+       /* grab the virtually remapped EFI runtime services table pointer */
+       movl    (%ebx), %ecx
+       movl    36(%esp), %edx          // &efi.runtime
+       movl    %ecx, (%edx)
 
        /* re-enable paging */
        movl    %cr0, %edx
        orl     $0x80000000, %edx
        movl    %edx, %cr0
 
+       pop     %ebx
+       leave
        ret
 SYM_FUNC_END(efi_call_svam)
index 5fc2f6813b847e619d9bc61620387e97ae51d77b..77048f7a96596a16cac04db7053dcc1e17412a5f 100644 (file)
@@ -104,6 +104,7 @@ static int __init uefi_init(void)
        if (retval)
                goto out;
 
+       efi.runtime = efi.systab->runtime;
        efi.runtime_version = efi.systab->hdr.revision;
 
        efi_systab_report_header(&efi.systab->hdr,
index 65fffaa222108602effc0d849e15963c6f6e545b..1410beaef5c30404e3cb1503b3e7b3c509d36674 100644 (file)
@@ -40,9 +40,9 @@
  * code doesn't get too cluttered:
  */
 #define efi_call_virt(f, args...)   \
-       efi_call_virt_pointer(efi.systab->runtime, f, args)
+       efi_call_virt_pointer(efi.runtime, f, args)
 #define __efi_call_virt(f, args...) \
-       __efi_call_virt_pointer(efi.systab->runtime, f, args)
+       __efi_call_virt_pointer(efi.runtime, f, args)
 
 struct efi_runtime_work efi_rts_work;
 
index a42045568df351dfb03c55305718061cd5393c54..1f69c4c2dd5ce61690c85676591b4c65b5ed686f 100644 (file)
@@ -529,6 +529,7 @@ typedef struct {
  * All runtime access to EFI goes through this structure:
  */
 extern struct efi {
+       const efi_runtime_services_t    *runtime;               /* EFI runtime services table */
        efi_system_table_t *systab;     /* EFI system table */
        unsigned int runtime_version;   /* Runtime services version */
        unsigned long acpi;             /* ACPI table  (IA64 ext 0.71) */