/** @file
Driver for Platform Boot Options support.
Copyright (c) 2017, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "BdsPlatform.h"
#include
BOOLEAN mContinueBoot = FALSE;
BOOLEAN mBootMenuBoot = FALSE;
BOOLEAN mPxeBoot = FALSE;
BOOLEAN mHotKeypressed = FALSE;
EFI_EVENT HotKeyEvent = NULL;
UINTN mBootMenuOptionNumber;
EFI_DEVICE_PATH_PROTOCOL *
BdsCreateShellDevicePath (
VOID
)
/*++
Routine Description:
This function will create a SHELL BootOption to boot.
Arguments:
None.
Returns:
Shell Device path for booting.
--*/
{
UINTN FvHandleCount;
EFI_HANDLE *FvHandleBuffer;
UINTN Index;
EFI_STATUS Status;
EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv;
UINTN Size;
UINT32 AuthenticationStatus;
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
VOID *Buffer;
DevicePath = NULL;
Status = EFI_SUCCESS;
DEBUG ((DEBUG_INFO, "BdsCreateShellDevicePath\n"));
gBS->LocateHandleBuffer (
ByProtocol,
&gEfiFirmwareVolume2ProtocolGuid,
NULL,
&FvHandleCount,
&FvHandleBuffer
);
for (Index = 0; Index < FvHandleCount; Index++) {
gBS->HandleProtocol (
FvHandleBuffer[Index],
&gEfiFirmwareVolume2ProtocolGuid,
(VOID **) &Fv
);
Buffer = NULL;
Size = 0;
Status = Fv->ReadSection (
Fv,
&gUefiShellFileGuid,
EFI_SECTION_PE32,
0,
&Buffer,
&Size,
&AuthenticationStatus
);
if (EFI_ERROR (Status)) {
//
// Skip if no shell file in the FV
//
continue;
} else {
//
// Found the shell
//
break;
}
}
if (EFI_ERROR (Status)) {
//
// No shell present
//
if (FvHandleCount) {
FreePool (FvHandleBuffer);
}
return NULL;
}
//
// Build the shell boot option
//
DevicePath = DevicePathFromHandle (FvHandleBuffer[Index]);
if (FvHandleCount) {
FreePool (FvHandleBuffer);
}
return DevicePath;
}
EFI_STATUS
CreateFvBootOption (
EFI_GUID *FileGuid,
CHAR16 *Description,
EFI_BOOT_MANAGER_LOAD_OPTION *BootOption,
UINT32 Attributes,
UINT8 *OptionalData, OPTIONAL
UINT32 OptionalDataSize
)
{
EFI_STATUS Status;
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
MEDIA_FW_VOL_FILEPATH_DEVICE_PATH FileNode;
EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv;
UINT32 AuthenticationStatus;
VOID *Buffer;
UINTN Size;
if ((BootOption == NULL) || (FileGuid == NULL) || (Description == NULL)) {
return EFI_INVALID_PARAMETER;
}
EfiInitializeFwVolDevicepathNode (&FileNode, FileGuid);
if (!CompareGuid (&gUefiShellFileGuid, FileGuid)) {
Status = gBS->HandleProtocol (
gImageHandle,
&gEfiLoadedImageProtocolGuid,
(VOID **) &LoadedImage
);
if (!EFI_ERROR (Status)) {
Status = gBS->HandleProtocol (
LoadedImage->DeviceHandle,
&gEfiFirmwareVolume2ProtocolGuid,
(VOID **) &Fv
);
if (!EFI_ERROR (Status)) {
Buffer = NULL;
Size = 0;
Status = Fv->ReadSection (
Fv,
FileGuid,
EFI_SECTION_PE32,
0,
&Buffer,
&Size,
&AuthenticationStatus
);
if (Buffer != NULL) {
FreePool (Buffer);
}
}
}
if (EFI_ERROR (Status)) {
return EFI_NOT_FOUND;
}
DevicePath = AppendDevicePathNode (
DevicePathFromHandle (LoadedImage->DeviceHandle),
(EFI_DEVICE_PATH_PROTOCOL *) &FileNode
);
} else {
DevicePath = AppendDevicePathNode (
BdsCreateShellDevicePath (),
(EFI_DEVICE_PATH_PROTOCOL *) &FileNode
);
}
Status = EfiBootManagerInitializeLoadOption (
BootOption,
LoadOptionNumberUnassigned,
LoadOptionTypeBoot,
Attributes,
Description,
DevicePath,
OptionalData,
OptionalDataSize
);
FreePool (DevicePath);
return Status;
}
EFI_GUID mUiFile = {
0x462CAA21, 0x7614, 0x4503, { 0x83, 0x6E, 0x8A, 0xB6, 0xF4, 0x66, 0x23, 0x31 }
};
EFI_GUID mBootMenuFile = {
0xEEC25BDC, 0x67F2, 0x4D95, { 0xB1, 0xD5, 0xF8, 0x1B, 0x20, 0x39, 0xD1, 0x1D }
};
/**
Return the index of the load option in the load option array.
The function consider two load options are equal when the
OptionType, Attributes, Description, FilePath and OptionalData are equal.
@param Key Pointer to the load option to be found.
@param Array Pointer to the array of load options to be found.
@param Count Number of entries in the Array.
@retval -1 Key wasn't found in the Array.
@retval 0 ~ Count-1 The index of the Key in the Array.
**/
INTN
PlatformFindLoadOption (
IN CONST EFI_BOOT_MANAGER_LOAD_OPTION *Key,
IN CONST EFI_BOOT_MANAGER_LOAD_OPTION *Array,
IN UINTN Count
)
{
UINTN Index;
for (Index = 0; Index < Count; Index++) {
if ((Key->OptionType == Array[Index].OptionType) &&
(Key->Attributes == Array[Index].Attributes) &&
(StrCmp (Key->Description, Array[Index].Description) == 0) &&
(CompareMem (Key->FilePath, Array[Index].FilePath, GetDevicePathSize (Key->FilePath)) == 0) &&
(Key->OptionalDataSize == Array[Index].OptionalDataSize) &&
(CompareMem (Key->OptionalData, Array[Index].OptionalData, Key->OptionalDataSize) == 0)) {
return (INTN) Index;
}
}
return -1;
}
UINTN
RegisterFvBootOption (
EFI_GUID *FileGuid,
CHAR16 *Description,
UINTN Position,
UINT32 Attributes,
UINT8 *OptionalData, OPTIONAL
UINT32 OptionalDataSize
)
{
EFI_STATUS Status;
UINTN OptionIndex;
EFI_BOOT_MANAGER_LOAD_OPTION NewOption;
EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;
UINTN BootOptionCount;
NewOption.OptionNumber = LoadOptionNumberUnassigned;
Status = CreateFvBootOption (FileGuid, Description, &NewOption, Attributes, OptionalData, OptionalDataSize);
if (!EFI_ERROR (Status)) {
BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot);
OptionIndex = PlatformFindLoadOption (&NewOption, BootOptions, BootOptionCount);
if (OptionIndex == -1) {
Status = EfiBootManagerAddLoadOptionVariable (&NewOption, Position);
ASSERT_EFI_ERROR (Status);
} else {
NewOption.OptionNumber = BootOptions[OptionIndex].OptionNumber;
}
EfiBootManagerFreeLoadOption (&NewOption);
EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);
}
return NewOption.OptionNumber;
}
VOID
EFIAPI
PlatformBootManagerWaitCallback (
UINT16 TimeoutRemain
)
{
EFI_STATUS Status;
EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *TxtInEx;
EFI_KEY_DATA KeyData;
BOOLEAN PausePressed;
//
// Pause on PAUSE key
//
Status = gBS->HandleProtocol (gST->ConsoleInHandle, &gEfiSimpleTextInputExProtocolGuid, (VOID **) &TxtInEx);
ASSERT_EFI_ERROR (Status);
PausePressed = FALSE;
while (TRUE) {
Status = TxtInEx->ReadKeyStrokeEx (TxtInEx, &KeyData);
if (EFI_ERROR (Status)) {
break;
}
if (KeyData.Key.ScanCode == SCAN_PAUSE) {
PausePressed = TRUE;
break;
}
}
//
// Loop until non-PAUSE key pressed
//
while (PausePressed) {
Status = TxtInEx->ReadKeyStrokeEx (TxtInEx, &KeyData);
if (!EFI_ERROR (Status)) {
DEBUG ((
DEBUG_INFO, "[PauseCallback] %x/%x %x/%x\n",
KeyData.Key.ScanCode, KeyData.Key.UnicodeChar,
KeyData.KeyState.KeyShiftState, KeyData.KeyState.KeyToggleState
));
PausePressed = (BOOLEAN) (KeyData.Key.ScanCode == SCAN_PAUSE);
}
}
}
EFI_GUID gUefiShellFileGuid = { 0x7C04A583, 0x9E3E, 0x4f1c, { 0xAD, 0x65, 0xE0, 0x52, 0x68, 0xD0, 0xB4, 0xD1 } };
#define INTERNAL_UEFI_SHELL_NAME L"Internal UEFI Shell 2.0"
#define UEFI_HARD_DRIVE_NAME L"UEFI Hard Drive"
VOID
RegisterDefaultBootOption (
VOID
)
{
#if 0
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
MEDIA_FW_VOL_FILEPATH_DEVICE_PATH FileNode;
#endif
UINT16 *ShellData;
UINT32 ShellDataSize;
ShellData = NULL;
ShellDataSize = 0;
RegisterFvBootOption (&gUefiShellFileGuid, INTERNAL_UEFI_SHELL_NAME, (UINTN) -1, LOAD_OPTION_ACTIVE, (UINT8 *)ShellData, ShellDataSize);
//
// Boot Menu
//
mBootMenuOptionNumber = RegisterFvBootOption (&mBootMenuFile, L"Boot Device List", (UINTN) -1, LOAD_OPTION_CATEGORY_APP | LOAD_OPTION_ACTIVE | LOAD_OPTION_HIDDEN, NULL, 0);
if (mBootMenuOptionNumber == LoadOptionNumberUnassigned) {
DEBUG ((DEBUG_INFO, "BootMenuOptionNumber (%d) should not be same to LoadOptionNumberUnassigned(%d).\n", mBootMenuOptionNumber, LoadOptionNumberUnassigned));
}
#if 0
//
// Boot Manager Menu
//
EfiInitializeFwVolDevicepathNode (&FileNode, &mUiFile);
gBS->HandleProtocol (
gImageHandle,
&gEfiLoadedImageProtocolGuid,
(VOID **) &LoadedImage
);
DevicePath = AppendDevicePathNode (DevicePathFromHandle (LoadedImage->DeviceHandle), (EFI_DEVICE_PATH_PROTOCOL *) &FileNode);
#endif
}
VOID
RegisterBootOptionHotkey (
UINT16 OptionNumber,
EFI_INPUT_KEY *Key,
BOOLEAN Add
)
{
EFI_STATUS Status;
if (!Add) {
//
// No enter hotkey when force to setup or there is no boot option
//
Status = EfiBootManagerDeleteKeyOptionVariable (NULL, 0, Key, NULL);
ASSERT (Status == EFI_SUCCESS || Status == EFI_NOT_FOUND);
} else {
//
// Register enter hotkey for the first boot option
//
Status = EfiBootManagerAddKeyOptionVariable (NULL, OptionNumber, 0, Key,NULL);
ASSERT (Status == EFI_SUCCESS || Status == EFI_ALREADY_STARTED);
}
}
EFI_STATUS
EFIAPI
DetectKeypressCallback (
IN EFI_KEY_DATA *KeyData
)
{
mHotKeypressed = TRUE;
if (HotKeyEvent != NULL) {
gBS->SignalEvent(HotKeyEvent);
}
return EFI_SUCCESS;
}
/**
This function is called after all the boot options are enumerated and ordered properly.
**/
VOID
RegisterStaticHotkey (
VOID
)
{
EFI_INPUT_KEY Enter;
EFI_KEY_DATA F2;
EFI_KEY_DATA F7;
BOOLEAN EnterSetup;
EFI_STATUS Status;
EFI_BOOT_MANAGER_LOAD_OPTION BootOption;
EnterSetup = FALSE;
//
// [Enter]
//
mContinueBoot = !EnterSetup;
if (mContinueBoot) {
Enter.ScanCode = SCAN_NULL;
Enter.UnicodeChar = CHAR_CARRIAGE_RETURN;
EfiBootManagerRegisterContinueKeyOption (0, &Enter, NULL);
}
//
// [F2]/[F7]
//
F2.Key.ScanCode = SCAN_F2;
F2.Key.UnicodeChar = CHAR_NULL;
F2.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID;
F2.KeyState.KeyToggleState = 0;
Status = EfiBootManagerGetBootManagerMenu (&BootOption);
ASSERT_EFI_ERROR (Status);
RegisterBootOptionHotkey ((UINT16) BootOption.OptionNumber, &F2.Key, TRUE);
EfiBootManagerFreeLoadOption (&BootOption);
F7.Key.ScanCode = SCAN_F7;
F7.Key.UnicodeChar = CHAR_NULL;
F7.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID;
F7.KeyState.KeyToggleState = 0;
mBootMenuBoot = !EnterSetup;
RegisterBootOptionHotkey ((UINT16) mBootMenuOptionNumber, &F7.Key, mBootMenuBoot);
}
UINT8
BootOptionType (
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath
)
{
EFI_DEVICE_PATH_PROTOCOL *Node;
EFI_DEVICE_PATH_PROTOCOL *NextNode;
for (Node = DevicePath; !IsDevicePathEndType (Node); Node = NextDevicePathNode (Node)) {
if (DevicePathType (Node) == MESSAGING_DEVICE_PATH) {
//
// Make sure the device path points to the driver device.
//
NextNode = NextDevicePathNode (Node);
if (DevicePathSubType(NextNode) == MSG_DEVICE_LOGICAL_UNIT_DP) {
//
// if the next node type is Device Logical Unit, which specify the Logical Unit Number (LUN),
// skip it
//
NextNode = NextDevicePathNode (NextNode);
}
if (IsDevicePathEndType (NextNode)) {
if ((DevicePathType (Node) == MESSAGING_DEVICE_PATH)) {
return DevicePathSubType (Node);
} else {
return MSG_SATA_DP;
}
}
}
}
return (UINT8) -1;
}
/**
Returns the priority number.
OptionType EFI
------------------------------------
PXE 2
DVD 4
USB 6
NVME 7
HDD 8
EFI Shell 9
Others 100
@param BootOption
**/
UINTN
BootOptionPriority (
CONST EFI_BOOT_MANAGER_LOAD_OPTION *BootOption
)
{
//
// EFI boot options
//
switch (BootOptionType (BootOption->FilePath)) {
case MSG_MAC_ADDR_DP:
case MSG_VLAN_DP:
case MSG_IPv4_DP:
case MSG_IPv6_DP:
return 2;
case MSG_SATA_DP:
case MSG_ATAPI_DP:
case MSG_UFS_DP:
case MSG_NVME_NAMESPACE_DP:
return 4;
case MSG_USB_DP:
return 6;
}
if (StrCmp (BootOption->Description, INTERNAL_UEFI_SHELL_NAME) == 0) {
if (PcdGetBool (PcdBootToShellOnly)) {
return 0;
}
return 9;
}
if (StrCmp (BootOption->Description, UEFI_HARD_DRIVE_NAME) == 0) {
return 8;
}
return 100;
}
INTN
EFIAPI
CompareBootOption (
CONST VOID *Left,
CONST VOID *Right
)
{
return BootOptionPriority ((EFI_BOOT_MANAGER_LOAD_OPTION *) Left) -
BootOptionPriority ((EFI_BOOT_MANAGER_LOAD_OPTION *) Right);
}