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
PreparePlatformHardware(VOID)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
ShellDynCmdRunAxfHandler(IN EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL * This,IN EFI_SYSTEM_TABLE * SystemTable,IN EFI_SHELL_PARAMETERS_PROTOCOL * ShellParameters,IN EFI_SHELL_PROTOCOL * Shell)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
ShellDynCmdRunAxfGetHelp(IN EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL * This,IN CONST CHAR8 * Language)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