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