alder_lake_bios/Insyde/InsydeModulePkg/Library/GenericBdsLib/BdsBootOption.c

2580 lines
77 KiB
C

/** @file
BDS boot option related functions
;******************************************************************************
;* Copyright (c) 2017 - 2021, Insyde Software Corp. All Rights Reserved.
;*
;* You may not reproduce, distribute, publish, display, perform, modify, adapt,
;* transmit, broadcast, present, recite, release, license or otherwise exploit
;* any part of this publication in any form, by any means, without the prior
;* written permission of Insyde Software Corporation.
;*
;******************************************************************************
*/
#include "BdsBootOption.h"
#define SECURE_BOOT_ENABLED 0x00
#define SECURE_BOOT_DISABLED 0x01
#define MAX_BIOS_ENVIRONMENT 0x02
#define UNKNOWN_SECURE_SETTING 0x02
LIST_ENTRY mWindowsToGoDeviceList = INITIALIZE_LIST_HEAD_VARIABLE (mWindowsToGoDeviceList);
STATIC DEVICE_TYPE_INFORMATION mDeviceTypeInfo[] = {
{BDS_EFI_ACPI_FLOPPY_BOOT, STRING_TOKEN (STR_DESCRIPTION_FLOPPY), 0, ATTR_ALL},
{BDS_EFI_MESSAGE_SATA_BOOT, STRING_TOKEN (STR_DESCRIPTION_CD_DVD), 0, ATTR_ALL},
{BDS_EFI_MESSAGE_ATAPI_BOOT, STRING_TOKEN (STR_DESCRIPTION_CD_DVD), 0, ATTR_ALL},
{BDS_EFI_MEDIA_CDROM_BOOT, STRING_TOKEN (STR_DESCRIPTION_CD_DVD), 0, ATTR_ALL},
{BDS_EFI_MESSAGE_USB_DEVICE_BOOT, STRING_TOKEN (STR_DESCRIPTION_USB), 0, ATTR_ALL},
{BDS_EFI_MESSAGE_SCSI_BOOT, STRING_TOKEN (STR_DESCRIPTION_SCSI), 0, ATTR_ALL},
{BDS_EFI_MESSAGE_ISCSI_BOOT, STRING_TOKEN (STR_DESCRIPTION_ISCSI), 0, ATTR_ALL},
{BDS_EFI_MESSAGE_MISC_BOOT, STRING_TOKEN (STR_DESCRIPTION_MISC), 0, ATTR_ALL},
{BDS_EFI_MEDIA_HD_BOOT, STRING_TOKEN (STR_DESCRIPTION_HDD), 0, ATTR_ALL},
{BDS_EFI_SDHC_BOOT, STRING_TOKEN (STR_DESCRIPTION_EMMC), 0, ATTR_UNREMOVABLE_DEV},
{BDS_EFI_SDHC_BOOT, STRING_TOKEN (STR_DESCRIPTION_SDMMC), 0, ATTR_REMOVABLE_DEV},
};
/**
Check if the target file is signed image or not
@param[in] Handle A pointer to a device handle.
@param[in] FileName Pointer to file name
@retval TRUE This is a signed image
@retval FALSE The file isn't exist, the image is not supported or this isn't a signed image
**/
STATIC
BOOLEAN
IsSignedImage (
IN EFI_HANDLE Handle,
IN CHAR16 *FileName
)
{
EFI_STATUS Status;
EFI_IMAGE_DOS_HEADER DosHeader;
EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr;
EFI_IMAGE_OPTIONAL_HEADER_UNION HdrData;
UINT16 Magic;
EFI_IMAGE_DATA_DIRECTORY *SecDataDir;
Hdr.Union = &HdrData;
Status = BdsLibGetImageHeader (
Handle,
FileName,
&DosHeader,
Hdr
);
if (EFI_ERROR (Status) || !EFI_IMAGE_MACHINE_TYPE_SUPPORTED (Hdr.Pe32->FileHeader.Machine)) {
return FALSE;
}
if (Hdr.Pe32->FileHeader.Machine == EFI_IMAGE_MACHINE_IA64 && Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
Magic = EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC;
} else {
Magic = Hdr.Pe32->OptionalHeader.Magic;
}
if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
//
// Use PE32 offset.
//
SecDataDir = (EFI_IMAGE_DATA_DIRECTORY *) &Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];
} else {
//
// Use PE32+ offset.
//
SecDataDir = (EFI_IMAGE_DATA_DIRECTORY *) &Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];
}
if (SecDataDir->Size != 0) {
return TRUE;
}
return FALSE;
}
/**
This function uses device handle to check the EFI boot option is Windows To Go device or not
@param[in] Handle A pointer to a device handle.
@retval TRUE This is a Windows To Go device
@retval FALSE This isn't a Windows To Go device
**/
BOOLEAN
IsWindowsToGo (
IN EFI_HANDLE Handle
)
{
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
DevicePath = DevicePathFromHandle (Handle);
if (DevicePath == NULL) {
return FALSE;
}
for (TempDevicePath = DevicePath; !IsDevicePathEndType (TempDevicePath); TempDevicePath = NextDevicePathNode (TempDevicePath)) {
if (DevicePathType (TempDevicePath) == MEDIA_DEVICE_PATH &&
DevicePathSubType (TempDevicePath) == MEDIA_CDROM_DP) {
return FALSE;
}
}
if (BdsLibGetBootTypeFromDevicePath (DevicePath) == BDS_EFI_MESSAGE_USB_DEVICE_BOOT &&
IsSignedImage (Handle, EFI_REMOVABLE_MEDIA_FILE_NAME)) {
return TRUE;
}
return FALSE;
}
/**
Add a BDS_COMMON_OPTION to portabl boot option list
@param[in] DevicePath A pointer to device path instance.
@param[in, out] BdsBootOptionList The header of the link list which indexed all current boot options
@retval EFI_SUCCESS Finished all the boot device enumerate and create the boot option base on that boot device
@retval EFI_OUT_OF_RESOURCES Fail to allocate pool
**/
STATIC
EFI_STATUS
RegisterPortalbeBootOption (
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
IN OUT LIST_ENTRY *BdsBootOptionList
)
{
BDS_COMMON_OPTION *Option;
Option = AllocateZeroPool (sizeof (BDS_COMMON_OPTION));
if (Option == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Option->Signature = BDS_LOAD_OPTION_SIGNATURE;
Option->DevicePath = AllocateZeroPool (GetDevicePathSize (DevicePath));
if (Option->DevicePath == NULL) {
FreePool (Option);
return EFI_OUT_OF_RESOURCES;
}
CopyMem (Option->DevicePath, DevicePath, GetDevicePathSize (DevicePath));
Option->Attribute = 0;
Option->Description = NULL;
Option->LoadOptions = NULL;
Option->LoadOptionsSize = 0;
InsertTailList (BdsBootOptionList, &Option->Link);
return EFI_SUCCESS;
}
/**
Enumerate all Windows To Go devices and return device number.
@param[out] DeviceNum Pointer to device number
@retval EFI_SUCCESS Enumerate all Windows To Go devices success
@retval EFI_NOT_FOUND Windows To Go device is not found
**/
STATIC
EFI_STATUS
EnumerateAllWindowsToGoDevice (
OUT UINT16 *DeviceNum
)
{
UINTN Index;
UINTN NumberFileSystemHandles;
EFI_HANDLE *FileSystemHandles;
EFI_DEVICE_PATH_PROTOCOL *FilePath;
UINT16 Count;
Count = 0;
NumberFileSystemHandles = 0;
gBS->LocateHandleBuffer (
ByProtocol,
&gEfiSimpleFileSystemProtocolGuid,
NULL,
&NumberFileSystemHandles,
&FileSystemHandles
);
for (Index = 0; Index < NumberFileSystemHandles; Index++) {
if (IsWindowsToGo (FileSystemHandles[Index])) {
//
// Add to Windows To Go list if this is USB Entry for Windows To Go
//
FilePath = FileDevicePath (FileSystemHandles[Index], EFI_REMOVABLE_MEDIA_FILE_NAME);
RegisterPortalbeBootOption (FilePath, &mWindowsToGoDeviceList);
FreePool (FilePath);
Count++;
}
}
if (NumberFileSystemHandles != 0) {
FreePool (FileSystemHandles);
}
if (Count == 0) {
return EFI_NOT_FOUND;
}
*DeviceNum = Count;
return EFI_SUCCESS;
}
/**
Set Windows To Go variable
@param[in] DeviceNum Windows To Go device number
@retval EFI_SUCCESS Set Windows To Go device variable success
@retval EFI_NOT_FOUND Windows To Go device is not found
@retval EFI_OUT_OF_RESOURCES Allocate memory fail
**/
STATIC
EFI_STATUS
SetWindowsToGoVariable (
IN UINT16 DeviceNum
)
{
EFI_STATUS Status;
BDS_COMMON_OPTION *Option;
UINT8 *Ptr;
UINT8 *VarData;
UINTN Size;
UINTN DevPathSize;
if (IsListEmpty (&mWindowsToGoDeviceList)) {
return EFI_NOT_FOUND;
}
Option = CR (mWindowsToGoDeviceList.ForwardLink, BDS_COMMON_OPTION, Link, BDS_LOAD_OPTION_SIGNATURE);
DevPathSize = GetDevicePathSize (Option->DevicePath);
Size = DevPathSize + sizeof(UINT16);
VarData = AllocatePool (Size);
if (VarData == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Ptr = VarData;
CopyMem (Ptr, Option->DevicePath, DevPathSize);
Ptr += DevPathSize;
CopyMem (Ptr, &DeviceNum, sizeof(UINT16));
Status = gRT->SetVariable (
L"WindowsToGo",
&gEfiGenericVariableGuid,
EFI_VARIABLE_BOOTSERVICE_ACCESS,
Size,
VarData
);
FreePool (VarData);
return Status;
}
/**
Enumerate all Windows To Go devices and update them to list.
**/
VOID
UpdateWindowsToGoList (
VOID
)
{
EFI_STATUS Status;
UINT16 DeviceNum;
KERNEL_CONFIGURATION SystemConfiguration;
Status = GetKernelConfiguration (&SystemConfiguration);
if (Status == EFI_SUCCESS && SystemConfiguration.UsbBoot != 0) {
return;
}
Status = EnumerateAllWindowsToGoDevice (&DeviceNum);
if (Status == EFI_SUCCESS) {
SetWindowsToGoVariable (DeviceNum);
}
}
/**
Check the MAC address is empty
@param MacAddr
@retval TRUE
@retval FALSE
**/
BOOLEAN
MacEmpty (
IN UINT8 *MacAddr,
IN UINTN MACLength
)
{
UINTN Index;
for (Index = 0; Index < MACLength; Index++ ) {
if (MacAddr[Index] != 0) {
return FALSE;
}
}
return TRUE;
}
/**
Check two MAC address is equal
@param MacAddr1
@param MacAddr2
@retval TRUE
@retval FALSE
**/
BOOLEAN
MacCompare (
IN UINT8 *MacAddr1,
IN UINT8 *MacAddr2,
IN UINTN MACLength
)
{
UINTN Index;
for (Index = 0; Index < MACLength; Index++) {
if (MacAddr1[Index] != MacAddr2[Index]) {
return FALSE;
}
}
return TRUE;
}
/**
Translate MAC address to index of remembered MAC address list;
This function will recoder MAC addresses is not scanned, max records of UEFI_NETWORK_BOOT_OPTION_MAX
@param DeviceMacAddr Your Devices MAC Address
@param MacAddressList Mac Address list buffer
@param DeviceId Result
@retval EFI_SUCCESS Found
@retval EFI_NOT_FOUND Not found or buffer full, check with UEFI_NETWORK_BOOT_OPTION_MAX
Id = UEFI_NETWORK_BOOT_OPTION_MAX
**/
EFI_STATUS
MacToIndex (
IN EFI_MAC_ADDRESS *DeviceMacAddr,
IN OUT EFI_MAC_ADDRESS *MacAddressList,
OUT UINTN *DeviceId
)
{
EFI_STATUS Status;
UINTN Index;
UINTN MACLength;
MACLength = sizeof (EFI_MAC_ADDRESS);
Status = EFI_NOT_FOUND;
for (Index = 0; Index < UEFI_NETWORK_BOOT_OPTION_MAX; Index++) {
if (MacEmpty ((VOID *) &(MacAddressList[Index]), MACLength)) {
CopyMem (&(MacAddressList[Index]), DeviceMacAddr, MACLength);
Status = EFI_SUCCESS;
} else if (MacCompare ((VOID *) &(MacAddressList[Index]), (VOID *)(DeviceMacAddr), MACLength)) {
Status = EFI_SUCCESS;
}
if (!EFI_ERROR (Status)) {
break;
}
}
*DeviceId = Index;
return Status;
}
/**
Build Boot option for UEFI PXE.
The boot option will follow style.
"EFI Network (Index) for IPv(4/6) (MAC Address)"
@param[in] BdsBootOptionList Your Devices MAC Address
@retval EFI_SUCCESS Boot option build success
@retval Other Memory allocate failed.
PXE boot option not found.
System configuration not found.
**/
STATIC
EFI_STATUS
BuildNetworkBootOption (
VOID
)
{
EFI_STATUS Status;
UINTN Index;
KERNEL_CONFIGURATION SystemConfiguration;
EFI_DEVICE_PATH_PROTOCOL *NetworkDevicePath;
EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
UINTN NumberLoadFileHandles;
EFI_HANDLE *LoadFileHandles;
CHAR16 Buffer[UEFI_NETWORK_BOOT_OPTION_MAX_CHAR];
MAC_ADDR_DEVICE_PATH *MAC;
UINTN DeviceId;
EFI_MAC_ADDRESS *MacAddressList = NULL;
BOOLEAN NeedBuild;
UINT8 IpType;
CHAR16 VlanString[UEFI_NETWORK_VLAN_STRING_LENGTH];
UINT16 VlanID;
BOOLEAN IsIPv4BootOption;
BOOLEAN IsIPv6BootOption;
BOOLEAN IsHttpBoot;
CHAR8 *UriString;
URI_DEVICE_PATH *UriDevicePath;
NetworkDevicePath = NULL;
TempDevicePath = NULL;
MacAddressList = NULL;
LoadFileHandles = NULL;
Status = GetKernelConfiguration (&SystemConfiguration);
if (EFI_ERROR (Status)) {
return Status;
}
//
// If Pxe disabled then do not build Network boot variables
//
if (SystemConfiguration.PxeBootToLan == 0) {
return EFI_SUCCESS;
}
ZeroMem (Buffer, sizeof (Buffer));
MacAddressList = AllocateZeroPool (sizeof (EFI_MAC_ADDRESS) * UEFI_NETWORK_BOOT_OPTION_MAX);
if (MacAddressList == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto ERROR_HANDLE;
}
Status = gBS->LocateHandleBuffer (
ByProtocol,
&gEfiLoadFileProtocolGuid,
NULL,
&NumberLoadFileHandles,
&LoadFileHandles
);
if (EFI_ERROR (Status)) {
goto ERROR_HANDLE;
}
for (Index = 0; Index < NumberLoadFileHandles; Index++) {
NeedBuild = FALSE;
//
// VlanID is Range 0 ~ 4094, If VlanID == 0xFFFF means not Vlan device
//
VlanID = 0xFFFF;
MAC = NULL;
IsIPv4BootOption = FALSE;
IsIPv6BootOption = FALSE;
IsHttpBoot = FALSE;
IpType = 0;
UriString = "";
Status = gBS->HandleProtocol (
LoadFileHandles[Index],
&gEfiDevicePathProtocolGuid,
(VOID **) &TempDevicePath
);
if (EFI_ERROR (Status)) {
continue;
}
//
// Parsing DevicePath to identifier MAC address or IP capability or HTTP URI
//
NetworkDevicePath = TempDevicePath;
while (!IsDevicePathEnd (TempDevicePath)) {
if (TempDevicePath->Type == MESSAGING_DEVICE_PATH) {
switch (TempDevicePath->SubType) {
case MSG_MAC_ADDR_DP:
MAC = (MAC_ADDR_DEVICE_PATH *)TempDevicePath;
break;
case MSG_VLAN_DP:
VlanID = ((VLAN_DEVICE_PATH *)TempDevicePath)->VlanId;
break;
case MSG_IPv4_DP:
IsIPv4BootOption = TRUE;
break;
case MSG_IPv6_DP:
IsIPv6BootOption = TRUE;
break;
case MSG_URI_DP:
IsHttpBoot = TRUE;
if (DevicePathNodeLength (TempDevicePath) > sizeof (EFI_DEVICE_PATH)) {
UriDevicePath = (URI_DEVICE_PATH *) TempDevicePath;
UriString = UriDevicePath->Uri;
}
break;
}
}
TempDevicePath = NextDevicePathNode (TempDevicePath);
}
//
// IPv4 and IPv6 both in DevicePath is incorrect
//
if (IsIPv4BootOption && IsIPv6BootOption) {
continue;
}
//
// If MAC has not found, Item cannot display
//
if (MAC == NULL) {
continue;
}
//
// Build Policy:
// Device Path is:
// MAC\IPv4 : "EFI Network X for IPv4 (MAC Address)"
// MAC\IPv6 : "EFI Network X for IPv6 (MAC Address)"
// MAC : "EFI Network X for IPv4 (MAC Address)"
// Other : "Others: (Device Path)"
//
if (MAC != NULL && (!IsIPv4BootOption && !IsIPv6BootOption)) {
IsIPv4BootOption = TRUE;
}
if (IsIPv4BootOption && !IsIPv6BootOption) {
IpType = 4;
} else if (!IsIPv4BootOption && IsIPv6BootOption) {
IpType = 6;
} else {
/// Shouldn't have this case
continue;
}
switch (SystemConfiguration.NetworkProtocol) {
case UEFI_NETWORK_BOOT_OPTION_PXE_IPV4:
case UEFI_NETWORK_BOOT_OPTION_PXE_IPV6:
case UEFI_NETWORK_BOOT_OPTION_PXE_BOTH:
if (IsHttpBoot) {
break;
}
if (((SystemConfiguration.NetworkProtocol == UEFI_NETWORK_BOOT_OPTION_PXE_IPV4) && (IpType == 4)) ||
((SystemConfiguration.NetworkProtocol == UEFI_NETWORK_BOOT_OPTION_PXE_IPV6) && (IpType == 6)) ||
(SystemConfiguration.NetworkProtocol == UEFI_NETWORK_BOOT_OPTION_PXE_BOTH)) {
NeedBuild = TRUE;
}
break;
case UEFI_NETWORK_BOOT_OPTION_HTTP_IPV4:
case UEFI_NETWORK_BOOT_OPTION_HTTP_IPV6:
case UEFI_NETWORK_BOOT_OPTION_HTTP_BOTH:
if (!IsHttpBoot) {
break;
}
if (((SystemConfiguration.NetworkProtocol == UEFI_NETWORK_BOOT_OPTION_HTTP_IPV4) && (IpType == 4)) ||
((SystemConfiguration.NetworkProtocol == UEFI_NETWORK_BOOT_OPTION_HTTP_IPV6) && (IpType == 6)) ||
(SystemConfiguration.NetworkProtocol == UEFI_NETWORK_BOOT_OPTION_HTTP_BOTH)) {
NeedBuild = TRUE;
}
break;
case UEFI_NETWORK_BOOT_OPTION_HTTP_PXE_IPV4:
case UEFI_NETWORK_BOOT_OPTION_HTTP_PXE_IPV6:
case UEFI_NETWORK_BOOT_OPTION_HTTP_PXE_BOTH:
if (((SystemConfiguration.NetworkProtocol == UEFI_NETWORK_BOOT_OPTION_HTTP_PXE_IPV4) && (IpType == 4)) ||
((SystemConfiguration.NetworkProtocol == UEFI_NETWORK_BOOT_OPTION_HTTP_PXE_IPV6) && (IpType == 6)) ||
(SystemConfiguration.NetworkProtocol == UEFI_NETWORK_BOOT_OPTION_HTTP_PXE_BOTH)) {
NeedBuild = TRUE;
}
break;
default:
break;
}
if (!NeedBuild) {
continue;
}
if (IsIPv4BootOption || IsIPv6BootOption) {
MacToIndex (&(MAC->MacAddress), MacAddressList, &DeviceId);
//
// Select Network boot type string.
//
if (!IsHttpBoot) {
UnicodeSPrint (
Buffer,
sizeof (Buffer),
L"EFI PXE %d for IPv%d (%02x-%02x-%02x-%02x-%02x-%02x) ",
DeviceId,
IpType,
MAC->MacAddress.Addr[0],
MAC->MacAddress.Addr[1],
MAC->MacAddress.Addr[2],
MAC->MacAddress.Addr[3],
MAC->MacAddress.Addr[4],
MAC->MacAddress.Addr[5]
);
} else {
if (AsciiStrLen (UriString) != 0) {
UnicodeSPrint (
Buffer,
sizeof (Buffer),
L"[IPv%d](%02x-%02x-%02x-%02x-%02x-%02x) %a",
IpType,
MAC->MacAddress.Addr[0],
MAC->MacAddress.Addr[1],
MAC->MacAddress.Addr[2],
MAC->MacAddress.Addr[3],
MAC->MacAddress.Addr[4],
MAC->MacAddress.Addr[5],
UriString
);
} else {
UnicodeSPrint (
Buffer,
sizeof (Buffer),
L"EFI HTTP[IPv%d](%02x-%02x-%02x-%02x-%02x-%02x) ",
IpType,
MAC->MacAddress.Addr[0],
MAC->MacAddress.Addr[1],
MAC->MacAddress.Addr[2],
MAC->MacAddress.Addr[3],
MAC->MacAddress.Addr[4],
MAC->MacAddress.Addr[5]
);
}
}
if (VlanID != 0xFFFF) {
UnicodeSPrint (
VlanString,
sizeof (VlanString),
L"VLAN(%d)",
VlanID
);
StrCatS (Buffer, UEFI_NETWORK_BOOT_OPTION_MAX_CHAR, VlanString);
}
BdsLibBuildOptionFromHandle (LoadFileHandles[Index], NULL, Buffer);
} else {
//
// Build unknown load file option
//
StrCatS (Buffer, UEFI_NETWORK_BOOT_OPTION_MAX_CHAR, L"Others: ");
StrCatS (Buffer, UEFI_NETWORK_BOOT_OPTION_MAX_CHAR, DevicePathToStr (NetworkDevicePath));
BdsLibBuildOptionFromHandle (LoadFileHandles[Index], NULL, Buffer);
}
}
ERROR_HANDLE:
if (LoadFileHandles) {
FreePool (LoadFileHandles);
}
if (MacAddressList) {
FreePool (MacAddressList);
}
return Status;
}
/**
Check whether the input device path is a valid network boot option or not.
@param[in] SystemConfiguration Pointer to system configuration
@param[in] OptionDevicePath Pointer to device path of boot option
@retval TRUE The input device path is a valid network boot option
@retval FALSE The input device path is not a valid network boot option
**/
BOOLEAN
IsValidNetworkBootOption (
IN KERNEL_CONFIGURATION *SystemConfiguration,
IN EFI_DEVICE_PATH_PROTOCOL *OptionDevicePath
)
{
EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
EFI_STATUS Status;
EFI_HANDLE Handle;
BOOLEAN IsValid;
BOOLEAN IsIPv4BootOption;
BOOLEAN IsIPv6BootOption;
BOOLEAN IsHttpBootOption;
TempDevicePath = OptionDevicePath;
Status = gBS->LocateDevicePath (
&gEfiLoadFileProtocolGuid,
&TempDevicePath,
&Handle
);
if (EFI_ERROR (Status)) {
return FALSE;
}
//
// Focus on removable media and network boot device
//
if (!FeaturePcdGet (PcdH2ONetworkSupported)) {
return TRUE;
}
IsValid = FALSE;
IsIPv4BootOption = FALSE;
IsIPv6BootOption = FALSE;
IsHttpBootOption = FALSE;
//
// Scan Build Option device path, if is IPv4 or IPv6 will trigger boolean
//
TempDevicePath = OptionDevicePath;
while (!IsDevicePathEnd (TempDevicePath)) {
if (TempDevicePath->Type == MESSAGING_DEVICE_PATH) {
if (TempDevicePath->SubType == MSG_IPv4_DP) {
IsIPv4BootOption = TRUE;
} else if (TempDevicePath->SubType == MSG_IPv6_DP) {
IsIPv6BootOption = TRUE;
} else if (TempDevicePath->SubType == MSG_URI_DP) {
IsHttpBootOption = TRUE;
}
}
TempDevicePath = NextDevicePathNode (TempDevicePath);
}
//
// Detect user set Network is IPv4/IPv6/Both
// Will delete delete not selected item
//
switch (SystemConfiguration->NetworkProtocol) {
case UEFI_NETWORK_BOOT_OPTION_PXE_IPV4:
if (!IsHttpBootOption && IsIPv4BootOption) {
IsValid = TRUE;
}
break;
case UEFI_NETWORK_BOOT_OPTION_PXE_IPV6:
if (!IsHttpBootOption && IsIPv6BootOption) {
IsValid = TRUE;
}
break;
case UEFI_NETWORK_BOOT_OPTION_PXE_BOTH:
if (!IsHttpBootOption) {
IsValid = TRUE;
}
break;
case UEFI_NETWORK_BOOT_OPTION_HTTP_IPV4:
if (IsHttpBootOption && IsIPv4BootOption) {
IsValid = TRUE;
}
break;
case UEFI_NETWORK_BOOT_OPTION_HTTP_IPV6:
if (IsHttpBootOption && IsIPv6BootOption) {
IsValid = TRUE;
}
break;
case UEFI_NETWORK_BOOT_OPTION_HTTP_BOTH:
if (IsHttpBootOption) {
IsValid = TRUE;
}
break;
case UEFI_NETWORK_BOOT_OPTION_HTTP_PXE_IPV4:
if (IsIPv4BootOption) {
IsValid = TRUE;
}
break;
case UEFI_NETWORK_BOOT_OPTION_HTTP_PXE_IPV6:
if (IsIPv6BootOption) {
IsValid = TRUE;
}
break;
case UEFI_NETWORK_BOOT_OPTION_HTTP_PXE_BOTH:
IsValid = TRUE;
break;
}
//
// If Boot option is valid, will not delete
// Include Unknown LoadFile build option
//
if (!IsIPv4BootOption && !IsIPv6BootOption) {
IsValid = TRUE;
}
return IsValid;
}
/**
Enumerate all network boot options.
@retval EFI_SUCCESS Enumerate all network boot options success
**/
EFI_STATUS
EnumerateAllNetworkBootOption (
VOID
)
{
EFI_STATUS Status;
UINTN NumberLoadFileHandles;
EFI_HANDLE *LoadFileHandles;
UINTN Index;
VOID *ProtocolInstance;
CHAR16 Buffer[128];
if (FeaturePcdGet (PcdH2ONetworkSupported)) {
BuildNetworkBootOption ();
} else {
gBS->LocateHandleBuffer (
ByProtocol,
&gEfiSimpleNetworkProtocolGuid,
NULL,
&NumberLoadFileHandles,
&LoadFileHandles
);
for (Index = 0; Index < NumberLoadFileHandles; Index++) {
Status = gBS->HandleProtocol (
LoadFileHandles[Index],
&gEfiLoadFileProtocolGuid,
(VOID **) &ProtocolInstance
);
if (EFI_ERROR (Status)) {
continue;
}
if (Index == 0) {
UnicodeSPrint (Buffer, sizeof (Buffer), L"%s", BdsLibGetStringById (STRING_TOKEN (STR_DESCRIPTION_NETWORK)));
} else {
UnicodeSPrint (Buffer, sizeof (Buffer), L"%s %d", BdsLibGetStringById (STRING_TOKEN (STR_DESCRIPTION_NETWORK)), Index);
}
BdsLibBuildOptionFromHandle (LoadFileHandles[Index], NULL, Buffer);
}
if (NumberLoadFileHandles) {
FreePool (LoadFileHandles);
}
}
return EFI_SUCCESS;
}
/**
Compare device path to check if this boot device is already exist in BootOrder.
@param[in] DevicePath Device path of the query boot device
@retval TRUE The device is already in BootOrder variable.
@retval FALSE The device is not in BootOrder variable.
**/
BOOLEAN
IsAlreadyInBootOrder (
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath
)
{
UINTN DevicePathSize;
UINT16 *BootOrder;
UINTN BootOrderSize;
UINTN BootDevNum;
UINTN Index;
UINT16 BootOptionName[20];
UINTN VariableSize;
UINT8 *BootOption;
UINT8 *WorkingPtr;
EFI_DEVICE_PATH_PROTOCOL *BootOptionDevicePath;
UINTN BootOptionDevicePathSize;
DevicePathSize = GetDevicePathSize (DevicePath);
BootOrder = BdsLibGetVariableAndSize (
L"BootOrder",
&gEfiGlobalVariableGuid,
&BootOrderSize
);
if (BootOrder == NULL) {
return FALSE;
}
BootDevNum = BootOrderSize / sizeof (UINT16);
for (Index = 0; Index < BootDevNum; Index++) {
UnicodeSPrint (BootOptionName, sizeof (BootOptionName), L"Boot%04x", BootOrder[Index]);
BootOption = BdsLibGetVariableAndSize (
BootOptionName,
&gEfiGlobalVariableGuid,
&VariableSize
);
if (BootOption == NULL) {
continue;
}
//
// Find device path in Bootxxxx variable
//
WorkingPtr = BootOption;
WorkingPtr += sizeof (UINT32);
WorkingPtr += sizeof (UINT16);
WorkingPtr += (UINTN) StrSize ((UINT16 *) WorkingPtr);
BootOptionDevicePath = (EFI_DEVICE_PATH_PROTOCOL *) WorkingPtr;
BootOptionDevicePathSize = GetDevicePathSize (BootOptionDevicePath);
if (DevicePathSize == BootOptionDevicePathSize &&
CompareMem (DevicePath, BootOptionDevicePath, BootOptionDevicePathSize) == 0) {
FreePool (BootOption);
FreePool (BootOrder);
return TRUE;
}
FreePool (BootOption);
}
FreePool (BootOrder);
return FALSE;
}
/**
Get the hash value of shell file
@param[in] Handle FV handle value
@param[out] HashValue Output hash value of file
@retval EFI_SUCCESS Get hash value of file successfully.
@retval EFI_ABORTED Read file fail.
@retval Other HandleProtocol or AllocatePage or CalculateCrc32 fail.
**/
EFI_STATUS
GetShellFileHashValue (
IN EFI_HANDLE Handle,
OUT UINT32 *HashValue
)
{
EFI_STATUS Status;
EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv;
UINT8 *File;
UINTN Size;
EFI_FV_FILETYPE Type;
EFI_FV_FILE_ATTRIBUTES Attributes;
UINT32 AuthenticationStatus;
UINTN PageNum;
PageNum = 0;
Status = gBS->HandleProtocol (
Handle,
&gEfiFirmwareVolume2ProtocolGuid,
(VOID **) &Fv
);
if (EFI_ERROR(Status)) {
return Status;
}
File = NULL;
Status = Fv->ReadFile (
Fv,
PcdGetPtr(PcdShellFile),
(VOID **) &File,
&Size,
&Type,
&Attributes,
&AuthenticationStatus
);
if (Status == EFI_WARN_BUFFER_TOO_SMALL || Status == EFI_BUFFER_TOO_SMALL) {
PageNum = EFI_SIZE_TO_PAGES(Size);
Status = gBS->AllocatePages (
AllocateMaxAddress,
EfiBootServicesData,
PageNum,
(EFI_PHYSICAL_ADDRESS *) &File
);
if (EFI_ERROR(Status)) {
return Status;
}
Size = EFI_PAGES_TO_SIZE(PageNum);
Status = Fv->ReadFile (
Fv,
PcdGetPtr(PcdShellFile),
(VOID **) &File,
&Size,
&Type,
&Attributes,
&AuthenticationStatus
);
if (Status != EFI_SUCCESS) {
gBS->FreePages ((EFI_PHYSICAL_ADDRESS) (UINTN) File, PageNum);
return EFI_ABORTED;
}
}
if (Status != EFI_SUCCESS) {
return EFI_ABORTED;
}
Status = gBS->CalculateCrc32 (File, Size, HashValue);
if (PageNum != 0) {
gBS->FreePages ((EFI_PHYSICAL_ADDRESS) (UINTN) File, PageNum);
} else {
FreePool (File);
}
return Status;
}
/**
Remove console out variable
@param[in] OptionName Boot option string
@param[in] Attribute Attributes of boot option
@param[in] Description Description of boot option
@param[in] BootOptionHashValue Hash value of shell boot option
@retval EFI_SUCCESS Success to update shell device path in BootXXXX variable.
@retval EFI_NOT_FOUND Can not find the corresponding device path by hash value.
@retval Other Update variable fail.
**/
EFI_STATUS
UpdateShellDevicePath (
IN CHAR16 *OptionName,
IN UINT32 Attribute,
IN CHAR16 *Description,
IN UINT32 BootOptionHashValue
)
{
EFI_STATUS Status;
UINTN Index;
UINTN FvHandleCount;
EFI_HANDLE *FvHandleBuffer;
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
MEDIA_FW_VOL_FILEPATH_DEVICE_PATH ShellNode;
UINT8 OptionalData[SHELL_OPTIONAL_DATA_SIZE];
UINT32 HashValue;
Status = EFI_NOT_FOUND;
FvHandleCount = 0;
FvHandleBuffer = NULL;
gBS->LocateHandleBuffer (
ByProtocol,
&gEfiFirmwareVolume2ProtocolGuid,
NULL,
&FvHandleCount,
&FvHandleBuffer
);
for (Index = 0; Index < FvHandleCount; Index++) {
Status = GetShellFileHashValue (FvHandleBuffer[Index], &HashValue);
if (EFI_ERROR (Status) || (BootOptionHashValue != HashValue)) {
continue;
}
DevicePath = DevicePathFromHandle (FvHandleBuffer[Index]);
EfiInitializeFwVolDevicepathNode (&ShellNode, PcdGetPtr(PcdShellFile));
DevicePath = AppendDevicePathNode (DevicePath, (EFI_DEVICE_PATH_PROTOCOL *) &ShellNode);
if (IsAlreadyInBootOrder (DevicePath)) {
FreePool (DevicePath);
continue;
}
gBS->CopyMem (OptionalData , "RC" , 2);
gBS->CopyMem (&OptionalData[2], &HashValue, 4);
Status = BdsLibUpdateOptionVar (
OptionName,
Attribute,
DevicePath,
Description,
OptionalData,
SHELL_OPTIONAL_DATA_SIZE
);
FreePool (DevicePath);
if (!EFI_ERROR (Status)) {
break;
}
}
if (FvHandleCount != 0) {
FreePool (FvHandleBuffer);
}
if (Index == FvHandleCount) {
return EFI_NOT_FOUND;
}
return Status;
}
/**
Build the on flash shell boot option with the handle parsed in.
@param[in] Handle The handle which present the device path to create on flash shell boot option
@param[in, out] BdsBootOptionList The header of the link list which indexed all current boot options. Deprecated.
@param[in] Description Pointer to description string of boot option
**/
VOID
EFIAPI
BdsLibBuildOptionFromShell (
IN EFI_HANDLE Handle,
IN OUT LIST_ENTRY *BdsBootOptionList OPTIONAL,
IN CHAR16 *Description
)
{
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
MEDIA_FW_VOL_FILEPATH_DEVICE_PATH ShellNode;
EFI_STATUS Status;
UINT32 HashValue;
UINT8 OptionalData[SHELL_OPTIONAL_DATA_SIZE];
EFI_DEVICE_PATH_PROTOCOL *FilePath;
CHAR16 *CustomizedDescription;
EFI_STATUS OemSvcStatus;
DEBUG_CODE (
if (BdsBootOptionList != NULL) {
DEBUG ((DEBUG_INFO, "BdsLibBuildOptionFromShell(): BdsBootOptionList is deprecated. Please upgrade caller code to input NULL.\n"));
}
);
FilePath = NULL;
CustomizedDescription = NULL;
DevicePath = DevicePathFromHandle (Handle);
//
// Build the shell device path
//
EfiInitializeFwVolDevicepathNode (&ShellNode, PcdGetPtr(PcdShellFile));
DevicePath = AppendDevicePathNode (DevicePath, (EFI_DEVICE_PATH_PROTOCOL *) &ShellNode);
if (IsAlreadyInBootOrder (DevicePath)) {
goto Exit;
}
FilePath = AllocateZeroPool (DevicePathNodeLength (&ShellNode) + END_DEVICE_PATH_LENGTH);
if (FilePath != NULL) {
CopyMem (FilePath, &ShellNode, DevicePathNodeLength (&ShellNode));
SetDevicePathEndNode (NextDevicePathNode (FilePath));
}
CustomizedDescription = AllocateCopyPool (StrSize (Description), Description);
DEBUG_OEM_SVC ((DEBUG_INFO, "OemKernelServices Call: OemSvcDxeUpdateDescriptionOfBootOption \n"));
OemSvcStatus = OemSvcDxeUpdateDescriptionOfBootOption (DevicePath, FilePath, NULL, &CustomizedDescription);
DEBUG_OEM_SVC ((DEBUG_INFO, "OemKernelServices OemSvcDxeUpdateDescriptionOfBootOption Status %r\n", OemSvcStatus));
if (CustomizedDescription != NULL) {
Description = CustomizedDescription;
}
Status = GetShellFileHashValue (Handle, &HashValue);
if (EFI_ERROR(Status)) {
BdsLibRegisterNewOption (NULL, DevicePath, Description, L"BootOrder", (UINT8 *) "RC", 2);
goto Exit;
}
gBS->CopyMem (OptionalData , "RC" , 2);
gBS->CopyMem (&OptionalData[2], (UINT8 *) &HashValue, 4);
//
// Create and register the shell boot option
//
BdsLibRegisterNewOption (NULL, DevicePath, Description, L"BootOrder", OptionalData, SHELL_OPTIONAL_DATA_SIZE);
Exit:
FreePool (DevicePath);
if (FilePath != NULL) {
FreePool (FilePath);
}
if (CustomizedDescription != NULL) {
FreePool (CustomizedDescription);
}
}
/**
Enumerate all shell boot options.
@retval EFI_SUCCESS Enumerate all shell boot options success
@retval Other Fail to get FV protocol instance.
**/
EFI_STATUS
EnumerateAllShellBootOption (
VOID
)
{
EFI_STATUS Status;
UINTN FvHandleCount;
EFI_HANDLE *FvHandleBuffer;
UINTN Index;
UINT16 InternalShellNumber;
EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv;
EFI_FV_FILETYPE Type;
UINTN Size;
EFI_FV_FILE_ATTRIBUTES Attributes;
UINT32 AuthenticationStatus;
CHAR16 Buffer[128];
CHAR16 *DescStr;
Status = gBS->LocateHandleBuffer (
ByProtocol,
&gEfiFirmwareVolume2ProtocolGuid,
NULL,
&FvHandleCount,
&FvHandleBuffer
);
if (EFI_ERROR(Status)) {
return Status;
}
DescStr = NULL;
for (Index = 0, InternalShellNumber = 0; Index < FvHandleCount; Index++) {
Status = gBS->HandleProtocol (
FvHandleBuffer[Index],
&gEfiFirmwareVolume2ProtocolGuid,
(VOID **) &Fv
);
if (EFI_ERROR (Status)) {
continue;
}
Status = Fv->ReadFile (
Fv,
PcdGetPtr(PcdShellFile),
NULL,
&Size,
&Type,
&Attributes,
&AuthenticationStatus
);
if (EFI_ERROR (Status)) {
//
// Skip if no shell file in the FV
//
continue;
}
if (DescStr == NULL) {
DescStr = BdsLibGetStringById (STRING_TOKEN (STR_DESCRIPTION_SHELL));
if (DescStr == NULL) {
break;
}
}
//
// Build the shell boot option
//
if (InternalShellNumber == 0) {
UnicodeSPrint (Buffer, sizeof (Buffer), DescStr);
} else {
UnicodeSPrint (Buffer, sizeof (Buffer), L"%s %d", DescStr, InternalShellNumber);
}
InternalShellNumber++;
BdsLibBuildOptionFromShell (FvHandleBuffer[Index], NULL, Buffer);
}
if (FvHandleBuffer != NULL) {
FreePool (FvHandleBuffer);
}
if (DescStr != NULL) {
FreePool (DescStr);
}
return EFI_SUCCESS;
}
/**
Check if the input device path contains the query type and subtype or not.
@param[in] DevicePath Pointer to device path instance
@param[in] QueryType Query type of device path
@param[in] QuerySubType Query subtype of device path
@retval TRUE Remove variable was success.
@retval FALSE DevicePath doesn't contain the query type and subtype.
**/
BOOLEAN
CheckDevicePath (
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
IN UINT8 QueryType,
IN UINT8 QuerySubType
)
{
BOOLEAN Match;
EFI_DEVICE_PATH_PROTOCOL *FullDevicePath;
EFI_DEVICE_PATH_PROTOCOL *WorkingDevicePath;
Match = FALSE;
if (DevicePath == NULL) {
return FALSE;
}
FullDevicePath = BdsExpandPartitionPartialDevicePathToFull ((HARDDRIVE_DEVICE_PATH *) DevicePath);
WorkingDevicePath = (FullDevicePath == NULL) ? DevicePath : FullDevicePath;
while (!IsDevicePathEnd (WorkingDevicePath)) {
if (DevicePathType (WorkingDevicePath) == QueryType &&
DevicePathSubType (WorkingDevicePath) == QuerySubType) {
Match = TRUE;
break;
}
WorkingDevicePath = NextDevicePathNode (WorkingDevicePath);
}
if (FullDevicePath != NULL) {
FreePool (FullDevicePath);
}
return Match;
}
/**
Free the file path list.
@param[in] FilePathList Pointer to the file path list
@param[in] FilePathCount File path count
**/
STATIC
VOID
FreeFilePathList (
IN EFI_DEVICE_PATH_PROTOCOL **FilePathList,
IN UINTN FilePathCount
)
{
UINTN Index;
if (FilePathList == NULL) {
return;
}
for (Index = 0; Index < FilePathCount; Index++) {
FreePool (FilePathList[Index]);
}
FreePool (FilePathList);
}
/**
Get whole file path from gH2OBdsDefaultBootListGenericOsTokenSpaceGuid token space PCD.
This function allocates space for a pointer list which record the new copy of the device paths. If DevicePath is not
found or allocate memory failed, then NULL is returned. The memory for the new device
path is allocated from EFI boot services memory. It is the responsibility of the caller
to free the memory allocated.
@param[in] Handle A pointer to a device handle.
@param[in] TokenNum Token number in gH2OBdsDefaultBootListGenericOsTokenSpaceGuid token space.
@param[out] FilePathCount Count of file path in the list.
@return pointer to list of device path pointer or NULL if not found or allocate memory failed.
**/
EFI_DEVICE_PATH_PROTOCOL **
GetFilePathFromBootListGenericOsPcd (
IN EFI_HANDLE Handle,
IN UINTN TokenNum,
OUT UINTN *FilePathCount
)
{
CHAR16 *GenericOs;
CHAR16 *FilePath;
CHAR16 *FilePathPtr;
CHAR16 *SingleFilePath;
CHAR16 *CpuPath;
EFI_DEVICE_PATH_PROTOCOL **FilePathList;
EFI_DEVICE_PATH_PROTOCOL *FullFilePath;
UINTN Count;
UINTN FilePathSize;
UINTN Index;
UINTN CurrentIndex;
GenericOs = (CHAR16 *) LibPcdGetExPtr(&gH2OBdsDefaultBootListGenericOsTokenSpaceGuid, TokenNum);
if (GenericOs == NULL) {
return NULL;
}
//
// paring result should be as below:
// L"\\EFI\\android\\bootx64.efi\tAndroid" -> "\\EFI\\android\\bootx64.efi"
// L"\\EFI\\tencent\\grubx64.efi\ttencent\tNORMAL" -> "\\EFI\\tencent\\grubx64.efi"
// L"\\EFI\\\tencent\\grubx64.efi\ttencent\tNORMAL" -> "\\EFI\\"
//
for (Index = 0; ; Index++) {
if (GenericOs[Index] == L'\0') {
return NULL;
}
if (GenericOs[Index] == L'\\' && GenericOs[Index + 1] == L't') {
break;
}
if (GenericOs[Index] == L'\\' && GenericOs[Index + 1] == L'\\') {
Index += 1;
continue;
}
}
FilePathSize = (Index + 1) * sizeof (CHAR16);
FilePath = AllocateZeroPool (FilePathSize);
if (FilePath == NULL) {
return NULL;
}
CopyMem (FilePath, GenericOs, FilePathSize - sizeof (CHAR16));
if (sizeof (ARCHITECTURE_NAME) > sizeof (L"$cpu$")) {
Index = 0;
CpuPath = FilePath;
while ((CpuPath = StrStr (CpuPath, L"$cpu$")) != NULL) {
Index++;
CpuPath++;
}
if (Index != 0) {
FilePath = ReallocatePool (
FilePathSize,
FilePathSize + Index * (StrSize (ARCHITECTURE_NAME) - StrSize (L"$cpu$")),
FilePath
);
if (FilePath == NULL) {
return NULL;
}
}
}
//
// Convert string "\\" to string "\"
//
for (Index = 0, CurrentIndex = 0; FilePath[Index] != L'\0'; Index++, CurrentIndex++) {
if (FilePath[Index] == L'\\' && FilePath[Index + 1] == L'\\') {
Index++;
}
FilePath[CurrentIndex] = FilePath[Index];
}
FilePath[CurrentIndex] = L'\0';
CpuPath = FilePath;
while ((CpuPath = StrStr (CpuPath, L"$cpu$")) != NULL) {
if (sizeof (ARCHITECTURE_NAME) > sizeof (L"$cpu$")) {
CopyMem (
CpuPath + StrLen (ARCHITECTURE_NAME),
CpuPath + StrLen (L"$cpu$"),
StrSize (CpuPath) - sizeof (L"$cpu$") + sizeof (CHAR16)
);
CopyMem (CpuPath, ARCHITECTURE_NAME, StrLen (ARCHITECTURE_NAME) * sizeof (CHAR16));
} else {
CopyMem (CpuPath, ARCHITECTURE_NAME, StrLen (ARCHITECTURE_NAME) * sizeof (CHAR16));
CopyMem (
CpuPath + StrLen (ARCHITECTURE_NAME),
CpuPath + StrLen (L"$cpu$"),
StrSize (CpuPath) - sizeof (L"$cpu$") + sizeof (CHAR16)
);
}
}
//
// Parsing file path string to get each file path which is delimited by '|' character.
// Create a list to record all file path.
//
Count = 0;
FilePathList = NULL;
FilePathPtr = FilePath;
while (FilePathPtr != NULL) {
SingleFilePath = FilePathPtr;
FilePathPtr = StrStr (FilePathPtr, L"|");
if (FilePathPtr != NULL) {
*FilePathPtr = L'\0';
FilePathPtr++;
}
if (*SingleFilePath == L'\0') {
continue;
}
FullFilePath = FileDevicePath (Handle, SingleFilePath);
if (FullFilePath == NULL) {
continue;
}
FilePathList = ReallocatePool (
sizeof(EFI_DEVICE_PATH_PROTOCOL **) * Count,
sizeof(EFI_DEVICE_PATH_PROTOCOL **) * (Count + 1),
FilePathList
);
if (FilePathList == NULL) {
return NULL;
}
FilePathList[Count++] = FullFilePath;
}
FreePool (FilePath);
*FilePathCount = Count;
return FilePathList;
}
/**
Get OS description string from gH2OBdsDefaultBootListGenericOsTokenSpaceGuid token space PCD.
This function allocates space for OS description string. If description is not found or allocate
memory failed, then NULL is returned. The memory for the description is allocated from EFI boot
services memory. It is the responsibility of the caller to free the memory allocated.
@param[in] TokenNum Token number in gH2OBdsDefaultBootListGenericOsTokenSpaceGuid token space.
@return pointer to description or NULL if not found.
**/
STATIC
CHAR16 *
GetDescriptionFromBootListGenericOsPcd (
IN UINTN TokenNum
)
{
CHAR16 *GenericOs;
CHAR16 *TempPtr;
CHAR16 *Description;
UINTN DescriptionSize;
UINTN Index;
GenericOs = (CHAR16 *) LibPcdGetExPtr(&gH2OBdsDefaultBootListGenericOsTokenSpaceGuid, TokenNum);
if (GenericOs == NULL) {
return NULL;
}
for (Index = 0; !(GenericOs[Index] == L'\\' && GenericOs[Index + 1] == L't'); Index++) {
if (GenericOs[Index] == L'\0') {
return NULL;
}
}
TempPtr = &GenericOs[Index];
Index++;
while (!(GenericOs[Index] == L'\\' && GenericOs[Index + 1] == L't') && GenericOs[Index] != L'\0') {
Index++;
}
DescriptionSize = (UINTN) (&GenericOs[Index]) - (UINTN) TempPtr - sizeof (CHAR16);
Description = AllocateZeroPool (DescriptionSize);
if (Description == NULL) {
return NULL;
}
CopyMem (Description, TempPtr + 2, DescriptionSize - sizeof (CHAR16));
return Description;
}
/**
Get OS attribute flag from from gH2OBdsDefaultBootListGenericOsTokenSpaceGuid token space PCD.
@param[in] TokenNum Token number in gH2OBdsDefaultBootListGenericOsTokenSpaceGuid token space.
@retval SECURE_BOOT_ENABLED The generic OS PCD is for secure boot enabled or doesn't specific
secure is enabled or not.
@retval SECURE_BOOT_DISABLED The generic OS PCD is for secure boot disabled.
@retval UNKNOWN_SECURE_SETTING Cannot find generic OS from input token.
**/
STATIC
UINTN
GetBootFlagFromBootListGenericOsPcd (
IN UINTN TokenNum
)
{
CHAR16 *GenericOs;
UINTN Index;
UINTN TabIndex;
GenericOs = (CHAR16 *) LibPcdGetExPtr(&gH2OBdsDefaultBootListGenericOsTokenSpaceGuid, TokenNum);
if (GenericOs == NULL) {
return UNKNOWN_SECURE_SETTING;
}
Index = 0;
for (TabIndex = 0; TabIndex < MAX_BIOS_ENVIRONMENT; TabIndex++) {
for ( ;!(GenericOs[Index] == L'\\' && GenericOs[Index + 1] == L't'); Index++) {
if (GenericOs[Index] == L'\0') {
return SECURE_BOOT_ENABLED;
}
}
Index += 2;
}
return StrCmp (&GenericOs[Index], L"SECURE") == 0 ? SECURE_BOOT_ENABLED : SECURE_BOOT_DISABLED;
}
/**
Enumerate all PCDs in gH2OBdsDefaultBootListGenericOsTokenSpaceGuid to find out the PCD token number which
input device path match the file path list.
@param[in] DevPath Pointer to device path
@return PCD token number or zero if PCD is not found or DevPath is NULL.
**/
UINTN
GetGenericOsPcdByFilePath (
IN EFI_DEVICE_PATH_PROTOCOL *DevPath
)
{
EFI_DEVICE_PATH_PROTOCOL *DevPathNode;
UINTN Index;
EFI_DEVICE_PATH_PROTOCOL **FilePathList;
UINTN FilePathCount;
UINTN FilePathIndex;
UINTN TokenNum;
if (DevPath == NULL) {
return 0;
}
for (DevPathNode = DevPath; !IsDevicePathEnd (DevPathNode); DevPathNode = NextDevicePathNode (DevPathNode)) {
if ((DevicePathType (DevPathNode) == MEDIA_DEVICE_PATH) &&
(DevicePathSubType (DevPathNode) == MEDIA_FILEPATH_DP)) {
break;
}
}
if (IsDevicePathEnd (DevPathNode)) {
return 0;
}
for (Index = 0; Index < MAX_BIOS_ENVIRONMENT; Index++) {
for (TokenNum = LibPcdGetNextToken(&gH2OBdsDefaultBootListGenericOsTokenSpaceGuid, 0);
TokenNum != 0;
TokenNum = LibPcdGetNextToken(&gH2OBdsDefaultBootListGenericOsTokenSpaceGuid, TokenNum)) {
if (GetBootFlagFromBootListGenericOsPcd (TokenNum) != Index) {
continue;
}
FilePathList = GetFilePathFromBootListGenericOsPcd (NULL, TokenNum, &FilePathCount);
if (FilePathList == NULL) {
continue;
}
for (FilePathIndex = 0; FilePathIndex < FilePathCount; FilePathIndex++) {
if (BdsLibMatchFilePathDevicePathNode (FilePathList[FilePathIndex], DevPathNode)) {
FreeFilePathList (FilePathList, FilePathCount);
return TokenNum;
}
}
FreeFilePathList (FilePathList, FilePathCount);
}
}
return 0;
}
/**
According to block IO and device path protocol which installed on the input handle to get media type
If input handle doesn't contain block IO or device path protocol this function will return MaxMediaType
to indicate we cannot recognize the media type.
@param[in] Handle The handle which contains block IO and device path protocol.
@return Media type
**/
STATIC
BDS_MEDIA_TYPE
GetMediaType (
IN EFI_HANDLE Handle
)
{
EFI_STATUS Status;
EFI_BLOCK_IO_PROTOCOL *BlkIo;
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
UINTN DevicePathType;
Status = gBS->HandleProtocol (
Handle,
&gEfiBlockIoProtocolGuid,
(VOID **) &BlkIo
);
if (Status != EFI_SUCCESS) {
return MaxMediaType;
}
DevicePath = DevicePathFromHandle (Handle);
if (DevicePath == NULL) {
return MaxMediaType;
}
DevicePathType = BdsLibGetBootTypeFromDevicePath (DevicePath);
if (!BlkIo->Media->RemovableMedia &&
DevicePathType != BDS_EFI_MESSAGE_USB_DEVICE_BOOT &&
CheckDevicePath (DevicePath, MEDIA_DEVICE_PATH, MEDIA_HARDDRIVE_DP)) {
return NonRemovableHdd;
} else if (DevicePathType == BDS_EFI_MESSAGE_USB_DEVICE_BOOT &&
CheckDevicePath (DevicePath, MEDIA_DEVICE_PATH, MEDIA_HARDDRIVE_DP)) {
return UsbHdd;
} else {
return MaxMediaType;
}
}
/**
According to block IO and device path protocol which installed on the input handle to determine
this is non-removable HDD or not
If input handle doesn't contain block IO or device path protocol this function will return FALSE
to indicate this media isn't a non-removable HDD.
@param[in] Handle The handle which contains block IO and device path protocol.
@retval TRUE The media is a non-removable HDD.
@retval FALSE The media isn't a removable HDD.
**/
BOOLEAN
IsNonRemovableHdd (
IN EFI_HANDLE Handle
)
{
return GetMediaType (Handle) == NonRemovableHdd;
}
/**
Internal function to check whether the media can install generic UEFI OS.
@param[in] Handle The handle which contains block IO and device path protocol.
@retval TRUE The media can install generic UEFI OS.
@retval FALSE The media cannot install generic UEFI OS.
**/
STATIC
BOOLEAN
IsGenericUefiOsMedia (
IN EFI_HANDLE Handle
)
{
BDS_MEDIA_TYPE MediaType;
MediaType = GetMediaType (Handle);
if (MediaType == NonRemovableHdd || MediaType == UsbHdd) {
return TRUE;
} else {
return FALSE;
}
}
/**
Check if it is a generic UEFI bootable OS or not.
@param[in] Handle A pointer to a device handle.
@param[in] WindowsToGoEnabled If True, Windows to Go is enabled.
If FALSE, Windows to Go is disabled.
@retval TRUE This is a generic UEFI Bootable OS.
@retval FALSE This is not a generic UEFI Bootable OS.
**/
BOOLEAN
IsGenericUefiBootOs (
IN EFI_HANDLE Handle,
IN BOOLEAN WindowsToGoEnabled
)
{
EFI_STATUS Status;
UINTN TokenNum;
UINTN Index;
UINTN FilePathCount;
EFI_DEVICE_PATH_PROTOCOL **FilePath;
EFI_FILE_HANDLE FileHandle;
if (!IsGenericUefiOsMedia (Handle)) {
return FALSE;
}
for (TokenNum = LibPcdGetNextToken(&gH2OBdsDefaultBootListGenericOsTokenSpaceGuid, 0); TokenNum != 0; \
TokenNum = LibPcdGetNextToken(&gH2OBdsDefaultBootListGenericOsTokenSpaceGuid, TokenNum)) {
if (!WindowsToGoEnabled && PcdToken (MicrosoftOS) == TokenNum && GetMediaType (Handle) == UsbHdd) {
continue;
}
FilePath = GetFilePathFromBootListGenericOsPcd (Handle, TokenNum, &FilePathCount);
if (FilePath == NULL) {
continue;
}
for (Index = 0; Index < FilePathCount; Index++) {
Status = BdsLibOpenFileFromDevicePath (
FilePath[Index],
EFI_FILE_MODE_READ,
0,
&FileHandle
);
if (Status == EFI_SUCCESS) {
if (FileHandle != NULL) {
FileHandle->Close (FileHandle);
}
FreeFilePathList (FilePath, FilePathCount);
return TRUE;
}
}
FreeFilePathList (FilePath, FilePathCount);
}
return FALSE;
}
/**
Check whether the input device path is in the file path list of input PCD.
@param[in] PcdTokenNum PCD token number in gH2OBdsDefaultBootListGenericOsTokenSpaceGuid.
@param[in] DevPath Pointer to device path
@retval TRUE The input device path is in the file path list of input PCD.
@retval FALSE The input device path is not in the file path list of input PCD.
**/
BOOLEAN
IsFilePathInGenericOsPcd (
IN UINTN PcdTokenNum,
IN EFI_DEVICE_PATH_PROTOCOL *DevPath
)
{
EFI_DEVICE_PATH_PROTOCOL **FilePathList;
UINTN FilePathCount;
UINTN FilePathIndex;
if (DevPath == NULL || PcdTokenNum == 0) {
return FALSE;
}
FilePathList = GetFilePathFromBootListGenericOsPcd (NULL, PcdTokenNum, &FilePathCount);
if (FilePathList == NULL) {
return FALSE;
}
for (FilePathIndex = 0; FilePathIndex < FilePathCount; FilePathIndex++) {
if (BdsLibMatchFilePathDevicePathNode (FilePathList[FilePathIndex], DevPath)) {
FreeFilePathList (FilePathList, FilePathCount);
return TRUE;
}
}
FreeFilePathList (FilePathList, FilePathCount);
return FALSE;
}
/**
Check if the hard drive file path is specified in the boot option variable or not.
@param[in] FilePath A pointer to hard drive file path
@retval TRUE The hard drive file path is specified in the boot option variable
@retval FALSE The hard drive file path is not specified in the boot option variable
**/
STATIC
BOOLEAN
IsHardDriveFilePathInBootOptionVariables (
IN EFI_DEVICE_PATH_PROTOCOL *FilePath
)
{
UINT16 *BootOrder;
UINTN BootOrderSize;
UINTN BootDevNum;
UINTN Index;
CHAR16 OptionName[10];
UINT8 *OptionPtr;
UINTN OptionSize;
UINT8 *TempPtr;
EFI_DEVICE_PATH_PROTOCOL *OptionDevicePath;
BootOrder = BdsLibGetVariableAndSize (L"BootOrder", &gEfiGlobalVariableGuid, &BootOrderSize);
if (BootOrder == NULL) {
return FALSE;
}
BootDevNum = BootOrderSize / sizeof (UINT16);
for (Index = 0; Index < BootDevNum; Index++) {
UnicodeSPrint (OptionName, sizeof (OptionName), L"Boot%04x", BootOrder[Index]);
OptionPtr = BdsLibGetVariableAndSize (OptionName, &gEfiGlobalVariableGuid, &OptionSize);
if (OptionPtr == NULL) {
continue;
}
TempPtr = OptionPtr;
TempPtr += sizeof (UINT32) + sizeof (UINT16);
TempPtr += StrSize ((CHAR16 *) TempPtr);
OptionDevicePath = (EFI_DEVICE_PATH_PROTOCOL *) TempPtr;
if (MatchPartitionDevicePathNode (OptionDevicePath, (HARDDRIVE_DEVICE_PATH *) FilePath) &&
BdsLibMatchFilePathDevicePathNode (OptionDevicePath, FilePath)) {
FreePool (OptionPtr);
FreePool (BootOrder);
return TRUE;
}
FreePool (OptionPtr);
}
FreePool (BootOrder);
return FALSE;
}
/**
Check whether the input device path is a generic UEFI OS boot option or not.
@param[in] OptionDevicePath Pointer to device path of boot option
@retval TRUE The input device path is a generic UEFI OS boot option
@retval FALSE The input device path is not a generic UEFI OS boot option
**/
BOOLEAN
IsGenericUefiBootOsDevPath (
IN EFI_DEVICE_PATH_PROTOCOL *OptionDevicePath
)
{
EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
EFI_DEVICE_PATH_PROTOCOL *LastDeviceNode;
TempDevicePath = OptionDevicePath;
LastDeviceNode = OptionDevicePath;
while (!IsDevicePathEnd (TempDevicePath)) {
LastDeviceNode = TempDevicePath;
TempDevicePath = NextDevicePathNode (TempDevicePath);
}
if ((DevicePathType (LastDeviceNode) == MEDIA_DEVICE_PATH) &&
(DevicePathSubType (LastDeviceNode) == MEDIA_FILEPATH_DP)) {
return TRUE;
}
return FALSE;
}
/**
Create generic UEFI bootable OS.
@param[in] Handle A pointer to a device handle.
@retval EFI_SUCCESS Create generic UEFI OS success
@retval Other Register new boot option fail
**/
EFI_STATUS
CreateGenericUefiBootOs (
IN EFI_HANDLE Handle
)
{
UINTN Index;
UINTN TokenNum;
EFI_DEVICE_PATH_PROTOCOL **FilePath;
UINTN FilePathCount;
UINTN FilePathIndex;
EFI_FILE_HANDLE FileHandle;
EFI_STATUS Status;
EFI_STATUS RegisterStatus;
EFI_DEVICE_PATH_PROTOCOL *TempFilePath;
EFI_DEVICE_PATH_PROTOCOL *OptionFilePath;
CHAR16 *Description;
CHAR16 **Descriptions;
UINTN DescriptionCount;
UINTN AddedSecureCount;
UINTN OsIndex;
EFI_STATUS OemSvcStatus;
RegisterStatus = EFI_SUCCESS;
DescriptionCount = 0;
for (TokenNum = LibPcdGetNextToken(&gH2OBdsDefaultBootListGenericOsTokenSpaceGuid, 0); TokenNum != 0; \
TokenNum = LibPcdGetNextToken(&gH2OBdsDefaultBootListGenericOsTokenSpaceGuid, TokenNum)) {
DescriptionCount++;
}
Descriptions = AllocateZeroPool (DescriptionCount * sizeof (CHAR16 *));
if (Descriptions == NULL) {
return EFI_OUT_OF_RESOURCES;
}
AddedSecureCount = 0;
for (Index = 0; Index < MAX_BIOS_ENVIRONMENT; Index++) {
for (TokenNum = LibPcdGetNextToken(&gH2OBdsDefaultBootListGenericOsTokenSpaceGuid, 0); TokenNum != 0; \
TokenNum = LibPcdGetNextToken(&gH2OBdsDefaultBootListGenericOsTokenSpaceGuid, TokenNum)) {
if (GetBootFlagFromBootListGenericOsPcd (TokenNum) != Index) {
continue;
}
FilePath = GetFilePathFromBootListGenericOsPcd (Handle, TokenNum, &FilePathCount);
if (FilePath == NULL) {
continue;
}
//
// Check all file paths in PCD. If the file exist and is defined in Boot#### variable, use this file path to register boot option.
// Otherwise, use the first one found file path to register boot option.
//
OptionFilePath = NULL;
for (FilePathIndex = 0; FilePathIndex < FilePathCount; FilePathIndex++) {
Status = BdsLibOpenFileFromDevicePath (
FilePath[FilePathIndex],
EFI_FILE_MODE_READ,
0,
&FileHandle
);
if (Status == EFI_SUCCESS) {
//
// If open generic OS loader file success, register a new boot option.
//
if (FileHandle != NULL) {
FileHandle->Close (FileHandle);
}
TempFilePath = FilePath[FilePathIndex];
while (!IsDevicePathEnd (TempFilePath) &&
(TempFilePath->Type != MEDIA_DEVICE_PATH || TempFilePath->SubType != MEDIA_HARDDRIVE_DP)) {
TempFilePath = NextDevicePathNode (TempFilePath);
}
if (IsDevicePathEnd (TempFilePath)) {
continue;
}
if (OptionFilePath == NULL) {
OptionFilePath = AllocateCopyPool (GetDevicePathSize (TempFilePath), TempFilePath);
continue;
}
if (IsHardDriveFilePathInBootOptionVariables (TempFilePath)) {
FreePool (OptionFilePath);
OptionFilePath = AllocateCopyPool (GetDevicePathSize (TempFilePath), TempFilePath);
break;
}
}
}
FreeFilePathList (FilePath, FilePathCount);
if (OptionFilePath == NULL) {
continue;
}
Description = GetDescriptionFromBootListGenericOsPcd (TokenNum);
if (Description == NULL) {
continue;
}
//
// Don't create Microsoft OS which installed on removable media. They will handle by
// windows to go mechanism.
//
if (PcdToken (MicrosoftOS) == TokenNum && !IsNonRemovableHdd (Handle)) {
continue;
}
if (Index == SECURE_BOOT_ENABLED) {
//
// Record added OS for secure boot enabled.
//
Descriptions[AddedSecureCount++] = AllocateCopyPool (StrSize (Description), Description);
} else {
//
// Don't add OS boot option for secure boot disabled if corresponding option for secure boot enabled is added
//
for (OsIndex = 0; OsIndex < AddedSecureCount; OsIndex++) {
if (StrCmp (Descriptions[OsIndex], Description) == 0) {
break;
}
}
if (OsIndex != AddedSecureCount) {
continue;
}
}
DEBUG_OEM_SVC ((DEBUG_INFO, "OemKernelServices Call: OemSvcDxeUpdateDescriptionOfBootOption \n"));
OemSvcStatus = OemSvcDxeUpdateDescriptionOfBootOption (DevicePathFromHandle (Handle), OptionFilePath, NULL, &Description);
DEBUG_OEM_SVC ((DEBUG_INFO, "OemKernelServices OemSvcDxeUpdateDescriptionOfBootOption Status: %r\n", OemSvcStatus));
Status = BdsLibRegisterNewOption (
NULL,
OptionFilePath,
Description,
L"BootOrder",
(UINT8 *) "RC",
2
);
ASSERT_EFI_ERROR (Status);
if (EFI_ERROR(Status)) {
RegisterStatus = Status;
}
FreePool (Description);
FreePool (OptionFilePath);
}
}
//
// Free allocated strings
//
for (OsIndex = 0; OsIndex < AddedSecureCount; OsIndex++) {
FreePool (Descriptions[OsIndex]);
}
FreePool (Descriptions);
return RegisterStatus;
}
/**
Initialize module device type information.
**/
STATIC
VOID
InitDeviceTypeInfo (
VOID
)
{
UINTN TableCount;
UINTN Index;
TableCount = sizeof (mDeviceTypeInfo) / sizeof (DEVICE_TYPE_INFORMATION);
for (Index = 0; Index < TableCount; Index++) {
mDeviceTypeInfo[Index].DeviceNum = 0;
}
}
/**
Check whether two boot options are the same or not.
@param[in] FirstBootOption Pointer to first boot option data.
@param[in] FirstBootOptionSize First boot option data size in bytes.
@param[in] SecondBootOption Pointer to second boot option data.
@param[in] SecondBootOptionSize Second boot option data size in bytes.
@retval TRUE Two boot options are the same.
@retval FALSE Two boot options aren't the same.
**/
BOOLEAN
IsSameBootOption (
IN UINT8 *FirstBootOption,
IN UINTN FirstBootOptionSize,
IN UINT8 *SecondBootOption,
IN UINTN SecondBootOptionSize
)
{
CHAR16 *FirstDescription;
CHAR16 *SecondeDescription;
UINTN FirstDevicePathOffset;
UINTN SecondDevicePathOffset;
UINTN TableCount;
UINTN Index;
UINTN CompareResult;
CHAR16 *DeviceStr;
//
// Whole boot option data are the identical indicates these two boot options are the same.
//
if (FirstBootOptionSize == SecondBootOptionSize &&
CompareMem (FirstBootOption, SecondBootOption, FirstBootOptionSize) == 0) {
return TRUE;
}
//
// return FALSE if device path or optional data in boot option are different.
//
FirstDescription = (CHAR16 *) (FirstBootOption + sizeof (UINT32) + sizeof (UINT16));
FirstDevicePathOffset = sizeof (UINT32) + sizeof (UINT16) + StrSize (FirstDescription);
SecondeDescription = (CHAR16 *) (SecondBootOption + sizeof (UINT32) + sizeof (UINT16));
SecondDevicePathOffset = sizeof (UINT32) + sizeof (UINT16) + StrSize (SecondeDescription);
if (FirstBootOptionSize - FirstDevicePathOffset != SecondBootOptionSize - SecondDevicePathOffset) {
return FALSE;
}
CompareResult = CompareMem (
FirstBootOption + FirstDevicePathOffset,
SecondBootOption + SecondDevicePathOffset,
FirstBootOptionSize - FirstDevicePathOffset
);
if (CompareResult != 0) {
return FALSE;
}
//
// Only return TRUE if the input description is the same type and created by BIOS.
//
TableCount = sizeof (mDeviceTypeInfo) / sizeof (DEVICE_TYPE_INFORMATION);
for (Index = 0; Index < TableCount; Index++) {
DeviceStr = BdsLibGetStringById (mDeviceTypeInfo[Index].StringToken);
ASSERT (DeviceStr != NULL);
if (DeviceStr == NULL) {
continue;
}
if (StrnCmp (FirstDescription, DeviceStr, StrLen (DeviceStr)) == 0 &&
StrnCmp (SecondeDescription, DeviceStr, StrLen (DeviceStr)) == 0) {
FreePool (DeviceStr);
return TRUE;
}
FreePool (DeviceStr);
}
return FALSE;
}
/**
Check if it has the default removable file or not.
@param[in] Handle A pointer to a device handle.
@retval TRUE It has the default removable file.
@retval FALSE It does not has the default removable file.
**/
BOOLEAN
HaveDefaultRemovableFile (
IN EFI_HANDLE Handle
)
{
EFI_DEVICE_PATH_PROTOCOL *FilePath;
EFI_FILE_HANDLE FileHandle;
EFI_STATUS Status;
FilePath = FileDevicePath (Handle, EFI_REMOVABLE_MEDIA_FILE_NAME);
Status = BdsLibOpenFileFromDevicePath (
FilePath,
EFI_FILE_MODE_READ,
0,
&FileHandle
);
FreePool (FilePath);
if (Status == EFI_SUCCESS) {
if (FileHandle != NULL) {
FileHandle->Close (FileHandle);
}
return TRUE;
}
return FALSE;
}
/**
Check whether the input device path is a removable boot option or not.
@param[in] OptionDevicePath Pointer to device path of boot option
@retval TRUE The input device path is a removable boot option
@retval FALSE The input device path is not a removable boot option
**/
BOOLEAN
IsValidRemovableBootOptionDevPath (
IN EFI_DEVICE_PATH_PROTOCOL *OptionDevicePath
)
{
EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
EFI_STATUS Status;
EFI_HANDLE Handle;
TempDevicePath = OptionDevicePath;
Status = gBS->LocateDevicePath (
&gEfiSimpleFileSystemProtocolGuid,
&TempDevicePath,
&Handle
);
if (!EFI_ERROR (Status) && IsDevicePathEnd (TempDevicePath)) {
return TRUE;
}
return FALSE;
}
/**
Create removable boot option form according to input handle.
@param[in] Handle The handle which present the device path to create
boot option.
@retval EFI_SUCCESS Create removable boot option successful.
@retval EFI_NOT_FOUND Cannot find device path from input handle.
**/
STATIC
EFI_STATUS
CreateRemovableBootOption (
IN EFI_HANDLE Handle
)
{
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
UINTN DevicePathType;
HARDWARE_BOOT_DEVICE_INFO *HwBootDeviceInfo;
UINTN HwBootDeviceCount;
UINTN HwNum;
UINTN TableCount;
UINTN Index;
CHAR16 Buffer[128];
CHAR16 *DeviceStr;
UINT32 Attribute;
EFI_STATUS Status;
EFI_BLOCK_IO_PROTOCOL *BlkIo;
Attribute = 0;
Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **) &BlkIo);
if (!EFI_ERROR (Status)) {
Attribute |= (BlkIo->Media->RemovableMedia ? ATTR_REMOVABLE_DEV : ATTR_UNREMOVABLE_DEV);
}
DevicePath = DevicePathFromHandle (Handle);
if (DevicePath == NULL) {
return EFI_NOT_FOUND;
}
ZeroMem (Buffer, sizeof (Buffer));
DevicePathType = BdsLibGetBootTypeFromDevicePath (DevicePath);
TableCount = sizeof (mDeviceTypeInfo) / sizeof (DEVICE_TYPE_INFORMATION);
//
// Get Device type string.
//
for (Index = 0; Index < TableCount; Index++) {
if ((DevicePathType == mDeviceTypeInfo[Index].DevicePathType) &&
(Attribute == (mDeviceTypeInfo[Index].Attribute & Attribute))) {
DeviceStr = BdsLibGetStringById (mDeviceTypeInfo[Index].StringToken);
ASSERT (DeviceStr != NULL);
if (DeviceStr == NULL) {
continue;
}
if (mDeviceTypeInfo[Index].DeviceNum == 0) {
UnicodeSPrint (Buffer, sizeof (Buffer), L"%s", DeviceStr);
} else {
UnicodeSPrint (
Buffer,
sizeof (Buffer),
L"%s %d",
DeviceStr,
mDeviceTypeInfo[Index].DeviceNum
);
}
FreePool (DeviceStr);
mDeviceTypeInfo[Index].DeviceNum++;
break;
}
}
if (Index == TableCount) {
BdsLibBuildOptionFromHandle (Handle, NULL, NULL);
} else {
//
// Get hardware device name for current boot option.
//
HwBootDeviceCount = 0;
BdsLibGetAllHwBootDeviceInfo (&HwBootDeviceCount, &HwBootDeviceInfo);
for (HwNum = 0; HwNum < HwBootDeviceCount; HwNum++) {
if (HwBootDeviceInfo != NULL && BdsLibCompareBlockIoDevicePath (HwBootDeviceInfo[HwNum].BlockIoDevicePath, DevicePath)) {
break;
}
}
if (HwNum == HwBootDeviceCount || HwBootDeviceInfo == NULL ) {
BdsLibBuildOptionFromHandle (Handle, NULL, Buffer);
} else {
UnicodeSPrint (Buffer, sizeof(Buffer), L"%s (%s)", Buffer, HwBootDeviceInfo[HwNum].HwDeviceName);
BdsLibBuildOptionFromHandle (Handle, NULL, Buffer);
}
//
// Free ardware device name.
//
if (HwBootDeviceCount != 0 && HwBootDeviceInfo != NULL ) {
for (Index = 0; Index < HwBootDeviceCount; Index++) {
FreePool (HwBootDeviceInfo[Index].HwDeviceName);
}
FreePool (HwBootDeviceInfo);
}
}
return EFI_SUCCESS;
}
/**
Delete boot option which device path and input device path is matched
@param[in] DevicePath Pointer to device path instance
@retval EFI_SUCCESS Finished all the boot device enumerate and create
the boot option base on that boot device
@retval Other
**/
STATIC
EFI_STATUS
DeleteRecoveryOption (
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath
)
{
UINT16 *BootOrder;
UINT8 *BootOptionVar;
UINTN BootOrderSize;
UINTN BootOptionSize;
EFI_STATUS Status;
UINTN Index;
UINT16 BootOption[BOOT_OPTION_MAX_CHAR];
EFI_DEVICE_PATH_PROTOCOL *OptionDevicePath;
UINTN OptionDevicePathSize;
EFI_DEVICE_PATH_PROTOCOL *ExpandedDevPath;
BOOLEAN Found;
UINT8 *TempPtr;
CHAR16 *Description;
UINTN DevPathSize;
Status = EFI_SUCCESS;
BootOrder = NULL;
BootOrderSize = 0;
BootOrder = BdsLibGetVariableAndSize (
L"BootOrder",
&gEfiGlobalVariableGuid,
&BootOrderSize
);
if (NULL == BootOrder) {
return EFI_NOT_FOUND;
}
DevPathSize = GetDevicePathSize (DevicePath);
Index = 0;
while (Index < BootOrderSize / sizeof (UINT16)) {
UnicodeSPrint (BootOption, sizeof (BootOption), L"Boot%04x", BootOrder[Index]);
BootOptionVar = BdsLibGetVariableAndSize (
BootOption,
&gEfiGlobalVariableGuid,
&BootOptionSize
);
if (NULL == BootOptionVar) {
//
//If the Boot Device is not exit, we should dynamically adjust the BootOrder
//
BdsLibUpdateInvalidBootOrder (&BootOrder, Index, &BootOrderSize);
if (BootOrder == NULL) {
break;
}
continue;
}
TempPtr = BootOptionVar;
TempPtr += sizeof (UINT32);
OptionDevicePathSize = (UINTN) (*((UINT16 *) TempPtr));
TempPtr += sizeof (UINT16);
Description = (CHAR16 *) TempPtr;
TempPtr += StrSize ((CHAR16 *) TempPtr);
OptionDevicePath = (EFI_DEVICE_PATH_PROTOCOL *) TempPtr;
ExpandedDevPath = IS_USB_SHORT_FORM_DEVICE_PATH(OptionDevicePath) ? BdsLibExpandUsbShortFormDevPath (OptionDevicePath) : NULL;
if (ExpandedDevPath != NULL) {
OptionDevicePath = ExpandedDevPath;
OptionDevicePathSize = GetDevicePathSize (ExpandedDevPath);
}
if (OptionDevicePathSize == DevPathSize && CompareMem (OptionDevicePath, DevicePath, OptionDevicePathSize) == 0) {
Found = TRUE;
} else {
Found = FALSE;
}
FreePool (BootOptionVar);
if (ExpandedDevPath != NULL) {
FreePool (ExpandedDevPath);
}
if (Found) {
BdsLibDeleteBootOption (BootOrder[Index], BootOrder, &BootOrderSize);
continue;
}
Index++;
}
Status = gRT->SetVariable (
L"BootOrder",
&gEfiGlobalVariableGuid,
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
BootOrderSize,
BootOrder
);
if (BootOrder != NULL) {
FreePool (BootOrder);
}
return Status;
}
/**
Enumerate all simple file system boot options.
@param[in] WindowsToGoBootVarExist Flag to indicate Windows To Go boot option is existence or not
@param[in] IsUsbBootSupported Flag to indicate USB boot is supported or not
@retval EFI_SUCCESS Enumerate all simple file system boot options success
**/
EFI_STATUS
EnumerateAllSimpleFileSysBootOption (
IN BOOLEAN WindowsToGoBootVarExist,
IN BOOLEAN IsUsbBootSupported
)
{
EFI_STATUS Status;
UINTN NumberFileSystemHandles;
EFI_HANDLE *FileSystemHandles;
UINTN Size;
OPROM_STORAGE_DEVICE_INFO *OpromStorageDev;
UINTN OpromStorageDevCount;
UINT8 *DisableOpromStorageDevBoot;
UINTN Index;
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
EFI_BLOCK_IO_PROTOCOL *BlkIo;
InitDeviceTypeInfo ();
Status = gBS->LocateHandleBuffer (
ByProtocol,
&gEfiSimpleFileSystemProtocolGuid,
NULL,
&NumberFileSystemHandles,
&FileSystemHandles
);
if (EFI_ERROR (Status)) {
return Status;
}
OpromStorageDev = NULL;
OpromStorageDevCount = 0;
DisableOpromStorageDevBoot = BdsLibGetVariableAndSize (
L"DisableOpromStorageDevBoot",
&gEfiGenericVariableGuid,
&Size
);
if (DisableOpromStorageDevBoot != NULL) {
BdsLibGetOpromStorageDevInfo (&OpromStorageDev, &OpromStorageDevCount);
}
for (Index = 0; Index < NumberFileSystemHandles; Index++) {
DevicePath = DevicePathFromHandle (FileSystemHandles[Index]);
//
// If need, skip the option ROM storage device.
//
if (IsOpromStorageDev (DevicePath, OpromStorageDev, OpromStorageDevCount)) {
continue;
}
//
// If system doesn't support USB boot, needn't add USB EFI boot option
//
if (!IsUsbBootSupported && IsUsbDevicePath (DevicePath)) {
continue;
}
Status = gBS->HandleProtocol (
FileSystemHandles[Index],
&gEfiBlockIoProtocolGuid,
(VOID **) &BlkIo
);
if (!EFI_ERROR (Status)) {
//
// If find UEFI OS option in unremovable device, delete exist recovery boot option.
// If cannot find UEFI OS option, try to find default EFI boot option as recovery boot option.
//
if (FeaturePcdGet (PcdH2OBdsDefaultBootListGenericOsSupported) &&
IsGenericUefiBootOs (FileSystemHandles[Index], WindowsToGoBootVarExist)) {
CreateGenericUefiBootOs (FileSystemHandles[Index]);
if (IsNonRemovableHdd (FileSystemHandles[Index])) {
DeleteRecoveryOption (DevicePath);
}
if (WindowsToGoBootVarExist && IsWindowsToGo (FileSystemHandles[Index])) {
BdsLibDeleteOptionFromHandle (FileSystemHandles[Index]);
}
continue;
}
}
if (WindowsToGoBootVarExist && IsWindowsToGo (FileSystemHandles[Index])) {
BdsLibDeleteOptionFromHandle (FileSystemHandles[Index]);
continue;
}
//
// Do the removable Media thing. \EFI\BOOT\boot{machinename}.EFI
// machinename is ia32, ia64, x64, ...
//
if (!HaveDefaultRemovableFile (FileSystemHandles[Index])) {
//
// No such file or the file is not a EFI application, delete this boot option
//
BdsLibDeleteOptionFromHandle (FileSystemHandles[Index]);
} else {
CreateRemovableBootOption (FileSystemHandles[Index]);
}
}
if (FileSystemHandles != NULL) {
FreePool (FileSystemHandles);
}
if (DisableOpromStorageDevBoot != NULL) {
FreePool (DisableOpromStorageDevBoot);
if (OpromStorageDevCount) {
FreePool (OpromStorageDev);
}
}
return EFI_SUCCESS;
}