Remove the option to update the "Fdt" UEFI variable in the ARM BDS as the "setfdt" EFI Shell command provides this service from now. Remove the use of this variable in the legacy kernel boot loader and use the FDT installed in the configuration table instead. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Ronald Cron <Ronald.Cron@arm.com> Reviewed-by: Olivier Martin <olivier.martin@arm.com> git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@16940 6f19259b-4bc3-4df7-8a09-765794883524
1088 lines
36 KiB
C
1088 lines
36 KiB
C
/** @file
|
|
*
|
|
* Copyright (c) 2011 - 2015, ARM Limited. All rights reserved.
|
|
*
|
|
* This program and the accompanying materials
|
|
* are licensed and made available under the terms and conditions of the BSD License
|
|
* which accompanies this distribution. The full text of the license may be found at
|
|
* http://opensource.org/licenses/bsd-license.php
|
|
*
|
|
* THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
|
*
|
|
**/
|
|
|
|
#include "BdsInternal.h"
|
|
|
|
#include <Guid/ArmGlobalVariableHob.h>
|
|
|
|
extern BDS_LOAD_OPTION_SUPPORT *BdsLoadOptionSupportList;
|
|
|
|
/**
|
|
Worker function that displays the list of boot options that is passed in.
|
|
|
|
The function loops over the entries of the list of boot options that is passed
|
|
in. For each entry, the boot option description is displayed on a single line
|
|
along with the position of the option in the list. In debug mode, the UEFI
|
|
device path and the arguments of the boot option are displayed as well in
|
|
subsequent lines.
|
|
|
|
@param[in] BootOptionsList List of the boot options
|
|
|
|
**/
|
|
STATIC
|
|
VOID
|
|
DisplayBootOptions (
|
|
IN LIST_ENTRY* BootOptionsList
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN BootOptionCount;
|
|
LIST_ENTRY *Entry;
|
|
BDS_LOAD_OPTION *BdsLoadOption;
|
|
BOOLEAN IsUnicode;
|
|
|
|
BootOptionCount = 0 ;
|
|
for (Entry = GetFirstNode (BootOptionsList);
|
|
!IsNull (BootOptionsList, Entry);
|
|
Entry = GetNextNode (BootOptionsList, Entry)
|
|
) {
|
|
|
|
BdsLoadOption = LOAD_OPTION_FROM_LINK (Entry);
|
|
Print (L"[%d] %s\n", ++BootOptionCount, BdsLoadOption->Description);
|
|
|
|
DEBUG_CODE_BEGIN ();
|
|
CHAR16* DevicePathTxt;
|
|
EFI_DEVICE_PATH_TO_TEXT_PROTOCOL* DevicePathToTextProtocol;
|
|
ARM_BDS_LOADER_TYPE LoaderType;
|
|
ARM_BDS_LOADER_OPTIONAL_DATA* OptionalData;
|
|
|
|
Status = gBS->LocateProtocol (
|
|
&gEfiDevicePathToTextProtocolGuid,
|
|
NULL,
|
|
(VOID **)&DevicePathToTextProtocol
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
DevicePathTxt = DevicePathToTextProtocol->ConvertDevicePathToText (
|
|
BdsLoadOption->FilePathList,
|
|
TRUE,
|
|
TRUE
|
|
);
|
|
Print (L"\t- %s\n", DevicePathTxt);
|
|
|
|
OptionalData = BdsLoadOption->OptionalData;
|
|
if (IS_ARM_BDS_BOOTENTRY (BdsLoadOption)) {
|
|
LoaderType = (ARM_BDS_LOADER_TYPE)ReadUnaligned32 ((CONST UINT32*)&OptionalData->Header.LoaderType);
|
|
if ((LoaderType == BDS_LOADER_KERNEL_LINUX_ATAG) ||
|
|
(LoaderType == BDS_LOADER_KERNEL_LINUX_FDT ) ) {
|
|
Print (L"\t- Arguments: %a\n", &OptionalData->Arguments.LinuxArguments + 1);
|
|
}
|
|
} else if (OptionalData != NULL) {
|
|
if (IsPrintableString (OptionalData, &IsUnicode)) {
|
|
if (IsUnicode) {
|
|
Print (L"\t- Arguments: %s\n", OptionalData);
|
|
} else {
|
|
AsciiPrint ("\t- Arguments: %a\n", OptionalData);
|
|
}
|
|
}
|
|
}
|
|
|
|
FreePool (DevicePathTxt);
|
|
DEBUG_CODE_END ();
|
|
}
|
|
}
|
|
|
|
/**
|
|
Worker function that asks for a boot option to be selected and returns a
|
|
pointer to the structure describing the selected boot option.
|
|
|
|
@param[in] BootOptionsList List of the boot options
|
|
|
|
@retval EFI_SUCCESS Selection succeeded
|
|
@retval !EFI_SUCCESS Input error or input cancelled
|
|
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
SelectBootOption (
|
|
IN LIST_ENTRY* BootOptionsList,
|
|
IN CONST CHAR16* InputStatement,
|
|
OUT BDS_LOAD_OPTION_ENTRY** BdsLoadOptionEntry
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN BootOptionCount;
|
|
UINT16 *BootOrder;
|
|
LIST_ENTRY* Entry;
|
|
UINTN BootOptionSelected;
|
|
UINTN Index;
|
|
|
|
// Get the number of boot options
|
|
Status = GetGlobalEnvironmentVariable (
|
|
L"BootOrder", NULL, &BootOptionCount, (VOID**)&BootOrder
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
goto ErrorExit;
|
|
}
|
|
FreePool (BootOrder);
|
|
BootOptionCount /= sizeof (UINT16);
|
|
|
|
// Check if a valid boot option(s) is found
|
|
if (BootOptionCount == 0) {
|
|
if (StrCmp (InputStatement, DELETE_BOOT_ENTRY) == 0) {
|
|
Print (L"Nothing to remove!\n");
|
|
} else if (StrCmp (InputStatement, UPDATE_BOOT_ENTRY) == 0) {
|
|
Print (L"Nothing to update!\n");
|
|
} else if (StrCmp (InputStatement, MOVE_BOOT_ENTRY) == 0) {
|
|
Print (L"Nothing to move!\n");
|
|
} else {
|
|
Print (L"No supported Boot Entry.\n");
|
|
}
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
// Get the index of the boot device to delete
|
|
BootOptionSelected = 0;
|
|
while (BootOptionSelected == 0) {
|
|
Print (InputStatement);
|
|
Status = GetHIInputInteger (&BootOptionSelected);
|
|
if (EFI_ERROR (Status)) {
|
|
Print (L"\n");
|
|
goto ErrorExit;
|
|
} else if ((BootOptionSelected == 0) || (BootOptionSelected > BootOptionCount)) {
|
|
Print (L"Invalid input (max %d)\n", BootOptionCount);
|
|
BootOptionSelected = 0;
|
|
}
|
|
}
|
|
|
|
// Get the structure of the Boot device to delete
|
|
Index = 1;
|
|
for (Entry = GetFirstNode (BootOptionsList);
|
|
!IsNull (BootOptionsList, Entry);
|
|
Entry = GetNextNode (BootOptionsList,Entry)
|
|
)
|
|
{
|
|
if (Index == BootOptionSelected) {
|
|
*BdsLoadOptionEntry = LOAD_OPTION_ENTRY_FROM_LINK (Entry);
|
|
break;
|
|
}
|
|
Index++;
|
|
}
|
|
|
|
ErrorExit:
|
|
return Status;
|
|
}
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
SelectBootDevice (
|
|
OUT BDS_SUPPORTED_DEVICE** SupportedBootDevice
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
LIST_ENTRY SupportedDeviceList;
|
|
UINTN SupportedDeviceCount;
|
|
LIST_ENTRY* Entry;
|
|
UINTN SupportedDeviceSelected;
|
|
UINTN Index;
|
|
|
|
//
|
|
// List the Boot Devices supported
|
|
//
|
|
|
|
// Start all the drivers first
|
|
BdsConnectAllDrivers ();
|
|
|
|
// List the supported devices
|
|
Status = BootDeviceListSupportedInit (&SupportedDeviceList);
|
|
ASSERT_EFI_ERROR(Status);
|
|
|
|
SupportedDeviceCount = 0;
|
|
for (Entry = GetFirstNode (&SupportedDeviceList);
|
|
!IsNull (&SupportedDeviceList,Entry);
|
|
Entry = GetNextNode (&SupportedDeviceList,Entry)
|
|
)
|
|
{
|
|
*SupportedBootDevice = SUPPORTED_BOOT_DEVICE_FROM_LINK(Entry);
|
|
Print(L"[%d] %s\n",SupportedDeviceCount+1,(*SupportedBootDevice)->Description);
|
|
|
|
DEBUG_CODE_BEGIN();
|
|
CHAR16* DevicePathTxt;
|
|
EFI_DEVICE_PATH_TO_TEXT_PROTOCOL* DevicePathToTextProtocol;
|
|
|
|
Status = gBS->LocateProtocol (&gEfiDevicePathToTextProtocolGuid, NULL, (VOID **)&DevicePathToTextProtocol);
|
|
ASSERT_EFI_ERROR(Status);
|
|
DevicePathTxt = DevicePathToTextProtocol->ConvertDevicePathToText ((*SupportedBootDevice)->DevicePathProtocol,TRUE,TRUE);
|
|
|
|
Print(L"\t- %s\n",DevicePathTxt);
|
|
|
|
FreePool(DevicePathTxt);
|
|
DEBUG_CODE_END();
|
|
|
|
SupportedDeviceCount++;
|
|
}
|
|
|
|
if (SupportedDeviceCount == 0) {
|
|
Print(L"There is no supported device.\n");
|
|
Status = EFI_ABORTED;
|
|
goto EXIT;
|
|
}
|
|
|
|
//
|
|
// Select the Boot Device
|
|
//
|
|
SupportedDeviceSelected = 0;
|
|
while (SupportedDeviceSelected == 0) {
|
|
Print(L"Select the Boot Device: ");
|
|
Status = GetHIInputInteger (&SupportedDeviceSelected);
|
|
if (EFI_ERROR(Status)) {
|
|
Status = EFI_ABORTED;
|
|
goto EXIT;
|
|
} else if ((SupportedDeviceSelected == 0) || (SupportedDeviceSelected > SupportedDeviceCount)) {
|
|
Print(L"Invalid input (max %d)\n",SupportedDeviceCount);
|
|
SupportedDeviceSelected = 0;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get the Device Path for the selected boot device
|
|
//
|
|
Index = 1;
|
|
for (Entry = GetFirstNode (&SupportedDeviceList);
|
|
!IsNull (&SupportedDeviceList,Entry);
|
|
Entry = GetNextNode (&SupportedDeviceList,Entry)
|
|
)
|
|
{
|
|
if (Index == SupportedDeviceSelected) {
|
|
*SupportedBootDevice = SUPPORTED_BOOT_DEVICE_FROM_LINK(Entry);
|
|
break;
|
|
}
|
|
Index++;
|
|
}
|
|
|
|
EXIT:
|
|
BootDeviceListSupportedFree (&SupportedDeviceList, *SupportedBootDevice);
|
|
return Status;
|
|
}
|
|
|
|
EFI_STATUS
|
|
BootMenuAddBootOption (
|
|
IN LIST_ENTRY *BootOptionsList
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
BDS_SUPPORTED_DEVICE* SupportedBootDevice;
|
|
ARM_BDS_LOADER_ARGUMENTS* BootArguments;
|
|
CHAR16 BootDescription[BOOT_DEVICE_DESCRIPTION_MAX];
|
|
CHAR8 AsciiCmdLine[BOOT_DEVICE_OPTION_MAX];
|
|
CHAR16 CmdLine[BOOT_DEVICE_OPTION_MAX];
|
|
UINT32 Attributes;
|
|
ARM_BDS_LOADER_TYPE BootType;
|
|
BDS_LOAD_OPTION_ENTRY *BdsLoadOptionEntry;
|
|
EFI_DEVICE_PATH *DevicePath;
|
|
EFI_DEVICE_PATH_PROTOCOL *DevicePathNodes;
|
|
EFI_DEVICE_PATH_PROTOCOL *InitrdPathNodes;
|
|
EFI_DEVICE_PATH_PROTOCOL *InitrdPath;
|
|
UINTN CmdLineSize;
|
|
BOOLEAN InitrdSupport;
|
|
UINTN InitrdSize;
|
|
UINT8* OptionalData;
|
|
UINTN OptionalDataSize;
|
|
|
|
Attributes = 0;
|
|
SupportedBootDevice = NULL;
|
|
|
|
// List the Boot Devices supported
|
|
Status = SelectBootDevice (&SupportedBootDevice);
|
|
if (EFI_ERROR(Status)) {
|
|
Status = EFI_ABORTED;
|
|
goto EXIT;
|
|
}
|
|
|
|
// Create the specific device path node
|
|
Status = SupportedBootDevice->Support->CreateDevicePathNode (L"EFI Application or the kernel", &DevicePathNodes);
|
|
if (EFI_ERROR(Status)) {
|
|
Status = EFI_ABORTED;
|
|
goto EXIT;
|
|
}
|
|
// Append the Device Path to the selected device path
|
|
DevicePath = AppendDevicePath (SupportedBootDevice->DevicePathProtocol, (CONST EFI_DEVICE_PATH_PROTOCOL *)DevicePathNodes);
|
|
if (DevicePath == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto EXIT;
|
|
}
|
|
|
|
if (SupportedBootDevice->Support->RequestBootType) {
|
|
Status = BootDeviceGetType (DevicePath, &BootType, &Attributes);
|
|
if (EFI_ERROR(Status)) {
|
|
Status = EFI_ABORTED;
|
|
goto EXIT;
|
|
}
|
|
} else {
|
|
BootType = BDS_LOADER_EFI_APPLICATION;
|
|
}
|
|
|
|
if ((BootType == BDS_LOADER_KERNEL_LINUX_ATAG) || (BootType == BDS_LOADER_KERNEL_LINUX_FDT)) {
|
|
Print(L"Add an initrd: ");
|
|
Status = GetHIInputBoolean (&InitrdSupport);
|
|
if (EFI_ERROR(Status)) {
|
|
Status = EFI_ABORTED;
|
|
goto EXIT;
|
|
}
|
|
|
|
if (InitrdSupport) {
|
|
// Create the specific device path node
|
|
Status = SupportedBootDevice->Support->CreateDevicePathNode (L"initrd", &InitrdPathNodes);
|
|
if (EFI_ERROR(Status) && Status != EFI_NOT_FOUND) { // EFI_NOT_FOUND is returned on empty input string, but we can boot without an initrd
|
|
Status = EFI_ABORTED;
|
|
goto EXIT;
|
|
}
|
|
|
|
if (InitrdPathNodes != NULL) {
|
|
// Append the Device Path to the selected device path
|
|
InitrdPath = AppendDevicePath (SupportedBootDevice->DevicePathProtocol, (CONST EFI_DEVICE_PATH_PROTOCOL *)InitrdPathNodes);
|
|
// Free the InitrdPathNodes created by Support->CreateDevicePathNode()
|
|
FreePool (InitrdPathNodes);
|
|
|
|
if (InitrdPath == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto EXIT;
|
|
}
|
|
} else {
|
|
InitrdPath = NULL;
|
|
}
|
|
} else {
|
|
InitrdPath = NULL;
|
|
}
|
|
|
|
Print(L"Arguments to pass to the binary: ");
|
|
Status = GetHIInputAscii (AsciiCmdLine, BOOT_DEVICE_OPTION_MAX);
|
|
if (EFI_ERROR(Status)) {
|
|
Status = EFI_ABORTED;
|
|
goto FREE_DEVICE_PATH;
|
|
}
|
|
|
|
CmdLineSize = AsciiStrSize (AsciiCmdLine);
|
|
InitrdSize = GetDevicePathSize (InitrdPath);
|
|
|
|
OptionalDataSize = sizeof(ARM_BDS_LOADER_ARGUMENTS) + CmdLineSize + InitrdSize;
|
|
BootArguments = (ARM_BDS_LOADER_ARGUMENTS*)AllocatePool (OptionalDataSize);
|
|
|
|
BootArguments->LinuxArguments.CmdLineSize = CmdLineSize;
|
|
BootArguments->LinuxArguments.InitrdSize = InitrdSize;
|
|
CopyMem ((VOID*)(&BootArguments->LinuxArguments + 1), AsciiCmdLine, CmdLineSize);
|
|
CopyMem ((VOID*)((UINTN)(&BootArguments->LinuxArguments + 1) + CmdLineSize), InitrdPath, InitrdSize);
|
|
|
|
OptionalData = (UINT8*)BootArguments;
|
|
} else {
|
|
Print (L"Arguments to pass to the EFI Application: ");
|
|
Status = GetHIInputStr (CmdLine, BOOT_DEVICE_OPTION_MAX);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_ABORTED;
|
|
goto EXIT;
|
|
}
|
|
|
|
OptionalData = (UINT8*)CmdLine;
|
|
OptionalDataSize = StrSize (CmdLine);
|
|
}
|
|
|
|
Print(L"Description for this new Entry: ");
|
|
Status = GetHIInputStr (BootDescription, BOOT_DEVICE_DESCRIPTION_MAX);
|
|
if (EFI_ERROR(Status)) {
|
|
Status = EFI_ABORTED;
|
|
goto FREE_DEVICE_PATH;
|
|
}
|
|
|
|
// Create new entry
|
|
BdsLoadOptionEntry = (BDS_LOAD_OPTION_ENTRY*)AllocatePool (sizeof(BDS_LOAD_OPTION_ENTRY));
|
|
Status = BootOptionCreate (Attributes, BootDescription, DevicePath, BootType, OptionalData, OptionalDataSize, &BdsLoadOptionEntry->BdsLoadOption);
|
|
if (!EFI_ERROR(Status)) {
|
|
InsertTailList (BootOptionsList, &BdsLoadOptionEntry->Link);
|
|
}
|
|
|
|
FREE_DEVICE_PATH:
|
|
FreePool (DevicePath);
|
|
|
|
EXIT:
|
|
if (Status == EFI_ABORTED) {
|
|
Print(L"\n");
|
|
}
|
|
FreePool(SupportedBootDevice);
|
|
return Status;
|
|
}
|
|
|
|
EFI_STATUS
|
|
BootMenuRemoveBootOption (
|
|
IN LIST_ENTRY *BootOptionsList
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
BDS_LOAD_OPTION_ENTRY* BootOptionEntry;
|
|
|
|
DisplayBootOptions (BootOptionsList);
|
|
Status = SelectBootOption (BootOptionsList, DELETE_BOOT_ENTRY, &BootOptionEntry);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
// If the Boot Option was attached to a list remove it
|
|
if (!IsListEmpty (&BootOptionEntry->Link)) {
|
|
// Remove the entry from the list
|
|
RemoveEntryList (&BootOptionEntry->Link);
|
|
}
|
|
|
|
// Delete the BDS Load option structures
|
|
BootOptionDelete (BootOptionEntry->BdsLoadOption);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
EFI_STATUS
|
|
BootMenuUpdateBootOption (
|
|
IN LIST_ENTRY *BootOptionsList
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
BDS_LOAD_OPTION_ENTRY *BootOptionEntry;
|
|
BDS_LOAD_OPTION *BootOption;
|
|
BDS_LOAD_OPTION_SUPPORT* DeviceSupport;
|
|
ARM_BDS_LOADER_ARGUMENTS* BootArguments;
|
|
CHAR16 BootDescription[BOOT_DEVICE_DESCRIPTION_MAX];
|
|
CHAR8 CmdLine[BOOT_DEVICE_OPTION_MAX];
|
|
CHAR16 UnicodeCmdLine[BOOT_DEVICE_OPTION_MAX];
|
|
EFI_DEVICE_PATH *DevicePath;
|
|
EFI_DEVICE_PATH *TempInitrdPath;
|
|
ARM_BDS_LOADER_TYPE BootType;
|
|
ARM_BDS_LOADER_OPTIONAL_DATA* LoaderOptionalData;
|
|
ARM_BDS_LINUX_ARGUMENTS* LinuxArguments;
|
|
EFI_DEVICE_PATH *InitrdPathNodes;
|
|
EFI_DEVICE_PATH *InitrdPath;
|
|
UINTN InitrdSize;
|
|
UINTN CmdLineSize;
|
|
BOOLEAN InitrdSupport;
|
|
UINT8* OptionalData;
|
|
UINTN OptionalDataSize;
|
|
BOOLEAN IsPrintable;
|
|
BOOLEAN IsUnicode;
|
|
|
|
DisplayBootOptions (BootOptionsList);
|
|
Status = SelectBootOption (BootOptionsList, UPDATE_BOOT_ENTRY, &BootOptionEntry);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
BootOption = BootOptionEntry->BdsLoadOption;
|
|
|
|
// Get the device support for this Boot Option
|
|
Status = BootDeviceGetDeviceSupport (BootOption->FilePathList, &DeviceSupport);
|
|
if (EFI_ERROR(Status)) {
|
|
Print(L"Not possible to retrieve the supported device for the update\n");
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
Status = DeviceSupport->UpdateDevicePathNode (BootOption->FilePathList, L"EFI Application or the kernel", &DevicePath);
|
|
if (EFI_ERROR(Status)) {
|
|
Status = EFI_ABORTED;
|
|
goto EXIT;
|
|
}
|
|
|
|
if (DeviceSupport->RequestBootType) {
|
|
Status = BootDeviceGetType (DevicePath, &BootType, &BootOption->Attributes);
|
|
if (EFI_ERROR(Status)) {
|
|
Status = EFI_ABORTED;
|
|
goto EXIT;
|
|
}
|
|
}
|
|
|
|
LoaderOptionalData = BootOption->OptionalData;
|
|
if (LoaderOptionalData != NULL) {
|
|
BootType = (ARM_BDS_LOADER_TYPE)ReadUnaligned32 ((UINT32 *)(&LoaderOptionalData->Header.LoaderType));
|
|
} else {
|
|
BootType = BDS_LOADER_EFI_APPLICATION;
|
|
}
|
|
|
|
if ((BootType == BDS_LOADER_KERNEL_LINUX_ATAG) || (BootType == BDS_LOADER_KERNEL_LINUX_FDT)) {
|
|
LinuxArguments = &LoaderOptionalData->Arguments.LinuxArguments;
|
|
|
|
CmdLineSize = ReadUnaligned16 ((CONST UINT16*)&LinuxArguments->CmdLineSize);
|
|
|
|
InitrdSize = ReadUnaligned16 ((CONST UINT16*)&LinuxArguments->InitrdSize);
|
|
if (InitrdSize > 0) {
|
|
Print(L"Keep the initrd: ");
|
|
} else {
|
|
Print(L"Add an initrd: ");
|
|
}
|
|
Status = GetHIInputBoolean (&InitrdSupport);
|
|
if (EFI_ERROR(Status)) {
|
|
Status = EFI_ABORTED;
|
|
goto EXIT;
|
|
}
|
|
|
|
if (InitrdSupport) {
|
|
if (InitrdSize > 0) {
|
|
// Case we update the initrd device path
|
|
Status = DeviceSupport->UpdateDevicePathNode ((EFI_DEVICE_PATH*)((UINTN)(LinuxArguments + 1) + CmdLineSize), L"initrd", &InitrdPath);
|
|
if (EFI_ERROR(Status) && Status != EFI_NOT_FOUND) {// EFI_NOT_FOUND is returned on empty input string, but we can boot without an initrd
|
|
Status = EFI_ABORTED;
|
|
goto EXIT;
|
|
}
|
|
InitrdSize = GetDevicePathSize (InitrdPath);
|
|
} else {
|
|
// Case we create the initrd device path
|
|
|
|
Status = DeviceSupport->CreateDevicePathNode (L"initrd", &InitrdPathNodes);
|
|
if (EFI_ERROR(Status) && Status != EFI_NOT_FOUND) { // EFI_NOT_FOUND is returned on empty input string, but we can boot without an initrd
|
|
Status = EFI_ABORTED;
|
|
goto EXIT;
|
|
}
|
|
|
|
if (InitrdPathNodes != NULL) {
|
|
// Duplicate Linux kernel Device Path
|
|
TempInitrdPath = DuplicateDevicePath (BootOption->FilePathList);
|
|
// Replace Linux kernel Node by EndNode
|
|
SetDevicePathEndNode (GetLastDevicePathNode (TempInitrdPath));
|
|
// Append the Device Path to the selected device path
|
|
InitrdPath = AppendDevicePath (TempInitrdPath, (CONST EFI_DEVICE_PATH_PROTOCOL *)InitrdPathNodes);
|
|
FreePool (TempInitrdPath);
|
|
// Free the InitrdPathNodes created by Support->CreateDevicePathNode()
|
|
FreePool (InitrdPathNodes);
|
|
if (InitrdPath == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto EXIT;
|
|
}
|
|
InitrdSize = GetDevicePathSize (InitrdPath);
|
|
} else {
|
|
InitrdPath = NULL;
|
|
}
|
|
}
|
|
} else {
|
|
InitrdSize = 0;
|
|
}
|
|
|
|
Print(L"Arguments to pass to the binary: ");
|
|
if (CmdLineSize > 0) {
|
|
AsciiStrnCpy (CmdLine, (CONST CHAR8*)(LinuxArguments + 1), sizeof (CmdLine));
|
|
CmdLine[sizeof (CmdLine) - 1] = '\0';
|
|
} else {
|
|
CmdLine[0] = '\0';
|
|
}
|
|
Status = EditHIInputAscii (CmdLine, BOOT_DEVICE_OPTION_MAX);
|
|
if (EFI_ERROR(Status)) {
|
|
Status = EFI_ABORTED;
|
|
goto FREE_DEVICE_PATH;
|
|
}
|
|
|
|
CmdLineSize = AsciiStrSize (CmdLine);
|
|
|
|
OptionalDataSize = sizeof(ARM_BDS_LOADER_ARGUMENTS) + CmdLineSize + InitrdSize;
|
|
BootArguments = (ARM_BDS_LOADER_ARGUMENTS*)AllocatePool (OptionalDataSize);
|
|
BootArguments->LinuxArguments.CmdLineSize = CmdLineSize;
|
|
BootArguments->LinuxArguments.InitrdSize = InitrdSize;
|
|
CopyMem (&BootArguments->LinuxArguments + 1, CmdLine, CmdLineSize);
|
|
CopyMem ((VOID*)((UINTN)(&BootArguments->LinuxArguments + 1) + CmdLineSize), InitrdPath, InitrdSize);
|
|
|
|
OptionalData = (UINT8*)BootArguments;
|
|
} else {
|
|
Print (L"Arguments to pass to the EFI Application: ");
|
|
|
|
if (BootOption->OptionalDataSize > 0) {
|
|
IsPrintable = IsPrintableString (BootOption->OptionalData, &IsUnicode);
|
|
if (IsPrintable) {
|
|
//
|
|
// The size in bytes of the string, final zero included, should
|
|
// be equal to or at least lower than "BootOption->OptionalDataSize"
|
|
// and the "IsPrintableString()" has already tested that the length
|
|
// in number of characters is smaller than BOOT_DEVICE_OPTION_MAX,
|
|
// final '\0' included. We can thus copy the string for editing
|
|
// using "CopyMem()". Furthermore, note that in the case of an Unicode
|
|
// string "StrnCpy()" and "StrCpy()" can not be used to copy the
|
|
// string because the data pointed to by "BootOption->OptionalData"
|
|
// is not necessarily 2-byte aligned.
|
|
//
|
|
if (IsUnicode) {
|
|
CopyMem (
|
|
UnicodeCmdLine, BootOption->OptionalData,
|
|
MIN (sizeof (UnicodeCmdLine),
|
|
BootOption->OptionalDataSize)
|
|
);
|
|
} else {
|
|
CopyMem (
|
|
CmdLine, BootOption->OptionalData,
|
|
MIN (sizeof (CmdLine),
|
|
BootOption->OptionalDataSize)
|
|
);
|
|
}
|
|
}
|
|
} else {
|
|
UnicodeCmdLine[0] = L'\0';
|
|
IsPrintable = TRUE;
|
|
IsUnicode = TRUE;
|
|
}
|
|
|
|
// We do not request arguments for OptionalData that cannot be printed
|
|
if (IsPrintable) {
|
|
if (IsUnicode) {
|
|
Status = EditHIInputStr (UnicodeCmdLine, BOOT_DEVICE_OPTION_MAX);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_ABORTED;
|
|
goto FREE_DEVICE_PATH;
|
|
}
|
|
|
|
OptionalData = (UINT8*)UnicodeCmdLine;
|
|
OptionalDataSize = StrSize (UnicodeCmdLine);
|
|
} else {
|
|
Status = EditHIInputAscii (CmdLine, BOOT_DEVICE_OPTION_MAX);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_ABORTED;
|
|
goto FREE_DEVICE_PATH;
|
|
}
|
|
|
|
OptionalData = (UINT8*)CmdLine;
|
|
OptionalDataSize = AsciiStrSize (CmdLine);
|
|
}
|
|
} else {
|
|
// We keep the former OptionalData
|
|
OptionalData = BootOption->OptionalData;
|
|
OptionalDataSize = BootOption->OptionalDataSize;
|
|
}
|
|
}
|
|
|
|
Print(L"Description for this new Entry: ");
|
|
StrnCpy (BootDescription, BootOption->Description, BOOT_DEVICE_DESCRIPTION_MAX);
|
|
Status = EditHIInputStr (BootDescription, BOOT_DEVICE_DESCRIPTION_MAX);
|
|
if (EFI_ERROR(Status)) {
|
|
Status = EFI_ABORTED;
|
|
goto FREE_DEVICE_PATH;
|
|
}
|
|
|
|
// Update the entry
|
|
Status = BootOptionUpdate (BootOption, BootOption->Attributes, BootDescription, DevicePath, BootType, OptionalData, OptionalDataSize);
|
|
|
|
FREE_DEVICE_PATH:
|
|
FreePool (DevicePath);
|
|
|
|
EXIT:
|
|
if (Status == EFI_ABORTED) {
|
|
Print(L"\n");
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Reorder boot options
|
|
|
|
Ask for the boot option to move and then move it when up or down arrows
|
|
are pressed. This function is called when the user selects the "Reorder Boot
|
|
Device Entries" entry in the boot manager menu.
|
|
The order of the boot options in BootOptionList and in the UEFI BootOrder
|
|
global variable are kept coherent until the user confirm his reordering (ie:
|
|
he does not exit by pressing escape).
|
|
|
|
@param[in] BootOptionsList List of the boot devices constructed in
|
|
BootMenuMain()
|
|
|
|
@retval EFI_SUCCESS No error encountered.
|
|
@retval !EFI_SUCCESS An error has occured either in the selection of the
|
|
boot option to move or while interacting with the user.
|
|
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
BootMenuReorderBootOptions (
|
|
IN LIST_ENTRY *BootOptionsList
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
BDS_LOAD_OPTION_ENTRY *BootOptionEntry;
|
|
LIST_ENTRY *SelectedEntry;
|
|
LIST_ENTRY *PrevEntry;
|
|
BOOLEAN Move;
|
|
BOOLEAN Save;
|
|
BOOLEAN Cancel;
|
|
UINTN WaitIndex;
|
|
EFI_INPUT_KEY Key;
|
|
LIST_ENTRY *SecondEntry;
|
|
UINTN BootOrderSize;
|
|
UINT16 *BootOrder;
|
|
LIST_ENTRY *Entry;
|
|
UINTN Index;
|
|
|
|
DisplayBootOptions (BootOptionsList);
|
|
|
|
// Ask to select the boot option to move
|
|
while (TRUE) {
|
|
Status = SelectBootOption (BootOptionsList, MOVE_BOOT_ENTRY, &BootOptionEntry);
|
|
if (EFI_ERROR (Status)) {
|
|
goto ErrorExit;
|
|
}
|
|
|
|
SelectedEntry = &BootOptionEntry->Link;
|
|
SecondEntry = NULL;
|
|
// Note down the previous entry in the list to be able to cancel changes
|
|
PrevEntry = GetPreviousNode (BootOptionsList, SelectedEntry);
|
|
|
|
// Start of interaction
|
|
while (TRUE) {
|
|
Print (
|
|
L"* Use up/down arrows to move the entry '%s'",
|
|
BootOptionEntry->BdsLoadOption->Description
|
|
);
|
|
|
|
// Wait for a move, save or cancel request
|
|
Move = FALSE;
|
|
Save = FALSE;
|
|
Cancel = FALSE;
|
|
do {
|
|
Status = gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &WaitIndex);
|
|
if (!EFI_ERROR (Status)) {
|
|
Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
|
|
}
|
|
if (EFI_ERROR (Status)) {
|
|
Print (L"\n");
|
|
goto ErrorExit;
|
|
}
|
|
|
|
switch (Key.ScanCode) {
|
|
case SCAN_NULL:
|
|
Save = (Key.UnicodeChar == CHAR_LINEFEED) ||
|
|
(Key.UnicodeChar == CHAR_CARRIAGE_RETURN) ||
|
|
(Key.UnicodeChar == 0x7f);
|
|
break;
|
|
|
|
case SCAN_UP:
|
|
SecondEntry = GetPreviousNode (BootOptionsList, SelectedEntry);
|
|
Move = SecondEntry != BootOptionsList;
|
|
break;
|
|
|
|
case SCAN_DOWN:
|
|
SecondEntry = GetNextNode (BootOptionsList, SelectedEntry);
|
|
Move = SecondEntry != BootOptionsList;
|
|
break;
|
|
|
|
case SCAN_ESC:
|
|
Cancel = TRUE;
|
|
break;
|
|
}
|
|
} while ((!Move) && (!Save) && (!Cancel));
|
|
|
|
if (Move) {
|
|
if ((SelectedEntry != NULL) && (SecondEntry != NULL)) {
|
|
SwapListEntries (SelectedEntry, SecondEntry);
|
|
}
|
|
} else {
|
|
if (Save) {
|
|
Status = GetGlobalEnvironmentVariable (
|
|
L"BootOrder", NULL, &BootOrderSize, (VOID**)&BootOrder
|
|
);
|
|
BootOrderSize /= sizeof (UINT16);
|
|
|
|
if (!EFI_ERROR (Status)) {
|
|
// The order of the boot options in the 'BootOptionsList' is the
|
|
// new order that has been just defined by the user. Save this new
|
|
// order in "BootOrder" UEFI global variable.
|
|
Entry = GetFirstNode (BootOptionsList);
|
|
for (Index = 0; Index < BootOrderSize; Index++) {
|
|
BootOrder[Index] = (LOAD_OPTION_FROM_LINK (Entry))->LoadOptionIndex;
|
|
Entry = GetNextNode (BootOptionsList, Entry);
|
|
}
|
|
Status = gRT->SetVariable (
|
|
(CHAR16*)L"BootOrder",
|
|
&gEfiGlobalVariableGuid,
|
|
EFI_VARIABLE_NON_VOLATILE |
|
|
EFI_VARIABLE_BOOTSERVICE_ACCESS |
|
|
EFI_VARIABLE_RUNTIME_ACCESS,
|
|
BootOrderSize * sizeof (UINT16),
|
|
BootOrder
|
|
);
|
|
FreePool (BootOrder);
|
|
}
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
Print (L"\nAn error occurred, move not completed!\n");
|
|
Cancel = TRUE;
|
|
}
|
|
}
|
|
|
|
if (Cancel) {
|
|
//
|
|
// Restore initial position of the selected boot option
|
|
//
|
|
RemoveEntryList (SelectedEntry);
|
|
InsertHeadList (PrevEntry, SelectedEntry);
|
|
}
|
|
}
|
|
|
|
Print (L"\n");
|
|
DisplayBootOptions (BootOptionsList);
|
|
// Saved or cancelled, back to the choice of boot option to move
|
|
if (!Move) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
ErrorExit:
|
|
return Status ;
|
|
}
|
|
|
|
/**
|
|
Set boot timeout
|
|
|
|
Ask for the boot timeout in seconds and if the input succeeds assign the
|
|
input value to the UEFI global variable "Timeout". This function is called
|
|
when the user selects the "Set Boot Timeout" of the boot manager menu.
|
|
|
|
@param[in] BootOptionsList List of the boot devices, not used here
|
|
|
|
@retval EFI_SUCCESS Boot timeout in second retrieved from the standard
|
|
input and assigned to the UEFI "Timeout" global
|
|
variable
|
|
@retval !EFI_SUCCESS Either the input or the setting of the UEFI global
|
|
variable "Timeout" has failed.
|
|
**/
|
|
EFI_STATUS
|
|
STATIC
|
|
BootMenuSetBootTimeout (
|
|
IN LIST_ENTRY *BootOptionsList
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN Input;
|
|
UINT16 Timeout;
|
|
|
|
Print (L"Timeout duration (in seconds): ");
|
|
Status = GetHIInputInteger (&Input);
|
|
if (EFI_ERROR (Status)) {
|
|
Print (L"\n");
|
|
goto ErrorExit;
|
|
}
|
|
|
|
Timeout = Input;
|
|
Status = gRT->SetVariable (
|
|
(CHAR16*)L"Timeout",
|
|
&gEfiGlobalVariableGuid,
|
|
EFI_VARIABLE_NON_VOLATILE |
|
|
EFI_VARIABLE_BOOTSERVICE_ACCESS |
|
|
EFI_VARIABLE_RUNTIME_ACCESS,
|
|
sizeof (UINT16),
|
|
&Timeout
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
ErrorExit:
|
|
return Status;
|
|
}
|
|
|
|
struct BOOT_MANAGER_ENTRY {
|
|
CONST CHAR16* Description;
|
|
EFI_STATUS (*Callback) (IN LIST_ENTRY *BootOptionsList);
|
|
} BootManagerEntries[] = {
|
|
{ L"Add Boot Device Entry", BootMenuAddBootOption },
|
|
{ L"Update Boot Device Entry", BootMenuUpdateBootOption },
|
|
{ L"Remove Boot Device Entry", BootMenuRemoveBootOption },
|
|
{ L"Reorder Boot Device Entries", BootMenuReorderBootOptions },
|
|
{ L"Set Boot Timeout", BootMenuSetBootTimeout },
|
|
};
|
|
|
|
EFI_STATUS
|
|
BootMenuManager (
|
|
IN LIST_ENTRY *BootOptionsList
|
|
)
|
|
{
|
|
UINTN Index;
|
|
UINTN OptionSelected;
|
|
UINTN BootManagerEntryCount;
|
|
EFI_STATUS Status;
|
|
|
|
BootManagerEntryCount = sizeof(BootManagerEntries) / sizeof(struct BOOT_MANAGER_ENTRY);
|
|
|
|
while (TRUE) {
|
|
// Display Boot Manager menu
|
|
for (Index = 0; Index < BootManagerEntryCount; Index++) {
|
|
Print(L"[%d] %s\n",Index+1,BootManagerEntries[Index]);
|
|
}
|
|
Print(L"[%d] Return to main menu\n",Index+1);
|
|
|
|
// Select which entry to call
|
|
Print(L"Choice: ");
|
|
Status = GetHIInputInteger (&OptionSelected);
|
|
if (EFI_ERROR(Status) || (OptionSelected == (BootManagerEntryCount+1))) {
|
|
if (EFI_ERROR(Status)) {
|
|
Print(L"\n");
|
|
}
|
|
return EFI_SUCCESS;
|
|
} else if ((OptionSelected > 0) && (OptionSelected <= BootManagerEntryCount)) {
|
|
BootManagerEntries[OptionSelected-1].Callback (BootOptionsList);
|
|
}
|
|
}
|
|
// Should never go here
|
|
}
|
|
|
|
EFI_STATUS
|
|
BootShell (
|
|
IN LIST_ENTRY *BootOptionsList
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
// Start EFI Shell
|
|
Status = BdsLoadApplication (gImageHandle, L"Shell", 0, NULL);
|
|
if (Status == EFI_NOT_FOUND) {
|
|
Print (L"Error: EFI Application not found.\n");
|
|
} else if (EFI_ERROR(Status)) {
|
|
Print (L"Error: Status Code: 0x%X\n",(UINT32)Status);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
struct BOOT_MAIN_ENTRY {
|
|
CONST CHAR16* Description;
|
|
EFI_STATUS (*Callback) (IN LIST_ENTRY *BootOptionsList);
|
|
} BootMainEntries[] = {
|
|
{ L"Shell", BootShell },
|
|
{ L"Boot Manager", BootMenuManager },
|
|
};
|
|
|
|
|
|
EFI_STATUS
|
|
BootMenuMain (
|
|
VOID
|
|
)
|
|
{
|
|
LIST_ENTRY BootOptionsList;
|
|
UINTN OptionCount;
|
|
UINTN BootOptionCount;
|
|
EFI_STATUS Status;
|
|
LIST_ENTRY* Entry;
|
|
BDS_LOAD_OPTION* BootOption;
|
|
UINTN BootOptionSelected;
|
|
UINTN Index;
|
|
UINTN BootMainEntryCount;
|
|
BOOLEAN IsUnicode;
|
|
|
|
BootOption = NULL;
|
|
BootMainEntryCount = sizeof(BootMainEntries) / sizeof(struct BOOT_MAIN_ENTRY);
|
|
|
|
while (TRUE) {
|
|
// Get Boot#### list
|
|
BootOptionList (&BootOptionsList);
|
|
|
|
OptionCount = 1;
|
|
|
|
// Display the Boot options
|
|
for (Entry = GetFirstNode (&BootOptionsList);
|
|
!IsNull (&BootOptionsList,Entry);
|
|
Entry = GetNextNode (&BootOptionsList,Entry)
|
|
)
|
|
{
|
|
BootOption = LOAD_OPTION_FROM_LINK(Entry);
|
|
|
|
Print(L"[%d] %s\n", OptionCount, BootOption->Description);
|
|
|
|
DEBUG_CODE_BEGIN();
|
|
CHAR16* DevicePathTxt;
|
|
EFI_DEVICE_PATH_TO_TEXT_PROTOCOL* DevicePathToTextProtocol;
|
|
ARM_BDS_LOADER_OPTIONAL_DATA* OptionalData;
|
|
UINTN CmdLineSize;
|
|
ARM_BDS_LOADER_TYPE LoaderType;
|
|
|
|
Status = gBS->LocateProtocol (&gEfiDevicePathToTextProtocolGuid, NULL, (VOID **)&DevicePathToTextProtocol);
|
|
if (EFI_ERROR(Status)) {
|
|
// You must provide an implementation of DevicePathToTextProtocol in your firmware (eg: DevicePathDxe)
|
|
DEBUG((EFI_D_ERROR,"Error: Bds requires DevicePathToTextProtocol\n"));
|
|
return Status;
|
|
}
|
|
DevicePathTxt = DevicePathToTextProtocol->ConvertDevicePathToText (BootOption->FilePathList, TRUE, TRUE);
|
|
|
|
Print(L"\t- %s\n",DevicePathTxt);
|
|
|
|
// If it is a supported BootEntry then print its details
|
|
if (IS_ARM_BDS_BOOTENTRY (BootOption)) {
|
|
OptionalData = BootOption->OptionalData;
|
|
LoaderType = (ARM_BDS_LOADER_TYPE)ReadUnaligned32 ((CONST UINT32*)&OptionalData->Header.LoaderType);
|
|
if ((LoaderType == BDS_LOADER_KERNEL_LINUX_ATAG) || (LoaderType == BDS_LOADER_KERNEL_LINUX_FDT)) {
|
|
if (ReadUnaligned16 (&OptionalData->Arguments.LinuxArguments.InitrdSize) > 0) {
|
|
CmdLineSize = ReadUnaligned16 (&OptionalData->Arguments.LinuxArguments.CmdLineSize);
|
|
DevicePathTxt = DevicePathToTextProtocol->ConvertDevicePathToText (
|
|
GetAlignedDevicePath ((EFI_DEVICE_PATH*)((UINTN)(&OptionalData->Arguments.LinuxArguments + 1) + CmdLineSize)), TRUE, TRUE);
|
|
Print(L"\t- Initrd: %s\n", DevicePathTxt);
|
|
}
|
|
if (ReadUnaligned16 (&OptionalData->Arguments.LinuxArguments.CmdLineSize) > 0) {
|
|
Print(L"\t- Arguments: %a\n", (&OptionalData->Arguments.LinuxArguments + 1));
|
|
}
|
|
}
|
|
|
|
switch (LoaderType) {
|
|
case BDS_LOADER_EFI_APPLICATION:
|
|
Print(L"\t- LoaderType: EFI Application\n");
|
|
break;
|
|
|
|
case BDS_LOADER_KERNEL_LINUX_ATAG:
|
|
Print(L"\t- LoaderType: Linux kernel with ATAG support\n");
|
|
break;
|
|
|
|
case BDS_LOADER_KERNEL_LINUX_FDT:
|
|
Print(L"\t- LoaderType: Linux kernel with FDT support\n");
|
|
break;
|
|
|
|
default:
|
|
Print(L"\t- LoaderType: Not recognized (%d)\n", LoaderType);
|
|
}
|
|
} else if (BootOption->OptionalData != NULL) {
|
|
if (IsPrintableString (BootOption->OptionalData, &IsUnicode)) {
|
|
if (IsUnicode) {
|
|
Print (L"\t- Arguments: %s\n", BootOption->OptionalData);
|
|
} else {
|
|
AsciiPrint ("\t- Arguments: %a\n", BootOption->OptionalData);
|
|
}
|
|
}
|
|
}
|
|
FreePool(DevicePathTxt);
|
|
DEBUG_CODE_END();
|
|
|
|
OptionCount++;
|
|
}
|
|
BootOptionCount = OptionCount-1;
|
|
|
|
// Display the hardcoded Boot entries
|
|
for (Index = 0; Index < BootMainEntryCount; Index++) {
|
|
Print(L"[%d] %s\n",OptionCount,BootMainEntries[Index]);
|
|
OptionCount++;
|
|
}
|
|
|
|
// Request the boot entry from the user
|
|
BootOptionSelected = 0;
|
|
while (BootOptionSelected == 0) {
|
|
Print(L"Start: ");
|
|
Status = GetHIInputInteger (&BootOptionSelected);
|
|
if (EFI_ERROR(Status) || (BootOptionSelected == 0) || (BootOptionSelected > OptionCount)) {
|
|
Print(L"Invalid input (max %d)\n",(OptionCount-1));
|
|
BootOptionSelected = 0;
|
|
}
|
|
}
|
|
|
|
// Start the selected entry
|
|
if (BootOptionSelected > BootOptionCount) {
|
|
// Start the hardcoded entry
|
|
Status = BootMainEntries[BootOptionSelected - BootOptionCount - 1].Callback (&BootOptionsList);
|
|
} else {
|
|
// Find the selected entry from the Boot#### list
|
|
Index = 1;
|
|
for (Entry = GetFirstNode (&BootOptionsList);
|
|
!IsNull (&BootOptionsList,Entry);
|
|
Entry = GetNextNode (&BootOptionsList,Entry)
|
|
)
|
|
{
|
|
if (Index == BootOptionSelected) {
|
|
BootOption = LOAD_OPTION_FROM_LINK(Entry);
|
|
break;
|
|
}
|
|
Index++;
|
|
}
|
|
|
|
Status = BootOptionStart (BootOption);
|
|
}
|
|
}
|
|
// Should never go here
|
|
}
|