From mboxrd@z Thu Jan 1 00:00:00 1970 From: Alexander Graf Date: Mon, 1 Feb 2016 23:58:54 +0100 Subject: [U-Boot] [PATCH 03/14] efi_loader: Add PE image loader In-Reply-To: References: <1452834380-164453-1-git-send-email-agraf@suse.de> <1452834380-164453-4-git-send-email-agraf@suse.de> Message-ID: <56AFE32E.1000806@suse.de> List-Id: MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit To: u-boot@lists.denx.de On 01/31/2016 04:18 PM, Simon Glass wrote: > Hi Alexander, > > On 14 January 2016 at 22:06, Alexander Graf wrote: >> 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 >> >> --- >> >> v1 -> v2: >> >> - move memory allocation to separate patch >> - limit 32/64 to hosts that support it >> - check 32bit optional nt header magic >> - switch to GPL2+ >> --- >> include/efi_loader.h | 23 ++++ >> include/pe.h | 263 ++++++++++++++++++++++++++++++++++++++ >> lib/efi_loader/efi_image_loader.c | 178 ++++++++++++++++++++++++++ >> 3 files changed, 464 insertions(+) >> create mode 100644 include/efi_loader.h >> create mode 100644 include/pe.h >> create mode 100644 lib/efi_loader/efi_image_loader.c >> >> diff --git a/include/efi_loader.h b/include/efi_loader.h >> new file mode 100644 >> index 0000000..bf77573 >> --- /dev/null >> +++ b/include/efi_loader.h >> @@ -0,0 +1,23 @@ >> +/* >> + * EFI application loader >> + * >> + * Copyright (c) 2016 Alexander Graf >> + * >> + * SPDX-License-Identifier: GPL-2.0+ >> + */ >> + >> +#include >> +#include >> +#include >> + >> +extern const efi_guid_t efi_guid_device_path; >> +extern const efi_guid_t efi_guid_loaded_image; >> + >> +efi_status_t efi_return_handle(void *handle, >> + efi_guid_t *protocol, void **protocol_interface, >> + void *agent_handle, void *controller_handle, >> + uint32_t attributes); >> +void *efi_load_pe(void *efi, struct efi_loaded_image *loaded_image_info); >> + >> +#define EFI_LOADER_POOL_SIZE (128 * 1024 * 1024) >> +void *efi_loader_alloc(uint64_t len); >> diff --git a/include/pe.h b/include/pe.h >> new file mode 100644 >> index 0000000..6379ae1 >> --- /dev/null >> +++ b/include/pe.h >> @@ -0,0 +1,263 @@ >> +/* >> + * Portable Executable binary format structures >> + * >> + * Copyright (c) 2016 Alexander Graf >> + * >> + * Based on wine code >> + * >> + * SPDX-License-Identifier: GPL-2.0+ >> + */ >> + >> +#ifndef _PE_H >> +#define _PE_H >> + >> +typedef struct _IMAGE_DOS_HEADER { >> + uint16_t e_magic; /* 00: MZ Header signature */ >> + uint16_t e_cblp; /* 02: Bytes on last page of file */ >> + uint16_t e_cp; /* 04: Pages in file */ >> + uint16_t e_crlc; /* 06: Relocations */ >> + uint16_t e_cparhdr; /* 08: Size of header in paragraphs */ >> + uint16_t e_minalloc; /* 0a: Minimum extra paragraphs needed */ >> + uint16_t e_maxalloc; /* 0c: Maximum extra paragraphs needed */ >> + uint16_t e_ss; /* 0e: Initial (relative) SS value */ >> + uint16_t e_sp; /* 10: Initial SP value */ >> + uint16_t e_csum; /* 12: Checksum */ >> + uint16_t e_ip; /* 14: Initial IP value */ >> + uint16_t e_cs; /* 16: Initial (relative) CS value */ >> + uint16_t e_lfarlc; /* 18: File address of relocation table */ >> + uint16_t e_ovno; /* 1a: Overlay number */ >> + uint16_t e_res[4]; /* 1c: Reserved words */ >> + uint16_t e_oemid; /* 24: OEM identifier (for e_oeminfo) */ >> + uint16_t e_oeminfo; /* 26: OEM information; e_oemid specific */ >> + uint16_t e_res2[10]; /* 28: Reserved words */ >> + uint32_t e_lfanew; /* 3c: Offset to extended header */ >> +} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER; >> + >> +#define IMAGE_DOS_SIGNATURE 0x5A4D /* MZ */ >> +#define IMAGE_NT_SIGNATURE 0x00004550 /* PE00 */ >> + >> +#define IMAGE_FILE_MACHINE_ARM 0x01c0 >> +#define IMAGE_FILE_MACHINE_THUMB 0x01c2 >> +#define IMAGE_FILE_MACHINE_ARMNT 0x01c4 >> +#define IMAGE_FILE_MACHINE_AMD64 0x8664 >> +#define IMAGE_FILE_MACHINE_ARM64 0xaa64 >> +#define IMAGE_NT_OPTIONAL_HDR32_MAGIC 0x10b >> +#define IMAGE_NT_OPTIONAL_HDR64_MAGIC 0x20b >> +#define IMAGE_SUBSYSTEM_EFI_APPLICATION 10 >> + >> +typedef struct _IMAGE_FILE_HEADER { >> + uint16_t Machine; >> + uint16_t NumberOfSections; >> + uint32_t TimeDateStamp; >> + uint32_t PointerToSymbolTable; >> + uint32_t NumberOfSymbols; >> + uint16_t SizeOfOptionalHeader; >> + uint16_t Characteristics; >> +} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER; >> + >> +typedef struct _IMAGE_DATA_DIRECTORY { >> + uint32_t VirtualAddress; >> + uint32_t Size; >> +} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY; >> + >> +#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16 >> + >> +typedef struct _IMAGE_OPTIONAL_HEADER64 { >> + uint16_t Magic; /* 0x20b */ >> + uint8_t MajorLinkerVersion; >> + uint8_t MinorLinkerVersion; >> + uint32_t SizeOfCode; >> + uint32_t SizeOfInitializedData; >> + uint32_t SizeOfUninitializedData; >> + uint32_t AddressOfEntryPoint; >> + uint32_t BaseOfCode; >> + uint64_t ImageBase; >> + uint32_t SectionAlignment; >> + uint32_t FileAlignment; >> + uint16_t MajorOperatingSystemVersion; >> + uint16_t MinorOperatingSystemVersion; >> + uint16_t MajorImageVersion; >> + uint16_t MinorImageVersion; >> + uint16_t MajorSubsystemVersion; >> + uint16_t MinorSubsystemVersion; >> + uint32_t Win32VersionValue; >> + uint32_t SizeOfImage; >> + uint32_t SizeOfHeaders; >> + uint32_t CheckSum; >> + uint16_t Subsystem; >> + uint16_t DllCharacteristics; >> + uint64_t SizeOfStackReserve; >> + uint64_t SizeOfStackCommit; >> + uint64_t SizeOfHeapReserve; >> + uint64_t SizeOfHeapCommit; >> + uint32_t LoaderFlags; >> + uint32_t NumberOfRvaAndSizes; >> + IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; >> +} IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64; >> + >> +typedef struct _IMAGE_NT_HEADERS64 { >> + uint32_t Signature; >> + IMAGE_FILE_HEADER FileHeader; >> + IMAGE_OPTIONAL_HEADER64 OptionalHeader; >> +} IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64; >> + >> +typedef struct _IMAGE_OPTIONAL_HEADER { >> + >> + /* Standard fields */ >> + >> + uint16_t Magic; /* 0x10b or 0x107 */ /* 0x00 */ >> + uint8_t MajorLinkerVersion; >> + uint8_t MinorLinkerVersion; >> + uint32_t SizeOfCode; >> + uint32_t SizeOfInitializedData; >> + uint32_t SizeOfUninitializedData; >> + uint32_t AddressOfEntryPoint; /* 0x10 */ >> + uint32_t BaseOfCode; >> + uint32_t BaseOfData; >> + >> + /* NT additional fields */ >> + >> + uint32_t ImageBase; >> + uint32_t SectionAlignment; /* 0x20 */ >> + uint32_t FileAlignment; >> + uint16_t MajorOperatingSystemVersion; >> + uint16_t MinorOperatingSystemVersion; >> + uint16_t MajorImageVersion; >> + uint16_t MinorImageVersion; >> + uint16_t MajorSubsystemVersion; /* 0x30 */ >> + uint16_t MinorSubsystemVersion; >> + uint32_t Win32VersionValue; >> + uint32_t SizeOfImage; >> + uint32_t SizeOfHeaders; >> + uint32_t CheckSum; /* 0x40 */ >> + uint16_t Subsystem; >> + uint16_t DllCharacteristics; >> + uint32_t SizeOfStackReserve; >> + uint32_t SizeOfStackCommit; >> + uint32_t SizeOfHeapReserve; /* 0x50 */ >> + uint32_t SizeOfHeapCommit; >> + uint32_t LoaderFlags; >> + uint32_t NumberOfRvaAndSizes; >> + IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; /* 0x60 */ >> + /* 0xE0 */ >> +} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32; >> + >> +typedef struct _IMAGE_NT_HEADERS { >> + uint32_t Signature; /* "PE"\0\0 */ /* 0x00 */ >> + IMAGE_FILE_HEADER FileHeader; /* 0x04 */ >> + IMAGE_OPTIONAL_HEADER32 OptionalHeader; /* 0x18 */ >> +} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32; >> + >> +#define IMAGE_SIZEOF_SHORT_NAME 8 >> + >> +typedef struct _IMAGE_SECTION_HEADER { >> + uint8_t Name[IMAGE_SIZEOF_SHORT_NAME]; >> + union { >> + uint32_t PhysicalAddress; >> + uint32_t VirtualSize; >> + } Misc; >> + uint32_t VirtualAddress; >> + uint32_t SizeOfRawData; >> + uint32_t PointerToRawData; >> + uint32_t PointerToRelocations; >> + uint32_t PointerToLinenumbers; >> + uint16_t NumberOfRelocations; >> + uint16_t NumberOfLinenumbers; >> + uint32_t Characteristics; >> +} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER; >> + >> +#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 >> + >> +typedef struct _IMAGE_BASE_RELOCATION >> +{ >> + uint32_t VirtualAddress; >> + uint32_t SizeOfBlock; >> + /* WORD TypeOffset[1]; */ >> +} IMAGE_BASE_RELOCATION,*PIMAGE_BASE_RELOCATION; >> + >> +typedef struct _IMAGE_RELOCATION >> +{ >> + union { >> + uint32_t VirtualAddress; >> + uint32_t RelocCount; >> + } DUMMYUNIONNAME; >> + uint32_t SymbolTableIndex; >> + uint16_t Type; >> +} IMAGE_RELOCATION, *PIMAGE_RELOCATION; >> + >> +#define IMAGE_SIZEOF_RELOCATION 10 >> + >> +/* generic relocation types */ >> +#define IMAGE_REL_BASED_ABSOLUTE 0 >> +#define IMAGE_REL_BASED_HIGH 1 >> +#define IMAGE_REL_BASED_LOW 2 >> +#define IMAGE_REL_BASED_HIGHLOW 3 >> +#define IMAGE_REL_BASED_HIGHADJ 4 >> +#define IMAGE_REL_BASED_MIPS_JMPADDR 5 >> +#define IMAGE_REL_BASED_ARM_MOV32A 5 /* yes, 5 too */ >> +#define IMAGE_REL_BASED_ARM_MOV32 5 /* yes, 5 too */ >> +#define IMAGE_REL_BASED_SECTION 6 >> +#define IMAGE_REL_BASED_REL 7 >> +#define IMAGE_REL_BASED_ARM_MOV32T 7 /* yes, 7 too */ >> +#define IMAGE_REL_BASED_THUMB_MOV32 7 /* yes, 7 too */ >> +#define IMAGE_REL_BASED_MIPS_JMPADDR16 9 >> +#define IMAGE_REL_BASED_IA64_IMM64 9 /* yes, 9 too */ >> +#define IMAGE_REL_BASED_DIR64 10 >> +#define IMAGE_REL_BASED_HIGH3ADJ 11 >> + >> +/* ARM relocation types */ >> +#define IMAGE_REL_ARM_ABSOLUTE 0x0000 >> +#define IMAGE_REL_ARM_ADDR 0x0001 >> +#define IMAGE_REL_ARM_ADDR32NB 0x0002 >> +#define IMAGE_REL_ARM_BRANCH24 0x0003 >> +#define IMAGE_REL_ARM_BRANCH11 0x0004 >> +#define IMAGE_REL_ARM_TOKEN 0x0005 >> +#define IMAGE_REL_ARM_GPREL12 0x0006 >> +#define IMAGE_REL_ARM_GPREL7 0x0007 >> +#define IMAGE_REL_ARM_BLX24 0x0008 >> +#define IMAGE_REL_ARM_BLX11 0x0009 >> +#define IMAGE_REL_ARM_SECTION 0x000E >> +#define IMAGE_REL_ARM_SECREL 0x000F >> +#define IMAGE_REL_ARM_MOV32A 0x0010 >> +#define IMAGE_REL_ARM_MOV32T 0x0011 >> +#define IMAGE_REL_ARM_BRANCH20T 0x0012 >> +#define IMAGE_REL_ARM_BRANCH24T 0x0014 >> +#define IMAGE_REL_ARM_BLX23T 0x0015 >> + >> +/* ARM64 relocation types */ >> +#define IMAGE_REL_ARM64_ABSOLUTE 0x0000 >> +#define IMAGE_REL_ARM64_ADDR32 0x0001 >> +#define IMAGE_REL_ARM64_ADDR32NB 0x0002 >> +#define IMAGE_REL_ARM64_BRANCH26 0x0003 >> +#define IMAGE_REL_ARM64_PAGEBASE_REL21 0x0004 >> +#define IMAGE_REL_ARM64_REL21 0x0005 >> +#define IMAGE_REL_ARM64_PAGEOFFSET_12A 0x0006 >> +#define IMAGE_REL_ARM64_PAGEOFFSET_12L 0x0007 >> +#define IMAGE_REL_ARM64_SECREL 0x0008 >> +#define IMAGE_REL_ARM64_SECREL_LOW12A 0x0009 >> +#define IMAGE_REL_ARM64_SECREL_HIGH12A 0x000A >> +#define IMAGE_REL_ARM64_SECREL_LOW12L 0x000B >> +#define IMAGE_REL_ARM64_TOKEN 0x000C >> +#define IMAGE_REL_ARM64_SECTION 0x000D >> +#define IMAGE_REL_ARM64_ADDR64 0x000E >> + >> +/* AMD64 relocation types */ >> +#define IMAGE_REL_AMD64_ABSOLUTE 0x0000 >> +#define IMAGE_REL_AMD64_ADDR64 0x0001 >> +#define IMAGE_REL_AMD64_ADDR32 0x0002 >> +#define IMAGE_REL_AMD64_ADDR32NB 0x0003 >> +#define IMAGE_REL_AMD64_REL32 0x0004 >> +#define IMAGE_REL_AMD64_REL32_1 0x0005 >> +#define IMAGE_REL_AMD64_REL32_2 0x0006 >> +#define IMAGE_REL_AMD64_REL32_3 0x0007 >> +#define IMAGE_REL_AMD64_REL32_4 0x0008 >> +#define IMAGE_REL_AMD64_REL32_5 0x0009 >> +#define IMAGE_REL_AMD64_SECTION 0x000A >> +#define IMAGE_REL_AMD64_SECREL 0x000B >> +#define IMAGE_REL_AMD64_SECREL7 0x000C >> +#define IMAGE_REL_AMD64_TOKEN 0x000D >> +#define IMAGE_REL_AMD64_SREL32 0x000E >> +#define IMAGE_REL_AMD64_PAIR 0x000F >> +#define IMAGE_REL_AMD64_SSPAN32 0x0010 >> + >> +#endif /* _PE_H */ >> diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c >> new file mode 100644 >> index 0000000..a7788bf >> --- /dev/null >> +++ b/lib/efi_loader/efi_image_loader.c >> @@ -0,0 +1,178 @@ >> +/* >> + * EFI image loader >> + * >> + * based partly on wine code >> + * >> + * Copyright (c) 2016 Alexander Graf >> + * >> + * SPDX-License-Identifier: GPL-2.0+ >> + */ >> + >> +#include >> +#include >> +#include > This should go above pe.h to keep alpha ordering. > >> +#include >> + >> +DECLARE_GLOBAL_DATA_PTR; >> + >> +#define ROUND_UP(val, round) ((val + (round - 1)) & ~(round - 1)) > Can you use round_up()? Turns out this just disappears with the new memory code :). > >> +#define MB (1024 * 1024) >> + >> +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 efi_return_handle(void *handle, efi_guid_t *protocol, >> + void **protocol_interface, void *agent_handle, >> + void *controller_handle, uint32_t attributes) >> +{ >> + *protocol_interface = handle; >> + return EFI_SUCCESS; >> +} >> + >> +/* Will be implemented in a later patch */ >> +void *efi_loader_alloc(uint64_t len) >> +{ >> + return NULL; >> +} >> + >> +/* >> + * 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 uint16_t *relocs; >> + const IMAGE_BASE_RELOCATION *rel; >> + const IMAGE_BASE_RELOCATION *end; >> + 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; > What does nt stand for? This is PE speech :). These headers probably got introduced back with Windows NT, so that's why they're called that way. > >> + >> +#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_loader_alloc(virt_size); >> + 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_loader_alloc(virt_size); >> + 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 */ >> + end = (const IMAGE_BASE_RELOCATION *)((const char *)rel + rel_size); >> + >> + while (rel < end - 1 && rel->SizeOfBlock) { >> + 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 >> 12; >> + 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; >> + } > How about putting the relocation code in a separate function, as this > one is getting large. Great idea. Alex