summaryrefslogtreecommitdiff
path: root/ArmPlatformPkg/Library/ArmShellCmdRunAxf/ElfLoader.c
diff options
context:
space:
mode:
Diffstat (limited to 'ArmPlatformPkg/Library/ArmShellCmdRunAxf/ElfLoader.c')
-rw-r--r--ArmPlatformPkg/Library/ArmShellCmdRunAxf/ElfLoader.c340
1 files changed, 340 insertions, 0 deletions
diff --git a/ArmPlatformPkg/Library/ArmShellCmdRunAxf/ElfLoader.c b/ArmPlatformPkg/Library/ArmShellCmdRunAxf/ElfLoader.c
new file mode 100644
index 000000000..6bb0d22de
--- /dev/null
+++ b/ArmPlatformPkg/Library/ArmShellCmdRunAxf/ElfLoader.c
@@ -0,0 +1,340 @@
+/** @file
+*
+* Copyright (c) 2014, ARM Limited. All rights reserved.
+*
+* This program and the accompanying materials
+* are licensed and made available under the terms and conditions of the BSD License
+* which accompanies this distribution. The full text of the license may be found at
+* http://opensource.org/licenses/bsd-license.php
+*
+* THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+* WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+*
+**/
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/DebugLib.h>
+#include <Library/UefiLib.h>
+
+#include "ArmShellCmdRunAxf.h"
+#include "ElfLoader.h"
+#include "elf_common.h"
+#include "elf32.h"
+#include "elf64.h"
+
+
+// Put the functions the #ifdef. We only use the appropriate one for the platform.
+// This prevents 'defined but not used' compiler warning.
+#ifdef MDE_CPU_ARM
+STATIC
+BOOLEAN
+IsArmElf (
+ IN CONST VOID *Buf
+ )
+{
+ Elf32_Ehdr *Hdr = (Elf32_Ehdr*)Buf;
+
+ if (Hdr->e_ident[EI_CLASS] != ELFCLASS32) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_RUNAXF_ELFWRONGCLASS_32), gRunAxfHiiHandle);
+ return FALSE;
+ }
+
+ if (Hdr->e_machine != EM_ARM) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_RUNAXF_ELFWRONGMACH_32), gRunAxfHiiHandle);
+ return FALSE;
+ }
+
+ // We don't currently check endianness of ELF data (hdr->e_ident[EI_DATA])
+
+ return TRUE;
+}
+#elif defined(MDE_CPU_AARCH64)
+STATIC
+BOOLEAN
+IsAarch64Elf (
+ IN CONST VOID *Buf
+ )
+{
+ Elf64_Ehdr *Hdr = (Elf64_Ehdr*)Buf;
+
+ if (Hdr->e_ident[EI_CLASS] != ELFCLASS64) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_RUNAXF_ELFWRONGCLASS_64), gRunAxfHiiHandle);
+ return FALSE;
+ }
+
+ if (Hdr->e_machine != EM_AARCH64) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_RUNAXF_ELFWRONGMACH_64), gRunAxfHiiHandle);
+ return FALSE;
+ }
+
+ // We don't currently check endianness of ELF data (hdr->e_ident[EI_DATA])
+
+ return TRUE;
+}
+#endif // MDE_CPU_ARM , MDE_CPU_AARCH64
+
+
+/**
+ Support checking 32 and 64bit as the header could be valid, we might just
+ not support loading it.
+**/
+STATIC
+EFI_STATUS
+ElfCheckHeader (
+ IN CONST VOID *Buf
+ )
+{
+ Elf32_Ehdr *Hdr32 = (Elf32_Ehdr*)Buf;
+
+ if (!IS_ELF (*Hdr32)) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_RUNAXF_ELFMAGIC), gRunAxfHiiHandle);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Hdr32->e_type != ET_EXEC) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_RUNAXF_ELFNOTEXEC), gRunAxfHiiHandle);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Hdr32->e_ident[EI_CLASS] == ELFCLASS32) {
+ if ((Hdr32->e_phoff == 0) || (Hdr32->e_phentsize == 0) || (Hdr32->e_phnum == 0)) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_RUNAXF_ELFNOPROG), gRunAxfHiiHandle);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Hdr32->e_flags != 0) {
+ DEBUG ((EFI_D_INFO, "Warning: Wrong processor-specific flags, expected 0.\n"));
+ }
+
+ DEBUG ((EFI_D_INFO, "Entry point addr: 0x%lx\n", Hdr32->e_entry));
+ DEBUG ((EFI_D_INFO, "Start of program headers: 0x%lx\n", Hdr32->e_phoff));
+ DEBUG ((EFI_D_INFO, "Size of 1 program header: %d\n", Hdr32->e_phentsize));
+ DEBUG ((EFI_D_INFO, "Number of program headers: %d\n", Hdr32->e_phnum));
+ } else if (Hdr32->e_ident[EI_CLASS] == ELFCLASS64) {
+ Elf64_Ehdr *Hdr64 = (Elf64_Ehdr*)Buf;
+
+ if ((Hdr64->e_phoff == 0) || (Hdr64->e_phentsize == 0) || (Hdr64->e_phnum == 0)) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_RUNAXF_ELFNOPROG), gRunAxfHiiHandle);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Hdr64->e_flags != 0) {
+ DEBUG ((EFI_D_INFO, "Warning: Wrong processor-specific flags, expected 0.\n"));
+ }
+
+ DEBUG ((EFI_D_INFO, "Entry point addr: 0x%lx\n", Hdr64->e_entry));
+ DEBUG ((EFI_D_INFO, "Start of program headers: 0x%lx\n", Hdr64->e_phoff));
+ DEBUG ((EFI_D_INFO, "Size of 1 program header: %d\n", Hdr64->e_phentsize));
+ DEBUG ((EFI_D_INFO, "Number of program headers: %d\n", Hdr64->e_phnum));
+ } else {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_RUNAXF_ELFWRONGCLASS), gRunAxfHiiHandle);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Load an ELF segment into memory.
+
+ This function assumes the ELF file is valid.
+ This function is meant to be called for PT_LOAD type segments only.
+**/
+STATIC
+EFI_STATUS
+ElfLoadSegment (
+ IN CONST VOID *ElfImage,
+ IN CONST VOID *PHdr,
+ IN LIST_ENTRY *LoadList
+ )
+{
+ VOID *FileSegment;
+ VOID *MemSegment;
+ UINTN ExtraZeroes;
+ UINTN ExtraZeroesCount;
+ RUNAXF_LOAD_LIST *LoadNode;
+
+#ifdef MDE_CPU_ARM
+ Elf32_Phdr *ProgramHdr;
+ ProgramHdr = (Elf32_Phdr *)PHdr;
+#elif defined(MDE_CPU_AARCH64)
+ Elf64_Phdr *ProgramHdr;
+ ProgramHdr = (Elf64_Phdr *)PHdr;
+#endif
+
+ ASSERT (ElfImage != NULL);
+ ASSERT (ProgramHdr != NULL);
+
+ FileSegment = (VOID *)((UINTN)ElfImage + ProgramHdr->p_offset);
+ MemSegment = (VOID *)ProgramHdr->p_vaddr;
+
+ // If the segment's memory size p_memsz is larger than the file size p_filesz,
+ // the "extra" bytes are defined to hold the value 0 and to follow the
+ // segment's initialised area.
+ // This is typically the case for the .bss segment.
+ // The file size may not be larger than the memory size.
+ if (ProgramHdr->p_filesz > ProgramHdr->p_memsz) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_RUNAXF_ELFBADFORMAT), gRunAxfHiiHandle);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ // Load the segment in memory.
+ if (ProgramHdr->p_filesz != 0) {
+ DEBUG ((EFI_D_INFO, "Loading segment from 0x%lx to 0x%lx (size = %ld)\n",
+ FileSegment, MemSegment, ProgramHdr->p_filesz));
+
+ LoadNode = AllocateRuntimeZeroPool (sizeof (RUNAXF_LOAD_LIST));
+ if (LoadNode == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ LoadNode->MemOffset = (UINTN)MemSegment;
+ LoadNode->FileOffset = (UINTN)FileSegment;
+ LoadNode->Length = (UINTN)ProgramHdr->p_filesz;
+ InsertTailList (LoadList, &LoadNode->Link);
+ }
+
+ ExtraZeroes = ((UINTN)MemSegment + ProgramHdr->p_filesz);
+ ExtraZeroesCount = ProgramHdr->p_memsz - ProgramHdr->p_filesz;
+ DEBUG ((EFI_D_INFO, "Completing segment with %d zero bytes.\n", ExtraZeroesCount));
+ if (ExtraZeroesCount > 0) {
+ // Extra Node to add the Zeroes.
+ LoadNode = AllocateRuntimeZeroPool (sizeof (RUNAXF_LOAD_LIST));
+ if (LoadNode == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ LoadNode->MemOffset = (UINTN)ExtraZeroes;
+ LoadNode->Zeroes = TRUE;
+ LoadNode->Length = ExtraZeroesCount;
+ InsertTailList (LoadList, &LoadNode->Link);
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Check that the ELF File Header is valid and Machine type supported.
+
+ Not all information is checked in the ELF header, only the stuff that
+ matters to us in our simplified ELF loader.
+
+ @param[in] ElfImage Address of the ELF file to check.
+
+ @retval EFI_SUCCESS on success.
+ @retval EFI_INVALID_PARAMETER if the header is invalid.
+ @retval EFI_UNSUPPORTED if the file type/platform is not supported.
+**/
+EFI_STATUS
+ElfCheckFile (
+ IN CONST VOID *ElfImage
+ )
+{
+ EFI_STATUS Status;
+
+ ASSERT (ElfImage != NULL);
+
+ // Check that the ELF header is valid.
+ Status = ElfCheckHeader (ElfImage);
+ if (EFI_ERROR(Status)) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_RUNAXF_ELFBADHEADER), gRunAxfHiiHandle);
+ return EFI_INVALID_PARAMETER;
+ }
+
+#ifdef MDE_CPU_ARM
+ if (IsArmElf (ElfImage)) {
+ return EFI_SUCCESS;
+ }
+#elif defined(MDE_CPU_AARCH64)
+ if (IsAarch64Elf (ElfImage)) {
+ return EFI_SUCCESS;
+ }
+#endif
+
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_RUNAXF_BAD_ARCH), gRunAxfHiiHandle);
+ return EFI_UNSUPPORTED;
+}
+
+
+/**
+ Load a ELF file.
+
+ @param[in] ElfImage Address of the ELF file in memory.
+
+ @param[out] EntryPoint Will be filled with the ELF entry point address.
+
+ @param[out] ImageSize Will be filled with the ELF size in memory. This will
+ effectively be equal to the sum of the segments sizes.
+
+ This functon assumes the header is valid and supported as checked with
+ ElfCheckFile().
+
+ @retval EFI_SUCCESS on success.
+ @retval EFI_INVALID_PARAMETER if the ELF file is invalid.
+**/
+EFI_STATUS
+ElfLoadFile (
+ IN CONST VOID *ElfImage,
+ OUT VOID **EntryPoint,
+ OUT LIST_ENTRY *LoadList
+ )
+{
+ EFI_STATUS Status;
+ UINT8 *ProgramHdr;
+ UINTN Index;
+ UINTN ImageSize;
+
+#ifdef MDE_CPU_ARM
+ Elf32_Ehdr *ElfHdr;
+ Elf32_Phdr *ProgramHdrPtr;
+
+ ElfHdr = (Elf32_Ehdr*)ElfImage;
+#elif defined(MDE_CPU_AARCH64)
+ Elf64_Ehdr *ElfHdr;
+ Elf64_Phdr *ProgramHdrPtr;
+
+ ElfHdr = (Elf64_Ehdr*)ElfImage;
+#endif
+
+ ASSERT (ElfImage != NULL);
+ ASSERT (EntryPoint != NULL);
+ ASSERT (LoadList != NULL);
+
+ ProgramHdr = (UINT8*)ElfImage + ElfHdr->e_phoff;
+ DEBUG ((EFI_D_INFO, "ELF program header entry : 0x%lx\n", ProgramHdr));
+
+ ImageSize = 0;
+
+ // Load every loadable ELF segment into memory.
+ for (Index = 0; Index < ElfHdr->e_phnum; ++Index) {
+
+#ifdef MDE_CPU_ARM
+ ProgramHdrPtr = (Elf32_Phdr*)ProgramHdr;
+#elif defined(MDE_CPU_AARCH64)
+ ProgramHdrPtr = (Elf64_Phdr*)ProgramHdr;
+#endif
+
+ // Only consider PT_LOAD type segments, ignore others.
+ if (ProgramHdrPtr->p_type == PT_LOAD) {
+ Status = ElfLoadSegment (ElfImage, (VOID *)ProgramHdrPtr, LoadList);
+ if (EFI_ERROR (Status)) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_RUNAXF_ELFFAILSEG), gRunAxfHiiHandle);
+ return EFI_INVALID_PARAMETER;
+ }
+ ImageSize += ProgramHdrPtr->p_memsz;
+ }
+ ProgramHdr += ElfHdr->e_phentsize;
+ }
+
+ if (ImageSize == 0) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_RUNAXF_ELFNOSEG), gRunAxfHiiHandle);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ // Return the entry point specified in the ELF header.
+ *EntryPoint = (void*)ElfHdr->e_entry;
+
+ return EFI_SUCCESS;
+}