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