Ryzen/AmiModulePkg/Bds/BootOptions.c
2022-12-23 15:14:44 +08:00

2674 lines
95 KiB
C

//***********************************************************************
//* *
//* Copyright (c) 1985-2020, American Megatrends International LLC. *
//* *
//* All rights reserved. Subject to AMI licensing agreement. *
//* *
//***********************************************************************
/**
* @file BootOptions.c
* Contains the code for dealing with boot options and their maintenance
*
*/
#include <BootOptions.h>
#include <Protocol/DiskInfo.h>
#include <Protocol/UsbIo.h>
#include <Protocol/NvmExpressPassthru.h>
#include <Protocol/PciIo.h>
#include <Library/DebugLib.h>
#include <Library/PrintLib.h>
#define BDS_DP_NODE(NodeType, Type, SubType) {(Type),(SubType),{sizeof(NodeType),0}}
#define BDS_END_OF_DP BDS_DP_NODE(EFI_DEVICE_PATH_PROTOCOL, END_DEVICE_PATH, END_ENTIRE_SUBTYPE)
EFI_HII_HANDLE HiiHandle=0;
EFI_GUID AmiBbsDevicePathGuid = AMI_BBS_DEVICE_PATH_GUID;
EFI_GUID AmiMaskedDevicePathGuid = AMI_MASKED_DEVICE_PATH_GUID;
EFI_GUID AmiDeviceNameDevicePathGuid = AMI_DEVICE_NAME_DEVICE_PATH_GUID;
EFI_GUID LegacyDevOrderGuid = LEGACY_DEV_ORDER_GUID;
DLIST BootOptionListStructure;
DLIST BootDeviceListStructure;
DLIST *BootOptionList = &BootOptionListStructure;
DLIST *BootDeviceList = &BootDeviceListStructure;
// Helper functions
// Defined in Bds.c
EFI_STATUS SetVariableWithNewAttributes(
IN CHAR16 *Name, IN EFI_GUID *Guid, IN UINT32 Attributes,
IN UINTN DataSize, IN VOID *Data
);
// Defined in BdsBoard.c
UINT32 GetSetupVariablesAttributes();
/**
* Sort the list of DLIST items using the passed COMPARE_FUNCTION to
* determine the correct ordering of items
*
* @param List Pointer to list of items to sort
* @param Compare Function to use to to sort the list
*/
VOID SortList(DLIST *List, COMPARE_FUNCTION Compare){
DLIST MergeList;
DLINK *p, *q, *e;
UINTN pSize, qSize, inSize;
UINT32 NoMerges;
UINTN i;
if(List->Size <= 1) //nothing to sort
return;
inSize = 1;
MergeList = *List;
while(1)
{
p = MergeList.pHead;
DListInit(&MergeList); //clear list
NoMerges = 0;
while(p != NULL)
{
NoMerges++;
q = p;
for(i = 0, pSize = 0; i < inSize; i++)
{
pSize++;
q = q->pNext;
if(q == NULL)
break;
}
qSize = inSize;
while(pSize > 0 || (qSize > 0 && q != NULL))
{
if(pSize == 0)
{
e = q;
q = q->pNext;
DListAdd(&MergeList, e);
qSize--;
}
else if(qSize == 0 || q == NULL)
{
e = p;
p = p->pNext;
DListAdd(&MergeList, e);
pSize--;
}
else if(Compare(p, q) > 0)
{
e = q;
q = q->pNext;
DListAdd(&MergeList, e);
qSize--;
}
else
{
e = p;
p = p->pNext;
DListAdd(&MergeList, e);
pSize--;
}
}
p = q;
}
if(NoMerges <= 1)
break;
inSize *= 2;
}
*List = MergeList;
}
/**
* Compare the two boot options passed and determine the priority of
* the first parameter in relation to the second parameter
* Try to compare based on the following parameters in order,
* Tag, then Group Header, then Priority
*
* @param Link1 Pointer to boot option 1
* @param Link2 Pointer to boot option 2
*
* @retval INT32 the comparison result Less than zero - Boot option 1 is lower priority than boot option 2
* @retval zero boot option 1 is same priority as boot option 2 greater than zero - boot option 1 is a higher priority than boot option 2
*/
INT32 CompareTagThenPriority(DLINK *Link1, DLINK *Link2){
BOOT_OPTION *Option1 = (BOOT_OPTION*)Link1;
BOOT_OPTION *Option2 = (BOOT_OPTION*)Link2;
if (Option1->Tag < Option2->Tag) return -1;
else if (Option1->Tag > Option2->Tag) return 1;
if (Option1->GroupHeader != Option2->GroupHeader)
return (Option1->GroupHeader) ? -1 : 1;
if (Option1->Priority < Option2->Priority) return -1;
else if (Option1->Priority > Option2->Priority) return 1;
return 0;
}
/**
* Compare the two passed boot options first by their priority
* if their priorities are equal, compare them based on their
* boot option number
*
* @param Link1 Pointer to boot option 1
* @param Link2 Pointer to boot option 2
*
* @retval INT32 the comparison result Less than zero - Boot option 1 is lower priority than boot option 2
* @retval zero boot option 1 is same priority as boot option 2 greater than zero - boot option 1 is a higher priority than boot option 2
*/
INT32 ComparePriorityThenBootOptionNumber(DLINK *Link1, DLINK *Link2){
BOOT_OPTION *Option1 = (BOOT_OPTION*)Link1;
BOOT_OPTION *Option2 = (BOOT_OPTION*)Link2;
if (Option1->Priority < Option2->Priority) return -1;
else if (Option1->Priority > Option2->Priority) return 1;
if (Option1->BootOptionNumber < Option2->BootOptionNumber) return -1;
else if (Option1->BootOptionNumber > Option2->BootOptionNumber) return 1;
if (Option1->GroupHeader != Option2->GroupHeader)
return (Option1->GroupHeader) ? -1 : 1;
return 0;
}
/**
* Create a BOOT_OPTION for the first entry in the passed BootOptionList
*
* @param BootOptionList The list entry that needs a BOOT_OPTION structure created
*
* @retval BOOT_OPTION Pointer to the created BOOT_OPTION
*/
BOOT_OPTION* CreateBootOption(DLIST *BootOptions){
static BOOT_OPTION BootOptionTemplate = {
{NULL,NULL}, LOAD_OPTION_ACTIVE, NULL, NULL, 0, NULL, 0,
INVALID_BOOT_OPTION_NUMBER, LOWEST_BOOT_OPTION_PRIORITY,
UNASSIGNED_HIGHEST_TAG,
INVALID_HANDLE, INVALID_BBS_INDEX, NULL, FALSE, FALSE
};
BOOT_OPTION *Option = Malloc(sizeof(BOOT_OPTION));
ASSERT(Option!=NULL);
*Option = BootOptionTemplate;
DListAdd(BootOptions, &Option->Link);
return Option;
}
/**
* Delete the passed BOOT_OPTION from the BootOptionList
*
* @param BootOptionList the master boot option list
* @param Option the option that should be deleted from the master boot option list
*/
VOID DeleteBootOption(DLIST *BootOptions, BOOT_OPTION *Option){
DListDelete(BootOptions,&Option->Link);
if (Option->Description!=NULL) pBS->FreePool(Option->Description);
if (Option->FilePathList!=NULL) pBS->FreePool(Option->FilePathList);
if (Option->OptionalDataSize!=0) pBS->FreePool(Option->OptionalData);
pBS->FreePool(Option);
}
/**
* Create a boot device entry and add it to the BootDeviceList
* fill the entry with the information from the passed parameters
*
* @param BootDeviceList The master boot device list to add the new boot device entry
* @param DeviceHandle The handle of the new device
* @param BbsIndex The index in the BBS table of the device
* @param BbsEntry The entire BBS table entry for the device
*
* @retval BOOT_DEVICE Pointer to the new boot device
*/
BOOT_DEVICE* CreateBootDevice(
DLIST *BootDevices, EFI_HANDLE DeviceHandle,
UINT16 BbsIndex, BBS_TABLE *BbsEntry
){
BOOT_DEVICE *Device = Malloc(sizeof(BOOT_DEVICE));
ASSERT(Device!=NULL);
Device->BbsEntry = BbsEntry;
Device->BbsIndex = BbsIndex;
Device->DeviceHandle = DeviceHandle;
DListAdd(BootDevices, &Device->Link);
return Device;
}
/**
* Delete the Device out of the passed BootDeviceList
*
* @param BootDeviceList The list to remove the Device from
* @param Device The entry to remove from the BootDeviceList
*/
VOID DeleteBootDevice(DLIST *BootDevices, BOOT_DEVICE* Device){
DListDelete(BootDevices, &Device->Link);
pBS->FreePool(Device);
}
/**
* Update the boot option with the information from the passed boot device information
*
* @param Option The boot option that needs updated
* @param Device The boot device that contains the information that the boot option needs updated with
*/
VOID UpdateBootOptionWithBootDeviceInfo(
BOOT_OPTION *Option, BOOT_DEVICE* Device
){
Option->BbsEntry = Device->BbsEntry;
Option->BbsIndex = Device->BbsIndex;
Option->DeviceHandle = Device->DeviceHandle;
}
/**
* Create a boot option using the NVRAM information
*
* @param BootOptionList The master boot option list
* @param BootOptionNumber The XXXX of the BootXXXX boot option
* @param NvramOption The associated EFI_LOAD_OPTION
* @param NvramOptionSize The size of the NVRAM option
* @param Priority The priority of the boot option
*
* @retval BOOT_OPTION The created boot option
*/
BOOT_OPTION* CreateBootOptionsFromNvramOption(
DLIST *BootOptions, UINT16 BootOptionNumber,
EFI_LOAD_OPTION *NvramOption, UINTN NvramOptionSize,
UINT32 *Priority
){
BOOT_OPTION *Option;
UINTN DescriptionSize;
UINT32 *OptionalData;
UINT8 *FilePathList;
UINTN OptionalDataSize;
Option = CreateBootOption(BootOptions);
Option->Attributes=NvramOption->Attributes;
DescriptionSize = (Wcslen((CHAR16*)(NvramOption+1))+1)*sizeof(CHAR16);
Option->Description = Malloc(DescriptionSize);
MemCpy(Option->Description, NvramOption+1, DescriptionSize);
FilePathList = (UINT8*)(NvramOption+1)+DescriptionSize;
Option->FilePathListLength = NvramOption->FilePathListLength;
Option->FilePathList = Malloc(Option->FilePathListLength);
MemCpy(
Option->FilePathList, FilePathList, Option->FilePathListLength
);
OptionalData = (UINT32*)(FilePathList + NvramOption->FilePathListLength);
OptionalDataSize = (UINT8*)NvramOption
+ NvramOptionSize
- (UINT8*)OptionalData;
Option->BootOptionNumber = BootOptionNumber;
Option->Priority = *Priority;
*Priority = GetNextBootOptionPriority(*Priority);
if ( OptionalDataSize >= sizeof(UINT32) ){
if ( ReadUnaligned32(OptionalData) == AMI_SIMPLE_BOOT_OPTION_SIGNATURE ){
OptionalDataSize -= sizeof(UINT32);
OptionalData++;
Option->FwBootOption = TRUE;
}else if ( ReadUnaligned32(OptionalData) == AMI_GROUP_BOOT_OPTION_SIGNATURE ){
Option->FwBootOption = TRUE;
Option->GroupHeader = TRUE;
OptionalDataSize -= sizeof(UINT32);
OptionalData++;
while ( OptionalDataSize > AMI_NESTED_BOOT_OPTION_HEADER_SIZE){
NESTED_BOOT_OPTION *NestedBootOption = (NESTED_BOOT_OPTION*)OptionalData;
if ( NestedBootOption->Signature != AMI_NESTED_BOOT_OPTION_SIGNATURE
|| NestedBootOption->Size > OptionalDataSize
) break;
CreateBootOptionsFromNvramOption(
BootOptions, BootOptionNumber, &NestedBootOption->Option,
NestedBootOption->Size-AMI_NESTED_BOOT_OPTION_HEADER_SIZE,
Priority
);
OptionalDataSize -= NestedBootOption->Size;
OptionalData = (UINT32*)((UINT8*)OptionalData+NestedBootOption->Size);
}
}
}
if (OptionalDataSize==0){
Option->OptionalData = NULL;
Option->OptionalDataSize = 0;
}else{
Option->OptionalData = Malloc(OptionalDataSize);
MemCpy(
Option->OptionalData, OptionalData, OptionalDataSize
);
Option->OptionalDataSize = OptionalDataSize;
}
return Option;
}
/**
* The function checks if boot option is well-formed
*
* @param NvramOption Pointer to the EFI_LOAD_OPTION structure
* @param NvramOptionSize The size of the EFI_LOAD_OPTION, including the optional data section
*
* @retval TRUE boot option is valid, FALSE otherwise
*/
BOOLEAN IsBootOptionValid(EFI_LOAD_OPTION *NvramOption, UINTN NvramOptionSize){
CHAR16 *Char;
CHAR16 *EndOfDescription;
EFI_DEVICE_PATH_PROTOCOL *Dp;
UINTN DevicePathSize;
// The boot option must have at least the header,
// an empty (just the NULL-terminator) description,
// and an empty device path (End-of-Device-Path node).
if (NvramOption->FilePathListLength<sizeof(EFI_DEVICE_PATH_PROTOCOL)) return FALSE;
if (NvramOptionSize < sizeof(*NvramOption)+sizeof(CHAR16)) return FALSE;
NvramOptionSize -= sizeof(*NvramOption);
if ( NvramOption->FilePathListLength >= NvramOptionSize ) return FALSE;
NvramOptionSize -= NvramOption->FilePathListLength;
// The description must include at least the NULL-terminator
if (NvramOptionSize < sizeof(CHAR16)) return FALSE;
// The description must be NULL-terminated
Char = (CHAR16*)(NvramOption+1);
EndOfDescription = (CHAR16*)((CHAR8*)Char+NvramOptionSize);
while( *Char && Char < EndOfDescription) Char++;
if (Char==EndOfDescription) return FALSE;
// Validate the device path;
Dp = (EFI_DEVICE_PATH_PROTOCOL*)(Char+1); // skip the terminating zero.
DevicePathSize = NvramOption->FilePathListLength;
while (TRUE){
UINTN Length;
if (EFI_ERROR(IsValidDevicePath(Dp))) return FALSE;
Length = DPLength(Dp);
if (Length > DevicePathSize) return FALSE;
DevicePathSize -= Length;
Dp = (EFI_DEVICE_PATH_PROTOCOL*)((UINT8*)Dp+Length);
if (DevicePathSize < sizeof(EFI_DEVICE_PATH_PROTOCOL)){
if (DevicePathSize != 0 ) return FALSE;
//The last node must be an End-of-Device-Path node.
Dp = (EFI_DEVICE_PATH_PROTOCOL*)((UINT8*)Dp - sizeof(EFI_DEVICE_PATH_PROTOCOL));
return
isEndNode(Dp)
&& Dp->SubType == END_ENTIRE_SUBTYPE
&& NODE_LENGTH(Dp) == sizeof(EFI_DEVICE_PATH_PROTOCOL)
;
}
}
//Should never reach this point.
return FALSE;
}
/**
* Read the boot options from NVRAM and create associated boot options in the master boot option list for each boot option
*/
VOID ReadBootOptions(){
UINT16 *BootOrder = NULL;
UINTN BootOrderSize = 0;
EFI_STATUS Status;
UINTN i;
EFI_LOAD_OPTION *NvramOption = NULL;
UINT32 Priority=IndexToBootPriority(0);
UINTN NvramOptionSize;
UINT16 MaxBootOptionNumber=0;
Status=GetEfiVariable(
L"BootOrder", &gEfiGlobalVariableGuid, NULL, &BootOrderSize,(VOID**)&BootOrder
);
if (EFI_ERROR(Status)) return;
for(i=0; i<BootOrderSize/sizeof(UINT16); i++){
CHAR16 BootStr[9];
// Get Boot Option
UnicodeSPrint(BootStr, 9*sizeof(CHAR16), L"Boot%04X",BootOrder[i]);
Status=GetEfiVariable(
BootStr, &gEfiGlobalVariableGuid, NULL, &NvramOptionSize,(VOID**)&NvramOption
);
if (EFI_ERROR(Status)){
if (Status==EFI_NOT_FOUND){
// Workaround for non-UEFI specification complaint OS.
// Some OS create BootXXXX variables using lower case letters A-F.
// Search for lower case boot option variable if no upper case variable found.
UnicodeSPrint(BootStr, 9*sizeof(CHAR16), L"Boot%04X",BootOrder[i]);
Status=GetEfiVariable(
BootStr, &gEfiGlobalVariableGuid, NULL, &NvramOptionSize,(VOID**)&NvramOption
);
}
if (EFI_ERROR(Status)) continue;
}
//Let's verify if boot option is well-formed
if (!IsBootOptionValid(NvramOption, NvramOptionSize)){
DEBUG((DEBUG_ERROR,"The boot option Boot%04X is ill-formed. Deleting...\n",
BootOrder[i]
));
pRS->SetVariable(BootStr, &gEfiGlobalVariableGuid, 0, 0, NULL);
continue;
}
CreateBootOptionsFromNvramOption(
BootOptionList, BootOrder[i], NvramOption, NvramOptionSize, &Priority
);
if (BootOrder[i] > MaxBootOptionNumber)
MaxBootOptionNumber = BootOrder[i];
}
pBS->FreePool(NvramOption);
pBS->FreePool(BootOrder);
}
/**
* Prepend the masking pattern to the device path of the passed BOOT_OPTION
*
* @param Option The boot option which needs to have the device path updated
*/
VOID MaskFilePathList(BOOT_OPTION *Option){
static struct {
VENDOR_DEVICE_PATH vendor;
EFI_DEVICE_PATH_PROTOCOL end;
} MaskedDp = {
{
BDS_DP_NODE(VENDOR_DEVICE_PATH, HARDWARE_DEVICE_PATH, HW_VENDOR_DP),
AMI_MASKED_DEVICE_PATH_GUID
},
BDS_END_OF_DP
};
static UINT32 MaskedDpLength = sizeof(MaskedDp);
UINT8 *NewDp;
NewDp = Malloc(Option->FilePathListLength + MaskedDpLength);
if (NewDp==NULL) return;
MemCpy(NewDp, &MaskedDp, MaskedDpLength);
MemCpy(
NewDp+MaskedDpLength, Option->FilePathList,
Option->FilePathListLength
);
pBS->FreePool(Option->FilePathList);
Option->FilePathList = (EFI_DEVICE_PATH_PROTOCOL*)NewDp;
Option->FilePathListLength += MaskedDpLength;
}
/**
* This function unmasks orphan devices. The orphan legacy devices are masked by the MaskOrphanDevices function.
* TSE does not like legacy boot options for devices that are not currently attached to the system. To trick TSE
* we we are camouflaging such boot options by adding a vendor GUIDed device path. The device path is added by
* the MaskOrphanDevices function before saving, and is removed by UnmaskOrphanDevices immediately after reading
*/
VOID UnmaskOrphanDevices(){
DLINK *Link;
BOOT_OPTION *Option;
VENDOR_DEVICE_PATH *MaskedDp;
UINTN MaskedDpLength;
FOR_EACH_BOOT_OPTION(BootOptionList,Link,Option){
MaskedDp = (VENDOR_DEVICE_PATH*)Option->FilePathList;
if ( MaskedDp->Header.Type != HARDWARE_DEVICE_PATH
|| MaskedDp->Header.SubType != HW_VENDOR_DP
|| guidcmp(&AmiMaskedDevicePathGuid, &MaskedDp->Guid) != 0
) continue;
MaskedDpLength = DPLength(&MaskedDp->Header);
if (Option->FilePathListLength <= MaskedDpLength) continue;
Option->FilePathListLength -=MaskedDpLength;
MemCpy(
Option->FilePathList, (UINT8*)Option->FilePathList+MaskedDpLength,
Option->FilePathListLength
);
if(!IsLegacyBootOption(Option))
Option->Attributes &= ~LOAD_OPTION_HIDDEN;
}
}
/**
* Using the passed position, return the BBS index from the legacy dev order group
*
* @param Group The legacy device order
* @param Position the index into the legacy dev order to return
*
* @retval UINT32 the BBS index from the the legacy dev order
*
* @note This function is only available, and used, if CSM_SUPPORT token is defined and enabled
*/
UINT32 GetBbsIndexByPositionInTheGroup(
LEGACY_DEVICE_ORDER *Group, UINT16 Position
){
UINT16* EndOfGroup = (UINT16*)(
(UINT8*)Group + Group->Length + sizeof(UINT32)
);
UINT16 *IndexPtr = &Group->Device[0];
UINT32 Counter = 0;
while(IndexPtr<EndOfGroup){
if (Counter == Position)
return *IndexPtr;
IndexPtr++;
Counter++;
}
return 0;
}
/**
* Get the corresponding device index in the LEGACY_DEV_ORDER based on the bbs index number. Legacy
* dev order organizes the boot priorities of a class of devices (Cdrom, hdd, bev, etc) and the
* individual indexes of the device are the relative priorities in which they should be used to
* attempt a legacy boot.
*
* @param Group pointer to the legacy dev order group
* @param BbsIndex index of the device trying to be matched
*
* @retval UINT32 the index of the device inside of the LEGACY_DEV_ORDER
*
* @note function only available, and used, if CSM_SUPPORT token is defined and enabled
*/
UINT32 FindPositionInTheGroupByBbsIndex(
LEGACY_DEVICE_ORDER *Group, UINT16 BbsIndex
){
UINT16* EndOfGroup = (UINT16*)(
(UINT8*)Group + Group->Length + sizeof(UINT32)
);
UINT16 *IndexPtr = &Group->Device[0];
UINT32 Counter = 0;
while(IndexPtr<EndOfGroup){
if (*(UINT8*)IndexPtr == *(UINT8*)&BbsIndex)
return Counter;
IndexPtr++;
Counter++;
}
return 0;
}
/**
* Go through the legacy device order structure and find the legacy dev order index that matches the BBS index.
*
* @param DevOrder pointer to the legacy device order
* @param DevOrderSize size of the legacy dev order structure
* @param BbsIndex index of the BBS device to match
* @retval LEGACY_DEVICE_ORDER *
*
* @note function only available, and used, if CSM_SUPPORT token is defined and enabled
*/
LEGACY_DEVICE_ORDER* FindLegacyDeviceGroupByBbsIndex(
LEGACY_DEVICE_ORDER *DevOrder, UINTN DevOrderSize, UINT16 BbsIndex
){
UINT16 *EndOfDevOrder = (UINT16*)((UINT8*)DevOrder+DevOrderSize);
LEGACY_DEVICE_ORDER* EndOfGroup = DevOrder;
LEGACY_DEVICE_ORDER* GroupStart = DevOrder;
UINT16 *IndexPtr = (UINT16*)EndOfGroup;
while(IndexPtr<EndOfDevOrder){
if (IndexPtr==(UINT16*)EndOfGroup){
IndexPtr = &EndOfGroup->Device[0];
GroupStart = EndOfGroup;
EndOfGroup = (LEGACY_DEVICE_ORDER*)(
(UINT8*)EndOfGroup + EndOfGroup->Length + sizeof(UINT32)
);
}
if (*(UINT8*)IndexPtr == *(UINT8*)&BbsIndex) return GroupStart;
IndexPtr++;
}
return NULL;
}
/**
* Go through the legacy dev order structure and adjust it to match the current boot priorities
*
* @param BootOptionList the master boot option list
*
* @note Function only available, and used, if CSM_SUPPORT token is defined and enabled
*/
VOID AdjustLegacyBootOptionPriorities(){
EFI_STATUS Status;
UINTN DevOrderSize, OldDevOrderSize;
LEGACY_DEVICE_ORDER *DevOrder=NULL, *OldDevOrder=NULL;
DLINK *Link;
UINT16 BootOptionNumber;
UINT16 *IndexPtr;
UINT16 *EndOfDeviceList;
BOOT_OPTION *Option;
UINT16 GroupSize=0;
INT32 IndexInTheGroup=0;
LEGACY_DEVICE_ORDER *CurrentGroup;
Status = GetEfiVariable(
L"LegacyDevOrder", &LegacyDevOrderGuid, NULL,
&DevOrderSize, (VOID **)&DevOrder
);
if(EFI_ERROR(Status) || DevOrderSize < sizeof(LEGACY_DEVICE_ORDER)) return ;
Status = GetEfiVariable(
L"OldLegacyDevOrder", &LegacyDevOrderGuid, NULL,
&OldDevOrderSize, (VOID **)&OldDevOrder
);
if( EFI_ERROR(Status)
|| OldDevOrderSize!=DevOrderSize
|| MemCmp(DevOrder,OldDevOrder, DevOrderSize)==0
){
pBS->FreePool(DevOrder);
if (!EFI_ERROR(Status)) pBS->FreePool(OldDevOrder);
return ;
}
BootOptionNumber = INVALID_BOOT_OPTION_NUMBER;
IndexPtr = (UINT16*)DevOrder;
EndOfDeviceList = (UINT16*)((UINT8*)IndexPtr+DevOrderSize);
FOR_EACH_BOOT_OPTION(BootOptionList,Link,Option){
INT32 NewIndexInTheGroup;
LEGACY_DEVICE_ORDER *OldGroup;
UINT16 BbsIndex;
if ( !IsLegacyBootOption(Option) || Option->GroupHeader) continue;
// this should never happen during the normal course of operation
if (IndexPtr>=EndOfDeviceList) break;
// during the normal course of operation both conditions should
// happen simultaneously
if (BootOptionNumber != Option->BootOptionNumber || GroupSize==0){
BootOptionNumber = Option->BootOptionNumber;
IndexPtr += GroupSize;
CurrentGroup = (LEGACY_DEVICE_ORDER*)IndexPtr;
GroupSize = (CurrentGroup->Length)/sizeof(UINT16)-1;
IndexPtr = &CurrentGroup->Device[0];
IndexInTheGroup = 0;
}
OldGroup = FindLegacyDeviceGroupByBbsIndex(OldDevOrder,DevOrderSize,*IndexPtr);
BbsIndex = (UINT16)GetBbsIndexByPositionInTheGroup(OldGroup,(UINT16)IndexInTheGroup);
NewIndexInTheGroup = FindPositionInTheGroupByBbsIndex(CurrentGroup,BbsIndex);
Option->Priority +=
(NewIndexInTheGroup-IndexInTheGroup)*DEFAULT_PRIORITY_INCREMENT;
if ((CurrentGroup->Device[NewIndexInTheGroup] & 0xff00)!=0)
Option->Attributes &= ~LOAD_OPTION_ACTIVE;
else
Option->Attributes |= LOAD_OPTION_ACTIVE;
IndexPtr++;
GroupSize--;
IndexInTheGroup++;
}
pBS->FreePool(DevOrder);
pBS->FreePool(OldDevOrder);
}
/**
* Go through the master boot option list and create memory representation of the legacy dev order variable
*
* @param BootOptionList the master boot option list
*
* @note function only available, and used, if CSM_SUPPORT token is defined and enabled
*/
VOID BuildLegacyDevOrderBuffer(
LEGACY_DEVICE_ORDER **DevOrderBuffer, UINTN *BufferSize
){
UINTN DevOrderSize;
LEGACY_DEVICE_ORDER *DevOrder, *GroupPtr;
UINT16 *DevPtr;
DLINK *Link;
UINT16 BootOptionNumber = INVALID_BOOT_OPTION_NUMBER;
BOOT_OPTION *Option;
if (DevOrderBuffer == NULL || BufferSize == NULL) return;
if (BootOptionList->Size == 0)
DevOrder = Malloc(sizeof(LEGACY_DEVICE_ORDER));
else
DevOrder = Malloc(BootOptionList->Size*sizeof(LEGACY_DEVICE_ORDER));
GroupPtr = DevOrder;
DevPtr = (UINT16*)DevOrder;
FOR_EACH_BOOT_OPTION(BootOptionList,Link,Option){
if ( !IsLegacyBootOption(Option) || Option->GroupHeader) continue;
if (Option->BootOptionNumber!=BootOptionNumber){
GroupPtr->Length = (UINT16)((UINT8*)DevPtr - (UINT8*)&GroupPtr->Length);
GroupPtr = (LEGACY_DEVICE_ORDER*)DevPtr;
BootOptionNumber = Option->BootOptionNumber;
GroupPtr->Type = GetLegacyDevOrderType(Option);
DevPtr = GroupPtr->Device;
}
if ((Option->Attributes&LOAD_OPTION_ACTIVE)==0)
*DevPtr = Option->BbsIndex | 0xff00;
else
*DevPtr = Option->BbsIndex;
DevPtr++;
}
GroupPtr->Length = (UINT16)((UINT8*)DevPtr - (UINT8*)&GroupPtr->Length);
DevOrderSize = (UINT8*)DevPtr - (UINT8*)DevOrder;
*DevOrderBuffer = DevOrder;
*BufferSize = DevOrderSize;
}
/**
* This function masks orphan devices before saving them. TSE does not like legacy boot
* options for devices that are not in the system. To trick TSE we we are camouflaging
* such boot options by adding GUIDed device path. The device path is added by
* MaskOrphanDevices function right before saving and removed by UnmaskOrphanDevices
* right after reading
*
* @param BootOptionList the master boot option list
*
* @note function only available, and used, if CSM_SUPPORT token is defined and enabled
*/
VOID MaskOrphanDevices(){
DLINK *Link;
BOOT_OPTION *Option;
BOOT_OPTION *GroupHeader = NULL;
SortList(BootOptionList, ComparePriorityThenBootOptionNumber);
FOR_EACH_BOOT_OPTION(BootOptionList,Link,Option){
if (!IsLegacyBootOption(Option))
continue;
if (Option->GroupHeader){
if (GroupHeader!=NULL) MaskFilePathList(GroupHeader);
GroupHeader=Option;
continue;
}
if (IsBootOptionWithDevice(Option)){
if (GroupHeader!=NULL && Option->BootOptionNumber==GroupHeader->BootOptionNumber)
GroupHeader=NULL;
continue;
}
MaskFilePathList(Option);
}
if (GroupHeader!=NULL) MaskFilePathList(GroupHeader);
}
/**
* Go through the master boot option list and use it to update the legacy dev order variable
*
* @param BootOptionList the master boot option list
*
* @note function only available, and used, if CSM_SUPPORT token is defined and enabled
**/
VOID SaveLegacyDevOrder(){
UINTN DevOrderSize;
LEGACY_DEVICE_ORDER *DevOrder;
SortList(BootOptionList, ComparePriorityThenBootOptionNumber);
BuildLegacyDevOrderBuffer(&DevOrder, &DevOrderSize);
SetVariableWithNewAttributes(
L"LegacyDevOrder", &LegacyDevOrderGuid,
GetSetupVariablesAttributes(),DevOrderSize,DevOrder
);
SetVariableWithNewAttributes(
L"OldLegacyDevOrder", &LegacyDevOrderGuid,
EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,DevOrderSize,DevOrder
);
pBS->FreePool(DevOrder);
}
/**
* Go through the BBS table and create a entry in the master boot order list for each
* BBS table entry
*
* @param BootDeviceList the master boot list
*
* @note function only available, and used, if CSM_SUPPORT token is defined and enabled
*/
VOID CollectBbsDevices(DLIST *BootDevices){
EFI_STATUS Status;
EFI_LEGACY_BIOS_PROTOCOL *LegacyBios;
UINT16 HddCount;
UINT16 BbsCount;
HDD_INFO *HddInfo;
BBS_TABLE *BbsTable;
UINT16 i;
Status = pBS->LocateProtocol(&gEfiLegacyBiosProtocolGuid, NULL, (VOID **)&LegacyBios);
if(EFI_ERROR(Status)) return ;
LegacyBios->GetBbsInfo(LegacyBios, &HddCount, &HddInfo, &BbsCount, &BbsTable);
for(i = 0; i < BbsCount; i++){
if(BbsTable[i].BootPriority == 0xffff) continue;
CreateBootDevice(
BootDevices,
(EFI_HANDLE)*(UINTN*)&BbsTable[i].IBV1,
i,&BbsTable[i]
);
}
}
/**
* Collect a list of all handles with a particular protocol installed on them and create a boot device for each handle.
*
* @param BootDeviceList the master boot list that needs boot devices added to it.
* @param ProtocolGuid the protocol to use when getting a list of device handles
*/
VOID CollectProtocolDevices(DLIST *BootDevices, EFI_GUID *ProtocolGuid){
EFI_HANDLE *Devices;
UINTN NumberOfDevices;
EFI_STATUS Status;
UINTN i;
Status = pBS->LocateHandleBuffer(
ByProtocol,ProtocolGuid, NULL,
&NumberOfDevices, &Devices
);
if (EFI_ERROR(Status)) return;
for(i=0; i<NumberOfDevices; i++){
CreateBootDevice(BootDevices,Devices[i],INVALID_BBS_INDEX,NULL);
}
pBS->FreePool(Devices);
}
/**
* Helper function to generate a master boot list based on the load file protocol,
* the simple file system protocol and, if CSM support is enabled, the legacy bbs
* table
*/
VOID CollectBootDevices(){
CollectProtocolDevices(BootDeviceList,&gEfiLoadFileProtocolGuid);
CollectProtocolDevices(BootDeviceList,&gEfiSimpleFileSystemProtocolGuid);
CollectBbsDevices(BootDeviceList);
}
/**
* Determine if this boot device is a UEFI HDD
*
* @param Device the device in question
*
* @retval BOOLEAN TRUE - Device is a UEFI HDD Boot Device and it should be removed from the boot order list
* @retval FALSE Device is not a UEFI HDD and it should be left in the boot order
*/
BOOLEAN IsUefiHddBootDevice(BOOT_DEVICE *Device){
EFI_BLOCK_IO_PROTOCOL *BlkIo;
EFI_STATUS Status;
if ( Device->DeviceHandle == INVALID_HANDLE
|| Device->BbsEntry != NULL
) return FALSE;
Status=pBS->HandleProtocol(
Device->DeviceHandle, &gEfiBlockIoProtocolGuid, (VOID**)&BlkIo
);
if (EFI_ERROR(Status)) return FALSE;
return !BlkIo->Media->RemovableMedia;
}
/**
* Master filter handler. Function will call all ELINK functions linked into the
* BootOptionBootDeviceFilteringFunctions list. If any ELINK function returns FALSE, the device will
* be removed from the function list
*/
VOID FilterBootDeviceList(){
// Filter Devices
BOOT_DEVICE *Device;
DLINK *Link;
FOR_EACH_BOOT_DEVICE(BootDeviceList, Link, Device){
UINT32 i;
for(i=0; FilteringFunction[i]!=NULL; i++)
if (FilteringFunction[i](Device)){
DeleteBootDevice(BootDeviceList, Device);
break;
}
}
}
/**
* Attempt to match the Boot Option Device path to this device by locating the handle associated
* with the passed device path and comparing it against the BOOT_DEVICE handle.
*
* @param OptionDevicePath The device path to match
* @param Device The device to match against
*
* @retval TRUE The device Path matches the device
* @retval FALSE The device is different than the device
*/
BOOLEAN LocateDevicePathTest(EFI_DEVICE_PATH_PROTOCOL *OptionDevicePath, BOOT_DEVICE *Device){
EFI_STATUS Status;
EFI_HANDLE Handle;
if (Device->DeviceHandle==INVALID_HANDLE) return FALSE;
Status=pBS->LocateDevicePath(
&gEfiDevicePathProtocolGuid, &OptionDevicePath, &Handle
);
if (EFI_ERROR(Status)) return FALSE;
if (Handle != Device->DeviceHandle) return FALSE;
if (isEndNode(OptionDevicePath)) return TRUE;
if (OptionDevicePath->Type != MEDIA_DEVICE_PATH) return FALSE;
return OptionDevicePath->SubType==MEDIA_FILEPATH_DP
|| OptionDevicePath->SubType==MEDIA_FV_FILEPATH_DP;
}
/**
* Attempt to match the passed device path to the passed boot device. Because
* of partial device paths that some OSes use, this requires extra logic perform.
*
* @param OptionDevicePath The device path to check
* @param Device The boot device
*
* @retval TRUE The device path matches the device
* @retval FALSE The device path does not match the device
*/
BOOLEAN PartitionDevicePathTest(
EFI_DEVICE_PATH_PROTOCOL *OptionDevicePath, BOOT_DEVICE *Device
){
EFI_STATUS Status;
EFI_BLOCK_IO_PROTOCOL *BlockIo;
EFI_DEVICE_PATH_PROTOCOL *PartitionDevicePath;
HARDDRIVE_DEVICE_PATH* BootParitionDevicePath;
HARDDRIVE_DEVICE_PATH* PartitionNode;
if (Device->DeviceHandle==INVALID_HANDLE) return FALSE;
if ( OptionDevicePath->Type!=MEDIA_DEVICE_PATH
|| OptionDevicePath->SubType!=MEDIA_HARDDRIVE_DP
) return FALSE;
BootParitionDevicePath = (HARDDRIVE_DEVICE_PATH*)OptionDevicePath;
Status = pBS->HandleProtocol(
Device->DeviceHandle,&gEfiBlockIoProtocolGuid,(VOID**)&BlockIo
);
if (EFI_ERROR(Status)) return FALSE;
// if this is not partition, continue
if (!BlockIo->Media->LogicalPartition) return FALSE;
Status = pBS->HandleProtocol(
Device->DeviceHandle,&gEfiDevicePathProtocolGuid,(VOID**)&PartitionDevicePath
);
if (EFI_ERROR(Status)) return FALSE;
// Get last node of the device path. It should be partition node
PartitionNode = (HARDDRIVE_DEVICE_PATH*)DPGetLastNode(PartitionDevicePath);
//Check if our partition matches Boot partition
if ( PartitionNode->Header.Type!=MEDIA_DEVICE_PATH
|| PartitionNode->Header.SubType!=MEDIA_HARDDRIVE_DP
) return FALSE;
if ( PartitionNode->PartitionNumber==BootParitionDevicePath->PartitionNumber
&& PartitionNode->SignatureType==BootParitionDevicePath->SignatureType
&& !MemCmp(PartitionNode->Signature,BootParitionDevicePath->Signature,16)
){
return TRUE;
}
return FALSE;
}
/**
* Attempt to locate a device path node in the device path that matches the passed type, and
* return the sub-type of that node
*
* @param Dp The device path to search
* @param Type The type to search for
*
* @retval Zero No node was found with the Type
* @retval Non-Zero A node was found the sub-type was returned
*/
UINT8 GetDevicePathSubtype(EFI_DEVICE_PATH_PROTOCOL *Dp, UINT8 Type){
UINT8 SubType;
if (Dp == NULL) return 0;
SubType = 0;
for( ; !(isEndNode(Dp)); Dp=NEXT_NODE(Dp))
if (Dp->Type == Type) SubType = Dp->SubType;
return SubType;
}
/**
* Attempt to locate a device path node in the passed device path of type Type.
* If DevicePath contains multiple nodes of type Type, the last node is returned.
*
* @param Dp The device path to search
* @param Type The type of node to search for
*
* @retval NULL No node was found
* @retval Non-null A node was found, and the pointer was returned.
* If DevicePath contains multiple nodes of type Type, the last node is returned.
*/
EFI_DEVICE_PATH_PROTOCOL* GetLastDevicePathNodeOfType(EFI_DEVICE_PATH_PROTOCOL *Dp, UINT8 Type){
EFI_DEVICE_PATH_PROTOCOL *Node = NULL;
if (Dp == NULL) return NULL;
for( ; !(isEndNode(Dp)); Dp=NEXT_NODE(Dp))
if (Dp->Type == Type) Node = Dp;
return Node;
}
/**
* Attempt to locate a device path node in the passed device path of type Type.
* If DevicePath contains multiple nodes of type Type, the first node is returned.
*
* @param Dp The device path to search
* @param Type The type of node to search for
*
* @retval NULL No node was found
* @retval Non-null A node was found, and the pointer was returned.
* If DevicePath contains multiple nodes of type Type, the first node is returned.
*/
EFI_DEVICE_PATH_PROTOCOL* GetFirstDevicePathNodeOfType(EFI_DEVICE_PATH_PROTOCOL *Dp, UINT8 Type){
if (Dp == NULL) return NULL;
for( ; !(isEndNode(Dp)); Dp=NEXT_NODE(Dp))
if (Dp->Type == Type) return Dp;
return NULL;
}
/**
* Attempt to match the passed device path to the passed BOOT_DEVICE. Handle the partial device
* path cases.
*
* @param OptionDevicePath The device path to match
* @param Device The device to attempt to match to the device path
*
* @retval TRUE The device path matches the device
* @retval FALSE The device path does not match the device
*/
BOOLEAN DeviceTypeDevicePathTest(EFI_DEVICE_PATH_PROTOCOL *OptionDevicePath, BOOT_DEVICE *Device){
EFI_STATUS Status;
EFI_DEVICE_PATH_PROTOCOL *Dp;
UINT8 DeviceInterface, OptionInterface;
EFI_DEVICE_PATH *DeviceMedia, *OptionMedia;
if (Device->DeviceHandle==INVALID_HANDLE){
AMI_BBS_DEVICE_PATH *AmiBbsDp;
if (Device->BbsEntry==NULL) return FALSE;
AmiBbsDp = (AMI_BBS_DEVICE_PATH*)OptionDevicePath;
if ( AmiBbsDp->Header.Header.Type != HARDWARE_DEVICE_PATH
|| AmiBbsDp->Header.Header.SubType != HW_VENDOR_DP
|| guidcmp(&AmiBbsDevicePathGuid, &AmiBbsDp->Header.Guid) != 0
){
AmiBbsDp = DPGetLastNode(OptionDevicePath);
}
return AmiBbsDp->Header.Header.Type == HARDWARE_DEVICE_PATH
&& AmiBbsDp->Header.Header.SubType == HW_VENDOR_DP
&& guidcmp(&AmiBbsDevicePathGuid, &AmiBbsDp->Header.Guid) == 0
&& AmiBbsDp->Class == Device->BbsEntry->Class
&& AmiBbsDp->SubClass == Device->BbsEntry->SubClass;
}
Status=pBS->HandleProtocol(
Device->DeviceHandle, &gEfiDevicePathProtocolGuid, (VOID**)&Dp
);
if (EFI_ERROR(Status)) return FALSE;
DeviceInterface = GetDevicePathSubtype(
Dp, MESSAGING_DEVICE_PATH
);
//If DeviceInterface is 0, the interface type is unknown.
//Can't do the matching.
if (DeviceInterface == 0) return FALSE;
if (DeviceInterface == MSG_SATA_DP)
DeviceInterface = MSG_ATAPI_DP;
OptionInterface = GetDevicePathSubtype(
OptionDevicePath, MESSAGING_DEVICE_PATH
);
if (OptionInterface == MSG_SATA_DP)
OptionInterface = MSG_ATAPI_DP;
if (DeviceInterface != OptionInterface) return FALSE;
DeviceMedia = GetLastDevicePathNodeOfType(Dp,MEDIA_DEVICE_PATH);
OptionMedia = GetLastDevicePathNodeOfType(OptionDevicePath,MEDIA_DEVICE_PATH);
if ( DeviceMedia != NULL && OptionMedia != NULL
&& NODE_LENGTH(DeviceMedia) == NODE_LENGTH(OptionMedia)
&& MemCmp(DeviceMedia,OptionMedia,NODE_LENGTH(DeviceMedia)) == 0
){
NormalizeBootOptionDevicePath = TRUE;
return TRUE;
}
return FALSE;
}
#define MSG_USB_DP_PRESENT 0x1
#define MSG_USB_CLASS_DP_PRESENT 0x2
#define MSG_USB_WWID_CLASS_DP_PRESENT 0x4
/**
* Check if the passed device path corresponds to a USB device
*
* @param Dp The device path to check for belonging to a USB device
* @param AvailableNodes OPTIONAL A bitmask of the USB device nodes encountered while parsing the device path
*
* @retval TRUE The device path is a USB device
* @retval FALSE The device path is not a USB device
*/
BOOLEAN IsUsbDp(
IN EFI_DEVICE_PATH_PROTOCOL *Dp,
OUT UINT32 *AvailableNodes OPTIONAL
)
{
UINT32 Flags = 0;
for( ; !(isEndNode(Dp)); Dp = NEXT_NODE(Dp)) {
if(Dp->Type == MESSAGING_DEVICE_PATH) {
if(Dp->SubType == MSG_USB_DP) {
Flags |= MSG_USB_DP_PRESENT;
continue;
}
if(Dp->SubType == MSG_USB_CLASS_DP) {
Flags |= MSG_USB_CLASS_DP_PRESENT;
continue;
}
if(Dp->SubType == MSG_USB_WWID_CLASS_DP) {
Flags |= MSG_USB_WWID_CLASS_DP_PRESENT;
}
}
}
if(AvailableNodes != NULL)
*AvailableNodes = Flags;
return (Flags != 0) ? TRUE : FALSE;
}
/**
* Attempt to match the Device path to the passed Device of type USB
*
* @param OptionDevicePath The device path to attempt to match
* @param Device The device to attempt to match
*
* @retval TRUE The device path matches the device
* @retval FALSE The device path does not match the device
*/
BOOLEAN UsbClassDevicePathTest(
IN EFI_DEVICE_PATH_PROTOCOL *OptionDevicePath,
IN BOOT_DEVICE *Device
)
{
EFI_STATUS Status;
USB_CLASS_DEVICE_PATH *UsbDp;
EFI_DEVICE_PATH_PROTOCOL *DevDp;
UINT32 AvailableNodes;
if(OptionDevicePath->Type != MESSAGING_DEVICE_PATH ||
OptionDevicePath->SubType != MSG_USB_CLASS_DP)
return FALSE; //boot option is not USB class boot option
Status = pBS->HandleProtocol(Device->DeviceHandle, &gEfiDevicePathProtocolGuid, (VOID**)&DevDp);
if(EFI_ERROR(Status))
return FALSE; //device doesn't support EFI_DEVICE_PATH protocol
if(!IsUsbDp(DevDp, &AvailableNodes))
return FALSE; //device is not a USB device
UsbDp = (USB_CLASS_DEVICE_PATH *)OptionDevicePath;
return (UsbDp->VendorId == 0xffff &&
UsbDp->ProductId == 0xffff &&
UsbDp->DeviceClass == 0xff &&
UsbDp->DeviceSubClass == 0xff &&
UsbDp->DeviceProtocol == 0xff) ? TRUE : FALSE;
// We don't support matching of a USB Class device path node to a specific USB device.
// We only support blanket matching to any USB device as per
// the UEFI 2.4 specification, Section 3.1.2:
//"If any of the ID, Product ID, Device Class, Device Subclass, or Device Protocol contain all F's
// (0xFFFF or 0xFF), this element is skipped for the purpose of matching."
}
/**
* Attempt to match the passed option with the boot device handling specifics of
* boot from URI devices.
*
* @param OptionDevicePath The device path to check
* @param Device The boot device
*
* @retval TRUE The device path matches the device
* @retval FALSE The device path does not match the device
*/
BOOLEAN UriDevicePathTest(
IN EFI_DEVICE_PATH_PROTOCOL *OptionDevicePath,
IN BOOT_DEVICE *Device
)
{
EFI_STATUS Status;
URI_DEVICE_PATH *OptionUriDp, *DeviceUriDp;
EFI_DEVICE_PATH_PROTOCOL *HandleDevicePath, *DevDp, *OptDp;
UINTN DpLength;
//Check if this is a URI boot option
OptionUriDp = (URI_DEVICE_PATH*)GetLastDevicePathNodeOfType(OptionDevicePath, MESSAGING_DEVICE_PATH);
if ( OptionUriDp == NULL
|| OptionUriDp->Header.SubType!=MSG_URI_DP
) return FALSE; // not a URI boot option
Status = pBS->HandleProtocol(Device->DeviceHandle, &gEfiDevicePathProtocolGuid, (VOID**)&HandleDevicePath);
if(EFI_ERROR(Status)) return FALSE;
DeviceUriDp = (URI_DEVICE_PATH*)GetLastDevicePathNodeOfType(HandleDevicePath, MESSAGING_DEVICE_PATH);
if ( DeviceUriDp == NULL
|| DeviceUriDp->Header.SubType!=MSG_URI_DP
) return FALSE; // not a URI boot device
// Handle short form of URI device path
if (&OptionUriDp->Header == OptionDevicePath){ // option device path starts with URI node
DpLength = NODE_LENGTH(&DeviceUriDp->Header);
return DpLength == sizeof(EFI_DEVICE_PATH_PROTOCOL) // Device has an empty URI
|| (DpLength == (UINTN)NODE_LENGTH(&OptionUriDp->Header)
&& MemCmp( OptionUriDp->Uri, DeviceUriDp->Uri, DpLength - sizeof(EFI_DEVICE_PATH_PROTOCOL) ) == 0);
}
// Handle long form of URI device path
for( DevDp = HandleDevicePath, OptDp=OptionDevicePath
; !isEndNode(DevDp) && !isEndNode(OptDp)
; DevDp=NEXT_NODE(DevDp), OptDp=NEXT_NODE(OptDp)
){
// Device path has to be the same except for a few special cases handled below
DpLength = NODE_LENGTH(DevDp);
if ( DpLength == (UINTN)NODE_LENGTH(OptDp)
&& MemCmp(DevDp, OptDp, DpLength) == 0
) continue;
if (DevDp->Type != OptDp->Type || DevDp->SubType != OptDp->SubType) return FALSE;
// According to specification HTTP Boot driver may update IP address and URI in the boot process,
// which is after execution of this code.
// We're ignoring IP node content and skipping URI comparison if handle device path has an empty URI.
if (DevDp->SubType == MSG_IPv4_DP || DevDp->SubType == MSG_IPv6_DP) continue;
if (DevDp->SubType == MSG_URI_DP && NODE_LENGTH(DevDp)==sizeof(EFI_DEVICE_PATH_PROTOCOL)) continue;
return FALSE;
}
return isEndNode(DevDp) && isEndNode(OptDp);
}
/**
* Attempt to match the passed boot option device path to a boot device (of BBS type)
*
* @param OptionDevicePath The device path to attempt to match
* @param Device The device to attempt to match it to
*
* @retval TRUE The device path matches the device
* @retval FALSE The device path does not match the device
*
* @note function only available, and used, if CSM_SUPPORT token is defined and enabled
*/
BOOLEAN BbsDevicePathTest(
EFI_DEVICE_PATH_PROTOCOL *OptionDevicePath, BOOT_DEVICE *Device
){
BBS_BBS_DEVICE_PATH *BbsDp;
if (OptionDevicePath->Type!=BBS_DEVICE_PATH) return FALSE;
if (Device->BbsEntry==NULL) return FALSE;
BbsDp = (BBS_BBS_DEVICE_PATH*)OptionDevicePath;
return BbsDp->DeviceType == GetBbsEntryDeviceType(Device->BbsEntry);
}
/**
* Go through the current BbsTable, passed in via the BbsEntry parameter, and attempt to find an entry
* that matches the current entry
*
* @param BbdsIndex The current index of the BBS device
* @param BbdsEntry Pointer to the BBS table
*
* @retval UINT8 The index that was matched
*/
UINT8 GetBbsDeviceInstance( UINT16 BbsIndex, BBS_TABLE *BbsEntry){
UINT8 Bus;
UINT8 Device;
UINT8 Function;
UINT8 Class;
UINT8 SubClass;
UINT32 Ibv1;
UINT32 Ibv2;
UINT16 Index;
if (BbsIndex==0) return 0;
Bus = (UINT8)BbsEntry->Bus;
Device = (UINT8)BbsEntry->Device;
Function = (UINT8)BbsEntry->Function;
Class = BbsEntry->Class;
SubClass = BbsEntry->SubClass;
Ibv1 = BbsEntry->IBV1;
Ibv2 = BbsEntry->IBV2;
BbsEntry -= BbsIndex;
for( Index = BbsIndex-1
; Index != 0xFFFF
; Index--
){
if( Bus != BbsEntry[Index].Bus
|| Device != BbsEntry[Index].Device
|| Function != BbsEntry[Index].Function
|| Class != BbsEntry[Index].Class
|| SubClass != BbsEntry[Index].SubClass
|| Ibv1 != BbsEntry[Index].IBV1
|| Ibv2 != BbsEntry[Index].IBV2
) return ((UINT8)(BbsIndex - 1 - Index));
}
return ((UINT8)(BbsIndex - 1 - Index));
}
EFI_HANDLE GetHandleByBbsEntryPciLocation(BBS_TABLE *BbsEntry){
EFI_STATUS Status;
EFI_HANDLE *Handle, DeivceHandle;
UINTN Number,i;
UINTN Segment, Bus, Device, Function;
//Get a list of all PCI devices
Status = pBS->LocateHandleBuffer(
ByProtocol, &gEfiPciIoProtocolGuid, NULL, &Number, &Handle
);
if (EFI_ERROR(Status)) return NULL;
DeivceHandle = NULL;
for(i=0; i<Number; i++)
{
EFI_PCI_IO_PROTOCOL *PciIo;
Status=pBS->HandleProtocol(Handle[i],&gEfiPciIoProtocolGuid,(VOID**)&PciIo);
if (EFI_ERROR(Status)) continue;
Status=PciIo->GetLocation(PciIo, &Segment, &Bus, &Device, &Function);
if (EFI_ERROR(Status)) continue;
if ( Segment == 0
&& Bus == BbsEntry->Bus
&& Device == BbsEntry->Device
&& Function == BbsEntry->Function
){
DeivceHandle = Handle[i];
break;
}
}
pBS->FreePool(Handle);
return DeivceHandle;
}
/**
* Attempt to match the passed device path to a device for devices of type AMI_BBS_DEVICE_PATH
*
* @param OptionDevicePath The device path to attempt to match
* @param Device The device to attempt to match
*
* @retval TRUE The device matches the device path
* @retval FALSE The device patch does not match the device
*/
BOOLEAN AmiBbsDevicePathTest(
EFI_DEVICE_PATH_PROTOCOL *OptionDevicePath, BOOT_DEVICE *Device
){
AMI_BBS_DEVICE_PATH *AmiBbsDp;
if (Device->BbsEntry==NULL) return FALSE;
AmiBbsDp = (AMI_BBS_DEVICE_PATH*)OptionDevicePath;
return AmiBbsDp->Header.Header.Type == HARDWARE_DEVICE_PATH
&& AmiBbsDp->Header.Header.SubType == HW_VENDOR_DP
&& guidcmp(&AmiBbsDevicePathGuid, &AmiBbsDp->Header.Guid) == 0
&& AmiBbsDp->Bus == Device->BbsEntry->Bus
&& AmiBbsDp->Device == Device->BbsEntry->Device
&& AmiBbsDp->Function == Device->BbsEntry->Function
&& AmiBbsDp->Class == Device->BbsEntry->Class
&& AmiBbsDp->SubClass == Device->BbsEntry->SubClass
&& AmiBbsDp->Instance == GetBbsDeviceInstance(Device->BbsIndex,Device->BbsEntry);
;
}
BOOLEAN LocateLegacyDevicePathTest(EFI_DEVICE_PATH_PROTOCOL *OptionDevicePath, BOOT_DEVICE *Device){
EFI_STATUS Status;
EFI_HANDLE Handle;
AMI_BBS_DEVICE_PATH *AmiBbsDp;
if ( Device->DeviceHandle!=INVALID_HANDLE
|| Device->BbsEntry==NULL
) return FALSE;
Status=pBS->LocateDevicePath(
&gEfiDevicePathProtocolGuid, &OptionDevicePath, &Handle
);
if (EFI_ERROR(Status)) return FALSE;
AmiBbsDp = (AMI_BBS_DEVICE_PATH*)OptionDevicePath;
return AmiBbsDp->Header.Header.Type == HARDWARE_DEVICE_PATH
&& AmiBbsDp->Header.Header.SubType == HW_VENDOR_DP
&& guidcmp(&AmiBbsDevicePathGuid, &AmiBbsDp->Header.Guid) == 0
&& AmiBbsDp->Bus == 0
&& AmiBbsDp->Device == Device->BbsEntry->Device
&& AmiBbsDp->Function == Device->BbsEntry->Function
&& AmiBbsDp->Class == Device->BbsEntry->Class
&& AmiBbsDp->SubClass == Device->BbsEntry->SubClass
&& AmiBbsDp->Instance == GetBbsDeviceInstance(Device->BbsIndex,Device->BbsEntry);
;
}
/**
* Attempt to match the passed device path to the device by calling all functions elinked into
* the list of device path matching functions
*
* @param OptionDp The device path to attempt to match
* @param Device The device to attempt to match
*
* @retval TRUE The device matches the device path
* @retval FALSE The device patch does not match the device
*/
BOOLEAN MatchDevicePathToDevice(EFI_DEVICE_PATH_PROTOCOL *OptionDp, BOOT_DEVICE *Device){
UINT32 i;
for(i=0; DpMatchingFunction[i]!=NULL; i++)
if (DpMatchingFunction[i](OptionDp,Device)) return TRUE;
return FALSE;
}
/**
* Function to ensure that a UEFI boot option is not matched to a legacy boot device. This case occurs
* especially with Floppy devices because of the lack of partition device paths for floppy devices
*
* @param Option The boot option
* @param Device The boot device
*
* @return TRUE The boot option and boot device are a legacy device
* @return FALSE The boot option and boot device are not a leagcy device
*/
BOOLEAN MatchUefiFloppyDrive(BOOT_OPTION *Option, BOOT_DEVICE *Device){
// This function makes sure that UEFI boot option is not matched
// with the legacy boot device.
// A legacy boot device is a device with a valid BbsIndex.
// A UEFI boot option is a boot option with FilePathList does not start
// with the BBS device path.
// When this function returns FALSE, the matching process fails.
// The function is executed via the BootOptionMatchingFunctions eLink.
return Device->BbsIndex==INVALID_BBS_INDEX || IsLegacyBootOption(Option);
}
/**
* Attemp to match the boot option to the boot device by calling all the matching functions
* elinked inot the list of MatchingFunctions
*
* @param Option The boot option
* @param Device The boot device
*
* @return TRUE The boot option and boot device are a legacy device
* @return FALSE The boot option and boot device are not a leagcy device
*/
BOOLEAN MatchBootOpionToDevice(BOOT_OPTION *Option, BOOT_DEVICE *Device){
UINT32 i;
//all the functions have to return TRUE
for(i=0; MatchingFunction[i]!=NULL; i++)
if (!MatchingFunction[i](Option,Device)) return FALSE;
return TRUE;
}
/**
* After collecting all the boot devices, and reading all the existing boot options, go through both
* lists and attempt to match each boot device to a boot option.
*/
VOID MatchBootOptionsToDevices(){
DLINK *OptionLink, *DeviceLink;
BOOT_OPTION *Option;
BOOT_DEVICE *Device;
FOR_EACH_BOOT_DEVICE(BootDeviceList,DeviceLink,Device){
BOOLEAN DeviceIsMatched = FALSE;
FOR_EACH_BOOT_OPTION(BootOptionList,OptionLink,Option){
EFI_DEVICE_PATH_PROTOCOL *OptionDp = Option->FilePathList;
EFI_DEVICE_PATH_PROTOCOL *DpInstance;
//Skip the group headers and the options that have already been matched.
if (Option->GroupHeader || IsBootOptionWithDevice(Option)) continue;
do {
EFI_DEVICE_PATH_PROTOCOL *TmpDp = OptionDp;
while( (DpInstance = DPNextInstance(&TmpDp,NULL))!=NULL
&& MatchDevicePathToDevice(DpInstance,Device)
) pBS->FreePool(DpInstance);
OptionDp = (EFI_DEVICE_PATH_PROTOCOL*)((UINT8*)OptionDp+DPLength(OptionDp));
} while( Option->FwBootOption && DpInstance==NULL
&& (UINT8*)OptionDp<(UINT8*)Option->FilePathList+Option->FilePathListLength
);
if (DpInstance == NULL && MatchBootOpionToDevice(Option,Device)){
UpdateBootOptionWithBootDeviceInfo(Option,Device);
DeviceIsMatched = TRUE;
// Legacy device can't be matched with more than one boot option.
if (IsLegacyBootOption(Option)) break;
}else{
if (DpInstance!=NULL) pBS->FreePool(DpInstance);
}
}
if (DeviceIsMatched) DeleteBootDevice(BootDeviceList, Device);
}
}
/**
* Go through the list of boot devices and delete entries for UEFI HDDs entries. These are considered
* fixed media devices, according to the UEFI specification, and should not be dynamically recreated.
*/
VOID DeleteUnmatchedUefiHddBootDevices(){
BOOT_DEVICE *Device;
DLINK *Link;
FOR_EACH_BOOT_DEVICE(BootDeviceList, Link, Device){
if (IsUefiHddBootDevice(Device)){
DeleteBootDevice(BootDeviceList, Device);
}
}
}
/**
* Get the parent Handle for the Block IO Handle passed. This is necessary because each
* partition can have its own Block I/O instance that will just allow communicating with that
* partition. This function will get the Block I/O Instance from the parent device that the partition
* is encompassed by.
*
* @param BlockIoHandle The Block I/O Handle that may be from a partition
*
* @retval EFI_HANDLE The handle of the parent Block I/O Device
*/
EFI_HANDLE GetPhysicalBlockIoHandle(EFI_HANDLE BlockIoHandle){
EFI_BLOCK_IO_PROTOCOL *BlkIo;
EFI_DEVICE_PATH_PROTOCOL *DevicePath, *Dp;
EFI_STATUS Status;
EFI_HANDLE Handle = BlockIoHandle;
Status=pBS->HandleProtocol(
Handle, &gEfiBlockIoProtocolGuid, (VOID**)&BlkIo
);
if (EFI_ERROR(Status)) return Handle;
if (!BlkIo->Media->LogicalPartition) return Handle;
Status=pBS->HandleProtocol(
Handle, &gEfiDevicePathProtocolGuid, (VOID**)&DevicePath
);
if (EFI_ERROR(Status)) return Handle;
Dp=DevicePath;
while(BlkIo->Media->LogicalPartition){
EFI_DEVICE_PATH_PROTOCOL *PrevDp=Dp;
//We need to cut Devicepath node to get Phisycal Partition
Dp=DPCut(PrevDp);
if (PrevDp!=DevicePath) pBS->FreePool(PrevDp);
if (Dp == NULL) break;
PrevDp=Dp;
Status=pBS->LocateDevicePath(
&gEfiBlockIoProtocolGuid,&PrevDp,&Handle
);
if(EFI_ERROR(Status)) break;
Status=pBS->HandleProtocol(
Handle, &gEfiBlockIoProtocolGuid, (VOID**)&BlkIo
);
if(EFI_ERROR(Status)) break;
}
if (Dp!=NULL && Dp!=DevicePath) pBS->FreePool(Dp);
//if physical Block I/O handle is not found, return original handle
return (BlkIo->Media->LogicalPartition) ? BlockIoHandle : Handle;
}
/**
* Return the handle of the device for this child handle
*
* @param[in] Handle The child handle
* @param[out] IpType The IP type (IP4/IP6) that this handle corresponds to.
* @param[out] Uri TRUE if this an URI device path.
*
* @return EFI_HANDLE the handle of the network controller itself.
*/
EFI_HANDLE GetPhysicalNetworkCardHandle(EFI_HANDLE Handle, UINT8 *IpType, BOOLEAN *Uri){
EFI_LOAD_FILE_PROTOCOL *LoadFile;
EFI_DEVICE_PATH_PROTOCOL *DevicePath, *Dp, *TmpDevicePath, *TmpDp, *MsgDp;
EFI_STATUS Status;
EFI_HANDLE NewHandle;
Status=pBS->HandleProtocol(
Handle, &gEfiLoadFileProtocolGuid, (VOID**)&LoadFile
);
if (EFI_ERROR(Status)) return Handle;
Status=pBS->HandleProtocol(
Handle, &gEfiDevicePathProtocolGuid, (VOID**)&DevicePath
);
if (EFI_ERROR(Status)) return Handle;
MsgDp = DevicePath;
do{
MsgDp = GetFirstDevicePathNodeOfType(MsgDp, MESSAGING_DEVICE_PATH);
if (MsgDp == NULL) return Handle;
if (MsgDp->SubType == MSG_MAC_ADDR_DP) break;
MsgDp=NEXT_NODE(MsgDp);
}while(TRUE);
MsgDp=NEXT_NODE(MsgDp);
if (isEndNode(MsgDp)) return Handle;
TmpDevicePath = TmpDp = DPCopy(DevicePath);
Dp = (EFI_DEVICE_PATH_PROTOCOL*)((UINT8*)MsgDp - (UINT8*)DevicePath + (UINT8*)TmpDp);
Dp->Type = END_DEVICE_PATH;
Dp->SubType = END_ENTIRE_SUBTYPE;
SET_NODE_LENGTH(Dp,sizeof(*Dp));
Status = pBS->LocateDevicePath(&gEfiDevicePathProtocolGuid, &TmpDp, &NewHandle);
pBS->FreePool(TmpDevicePath);
if (EFI_ERROR(Status) || TmpDp!=Dp) return Handle;
Handle = NewHandle;
if ( IpType != NULL) *IpType = 0;
if (Uri != NULL) *Uri = FALSE;
do{
MsgDp = GetFirstDevicePathNodeOfType(MsgDp, MESSAGING_DEVICE_PATH);
if (MsgDp == NULL) return Handle;
switch (MsgDp->SubType){
case MSG_IPv4_DP: case MSG_IPv6_DP:
if (IpType != NULL) *IpType = MsgDp->SubType;
break;
case MSG_URI_DP:
if (Uri != NULL) *Uri = TRUE;
break;
default:
break;
}
MsgDp=NEXT_NODE(MsgDp);
}while(TRUE);
return Handle;
}
/**
* Remove any trailing spaces from the passed parameter
*
* @param Name The string to remove the trailing spaces
* @param NumberOfCharacters The number of characters in the string
*
* @retval the new length of the string
*/
UINTN RemoveTrailingSpaces(CHAR16 *Name, UINTN NumberOfCharacters){
//remove trailing spaces
while(NumberOfCharacters>0 && Name[NumberOfCharacters-1]==L' ')
NumberOfCharacters--;
Name[NumberOfCharacters]=0;
return NumberOfCharacters;
}
/**
* Create a string description for the boot option pointed to by BOOT_OPTION.
*
* @param Option The Boot option to create the string description
* @param Name The buffer to fill with the description string
* @param NameSize The size of the buffer for the description string
*
* @retval Number of Unicode characters printed into the Name buffer excluding the terminating zero.
*/
UINTN ConstructBootOptionNameByHandle(
BOOT_OPTION *Option, CHAR16 *Name, UINTN NameSize
){
EFI_HANDLE Handle;
CHAR16 *ControllerName;
CHAR16 PrefixBuffer[20] = {L'\0'};
CHAR16 PrefixBuffer2[20] = {L'\0'};
UINTN PrefixSize;
CHAR16 *Prefix = PrefixBuffer;
CHAR16 *Prefix2 = PrefixBuffer2;
UINTN NumberOfCharacters;
UINT8 IpType;
BOOLEAN HttpBoot;
if (Option->DeviceHandle == INVALID_HANDLE) return 0;
//Name from Controller Handle
Handle = GetPhysicalBlockIoHandle(Option->DeviceHandle);
if (Handle==Option->DeviceHandle){
EFI_HANDLE OldHandle = Handle;
Handle = GetPhysicalNetworkCardHandle(Option->DeviceHandle,&IpType,&HttpBoot);
if (Handle != OldHandle) {
PrefixSize = sizeof(PrefixBuffer)/sizeof(CHAR16);
if ( IpType == MSG_IPv4_DP ){
if (EFI_ERROR(HiiLibGetString(HiiHandle, STRING_TOKEN(STR_IPV4_PREFIX), &PrefixSize, Prefix))) Prefix=L"IP4 ";
} else if ( IpType == MSG_IPv6_DP ){
if (EFI_ERROR(HiiLibGetString(HiiHandle, STRING_TOKEN(STR_IPV6_PREFIX), &PrefixSize, Prefix))) Prefix=L"IP6 ";
}
PrefixSize = sizeof(PrefixBuffer2)/sizeof(CHAR16);
if ( HttpBoot ){
if (EFI_ERROR(HiiLibGetString(HiiHandle, STRING_TOKEN(STR_HTTP_PREFIX), &PrefixSize, Prefix2))) Prefix2=L"HTTP ";
} else {
if (EFI_ERROR(HiiLibGetString(HiiHandle, STRING_TOKEN(STR_PXE_PREFIX), &PrefixSize, Prefix2))) Prefix2=L"PXE ";
}
}
}
if (!GetControllerName(Handle, &ControllerName)) return 0;
NumberOfCharacters = UnicodeSPrint(Name, NameSize, L"%s%s%s", Prefix2, Prefix, ControllerName);
return RemoveTrailingSpaces(Name, NumberOfCharacters);
}
/**
* Create a boot option description string from the BBS information for the passed boot option.
*
* @param Option The Boot option to create the string description
* @param Name The buffer to fill with the description string
* @param NameSize The size of the buffer for the description string
*
* @retval UINTN The length of the description string returned
*
* @note function only available, and used, if CSM_SUPPORT token is defined and enabled
*/
UINTN ConstructBootOptionNameByBbsDescription(
BOOT_OPTION *Option, CHAR16 *Name, UINTN NameSize
){
CHAR8 *AsciiNameStr;
UINTN NumberOfCharacters;
if (Option->BbsEntry == NULL) return 0;
//Name from BBS table
AsciiNameStr = (CHAR8*)(UINTN)(
(Option->BbsEntry->DescStringSegment<<4)
+ Option->BbsEntry->DescStringOffset
);
if (AsciiNameStr == NULL) return 0;
for( NumberOfCharacters = 0
; NumberOfCharacters < NameSize-1
; NumberOfCharacters++
){
if (!AsciiNameStr[NumberOfCharacters]) break;
Name[NumberOfCharacters] = AsciiNameStr[NumberOfCharacters];
}
Name[NumberOfCharacters]=0;
return RemoveTrailingSpaces(Name, NumberOfCharacters);
}
/**
* Create a device description string in the passed Name buffer by parsing the device
* path and creating a string based upon the nodes encountered
*
* @param DevicePath The device path to use to create the description string
* @param Name The buffer to fill with the description string
* @param NameSize The size of the buffer for the description string
*
* @retval UINTN The length of the description string returned
*/
UINTN DevicePathToNameString(
EFI_DEVICE_PATH_PROTOCOL *DevicePath, CHAR16 *Name, UINTN NameSize
){
STRING_REF StrToken;
EFI_DEVICE_PATH_PROTOCOL *Dp;
UINTN BufferSize;
UINTN NumberOfCharacters = 0;
for( Dp = DevicePath; !(isEndNode(Dp)); Dp=NEXT_NODE(Dp)){
StrToken = DevicePathNodeToStrRef(Dp);
BufferSize = (NameSize-NumberOfCharacters)*sizeof(CHAR16);
if ( StrToken!= INVALID_STR_TOKEN
&& !EFI_ERROR(HiiLibGetString(
HiiHandle,StrToken,&BufferSize, &Name[NumberOfCharacters]
))
&& BufferSize != 0
){
NumberOfCharacters += (BufferSize-1)/sizeof(CHAR16);
if (NumberOfCharacters < NameSize - 1){
Name[NumberOfCharacters++]=L' ';
}
}
}//for ;
if (NumberOfCharacters !=0 ) Name[--NumberOfCharacters]=0; //override last space with the terminating zero
return NumberOfCharacters;
}
/**
* Create a device description string by parsing the device handle.
*
* @param Option The Boot option to create the string description
* @param Name The buffer to fill with the description string
* @param NameSize The size of the buffer for the description string
*
* @retval UINTN The length of the description string returned
*/
UINTN ConstructBootOptionNameByHandleDevicePath(
BOOT_OPTION *Option, CHAR16 *Name, UINTN NameSize
){
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
if (Option->DeviceHandle == INVALID_HANDLE) return 0;
//Name from Device Path
if (!EFI_ERROR(pBS->HandleProtocol(
Option->DeviceHandle, &gEfiDevicePathProtocolGuid, (VOID**)&DevicePath
))) return DevicePathToNameString(DevicePath, Name, NameSize);
return 0;
}
/**
* Create a device description string by parsing the boot options device path.
*
* @param Option The Boot option to create the string description
* @param Name The buffer to fill with the description string
* @param NameSize The size of the buffer for the description string
*
* @retval UINTN The length of the description string returned
*/UINTN ConstructBootOptionNameByFilePathList(
BOOT_OPTION *Option, CHAR16 *Name, UINTN NameSize
){
if (Option->FilePathList == NULL) return 0;
return DevicePathToNameString(Option->FilePathList, Name, NameSize);
}
/**
* Create a device description string by calling all the functions linked into the BuildNameFunction
* eLink list
*
* @param Option The Boot option to create the string description
* @param Name The buffer to fill with the description string
* @param NameSize The size of the buffer for the description string
*
* @retval Number of Unicode characters printed into the Name buffer excluding the terminating zero.
*/
UINTN ConstructBootOptionBaseName(BOOT_OPTION *Option, CHAR16 *Name, UINTN NameSize){
UINTN NumberOfCharacters;
UINT32 i;
for(i=0; BuildNameFunctions[i]!=NULL; i++){
NumberOfCharacters = BuildNameFunctions[i](Option, Name, NameSize);
if ( NumberOfCharacters!=0 ) return NumberOfCharacters;
}
return 0;
}
/**
* Create a boot option name by calling the ConstructBootOptionNamePrefix, ConstructBootOptionNameSuffix,
* and ConstructBootOptionBaseName. Combine all those into the device's Description string
*
* @param Option The boot option
*
* @retval UINTN The length of the string created
*/
UINTN ConstructBootOptionName(BOOT_OPTION *Option){
CHAR16 Name[1024];
UINTN Length = sizeof(Name)/sizeof(CHAR16);
UINTN NumberOfCharacters, BaseNameLength;
do {
NumberOfCharacters = ConstructBootOptionNamePrefix(Option,Name,Length);
ASSERT(NumberOfCharacters < Length);
if (NumberOfCharacters >= Length) break;
Length -= NumberOfCharacters;
BaseNameLength = ConstructBootOptionBaseName(
Option, &Name[NumberOfCharacters], Length
);
NumberOfCharacters += BaseNameLength;
if (BaseNameLength==0){
//Unknown Device
UINTN BufferSize = Length*sizeof(CHAR16);
if (EFI_ERROR(
HiiLibGetString(
HiiHandle, STRING_TOKEN(STR_UNKNOWN), &BufferSize, &Name[NumberOfCharacters]
)
)){
NumberOfCharacters += UnicodeSPrint(
&Name[NumberOfCharacters], BufferSize, L"Unknown Device"
);
}else{
NumberOfCharacters += (BufferSize-1)/sizeof(CHAR16);
}
ASSERT(NumberOfCharacters < Length);
if (NumberOfCharacters >= Length) break;
Length -= NumberOfCharacters;
}
NumberOfCharacters += ConstructBootOptionNameSuffix(
Option, &Name[NumberOfCharacters], Length
);
ASSERT(NumberOfCharacters < Length);
} while (FALSE);
if (NumberOfCharacters >= Length){
NumberOfCharacters = Length-1;
Name[NumberOfCharacters] = 0;
}
//convert number of characters into string buffer size
Length = (NumberOfCharacters+1)*sizeof(CHAR16);
Option->Description = Malloc(Length);
MemCpy(Option->Description,Name,Length);
return NumberOfCharacters;
}
/**
* Create a description string for the device by reading the ATA serial number
*
* @param Option The boot option
* @param Name The buffer to fill with the ATA serial number
* @param NameSize The current index of the last character in the Name parameter
*
* @retval UINTN the new index of the last character in the Name parameter
*/
UINTN ConstructAtaBootOptionNameBySerialNumber(
BOOT_OPTION *Option, CHAR16 *Name, UINTN NameSize
){
EFI_HANDLE Handle;
UINTN NumberOfCharacters;
EFI_DISK_INFO_PROTOCOL *DiskInfo;
EFI_STATUS Status;
UINT16 IdentifyData[256];
UINT32 Size;
CHAR8 SerialNumber[21];
if (Option->DeviceHandle == INVALID_HANDLE) return 0;
Handle = GetPhysicalBlockIoHandle(Option->DeviceHandle);
Status = pBS->HandleProtocol(
Handle, &gEfiDiskInfoProtocolGuid, (VOID**)&DiskInfo
);
if (EFI_ERROR(Status)) return 0;
// If this is not IDE/AHCI device, return.
// There is no standard way to extract serial number from other device types.
if ( guidcmp(&gEfiDiskInfoIdeInterfaceGuid, &DiskInfo->Interface) != 0
&& guidcmp(&gEfiDiskInfoAhciInterfaceGuid, &DiskInfo->Interface) != 0
) return 0;
Size = sizeof(IdentifyData);
Status = DiskInfo->Identify ( DiskInfo, IdentifyData, &Size );
if (EFI_ERROR(Status)) return 0;
MemCpy(SerialNumber, IdentifyData+10, 20);
SerialNumber[20] = 0;
NumberOfCharacters = UnicodeSPrint(Name, NameSize, L"%a", SerialNumber);
return NumberOfCharacters;
}
/**
* Create a description string for the device by reading the USB serial number
*
* @param Option The boot option
* @param Name The buffer to fill with the USB serial number
* @param NameSize The current index of the last character in the Name parameter
*
* @retval UINTN the new index of the last character in the Name parameter
**/
UINTN ConstructUsbBootOptionNameBySerialNumber(
BOOT_OPTION *Option, CHAR16 *Name, UINTN NameSize
){
EFI_HANDLE Handle;
UINTN NumberOfCharacters;
EFI_STATUS Status;
EFI_USB_IO_PROTOCOL *UsbIo = NULL;
EFI_USB_DEVICE_DESCRIPTOR DevDesc = {0};
CHAR16 *UsbSerialNumber=NULL;
if (Option->DeviceHandle == INVALID_HANDLE) return 0;
Handle = GetPhysicalBlockIoHandle(Option->DeviceHandle);
Status = pBS->HandleProtocol(Handle, &gEfiUsbIoProtocolGuid, (VOID**)&UsbIo);
if(EFI_ERROR(Status)) return 0;
Status = UsbIo->UsbGetDeviceDescriptor(UsbIo, &DevDesc);
if(EFI_ERROR(Status)) return 0;
if (DevDesc.StrSerialNumber) {
Status = UsbIo->UsbGetStringDescriptor(UsbIo, 0x0409, DevDesc.StrSerialNumber, &UsbSerialNumber);
if(EFI_ERROR(Status)) return 0;
pBS->CopyMem( Name, UsbSerialNumber, (Wcslen(UsbSerialNumber)+1)*2 );
NumberOfCharacters = Wcslen(UsbSerialNumber);
return NumberOfCharacters;
}
return 0;
}
/**
* Create a description string for the device by reading the NVMe serial number
*
* @param Option The boot option
* @param Name The buffer to fill with the NVMe serial number
* @param NameSize The current index of the last character in the Name parameter
*
* @retval UINTN the new index of the last character in the Name parameter
**/
UINTN ConstructNvmeBootOptionNameBySerialNumber(
BOOT_OPTION *Option, CHAR16 *Name, UINTN NameSize
){
EFI_HANDLE Handle;
UINTN NumberOfCharacters;
EFI_STATUS Status;
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL *NvmePassThru = NULL;
EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket;
EFI_NVM_EXPRESS_COMMAND Command;
EFI_NVM_EXPRESS_COMPLETION Completion;
UINT8 NvmeControllerData[4096];
if (Option->DeviceHandle == INVALID_HANDLE) return 0;
Status=pBS->HandleProtocol(
Option->DeviceHandle, &gEfiDevicePathProtocolGuid, (VOID**)&DevicePath
);
if( EFI_ERROR(Status)
|| GetDevicePathSubtype(DevicePath, MESSAGING_DEVICE_PATH) != MSG_NVME_NAMESPACE_DP
) return 0;
Status=pBS->LocateDevicePath(
&gEfiNvmExpressPassThruProtocolGuid, &DevicePath, &Handle
);
if(EFI_ERROR(Status)) return 0;
Status = pBS->HandleProtocol(Handle, &gEfiNvmExpressPassThruProtocolGuid, (VOID**)&NvmePassThru);
if(EFI_ERROR(Status)) return 0;
MemSet(&CommandPacket, sizeof(CommandPacket), 0);
MemSet(&Command, sizeof(Command), 0);
MemSet(&Completion, sizeof(Completion), 0);
Command.Cdw0.Opcode = 6; // NVME_ADMIN_IDENTIFY_CMD
CommandPacket.NvmeCmd = &Command;
CommandPacket.NvmeCompletion = &Completion;
CommandPacket.TransferBuffer = NvmeControllerData;
CommandPacket.TransferLength = sizeof (NvmeControllerData);
CommandPacket.CommandTimeout = 10000000; // 1 second
CommandPacket.QueueType = NVME_ADMIN_QUEUE;
Command.Cdw10 = 1; // Set bit 0 (Cns bit) to 1 to identify a controller
Command.Flags = CDW10_VALID;
Status = NvmePassThru->PassThru (NvmePassThru, 0, &CommandPacket, NULL);
if(EFI_ERROR(Status)) return 0;
// Serial number is in bytes [4..23]
NvmeControllerData[24] = 0;
NumberOfCharacters = UnicodeSPrint(Name, NameSize, L"%a", &NvmeControllerData[4]);
return RemoveTrailingSpaces(Name, NumberOfCharacters);
}
UINTN ConstructBootOptionNameByBbsPciId(
BOOT_OPTION *Option, CHAR16 *Name, UINTN NameSize
){
EFI_HANDLE Handle;
EFI_PCI_IO_PROTOCOL *PciIo;
UINT32 DidVid;
UINT8 Instance;
EFI_STATUS Status;
if (Option->BbsEntry == NULL || Option->BbsIndex == INVALID_BBS_INDEX) return 0;
Handle = GetHandleByBbsEntryPciLocation(Option->BbsEntry);
if (Handle == INVALID_HANDLE) return 0;
Status=pBS->HandleProtocol(Handle,&gEfiPciIoProtocolGuid,(VOID**)&PciIo);
if (EFI_ERROR(Status)) return 0;
Status=PciIo->Pci.Read(PciIo, EfiPciIoWidthUint32, 0, 1, &DidVid);
if(EFI_ERROR(Status)) return 0;
Instance = GetBbsDeviceInstance(Option->BbsIndex,Option->BbsEntry);
return UnicodeSPrint(Name, NameSize, L"%x;%x", DidVid, Instance);
}
static CONSTRUCT_BOOT_OPTION_NAME *IntBuildNameFunctions[] = {
ConstructAtaBootOptionNameBySerialNumber,
ConstructUsbBootOptionNameBySerialNumber,
ConstructNvmeBootOptionNameBySerialNumber,
ConstructBootOptionNameByHandle,
ConstructBootOptionNameByBbsPciId,
ConstructBootOptionNameByBbsDescription,
NULL
};
/**
* Create a description string for the device by calling the Internal Build name functions
*
* @param Option The boot option
* @param Name The buffer to fill with the boot option name
* @param NameSize The current index of the last character in the Name parameter
*
* @retval UINTN the new index of the last character in the Name parameter
*/
UINTN ConstructInternalBootOptionName(
BOOT_OPTION *Option, CHAR16 *Name, UINTN NameSize
){
UINTN NumberOfCharacters;
UINT32 i;
for(i=0; IntBuildNameFunctions[i]!=NULL; i++){
NumberOfCharacters = IntBuildNameFunctions[i](Option, Name, NameSize);
if ( NumberOfCharacters!=0 ) return NumberOfCharacters;
}
return 0;
}
/**
* Create a description string for the by calling the internal helper functions
* UpdateBootOptionWithBootDeviceInfo and ConstructInternalBootOptionName
*
* @param Device The boot device
* @param Name The buffer to fill with the device name
* @param NameSize The current index of the last character in the Name parameter
*
* @retval UINTN the new index of the last character in the Name parameter
*/
UINTN ConstructInternalDeviceName(
BOOT_DEVICE *Device, CHAR16 *Name, UINTN NameSize
){
static BOOT_OPTION BootOption = {
{NULL,NULL}, LOAD_OPTION_ACTIVE, NULL, NULL, 0, NULL, 0,
INVALID_BOOT_OPTION_NUMBER, LOWEST_BOOT_OPTION_PRIORITY,
UNASSIGNED_HIGHEST_TAG,
INVALID_HANDLE, INVALID_BBS_INDEX, NULL, TRUE, FALSE
};
UpdateBootOptionWithBootDeviceInfo(&BootOption,Device);
return ConstructInternalBootOptionName(&BootOption, Name, NameSize);
}
/**
* Attempt to match the OptionDevicePath to the Boot Device by directly comparing
* the device paths.
*
* @param OptionDevicePath The device path to test against the boot device
* @param Device The boot device
*
* @retval TRUE The device path matches the boot device
* @retval FALSE The device path does not match the boot device
*/
BOOLEAN AmiDeviceNameDevicePathTest(
EFI_DEVICE_PATH_PROTOCOL *OptionDevicePath, BOOT_DEVICE *Device
){
AMI_DEVICE_NAME_DEVICE_PATH *NameDp;
CHAR16 Name[1024];
UINTN Size = sizeof(Name)/sizeof(CHAR16);
UINTN NumberOfCharacters;
CONSTRUCT_BOOT_OPTION_NAME *Tmp[2];
NameDp = (AMI_DEVICE_NAME_DEVICE_PATH*)OptionDevicePath;
if( NameDp->Header.Header.Type != HARDWARE_DEVICE_PATH
|| NameDp->Header.Header.SubType != HW_VENDOR_DP
|| guidcmp(
&AmiDeviceNameDevicePathGuid,
&NameDp->Header.Guid
) != 0
) return FALSE;
NumberOfCharacters = ConstructInternalDeviceName(
Device, Name, Size
);
if (NumberOfCharacters==0) return FALSE;
//convert number of characters into string buffer size
Size = (NumberOfCharacters+1)*sizeof(CHAR16);
if ( Size == NODE_LENGTH(&NameDp->Header.Header)-sizeof(*NameDp)
&& !MemCmp(Name,NameDp+1,Size)
) return TRUE;
// In case of a legacy device matching may has failed
// because we're dealing with the device path constructed by
// an older versions of BDS. Let's check for that.
if (Device->BbsIndex == INVALID_BBS_INDEX) return FALSE;
// Older versions of BDS constructed name from BBS description
// (ConstructBootOptionNameByBbsPciId was not in the IntBuildNameFunctions list)
// Save original values
Tmp[0] = IntBuildNameFunctions[0];
Tmp[1] = IntBuildNameFunctions[1];
IntBuildNameFunctions[0] = ConstructBootOptionNameByBbsDescription;
IntBuildNameFunctions[1] = NULL;
Size = sizeof(Name)/sizeof(CHAR16);
// Construct the name
NumberOfCharacters = ConstructInternalDeviceName(
Device, Name, Size
);
// Restore the original value
IntBuildNameFunctions[0] = Tmp[0];
IntBuildNameFunctions[1] = Tmp[1];
if (NumberOfCharacters==0) return FALSE;
//convert number of characters into string buffer size
Size = (NumberOfCharacters+1)*sizeof(CHAR16);
return
Size == NODE_LENGTH(&NameDp->Header.Header)-sizeof(*NameDp)
&& !MemCmp(Name,NameDp+1,Size);
}
/**
* Adds another device path to an array of boot option device paths
*
* @param FilePathListPtr
* On input, pointer to the current boot option FilePathList
* On output, pointer to the new FilePathList. Memory used by original FilePathList is deallocated.
* @param FilePathListLength
* On input, pointer to the length of the current FilePathList
* On output, pointer to the length of the new FilePathList
* @param DevicePath Device path to add to the FilePathList
*/
VOID AddDevicePathToFilePathList(
EFI_DEVICE_PATH_PROTOCOL **FilePathListPtr, UINTN *FilePathListLength,
EFI_DEVICE_PATH_PROTOCOL *DevicePath
)
{
UINTN DevicePathLength;
EFI_DEVICE_PATH_PROTOCOL *NewFilePathList;
if ( FilePathListPtr == NULL
|| FilePathListLength == NULL
|| DevicePath == NULL
) return;
DevicePathLength = DPLength(DevicePath);
if (*FilePathListPtr == NULL) *FilePathListLength = 0;
NewFilePathList = Malloc(*FilePathListLength + DevicePathLength);
if ( *FilePathListPtr != NULL ){
MemCpy(NewFilePathList, *FilePathListPtr, *FilePathListLength);
pBS->FreePool(*FilePathListPtr);
}
MemCpy(
(UINT8*)NewFilePathList+*FilePathListLength,
DevicePath, DevicePathLength
);
*FilePathListLength += DevicePathLength;
*FilePathListPtr = NewFilePathList;
}
/**
* Create a new device path for the boot option by copying the handle's device path
*
* @param Option The boot option to crate a device path
*
* @retval TRUE The device path was created
* @retval FALSE The device path was not created
*/
BOOLEAN BuildEfiFilePath(BOOT_OPTION *Option){
EFI_DEVICE_PATH_PROTOCOL *Dp;
EFI_STATUS Status;
if ( Option->FilePathList!=NULL
|| Option->BbsEntry != NULL
|| Option->DeviceHandle == INVALID_HANDLE
) return FALSE;
Status = pBS->HandleProtocol(Option->DeviceHandle, &gEfiDevicePathProtocolGuid, (VOID**)&Dp);
if (EFI_ERROR(Status)) return FALSE;
Option->FilePathList = DPCopy(Dp);
Option->FilePathListLength = DPLength(Dp);
return TRUE;
}
/**
* Build a legacy device path for this device by parsing the device's actual device path
*
* @param Option The boot option to create a device path for
*
* @retval TRUE The device path was created
* @retval FALSE The device path was not created
*
* @note function only available, and used, if CSM_SUPPORT token is defined and enabled
*/
BOOLEAN BuildLegacyFilePath(BOOT_OPTION *Option){
static struct {
BBS_BBS_DEVICE_PATH bbs;
EFI_DEVICE_PATH_PROTOCOL end;
} BbsDpTemplate = {
{
BDS_DP_NODE(BBS_BBS_DEVICE_PATH,BBS_DEVICE_PATH,BBS_BBS_DP),
BBS_HARDDISK,0,{0}
},
BDS_END_OF_DP
};
if (Option->FilePathList!=NULL || Option->BbsEntry == NULL) return FALSE;
BbsDpTemplate.bbs.DeviceType=GetBbsEntryDeviceType(Option->BbsEntry);
Option->FilePathList = DPCopy(&BbsDpTemplate.bbs.Header);
Option->FilePathListLength = DPLength(Option->FilePathList);
return TRUE;
}
/**
* Build a AMI BBS Device Path instance for this boot option
*
* @param Option Boot option to create an AMI BBS device path instance for
*
* @retval TRUE The device path was created
* @retval FALSE The device path was not created
*
* @note function only available, and used, if CSM_SUPPORT token is defined and enabled
**/
BOOLEAN BuildLegacyLocationFilePath(BOOT_OPTION *Option){
static struct {
AMI_BBS_DEVICE_PATH amibbs;
EFI_DEVICE_PATH_PROTOCOL end;
} AmiBbsDpTemplate = {
{
{
BDS_DP_NODE(AMI_BBS_DEVICE_PATH, HARDWARE_DEVICE_PATH, HW_VENDOR_DP),
AMI_BBS_DEVICE_PATH_GUID
},
0, 0, 0, 0, 0, 0
},
BDS_END_OF_DP
};
EFI_DEVICE_PATH_PROTOCOL *Dp = NULL;
EFI_HANDLE Handle;
BOOLEAN FreeDp = FALSE;
if (Option->BbsEntry == NULL) return FALSE;
if (Option->DeviceHandle == INVALID_HANDLE){
Handle = GetHandleByBbsEntryPciLocation(Option->BbsEntry);
} else {
Handle = Option->DeviceHandle;
}
if (Handle != INVALID_HANDLE ){
if (EFI_ERROR(pBS->HandleProtocol(Handle, &gEfiDevicePathProtocolGuid, (VOID **)&Dp)))
Dp = NULL;
}
AmiBbsDpTemplate.amibbs.Class = (UINT8)Option->BbsEntry->Class;
AmiBbsDpTemplate.amibbs.SubClass = (UINT8)Option->BbsEntry->SubClass;
AmiBbsDpTemplate.amibbs.Instance = GetBbsDeviceInstance(Option->BbsIndex,Option->BbsEntry);
AmiBbsDpTemplate.amibbs.Device = (UINT8)Option->BbsEntry->Device;
AmiBbsDpTemplate.amibbs.Function = (UINT8)Option->BbsEntry->Function;
if ( Dp == NULL ){
Dp = &AmiBbsDpTemplate.amibbs.Header.Header;
AmiBbsDpTemplate.amibbs.Bus = (UINT8)Option->BbsEntry->Bus;
} else if (Option->DeviceHandle == INVALID_HANDLE){
// We're ignoring bus number as it may change from boot to boot.
AmiBbsDpTemplate.amibbs.Bus = 0;
Dp = DPAddNode(Dp, &AmiBbsDpTemplate.amibbs.Header.Header);
FreeDp = TRUE;
}
AddDevicePathToFilePathList(
&Option->FilePathList, &Option->FilePathListLength, Dp
);
if (FreeDp) pBS->FreePool(Dp);
return TRUE;
}
/**
* Build a device path name for this device by calling the ConstructInternalBootOptionName function
*
* @param Option The boot option to create a device path for
*
* @retval TRUE The device path was created
* @retval FALSE The device path was not created
*/
BOOLEAN BuildNameFilePath(BOOT_OPTION *Option){
static AMI_DEVICE_NAME_DEVICE_PATH AmiNameDpTemplate = {
{
BDS_DP_NODE(AMI_DEVICE_NAME_DEVICE_PATH, HARDWARE_DEVICE_PATH, HW_VENDOR_DP),
AMI_DEVICE_NAME_DEVICE_PATH_GUID
}
};
static EFI_DEVICE_PATH_PROTOCOL EndOfDevicePathNode = BDS_END_OF_DP;
CHAR16 Name[1024];
UINTN Size = sizeof(Name)/sizeof(CHAR16);
UINTN NumberOfCharacters;
AMI_DEVICE_NAME_DEVICE_PATH *NameDp;
EFI_DEVICE_PATH_PROTOCOL *Dp;
NumberOfCharacters = ConstructInternalBootOptionName(
Option, Name, Size
);
if (NumberOfCharacters==0) return FALSE;
//convert number of characters into string buffer size
Size = (NumberOfCharacters+1)*sizeof(CHAR16);
Dp = Malloc(sizeof(AmiNameDpTemplate)+Size+sizeof(EndOfDevicePathNode));
ASSERT(Dp!=NULL);
if (Dp==NULL) return FALSE;
NameDp = (AMI_DEVICE_NAME_DEVICE_PATH*)Dp;
*NameDp = AmiNameDpTemplate;
SET_NODE_LENGTH(Dp,(UINT16)(sizeof(AmiNameDpTemplate)+Size));
MemCpy(NameDp+1, Name, Size);
MemCpy(NEXT_NODE(Dp), &EndOfDevicePathNode, sizeof(EndOfDevicePathNode));
AddDevicePathToFilePathList(
&Option->FilePathList, &Option->FilePathListLength, Dp
);
pBS->FreePool(Dp);
return TRUE;
}
/**
* Build a device path name for this device by calling the BuildFilePathFunctions elink list
*
* @param Option The boot option to create a device path for
*
* @retval TRUE The device path was created
* @retval FALSE The device path was not created
*/
BOOLEAN BuildBootOptionFilePath(BOOT_OPTION *Option){
UINT32 i;
BOOLEAN FilePathCreated = FALSE;
for(i=0; BuildFilePathFunctions[i]!=NULL; i++){
FilePathCreated |= BuildFilePathFunctions[i](Option);
}
return FilePathCreated;
}
/**
* Create boot options for the the newly discovered boot devices that have not been matched to
* existing boot options
*/
VOID CreateBootOptionsForNewBootDevices(){
DLINK *Link;
BOOT_DEVICE *Device;
FOR_EACH_BOOT_DEVICE(BootDeviceList,Link,Device){
BOOT_OPTION *Option = CreateBootOption(BootOptionList);
UpdateBootOptionWithBootDeviceInfo(Option,Device);
DeleteBootDevice(BootDeviceList, Device);
Option->FwBootOption = TRUE;
ConstructBootOptionName(Option);
if (!BuildBootOptionFilePath(Option)){
Option->FilePathList=NULL;
Option->FilePathListLength=0;
}
}
DUMP_BOOT_OPTION_LIST(BootOptionList,"Before Processing");
}
/**
* If normalization is enabled, regenerates all the description strings and/or file path lists instead
* of attempting to rely on the information that was read out the boot options
*/
VOID NormalizeBootOptions(){
DLINK *Link;
BOOT_OPTION *Option;
// Normalize boot options
//(regenerate the description string and the file path list)
FOR_EACH_BOOT_OPTION(BootOptionList,Link,Option){
if ( !Option->FwBootOption || !IsBootOptionWithDevice(Option)
|| Option->BootOptionNumber == INVALID_BOOT_OPTION_NUMBER
) continue;
if (NormalizeBootOptionDevicePath){
EFI_DEVICE_PATH_PROTOCOL *OldFilePathList = Option->FilePathList;
UINTN OldFilePathListLength = Option->FilePathListLength;
Option->FilePathList = NULL;
Option->FilePathListLength = 0;
BuildBootOptionFilePath(Option);
if (Option->FilePathList == NULL){
Option->FilePathList = OldFilePathList;
Option->FilePathListLength = OldFilePathListLength;
}else if (OldFilePathList != NULL){
pBS->FreePool(OldFilePathList);
}
}
if (NormalizeBootOptionName){
CHAR16 *OldDescription = Option->Description;
Option->Description = NULL;
ConstructBootOptionName(Option);
if (Option->Description == NULL)
Option->Description = OldDescription;
else if (OldDescription != NULL)
pBS->FreePool(OldDescription);
}
}
}
/**
* Debug function to dump the current BootOptionList (in the order its encountered) and output the
* information for debugging
*
* @param BootOptionList Pointer to the list of boot options
* @param ListCaption The caption to output to describe this list
*/
VOID DumpBootOptionList(DLIST *BootOptions, CHAR8 *ListCaption){
DLINK *Link;
BOOT_OPTION *Option;
if (ListCaption!=NULL)
DEBUG((DEBUG_INFO,"%a:\n",ListCaption));
FOR_EACH_BOOT_OPTION(BootOptions,Link,Option){
CHAR8 *Details1, *Details2, *Details3;
if (Option->GroupHeader) Details1="(group header)";
else if (!IsBootOptionWithDevice(Option)) Details1 ="(orphan)";
else Details1="";
if ((Option->Attributes&LOAD_OPTION_ACTIVE)!=LOAD_OPTION_ACTIVE) Details2="(disabled)";
else Details2="";
if ((Option->Attributes&LOAD_OPTION_HIDDEN)==LOAD_OPTION_HIDDEN) Details3="(hidden)";
else Details3="";
DEBUG((DEBUG_INFO,
"%X(%X/%X).%S%a%a%a\n",
Option->BootOptionNumber,Option->Priority,Option->Tag,Option->Description,
Details1,Details2,Details3
));
}
}
/**
* Return the required size to store this boot option
*
* @param Option Pointer to the boot option
*
* @retval UINTN The size required to store this boot option
*/
UINTN GetBootOptionPackedSize(BOOT_OPTION *Option){
return
sizeof(EFI_LOAD_OPTION)
+ (Wcslen(Option->Description)+1)*sizeof(CHAR16)
+ Option->FilePathListLength
+ Option->OptionalDataSize;
}
/**
* Using the passed NvramOption as a starting point, pack the passed Option into the OptionalData area of the
* NvramOption. Return a pointer to the new memory.
*
* @param Option The Boot Option to pack behind the NvramOption
* @param NvramOption The start of the NvramOption
*
* @return Pointer to the new memory
*/
VOID* PackBootOption(BOOT_OPTION *Option, EFI_LOAD_OPTION *NvramOption){
UINTN DescriptionSize;
UINT8 *Ptr;
DescriptionSize = (Wcslen(Option->Description)+1)*sizeof(CHAR16);
NvramOption->Attributes = Option->Attributes;
NvramOption->FilePathListLength = (UINT16)Option->FilePathListLength;
MemCpy(NvramOption+1,Option->Description,DescriptionSize);
Ptr = (UINT8*)(NvramOption+1)+DescriptionSize;
MemCpy(Ptr, Option->FilePathList, Option->FilePathListLength);
Ptr += Option->FilePathListLength;
return Ptr;
}
/**
* Save the boot options into NVRAM as L"BootXXXX" variables, and then update the
* L"BootOrder" NVRAM Variable
*/
VOID SaveBootOptions(){
UINTN BootOrderSize;
UINT16 *BootOrder;
DLINK *Link;
UINTN BootOrderIndex = 0;
BOOT_OPTION *Option;
//PRECONDITION: All Boot Option Numbers are set
SortList(BootOptionList, ComparePriorityThenBootOptionNumber);
DUMP_BOOT_OPTION_LIST(BootOptionList, "Before Saving");
BootOrderSize = BootOptionList->Size*sizeof(UINT16);
if (BootOrderSize==0){
// Boot Option List is empty. No boot option processing is required.
pRS->SetVariable(
L"BootOrder", &gEfiGlobalVariableGuid, 0, 0, NULL
);
return;
}
BootOrder = Malloc(BootOrderSize);
FOR_EACH_BOOT_OPTION(BootOptionList,Link,Option){
EFI_LOAD_OPTION *NvramOption;
BOOT_OPTION *NestedOption;
DLINK *TmpLink;
UINTN NvramOptionSize;
UINT8 *Ptr;
CHAR16 BootStr[9];
EFI_STATUS Status;
BOOLEAN HasNestedOptions = FALSE;
//Make sure the boot option is well-formed
if( Option->FilePathListLength == 0
|| Option->FilePathList == NULL
|| Option->BootOptionNumber == INVALID_BOOT_OPTION_NUMBER
){
DEBUG((DEBUG_ERROR,
"SaveBootOptions: skipping invalid boot option '%X.%S'\n",
Option->BootOptionNumber,Option->Description
));
continue;
}
NvramOptionSize = GetBootOptionPackedSize(Option);
if (Option->FwBootOption){
NvramOptionSize += sizeof(UINT32); //signature
FOR_EACH_LIST_ELEMENT(Option->Link.pNext, TmpLink, NestedOption, BOOT_OPTION){
if (Option->BootOptionNumber != NestedOption->BootOptionNumber)
break;
NvramOptionSize += AMI_NESTED_BOOT_OPTION_HEADER_SIZE
+ GetBootOptionPackedSize(NestedOption)
+ sizeof(UINT32); //signature;
HasNestedOptions = TRUE;
}
}
NvramOption = Malloc(NvramOptionSize);
Ptr = PackBootOption(Option,NvramOption);
if (Option->FwBootOption){
if (HasNestedOptions || Option->GroupHeader)
WriteUnaligned32((UINT32*)Ptr,AMI_GROUP_BOOT_OPTION_SIGNATURE);
else
WriteUnaligned32((UINT32*)Ptr,AMI_SIMPLE_BOOT_OPTION_SIGNATURE);
Ptr += sizeof(UINT32);
if (HasNestedOptions){
FOR_EACH_LIST_ELEMENT(Option->Link.pNext, TmpLink, NestedOption, BOOT_OPTION){
NESTED_BOOT_OPTION *NestedPackedOption;
if (Option->BootOptionNumber != NestedOption->BootOptionNumber)
break;
NestedPackedOption = (NESTED_BOOT_OPTION*)Ptr;
NestedPackedOption->Signature = AMI_NESTED_BOOT_OPTION_SIGNATURE;
Ptr = PackBootOption(NestedOption,&NestedPackedOption->Option);
WriteUnaligned32((UINT32*)Ptr,AMI_SIMPLE_BOOT_OPTION_SIGNATURE);
Ptr += sizeof(UINT32);
if (NestedOption->OptionalDataSize!=0){
MemCpy(
Ptr, NestedOption->OptionalData, NestedOption->OptionalDataSize
);
Ptr += NestedOption->OptionalDataSize;
}
NestedPackedOption->Size = (UINT32)(Ptr - (UINT8*)NestedPackedOption);
//Skip nested boot option that we've just processed.
Link = NestedOption->Link.pNext;
}
}
}
if (Option->OptionalDataSize!=0){
MemCpy(
Ptr, Option->OptionalData, Option->OptionalDataSize
);
}
ASSERT(Ptr+Option->OptionalDataSize == (UINT8*)NvramOption+NvramOptionSize);
UnicodeSPrint(BootStr,9*sizeof(CHAR16),L"Boot%04X",Option->BootOptionNumber);
Status = pRS->SetVariable(
BootStr, &gEfiGlobalVariableGuid,
BOOT_VARIABLE_ATTRIBUTES, NvramOptionSize, NvramOption
);
BootOrder[BootOrderIndex]=Option->BootOptionNumber;
pBS->FreePool(NvramOption);
if (EFI_ERROR(Status)) continue;
BootOrderIndex++;
}
pRS->SetVariable(
L"BootOrder", &gEfiGlobalVariableGuid,
BOOT_VARIABLE_ATTRIBUTES, BootOrderIndex*sizeof(UINT16), BootOrder
);
pBS->FreePool(BootOrder);
}
/**
* This function initializes the global variables. Must be called before any other boot option processing function can be used.
*/
VOID UpdateBootOptionVariables(){
LoadStrings(TheImageHandle, &HiiHandle);
DListInit(BootOptionList);
DListInit(BootDeviceList);
}
/**
* Deallocate BootOptionList.
*/
VOID FreeBootOptionList(){
DLINK *Link;
BOOT_OPTION *Option;
FOR_EACH_BOOT_OPTION(BootOptionList,Link,Option){
DeleteBootOption(BootOptionList,Option);
}
}
//**********************************************************************
//**********************************************************************
//** **
//** (C)Copyright 1985-2019, American Megatrends, Inc. **
//** **
//** All Rights Reserved. **
//** **
//** 5555 Oakbrook Parkway, Suite 200, Norcross, GA 30093 **
//** **
//** Phone: (770)-246-8600 **
//** **
//**********************************************************************
//**********************************************************************