]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPlatformPkg/Bds/BootMenu.c
ArmPlatformPkg/Bds: Remove Linux specific boot path
[mirror_edk2.git] / ArmPlatformPkg / Bds / BootMenu.c
1 /** @file
2 *
3 * Copyright (c) 2011 - 2015, ARM Limited. All rights reserved.
4 *
5 * This program and the accompanying materials
6 * are licensed and made available under the terms and conditions of the BSD License
7 * which accompanies this distribution. The full text of the license may be found at
8 * http://opensource.org/licenses/bsd-license.php
9 *
10 * THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12 *
13 **/
14
15 #include "BdsInternal.h"
16
17 #include <Guid/ArmGlobalVariableHob.h>
18
19 #include <libfdt.h>
20
21 /**
22 Worker function that displays the list of boot options that is passed in.
23
24 The function loops over the entries of the list of boot options that is passed
25 in. For each entry, the boot option description is displayed on a single line
26 along with the position of the option in the list. In debug mode, the UEFI
27 device path and the arguments of the boot option are displayed as well in
28 subsequent lines.
29
30 @param[in] BootOptionsList List of the boot options
31
32 **/
33 STATIC
34 VOID
35 DisplayBootOptions (
36 IN LIST_ENTRY* BootOptionsList
37 )
38 {
39 EFI_STATUS Status;
40 UINTN BootOptionCount;
41 LIST_ENTRY *Entry;
42 BDS_LOAD_OPTION *BdsLoadOption;
43 BOOLEAN IsUnicode;
44
45 BootOptionCount = 0 ;
46 for (Entry = GetFirstNode (BootOptionsList);
47 !IsNull (BootOptionsList, Entry);
48 Entry = GetNextNode (BootOptionsList, Entry)
49 ) {
50
51 BdsLoadOption = LOAD_OPTION_FROM_LINK (Entry);
52 Print (L"[%d] %s\n", ++BootOptionCount, BdsLoadOption->Description);
53
54 DEBUG_CODE_BEGIN ();
55 CHAR16* DevicePathTxt;
56 EFI_DEVICE_PATH_TO_TEXT_PROTOCOL* DevicePathToTextProtocol;
57
58 Status = gBS->LocateProtocol (
59 &gEfiDevicePathToTextProtocolGuid,
60 NULL,
61 (VOID **)&DevicePathToTextProtocol
62 );
63 ASSERT_EFI_ERROR (Status);
64 DevicePathTxt = DevicePathToTextProtocol->ConvertDevicePathToText (
65 BdsLoadOption->FilePathList,
66 TRUE,
67 TRUE
68 );
69 Print (L"\t- %s\n", DevicePathTxt);
70
71 if (IsPrintableString (BdsLoadOption->OptionalData, &IsUnicode)) {
72 if (IsUnicode) {
73 Print (L"\t- Arguments: %s\n", BdsLoadOption->OptionalData);
74 } else {
75 AsciiPrint ("\t- Arguments: %a\n", BdsLoadOption->OptionalData);
76 }
77 }
78
79 FreePool (DevicePathTxt);
80 DEBUG_CODE_END ();
81 }
82 }
83
84 /**
85 Worker function that asks for a boot option to be selected and returns a
86 pointer to the structure describing the selected boot option.
87
88 @param[in] BootOptionsList List of the boot options
89
90 @retval EFI_SUCCESS Selection succeeded
91 @retval !EFI_SUCCESS Input error or input cancelled
92
93 **/
94 STATIC
95 EFI_STATUS
96 SelectBootOption (
97 IN LIST_ENTRY* BootOptionsList,
98 IN CONST CHAR16* InputStatement,
99 OUT BDS_LOAD_OPTION_ENTRY** BdsLoadOptionEntry
100 )
101 {
102 EFI_STATUS Status;
103 UINTN BootOptionCount;
104 UINT16 *BootOrder;
105 LIST_ENTRY* Entry;
106 UINTN BootOptionSelected;
107 UINTN Index;
108
109 // Get the number of boot options
110 Status = GetGlobalEnvironmentVariable (
111 L"BootOrder", NULL, &BootOptionCount, (VOID**)&BootOrder
112 );
113 if (EFI_ERROR (Status)) {
114 goto ErrorExit;
115 }
116 FreePool (BootOrder);
117 BootOptionCount /= sizeof (UINT16);
118
119 // Check if a valid boot option(s) is found
120 if (BootOptionCount == 0) {
121 if (StrCmp (InputStatement, DELETE_BOOT_ENTRY) == 0) {
122 Print (L"Nothing to remove!\n");
123 } else if (StrCmp (InputStatement, UPDATE_BOOT_ENTRY) == 0) {
124 Print (L"Nothing to update!\n");
125 } else if (StrCmp (InputStatement, MOVE_BOOT_ENTRY) == 0) {
126 Print (L"Nothing to move!\n");
127 } else {
128 Print (L"No supported Boot Entry.\n");
129 }
130 return EFI_NOT_FOUND;
131 }
132
133 // Get the index of the boot device to delete
134 BootOptionSelected = 0;
135 while (BootOptionSelected == 0) {
136 Print (InputStatement);
137 Status = GetHIInputInteger (&BootOptionSelected);
138 if (EFI_ERROR (Status)) {
139 Print (L"\n");
140 goto ErrorExit;
141 } else if ((BootOptionSelected == 0) || (BootOptionSelected > BootOptionCount)) {
142 Print (L"Invalid input (max %d)\n", BootOptionCount);
143 BootOptionSelected = 0;
144 }
145 }
146
147 // Get the structure of the Boot device to delete
148 Index = 1;
149 for (Entry = GetFirstNode (BootOptionsList);
150 !IsNull (BootOptionsList, Entry);
151 Entry = GetNextNode (BootOptionsList,Entry)
152 )
153 {
154 if (Index == BootOptionSelected) {
155 *BdsLoadOptionEntry = LOAD_OPTION_ENTRY_FROM_LINK (Entry);
156 break;
157 }
158 Index++;
159 }
160
161 ErrorExit:
162 return Status;
163 }
164
165 STATIC
166 EFI_STATUS
167 SelectBootDevice (
168 OUT BDS_SUPPORTED_DEVICE** SupportedBootDevice
169 )
170 {
171 EFI_STATUS Status;
172 LIST_ENTRY SupportedDeviceList;
173 UINTN SupportedDeviceCount;
174 LIST_ENTRY* Entry;
175 UINTN SupportedDeviceSelected;
176 UINTN Index;
177
178 //
179 // List the Boot Devices supported
180 //
181
182 // Start all the drivers first
183 BdsConnectAllDrivers ();
184
185 // List the supported devices
186 Status = BootDeviceListSupportedInit (&SupportedDeviceList);
187 ASSERT_EFI_ERROR(Status);
188
189 SupportedDeviceCount = 0;
190 for (Entry = GetFirstNode (&SupportedDeviceList);
191 !IsNull (&SupportedDeviceList,Entry);
192 Entry = GetNextNode (&SupportedDeviceList,Entry)
193 )
194 {
195 *SupportedBootDevice = SUPPORTED_BOOT_DEVICE_FROM_LINK(Entry);
196 Print(L"[%d] %s\n",SupportedDeviceCount+1,(*SupportedBootDevice)->Description);
197
198 DEBUG_CODE_BEGIN();
199 CHAR16* DevicePathTxt;
200 EFI_DEVICE_PATH_TO_TEXT_PROTOCOL* DevicePathToTextProtocol;
201
202 Status = gBS->LocateProtocol (&gEfiDevicePathToTextProtocolGuid, NULL, (VOID **)&DevicePathToTextProtocol);
203 ASSERT_EFI_ERROR(Status);
204 DevicePathTxt = DevicePathToTextProtocol->ConvertDevicePathToText ((*SupportedBootDevice)->DevicePathProtocol,TRUE,TRUE);
205
206 Print(L"\t- %s\n",DevicePathTxt);
207
208 FreePool(DevicePathTxt);
209 DEBUG_CODE_END();
210
211 SupportedDeviceCount++;
212 }
213
214 if (SupportedDeviceCount == 0) {
215 Print(L"There is no supported device.\n");
216 Status = EFI_ABORTED;
217 goto EXIT;
218 }
219
220 //
221 // Select the Boot Device
222 //
223 SupportedDeviceSelected = 0;
224 while (SupportedDeviceSelected == 0) {
225 Print(L"Select the Boot Device: ");
226 Status = GetHIInputInteger (&SupportedDeviceSelected);
227 if (EFI_ERROR(Status)) {
228 Status = EFI_ABORTED;
229 goto EXIT;
230 } else if ((SupportedDeviceSelected == 0) || (SupportedDeviceSelected > SupportedDeviceCount)) {
231 Print(L"Invalid input (max %d)\n",SupportedDeviceCount);
232 SupportedDeviceSelected = 0;
233 }
234 }
235
236 //
237 // Get the Device Path for the selected boot device
238 //
239 Index = 1;
240 for (Entry = GetFirstNode (&SupportedDeviceList);
241 !IsNull (&SupportedDeviceList,Entry);
242 Entry = GetNextNode (&SupportedDeviceList,Entry)
243 )
244 {
245 if (Index == SupportedDeviceSelected) {
246 *SupportedBootDevice = SUPPORTED_BOOT_DEVICE_FROM_LINK(Entry);
247 break;
248 }
249 Index++;
250 }
251
252 EXIT:
253 BootDeviceListSupportedFree (&SupportedDeviceList, *SupportedBootDevice);
254 return Status;
255 }
256
257 EFI_STATUS
258 BootMenuAddBootOption (
259 IN LIST_ENTRY *BootOptionsList
260 )
261 {
262 EFI_STATUS Status;
263 BDS_SUPPORTED_DEVICE* SupportedBootDevice;
264 CHAR16 BootDescription[BOOT_DEVICE_DESCRIPTION_MAX];
265 CHAR16 CmdLine[BOOT_DEVICE_OPTION_MAX];
266 UINT32 Attributes;
267 BDS_LOAD_OPTION_ENTRY *BdsLoadOptionEntry;
268 EFI_DEVICE_PATH *DevicePath;
269 EFI_DEVICE_PATH_PROTOCOL *DevicePathNodes;
270 UINT8* OptionalData;
271 UINTN OptionalDataSize;
272
273 Attributes = 0;
274 SupportedBootDevice = NULL;
275
276 // List the Boot Devices supported
277 Status = SelectBootDevice (&SupportedBootDevice);
278 if (EFI_ERROR(Status)) {
279 Status = EFI_ABORTED;
280 goto EXIT;
281 }
282
283 // Create the specific device path node
284 Status = SupportedBootDevice->Support->CreateDevicePathNode (L"EFI Application or the kernel", &DevicePathNodes);
285 if (EFI_ERROR(Status)) {
286 Status = EFI_ABORTED;
287 goto EXIT;
288 }
289 // Append the Device Path to the selected device path
290 DevicePath = AppendDevicePath (SupportedBootDevice->DevicePathProtocol, (CONST EFI_DEVICE_PATH_PROTOCOL *)DevicePathNodes);
291 if (DevicePath == NULL) {
292 Status = EFI_OUT_OF_RESOURCES;
293 goto EXIT;
294 }
295
296 Print (L"Arguments to pass to the EFI Application: ");
297 Status = GetHIInputStr (CmdLine, BOOT_DEVICE_OPTION_MAX);
298 if (EFI_ERROR (Status)) {
299 Status = EFI_ABORTED;
300 goto EXIT;
301 }
302
303 OptionalData = (UINT8*)CmdLine;
304 OptionalDataSize = StrSize (CmdLine);
305
306 Print(L"Description for this new Entry: ");
307 Status = GetHIInputStr (BootDescription, BOOT_DEVICE_DESCRIPTION_MAX);
308 if (EFI_ERROR(Status)) {
309 Status = EFI_ABORTED;
310 goto FREE_DEVICE_PATH;
311 }
312
313 // Create new entry
314 BdsLoadOptionEntry = (BDS_LOAD_OPTION_ENTRY*)AllocatePool (sizeof(BDS_LOAD_OPTION_ENTRY));
315 Status = BootOptionCreate (Attributes, BootDescription, DevicePath, OptionalData, OptionalDataSize, &BdsLoadOptionEntry->BdsLoadOption);
316 if (!EFI_ERROR(Status)) {
317 InsertTailList (BootOptionsList, &BdsLoadOptionEntry->Link);
318 }
319
320 FREE_DEVICE_PATH:
321 FreePool (DevicePath);
322
323 EXIT:
324 if (Status == EFI_ABORTED) {
325 Print(L"\n");
326 }
327 FreePool(SupportedBootDevice);
328 return Status;
329 }
330
331 EFI_STATUS
332 BootMenuRemoveBootOption (
333 IN LIST_ENTRY *BootOptionsList
334 )
335 {
336 EFI_STATUS Status;
337 BDS_LOAD_OPTION_ENTRY* BootOptionEntry;
338
339 DisplayBootOptions (BootOptionsList);
340 Status = SelectBootOption (BootOptionsList, DELETE_BOOT_ENTRY, &BootOptionEntry);
341 if (EFI_ERROR (Status)) {
342 return Status;
343 }
344
345 // If the Boot Option was attached to a list remove it
346 if (!IsListEmpty (&BootOptionEntry->Link)) {
347 // Remove the entry from the list
348 RemoveEntryList (&BootOptionEntry->Link);
349 }
350
351 // Delete the BDS Load option structures
352 BootOptionDelete (BootOptionEntry->BdsLoadOption);
353
354 return EFI_SUCCESS;
355 }
356
357 EFI_STATUS
358 BootMenuUpdateBootOption (
359 IN LIST_ENTRY *BootOptionsList
360 )
361 {
362 EFI_STATUS Status;
363 BDS_LOAD_OPTION_ENTRY *BootOptionEntry;
364 BDS_LOAD_OPTION *BootOption;
365 BDS_LOAD_OPTION_SUPPORT* DeviceSupport;
366 CHAR16 BootDescription[BOOT_DEVICE_DESCRIPTION_MAX];
367 CHAR8 CmdLine[BOOT_DEVICE_OPTION_MAX];
368 CHAR16 UnicodeCmdLine[BOOT_DEVICE_OPTION_MAX];
369 EFI_DEVICE_PATH *DevicePath;
370 UINT8* OptionalData;
371 UINTN OptionalDataSize;
372 BOOLEAN IsPrintable;
373 BOOLEAN IsUnicode;
374
375 DisplayBootOptions (BootOptionsList);
376 Status = SelectBootOption (BootOptionsList, UPDATE_BOOT_ENTRY, &BootOptionEntry);
377 if (EFI_ERROR (Status)) {
378 return Status;
379 }
380 BootOption = BootOptionEntry->BdsLoadOption;
381
382 // Get the device support for this Boot Option
383 Status = BootDeviceGetDeviceSupport (BootOption->FilePathList, &DeviceSupport);
384 if (EFI_ERROR(Status)) {
385 Print(L"Not possible to retrieve the supported device for the update\n");
386 return EFI_UNSUPPORTED;
387 }
388
389 Status = DeviceSupport->UpdateDevicePathNode (BootOption->FilePathList, L"EFI Application or the kernel", &DevicePath);
390 if (EFI_ERROR(Status)) {
391 Status = EFI_ABORTED;
392 goto EXIT;
393 }
394
395 Print (L"Arguments to pass to the EFI Application: ");
396
397 if (BootOption->OptionalDataSize > 0) {
398 IsPrintable = IsPrintableString (BootOption->OptionalData, &IsUnicode);
399 if (IsPrintable) {
400 //
401 // The size in bytes of the string, final zero included, should
402 // be equal to or at least lower than "BootOption->OptionalDataSize"
403 // and the "IsPrintableString()" has already tested that the length
404 // in number of characters is smaller than BOOT_DEVICE_OPTION_MAX,
405 // final '\0' included. We can thus copy the string for editing
406 // using "CopyMem()". Furthermore, note that in the case of an Unicode
407 // string "StrnCpy()" and "StrCpy()" can not be used to copy the
408 // string because the data pointed to by "BootOption->OptionalData"
409 // is not necessarily 2-byte aligned.
410 //
411 if (IsUnicode) {
412 CopyMem (
413 UnicodeCmdLine, BootOption->OptionalData,
414 MIN (sizeof (UnicodeCmdLine),
415 BootOption->OptionalDataSize)
416 );
417 } else {
418 CopyMem (
419 CmdLine, BootOption->OptionalData,
420 MIN (sizeof (CmdLine),
421 BootOption->OptionalDataSize)
422 );
423 }
424 }
425 } else {
426 UnicodeCmdLine[0] = L'\0';
427 IsPrintable = TRUE;
428 IsUnicode = TRUE;
429 }
430
431 // We do not request arguments for OptionalData that cannot be printed
432 if (IsPrintable) {
433 if (IsUnicode) {
434 Status = EditHIInputStr (UnicodeCmdLine, BOOT_DEVICE_OPTION_MAX);
435 if (EFI_ERROR (Status)) {
436 Status = EFI_ABORTED;
437 goto FREE_DEVICE_PATH;
438 }
439
440 OptionalData = (UINT8*)UnicodeCmdLine;
441 OptionalDataSize = StrSize (UnicodeCmdLine);
442 } else {
443 Status = EditHIInputAscii (CmdLine, BOOT_DEVICE_OPTION_MAX);
444 if (EFI_ERROR (Status)) {
445 Status = EFI_ABORTED;
446 goto FREE_DEVICE_PATH;
447 }
448
449 OptionalData = (UINT8*)CmdLine;
450 OptionalDataSize = AsciiStrSize (CmdLine);
451 }
452 } else {
453 // We keep the former OptionalData
454 OptionalData = BootOption->OptionalData;
455 OptionalDataSize = BootOption->OptionalDataSize;
456 }
457
458 Print(L"Description for this new Entry: ");
459 StrnCpy (BootDescription, BootOption->Description, BOOT_DEVICE_DESCRIPTION_MAX);
460 Status = EditHIInputStr (BootDescription, BOOT_DEVICE_DESCRIPTION_MAX);
461 if (EFI_ERROR(Status)) {
462 Status = EFI_ABORTED;
463 goto FREE_DEVICE_PATH;
464 }
465
466 // Update the entry
467 Status = BootOptionUpdate (BootOption, BootOption->Attributes, BootDescription, DevicePath, OptionalData, OptionalDataSize);
468
469 FREE_DEVICE_PATH:
470 FreePool (DevicePath);
471
472 EXIT:
473 if (Status == EFI_ABORTED) {
474 Print(L"\n");
475 }
476 return Status;
477 }
478
479 /**
480 Reorder boot options
481
482 Ask for the boot option to move and then move it when up or down arrows
483 are pressed. This function is called when the user selects the "Reorder Boot
484 Device Entries" entry in the boot manager menu.
485 The order of the boot options in BootOptionList and in the UEFI BootOrder
486 global variable are kept coherent until the user confirm his reordering (ie:
487 he does not exit by pressing escape).
488
489 @param[in] BootOptionsList List of the boot devices constructed in
490 BootMenuMain()
491
492 @retval EFI_SUCCESS No error encountered.
493 @retval !EFI_SUCCESS An error has occured either in the selection of the
494 boot option to move or while interacting with the user.
495
496 **/
497 STATIC
498 EFI_STATUS
499 BootMenuReorderBootOptions (
500 IN LIST_ENTRY *BootOptionsList
501 )
502 {
503 EFI_STATUS Status;
504 BDS_LOAD_OPTION_ENTRY *BootOptionEntry;
505 LIST_ENTRY *SelectedEntry;
506 LIST_ENTRY *PrevEntry;
507 BOOLEAN Move;
508 BOOLEAN Save;
509 BOOLEAN Cancel;
510 UINTN WaitIndex;
511 EFI_INPUT_KEY Key;
512 LIST_ENTRY *SecondEntry;
513 UINTN BootOrderSize;
514 UINT16 *BootOrder;
515 LIST_ENTRY *Entry;
516 UINTN Index;
517
518 DisplayBootOptions (BootOptionsList);
519
520 // Ask to select the boot option to move
521 while (TRUE) {
522 Status = SelectBootOption (BootOptionsList, MOVE_BOOT_ENTRY, &BootOptionEntry);
523 if (EFI_ERROR (Status)) {
524 goto ErrorExit;
525 }
526
527 SelectedEntry = &BootOptionEntry->Link;
528 SecondEntry = NULL;
529 // Note down the previous entry in the list to be able to cancel changes
530 PrevEntry = GetPreviousNode (BootOptionsList, SelectedEntry);
531
532 // Start of interaction
533 while (TRUE) {
534 Print (
535 L"* Use up/down arrows to move the entry '%s'",
536 BootOptionEntry->BdsLoadOption->Description
537 );
538
539 // Wait for a move, save or cancel request
540 Move = FALSE;
541 Save = FALSE;
542 Cancel = FALSE;
543 do {
544 Status = gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &WaitIndex);
545 if (!EFI_ERROR (Status)) {
546 Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
547 }
548 if (EFI_ERROR (Status)) {
549 Print (L"\n");
550 goto ErrorExit;
551 }
552
553 switch (Key.ScanCode) {
554 case SCAN_NULL:
555 Save = (Key.UnicodeChar == CHAR_LINEFEED) ||
556 (Key.UnicodeChar == CHAR_CARRIAGE_RETURN) ||
557 (Key.UnicodeChar == 0x7f);
558 break;
559
560 case SCAN_UP:
561 SecondEntry = GetPreviousNode (BootOptionsList, SelectedEntry);
562 Move = SecondEntry != BootOptionsList;
563 break;
564
565 case SCAN_DOWN:
566 SecondEntry = GetNextNode (BootOptionsList, SelectedEntry);
567 Move = SecondEntry != BootOptionsList;
568 break;
569
570 case SCAN_ESC:
571 Cancel = TRUE;
572 break;
573 }
574 } while ((!Move) && (!Save) && (!Cancel));
575
576 if (Move) {
577 if ((SelectedEntry != NULL) && (SecondEntry != NULL)) {
578 SwapListEntries (SelectedEntry, SecondEntry);
579 }
580 } else {
581 if (Save) {
582 Status = GetGlobalEnvironmentVariable (
583 L"BootOrder", NULL, &BootOrderSize, (VOID**)&BootOrder
584 );
585 BootOrderSize /= sizeof (UINT16);
586
587 if (!EFI_ERROR (Status)) {
588 // The order of the boot options in the 'BootOptionsList' is the
589 // new order that has been just defined by the user. Save this new
590 // order in "BootOrder" UEFI global variable.
591 Entry = GetFirstNode (BootOptionsList);
592 for (Index = 0; Index < BootOrderSize; Index++) {
593 BootOrder[Index] = (LOAD_OPTION_FROM_LINK (Entry))->LoadOptionIndex;
594 Entry = GetNextNode (BootOptionsList, Entry);
595 }
596 Status = gRT->SetVariable (
597 (CHAR16*)L"BootOrder",
598 &gEfiGlobalVariableGuid,
599 EFI_VARIABLE_NON_VOLATILE |
600 EFI_VARIABLE_BOOTSERVICE_ACCESS |
601 EFI_VARIABLE_RUNTIME_ACCESS,
602 BootOrderSize * sizeof (UINT16),
603 BootOrder
604 );
605 FreePool (BootOrder);
606 }
607
608 if (EFI_ERROR (Status)) {
609 Print (L"\nAn error occurred, move not completed!\n");
610 Cancel = TRUE;
611 }
612 }
613
614 if (Cancel) {
615 //
616 // Restore initial position of the selected boot option
617 //
618 RemoveEntryList (SelectedEntry);
619 InsertHeadList (PrevEntry, SelectedEntry);
620 }
621 }
622
623 Print (L"\n");
624 DisplayBootOptions (BootOptionsList);
625 // Saved or cancelled, back to the choice of boot option to move
626 if (!Move) {
627 break;
628 }
629 }
630 }
631
632 ErrorExit:
633 return Status ;
634 }
635
636 EFI_STATUS
637 UpdateFdtPath (
638 IN LIST_ENTRY *BootOptionsList
639 )
640 {
641 EFI_STATUS Status;
642 BDS_SUPPORTED_DEVICE *SupportedBootDevice;
643 EFI_DEVICE_PATH_PROTOCOL *FdtDevicePathNodes;
644 EFI_DEVICE_PATH_PROTOCOL *FdtDevicePath;
645 CHAR16 *FdtTextDevicePath;
646 EFI_PHYSICAL_ADDRESS FdtBlobBase;
647 UINTN FdtBlobSize;
648 UINTN NumPages;
649 EFI_PHYSICAL_ADDRESS FdtConfigurationTableBase;
650
651 SupportedBootDevice = NULL;
652
653 Status = SelectBootDevice (&SupportedBootDevice);
654 if (EFI_ERROR (Status)) {
655 Status = EFI_ABORTED;
656 goto EXIT;
657 }
658
659 // Create the specific device path node
660 Status = SupportedBootDevice->Support->CreateDevicePathNode (L"FDT blob", &FdtDevicePathNodes);
661 if (EFI_ERROR (Status)) {
662 Status = EFI_ABORTED;
663 goto EXIT;
664 }
665
666 if (FdtDevicePathNodes != NULL) {
667 Status = EFI_OUT_OF_RESOURCES;
668
669 FdtDevicePath = AppendDevicePath (SupportedBootDevice->DevicePathProtocol, FdtDevicePathNodes);
670 FreePool (FdtDevicePathNodes);
671 if (FdtDevicePath == NULL) {
672 goto EXIT;
673 }
674
675 FdtTextDevicePath = ConvertDevicePathToText (FdtDevicePath, TRUE, TRUE);
676 if (FdtTextDevicePath == NULL) {
677 goto EXIT;
678 }
679
680 Status = gRT->SetVariable (
681 (CHAR16*)L"Fdt",
682 &gFdtVariableGuid,
683 EFI_VARIABLE_RUNTIME_ACCESS |
684 EFI_VARIABLE_NON_VOLATILE |
685 EFI_VARIABLE_BOOTSERVICE_ACCESS,
686 StrSize (FdtTextDevicePath),
687 FdtTextDevicePath
688 );
689 ASSERT_EFI_ERROR (Status);
690 FreePool (FdtTextDevicePath);
691 } else {
692 Status = gRT->SetVariable (
693 (CHAR16*)L"Fdt",
694 &gFdtVariableGuid,
695 EFI_VARIABLE_RUNTIME_ACCESS |
696 EFI_VARIABLE_NON_VOLATILE |
697 EFI_VARIABLE_BOOTSERVICE_ACCESS,
698 0,
699 NULL
700 );
701 ASSERT_EFI_ERROR (Status);
702 return Status;
703 }
704
705 //
706 // Try to load FDT from the new EFI Device Path
707 //
708
709 //
710 // Load the FDT given its device path.
711 // This operation may fail if the device path is not supported.
712 //
713 FdtBlobBase = 0;
714 NumPages = 0;
715 Status = BdsLoadImage (FdtDevicePath, AllocateAnyPages, &FdtBlobBase, &FdtBlobSize);
716 FreePool (FdtDevicePath);
717
718 if (EFI_ERROR (Status)) {
719 goto EXIT_LOAD_FDT;
720 }
721
722 // Check the FDT header is valid. We only make this check in DEBUG mode in
723 // case the FDT header change on production device and this ASSERT() becomes
724 // not valid.
725 ASSERT (fdt_check_header ((VOID*)(UINTN)FdtBlobBase) == 0);
726
727 //
728 // Ensure the Size of the Device Tree is smaller than the size of the read file
729 //
730 ASSERT ((UINTN)fdt_totalsize ((VOID*)(UINTN)FdtBlobBase) <= FdtBlobSize);
731
732 //
733 // Store the FDT as Runtime Service Data to prevent the Kernel from
734 // overwritting its data.
735 //
736 NumPages = EFI_SIZE_TO_PAGES (FdtBlobSize);
737 Status = gBS->AllocatePages (
738 AllocateAnyPages, EfiRuntimeServicesData,
739 NumPages, &FdtConfigurationTableBase
740 );
741 if (EFI_ERROR (Status)) {
742 goto EXIT_LOAD_FDT;
743 }
744 gBS->CopyMem (
745 (VOID*)(UINTN)FdtConfigurationTableBase,
746 (VOID*)(UINTN)FdtBlobBase,
747 FdtBlobSize
748 );
749
750 //
751 // Install the FDT into the Configuration Table
752 //
753 Status = gBS->InstallConfigurationTable (
754 &gFdtTableGuid,
755 (VOID*)(UINTN)FdtConfigurationTableBase
756 );
757 if (EFI_ERROR (Status)) {
758 gBS->FreePages (FdtConfigurationTableBase, NumPages);
759 }
760
761 EXIT_LOAD_FDT:
762 if (EFI_ERROR (Status)) {
763 Print (L"\nWarning: Did not manage to install the new device tree. Try to restart the platform.\n");
764 }
765
766 if (FdtBlobBase != 0) {
767 gBS->FreePages (FdtBlobBase, NumPages);
768 }
769
770 EXIT:
771 if (Status == EFI_ABORTED) {
772 Print (L"\n");
773 }
774
775 if (SupportedBootDevice != NULL) {
776 FreePool (SupportedBootDevice);
777 }
778
779 return Status;
780 }
781
782 /**
783 Set boot timeout
784
785 Ask for the boot timeout in seconds and if the input succeeds assign the
786 input value to the UEFI global variable "Timeout". This function is called
787 when the user selects the "Set Boot Timeout" of the boot manager menu.
788
789 @param[in] BootOptionsList List of the boot devices, not used here
790
791 @retval EFI_SUCCESS Boot timeout in second retrieved from the standard
792 input and assigned to the UEFI "Timeout" global
793 variable
794 @retval !EFI_SUCCESS Either the input or the setting of the UEFI global
795 variable "Timeout" has failed.
796 **/
797 EFI_STATUS
798 STATIC
799 BootMenuSetBootTimeout (
800 IN LIST_ENTRY *BootOptionsList
801 )
802 {
803 EFI_STATUS Status;
804 UINTN Input;
805 UINT16 Timeout;
806
807 Print (L"Timeout duration (in seconds): ");
808 Status = GetHIInputInteger (&Input);
809 if (EFI_ERROR (Status)) {
810 Print (L"\n");
811 goto ErrorExit;
812 }
813
814 Timeout = Input;
815 Status = gRT->SetVariable (
816 (CHAR16*)L"Timeout",
817 &gEfiGlobalVariableGuid,
818 EFI_VARIABLE_NON_VOLATILE |
819 EFI_VARIABLE_BOOTSERVICE_ACCESS |
820 EFI_VARIABLE_RUNTIME_ACCESS,
821 sizeof (UINT16),
822 &Timeout
823 );
824 ASSERT_EFI_ERROR (Status);
825
826 ErrorExit:
827 return Status;
828 }
829
830 struct BOOT_MANAGER_ENTRY {
831 CONST CHAR16* Description;
832 EFI_STATUS (*Callback) (IN LIST_ENTRY *BootOptionsList);
833 } BootManagerEntries[] = {
834 { L"Add Boot Device Entry", BootMenuAddBootOption },
835 { L"Update Boot Device Entry", BootMenuUpdateBootOption },
836 { L"Remove Boot Device Entry", BootMenuRemoveBootOption },
837 { L"Reorder Boot Device Entries", BootMenuReorderBootOptions },
838 { L"Update FDT path", UpdateFdtPath },
839 { L"Set Boot Timeout", BootMenuSetBootTimeout },
840 };
841
842 EFI_STATUS
843 BootMenuManager (
844 IN LIST_ENTRY *BootOptionsList
845 )
846 {
847 UINTN Index;
848 UINTN OptionSelected;
849 UINTN BootManagerEntryCount;
850 EFI_STATUS Status;
851
852 BootManagerEntryCount = sizeof(BootManagerEntries) / sizeof(struct BOOT_MANAGER_ENTRY);
853
854 while (TRUE) {
855 // Display Boot Manager menu
856 for (Index = 0; Index < BootManagerEntryCount; Index++) {
857 Print(L"[%d] %s\n",Index+1,BootManagerEntries[Index]);
858 }
859 Print(L"[%d] Return to main menu\n",Index+1);
860
861 // Select which entry to call
862 Print(L"Choice: ");
863 Status = GetHIInputInteger (&OptionSelected);
864 if (EFI_ERROR(Status) || (OptionSelected == (BootManagerEntryCount+1))) {
865 if (EFI_ERROR(Status)) {
866 Print(L"\n");
867 }
868 return EFI_SUCCESS;
869 } else if ((OptionSelected > 0) && (OptionSelected <= BootManagerEntryCount)) {
870 BootManagerEntries[OptionSelected-1].Callback (BootOptionsList);
871 }
872 }
873 // Should never go here
874 }
875
876 EFI_STATUS
877 BootShell (
878 IN LIST_ENTRY *BootOptionsList
879 )
880 {
881 EFI_STATUS Status;
882 EFI_DEVICE_PATH* EfiShellDevicePath;
883
884 // Find the EFI Shell
885 Status = LocateEfiApplicationInFvByName (L"Shell", &EfiShellDevicePath);
886 if (Status == EFI_NOT_FOUND) {
887 Print (L"Error: EFI Application not found.\n");
888 return Status;
889 } else if (EFI_ERROR (Status)) {
890 Print (L"Error: Status Code: 0x%X\n", (UINT32)Status);
891 return Status;
892 } else {
893 // Need to connect every drivers to ensure no dependencies are missing for the application
894 Status = BdsConnectAllDrivers ();
895 if (EFI_ERROR (Status)) {
896 DEBUG ((EFI_D_ERROR, "FAIL to connect all drivers\n"));
897 return Status;
898 }
899
900 return BdsStartEfiApplication (gImageHandle, EfiShellDevicePath, 0, NULL);
901 }
902 }
903
904 struct BOOT_MAIN_ENTRY {
905 CONST CHAR16* Description;
906 EFI_STATUS (*Callback) (IN LIST_ENTRY *BootOptionsList);
907 } BootMainEntries[] = {
908 { L"Shell", BootShell },
909 { L"Boot Manager", BootMenuManager },
910 };
911
912
913 EFI_STATUS
914 BootMenuMain (
915 VOID
916 )
917 {
918 LIST_ENTRY BootOptionsList;
919 UINTN OptionCount;
920 UINTN BootOptionCount;
921 EFI_STATUS Status;
922 LIST_ENTRY* Entry;
923 BDS_LOAD_OPTION* BootOption;
924 UINTN BootOptionSelected;
925 UINTN Index;
926 UINTN BootMainEntryCount;
927 BOOLEAN IsUnicode;
928
929 BootOption = NULL;
930 BootMainEntryCount = sizeof(BootMainEntries) / sizeof(struct BOOT_MAIN_ENTRY);
931
932 while (TRUE) {
933 // Get Boot#### list
934 BootOptionList (&BootOptionsList);
935
936 OptionCount = 1;
937
938 // Display the Boot options
939 for (Entry = GetFirstNode (&BootOptionsList);
940 !IsNull (&BootOptionsList,Entry);
941 Entry = GetNextNode (&BootOptionsList,Entry)
942 )
943 {
944 BootOption = LOAD_OPTION_FROM_LINK(Entry);
945
946 Print(L"[%d] %s\n", OptionCount, BootOption->Description);
947
948 DEBUG_CODE_BEGIN();
949 CHAR16* DevicePathTxt;
950 EFI_DEVICE_PATH_TO_TEXT_PROTOCOL* DevicePathToTextProtocol;
951
952 Status = gBS->LocateProtocol (&gEfiDevicePathToTextProtocolGuid, NULL, (VOID **)&DevicePathToTextProtocol);
953 if (EFI_ERROR(Status)) {
954 // You must provide an implementation of DevicePathToTextProtocol in your firmware (eg: DevicePathDxe)
955 DEBUG((EFI_D_ERROR,"Error: Bds requires DevicePathToTextProtocol\n"));
956 return Status;
957 }
958 DevicePathTxt = DevicePathToTextProtocol->ConvertDevicePathToText (BootOption->FilePathList, TRUE, TRUE);
959
960 Print(L"\t- %s\n",DevicePathTxt);
961
962 if (BootOption->OptionalData != NULL) {
963 if (IsPrintableString (BootOption->OptionalData, &IsUnicode)) {
964 if (IsUnicode) {
965 Print (L"\t- Arguments: %s\n", BootOption->OptionalData);
966 } else {
967 AsciiPrint ("\t- Arguments: %a\n", BootOption->OptionalData);
968 }
969 }
970 }
971 FreePool(DevicePathTxt);
972 DEBUG_CODE_END();
973
974 OptionCount++;
975 }
976 BootOptionCount = OptionCount-1;
977
978 // Display the hardcoded Boot entries
979 for (Index = 0; Index < BootMainEntryCount; Index++) {
980 Print(L"[%d] %s\n",OptionCount,BootMainEntries[Index]);
981 OptionCount++;
982 }
983
984 // Request the boot entry from the user
985 BootOptionSelected = 0;
986 while (BootOptionSelected == 0) {
987 Print(L"Start: ");
988 Status = GetHIInputInteger (&BootOptionSelected);
989 if (EFI_ERROR(Status) || (BootOptionSelected == 0) || (BootOptionSelected > OptionCount)) {
990 Print(L"Invalid input (max %d)\n",(OptionCount-1));
991 BootOptionSelected = 0;
992 }
993 }
994
995 // Start the selected entry
996 if (BootOptionSelected > BootOptionCount) {
997 // Start the hardcoded entry
998 Status = BootMainEntries[BootOptionSelected - BootOptionCount - 1].Callback (&BootOptionsList);
999 } else {
1000 // Find the selected entry from the Boot#### list
1001 Index = 1;
1002 for (Entry = GetFirstNode (&BootOptionsList);
1003 !IsNull (&BootOptionsList,Entry);
1004 Entry = GetNextNode (&BootOptionsList,Entry)
1005 )
1006 {
1007 if (Index == BootOptionSelected) {
1008 BootOption = LOAD_OPTION_FROM_LINK(Entry);
1009 break;
1010 }
1011 Index++;
1012 }
1013
1014 Status = BootOptionStart (BootOption);
1015 }
1016 }
1017 // Should never go here
1018 }