efi/libstub/arm: Make efi_entry() an ordinary PE/COFF entrypoint
authorArd Biesheuvel <ardb@kernel.org>
Mon, 17 Feb 2020 11:44:37 +0000 (12:44 +0100)
committerArd Biesheuvel <ardb@kernel.org>
Sat, 22 Feb 2020 22:37:37 +0000 (23:37 +0100)
Expose efi_entry() as the PE/COFF entrypoint directly, instead of
jumping into a wrapper that fiddles with stack buffers and other
stuff that the compiler is much better at. The only reason this
code exists is to obtain a pointer to the base of the image, but
we can get the same value from the loaded_image protocol, which
we already need for other reasons anyway.

Update the return type as well, to make it consistent with what
is required for a PE/COFF executable entrypoint.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
arch/arm/boot/compressed/efi-header.S
arch/arm/boot/compressed/head.S
arch/arm64/kernel/efi-entry.S
arch/arm64/kernel/efi-header.S
arch/arm64/kernel/image-vars.h
drivers/firmware/efi/libstub/arm-stub.c
drivers/firmware/efi/libstub/arm32-stub.c
drivers/firmware/efi/libstub/arm64-stub.c

index a598358..e38fbda 100644 (file)
@@ -60,7 +60,7 @@ optional_header:
                .long   __pecoff_code_size              @ SizeOfCode
                .long   __pecoff_data_size              @ SizeOfInitializedData
                .long   0                               @ SizeOfUninitializedData
-               .long   efi_stub_entry - start          @ AddressOfEntryPoint
+               .long   efi_entry - start               @ AddressOfEntryPoint
                .long   start_offset                    @ BaseOfCode
                .long   __pecoff_data_start - start     @ BaseOfData
 
index 8487221..36ffbee 100644 (file)
@@ -1437,33 +1437,15 @@ __enter_kernel:
 reloc_code_end:
 
 #ifdef CONFIG_EFI_STUB
-               .align  2
-_start:                .long   start - .
-
-ENTRY(efi_stub_entry)
-               @ allocate space on stack for passing current zImage address
-               @ and for the EFI stub to return of new entry point of
-               @ zImage, as EFI stub may copy the kernel. Pointer address
-               @ is passed in r2. r0 and r1 are passed through from the
-               @ EFI firmware to efi_entry
-               adr     ip, _start
-               ldr     r3, [ip]
-               add     r3, r3, ip
-               stmfd   sp!, {r3, lr}
-               mov     r2, sp                  @ pass zImage address in r2
-               bl      efi_entry
-
-               @ Check for error return from EFI stub. r0 has FDT address
-               @ or error code.
-               cmn     r0, #1
-               beq     efi_load_fail
-
-               @ Preserve return value of efi_entry() in r4
-               mov     r4, r0
-               add     r1, r4, #SZ_2M                  @ DT end
+ENTRY(efi_enter_kernel)
+               mov     r7, r0                          @ preserve image base
+               mov     r4, r1                          @ preserve DT pointer
+
+               mov     r0, r4                          @ DT start
+               add     r1, r4, r2                      @ DT end
                bl      cache_clean_flush
 
-               ldr     r0, [sp]                        @ relocated zImage
+               mov     r0, r7                          @ relocated zImage
                ldr     r1, =_edata                     @ size of zImage
                add     r1, r1, r0                      @ end of zImage
                bl      cache_clean_flush
@@ -1473,9 +1455,8 @@ ENTRY(efi_stub_entry)
                @ inside the PE/COFF loader allocated region is unsafe. Let's
                @ assume our own zImage relocation code did a better job, and
                @ jump into its version of this routine before proceeding.
-               ldr     r0, [sp]                        @ relocated zImage
                ldr     r1, .Ljmp
-               sub     r1, r0, r1
+               sub     r1, r7, r1
                mov     pc, r1                          @ no mode switch
 0:
                bl      cache_off
@@ -1487,12 +1468,7 @@ ENTRY(efi_stub_entry)
                mov     r1, #0xFFFFFFFF
                mov     r2, r4
                b       __efi_start
-
-efi_load_fail:
-               @ Return EFI_LOAD_ERROR to EFI firmware on error.
-               ldr     r0, =0x80000001
-               ldmfd   sp!, {ip, pc}
-ENDPROC(efi_stub_entry)
+ENDPROC(efi_enter_kernel)
                .align  2
 .Ljmp:         .long   start - 0b
 #endif
index 304d5b0..4cfd03c 100644 (file)
 
 #include <asm/assembler.h>
 
-#define EFI_LOAD_ERROR 0x8000000000000001
-
        __INIT
 
