]> git.sur5r.net Git - u-boot/commitdiff
efi_loader: Add PE image loader
authorAlexander Graf <agraf@suse.de>
Fri, 4 Mar 2016 00:09:58 +0000 (01:09 +0100)
committerTom Rini <trini@konsulko.com>
Tue, 15 Mar 2016 19:19:23 +0000 (15:19 -0400)
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 <agraf@suse.de>
Reviewed-by: Simon Glass <sjg@chromium.org>
Tested-by: Simon Glass <sjg@chromium.org>
include/efi_loader.h [new file with mode: 0644]
include/pe.h [new file with mode: 0644]
lib/efi_loader/efi_image_loader.c [new file with mode: 0644]

diff --git a/include/efi_loader.h b/include/efi_loader.h
new file mode 100644 (file)
index 0000000..5618185
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ *  EFI application loader
+ *
+ *  Copyright (c) 2016 Alexander Graf
+ *
+ *  SPDX-License-Identifier:     GPL-2.0+
+ */
+
+#include <part_efi.h>
+#include <efi_api.h>
+#include <linux/list.h>
+
+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 (file)
index 0000000..deb35a0
--- /dev/null
@@ -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 (file)
index 0000000..d558f5a
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ *  EFI image loader
+ *
+ *  based partly on wine code
+ *
+ *  Copyright (c) 2016 Alexander Graf
+ *
+ *  SPDX-License-Identifier:     GPL-2.0+
+ */
+
+#include <common.h>
+#include <efi_loader.h>
+#include <pe.h>
+#include <asm/global_data.h>
+
+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 = &sections[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 = &sections[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;
+}