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