-       /*
-        * We arrive here from the EFI boot manager with:
-        *
-        *    * CPU in little-endian mode
-        *    * MMU on with identity-mapped RAM
-        *    * Icache and Dcache on
-        *
-        * We will most likely be running from some place other than where
-        * we want to be. The kernel image wants to be placed at TEXT_OFFSET
-        * from start of RAM.
-        */
-ENTRY(entry)
-       /*
-        * Create a stack frame to save FP/LR with extra space
-        * for image_addr variable passed to efi_entry().
-        */
-       stp     x29, x30, [sp, #-32]!
-       mov     x29, sp
-
-       /*
-        * Call efi_entry to do the real work.
-        * x0 and x1 are already set up by firmware. Current runtime
-        * address of image is calculated and passed via *image_addr.
-        *
-        * unsigned long efi_entry(void *handle,
-        *                         efi_system_table_t *sys_table,
-        *                         unsigned long *image_addr) ;
-        */
-       adr_l   x8, _text
-       add     x2, sp, 16
-       str     x8, [x2]
-       bl      efi_entry
-       cmn     x0, #1
-       b.eq    efi_load_fail
-
+ENTRY(efi_enter_kernel)
        /*
         * efi_entry() will have copied the kernel image if necessary and we
-        * return here with device tree address in x0 and the kernel entry
-        * point stored at *image_addr. Save those values in registers which
-        * are callee preserved.
-        */
-       mov     x20, x0         // DTB address
-       ldr     x0, [sp, #16]   // relocated _text address
-       ldr     w21, =stext_offset
-       add     x21, x0, x21
-
-       /*
-        * Calculate size of the kernel Image (same for original and copy).
+        * end up here with device tree address in x1 and the kernel entry
+        * point stored in x0. Save those values in registers which are
+        * callee preserved.
         */
-       adr_l   x1, _text
-       adr_l   x2, _edata
-       sub     x1, x2, x1
+       mov     x19, x0                 // relocated Image address
+       mov     x20, x1                 // DTB address
 
        /*
         * Flush the copied Image to the PoC, and ensure it is not shadowed by
         * stale icache entries from before relocation.
         */
+       ldr     w1, =kernel_size
        bl      __flush_dcache_area
        ic      ialluis
+       dsb     sy
 
        /*
-        * Ensure that the rest of this function (in the original Image) is
-        * visible when the caches are disabled. The I-cache can't have stale
-        * entries for the VA range of the current image, so no maintenance is
-        * necessary.
+        * Jump across, into the copy of the image that we just cleaned
+        * to the PoC, so that we can safely disable the MMU and caches.
         */
-       adr     x0, entry
-       adr     x1, entry_end
-       sub     x1, x1, x0
-       bl      __flush_dcache_area
-
+       ldr     w0, .Ljmp
+       sub     x0, x19, w0, sxtw
+       br      x0
+0:
        /* Turn off Dcache and MMU */
        mrs     x0, CurrentEL
        cmp     x0, #CurrentEL_EL2
@@ -109,12 +63,6 @@ ENTRY(entry)
        mov     x1, xzr
        mov     x2, xzr
        mov     x3, xzr
-       br      x21
-
-efi_load_fail:
-       mov     x0, #EFI_LOAD_ERROR
-       ldp     x29, x30, [sp], #32
-       ret
-
-entry_end:
-ENDPROC(entry)
+       b       stext
+ENDPROC(efi_enter_kernel)
+.Ljmp: .long   _text - 0b
index a7cfacc..40c704c 100644 (file)
@@ -27,7 +27,7 @@ optional_header:
        .long   __initdata_begin - efi_header_end       // SizeOfCode
        .long   __pecoff_data_size                      // SizeOfInitializedData
        .long   0                                       // SizeOfUninitializedData
-       .long   __efistub_entry - _head                 // AddressOfEntryPoint
+       .long   __efistub_efi_entry - _head             // AddressOfEntryPoint
        .long   efi_header_end - _head                  // BaseOfCode
 
 extra_header_fields:
index 25a2a9b..87cb3d4 100644 (file)
@@ -12,7 +12,8 @@
 
 #ifdef CONFIG_EFI
 
-__efistub_stext_offset = stext - _text;
+__efistub_kernel_size          = _edata - _text;
+
 
 /*
  * The EFI stub has its own symbol namespace prefixed by __efistub_, to
@@ -42,6 +43,7 @@ __efistub___memset            = __pi_memset;
 #endif
 
 __efistub__text                        = _text;
+__efistub_stext                        = stext;
 __efistub__end                 = _end;
 __efistub__edata               = _edata;
 __efistub_screen_info          = screen_info;
index 7bbef4a..f12736f 100644 (file)
@@ -10,6 +10,7 @@
  */
 
 #include <linux/efi.h>
+#include <linux/libfdt.h>
 #include <linux/sort.h>
 #include <asm/efi.h>
 
@@ -100,17 +101,22 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
                                 unsigned long *reserve_size,
                                 unsigned long dram_base,
                                 efi_loaded_image_t *image);
+
+asmlinkage void __noreturn efi_enter_kernel(unsigned long entrypoint,
+                                           unsigned long fdt_addr,
+                                           unsigned long fdt_size);
+
 /*
  * EFI entry point for the arm/arm64 EFI stubs.  This is the entrypoint
  * that is described in the PE/COFF header.  Most of the code is the same
  * for both archictectures, with the arch-specific code provided in the
  * handle_kernel_image() function.
  */
-unsigned long efi_entry(void *handle, efi_system_table_t *sys_table_arg,
-                              unsigned long *image_addr)
+efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg)
 {
        efi_loaded_image_t *image;
        efi_status_t status;
+       unsigned long image_addr;
        unsigned long image_size = 0;
        unsigned long dram_base;
        /* addr/point and size pairs for memory management*/
@@ -120,7 +126,6 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table_arg,
        unsigned long fdt_size = 0;
        char *cmdline_ptr = NULL;
        int cmdline_size = 0;
-       unsigned long new_fdt_addr;
        efi_guid_t loaded_image_proto = LOADED_IMAGE_PROTOCOL_GUID;
        unsigned long reserve_addr = 0;
        unsigned long reserve_size = 0;
@@ -130,8 +135,10 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table_arg,
        sys_table = sys_table_arg;
 
        /* Check if we were booted by the EFI firmware */
-       if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE)
+       if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) {
+               status = EFI_INVALID_PARAMETER;
                goto fail;
+       }
 
        status = check_platform_features();
        if (status != EFI_SUCCESS)
