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