1 /** @file
2 *
3 * Copyright (c) 2014, ARM Limited. All rights reserved.
4 *
5 * This program and the accompanying materials
6 * are licensed and made available under the terms and conditions of the BSD License
7 * which accompanies this distribution. The full text of the license may be found at
8 * http://opensource.org/licenses/bsd-license.php
9 *
10 * THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12 *
13 **/
14
15 #include <Library/BaseLib.h>
16 #include <Library/BaseMemoryLib.h>
17 #include <Library/MemoryAllocationLib.h>
18 #include <Library/DebugLib.h>
19 #include <Library/UefiLib.h>
20
21 #include "ArmShellCmdRunAxf.h"
22 #include "ElfLoader.h"
23 #include "elf_common.h"
24 #include "elf32.h"
25 #include "elf64.h"
26
27
28 // Put the functions the #ifdef. We only use the appropriate one for the platform.
29 // This prevents 'defined but not used' compiler warning.
30 #ifdef MDE_CPU_ARM
31 STATIC
32 BOOLEAN
IsArmElf(IN CONST VOID * Buf)33 IsArmElf (
34 IN CONST VOID *Buf
35 )
36 {
37 Elf32_Ehdr *Hdr = (Elf32_Ehdr*)Buf;
38
39 if (Hdr->e_ident[EI_CLASS] != ELFCLASS32) {
40 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_RUNAXF_ELFWRONGCLASS_32), gRunAxfHiiHandle);
41 return FALSE;
42 }
43
44 if (Hdr->e_machine != EM_ARM) {
45 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_RUNAXF_ELFWRONGMACH_32), gRunAxfHiiHandle);
46 return FALSE;
47 }
48
49 // We don't currently check endianness of ELF data (hdr->e_ident[EI_DATA])
50
51 return TRUE;
52 }
53 #elif defined(MDE_CPU_AARCH64)
54 STATIC
55 BOOLEAN
IsAarch64Elf(IN CONST VOID * Buf)56 IsAarch64Elf (
57 IN CONST VOID *Buf
58 )
59 {
60 Elf64_Ehdr *Hdr = (Elf64_Ehdr*)Buf;
61
62 if (Hdr->e_ident[EI_CLASS] != ELFCLASS64) {
63 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_RUNAXF_ELFWRONGCLASS_64), gRunAxfHiiHandle);
64 return FALSE;
65 }
66
67 if (Hdr->e_machine != EM_AARCH64) {
68 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_RUNAXF_ELFWRONGMACH_64), gRunAxfHiiHandle);
69 return FALSE;
70 }
71
72 // We don't currently check endianness of ELF data (hdr->e_ident[EI_DATA])
73
74 return TRUE;
75 }
76 #endif // MDE_CPU_ARM , MDE_CPU_AARCH64
77
78
79 /**
80 Support checking 32 and 64bit as the header could be valid, we might just
81 not support loading it.
82 **/
83 STATIC
84 EFI_STATUS
ElfCheckHeader(IN CONST VOID * Buf)85 ElfCheckHeader (
86 IN CONST VOID *Buf
87 )
88 {
89 Elf32_Ehdr *Hdr32 = (Elf32_Ehdr*)Buf;
90
91 if (!IS_ELF (*Hdr32)) {
92 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_RUNAXF_ELFMAGIC), gRunAxfHiiHandle);
93 return EFI_INVALID_PARAMETER;
94 }
95
96 if (Hdr32->e_type != ET_EXEC) {
97 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_RUNAXF_ELFNOTEXEC), gRunAxfHiiHandle);
98 return EFI_INVALID_PARAMETER;
99 }
100
101 if (Hdr32->e_ident[EI_CLASS] == ELFCLASS32) {
102 if ((Hdr32->e_phoff == 0) || (Hdr32->e_phentsize == 0) || (Hdr32->e_phnum == 0)) {
103 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_RUNAXF_ELFNOPROG), gRunAxfHiiHandle);
104 return EFI_INVALID_PARAMETER;
105 }
106
107 if (Hdr32->e_flags != 0) {
108 DEBUG ((EFI_D_INFO, "Warning: Wrong processor-specific flags, expected 0.\n"));
109 }
110
111 DEBUG ((EFI_D_INFO, "Entry point addr: 0x%lx\n", Hdr32->e_entry));
112 DEBUG ((EFI_D_INFO, "Start of program headers: 0x%lx\n", Hdr32->e_phoff));
113 DEBUG ((EFI_D_INFO, "Size of 1 program header: %d\n", Hdr32->e_phentsize));
114 DEBUG ((EFI_D_INFO, "Number of program headers: %d\n", Hdr32->e_phnum));
115 } else if (Hdr32->e_ident[EI_CLASS] == ELFCLASS64) {
116 Elf64_Ehdr *Hdr64 = (Elf64_Ehdr*)Buf;
117
118 if ((Hdr64->e_phoff == 0) || (Hdr64->e_phentsize == 0) || (Hdr64->e_phnum == 0)) {
119 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_RUNAXF_ELFNOPROG), gRunAxfHiiHandle);
120 return EFI_INVALID_PARAMETER;
121 }
122
123 if (Hdr64->e_flags != 0) {
124 DEBUG ((EFI_D_INFO, "Warning: Wrong processor-specific flags, expected 0.\n"));
125 }
126
127 DEBUG ((EFI_D_INFO, "Entry point addr: 0x%lx\n", Hdr64->e_entry));
128 DEBUG ((EFI_D_INFO, "Start of program headers: 0x%lx\n", Hdr64->e_phoff));
129 DEBUG ((EFI_D_INFO, "Size of 1 program header: %d\n", Hdr64->e_phentsize));
130 DEBUG ((EFI_D_INFO, "Number of program headers: %d\n", Hdr64->e_phnum));
131 } else {
132 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_RUNAXF_ELFWRONGCLASS), gRunAxfHiiHandle);
133 return EFI_INVALID_PARAMETER;
134 }
135
136 return EFI_SUCCESS;
137 }
138
139
140 /**
141 Load an ELF segment into memory.
142
143 This function assumes the ELF file is valid.
144 This function is meant to be called for PT_LOAD type segments only.
145 **/
146 STATIC
147 EFI_STATUS
ElfLoadSegment(IN CONST VOID * ElfImage,IN CONST VOID * PHdr,IN LIST_ENTRY * LoadList)148 ElfLoadSegment (
149 IN CONST VOID *ElfImage,
150 IN CONST VOID *PHdr,
151 IN LIST_ENTRY *LoadList
152 )
153 {
154 VOID *FileSegment;
155 VOID *MemSegment;
156 UINTN ExtraZeroes;
157 UINTN ExtraZeroesCount;
158 RUNAXF_LOAD_LIST *LoadNode;
159
160 #ifdef MDE_CPU_ARM
161 Elf32_Phdr *ProgramHdr;
162 ProgramHdr = (Elf32_Phdr *)PHdr;
163 #elif defined(MDE_CPU_AARCH64)
164 Elf64_Phdr *ProgramHdr;
165 ProgramHdr = (Elf64_Phdr *)PHdr;
166 #endif
167
168 ASSERT (ElfImage != NULL);
169 ASSERT (ProgramHdr != NULL);
170
171 FileSegment = (VOID *)((UINTN)ElfImage + ProgramHdr->p_offset);
172 MemSegment = (VOID *)ProgramHdr->p_vaddr;
173
174 // If the segment's memory size p_memsz is larger than the file size p_filesz,
175 // the "extra" bytes are defined to hold the value 0 and to follow the
176 // segment's initialised area.
177 // This is typically the case for the .bss segment.
178 // The file size may not be larger than the memory size.
179 if (ProgramHdr->p_filesz > ProgramHdr->p_memsz) {
180 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_RUNAXF_ELFBADFORMAT), gRunAxfHiiHandle);
181 return EFI_INVALID_PARAMETER;
182 }
183
184 // Load the segment in memory.
185 if (ProgramHdr->p_filesz != 0) {
186 DEBUG ((EFI_D_INFO, "Loading segment from 0x%lx to 0x%lx (size = %ld)\n",
187 FileSegment, MemSegment, ProgramHdr->p_filesz));
188
189 LoadNode = AllocateRuntimeZeroPool (sizeof (RUNAXF_LOAD_LIST));
190 if (LoadNode == NULL) {
191 return EFI_OUT_OF_RESOURCES;
192 }
193 LoadNode->MemOffset = (UINTN)MemSegment;
194 LoadNode->FileOffset = (UINTN)FileSegment;
195 LoadNode->Length = (UINTN)ProgramHdr->p_filesz;
196 InsertTailList (LoadList, &LoadNode->Link);
197 }
198
199 ExtraZeroes = ((UINTN)MemSegment + ProgramHdr->p_filesz);
200 ExtraZeroesCount = ProgramHdr->p_memsz - ProgramHdr->p_filesz;
201 DEBUG ((EFI_D_INFO, "Completing segment with %d zero bytes.\n", ExtraZeroesCount));
202 if (ExtraZeroesCount > 0) {
203 // Extra Node to add the Zeroes.
204 LoadNode = AllocateRuntimeZeroPool (sizeof (RUNAXF_LOAD_LIST));
205 if (LoadNode == NULL) {
206 return EFI_OUT_OF_RESOURCES;
207 }
208 LoadNode->MemOffset = (UINTN)ExtraZeroes;
209 LoadNode->Zeroes = TRUE;
210 LoadNode->Length = ExtraZeroesCount;
211 InsertTailList (LoadList, &LoadNode->Link);
212 }
213
214 return EFI_SUCCESS;
215 }
216
217
218 /**
219 Check that the ELF File Header is valid and Machine type supported.
220
221 Not all information is checked in the ELF header, only the stuff that
222 matters to us in our simplified ELF loader.
223
224 @param[in] ElfImage Address of the ELF file to check.
225
226 @retval EFI_SUCCESS on success.
227 @retval EFI_INVALID_PARAMETER if the header is invalid.
228 @retval EFI_UNSUPPORTED if the file type/platform is not supported.
229 **/
230 EFI_STATUS
ElfCheckFile(IN CONST VOID * ElfImage)231 ElfCheckFile (
232 IN CONST VOID *ElfImage
233 )
234 {
235 EFI_STATUS Status;
236
237 ASSERT (ElfImage != NULL);
238
239 // Check that the ELF header is valid.
240 Status = ElfCheckHeader (ElfImage);
241 if (EFI_ERROR(Status)) {
242 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_RUNAXF_ELFBADHEADER), gRunAxfHiiHandle);
243 return EFI_INVALID_PARAMETER;
244 }
245
246 #ifdef MDE_CPU_ARM
247 if (IsArmElf (ElfImage)) {
248 return EFI_SUCCESS;
249 }
250 #elif defined(MDE_CPU_AARCH64)
251 if (IsAarch64Elf (ElfImage)) {
252 return EFI_SUCCESS;
253 }
254 #endif
255
256 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_RUNAXF_BAD_ARCH), gRunAxfHiiHandle);
257 return EFI_UNSUPPORTED;
258 }
259
260
261 /**
262 Load a ELF file.
263
264 @param[in] ElfImage Address of the ELF file in memory.
265
266 @param[out] EntryPoint Will be filled with the ELF entry point address.
267
268 @param[out] ImageSize Will be filled with the ELF size in memory. This will
269 effectively be equal to the sum of the segments sizes.
270
271 This functon assumes the header is valid and supported as checked with
272 ElfCheckFile().
273
274 @retval EFI_SUCCESS on success.
275 @retval EFI_INVALID_PARAMETER if the ELF file is invalid.
276 **/
277 EFI_STATUS
ElfLoadFile(IN CONST VOID * ElfImage,OUT VOID ** EntryPoint,OUT LIST_ENTRY * LoadList)278 ElfLoadFile (
279 IN CONST VOID *ElfImage,
280 OUT VOID **EntryPoint,
281 OUT LIST_ENTRY *LoadList
282 )
283 {
284 EFI_STATUS Status;
285 UINT8 *ProgramHdr;
286 UINTN Index;
287 UINTN ImageSize;
288
289 #ifdef MDE_CPU_ARM
290 Elf32_Ehdr *ElfHdr;
291 Elf32_Phdr *ProgramHdrPtr;
292
293 ElfHdr = (Elf32_Ehdr*)ElfImage;
294 #elif defined(MDE_CPU_AARCH64)
295 Elf64_Ehdr *ElfHdr;
296 Elf64_Phdr *ProgramHdrPtr;
297
298 ElfHdr = (Elf64_Ehdr*)ElfImage;
299 #endif
300
301 ASSERT (ElfImage != NULL);
302 ASSERT (EntryPoint != NULL);
303 ASSERT (LoadList != NULL);
304
305 ProgramHdr = (UINT8*)ElfImage + ElfHdr->e_phoff;
306 DEBUG ((EFI_D_INFO, "ELF program header entry : 0x%lx\n", ProgramHdr));
307
308 ImageSize = 0;
309
310 // Load every loadable ELF segment into memory.
311 for (Index = 0; Index < ElfHdr->e_phnum; ++Index) {
312
313 #ifdef MDE_CPU_ARM
314 ProgramHdrPtr = (Elf32_Phdr*)ProgramHdr;
315 #elif defined(MDE_CPU_AARCH64)
316 ProgramHdrPtr = (Elf64_Phdr*)ProgramHdr;
317 #endif
318
319 // Only consider PT_LOAD type segments, ignore others.
320 if (ProgramHdrPtr->p_type == PT_LOAD) {
321 Status = ElfLoadSegment (ElfImage, (VOID *)ProgramHdrPtr, LoadList);
322 if (EFI_ERROR (Status)) {
323 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_RUNAXF_ELFFAILSEG), gRunAxfHiiHandle);
324 return EFI_INVALID_PARAMETER;
325 }
326 ImageSize += ProgramHdrPtr->p_memsz;
327 }
328 ProgramHdr += ElfHdr->e_phentsize;
329 }
330
331 if (ImageSize == 0) {
332 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_RUNAXF_ELFNOSEG), gRunAxfHiiHandle);
333 return EFI_INVALID_PARAMETER;
334 }
335
336 // Return the entry point specified in the ELF header.
337 *EntryPoint = (void*)ElfHdr->e_entry;
338
339 return EFI_SUCCESS;
340 }
341