@@ -152,6 +159,7 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table_arg,
        dram_base = get_dram_base();
        if (dram_base == EFI_ERROR) {
                pr_efi_err("Failed to find DRAM base\n");
+               status = EFI_LOAD_ERROR;
                goto fail;
        }
 
@@ -163,6 +171,7 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table_arg,
        cmdline_ptr = efi_convert_cmdline(image, &cmdline_size);
        if (!cmdline_ptr) {
                pr_efi_err("getting command line via LOADED_IMAGE_PROTOCOL\n");
+               status = EFI_OUT_OF_RESOURCES;
                goto fail;
        }
 
@@ -178,7 +187,7 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table_arg,
 
        si = setup_graphics();
 
-       status = handle_kernel_image(image_addr, &image_size,
+       status = handle_kernel_image(&image_addr, &image_size,
                                     &reserve_addr,
                                     &reserve_size,
                                     dram_base, image);
@@ -227,7 +236,7 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table_arg,
 
        status = handle_cmdline_files(image, cmdline_ptr, "initrd=",
                                      efi_get_max_initrd_addr(dram_base,
-                                                             *image_addr),
+                                                             image_addr),
                                      (unsigned long *)&initrd_addr,
                                      (unsigned long *)&initrd_size);
        if (status != EFI_SUCCESS)
@@ -257,33 +266,30 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table_arg,
 
        install_memreserve_table();
 
-       new_fdt_addr = fdt_addr;
-       status = allocate_new_fdt_and_exit_boot(handle,
-                               &new_fdt_addr, efi_get_max_fdt_addr(dram_base),
-                               initrd_addr, initrd_size, cmdline_ptr,
-                               fdt_addr, fdt_size);
+       status = allocate_new_fdt_and_exit_boot(handle, &fdt_addr,
+                                               efi_get_max_fdt_addr(dram_base),
+                                               initrd_addr, initrd_size,
+                                               cmdline_ptr, fdt_addr, fdt_size);
+       if (status != EFI_SUCCESS)
+               goto fail_free_initrd;
 
-       /*
-        * If all went well, we need to return the FDT address to the
-        * calling function so it can be passed to kernel as part of
-        * the kernel boot protocol.
-        */
-       if (status == EFI_SUCCESS)
-               return new_fdt_addr;
+       efi_enter_kernel(image_addr, fdt_addr, fdt_totalsize((void *)fdt_addr));
+       /* not reached */
 
+fail_free_initrd:
        pr_efi_err("Failed to update FDT and exit boot services\n");
 
        efi_free(initrd_size, initrd_addr);
        efi_free(fdt_size, fdt_addr);
 
 fail_free_image:
-       efi_free(image_size, *image_addr);
+       efi_free(image_size, image_addr);
        efi_free(reserve_size, reserve_addr);
 fail_free_cmdline:
        free_screen_info(si);
        efi_free(cmdline_size, (unsigned long)cmdline_ptr);
 fail:
-       return EFI_ERROR;
+       return status;
 }
 
 static int cmp_mem_desc(const void *l, const void *r)
index 7b2a638..7826553 100644 (file)
@@ -227,6 +227,7 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
         * Relocate the zImage, so that it appears in the lowest 128 MB
         * memory window.
         */
+       *image_addr = (unsigned long)image->image_base;
        *image_size = image->image_size;
        status = efi_relocate_kernel(image_addr, *image_size, *image_size,
                                     kernel_base + MAX_UNCOMP_KERNEL_SIZE, 0, 0);
index 2915b44..ad5f24a 100644 (file)
@@ -49,7 +49,6 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
 {
        efi_status_t status;
        unsigned long kernel_size, kernel_memsize = 0;
-       void *old_image_addr = (void *)*image_addr;
        unsigned long preferred_offset;
        u64 phys_seed = 0;
 
@@ -147,7 +146,7 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
                }
                *image_addr = *reserve_addr + TEXT_OFFSET;
        }
-       memcpy((void *)*image_addr, old_image_addr, kernel_size);
+       memcpy((void *)*image_addr, image->image_base, kernel_size);
 
        return EFI_SUCCESS;
 }