• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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