audk/IntelFrameworkModulePkg/Library/GenericBdsLib/BdsBoot.c

4357 lines
137 KiB
C

/** @file
BDS Lib functions which relate with create or process the boot option.
Copyright (c) 2004 - 2017, 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"
BOOLEAN mEnumBootDevice = FALSE;
EFI_HII_HANDLE gBdsLibStringPackHandle = NULL;
/**
The constructor function register UNI strings into imageHandle.
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
)
{
gBdsLibStringPackHandle = HiiAddPackages (
&gBdsLibStringPackageGuid,
ImageHandle,
GenericBdsLibStrings,
NULL
);
ASSERT (gBdsLibStringPackHandle != NULL);
return EFI_SUCCESS;
}
/**
Deletete the Boot Option from EFI Variable. The Boot Order Arrray
is also updated.
@param OptionNumber The number of Boot option want to be deleted.
@param BootOrder The Boot Order array.
@param BootOrderSize The size of the Boot Order Array.
@retval EFI_SUCCESS The Boot Option Variable was found and removed
@retval EFI_UNSUPPORTED The Boot Option Variable store was inaccessible
@retval EFI_NOT_FOUND The Boot Option Variable was not found
**/
EFI_STATUS
EFIAPI
BdsDeleteBootOption (
IN UINTN OptionNumber,
IN OUT UINT16 *BootOrder,
IN OUT UINTN *BootOrderSize
)
{
CHAR16 BootOption[9];
UINTN Index;
EFI_STATUS Status;
UnicodeSPrint (BootOption, sizeof (BootOption), L"Boot%04x", OptionNumber);
Status = gRT->SetVariable (
BootOption,
&gEfiGlobalVariableGuid,
0,
0,
NULL
);
//
// Deleting variable with existing variable implementation shouldn't fail.
//
ASSERT_EFI_ERROR (Status);
//
// adjust boot order array
//
for (Index = 0; Index < *BootOrderSize / sizeof (UINT16); Index++) {
if (BootOrder[Index] == OptionNumber) {
CopyMem (&BootOrder[Index], &BootOrder[Index+1], *BootOrderSize - (Index+1) * sizeof (UINT16));
*BootOrderSize -= sizeof (UINT16);
break;
}
}
return Status;
}
/**
Translate the first n characters of an Ascii string to
Unicode characters. The count n is indicated by parameter
Size. If Size is greater than the length of string, then
the entire string is translated.
@param AStr Pointer to input Ascii string.
@param Size The number of characters to translate.
@param UStr Pointer to output Unicode string buffer.
**/
VOID
AsciiToUnicodeSize (
IN UINT8 *AStr,
IN UINTN Size,
OUT UINT16 *UStr
)
{
UINTN Idx;
Idx = 0;
while (AStr[Idx] != 0) {
UStr[Idx] = (CHAR16) AStr[Idx];
if (Idx == Size) {
break;
}
Idx++;
}
UStr[Idx] = 0;
}
/**
Build Legacy Device Name String according.
@param CurBBSEntry BBS Table.
@param Index Index.
@param BufSize The buffer size.
@param BootString The output string.
**/
VOID
BdsBuildLegacyDevNameString (
IN BBS_TABLE *CurBBSEntry,
IN UINTN Index,
IN UINTN BufSize,
OUT CHAR16 *BootString
)
{
CHAR16 *Fmt;
CHAR16 *Type;
UINT8 *StringDesc;
CHAR16 Temp[80];
switch (Index) {
//
// Primary Master
//
case 1:
Fmt = L"Primary Master %s";
break;
//
// Primary Slave
//
case 2:
Fmt = L"Primary Slave %s";
break;
//
// Secondary Master
//
case 3:
Fmt = L"Secondary Master %s";
break;
//
// Secondary Slave
//
case 4:
Fmt = L"Secondary Slave %s";
break;
default:
Fmt = L"%s";
break;
}
switch (CurBBSEntry->DeviceType) {
case BBS_FLOPPY:
Type = L"Floppy";
break;
case BBS_HARDDISK:
Type = L"Harddisk";
break;
case BBS_CDROM:
Type = L"CDROM";
break;
case BBS_PCMCIA:
Type = L"PCMCIAe";
break;
case BBS_USB:
Type = L"USB";
break;
case BBS_EMBED_NETWORK:
Type = L"Network";
break;
case BBS_BEV_DEVICE:
Type = L"BEVe";
break;
case BBS_UNKNOWN:
default:
Type = L"Unknown";
break;
}
//
// If current BBS entry has its description then use it.
//
StringDesc = (UINT8 *) (((UINTN) CurBBSEntry->DescStringSegment << 4) + CurBBSEntry->DescStringOffset);
if (NULL != StringDesc) {
//
// Only get fisrt 32 characters, this is suggested by BBS spec
//
AsciiToUnicodeSize (StringDesc, 32, Temp);
Fmt = L"%s";
Type = Temp;
}
//
// BbsTable 16 entries are for onboard IDE.
// Set description string for SATA harddisks, Harddisk 0 ~ Harddisk 11
//
if (Index >= 5 && Index <= 16 && (CurBBSEntry->DeviceType == BBS_HARDDISK || CurBBSEntry->DeviceType == BBS_CDROM)) {
Fmt = L"%s %d";
UnicodeSPrint (BootString, BufSize, Fmt, Type, Index - 5);
} else {
UnicodeSPrint (BootString, BufSize, Fmt, Type);
}
}
/**
Create a legacy boot option for the specified entry of
BBS table, save it as variable, and append it to the boot
order list.
@param CurrentBbsEntry Pointer to current BBS table.
@param CurrentBbsDevPath Pointer to the Device Path Protocol instance of BBS
@param Index Index of the specified entry in BBS table.
@param BootOrderList On input, the original boot order list.
On output, the new boot order list attached with the
created node.
@param BootOrderListSize On input, the original size of boot order list.
On output, the size of new boot order list.
@retval EFI_SUCCESS Boot Option successfully created.
@retval EFI_OUT_OF_RESOURCES Fail to allocate necessary memory.
@retval Other Error occurs while setting variable.
**/
EFI_STATUS
BdsCreateLegacyBootOption (
IN BBS_TABLE *CurrentBbsEntry,
IN EFI_DEVICE_PATH_PROTOCOL *CurrentBbsDevPath,
IN UINTN Index,
IN OUT UINT16 **BootOrderList,
IN OUT UINTN *BootOrderListSize
)
{
EFI_STATUS Status;
UINT16 CurrentBootOptionNo;
UINT16 BootString[10];
CHAR16 BootDesc[100];
CHAR8 HelpString[100];
UINT16 *NewBootOrderList;
UINTN BufferSize;
UINTN StringLen;
VOID *Buffer;
UINT8 *Ptr;
UINT16 CurrentBbsDevPathSize;
UINTN BootOrderIndex;
UINTN BootOrderLastIndex;
UINTN ArrayIndex;
BOOLEAN IndexNotFound;
BBS_BBS_DEVICE_PATH *NewBbsDevPathNode;
if ((*BootOrderList) == NULL) {
CurrentBootOptionNo = 0;
} else {
for (ArrayIndex = 0; ArrayIndex < (UINTN) (*BootOrderListSize / sizeof (UINT16)); ArrayIndex++) {
IndexNotFound = TRUE;
for (BootOrderIndex = 0; BootOrderIndex < (UINTN) (*BootOrderListSize / sizeof (UINT16)); BootOrderIndex++) {
if ((*BootOrderList)[BootOrderIndex] == ArrayIndex) {
IndexNotFound = FALSE;
break;
}
}
if (!IndexNotFound) {
continue;
} else {
break;
}
}
CurrentBootOptionNo = (UINT16) ArrayIndex;
}
UnicodeSPrint (
BootString,
sizeof (BootString),
L"Boot%04x",
CurrentBootOptionNo
);
BdsBuildLegacyDevNameString (CurrentBbsEntry, Index, sizeof (BootDesc), BootDesc);
//
// Create new BBS device path node with description string
//
UnicodeStrToAsciiStrS (BootDesc, HelpString, sizeof (HelpString));
StringLen = AsciiStrLen (HelpString);
NewBbsDevPathNode = AllocateZeroPool (sizeof (BBS_BBS_DEVICE_PATH) + StringLen);
if (NewBbsDevPathNode == NULL) {
return EFI_OUT_OF_RESOURCES;
}
CopyMem (NewBbsDevPathNode, CurrentBbsDevPath, sizeof (BBS_BBS_DEVICE_PATH));
CopyMem (NewBbsDevPathNode->String, HelpString, StringLen + 1);
SetDevicePathNodeLength (&(NewBbsDevPathNode->Header), sizeof (BBS_BBS_DEVICE_PATH) + StringLen);
//
// Create entire new CurrentBbsDevPath with end node
//
CurrentBbsDevPath = AppendDevicePathNode (
NULL,
(EFI_DEVICE_PATH_PROTOCOL *) NewBbsDevPathNode
);
if (CurrentBbsDevPath == NULL) {
FreePool (NewBbsDevPathNode);
return EFI_OUT_OF_RESOURCES;
}
CurrentBbsDevPathSize = (UINT16) (GetDevicePathSize (CurrentBbsDevPath));
BufferSize = sizeof (UINT32) +
sizeof (UINT16) +
StrSize (BootDesc) +
CurrentBbsDevPathSize +
sizeof (BBS_TABLE) +
sizeof (UINT16);
Buffer = AllocateZeroPool (BufferSize);
if (Buffer == NULL) {
FreePool (NewBbsDevPathNode);
FreePool (CurrentBbsDevPath);
return EFI_OUT_OF_RESOURCES;
}
Ptr = (UINT8 *) Buffer;
*((UINT32 *) Ptr) = LOAD_OPTION_ACTIVE;
Ptr += sizeof (UINT32);
*((UINT16 *) Ptr) = CurrentBbsDevPathSize;
Ptr += sizeof (UINT16);
CopyMem (
Ptr,
BootDesc,
StrSize (BootDesc)
);
Ptr += StrSize (BootDesc);
CopyMem (
Ptr,
CurrentBbsDevPath,
CurrentBbsDevPathSize
);
Ptr += CurrentBbsDevPathSize;
CopyMem (
Ptr,
CurrentBbsEntry,
sizeof (BBS_TABLE)
);
Ptr += sizeof (BBS_TABLE);
*((UINT16 *) Ptr) = (UINT16) Index;
Status = gRT->SetVariable (
BootString,
&gEfiGlobalVariableGuid,
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
BufferSize,
Buffer
);
FreePool (Buffer);
Buffer = NULL;
NewBootOrderList = AllocateZeroPool (*BootOrderListSize + sizeof (UINT16));
if (NULL == NewBootOrderList) {
FreePool (NewBbsDevPathNode);
FreePool (CurrentBbsDevPath);
return EFI_OUT_OF_RESOURCES;
}
if (*BootOrderList != NULL) {
CopyMem (NewBootOrderList, *BootOrderList, *BootOrderListSize);
FreePool (*BootOrderList);
}
BootOrderLastIndex = (UINTN) (*BootOrderListSize / sizeof (UINT16));
NewBootOrderList[BootOrderLastIndex] = CurrentBootOptionNo;
*BootOrderListSize += sizeof (UINT16);
*BootOrderList = NewBootOrderList;
FreePool (NewBbsDevPathNode);
FreePool (CurrentBbsDevPath);
return Status;
}
/**
Check if the boot option is a legacy one.
@param BootOptionVar The boot option data payload.
@param BbsEntry The BBS Table.
@param BbsIndex The table index.
@retval TRUE It is a legacy boot option.
@retval FALSE It is not a legacy boot option.
**/
BOOLEAN
BdsIsLegacyBootOption (
IN UINT8 *BootOptionVar,
OUT BBS_TABLE **BbsEntry,
OUT UINT16 *BbsIndex
)
{
UINT8 *Ptr;
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
BOOLEAN Ret;
UINT16 DevPathLen;
Ptr = BootOptionVar;
Ptr += sizeof (UINT32);
DevPathLen = *(UINT16 *) Ptr;
Ptr += sizeof (UINT16);
Ptr += StrSize ((UINT16 *) Ptr);
DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) Ptr;
if ((BBS_DEVICE_PATH == DevicePath->Type) && (BBS_BBS_DP == DevicePath->SubType)) {
Ptr += DevPathLen;
*BbsEntry = (BBS_TABLE *) Ptr;
Ptr += sizeof (BBS_TABLE);
*BbsIndex = *(UINT16 *) Ptr;
Ret = TRUE;
} else {
*BbsEntry = NULL;
Ret = FALSE;
}
return Ret;
}
/**
Delete all the invalid legacy boot options.
@retval EFI_SUCCESS All invalide legacy boot options are deleted.
@retval EFI_OUT_OF_RESOURCES Fail to allocate necessary memory.
@retval EFI_NOT_FOUND Fail to retrive variable of boot order.
**/
EFI_STATUS
EFIAPI
BdsDeleteAllInvalidLegacyBootOptions (
VOID
)
{
UINT16 *BootOrder;
UINT8 *BootOptionVar;
UINTN BootOrderSize;
UINTN BootOptionSize;
EFI_STATUS Status;
UINT16 HddCount;
UINT16 BbsCount;
HDD_INFO *LocalHddInfo;
BBS_TABLE *LocalBbsTable;
BBS_TABLE *BbsEntry;
UINT16 BbsIndex;
EFI_LEGACY_BIOS_PROTOCOL *LegacyBios;
UINTN Index;
UINT16 BootOption[10];
UINT16 BootDesc[100];
BOOLEAN DescStringMatch;
Status = EFI_SUCCESS;
BootOrder = NULL;
BootOrderSize = 0;
HddCount = 0;
BbsCount = 0;
LocalHddInfo = NULL;
LocalBbsTable = NULL;
BbsEntry = NULL;
Status = gBS->LocateProtocol (&gEfiLegacyBiosProtocolGuid, NULL, (VOID **) &LegacyBios);
if (EFI_ERROR (Status)) {
return Status;
}
BootOrder = BdsLibGetVariableAndSize (
L"BootOrder",
&gEfiGlobalVariableGuid,
&BootOrderSize
);
if (BootOrder == NULL) {
return EFI_NOT_FOUND;
}
LegacyBios->GetBbsInfo (
LegacyBios,
&HddCount,
&LocalHddInfo,
&BbsCount,
&LocalBbsTable
);
Index = 0;
while (Index < BootOrderSize / sizeof (UINT16)) {
UnicodeSPrint (BootOption, sizeof (BootOption), L"Boot%04x", BootOrder[Index]);
BootOptionVar = BdsLibGetVariableAndSize (
BootOption,
&gEfiGlobalVariableGuid,
&BootOptionSize
);
if (NULL == BootOptionVar) {
BootOptionSize = 0;
Status = gRT->GetVariable (
BootOption,
&gEfiGlobalVariableGuid,
NULL,
&BootOptionSize,
BootOptionVar
);
if (Status == EFI_NOT_FOUND) {
//
// Update BootOrder
//
BdsDeleteBootOption (
BootOrder[Index],
BootOrder,
&BootOrderSize
);
continue;
} else {
FreePool (BootOrder);
return EFI_OUT_OF_RESOURCES;
}
}
//
// Skip Non-Legacy boot option
//
if (!BdsIsLegacyBootOption (BootOptionVar, &BbsEntry, &BbsIndex)) {
if (BootOptionVar!= NULL) {
FreePool (BootOptionVar);
}
Index++;
continue;
}
if (BbsIndex < BbsCount) {
//
// Check if BBS Description String is changed
//
DescStringMatch = FALSE;
BdsBuildLegacyDevNameString (
&LocalBbsTable[BbsIndex],
BbsIndex,
sizeof (BootDesc),
BootDesc
);
if (StrCmp (BootDesc, (UINT16*)(BootOptionVar + sizeof (UINT32) + sizeof (UINT16))) == 0) {
DescStringMatch = TRUE;
}
if (!((LocalBbsTable[BbsIndex].BootPriority == BBS_IGNORE_ENTRY) ||
(LocalBbsTable[BbsIndex].BootPriority == BBS_DO_NOT_BOOT_FROM)) &&
(LocalBbsTable[BbsIndex].DeviceType == BbsEntry->DeviceType) &&
DescStringMatch) {
Index++;
continue;
}
}
if (BootOptionVar != NULL) {
FreePool (BootOptionVar);
}
//
// should delete
//
BdsDeleteBootOption (
BootOrder[Index],
BootOrder,
&BootOrderSize
);
}
//
// Adjust the number of boot options.
//
Status = gRT->SetVariable (
L"BootOrder",
&gEfiGlobalVariableGuid,
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
BootOrderSize,
BootOrder
);
//
// Shrinking variable with existing variable implementation shouldn't fail.
//
ASSERT_EFI_ERROR (Status);
FreePool (BootOrder);
return Status;
}
/**
Find all legacy boot option by device type.
@param BootOrder The boot order array.
@param BootOptionNum The number of boot option.
@param DevType Device type.
@param DevName Device name.
@param Attribute The boot option attribute.
@param BbsIndex The BBS table index.
@param OptionNumber The boot option index.
@retval TRUE The Legacy boot option is found.
@retval FALSE The legacy boot option is not found.
**/
BOOLEAN
BdsFindLegacyBootOptionByDevTypeAndName (
IN UINT16 *BootOrder,
IN UINTN BootOptionNum,
IN UINT16 DevType,
IN CHAR16 *DevName,
OUT UINT32 *Attribute,
OUT UINT16 *BbsIndex,
OUT UINT16 *OptionNumber
)
{
UINTN Index;
CHAR16 BootOption[9];
UINTN BootOptionSize;
UINT8 *BootOptionVar;
BBS_TABLE *BbsEntry;
BOOLEAN Found;
BbsEntry = NULL;
Found = FALSE;
if (NULL == BootOrder) {
return Found;
}
//
// Loop all boot option from variable
//
for (Index = 0; Index < BootOptionNum; Index++) {
UnicodeSPrint (BootOption, sizeof (BootOption), L"Boot%04x", (UINTN) BootOrder[Index]);
BootOptionVar = BdsLibGetVariableAndSize (
BootOption,
&gEfiGlobalVariableGuid,
&BootOptionSize
);
if (NULL == BootOptionVar) {
continue;
}
//
// Skip Non-legacy boot option
//
if (!BdsIsLegacyBootOption (BootOptionVar, &BbsEntry, BbsIndex)) {
FreePool (BootOptionVar);
continue;
}
if (
(BbsEntry->DeviceType != DevType) ||
(StrCmp (DevName, (CHAR16*)(BootOptionVar + sizeof (UINT32) + sizeof (UINT16))) != 0)
) {
FreePool (BootOptionVar);
continue;
}
*Attribute = *(UINT32 *) BootOptionVar;
*OptionNumber = BootOrder[Index];
Found = TRUE;
FreePool (BootOptionVar);
break;
}
return Found;
}
/**
Create a legacy boot option.
@param BbsItem The BBS Table entry.
@param Index Index of the specified entry in BBS table.
@param BootOrderList The boot order list.
@param BootOrderListSize The size of boot order list.
@retval EFI_OUT_OF_RESOURCE No enough memory.
@retval EFI_SUCCESS The function complete successfully.
@return Other value if the legacy boot option is not created.
**/
EFI_STATUS
BdsCreateOneLegacyBootOption (
IN BBS_TABLE *BbsItem,
IN UINTN Index,
IN OUT UINT16 **BootOrderList,
IN OUT UINTN *BootOrderListSize
)
{
BBS_BBS_DEVICE_PATH BbsDevPathNode;
EFI_STATUS Status;
EFI_DEVICE_PATH_PROTOCOL *DevPath;
DevPath = NULL;
//
// Create device path node.
//
BbsDevPathNode.Header.Type = BBS_DEVICE_PATH;
BbsDevPathNode.Header.SubType = BBS_BBS_DP;
SetDevicePathNodeLength (&BbsDevPathNode.Header, sizeof (BBS_BBS_DEVICE_PATH));
BbsDevPathNode.DeviceType = BbsItem->DeviceType;
CopyMem (&BbsDevPathNode.StatusFlag, &BbsItem->StatusFlags, sizeof (UINT16));
DevPath = AppendDevicePathNode (
NULL,
(EFI_DEVICE_PATH_PROTOCOL *) &BbsDevPathNode
);
if (NULL == DevPath) {
return EFI_OUT_OF_RESOURCES;
}
Status = BdsCreateLegacyBootOption (
BbsItem,
DevPath,
Index,
BootOrderList,
BootOrderListSize
);
BbsItem->BootPriority = 0x00;
FreePool (DevPath);
return Status;
}
/**
Add the legacy boot options from BBS table if they do not exist.
@retval EFI_SUCCESS The boot options are added successfully
or they are already in boot options.
@retval EFI_NOT_FOUND No legacy boot options is found.
@retval EFI_OUT_OF_RESOURCE No enough memory.
@return Other value LegacyBoot options are not added.
**/
EFI_STATUS
EFIAPI
BdsAddNonExistingLegacyBootOptions (
VOID
)
{
UINT16 *BootOrder;
UINTN BootOrderSize;
EFI_STATUS Status;
CHAR16 Desc[100];
UINT16 HddCount;
UINT16 BbsCount;
HDD_INFO *LocalHddInfo;
BBS_TABLE *LocalBbsTable;
UINT16 BbsIndex;
EFI_LEGACY_BIOS_PROTOCOL *LegacyBios;
UINT16 Index;
UINT32 Attribute;
UINT16 OptionNumber;
BOOLEAN Exist;
HddCount = 0;
BbsCount = 0;
LocalHddInfo = NULL;
LocalBbsTable = NULL;
Status = gBS->LocateProtocol (&gEfiLegacyBiosProtocolGuid, NULL, (VOID **) &LegacyBios);
if (EFI_ERROR (Status)) {
return Status;
}
LegacyBios->GetBbsInfo (
LegacyBios,
&HddCount,
&LocalHddInfo,
&BbsCount,
&LocalBbsTable
);
BootOrder = BdsLibGetVariableAndSize (
L"BootOrder",
&gEfiGlobalVariableGuid,
&BootOrderSize
);
if (BootOrder == NULL) {
BootOrderSize = 0;
}
for (Index = 0; Index < BbsCount; Index++) {
if ((LocalBbsTable[Index].BootPriority == BBS_IGNORE_ENTRY) ||
(LocalBbsTable[Index].BootPriority == BBS_DO_NOT_BOOT_FROM)
) {
continue;
}
BdsBuildLegacyDevNameString (&LocalBbsTable[Index], Index, sizeof (Desc), Desc);
Exist = BdsFindLegacyBootOptionByDevTypeAndName (
BootOrder,
BootOrderSize / sizeof (UINT16),
LocalBbsTable[Index].DeviceType,
Desc,
&Attribute,
&BbsIndex,
&OptionNumber
);
if (!Exist) {
//
// Not found such type of legacy device in boot options or we found but it's disabled
// so we have to create one and put it to the tail of boot order list
//
Status = BdsCreateOneLegacyBootOption (
&LocalBbsTable[Index],
Index,
&BootOrder,
&BootOrderSize
);
if (!EFI_ERROR (Status)) {
ASSERT (BootOrder != NULL);
BbsIndex = Index;
OptionNumber = BootOrder[BootOrderSize / sizeof (UINT16) - 1];
}
}
ASSERT (BbsIndex == Index);
}
Status = gRT->SetVariable (
L"BootOrder",
&gEfiGlobalVariableGuid,
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
BootOrderSize,
BootOrder
);
if (BootOrder != NULL) {
FreePool (BootOrder);
}
return Status;
}
/**
Fill the device order buffer.
@param BbsTable The BBS table.
@param BbsType The BBS Type.
@param BbsCount The BBS Count.
@param Buf device order buffer.
@return The device order buffer.
**/
UINT16 *
BdsFillDevOrderBuf (
IN BBS_TABLE *BbsTable,
IN BBS_TYPE BbsType,
IN UINTN BbsCount,
OUT UINT16 *Buf
)
{
UINTN Index;
for (Index = 0; Index < BbsCount; Index++) {
if (BbsTable[Index].BootPriority == BBS_IGNORE_ENTRY) {
continue;
}
if (BbsTable[Index].DeviceType != BbsType) {
continue;
}
*Buf = (UINT16) (Index & 0xFF);
Buf++;
}
return Buf;
}
/**
Create the device order buffer.
@param BbsTable The BBS table.
@param BbsCount The BBS Count.
@retval EFI_SUCCES The buffer is created and the EFI variable named
VAR_LEGACY_DEV_ORDER and gEfiLegacyDevOrderVariableGuid is
set correctly.
@retval EFI_OUT_OF_RESOURCES Memmory or storage is not enough.
@retval EFI_DEVICE_ERROR Fail to add the device order into EFI variable fail
because of hardware error.
**/
EFI_STATUS
BdsCreateDevOrder (
IN BBS_TABLE *BbsTable,
IN UINT16 BbsCount
)
{
UINTN Index;
UINTN FDCount;
UINTN HDCount;
UINTN CDCount;
UINTN NETCount;
UINTN BEVCount;
UINTN TotalSize;
UINTN HeaderSize;
LEGACY_DEV_ORDER_ENTRY *DevOrder;
LEGACY_DEV_ORDER_ENTRY *DevOrderPtr;
EFI_STATUS Status;
FDCount = 0;
HDCount = 0;
CDCount = 0;
NETCount = 0;
BEVCount = 0;
TotalSize = 0;
HeaderSize = sizeof (BBS_TYPE) + sizeof (UINT16);
DevOrder = NULL;
Status = EFI_SUCCESS;
//
// Count all boot devices
//
for (Index = 0; Index < BbsCount; Index++) {
if (BbsTable[Index].BootPriority == BBS_IGNORE_ENTRY) {
continue;
}
switch (BbsTable[Index].DeviceType) {
case BBS_FLOPPY:
FDCount++;
break;
case BBS_HARDDISK:
HDCount++;
break;
case BBS_CDROM:
CDCount++;
break;
case BBS_EMBED_NETWORK:
NETCount++;
break;
case BBS_BEV_DEVICE:
BEVCount++;
break;
default:
break;
}
}
TotalSize += (HeaderSize + sizeof (UINT16) * FDCount);
TotalSize += (HeaderSize + sizeof (UINT16) * HDCount);
TotalSize += (HeaderSize + sizeof (UINT16) * CDCount);
TotalSize += (HeaderSize + sizeof (UINT16) * NETCount);
TotalSize += (HeaderSize + sizeof (UINT16) * BEVCount);
//
// Create buffer to hold all boot device order
//
DevOrder = AllocateZeroPool (TotalSize);
if (NULL == DevOrder) {
return EFI_OUT_OF_RESOURCES;
}
DevOrderPtr = DevOrder;
DevOrderPtr->BbsType = BBS_FLOPPY;
DevOrderPtr->Length = (UINT16) (sizeof (DevOrderPtr->Length) + FDCount * sizeof (UINT16));
DevOrderPtr = (LEGACY_DEV_ORDER_ENTRY *) BdsFillDevOrderBuf (BbsTable, BBS_FLOPPY, BbsCount, DevOrderPtr->Data);
DevOrderPtr->BbsType = BBS_HARDDISK;
DevOrderPtr->Length = (UINT16) (sizeof (UINT16) + HDCount * sizeof (UINT16));
DevOrderPtr = (LEGACY_DEV_ORDER_ENTRY *) BdsFillDevOrderBuf (BbsTable, BBS_HARDDISK, BbsCount, DevOrderPtr->Data);
DevOrderPtr->BbsType = BBS_CDROM;
DevOrderPtr->Length = (UINT16) (sizeof (UINT16) + CDCount * sizeof (UINT16));
DevOrderPtr = (LEGACY_DEV_ORDER_ENTRY *) BdsFillDevOrderBuf (BbsTable, BBS_CDROM, BbsCount, DevOrderPtr->Data);
DevOrderPtr->BbsType = BBS_EMBED_NETWORK;
DevOrderPtr->Length = (UINT16) (sizeof (UINT16) + NETCount * sizeof (UINT16));
DevOrderPtr = (LEGACY_DEV_ORDER_ENTRY *) BdsFillDevOrderBuf (BbsTable, BBS_EMBED_NETWORK, BbsCount, DevOrderPtr->Data);
DevOrderPtr->BbsType = BBS_BEV_DEVICE;
DevOrderPtr->Length = (UINT16) (sizeof (UINT16) + BEVCount * sizeof (UINT16));
DevOrderPtr = (LEGACY_DEV_ORDER_ENTRY *) BdsFillDevOrderBuf (BbsTable, BBS_BEV_DEVICE, BbsCount, DevOrderPtr->Data);
ASSERT (TotalSize == ((UINTN) DevOrderPtr - (UINTN) DevOrder));
//
// Save device order for legacy boot device to variable.
//
Status = gRT->SetVariable (
VAR_LEGACY_DEV_ORDER,
&gEfiLegacyDevOrderVariableGuid,
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,
TotalSize,
DevOrder
);
FreePool (DevOrder);
return Status;
}
/**
Add the legacy boot devices from BBS table into
the legacy device boot order.
@retval EFI_SUCCESS The boot devices are added successfully.
@retval EFI_NOT_FOUND The legacy boot devices are not found.
@retval EFI_OUT_OF_RESOURCES Memmory or storage is not enough.
@retval EFI_DEVICE_ERROR Fail to add the legacy device boot order into EFI variable
because of hardware error.
**/
EFI_STATUS
EFIAPI
BdsUpdateLegacyDevOrder (
VOID
)
{
LEGACY_DEV_ORDER_ENTRY *DevOrder;
LEGACY_DEV_ORDER_ENTRY *NewDevOrder;
LEGACY_DEV_ORDER_ENTRY *Ptr;
LEGACY_DEV_ORDER_ENTRY *NewPtr;
UINTN DevOrderSize;
EFI_LEGACY_BIOS_PROTOCOL *LegacyBios;
EFI_STATUS Status;
UINT16 HddCount;
UINT16 BbsCount;
HDD_INFO *LocalHddInfo;
BBS_TABLE *LocalBbsTable;
UINTN Index;
UINTN Index2;
UINTN *Idx;
UINTN FDCount;
UINTN HDCount;
UINTN CDCount;
UINTN NETCount;
UINTN BEVCount;
UINTN TotalSize;
UINTN HeaderSize;
UINT16 *NewFDPtr;
UINT16 *NewHDPtr;
UINT16 *NewCDPtr;
UINT16 *NewNETPtr;
UINT16 *NewBEVPtr;
UINT16 *NewDevPtr;
UINTN FDIndex;
UINTN HDIndex;
UINTN CDIndex;
UINTN NETIndex;
UINTN BEVIndex;
Idx = NULL;
FDCount = 0;
HDCount = 0;
CDCount = 0;
NETCount = 0;
BEVCount = 0;
TotalSize = 0;
HeaderSize = sizeof (BBS_TYPE) + sizeof (UINT16);
FDIndex = 0;
HDIndex = 0;
CDIndex = 0;
NETIndex = 0;
BEVIndex = 0;
NewDevPtr = NULL;
Status = gBS->LocateProtocol (&gEfiLegacyBiosProtocolGuid, NULL, (VOID **) &LegacyBios);
if (EFI_ERROR (Status)) {
return Status;
}
Status = LegacyBios->GetBbsInfo (
LegacyBios,
&HddCount,
&LocalHddInfo,
&BbsCount,
&LocalBbsTable
);
if (EFI_ERROR (Status)) {
return Status;
}
DevOrder = BdsLibGetVariableAndSize (
VAR_LEGACY_DEV_ORDER,
&gEfiLegacyDevOrderVariableGuid,
&DevOrderSize
);
if (NULL == DevOrder) {
return BdsCreateDevOrder (LocalBbsTable, BbsCount);
}
//
// First we figure out how many boot devices with same device type respectively
//
for (Index = 0; Index < BbsCount; Index++) {
if ((LocalBbsTable[Index].BootPriority == BBS_IGNORE_ENTRY) ||
(LocalBbsTable[Index].BootPriority == BBS_DO_NOT_BOOT_FROM)
) {
continue;
}
switch (LocalBbsTable[Index].DeviceType) {
case BBS_FLOPPY:
FDCount++;
break;
case BBS_HARDDISK:
HDCount++;
break;
case BBS_CDROM:
CDCount++;
break;
case BBS_EMBED_NETWORK:
NETCount++;
break;
case BBS_BEV_DEVICE:
BEVCount++;
break;
default:
break;
}
}
TotalSize += (HeaderSize + FDCount * sizeof (UINT16));
TotalSize += (HeaderSize + HDCount * sizeof (UINT16));
TotalSize += (HeaderSize + CDCount * sizeof (UINT16));
TotalSize += (HeaderSize + NETCount * sizeof (UINT16));
TotalSize += (HeaderSize + BEVCount * sizeof (UINT16));
NewDevOrder = AllocateZeroPool (TotalSize);
if (NULL == NewDevOrder) {
return EFI_OUT_OF_RESOURCES;
}
//
// copy FD
//
Ptr = DevOrder;
NewPtr = NewDevOrder;
NewPtr->BbsType = Ptr->BbsType;
NewPtr->Length = (UINT16) (sizeof (UINT16) + FDCount * sizeof (UINT16));
for (Index = 0; Index < Ptr->Length / sizeof (UINT16) - 1; Index++) {
if (LocalBbsTable[Ptr->Data[Index] & 0xFF].BootPriority == BBS_IGNORE_ENTRY ||
LocalBbsTable[Ptr->Data[Index] & 0xFF].BootPriority == BBS_DO_NOT_BOOT_FROM ||
LocalBbsTable[Ptr->Data[Index] & 0xFF].DeviceType != BBS_FLOPPY
) {
continue;
}
NewPtr->Data[FDIndex] = Ptr->Data[Index];
FDIndex++;
}
NewFDPtr = NewPtr->Data;
//
// copy HD
//
Ptr = (LEGACY_DEV_ORDER_ENTRY *) (&Ptr->Data[Ptr->Length / sizeof (UINT16) - 1]);
NewPtr = (LEGACY_DEV_ORDER_ENTRY *) (&NewPtr->Data[NewPtr->Length / sizeof (UINT16) -1]);
NewPtr->BbsType = Ptr->BbsType;
NewPtr->Length = (UINT16) (sizeof (UINT16) + HDCount * sizeof (UINT16));
for (Index = 0; Index < Ptr->Length / sizeof (UINT16) - 1; Index++) {
if (LocalBbsTable[Ptr->Data[Index] & 0xFF].BootPriority == BBS_IGNORE_ENTRY ||
LocalBbsTable[Ptr->Data[Index] & 0xFF].BootPriority == BBS_DO_NOT_BOOT_FROM ||
LocalBbsTable[Ptr->Data[Index] & 0xFF].BootPriority == BBS_LOWEST_PRIORITY ||
LocalBbsTable[Ptr->Data[Index] & 0xFF].DeviceType != BBS_HARDDISK
) {
continue;
}
NewPtr->Data[HDIndex] = Ptr->Data[Index];
HDIndex++;
}
NewHDPtr = NewPtr->Data;
//
// copy CD
//
Ptr = (LEGACY_DEV_ORDER_ENTRY *) (&Ptr->Data[Ptr->Length / sizeof (UINT16) - 1]);
NewPtr = (LEGACY_DEV_ORDER_ENTRY *) (&NewPtr->Data[NewPtr->Length / sizeof (UINT16) -1]);
NewPtr->BbsType = Ptr->BbsType;
NewPtr->Length = (UINT16) (sizeof (UINT16) + CDCount * sizeof (UINT16));
for (Index = 0; Index < Ptr->Length / sizeof (UINT16) - 1; Index++) {
if (LocalBbsTable[Ptr->Data[Index] & 0xFF].BootPriority == BBS_IGNORE_ENTRY ||
LocalBbsTable[Ptr->Data[Index] & 0xFF].BootPriority == BBS_DO_NOT_BOOT_FROM ||
LocalBbsTable[Ptr->Data[Index] & 0xFF].BootPriority == BBS_LOWEST_PRIORITY ||
LocalBbsTable[Ptr->Data[Index] & 0xFF].DeviceType != BBS_CDROM
) {
continue;
}
NewPtr->Data[CDIndex] = Ptr->Data[Index];
CDIndex++;
}
NewCDPtr = NewPtr->Data;
//
// copy NET
//
Ptr = (LEGACY_DEV_ORDER_ENTRY *) (&Ptr->Data[Ptr->Length / sizeof (UINT16) - 1]);
NewPtr = (LEGACY_DEV_ORDER_ENTRY *) (&NewPtr->Data[NewPtr->Length / sizeof (UINT16) -1]);
NewPtr->BbsType = Ptr->BbsType;
NewPtr->Length = (UINT16) (sizeof (UINT16) + NETCount * sizeof (UINT16));
for (Index = 0; Index < Ptr->Length / sizeof (UINT16) - 1; Index++) {
if (LocalBbsTable[Ptr->Data[Index] & 0xFF].BootPriority == BBS_IGNORE_ENTRY ||
LocalBbsTable[Ptr->Data[Index] & 0xFF].BootPriority == BBS_DO_NOT_BOOT_FROM ||
LocalBbsTable[Ptr->Data[Index] & 0xFF].BootPriority == BBS_LOWEST_PRIORITY ||
LocalBbsTable[Ptr->Data[Index] & 0xFF].DeviceType != BBS_EMBED_NETWORK
) {
continue;
}
NewPtr->Data[NETIndex] = Ptr->Data[Index];
NETIndex++;
}
NewNETPtr = NewPtr->Data;
//
// copy BEV
//
Ptr = (LEGACY_DEV_ORDER_ENTRY *) (&Ptr->Data[Ptr->Length / sizeof (UINT16) - 1]);
NewPtr = (LEGACY_DEV_ORDER_ENTRY *) (&NewPtr->Data[NewPtr->Length / sizeof (UINT16) -1]);
NewPtr->BbsType = Ptr->BbsType;
NewPtr->Length = (UINT16) (sizeof (UINT16) + BEVCount * sizeof (UINT16));
for (Index = 0; Index < Ptr->Length / sizeof (UINT16) - 1; Index++) {
if (LocalBbsTable[Ptr->Data[Index] & 0xFF].BootPriority == BBS_IGNORE_ENTRY ||
LocalBbsTable[Ptr->Data[Index] & 0xFF].BootPriority == BBS_DO_NOT_BOOT_FROM ||
LocalBbsTable[Ptr->Data[Index] & 0xFF].BootPriority == BBS_LOWEST_PRIORITY ||
LocalBbsTable[Ptr->Data[Index] & 0xFF].DeviceType != BBS_BEV_DEVICE
) {
continue;
}
NewPtr->Data[BEVIndex] = Ptr->Data[Index];
BEVIndex++;
}
NewBEVPtr = NewPtr->Data;
for (Index = 0; Index < BbsCount; Index++) {
if ((LocalBbsTable[Index].BootPriority == BBS_IGNORE_ENTRY) ||
(LocalBbsTable[Index].BootPriority == BBS_DO_NOT_BOOT_FROM)
) {
continue;
}
switch (LocalBbsTable[Index].DeviceType) {
case BBS_FLOPPY:
Idx = &FDIndex;
NewDevPtr = NewFDPtr;
break;
case BBS_HARDDISK:
Idx = &HDIndex;
NewDevPtr = NewHDPtr;
break;
case BBS_CDROM:
Idx = &CDIndex;
NewDevPtr = NewCDPtr;
break;
case BBS_EMBED_NETWORK:
Idx = &NETIndex;
NewDevPtr = NewNETPtr;
break;
case BBS_BEV_DEVICE:
Idx = &BEVIndex;
NewDevPtr = NewBEVPtr;
break;
default:
Idx = NULL;
break;
}
//
// at this point we have copied those valid indexes to new buffer
// and we should check if there is any new appeared boot device
//
if (Idx != NULL) {
for (Index2 = 0; Index2 < *Idx; Index2++) {
if ((NewDevPtr[Index2] & 0xFF) == (UINT16) Index) {
break;
}
}
if (Index2 == *Idx) {
//
// Index2 == *Idx means we didn't find Index
// so Index is a new appeared device's index in BBS table
// insert it before disabled indexes.
//
for (Index2 = 0; Index2 < *Idx; Index2++) {
if ((NewDevPtr[Index2] & 0xFF00) == 0xFF00) {
break;
}
}
CopyMem (&NewDevPtr[Index2 + 1], &NewDevPtr[Index2], (*Idx - Index2) * sizeof (UINT16));
NewDevPtr[Index2] = (UINT16) (Index & 0xFF);
(*Idx)++;
}
}
}
FreePool (DevOrder);
Status = gRT->SetVariable (
VAR_LEGACY_DEV_ORDER,
&gEfiLegacyDevOrderVariableGuid,
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,
TotalSize,
NewDevOrder
);
FreePool (NewDevOrder);
return Status;
}
/**
Set Boot Priority for specified device type.
@param DeviceType The device type.
@param BbsIndex The BBS index to set the highest priority. Ignore when -1.
@param LocalBbsTable The BBS table.
@param Priority The prority table.
@retval EFI_SUCCESS The function completes successfully.
@retval EFI_NOT_FOUND Failed to find device.
@retval EFI_OUT_OF_RESOURCES Failed to get the efi variable of device order.
**/
EFI_STATUS
BdsSetBootPriority4SameTypeDev (
IN UINT16 DeviceType,
IN UINTN BbsIndex,
IN OUT BBS_TABLE *LocalBbsTable,
IN OUT UINT16 *Priority
)
{
LEGACY_DEV_ORDER_ENTRY *DevOrder;
LEGACY_DEV_ORDER_ENTRY *DevOrderPtr;
UINTN DevOrderSize;
UINTN Index;
DevOrder = BdsLibGetVariableAndSize (
VAR_LEGACY_DEV_ORDER,
&gEfiLegacyDevOrderVariableGuid,
&DevOrderSize
);
if (NULL == DevOrder) {
return EFI_OUT_OF_RESOURCES;
}
DevOrderPtr = DevOrder;
while ((UINT8 *) DevOrderPtr < (UINT8 *) DevOrder + DevOrderSize) {
if (DevOrderPtr->BbsType == DeviceType) {
break;
}
DevOrderPtr = (LEGACY_DEV_ORDER_ENTRY *) ((UINTN) DevOrderPtr + sizeof (BBS_TYPE) + DevOrderPtr->Length);
}
if ((UINT8 *) DevOrderPtr >= (UINT8 *) DevOrder + DevOrderSize) {
FreePool (DevOrder);
return EFI_NOT_FOUND;
}
if (BbsIndex != (UINTN) -1) {
LocalBbsTable[BbsIndex].BootPriority = *Priority;
(*Priority)++;
}
//
// If the high byte of the DevIndex is 0xFF, it indicates that this device has been disabled.
//
for (Index = 0; Index < DevOrderPtr->Length / sizeof (UINT16) - 1; Index++) {
if ((DevOrderPtr->Data[Index] & 0xFF00) == 0xFF00) {
//
// LocalBbsTable[DevIndex[Index] & 0xFF].BootPriority = BBS_DISABLED_ENTRY;
//
} else if (DevOrderPtr->Data[Index] != BbsIndex) {
LocalBbsTable[DevOrderPtr->Data[Index]].BootPriority = *Priority;
(*Priority)++;
}
}
FreePool (DevOrder);
return EFI_SUCCESS;
}
/**
Print the BBS Table.
@param LocalBbsTable The BBS table.
@param BbsCount The count of entry in BBS table.
**/
VOID
PrintBbsTable (
IN BBS_TABLE *LocalBbsTable,
IN UINT16 BbsCount
)
{
UINT16 Idx;
DEBUG ((DEBUG_ERROR, "\n"));
DEBUG ((DEBUG_ERROR, " NO Prio bb/dd/ff cl/sc Type Stat segm:offs\n"));
DEBUG ((DEBUG_ERROR, "=============================================\n"));
for (Idx = 0; Idx < BbsCount; Idx++) {
if ((LocalBbsTable[Idx].BootPriority == BBS_IGNORE_ENTRY) ||
(LocalBbsTable[Idx].BootPriority == BBS_DO_NOT_BOOT_FROM) ||
(LocalBbsTable[Idx].BootPriority == BBS_LOWEST_PRIORITY)
) {
continue;
}
DEBUG (
(DEBUG_ERROR,
" %02x: %04x %02x/%02x/%02x %02x/%02x %04x %04x %04x:%04x\n",
(UINTN) Idx,
(UINTN) LocalBbsTable[Idx].BootPriority,
(UINTN) LocalBbsTable[Idx].Bus,
(UINTN) LocalBbsTable[Idx].Device,
(UINTN) LocalBbsTable[Idx].Function,
(UINTN) LocalBbsTable[Idx].Class,
(UINTN) LocalBbsTable[Idx].SubClass,
(UINTN) LocalBbsTable[Idx].DeviceType,
(UINTN) * (UINT16 *) &LocalBbsTable[Idx].StatusFlags,
(UINTN) LocalBbsTable[Idx].BootHandlerSegment,
(UINTN) LocalBbsTable[Idx].BootHandlerOffset,
(UINTN) ((LocalBbsTable[Idx].MfgStringSegment << 4) + LocalBbsTable[Idx].MfgStringOffset),
(UINTN) ((LocalBbsTable[Idx].DescStringSegment << 4) + LocalBbsTable[Idx].DescStringOffset))
);
}
DEBUG ((DEBUG_ERROR, "\n"));
}
/**
Set the boot priority for BBS entries based on boot option entry and boot order.
@param Entry The boot option is to be checked for refresh BBS table.
@retval EFI_SUCCESS The boot priority for BBS entries is refreshed successfully.
@retval EFI_NOT_FOUND BBS entries can't be found.
@retval EFI_OUT_OF_RESOURCES Failed to get the legacy device boot order.
**/
EFI_STATUS
EFIAPI
BdsRefreshBbsTableForBoot (
IN BDS_COMMON_OPTION *Entry
)
{
EFI_STATUS Status;
UINT16 BbsIndex;
UINT16 HddCount;
UINT16 BbsCount;
HDD_INFO *LocalHddInfo;
BBS_TABLE *LocalBbsTable;
UINT16 DevType;
EFI_LEGACY_BIOS_PROTOCOL *LegacyBios;
UINTN Index;
UINT16 Priority;
UINT16 *BootOrder;
UINTN BootOrderSize;
UINT8 *BootOptionVar;
UINTN BootOptionSize;
CHAR16 BootOption[9];
UINT8 *Ptr;
UINT16 DevPathLen;
EFI_DEVICE_PATH_PROTOCOL *DevPath;
UINT16 *DeviceType;
UINTN DeviceTypeCount;
UINTN DeviceTypeIndex;
HddCount = 0;
BbsCount = 0;
LocalHddInfo = NULL;
LocalBbsTable = NULL;
DevType = BBS_UNKNOWN;
Status = gBS->LocateProtocol (&gEfiLegacyBiosProtocolGuid, NULL, (VOID **) &LegacyBios);
if (EFI_ERROR (Status)) {
return Status;
}
LegacyBios->GetBbsInfo (
LegacyBios,
&HddCount,
&LocalHddInfo,
&BbsCount,
&LocalBbsTable
);
//
// First, set all the present devices' boot priority to BBS_UNPRIORITIZED_ENTRY
// We will set them according to the settings setup by user
//
for (Index = 0; Index < BbsCount; Index++) {
if (!((BBS_IGNORE_ENTRY == LocalBbsTable[Index].BootPriority) ||
(BBS_DO_NOT_BOOT_FROM == LocalBbsTable[Index].BootPriority) ||
(BBS_LOWEST_PRIORITY == LocalBbsTable[Index].BootPriority))) {
LocalBbsTable[Index].BootPriority = BBS_UNPRIORITIZED_ENTRY;
}
}
//
// boot priority always starts at 0
//
Priority = 0;
if (Entry->LoadOptionsSize == sizeof (BBS_TABLE) + sizeof (UINT16)) {
//
// If Entry stands for a legacy boot option, we prioritize the devices with the same type first.
//
DevType = ((BBS_TABLE *) Entry->LoadOptions)->DeviceType;
BbsIndex = *(UINT16 *) ((BBS_TABLE *) Entry->LoadOptions + 1);
Status = BdsSetBootPriority4SameTypeDev (
DevType,
BbsIndex,
LocalBbsTable,
&Priority
);
if (EFI_ERROR (Status)) {
return Status;
}
}
//
// we have to set the boot priority for other BBS entries with different device types
//
BootOrder = BdsLibGetVariableAndSize (
L"BootOrder",
&gEfiGlobalVariableGuid,
&BootOrderSize
);
DeviceType = AllocatePool (BootOrderSize + sizeof (UINT16));
ASSERT (DeviceType != NULL);
DeviceType[0] = DevType;
DeviceTypeCount = 1;
for (Index = 0; ((BootOrder != NULL) && (Index < BootOrderSize / sizeof (UINT16))); Index++) {
UnicodeSPrint (BootOption, sizeof (BootOption), L"Boot%04x", BootOrder[Index]);
BootOptionVar = BdsLibGetVariableAndSize (
BootOption,
&gEfiGlobalVariableGuid,
&BootOptionSize
);
if (NULL == BootOptionVar) {
continue;
}
Ptr = BootOptionVar;
Ptr += sizeof (UINT32);
DevPathLen = *(UINT16 *) Ptr;
Ptr += sizeof (UINT16);
Ptr += StrSize ((UINT16 *) Ptr);
DevPath = (EFI_DEVICE_PATH_PROTOCOL *) Ptr;
if (BBS_DEVICE_PATH != DevPath->Type || BBS_BBS_DP != DevPath->SubType) {
FreePool (BootOptionVar);
continue;
}
Ptr += DevPathLen;
DevType = ((BBS_TABLE *) Ptr)->DeviceType;
for (DeviceTypeIndex = 0; DeviceTypeIndex < DeviceTypeCount; DeviceTypeIndex++) {
if (DeviceType[DeviceTypeIndex] == DevType) {
break;
}
}
if (DeviceTypeIndex < DeviceTypeCount) {
//
// We don't want to process twice for a device type
//
FreePool (BootOptionVar);
continue;
}
DeviceType[DeviceTypeCount] = DevType;
DeviceTypeCount++;
Status = BdsSetBootPriority4SameTypeDev (
DevType,
(UINTN) -1,
LocalBbsTable,
&Priority
);
FreePool (BootOptionVar);
if (EFI_ERROR (Status)) {
break;
}
}
FreePool (DeviceType);
if (BootOrder != NULL) {
FreePool (BootOrder);
}
DEBUG_CODE_BEGIN();
PrintBbsTable (LocalBbsTable, BbsCount);
DEBUG_CODE_END();
return Status;
}
/**
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;
EFI_EVENT LegacyBootEvent;
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
//
BdsRefreshBbsTableForBoot (Option);
//
// Write boot to OS performance data for legacy boot.
//
PERF_CODE (
//
// Create an event to be signalled when Legacy Boot occurs to write performance data.
//
Status = EfiCreateEventLegacyBootEx(
TPL_NOTIFY,
WriteBootToOsPerformanceData,
NULL,
&LegacyBootEvent
);
ASSERT_EFI_ERROR (Status);
);
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
);
}
/**
Internal function to check if the input boot option is a valid EFI NV Boot####.
@param OptionToCheck Boot option to be checked.
@retval TRUE This boot option matches a valid EFI NV Boot####.
@retval FALSE If not.
**/
BOOLEAN
IsBootOptionValidNVVarialbe (
IN BDS_COMMON_OPTION *OptionToCheck
)
{
LIST_ENTRY TempList;
BDS_COMMON_OPTION *BootOption;
BOOLEAN Valid;
CHAR16 OptionName[20];
Valid = FALSE;
InitializeListHead (&TempList);
UnicodeSPrint (OptionName, sizeof (OptionName), L"Boot%04x", OptionToCheck->BootCurrent);
BootOption = BdsLibVariableToOption (&TempList, OptionName);
if (BootOption == NULL) {
return FALSE;
}
//
// If the Boot Option Number and Device Path matches, OptionToCheck matches a
// valid EFI NV Boot####.
//
if ((OptionToCheck->BootCurrent == BootOption->BootCurrent) &&
(CompareMem (OptionToCheck->DevicePath, BootOption->DevicePath, GetDevicePathSize (OptionToCheck->DevicePath)) == 0))
{
Valid = TRUE;
}
FreePool (BootOption);
return Valid;
}
/**
Check whether a USB device match the specified USB Class device path. This
function follows "Load Option Processing" behavior in UEFI specification.
@param UsbIo USB I/O protocol associated with the USB device.
@param UsbClass The USB Class device path to match.
@retval TRUE The USB device match the USB Class device path.
@retval FALSE The USB device does not match the USB Class device path.
**/
BOOLEAN
BdsMatchUsbClass (
IN EFI_USB_IO_PROTOCOL *UsbIo,
IN USB_CLASS_DEVICE_PATH *UsbClass
)
{
EFI_STATUS Status;
EFI_USB_DEVICE_DESCRIPTOR DevDesc;
EFI_USB_INTERFACE_DESCRIPTOR IfDesc;
UINT8 DeviceClass;
UINT8 DeviceSubClass;
UINT8 DeviceProtocol;
if ((DevicePathType (UsbClass) != MESSAGING_DEVICE_PATH) ||
(DevicePathSubType (UsbClass) != MSG_USB_CLASS_DP)){
return FALSE;
}
//
// Check Vendor Id and Product Id.
//
Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DevDesc);
if (EFI_ERROR (Status)) {
return FALSE;
}
if ((UsbClass->VendorId != 0xffff) &&
(UsbClass->VendorId != DevDesc.IdVendor)) {
return FALSE;
}
if ((UsbClass->ProductId != 0xffff) &&
(UsbClass->ProductId != DevDesc.IdProduct)) {
return FALSE;
}
DeviceClass = DevDesc.DeviceClass;
DeviceSubClass = DevDesc.DeviceSubClass;
DeviceProtocol = DevDesc.DeviceProtocol;
if (DeviceClass == 0) {
//
// If Class in Device Descriptor is set to 0, use the Class, SubClass and
// Protocol in Interface Descriptor instead.
//
Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &IfDesc);
if (EFI_ERROR (Status)) {
return FALSE;
}
DeviceClass = IfDesc.InterfaceClass;
DeviceSubClass = IfDesc.InterfaceSubClass;
DeviceProtocol = IfDesc.InterfaceProtocol;
}
//
// Check Class, SubClass and Protocol.
//
if ((UsbClass->DeviceClass != 0xff) &&
(UsbClass->DeviceClass != DeviceClass)) {
return FALSE;
}
if ((UsbClass->DeviceSubClass != 0xff) &&
(UsbClass->DeviceSubClass != DeviceSubClass)) {
return FALSE;
}
if ((UsbClass->DeviceProtocol != 0xff) &&
(UsbClass->DeviceProtocol != DeviceProtocol)) {
return FALSE;
}
return TRUE;
}
/**
Check whether a USB device match the specified USB WWID device path. This
function follows "Load Option Processing" behavior in UEFI specification.
@param UsbIo USB I/O protocol associated with the USB device.
@param UsbWwid The USB WWID device path to match.
@retval TRUE The USB device match the USB WWID device path.
@retval FALSE The USB device does not match the USB WWID device path.
**/
BOOLEAN
BdsMatchUsbWwid (
IN EFI_USB_IO_PROTOCOL *UsbIo,
IN USB_WWID_DEVICE_PATH *UsbWwid
)
{
EFI_STATUS Status;
EFI_USB_DEVICE_DESCRIPTOR DevDesc;
EFI_USB_INTERFACE_DESCRIPTOR IfDesc;
UINT16 *LangIdTable;
UINT16 TableSize;
UINT16 Index;
CHAR16 *CompareStr;
UINTN CompareLen;
CHAR16 *SerialNumberStr;
UINTN Length;
if ((DevicePathType (UsbWwid) != MESSAGING_DEVICE_PATH) ||
(DevicePathSubType (UsbWwid) != MSG_USB_WWID_DP )){
return FALSE;
}
//
// Check Vendor Id and Product Id.
//
Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DevDesc);
if (EFI_ERROR (Status)) {
return FALSE;
}
if ((DevDesc.IdVendor != UsbWwid->VendorId) ||
(DevDesc.IdProduct != UsbWwid->ProductId)) {
return FALSE;
}
//
// Check Interface Number.
//
Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &IfDesc);
if (EFI_ERROR (Status)) {
return FALSE;
}
if (IfDesc.InterfaceNumber != UsbWwid->InterfaceNumber) {
return FALSE;
}
//
// Check Serial Number.
//
if (DevDesc.StrSerialNumber == 0) {
return FALSE;
}
//
// Get all supported languages.
//
TableSize = 0;
LangIdTable = NULL;
Status = UsbIo->UsbGetSupportedLanguages (UsbIo, &LangIdTable, &TableSize);
if (EFI_ERROR (Status) || (TableSize == 0) || (LangIdTable == NULL)) {
return FALSE;
}
//
// Serial number in USB WWID device path is the last 64-or-less UTF-16 characters.
//
CompareStr = (CHAR16 *) (UINTN) (UsbWwid + 1);
CompareLen = (DevicePathNodeLength (UsbWwid) - sizeof (USB_WWID_DEVICE_PATH)) / sizeof (CHAR16);
if (CompareStr[CompareLen - 1] == L'\0') {
CompareLen--;
}
//
// Compare serial number in each supported language.
//
for (Index = 0; Index < TableSize / sizeof (UINT16); Index++) {
SerialNumberStr = NULL;
Status = UsbIo->UsbGetStringDescriptor (
UsbIo,
LangIdTable[Index],
DevDesc.StrSerialNumber,
&SerialNumberStr
);
if (EFI_ERROR (Status) || (SerialNumberStr == NULL)) {
continue;
}
Length = StrLen (SerialNumberStr);
if ((Length >= CompareLen) &&
(CompareMem (SerialNumberStr + Length - CompareLen, CompareStr, CompareLen * sizeof (CHAR16)) == 0)) {
FreePool (SerialNumberStr);
return TRUE;
}
FreePool (SerialNumberStr);
}
return FALSE;
}
/**
Find a USB device path which match the specified short-form device path start
with USB Class or USB WWID device path and load the boot file then return the
image handle. If ParentDevicePath is NULL, this function will search in all USB
devices of the platform. If ParentDevicePath is not NULL,this function will only
search in its child devices.
@param ParentDevicePath The device path of the parent.
@param ShortFormDevicePath The USB Class or USB WWID device path to match.
@return The image Handle if find load file from specified short-form device path
or NULL if not found.
**/
EFI_HANDLE *
BdsFindUsbDevice (
IN EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath,
IN EFI_DEVICE_PATH_PROTOCOL *ShortFormDevicePath
)
{
EFI_STATUS Status;
UINTN UsbIoHandleCount;
EFI_HANDLE *UsbIoHandleBuffer;
EFI_DEVICE_PATH_PROTOCOL *UsbIoDevicePath;
EFI_USB_IO_PROTOCOL *UsbIo;
UINTN Index;
UINTN ParentSize;
UINTN Size;
EFI_HANDLE ImageHandle;
EFI_HANDLE Handle;
EFI_DEVICE_PATH_PROTOCOL *FullDevicePath;
EFI_DEVICE_PATH_PROTOCOL *NextDevicePath;
FullDevicePath = NULL;
ImageHandle = NULL;
//
// Get all UsbIo Handles.
//
UsbIoHandleCount = 0;
UsbIoHandleBuffer = NULL;
Status = gBS->LocateHandleBuffer (
ByProtocol,
&gEfiUsbIoProtocolGuid,
NULL,
&UsbIoHandleCount,
&UsbIoHandleBuffer
);
if (EFI_ERROR (Status) || (UsbIoHandleCount == 0) || (UsbIoHandleBuffer == NULL)) {
return NULL;
}
ParentSize = (ParentDevicePath == NULL) ? 0 : GetDevicePathSize (ParentDevicePath);
for (Index = 0; Index < UsbIoHandleCount; Index++) {
//
// Get the Usb IO interface.
//
Status = gBS->HandleProtocol(
UsbIoHandleBuffer[Index],
&gEfiUsbIoProtocolGuid,
(VOID **) &UsbIo
);
if (EFI_ERROR (Status)) {
continue;
}
UsbIoDevicePath = DevicePathFromHandle (UsbIoHandleBuffer[Index]);
if (UsbIoDevicePath == NULL) {
continue;
}
if (ParentDevicePath != NULL) {
//
// Compare starting part of UsbIoHandle's device path with ParentDevicePath.
//
Size = GetDevicePathSize (UsbIoDevicePath);
if ((Size < ParentSize) ||
(CompareMem (UsbIoDevicePath, ParentDevicePath, ParentSize - END_DEVICE_PATH_LENGTH) != 0)) {
continue;
}
}
if (BdsMatchUsbClass (UsbIo, (USB_CLASS_DEVICE_PATH *) ShortFormDevicePath) ||
BdsMatchUsbWwid (UsbIo, (USB_WWID_DEVICE_PATH *) ShortFormDevicePath)) {
//
// Try to find if there is the boot file in this DevicePath
//
NextDevicePath = NextDevicePathNode (ShortFormDevicePath);
if (!IsDevicePathEnd (NextDevicePath)) {
FullDevicePath = AppendDevicePath (UsbIoDevicePath, NextDevicePath);
//
// Connect the full device path, so that Simple File System protocol
// could be installed for this USB device.
//
BdsLibConnectDevicePath (FullDevicePath);
REPORT_STATUS_CODE (EFI_PROGRESS_CODE, PcdGet32 (PcdProgressCodeOsLoaderLoad));
Status = gBS->LoadImage (
TRUE,
gImageHandle,
FullDevicePath,
NULL,
0,
&ImageHandle
);
FreePool (FullDevicePath);
} else {
FullDevicePath = UsbIoDevicePath;
Status = EFI_NOT_FOUND;
}
//
// If we didn't find an image directly, we need to try as if it is a removable device boot option
// and load the image according to the default boot behavior for removable device.
//
if (EFI_ERROR (Status)) {
//
// check if there is a bootable removable media could be found in this device path ,
// and get the bootable media handle
//
Handle = BdsLibGetBootableHandle(UsbIoDevicePath);
if (Handle == NULL) {
continue;
}
//
// Load the default boot file \EFI\BOOT\boot{machinename}.EFI from removable Media
// machinename is ia32, ia64, x64, ...
//
FullDevicePath = FileDevicePath (Handle, EFI_REMOVABLE_MEDIA_FILE_NAME);
if (FullDevicePath != NULL) {
REPORT_STATUS_CODE (EFI_PROGRESS_CODE, PcdGet32 (PcdProgressCodeOsLoaderLoad));
Status = gBS->LoadImage (
TRUE,
gImageHandle,
FullDevicePath,
NULL,
0,
&ImageHandle
);
if (EFI_ERROR (Status)) {
//
// The DevicePath failed, and it's not a valid
// removable media device.
//
continue;
}
} else {
continue;
}
}
break;
}
}
FreePool (UsbIoHandleBuffer);
return ImageHandle;
}
/**
Expand USB Class or USB WWID device path node to be full device path of a USB
device in platform then load the boot file on this full device path and return the
image handle.
This function support following 4 cases:
1) Boot Option device path starts with a USB Class or USB WWID device path,
and there is no Media FilePath device path in the end.
In this case, it will follow Removable Media Boot Behavior.
2) Boot Option device path starts with a USB Class or USB WWID device path,
and ended with Media FilePath device path.
3) Boot Option device path starts with a full device path to a USB Host Controller,
contains a USB Class or USB WWID device path node, while not ended with Media
FilePath device path. In this case, it will follow Removable Media Boot Behavior.
4) Boot Option device path starts with a full device path to a USB Host Controller,
contains a USB Class or USB WWID device path node, and ended with Media
FilePath device path.
@param DevicePath The Boot Option device path.
@return The image handle of boot file, or NULL if there is no boot file found in
the specified USB Class or USB WWID device path.
**/
EFI_HANDLE *
BdsExpandUsbShortFormDevicePath (
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath
)
{
EFI_HANDLE *ImageHandle;
EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
EFI_DEVICE_PATH_PROTOCOL *ShortFormDevicePath;
//
// Search for USB Class or USB WWID device path node.
//
ShortFormDevicePath = NULL;
ImageHandle = NULL;
TempDevicePath = DevicePath;
while (!IsDevicePathEnd (TempDevicePath)) {
if ((DevicePathType (TempDevicePath) == MESSAGING_DEVICE_PATH) &&
((DevicePathSubType (TempDevicePath) == MSG_USB_CLASS_DP) ||
(DevicePathSubType (TempDevicePath) == MSG_USB_WWID_DP))) {
ShortFormDevicePath = TempDevicePath;
break;
}
TempDevicePath = NextDevicePathNode (TempDevicePath);
}
if (ShortFormDevicePath == NULL) {
//
// No USB Class or USB WWID device path node found, do nothing.
//
return NULL;
}
if (ShortFormDevicePath == DevicePath) {
//
// Boot Option device path starts with USB Class or USB WWID device path.
//
ImageHandle = BdsFindUsbDevice (NULL, ShortFormDevicePath);
if (ImageHandle == NULL) {
//
// Failed to find a match in existing devices, connect the short form USB
// device path and try again.
//
BdsLibConnectUsbDevByShortFormDP (0xff, ShortFormDevicePath);
ImageHandle = BdsFindUsbDevice (NULL, ShortFormDevicePath);
}
} else {
//
// Boot Option device path contains USB Class or USB WWID device path node.
//
//
// Prepare the parent device path for search.
//
TempDevicePath = DuplicateDevicePath (DevicePath);
ASSERT (TempDevicePath != NULL);
SetDevicePathEndNode (((UINT8 *) TempDevicePath) + ((UINTN) ShortFormDevicePath - (UINTN) DevicePath));
//
// The USB Host Controller device path is already in Boot Option device path
// and USB Bus driver already support RemainingDevicePath starts with USB
// Class or USB WWID device path, so just search in existing USB devices and
// doesn't perform ConnectController here.
//
ImageHandle = BdsFindUsbDevice (TempDevicePath, ShortFormDevicePath);
FreePool (TempDevicePath);
}
return ImageHandle;
}
/**
Process the boot option follow the UEFI specification and
special treat the legacy boot option with BBS_DEVICE_PATH.
@param Option The boot option need to be processed
@param DevicePath The device path which describe where to load the
boot image or the legacy BBS device path to boot
the legacy OS
@param ExitDataSize The size of exit data.
@param ExitData Data returned when Boot image failed.
@retval EFI_SUCCESS Boot from the input boot option successfully.
@retval EFI_NOT_FOUND If the Device Path is not found in the system
**/
EFI_STATUS
EFIAPI
BdsLibBootViaBootOption (
IN BDS_COMMON_OPTION *Option,
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
OUT UINTN *ExitDataSize,
OUT CHAR16 **ExitData OPTIONAL
)
{
EFI_STATUS Status;
EFI_STATUS StatusLogo;
EFI_HANDLE Handle;
EFI_HANDLE ImageHandle;
EFI_DEVICE_PATH_PROTOCOL *FilePath;
EFI_LOADED_IMAGE_PROTOCOL *ImageInfo;
EFI_DEVICE_PATH_PROTOCOL *WorkingDevicePath;
LIST_ENTRY TempBootLists;
EFI_BOOT_LOGO_PROTOCOL *BootLogo;
Status = EFI_SUCCESS;
*ExitDataSize = 0;
*ExitData = NULL;
//
// If it's Device Path that starts with a hard drive path, append it with the front part to compose a
// full device path
//
WorkingDevicePath = NULL;
if ((DevicePathType (DevicePath) == MEDIA_DEVICE_PATH) &&
(DevicePathSubType (DevicePath) == MEDIA_HARDDRIVE_DP)) {
WorkingDevicePath = BdsExpandPartitionPartialDevicePathToFull (
(HARDDRIVE_DEVICE_PATH *)DevicePath
);
if (WorkingDevicePath != NULL) {
DevicePath = WorkingDevicePath;
}
}
//
// Set Boot Current
//
if (IsBootOptionValidNVVarialbe (Option)) {
//
// For a temporary boot (i.e. a boot by selected a EFI Shell using "Boot From File"), Boot Current is actually not valid.
// In this case, "BootCurrent" is not created.
// Only create the BootCurrent variable when it points to a valid Boot#### variable.
//
SetVariableAndReportStatusCodeOnError (
L"BootCurrent",
&gEfiGlobalVariableGuid,
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
sizeof (UINT16),
&Option->BootCurrent
);
}
//
// Signal the EVT_SIGNAL_READY_TO_BOOT event
//
EfiSignalEventReadyToBoot();
//
// Report Status Code to indicate ReadyToBoot event was signalled
//
REPORT_STATUS_CODE (EFI_PROGRESS_CODE, (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_PC_READY_TO_BOOT_EVENT));
//
// Expand USB Class or USB WWID device path node to be full device path of a USB
// device in platform then load the boot file on this full device path and get the
// image handle.
//
ImageHandle = BdsExpandUsbShortFormDevicePath (DevicePath);
//
// Adjust the different type memory page number just before booting
// and save the updated info into the variable for next boot to use
//
BdsSetMemoryTypeInformationVariable ();
//
// By expanding the USB Class or WWID device path, the ImageHandle has returnned.
// Here get the ImageHandle for the non USB class or WWID device path.
//
if (ImageHandle == NULL) {
ASSERT (Option->DevicePath != NULL);
if ((DevicePathType (Option->DevicePath) == BBS_DEVICE_PATH) &&
(DevicePathSubType (Option->DevicePath) == BBS_BBS_DP)
) {
//
// Check to see if we should legacy BOOT. If yes then do the legacy boot
//
return BdsLibDoLegacyBoot (Option);
}
//
// If the boot option point to Internal FV shell, make sure it is valid
//
Status = BdsLibUpdateFvFileDevicePath (&DevicePath, PcdGetPtr(PcdShellFile));
if (!EFI_ERROR(Status)) {
if (Option->DevicePath != NULL) {
FreePool(Option->DevicePath);
}
Option->DevicePath = AllocateZeroPool (GetDevicePathSize (DevicePath));
ASSERT(Option->DevicePath != NULL);
CopyMem (Option->DevicePath, DevicePath, GetDevicePathSize (DevicePath));
//
// Update the shell boot option
//
InitializeListHead (&TempBootLists);
BdsLibRegisterNewOption (&TempBootLists, DevicePath, L"EFI Internal Shell", L"BootOrder");
//
// free the temporary device path created by BdsLibUpdateFvFileDevicePath()
//
FreePool (DevicePath);
DevicePath = Option->DevicePath;
}
DEBUG_CODE_BEGIN();
if (Option->Description == NULL) {
DEBUG ((DEBUG_INFO | DEBUG_LOAD, "Booting from unknown device path\n"));
} else {
DEBUG ((DEBUG_INFO | DEBUG_LOAD, "Booting %S\n", Option->Description));
}
DEBUG_CODE_END();
//
// Report status code for OS Loader LoadImage.
//
REPORT_STATUS_CODE (EFI_PROGRESS_CODE, PcdGet32 (PcdProgressCodeOsLoaderLoad));
Status = gBS->LoadImage (
TRUE,
gImageHandle,
DevicePath,
NULL,
0,
&ImageHandle
);
//
// If we didn't find an image directly, we need to try as if it is a removable device boot option
// and load the image according to the default boot behavior for removable device.
//
if (EFI_ERROR (Status)) {
//
// check if there is a bootable removable media could be found in this device path ,
// and get the bootable media handle
//
Handle = BdsLibGetBootableHandle(DevicePath);
if (Handle != NULL) {
//
// Load the default boot file \EFI\BOOT\boot{machinename}.EFI from removable Media
// machinename is ia32, ia64, x64, ...
//
FilePath = FileDevicePath (Handle, EFI_REMOVABLE_MEDIA_FILE_NAME);
if (FilePath != NULL) {
REPORT_STATUS_CODE (EFI_PROGRESS_CODE, PcdGet32 (PcdProgressCodeOsLoaderLoad));
Status = gBS->LoadImage (
TRUE,
gImageHandle,
FilePath,
NULL,
0,
&ImageHandle
);
}
}
}
}
//
// Provide the image with it's load options
//
if ((ImageHandle == NULL) || (EFI_ERROR(Status))) {
//
// Report Status Code to indicate that the failure to load boot option
//
REPORT_STATUS_CODE (
EFI_ERROR_CODE | EFI_ERROR_MINOR,
(EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_EC_BOOT_OPTION_LOAD_ERROR)
);
goto Done;
}
Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **) &ImageInfo);
ASSERT_EFI_ERROR (Status);
if (Option->LoadOptionsSize != 0) {
ImageInfo->LoadOptionsSize = Option->LoadOptionsSize;
ImageInfo->LoadOptions = Option->LoadOptions;
}
//
// Clean to NULL because the image is loaded directly from the firmwares boot manager.
//
ImageInfo->ParentHandle = NULL;
//
// Before calling the image, enable the Watchdog Timer for
// the 5 Minute period
//
gBS->SetWatchdogTimer (5 * 60, 0x0000, 0x00, NULL);
//
// Write boot to OS performance data for UEFI boot
//
PERF_CODE (
WriteBootToOsPerformanceData (NULL, NULL);
);
//
// Report status code for OS Loader StartImage.
//
REPORT_STATUS_CODE (EFI_PROGRESS_CODE, PcdGet32 (PcdProgressCodeOsLoaderStart));
Status = gBS->StartImage (ImageHandle, ExitDataSize, ExitData);
DEBUG ((DEBUG_INFO | DEBUG_LOAD, "Image Return Status = %r\n", Status));
if (EFI_ERROR (Status)) {
//
// Report Status Code to indicate that boot failure
//
REPORT_STATUS_CODE (
EFI_ERROR_CODE | EFI_ERROR_MINOR,
(EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_EC_BOOT_OPTION_FAILED)
);
}
//
// Clear the Watchdog Timer after the image returns
//
gBS->SetWatchdogTimer (0x0000, 0x0000, 0x0000, NULL);
Done:
//
// Set Logo status invalid after trying one boot option
//
BootLogo = NULL;
StatusLogo = gBS->LocateProtocol (&gEfiBootLogoProtocolGuid, NULL, (VOID **) &BootLogo);
if (!EFI_ERROR (StatusLogo) && (BootLogo != NULL)) {
BootLogo->SetBootLogo (BootLogo, NULL, 0, 0, 0, 0);
}
//
// Clear Boot Current
// Deleting variable with current implementation shouldn't fail.
//
gRT->SetVariable (
L"BootCurrent",
&gEfiGlobalVariableGuid,
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
0,
NULL
);
return Status;
}
/**
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 devic 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;
FullDevicePath = 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
//
GetVariable2 (
HD_BOOT_DEVICE_PATH_VARIABLE_NAME,
&gHdBootDevicePathVariablGuid,
(VOID **) &CachedDevicePath,
&CachedDevicePathSize
);
//
// Delete the invalid HD_BOOT_DEVICE_PATH_VARIABLE_NAME variable.
//
if ((CachedDevicePath != NULL) && !IsDevicePathValid (CachedDevicePath, CachedDevicePathSize)) {
FreePool (CachedDevicePath);
CachedDevicePath = NULL;
Status = gRT->SetVariable (
HD_BOOT_DEVICE_PATH_VARIABLE_NAME,
&gHdBootDevicePathVariablGuid,
0,
0,
NULL
);
ASSERT_EFI_ERROR (Status);
}
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
// Failure to set the variable only impacts the performance when next time expanding the short-form device path.
//
Status = gRT->SetVariable (
HD_BOOT_DEVICE_PATH_VARIABLE_NAME,
&gHdBootDevicePathVariablGuid,
EFI_VARIABLE_BOOTSERVICE_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);
//
// 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);
}
if (CachedDevicePath != NULL) {
TempNewDevicePath = CachedDevicePath;
CachedDevicePath = AppendDevicePathInstance (BlockIoDevicePath, CachedDevicePath);
FreePool(TempNewDevicePath);
} else {
CachedDevicePath = DuplicateDevicePath (BlockIoDevicePath);
}
//
// 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);
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
// Failure to set the variable only impacts the performance when next time expanding the short-form device path.
//
Status = gRT->SetVariable (
HD_BOOT_DEVICE_PATH_VARIABLE_NAME,
&gHdBootDevicePathVariablGuid,
EFI_VARIABLE_BOOTSERVICE_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 *TmpHdPath;
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
BOOLEAN Match;
EFI_DEVICE_PATH_PROTOCOL *BlockIoHdDevicePathNode;
if ((BlockIoDevicePath == NULL) || (HardDriveDevicePath == NULL)) {
return FALSE;
}
//
// Make PreviousDevicePath == the device path node before the end node
//
DevicePath = BlockIoDevicePath;
BlockIoHdDevicePathNode = NULL;
//
// find the partition device path node
//
while (!IsDevicePathEnd (DevicePath)) {
if ((DevicePathType (DevicePath) == MEDIA_DEVICE_PATH) &&
(DevicePathSubType (DevicePath) == MEDIA_HARDDRIVE_DP)
) {
BlockIoHdDevicePathNode = DevicePath;
break;
}
DevicePath = NextDevicePathNode (DevicePath);
}
if (BlockIoHdDevicePathNode == NULL) {
return FALSE;
}
//
// See if the harddrive device path in blockio matches the orig Hard Drive Node
//
TmpHdPath = (HARDDRIVE_DEVICE_PATH *) BlockIoHdDevicePathNode;
Match = FALSE;
//
// Check for the match
//
if ((TmpHdPath->MBRType == HardDriveDevicePath->MBRType) &&
(TmpHdPath->SignatureType == HardDriveDevicePath->SignatureType)) {
switch (TmpHdPath->SignatureType) {
case SIGNATURE_TYPE_GUID:
Match = CompareGuid ((EFI_GUID *)TmpHdPath->Signature, (EFI_GUID *)HardDriveDevicePath->Signature);
break;
case SIGNATURE_TYPE_MBR:
Match = (BOOLEAN)(*((UINT32 *)(&(TmpHdPath->Signature[0]))) == ReadUnaligned32((UINT32 *)(&(HardDriveDevicePath->Signature[0]))));
break;
default:
Match = FALSE;
break;
}
}
return Match;
}
/**
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;
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
//
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;
}
if (!ValidateOption(BootOptionVar, BootOptionSize)) {
BdsDeleteBootOption (BootOrder[Index], BootOrder, &BootOrderSize);
FreePool (BootOptionVar);
Index++;
continue;
}
TempPtr = BootOptionVar;
TempPtr += sizeof (UINT32) + sizeof (UINT16);
TempPtr += StrSize ((CHAR16 *) TempPtr);
OptionDevicePath = (EFI_DEVICE_PATH_PROTOCOL *) TempPtr;
OptionDevicePathSize = GetDevicePathSize (OptionDevicePath);
//
// Check whether the device path match
//
if ((OptionDevicePathSize == DevicePathSize) &&
(CompareMem (DevicePath, OptionDevicePath, DevicePathSize) == 0)) {
BdsDeleteBootOption (BootOrder[Index], BootOrder, &BootOrderSize);
FreePool (BootOptionVar);
break;
}
FreePool (BootOptionVar);
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
);
//
// Shrinking variable with existing variable implementation shouldn't fail.
//
ASSERT_EFI_ERROR (Status);
FreePool (BootOrder);
return Status;
}
/**
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 (
VOID
)
{
UINT16 *BootOrder;
UINT8 *BootOptionVar;
UINTN BootOrderSize;
UINTN BootOptionSize;
EFI_STATUS Status;
UINTN Index;
UINTN Index2;
UINT16 BootOption[BOOT_OPTION_MAX_CHAR];
EFI_DEVICE_PATH_PROTOCOL *OptionDevicePath;
UINT8 *TempPtr;
CHAR16 *Description;
BOOLEAN Corrupted;
Status = EFI_SUCCESS;
BootOrder = NULL;
Description = NULL;
OptionDevicePath = NULL;
BootOrderSize = 0;
Corrupted = FALSE;
//
// Check "BootOrder" variable firstly, this variable hold the number of boot options
//
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;
}
if (!ValidateOption(BootOptionVar, BootOptionSize)) {
Corrupted = TRUE;
} else {
TempPtr = BootOptionVar;
TempPtr += sizeof (UINT32) + sizeof (UINT16);
Description = (CHAR16 *) TempPtr;
TempPtr += StrSize ((CHAR16 *) TempPtr);
OptionDevicePath = (EFI_DEVICE_PATH_PROTOCOL *) TempPtr;
//
// Skip legacy boot option (BBS boot device)
//
if ((DevicePathType (OptionDevicePath) == BBS_DEVICE_PATH) &&
(DevicePathSubType (OptionDevicePath) == BBS_BBS_DP)) {
FreePool (BootOptionVar);
Index++;
continue;
}
}
if (Corrupted || !BdsLibIsValidEFIBootOptDevicePathExt (OptionDevicePath, FALSE, Description)) {
//
// Delete this invalid boot option "Boot####"
//
Status = gRT->SetVariable (
BootOption,
&gEfiGlobalVariableGuid,
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
0,
NULL
);
//
// Deleting variable with current variable implementation shouldn't fail.
//
ASSERT_EFI_ERROR (Status);
//
// Mark this boot option in boot order as deleted
//
BootOrder[Index] = 0xffff;
Corrupted = FALSE;
}
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
);
//
// Shrinking variable with current variable implementation shouldn't fail.
//
ASSERT_EFI_ERROR (Status);
FreePool (BootOrder);
return Status;
}
/**
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
@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
EFIAPI
BdsLibEnumerateAllBootOption (
IN OUT LIST_ENTRY *BdsBootOptionList
)
{
EFI_STATUS Status;
UINT16 FloppyNumber;
UINT16 HarddriveNumber;
UINT16 CdromNumber;
UINT16 UsbNumber;
UINT16 MiscNumber;
UINT16 ScsiNumber;
UINT16 NonBlockNumber;
UINTN NumberBlockIoHandles;
EFI_HANDLE *BlockIoHandles;
EFI_BLOCK_IO_PROTOCOL *BlkIo;
BOOLEAN Removable[2];
UINTN RemovableIndex;
UINTN Index;
UINTN NumOfLoadFileHandles;
EFI_HANDLE *LoadFileHandles;
UINTN FvHandleCount;
EFI_HANDLE *FvHandleBuffer;
EFI_FV_FILETYPE Type;
UINTN Size;
EFI_FV_FILE_ATTRIBUTES Attributes;
UINT32 AuthenticationStatus;
EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv;
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
UINTN DevicePathType;
CHAR16 Buffer[40];
EFI_HANDLE *FileSystemHandles;
UINTN NumberFileSystemHandles;
BOOLEAN NeedDelete;
EFI_IMAGE_DOS_HEADER DosHeader;
CHAR8 *PlatLang;
CHAR8 *LastLang;
EFI_IMAGE_OPTIONAL_HEADER_UNION HdrData;
EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr;
FloppyNumber = 0;
HarddriveNumber = 0;
CdromNumber = 0;
UsbNumber = 0;
MiscNumber = 0;
ScsiNumber = 0;
PlatLang = NULL;
LastLang = NULL;
ZeroMem (Buffer, sizeof (Buffer));
//
// If the boot device enumerate happened, just get the boot
// device from the boot order variable
//
if (mEnumBootDevice) {
GetVariable2 (LAST_ENUM_LANGUAGE_VARIABLE_NAME, &gLastEnumLangGuid, (VOID**)&LastLang, NULL);
GetEfiGlobalVariable2 (L"PlatformLang", (VOID**)&PlatLang, NULL);
ASSERT (PlatLang != NULL);
if ((LastLang != NULL) && (AsciiStrCmp (LastLang, PlatLang) == 0)) {
Status = BdsLibBuildOptionFromVar (BdsBootOptionList, L"BootOrder");
FreePool (LastLang);
FreePool (PlatLang);
return Status;
} else {
Status = gRT->SetVariable (
LAST_ENUM_LANGUAGE_VARIABLE_NAME,
&gLastEnumLangGuid,
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,
AsciiStrSize (PlatLang),
PlatLang
);
//
// Failure to set the variable only impacts the performance next time enumerating the boot options.
//
if (LastLang != NULL) {
FreePool (LastLang);
}
FreePool (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
//
REFRESH_LEGACY_BOOT_OPTIONS;
//
// Delete invalid boot option
//
BdsDeleteAllInvalidEfiBootOption ();
//
// Parse removable media followed by fixed media.
// The Removable[] array is used by the for-loop below to create removable media boot options
// at first, and then to create fixed media boot options.
//
Removable[0] = FALSE;
Removable[1] = TRUE;
gBS->LocateHandleBuffer (
ByProtocol,
&gEfiBlockIoProtocolGuid,
NULL,
&NumberBlockIoHandles,
&BlockIoHandles
);
for (RemovableIndex = 0; RemovableIndex < 2; RemovableIndex++) {
for (Index = 0; Index < NumberBlockIoHandles; Index++) {
Status = gBS->HandleProtocol (
BlockIoHandles[Index],
&gEfiBlockIoProtocolGuid,
(VOID **) &BlkIo
);
//
// skip the logical partition
//
if (EFI_ERROR (Status) || BlkIo->Media->LogicalPartition) {
continue;
}
//
// firstly fixed block io then the removable block io
//
if (BlkIo->Media->RemovableMedia == Removable[RemovableIndex]) {
continue;
}
DevicePath = DevicePathFromHandle (BlockIoHandles[Index]);
DevicePathType = BdsGetBootTypeFromDevicePath (DevicePath);
switch (DevicePathType) {
case BDS_EFI_ACPI_FLOPPY_BOOT:
if (FloppyNumber != 0) {
UnicodeSPrint (Buffer, sizeof (Buffer), L"%s %d", BdsLibGetStringById (STRING_TOKEN (STR_DESCRIPTION_FLOPPY)), FloppyNumber);
} else {
UnicodeSPrint (Buffer, sizeof (Buffer), L"%s", BdsLibGetStringById (STRING_TOKEN (STR_DESCRIPTION_FLOPPY)));
}
BdsLibBuildOptionFromHandle (BlockIoHandles[Index], BdsBootOptionList, Buffer);
FloppyNumber++;
break;
//
// Assume a removable SATA device should be the DVD/CD device, a fixed SATA device should be the Hard Drive device.
//
case BDS_EFI_MESSAGE_ATAPI_BOOT:
case BDS_EFI_MESSAGE_SATA_BOOT:
if (BlkIo->Media->RemovableMedia) {
if (CdromNumber != 0) {
UnicodeSPrint (Buffer, sizeof (Buffer), L"%s %d", BdsLibGetStringById (STRING_TOKEN (STR_DESCRIPTION_CD_DVD)), CdromNumber);
} else {
UnicodeSPrint (Buffer, sizeof (Buffer), L"%s", BdsLibGetStringById (STRING_TOKEN (STR_DESCRIPTION_CD_DVD)));
}
CdromNumber++;
} else {
if (HarddriveNumber != 0) {
UnicodeSPrint (Buffer, sizeof (Buffer), L"%s %d", BdsLibGetStringById (STRING_TOKEN (STR_DESCRIPTION_HARDDRIVE)), HarddriveNumber);
} else {
UnicodeSPrint (Buffer, sizeof (Buffer), L"%s", BdsLibGetStringById (STRING_TOKEN (STR_DESCRIPTION_HARDDRIVE)));
}
HarddriveNumber++;
}
DEBUG ((DEBUG_INFO | DEBUG_LOAD, "Buffer: %S\n", Buffer));
BdsLibBuildOptionFromHandle (BlockIoHandles[Index], BdsBootOptionList, Buffer);
break;
case BDS_EFI_MESSAGE_USB_DEVICE_BOOT:
if (UsbNumber != 0) {
UnicodeSPrint (Buffer, sizeof (Buffer), L"%s %d", BdsLibGetStringById (STRING_TOKEN (STR_DESCRIPTION_USB)), UsbNumber);
} else {
UnicodeSPrint (Buffer, sizeof (Buffer), L"%s", BdsLibGetStringById (STRING_TOKEN (STR_DESCRIPTION_USB)));
}
BdsLibBuildOptionFromHandle (BlockIoHandles[Index], BdsBootOptionList, Buffer);
UsbNumber++;
break;
case BDS_EFI_MESSAGE_SCSI_BOOT:
if (ScsiNumber != 0) {
UnicodeSPrint (Buffer, sizeof (Buffer), L"%s %d", BdsLibGetStringById (STRING_TOKEN (STR_DESCRIPTION_SCSI)), ScsiNumber);
} else {
UnicodeSPrint (Buffer, sizeof (Buffer), L"%s", BdsLibGetStringById (STRING_TOKEN (STR_DESCRIPTION_SCSI)));
}
BdsLibBuildOptionFromHandle (BlockIoHandles[Index], BdsBootOptionList, Buffer);
ScsiNumber++;
break;
case BDS_EFI_MESSAGE_MISC_BOOT:
default:
if (MiscNumber != 0) {
UnicodeSPrint (Buffer, sizeof (Buffer), L"%s %d", BdsLibGetStringById (STRING_TOKEN (STR_DESCRIPTION_MISC)), MiscNumber);
} else {
UnicodeSPrint (Buffer, sizeof (Buffer), L"%s", BdsLibGetStringById (STRING_TOKEN (STR_DESCRIPTION_MISC)));
}
BdsLibBuildOptionFromHandle (BlockIoHandles[Index], BdsBootOptionList, Buffer);
MiscNumber++;
break;
}
}
}
if (NumberBlockIoHandles != 0) {
FreePool (BlockIoHandles);
}
//
// If there is simple file protocol which does not consume block Io protocol, create a boot option for it here.
//
NonBlockNumber = 0;
gBS->LocateHandleBuffer (
ByProtocol,
&gEfiSimpleFileSystemProtocolGuid,
NULL,
&NumberFileSystemHandles,
&FileSystemHandles
);
for (Index = 0; Index < NumberFileSystemHandles; Index++) {
Status = gBS->HandleProtocol (
FileSystemHandles[Index],
&gEfiBlockIoProtocolGuid,
(VOID **) &BlkIo
);
if (!EFI_ERROR (Status)) {
//
// Skip if the file system handle supports a BlkIo protocol,
//
continue;
}
//
// Do the removable Media thing. \EFI\BOOT\boot{machinename}.EFI
// machinename is ia32, ia64, x64, ...
//
Hdr.Union = &HdrData;
NeedDelete = TRUE;
Status = BdsLibGetImageHeader (
FileSystemHandles[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) {
NeedDelete = FALSE;
}
if (NeedDelete) {
//
// No such file or the file is not a EFI application, delete this boot option
//
BdsLibDeleteOptionFromHandle (FileSystemHandles[Index]);
} else {
if (NonBlockNumber != 0) {
UnicodeSPrint (Buffer, sizeof (Buffer), L"%s %d", BdsLibGetStringById (STRING_TOKEN (STR_DESCRIPTION_NON_BLOCK)), NonBlockNumber);
} else {
UnicodeSPrint (Buffer, sizeof (Buffer), L"%s", BdsLibGetStringById (STRING_TOKEN (STR_DESCRIPTION_NON_BLOCK)));
}
BdsLibBuildOptionFromHandle (FileSystemHandles[Index], BdsBootOptionList, Buffer);
NonBlockNumber++;
}
}
if (NumberFileSystemHandles != 0) {
FreePool (FileSystemHandles);
}
//
// Parse Network Boot Device
//
NumOfLoadFileHandles = 0;
//
// Search Load File protocol for PXE boot option.
//
gBS->LocateHandleBuffer (
ByProtocol,
&gEfiLoadFileProtocolGuid,
NULL,
&NumOfLoadFileHandles,
&LoadFileHandles
);
for (Index = 0; Index < NumOfLoadFileHandles; Index++) {
if (Index != 0) {
UnicodeSPrint (Buffer, sizeof (Buffer), L"%s %d", BdsLibGetStringById (STRING_TOKEN (STR_DESCRIPTION_NETWORK)), Index);
} else {
UnicodeSPrint (Buffer, sizeof (Buffer), L"%s", BdsLibGetStringById (STRING_TOKEN (STR_DESCRIPTION_NETWORK)));
}
BdsLibBuildOptionFromHandle (LoadFileHandles[Index], BdsBootOptionList, Buffer);
}
if (NumOfLoadFileHandles != 0) {
FreePool (LoadFileHandles);
}
//
// Check if we have on flash shell
//
gBS->LocateHandleBuffer (
ByProtocol,
&gEfiFirmwareVolume2ProtocolGuid,
NULL,
&FvHandleCount,
&FvHandleBuffer
);
for (Index = 0; Index < FvHandleCount; Index++) {
gBS->HandleProtocol (
FvHandleBuffer[Index],
&gEfiFirmwareVolume2ProtocolGuid,
(VOID **) &Fv
);
Status = Fv->ReadFile (
Fv,
PcdGetPtr(PcdShellFile),
NULL,
&Size,
&Type,
&Attributes,
&AuthenticationStatus
);
if (EFI_ERROR (Status)) {
//
// Skip if no shell file in the FV
//
continue;
}
//
// Build the shell boot option
//
BdsLibBuildOptionFromShell (FvHandleBuffer[Index], BdsBootOptionList);
}
if (FvHandleCount != 0) {
FreePool (FvHandleBuffer);
}
//
// Make sure every boot only have one time
// boot device enumerate
//
Status = BdsLibBuildOptionFromVar (BdsBootOptionList, L"BootOrder");
mEnumBootDevice = TRUE;
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
@param String The description of the boot option.
**/
VOID
EFIAPI
BdsLibBuildOptionFromHandle (
IN EFI_HANDLE Handle,
IN LIST_ENTRY *BdsBootOptionList,
IN CHAR16 *String
)
{
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
DevicePath = DevicePathFromHandle (Handle);
//
// Create and register new boot option
//
BdsLibRegisterNewOption (BdsBootOptionList, DevicePath, String, L"BootOrder");
}
/**
Build the on flash shell boot option with the handle parsed in.
@param Handle The handle which present the device path to create
on flash shell boot option
@param BdsBootOptionList The header of the link list which indexed all
current boot options
**/
VOID
EFIAPI
BdsLibBuildOptionFromShell (
IN EFI_HANDLE Handle,
IN OUT LIST_ENTRY *BdsBootOptionList
)
{
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
MEDIA_FW_VOL_FILEPATH_DEVICE_PATH ShellNode;
DevicePath = DevicePathFromHandle (Handle);
//
// Build the shell device path
//
EfiInitializeFwVolDevicepathNode (&ShellNode, PcdGetPtr(PcdShellFile));
DevicePath = AppendDevicePathNode (DevicePath, (EFI_DEVICE_PATH_PROTOCOL *) &ShellNode);
//
// Create and register the shell boot option
//
BdsLibRegisterNewOption (BdsBootOptionList, DevicePath, L"EFI Internal Shell", L"BootOrder");
}
/**
Boot from the UEFI spec defined "BootNext" variable.
**/
VOID
EFIAPI
BdsLibBootNext (
VOID
)
{
EFI_STATUS Status;
UINT16 *BootNext;
UINTN BootNextSize;
CHAR16 Buffer[20];
BDS_COMMON_OPTION *BootOption;
LIST_ENTRY TempList;
UINTN ExitDataSize;
CHAR16 *ExitData;
//
// Init the boot option name buffer and temp link list
//
InitializeListHead (&TempList);
ZeroMem (Buffer, sizeof (Buffer));
BootNext = BdsLibGetVariableAndSize (
L"BootNext",
&gEfiGlobalVariableGuid,
&BootNextSize
);
//
// Clear the boot next variable first
//
if (BootNext != NULL) {
Status = gRT->SetVariable (
L"BootNext",
&gEfiGlobalVariableGuid,
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
0,
NULL
);
//
// Deleting variable with current variable implementation shouldn't fail.
//
ASSERT_EFI_ERROR (Status);
//
// Start to build the boot option and try to boot
//
UnicodeSPrint (Buffer, sizeof (Buffer), L"Boot%04x", *BootNext);
BootOption = BdsLibVariableToOption (&TempList, Buffer);
ASSERT (BootOption != NULL);
BdsLibConnectDevicePath (BootOption->DevicePath);
BdsLibBootViaBootOption (BootOption, BootOption->DevicePath, &ExitDataSize, &ExitData);
FreePool(BootOption);
FreePool(BootNext);
}
}
/**
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;
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;
}
/**
For a bootable Device path, return its boot type.
@param DevicePath The bootable device Path to check
@retval BDS_EFI_MEDIA_HD_BOOT If given device path contains MEDIA_DEVICE_PATH type device path node
which subtype is MEDIA_HARDDRIVE_DP
@retval BDS_EFI_MEDIA_CDROM_BOOT If given device path contains MEDIA_DEVICE_PATH type device path node
which subtype is MEDIA_CDROM_DP
@retval BDS_EFI_ACPI_FLOPPY_BOOT If given device path contains ACPI_DEVICE_PATH type device path node
which HID is floppy device.
@retval BDS_EFI_MESSAGE_ATAPI_BOOT If given device path contains MESSAGING_DEVICE_PATH type device path node
and its last device path node's subtype is MSG_ATAPI_DP.
@retval BDS_EFI_MESSAGE_SCSI_BOOT If given device path contains MESSAGING_DEVICE_PATH type device path node
and its last device path node's subtype is MSG_SCSI_DP.
@retval BDS_EFI_MESSAGE_USB_DEVICE_BOOT If given device path contains MESSAGING_DEVICE_PATH type device path node
and its last device path node's subtype is MSG_USB_DP.
@retval BDS_EFI_MESSAGE_MISC_BOOT If the device path not contains any media device path node, and
its last device path node point to a message device path node.
@retval BDS_LEGACY_BBS_BOOT If given device path contains BBS_DEVICE_PATH type device path node.
@retval BDS_EFI_UNSUPPORT An EFI Removable BlockIO device path not point to a media and message device,
**/
UINT32
EFIAPI
BdsGetBootTypeFromDevicePath (
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;
}
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 MESSAGING_DEVICE_PATH:
//
// 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),
// skip 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_USB_DP:
BootType = BDS_EFI_MESSAGE_USB_DEVICE_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;
}
/**
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);
ASSERT (NewDevicePath != NULL);
*DevicePath = NewDevicePath;
return EFI_SUCCESS;
}
return EFI_NOT_FOUND;
}