On Fri, Aug 02, 2013 at 02:29:07PM -0700, Roy Franz wrote: > This patch adds EFI stub support for the ARM Linux kernel. The EFI stub > operations similarly to the x86 stub: it is a shim between the EFI firmware > and the normal zImage entry point, and sets up the environment that the > zImage is expecting. This includes loading the initrd (optionaly) and > device tree from the system partition based on the kernel command line. > The stub updates the device tree as necessary, including adding reserved > memory regions and adding entries for EFI runtime services. The PE/COFF > "MZ" header at offset 0 results in the first instruction being an add > that corrupts r5, which is not used by the zImage interface. > > Signed-off-by: Roy Franz <roy.franz@xxxxxxxxxx> > --- > arch/arm/boot/compressed/Makefile | 18 +- > arch/arm/boot/compressed/efi-header.S | 114 ++++++++ > arch/arm/boot/compressed/efi-stub.c | 514 +++++++++++++++++++++++++++++++++ > arch/arm/boot/compressed/head.S | 90 +++++- > 4 files changed, 732 insertions(+), 4 deletions(-) > create mode 100644 arch/arm/boot/compressed/efi-header.S > create mode 100644 arch/arm/boot/compressed/efi-stub.c > > diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile > index 7ac1610..c62826a 100644 > --- a/arch/arm/boot/compressed/Makefile > +++ b/arch/arm/boot/compressed/Makefile > @@ -106,8 +106,22 @@ $(addprefix $(obj)/,$(libfdt) $(libfdt_hdrs)): $(obj)/%: $(srctree)/scripts/dtc/ > $(addprefix $(obj)/,$(libfdt_objs) atags_to_fdt.o): \ > $(addprefix $(obj)/,$(libfdt_hdrs)) > > +$(addprefix $(obj)/,$(libfdt_objs) efi-stub.o): \ > + $(addprefix $(obj)/,$(libfdt_hdrs)) > + > ifeq ($(CONFIG_ARM_ATAG_DTB_COMPAT),y) > -OBJS += $(libfdt_objs) atags_to_fdt.o > +OBJS += atags_to_fdt.o > +USE_LIBFDT = y > +endif > + > +ifeq ($(CONFIG_EFI_STUB),y) > +CFLAGS_efi-stub.o += -DTEXT_OFFSET=$(TEXT_OFFSET) > +OBJS += efi-stub.o > +USE_LIBFDT = y > +endif > + > +ifeq ($(USE_LIBFDT),y) > +OBJS += $(libfdt_objs) > endif > > targets := vmlinux vmlinux.lds \ > @@ -125,7 +139,7 @@ ORIG_CFLAGS := $(KBUILD_CFLAGS) > KBUILD_CFLAGS = $(subst -pg, , $(ORIG_CFLAGS)) > endif > > -ccflags-y := -fpic -mno-single-pic-base -fno-builtin -I$(obj) > +ccflags-y := -fpic -mno-single-pic-base -fno-builtin -I$(obj) -fno-stack-protector > asflags-y := -DZIMAGE > > # Supply kernel BSS size to the decompressor via a linker symbol. > diff --git a/arch/arm/boot/compressed/efi-header.S b/arch/arm/boot/compressed/efi-header.S > new file mode 100644 > index 0000000..6ff32cc > --- /dev/null > +++ b/arch/arm/boot/compressed/efi-header.S > @@ -0,0 +1,114 @@ > +@ Copyright (C) 2013 Linaro Ltd; <roy.franz@xxxxxxxxxx> > +@ > +@ This file contains the PE/COFF header that is part of the > +@ EFI stub. > +@ > + > + .org 0x3c > + @ > + @ The PE header can be anywhere in the file, but for > + @ simplicity we keep it together with the MSDOS header > + @ The offset to the PE/COFF header needs to be at offset > + @ 0x3C in the MSDOS header. > + @ The only 2 fields of the MSDOS header that are used are this > + @ PE/COFF offset, and the "MZ" bytes at offset 0x0. > + @ > + .long pe_header @ Offset to the PE header. > + > + .align 3 > +pe_header: > + > + > +pe_header: > + .ascii "PE" > + .short 0 > + > +coff_header: > + .short 0x01c2 @ ARM or Thumb > + .short 2 @ nr_sections > + .long 0 @ TimeDateStamp > + .long 0 @ PointerToSymbolTable > + .long 1 @ NumberOfSymbols > + .short section_table - optional_header @ SizeOfOptionalHeader > + .short 0x306 @ Characteristics. > + @ IMAGE_FILE_32BIT_MACHINE | > + @ IMAGE_FILE_DEBUG_STRIPPED | > + @ IMAGE_FILE_EXECUTABLE_IMAGE | > + @ IMAGE_FILE_LINE_NUMS_STRIPPED > + > +optional_header: > + .short 0x10b @ PE32 format > + .byte 0x02 @ MajorLinkerVersion > + .byte 0x14 @ MinorLinkerVersion > + > + .long 0 @ SizeOfCode > + > + .long 0 @ SizeOfInitializedData > + .long 0 @ SizeOfUninitializedData > + > + .long efi_stub_entry @ AddressOfEntryPoint > + .long efi_stub_entry @ BaseOfCode > + .long 0 @ data > + > +extra_header_fields: > + .long 0 @ ImageBase > + .long 0x20 @ SectionAlignment > + .long 0x20 @ FileAlignment > + .short 0 @ MajorOperatingSystemVersion > + .short 0 @ MinorOperatingSystemVersion > + .short 0 @ MajorImageVersion > + .short 0 @ MinorImageVersion > + .short 0 @ MajorSubsystemVersion > + .short 0 @ MinorSubsystemVersion > + .long 0 @ Win32VersionValue > + > + .long _edata @ SizeOfImage > + > + @ Everything before the entry point is considered part of the header > + .long efi_stub_entry @ SizeOfHeaders > + .long 0 @ CheckSum > + .short 0xa @ Subsystem (EFI application) > + .short 0 @ DllCharacteristics > + .long 0 @ SizeOfStackReserve > + .long 0 @ SizeOfStackCommit > + .long 0 @ SizeOfHeapReserve > + .long 0 @ SizeOfHeapCommit > + .long 0 @ LoaderFlags > + .long 0x0 @ NumberOfRvaAndSizes > + > + # Section table > +section_table: > + > + # > + # The EFI application loader requires a relocation section > + # because EFI applications must be relocatable. This is a > + # dummy section as far as we are concerned. > + # > + .ascii ".reloc" > + .byte 0 > + .byte 0 @ end of 0 padding of section name > + .long 0 > + .long 0 > + .long 0 @ SizeOfRawData > + .long 0 @ PointerToRawData > + .long 0 @ PointerToRelocations > + .long 0 @ PointerToLineNumbers > + .short 0 @ NumberOfRelocations > + .short 0 @ NumberOfLineNumbers > + .long 0x42100040 @ Characteristics (section flags) > + > + > + .ascii ".text" > + .byte 0 > + .byte 0 > + .byte 0 @ end of 0 padding of section name > + .long _edata - efi_stub_entry @ VirtualSize > + .long efi_stub_entry @ VirtualAddress > + .long _edata - efi_stub_entry @ SizeOfRawData > + .long efi_stub_entry @ PointerToRawData > + > + .long 0 @ PointerToRelocations (0 for executables) > + .long 0 @ PointerToLineNumbers (0 for executables) > + .short 0 @ NumberOfRelocations (0 for executables) > + .short 0 @ NumberOfLineNumbers (0 for executables) > + .long 0xe0500020 @ Characteristics (section flags) > diff --git a/arch/arm/boot/compressed/efi-stub.c b/arch/arm/boot/compressed/efi-stub.c > new file mode 100644 > index 0000000..41fa1e2 > --- /dev/null > +++ b/arch/arm/boot/compressed/efi-stub.c > @@ -0,0 +1,514 @@ > +/* > + * linux/arch/arm/boot/compressed/efi-stub.c > + * > + * Copyright (C) 2013 Linaro Ltd; <roy.franz@xxxxxxxxxx> > + * > + * This file implements the EFI boot stub for the ARM kernel > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2 as > + * published by the Free Software Foundation. > + * > + */ > +#include <linux/efi.h> > +#include <libfdt.h> > + > + > +/* Error code returned to ASM code instead of valid FDT address. */ > +#define EFI_STUB_ERROR (~0) > + > +/* EFI function call wrappers. These are not required for > + * ARM, but wrappers are required for X86 to convert between > + * ABIs. These wrappers are provided to allow code sharing > + * between X86 and ARM. Since these wrappers directly invoke the > + * EFI function pointer, the function pointer type must be properly > + * defined, which is not the case for X86 One advantage of this is > + * it allows for type checking of arguments, which is not > + * possible with the X86 wrappers. > + */ > +#define efi_call_phys0(f) f() > +#define efi_call_phys1(f, a1) f(a1) > +#define efi_call_phys2(f, a1, a2) f(a1, a2) > +#define efi_call_phys3(f, a1, a2, a3) f(a1, a2, a3) > +#define efi_call_phys4(f, a1, a2, a3, a4) f(a1, a2, a3, a4) > +#define efi_call_phys5(f, a1, a2, a3, a4, a5) f(a1, a2, a3, a4, a5) > + > +/* The maximum uncompressed kernel size is 32 MBytes, so we will reserve > + * that for the decompressed kernel. We have no easy way to tell what > + * the actuall size of code + data the uncompressed kernel will use. > + */ > +#define MAX_UNCOMP_KERNEL_SIZE 0x02000000 > + > +/* The kernel zImage should be located between 32 Mbytes > + * and 128 MBytes from the base of DRAM. The min > + * address leaves space for a maximal size uncompressed image, > + * and the max address is due to how the zImage decompressor > + * picks a destination address. > + */ > +#define MAX_ZIMAGE_OFFSET 0x08000000 > +#define MIN_ZIMAGE_OFFSET MAX_UNCOMP_KERNEL_SIZE > + > +#define MAX_CMDLINE_LEN 500 > + > +struct fdt_region { > + u64 base; > + u64 size; > +}; > + > +/* > + * Additional size that could be used for FDT entries added by > + * the UEFI OS Loader Estimation based on: > + * EDID (300bytes) + bootargs + initrd region (20bytes) > + * + system memory region (20bytes) + mp_core entries (200 > + * bytes) > + */ > +#define FDT_ADDITIONAL_ENTRIES_SIZE (0x300 + MAX_CMDLINE_LEN) > + > +/* Include shared EFI stub code */ > +#include "../../../../drivers/firmware/efi/efi-stub-helper.c" > + > + > +static int is_linux_reserved_region(int memory_type) > +{ > + switch (memory_type) { > + case EfiRuntimeServicesCode: > + case EfiRuntimeServicesData: > + case EfiUnusableMemory: > + case EfiACPIReclaimMemory: > + case EfiACPIMemoryNVS: > + return 1; > + default: > + return 0; > + } > +} > + > + > +static int relocate_kernel(efi_system_table_t *sys_table, > + unsigned long *load_addr, unsigned long *load_size, > + unsigned long min_addr, unsigned long max_addr) > +{ > + /* Get current address of kernel. */ > + unsigned long cur_zimage_addr = *load_addr; > + unsigned long zimage_size = *load_size; > + unsigned long new_addr = 0; > + unsigned long nr_pages; > + > + efi_status_t status; > + > + if (!load_addr || !load_size) > + return EFI_INVALID_PARAMETER; > + > + *load_size = 0; > + if (cur_zimage_addr > min_addr > + && (cur_zimage_addr + zimage_size) < max_addr) { > + /* We don't need to do anything, as kernel at an acceptable > + * address already. > + */ > + return EFI_SUCCESS; > + } > + /* > + * The EFI firmware loader could have placed the kernel image > + * anywhere in memory, but the kernel has restrictions on the > + * min and max physical address it can run at. > + */ > + nr_pages = round_up(zimage_size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; > + > + status = efi_low_alloc(sys_table, zimage_size, 0, > + &new_addr, min_addr); > + if (status != EFI_SUCCESS) { > + efi_printk(sys_table, "Failed to alloc memory for kernel.\n"); > + return status; > + } > + > + if (new_addr > (max_addr - zimage_size)) { > + efi_free(sys_table, zimage_size, new_addr); > + efi_printk(sys_table, "Failed to alloc usable memory for kernel.\n"); > + return EFI_INVALID_PARAMETER; > + } > + > + /* We know source/dest won't overlap since both memory ranges > + * have been allocated by UEFI, so we can safely use memcpy. > + */ > + memcpy((void *)new_addr, (void *)(unsigned long)cur_zimage_addr, > + zimage_size); > + > + /* Return the load address and size */ > + *load_addr = new_addr; > + *load_size = zimage_size; > + > + > + return status; > +} > + > + > +/* Convert the unicode UEFI command line to ASCII to pass to kernel. > + * Size of memory allocated return in *cmd_line_len. > + * Returns NULL on error. > + */ > +static char *convert_cmdline_to_ascii(efi_system_table_t *sys_table, > + efi_loaded_image_t *image, > + unsigned long *cmd_line_len, > + u32 max_addr) > +{ > + u16 *s2; > + u8 *s1 = NULL; > + unsigned long cmdline_addr = 0; > + int load_options_size = image->load_options_size / 2; /* ASCII */ > + void *options = (u16 *)image->load_options; > + int options_size = 0; > + int status; > + int i; > + u16 zero = 0; > + > + if (options) { > + s2 = options; > + while (*s2 && *s2 != '\n' && options_size < load_options_size) { > + s2++; > + options_size++; > + } > + } > + > + if (options_size == 0) { > + /* No command line options, so return empty string*/ > + options_size = 1; > + options = &zero; > + } > + > + if (options_size > MAX_CMDLINE_LEN) > + options_size = MAX_CMDLINE_LEN; > + > + options_size++; /* NUL termination */ > + > + status = efi_high_alloc(sys_table, options_size, 0, > + &cmdline_addr, max_addr); > + if (status != EFI_SUCCESS) > + return NULL; > + > + s1 = (u8 *)(unsigned long)cmdline_addr; > + s2 = (u16 *)options; > + > + for (i = 0; i < options_size - 1; i++) > + *s1++ = *s2++; > + > + *s1 = '\0'; > + > + *cmd_line_len = options_size; > + return (char *)(unsigned long)cmdline_addr; > +} > + > +static u32 update_fdt_and_exit_boot(efi_system_table_t *sys_table, > + void *handle, unsigned long dram_base, > + void *orig_fdt, u64 *orig_fdt_size, > + char *cmdline_ptr, > + unsigned long *cmdline_size, > + u64 initrd_addr, u64 initrd_size) > +{ > + unsigned long new_fdt_size; > + unsigned long new_fdt_addr; > + void *fdt; > + int node; > + int status; > + int i; > + unsigned long map_size, desc_size; > + unsigned long mmap_key; > + efi_memory_desc_t *memory_map; > + unsigned long fdt_val; > + > + new_fdt_size = *orig_fdt_size + FDT_ADDITIONAL_ENTRIES_SIZE; > + status = efi_high_alloc(sys_table, new_fdt_size, 0, &new_fdt_addr, > + dram_base + MAX_ZIMAGE_OFFSET); > + if (status != EFI_SUCCESS) { > + efi_printk(sys_table, "ERROR: Unable to allocate memory for new device tree.\n"); > + goto fail; > + } > + > + > + fdt = (void *)new_fdt_addr; > + status = fdt_open_into(orig_fdt, fdt, new_fdt_size); > + if (status != 0) { > + efi_printk(sys_table, "ERROR: Device Tree open_int failed.\n"); > + goto fail_free_new_fdt; > + } > + /* We are done with the original DTB, so free it. */ > + efi_free(sys_table, *orig_fdt_size, (u32)orig_fdt); > + *orig_fdt_size = 0; > + > + node = fdt_subnode_offset(fdt, 0, "chosen"); > + if (node < 0) { > + node = fdt_add_subnode(fdt, 0, "chosen"); > + if (node < 0) { > + efi_printk(sys_table, "Error on finding 'chosen' node\n"); > + goto fail_free_new_fdt; > + } > + } > + > + if ((cmdline_ptr != NULL) && (strlen(cmdline_ptr) > 0)) { > + status = fdt_setprop(fdt, node, "bootargs", cmdline_ptr, > + strlen(cmdline_ptr) + 1); > + if (status) { > + efi_printk(sys_table, "Failed to set new bootarg\n"); > + goto fail_free_new_fdt; > + } > + } > + /* We are done with original command line, so free it. */ > + efi_free(sys_table, *cmdline_size, (u32)cmdline_ptr); > + *cmdline_size = 0; > + > + /* Set intird address/end in device tree, if present */ > + if (initrd_size != 0) { > + u64 initrd_image_end; > + u64 initrd_image_start = cpu_to_fdt64(initrd_addr); > + status = fdt_setprop(fdt, node, "linux,initrd-start", > + &initrd_image_start, sizeof(u64)); > + if (status) { > + efi_printk(sys_table, "Failed to set new 'linux,initrd-start'\n"); > + goto fail_free_new_fdt; > + } > + initrd_image_end = cpu_to_fdt64(initrd_addr + initrd_size); > + status = fdt_setprop(fdt, node, "linux,initrd-end", > + &initrd_image_end, sizeof(u64)); > + if (status) { > + efi_printk(sys_table, "Failed to set new 'linux,initrd-end'\n"); > + goto fail_free_new_fdt; > + } > + } > + > + /* Update memory map in the device tree. The memory node must > + * be present in the tree.*/ > + node = fdt_subnode_offset(fdt, 0, "memory"); > + if (node < 0) { > + efi_printk(sys_table, "ERROR: FDT memory node does not exist in DTB.\n"); > + goto fail_free_new_fdt; > + } > + > + status = efi_get_memory_map(sys_table, &memory_map, &map_size, > + &desc_size, &mmap_key); > + if (status != EFI_SUCCESS) > + goto fail_free_new_fdt; > + > + for (i = 0; i < (map_size / sizeof(efi_memory_desc_t)); i++) { > + efi_memory_desc_t *desc; > + unsigned long m = (unsigned long)memory_map; > + desc = (efi_memory_desc_t *)(m + (i * desc_size)); > + > + if (is_linux_reserved_region(desc->type)) { > + status = fdt_add_mem_rsv(fdt, desc->phys_addr, > + desc->num_pages * EFI_PAGE_SIZE); > + if (status != 0) { > + efi_printk(sys_table, "ERROR: Failed to add 'memreserve' to fdt.\n"); > + goto fail_free_mmap; > + } > + } > + } > + > + > + /* Add FDT entries for EFI runtime services in chosen node. > + * We need to add the final memory map, so this is done at > + * the very end. > + */ > + node = fdt_subnode_offset(fdt, 0, "chosen"); > + fdt_val = cpu_to_fdt32((unsigned long)sys_table); > + status = fdt_setprop(fdt, node, "efi-system-table", > + &fdt_val, sizeof(fdt_val)); > + if (status) { > + efi_printk(sys_table, "Failed to set new 'efi-system-table'\n"); > + goto fail_free_new_fdt; > + } > + fdt_val = cpu_to_fdt32(desc_size); > + status = fdt_setprop(fdt, node, "efi-mmap-desc-size", > + &fdt_val, sizeof(fdt_val)); > + if (status) { > + efi_printk(sys_table, "Failed to set new 'efi-mmap-desc-size'\n"); > + goto fail_free_new_fdt; > + } > + fdt_val = cpu_to_fdt32(map_size); > + status = fdt_setprop(fdt, node, "efi-runtime-mmap-size", > + &fdt_val, sizeof(fdt_val)); > + if (status) { > + efi_printk(sys_table, "Failed to set new 'efi-runtime-mmap-size'\n"); > + goto fail_free_new_fdt; > + } > + fdt_val = cpu_to_fdt32((unsigned long)memory_map); > + status = fdt_setprop(fdt, node, "efi-runtime-mmap", > + &fdt_val, sizeof(fdt_val)); > + if (status) { > + efi_printk(sys_table, "Failed to set new 'efi-runtime-mmap'\n"); > + goto fail_free_new_fdt; > + } > + > + /* Now we need to exit boot services. We need the key from > + * the most recent read of the memory map to do this. We can't > + * free this buffer in the normal case, but do free it when > + * exit_boot_services() fails or adding the memory map to the FDT > + * fails. > + */ > + status = efi_call_phys2(sys_table->boottime->exit_boot_services, > + handle, mmap_key); > + > + if (status != EFI_SUCCESS) { > + efi_printk(sys_table, "exit boot services failed.\n"); > + goto fail_free_mmap; > + } > + > + return new_fdt_addr; > + > +fail_free_mmap: > + efi_call_phys1(sys_table->boottime->free_pool, memory_map); > + > +fail_free_new_fdt: > + efi_free(sys_table, new_fdt_size, new_fdt_addr); > + > +fail: > + return 0; > +} > + > + > +int efi_entry(void *handle, efi_system_table_t *sys_table, > + unsigned long *zimage_addr) > +{ > + efi_loaded_image_t *image; > + int status; > + unsigned long nr_pages; > + const struct fdt_region *region; > + > + void *fdt; > + int err; > + int node; > + unsigned long zimage_size = 0; > + unsigned long dram_base; > + /* addr/point and size pairs for memory management*/ > + u64 initrd_addr; > + u64 initrd_size = 0; > + u64 fdt_addr; /* Original DTB */ > + u64 fdt_size = 0; > + u64 kernel_reserve_addr; > + u64 kernel_reserve_size = 0; > + char *cmdline_ptr; > + unsigned long cmdline_size = 0; > + unsigned long new_fdt_addr; > + > + efi_guid_t proto = LOADED_IMAGE_PROTOCOL_GUID; > + > + /* Check if we were booted by the EFI firmware */ > + if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) > + goto fail; > + > + efi_printk(sys_table, "Booting Linux using EFI stub.\n"); > + > + > + /* get the command line from EFI, using the LOADED_IMAGE protocol */ > + status = efi_call_phys3(sys_table->boottime->handle_protocol, > + handle, &proto, (void *)&image); > + if (status != EFI_SUCCESS) { > + efi_printk(sys_table, "Failed to get handle for LOADED_IMAGE_PROTOCOL\n"); > + goto fail; > + } > + > + /* We are going to copy this into device tree, so we don't care where in > + * memory it is. > + */ > + cmdline_ptr = convert_cmdline_to_ascii(sys_table, image, > + &cmdline_size, 0xFFFFFFFF); > + if (!cmdline_ptr) { > + efi_printk(sys_table, "ERROR converting command line to ascii.\n"); > + goto fail; > + } > + > + /* We first load the device tree, as we need to get the base address of > + * DRAM from the device tree. The zImage, device tree, and initrd > + * have address restrictions that are relative to the base of DRAM. > + */ > + status = handle_cmdline_files(sys_table, image, cmdline_ptr, "dtb=", > + 0xffffffff, &fdt_addr, &fdt_size); > + if (status != EFI_SUCCESS) { > + efi_printk(sys_table, "Error loading dtb blob\n"); > + goto fail_free_cmdline; > + } > + > + err = fdt_check_header((void *)(unsigned long)fdt_addr); > + if (err != 0) { > + efi_printk(sys_table, "ERROR: Device Tree header not valid\n"); > + goto fail_free_dtb; > + } > + if (fdt_totalsize((void *)(unsigned long)fdt_addr) > fdt_size) { > + efi_printk(sys_table, "ERROR: Incomplete device tree.\n"); > + goto fail_free_dtb; > + > + } > + > + > + /* Look up the base of DRAM from the device tree.*/ > + fdt = (void *)(u32)fdt_addr; > + node = fdt_subnode_offset(fdt, 0, "memory"); > + region = fdt_getprop(fdt, node, "reg", NULL); > + if (region) { > + dram_base = fdt64_to_cpu(region->base); > + } else { > + efi_printk(sys_table, "Error: no 'memory' node in device tree.\n"); > + goto fail_free_dtb; > + } > + > + /* Reserve memory for the uncompressed kernel image. */ > + kernel_reserve_addr = dram_base; > + kernel_reserve_size = MAX_UNCOMP_KERNEL_SIZE; > + nr_pages = round_up(kernel_reserve_size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; > + status = efi_call_phys4(sys_table->boottime->allocate_pages, > + EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA, > + nr_pages, &kernel_reserve_addr); > + if (status != EFI_SUCCESS) { > + efi_printk(sys_table, "ERROR allocating memory for uncompressed kernel.\n"); > + goto fail_free_dtb; > + } > + > + /* Relocate the zImage, if required. */ > + zimage_size = image->image_size; > + status = relocate_kernel(sys_table, zimage_addr, &zimage_size, > + dram_base + MIN_ZIMAGE_OFFSET, > + dram_base + MAX_ZIMAGE_OFFSET); > + if (status != EFI_SUCCESS) { > + efi_printk(sys_table, "Failed to relocate kernel\n"); > + goto fail_free_kernel_reserve; > + } > + > + status = handle_cmdline_files(sys_table, image, cmdline_ptr, "initrd=", > + dram_base + MAX_ZIMAGE_OFFSET, > + &initrd_addr, &initrd_size); > + if (status != EFI_SUCCESS) { > + efi_printk(sys_table, "Error loading initrd\n"); > + goto fail_free_zimage; > + } > + > + new_fdt_addr = update_fdt_and_exit_boot(sys_table, handle, > + dram_base, fdt, &fdt_size, > + cmdline_ptr, &cmdline_size, > + initrd_addr, initrd_size); > + > + if (new_fdt_addr == 0) { > + efi_printk(sys_table, "Error updating device tree and exiting boot services.\n"); > + goto fail_free_initrd; > + } > + > + > + /* Now we need to return the FDT address to the calling > + * assembly to this can be used as part of normal boot. > + */ > + return new_fdt_addr; > + > +fail_free_initrd: > + efi_free(sys_table, initrd_size, initrd_addr); > + > +fail_free_zimage: > + efi_free(sys_table, zimage_size, *zimage_addr); > + > +fail_free_kernel_reserve: > + efi_free(sys_table, kernel_reserve_addr, kernel_reserve_size); > + > +fail_free_dtb: > + efi_free(sys_table, fdt_size, fdt_addr); > + > +fail_free_cmdline: > + efi_free(sys_table, cmdline_size, (u32)cmdline_ptr); > + > +fail: > + return EFI_STUB_ERROR; > +} > diff --git a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S > index 75189f1..4c70b9e 100644 > --- a/arch/arm/boot/compressed/head.S > +++ b/arch/arm/boot/compressed/head.S > @@ -122,19 +122,106 @@ > .arm @ Always enter in ARM state > start: > .type start,#function > - .rept 7 > +#ifdef CONFIG_EFI_STUB > + @ Magic MSDOS signature for PE/COFF + ADD opcode > + .word 0x62805a4d What about BE32? In that case, the instruction is a coprocessor load, that loads from a random address to a coprocessor that almost certainly doesn't exist. This will probably fault. Since BE32 is only for older platforms (<v6) and this is not easily solvable, it might be sensible to make the EFI stub support depend on !CPU_ENDIAN_BE32. > +#else > + mov r0, r0 > +#endif > + .rept 5 You reduced the .rept count by 2, but only inserted one extra word, perhaps because of the extra, but buggy, insertion below. > mov r0, r0 > .endr > ARM( mov r0, r0 ) > ARM( b 1f ) > THUMB( adr r12, BSYM(1f) ) > THUMB( bx r12 ) Can't you just replace 1f with zimage_continue directly in the above lines, instead of the subsequent extra branch: > + THUMB( .thumb ) > +1: > + b zimage_continue This also avoids having two labels both called '1'. I believe the magic word is expected to be in a predictable offset, but the size of the preceding branch is unpredictable in Thumb (you could use b.w, or possibly remove the branch altogether, as explained above). > .word 0x016f2818 @ Magic numbers to help the loader > .word start @ absolute load/run zImage address > .word _edata @ zImage end address > + > +#ifdef CONFIG_EFI_STUB > + @ Portions of the MSDOS file header must be at offset > + @ 0x3c from the start of the file. All PE/COFF headers > + @ are kept contiguous for simplicity. > +#include "efi-header.S" > + > +efi_stub_entry: > + .text > + @ The EFI stub entry point is not at a fixed address, however > + @ this address must be set in the PE/COFF header. > + @ EFI entry point is in A32 mode, switch to T32 if configured. > + .arm > + ARM( mov r0, r0 ) > + ARM( b 1f ) Those above two instructions are effectively just no-op padding. Do you need them at all? > + THUMB( adr r12, BSYM(1f) ) > + THUMB( bx r12 ) > THUMB( .thumb ) > 1: > + @ Save lr on stack for possible return to EFI firmware. > + @ Don't care about fp, but need 64 bit alignment.... > + stmfd sp!, {fp, lr} > + > + @ Save args to EFI app across got fixup call > + stmfd sp!, {r0, r1} > + ldmfd sp!, {r0, r1} > + > + @ allocate space on stack for return of new entry point of > + @ zImage, as EFI stub may copy the kernel. Pass address > + @ of space in r2 - EFI stub will fill in the pointer. > + > + sub sp, #8 @ we only need 4 bytes, > + @ but keep stack 8 byte aligned. > + mov r2, sp > + @ Pass our actual runtime start address in pointer data > + adr r11, LC0 @ address of LC0 at run time > + ldr r12, [r11, #0] @ address of LC0 at link time > + > + sub r3, r11, r12 @ calculate the delta offset > + str r3, [r2, #0] > + bl efi_entry > + > + @ get new zImage entry address from stack, put into r3 > + ldr r3, [sp, #0] > + add sp, #8 @ restore stack > + > + @ Check for error return from EFI stub (0xFFFFFFFF) > + ldr r1, =0xffffffff > + cmp r0, r1 > + beq efi_load_fail > + > + > + @ Save return values of efi_entry > + stmfd sp!, {r0, r3} > + bl cache_clean_flush > + bl cache_off > + ldmfd sp!, {r0, r3} > + > + @ put DTB address in r2, it was returned by EFI entry > + mov r2, r0 > + ldr r1, =0xffffffff @ DTB machine type > + mov r0, #0 @ r0 is 0 > + > + @ Branch to (possibly) relocated zImage entry that is in r3 > + bx r3 > + > +efi_load_fail: > + @ We need to exit THUMB mode here, to return to EFI firmware. If you lose these 4 lines: > + THUMB( adr r12, BSYM(1f) ) > + THUMB( bx r12 ) > +1: > + .arm ... > + @ Return EFI_LOAD_ERROR to EFI firmware on error. > + ldr r0, =0x80000001 and replace these > + ldmfd sp!, {fp, lr} > + mov pc, lr with: ldmfd sp!, {fp, pc} then the Thumb-ness on return will be determined by whatever is loaded into pc. There's no special need to switch back to ARM before the final return, provided that "mov pc,<Rm>" is not used (that will never switch from Thumb to ARM). Cheers ---Dave > + THUMB( .thumb ) Then you can also remove the above line. > +#endif > + > +zimage_continue: > mrs r9, cpsr > #ifdef CONFIG_ARM_VIRT_EXT > bl __hyp_stub_install @ get into SVC mode, reversibly > @@ -167,7 +254,6 @@ not_angel: > * by the linker here, but it should preserve r7, r8, and r9. > */ > > - .text > > #ifdef CONFIG_AUTO_ZRELADDR > @ determine final kernel image address > -- > 1.7.10.4 > > > _______________________________________________ > linux-arm-kernel mailing list > linux-arm-kernel@xxxxxxxxxxxxxxxxxxx > http://lists.infradead.org/mailman/listinfo/linux-arm-kernel -- To unsubscribe from this list: send the line "unsubscribe linux-efi" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html