ArmPlatformPkg/Bds: Add return carriage after the user presses 'ESC'
[mirror_edk2.git] / ArmPlatformPkg / Bds / BootMenu.c
CommitLineData
ea46ebbe 1/** @file\r
2*\r
3* Copyright (c) 2011, ARM Limited. All rights reserved.\r
4*\r
5* This program and the accompanying materials\r
6* are licensed and made available under the terms and conditions of the BSD License\r
7* which accompanies this distribution. The full text of the license may be found at\r
8* http://opensource.org/licenses/bsd-license.php\r
9*\r
10* THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11* WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
12*\r
13**/\r
14\r
15#include "BdsInternal.h"\r
16\r
17extern EFI_HANDLE mImageHandle;\r
18extern BDS_LOAD_OPTION_SUPPORT *BdsLoadOptionSupportList;\r
19\r
20EFI_STATUS\r
21BootMenuAddBootOption (\r
22 IN LIST_ENTRY *BootOptionsList\r
23 )\r
24{\r
25 EFI_STATUS Status;\r
26 LIST_ENTRY SupportedDeviceList;\r
27 UINTN SupportedDeviceCount;\r
28 BDS_SUPPORTED_DEVICE* SupportedBootDevice;\r
29 LIST_ENTRY* Entry;\r
30 UINTN SupportedDeviceSelected;\r
31 CHAR8 AsciiBootOption[BOOT_DEVICE_OPTION_MAX];\r
32 CHAR8 AsciiBootDescription[BOOT_DEVICE_DESCRIPTION_MAX];\r
33 CHAR16 *BootDescription;\r
34 UINT32 Attributes;\r
35 BDS_LOADER_TYPE BootType;\r
36 UINTN Index;\r
37 BDS_LOAD_OPTION *BdsLoadOption;\r
38 EFI_DEVICE_PATH* DevicePath;\r
39 EFI_DEVICE_PATH_PROTOCOL *DevicePathNode;\r
40\r
e862cd50 41 Attributes = 0;\r
42 SupportedBootDevice = NULL;\r
ea46ebbe 43\r
44 //\r
45 // List the Boot Devices supported\r
46 //\r
47\r
48 // Start all the drivers first\r
49 BdsConnectAllDrivers ();\r
50\r
51 // List the supported devices\r
52 Status = BootDeviceListSupportedInit (&SupportedDeviceList);\r
53 ASSERT_EFI_ERROR(Status);\r
54\r
55 SupportedDeviceCount = 0;\r
56 for (Entry = GetFirstNode (&SupportedDeviceList);\r
57 !IsNull (&SupportedDeviceList,Entry);\r
58 Entry = GetNextNode (&SupportedDeviceList,Entry)\r
59 )\r
60 {\r
61 SupportedBootDevice = SUPPORTED_BOOT_DEVICE_FROM_LINK(Entry);\r
62 Print(L"[%d] %s\n",SupportedDeviceCount+1,SupportedBootDevice->Description);\r
63\r
64 DEBUG_CODE_BEGIN();\r
65 CHAR16* DevicePathTxt;\r
66 EFI_DEVICE_PATH_TO_TEXT_PROTOCOL* DevicePathToTextProtocol;\r
67\r
68 Status = gBS->LocateProtocol(&gEfiDevicePathToTextProtocolGuid, NULL, (VOID **)&DevicePathToTextProtocol);\r
69 ASSERT_EFI_ERROR(Status);\r
70 DevicePathTxt = DevicePathToTextProtocol->ConvertDevicePathToText(SupportedBootDevice->DevicePathProtocol,TRUE,TRUE);\r
71\r
72 Print(L"\t- %s\n",DevicePathTxt);\r
73\r
74 FreePool(DevicePathTxt);\r
75 DEBUG_CODE_END();\r
76\r
77 SupportedDeviceCount++;\r
78 }\r
79\r
80 if (SupportedDeviceCount == 0) {\r
81 Print(L"There is no supported device.\n");\r
82 Status = EFI_ABORTED;\r
83 goto EXIT;\r
84 }\r
85\r
86 //\r
87 // Select the Boot Device\r
88 //\r
89 SupportedDeviceSelected = 0;\r
90 while (SupportedDeviceSelected == 0) {\r
91 Print(L"Select the Boot Device: ");\r
92 Status = GetHIInputInteger (&SupportedDeviceSelected);\r
93 if (EFI_ERROR(Status)) {\r
94 Status = EFI_ABORTED;\r
95 goto EXIT;\r
96 } else if ((SupportedDeviceSelected == 0) || (SupportedDeviceSelected > SupportedDeviceCount)) {\r
97 Print(L"Invalid input (max %d)\n",SupportedDeviceSelected);\r
98 SupportedDeviceSelected = 0;\r
99 }\r
100 }\r
101\r
102 //\r
103 // Get the Device Path for the selected boot device\r
104 //\r
105 Index = 1;\r
106 for (Entry = GetFirstNode (&SupportedDeviceList);\r
107 !IsNull (&SupportedDeviceList,Entry);\r
108 Entry = GetNextNode (&SupportedDeviceList,Entry)\r
109 )\r
110 {\r
111 if (Index == SupportedDeviceSelected) {\r
112 SupportedBootDevice = SUPPORTED_BOOT_DEVICE_FROM_LINK(Entry);\r
113 break;\r
114 }\r
115 Index++;\r
116 }\r
117\r
118 // Create the specific device path node\r
119 Status = SupportedBootDevice->Support->CreateDevicePathNode (SupportedBootDevice, &DevicePathNode, &BootType, &Attributes);\r
120 if (EFI_ERROR(Status)) {\r
121 Status = EFI_ABORTED;\r
122 goto EXIT;\r
123 }\r
124 // Append the Device Path node to the select device path\r
125 DevicePath = AppendDevicePathNode (SupportedBootDevice->DevicePathProtocol, (CONST EFI_DEVICE_PATH_PROTOCOL *)DevicePathNode);\r
126\r
127 Print(L"Arguments to pass to the binary: ");\r
128 Status = GetHIInputAscii (AsciiBootOption,BOOT_DEVICE_OPTION_MAX);\r
129 if (EFI_ERROR(Status)) {\r
130 Status = EFI_ABORTED;\r
131 goto FREE_DEVICE_PATH;\r
132 }\r
133\r
134 Print(L"Description for this new Entry: ");\r
135 Status = GetHIInputAscii (AsciiBootDescription,BOOT_DEVICE_DESCRIPTION_MAX);\r
136 if (EFI_ERROR(Status)) {\r
137 Status = EFI_ABORTED;\r
138 goto FREE_DEVICE_PATH;\r
139 }\r
140\r
141 // Convert Ascii into Unicode\r
142 BootDescription = (CHAR16*)AllocatePool(AsciiStrSize(AsciiBootDescription) * sizeof(CHAR16));\r
143 AsciiStrToUnicodeStr (AsciiBootDescription, BootDescription);\r
144\r
145 // Create new entry\r
146 Status = BootOptionCreate (Attributes, BootDescription, DevicePath, BootType, AsciiBootOption, &BdsLoadOption);\r
147 if (!EFI_ERROR(Status)) {\r
148 InsertTailList (BootOptionsList,&BdsLoadOption->Link);\r
149 }\r
150\r
151 FreePool (BootDescription);\r
152\r
153FREE_DEVICE_PATH:\r
154 FreePool (DevicePath);\r
155\r
156EXIT:\r
157 BootDeviceListSupportedFree (&SupportedDeviceList);\r
158 return Status;\r
159}\r
160\r
161STATIC\r
162EFI_STATUS\r
163BootMenuSelectBootOption (\r
164 IN LIST_ENTRY *BootOptionsList,\r
165 IN CONST CHAR16* InputStatement,\r
166 OUT BDS_LOAD_OPTION **BdsLoadOption\r
167 )\r
168{\r
169 EFI_STATUS Status;\r
170 LIST_ENTRY* Entry;\r
171 BDS_LOAD_OPTION *BootOption;\r
172 UINTN BootOptionSelected;\r
173 UINTN BootOptionCount;\r
174 UINTN Index;\r
175\r
176 // Display the list of supported boot devices\r
177 BootOptionCount = 1;\r
178 for (Entry = GetFirstNode (BootOptionsList);\r
179 !IsNull (BootOptionsList,Entry);\r
180 Entry = GetNextNode (BootOptionsList,Entry)\r
181 )\r
182 {\r
183 BootOption = LOAD_OPTION_FROM_LINK(Entry);\r
184 Print(L"[%d] %s\n",BootOptionCount,BootOption->Description);\r
185\r
186 DEBUG_CODE_BEGIN();\r
187 CHAR16* DevicePathTxt;\r
188 EFI_DEVICE_PATH_TO_TEXT_PROTOCOL* DevicePathToTextProtocol;\r
189\r
190 Status = gBS->LocateProtocol(&gEfiDevicePathToTextProtocolGuid, NULL, (VOID **)&DevicePathToTextProtocol);\r
191 ASSERT_EFI_ERROR(Status);\r
192 DevicePathTxt = DevicePathToTextProtocol->ConvertDevicePathToText(BootOption->FilePathList,TRUE,TRUE);\r
193\r
194 Print(L"\t- %s\n",DevicePathTxt);\r
195 if ((BootOption->OptionalData != NULL) && (BootOption->OptionalData->Arguments != NULL)) {\r
196 Print(L"\t- Arguments: %a\n",BootOption->OptionalData->Arguments);\r
197 }\r
198\r
199 FreePool(DevicePathTxt);\r
200 DEBUG_CODE_END();\r
201\r
202 BootOptionCount++;\r
203 }\r
204\r
205 // Get the index of the boot device to delete\r
206 BootOptionSelected = 0;\r
207 while (BootOptionSelected == 0) {\r
208 Print(InputStatement);\r
209 Status = GetHIInputInteger (&BootOptionSelected);\r
210 if (EFI_ERROR(Status)) {\r
211 return Status;\r
212 } else if ((BootOptionSelected == 0) || (BootOptionSelected >= BootOptionCount)) {\r
213 Print(L"Invalid input (max %d)\n",BootOptionCount);\r
214 BootOptionSelected = 0;\r
215 }\r
216 }\r
217\r
218 // Get the structure of the Boot device to delete\r
219 Index = 1;\r
220 for (Entry = GetFirstNode (BootOptionsList);\r
221 !IsNull (BootOptionsList,Entry);\r
222 Entry = GetNextNode (BootOptionsList,Entry)\r
223 )\r
224 {\r
225 if (Index == BootOptionSelected) {\r
226 *BdsLoadOption = LOAD_OPTION_FROM_LINK(Entry);\r
227 break;\r
228 }\r
229 Index++;\r
230 }\r
231\r
232 return EFI_SUCCESS;\r
233}\r
234\r
235EFI_STATUS\r
236BootMenuRemoveBootOption (\r
237 IN LIST_ENTRY *BootOptionsList\r
238 )\r
239{\r
240 EFI_STATUS Status;\r
241 BDS_LOAD_OPTION *BootOption;\r
242\r
243 Status = BootMenuSelectBootOption (BootOptionsList,L"Delete entry: ",&BootOption);\r
244 if (EFI_ERROR(Status)) {\r
245 return Status;\r
246 }\r
247\r
248 // Delete the BDS Load option structures\r
249 BootOptionDelete (BootOption);\r
250\r
251 return EFI_SUCCESS;\r
252}\r
253\r
254EFI_STATUS\r
255BootMenuUpdateBootOption (\r
256 IN LIST_ENTRY *BootOptionsList\r
257 )\r
258{\r
259 EFI_STATUS Status;\r
260 BDS_LOAD_OPTION *BootOption;\r
261 BDS_LOAD_OPTION_SUPPORT* DeviceSupport;\r
262 CHAR8 AsciiBootOption[BOOT_DEVICE_OPTION_MAX];\r
263 CHAR8 AsciiBootDescription[BOOT_DEVICE_DESCRIPTION_MAX];\r
264 CHAR16 *BootDescription;\r
265 EFI_DEVICE_PATH* DevicePath;\r
266 UINT32 Attributes;\r
267 BDS_LOADER_TYPE BootType;\r
268\r
269 Status = BootMenuSelectBootOption (BootOptionsList,L"Update entry: ",&BootOption);\r
270 if (EFI_ERROR(Status)) {\r
271 return Status;\r
272 }\r
273\r
274 // Get the device support for this Boot Option\r
275 Status = BootDeviceGetDeviceSupport (BootOption,&DeviceSupport);\r
276 if (EFI_ERROR(Status)) {\r
277 Print(L"Impossible to retrieve the supported device for the update\n");\r
278 return EFI_UNSUPPORTED;\r
279 }\r
280\r
281 Status = DeviceSupport->UpdateDevicePathNode (BootOption,&DevicePath,&BootType,&Attributes);\r
282 if (EFI_ERROR(Status)) {\r
283 Status = EFI_ABORTED;\r
284 goto EXIT;\r
285 }\r
286\r
287 Print(L"Arguments to pass to the binary: ");\r
288 if (BootOption->OptionalData) {\r
289 AsciiStrnCpy(AsciiBootOption,BootOption->OptionalData->Arguments,BOOT_DEVICE_FILEPATH_MAX);\r
290 } else {\r
291 AsciiBootOption[0] = '\0';\r
292 }\r
293 Status = EditHIInputAscii (AsciiBootOption,BOOT_DEVICE_OPTION_MAX);\r
294 if (EFI_ERROR(Status)) {\r
295 Status = EFI_ABORTED;\r
296 goto FREE_DEVICE_PATH;\r
297 }\r
298\r
299 Print(L"Description for this new Entry: ");\r
300 UnicodeStrToAsciiStr (BootOption->Description,AsciiBootDescription);\r
301 Status = EditHIInputAscii (AsciiBootDescription,BOOT_DEVICE_DESCRIPTION_MAX);\r
302 if (EFI_ERROR(Status)) {\r
303 Status = EFI_ABORTED;\r
304 goto FREE_DEVICE_PATH;\r
305 }\r
306\r
307 // Convert Ascii into Unicode\r
308 BootDescription = (CHAR16*)AllocatePool(AsciiStrSize(AsciiBootDescription) * sizeof(CHAR16));\r
309 AsciiStrToUnicodeStr (AsciiBootDescription, BootDescription);\r
310\r
311 // Update the entry\r
312 Status = BootOptionUpdate (BootOption, Attributes, BootDescription, DevicePath, BootType, AsciiBootOption);\r
313\r
314 FreePool (BootDescription);\r
315\r
316FREE_DEVICE_PATH:\r
317 FreePool (DevicePath);\r
318\r
319EXIT:\r
326d1df9 320 if (Status == EFI_ABORTED) {\r
321 Print(L"\n");\r
322 }\r
ea46ebbe 323 return Status;\r
324}\r
325\r
326struct BOOT_MANAGER_ENTRY {\r
327 CONST CHAR16* Description;\r
328 EFI_STATUS (*Callback) (IN LIST_ENTRY *BootOptionsList);\r
329} BootManagerEntries[] = {\r
330 { L"Add Boot Device Entry", BootMenuAddBootOption },\r
331 { L"Update Boot Device Entry", BootMenuUpdateBootOption },\r
332 { L"Remove Boot Device Entry", BootMenuRemoveBootOption },\r
333};\r
334\r
335EFI_STATUS\r
336BootMenuManager (\r
337 IN LIST_ENTRY *BootOptionsList\r
338 )\r
339{\r
340 UINTN Index;\r
341 UINTN OptionSelected;\r
342 UINTN BootManagerEntryCount;\r
343 EFI_STATUS Status;\r
344\r
345 BootManagerEntryCount = sizeof(BootManagerEntries) / sizeof(struct BOOT_MANAGER_ENTRY);\r
346\r
347 while (TRUE) {\r
348 // Display Boot Manager menu\r
349 for (Index = 0; Index < BootManagerEntryCount; Index++) {\r
350 Print(L"[%d] %s\n",Index+1,BootManagerEntries[Index]);\r
351 }\r
352 Print(L"[%d] Return to main menu\n",Index+1);\r
353\r
354 // Select which entry to call\r
355 Print(L"Choice: ");\r
356 Status = GetHIInputInteger (&OptionSelected);\r
357 if (EFI_ERROR(Status) || (OptionSelected == (BootManagerEntryCount+1))) {\r
326d1df9 358 if (EFI_ERROR(Status)) {\r
359 Print(L"\n");\r
360 }\r
ea46ebbe 361 return EFI_SUCCESS;\r
362 } else if ((OptionSelected > 0) && (OptionSelected <= BootManagerEntryCount)) {\r
363 Status = BootManagerEntries[OptionSelected-1].Callback (BootOptionsList);\r
364 }\r
365 }\r
366\r
367 return EFI_SUCCESS;\r
368}\r
369\r
370EFI_STATUS\r
371BootEBL (\r
372 IN LIST_ENTRY *BootOptionsList\r
373 )\r
374{\r
375 EFI_STATUS Status;\r
376\r
377 // Start EFI Shell\r
378 Status = BdsLoadApplication(mImageHandle, L"Ebl");\r
379 if (Status == EFI_NOT_FOUND) {\r
380 Print (L"Error: EFI Application not found.\n");\r
381 } else if (EFI_ERROR(Status)) {\r
382 Print (L"Error: Status Code: 0x%X\n",(UINT32)Status);\r
383 }\r
384\r
385 return Status;\r
386}\r
387\r
388struct BOOT_MAIN_ENTRY {\r
389 CONST CHAR16* Description;\r
390 EFI_STATUS (*Callback) (IN LIST_ENTRY *BootOptionsList);\r
391} BootMainEntries[] = {\r
392 { L"EBL", BootEBL },\r
393 { L"Boot Manager", BootMenuManager },\r
394};\r
395\r
396\r
397EFI_STATUS\r
398BootMenuMain (\r
399 VOID\r
400 )\r
401{\r
402 LIST_ENTRY BootOptionsList;\r
403 UINTN OptionCount;\r
404 UINTN BootOptionCount;\r
405 EFI_STATUS Status;\r
406 LIST_ENTRY *Entry;\r
407 BDS_LOAD_OPTION *BootOption;\r
408 UINTN BootOptionSelected;\r
409 UINTN Index;\r
410 UINTN BootMainEntryCount;\r
411\r
e862cd50 412 BootOption = NULL;\r
ea46ebbe 413 BootMainEntryCount = sizeof(BootMainEntries) / sizeof(struct BOOT_MAIN_ENTRY);\r
a6caee65 414\r
ea46ebbe 415 // Get Boot#### list\r
416 BootOptionList (&BootOptionsList);\r
417\r
418 while (TRUE) {\r
419 OptionCount = 1;\r
420\r
421 // Display the Boot options\r
422 for (Entry = GetFirstNode (&BootOptionsList);\r
423 !IsNull (&BootOptionsList,Entry);\r
424 Entry = GetNextNode (&BootOptionsList,Entry)\r
425 )\r
426 {\r
427 BootOption = LOAD_OPTION_FROM_LINK(Entry);\r
428\r
429 Print(L"[%d] %s\n",OptionCount,BootOption->Description);\r
430\r
431 DEBUG_CODE_BEGIN();\r
432 CHAR16* DevicePathTxt;\r
433 EFI_DEVICE_PATH_TO_TEXT_PROTOCOL* DevicePathToTextProtocol;\r
434\r
435 Status = gBS->LocateProtocol(&gEfiDevicePathToTextProtocolGuid, NULL, (VOID **)&DevicePathToTextProtocol);\r
aa95e2f7 436 if (EFI_ERROR(Status)) {\r
437 // You must provide an implementation of DevicePathToTextProtocol in your firmware (eg: DevicePathDxe)\r
438 DEBUG((EFI_D_ERROR,"Error: Bds requires DevicePathToTextProtocol\n"));\r
439 return Status;\r
440 }\r
ea46ebbe 441 DevicePathTxt = DevicePathToTextProtocol->ConvertDevicePathToText(BootOption->FilePathList,TRUE,TRUE);\r
442\r
443 Print(L"\t- %s\n",DevicePathTxt);\r
444 if (BootOption->OptionalData != NULL) {\r
c60ea9a8 445 Print(L"\t- LoaderType: %d\n", ReadUnaligned32 (&BootOption->OptionalData->LoaderType));\r
ea46ebbe 446 if (BootOption->OptionalData->Arguments != NULL) {\r
447 Print(L"\t- Arguments: %a\n",BootOption->OptionalData->Arguments);\r
448 }\r
449 }\r
450\r
451 FreePool(DevicePathTxt);\r
452 DEBUG_CODE_END();\r
453\r
454 OptionCount++;\r
455 }\r
456 BootOptionCount = OptionCount-1;\r
457\r
458 // Display the hardcoded Boot entries\r
459 for (Index = 0; Index < BootMainEntryCount; Index++) {\r
460 Print(L"[%d] %s\n",OptionCount,BootMainEntries[Index]);\r
461 OptionCount++;\r
462 }\r
463\r
464 // Request the boot entry from the user\r
465 BootOptionSelected = 0;\r
466 while (BootOptionSelected == 0) {\r
467 Print(L"Start: ");\r
468 Status = GetHIInputInteger (&BootOptionSelected);\r
469 if (EFI_ERROR(Status) || (BootOptionSelected == 0) || (BootOptionSelected > OptionCount)) {\r
470 Print(L"Invalid input (max %d)\n",(OptionCount-1));\r
471 BootOptionSelected = 0;\r
472 }\r
473 }\r
474\r
475 // Start the selected entry\r
476 if (BootOptionSelected > BootOptionCount) {\r
477 // Start the hardcoded entry\r
478 Status = BootMainEntries[BootOptionSelected - BootOptionCount - 1].Callback (&BootOptionsList);\r
479 } else {\r
480 // Find the selected entry from the Boot#### list\r
481 Index = 1;\r
482 for (Entry = GetFirstNode (&BootOptionsList);\r
483 !IsNull (&BootOptionsList,Entry);\r
484 Entry = GetNextNode (&BootOptionsList,Entry)\r
485 )\r
486 {\r
487 if (Index == BootOptionSelected) {\r
488 BootOption = LOAD_OPTION_FROM_LINK(Entry);\r
489 break;\r
490 }\r
491 Index++;\r
492 }\r
493\r
494 Status = BootOptionStart (BootOption);\r
495 }\r
496 }\r
497\r
498 return Status;\r
499}\r