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 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 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 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 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 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 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