Commit 9f922377 authored by Ard Biesheuvel's avatar Ard Biesheuvel

efi/libstub/arm: Make efi_entry() an ordinary PE/COFF entrypoint

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: default avatarArd Biesheuvel <ardb@kernel.org>
parent e951a1f4
...@@ -60,7 +60,7 @@ optional_header: ...@@ -60,7 +60,7 @@ optional_header:
.long __pecoff_code_size @ SizeOfCode .long __pecoff_code_size @ SizeOfCode
.long __pecoff_data_size @ SizeOfInitializedData .long __pecoff_data_size @ SizeOfInitializedData
.long 0 @ SizeOfUninitializedData .long 0 @ SizeOfUninitializedData
.long efi_stub_entry - start @ AddressOfEntryPoint .long efi_entry - start @ AddressOfEntryPoint
.long start_offset @ BaseOfCode .long start_offset @ BaseOfCode
.long __pecoff_data_start - start @ BaseOfData .long __pecoff_data_start - start @ BaseOfData
......
...@@ -1437,33 +1437,15 @@ __enter_kernel: ...@@ -1437,33 +1437,15 @@ __enter_kernel:
reloc_code_end: reloc_code_end:
#ifdef CONFIG_EFI_STUB #ifdef CONFIG_EFI_STUB
.align 2 ENTRY(efi_enter_kernel)
_start: .long start - . mov r7, r0 @ preserve image base
mov r4, r1 @ preserve DT pointer
ENTRY(efi_stub_entry)
@ allocate space on stack for passing current zImage address mov r0, r4 @ DT start
@ and for the EFI stub to return of new entry point of add r1, r4, r2 @ DT end
@ 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
bl cache_clean_flush bl cache_clean_flush
ldr r0, [sp] @ relocated zImage mov r0, r7 @ relocated zImage
ldr r1, =_edata @ size of zImage ldr r1, =_edata @ size of zImage
add r1, r1, r0 @ end of zImage add r1, r1, r0 @ end of zImage
bl cache_clean_flush bl cache_clean_flush
...@@ -1473,9 +1455,8 @@ ENTRY(efi_stub_entry) ...@@ -1473,9 +1455,8 @@ ENTRY(efi_stub_entry)
@ inside the PE/COFF loader allocated region is unsafe. Let's @ inside the PE/COFF loader allocated region is unsafe. Let's
@ assume our own zImage relocation code did a better job, and @ assume our own zImage relocation code did a better job, and
@ jump into its version of this routine before proceeding. @ jump into its version of this routine before proceeding.
ldr r0, [sp] @ relocated zImage
ldr r1, .Ljmp ldr r1, .Ljmp
sub r1, r0, r1 sub r1, r7, r1
mov pc, r1 @ no mode switch mov pc, r1 @ no mode switch
0: 0:
bl cache_off bl cache_off
...@@ -1487,12 +1468,7 @@ ENTRY(efi_stub_entry) ...@@ -1487,12 +1468,7 @@ ENTRY(efi_stub_entry)
mov r1, #0xFFFFFFFF mov r1, #0xFFFFFFFF
mov r2, r4 mov r2, r4
b __efi_start b __efi_start
ENDPROC(efi_enter_kernel)
efi_load_fail:
@ Return EFI_LOAD_ERROR to EFI firmware on error.
ldr r0, =0x80000001
ldmfd sp!, {ip, pc}
ENDPROC(efi_stub_entry)
.align 2 .align 2
.Ljmp: .long start - 0b .Ljmp: .long start - 0b
#endif #endif
......
...@@ -10,81 +10,35 @@ ...@@ -10,81 +10,35 @@
#include <asm/assembler.h> #include <asm/assembler.h>
#define EFI_LOAD_ERROR 0x8000000000000001
__INIT __INIT
/* ENTRY(efi_enter_kernel)
* 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
/* /*
* efi_entry() will have copied the kernel image if necessary and we * efi_entry() will have copied the kernel image if necessary and we
* return here with device tree address in x0 and the kernel entry * end up here with device tree address in x1 and the kernel entry
* point stored at *image_addr. Save those values in registers which * point stored in x0. Save those values in registers which are
* are callee preserved. * 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).
*/ */
adr_l x1, _text mov x19, x0 // relocated Image address
adr_l x2, _edata mov x20, x1 // DTB address
sub x1, x2, x1
/* /*
* Flush the copied Image to the PoC, and ensure it is not shadowed by * Flush the copied Image to the PoC, and ensure it is not shadowed by
* stale icache entries from before relocation. * stale icache entries from before relocation.
*/ */
ldr w1, =kernel_size
bl __flush_dcache_area bl __flush_dcache_area
ic ialluis ic ialluis
dsb sy
/* /*
* Ensure that the rest of this function (in the original Image) is * Jump across, into the copy of the image that we just cleaned
* visible when the caches are disabled. The I-cache can't have stale * to the PoC, so that we can safely disable the MMU and caches.
* entries for the VA range of the current image, so no maintenance is
* necessary.
*/ */
adr x0, entry ldr w0, .Ljmp
adr x1, entry_end sub x0, x19, w0, sxtw
sub x1, x1, x0 br x0
bl __flush_dcache_area 0:
/* Turn off Dcache and MMU */ /* Turn off Dcache and MMU */
mrs x0, CurrentEL mrs x0, CurrentEL
cmp x0, #CurrentEL_EL2 cmp x0, #CurrentEL_EL2
...@@ -109,12 +63,6 @@ ENTRY(entry) ...@@ -109,12 +63,6 @@ ENTRY(entry)
mov x1, xzr mov x1, xzr
mov x2, xzr mov x2, xzr
mov x3, xzr mov x3, xzr
br x21 b stext
ENDPROC(efi_enter_kernel)
efi_load_fail: .Ljmp: .long _text - 0b
mov x0, #EFI_LOAD_ERROR
ldp x29, x30, [sp], #32
ret
entry_end:
ENDPROC(entry)
...@@ -27,7 +27,7 @@ optional_header: ...@@ -27,7 +27,7 @@ optional_header:
.long __initdata_begin - efi_header_end // SizeOfCode .long __initdata_begin - efi_header_end // SizeOfCode
.long __pecoff_data_size // SizeOfInitializedData .long __pecoff_data_size // SizeOfInitializedData
.long 0 // SizeOfUninitializedData .long 0 // SizeOfUninitializedData
.long __efistub_entry - _head // AddressOfEntryPoint .long __efistub_efi_entry - _head // AddressOfEntryPoint
.long efi_header_end - _head // BaseOfCode .long efi_header_end - _head // BaseOfCode
extra_header_fields: extra_header_fields:
......
...@@ -12,7 +12,8 @@ ...@@ -12,7 +12,8 @@
#ifdef CONFIG_EFI #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 * The EFI stub has its own symbol namespace prefixed by __efistub_, to
...@@ -42,6 +43,7 @@ __efistub___memset = __pi_memset; ...@@ -42,6 +43,7 @@ __efistub___memset = __pi_memset;
#endif #endif
__efistub__text = _text; __efistub__text = _text;
__efistub_stext = stext;
__efistub__end = _end; __efistub__end = _end;
__efistub__edata = _edata; __efistub__edata = _edata;
__efistub_screen_info = screen_info; __efistub_screen_info = screen_info;
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
*/ */
#include <linux/efi.h> #include <linux/efi.h>
#include <linux/libfdt.h>
#include <linux/sort.h> #include <linux/sort.h>
#include <asm/efi.h> #include <asm/efi.h>
...@@ -100,17 +101,22 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, ...@@ -100,17 +101,22 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
unsigned long *reserve_size, unsigned long *reserve_size,
unsigned long dram_base, unsigned long dram_base,
efi_loaded_image_t *image); 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 * 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 * 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 * for both archictectures, with the arch-specific code provided in the
* handle_kernel_image() function. * handle_kernel_image() function.
*/ */
unsigned long efi_entry(void *handle, efi_system_table_t *sys_table_arg, efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg)
unsigned long *image_addr)
{ {
efi_loaded_image_t *image; efi_loaded_image_t *image;
efi_status_t status; efi_status_t status;
unsigned long image_addr;
unsigned long image_size = 0; unsigned long image_size = 0;
unsigned long dram_base; unsigned long dram_base;
/* addr/point and size pairs for memory management*/ /* 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, ...@@ -120,7 +126,6 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table_arg,
unsigned long fdt_size = 0; unsigned long fdt_size = 0;
char *cmdline_ptr = NULL; char *cmdline_ptr = NULL;
int cmdline_size = 0; int cmdline_size = 0;
unsigned long new_fdt_addr;
efi_guid_t loaded_image_proto = LOADED_IMAGE_PROTOCOL_GUID; efi_guid_t loaded_image_proto = LOADED_IMAGE_PROTOCOL_GUID;
unsigned long reserve_addr = 0; unsigned long reserve_addr = 0;
unsigned long reserve_size = 0; unsigned long reserve_size = 0;
...@@ -130,8 +135,10 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table_arg, ...@@ -130,8 +135,10 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table_arg,
sys_table = sys_table_arg; sys_table = sys_table_arg;
/* Check if we were booted by the EFI firmware */ /* 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; goto fail;
}
status = check_platform_features(); status = check_platform_features();
if (status != EFI_SUCCESS) if (status != EFI_SUCCESS)
...@@ -152,6 +159,7 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table_arg, ...@@ -152,6 +159,7 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table_arg,
dram_base = get_dram_base(); dram_base = get_dram_base();
if (dram_base == EFI_ERROR) { if (dram_base == EFI_ERROR) {
pr_efi_err("Failed to find DRAM base\n"); pr_efi_err("Failed to find DRAM base\n");
status = EFI_LOAD_ERROR;
goto fail; goto fail;
} }
...@@ -163,6 +171,7 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table_arg, ...@@ -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); cmdline_ptr = efi_convert_cmdline(image, &cmdline_size);
if (!cmdline_ptr) { if (!cmdline_ptr) {
pr_efi_err("getting command line via LOADED_IMAGE_PROTOCOL\n"); pr_efi_err("getting command line via LOADED_IMAGE_PROTOCOL\n");
status = EFI_OUT_OF_RESOURCES;
goto fail; goto fail;
} }
...@@ -178,7 +187,7 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table_arg, ...@@ -178,7 +187,7 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table_arg,
si = setup_graphics(); si = setup_graphics();
status = handle_kernel_image(image_addr, &image_size, status = handle_kernel_image(&image_addr, &image_size,
&reserve_addr, &reserve_addr,
&reserve_size, &reserve_size,
dram_base, image); dram_base, image);
...@@ -227,7 +236,7 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table_arg, ...@@ -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=", status = handle_cmdline_files(image, cmdline_ptr, "initrd=",
efi_get_max_initrd_addr(dram_base, efi_get_max_initrd_addr(dram_base,
*image_addr), image_addr),
(unsigned long *)&initrd_addr, (unsigned long *)&initrd_addr,
(unsigned long *)&initrd_size); (unsigned long *)&initrd_size);
if (status != EFI_SUCCESS) if (status != EFI_SUCCESS)
...@@ -257,33 +266,30 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table_arg, ...@@ -257,33 +266,30 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table_arg,
install_memreserve_table(); install_memreserve_table();
new_fdt_addr = fdt_addr; status = allocate_new_fdt_and_exit_boot(handle, &fdt_addr,
status = allocate_new_fdt_and_exit_boot(handle, efi_get_max_fdt_addr(dram_base),
&new_fdt_addr, efi_get_max_fdt_addr(dram_base), initrd_addr, initrd_size,
initrd_addr, initrd_size, cmdline_ptr, cmdline_ptr, fdt_addr, fdt_size);
fdt_addr, fdt_size); if (status != EFI_SUCCESS)
goto fail_free_initrd;
/* efi_enter_kernel(image_addr, fdt_addr, fdt_totalsize((void *)fdt_addr));
* If all went well, we need to return the FDT address to the /* not reached */
* calling function so it can be passed to kernel as part of
* the kernel boot protocol.
*/
if (status == EFI_SUCCESS)
return new_fdt_addr;
fail_free_initrd:
pr_efi_err("Failed to update FDT and exit boot services\n"); pr_efi_err("Failed to update FDT and exit boot services\n");
efi_free(initrd_size, initrd_addr); efi_free(initrd_size, initrd_addr);
efi_free(fdt_size, fdt_addr); efi_free(fdt_size, fdt_addr);
fail_free_image: fail_free_image:
efi_free(image_size, *image_addr); efi_free(image_size, image_addr);
efi_free(reserve_size, reserve_addr); efi_free(reserve_size, reserve_addr);
fail_free_cmdline: fail_free_cmdline:
free_screen_info(si); free_screen_info(si);
efi_free(cmdline_size, (unsigned long)cmdline_ptr); efi_free(cmdline_size, (unsigned long)cmdline_ptr);
fail: fail:
return EFI_ERROR; return status;
} }
static int cmp_mem_desc(const void *l, const void *r) static int cmp_mem_desc(const void *l, const void *r)
......
...@@ -227,6 +227,7 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, ...@@ -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 * Relocate the zImage, so that it appears in the lowest 128 MB
* memory window. * memory window.
*/ */
*image_addr = (unsigned long)image->image_base;
*image_size = image->image_size; *image_size = image->image_size;
status = efi_relocate_kernel(image_addr, *image_size, *image_size, status = efi_relocate_kernel(image_addr, *image_size, *image_size,
kernel_base + MAX_UNCOMP_KERNEL_SIZE, 0, 0); kernel_base + MAX_UNCOMP_KERNEL_SIZE, 0, 0);
......
...@@ -49,7 +49,6 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, ...@@ -49,7 +49,6 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
{ {
efi_status_t status; efi_status_t status;
unsigned long kernel_size, kernel_memsize = 0; unsigned long kernel_size, kernel_memsize = 0;
void *old_image_addr = (void *)*image_addr;
unsigned long preferred_offset; unsigned long preferred_offset;
u64 phys_seed = 0; u64 phys_seed = 0;
...@@ -147,7 +146,7 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, ...@@ -147,7 +146,7 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
} }
*image_addr = *reserve_addr + TEXT_OFFSET; *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; return EFI_SUCCESS;
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment