Home | History | Annotate | Download | only in Bds
      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 }
   1102