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
DisplayBootOptions(IN LIST_ENTRY * BootOptionsList)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
SelectBootOption(IN LIST_ENTRY * BootOptionsList,IN CONST CHAR16 * InputStatement,OUT BDS_LOAD_OPTION_ENTRY ** BdsLoadOptionEntry)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
SelectBootDevice(OUT BDS_SUPPORTED_DEVICE ** SupportedBootDevice)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
BootMenuAddBootOption(IN LIST_ENTRY * BootOptionsList)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
BootMenuRemoveBootOption(IN LIST_ENTRY * BootOptionsList)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
BootMenuUpdateBootOption(IN LIST_ENTRY * BootOptionsList)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
BootMenuReorderBootOptions(IN LIST_ENTRY * BootOptionsList)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
UpdateFdtPath(IN LIST_ENTRY * BootOptionsList)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
BootMenuSetBootTimeout(IN LIST_ENTRY * BootOptionsList)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
BootMenuManager(IN LIST_ENTRY * BootOptionsList)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
BootShell(IN LIST_ENTRY * BootOptionsList)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
BootMenuMain(VOID)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 }
1102