]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPlatformPkg/Bds/BootMenu.c
NetworkPkg: Stop and release DHCP4 child after boot info is ready
[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 BOOLEAN EfiBinary;
273 CHAR16 *LinuxDevicePath;
274
275 Attributes = 0;
276 SupportedBootDevice = NULL;
277
278 // List the Boot Devices supported
279 Status = SelectBootDevice (&SupportedBootDevice);
280 if (EFI_ERROR(Status)) {
281 Status = EFI_ABORTED;
282 goto EXIT;
283 }
284
285 // Create the specific device path node
286 if (FeaturePcdGet (PcdBdsLinuxSupport) && mLinuxLoaderDevicePath) {
287 Status = SupportedBootDevice->Support->CreateDevicePathNode (L"EFI Application or the kernel", &DevicePathNodes);
288 } else {
289 Status = SupportedBootDevice->Support->CreateDevicePathNode (L"EFI Application", &DevicePathNodes);
290 }
291 if (EFI_ERROR (Status)) {
292 Status = EFI_ABORTED;
293 goto EXIT;
294 }
295 // Append the Device Path to the selected device path
296 DevicePath = AppendDevicePath (SupportedBootDevice->DevicePathProtocol, (CONST EFI_DEVICE_PATH_PROTOCOL *)DevicePathNodes);
297 if (DevicePath == NULL) {
298 Status = EFI_OUT_OF_RESOURCES;
299 goto EXIT;
300 }
301
302 // Is it an EFI application?
303 if (FeaturePcdGet (PcdBdsLinuxSupport) && mLinuxLoaderDevicePath) {
304 Status = IsEfiBinary (DevicePath, &EfiBinary);
305 if (EFI_ERROR (Status)) {
306 Status = EFI_ABORTED;
307 goto EXIT;
308 }
309
310 if (EfiBinary == FALSE) {
311 Print (L"It is assumed the binary is a Linux kernel and the embedded Linux Loader is going to be used.\n");
312 Print (L"Supported command line formats by the embedded Linux Loader:\n");
313 Print (L"- <EFI device path of the Linux kernel> -c \"<Linux kernel command line>\"\n");
314 Print (L"- <EFI device path of the Linux kernel> -c \"<Linux kernel command line>\" -f <EFI Device Path of the Linux initrd>\n");
315 Print (L"- <EFI device path of the Linux kernel> -c \"<Linux kernel command line>\" -a <Machine Type for ATAG Linux kernel>\n");
316
317 // Copy the Linux path into the command line
318 LinuxDevicePath = ConvertDevicePathToText (DevicePath, FALSE, FALSE);
319 CopyMem (CmdLine, LinuxDevicePath, MAX (sizeof (CmdLine), StrSize (LinuxDevicePath)));
320 FreePool (LinuxDevicePath);
321
322 // Free the generated Device Path
323 FreePool (DevicePath);
324 // and use the embedded Linux Loader as the EFI application
325 DevicePath = mLinuxLoaderDevicePath;
326 } else {
327 CmdLine[0] = L'\0';
328 }
329 } else {
330 CmdLine[0] = L'\0';
331 }
332
333 Print (L"Arguments to pass to the EFI Application: ");
334 Status = EditHIInputStr (CmdLine, BOOT_DEVICE_OPTION_MAX);
335 if (EFI_ERROR (Status)) {
336 Status = EFI_ABORTED;
337 goto EXIT;
338 }
339
340 OptionalData = (UINT8*)CmdLine;
341 OptionalDataSize = StrSize (CmdLine);
342
343 Print(L"Description for this new Entry: ");
344 Status = GetHIInputStr (BootDescription, BOOT_DEVICE_DESCRIPTION_MAX);
345 if (EFI_ERROR(Status)) {
346 Status = EFI_ABORTED;
347 goto FREE_DEVICE_PATH;
348 }
349
350 // Create new entry
351 BdsLoadOptionEntry = (BDS_LOAD_OPTION_ENTRY*)AllocatePool (sizeof(BDS_LOAD_OPTION_ENTRY));
352 Status = BootOptionCreate (Attributes, BootDescription, DevicePath, OptionalData, OptionalDataSize, &BdsLoadOptionEntry->BdsLoadOption);
353 if (!EFI_ERROR(Status)) {
354 InsertTailList (BootOptionsList, &BdsLoadOptionEntry->Link);
355 }
356
357 FREE_DEVICE_PATH:
358 FreePool (DevicePath);
359
360 EXIT:
361 if (Status == EFI_ABORTED) {
362 Print(L"\n");
363 }
364 FreePool(SupportedBootDevice);
365 return Status;
366 }
367
368 EFI_STATUS
369 BootMenuRemoveBootOption (
370 IN LIST_ENTRY *BootOptionsList
371 )
372 {
373 EFI_STATUS Status;
374 BDS_LOAD_OPTION_ENTRY* BootOptionEntry;
375
376 DisplayBootOptions (BootOptionsList);
377 Status = SelectBootOption (BootOptionsList, DELETE_BOOT_ENTRY, &BootOptionEntry);
378 if (EFI_ERROR (Status)) {
379 return Status;
380 }
381
382 // If the Boot Option was attached to a list remove it
383 if (!IsListEmpty (&BootOptionEntry->Link)) {
384 // Remove the entry from the list
385 RemoveEntryList (&BootOptionEntry->Link);
386 }
387
388 // Delete the BDS Load option structures
389 BootOptionDelete (BootOptionEntry->BdsLoadOption);
390
391 return EFI_SUCCESS;
392 }
393
394 EFI_STATUS
395 BootMenuUpdateBootOption (
396 IN LIST_ENTRY *BootOptionsList
397 )
398 {
399 EFI_STATUS Status;
400 BDS_LOAD_OPTION_ENTRY *BootOptionEntry;
401 BDS_LOAD_OPTION *BootOption;
402 BDS_LOAD_OPTION_SUPPORT* DeviceSupport;
403 CHAR16 BootDescription[BOOT_DEVICE_DESCRIPTION_MAX];
404 CHAR8 CmdLine[BOOT_DEVICE_OPTION_MAX];
405 CHAR16 UnicodeCmdLine[BOOT_DEVICE_OPTION_MAX];
406 CHAR16 *LinuxDevicePath;
407 EFI_DEVICE_PATH *DevicePath;
408 UINT8* OptionalData;
409 UINTN OptionalDataSize;
410 BOOLEAN IsPrintable;
411 BOOLEAN IsUnicode;
412 BOOLEAN EfiBinary;
413
414 DisplayBootOptions (BootOptionsList);
415 Status = SelectBootOption (BootOptionsList, UPDATE_BOOT_ENTRY, &BootOptionEntry);
416 if (EFI_ERROR (Status)) {
417 return Status;
418 }
419 BootOption = BootOptionEntry->BdsLoadOption;
420
421 // Get the device support for this Boot Option
422 Status = BootDeviceGetDeviceSupport (BootOption->FilePathList, &DeviceSupport);
423 if (EFI_ERROR(Status)) {
424 Print(L"Not possible to retrieve the supported device for the update\n");
425 return EFI_UNSUPPORTED;
426 }
427
428 EfiBinary = TRUE;
429 if (FeaturePcdGet (PcdBdsLinuxSupport) && mLinuxLoaderDevicePath) {
430 Status = DeviceSupport->UpdateDevicePathNode (BootOption->FilePathList, L"EFI Application or the kernel", &DevicePath);
431 if (EFI_ERROR (Status)) {
432 Status = EFI_ABORTED;
433 goto EXIT;
434 }
435
436 // Is it an EFI application?
437 Status = IsEfiBinary (DevicePath, &EfiBinary);
438 if (EFI_ERROR (Status)) {
439 Status = EFI_ABORTED;
440 goto EXIT;
441 }
442
443 if (EfiBinary == FALSE) {
444 Print (L"It is assumed the binary is a Linux kernel and the embedded Linux Loader is going to be used.\n");
445 Print (L"Supported command line formats by the embedded Linux Loader:\n");
446 Print (L"- <EFI device path of the Linux kernel> -c \"<Linux kernel command line>\"\n");
447 Print (L"- <EFI device path of the Linux kernel> -c \"<Linux kernel command line>\" -f <EFI Device Path of the Linux initrd>\n");
448 Print (L"- <EFI device path of the Linux kernel> -c \"<Linux kernel command line>\" -a <Machine Type for ATAG Linux kernel>\n");
449
450 // Copy the Linux path into the command line
451 LinuxDevicePath = ConvertDevicePathToText (DevicePath, FALSE, FALSE);
452 CopyMem (UnicodeCmdLine, LinuxDevicePath, MAX (sizeof (UnicodeCmdLine), StrSize (LinuxDevicePath)));
453 FreePool (LinuxDevicePath);
454
455 // Free the generated Device Path
456 FreePool (DevicePath);
457 // and use the embedded Linux Loader as the EFI application
458 DevicePath = mLinuxLoaderDevicePath;
459
460 // The command line is a unicode printable string
461 IsPrintable = TRUE;
462 IsUnicode = TRUE;
463 }
464 } else {
465 Status = DeviceSupport->UpdateDevicePathNode (BootOption->FilePathList, L"EFI Application", &DevicePath);
466 if (EFI_ERROR (Status)) {
467 Status = EFI_ABORTED;
468 goto EXIT;
469 }
470 }
471
472 Print (L"Arguments to pass to the EFI Application: ");
473
474 // When the command line has not been initialized by the embedded Linux loader earlier
475 if (EfiBinary) {
476 if (BootOption->OptionalDataSize > 0) {
477 IsPrintable = IsPrintableString (BootOption->OptionalData, &IsUnicode);
478 if (IsPrintable) {
479 //
480 // The size in bytes of the string, final zero included, should
481 // be equal to or at least lower than "BootOption->OptionalDataSize"
482 // and the "IsPrintableString()" has already tested that the length
483 // in number of characters is smaller than BOOT_DEVICE_OPTION_MAX,
484 // final '\0' included. We can thus copy the string for editing
485 // using "CopyMem()". Furthermore, note that in the case of an Unicode
486 // string "StrnCpy()" and "StrCpy()" can not be used to copy the
487 // string because the data pointed to by "BootOption->OptionalData"
488 // is not necessarily 2-byte aligned.
489 //
490 if (IsUnicode) {
491 CopyMem (
492 UnicodeCmdLine, BootOption->OptionalData,
493 MIN (sizeof (UnicodeCmdLine),
494 BootOption->OptionalDataSize)
495 );
496 } else {
497 CopyMem (
498 CmdLine, BootOption->OptionalData,
499 MIN (sizeof (CmdLine),
500 BootOption->OptionalDataSize)
501 );
502 }
503 }
504 } else {
505 UnicodeCmdLine[0] = L'\0';
506 IsPrintable = TRUE;
507 IsUnicode = TRUE;
508 }
509 }
510
511 // We do not request arguments for OptionalData that cannot be printed
512 if (IsPrintable) {
513 if (IsUnicode) {
514 Status = EditHIInputStr (UnicodeCmdLine, BOOT_DEVICE_OPTION_MAX);
515 if (EFI_ERROR (Status)) {
516 Status = EFI_ABORTED;
517 goto FREE_DEVICE_PATH;
518 }
519
520 OptionalData = (UINT8*)UnicodeCmdLine;
521 OptionalDataSize = StrSize (UnicodeCmdLine);
522 } else {
523 Status = EditHIInputAscii (CmdLine, BOOT_DEVICE_OPTION_MAX);
524 if (EFI_ERROR (Status)) {
525 Status = EFI_ABORTED;
526 goto FREE_DEVICE_PATH;
527 }
528
529 OptionalData = (UINT8*)CmdLine;
530 OptionalDataSize = AsciiStrSize (CmdLine);
531 }
532 } else {
533 // We keep the former OptionalData
534 OptionalData = BootOption->OptionalData;
535 OptionalDataSize = BootOption->OptionalDataSize;
536 }
537
538 Print(L"Description for this new Entry: ");
539 StrnCpy (BootDescription, BootOption->Description, BOOT_DEVICE_DESCRIPTION_MAX);
540 Status = EditHIInputStr (BootDescription, BOOT_DEVICE_DESCRIPTION_MAX);
541 if (EFI_ERROR(Status)) {
542 Status = EFI_ABORTED;
543 goto FREE_DEVICE_PATH;
544 }
545
546 // Update the entry
547 Status = BootOptionUpdate (BootOption, BootOption->Attributes, BootDescription, DevicePath, OptionalData, OptionalDataSize);
548
549 FREE_DEVICE_PATH:
550 FreePool (DevicePath);
551
552 EXIT:
553 if (Status == EFI_ABORTED) {
554 Print(L"\n");
555 }
556 return Status;
557 }
558
559 /**
560 Reorder boot options
561
562 Ask for the boot option to move and then move it when up or down arrows
563 are pressed. This function is called when the user selects the "Reorder Boot
564 Device Entries" entry in the boot manager menu.
565 The order of the boot options in BootOptionList and in the UEFI BootOrder
566 global variable are kept coherent until the user confirm his reordering (ie:
567 he does not exit by pressing escape).
568
569 @param[in] BootOptionsList List of the boot devices constructed in
570 BootMenuMain()
571
572 @retval EFI_SUCCESS No error encountered.
573 @retval !EFI_SUCCESS An error has occured either in the selection of the
574 boot option to move or while interacting with the user.
575
576 **/
577 STATIC
578 EFI_STATUS
579 BootMenuReorderBootOptions (
580 IN LIST_ENTRY *BootOptionsList
581 )
582 {
583 EFI_STATUS Status;
584 BDS_LOAD_OPTION_ENTRY *BootOptionEntry;
585 LIST_ENTRY *SelectedEntry;
586 LIST_ENTRY *PrevEntry;
587 BOOLEAN Move;
588 BOOLEAN Save;
589 BOOLEAN Cancel;
590 UINTN WaitIndex;
591 EFI_INPUT_KEY Key;
592 LIST_ENTRY *SecondEntry;
593 UINTN BootOrderSize;
594 UINT16 *BootOrder;
595 LIST_ENTRY *Entry;
596 UINTN Index;
597
598 DisplayBootOptions (BootOptionsList);
599
600 // Ask to select the boot option to move
601 while (TRUE) {
602 Status = SelectBootOption (BootOptionsList, MOVE_BOOT_ENTRY, &BootOptionEntry);
603 if (EFI_ERROR (Status)) {
604 goto ErrorExit;
605 }
606
607 SelectedEntry = &BootOptionEntry->Link;
608 SecondEntry = NULL;
609 // Note down the previous entry in the list to be able to cancel changes
610 PrevEntry = GetPreviousNode (BootOptionsList, SelectedEntry);
611
612 // Start of interaction
613 while (TRUE) {
614 Print (
615 L"* Use up/down arrows to move the entry '%s'",
616 BootOptionEntry->BdsLoadOption->Description
617 );
618
619 // Wait for a move, save or cancel request
620 Move = FALSE;
621 Save = FALSE;
622 Cancel = FALSE;
623 do {
624 Status = gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &WaitIndex);
625 if (!EFI_ERROR (Status)) {
626 Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
627 }
628 if (EFI_ERROR (Status)) {
629 Print (L"\n");
630 goto ErrorExit;
631 }
632
633 switch (Key.ScanCode) {
634 case SCAN_NULL:
635 Save = (Key.UnicodeChar == CHAR_LINEFEED) ||
636 (Key.UnicodeChar == CHAR_CARRIAGE_RETURN) ||
637 (Key.UnicodeChar == 0x7f);
638 break;
639
640 case SCAN_UP:
641 SecondEntry = GetPreviousNode (BootOptionsList, SelectedEntry);
642 Move = SecondEntry != BootOptionsList;
643 break;
644
645 case SCAN_DOWN:
646 SecondEntry = GetNextNode (BootOptionsList, SelectedEntry);
647 Move = SecondEntry != BootOptionsList;
648 break;
649
650 case SCAN_ESC:
651 Cancel = TRUE;
652 break;
653 }
654 } while ((!Move) && (!Save) && (!Cancel));
655
656 if (Move) {
657 if ((SelectedEntry != NULL) && (SecondEntry != NULL)) {
658 SwapListEntries (SelectedEntry, SecondEntry);
659 }
660 } else {
661 if (Save) {
662 Status = GetGlobalEnvironmentVariable (
663 L"BootOrder", NULL, &BootOrderSize, (VOID**)&BootOrder
664 );
665 BootOrderSize /= sizeof (UINT16);
666
667 if (!EFI_ERROR (Status)) {
668 // The order of the boot options in the 'BootOptionsList' is the
669 // new order that has been just defined by the user. Save this new
670 // order in "BootOrder" UEFI global variable.
671 Entry = GetFirstNode (BootOptionsList);
672 for (Index = 0; Index < BootOrderSize; Index++) {
673 BootOrder[Index] = (LOAD_OPTION_FROM_LINK (Entry))->LoadOptionIndex;
674 Entry = GetNextNode (BootOptionsList, Entry);
675 }
676 Status = gRT->SetVariable (
677 (CHAR16*)L"BootOrder",
678 &gEfiGlobalVariableGuid,
679 EFI_VARIABLE_NON_VOLATILE |
680 EFI_VARIABLE_BOOTSERVICE_ACCESS |
681 EFI_VARIABLE_RUNTIME_ACCESS,
682 BootOrderSize * sizeof (UINT16),
683 BootOrder
684 );
685 FreePool (BootOrder);
686 }
687
688 if (EFI_ERROR (Status)) {
689 Print (L"\nAn error occurred, move not completed!\n");
690 Cancel = TRUE;
691 }
692 }
693
694 if (Cancel) {
695 //
696 // Restore initial position of the selected boot option
697 //
698 RemoveEntryList (SelectedEntry);
699 InsertHeadList (PrevEntry, SelectedEntry);
700 }
701 }
702
703 Print (L"\n");
704 DisplayBootOptions (BootOptionsList);
705 // Saved or cancelled, back to the choice of boot option to move
706 if (!Move) {
707 break;
708 }
709 }
710 }
711
712 ErrorExit:
713 return Status ;
714 }
715
716 EFI_STATUS
717 UpdateFdtPath (
718 IN LIST_ENTRY *BootOptionsList
719 )
720 {
721 EFI_STATUS Status;
722 BDS_SUPPORTED_DEVICE *SupportedBootDevice;
723 EFI_DEVICE_PATH_PROTOCOL *FdtDevicePathNodes;
724 EFI_DEVICE_PATH_PROTOCOL *FdtDevicePath;
725 CHAR16 *FdtTextDevicePath;
726 EFI_PHYSICAL_ADDRESS FdtBlobBase;
727 UINTN FdtBlobSize;
728 UINTN NumPages;
729 EFI_PHYSICAL_ADDRESS FdtConfigurationTableBase;
730
731 SupportedBootDevice = NULL;
732
733 Status = SelectBootDevice (&SupportedBootDevice);
734 if (EFI_ERROR (Status)) {
735 Status = EFI_ABORTED;
736 goto EXIT;
737 }
738
739 // Create the specific device path node
740 Status = SupportedBootDevice->Support->CreateDevicePathNode (L"FDT blob", &FdtDevicePathNodes);
741 if (EFI_ERROR (Status)) {
742 Status = EFI_ABORTED;
743 goto EXIT;
744 }
745
746 if (FdtDevicePathNodes != NULL) {
747 Status = EFI_OUT_OF_RESOURCES;
748
749 FdtDevicePath = AppendDevicePath (SupportedBootDevice->DevicePathProtocol, FdtDevicePathNodes);
750 FreePool (FdtDevicePathNodes);
751 if (FdtDevicePath == NULL) {
752 goto EXIT;
753 }
754
755 FdtTextDevicePath = ConvertDevicePathToText (FdtDevicePath, TRUE, TRUE);
756 if (FdtTextDevicePath == NULL) {
757 goto EXIT;
758 }
759
760 Status = gRT->SetVariable (
761 (CHAR16*)L"Fdt",
762 &gFdtVariableGuid,
763 EFI_VARIABLE_RUNTIME_ACCESS |
764 EFI_VARIABLE_NON_VOLATILE |
765 EFI_VARIABLE_BOOTSERVICE_ACCESS,
766 StrSize (FdtTextDevicePath),
767 FdtTextDevicePath
768 );
769 ASSERT_EFI_ERROR (Status);
770 FreePool (FdtTextDevicePath);
771 } else {
772 Status = gRT->SetVariable (
773 (CHAR16*)L"Fdt",
774 &gFdtVariableGuid,
775 EFI_VARIABLE_RUNTIME_ACCESS |
776 EFI_VARIABLE_NON_VOLATILE |
777 EFI_VARIABLE_BOOTSERVICE_ACCESS,
778 0,
779 NULL
780 );
781 ASSERT_EFI_ERROR (Status);
782 return Status;
783 }
784
785 //
786 // Try to load FDT from the new EFI Device Path
787 //
788
789 //
790 // Load the FDT given its device path.
791 // This operation may fail if the device path is not supported.
792 //
793 FdtBlobBase = 0;
794 NumPages = 0;
795 Status = BdsLoadImage (FdtDevicePath, AllocateAnyPages, &FdtBlobBase, &FdtBlobSize);
796 FreePool (FdtDevicePath);
797
798 if (EFI_ERROR (Status)) {
799 goto EXIT_LOAD_FDT;
800 }
801
802 // Check the FDT header is valid. We only make this check in DEBUG mode in
803 // case the FDT header change on production device and this ASSERT() becomes
804 // not valid.
805 ASSERT (fdt_check_header ((VOID*)(UINTN)FdtBlobBase) == 0);
806
807 //
808 // Ensure the Size of the Device Tree is smaller than the size of the read file
809 //
810 ASSERT ((UINTN)fdt_totalsize ((VOID*)(UINTN)FdtBlobBase) <= FdtBlobSize);
811
812 //
813 // Store the FDT as Runtime Service Data to prevent the Kernel from
814 // overwritting its data.
815 //
816 NumPages = EFI_SIZE_TO_PAGES (FdtBlobSize);
817 Status = gBS->AllocatePages (
818 AllocateAnyPages, EfiRuntimeServicesData,
819 NumPages, &FdtConfigurationTableBase
820 );
821 if (EFI_ERROR (Status)) {
822 goto EXIT_LOAD_FDT;
823 }
824 gBS->CopyMem (
825 (VOID*)(UINTN)FdtConfigurationTableBase,
826 (VOID*)(UINTN)FdtBlobBase,
827 FdtBlobSize
828 );
829
830 //
831 // Install the FDT into the Configuration Table
832 //
833 Status = gBS->InstallConfigurationTable (
834 &gFdtTableGuid,
835 (VOID*)(UINTN)FdtConfigurationTableBase
836 );
837 if (EFI_ERROR (Status)) {
838 gBS->FreePages (FdtConfigurationTableBase, NumPages);
839 }
840
841 EXIT_LOAD_FDT:
842 if (EFI_ERROR (Status)) {
843 Print (L"\nWarning: Did not manage to install the new device tree. Try to restart the platform.\n");
844 }
845
846 if (FdtBlobBase != 0) {
847 gBS->FreePages (FdtBlobBase, NumPages);
848 }
849
850 EXIT:
851 if (Status == EFI_ABORTED) {
852 Print (L"\n");
853 }
854
855 if (SupportedBootDevice != NULL) {
856 FreePool (SupportedBootDevice);
857 }
858
859 return Status;
860 }
861
862 /**
863 Set boot timeout
864
865 Ask for the boot timeout in seconds and if the input succeeds assign the
866 input value to the UEFI global variable "Timeout". This function is called
867 when the user selects the "Set Boot Timeout" of the boot manager menu.
868
869 @param[in] BootOptionsList List of the boot devices, not used here
870
871 @retval EFI_SUCCESS Boot timeout in second retrieved from the standard
872 input and assigned to the UEFI "Timeout" global
873 variable
874 @retval !EFI_SUCCESS Either the input or the setting of the UEFI global
875 variable "Timeout" has failed.
876 **/
877 EFI_STATUS
878 STATIC
879 BootMenuSetBootTimeout (
880 IN LIST_ENTRY *BootOptionsList
881 )
882 {
883 EFI_STATUS Status;
884 UINTN Input;
885 UINT16 Timeout;
886
887 Print (L"Timeout duration (in seconds): ");
888 Status = GetHIInputInteger (&Input);
889 if (EFI_ERROR (Status)) {
890 Print (L"\n");
891 goto ErrorExit;
892 }
893
894 Timeout = Input;
895 Status = gRT->SetVariable (
896 (CHAR16*)L"Timeout",
897 &gEfiGlobalVariableGuid,
898 EFI_VARIABLE_NON_VOLATILE |
899 EFI_VARIABLE_BOOTSERVICE_ACCESS |
900 EFI_VARIABLE_RUNTIME_ACCESS,
901 sizeof (UINT16),
902 &Timeout
903 );
904 ASSERT_EFI_ERROR (Status);
905
906 ErrorExit:
907 return Status;
908 }
909
910 struct BOOT_MANAGER_ENTRY {
911 CONST CHAR16* Description;
912 EFI_STATUS (*Callback) (IN LIST_ENTRY *BootOptionsList);
913 } BootManagerEntries[] = {
914 { L"Add Boot Device Entry", BootMenuAddBootOption },
915 { L"Update Boot Device Entry", BootMenuUpdateBootOption },
916 { L"Remove Boot Device Entry", BootMenuRemoveBootOption },
917 { L"Reorder Boot Device Entries", BootMenuReorderBootOptions },
918 { L"Update FDT path", UpdateFdtPath },
919 { L"Set Boot Timeout", BootMenuSetBootTimeout },
920 };
921
922 EFI_STATUS
923 BootMenuManager (
924 IN LIST_ENTRY *BootOptionsList
925 )
926 {
927 UINTN Index;
928 UINTN OptionSelected;
929 UINTN BootManagerEntryCount;
930 EFI_STATUS Status;
931
932 BootManagerEntryCount = sizeof(BootManagerEntries) / sizeof(struct BOOT_MANAGER_ENTRY);
933
934 while (TRUE) {
935 // Display Boot Manager menu
936 for (Index = 0; Index < BootManagerEntryCount; Index++) {
937 Print(L"[%d] %s\n",Index+1,BootManagerEntries[Index]);
938 }
939 Print(L"[%d] Return to main menu\n",Index+1);
940
941 // Select which entry to call
942 Print(L"Choice: ");
943 Status = GetHIInputInteger (&OptionSelected);
944 if (EFI_ERROR(Status) || (OptionSelected == (BootManagerEntryCount+1))) {
945 if (EFI_ERROR(Status)) {
946 Print(L"\n");
947 }
948 return EFI_SUCCESS;
949 } else if ((OptionSelected > 0) && (OptionSelected <= BootManagerEntryCount)) {
950 BootManagerEntries[OptionSelected-1].Callback (BootOptionsList);
951 }
952 }
953 // Should never go here
954 }
955
956 EFI_STATUS
957 BootShell (
958 IN LIST_ENTRY *BootOptionsList
959 )
960 {
961 EFI_STATUS Status;
962 EFI_DEVICE_PATH* EfiShellDevicePath;
963
964 // Find the EFI Shell
965 Status = LocateEfiApplicationInFvByName (L"Shell", &EfiShellDevicePath);
966 if (Status == EFI_NOT_FOUND) {
967 Print (L"Error: EFI Application not found.\n");
968 return Status;
969 } else if (EFI_ERROR (Status)) {
970 Print (L"Error: Status Code: 0x%X\n", (UINT32)Status);
971 return Status;
972 } else {
973 // Need to connect every drivers to ensure no dependencies are missing for the application
974 Status = BdsConnectAllDrivers ();
975 if (EFI_ERROR (Status)) {
976 DEBUG ((EFI_D_ERROR, "FAIL to connect all drivers\n"));
977 return Status;
978 }
979
980 return BdsStartEfiApplication (gImageHandle, EfiShellDevicePath, 0, NULL);
981 }
982 }
983
984 struct BOOT_MAIN_ENTRY {
985 CONST CHAR16* Description;
986 EFI_STATUS (*Callback) (IN LIST_ENTRY *BootOptionsList);
987 } BootMainEntries[] = {
988 { L"Shell", BootShell },
989 { L"Boot Manager", BootMenuManager },
990 };
991
992 EFI_STATUS
993 BootMenuMain (
994 VOID
995 )
996 {
997 LIST_ENTRY BootOptionsList;
998 UINTN OptionCount;
999 UINTN BootOptionCount;
1000 EFI_STATUS Status;
1001 LIST_ENTRY* Entry;
1002 BDS_LOAD_OPTION* BootOption;
1003 UINTN BootOptionSelected;
1004 UINTN Index;
1005 UINTN BootMainEntryCount;
1006 BOOLEAN IsUnicode;
1007
1008 BootOption = NULL;
1009 BootMainEntryCount = sizeof(BootMainEntries) / sizeof(struct BOOT_MAIN_ENTRY);
1010
1011 if (FeaturePcdGet (PcdBdsLinuxSupport)) {
1012 // Check Linux Loader is present
1013 Status = LocateEfiApplicationInFvByGuid (&mLinuxLoaderAppGuid, &mLinuxLoaderDevicePath);
1014 ASSERT_EFI_ERROR (Status);
1015 }
1016
1017 while (TRUE) {
1018 // Get Boot#### list
1019 BootOptionList (&BootOptionsList);
1020
1021 OptionCount = 1;
1022
1023 // Display the Boot options
1024 for (Entry = GetFirstNode (&BootOptionsList);
1025 !IsNull (&BootOptionsList,Entry);
1026 Entry = GetNextNode (&BootOptionsList,Entry)
1027 )
1028 {
1029 BootOption = LOAD_OPTION_FROM_LINK(Entry);
1030
1031 Print(L"[%d] %s\n", OptionCount, BootOption->Description);
1032
1033 DEBUG_CODE_BEGIN();
1034 CHAR16* DevicePathTxt;
1035 EFI_DEVICE_PATH_TO_TEXT_PROTOCOL* DevicePathToTextProtocol;
1036
1037 Status = gBS->LocateProtocol (&gEfiDevicePathToTextProtocolGuid, NULL, (VOID **)&DevicePathToTextProtocol);
1038 if (EFI_ERROR(Status)) {
1039 // You must provide an implementation of DevicePathToTextProtocol in your firmware (eg: DevicePathDxe)
1040 DEBUG((EFI_D_ERROR,"Error: Bds requires DevicePathToTextProtocol\n"));
1041 return Status;
1042 }
1043 DevicePathTxt = DevicePathToTextProtocol->ConvertDevicePathToText (BootOption->FilePathList, TRUE, TRUE);
1044
1045 Print(L"\t- %s\n",DevicePathTxt);
1046
1047 if (BootOption->OptionalData != NULL) {
1048 if (IsPrintableString (BootOption->OptionalData, &IsUnicode)) {
1049 if (IsUnicode) {
1050 Print (L"\t- Arguments: %s\n", BootOption->OptionalData);
1051 } else {
1052 AsciiPrint ("\t- Arguments: %a\n", BootOption->OptionalData);
1053 }
1054 }
1055 }
1056 FreePool(DevicePathTxt);
1057 DEBUG_CODE_END();
1058
1059 OptionCount++;
1060 }
1061 BootOptionCount = OptionCount-1;
1062
1063 // Display the hardcoded Boot entries
1064 for (Index = 0; Index < BootMainEntryCount; Index++) {
1065 Print(L"[%d] %s\n",OptionCount,BootMainEntries[Index]);
1066 OptionCount++;
1067 }
1068
1069 // Request the boot entry from the user
1070 BootOptionSelected = 0;
1071 while (BootOptionSelected == 0) {
1072 Print(L"Start: ");
1073 Status = GetHIInputInteger (&BootOptionSelected);
1074 if (EFI_ERROR(Status) || (BootOptionSelected == 0) || (BootOptionSelected > OptionCount)) {
1075 Print(L"Invalid input (max %d)\n",(OptionCount-1));
1076 BootOptionSelected = 0;
1077 }
1078 }
1079
1080 // Start the selected entry
1081 if (BootOptionSelected > BootOptionCount) {
1082 // Start the hardcoded entry
1083 Status = BootMainEntries[BootOptionSelected - BootOptionCount - 1].Callback (&BootOptionsList);
1084 } else {
1085 // Find the selected entry from the Boot#### list
1086 Index = 1;
1087 for (Entry = GetFirstNode (&BootOptionsList);
1088 !IsNull (&BootOptionsList,Entry);
1089 Entry = GetNextNode (&BootOptionsList,Entry)
1090 )
1091 {
1092 if (Index == BootOptionSelected) {
1093 BootOption = LOAD_OPTION_FROM_LINK(Entry);
1094 break;
1095 }
1096 Index++;
1097 }
1098
1099 Status = BootOptionStart (BootOption);
1100 }
1101 }
1102 // Should never go here
1103 }