diff --git a/include/efi_loader.h b/include/efi_loader.h new file mode 100644 index 0000000000..5618185489 --- /dev/null +++ b/include/efi_loader.h @@ -0,0 +1,20 @@ +/* + * 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); diff --git a/include/pe.h b/include/pe.h new file mode 100644 index 0000000000..deb35a0ea4 --- /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 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; +}