From cb149c66342ce07b9fa45cb8d4d98081aab1afbb Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Fri, 4 Mar 2016 01:09:58 +0100 Subject: efi_loader: Add PE image loader EFI uses the PE binary format for its application images. Add support to EFI PE binaries as well as all necessary bits for the "EFI image loader" interfaces. Signed-off-by: Alexander Graf Reviewed-by: Simon Glass Tested-by: Simon Glass --- lib/efi_loader/efi_image_loader.c | 188 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 188 insertions(+) create mode 100644 lib/efi_loader/efi_image_loader.c (limited to 'lib') diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c new file mode 100644 index 0000000000..d558f5a8a9 --- /dev/null +++ b/lib/efi_loader/efi_image_loader.c @@ -0,0 +1,188 @@ +/* + * EFI image loader + * + * based partly on wine code + * + * Copyright (c) 2016 Alexander Graf + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +const efi_guid_t efi_guid_device_path = DEVICE_PATH_GUID; +const efi_guid_t efi_guid_loaded_image = LOADED_IMAGE_GUID; + +efi_status_t EFIAPI efi_return_handle(void *handle, efi_guid_t *protocol, + void **protocol_interface, void *agent_handle, + void *controller_handle, uint32_t attributes) +{ + EFI_ENTRY("%p, %p, %p, %p, %p, 0x%x", handle, protocol, + protocol_interface, agent_handle, controller_handle, + attributes); + *protocol_interface = handle; + return EFI_EXIT(EFI_SUCCESS); +} + +static void efi_loader_relocate(const IMAGE_BASE_RELOCATION *rel, + unsigned long rel_size, void *efi_reloc) +{ + const IMAGE_BASE_RELOCATION *end; + int i; + + end = (const IMAGE_BASE_RELOCATION *)((const char *)rel + rel_size); + while (rel < end - 1 && rel->SizeOfBlock) { + const uint16_t *relocs = (const uint16_t *)(rel + 1); + i = (rel->SizeOfBlock - sizeof(*rel)) / sizeof(uint16_t); + while (i--) { + uint16_t offset = (*relocs & 0xfff) + + rel->VirtualAddress; + int type = *relocs >> EFI_PAGE_SHIFT; + unsigned long delta = (unsigned long)efi_reloc; + uint64_t *x64 = efi_reloc + offset; + uint32_t *x32 = efi_reloc + offset; + uint16_t *x16 = efi_reloc + offset; + + switch (type) { + case IMAGE_REL_BASED_ABSOLUTE: + break; + case IMAGE_REL_BASED_HIGH: + *x16 += ((uint32_t)delta) >> 16; + break; + case IMAGE_REL_BASED_LOW: + *x16 += (uint16_t)delta; + break; + case IMAGE_REL_BASED_HIGHLOW: + *x32 += (uint32_t)delta; + break; + case IMAGE_REL_BASED_DIR64: + *x64 += (uint64_t)delta; + break; + default: + printf("Unknown Relocation off %x type %x\n", + offset, type); + } + relocs++; + } + rel = (const IMAGE_BASE_RELOCATION *)relocs; + } +} + +void __weak invalidate_icache_all(void) +{ + /* If the system doesn't support icache_all flush, cross our fingers */ +} + +/* + * This function loads all sections from a PE binary into a newly reserved + * piece of memory. On successful load it then returns the entry point for + * the binary. Otherwise NULL. + */ +void *efi_load_pe(void *efi, struct efi_loaded_image *loaded_image_info) +{ + IMAGE_NT_HEADERS32 *nt; + IMAGE_DOS_HEADER *dos; + IMAGE_SECTION_HEADER *sections; + int num_sections; + void *efi_reloc; + int i; + const IMAGE_BASE_RELOCATION *rel; + unsigned long rel_size; + int rel_idx = IMAGE_DIRECTORY_ENTRY_BASERELOC; + void *entry; + uint64_t image_size; + unsigned long virt_size = 0; + bool can_run_nt64 = true; + bool can_run_nt32 = true; + +#if defined(CONFIG_ARM64) + can_run_nt32 = false; +#elif defined(CONFIG_ARM) + can_run_nt64 = false; +#endif + + dos = efi; + if (dos->e_magic != IMAGE_DOS_SIGNATURE) { + printf("%s: Invalid DOS Signature\n", __func__); + return NULL; + } + + nt = (void *) ((char *)efi + dos->e_lfanew); + if (nt->Signature != IMAGE_NT_SIGNATURE) { + printf("%s: Invalid NT Signature\n", __func__); + return NULL; + } + + /* Calculate upper virtual address boundary */ + num_sections = nt->FileHeader.NumberOfSections; + sections = (void *)&nt->OptionalHeader + + nt->FileHeader.SizeOfOptionalHeader; + + for (i = num_sections - 1; i >= 0; i--) { + IMAGE_SECTION_HEADER *sec = §ions[i]; + virt_size = max_t(unsigned long, virt_size, + sec->VirtualAddress + sec->Misc.VirtualSize); + } + + /* Read 32/64bit specific header bits */ + if (can_run_nt64 && + (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC)) { + IMAGE_NT_HEADERS64 *nt64 = (void *)nt; + IMAGE_OPTIONAL_HEADER64 *opt = &nt64->OptionalHeader; + image_size = opt->SizeOfImage; + efi_reloc = efi_alloc(virt_size, EFI_LOADER_DATA); + if (!efi_reloc) { + printf("%s: Could not allocate %ld bytes\n", + __func__, virt_size); + return NULL; + } + entry = efi_reloc + opt->AddressOfEntryPoint; + rel_size = opt->DataDirectory[rel_idx].Size; + rel = efi_reloc + opt->DataDirectory[rel_idx].VirtualAddress; + } else if (can_run_nt32 && + (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC)) { + IMAGE_OPTIONAL_HEADER32 *opt = &nt->OptionalHeader; + image_size = opt->SizeOfImage; + efi_reloc = efi_alloc(virt_size, EFI_LOADER_DATA); + if (!efi_reloc) { + printf("%s: Could not allocate %ld bytes\n", + __func__, virt_size); + return NULL; + } + entry = efi_reloc + opt->AddressOfEntryPoint; + rel_size = opt->DataDirectory[rel_idx].Size; + rel = efi_reloc + opt->DataDirectory[rel_idx].VirtualAddress; + } else { + printf("%s: Invalid optional header magic %x\n", __func__, + nt->OptionalHeader.Magic); + return NULL; + } + + /* Load sections into RAM */ + for (i = num_sections - 1; i >= 0; i--) { + IMAGE_SECTION_HEADER *sec = §ions[i]; + memset(efi_reloc + sec->VirtualAddress, 0, + sec->Misc.VirtualSize); + memcpy(efi_reloc + sec->VirtualAddress, + efi + sec->PointerToRawData, + sec->SizeOfRawData); + } + + /* Run through relocations */ + efi_loader_relocate(rel, rel_size, efi_reloc); + + /* Flush cache */ + flush_cache((ulong)efi_reloc, virt_size); + invalidate_icache_all(); + + /* Populate the loaded image interface bits */ + loaded_image_info->image_base = efi; + loaded_image_info->image_size = image_size; + + return entry; +} -- cgit v1.2.1