Home | History | Annotate | Download | only in ArmShellCmdRunAxf
      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