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