/** @file * * Shell command for launching AXF files. * * 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 <Guid/GlobalVariable.h> #include <Library/PrintLib.h> #include <Library/HandleParsingLib.h> #include <Library/DevicePathLib.h> #include <Library/BaseLib.h> #include <Library/BaseMemoryLib.h> #include <Library/BdsLib.h> #include <Library/MemoryAllocationLib.h> #include <Library/DebugLib.h> #include <Library/ArmLib.h> #include "ArmShellCmdRunAxf.h" #include "ElfLoader.h" #include "BootMonFsLoader.h" // Provide arguments to AXF? typedef VOID (*ELF_ENTRYPOINT)(UINTN arg0, UINTN arg1, UINTN arg2, UINTN arg3); STATIC EFI_STATUS PreparePlatformHardware ( VOID ) { //Note: Interrupts will be disabled by the GIC driver when ExitBootServices() will be called. // Clean before Disable else the Stack gets corrupted with old data. ArmCleanDataCache (); ArmDisableDataCache (); // Invalidate all the entries that might have snuck in. ArmInvalidateDataCache (); // Disable and invalidate the instruction cache ArmDisableInstructionCache (); ArmInvalidateInstructionCache (); // Turn off MMU ArmDisableMmu(); return EFI_SUCCESS; } // Process arguments to pass to AXF? STATIC CONST SHELL_PARAM_ITEM ParamList[] = { {NULL, TypeMax} }; /** This is the shell command handler function pointer callback type. This function handles the command when it is invoked in the shell. @param[in] This The instance of the EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL. @param[in] SystemTable The pointer to the system table. @param[in] ShellParameters The parameters associated with the command. @param[in] Shell The instance of the shell protocol used in the context of processing this command. @return EFI_SUCCESS The operation was successful. @return other The operation failed. **/ SHELL_STATUS EFIAPI ShellDynCmdRunAxfHandler ( IN EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *This, IN EFI_SYSTEM_TABLE *SystemTable, IN EFI_SHELL_PARAMETERS_PROTOCOL *ShellParameters, IN EFI_SHELL_PROTOCOL *Shell ) { LIST_ENTRY *ParamPackage; EFI_STATUS Status; SHELL_STATUS ShellStatus; SHELL_FILE_HANDLE FileHandle; ELF_ENTRYPOINT StartElf; CONST CHAR16 *FileName; EFI_FILE_INFO *Info; UINTN FileSize; VOID *FileData; VOID *Entrypoint; LIST_ENTRY LoadList; LIST_ENTRY *Node; LIST_ENTRY *NextNode; RUNAXF_LOAD_LIST *LoadNode; CHAR16 *TmpFileName; CHAR16 *TmpChar16; ShellStatus = SHELL_SUCCESS; FileHandle = NULL; FileData = NULL; InitializeListHead (&LoadList); // Only install if they are not there yet? First time or every time? // These can change if the shell exits and start again. Status = gBS->InstallMultipleProtocolInterfaces (&gImageHandle, &gEfiShellProtocolGuid, Shell, &gEfiShellParametersProtocolGuid, ShellParameters, NULL); if (EFI_ERROR (Status)) { return SHELL_DEVICE_ERROR; } // Update the protocols for the application library Status = ShellInitialize (); ASSERT_EFI_ERROR (Status); // Add support to load AXF with optipnal args? // // Process Command Line arguments // Status = ShellCommandLineParse (ParamList, &ParamPackage, NULL, TRUE); if (EFI_ERROR (Status)) { ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_RUNAXF_INVALID_ARG), gRunAxfHiiHandle); ShellStatus = SHELL_INVALID_PARAMETER; } else { // // Check for "-?" // if ((ShellCommandLineGetFlag (ParamPackage, L"-?")) || (ShellCommandLineGetRawValue (ParamPackage, 1) == NULL)) { // // We didn't get a file to load // ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_RUNAXF_INVALID_ARG), gRunAxfHiiHandle); ShellStatus = SHELL_INVALID_PARAMETER; } else { // For the moment we assume we only ever get one file to load with no arguments. FileName = ShellCommandLineGetRawValue (ParamPackage, 1); Status = ShellOpenFileByName (FileName, &FileHandle, EFI_FILE_MODE_READ, 0); if (EFI_ERROR (Status)) { // BootMonFS supports file extensions, but they are stripped by default // when the NOR is programmed. // Remove the file extension and try to open again. ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_RUNAXF_FILE_NOT_FOUND), gRunAxfHiiHandle, FileName); // Go through the filename and remove file extension. Preserve the // original name. TmpFileName = AllocateCopyPool (StrSize (FileName), (VOID *)FileName); if (TmpFileName != NULL) { TmpChar16 = StrStr (TmpFileName, L"."); if (TmpChar16 != NULL) { *TmpChar16 = '\0'; DEBUG((EFI_D_ERROR, "Trying to open file: %s\n", TmpFileName)); Status = ShellOpenFileByName (TmpFileName, &FileHandle, EFI_FILE_MODE_READ, 0); } FreePool (TmpFileName); } // Do we now have an open file after trying again? if (EFI_ERROR (Status)) { ShellStatus = SHELL_INVALID_PARAMETER; FileHandle = NULL; } } if (FileHandle != NULL) { Info = ShellGetFileInfo (FileHandle); FileSize = (UINTN) Info->FileSize; FreePool (Info); // // Allocate buffer to read file. 'Runtime' so we can access it after // ExitBootServices(). // FileData = AllocateRuntimeZeroPool (FileSize); if (FileData == NULL) { ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_RUNAXF_NO_MEM), gRunAxfHiiHandle); ShellStatus = SHELL_OUT_OF_RESOURCES; } else { // // Read file into Buffer // Status = ShellReadFile (FileHandle, &FileSize, FileData); if (EFI_ERROR (Status)) { ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_RUNAXF_READ_FAIL), gRunAxfHiiHandle); SHELL_FREE_NON_NULL (FileData); FileData = NULL; ShellStatus = SHELL_DEVICE_ERROR; } } } } // // Free the command line package // ShellCommandLineFreeVarList (ParamPackage); } // We have a file in memory. Try to work out if we can use it. // It can either be in ELF format or BootMonFS format. if (FileData != NULL) { // Do some validation on the file before we try to load it. The file can // either be an proper ELF file or one processed by the FlashLoader. // Since the data might need to go to various locations in memory we cannot // load the data directly while UEFI is running. We use the file loaders to // populate a linked list of data and load addresses. This is processed and // data copied to where it needs to go after calling ExitBootServices. At // that stage we've reached the point of no return, so overwriting UEFI code // does not make a difference. Status = ElfCheckFile (FileData); if (!EFI_ERROR (Status)) { // Load program into memory Status = ElfLoadFile ((VOID*)FileData, &Entrypoint, &LoadList); } else { // Try to see if it is a BootMonFs executable Status = BootMonFsCheckFile ((EFI_FILE_HANDLE)FileHandle); if (!EFI_ERROR (Status)) { // Load program into memory Status = BootMonFsLoadFile ((EFI_FILE_HANDLE)FileHandle, (VOID*)FileData, &Entrypoint, &LoadList); } else { ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_RUNAXF_BAD_FILE), gRunAxfHiiHandle); SHELL_FREE_NON_NULL (FileData); ShellStatus = SHELL_UNSUPPORTED; } } } // Program load list created. // Shutdown UEFI, copy and jump to code. if (!IsListEmpty (&LoadList) && !EFI_ERROR (Status)) { // Exit boot services here. This means we cannot return and cannot assume to // have access to UEFI functions. Status = ShutdownUefiBootServices (); if (EFI_ERROR (Status)) { DEBUG ((EFI_D_ERROR,"Can not shutdown UEFI boot services. Status=0x%X\n", Status)); } else { // Process linked list. Copy data to Memory. Node = GetFirstNode (&LoadList); while (!IsNull (&LoadList, Node)) { LoadNode = (RUNAXF_LOAD_LIST *)Node; // Do we have data to copy or do we need to set Zeroes (.bss)? if (LoadNode->Zeroes) { ZeroMem ((VOID*)LoadNode->MemOffset, LoadNode->Length); } else { CopyMem ((VOID *)LoadNode->MemOffset, (VOID *)LoadNode->FileOffset, LoadNode->Length); } Node = GetNextNode (&LoadList, Node); } // // Switch off interrupts, caches, mmu, etc // Status = PreparePlatformHardware (); ASSERT_EFI_ERROR (Status); StartElf = (ELF_ENTRYPOINT)Entrypoint; StartElf (0,0,0,0); // We should never get here.. But if we do, spin.. ASSERT (FALSE); while (1); } } // Free file related information as we are returning to UEFI. Node = GetFirstNode (&LoadList); while (!IsNull (&LoadList, Node)) { NextNode = RemoveEntryList (Node); FreePool (Node); Node = NextNode; } SHELL_FREE_NON_NULL (FileData); if (FileHandle != NULL) { ShellCloseFile (&FileHandle); } // Uninstall protocols as we don't know if they will change. // If the shell exits and come in again these mappings may be different // and cause a crash. Status = gBS->UninstallMultipleProtocolInterfaces (gImageHandle, &gEfiShellProtocolGuid, Shell, &gEfiShellParametersProtocolGuid, ShellParameters, NULL); if (EFI_ERROR (Status) && ShellStatus == SHELL_SUCCESS) { ShellStatus = SHELL_DEVICE_ERROR; } return ShellStatus; } /** This is the command help handler function pointer callback type. This function is responsible for displaying help information for the associated command. @param[in] This The instance of the EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL. @param[in] Language The pointer to the language string to use. @return string Pool allocated help string, must be freed by caller. **/ CHAR16* EFIAPI ShellDynCmdRunAxfGetHelp ( IN EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *This, IN CONST CHAR8 *Language ) { CHAR16 *HelpText; ASSERT (gRunAxfHiiHandle != NULL); // This allocates memory. The caller is responsoible to free. HelpText = HiiGetString (gRunAxfHiiHandle, STRING_TOKEN (STR_GET_HELP_RUNAXF), Language); return HelpText; }