From c55fa726d3e67da11a7ccd16ca367e9094265e4e Mon Sep 17 00:00:00 2001 From: Adrian Calianu Date: Mon, 16 Feb 2015 13:56:36 +0100 Subject: [PATCH 1/1] arm64: boot BE kernels from UEFI Adds support for booting BE kernels from UEFI. As UEFI is defined to be strictly little endian, some workarounds are required to combine a little endian EFI stub with a big endian kernel. Also, runtime services need to be wrapped so they can be executed in little endian mode. This patch is the resulting of porting on 3.19 kernel of a patch set ([RFC PATCH 00/10] arm64: boot BE kernels from UEFI) provided by Ard Biesheuvel ard.biesheuvel at linaro.org for 3.17 kernel. http://lists.infradead.org/pipermail/linux-arm-kernel/2014-July/274208.html Signed-off-by: Adrian Calianu --- arch/arm64/Kconfig | 10 ++- arch/arm64/include/asm/assembler.h | 18 +++++ arch/arm64/include/asm/efi.h | 2 + arch/arm64/kernel/Makefile | 7 +- arch/arm64/kernel/efi-be-call.S | 55 +++++++++++++++ arch/arm64/kernel/efi-be-runtime.c | 104 ++++++++++++++++++++++++++++ arch/arm64/kernel/efi-entry.S | 43 +++++++++--- arch/arm64/kernel/efi-stub.c | 10 +-- arch/arm64/kernel/efi.c | 64 ++++++++++------- arch/arm64/kernel/efistub-le/Makefile | 52 ++++++++++++++ arch/arm64/kernel/efistub-le/efi-le-entry.S | 12 ++++ arch/arm64/kernel/efistub-le/efistub-le.lds | 35 ++++++++++ arch/arm64/kernel/efistub-le/le.h | 12 ++++ arch/arm64/kernel/efistub-le/strstr.c | 20 ++++++ arch/arm64/kernel/head.S | 48 +++++++------ arch/arm64/kernel/image.h | 16 ++++- drivers/firmware/efi/efi.c | 26 ++++--- drivers/firmware/efi/efivars.c | 2 +- drivers/firmware/efi/libstub/fdt.c | 4 ++ 19 files changed, 459 insertions(+), 81 deletions(-) create mode 100644 arch/arm64/kernel/efi-be-call.S create mode 100644 arch/arm64/kernel/efi-be-runtime.c create mode 100644 arch/arm64/kernel/efistub-le/Makefile create mode 100644 arch/arm64/kernel/efistub-le/efi-le-entry.S create mode 100644 arch/arm64/kernel/efistub-le/efistub-le.lds create mode 100644 arch/arm64/kernel/efistub-le/le.h create mode 100644 arch/arm64/kernel/efistub-le/strstr.c diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 3f08727..d35a06c 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -573,16 +573,20 @@ config CMDLINE_FORCE config EFI_STUB bool +config EFI_LE_STUB + bool + config EFI bool "UEFI runtime support" - depends on OF && !CPU_BIG_ENDIAN + depends on OF select LIBFDT select UCS2_STRING select EFI_PARAMS_FROM_FDT select EFI_RUNTIME_WRAPPERS - select EFI_STUB + select EFI_STUB if !CPU_BIG_ENDIAN + select EFI_LE_STUB if CPU_BIG_ENDIAN select EFI_ARMSTUB - default y + default y if !CPU_BIG_ENDIAN help This option provides support for runtime services provided by UEFI firmware (such as non-volatile variables, realtime diff --git a/arch/arm64/include/asm/assembler.h b/arch/arm64/include/asm/assembler.h index 5901480..ad3aa92 100644 --- a/arch/arm64/include/asm/assembler.h +++ b/arch/arm64/include/asm/assembler.h @@ -155,3 +155,21 @@ lr .req x30 // link register #endif orr \rd, \lbits, \hbits, lsl #32 .endm + + /* + * Define LE constants + */ + .macro le16, x + .byte \x & 0xff + .byte (\x >> 8) & 0xff + .endm + + .macro le32, x + le16 \x + le16 \x >> 16 + .endm + + .macro le64, x + le32 \x + le32 \x >> 32 + .endm diff --git a/arch/arm64/include/asm/efi.h b/arch/arm64/include/asm/efi.h index a34fd3b..44e642b 100644 --- a/arch/arm64/include/asm/efi.h +++ b/arch/arm64/include/asm/efi.h @@ -44,4 +44,6 @@ extern void efi_idmap_init(void); #define efi_call_early(f, ...) sys_table_arg->boottime->f(__VA_ARGS__) +extern void efi_be_runtime_setup(void); + #endif /* _ASM_EFI_H */ diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index 79bdd3b..1ab3ff4 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -32,7 +32,12 @@ arm64-obj-$(CONFIG_ARM64_CPU_SUSPEND) += sleep.o suspend.o arm64-obj-$(CONFIG_CPU_IDLE) += cpuidle.o arm64-obj-$(CONFIG_JUMP_LABEL) += jump_label.o arm64-obj-$(CONFIG_KGDB) += kgdb.o -arm64-obj-$(CONFIG_EFI) += efi.o efi-stub.o efi-entry.o +arm64-efi-obj-y := efi.o +arm64-efi-obj-$(CONFIG_EFI_STUB) += efi-stub.o efi-entry.o +arm64-efi-obj-$(CONFIG_EFI_LE_STUB) += efistub-le/ +arm64-efi-obj-$(CONFIG_CPU_BIG_ENDIAN) += efi-be-runtime.o efi-be-call.o +arm64-obj-$(CONFIG_EFI) += $(arm64-efi-obj-y) + arm64-obj-$(CONFIG_PCI) += pci.o arm64-obj-$(CONFIG_ARMV8_DEPRECATED) += armv8_deprecated.o arm64-obj-$(CONFIG_ACPI) += acpi.o diff --git a/arch/arm64/kernel/efi-be-call.S b/arch/arm64/kernel/efi-be-call.S new file mode 100644 index 0000000..b395c8c --- /dev/null +++ b/arch/arm64/kernel/efi-be-call.S @@ -0,0 +1,55 @@ + +#include + + .text + .align 3 +ENTRY(efi_be_phys_call) + /* + * Entered at physical address with 1:1 mapping enabled. + */ + stp x29, x30, [sp, #-96]! + mov x29, sp + str x27, [sp, #16] + + ldr x8, =efi_be_phys_call // virt address of this function + adr x9, efi_be_phys_call // phys address of this function + sub x9, x8, x9 // calculate virt to phys offset in x9 + + /* preserve all inputs */ + stp x0, x1, [sp, #32] + stp x2, x3, [sp, #48] + stp x4, x5, [sp, #64] + stp x6, x7, [sp, #80] + + /* get phys address of stack */ + sub sp, sp, x9 + + /* switch to LE, disable MMU and D-cache but leave I-cache enabled */ + mrs x27, sctlr_el1 + bic x8, x27, #1 << 2 // clear SCTLR.C + msr sctlr_el1, x8 + + bl flush_cache_all + + /* restore inputs but rotated by 1 register */ + ldp x7, x0, [sp, #32] + ldp x1, x2, [sp, #48] + ldp x3, x4, [sp, #64] + ldp x5, x6, [sp, #80] + + bic x8, x27, #1 << 2 // clear SCTLR.C + bic x8, x8, #1 << 0 // clear SCTLR.M + bic x8, x8, #1 << 25 // clear SCTLR.EE + msr sctlr_el1, x8 + isb + + blr x7 + + /* switch back to BE and reenable MMU and D-cache */ + msr sctlr_el1, x27 + + mov sp, x29 + ldr x27, [sp, #16] + ldp x29, x30, [sp], #96 + ret +ENDPROC(efi_be_phys_call) diff --git a/arch/arm64/kernel/efi-be-runtime.c b/arch/arm64/kernel/efi-be-runtime.c new file mode 100644 index 0000000..28d7406 --- /dev/null +++ b/arch/arm64/kernel/efi-be-runtime.c @@ -0,0 +1,104 @@ + +#include +#include +#include +#include +#include + +static efi_runtime_services_t *runtime; +static efi_status_t (*efi_be_call)(phys_addr_t func, ...); + +static DEFINE_SPINLOCK(efi_be_rt_lock); + +static unsigned long efi_be_call_pre(void) +{ + unsigned long flags; + + kernel_neon_begin(); + spin_lock_irqsave(&efi_be_rt_lock, flags); + cpu_switch_mm(idmap_pg_dir, &init_mm); + flush_tlb_all(); + return flags; +} + +static void efi_be_call_post(unsigned long flags) +{ + cpu_switch_mm(current, current->active_mm); + flush_tlb_all(); + spin_unlock_irqrestore(&efi_be_rt_lock, flags); + kernel_neon_end(); +} + +static efi_status_t efi_be_get_variable(efi_char16_t *name, + efi_guid_t *vendor, + u32 *attr, + unsigned long *data_size, + void *data) +{ + unsigned long flags; + efi_status_t status; + + *data_size = cpu_to_le64(*data_size); + flags = efi_be_call_pre(); + status = efi_be_call(le64_to_cpu(runtime->get_variable), + virt_to_phys(name), virt_to_phys(vendor), + virt_to_phys(attr), virt_to_phys(data_size), + virt_to_phys(data)); + efi_be_call_post(flags); + *attr = le32_to_cpu(*attr); + *data_size = le64_to_cpu(*data_size); + return status; +} + +static efi_status_t efi_be_get_next_variable(unsigned long *name_size, + efi_char16_t *name, + efi_guid_t *vendor) +{ + unsigned long flags; + efi_status_t status; + + *name_size = cpu_to_le64(*name_size); + flags = efi_be_call_pre(); + status = efi_be_call(le64_to_cpu(runtime->get_next_variable), + virt_to_phys(name_size), virt_to_phys(name), + virt_to_phys(vendor)); + efi_be_call_post(flags); + *name_size = le64_to_cpu(*name_size); + return status; +} + +static efi_status_t efi_be_set_variable(efi_char16_t *name, + efi_guid_t *vendor, + u32 attr, + unsigned long data_size, + void *data) +{ + unsigned long flags; + efi_status_t status; + + flags = efi_be_call_pre(); + status = efi_be_call(le64_to_cpu(runtime->set_variable), + virt_to_phys(name), virt_to_phys(vendor), + cpu_to_le32(attr), cpu_to_le64(data_size), + virt_to_phys(data)); + efi_be_call_post(flags); + return status; +} + +void efi_be_runtime_setup(void) +{ + extern u8 efi_be_phys_call[]; + + runtime = ioremap_cache(le64_to_cpu(efi.systab->runtime), + sizeof(efi_runtime_services_t)); + if (!runtime) { + pr_err("Failed to set up BE wrappers for UEFI Runtime Services!\n"); + return; + } + + efi_be_call = (void *)virt_to_phys(efi_be_phys_call); + + efi.get_variable = efi_be_get_variable; + efi.get_next_variable = efi_be_get_next_variable; + efi.set_variable = efi_be_set_variable; +} diff --git a/arch/arm64/kernel/efi-entry.S b/arch/arm64/kernel/efi-entry.S index 8ce9b05..760fbb5 100644 --- a/arch/arm64/kernel/efi-entry.S +++ b/arch/arm64/kernel/efi-entry.S @@ -34,7 +34,34 @@ ENTRY(efi_stub_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]! + stp x29, x30, [sp, #-48]! + stp x22, x23, [sp, #32] + +#ifdef CONFIG_EFI_LE_STUB + adr x4, efi_stub_entry + ldp w8, w9, [x4, #-32] +STUB_BE(rev w8, w8 ) +STUB_BE(rev w9, w9 ) + add x8, x4, w8, sxtw // x8: base of Image + add x9, x4, w9, sxtw // x9: offset of linux_banner + + ldp x22, x23, [x4, #-24] // x22: size of Image +STUB_BE(rev x23, x23 ) // x23: stext offset + + /* + * Get a pointer to linux_banner in the outer image and store it + * in this image. + */ + adrp x4, le_linux_banner + str x9, [x4, #:lo12:le_linux_banner] +#else + adrp x8, _text + add x8, x8, #:lo12:_text // x8: base of Image + adrp x9, _edata + add x9, x9, #:lo12:_edata + sub x22, x9, x8 // x22: size of Image + ldr x23, =stext_offset // x23: stext offset +#endif /* * Call efi_entry to do the real work. @@ -45,8 +72,6 @@ ENTRY(efi_stub_entry) * efi_system_table_t *sys_table, * unsigned long *image_addr) ; */ - adrp x8, _text - add x8, x8, #:lo12:_text add x2, sp, 16 str x8, [x2] bl efi_entry @@ -61,17 +86,12 @@ ENTRY(efi_stub_entry) */ mov x20, x0 // DTB address ldr x0, [sp, #16] // relocated _text address - ldr x21, =stext_offset - add x21, x0, x21 + add x21, x0, x23 /* * Calculate size of the kernel Image (same for original and copy). */ - adrp x1, _text - add x1, x1, #:lo12:_text - adrp x2, _edata - add x2, x2, #:lo12:_edata - sub x1, x2, x1 + mov x1, x22 /* * Flush the copied Image to the PoC, and ensure it is not shadowed by @@ -117,7 +137,8 @@ ENTRY(efi_stub_entry) efi_load_fail: mov x0, #EFI_LOAD_ERROR - ldp x29, x30, [sp], #32 + ldp x22, x23, [sp, #32] + ldp x29, x30, [sp], #48 ret efi_stub_entry_end: diff --git a/arch/arm64/kernel/efi-stub.c b/arch/arm64/kernel/efi-stub.c index d27dd98..dd6d6bc 100644 --- a/arch/arm64/kernel/efi-stub.c +++ b/arch/arm64/kernel/efi-stub.c @@ -11,7 +11,6 @@ */ #include #include -#include efi_status_t handle_kernel_image(efi_system_table_t *sys_table, unsigned long *image_addr, @@ -22,22 +21,19 @@ efi_status_t handle_kernel_image(efi_system_table_t *sys_table, efi_loaded_image_t *image) { efi_status_t status; - unsigned long kernel_size, kernel_memsize = 0; /* Relocate the image, if required. */ - kernel_size = _edata - _text; if (*image_addr != (dram_base + TEXT_OFFSET)) { - kernel_memsize = kernel_size + (_end - _edata); - status = efi_low_alloc(sys_table, kernel_memsize + TEXT_OFFSET, + status = efi_low_alloc(sys_table, image->image_size + TEXT_OFFSET, SZ_2M, reserve_addr); if (status != EFI_SUCCESS) { pr_efi_err(sys_table, "Failed to relocate kernel\n"); return status; } memcpy((void *)*reserve_addr + TEXT_OFFSET, (void *)*image_addr, - kernel_size); + image->image_size); *image_addr = *reserve_addr + TEXT_OFFSET; - *reserve_size = kernel_memsize + TEXT_OFFSET; + *reserve_size = image->image_size + TEXT_OFFSET; } diff --git a/arch/arm64/kernel/efi.c b/arch/arm64/kernel/efi.c index 4178e9e..133c599 100644 --- a/arch/arm64/kernel/efi.c +++ b/arch/arm64/kernel/efi.c @@ -43,7 +43,7 @@ early_param("uefi_debug", uefi_debug_setup); static int __init is_normal_ram(efi_memory_desc_t *md) { - if (md->attribute & EFI_MEMORY_WB) + if (le64_to_cpu(md->attribute) & EFI_MEMORY_WB) return 1; return 0; } @@ -59,10 +59,10 @@ static void __init efi_setup_idmap(void) /* map runtime io spaces */ for_each_efi_memory_desc(&memmap, md) { - if (!(md->attribute & EFI_MEMORY_RUNTIME) || is_normal_ram(md)) + if (!(le64_to_cpu(md->attribute) & EFI_MEMORY_RUNTIME) || is_normal_ram(md)) continue; - paddr = md->phys_addr; - npages = md->num_pages; + paddr = le64_to_cpu(md->phys_addr); + npages = le64_to_cpu(md->num_pages); memrange_efi_to_native(&paddr, &npages); size = npages << PAGE_SHIFT; create_id_mapping(paddr, size, 1); @@ -88,29 +88,29 @@ static int __init uefi_init(void) /* * Verify the EFI Table */ - if (efi.systab->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) { + if (le64_to_cpu(efi.systab->hdr.signature) != EFI_SYSTEM_TABLE_SIGNATURE) { pr_err("System table signature incorrect\n"); retval = -EINVAL; goto out; } - if ((efi.systab->hdr.revision >> 16) < 2) + if ((le32_to_cpu(efi.systab->hdr.revision) >> 16) < 2) pr_warn("Warning: EFI system table version %d.%02d, expected 2.00 or greater\n", efi.systab->hdr.revision >> 16, efi.systab->hdr.revision & 0xffff); /* Show what we know for posterity */ - c16 = early_memremap(efi.systab->fw_vendor, + c16 = early_memremap(le64_to_cpu(efi.systab->fw_vendor), sizeof(vendor)); if (c16) { for (i = 0; i < (int) sizeof(vendor) - 1 && *c16; ++i) - vendor[i] = c16[i]; + vendor[i] = le16_to_cpu(c16[i]); vendor[i] = '\0'; early_memunmap(c16, sizeof(vendor)); } pr_info("EFI v%u.%.02u by %s\n", - efi.systab->hdr.revision >> 16, - efi.systab->hdr.revision & 0xffff, vendor); + le32_to_cpu(efi.systab->hdr.revision) >> 16, + le32_to_cpu(efi.systab->hdr.revision) & 0xffff, vendor); retval = efi_config_init(NULL); @@ -124,7 +124,7 @@ out: */ static __init int is_reserve_region(efi_memory_desc_t *md) { - switch (md->type) { + switch (le32_to_cpu(md->type)) { case EFI_LOADER_CODE: case EFI_LOADER_DATA: case EFI_BOOT_SERVICES_CODE: @@ -146,8 +146,9 @@ static __init void reserve_regions(void) pr_info("Processing EFI memory map:\n"); for_each_efi_memory_desc(&memmap, md) { - paddr = md->phys_addr; - npages = md->num_pages; + u32 md_type = le32_to_cpu(md->type); + paddr = le64_to_cpu(md->phys_addr); + npages = le64_to_cpu(md->num_pages); if (uefi_debug) { char buf[64]; @@ -164,8 +165,8 @@ static __init void reserve_regions(void) early_init_dt_add_memory_arch(paddr, size); if (is_reserve_region(md) || - md->type == EFI_BOOT_SERVICES_CODE || - md->type == EFI_BOOT_SERVICES_DATA) { + md_type == EFI_BOOT_SERVICES_CODE || + md_type == EFI_BOOT_SERVICES_DATA) { memblock_reserve(paddr, size); if (uefi_debug) pr_cont("*"); @@ -246,17 +247,17 @@ static void __init free_boot_services(void) */ if (free_start) { /* adjust free_end then free region */ - if (free_end > md->phys_addr) + if (free_end > le64_to_cpu(md->phys_addr)) free_end -= PAGE_SIZE; total_freed += free_region(free_start, free_end); free_start = 0; } - keep_end = md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT); + keep_end = le64_to_cpu(md->phys_addr) + (le64_to_cpu(md->num_pages) << EFI_PAGE_SHIFT); continue; } - if (md->type != EFI_BOOT_SERVICES_CODE && - md->type != EFI_BOOT_SERVICES_DATA) { + if (le32_to_cpu(md->type) != EFI_BOOT_SERVICES_CODE && + le32_to_cpu(md->type) != EFI_BOOT_SERVICES_DATA) { /* no need to free this region */ continue; } @@ -264,8 +265,8 @@ static void __init free_boot_services(void) /* * We want to free memory from this region. */ - paddr = md->phys_addr; - npages = md->num_pages; + paddr = le64_to_cpu(md->phys_addr); + npages = le64_to_cpu(md->num_pages); memrange_efi_to_native(&paddr, &npages); size = npages << PAGE_SHIFT; @@ -333,8 +334,8 @@ static int __init remap_region(efi_memory_desc_t *md, void **new) { u64 paddr, vaddr, npages, size; - paddr = md->phys_addr; - npages = md->num_pages; + paddr = le64_to_cpu(md->phys_addr); + npages = le64_to_cpu(md->num_pages); memrange_efi_to_native(&paddr, &npages); size = npages << PAGE_SHIFT; @@ -350,7 +351,7 @@ static int __init remap_region(efi_memory_desc_t *md, void **new) } /* adjust for any rounding when EFI and system pagesize differs */ - md->virt_addr = vaddr + (md->phys_addr - paddr); + md->virt_addr = vaddr + (le64_to_cpu(md->phys_addr) - paddr); if (uefi_debug) pr_info(" EFI remap 0x%012llx => %p\n", @@ -395,6 +396,21 @@ static int __init arm64_enter_virtual_mode(void) efi.memmap = &memmap; + if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) { + efi.systab = ioremap_cache(efi_system_table, + sizeof(efi_system_table_t)); + if (!efi.systab) { + pr_err("Failed to remap EFI system table!\n"); + return -1; + } + free_boot_services(); + efi_be_runtime_setup(); + + set_bit(EFI_SYSTEM_TABLES, &efi.flags); + set_bit(EFI_RUNTIME_SERVICES, &efi.flags); + return 0; + } + /* Map the runtime regions */ virtmap = kmalloc(mapsize, GFP_KERNEL); if (!virtmap) { diff --git a/arch/arm64/kernel/efistub-le/Makefile b/arch/arm64/kernel/efistub-le/Makefile new file mode 100644 index 0000000..8a1c2a8 --- /dev/null +++ b/arch/arm64/kernel/efistub-le/Makefile @@ -0,0 +1,52 @@ + +# +# Build a little endian EFI stub and wrap it into a single .o +# + +# the LE objects making up the LE efi stub +le-objs := efi-entry.o efi-stub.o strstr.o cache.o \ + lib-memchr.o lib-memcmp.o lib-memcpy.o lib-memmove.o \ + lib-memset.o lib-strchr.o lib-strlen.o lib-strncmp.o \ + fdt-fdt.o fdt-fdt_ro.o fdt-fdt_rw.o fdt-fdt_sw.o \ + fdt-fdt_wip.o fdt-fdt_empty_tree.o \ + libstub-fdt.o libstub-arm-stub.o libstub-efi-stub-helper.o + +extra-y := efi-le-stub.bin efi-le-stub.elf $(le-objs) + +KBUILD_CFLAGS := $(subst -pg,,$(KBUILD_CFLAGS)) -fno-stack-protector \ + -mlittle-endian -I$(srctree)/scripts/dtc/libfdt + +le-targets := $(addprefix $(obj)/, $(le-objs)) +$(le-targets): KBUILD_AFLAGS += -mlittle-endian -include $(srctree)/$(src)/le.h + +$(obj)/efi-entry.o: $(obj)/../efi-entry.S FORCE + $(call if_changed_dep,as_o_S) + +CFLAGS_efi-stub.o += -DTEXT_OFFSET=$(TEXT_OFFSET) +$(obj)/efi-stub.o: $(obj)/../efi-stub.c FORCE + $(call if_changed_dep,cc_o_c) + +$(obj)/cache.o: $(src)/../../mm/cache.S FORCE + $(call if_changed_dep,as_o_S) + +$(obj)/lib-%.o: $(src)/../../lib/%.S FORCE + $(call if_changed_dep,as_o_S) + +$(obj)/fdt-%.o: $(srctree)/lib/%.c FORCE + $(call if_changed_dep,cc_o_c) + +$(obj)/libstub-%.o: $(srctree)/drivers/firmware/efi/libstub/%.c FORCE + $(call if_changed_dep,cc_o_c) + +$(obj)/efi-le-stub.elf: LDFLAGS=-EL -Map $@.map -T +$(obj)/efi-le-stub.elf: $(src)/efistub-le.lds $(le-targets) FORCE + $(call if_changed,ld) + +$(obj)/efi-le-stub.bin: OBJCOPYFLAGS=-O binary +$(obj)/efi-le-stub.bin: $(obj)/efi-le-stub.elf FORCE + $(call if_changed,objcopy) + +# the BE object containing the entire LE stub +obj-y := efi-le-entry.o + +$(obj)/efi-le-entry.o: $(obj)/efi-le-stub.bin diff --git a/arch/arm64/kernel/efistub-le/efi-le-entry.S b/arch/arm64/kernel/efistub-le/efi-le-entry.S new file mode 100644 index 0000000..755364c --- /dev/null +++ b/arch/arm64/kernel/efistub-le/efi-le-entry.S @@ -0,0 +1,12 @@ +#include + + .text + .align 12 + .long _text - efi_stub_entry + .long linux_banner - efi_stub_entry + .quad _kernel_size_le + .quad stext_offset + .quad 0 +ENTRY(efi_stub_entry) + .incbin "arch/arm64/kernel/efistub-le/efi-le-stub.bin" +ENDPROC(efi_stub_entry) diff --git a/arch/arm64/kernel/efistub-le/efistub-le.lds b/arch/arm64/kernel/efistub-le/efistub-le.lds new file mode 100644 index 0000000..f64d542 --- /dev/null +++ b/arch/arm64/kernel/efistub-le/efistub-le.lds @@ -0,0 +1,35 @@ + +ENTRY(efi_stub_entry) + +SECTIONS { + /* + * The inner and outer alignment of this chunk of code need to be the + * same so that PC relative references using adrp/add or adrp/ldr pairs + * will work correctly. + * Skip 32 bytes here, so we can put the binary blob at an offset of + * 4k + 0x20 in the outer image, and use the gap to share constants + * emitted by the outer linker but required in the stub. + */ + .text 0x20 : { + arch/arm64/kernel/efistub-le/efi-entry.o(.init.text) + *(.init.text) + *(.text) + *(.text*) + } + .rodata : { + . = ALIGN(16); + *(.rodata) + *(.rodata*) + *(.init.rodata) + } + .data : { + . = ALIGN(16); + *(.data) + *(.data*) + le_linux_banner = .; + . += 8; + } + /DISCARD/ : { + *(__ex_table) + } +} diff --git a/arch/arm64/kernel/efistub-le/le.h b/arch/arm64/kernel/efistub-le/le.h new file mode 100644 index 0000000..a9f6dfc --- /dev/null +++ b/arch/arm64/kernel/efistub-le/le.h @@ -0,0 +1,12 @@ + +/* + * This is a bit of a hack, but it is necessary to correctly compile .S files + * that contain CPU_LE()/CPU_BE() statements, as these are defined to depend on + * CONFIG_ symbols and not on the endianness of the compiler. + */ +#ifdef CONFIG_CPU_BIG_ENDIAN +#define STUB_BE(code...) code +#else +#define STUB_BE(code...) +#endif +#undef CONFIG_CPU_BIG_ENDIAN diff --git a/arch/arm64/kernel/efistub-le/strstr.c b/arch/arm64/kernel/efistub-le/strstr.c new file mode 100644 index 0000000..bd16094 --- /dev/null +++ b/arch/arm64/kernel/efistub-le/strstr.c @@ -0,0 +1,20 @@ + +#include +#include + +char *strstr(const char *s1, const char *s2) +{ + size_t l1, l2; + + l2 = strlen(s2); + if (!l2) + return (char *)s1; + l1 = strlen(s1); + while (l1 >= l2) { + l1--; + if (!memcmp(s1, s2, l2)) + return (char *)s1; + s1++; + } + return NULL; +} diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S index 8ce88e0..8b1eba5 100644 --- a/arch/arm64/kernel/head.S +++ b/arch/arm64/kernel/head.S @@ -126,7 +126,10 @@ efi_head: .byte 0x4d .byte 0x64 #ifdef CONFIG_EFI - .long pe_header - efi_head // Offset to the PE header. + .byte pe_header - efi_head // Offset to the PE header. + .byte 0 + .byte 0 + .byte 0 #else .word 0 // reserved #endif @@ -139,30 +142,31 @@ pe_header: .ascii "PE" .short 0 coff_header: - .short 0xaa64 // AArch64 - .short 2 // nr_sections + le16 0xaa64 // AArch64 + le16 2 // nr_sections .long 0 // TimeDateStamp .long 0 // PointerToSymbolTable - .long 1 // NumberOfSymbols - .short section_table - optional_header // SizeOfOptionalHeader - .short 0x206 // Characteristics. + le32 1 // NumberOfSymbols + .byte section_table - optional_header // SizeOfOptionalHeader + .byte 0 + le16 0x206 // Characteristics. // IMAGE_FILE_DEBUG_STRIPPED | // IMAGE_FILE_EXECUTABLE_IMAGE | // IMAGE_FILE_LINE_NUMS_STRIPPED optional_header: - .short 0x20b // PE32+ format + le16 0x20b // PE32+ format .byte 0x02 // MajorLinkerVersion .byte 0x14 // MinorLinkerVersion - .long _end - stext // SizeOfCode + .long _efi_code_virtsize_le // SizeOfCode .long 0 // SizeOfInitializedData .long 0 // SizeOfUninitializedData - .long efi_stub_entry - efi_head // AddressOfEntryPoint - .long stext_offset // BaseOfCode + .long _efi_entry_point_le // AddressOfEntryPoint + .long _efi_stext_offset_le // BaseOfCode extra_header_fields: .quad 0 // ImageBase - .long 0x1000 // SectionAlignment - .long PECOFF_FILE_ALIGNMENT // FileAlignment + le32 0x1000 // SectionAlignment + le32 0x200 // FileAlignment .short 0 // MajorOperatingSystemVersion .short 0 // MinorOperatingSystemVersion .short 0 // MajorImageVersion @@ -171,19 +175,19 @@ extra_header_fields: .short 0 // MinorSubsystemVersion .long 0 // Win32VersionValue - .long _end - efi_head // SizeOfImage + .long _efi_image_size_le // SizeOfImage // Everything before the kernel image is considered part of the header - .long stext_offset // SizeOfHeaders + .long _efi_stext_offset_le // SizeOfHeaders .long 0 // CheckSum - .short 0xa // Subsystem (EFI application) + le16 0xa // Subsystem (EFI application) .short 0 // DllCharacteristics .quad 0 // SizeOfStackReserve .quad 0 // SizeOfStackCommit .quad 0 // SizeOfHeapReserve .quad 0 // SizeOfHeapCommit .long 0 // LoaderFlags - .long 0x6 // NumberOfRvaAndSizes + le32 0x6 // NumberOfRvaAndSizes .quad 0 // ExportTable .quad 0 // ImportTable @@ -211,23 +215,23 @@ section_table: .long 0 // PointerToLineNumbers .short 0 // NumberOfRelocations .short 0 // NumberOfLineNumbers - .long 0x42100040 // Characteristics (section flags) + le32 0x42100040 // Characteristics (section flags) .ascii ".text" .byte 0 .byte 0 .byte 0 // end of 0 padding of section name - .long _end - stext // VirtualSize - .long stext_offset // VirtualAddress - .long _edata - stext // SizeOfRawData - .long stext_offset // PointerToRawData + .long _efi_code_virtsize_le // VirtualSize + .long _efi_stext_offset_le // VirtualAddress + .long _efi_code_rawsize_le // SizeOfRawData + .long _efi_stext_offset_le // 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) + le32 0xe0500020 // Characteristics (section flags) /* * EFI will load stext onwards at the 4k section alignment diff --git a/arch/arm64/kernel/image.h b/arch/arm64/kernel/image.h index 8fae075..d08ce56 100644 --- a/arch/arm64/kernel/image.h +++ b/arch/arm64/kernel/image.h @@ -37,8 +37,10 @@ (((data) & 0x0000ff0000000000) >> 24) | \ (((data) & 0x00ff000000000000) >> 40) | \ (((data) & 0xff00000000000000) >> 56)) +#define DATA_LE32(data) (DATA_LE64(data) >> 32) #else #define DATA_LE64(data) ((data) & 0xffffffffffffffff) +#define DATA_LE32(data) ((data) & 0xffffffff) #endif #ifdef CONFIG_CPU_BIG_ENDIAN @@ -57,6 +59,18 @@ #define HEAD_SYMBOLS \ _kernel_size_le = DATA_LE64(_end - _text); \ _kernel_offset_le = DATA_LE64(TEXT_OFFSET); \ - _kernel_flags_le = DATA_LE64(__HEAD_FLAGS); + _kernel_flags_le = DATA_LE64(__HEAD_FLAGS); \ + EFI_HEAD_SYMBOLS + +#ifdef CONFIG_EFI +#define EFI_HEAD_SYMBOLS \ + _efi_stext_offset_le = DATA_LE32(stext_offset); \ + _efi_code_virtsize_le = DATA_LE32(_end - _text - stext_offset); \ + _efi_code_rawsize_le = DATA_LE32(_edata - _text - stext_offset); \ + _efi_image_size_le = DATA_LE32(_end - _text); \ + _efi_entry_point_le = DATA_LE32(efi_stub_entry - _text); +#else +#define EFI_HEAD_SYMBOLS +#endif #endif /* __ASM_IMAGE_H */ diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 9035c1b..4e98daa 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -296,18 +296,23 @@ static __init int match_config_table(efi_guid_t *guid, int __init efi_config_init(efi_config_table_type_t *arch_tables) { void *config_tables, *tablep; - int i, sz; + unsigned long __tables; + int i, sz, nr_tables; - if (efi_enabled(EFI_64BIT)) + if (efi_enabled(EFI_64BIT)) { sz = sizeof(efi_config_table_64_t); - else + nr_tables = le64_to_cpu((__force __le64)efi.systab->nr_tables); + __tables = le64_to_cpu((__force __le64)efi.systab->tables); + } else { sz = sizeof(efi_config_table_32_t); + nr_tables = le32_to_cpu((__force __le32)efi.systab->nr_tables); + __tables = le32_to_cpu((__force __le32)efi.systab->tables); + } /* * Let's see what config tables the firmware passed to us. */ - config_tables = early_memremap(efi.systab->tables, - efi.systab->nr_tables * sz); + config_tables = early_memremap(__tables, nr_tables * sz); if (config_tables == NULL) { pr_err("Could not map Configuration table!\n"); return -ENOMEM; @@ -315,21 +320,20 @@ int __init efi_config_init(efi_config_table_type_t *arch_tables) tablep = config_tables; pr_info(""); - for (i = 0; i < efi.systab->nr_tables; i++) { + for (i = 0; i < nr_tables; i++) { efi_guid_t guid; unsigned long table; if (efi_enabled(EFI_64BIT)) { u64 table64; guid = ((efi_config_table_64_t *)tablep)->guid; - table64 = ((efi_config_table_64_t *)tablep)->table; - table = table64; + table = table64 = le64_to_cpu((__force __le64) + ((efi_config_table_64_t *)tablep)->table); #ifndef CONFIG_64BIT if (table64 >> 32) { pr_cont("\n"); pr_err("Table located above 4GB, disabling EFI.\n"); - early_memunmap(config_tables, - efi.systab->nr_tables * sz); + early_memunmap(config_tables, nr_tables * sz); return -EINVAL; } #endif @@ -344,7 +348,7 @@ int __init efi_config_init(efi_config_table_type_t *arch_tables) tablep += sz; } pr_cont("\n"); - early_memunmap(config_tables, efi.systab->nr_tables * sz); + early_memunmap(config_tables, nr_tables * sz); set_bit(EFI_CONFIG_TABLES, &efi.flags); diff --git a/drivers/firmware/efi/efivars.c b/drivers/firmware/efi/efivars.c index f256ecd..2b1c8be 100644 --- a/drivers/firmware/efi/efivars.c +++ b/drivers/firmware/efi/efivars.c @@ -563,7 +563,7 @@ efivar_create_sysfs_entry(struct efivar_entry *new_var) /* Convert Unicode to normal chars (assume top bits are 0), ala UTF-8 */ for (i=0; i < (int)(variable_name_size / sizeof(efi_char16_t)); i++) { - short_name[i] = variable_name[i] & 0xFF; + short_name[i] = le16_to_cpu((__force __le16)variable_name[i]); } /* This is ugly, but necessary to separate one vendor's private variables from another's. */ diff --git a/drivers/firmware/efi/libstub/fdt.c b/drivers/firmware/efi/libstub/fdt.c index c846a96..f0f9d54 100644 --- a/drivers/firmware/efi/libstub/fdt.c +++ b/drivers/firmware/efi/libstub/fdt.c @@ -22,6 +22,10 @@ efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt, unsigned long map_size, unsigned long desc_size, u32 desc_ver) { +#ifdef CONFIG_EFI_LE_STUB + extern char const *le_linux_banner; + char const *linux_banner = le_linux_banner; +#endif int node, prev, num_rsv; int status; u32 fdt_val32; -- 1.9.1