uboot/lib/efi_loader/efi_image_loader.c
<<
>>
Prefs
   1/*
   2 *  EFI image loader
   3 *
   4 *  based partly on wine code
   5 *
   6 *  Copyright (c) 2016 Alexander Graf
   7 *
   8 *  SPDX-License-Identifier:     GPL-2.0+
   9 */
  10
  11#include <common.h>
  12#include <efi_loader.h>
  13#include <pe.h>
  14#include <asm/global_data.h>
  15
  16DECLARE_GLOBAL_DATA_PTR;
  17
  18const efi_guid_t efi_global_variable_guid = EFI_GLOBAL_VARIABLE_GUID;
  19const efi_guid_t efi_guid_device_path = DEVICE_PATH_GUID;
  20const efi_guid_t efi_guid_loaded_image = LOADED_IMAGE_GUID;
  21const efi_guid_t efi_simple_file_system_protocol_guid =
  22                EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
  23const efi_guid_t efi_file_info_guid = EFI_FILE_INFO_GUID;
  24
  25static efi_status_t efi_loader_relocate(const IMAGE_BASE_RELOCATION *rel,
  26                        unsigned long rel_size, void *efi_reloc)
  27{
  28        const IMAGE_BASE_RELOCATION *end;
  29        int i;
  30
  31        end = (const IMAGE_BASE_RELOCATION *)((const char *)rel + rel_size);
  32        while (rel < end - 1 && rel->SizeOfBlock) {
  33                const uint16_t *relocs = (const uint16_t *)(rel + 1);
  34                i = (rel->SizeOfBlock - sizeof(*rel)) / sizeof(uint16_t);
  35                while (i--) {
  36                        uint32_t offset = (uint32_t)(*relocs & 0xfff) +
  37                                          rel->VirtualAddress;
  38                        int type = *relocs >> EFI_PAGE_SHIFT;
  39                        unsigned long delta = (unsigned long)efi_reloc;
  40                        uint64_t *x64 = efi_reloc + offset;
  41                        uint32_t *x32 = efi_reloc + offset;
  42                        uint16_t *x16 = efi_reloc + offset;
  43
  44                        switch (type) {
  45                        case IMAGE_REL_BASED_ABSOLUTE:
  46                                break;
  47                        case IMAGE_REL_BASED_HIGH:
  48                                *x16 += ((uint32_t)delta) >> 16;
  49                                break;
  50                        case IMAGE_REL_BASED_LOW:
  51                                *x16 += (uint16_t)delta;
  52                                break;
  53                        case IMAGE_REL_BASED_HIGHLOW:
  54                                *x32 += (uint32_t)delta;
  55                                break;
  56                        case IMAGE_REL_BASED_DIR64:
  57                                *x64 += (uint64_t)delta;
  58                                break;
  59                        default:
  60                                printf("Unknown Relocation off %x type %x\n",
  61                                       offset, type);
  62                                return EFI_LOAD_ERROR;
  63                        }
  64                        relocs++;
  65                }
  66                rel = (const IMAGE_BASE_RELOCATION *)relocs;
  67        }
  68        return EFI_SUCCESS;
  69}
  70
  71void __weak invalidate_icache_all(void)
  72{
  73        /* If the system doesn't support icache_all flush, cross our fingers */
  74}
  75
  76/*
  77 * Determine the memory types to be used for code and data.
  78 *
  79 * @loaded_image_info   image descriptor
  80 * @image_type          field Subsystem of the optional header for
  81 *                      Windows specific field
  82 */
  83static void efi_set_code_and_data_type(
  84                        struct efi_loaded_image *loaded_image_info,
  85                        uint16_t image_type)
  86{
  87        switch (image_type) {
  88        case IMAGE_SUBSYSTEM_EFI_APPLICATION:
  89                loaded_image_info->image_code_type = EFI_LOADER_CODE;
  90                loaded_image_info->image_data_type = EFI_LOADER_DATA;
  91                break;
  92        case IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER:
  93                loaded_image_info->image_code_type = EFI_BOOT_SERVICES_CODE;
  94                loaded_image_info->image_data_type = EFI_BOOT_SERVICES_DATA;
  95                break;
  96        case IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER:
  97        case IMAGE_SUBSYSTEM_EFI_ROM:
  98                loaded_image_info->image_code_type = EFI_RUNTIME_SERVICES_CODE;
  99                loaded_image_info->image_data_type = EFI_RUNTIME_SERVICES_DATA;
 100                break;
 101        default:
 102                printf("%s: invalid image type: %u\n", __func__, image_type);
 103                /* Let's assume it is an application */
 104                loaded_image_info->image_code_type = EFI_LOADER_CODE;
 105                loaded_image_info->image_data_type = EFI_LOADER_DATA;
 106                break;
 107        }
 108}
 109
 110/*
 111 * This function loads all sections from a PE binary into a newly reserved
 112 * piece of memory. On successful load it then returns the entry point for
 113 * the binary. Otherwise NULL.
 114 */
 115void *efi_load_pe(void *efi, struct efi_loaded_image *loaded_image_info)
 116{
 117        IMAGE_NT_HEADERS32 *nt;
 118        IMAGE_DOS_HEADER *dos;
 119        IMAGE_SECTION_HEADER *sections;
 120        int num_sections;
 121        void *efi_reloc;
 122        int i;
 123        const IMAGE_BASE_RELOCATION *rel;
 124        unsigned long rel_size;
 125        int rel_idx = IMAGE_DIRECTORY_ENTRY_BASERELOC;
 126        void *entry;
 127        uint64_t image_size;
 128        unsigned long virt_size = 0;
 129        bool can_run_nt64 = true;
 130        bool can_run_nt32 = true;
 131
 132#if defined(CONFIG_ARM64)
 133        can_run_nt32 = false;
 134#elif defined(CONFIG_ARM)
 135        can_run_nt64 = false;
 136#endif
 137
 138        dos = efi;
 139        if (dos->e_magic != IMAGE_DOS_SIGNATURE) {
 140                printf("%s: Invalid DOS Signature\n", __func__);
 141                return NULL;
 142        }
 143
 144        nt = (void *) ((char *)efi + dos->e_lfanew);
 145        if (nt->Signature != IMAGE_NT_SIGNATURE) {
 146                printf("%s: Invalid NT Signature\n", __func__);
 147                return NULL;
 148        }
 149
 150        /* Calculate upper virtual address boundary */
 151        num_sections = nt->FileHeader.NumberOfSections;
 152        sections = (void *)&nt->OptionalHeader +
 153                            nt->FileHeader.SizeOfOptionalHeader;
 154
 155        for (i = num_sections - 1; i >= 0; i--) {
 156                IMAGE_SECTION_HEADER *sec = &sections[i];
 157                virt_size = max_t(unsigned long, virt_size,
 158                                  sec->VirtualAddress + sec->Misc.VirtualSize);
 159        }
 160
 161        /* Read 32/64bit specific header bits */
 162        if (can_run_nt64 &&
 163            (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC)) {
 164                IMAGE_NT_HEADERS64 *nt64 = (void *)nt;
 165                IMAGE_OPTIONAL_HEADER64 *opt = &nt64->OptionalHeader;
 166                image_size = opt->SizeOfImage;
 167                efi_set_code_and_data_type(loaded_image_info, opt->Subsystem);
 168                efi_reloc = efi_alloc(virt_size,
 169                                      loaded_image_info->image_code_type);
 170                if (!efi_reloc) {
 171                        printf("%s: Could not allocate %lu bytes\n",
 172                               __func__, virt_size);
 173                        return NULL;
 174                }
 175                entry = efi_reloc + opt->AddressOfEntryPoint;
 176                rel_size = opt->DataDirectory[rel_idx].Size;
 177                rel = efi_reloc + opt->DataDirectory[rel_idx].VirtualAddress;
 178        } else if (can_run_nt32 &&
 179                   (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC)) {
 180                IMAGE_OPTIONAL_HEADER32 *opt = &nt->OptionalHeader;
 181                image_size = opt->SizeOfImage;
 182                efi_set_code_and_data_type(loaded_image_info, opt->Subsystem);
 183                efi_reloc = efi_alloc(virt_size,
 184                                      loaded_image_info->image_code_type);
 185                if (!efi_reloc) {
 186                        printf("%s: Could not allocate %lu bytes\n",
 187                               __func__, virt_size);
 188                        return NULL;
 189                }
 190                entry = efi_reloc + opt->AddressOfEntryPoint;
 191                rel_size = opt->DataDirectory[rel_idx].Size;
 192                rel = efi_reloc + opt->DataDirectory[rel_idx].VirtualAddress;
 193        } else {
 194                printf("%s: Invalid optional header magic %x\n", __func__,
 195                       nt->OptionalHeader.Magic);
 196                return NULL;
 197        }
 198
 199        /* Load sections into RAM */
 200        for (i = num_sections - 1; i >= 0; i--) {
 201                IMAGE_SECTION_HEADER *sec = &sections[i];
 202                memset(efi_reloc + sec->VirtualAddress, 0,
 203                       sec->Misc.VirtualSize);
 204                memcpy(efi_reloc + sec->VirtualAddress,
 205                       efi + sec->PointerToRawData,
 206                       sec->SizeOfRawData);
 207        }
 208
 209        /* Run through relocations */
 210        if (efi_loader_relocate(rel, rel_size, efi_reloc) != EFI_SUCCESS) {
 211                efi_free_pages((uintptr_t) efi_reloc,
 212                               (virt_size + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT);
 213                return NULL;
 214        }
 215
 216        /* Flush cache */
 217        flush_cache((ulong)efi_reloc,
 218                    ALIGN(virt_size, CONFIG_SYS_CACHELINE_SIZE));
 219        invalidate_icache_all();
 220
 221        /* Populate the loaded image interface bits */
 222        loaded_image_info->image_base = efi;
 223        loaded_image_info->image_size = image_size;
 224
 225        return entry;
 226}
 227