//*********************************************************************** //* * //* 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 #include #include #include #include #include #include #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->FilePathListLengthFilePathListLength >= 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; iSetVariable(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(IndexPtrLength + sizeof(UINT32) ); UINT16 *IndexPtr = &Group->Device[0]; UINT32 Counter = 0; while(IndexPtrDevice[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; iFreePool(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; iHandleProtocol(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 ** //** ** //********************************************************************** //**********************************************************************