/** @file
Driver for Platform Boot Options support.
Copyright (c) 2017, Intel Corporation. All rights reserved.
This program and the accompanying materials are licensed and made available under
the terms and conditions of the BSD License that 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 "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
)
{
UINT16 Timeout;
EFI_STATUS Status;
EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *TxtInEx;
EFI_KEY_DATA KeyData;
BOOLEAN PausePressed;
Timeout = PcdGet16 (PcdPlatformBootTimeOut);
//
// 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
)
{
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
MEDIA_FW_VOL_FILEPATH_DEVICE_PATH FileNode;
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));
}
//
// Boot Manager Menu
//
EfiInitializeFwVolDevicepathNode (&FileNode, &mUiFile);
gBS->HandleProtocol (
gImageHandle,
&gEfiLoadedImageProtocolGuid,
(VOID **) &LoadedImage
);
DevicePath = AppendDevicePathNode (DevicePathFromHandle (LoadedImage->DeviceHandle), (EFI_DEVICE_PATH_PROTOCOL *) &FileNode);
}
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:
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);
}