Home | History | Annotate | Download | only in ArmShellCmdRunAxf
      1 /** @file
      2 *
      3 *  Shell command for launching AXF files.
      4 *
      5 *  Copyright (c) 2014, ARM Limited. All rights reserved.
      6 *
      7 *  This program and the accompanying materials
      8 *  are licensed and made available under the terms and conditions of the BSD License
      9 *  which accompanies this distribution.  The full text of the license may be found at
     10 *  http://opensource.org/licenses/bsd-license.php
     11 *
     12 *  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     13 *  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     14 *
     15 **/
     16 
     17 #include <Guid/GlobalVariable.h>
     18 
     19 #include <Library/PrintLib.h>
     20 #include <Library/HandleParsingLib.h>
     21 #include <Library/DevicePathLib.h>
     22 #include <Library/BaseLib.h>
     23 #include <Library/BaseMemoryLib.h>
     24 #include <Library/BdsLib.h>
     25 #include <Library/MemoryAllocationLib.h>
     26 #include <Library/DebugLib.h>
     27 
     28 #include <Library/ArmLib.h>
     29 
     30 #include "ArmShellCmdRunAxf.h"
     31 #include "ElfLoader.h"
     32 #include "BootMonFsLoader.h"
     33 
     34 // Provide arguments to AXF?
     35 typedef VOID (*ELF_ENTRYPOINT)(UINTN arg0, UINTN arg1,
     36                                UINTN arg2, UINTN arg3);
     37 
     38 
     39 STATIC
     40 EFI_STATUS
     41 PreparePlatformHardware (
     42   VOID
     43   )
     44 {
     45   //Note: Interrupts will be disabled by the GIC driver when ExitBootServices() will be called.
     46 
     47   // Clean before Disable else the Stack gets corrupted with old data.
     48   ArmCleanDataCache ();
     49   ArmDisableDataCache ();
     50   // Invalidate all the entries that might have snuck in.
     51   ArmInvalidateDataCache ();
     52 
     53   // Disable and invalidate the instruction cache
     54   ArmDisableInstructionCache ();
     55   ArmInvalidateInstructionCache ();
     56 
     57   // Turn off MMU
     58   ArmDisableMmu();
     59 
     60   return EFI_SUCCESS;
     61 }
     62 
     63 // Process arguments to pass to AXF?
     64 STATIC CONST SHELL_PARAM_ITEM ParamList[] = {
     65   {NULL, TypeMax}
     66 };
     67 
     68 
     69 /**
     70   This is the shell command handler function pointer callback type. This
     71   function handles the command when it is invoked in the shell.
     72 
     73   @param[in] This             The instance of the
     74                               EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL.
     75   @param[in] SystemTable      The pointer to the system table.
     76   @param[in] ShellParameters  The parameters associated with the command.
     77   @param[in] Shell            The instance of the shell protocol used in the
     78                               context of processing this command.
     79 
     80   @return EFI_SUCCESS         The operation was successful.
     81   @return other               The operation failed.
     82 **/
     83 SHELL_STATUS
     84 EFIAPI
     85 ShellDynCmdRunAxfHandler (
     86   IN EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL    *This,
     87   IN EFI_SYSTEM_TABLE                      *SystemTable,
     88   IN EFI_SHELL_PARAMETERS_PROTOCOL         *ShellParameters,
     89   IN EFI_SHELL_PROTOCOL                    *Shell
     90   )
     91 {
     92   LIST_ENTRY        *ParamPackage;
     93   EFI_STATUS         Status;
     94   SHELL_STATUS       ShellStatus;
     95   SHELL_FILE_HANDLE  FileHandle;
     96   ELF_ENTRYPOINT     StartElf;
     97   CONST CHAR16      *FileName;
     98   EFI_FILE_INFO     *Info;
     99   UINTN              FileSize;
    100   VOID              *FileData;
    101   VOID              *Entrypoint;
    102   LIST_ENTRY         LoadList;
    103   LIST_ENTRY        *Node;
    104   LIST_ENTRY        *NextNode;
    105   RUNAXF_LOAD_LIST  *LoadNode;
    106   CHAR16            *TmpFileName;
    107   CHAR16            *TmpChar16;
    108 
    109 
    110   ShellStatus = SHELL_SUCCESS;
    111   FileHandle = NULL;
    112   FileData = NULL;
    113   InitializeListHead (&LoadList);
    114 
    115   // Only install if they are not there yet? First time or every time?
    116   // These can change if the shell exits and start again.
    117   Status = gBS->InstallMultipleProtocolInterfaces (&gImageHandle,
    118                 &gEfiShellProtocolGuid, Shell,
    119                 &gEfiShellParametersProtocolGuid, ShellParameters,
    120                 NULL);
    121 
    122   if (EFI_ERROR (Status)) {
    123     return SHELL_DEVICE_ERROR;
    124   }
    125 
    126   // Update the protocols for the application library
    127   Status = ShellInitialize ();
    128   ASSERT_EFI_ERROR (Status);
    129   // Add support to load AXF with optipnal args?
    130 
    131   //
    132   // Process Command Line arguments
    133   //
    134   Status = ShellCommandLineParse (ParamList, &ParamPackage, NULL, TRUE);
    135   if (EFI_ERROR (Status)) {
    136     ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_RUNAXF_INVALID_ARG), gRunAxfHiiHandle);
    137     ShellStatus = SHELL_INVALID_PARAMETER;
    138   } else {
    139     //
    140     // Check for "-?"
    141     //
    142     if ((ShellCommandLineGetFlag (ParamPackage, L"-?")) ||
    143         (ShellCommandLineGetRawValue (ParamPackage, 1) == NULL)) {
    144       //
    145       // We didn't get a file to load
    146       //
    147       ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_RUNAXF_INVALID_ARG), gRunAxfHiiHandle);
    148       ShellStatus = SHELL_INVALID_PARAMETER;
    149     } else {
    150       // For the moment we assume we only ever get one file to load with no arguments.
    151       FileName = ShellCommandLineGetRawValue (ParamPackage, 1);
    152       Status = ShellOpenFileByName (FileName, &FileHandle, EFI_FILE_MODE_READ, 0);
    153       if (EFI_ERROR (Status)) {
    154         // BootMonFS supports file extensions, but they are stripped by default
    155         // when the NOR is programmed.
    156         // Remove the file extension and try to open again.
    157         ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_RUNAXF_FILE_NOT_FOUND),
    158                          gRunAxfHiiHandle, FileName);
    159         // Go through the filename and remove file extension. Preserve the
    160         // original name.
    161         TmpFileName = AllocateCopyPool (StrSize (FileName), (VOID *)FileName);
    162         if (TmpFileName != NULL) {
    163           TmpChar16 = StrStr (TmpFileName, L".");
    164           if (TmpChar16 != NULL) {
    165             *TmpChar16 = '\0';
    166             DEBUG((EFI_D_ERROR, "Trying to open file: %s\n", TmpFileName));
    167             Status = ShellOpenFileByName (TmpFileName, &FileHandle,
    168                                           EFI_FILE_MODE_READ, 0);
    169           }
    170           FreePool (TmpFileName);
    171         }
    172         // Do we now have an open file after trying again?
    173         if (EFI_ERROR (Status)) {
    174           ShellStatus = SHELL_INVALID_PARAMETER;
    175           FileHandle = NULL;
    176         }
    177       }
    178 
    179       if (FileHandle != NULL) {
    180         Info = ShellGetFileInfo (FileHandle);
    181         FileSize = (UINTN) Info->FileSize;
    182         FreePool (Info);
    183 
    184         //
    185         // Allocate buffer to read file. 'Runtime' so we can access it after
    186         // ExitBootServices().
    187         //
    188         FileData = AllocateRuntimeZeroPool (FileSize);
    189         if (FileData == NULL) {
    190           ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_RUNAXF_NO_MEM), gRunAxfHiiHandle);
    191           ShellStatus = SHELL_OUT_OF_RESOURCES;
    192         } else {
    193           //
    194           // Read file into Buffer
    195           //
    196           Status = ShellReadFile (FileHandle, &FileSize, FileData);
    197           if (EFI_ERROR (Status)) {
    198             ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_RUNAXF_READ_FAIL), gRunAxfHiiHandle);
    199             SHELL_FREE_NON_NULL (FileData);
    200             FileData = NULL;
    201             ShellStatus = SHELL_DEVICE_ERROR;
    202           }
    203         }
    204       }
    205     }
    206 
    207     //
    208     // Free the command line package
    209     //
    210     ShellCommandLineFreeVarList (ParamPackage);
    211   }
    212 
    213   // We have a file in memory. Try to work out if we can use it.
    214   // It can either be in ELF format or BootMonFS format.
    215   if (FileData != NULL) {
    216     // Do some validation on the file before we try to load it. The file can
    217     // either be an proper ELF file or one processed by the FlashLoader.
    218     // Since the data might need to go to various locations in memory we cannot
    219     // load the data directly while UEFI is running. We use the file loaders to
    220     // populate a linked list of data and load addresses. This is processed and
    221     // data copied to where it needs to go after calling ExitBootServices. At
    222     // that stage we've reached the point of no return, so overwriting UEFI code
    223     // does not make a difference.
    224     Status = ElfCheckFile (FileData);
    225     if (!EFI_ERROR (Status)) {
    226       // Load program into memory
    227       Status = ElfLoadFile ((VOID*)FileData, &Entrypoint, &LoadList);
    228     } else {
    229       // Try to see if it is a BootMonFs executable
    230       Status = BootMonFsCheckFile ((EFI_FILE_HANDLE)FileHandle);
    231       if (!EFI_ERROR (Status)) {
    232         // Load program into memory
    233         Status = BootMonFsLoadFile ((EFI_FILE_HANDLE)FileHandle,
    234                                     (VOID*)FileData, &Entrypoint, &LoadList);
    235       } else {
    236         ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_RUNAXF_BAD_FILE),
    237                          gRunAxfHiiHandle);
    238         SHELL_FREE_NON_NULL (FileData);
    239         ShellStatus = SHELL_UNSUPPORTED;
    240       }
    241     }
    242   }
    243 
    244   // Program load list created.
    245   // Shutdown UEFI, copy and jump to code.
    246   if (!IsListEmpty (&LoadList) && !EFI_ERROR (Status)) {
    247     // Exit boot services here. This means we cannot return and cannot assume to
    248     // have access to UEFI functions.
    249     Status = ShutdownUefiBootServices ();
    250     if (EFI_ERROR (Status)) {
    251       DEBUG ((EFI_D_ERROR,"Can not shutdown UEFI boot services. Status=0x%X\n",
    252               Status));
    253     } else {
    254       // Process linked list. Copy data to Memory.
    255       Node = GetFirstNode (&LoadList);
    256       while (!IsNull (&LoadList, Node)) {
    257         LoadNode = (RUNAXF_LOAD_LIST *)Node;
    258         // Do we have data to copy or do we need to set Zeroes (.bss)?
    259         if (LoadNode->Zeroes) {
    260           ZeroMem ((VOID*)LoadNode->MemOffset, LoadNode->Length);
    261         } else {
    262           CopyMem ((VOID *)LoadNode->MemOffset, (VOID *)LoadNode->FileOffset,
    263                    LoadNode->Length);
    264         }
    265         Node = GetNextNode (&LoadList, Node);
    266       }
    267 
    268       //
    269       // Switch off interrupts, caches, mmu, etc
    270       //
    271       Status = PreparePlatformHardware ();
    272       ASSERT_EFI_ERROR (Status);
    273 
    274       StartElf = (ELF_ENTRYPOINT)Entrypoint;
    275       StartElf (0,0,0,0);
    276 
    277       // We should never get here.. But if we do, spin..
    278       ASSERT (FALSE);
    279       while (1);
    280     }
    281   }
    282 
    283   // Free file related information as we are returning to UEFI.
    284   Node = GetFirstNode (&LoadList);
    285   while (!IsNull (&LoadList, Node)) {
    286     NextNode = RemoveEntryList (Node);
    287     FreePool (Node);
    288     Node = NextNode;
    289   }
    290   SHELL_FREE_NON_NULL (FileData);
    291   if (FileHandle != NULL) {
    292     ShellCloseFile (&FileHandle);
    293   }
    294 
    295   // Uninstall protocols as we don't know if they will change.
    296   // If the shell exits and come in again these mappings may be different
    297   // and cause a crash.
    298   Status = gBS->UninstallMultipleProtocolInterfaces (gImageHandle,
    299                 &gEfiShellProtocolGuid, Shell,
    300                 &gEfiShellParametersProtocolGuid, ShellParameters,
    301                 NULL);
    302 
    303   if (EFI_ERROR (Status) && ShellStatus == SHELL_SUCCESS) {
    304     ShellStatus = SHELL_DEVICE_ERROR;
    305   }
    306 
    307   return ShellStatus;
    308 }
    309 
    310 
    311 /**
    312   This is the command help handler function pointer callback type. This
    313   function is responsible for displaying help information for the associated
    314   command.
    315 
    316   @param[in] This             The instance of the
    317                               EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL.
    318   @param[in] Language         The pointer to the language string to use.
    319 
    320   @return string              Pool allocated help string, must be freed by
    321                               caller.
    322 **/
    323 CHAR16*
    324 EFIAPI
    325 ShellDynCmdRunAxfGetHelp (
    326   IN EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL    *This,
    327   IN CONST CHAR8                           *Language
    328   )
    329 {
    330   CHAR16 *HelpText;
    331 
    332   ASSERT (gRunAxfHiiHandle != NULL);
    333 
    334   // This allocates memory. The caller is responsoible to free.
    335   HelpText = HiiGetString (gRunAxfHiiHandle, STRING_TOKEN (STR_GET_HELP_RUNAXF),
    336                            Language);
    337 
    338   return HelpText;
    339 }
    340