3 * Shell command for launching AXF files.
5 * Copyright (c) 2014, ARM Limited. All rights reserved.
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
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.
17 #include <Guid/GlobalVariable.h>
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/MemoryAllocationLib.h>
25 #include <Library/DebugLib.h>
27 #include <Library/ArmLib.h>
29 #include "ArmShellCmdRunAxf.h"
30 #include "ElfLoader.h"
31 #include "BootMonFsLoader.h"
33 // Provide arguments to AXF?
34 typedef VOID (*ELF_ENTRYPOINT
)(UINTN arg0
, UINTN arg1
,
35 UINTN arg2
, UINTN arg3
);
39 ShutdownUefiBootServices (
45 EFI_MEMORY_DESCRIPTOR
*MemoryMap
;
48 UINT32 DescriptorVersion
;
56 Status
= gBS
->GetMemoryMap (
63 if (Status
== EFI_BUFFER_TOO_SMALL
) {
65 Pages
= EFI_SIZE_TO_PAGES (MemoryMapSize
) + 1;
66 MemoryMap
= AllocatePages (Pages
);
69 // Get System MemoryMap
71 Status
= gBS
->GetMemoryMap (
80 // Don't do anything between the GetMemoryMap() and ExitBootServices()
81 if (!EFI_ERROR(Status
)) {
82 Status
= gBS
->ExitBootServices (gImageHandle
, MapKey
);
83 if (EFI_ERROR(Status
)) {
84 FreePages (MemoryMap
, Pages
);
89 } while (EFI_ERROR(Status
));
97 PreparePlatformHardware (
101 //Note: Interrupts will be disabled by the GIC driver when ExitBootServices() will be called.
103 // Clean before Disable else the Stack gets corrupted with old data.
104 ArmCleanDataCache ();
105 ArmDisableDataCache ();
106 // Invalidate all the entries that might have snuck in.
107 ArmInvalidateDataCache ();
109 // Disable and invalidate the instruction cache
110 ArmDisableInstructionCache ();
111 ArmInvalidateInstructionCache ();
119 // Process arguments to pass to AXF?
120 STATIC CONST SHELL_PARAM_ITEM ParamList
[] = {
126 This is the shell command handler function pointer callback type. This
127 function handles the command when it is invoked in the shell.
129 @param[in] This The instance of the
130 EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL.
131 @param[in] SystemTable The pointer to the system table.
132 @param[in] ShellParameters The parameters associated with the command.
133 @param[in] Shell The instance of the shell protocol used in the
134 context of processing this command.
136 @return EFI_SUCCESS The operation was successful.
137 @return other The operation failed.
141 ShellDynCmdRunAxfHandler (
142 IN EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL
*This
,
143 IN EFI_SYSTEM_TABLE
*SystemTable
,
144 IN EFI_SHELL_PARAMETERS_PROTOCOL
*ShellParameters
,
145 IN EFI_SHELL_PROTOCOL
*Shell
148 LIST_ENTRY
*ParamPackage
;
150 SHELL_STATUS ShellStatus
;
151 SHELL_FILE_HANDLE FileHandle
;
152 ELF_ENTRYPOINT StartElf
;
153 CONST CHAR16
*FileName
;
160 LIST_ENTRY
*NextNode
;
161 RUNAXF_LOAD_LIST
*LoadNode
;
166 ShellStatus
= SHELL_SUCCESS
;
169 InitializeListHead (&LoadList
);
171 // Only install if they are not there yet? First time or every time?
172 // These can change if the shell exits and start again.
173 Status
= gBS
->InstallMultipleProtocolInterfaces (&gImageHandle
,
174 &gEfiShellProtocolGuid
, Shell
,
175 &gEfiShellParametersProtocolGuid
, ShellParameters
,
178 if (EFI_ERROR (Status
)) {
179 return SHELL_DEVICE_ERROR
;
182 // Update the protocols for the application library
183 Status
= ShellInitialize ();
184 ASSERT_EFI_ERROR (Status
);
185 // Add support to load AXF with optipnal args?
188 // Process Command Line arguments
190 Status
= ShellCommandLineParse (ParamList
, &ParamPackage
, NULL
, TRUE
);
191 if (EFI_ERROR (Status
)) {
192 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_RUNAXF_INVALID_ARG
), gRunAxfHiiHandle
);
193 ShellStatus
= SHELL_INVALID_PARAMETER
;
198 if ((ShellCommandLineGetFlag (ParamPackage
, L
"-?")) ||
199 (ShellCommandLineGetRawValue (ParamPackage
, 1) == NULL
)) {
201 // We didn't get a file to load
203 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_RUNAXF_INVALID_ARG
), gRunAxfHiiHandle
);
204 ShellStatus
= SHELL_INVALID_PARAMETER
;
206 // For the moment we assume we only ever get one file to load with no arguments.
207 FileName
= ShellCommandLineGetRawValue (ParamPackage
, 1);
208 Status
= ShellOpenFileByName (FileName
, &FileHandle
, EFI_FILE_MODE_READ
, 0);
209 if (EFI_ERROR (Status
)) {
210 // BootMonFS supports file extensions, but they are stripped by default
211 // when the NOR is programmed.
212 // Remove the file extension and try to open again.
213 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_RUNAXF_FILE_NOT_FOUND
),
214 gRunAxfHiiHandle
, FileName
);
215 // Go through the filename and remove file extension. Preserve the
217 TmpFileName
= AllocateCopyPool (StrSize (FileName
), (VOID
*)FileName
);
218 if (TmpFileName
!= NULL
) {
219 TmpChar16
= StrStr (TmpFileName
, L
".");
220 if (TmpChar16
!= NULL
) {
222 DEBUG((EFI_D_ERROR
, "Trying to open file: %s\n", TmpFileName
));
223 Status
= ShellOpenFileByName (TmpFileName
, &FileHandle
,
224 EFI_FILE_MODE_READ
, 0);
226 FreePool (TmpFileName
);
228 // Do we now have an open file after trying again?
229 if (EFI_ERROR (Status
)) {
230 ShellStatus
= SHELL_INVALID_PARAMETER
;
235 if (FileHandle
!= NULL
) {
236 Info
= ShellGetFileInfo (FileHandle
);
237 FileSize
= (UINTN
) Info
->FileSize
;
241 // Allocate buffer to read file. 'Runtime' so we can access it after
242 // ExitBootServices().
244 FileData
= AllocateRuntimeZeroPool (FileSize
);
245 if (FileData
== NULL
) {
246 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_RUNAXF_NO_MEM
), gRunAxfHiiHandle
);
247 ShellStatus
= SHELL_OUT_OF_RESOURCES
;
250 // Read file into Buffer
252 Status
= ShellReadFile (FileHandle
, &FileSize
, FileData
);
253 if (EFI_ERROR (Status
)) {
254 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_RUNAXF_READ_FAIL
), gRunAxfHiiHandle
);
255 SHELL_FREE_NON_NULL (FileData
);
257 ShellStatus
= SHELL_DEVICE_ERROR
;
264 // Free the command line package
266 ShellCommandLineFreeVarList (ParamPackage
);
269 // We have a file in memory. Try to work out if we can use it.
270 // It can either be in ELF format or BootMonFS format.
271 if (FileData
!= NULL
) {
272 // Do some validation on the file before we try to load it. The file can
273 // either be an proper ELF file or one processed by the FlashLoader.
274 // Since the data might need to go to various locations in memory we cannot
275 // load the data directly while UEFI is running. We use the file loaders to
276 // populate a linked list of data and load addresses. This is processed and
277 // data copied to where it needs to go after calling ExitBootServices. At
278 // that stage we've reached the point of no return, so overwriting UEFI code
279 // does not make a difference.
280 Status
= ElfCheckFile (FileData
);
281 if (!EFI_ERROR (Status
)) {
282 // Load program into memory
283 Status
= ElfLoadFile ((VOID
*)FileData
, &Entrypoint
, &LoadList
);
285 // Try to see if it is a BootMonFs executable
286 Status
= BootMonFsCheckFile ((EFI_FILE_HANDLE
)FileHandle
);
287 if (!EFI_ERROR (Status
)) {
288 // Load program into memory
289 Status
= BootMonFsLoadFile ((EFI_FILE_HANDLE
)FileHandle
,
290 (VOID
*)FileData
, &Entrypoint
, &LoadList
);
292 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_RUNAXF_BAD_FILE
),
294 SHELL_FREE_NON_NULL (FileData
);
295 ShellStatus
= SHELL_UNSUPPORTED
;
300 // Program load list created.
301 // Shutdown UEFI, copy and jump to code.
302 if (!IsListEmpty (&LoadList
) && !EFI_ERROR (Status
)) {
303 // Exit boot services here. This means we cannot return and cannot assume to
304 // have access to UEFI functions.
305 Status
= ShutdownUefiBootServices ();
306 if (EFI_ERROR (Status
)) {
307 DEBUG ((EFI_D_ERROR
,"Can not shutdown UEFI boot services. Status=0x%X\n",
310 // Process linked list. Copy data to Memory.
311 Node
= GetFirstNode (&LoadList
);
312 while (!IsNull (&LoadList
, Node
)) {
313 LoadNode
= (RUNAXF_LOAD_LIST
*)Node
;
314 // Do we have data to copy or do we need to set Zeroes (.bss)?
315 if (LoadNode
->Zeroes
) {
316 ZeroMem ((VOID
*)LoadNode
->MemOffset
, LoadNode
->Length
);
318 CopyMem ((VOID
*)LoadNode
->MemOffset
, (VOID
*)LoadNode
->FileOffset
,
321 Node
= GetNextNode (&LoadList
, Node
);
325 // Switch off interrupts, caches, mmu, etc
327 Status
= PreparePlatformHardware ();
328 ASSERT_EFI_ERROR (Status
);
330 StartElf
= (ELF_ENTRYPOINT
)Entrypoint
;
333 // We should never get here.. But if we do, spin..
339 // Free file related information as we are returning to UEFI.
340 Node
= GetFirstNode (&LoadList
);
341 while (!IsNull (&LoadList
, Node
)) {
342 NextNode
= RemoveEntryList (Node
);
346 SHELL_FREE_NON_NULL (FileData
);
347 if (FileHandle
!= NULL
) {
348 ShellCloseFile (&FileHandle
);
351 // Uninstall protocols as we don't know if they will change.
352 // If the shell exits and come in again these mappings may be different
353 // and cause a crash.
354 Status
= gBS
->UninstallMultipleProtocolInterfaces (gImageHandle
,
355 &gEfiShellProtocolGuid
, Shell
,
356 &gEfiShellParametersProtocolGuid
, ShellParameters
,
359 if (EFI_ERROR (Status
) && ShellStatus
== SHELL_SUCCESS
) {
360 ShellStatus
= SHELL_DEVICE_ERROR
;
368 This is the command help handler function pointer callback type. This
369 function is responsible for displaying help information for the associated
372 @param[in] This The instance of the
373 EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL.
374 @param[in] Language The pointer to the language string to use.
376 @return string Pool allocated help string, must be freed by
381 ShellDynCmdRunAxfGetHelp (
382 IN EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL
*This
,
383 IN CONST CHAR8
*Language
388 ASSERT (gRunAxfHiiHandle
!= NULL
);
390 // This allocates memory. The caller is responsoible to free.
391 HelpText
= HiiGetString (gRunAxfHiiHandle
, STRING_TOKEN (STR_GET_HELP_RUNAXF
),