3454 lines
112 KiB
C
3454 lines
112 KiB
C
/** @file
|
|
GenericBdsLib
|
|
|
|
;******************************************************************************
|
|
;* Copyright (c) 2012 - 2021, Insyde Software Corporation. 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.
|
|
;*
|
|
;******************************************************************************
|
|
*/
|
|
|
|
/**
|
|
BDS Lib functions which relate with create or process the boot option.
|
|
|
|
Copyright (c) 2004 - 2011, Intel Corporation. All rights reserved.<BR>
|
|
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 "InternalBdsLib.h"
|
|
#include "String.h"
|
|
#include "BdsBootOption.h"
|
|
#include "BdsCheckFunctions.h"
|
|
#include "BdsDummyBootOption.h"
|
|
#include <Guid/ReturnFromImage.h>
|
|
#include <Guid/ConsoleOutDevice.h>
|
|
#include <Guid/Gpt.h>
|
|
#include <Guid/BdsDefaultBootOptions.h>
|
|
|
|
EFI_HII_HANDLE gBdsLibStringPackHandle = NULL;
|
|
STATIC EFI_EVENT mBdsServicesEvent;
|
|
H2O_BDS_SERVICES_PROTOCOL *gBdsServices;
|
|
|
|
GENERIC_BDS_LIB_GLOBAL_DATA *mGenericBdsLibGlobalData = NULL;
|
|
EFI_GUID mGenericBdsLibGlobalDataProtocolGuid = {0xda8a55e4, 0x4940, 0x4af9, {0x9f, 0x36, 0x2d, 0xb4, 0x3e, 0xc2, 0x82, 0x59}};
|
|
|
|
/**
|
|
Initialize GenericBdsLib global data.
|
|
|
|
@retval EFI_SUCCESS Initialize global data successful.
|
|
@retval EFI_OUT_OF_RESOURCES Allocate memory failed.
|
|
@retval Other Install protocol failed.
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
InitGenericBdsGlobalData (
|
|
VOID
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_HANDLE Handle;
|
|
|
|
Status = gBS->LocateProtocol (
|
|
&mGenericBdsLibGlobalDataProtocolGuid,
|
|
NULL,
|
|
(VOID **) &mGenericBdsLibGlobalData
|
|
);
|
|
if (!EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
mGenericBdsLibGlobalData = AllocateZeroPool (sizeof(GENERIC_BDS_LIB_GLOBAL_DATA));
|
|
if (mGenericBdsLibGlobalData == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
mGenericBdsLibGlobalData->EnumBootDevice = FALSE;
|
|
mGenericBdsLibGlobalData->EnableBootOrderHook = FALSE;
|
|
mGenericBdsLibGlobalData->UefiFastBootEnabled = TRUE;
|
|
mGenericBdsLibGlobalData->ResetReminderFeatureSwitch = TRUE;
|
|
mGenericBdsLibGlobalData->ResetRequired = FALSE;
|
|
mGenericBdsLibGlobalData->PreviousHandlesNum = 0;
|
|
mGenericBdsLibGlobalData->PreviousHandles = NULL;
|
|
InitializeListHead (&mGenericBdsLibGlobalData->UsbShortFormInfoList);
|
|
|
|
Handle = NULL;
|
|
Status = gBS->InstallProtocolInterface (
|
|
&Handle,
|
|
&mGenericBdsLibGlobalDataProtocolGuid,
|
|
EFI_NATIVE_INTERFACE,
|
|
mGenericBdsLibGlobalData
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
FreePool (mGenericBdsLibGlobalData);
|
|
mGenericBdsLibGlobalData = NULL;
|
|
return Status;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Notification function for gH2OBdsServicesProtocolGuid handler Protocol
|
|
|
|
@param[in] EFI_EVENT Event of the notification
|
|
@param[in] Context not used in this function
|
|
**/
|
|
STATIC
|
|
VOID
|
|
EFIAPI
|
|
BdsServicesCallBack (
|
|
IN EFI_EVENT Event,
|
|
IN VOID *Context
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
Status = gBS->LocateProtocol (
|
|
&gH2OBdsServicesProtocolGuid,
|
|
NULL,
|
|
(VOID **) &gBdsServices
|
|
);
|
|
if (Status != EFI_SUCCESS) {
|
|
return;
|
|
}
|
|
gBS->CloseEvent (Event);
|
|
}
|
|
|
|
/**
|
|
The constructor function register UNI strings into imageHandle and register event to
|
|
locate gH2OBdsServicesProtocolGuid protocol.
|
|
|
|
It will ASSERT() if that operation fails and it will always return EFI_SUCCESS.
|
|
|
|
@param ImageHandle The firmware allocated handle for the EFI image.
|
|
@param SystemTable A pointer to the EFI System Table.
|
|
|
|
@retval EFI_SUCCESS The constructor successfully added string package.
|
|
@retval Other value The constructor can't add string package.
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
GenericBdsLibConstructor (
|
|
IN EFI_HANDLE ImageHandle,
|
|
IN EFI_SYSTEM_TABLE *SystemTable
|
|
)
|
|
{
|
|
VOID *Registration;
|
|
EFI_STATUS Status;
|
|
|
|
Status = InitGenericBdsGlobalData ();
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
gBdsLibStringPackHandle = HiiAddPackages (
|
|
&gBdsLibStringPackageGuid,
|
|
ImageHandle,
|
|
GenericBdsLibStrings,
|
|
NULL
|
|
);
|
|
|
|
ASSERT (gBdsLibStringPackHandle != NULL);
|
|
if (gBdsLibStringPackHandle == NULL) {
|
|
return EFI_ABORTED;
|
|
}
|
|
|
|
UpdateBvdtToHii (gBdsLibStringPackHandle);
|
|
|
|
mBdsServicesEvent = EfiCreateProtocolNotifyEvent (
|
|
&gH2OBdsServicesProtocolGuid,
|
|
TPL_CALLBACK,
|
|
BdsServicesCallBack,
|
|
NULL,
|
|
&Registration
|
|
);
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
The destructor function to close mBdsServicesEvent event.
|
|
|
|
@param ImageHandle The firmware allocated handle for the EFI image.
|
|
@param SystemTable A pointer to the EFI System Table.
|
|
|
|
@retval EFI_SUCCESS
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
GenericBdsLibDestructor (
|
|
IN EFI_HANDLE ImageHandle,
|
|
IN EFI_SYSTEM_TABLE *SystemTable
|
|
)
|
|
{
|
|
if (mBdsServicesEvent != NULL) {
|
|
gBS->CloseEvent (mBdsServicesEvent);
|
|
}
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Un-support the CSM Opt-out when switch disabled.
|
|
|
|
@param Event The triggered event.
|
|
|
|
@retval EFI_UNSUPPORTED
|
|
**/
|
|
EFI_STATUS
|
|
LegacyBiosDependency (
|
|
IN EFI_EVENT Event
|
|
)
|
|
{
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
/**
|
|
Boot the legacy system with the boot option
|
|
|
|
@param Option The legacy boot option which have BBS device path
|
|
|
|
@retval EFI_UNSUPPORTED There is no legacybios protocol, do not support
|
|
legacy boot.
|
|
@retval EFI_STATUS Return the status of LegacyBios->LegacyBoot ().
|
|
**/
|
|
EFI_STATUS
|
|
BdsLibDoLegacyBoot (
|
|
IN BDS_COMMON_OPTION *Option
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_LEGACY_BIOS_PROTOCOL *LegacyBios;
|
|
|
|
Status = gBS->LocateProtocol (&gEfiLegacyBiosProtocolGuid, NULL, (VOID **) &LegacyBios);
|
|
if (EFI_ERROR (Status)) {
|
|
//
|
|
// If no LegacyBios protocol we do not support legacy boot
|
|
//
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
//
|
|
// Notes: if we separate the int 19, then we don't need to refresh BBS
|
|
//
|
|
// to set BBS Table priority
|
|
//
|
|
SetBbsPriority (LegacyBios, Option);
|
|
//
|
|
// Write boot to OS performance data for legacy boot.
|
|
//
|
|
WRITE_BOOT_TO_OS_PERFORMANCE_DATA;
|
|
|
|
DEBUG ((DEBUG_INFO | DEBUG_LOAD, "Legacy Boot: %S\n", Option->Description));
|
|
return LegacyBios->LegacyBoot (
|
|
LegacyBios,
|
|
(BBS_BBS_DEVICE_PATH *) Option->DevicePath,
|
|
Option->LoadOptionsSize,
|
|
Option->LoadOptions
|
|
);
|
|
}
|
|
|
|
/**
|
|
Create USB WWID device path node.
|
|
|
|
@param[in] UsbIoHandle USB Io protocol handle
|
|
|
|
@return A Pointer to the USB WWID device path node or NULL if not found.
|
|
**/
|
|
STATIC
|
|
USB_WWID_DEVICE_PATH *
|
|
CreateUsbWwidDevPathNode (
|
|
IN EFI_HANDLE UsbIoHandle
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_USB_IO_PROTOCOL *UsbIo;
|
|
EFI_USB_INTERFACE_DESCRIPTOR IfDesc;
|
|
EFI_USB_DEVICE_DESCRIPTOR DevDesc;
|
|
UINT16 *LangIdTable;
|
|
UINT16 TableSize;
|
|
UINT16 Index;
|
|
CHAR16 *SerialNumberStr;
|
|
UINTN SerialNumberStrLen;
|
|
USB_WWID_DEVICE_PATH *UsbWwid;
|
|
|
|
Status = gBS->HandleProtocol (UsbIoHandle, &gEfiUsbIoProtocolGuid, (VOID **) &UsbIo);
|
|
if (EFI_ERROR (Status)) {
|
|
return NULL;
|
|
}
|
|
|
|
Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &IfDesc);
|
|
if (EFI_ERROR (Status)) {
|
|
return NULL;
|
|
}
|
|
|
|
Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DevDesc);
|
|
if (EFI_ERROR (Status) || DevDesc.StrSerialNumber == 0) {
|
|
return NULL;
|
|
}
|
|
|
|
TableSize = 0;
|
|
LangIdTable = NULL;
|
|
Status = UsbIo->UsbGetSupportedLanguages (UsbIo, &LangIdTable, &TableSize);
|
|
if (EFI_ERROR (Status)) {
|
|
return NULL;
|
|
}
|
|
|
|
SerialNumberStr = NULL;
|
|
for (Index = 0; Index < TableSize / sizeof (UINT16); Index++) {
|
|
Status = UsbIo->UsbGetStringDescriptor (
|
|
UsbIo,
|
|
LangIdTable[Index],
|
|
DevDesc.StrSerialNumber,
|
|
&SerialNumberStr
|
|
);
|
|
if (!EFI_ERROR (Status)) {
|
|
break;
|
|
}
|
|
}
|
|
if (SerialNumberStr == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
SerialNumberStrLen = StrLen (SerialNumberStr);
|
|
UsbWwid = (USB_WWID_DEVICE_PATH *) CreateDeviceNode (
|
|
MESSAGING_DEVICE_PATH,
|
|
MSG_USB_WWID_DP,
|
|
(UINT16) (sizeof (USB_WWID_DEVICE_PATH) + SerialNumberStrLen * sizeof (CHAR16))
|
|
);
|
|
if (UsbWwid == NULL) {
|
|
FreePool (SerialNumberStr);
|
|
return NULL;
|
|
}
|
|
|
|
UsbWwid->InterfaceNumber = IfDesc.InterfaceNumber;
|
|
UsbWwid->VendorId = DevDesc.IdVendor;
|
|
UsbWwid->ProductId = DevDesc.IdProduct;
|
|
CopyMem (UsbWwid + 1, SerialNumberStr, SerialNumberStrLen * sizeof (CHAR16));
|
|
|
|
FreePool (SerialNumberStr);
|
|
return UsbWwid;
|
|
}
|
|
|
|
/**
|
|
Refresh USB short form device path info list.
|
|
|
|
@retval EFI_SUCCESS Refresh USB short form device path info list successfully.
|
|
@retval EFI_OUT_OF_RESOURCES Lack of memory resource.
|
|
@retval Others There is no simple file system protocol instance on system.
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
RefreshUsbShortFormDevPathList (
|
|
VOID
|
|
)
|
|
{
|
|
LIST_ENTRY *Link;
|
|
GENERIC_BDS_LIB_USB_SHORT_FORM_INFO *UsbShortFormInfo;
|
|
EFI_STATUS Status;
|
|
EFI_HANDLE *HandleList;
|
|
UINTN HandleCount;
|
|
UINTN Index;
|
|
UINTN FileSysDevPathSize;
|
|
EFI_DEVICE_PATH_PROTOCOL *FileSysDevPath;
|
|
EFI_DEVICE_PATH_PROTOCOL *RemaindingDevPath;
|
|
EFI_HANDLE UsbIoHandle;
|
|
BOOLEAN Found;
|
|
USB_WWID_DEVICE_PATH *UsbWwid;
|
|
|
|
//
|
|
// Remove nonexisting USB device from info list.
|
|
//
|
|
Link = GetFirstNode (&mGenericBdsLibGlobalData->UsbShortFormInfoList);
|
|
while (!IsNull (&mGenericBdsLibGlobalData->UsbShortFormInfoList, Link)) {
|
|
UsbShortFormInfo = (GENERIC_BDS_LIB_USB_SHORT_FORM_INFO *) Link;
|
|
Link = GetNextNode (&mGenericBdsLibGlobalData->UsbShortFormInfoList, Link);
|
|
|
|
RemaindingDevPath = UsbShortFormInfo->FileSysDevPath;
|
|
Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &RemaindingDevPath, &UsbIoHandle);
|
|
if (EFI_ERROR (Status) || !IsDevicePathEnd (RemaindingDevPath)) {
|
|
RemoveEntryList (&UsbShortFormInfo->Link);
|
|
FreePool (UsbShortFormInfo->FileSysDevPath);
|
|
FreePool (UsbShortFormInfo->UsbWwid);
|
|
FreePool (UsbShortFormInfo);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Add new USB device in info list.
|
|
//
|
|
Status = gBS->LocateHandleBuffer (
|
|
ByProtocol,
|
|
&gEfiSimpleFileSystemProtocolGuid,
|
|
NULL,
|
|
&HandleCount,
|
|
&HandleList
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
for (Index = 0; Index < HandleCount; Index++) {
|
|
Status = gBS->HandleProtocol (HandleList[Index], &gEfiDevicePathProtocolGuid, (VOID **) &FileSysDevPath);
|
|
if (EFI_ERROR (Status)) {
|
|
continue;
|
|
}
|
|
|
|
RemaindingDevPath = FileSysDevPath;
|
|
Status = gBS->LocateDevicePath (&gEfiUsbIoProtocolGuid, &RemaindingDevPath, &UsbIoHandle);
|
|
if (EFI_ERROR (Status)) {
|
|
continue;
|
|
}
|
|
|
|
Found = FALSE;
|
|
UsbShortFormInfo = NULL;
|
|
FileSysDevPathSize = GetDevicePathSize (FileSysDevPath);
|
|
Link = GetFirstNode (&mGenericBdsLibGlobalData->UsbShortFormInfoList);
|
|
while (!IsNull (&mGenericBdsLibGlobalData->UsbShortFormInfoList, Link)) {
|
|
UsbShortFormInfo = (GENERIC_BDS_LIB_USB_SHORT_FORM_INFO *) Link;
|
|
if (CompareMem (UsbShortFormInfo->FileSysDevPath, FileSysDevPath, FileSysDevPathSize) == 0) {
|
|
Found = TRUE;
|
|
break;
|
|
}
|
|
|
|
Link = GetNextNode (&mGenericBdsLibGlobalData->UsbShortFormInfoList, Link);
|
|
}
|
|
if (Found) {
|
|
continue;
|
|
}
|
|
|
|
UsbWwid = CreateUsbWwidDevPathNode (UsbIoHandle);
|
|
if (UsbWwid == NULL) {
|
|
continue;
|
|
}
|
|
|
|
UsbShortFormInfo = AllocateZeroPool (sizeof (GENERIC_BDS_LIB_USB_SHORT_FORM_INFO));
|
|
if (UsbShortFormInfo == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
UsbShortFormInfo->FileSysDevPath = AllocateCopyPool (FileSysDevPathSize, FileSysDevPath);
|
|
if (UsbShortFormInfo->FileSysDevPath == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
UsbShortFormInfo->UsbIoRemaindingDevPathOffset = (UINTN) RemaindingDevPath - (UINTN) FileSysDevPath;
|
|
UsbShortFormInfo->UsbWwid = UsbWwid;
|
|
InsertTailList (&mGenericBdsLibGlobalData->UsbShortFormInfoList, &UsbShortFormInfo->Link);
|
|
}
|
|
|
|
FreePool (HandleList);
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Transfer input device path to USB short form device path.
|
|
|
|
@param[in] DevPath Pointer to device path
|
|
|
|
@return A Pointer to the USB short form device path or NULL if failed to transfer.
|
|
**/
|
|
STATIC
|
|
EFI_DEVICE_PATH_PROTOCOL *
|
|
GetUsbShortFormDevPath (
|
|
IN EFI_DEVICE_PATH_PROTOCOL *DevPath
|
|
)
|
|
{
|
|
LIST_ENTRY *Link;
|
|
GENERIC_BDS_LIB_USB_SHORT_FORM_INFO *UsbShortFormInfo;
|
|
BOOLEAN Found;
|
|
UINT8 *RemaindingDevPathPtr;
|
|
UINTN RemaindingDevPathSize;
|
|
UINTN UsbWwidSize;
|
|
UINT8 *ShortformDevPathPtr;
|
|
|
|
RefreshUsbShortFormDevPathList ();
|
|
|
|
Found = FALSE;
|
|
UsbShortFormInfo = NULL;
|
|
Link = GetFirstNode (&mGenericBdsLibGlobalData->UsbShortFormInfoList);
|
|
while (!IsNull (&mGenericBdsLibGlobalData->UsbShortFormInfoList, Link)) {
|
|
UsbShortFormInfo = (GENERIC_BDS_LIB_USB_SHORT_FORM_INFO *) Link;
|
|
if (CompareMem (
|
|
DevPath,
|
|
UsbShortFormInfo->FileSysDevPath,
|
|
GetDevicePathSize (UsbShortFormInfo->FileSysDevPath) - sizeof(EFI_DEVICE_PATH_PROTOCOL)
|
|
) == 0) {
|
|
Found = TRUE;
|
|
break;
|
|
}
|
|
|
|
Link = GetNextNode (&mGenericBdsLibGlobalData->UsbShortFormInfoList, Link);
|
|
}
|
|
if (!Found) {
|
|
return NULL;
|
|
}
|
|
|
|
RemaindingDevPathPtr = (UINT8 *) DevPath + UsbShortFormInfo->UsbIoRemaindingDevPathOffset;
|
|
RemaindingDevPathSize = GetDevicePathSize (DevPath) - UsbShortFormInfo->UsbIoRemaindingDevPathOffset;
|
|
UsbWwidSize = DevicePathNodeLength (UsbShortFormInfo->UsbWwid);
|
|
ShortformDevPathPtr = AllocatePool (UsbWwidSize + RemaindingDevPathSize);
|
|
if (ShortformDevPathPtr == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
CopyMem (ShortformDevPathPtr , UsbShortFormInfo->UsbWwid, UsbWwidSize);
|
|
CopyMem (ShortformDevPathPtr + UsbWwidSize, RemaindingDevPathPtr , RemaindingDevPathSize);
|
|
return (EFI_DEVICE_PATH_PROTOCOL *) ShortformDevPathPtr;
|
|
}
|
|
|
|
/**
|
|
Get the device path that matches the specified short-form device path that starts with the USB WWID device path.
|
|
|
|
@param[in] UsbShortFormDevPath Pointer to the USB short-form device path instance
|
|
|
|
@return A pointer to device path which matches the specified USB short-form device path or NULL if failed to transfer.
|
|
**/
|
|
EFI_DEVICE_PATH_PROTOCOL *
|
|
EFIAPI
|
|
BdsLibExpandUsbShortFormDevPath (
|
|
IN EFI_DEVICE_PATH_PROTOCOL *UsbShortFormDevPath
|
|
)
|
|
{
|
|
LIST_ENTRY *Link;
|
|
GENERIC_BDS_LIB_USB_SHORT_FORM_INFO *UsbShortFormInfo;
|
|
BOOLEAN Found;
|
|
EFI_DEVICE_PATH_PROTOCOL *RemaindingDevPath;
|
|
UINTN RemaindingDevPathSize;
|
|
UINTN UsbWwidSize;
|
|
UINT8 *DevPathPtr;
|
|
|
|
if (!IS_USB_SHORT_FORM_DEVICE_PATH (UsbShortFormDevPath)) {
|
|
return NULL;
|
|
}
|
|
|
|
RefreshUsbShortFormDevPathList ();
|
|
|
|
Found = FALSE;
|
|
UsbShortFormInfo = NULL;
|
|
UsbWwidSize = DevicePathNodeLength (UsbShortFormDevPath);
|
|
Link = GetFirstNode (&mGenericBdsLibGlobalData->UsbShortFormInfoList);
|
|
while (!IsNull (&mGenericBdsLibGlobalData->UsbShortFormInfoList, Link)) {
|
|
UsbShortFormInfo = (GENERIC_BDS_LIB_USB_SHORT_FORM_INFO *) Link;
|
|
if (DevicePathNodeLength (UsbShortFormInfo->UsbWwid) == UsbWwidSize &&
|
|
CompareMem (UsbShortFormInfo->UsbWwid, UsbShortFormDevPath, UsbWwidSize) == 0) {
|
|
Found = TRUE;
|
|
break;
|
|
}
|
|
|
|
Link = GetNextNode (&mGenericBdsLibGlobalData->UsbShortFormInfoList, Link);
|
|
}
|
|
if (!Found) {
|
|
return NULL;
|
|
}
|
|
|
|
RemaindingDevPath = NextDevicePathNode (UsbShortFormDevPath);
|
|
RemaindingDevPathSize = GetDevicePathSize (RemaindingDevPath);
|
|
DevPathPtr = AllocatePool (UsbShortFormInfo->UsbIoRemaindingDevPathOffset + RemaindingDevPathSize);
|
|
if (DevPathPtr == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
CopyMem (
|
|
DevPathPtr,
|
|
UsbShortFormInfo->FileSysDevPath,
|
|
UsbShortFormInfo->UsbIoRemaindingDevPathOffset
|
|
);
|
|
CopyMem (
|
|
DevPathPtr + UsbShortFormInfo->UsbIoRemaindingDevPathOffset,
|
|
RemaindingDevPath,
|
|
RemaindingDevPathSize
|
|
);
|
|
|
|
return (EFI_DEVICE_PATH_PROTOCOL *) DevPathPtr;
|
|
}
|
|
|
|
/**
|
|
Expand a device path that starts with a hard drive media device path node to be a
|
|
full device path that includes the full hardware path to the device. We need
|
|
to do this so it can be booted. As an optimization the front match (the part point
|
|
to the partition node. E.g. ACPI() /PCI()/ATA()/Partition() ) is saved in a variable
|
|
so a connect all is not required on every boot. All successful history device path
|
|
which point to partition node (the front part) will be saved.
|
|
|
|
@param HardDriveDevicePath EFI Device Path to boot, if it starts with a hard
|
|
drive media device path.
|
|
@return A Pointer to the full device path or NULL if a valid Hard Drive device path
|
|
cannot be found.
|
|
**/
|
|
EFI_DEVICE_PATH_PROTOCOL *
|
|
EFIAPI
|
|
BdsExpandPartitionPartialDevicePathToFull (
|
|
IN HARDDRIVE_DEVICE_PATH *HardDriveDevicePath
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN BlockIoHandleCount;
|
|
EFI_HANDLE *BlockIoBuffer;
|
|
EFI_DEVICE_PATH_PROTOCOL *FullDevicePath;
|
|
EFI_DEVICE_PATH_PROTOCOL *BlockIoDevicePath;
|
|
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
|
|
UINTN Index;
|
|
UINTN InstanceNum;
|
|
EFI_DEVICE_PATH_PROTOCOL *CachedDevicePath;
|
|
EFI_DEVICE_PATH_PROTOCOL *TempNewDevicePath;
|
|
UINTN CachedDevicePathSize;
|
|
BOOLEAN DeviceExist;
|
|
BOOLEAN NeedAdjust;
|
|
EFI_DEVICE_PATH_PROTOCOL *Instance;
|
|
UINTN Size;
|
|
|
|
if (!((DevicePathType (&HardDriveDevicePath->Header) == MEDIA_DEVICE_PATH) &&
|
|
(DevicePathSubType (&HardDriveDevicePath->Header) == MEDIA_HARDDRIVE_DP))) {
|
|
return NULL;
|
|
}
|
|
|
|
FullDevicePath = NULL;
|
|
CachedDevicePath = NULL;
|
|
//
|
|
// Check if there is prestore HD_BOOT_DEVICE_PATH_VARIABLE_NAME variable.
|
|
// If exist, search the front path which point to partition node in the variable instants.
|
|
// If fail to find or HD_BOOT_DEVICE_PATH_VARIABLE_NAME not exist, reconnect all and search in all system
|
|
//
|
|
if (FeaturePcdGet (PcdHdBootDevPathVarSupported)) {
|
|
CachedDevicePath = BdsLibGetVariableAndSize (
|
|
HD_BOOT_DEVICE_PATH_VARIABLE_NAME,
|
|
&gHdBootDevicePathVariablGuid,
|
|
&CachedDevicePathSize
|
|
);
|
|
|
|
if (CachedDevicePath != NULL) {
|
|
TempNewDevicePath = CachedDevicePath;
|
|
DeviceExist = FALSE;
|
|
NeedAdjust = FALSE;
|
|
do {
|
|
//
|
|
// Check every instance of the variable
|
|
// First, check whether the instance contain the partition node, which is needed for distinguishing multi
|
|
// partial partition boot option. Second, check whether the instance could be connected.
|
|
//
|
|
Instance = GetNextDevicePathInstance (&TempNewDevicePath, &Size);
|
|
if (MatchPartitionDevicePathNode (Instance, HardDriveDevicePath)) {
|
|
//
|
|
// Connect the device path instance, the device path point to hard drive media device path node
|
|
// e.g. ACPI() /PCI()/ATA()/Partition()
|
|
//
|
|
Status = BdsLibConnectDevicePath (Instance);
|
|
if (!EFI_ERROR (Status)) {
|
|
DeviceExist = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
//
|
|
// Come here means the first instance is not matched
|
|
//
|
|
NeedAdjust = TRUE;
|
|
FreePool(Instance);
|
|
} while (TempNewDevicePath != NULL);
|
|
|
|
if (DeviceExist) {
|
|
//
|
|
// Find the matched device path.
|
|
// Append the file path information from the boot option and return the fully expanded device path.
|
|
//
|
|
DevicePath = NextDevicePathNode ((EFI_DEVICE_PATH_PROTOCOL *) HardDriveDevicePath);
|
|
FullDevicePath = AppendDevicePath (Instance, DevicePath);
|
|
|
|
//
|
|
// Adjust the HD_BOOT_DEVICE_PATH_VARIABLE_NAME instances sequence if the matched one is not first one.
|
|
//
|
|
if (NeedAdjust) {
|
|
//
|
|
// First delete the matched instance.
|
|
//
|
|
TempNewDevicePath = CachedDevicePath;
|
|
CachedDevicePath = BdsLibDelPartMatchInstance (CachedDevicePath, Instance );
|
|
FreePool (TempNewDevicePath);
|
|
|
|
//
|
|
// Second, append the remaining path after the matched instance
|
|
//
|
|
TempNewDevicePath = CachedDevicePath;
|
|
CachedDevicePath = AppendDevicePathInstance (Instance, CachedDevicePath );
|
|
FreePool (TempNewDevicePath);
|
|
//
|
|
// Save the matching Device Path so we don't need to do a connect all next time
|
|
//
|
|
Status = gRT->SetVariable (
|
|
HD_BOOT_DEVICE_PATH_VARIABLE_NAME,
|
|
&gHdBootDevicePathVariablGuid,
|
|
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
|
|
GetDevicePathSize (CachedDevicePath),
|
|
CachedDevicePath
|
|
);
|
|
}
|
|
|
|
FreePool (Instance);
|
|
FreePool (CachedDevicePath);
|
|
return FullDevicePath;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we get here we fail to find or HD_BOOT_DEVICE_PATH_VARIABLE_NAME not exist, and now we need
|
|
// to search all devices in the system for a matched partition
|
|
//
|
|
BdsLibConnectAllDriversToAllControllers ();
|
|
}
|
|
|
|
Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiBlockIoProtocolGuid, NULL, &BlockIoHandleCount, &BlockIoBuffer);
|
|
if (EFI_ERROR (Status) || BlockIoHandleCount == 0 || BlockIoBuffer == NULL) {
|
|
//
|
|
// If there was an error or there are no device handles that support
|
|
// the BLOCK_IO Protocol, then return.
|
|
//
|
|
return NULL;
|
|
}
|
|
//
|
|
// Loop through all the device handles that support the BLOCK_IO Protocol
|
|
//
|
|
for (Index = 0; Index < BlockIoHandleCount; Index++) {
|
|
|
|
Status = gBS->HandleProtocol (BlockIoBuffer[Index], &gEfiDevicePathProtocolGuid, (VOID *) &BlockIoDevicePath);
|
|
if (EFI_ERROR (Status) || BlockIoDevicePath == NULL) {
|
|
continue;
|
|
}
|
|
|
|
if (MatchPartitionDevicePathNode (BlockIoDevicePath, HardDriveDevicePath)) {
|
|
//
|
|
// Find the matched partition device path
|
|
//
|
|
DevicePath = NextDevicePathNode ((EFI_DEVICE_PATH_PROTOCOL *) HardDriveDevicePath);
|
|
FullDevicePath = AppendDevicePath (BlockIoDevicePath, DevicePath);
|
|
|
|
if (FeaturePcdGet (PcdHdBootDevPathVarSupported)) {
|
|
//
|
|
// Save the matched partition device path in HD_BOOT_DEVICE_PATH_VARIABLE_NAME variable
|
|
//
|
|
if (CachedDevicePath != NULL) {
|
|
//
|
|
// Save the matched partition device path as first instance of HD_BOOT_DEVICE_PATH_VARIABLE_NAME variable
|
|
//
|
|
if (BdsLibMatchDevicePaths (CachedDevicePath, BlockIoDevicePath)) {
|
|
TempNewDevicePath = CachedDevicePath;
|
|
CachedDevicePath = BdsLibDelPartMatchInstance (CachedDevicePath, BlockIoDevicePath);
|
|
FreePool(TempNewDevicePath);
|
|
|
|
TempNewDevicePath = CachedDevicePath;
|
|
CachedDevicePath = AppendDevicePathInstance (BlockIoDevicePath, CachedDevicePath);
|
|
if (TempNewDevicePath != NULL) {
|
|
FreePool(TempNewDevicePath);
|
|
}
|
|
} else {
|
|
TempNewDevicePath = CachedDevicePath;
|
|
CachedDevicePath = AppendDevicePathInstance (BlockIoDevicePath, CachedDevicePath);
|
|
FreePool(TempNewDevicePath);
|
|
}
|
|
//
|
|
// Here limit the device path instance number to 12, which is max number for a system support 3 IDE controller
|
|
// If the user try to boot many OS in different HDs or partitions, in theory,
|
|
// the HD_BOOT_DEVICE_PATH_VARIABLE_NAME variable maybe become larger and larger.
|
|
//
|
|
InstanceNum = 0;
|
|
ASSERT (CachedDevicePath != NULL);
|
|
if (CachedDevicePath == NULL) {
|
|
break;
|
|
}
|
|
TempNewDevicePath = CachedDevicePath;
|
|
while (!IsDevicePathEnd (TempNewDevicePath)) {
|
|
TempNewDevicePath = NextDevicePathNode (TempNewDevicePath);
|
|
//
|
|
// Parse one instance
|
|
//
|
|
while (!IsDevicePathEndType (TempNewDevicePath)) {
|
|
TempNewDevicePath = NextDevicePathNode (TempNewDevicePath);
|
|
}
|
|
InstanceNum++;
|
|
//
|
|
// If the CachedDevicePath variable contain too much instance, only remain 12 instances.
|
|
//
|
|
if (InstanceNum >= 12) {
|
|
SetDevicePathEndNode (TempNewDevicePath);
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
CachedDevicePath = DuplicateDevicePath (BlockIoDevicePath);
|
|
}
|
|
|
|
//
|
|
// Save the matching Device Path so we don't need to do a connect all next time
|
|
//
|
|
Status = gRT->SetVariable (
|
|
HD_BOOT_DEVICE_PATH_VARIABLE_NAME,
|
|
&gHdBootDevicePathVariablGuid,
|
|
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
|
|
GetDevicePathSize (CachedDevicePath),
|
|
CachedDevicePath
|
|
);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (CachedDevicePath != NULL) {
|
|
FreePool (CachedDevicePath);
|
|
}
|
|
if (BlockIoBuffer != NULL) {
|
|
FreePool (BlockIoBuffer);
|
|
}
|
|
return FullDevicePath;
|
|
}
|
|
|
|
/**
|
|
Check whether there is a instance in BlockIoDevicePath, which contain multi device path
|
|
instances, has the same partition node with HardDriveDevicePath device path
|
|
|
|
@param BlockIoDevicePath Multi device path instances which need to check
|
|
@param HardDriveDevicePath A device path which starts with a hard drive media
|
|
device path.
|
|
|
|
@retval TRUE There is a matched device path instance.
|
|
@retval FALSE There is no matched device path instance.
|
|
**/
|
|
BOOLEAN
|
|
EFIAPI
|
|
MatchPartitionDevicePathNode (
|
|
IN EFI_DEVICE_PATH_PROTOCOL *BlockIoDevicePath,
|
|
IN HARDDRIVE_DEVICE_PATH *HardDriveDevicePath
|
|
)
|
|
{
|
|
HARDDRIVE_DEVICE_PATH *Node;
|
|
|
|
if ((BlockIoDevicePath == NULL) || (HardDriveDevicePath == NULL)) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Match all the partition device path nodes including the nested partition nodes
|
|
//
|
|
while (!IsDevicePathEnd (BlockIoDevicePath)) {
|
|
if ((DevicePathType (BlockIoDevicePath) == MEDIA_DEVICE_PATH) &&
|
|
(DevicePathSubType (BlockIoDevicePath) == MEDIA_HARDDRIVE_DP)
|
|
) {
|
|
//
|
|
// See if the harddrive device path in blockio matches the orig Hard Drive Node
|
|
//
|
|
Node = (HARDDRIVE_DEVICE_PATH *) BlockIoDevicePath;
|
|
|
|
//
|
|
// Match Signature and PartitionNumber.
|
|
// Unused bytes in Signature are initiaized with zeros.
|
|
//
|
|
if ((Node->PartitionNumber == HardDriveDevicePath->PartitionNumber) &&
|
|
(Node->MBRType == HardDriveDevicePath->MBRType) &&
|
|
(Node->SignatureType == HardDriveDevicePath->SignatureType) &&
|
|
(CompareMem (Node->Signature, HardDriveDevicePath->Signature, sizeof (Node->Signature)) == 0)) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
BlockIoDevicePath = NextDevicePathNode (BlockIoDevicePath);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
Delete the boot option that content was duplicated.
|
|
|
|
**/
|
|
VOID
|
|
BdsLibDeleteRedundantOption (
|
|
)
|
|
{
|
|
UINT16 *BootOrder;
|
|
UINT16 *TmpBootOrder;
|
|
UINTN BootOrderSize;
|
|
UINTN SearchIndex;
|
|
UINTN Index;
|
|
UINTN BootOptionCnt;
|
|
UINT16 BootIndex[BOOT_OPTION_MAX_CHAR];
|
|
UINT8 **BootOptionPool;
|
|
UINTN *BootOptionSizePool;
|
|
BOOLEAN BootOrderChanged;
|
|
BOOLEAN SameBootOption;
|
|
|
|
BootOrder = NULL;
|
|
TmpBootOrder = NULL;
|
|
BootOptionPool = NULL;
|
|
BootOptionSizePool = NULL;
|
|
BootOrderChanged = FALSE;
|
|
BootOptionCnt = 0;
|
|
BootOrderSize = 0;
|
|
//
|
|
// Get all boot option
|
|
//
|
|
BootOrder = BdsLibGetVariableAndSize (
|
|
L"BootOrder",
|
|
&gEfiGlobalVariableGuid,
|
|
&BootOrderSize
|
|
);
|
|
TmpBootOrder = AllocateZeroPool (BootOrderSize);
|
|
if (NULL == BootOrder || TmpBootOrder == NULL) {
|
|
goto EXIT;
|
|
}
|
|
CopyMem (TmpBootOrder, BootOrder, BootOrderSize);
|
|
BootOptionCnt = BootOrderSize / sizeof (UINT16);
|
|
if (BootOptionCnt == 1) {
|
|
goto EXIT;
|
|
}
|
|
|
|
BootOptionSizePool = AllocateZeroPool (BootOptionCnt * sizeof (UINTN));
|
|
BootOptionPool = AllocateZeroPool (BootOptionCnt * sizeof (UINT8 *));
|
|
if (BootOptionSizePool == NULL || BootOptionPool == NULL) {
|
|
goto EXIT;
|
|
}
|
|
Index = 0;
|
|
while (Index < BootOptionCnt) {
|
|
UnicodeSPrint (BootIndex, sizeof (BootIndex), L"Boot%04x", BootOrder[Index]);
|
|
BootOptionPool[Index] = BdsLibGetVariableAndSize (
|
|
BootIndex,
|
|
&gEfiGlobalVariableGuid,
|
|
(BootOptionSizePool + Index)
|
|
);
|
|
if (NULL == BootOptionPool[Index]) {
|
|
goto EXIT;
|
|
}
|
|
Index++;
|
|
}
|
|
|
|
//
|
|
// Scan and delete redundant option
|
|
//
|
|
for (Index = 0; Index < BootOptionCnt; Index++) {
|
|
if (BootOptionSizePool[Index] == 0) {
|
|
continue;
|
|
}
|
|
for (SearchIndex = Index + 1; SearchIndex < BootOptionCnt; SearchIndex++) {
|
|
SameBootOption = IsSameBootOption (
|
|
BootOptionPool[Index],
|
|
BootOptionSizePool[Index],
|
|
BootOptionPool[SearchIndex],
|
|
BootOptionSizePool[SearchIndex]
|
|
);
|
|
if (SameBootOption) {
|
|
BootOrderChanged = TRUE;
|
|
BdsLibDeleteBootOption (TmpBootOrder[SearchIndex], BootOrder, &BootOrderSize);
|
|
BootOptionSizePool[SearchIndex] = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Restore BootOrder if need.
|
|
//
|
|
if (BootOrderChanged) {
|
|
gRT->SetVariable (
|
|
L"BootOrder",
|
|
&gEfiGlobalVariableGuid,
|
|
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
|
|
BootOrderSize,
|
|
BootOrder
|
|
);
|
|
}
|
|
|
|
EXIT:
|
|
if (TmpBootOrder != NULL) {
|
|
FreePool (TmpBootOrder);
|
|
}
|
|
if (BootOptionSizePool != NULL) {
|
|
FreePool (BootOptionSizePool);
|
|
}
|
|
if (BootOptionPool != NULL) {
|
|
for (Index = 0; Index < BootOptionCnt; Index++) {
|
|
if (BootOptionPool[Index] != NULL) {
|
|
FreePool (BootOptionPool[Index]);
|
|
}
|
|
}
|
|
FreePool (BootOptionPool);
|
|
}
|
|
if (BootOrder != NULL) {
|
|
FreePool (BootOrder);
|
|
}
|
|
}
|
|
|
|
/**
|
|
Delete the boot option associated with the handle passed in.
|
|
|
|
@param Handle The handle which present the device path to create
|
|
boot option
|
|
|
|
@retval EFI_SUCCESS Delete the boot option success
|
|
@retval EFI_NOT_FOUND If the Device Path is not found in the system
|
|
@retval EFI_OUT_OF_RESOURCES Lack of memory resource
|
|
@retval Other Error return value from SetVariable()
|
|
**/
|
|
EFI_STATUS
|
|
BdsLibDeleteOptionFromHandle (
|
|
IN EFI_HANDLE Handle
|
|
)
|
|
{
|
|
UINT16 *BootOrder;
|
|
UINT8 *BootOptionVar;
|
|
UINTN BootOrderSize;
|
|
UINTN BootOptionSize;
|
|
EFI_STATUS Status;
|
|
UINTN Index;
|
|
UINT16 BootOption[BOOT_OPTION_MAX_CHAR];
|
|
UINTN DevicePathSize;
|
|
UINTN OptionDevicePathSize;
|
|
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
|
|
EFI_DEVICE_PATH_PROTOCOL *OptionDevicePath;
|
|
UINT8 *TempPtr;
|
|
EFI_DEVICE_PATH_PROTOCOL *ExpandedDevPath;
|
|
BOOLEAN Found;
|
|
|
|
Status = EFI_SUCCESS;
|
|
BootOrder = NULL;
|
|
BootOrderSize = 0;
|
|
|
|
//
|
|
// Check "BootOrder" variable, if no, means there is no any boot order.
|
|
//
|
|
BootOrder = BdsLibGetVariableAndSize (
|
|
L"BootOrder",
|
|
&gEfiGlobalVariableGuid,
|
|
&BootOrderSize
|
|
);
|
|
if (BootOrder == NULL) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
//
|
|
// Convert device handle to device path protocol instance
|
|
//
|
|
DevicePath = DevicePathFromHandle (Handle);
|
|
if (DevicePath == NULL) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
DevicePathSize = GetDevicePathSize (DevicePath);
|
|
|
|
//
|
|
// Loop all boot order variable and find the matching device path
|
|
//
|
|
Found = FALSE;
|
|
Index = 0;
|
|
while (Index < BootOrderSize / sizeof (UINT16)) {
|
|
UnicodeSPrint (BootOption, sizeof (BootOption), L"Boot%04x", BootOrder[Index]);
|
|
BootOptionVar = BdsLibGetVariableAndSize (
|
|
BootOption,
|
|
&gEfiGlobalVariableGuid,
|
|
&BootOptionSize
|
|
);
|
|
|
|
if (BootOptionVar == NULL) {
|
|
FreePool (BootOrder);
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
TempPtr = BootOptionVar;
|
|
TempPtr += sizeof (UINT32) + sizeof (UINT16);
|
|
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 (OptionDevicePath);
|
|
|
|
//
|
|
// Check whether the device path match
|
|
//
|
|
if ((OptionDevicePathSize == DevicePathSize) &&
|
|
(CompareMem (DevicePath, OptionDevicePath, DevicePathSize) == 0)) {
|
|
Found = TRUE;
|
|
}
|
|
|
|
FreePool (BootOptionVar);
|
|
if (ExpandedDevPath != NULL) {
|
|
FreePool (ExpandedDevPath);
|
|
}
|
|
|
|
if (Found) {
|
|
BdsLibDeleteBootOption (BootOrder[Index], BootOrder, &BootOrderSize);
|
|
break;
|
|
}
|
|
|
|
Index++;
|
|
}
|
|
|
|
//
|
|
// Adjust number of boot option for "BootOrder" variable.
|
|
//
|
|
Status = gRT->SetVariable (
|
|
L"BootOrder",
|
|
&gEfiGlobalVariableGuid,
|
|
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
|
|
BootOrderSize,
|
|
BootOrder
|
|
);
|
|
|
|
FreePool (BootOrder);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
According to input parameter to adjust boot order to EFI device first or
|
|
legacy device first.
|
|
|
|
@param EfiDeviceFirst TRUE : Indicate EFI device first.
|
|
FALSE: Indicate legacy device first.
|
|
@param DeviceCount Total device count
|
|
@param BootOrder Pointer to BootOrder.
|
|
|
|
@retval EFI_SUCCESS Adjust boot order successful.
|
|
@retval EFI_INVALID_PARAMETER Input parameter is invalid.
|
|
**/
|
|
EFI_STATUS
|
|
AdjustBootOrder (
|
|
IN BOOLEAN EfiDeviceFirst,
|
|
IN UINTN DeviceCount,
|
|
IN OUT UINT16 *BootOrder
|
|
)
|
|
{
|
|
UINTN BootOrderIndex;
|
|
UINT16 *EfiDeviceOrder;
|
|
UINTN EfiDeviceIndex;
|
|
UINT16 *LegacyDeviceOrder;
|
|
UINTN LegacyDeviceIndex;
|
|
|
|
if (DeviceCount == 0 || BootOrder == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
EfiDeviceOrder = AllocateZeroPool (DeviceCount * sizeof (CHAR16));
|
|
if (EfiDeviceOrder == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
LegacyDeviceOrder = AllocateZeroPool (DeviceCount * sizeof (CHAR16));
|
|
if (LegacyDeviceOrder == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
EfiDeviceIndex = 0;
|
|
LegacyDeviceIndex = 0;
|
|
|
|
//
|
|
// According boot type (EFI or legacy) to put boot order respective buffer
|
|
//
|
|
for (BootOrderIndex = 0; BootOrderIndex < DeviceCount; BootOrderIndex++) {
|
|
if (IsEfiDevice (BootOrder[BootOrderIndex])) {
|
|
EfiDeviceOrder[EfiDeviceIndex++] = BootOrder[BootOrderIndex];
|
|
} else {
|
|
LegacyDeviceOrder[LegacyDeviceIndex++] = BootOrder[BootOrderIndex];
|
|
}
|
|
}
|
|
|
|
//
|
|
// Adjust boot order depend on EFI device first or legacy device first
|
|
//
|
|
if (EfiDeviceFirst) {
|
|
CopyMem (BootOrder, EfiDeviceOrder, EfiDeviceIndex * sizeof (CHAR16));
|
|
CopyMem (&BootOrder[EfiDeviceIndex], LegacyDeviceOrder, LegacyDeviceIndex * sizeof (CHAR16));
|
|
} else {
|
|
CopyMem (BootOrder, LegacyDeviceOrder, LegacyDeviceIndex * sizeof (CHAR16));
|
|
CopyMem (&BootOrder[LegacyDeviceIndex], EfiDeviceOrder, EfiDeviceIndex * sizeof (CHAR16));
|
|
}
|
|
|
|
FreePool (EfiDeviceOrder);
|
|
FreePool (LegacyDeviceOrder);
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Based on the input oprom storage device table, check if the device path belongs to oprom storage device.
|
|
|
|
@param DevicePath The list of device patch
|
|
@param OpromStorageDev Pointer of array which contains oprom storage device information
|
|
@param OpromStorageDevCount Number of oprom storage device
|
|
|
|
@retval TRUE Device path belongs to oprom storage device.
|
|
@retval FALSE otherwise
|
|
**/
|
|
BOOLEAN
|
|
IsOpromStorageDev (
|
|
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
|
|
IN OPROM_STORAGE_DEVICE_INFO *OpromStorageDev,
|
|
IN UINTN OpromStorageDevCount
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_HANDLE PciIoHandle;
|
|
EFI_PCI_IO_PROTOCOL *PciIo;
|
|
UINTN Segment;
|
|
UINTN Bus;
|
|
UINTN Device;
|
|
UINTN Function;
|
|
UINTN Index;
|
|
|
|
if (OpromStorageDev == NULL || OpromStorageDevCount == 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
Status = gBS->LocateDevicePath (
|
|
&gEfiPciIoProtocolGuid,
|
|
&DevicePath,
|
|
&PciIoHandle
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return FALSE;
|
|
}
|
|
Status = gBS->HandleProtocol (
|
|
PciIoHandle,
|
|
&gEfiPciIoProtocolGuid,
|
|
(VOID **) &PciIo
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return FALSE;
|
|
}
|
|
Status = PciIo->GetLocation (PciIo, &Segment, &Bus, &Device, &Function);
|
|
if (EFI_ERROR (Status)) {
|
|
return FALSE;
|
|
}
|
|
|
|
for (Index = 0; Index < OpromStorageDevCount; Index++) {
|
|
if (Segment == OpromStorageDev[Index].Segment &&
|
|
Bus == OpromStorageDev[Index].Bus &&
|
|
Device == OpromStorageDev[Index].Device &&
|
|
Function == OpromStorageDev[Index].Function) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
Collect option ROM storage device information.
|
|
|
|
@param OpromStorageDev Pointer of array which contains oprom storage device information
|
|
@param OpromStorageDevCount Number of oprom storage device
|
|
|
|
@retval EFI_SUCCESS Get Oprom storage device information successfully.
|
|
@retval EFI_INVALID_PARAMETER Input parameter is NULL.
|
|
@retval Other LocateHandleBuffer or AllocatePool fail.
|
|
**/
|
|
EFI_STATUS
|
|
BdsLibGetOpromStorageDevInfo (
|
|
OUT OPROM_STORAGE_DEVICE_INFO **OpromStorageDev,
|
|
OUT UINTN *OpromStorageDevCount
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN NumberPciIoHandles;
|
|
EFI_HANDLE *PciIoHandles;
|
|
EFI_PCI_IO_PROTOCOL *PciIo;
|
|
UINT8 Class;
|
|
UINTN Index;
|
|
EFI_PCI_IO_PROTOCOL *OpromStorageDevTable[MAX_OPTION_ROM_STORAGE_DEVICE];
|
|
|
|
if (OpromStorageDev == NULL || OpromStorageDevCount == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
*OpromStorageDev = NULL;
|
|
*OpromStorageDevCount = 0;
|
|
NumberPciIoHandles = 0;
|
|
PciIoHandles = NULL;
|
|
|
|
//
|
|
// Find all storage devices which are controlled by option ROM.
|
|
//
|
|
Status = gBS->LocateHandleBuffer (
|
|
ByProtocol,
|
|
&gEfiPciIoProtocolGuid,
|
|
NULL,
|
|
&NumberPciIoHandles,
|
|
&PciIoHandles
|
|
);
|
|
if (EFI_ERROR(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
for (Index = 0; Index < NumberPciIoHandles; Index++) {
|
|
Status = gBS->HandleProtocol (PciIoHandles[Index], &gEfiPciIoProtocolGuid, (VOID **) &PciIo);
|
|
if (EFI_ERROR (Status) || PciIo->RomSize == 0) {
|
|
continue;
|
|
}
|
|
|
|
Status = PciIo->Pci.Read (
|
|
PciIo,
|
|
EfiPciWidthUint8,
|
|
PCI_CLASSCODE_OFFSET + 2,
|
|
1,
|
|
&Class
|
|
);
|
|
if (EFI_ERROR (Status) || Class != PCI_CLASS_MASS_STORAGE) {
|
|
continue;
|
|
}
|
|
|
|
if (*OpromStorageDevCount == MAX_OPTION_ROM_STORAGE_DEVICE) {
|
|
DEBUG((EFI_D_ERROR, "WARNING: Unable to get all option ROM storage device information!\n"));
|
|
break;
|
|
}
|
|
|
|
OpromStorageDevTable[*OpromStorageDevCount] = PciIo;
|
|
(*OpromStorageDevCount)++;
|
|
}
|
|
|
|
if (NumberPciIoHandles) {
|
|
FreePool (PciIoHandles);
|
|
}
|
|
|
|
//
|
|
// From the table of oprom storage device, get info one by one.
|
|
//
|
|
if (*OpromStorageDevCount) {
|
|
*OpromStorageDev = AllocatePool (*OpromStorageDevCount * sizeof (OPROM_STORAGE_DEVICE_INFO));
|
|
if (*OpromStorageDev == NULL) {
|
|
*OpromStorageDevCount = 0;
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
for (Index = 0; Index < *OpromStorageDevCount; Index++) {
|
|
PciIo = OpromStorageDevTable[Index];
|
|
PciIo->GetLocation (
|
|
PciIo,
|
|
&((*OpromStorageDev)[Index].Segment),
|
|
&((*OpromStorageDev)[Index].Bus),
|
|
&((*OpromStorageDev)[Index].Device),
|
|
&((*OpromStorageDev)[Index].Function)
|
|
);
|
|
}
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
According input boot order index to delete BIOS created boot option which partition device path is same
|
|
as input boot option. If input boot option is created by BIOS, this function will delete input boot option
|
|
directly.
|
|
|
|
@param[in] BootOrderIndex Input boot order index.
|
|
@param[in] PositionPolicy The new created boot order policy.
|
|
@param[in, out] BootOrder Boot order list.
|
|
@param[in, out] BootOrderSize Boot order size by byte.
|
|
@param[in] BiosCreatedBootNum The boot option number which is created by BIOS.
|
|
|
|
@retval EFI_SUCCESS BIOS created boot option is deleted and the delete boot order index is returned.
|
|
@retval EFI_INVALID_PARAMETER BootOrder is NULL or BiosCreatedIndex is NULL or corresponding Boot#### is incorrect.
|
|
@retval EFI_NOT_FOUND Cannot find BIOS created boot option.
|
|
@retval EFI_OUT_OF_RESOURCES Lack of memory resource.
|
|
**/
|
|
EFI_STATUS
|
|
DeleteBiosCreateOption (
|
|
IN UINTN BootOrderIndex,
|
|
IN UINT16 PositionPolicy,
|
|
IN OUT UINT16 *BootOrder,
|
|
IN OUT UINTN *BootOrderSize,
|
|
OUT UINT16 *BiosCreatedBootNum
|
|
)
|
|
{
|
|
UINT16 BootOption[BOOT_OPTION_MAX_CHAR];
|
|
UINT8 *BootOptionVar;
|
|
UINTN VariableSize;
|
|
UINT16 BootOrderNum;
|
|
UINT16 Index;
|
|
EFI_DEVICE_PATH_PROTOCOL *OptionDevicePath;
|
|
EFI_DEVICE_PATH_PROTOCOL *WorkingDevicePath;
|
|
BOOLEAN BiosCreatedFound;
|
|
UINTN PcdTokenNum;
|
|
|
|
if (BootOrder == NULL || BootOrderSize == NULL || BiosCreatedBootNum == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
UnicodeSPrint (BootOption, sizeof (BootOption), L"Boot%04x", BootOrder[BootOrderIndex]);
|
|
BootOptionVar = BdsLibGetVariableAndSize (
|
|
BootOption,
|
|
&gEfiGlobalVariableGuid,
|
|
&VariableSize
|
|
);
|
|
if (BootOptionVar == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
BiosCreatedFound = FALSE;
|
|
OptionDevicePath = NULL;
|
|
if (BdsLibIsBiosCreatedOption (BootOptionVar, VariableSize)) {
|
|
FreePool (BootOptionVar);
|
|
*BiosCreatedBootNum = BootOrder[BootOrderIndex];
|
|
BdsLibDeleteBootOption (BootOrder[BootOrderIndex], BootOrder, BootOrderSize);
|
|
BiosCreatedFound = TRUE;
|
|
} else {
|
|
FreePool (BootOptionVar);
|
|
OptionDevicePath = BdsLibGetDevicePathFromBootOption (BootOrder[BootOrderIndex]);
|
|
if (OptionDevicePath == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
PcdTokenNum = GetGenericOsPcdByFilePath (OptionDevicePath);
|
|
BootOrderNum = (UINT16) (*BootOrderSize / sizeof (UINT16));
|
|
for (Index = 0; Index < BootOrderNum; Index++) {
|
|
if (Index == BootOrderIndex) {
|
|
continue;
|
|
}
|
|
UnicodeSPrint (BootOption, sizeof (BootOption), L"Boot%04x", BootOrder[Index]);
|
|
BootOptionVar = BdsLibGetVariableAndSize (
|
|
BootOption,
|
|
&gEfiGlobalVariableGuid,
|
|
&VariableSize
|
|
);
|
|
if (BootOptionVar == NULL || !BdsLibIsBiosCreatedOption (BootOptionVar, VariableSize)) {
|
|
if (BootOptionVar != NULL) {
|
|
FreePool (BootOptionVar);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
WorkingDevicePath = BdsLibGetDevicePathFromBootOption (BootOrder[Index]);
|
|
if (WorkingDevicePath == NULL) {
|
|
FreePool (BootOptionVar);
|
|
continue;
|
|
}
|
|
if (MatchPartitionDevicePathNode (OptionDevicePath, (HARDDRIVE_DEVICE_PATH *) WorkingDevicePath) &&
|
|
(BdsLibMatchFilePathDevicePathNode (OptionDevicePath, WorkingDevicePath) ||
|
|
IsFilePathInGenericOsPcd (PcdTokenNum, WorkingDevicePath))) {
|
|
BiosCreatedFound = TRUE;
|
|
*BiosCreatedBootNum = BootOrder[Index];
|
|
}
|
|
|
|
FreePool (BootOptionVar);
|
|
FreePool (WorkingDevicePath);
|
|
if (BiosCreatedFound) {
|
|
UnicodeSPrint (BootOption, sizeof (BootOption), L"Boot%04x", BootOrder[Index]);
|
|
gRT->SetVariable (
|
|
BootOption,
|
|
&gEfiGlobalVariableGuid,
|
|
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
|
|
0,
|
|
NULL
|
|
);
|
|
if (PositionPolicy == IN_AUTO) {
|
|
//
|
|
// Keep OS created EFI Boot option priority, if NewPositionPolicy is auto
|
|
//
|
|
CopyMem (
|
|
&BootOrder[Index],
|
|
BootOrder + Index + 1,
|
|
*BootOrderSize - (Index + 1) * sizeof (UINT16)
|
|
);
|
|
} else {
|
|
//
|
|
// Keep recovery EFI Boot option priority, if NewPositionPolicy isn't auto
|
|
//
|
|
BootOrder[Index] = BootOrder[BootOrderIndex];
|
|
CopyMem (
|
|
&BootOrder[BootOrderIndex],
|
|
BootOrder + BootOrderIndex + 1,
|
|
*BootOrderSize - (BootOrderIndex + 1) * sizeof (UINT16)
|
|
);
|
|
}
|
|
*BootOrderSize -= sizeof (UINT16);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (OptionDevicePath != NULL) {
|
|
FreePool (OptionDevicePath);
|
|
}
|
|
|
|
return BiosCreatedFound ? EFI_SUCCESS : EFI_NOT_FOUND;
|
|
}
|
|
|
|
/**
|
|
Delete all invalid EFI boot options.
|
|
|
|
@retval EFI_SUCCESS Delete all invalid boot option success
|
|
@retval EFI_NOT_FOUND Variable "BootOrder" is not found
|
|
@retval EFI_OUT_OF_RESOURCES Lack of memory resource
|
|
@retval Other Error return value from SetVariable()
|
|
**/
|
|
EFI_STATUS
|
|
BdsDeleteAllInvalidEfiBootOption (
|
|
OUT BOOLEAN *WindowsToGoBootVarExist
|
|
)
|
|
{
|
|
UINT16 *BootOrder;
|
|
UINT8 *BootOptionVar;
|
|
UINTN BootOrderSize;
|
|
UINTN BootOptionSize;
|
|
EFI_STATUS Status;
|
|
UINTN Index;
|
|
UINT16 BootOption[BOOT_OPTION_MAX_CHAR];
|
|
EFI_DEVICE_PATH_PROTOCOL *OptionDevicePath;
|
|
UINT8 *TempPtr;
|
|
CHAR16 *Description;
|
|
BOOLEAN NeedDelete;
|
|
EFI_DEVICE_PATH_PROTOCOL *NewDevicePath;
|
|
UINT16 BiosCreatedBootNum;
|
|
KERNEL_CONFIGURATION SystemConfiguration;
|
|
EFI_STATUS SystemConfigStatus;
|
|
EFI_FILE_HANDLE FileHandle;
|
|
UINTN Size;
|
|
OPROM_STORAGE_DEVICE_INFO *OpromStorageDev;
|
|
UINTN OpromStorageDevCount;
|
|
UINT8 *DisableOpromStorageDevBoot;
|
|
UINTN VariableSize;
|
|
UINTN OptionDevicePathSize;
|
|
UINT8 *OptionalData;
|
|
UINT32 HashValue;
|
|
BOOLEAN IsAppOption;
|
|
UINTN BootType;
|
|
BOOLEAN IsUsbBootSupported;
|
|
BOOLEAN IsNetworkBootSupported;
|
|
BOOLEAN IsWindowsToGoBootOption;
|
|
EFI_DEVICE_PATH_PROTOCOL *ExpandedDevPath;
|
|
|
|
*WindowsToGoBootVarExist = FALSE;
|
|
|
|
BootOrder = BdsLibGetVariableAndSize (
|
|
L"BootOrder",
|
|
&gEfiGlobalVariableGuid,
|
|
&BootOrderSize
|
|
);
|
|
if (NULL == BootOrder) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
OpromStorageDev = NULL;
|
|
OpromStorageDevCount = 0;
|
|
DisableOpromStorageDevBoot = BdsLibGetVariableAndSize (
|
|
L"DisableOpromStorageDevBoot",
|
|
&gEfiGenericVariableGuid,
|
|
&Size
|
|
);
|
|
if (DisableOpromStorageDevBoot != NULL) {
|
|
BdsLibGetOpromStorageDevInfo (&OpromStorageDev, &OpromStorageDevCount);
|
|
}
|
|
|
|
SystemConfigStatus = GetKernelConfiguration (&SystemConfiguration);
|
|
BootType = H2OGetBootType ();
|
|
IsUsbBootSupported = (BOOLEAN) (EFI_ERROR (SystemConfigStatus) || SystemConfiguration.UsbBoot == 0);
|
|
IsNetworkBootSupported = (BOOLEAN) (!EFI_ERROR (SystemConfigStatus) && SystemConfiguration.PxeBootToLan != 0);
|
|
|
|
Index = 0;
|
|
while (Index < BootOrderSize / sizeof (UINT16)) {
|
|
if (FeaturePcdGet (PcdAutoCreateDummyBootOption) && BdsLibIsDummyBootOption (BootOrder[Index])) {
|
|
Index++;
|
|
continue;
|
|
}
|
|
UnicodeSPrint (BootOption, sizeof (BootOption), L"Boot%04x", BootOrder[Index]);
|
|
BootOptionVar = BdsLibGetVariableAndSize (
|
|
BootOption,
|
|
&gEfiGlobalVariableGuid,
|
|
&VariableSize
|
|
);
|
|
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;
|
|
}
|
|
|
|
if (!ValidateOption (BootOptionVar, VariableSize)) {
|
|
BdsLibDeleteBootOption (BootOrder[Index], BootOrder, &BootOrderSize);
|
|
FreePool (BootOptionVar);
|
|
continue;
|
|
}
|
|
|
|
TempPtr = BootOptionVar;
|
|
IsAppOption = (BOOLEAN) ((*(UINT32 *) BootOptionVar & LOAD_OPTION_CATEGORY_APP) == LOAD_OPTION_CATEGORY_APP);
|
|
TempPtr += sizeof (UINT32) + sizeof (UINT16);
|
|
Description = (CHAR16 *) TempPtr;
|
|
TempPtr += StrSize ((CHAR16 *) TempPtr);
|
|
OptionDevicePath = (EFI_DEVICE_PATH_PROTOCOL *) TempPtr;
|
|
OptionDevicePathSize = GetDevicePathSize (OptionDevicePath);
|
|
BootOptionSize = (UINTN) (TempPtr - BootOptionVar) + OptionDevicePathSize;
|
|
OptionalData = BootOptionVar + BootOptionSize;
|
|
|
|
ExpandedDevPath = IS_USB_SHORT_FORM_DEVICE_PATH(OptionDevicePath) ? BdsLibExpandUsbShortFormDevPath (OptionDevicePath) : NULL;
|
|
if (ExpandedDevPath != NULL) {
|
|
OptionDevicePath = ExpandedDevPath;
|
|
}
|
|
IsWindowsToGoBootOption = IsWindowsToGoBootOptionDevPath (OptionDevicePath);
|
|
|
|
if (IsLegacyBootOptionDevPath (OptionDevicePath)) {
|
|
//
|
|
// Skip legacy boot option (BBS boot device)
|
|
//
|
|
NeedDelete = FALSE;
|
|
} else if (BootType == LEGACY_BOOT_TYPE && !IsAppOption) {
|
|
//
|
|
// SCU disable EFI Boot
|
|
//
|
|
NeedDelete = TRUE;
|
|
} else if (IsValidHwVendorBootOptionDevPath (OptionDevicePath)) {
|
|
NeedDelete = FALSE;
|
|
} else if (IsWindowsToGoBootOption) {
|
|
NeedDelete = FALSE;
|
|
} else if (!IsUsbBootSupported && IsUsbDevicePath (OptionDevicePath)) {
|
|
NeedDelete = TRUE;
|
|
} else if (IsFvFileBootOptionDevPath (OptionDevicePath)) {
|
|
//
|
|
// Skip boot option for FV file if read file successfully.
|
|
//
|
|
NeedDelete = TRUE;
|
|
if (IsValidFvFileBootOptionDevPath (OptionDevicePath)) {
|
|
NeedDelete = FALSE;
|
|
} else if (VariableSize - BootOptionSize == SHELL_OPTIONAL_DATA_SIZE) {
|
|
gBS->CopyMem (&HashValue, &OptionalData[2], 4);
|
|
Status = UpdateShellDevicePath ((CHAR16 *) BootOption, *((UINT32 *) BootOptionVar), Description, HashValue);
|
|
if (!EFI_ERROR (Status)) {
|
|
NeedDelete = FALSE;
|
|
}
|
|
}
|
|
} else if (IsGenericUefiBootOsDevPath (OptionDevicePath)) {
|
|
NewDevicePath = BdsExpandPartitionPartialDevicePathToFull ((HARDDRIVE_DEVICE_PATH *) OptionDevicePath);
|
|
if (NewDevicePath == NULL) {
|
|
NewDevicePath = OptionDevicePath;
|
|
}
|
|
|
|
if (IsOpromStorageDev (NewDevicePath, OpromStorageDev, OpromStorageDevCount)) {
|
|
FileHandle = NULL;
|
|
} else {
|
|
Status = BdsLibOpenFileFromDevicePath (
|
|
NewDevicePath,
|
|
EFI_FILE_MODE_READ,
|
|
0,
|
|
&FileHandle
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
FileHandle = NULL;
|
|
}
|
|
}
|
|
if (NewDevicePath != OptionDevicePath) {
|
|
FreePool (NewDevicePath);
|
|
}
|
|
|
|
NeedDelete = TRUE;
|
|
if (FileHandle != NULL) {
|
|
NeedDelete = FALSE;
|
|
FileHandle->Close (FileHandle);
|
|
if (!BdsLibIsBiosCreatedOption (BootOptionVar, VariableSize)) {
|
|
Status = DeleteBiosCreateOption (Index, SystemConfiguration.NewPositionPolicy, BootOrder, &BootOrderSize, &BiosCreatedBootNum);
|
|
if (!EFI_ERROR (Status)) {
|
|
if (ReadCmos8 (LastBootDevice) == (UINT8) BiosCreatedBootNum) {
|
|
WriteCmos8 (LastBootDevice, (UINT8) BootOrder[Index]);
|
|
}
|
|
FreePool (BootOptionVar);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
} else if (IsValidRemovableBootOptionDevPath (OptionDevicePath) &&
|
|
!IsOpromStorageDev (OptionDevicePath, OpromStorageDev, OpromStorageDevCount)) {
|
|
NeedDelete = FALSE;
|
|
} else if (IsNetworkBootSupported && IsValidNetworkBootOption (&SystemConfiguration, OptionDevicePath)) {
|
|
NeedDelete = FALSE;
|
|
} else {
|
|
NeedDelete = TRUE;
|
|
}
|
|
|
|
if (FeaturePcdGet (PcdH2OBdsCpBootDeviceEnumCheckBootOptionSupported)) {
|
|
TriggerCpBootDeviceEnumCheckBootOption (BootOption, &gEfiGlobalVariableGuid, &NeedDelete);
|
|
}
|
|
|
|
FreePool (BootOptionVar);
|
|
if (ExpandedDevPath != NULL) {
|
|
FreePool (ExpandedDevPath);
|
|
}
|
|
|
|
if (NeedDelete) {
|
|
BdsLibDeleteBootOption (BootOrder[Index], BootOrder, &BootOrderSize);
|
|
continue;
|
|
}
|
|
|
|
if (IsWindowsToGoBootOption) {
|
|
*WindowsToGoBootVarExist = TRUE;
|
|
}
|
|
|
|
Index++;
|
|
}
|
|
|
|
if (!EFI_ERROR (SystemConfigStatus)) {
|
|
AdjustBootOrder (
|
|
!((BOOLEAN) SystemConfiguration.BootNormalPriority),
|
|
BootOrderSize / sizeof (UINT16),
|
|
BootOrder
|
|
);
|
|
}
|
|
|
|
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);
|
|
}
|
|
if (DisableOpromStorageDevBoot != NULL) {
|
|
FreePool (DisableOpromStorageDevBoot);
|
|
|
|
if (OpromStorageDevCount) {
|
|
FreePool (OpromStorageDev);
|
|
}
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
|
|
/**
|
|
Get size of buffer to get protocol's handles.
|
|
|
|
@param[in] ProtocolGuid The protocol GUID of handles.
|
|
@param[out] BufferSize Buffer size to get protocol handles.
|
|
|
|
@retval EFI_SUCCESS Get size successful.
|
|
@retval other Error status from gBS->LocateHandle function.
|
|
**/
|
|
EFI_STATUS
|
|
GetHandleBufferSize (
|
|
IN EFI_GUID *ProtocolGuid,
|
|
OUT UINTN *BufferSize
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN TmpBufferSize;
|
|
|
|
TmpBufferSize = 0;
|
|
Status = gBS->LocateHandle (
|
|
ByProtocol,
|
|
ProtocolGuid,
|
|
NULL,
|
|
&TmpBufferSize,
|
|
NULL
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
if (Status == EFI_NOT_FOUND) {
|
|
TmpBufferSize = 0;
|
|
} else if (Status != EFI_BUFFER_TOO_SMALL) {
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
/// Status will be EFI_NOT_FOUND or EFI_BUFFER_TOO_SMALL
|
|
*BufferSize = TmpBufferSize;
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
Determine whether there is a added or removed EFI device on system
|
|
|
|
@retval TRUE There is a added or removed EFI device on system
|
|
@retval FALSE There is no added or removed EFI device on system
|
|
**/
|
|
STATIC
|
|
BOOLEAN
|
|
IsEfiDevAddedOrRemoved (
|
|
VOID
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN FileSysHandlesNum;
|
|
UINTN LoadFileHandlesNum;
|
|
UINTN TotalHandlesNum;
|
|
EFI_HANDLE *ProtocolHandles;
|
|
UINTN RecordIndex;
|
|
UINTN Index;
|
|
BOOLEAN DevIsAddedOrRemoved;
|
|
|
|
FileSysHandlesNum = 0;
|
|
Status = GetHandleBufferSize (
|
|
&gEfiSimpleFileSystemProtocolGuid,
|
|
&FileSysHandlesNum
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return TRUE;
|
|
}
|
|
LoadFileHandlesNum = 0;
|
|
Status = GetHandleBufferSize (
|
|
&gEfiLoadFileProtocolGuid,
|
|
&LoadFileHandlesNum
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return TRUE;
|
|
}
|
|
TotalHandlesNum = FileSysHandlesNum + LoadFileHandlesNum;
|
|
if (TotalHandlesNum != 0) {
|
|
ProtocolHandles = AllocateZeroPool (sizeof (EFI_HANDLE) * TotalHandlesNum);
|
|
if (ProtocolHandles == NULL) {
|
|
return TRUE;
|
|
}
|
|
} else {
|
|
ProtocolHandles = NULL;
|
|
}
|
|
|
|
if (FileSysHandlesNum != 0) {
|
|
gBS->LocateHandle (
|
|
ByProtocol,
|
|
&gEfiSimpleFileSystemProtocolGuid,
|
|
NULL,
|
|
&FileSysHandlesNum,
|
|
ProtocolHandles
|
|
);
|
|
}
|
|
|
|
if (LoadFileHandlesNum != 0) {
|
|
gBS->LocateHandle (
|
|
ByProtocol,
|
|
&gEfiLoadFileProtocolGuid,
|
|
NULL,
|
|
&LoadFileHandlesNum,
|
|
(ProtocolHandles + FileSysHandlesNum)
|
|
);
|
|
}
|
|
|
|
if (TotalHandlesNum == mGenericBdsLibGlobalData->PreviousHandlesNum) {
|
|
DevIsAddedOrRemoved = FALSE;
|
|
for (Index = 0; Index < TotalHandlesNum; Index++) {
|
|
for (RecordIndex = 0; RecordIndex < mGenericBdsLibGlobalData->PreviousHandlesNum; RecordIndex++) {
|
|
if (mGenericBdsLibGlobalData->PreviousHandles[RecordIndex] == ProtocolHandles[Index]) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (RecordIndex == mGenericBdsLibGlobalData->PreviousHandlesNum) {
|
|
DevIsAddedOrRemoved = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
DevIsAddedOrRemoved = TRUE;
|
|
}
|
|
|
|
if (mGenericBdsLibGlobalData->PreviousHandles != NULL) {
|
|
FreePool (mGenericBdsLibGlobalData->PreviousHandles);
|
|
}
|
|
mGenericBdsLibGlobalData->PreviousHandlesNum = TotalHandlesNum;
|
|
mGenericBdsLibGlobalData->PreviousHandles = (TotalHandlesNum > 0) ? ProtocolHandles : NULL;
|
|
|
|
return DevIsAddedOrRemoved;
|
|
}
|
|
|
|
/**
|
|
Internal function to trigger BOOT_DEVICE_ENUM_AFTER checkpoint.
|
|
**/
|
|
STATIC
|
|
VOID
|
|
TriggerCpBootDeviceEnumAfter (
|
|
VOID
|
|
)
|
|
{
|
|
H2O_BDS_CP_BOOT_DEVICE_ENUM_AFTER_DATA BootDevEnumAfterData;
|
|
|
|
BootDevEnumAfterData.Size = sizeof (H2O_BDS_CP_BOOT_DEVICE_ENUM_AFTER_DATA);
|
|
BootDevEnumAfterData.Status = H2O_CP_TASK_NORMAL;
|
|
|
|
DEBUG_CP ((DEBUG_INFO, "Checkpoint Trigger: %g\n", &gH2OBdsCpBootDeviceEnumAfterGuid));
|
|
H2OCpTrigger (&gH2OBdsCpBootDeviceEnumAfterGuid, &BootDevEnumAfterData);
|
|
DEBUG_CP ((DEBUG_INFO, "Checkpoint Result: %x\n", BootDevEnumAfterData.Status));
|
|
}
|
|
|
|
/**
|
|
Internal function to trigger BOOT_DEVICE_ENUM_BEFORE checkpoint.
|
|
**/
|
|
STATIC
|
|
VOID
|
|
TriggerCpBootDeviceEnumBefore (
|
|
VOID
|
|
)
|
|
{
|
|
H2O_BDS_CP_BOOT_DEVICE_ENUM_BEFORE_DATA BootDevEnumBeforeData;
|
|
|
|
BootDevEnumBeforeData.Size = sizeof (H2O_BDS_CP_BOOT_DEVICE_ENUM_BEFORE_DATA);
|
|
BootDevEnumBeforeData.Status = H2O_CP_TASK_NORMAL;
|
|
|
|
DEBUG_CP ((DEBUG_INFO, "Checkpoint Trigger: %g\n", &gH2OBdsCpBootDeviceEnumBeforeGuid));
|
|
H2OCpTrigger (&gH2OBdsCpBootDeviceEnumBeforeGuid, &BootDevEnumBeforeData);
|
|
DEBUG_CP ((DEBUG_INFO, "Checkpoint Result: %x\n", BootDevEnumBeforeData.Status));
|
|
}
|
|
|
|
/**
|
|
Internal function to trigger BOOT_DEVICE_ENUM_CHECK_BOOT_OPTION checkpoint.
|
|
|
|
@param[in] VariableName Pointer to boot option variable name.
|
|
@param[in] VariableGuid Pointer to boot option variable GUID.
|
|
@param[in, out] Invalid Pointer to flag to specify this boot option variable is invalid or not.
|
|
|
|
@retval EFI_SUCCESS Trigger BOOT_DEVICE_ENUM_CHECK_BOOT_OPTION checkpoint successfully.
|
|
@retval Other Fail to convert boot option variable to load option data.
|
|
**/
|
|
EFI_STATUS
|
|
TriggerCpBootDeviceEnumCheckBootOption (
|
|
IN CHAR16 *VariableName,
|
|
IN EFI_GUID *VariableGuid,
|
|
IN OUT BOOLEAN *Invalid
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
H2O_BDS_LOAD_OPTION *BdsLoadOption;
|
|
H2O_BDS_CP_BOOT_DEVICE_ENUM_CHECK_BOOT_OPTION_DATA BootDevEnumCheckBootOptionData;
|
|
|
|
Status = gBdsServices->ConvertVarToLoadOption (gBdsServices, VariableName, VariableGuid, &BdsLoadOption);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
BootDevEnumCheckBootOptionData.Size = sizeof (H2O_BDS_CP_BOOT_DEVICE_ENUM_CHECK_BOOT_OPTION_DATA);
|
|
BootDevEnumCheckBootOptionData.Status = H2O_CP_TASK_NORMAL;
|
|
BootDevEnumCheckBootOptionData.BootOption = BdsLoadOption;
|
|
BootDevEnumCheckBootOptionData.Valid = !(*Invalid);
|
|
|
|
DEBUG_CP ((DEBUG_INFO, "Checkpoint Trigger: %g\n", &gH2OBdsCpBootDeviceEnumCheckBootOptionGuid));
|
|
H2OCpTrigger (&gH2OBdsCpBootDeviceEnumCheckBootOptionGuid, &BootDevEnumCheckBootOptionData);
|
|
DEBUG_CP ((DEBUG_INFO, "Checkpoint Result: %x\n", BootDevEnumCheckBootOptionData.Status));
|
|
|
|
if (BootDevEnumCheckBootOptionData.Status == H2O_CP_TASK_UPDATE) {
|
|
*Invalid = !(BootDevEnumCheckBootOptionData.Valid);
|
|
}
|
|
|
|
gBdsServices->FreeLoadOption (gBdsServices, BdsLoadOption);
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
For EFI boot option, BDS separate them as six types:
|
|
1. Network - The boot option points to the SimpleNetworkProtocol device.
|
|
Bds will try to automatically create this type boot option when enumerate.
|
|
2. Shell - The boot option points to internal flash shell.
|
|
Bds will try to automatically create this type boot option when enumerate.
|
|
3. Removable BlockIo - The boot option only points to the removable media
|
|
device, like USB flash disk, DVD, Floppy etc.
|
|
These device should contain a *removable* blockIo
|
|
protocol in their device handle.
|
|
Bds will try to automatically create this type boot option
|
|
when enumerate.
|
|
4. Fixed BlockIo - The boot option only points to a Fixed blockIo device,
|
|
like HardDisk.
|
|
These device should contain a *fixed* blockIo
|
|
protocol in their device handle.
|
|
BDS will skip fixed blockIo devices, and NOT
|
|
automatically create boot option for them. But BDS
|
|
will help to delete those fixed blockIo boot option,
|
|
whose description rule conflict with other auto-created
|
|
boot options.
|
|
5. Non-BlockIo Simplefile - The boot option points to a device whose handle
|
|
has SimpleFileSystem Protocol, but has no blockio
|
|
protocol. These devices do not offer blockIo
|
|
protocol, but BDS still can get the
|
|
\EFI\BOOT\boot{machinename}.EFI by SimpleFileSystem
|
|
Protocol.
|
|
6. File - The boot option points to a file. These boot options are usually
|
|
created by user manually or OS loader. BDS will not delete or modify
|
|
these boot options.
|
|
|
|
This function will enumerate all possible boot device in the system, and
|
|
automatically create boot options for Network, Shell, Removable BlockIo,
|
|
and Non-BlockIo Simplefile devices.
|
|
It will only execute once of every boot.
|
|
|
|
@param BdsBootOptionList The header of the link list which indexed all
|
|
current boot options. Deprecated.
|
|
|
|
@retval EFI_SUCCESS Finished all the boot device enumerate and create
|
|
the boot option base on that boot device
|
|
|
|
@retval EFI_OUT_OF_RESOURCES Failed to enumerate the boot device and create the boot option list
|
|
**/
|
|
EFI_STATUS
|
|
BdsLibEnumerateAllBootOption (
|
|
IN BOOLEAN ForceEnumerateAll,
|
|
IN OUT LIST_ENTRY *BdsBootOptionList OPTIONAL
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
KERNEL_CONFIGURATION SystemConfiguration;
|
|
EFI_STATUS SystemConfigStatus;
|
|
BOOLEAN WindowsToGoBootVarExist;
|
|
CHAR8 *PlatLang;
|
|
CHAR8 *LastLang;
|
|
UINTN BootType;
|
|
BOOLEAN IsUsbBootSupported;
|
|
BOOLEAN IsNetworkBootSupported;
|
|
|
|
POST_CODE (BDS_ENUMERATE_ALL_BOOT_OPTION);
|
|
DEBUG_CODE (
|
|
if (BdsBootOptionList != NULL) {
|
|
DEBUG ((DEBUG_INFO, "BdsLibEnumerateAllBootOption(): BdsBootOptionList is deprecated. Please upgrade caller code to input NULL and use GetBootList() to get boot option list.\n"));
|
|
}
|
|
);
|
|
|
|
if (FeaturePcdGet (PcdH2OBdsCpBootDeviceEnumBeforeSupported)) {
|
|
TriggerCpBootDeviceEnumBefore ();
|
|
}
|
|
|
|
Status = EFI_SUCCESS;
|
|
PlatLang = NULL;
|
|
LastLang = NULL;
|
|
BootType = H2OGetBootType ();
|
|
BdsLibDeleteInvalidBootOptions ();
|
|
//
|
|
// If the boot device enumerate happened, just get the boot device from the boot order variable
|
|
//
|
|
if (!IsEfiDevAddedOrRemoved () && mGenericBdsLibGlobalData->EnumBootDevice && !ForceEnumerateAll) {
|
|
//
|
|
// No new bootable device. Get PlatformLang variable and compare with LastEnumLang variable to check if there is a
|
|
// need to update the boot option description or not. If there is no PlatformLang variable, enumeration has occurred
|
|
// but no language change has happened, so there is no need to update the boot option description.
|
|
//
|
|
GetVariable2 (LAST_ENUM_LANGUAGE_VARIABLE_NAME, &gLastEnumLangGuid, (VOID **)&LastLang, NULL);
|
|
GetEfiGlobalVariable2 (L"PlatformLang", (VOID **)&PlatLang, NULL);
|
|
if (PlatLang == NULL) {
|
|
goto Exit;
|
|
}
|
|
|
|
if ((LastLang != NULL) && (AsciiStrCmp (LastLang, PlatLang) == 0)) {
|
|
SetAllBootOptionsConnected ();
|
|
goto Exit;
|
|
} else {
|
|
gRT->SetVariable (
|
|
LAST_ENUM_LANGUAGE_VARIABLE_NAME,
|
|
&gLastEnumLangGuid,
|
|
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,
|
|
AsciiStrSize (PlatLang),
|
|
PlatLang
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Notes:
|
|
// this dirty code is to get the legacy boot option from the BBS table and create to variable as the EFI boot option,
|
|
// it should be removed after the CSM can provide legacy boot option directly
|
|
//
|
|
if (BootType != EFI_BOOT_TYPE) {
|
|
if (FeaturePcdGet (PcdH2OCsmSupported)) {
|
|
BdsDeleteAllInvalidLegacyBootOptions ();
|
|
BdsAddNonExistingLegacyBootOptions ();
|
|
BdsUpdateLegacyDevOrder ();
|
|
}
|
|
|
|
if (BootType == LEGACY_BOOT_TYPE) {
|
|
BdsLibRemovedBootOption (FALSE);
|
|
}
|
|
} else {
|
|
BdsLibRemovedBootOption (TRUE);
|
|
}
|
|
BdsLibDeleteRedundantOption ();
|
|
|
|
if (FeaturePcdGet (PcdAutoCreateDummyBootOption) && BootType != LEGACY_BOOT_TYPE) {
|
|
CreateDummyBootOptions ();
|
|
SyncBootOrder ();
|
|
}
|
|
|
|
if (mGenericBdsLibGlobalData->UefiFastBootEnabled) {
|
|
mGenericBdsLibGlobalData->UefiFastBootEnabled = BdsLibIsWin8FastBootActive ();
|
|
}
|
|
|
|
if (BootType != LEGACY_BOOT_TYPE && !mGenericBdsLibGlobalData->EnableBootOrderHook) {
|
|
if (IsVirtualBootOrder ()) {
|
|
BdsLibEnableBootOrderHook ();
|
|
}
|
|
mGenericBdsLibGlobalData->EnableBootOrderHook = TRUE;
|
|
}
|
|
|
|
if (mGenericBdsLibGlobalData->UefiFastBootEnabled) {
|
|
//
|
|
// UEFI fast boot feature is only enabled for first time enumeration.
|
|
//
|
|
mGenericBdsLibGlobalData->UefiFastBootEnabled = FALSE;
|
|
SetTargetHddConnected ();
|
|
goto Exit;
|
|
}
|
|
|
|
BdsDeleteAllInvalidEfiBootOption (&WindowsToGoBootVarExist);
|
|
|
|
InitializeListHead (&mWindowsToGoDeviceList);
|
|
if (WindowsToGoBootVarExist) {
|
|
UpdateWindowsToGoList ();
|
|
}
|
|
|
|
//
|
|
// SCU disable EFI Boot
|
|
//
|
|
if (BootType == LEGACY_BOOT_TYPE) {
|
|
mGenericBdsLibGlobalData->EnumBootDevice = TRUE;
|
|
SetAllBootOptionsConnected ();
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Enumerate all boot options
|
|
//
|
|
SystemConfigStatus = GetKernelConfiguration (&SystemConfiguration);
|
|
ASSERT_EFI_ERROR (SystemConfigStatus);
|
|
IsUsbBootSupported = (BOOLEAN) (EFI_ERROR (SystemConfigStatus) || SystemConfiguration.UsbBoot == 0);
|
|
IsNetworkBootSupported = (BOOLEAN) (!EFI_ERROR (SystemConfigStatus) && SystemConfiguration.PxeBootToLan != 0);
|
|
|
|
EnumerateAllSimpleFileSysBootOption (WindowsToGoBootVarExist, IsUsbBootSupported);
|
|
if (IsNetworkBootSupported) {
|
|
EnumerateAllNetworkBootOption ();
|
|
}
|
|
EnumerateAllShellBootOption ();
|
|
|
|
//
|
|
// Make sure every boot only have one time boot device enumerate
|
|
//
|
|
if (SystemConfiguration.Win8FastBoot == 0 &&
|
|
BootType == EFI_BOOT_TYPE) {
|
|
UpdateTargetHddVariable ();
|
|
}
|
|
SetAllBootOptionsConnected ();
|
|
mGenericBdsLibGlobalData->EnumBootDevice = TRUE;
|
|
|
|
Exit:
|
|
if (FeaturePcdGet (PcdH2OBdsCpBootDeviceEnumAfterSupported)) {
|
|
TriggerCpBootDeviceEnumAfter ();
|
|
}
|
|
|
|
if (BdsBootOptionList != NULL) {
|
|
Status = BdsLibBuildOptionFromVar (BdsBootOptionList, L"BootOrder");
|
|
}
|
|
if (FeaturePcdGet (PcdAutoCreateDummyBootOption) && BootType != LEGACY_BOOT_TYPE) {
|
|
BdsLibSyncPhysicalBootOrder ();
|
|
}
|
|
|
|
if (LastLang != NULL) {
|
|
FreePool (LastLang);
|
|
}
|
|
if (PlatLang != NULL) {
|
|
FreePool (PlatLang);
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Build the boot option with the handle parsed in
|
|
|
|
@param Handle The handle which present the device path to create
|
|
boot option
|
|
@param BdsBootOptionList The header of the link list which indexed all
|
|
current boot options. Deprecated.
|
|
@param String The description of the boot option.
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
BdsLibBuildOptionFromHandle (
|
|
IN EFI_HANDLE Handle,
|
|
IN LIST_ENTRY *BdsBootOptionList OPTIONAL,
|
|
IN CHAR16 *String
|
|
)
|
|
{
|
|
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
|
|
CHAR16 *TempString;
|
|
CHAR16 *CustomizedDescription;
|
|
EFI_STATUS OemSvcStatus;
|
|
EFI_DEVICE_PATH_PROTOCOL *UsbShortFormDevPath;
|
|
|
|
DEBUG_CODE (
|
|
if (BdsBootOptionList != NULL) {
|
|
DEBUG ((DEBUG_INFO, "BdsLibBuildOptionFromHandle(): BdsBootOptionList is deprecated. Please upgrade caller code to input NULL.\n"));
|
|
}
|
|
);
|
|
|
|
DevicePath = DevicePathFromHandle (Handle);
|
|
if (String == NULL) {
|
|
TempString = DevicePathToStr (DevicePath);
|
|
} else {
|
|
TempString = String;
|
|
}
|
|
|
|
ASSERT (TempString != NULL);
|
|
if (TempString == NULL) {
|
|
return;
|
|
}
|
|
|
|
CustomizedDescription = AllocateCopyPool (StrSize (TempString), TempString);
|
|
DEBUG_OEM_SVC ((DEBUG_INFO, "OemKernelServices Call: OemSvcDxeUpdateDescriptionOfBootOption \n"));
|
|
OemSvcStatus = OemSvcDxeUpdateDescriptionOfBootOption (DevicePath, NULL, NULL, &CustomizedDescription);
|
|
DEBUG_OEM_SVC ((DEBUG_INFO, "OemKernelServices OemSvcDxeUpdateDescriptionOfBootOption Status: %r\n", OemSvcStatus));
|
|
if (CustomizedDescription != NULL) {
|
|
if (TempString != String) {
|
|
FreePool (TempString);
|
|
}
|
|
TempString = CustomizedDescription;
|
|
}
|
|
|
|
//
|
|
// Create and register new boot option
|
|
//
|
|
UsbShortFormDevPath = NULL;
|
|
if (FeaturePcdGet (PcdH2OBdsUsbCreateShortFormBootOption)) {
|
|
if (IsUsbDevicePath (DevicePath)) {
|
|
UsbShortFormDevPath = GetUsbShortFormDevPath (DevicePath);
|
|
if (UsbShortFormDevPath != NULL) {
|
|
DevicePath = UsbShortFormDevPath;
|
|
}
|
|
}
|
|
}
|
|
|
|
BdsLibRegisterNewOption (NULL, DevicePath, TempString, L"BootOrder", (UINT8 *) "RC", 2);
|
|
if (TempString != String) {
|
|
FreePool (TempString);
|
|
}
|
|
if (UsbShortFormDevPath != NULL) {
|
|
FreePool (UsbShortFormDevPath);
|
|
}
|
|
}
|
|
|
|
EFI_STATUS
|
|
SetBbsPriority (
|
|
IN EFI_LEGACY_BIOS_PROTOCOL *LegacyBios,
|
|
IN BDS_COMMON_OPTION *Option
|
|
)
|
|
{
|
|
BBS_TABLE *LocalBbsTable;
|
|
UINT16 BbsIndex;
|
|
UINT16 BootOption[10];
|
|
UINTN BootOptionSize;
|
|
UINT8 *Ptr, *BootOptionVar;
|
|
UINT16 *OptionOrder;
|
|
UINTN OptionOrderSize;
|
|
UINT16 PriorityIndex;
|
|
BOOLEAN Flag; // TRUE for Option->BootCurrent is the highest priority
|
|
UINT16 DevPathSize;
|
|
CHAR16 *BootDesc;
|
|
UINT16 Index;
|
|
|
|
//
|
|
// Read the BootOrder variable.
|
|
//
|
|
OptionOrder = BdsLibGetVariableAndSize (
|
|
L"BootOrder",
|
|
&gEfiGlobalVariableGuid,
|
|
&OptionOrderSize
|
|
);
|
|
if (OptionOrder == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
Flag = FALSE;
|
|
|
|
//
|
|
// Set BBS priority according OptionOrder variable
|
|
//
|
|
Index = 0;
|
|
for (PriorityIndex = 0; PriorityIndex < OptionOrderSize / sizeof (UINT16); PriorityIndex++) {
|
|
UnicodeSPrint (BootOption, sizeof (BootOption), L"Boot%04x", OptionOrder[PriorityIndex]);
|
|
|
|
BootOptionVar = BdsLibGetVariableAndSize (
|
|
BootOption,
|
|
&gEfiGlobalVariableGuid,
|
|
&BootOptionSize
|
|
);
|
|
if (BootOptionVar == NULL) {
|
|
continue;
|
|
}
|
|
//
|
|
// Skip the native boot options(EFI shell...)
|
|
//
|
|
Ptr = BootOptionVar + sizeof(UINT32) + sizeof(UINT16) + StrSize ((CHAR16 *)(BootOptionVar + 6));
|
|
if (*Ptr != BBS_DEVICE_PATH) {
|
|
continue;
|
|
}
|
|
|
|
Ptr = BootOptionVar;
|
|
Ptr += sizeof (UINT32);
|
|
|
|
DevPathSize = *((UINT16 *) Ptr);
|
|
Ptr += sizeof (UINT16);
|
|
|
|
BootDesc = (CHAR16*) Ptr;
|
|
Ptr += StrSize (BootDesc);
|
|
|
|
Ptr += DevPathSize;
|
|
Ptr += sizeof (BBS_TABLE);
|
|
|
|
BbsIndex = *(UINT16 *)Ptr;
|
|
|
|
LegacyBios->GetBbsInfo (LegacyBios, NULL, NULL, NULL, &LocalBbsTable);
|
|
|
|
LocalBbsTable[BbsIndex].BootPriority = Index;
|
|
Index++;
|
|
//
|
|
// Pull Option->BootCurrent up to the highest priority
|
|
//
|
|
if (!Flag) {
|
|
if (Option->BootCurrent == OptionOrder[PriorityIndex]) {
|
|
LocalBbsTable[BbsIndex].BootPriority = 0;
|
|
Flag = TRUE;
|
|
} else {
|
|
LocalBbsTable[BbsIndex].BootPriority++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Return the bootable media handle.
|
|
First, check the device is connected
|
|
Second, check whether the device path point to a device which support SimpleFileSystemProtocol,
|
|
Third, detect the the default boot file in the Media, and return the removable Media handle.
|
|
|
|
@param DevicePath Device Path to a bootable device
|
|
|
|
@return The bootable media handle. If the media on the DevicePath is not bootable, NULL will return.
|
|
**/
|
|
EFI_HANDLE
|
|
EFIAPI
|
|
BdsLibGetBootableHandle (
|
|
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_TPL OldTpl;
|
|
EFI_DEVICE_PATH_PROTOCOL *UpdatedDevicePath;
|
|
EFI_DEVICE_PATH_PROTOCOL *DupDevicePath;
|
|
EFI_HANDLE Handle;
|
|
EFI_BLOCK_IO_PROTOCOL *BlockIo;
|
|
VOID *Buffer;
|
|
EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
|
|
UINTN Size;
|
|
UINTN TempSize;
|
|
EFI_HANDLE ReturnHandle;
|
|
EFI_HANDLE *SimpleFileSystemHandles;
|
|
|
|
UINTN NumberSimpleFileSystemHandles;
|
|
UINTN Index;
|
|
EFI_IMAGE_DOS_HEADER DosHeader;
|
|
EFI_IMAGE_OPTIONAL_HEADER_UNION HdrData;
|
|
EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr;
|
|
|
|
if (!IsDevicePathValid (DevicePath, 0)) {
|
|
ASSERT (FALSE);
|
|
return NULL;
|
|
}
|
|
|
|
UpdatedDevicePath = DevicePath;
|
|
|
|
//
|
|
// Enter to critical section to protect the acquired BlockIo instance
|
|
// from getting released due to the USB mass storage hotplug event
|
|
//
|
|
OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
|
|
|
|
//
|
|
// Check whether the device is connected
|
|
//
|
|
Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &UpdatedDevicePath, &Handle);
|
|
if (EFI_ERROR (Status)) {
|
|
//
|
|
// Skip the case that the boot option point to a simple file protocol which does not consume block Io protocol,
|
|
//
|
|
Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &UpdatedDevicePath, &Handle);
|
|
if (EFI_ERROR (Status)) {
|
|
//
|
|
// Fail to find the proper BlockIo and simple file protocol, maybe because device not present, we need to connect it firstly
|
|
//
|
|
UpdatedDevicePath = DevicePath;
|
|
Status = gBS->LocateDevicePath (&gEfiDevicePathProtocolGuid, &UpdatedDevicePath, &Handle);
|
|
gBS->ConnectController (Handle, NULL, NULL, TRUE);
|
|
}
|
|
} else {
|
|
//
|
|
// For removable device boot option, its contained device path only point to the removable device handle,
|
|
// should make sure all its children handles (its child partion or media handles) are created and connected.
|
|
//
|
|
gBS->ConnectController (Handle, NULL, NULL, TRUE);
|
|
//
|
|
// Get BlockIo protocol and check removable attribute
|
|
//
|
|
Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **)&BlockIo);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
//
|
|
// Issue a dummy read to the device to check for media change.
|
|
// When the removable media is changed, any Block IO read/write will
|
|
// cause the BlockIo protocol be reinstalled and EFI_MEDIA_CHANGED is
|
|
// returned. After the Block IO protocol is reinstalled, subsequent
|
|
// Block IO read/write will success.
|
|
//
|
|
Buffer = AllocatePool (BlockIo->Media->BlockSize);
|
|
if (Buffer != NULL) {
|
|
BlockIo->ReadBlocks (
|
|
BlockIo,
|
|
BlockIo->Media->MediaId,
|
|
0,
|
|
BlockIo->Media->BlockSize,
|
|
Buffer
|
|
);
|
|
FreePool(Buffer);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Detect the the default boot file from removable Media
|
|
//
|
|
|
|
//
|
|
// If fail to get bootable handle specified by a USB boot option, the BDS should try to find other bootable device in the same USB bus
|
|
// Try to locate the USB node device path first, if fail then use its previous PCI node to search
|
|
//
|
|
DupDevicePath = DuplicateDevicePath (DevicePath);
|
|
ASSERT (DupDevicePath != NULL);
|
|
|
|
UpdatedDevicePath = DupDevicePath;
|
|
Status = gBS->LocateDevicePath (&gEfiDevicePathProtocolGuid, &UpdatedDevicePath, &Handle);
|
|
//
|
|
// if the resulting device path point to a usb node, and the usb node is a dummy node, should only let device path only point to the previous Pci node
|
|
// Acpi()/Pci()/Usb() --> Acpi()/Pci()
|
|
//
|
|
if ((DevicePathType (UpdatedDevicePath) == MESSAGING_DEVICE_PATH) &&
|
|
(DevicePathSubType (UpdatedDevicePath) == MSG_USB_DP)) {
|
|
//
|
|
// Remove the usb node, let the device path only point to PCI node
|
|
//
|
|
SetDevicePathEndNode (UpdatedDevicePath);
|
|
UpdatedDevicePath = DupDevicePath;
|
|
} else {
|
|
UpdatedDevicePath = DevicePath;
|
|
}
|
|
|
|
//
|
|
// Get the device path size of boot option
|
|
//
|
|
Size = GetDevicePathSize(UpdatedDevicePath) - sizeof (EFI_DEVICE_PATH_PROTOCOL); // minus the end node
|
|
ReturnHandle = NULL;
|
|
gBS->LocateHandleBuffer (
|
|
ByProtocol,
|
|
&gEfiSimpleFileSystemProtocolGuid,
|
|
NULL,
|
|
&NumberSimpleFileSystemHandles,
|
|
&SimpleFileSystemHandles
|
|
);
|
|
for (Index = 0; Index < NumberSimpleFileSystemHandles; Index++) {
|
|
//
|
|
// Get the device path size of SimpleFileSystem handle
|
|
//
|
|
TempDevicePath = DevicePathFromHandle (SimpleFileSystemHandles[Index]);
|
|
TempSize = GetDevicePathSize (TempDevicePath)- sizeof (EFI_DEVICE_PATH_PROTOCOL); // minus the end node
|
|
//
|
|
// Check whether the device path of boot option is part of the SimpleFileSystem handle's device path
|
|
//
|
|
if (Size <= TempSize && CompareMem (TempDevicePath, UpdatedDevicePath, Size)==0) {
|
|
//
|
|
// Load the default boot file \EFI\BOOT\boot{machinename}.EFI from removable Media
|
|
// machinename is ia32, ia64, x64, ...
|
|
//
|
|
Hdr.Union = &HdrData;
|
|
Status = BdsLibGetImageHeader (
|
|
SimpleFileSystemHandles[Index],
|
|
EFI_REMOVABLE_MEDIA_FILE_NAME,
|
|
&DosHeader,
|
|
Hdr
|
|
);
|
|
if (!EFI_ERROR (Status) &&
|
|
EFI_IMAGE_MACHINE_TYPE_SUPPORTED (Hdr.Pe32->FileHeader.Machine) &&
|
|
Hdr.Pe32->OptionalHeader.Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION) {
|
|
ReturnHandle = SimpleFileSystemHandles[Index];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
FreePool(DupDevicePath);
|
|
|
|
if (SimpleFileSystemHandles != NULL) {
|
|
FreePool(SimpleFileSystemHandles);
|
|
}
|
|
|
|
gBS->RestoreTPL (OldTpl);
|
|
|
|
return ReturnHandle;
|
|
}
|
|
|
|
/**
|
|
Check to see if the network cable is plugged in. If the DevicePath is not
|
|
connected it will be connected.
|
|
|
|
@param DevicePath Device Path to check
|
|
|
|
@retval TRUE DevicePath points to an Network that is connected
|
|
@retval FALSE DevicePath does not point to a bootable network
|
|
**/
|
|
BOOLEAN
|
|
BdsLibNetworkBootWithMediaPresent (
|
|
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_DEVICE_PATH_PROTOCOL *UpdatedDevicePath;
|
|
EFI_HANDLE Handle;
|
|
EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
|
|
BOOLEAN MediaPresent;
|
|
UINT32 InterruptStatus;
|
|
|
|
MediaPresent = FALSE;
|
|
|
|
UpdatedDevicePath = DevicePath;
|
|
//
|
|
// Locate Load File Protocol for PXE boot option first
|
|
//
|
|
Status = gBS->LocateDevicePath (&gEfiLoadFileProtocolGuid, &UpdatedDevicePath, &Handle);
|
|
if (EFI_ERROR (Status)) {
|
|
//
|
|
// Device not present so see if we need to connect it
|
|
//
|
|
Status = BdsLibConnectDevicePath (DevicePath);
|
|
if (!EFI_ERROR (Status)) {
|
|
//
|
|
// This one should work after we did the connect
|
|
//
|
|
Status = gBS->LocateDevicePath (&gEfiLoadFileProtocolGuid, &UpdatedDevicePath, &Handle);
|
|
}
|
|
}
|
|
|
|
if (!EFI_ERROR (Status)) {
|
|
Status = gBS->HandleProtocol (Handle, &gEfiSimpleNetworkProtocolGuid, (VOID **)&Snp);
|
|
if (EFI_ERROR (Status)) {
|
|
//
|
|
// Failed to open SNP from this handle, try to get SNP from parent handle
|
|
//
|
|
UpdatedDevicePath = DevicePathFromHandle (Handle);
|
|
if (UpdatedDevicePath != NULL) {
|
|
Status = gBS->LocateDevicePath (&gEfiSimpleNetworkProtocolGuid, &UpdatedDevicePath, &Handle);
|
|
if (!EFI_ERROR (Status)) {
|
|
//
|
|
// SNP handle found, get SNP from it
|
|
//
|
|
Status = gBS->HandleProtocol (Handle, &gEfiSimpleNetworkProtocolGuid, (VOID **) &Snp);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!EFI_ERROR (Status)) {
|
|
if (Snp->Mode->MediaPresentSupported) {
|
|
if (Snp->Mode->State == EfiSimpleNetworkInitialized) {
|
|
//
|
|
// Invoke Snp->GetStatus() to refresh the media status
|
|
//
|
|
Snp->GetStatus (Snp, &InterruptStatus, NULL);
|
|
|
|
//
|
|
// In case some one else is using the SNP check to see if it's connected
|
|
//
|
|
MediaPresent = Snp->Mode->MediaPresent;
|
|
} else {
|
|
//
|
|
// No one is using SNP so we need to Start and Initialize so
|
|
// MediaPresent will be valid.
|
|
//
|
|
Status = Snp->Start (Snp);
|
|
if (!EFI_ERROR (Status)) {
|
|
Status = Snp->Initialize (Snp, 0, 0);
|
|
if (!EFI_ERROR (Status)) {
|
|
MediaPresent = Snp->Mode->MediaPresent;
|
|
Snp->Shutdown (Snp);
|
|
}
|
|
Snp->Stop (Snp);
|
|
}
|
|
}
|
|
} else {
|
|
MediaPresent = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return MediaPresent;
|
|
}
|
|
|
|
/**
|
|
Check whether the Device path in a boot option point to a valid bootable device,
|
|
And if CheckMedia is true, check the device is ready to boot now.
|
|
|
|
@param DevPath the Device path in a boot option
|
|
@param CheckMedia if true, check the device is ready to boot now.
|
|
|
|
@retval TRUE the Device path is valid
|
|
@retval FALSE the Device path is invalid .
|
|
**/
|
|
BOOLEAN
|
|
EFIAPI
|
|
BdsLibIsValidEFIBootOptDevicePath (
|
|
IN EFI_DEVICE_PATH_PROTOCOL *DevPath,
|
|
IN BOOLEAN CheckMedia
|
|
)
|
|
{
|
|
return BdsLibIsValidEFIBootOptDevicePathExt (DevPath, CheckMedia, NULL);
|
|
}
|
|
|
|
/**
|
|
Check whether the Device path in a boot option point to a valid bootable device,
|
|
And if CheckMedia is true, check the device is ready to boot now.
|
|
If Description is not NULL and the device path point to a fixed BlockIo
|
|
device, check the description whether conflict with other auto-created
|
|
boot options.
|
|
|
|
@param DevPath the Device path in a boot option
|
|
@param CheckMedia if true, check the device is ready to boot now.
|
|
@param Description the description in a boot option
|
|
|
|
@retval TRUE the Device path is valid
|
|
@retval FALSE the Device path is invalid .
|
|
**/
|
|
BOOLEAN
|
|
EFIAPI
|
|
BdsLibIsValidEFIBootOptDevicePathExt (
|
|
IN EFI_DEVICE_PATH_PROTOCOL *DevPath,
|
|
IN BOOLEAN CheckMedia,
|
|
IN CHAR16 *Description
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_HANDLE Handle;
|
|
EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
|
|
EFI_DEVICE_PATH_PROTOCOL *LastDeviceNode;
|
|
EFI_BLOCK_IO_PROTOCOL *BlockIo;
|
|
|
|
TempDevicePath = DevPath;
|
|
LastDeviceNode = DevPath;
|
|
|
|
//
|
|
// Check if it's a valid boot option for network boot device.
|
|
// Check if there is EfiLoadFileProtocol installed.
|
|
// If yes, that means there is a boot option for network.
|
|
//
|
|
Status = gBS->LocateDevicePath (
|
|
&gEfiLoadFileProtocolGuid,
|
|
&TempDevicePath,
|
|
&Handle
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
//
|
|
// Device not present so see if we need to connect it
|
|
//
|
|
TempDevicePath = DevPath;
|
|
BdsLibConnectDevicePath (TempDevicePath);
|
|
Status = gBS->LocateDevicePath (
|
|
&gEfiLoadFileProtocolGuid,
|
|
&TempDevicePath,
|
|
&Handle
|
|
);
|
|
}
|
|
|
|
if (!EFI_ERROR (Status)) {
|
|
if (!IsDevicePathEnd (TempDevicePath)) {
|
|
//
|
|
// LoadFile protocol is not installed on handle with exactly the same DevPath
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
if (CheckMedia) {
|
|
//
|
|
// Test if it is ready to boot now
|
|
//
|
|
if (BdsLibNetworkBootWithMediaPresent(DevPath)) {
|
|
return TRUE;
|
|
}
|
|
} else {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the boot option point to a file, it is a valid EFI boot option,
|
|
// and assume it is ready to boot now
|
|
//
|
|
while (!IsDevicePathEnd (TempDevicePath)) {
|
|
//
|
|
// If there is USB Class or USB WWID device path node, treat it as valid EFI
|
|
// Boot Option. BdsExpandUsbShortFormDevicePath () will be used to expand it
|
|
// to full device path.
|
|
//
|
|
if ((DevicePathType (TempDevicePath) == MESSAGING_DEVICE_PATH) &&
|
|
((DevicePathSubType (TempDevicePath) == MSG_USB_CLASS_DP) ||
|
|
(DevicePathSubType (TempDevicePath) == MSG_USB_WWID_DP))) {
|
|
return TRUE;
|
|
}
|
|
|
|
LastDeviceNode = TempDevicePath;
|
|
TempDevicePath = NextDevicePathNode (TempDevicePath);
|
|
}
|
|
if ((DevicePathType (LastDeviceNode) == MEDIA_DEVICE_PATH) &&
|
|
(DevicePathSubType (LastDeviceNode) == MEDIA_FILEPATH_DP)) {
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Check if it's a valid boot option for internal FV application
|
|
//
|
|
if (EfiGetNameGuidFromFwVolDevicePathNode ((MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *) LastDeviceNode) != NULL) {
|
|
//
|
|
// If the boot option point to internal FV application, make sure it is valid
|
|
//
|
|
TempDevicePath = DevPath;
|
|
Status = BdsLibUpdateFvFileDevicePath (
|
|
&TempDevicePath,
|
|
EfiGetNameGuidFromFwVolDevicePathNode ((MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *) LastDeviceNode)
|
|
);
|
|
if (Status == EFI_ALREADY_STARTED) {
|
|
return TRUE;
|
|
} else {
|
|
if (Status == EFI_SUCCESS) {
|
|
FreePool (TempDevicePath);
|
|
}
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the boot option point to a blockIO device:
|
|
// if it is a removable blockIo device, it is valid.
|
|
// if it is a fixed blockIo device, check its description confliction.
|
|
//
|
|
TempDevicePath = DevPath;
|
|
Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &TempDevicePath, &Handle);
|
|
if (EFI_ERROR (Status)) {
|
|
//
|
|
// Device not present so see if we need to connect it
|
|
//
|
|
Status = BdsLibConnectDevicePath (DevPath);
|
|
if (!EFI_ERROR (Status)) {
|
|
//
|
|
// Try again to get the Block Io protocol after we did the connect
|
|
//
|
|
TempDevicePath = DevPath;
|
|
Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &TempDevicePath, &Handle);
|
|
}
|
|
}
|
|
|
|
if (!EFI_ERROR (Status)) {
|
|
Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **)&BlockIo);
|
|
if (!EFI_ERROR (Status)) {
|
|
if (CheckMedia) {
|
|
//
|
|
// Test if it is ready to boot now
|
|
//
|
|
if (BdsLibGetBootableHandle (DevPath) != NULL) {
|
|
return TRUE;
|
|
}
|
|
} else {
|
|
return TRUE;
|
|
}
|
|
}
|
|
} else {
|
|
//
|
|
// if the boot option point to a simple file protocol which does not consume block Io protocol, it is also a valid EFI boot option,
|
|
//
|
|
Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &TempDevicePath, &Handle);
|
|
if (!EFI_ERROR (Status)) {
|
|
if (CheckMedia) {
|
|
//
|
|
// Test if it is ready to boot now
|
|
//
|
|
if (BdsLibGetBootableHandle (DevPath) != NULL) {
|
|
return TRUE;
|
|
}
|
|
} else {
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
According to a file guild, check a Fv file device path is valid. If it is invalid,
|
|
try to return the valid device path.
|
|
FV address maybe changes for memory layout adjust from time to time, use this function
|
|
could promise the Fv file device path is right.
|
|
|
|
@param DevicePath on input, the Fv file device path need to check on
|
|
output, the updated valid Fv file device path
|
|
@param FileGuid the Fv file guild
|
|
|
|
@retval EFI_INVALID_PARAMETER the input DevicePath or FileGuid is invalid
|
|
parameter
|
|
@retval EFI_UNSUPPORTED the input DevicePath does not contain Fv file
|
|
guild at all
|
|
@retval EFI_ALREADY_STARTED the input DevicePath has pointed to Fv file, it is
|
|
valid
|
|
@retval EFI_SUCCESS has successfully updated the invalid DevicePath,
|
|
and return the updated device path in DevicePath
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
BdsLibUpdateFvFileDevicePath (
|
|
IN OUT EFI_DEVICE_PATH_PROTOCOL ** DevicePath,
|
|
IN EFI_GUID *FileGuid
|
|
)
|
|
{
|
|
EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
|
|
EFI_DEVICE_PATH_PROTOCOL *LastDeviceNode;
|
|
EFI_STATUS Status;
|
|
EFI_GUID *GuidPoint;
|
|
UINTN Index;
|
|
UINTN FvHandleCount;
|
|
EFI_HANDLE *FvHandleBuffer;
|
|
EFI_FV_FILETYPE Type;
|
|
UINTN Size;
|
|
EFI_FV_FILE_ATTRIBUTES Attributes;
|
|
UINT32 AuthenticationStatus;
|
|
BOOLEAN FindFvFile;
|
|
EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
|
|
EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv;
|
|
MEDIA_FW_VOL_FILEPATH_DEVICE_PATH FvFileNode;
|
|
EFI_HANDLE FoundFvHandle;
|
|
EFI_DEVICE_PATH_PROTOCOL *NewDevicePath;
|
|
|
|
if ((DevicePath == NULL) || (*DevicePath == NULL)) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
if (FileGuid == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Check whether the device path point to the default the input Fv file
|
|
//
|
|
TempDevicePath = *DevicePath;
|
|
LastDeviceNode = TempDevicePath;
|
|
while (!IsDevicePathEnd (TempDevicePath)) {
|
|
LastDeviceNode = TempDevicePath;
|
|
TempDevicePath = NextDevicePathNode (TempDevicePath);
|
|
}
|
|
GuidPoint = EfiGetNameGuidFromFwVolDevicePathNode (
|
|
(MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *) LastDeviceNode
|
|
);
|
|
if (GuidPoint == NULL) {
|
|
//
|
|
// if this option does not points to a Fv file, just return EFI_UNSUPPORTED
|
|
//
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
if (!CompareGuid (GuidPoint, FileGuid)) {
|
|
//
|
|
// If the Fv file is not the input file guid, just return EFI_UNSUPPORTED
|
|
//
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
//
|
|
// Check whether the input Fv file device path is valid
|
|
//
|
|
TempDevicePath = *DevicePath;
|
|
FoundFvHandle = NULL;
|
|
Status = gBS->LocateDevicePath (
|
|
&gEfiFirmwareVolume2ProtocolGuid,
|
|
&TempDevicePath,
|
|
&FoundFvHandle
|
|
);
|
|
if (!EFI_ERROR (Status)) {
|
|
Status = gBS->HandleProtocol (
|
|
FoundFvHandle,
|
|
&gEfiFirmwareVolume2ProtocolGuid,
|
|
(VOID **) &Fv
|
|
);
|
|
if (!EFI_ERROR (Status)) {
|
|
//
|
|
// Set FV ReadFile Buffer as NULL, only need to check whether input Fv file exist there
|
|
//
|
|
Status = Fv->ReadFile (
|
|
Fv,
|
|
FileGuid,
|
|
NULL,
|
|
&Size,
|
|
&Type,
|
|
&Attributes,
|
|
&AuthenticationStatus
|
|
);
|
|
if (!EFI_ERROR (Status)) {
|
|
return EFI_ALREADY_STARTED;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Look for the input wanted FV file in current FV
|
|
// First, try to look for in Bds own FV. Bds and input wanted FV file usually are in the same FV
|
|
//
|
|
FindFvFile = FALSE;
|
|
FoundFvHandle = NULL;
|
|
Status = gBS->HandleProtocol (
|
|
gImageHandle,
|
|
&gEfiLoadedImageProtocolGuid,
|
|
(VOID **) &LoadedImage
|
|
);
|
|
if (!EFI_ERROR (Status)) {
|
|
Status = gBS->HandleProtocol (
|
|
LoadedImage->DeviceHandle,
|
|
&gEfiFirmwareVolume2ProtocolGuid,
|
|
(VOID **) &Fv
|
|
);
|
|
if (!EFI_ERROR (Status)) {
|
|
Status = Fv->ReadFile (
|
|
Fv,
|
|
FileGuid,
|
|
NULL,
|
|
&Size,
|
|
&Type,
|
|
&Attributes,
|
|
&AuthenticationStatus
|
|
);
|
|
if (!EFI_ERROR (Status)) {
|
|
FindFvFile = TRUE;
|
|
FoundFvHandle = LoadedImage->DeviceHandle;
|
|
}
|
|
}
|
|
}
|
|
//
|
|
// Second, if fail to find, try to enumerate all FV
|
|
//
|
|
if (!FindFvFile) {
|
|
FvHandleBuffer = NULL;
|
|
gBS->LocateHandleBuffer (
|
|
ByProtocol,
|
|
&gEfiFirmwareVolume2ProtocolGuid,
|
|
NULL,
|
|
&FvHandleCount,
|
|
&FvHandleBuffer
|
|
);
|
|
for (Index = 0; Index < FvHandleCount; Index++) {
|
|
gBS->HandleProtocol (
|
|
FvHandleBuffer[Index],
|
|
&gEfiFirmwareVolume2ProtocolGuid,
|
|
(VOID **) &Fv
|
|
);
|
|
|
|
Status = Fv->ReadFile (
|
|
Fv,
|
|
FileGuid,
|
|
NULL,
|
|
&Size,
|
|
&Type,
|
|
&Attributes,
|
|
&AuthenticationStatus
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
//
|
|
// Skip if input Fv file not in the FV
|
|
//
|
|
continue;
|
|
}
|
|
FindFvFile = TRUE;
|
|
FoundFvHandle = FvHandleBuffer[Index];
|
|
break;
|
|
}
|
|
|
|
if (FvHandleBuffer != NULL) {
|
|
FreePool (FvHandleBuffer);
|
|
}
|
|
}
|
|
|
|
if (FindFvFile) {
|
|
//
|
|
// Build the shell device path
|
|
//
|
|
NewDevicePath = DevicePathFromHandle (FoundFvHandle);
|
|
EfiInitializeFwVolDevicepathNode (&FvFileNode, FileGuid);
|
|
NewDevicePath = AppendDevicePathNode (NewDevicePath, (EFI_DEVICE_PATH_PROTOCOL *) &FvFileNode);
|
|
*DevicePath = NewDevicePath;
|
|
return EFI_SUCCESS;
|
|
} else {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
}
|
|
|
|
/**
|
|
According Input value to determine remove legacy boot device or EFI
|
|
boot device.
|
|
|
|
@param RemovedLegacy TRUE: Remove all of legacy boot devices
|
|
FALSE: Remove all of EFI boot devices
|
|
|
|
@retval EFI_SUCCESS Remove boot devices successful.
|
|
@retval EFI_NOT_FOUND Cannot find any boot device.
|
|
@retval EFI_OUT_OF_RESOURCES Boot Order and Boot option does not synchronization
|
|
**/
|
|
EFI_STATUS
|
|
BdsLibRemovedBootOption (
|
|
IN BOOLEAN RemovedLegacy
|
|
)
|
|
{
|
|
UINT16 *BootOrder;
|
|
UINT8 *BootOptionVar;
|
|
UINTN BootOrderSize;
|
|
UINTN BootOptionSize;
|
|
EFI_STATUS Status;
|
|
UINTN Index;
|
|
UINTN Index2;
|
|
UINT16 BootOption[BOOT_OPTION_MAX_CHAR];
|
|
|
|
BOOLEAN IsLegacyBootOption;
|
|
BBS_TABLE *BbsEntry;
|
|
UINT16 BbsIndex;
|
|
BOOLEAN NeedDelete;
|
|
|
|
Status = EFI_SUCCESS;
|
|
BootOrder = NULL;
|
|
BootOrderSize = 0;
|
|
|
|
//
|
|
// delete physical and virtual boot order variable if system is in legacy mode.
|
|
//
|
|
if (FeaturePcdGet (PcdAutoCreateDummyBootOption) && !RemovedLegacy) {
|
|
Status = gRT->SetVariable (
|
|
PHYSICAL_BOOT_ORDER_NAME,
|
|
&gEfiGenericVariableGuid,
|
|
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
|
|
0,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
BootOrder = BdsLibGetVariableAndSize (
|
|
L"BootOrder",
|
|
&gEfiGlobalVariableGuid,
|
|
&BootOrderSize
|
|
);
|
|
if (NULL == BootOrder) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
Index = 0;
|
|
while (Index < BootOrderSize / sizeof (UINT16)) {
|
|
UnicodeSPrint (BootOption, sizeof (BootOption), L"Boot%04x", BootOrder[Index]);
|
|
BootOptionVar = BdsLibGetVariableAndSize (
|
|
BootOption,
|
|
&gEfiGlobalVariableGuid,
|
|
&BootOptionSize
|
|
);
|
|
if (NULL == BootOptionVar) {
|
|
FreePool (BootOrder);
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
IsLegacyBootOption = BdsLibIsLegacyBootOption (BootOptionVar, &BbsEntry, &BbsIndex);
|
|
|
|
NeedDelete = TRUE;
|
|
if (RemovedLegacy && !IsLegacyBootOption) {
|
|
NeedDelete = FALSE;
|
|
}
|
|
|
|
if (!RemovedLegacy && IsLegacyBootOption) {
|
|
NeedDelete = FALSE;
|
|
}
|
|
|
|
if (FeaturePcdGet (PcdH2OBdsCpBootDeviceEnumCheckBootOptionSupported)) {
|
|
TriggerCpBootDeviceEnumCheckBootOption (BootOption, &gEfiGlobalVariableGuid, &NeedDelete);
|
|
}
|
|
|
|
//
|
|
// Delete this invalid boot option "Boot####"
|
|
//
|
|
if (NeedDelete) {
|
|
Status = gRT->SetVariable (
|
|
BootOption,
|
|
&gEfiGlobalVariableGuid,
|
|
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
|
|
0,
|
|
NULL
|
|
);
|
|
//
|
|
// Mark this boot option in boot order as deleted
|
|
//
|
|
BootOrder[Index] = 0xffff;
|
|
}
|
|
|
|
FreePool (BootOptionVar);
|
|
Index++;
|
|
}
|
|
|
|
//
|
|
// Adjust boot order array
|
|
//
|
|
Index2 = 0;
|
|
for (Index = 0; Index < BootOrderSize / sizeof (UINT16); Index++) {
|
|
if (BootOrder[Index] != 0xffff) {
|
|
BootOrder[Index2] = BootOrder[Index];
|
|
Index2 ++;
|
|
}
|
|
}
|
|
Status = gRT->SetVariable (
|
|
L"BootOrder",
|
|
&gEfiGlobalVariableGuid,
|
|
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
|
|
Index2 * sizeof (UINT16),
|
|
BootOrder
|
|
);
|
|
|
|
FreePool (BootOrder);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
For a bootable Device path, return its boot type
|
|
|
|
@param DevicePath The bootable device Path to check
|
|
|
|
@return UINT32 Boot type :
|
|
//
|
|
// If the device path contains any media deviec path node, it is media boot type
|
|
// For the floppy node, handle it as media node
|
|
//
|
|
BDS_EFI_MEDIA_HD_BOOT
|
|
BDS_EFI_MEDIA_CDROM_BOOT
|
|
BDS_EFI_ACPI_FLOPPY_BOOT
|
|
//
|
|
// If the device path not contains any media deviec path node, and
|
|
// its last device path node point to a message device path node, it is
|
|
// a message boot type
|
|
//
|
|
BDS_EFI_MESSAGE_ATAPI_BOOT
|
|
BDS_EFI_MESSAGE_SCSI_BOOT
|
|
BDS_EFI_MESSAGE_USB_DEVICE_BOOT
|
|
BDS_EFI_MESSAGE_MISC_BOOT
|
|
//
|
|
// Legacy boot type
|
|
//
|
|
BDS_LEGACY_BBS_BOOT
|
|
//
|
|
// If a EFI Removable BlockIO device path not point to a media and message device,
|
|
// it is unsupported
|
|
//
|
|
BDS_EFI_UNSUPPORT
|
|
**/
|
|
UINT32
|
|
BdsLibGetBootTypeFromDevicePath (
|
|
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath
|
|
)
|
|
{
|
|
ACPI_HID_DEVICE_PATH *Acpi;
|
|
EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
|
|
EFI_DEVICE_PATH_PROTOCOL *LastDeviceNode;
|
|
UINT32 BootType;
|
|
|
|
if (NULL == DevicePath) {
|
|
return BDS_EFI_UNSUPPORT;
|
|
}
|
|
|
|
TempDevicePath = DevicePath;
|
|
|
|
while (!IsDevicePathEndType (TempDevicePath)) {
|
|
switch (DevicePathType (TempDevicePath)) {
|
|
|
|
case BBS_DEVICE_PATH:
|
|
return BDS_LEGACY_BBS_BOOT;
|
|
|
|
case MEDIA_DEVICE_PATH:
|
|
if (DevicePathSubType (TempDevicePath) == MEDIA_HARDDRIVE_DP) {
|
|
return BDS_EFI_MEDIA_HD_BOOT;
|
|
} else if (DevicePathSubType (TempDevicePath) == MEDIA_CDROM_DP) {
|
|
return BDS_EFI_MEDIA_CDROM_BOOT;
|
|
} else if (DevicePathSubType (TempDevicePath) == MEDIA_PIWG_FW_FILE_DP) {
|
|
return BDS_EFI_MEDIA_FV_FILEPATH_BOOT;
|
|
}
|
|
break;
|
|
|
|
case ACPI_DEVICE_PATH:
|
|
Acpi = (ACPI_HID_DEVICE_PATH *) TempDevicePath;
|
|
if (EISA_ID_TO_NUM (Acpi->HID) == 0x0604) {
|
|
return BDS_EFI_ACPI_FLOPPY_BOOT;
|
|
}
|
|
break;
|
|
|
|
case HARDWARE_DEVICE_PATH:
|
|
if (DevicePathSubType (TempDevicePath) == HW_CONTROLLER_DP) {
|
|
//
|
|
// Get the next device path node
|
|
//
|
|
LastDeviceNode = NextDevicePathNode (TempDevicePath);
|
|
//
|
|
// Multi-partition devices
|
|
//
|
|
if (DevicePathSubType (LastDeviceNode) == MSG_DEVICE_LOGICAL_UNIT_DP) {
|
|
return BDS_EFI_SDHC_BOOT;
|
|
}
|
|
//
|
|
// Single-partition devices
|
|
//
|
|
if (!IsDevicePathEndType (LastDeviceNode)) {
|
|
return BDS_EFI_SDHC_BOOT;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case MESSAGING_DEVICE_PATH:
|
|
|
|
//
|
|
// check message device path is USB device first.
|
|
//
|
|
if (DevicePathSubType(TempDevicePath) == MSG_USB_DP ||
|
|
DevicePathSubType(TempDevicePath) == MSG_USB_WWID_DP) {
|
|
//
|
|
// Check if it is USB LAN.
|
|
//
|
|
LastDeviceNode = TempDevicePath;
|
|
while (!IsDevicePathEndType (LastDeviceNode)) {
|
|
if (DevicePathType (LastDeviceNode) == MESSAGING_DEVICE_PATH &&
|
|
DevicePathSubType(LastDeviceNode) == MSG_MAC_ADDR_DP) {
|
|
return BDS_EFI_MESSAGE_MAC_BOOT;
|
|
}
|
|
LastDeviceNode = NextDevicePathNode (LastDeviceNode);
|
|
}
|
|
return BDS_EFI_MESSAGE_USB_DEVICE_BOOT;
|
|
}
|
|
|
|
//
|
|
// check message device path is ISCSI device.
|
|
//
|
|
if (DevicePathSubType(TempDevicePath) == MSG_ISCSI_DP) {
|
|
return BDS_EFI_MESSAGE_ISCSI_BOOT;
|
|
}
|
|
|
|
//
|
|
// Check message device path is SD/MMC device.
|
|
//
|
|
if ((DevicePathSubType (TempDevicePath) == MSG_SD_DP) ||
|
|
(DevicePathSubType (TempDevicePath) == MSG_EMMC_DP)) {
|
|
return BDS_EFI_SDHC_BOOT;
|
|
}
|
|
|
|
//
|
|
// Get the last device path node
|
|
//
|
|
LastDeviceNode = NextDevicePathNode (TempDevicePath);
|
|
|
|
if (DevicePathSubType(LastDeviceNode) == MSG_DEVICE_LOGICAL_UNIT_DP) {
|
|
//
|
|
// if the next node type is Device Logical Unit, which specify the Logical Unit Number (LUN),
|
|
// skit it
|
|
//
|
|
LastDeviceNode = NextDevicePathNode (LastDeviceNode);
|
|
}
|
|
|
|
//
|
|
// if the device path not only point to driver device, it is not a messaging device path,
|
|
//
|
|
if (!IsDevicePathEndType (LastDeviceNode)) {
|
|
break;
|
|
}
|
|
|
|
switch (DevicePathSubType (TempDevicePath)) {
|
|
case MSG_ATAPI_DP:
|
|
BootType = BDS_EFI_MESSAGE_ATAPI_BOOT;
|
|
break;
|
|
|
|
case MSG_SCSI_DP:
|
|
BootType = BDS_EFI_MESSAGE_SCSI_BOOT;
|
|
break;
|
|
|
|
case MSG_SATA_DP:
|
|
BootType = BDS_EFI_MESSAGE_SATA_BOOT;
|
|
break;
|
|
|
|
case MSG_MAC_ADDR_DP:
|
|
case MSG_VLAN_DP:
|
|
case MSG_IPv4_DP:
|
|
case MSG_IPv6_DP:
|
|
BootType = BDS_EFI_MESSAGE_MAC_BOOT;
|
|
break;
|
|
|
|
default:
|
|
BootType = BDS_EFI_MESSAGE_MISC_BOOT;
|
|
break;
|
|
}
|
|
return BootType;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
TempDevicePath = NextDevicePathNode (TempDevicePath);
|
|
}
|
|
|
|
return BDS_EFI_UNSUPPORT;
|
|
}
|
|
|
|
/**
|
|
According to device path to open file .
|
|
|
|
@param DevicePath Pointer to EFI_DEVICE_PATH_PROTOCOL
|
|
@param OpenMode The mode to open the file. The only valid combinations that the
|
|
file may be opened with are: Read, Read/Write, or Create/Read/
|
|
Write. See "Related Definitions" below.
|
|
@param Attributes Only valid for EFI_FILE_MODE_CREATE, in which case these
|
|
are the attribute bits for the newly created file. See "Related
|
|
Definitions" below.
|
|
@param NewHandle A pointer to the location to return the opened handle for the new
|
|
file. See the type EFI_FILE_PROTOCOL description.
|
|
|
|
@retval EFI_SUCCESS The file was opened.
|
|
@retval EFI_NOT_FOUND The specified file could not be found on the device.
|
|
@retval EFI_NO_MEDIA The device has no medium.
|
|
@retval EFI_MEDIA_CHANGED The device has a different medium in it or the medium is no
|
|
longer supported.
|
|
@retval EFI_DEVICE_ERROR The device reported an error.
|
|
@retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
|
|
@retval EFI_WRITE_PROTECTED An attempt was made to create a file, or open a file for write
|
|
when the media is write-protected.
|
|
@retval EFI_ACCESS_DENIED The service denied access to the file.
|
|
@retval EFI_OUT_OF_RESOURCES Not enough resources were available to open the file.
|
|
@retval EFI_VOLUME_FULL The volume is full.
|
|
**/
|
|
EFI_STATUS
|
|
BdsLibOpenFileFromDevicePath (
|
|
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
|
|
IN UINT64 OpenMode,
|
|
IN UINT64 Attributes,
|
|
OUT EFI_FILE_HANDLE *NewHandle
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_HANDLE DeviceHandle;
|
|
FILEPATH_DEVICE_PATH *FilePathNode;
|
|
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Volume;
|
|
EFI_FILE_HANDLE FileHandle;
|
|
EFI_FILE_HANDLE LastHandle;
|
|
FILEPATH_DEVICE_PATH *OriginalFilePathNode;
|
|
|
|
FilePathNode = (FILEPATH_DEVICE_PATH *) DevicePath;
|
|
Volume = NULL;
|
|
*NewHandle = NULL;
|
|
|
|
Status = gBS->LocateDevicePath (
|
|
&gEfiSimpleFileSystemProtocolGuid,
|
|
(EFI_DEVICE_PATH_PROTOCOL **) &FilePathNode,
|
|
&DeviceHandle
|
|
);
|
|
if (!EFI_ERROR (Status)) {
|
|
Status = gBS->HandleProtocol (DeviceHandle, &gEfiSimpleFileSystemProtocolGuid, (VOID **) &Volume);
|
|
}
|
|
|
|
if (EFI_ERROR (Status) || Volume == NULL) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
Status = Volume->OpenVolume (Volume, &FileHandle);
|
|
if (!EFI_ERROR (Status)) {
|
|
//
|
|
// Duplicate the device path to avoid the access to unaligned device path node.
|
|
// Because the device path consists of one or more FILE PATH MEDIA DEVICE PATH
|
|
// nodes, It assures the fields in device path nodes are 2 byte aligned.
|
|
//
|
|
FilePathNode = (FILEPATH_DEVICE_PATH *) DuplicateDevicePath (
|
|
(EFI_DEVICE_PATH_PROTOCOL *)(UINTN)FilePathNode
|
|
);
|
|
if (FilePathNode == NULL) {
|
|
FileHandle->Close (FileHandle);
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
} else {
|
|
OriginalFilePathNode = FilePathNode;
|
|
//
|
|
// Parse each MEDIA_FILEPATH_DP node. There may be more than one, since the
|
|
// directory information and filename can be seperate. The goal is to inch
|
|
// our way down each device path node and close the previous node
|
|
//
|
|
while (!IsDevicePathEnd (&FilePathNode->Header)) {
|
|
if (DevicePathType (&FilePathNode->Header) != MEDIA_DEVICE_PATH ||
|
|
DevicePathSubType (&FilePathNode->Header) != MEDIA_FILEPATH_DP) {
|
|
Status = EFI_UNSUPPORTED;
|
|
}
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
//
|
|
// Exit loop on Error
|
|
//
|
|
break;
|
|
}
|
|
|
|
LastHandle = FileHandle;
|
|
FileHandle = NULL;
|
|
Status = LastHandle->Open (
|
|
LastHandle,
|
|
&FileHandle,
|
|
FilePathNode->PathName,
|
|
OpenMode,
|
|
Attributes
|
|
);
|
|
|
|
//
|
|
// Close the previous node
|
|
//
|
|
LastHandle->Close (LastHandle);
|
|
FilePathNode = (FILEPATH_DEVICE_PATH *) NextDevicePathNode (&FilePathNode->Header);
|
|
}
|
|
//
|
|
// Free the allocated memory pool
|
|
//
|
|
FreePool (OriginalFilePathNode);
|
|
}
|
|
}
|
|
|
|
if (Status == EFI_SUCCESS) {
|
|
*NewHandle = FileHandle;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
VOID
|
|
SignalImageReturns (
|
|
VOID
|
|
)
|
|
{
|
|
EFI_HANDLE SignalHandle;
|
|
EFI_HANDLE *HandleBuffer;
|
|
UINTN NumberOfHandles;
|
|
EFI_STATUS Status;
|
|
|
|
Status = gBS->LocateHandleBuffer (
|
|
ByProtocol,
|
|
&gReturnFromImageGuid,
|
|
NULL,
|
|
&NumberOfHandles,
|
|
&HandleBuffer
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
SignalHandle = NULL;
|
|
Status = gBS->InstallProtocolInterface (
|
|
&SignalHandle,
|
|
&gReturnFromImageGuid,
|
|
EFI_NATIVE_INTERFACE,
|
|
NULL
|
|
);
|
|
} else {
|
|
Status = gBS->ReinstallProtocolInterface (
|
|
HandleBuffer[0],
|
|
&gReturnFromImageGuid,
|
|
NULL,
|
|
NULL
|
|
);
|
|
FreePool (HandleBuffer);
|
|
}
|
|
//
|
|
// Clear enumerate boot device flag to allow system re-enumerate all boot options.
|
|
// This action is to make sure all of device drivers are loaded in shell environment
|
|
// can be scanned if user returns from shell environment.
|
|
//
|
|
mGenericBdsLibGlobalData->EnumBootDevice = FALSE;
|
|
}
|
|
|