| /** @file | |
| BDS Lib functions which relate with create or process the boot option. | |
| Copyright (c) 2004 - 2015, Intel Corporation. All rights reserved.<BR> | |
| This program and the accompanying materials | |
| are licensed and made available under the terms and conditions of the BSD License | |
| which accompanies this distribution. The full text of the license may be found at | |
| http://opensource.org/licenses/bsd-license.php | |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
| **/ | |
| #include "InternalBdsLib.h" | |
| #include "String.h" | |
| #include <Library/NetLib.h> | |
| #include "Library/DebugLib.h" | |
| BOOLEAN mEnumBootDevice = FALSE; | |
| EFI_HII_HANDLE gBdsLibStringPackHandle = NULL; | |
| /** | |
| The constructor function register UNI strings into imageHandle. | |
| It will ASSERT() if that operation fails and it will always return EFI_SUCCESS. | |
| @param ImageHandle The firmware allocated handle for the EFI image. | |
| @param SystemTable A pointer to the EFI System Table. | |
| @retval EFI_SUCCESS The constructor successfully added string package. | |
| @retval Other value The constructor can't add string package. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| GenericBdsLibConstructor ( | |
| IN EFI_HANDLE ImageHandle, | |
| IN EFI_SYSTEM_TABLE *SystemTable | |
| ) | |
| { | |
| gBdsLibStringPackHandle = HiiAddPackages ( | |
| &gBdsLibStringPackageGuid, | |
| ImageHandle, | |
| GenericBdsLibStrings, | |
| NULL | |
| ); | |
| ASSERT (gBdsLibStringPackHandle != NULL); | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Deletete the Boot Option from EFI Variable. The Boot Order Arrray | |
| is also updated. | |
| @param OptionNumber The number of Boot option want to be deleted. | |
| @param BootOrder The Boot Order array. | |
| @param BootOrderSize The size of the Boot Order Array. | |
| @retval EFI_SUCCESS The Boot Option Variable was found and removed | |
| @retval EFI_UNSUPPORTED The Boot Option Variable store was inaccessible | |
| @retval EFI_NOT_FOUND The Boot Option Variable was not found | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| BdsDeleteBootOption ( | |
| IN UINTN OptionNumber, | |
| IN OUT UINT16 *BootOrder, | |
| IN OUT UINTN *BootOrderSize | |
| ) | |
| { | |
| CHAR16 BootOption[9]; | |
| UINTN Index; | |
| EFI_STATUS Status; | |
| UnicodeSPrint (BootOption, sizeof (BootOption), L"Boot%04x", OptionNumber); | |
| Status = gRT->SetVariable ( | |
| BootOption, | |
| &gEfiGlobalVariableGuid, | |
| 0, | |
| 0, | |
| NULL | |
| ); | |
| // | |
| // Deleting variable with existing variable implementation shouldn't fail. | |
| // | |
| ASSERT_EFI_ERROR (Status); | |
| // | |
| // adjust boot order array | |
| // | |
| for (Index = 0; Index < *BootOrderSize / sizeof (UINT16); Index++) { | |
| if (BootOrder[Index] == OptionNumber) { | |
| CopyMem (&BootOrder[Index], &BootOrder[Index+1], *BootOrderSize - (Index+1) * sizeof (UINT16)); | |
| *BootOrderSize -= sizeof (UINT16); | |
| break; | |
| } | |
| } | |
| return Status; | |
| } | |
| /** | |
| Translate the first n characters of an Ascii string to | |
| Unicode characters. The count n is indicated by parameter | |
| Size. If Size is greater than the length of string, then | |
| the entire string is translated. | |
| @param AStr Pointer to input Ascii string. | |
| @param Size The number of characters to translate. | |
| @param UStr Pointer to output Unicode string buffer. | |
| **/ | |
| VOID | |
| AsciiToUnicodeSize ( | |
| IN UINT8 *AStr, | |
| IN UINTN Size, | |
| OUT UINT16 *UStr | |
| ) | |
| { | |
| UINTN Idx; | |
| Idx = 0; | |
| while (AStr[Idx] != 0) { | |
| UStr[Idx] = (CHAR16) AStr[Idx]; | |
| if (Idx == Size) { | |
| break; | |
| } | |
| Idx++; | |
| } | |
| UStr[Idx] = 0; | |
| } | |
| /** | |
| Build Legacy Device Name String according. | |
| @param CurBBSEntry BBS Table. | |
| @param Index Index. | |
| @param BufSize The buffer size. | |
| @param BootString The output string. | |
| **/ | |
| VOID | |
| BdsBuildLegacyDevNameString ( | |
| IN BBS_TABLE *CurBBSEntry, | |
| IN UINTN Index, | |
| IN UINTN BufSize, | |
| OUT CHAR16 *BootString | |
| ) | |
| { | |
| CHAR16 *Fmt; | |
| CHAR16 *Type; | |
| UINT8 *StringDesc; | |
| CHAR16 Temp[80]; | |
| switch (Index) { | |
| // | |
| // Primary Master | |
| // | |
| case 1: | |
| Fmt = L"Primary Master %s"; | |
| break; | |
| // | |
| // Primary Slave | |
| // | |
| case 2: | |
| Fmt = L"Primary Slave %s"; | |
| break; | |
| // | |
| // Secondary Master | |
| // | |
| case 3: | |
| Fmt = L"Secondary Master %s"; | |
| break; | |
| // | |
| // Secondary Slave | |
| // | |
| case 4: | |
| Fmt = L"Secondary Slave %s"; | |
| break; | |
| default: | |
| Fmt = L"%s"; | |
| break; | |
| } | |
| switch (CurBBSEntry->DeviceType) { | |
| case BBS_FLOPPY: | |
| Type = L"Floppy"; | |
| break; | |
| case BBS_HARDDISK: | |
| Type = L"Harddisk"; | |
| break; | |
| case BBS_CDROM: | |
| Type = L"CDROM"; | |
| break; | |
| case BBS_PCMCIA: | |
| Type = L"PCMCIAe"; | |
| break; | |
| case BBS_USB: | |
| Type = L"USB"; | |
| break; | |
| case BBS_EMBED_NETWORK: | |
| Type = L"Network"; | |
| break; | |
| case BBS_BEV_DEVICE: | |
| Type = L"BEVe"; | |
| break; | |
| case BBS_UNKNOWN: | |
| default: | |
| Type = L"Unknown"; | |
| break; | |
| } | |
| // | |
| // If current BBS entry has its description then use it. | |
| // | |
| StringDesc = (UINT8 *) (UINTN) ((CurBBSEntry->DescStringSegment << 4) + CurBBSEntry->DescStringOffset); | |
| if (NULL != StringDesc) { | |
| // | |
| // Only get fisrt 32 characters, this is suggested by BBS spec | |
| // | |
| AsciiToUnicodeSize (StringDesc, 32, Temp); | |
| Fmt = L"%s"; | |
| Type = Temp; | |
| } | |
| // | |
| // BbsTable 16 entries are for onboard IDE. | |
| // Set description string for SATA harddisks, Harddisk 0 ~ Harddisk 11 | |
| // | |
| if (Index >= 5 && Index <= 16 && (CurBBSEntry->DeviceType == BBS_HARDDISK || CurBBSEntry->DeviceType == BBS_CDROM)) { | |
| Fmt = L"%s %d"; | |
| UnicodeSPrint (BootString, BufSize, Fmt, Type, Index - 5); | |
| } else { | |
| UnicodeSPrint (BootString, BufSize, Fmt, Type); | |
| } | |
| } | |
| /** | |
| Create a legacy boot option for the specified entry of | |
| BBS table, save it as variable, and append it to the boot | |
| order list. | |
| @param CurrentBbsEntry Pointer to current BBS table. | |
| @param CurrentBbsDevPath Pointer to the Device Path Protocol instance of BBS | |
| @param Index Index of the specified entry in BBS table. | |
| @param BootOrderList On input, the original boot order list. | |
| On output, the new boot order list attached with the | |
| created node. | |
| @param BootOrderListSize On input, the original size of boot order list. | |
| On output, the size of new boot order list. | |
| @retval EFI_SUCCESS Boot Option successfully created. | |
| @retval EFI_OUT_OF_RESOURCES Fail to allocate necessary memory. | |
| @retval Other Error occurs while setting variable. | |
| **/ | |
| EFI_STATUS | |
| BdsCreateLegacyBootOption ( | |
| IN BBS_TABLE *CurrentBbsEntry, | |
| IN EFI_DEVICE_PATH_PROTOCOL *CurrentBbsDevPath, | |
| IN UINTN Index, | |
| IN OUT UINT16 **BootOrderList, | |
| IN OUT UINTN *BootOrderListSize | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINT16 CurrentBootOptionNo; | |
| UINT16 BootString[10]; | |
| CHAR16 BootDesc[100]; | |
| CHAR8 HelpString[100]; | |
| UINT16 *NewBootOrderList; | |
| UINTN BufferSize; | |
| UINTN StringLen; | |
| VOID *Buffer; | |
| UINT8 *Ptr; | |
| UINT16 CurrentBbsDevPathSize; | |
| UINTN BootOrderIndex; | |
| UINTN BootOrderLastIndex; | |
| UINTN ArrayIndex; | |
| BOOLEAN IndexNotFound; | |
| BBS_BBS_DEVICE_PATH *NewBbsDevPathNode; | |
| if ((*BootOrderList) == NULL) { | |
| CurrentBootOptionNo = 0; | |
| } else { | |
| for (ArrayIndex = 0; ArrayIndex < (UINTN) (*BootOrderListSize / sizeof (UINT16)); ArrayIndex++) { | |
| IndexNotFound = TRUE; | |
| for (BootOrderIndex = 0; BootOrderIndex < (UINTN) (*BootOrderListSize / sizeof (UINT16)); BootOrderIndex++) { | |
| if ((*BootOrderList)[BootOrderIndex] == ArrayIndex) { | |
| IndexNotFound = FALSE; | |
| break; | |
| } | |
| } | |
| if (!IndexNotFound) { | |
| continue; | |
| } else { | |
| break; | |
| } | |
| } | |
| CurrentBootOptionNo = (UINT16) ArrayIndex; | |
| } | |
| UnicodeSPrint ( | |
| BootString, | |
| sizeof (BootString), | |
| L"Boot%04x", | |
| CurrentBootOptionNo | |
| ); | |
| BdsBuildLegacyDevNameString (CurrentBbsEntry, Index, sizeof (BootDesc), BootDesc); | |
| // | |
| // Create new BBS device path node with description string | |
| // | |
| UnicodeStrToAsciiStr (BootDesc, HelpString); | |
| StringLen = AsciiStrLen (HelpString); | |
| NewBbsDevPathNode = AllocateZeroPool (sizeof (BBS_BBS_DEVICE_PATH) + StringLen); | |
| if (NewBbsDevPathNode == NULL) { | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| CopyMem (NewBbsDevPathNode, CurrentBbsDevPath, sizeof (BBS_BBS_DEVICE_PATH)); | |
| CopyMem (NewBbsDevPathNode->String, HelpString, StringLen + 1); | |
| SetDevicePathNodeLength (&(NewBbsDevPathNode->Header), sizeof (BBS_BBS_DEVICE_PATH) + StringLen); | |
| // | |
| // Create entire new CurrentBbsDevPath with end node | |
| // | |
| CurrentBbsDevPath = AppendDevicePathNode ( | |
| NULL, | |
| (EFI_DEVICE_PATH_PROTOCOL *) NewBbsDevPathNode | |
| ); | |
| if (CurrentBbsDevPath == NULL) { | |
| FreePool (NewBbsDevPathNode); | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| CurrentBbsDevPathSize = (UINT16) (GetDevicePathSize (CurrentBbsDevPath)); | |
| BufferSize = sizeof (UINT32) + | |
| sizeof (UINT16) + | |
| StrSize (BootDesc) + | |
| CurrentBbsDevPathSize + | |
| sizeof (BBS_TABLE) + | |
| sizeof (UINT16); | |
| Buffer = AllocateZeroPool (BufferSize); | |
| if (Buffer == NULL) { | |
| FreePool (NewBbsDevPathNode); | |
| FreePool (CurrentBbsDevPath); | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| Ptr = (UINT8 *) Buffer; | |
| *((UINT32 *) Ptr) = LOAD_OPTION_ACTIVE; | |
| Ptr += sizeof (UINT32); | |
| *((UINT16 *) Ptr) = CurrentBbsDevPathSize; | |
| Ptr += sizeof (UINT16); | |
| CopyMem ( | |
| Ptr, | |
| BootDesc, | |
| StrSize (BootDesc) | |
| ); | |
| Ptr += StrSize (BootDesc); | |
| CopyMem ( | |
| Ptr, | |
| CurrentBbsDevPath, | |
| CurrentBbsDevPathSize | |
| ); | |
| Ptr += CurrentBbsDevPathSize; | |
| CopyMem ( | |
| Ptr, | |
| CurrentBbsEntry, | |
| sizeof (BBS_TABLE) | |
| ); | |
| Ptr += sizeof (BBS_TABLE); | |
| *((UINT16 *) Ptr) = (UINT16) Index; | |
| Status = gRT->SetVariable ( | |
| BootString, | |
| &gEfiGlobalVariableGuid, | |
| EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE, | |
| BufferSize, | |
| Buffer | |
| ); | |
| FreePool (Buffer); | |
| Buffer = NULL; | |
| NewBootOrderList = AllocateZeroPool (*BootOrderListSize + sizeof (UINT16)); | |
| if (NULL == NewBootOrderList) { | |
| FreePool (NewBbsDevPathNode); | |
| FreePool (CurrentBbsDevPath); | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| if (*BootOrderList != NULL) { | |
| CopyMem (NewBootOrderList, *BootOrderList, *BootOrderListSize); | |
| FreePool (*BootOrderList); | |
| } | |
| BootOrderLastIndex = (UINTN) (*BootOrderListSize / sizeof (UINT16)); | |
| NewBootOrderList[BootOrderLastIndex] = CurrentBootOptionNo; | |
| *BootOrderListSize += sizeof (UINT16); | |
| *BootOrderList = NewBootOrderList; | |
| FreePool (NewBbsDevPathNode); | |
| FreePool (CurrentBbsDevPath); | |
| return Status; | |
| } | |
| /** | |
| Check if the boot option is a legacy one. | |
| @param BootOptionVar The boot option data payload. | |
| @param BbsEntry The BBS Table. | |
| @param BbsIndex The table index. | |
| @retval TRUE It is a legacy boot option. | |
| @retval FALSE It is not a legacy boot option. | |
| **/ | |
| BOOLEAN | |
| BdsIsLegacyBootOption ( | |
| IN UINT8 *BootOptionVar, | |
| OUT BBS_TABLE **BbsEntry, | |
| OUT UINT16 *BbsIndex | |
| ) | |
| { | |
| UINT8 *Ptr; | |
| EFI_DEVICE_PATH_PROTOCOL *DevicePath; | |
| BOOLEAN Ret; | |
| UINT16 DevPathLen; | |
| Ptr = BootOptionVar; | |
| Ptr += sizeof (UINT32); | |
| DevPathLen = *(UINT16 *) Ptr; | |
| Ptr += sizeof (UINT16); | |
| Ptr += StrSize ((UINT16 *) Ptr); | |
| DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) Ptr; | |
| if ((BBS_DEVICE_PATH == DevicePath->Type) && (BBS_BBS_DP == DevicePath->SubType)) { | |
| Ptr += DevPathLen; | |
| *BbsEntry = (BBS_TABLE *) Ptr; | |
| Ptr += sizeof (BBS_TABLE); | |
| *BbsIndex = *(UINT16 *) Ptr; | |
| Ret = TRUE; | |
| } else { | |
| *BbsEntry = NULL; | |
| Ret = FALSE; | |
| } | |
| return Ret; | |
| } | |
| /** | |
| Delete all the invalid legacy boot options. | |
| @retval EFI_SUCCESS All invalide legacy boot options are deleted. | |
| @retval EFI_OUT_OF_RESOURCES Fail to allocate necessary memory. | |
| @retval EFI_NOT_FOUND Fail to retrive variable of boot order. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| BdsDeleteAllInvalidLegacyBootOptions ( | |
| VOID | |
| ) | |
| { | |
| UINT16 *BootOrder; | |
| UINT8 *BootOptionVar; | |
| UINTN BootOrderSize; | |
| UINTN BootOptionSize; | |
| EFI_STATUS Status; | |
| UINT16 HddCount; | |
| UINT16 BbsCount; | |
| HDD_INFO *LocalHddInfo; | |
| BBS_TABLE *LocalBbsTable; | |
| BBS_TABLE *BbsEntry; | |
| UINT16 BbsIndex; | |
| EFI_LEGACY_BIOS_PROTOCOL *LegacyBios; | |
| UINTN Index; | |
| UINT16 BootOption[10]; | |
| UINT16 BootDesc[100]; | |
| BOOLEAN DescStringMatch; | |
| Status = EFI_SUCCESS; | |
| BootOrder = NULL; | |
| BootOrderSize = 0; | |
| HddCount = 0; | |
| BbsCount = 0; | |
| LocalHddInfo = NULL; | |
| LocalBbsTable = NULL; | |
| BbsEntry = NULL; | |
| Status = gBS->LocateProtocol (&gEfiLegacyBiosProtocolGuid, NULL, (VOID **) &LegacyBios); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| BootOrder = BdsLibGetVariableAndSize ( | |
| L"BootOrder", | |
| &gEfiGlobalVariableGuid, | |
| &BootOrderSize | |
| ); | |
| if (BootOrder == NULL) { | |
| return EFI_NOT_FOUND; | |
| } | |
| LegacyBios->GetBbsInfo ( | |
| LegacyBios, | |
| &HddCount, | |
| &LocalHddInfo, | |
| &BbsCount, | |
| &LocalBbsTable | |
| ); | |
| Index = 0; | |
| while (Index < BootOrderSize / sizeof (UINT16)) { | |
| UnicodeSPrint (BootOption, sizeof (BootOption), L"Boot%04x", BootOrder[Index]); | |
| BootOptionVar = BdsLibGetVariableAndSize ( | |
| BootOption, | |
| &gEfiGlobalVariableGuid, | |
| &BootOptionSize | |
| ); | |
| if (NULL == BootOptionVar) { | |
| BootOptionSize = 0; | |
| Status = gRT->GetVariable ( | |
| BootOption, | |
| &gEfiGlobalVariableGuid, | |
| NULL, | |
| &BootOptionSize, | |
| BootOptionVar | |
| ); | |
| if (Status == EFI_NOT_FOUND) { | |
| // | |
| // Update BootOrder | |
| // | |
| BdsDeleteBootOption ( | |
| BootOrder[Index], | |
| BootOrder, | |
| &BootOrderSize | |
| ); | |
| continue; | |
| } else { | |
| FreePool (BootOrder); | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| } | |
| // | |
| // Skip Non-Legacy boot option | |
| // | |
| if (!BdsIsLegacyBootOption (BootOptionVar, &BbsEntry, &BbsIndex)) { | |
| if (BootOptionVar!= NULL) { | |
| FreePool (BootOptionVar); | |
| } | |
| Index++; | |
| continue; | |
| } | |
| if (BbsIndex < BbsCount) { | |
| // | |
| // Check if BBS Description String is changed | |
| // | |
| DescStringMatch = FALSE; | |
| BdsBuildLegacyDevNameString ( | |
| &LocalBbsTable[BbsIndex], | |
| BbsIndex, | |
| sizeof (BootDesc), | |
| BootDesc | |
| ); | |
| if (StrCmp (BootDesc, (UINT16*)(BootOptionVar + sizeof (UINT32) + sizeof (UINT16))) == 0) { | |
| DescStringMatch = TRUE; | |
| } | |
| if (!((LocalBbsTable[BbsIndex].BootPriority == BBS_IGNORE_ENTRY) || | |
| (LocalBbsTable[BbsIndex].BootPriority == BBS_DO_NOT_BOOT_FROM)) && | |
| (LocalBbsTable[BbsIndex].DeviceType == BbsEntry->DeviceType) && | |
| DescStringMatch) { | |
| Index++; | |
| continue; | |
| } | |
| } | |
| if (BootOptionVar != NULL) { | |
| FreePool (BootOptionVar); | |
| } | |
| // | |
| // should delete | |
| // | |
| BdsDeleteBootOption ( | |
| BootOrder[Index], | |
| BootOrder, | |
| &BootOrderSize | |
| ); | |
| } | |
| // | |
| // Adjust the number of boot options. | |
| // | |
| Status = gRT->SetVariable ( | |
| L"BootOrder", | |
| &gEfiGlobalVariableGuid, | |
| EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE, | |
| BootOrderSize, | |
| BootOrder | |
| ); | |
| // | |
| // Shrinking variable with existing variable implementation shouldn't fail. | |
| // | |
| ASSERT_EFI_ERROR (Status); | |
| FreePool (BootOrder); | |
| return Status; | |
| } | |
| /** | |
| Find all legacy boot option by device type. | |
| @param BootOrder The boot order array. | |
| @param BootOptionNum The number of boot option. | |
| @param DevType Device type. | |
| @param DevName Device name. | |
| @param Attribute The boot option attribute. | |
| @param BbsIndex The BBS table index. | |
| @param OptionNumber The boot option index. | |
| @retval TRUE The Legacy boot option is found. | |
| @retval FALSE The legacy boot option is not found. | |
| **/ | |
| BOOLEAN | |
| BdsFindLegacyBootOptionByDevTypeAndName ( | |
| IN UINT16 *BootOrder, | |
| IN UINTN BootOptionNum, | |
| IN UINT16 DevType, | |
| IN CHAR16 *DevName, | |
| OUT UINT32 *Attribute, | |
| OUT UINT16 *BbsIndex, | |
| OUT UINT16 *OptionNumber | |
| ) | |
| { | |
| UINTN Index; | |
| CHAR16 BootOption[9]; | |
| UINTN BootOptionSize; | |
| UINT8 *BootOptionVar; | |
| BBS_TABLE *BbsEntry; | |
| BOOLEAN Found; | |
| BbsEntry = NULL; | |
| Found = FALSE; | |
| if (NULL == BootOrder) { | |
| return Found; | |
| } | |
| // | |
| // Loop all boot option from variable | |
| // | |
| for (Index = 0; Index < BootOptionNum; Index++) { | |
| UnicodeSPrint (BootOption, sizeof (BootOption), L"Boot%04x", (UINTN) BootOrder[Index]); | |
| BootOptionVar = BdsLibGetVariableAndSize ( | |
| BootOption, | |
| &gEfiGlobalVariableGuid, | |
| &BootOptionSize | |
| ); | |
| if (NULL == BootOptionVar) { | |
| continue; | |
| } | |
| // | |
| // Skip Non-legacy boot option | |
| // | |
| if (!BdsIsLegacyBootOption (BootOptionVar, &BbsEntry, BbsIndex)) { | |
| FreePool (BootOptionVar); | |
| continue; | |
| } | |
| if ( | |
| (BbsEntry->DeviceType != DevType) || | |
| (StrCmp (DevName, (CHAR16*)(BootOptionVar + sizeof (UINT32) + sizeof (UINT16))) != 0) | |
| ) { | |
| FreePool (BootOptionVar); | |
| continue; | |
| } | |
| *Attribute = *(UINT32 *) BootOptionVar; | |
| *OptionNumber = BootOrder[Index]; | |
| Found = TRUE; | |
| FreePool (BootOptionVar); | |
| break; | |
| } | |
| return Found; | |
| } | |
| /** | |
| Create a legacy boot option. | |
| @param BbsItem The BBS Table entry. | |
| @param Index Index of the specified entry in BBS table. | |
| @param BootOrderList The boot order list. | |
| @param BootOrderListSize The size of boot order list. | |
| @retval EFI_OUT_OF_RESOURCE No enough memory. | |
| @retval EFI_SUCCESS The function complete successfully. | |
| @return Other value if the legacy boot option is not created. | |
| **/ | |
| EFI_STATUS | |
| BdsCreateOneLegacyBootOption ( | |
| IN BBS_TABLE *BbsItem, | |
| IN UINTN Index, | |
| IN OUT UINT16 **BootOrderList, | |
| IN OUT UINTN *BootOrderListSize | |
| ) | |
| { | |
| BBS_BBS_DEVICE_PATH BbsDevPathNode; | |
| EFI_STATUS Status; | |
| EFI_DEVICE_PATH_PROTOCOL *DevPath; | |
| DevPath = NULL; | |
| // | |
| // Create device path node. | |
| // | |
| BbsDevPathNode.Header.Type = BBS_DEVICE_PATH; | |
| BbsDevPathNode.Header.SubType = BBS_BBS_DP; | |
| SetDevicePathNodeLength (&BbsDevPathNode.Header, sizeof (BBS_BBS_DEVICE_PATH)); | |
| BbsDevPathNode.DeviceType = BbsItem->DeviceType; | |
| CopyMem (&BbsDevPathNode.StatusFlag, &BbsItem->StatusFlags, sizeof (UINT16)); | |
| DevPath = AppendDevicePathNode ( | |
| NULL, | |
| (EFI_DEVICE_PATH_PROTOCOL *) &BbsDevPathNode | |
| ); | |
| if (NULL == DevPath) { | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| Status = BdsCreateLegacyBootOption ( | |
| BbsItem, | |
| DevPath, | |
| Index, | |
| BootOrderList, | |
| BootOrderListSize | |
| ); | |
| BbsItem->BootPriority = 0x00; | |
| FreePool (DevPath); | |
| return Status; | |
| } | |
| /** | |
| Add the legacy boot options from BBS table if they do not exist. | |
| @retval EFI_SUCCESS The boot options are added successfully | |
| or they are already in boot options. | |
| @retval EFI_NOT_FOUND No legacy boot options is found. | |
| @retval EFI_OUT_OF_RESOURCE No enough memory. | |
| @return Other value LegacyBoot options are not added. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| BdsAddNonExistingLegacyBootOptions ( | |
| VOID | |
| ) | |
| { | |
| UINT16 *BootOrder; | |
| UINTN BootOrderSize; | |
| EFI_STATUS Status; | |
| CHAR16 Desc[100]; | |
| UINT16 HddCount; | |
| UINT16 BbsCount; | |
| HDD_INFO *LocalHddInfo; | |
| BBS_TABLE *LocalBbsTable; | |
| UINT16 BbsIndex; | |
| EFI_LEGACY_BIOS_PROTOCOL *LegacyBios; | |
| UINT16 Index; | |
| UINT32 Attribute; | |
| UINT16 OptionNumber; | |
| BOOLEAN Exist; | |
| HddCount = 0; | |
| BbsCount = 0; | |
| LocalHddInfo = NULL; | |
| LocalBbsTable = NULL; | |
| Status = gBS->LocateProtocol (&gEfiLegacyBiosProtocolGuid, NULL, (VOID **) &LegacyBios); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| LegacyBios->GetBbsInfo ( | |
| LegacyBios, | |
| &HddCount, | |
| &LocalHddInfo, | |
| &BbsCount, | |
| &LocalBbsTable | |
| ); | |
| BootOrder = BdsLibGetVariableAndSize ( | |
| L"BootOrder", | |
| &gEfiGlobalVariableGuid, | |
| &BootOrderSize | |
| ); | |
| if (BootOrder == NULL) { | |
| BootOrderSize = 0; | |
| } | |
| for (Index = 0; Index < BbsCount; Index++) { | |
| if ((LocalBbsTable[Index].BootPriority == BBS_IGNORE_ENTRY) || | |
| (LocalBbsTable[Index].BootPriority == BBS_DO_NOT_BOOT_FROM) | |
| ) { | |
| continue; | |
| } | |
| BdsBuildLegacyDevNameString (&LocalBbsTable[Index], Index, sizeof (Desc), Desc); | |
| Exist = BdsFindLegacyBootOptionByDevTypeAndName ( | |
| BootOrder, | |
| BootOrderSize / sizeof (UINT16), | |
| LocalBbsTable[Index].DeviceType, | |
| Desc, | |
| &Attribute, | |
| &BbsIndex, | |
| &OptionNumber | |
| ); | |
| if (!Exist) { | |
| // | |
| // Not found such type of legacy device in boot options or we found but it's disabled | |
| // so we have to create one and put it to the tail of boot order list | |
| // | |
| Status = BdsCreateOneLegacyBootOption ( | |
| &LocalBbsTable[Index], | |
| Index, | |
| &BootOrder, | |
| &BootOrderSize | |
| ); | |
| if (!EFI_ERROR (Status)) { | |
| ASSERT (BootOrder != NULL); | |
| BbsIndex = Index; | |
| OptionNumber = BootOrder[BootOrderSize / sizeof (UINT16) - 1]; | |
| } | |
| } | |
| ASSERT (BbsIndex == Index); | |
| } | |
| Status = gRT->SetVariable ( | |
| L"BootOrder", | |
| &gEfiGlobalVariableGuid, | |
| EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE, | |
| BootOrderSize, | |
| BootOrder | |
| ); | |
| if (BootOrder != NULL) { | |
| FreePool (BootOrder); | |
| } | |
| return Status; | |
| } | |
| /** | |
| Fill the device order buffer. | |
| @param BbsTable The BBS table. | |
| @param BbsType The BBS Type. | |
| @param BbsCount The BBS Count. | |
| @param Buf device order buffer. | |
| @return The device order buffer. | |
| **/ | |
| UINT16 * | |
| BdsFillDevOrderBuf ( | |
| IN BBS_TABLE *BbsTable, | |
| IN BBS_TYPE BbsType, | |
| IN UINTN BbsCount, | |
| OUT UINT16 *Buf | |
| ) | |
| { | |
| UINTN Index; | |
| for (Index = 0; Index < BbsCount; Index++) { | |
| if (BbsTable[Index].BootPriority == BBS_IGNORE_ENTRY) { | |
| continue; | |
| } | |
| if (BbsTable[Index].DeviceType != BbsType) { | |
| continue; | |
| } | |
| *Buf = (UINT16) (Index & 0xFF); | |
| Buf++; | |
| } | |
| return Buf; | |
| } | |
| /** | |
| Create the device order buffer. | |
| @param BbsTable The BBS table. | |
| @param BbsCount The BBS Count. | |
| @retval EFI_SUCCES The buffer is created and the EFI variable named | |
| VAR_LEGACY_DEV_ORDER and gEfiLegacyDevOrderVariableGuid is | |
| set correctly. | |
| @retval EFI_OUT_OF_RESOURCES Memmory or storage is not enough. | |
| @retval EFI_DEVICE_ERROR Fail to add the device order into EFI variable fail | |
| because of hardware error. | |
| **/ | |
| EFI_STATUS | |
| BdsCreateDevOrder ( | |
| IN BBS_TABLE *BbsTable, | |
| IN UINT16 BbsCount | |
| ) | |
| { | |
| UINTN Index; | |
| UINTN FDCount; | |
| UINTN HDCount; | |
| UINTN CDCount; | |
| UINTN NETCount; | |
| UINTN BEVCount; | |
| UINTN TotalSize; | |
| UINTN HeaderSize; | |
| LEGACY_DEV_ORDER_ENTRY *DevOrder; | |
| LEGACY_DEV_ORDER_ENTRY *DevOrderPtr; | |
| EFI_STATUS Status; | |
| FDCount = 0; | |
| HDCount = 0; | |
| CDCount = 0; | |
| NETCount = 0; | |
| BEVCount = 0; | |
| TotalSize = 0; | |
| HeaderSize = sizeof (BBS_TYPE) + sizeof (UINT16); | |
| DevOrder = NULL; | |
| Status = EFI_SUCCESS; | |
| // | |
| // Count all boot devices | |
| // | |
| for (Index = 0; Index < BbsCount; Index++) { | |
| if (BbsTable[Index].BootPriority == BBS_IGNORE_ENTRY) { | |
| continue; | |
| } | |
| switch (BbsTable[Index].DeviceType) { | |
| case BBS_FLOPPY: | |
| FDCount++; | |
| break; | |
| case BBS_HARDDISK: | |
| HDCount++; | |
| break; | |
| case BBS_CDROM: | |
| CDCount++; | |
| break; | |
| case BBS_EMBED_NETWORK: | |
| NETCount++; | |
| break; | |
| case BBS_BEV_DEVICE: | |
| BEVCount++; | |
| break; | |
| default: | |
| break; | |
| } | |
| } | |
| TotalSize += (HeaderSize + sizeof (UINT16) * FDCount); | |
| TotalSize += (HeaderSize + sizeof (UINT16) * HDCount); | |
| TotalSize += (HeaderSize + sizeof (UINT16) * CDCount); | |
| TotalSize += (HeaderSize + sizeof (UINT16) * NETCount); | |
| TotalSize += (HeaderSize + sizeof (UINT16) * BEVCount); | |
| // | |
| // Create buffer to hold all boot device order | |
| // | |
| DevOrder = AllocateZeroPool (TotalSize); | |
| if (NULL == DevOrder) { | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| DevOrderPtr = DevOrder; | |
| DevOrderPtr->BbsType = BBS_FLOPPY; | |
| DevOrderPtr->Length = (UINT16) (sizeof (DevOrderPtr->Length) + FDCount * sizeof (UINT16)); | |
| DevOrderPtr = (LEGACY_DEV_ORDER_ENTRY *) BdsFillDevOrderBuf (BbsTable, BBS_FLOPPY, BbsCount, DevOrderPtr->Data); | |
| DevOrderPtr->BbsType = BBS_HARDDISK; | |
| DevOrderPtr->Length = (UINT16) (sizeof (UINT16) + HDCount * sizeof (UINT16)); | |
| DevOrderPtr = (LEGACY_DEV_ORDER_ENTRY *) BdsFillDevOrderBuf (BbsTable, BBS_HARDDISK, BbsCount, DevOrderPtr->Data); | |
| DevOrderPtr->BbsType = BBS_CDROM; | |
| DevOrderPtr->Length = (UINT16) (sizeof (UINT16) + CDCount * sizeof (UINT16)); | |
| DevOrderPtr = (LEGACY_DEV_ORDER_ENTRY *) BdsFillDevOrderBuf (BbsTable, BBS_CDROM, BbsCount, DevOrderPtr->Data); | |
| DevOrderPtr->BbsType = BBS_EMBED_NETWORK; | |
| DevOrderPtr->Length = (UINT16) (sizeof (UINT16) + NETCount * sizeof (UINT16)); | |
| DevOrderPtr = (LEGACY_DEV_ORDER_ENTRY *) BdsFillDevOrderBuf (BbsTable, BBS_EMBED_NETWORK, BbsCount, DevOrderPtr->Data); | |
| DevOrderPtr->BbsType = BBS_BEV_DEVICE; | |
| DevOrderPtr->Length = (UINT16) (sizeof (UINT16) + BEVCount * sizeof (UINT16)); | |
| DevOrderPtr = (LEGACY_DEV_ORDER_ENTRY *) BdsFillDevOrderBuf (BbsTable, BBS_BEV_DEVICE, BbsCount, DevOrderPtr->Data); | |
| ASSERT (TotalSize == (UINTN) ((UINT8 *) DevOrderPtr - (UINT8 *) DevOrder)); | |
| // | |
| // Save device order for legacy boot device to variable. | |
| // | |
| Status = gRT->SetVariable ( | |
| VAR_LEGACY_DEV_ORDER, | |
| &gEfiLegacyDevOrderVariableGuid, | |
| EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE, | |
| TotalSize, | |
| DevOrder | |
| ); | |
| FreePool (DevOrder); | |
| return Status; | |
| } | |
| /** | |
| Add the legacy boot devices from BBS table into | |
| the legacy device boot order. | |
| @retval EFI_SUCCESS The boot devices are added successfully. | |
| @retval EFI_NOT_FOUND The legacy boot devices are not found. | |
| @retval EFI_OUT_OF_RESOURCES Memmory or storage is not enough. | |
| @retval EFI_DEVICE_ERROR Fail to add the legacy device boot order into EFI variable | |
| because of hardware error. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| BdsUpdateLegacyDevOrder ( | |
| VOID | |
| ) | |
| { | |
| LEGACY_DEV_ORDER_ENTRY *DevOrder; | |
| LEGACY_DEV_ORDER_ENTRY *NewDevOrder; | |
| LEGACY_DEV_ORDER_ENTRY *Ptr; | |
| LEGACY_DEV_ORDER_ENTRY *NewPtr; | |
| UINTN DevOrderSize; | |
| EFI_LEGACY_BIOS_PROTOCOL *LegacyBios; | |
| EFI_STATUS Status; | |
| UINT16 HddCount; | |
| UINT16 BbsCount; | |
| HDD_INFO *LocalHddInfo; | |
| BBS_TABLE *LocalBbsTable; | |
| UINTN Index; | |
| UINTN Index2; | |
| UINTN *Idx; | |
| UINTN FDCount; | |
| UINTN HDCount; | |
| UINTN CDCount; | |
| UINTN NETCount; | |
| UINTN BEVCount; | |
| UINTN TotalSize; | |
| UINTN HeaderSize; | |
| UINT16 *NewFDPtr; | |
| UINT16 *NewHDPtr; | |
| UINT16 *NewCDPtr; | |
| UINT16 *NewNETPtr; | |
| UINT16 *NewBEVPtr; | |
| UINT16 *NewDevPtr; | |
| UINTN FDIndex; | |
| UINTN HDIndex; | |
| UINTN CDIndex; | |
| UINTN NETIndex; | |
| UINTN BEVIndex; | |
| Idx = NULL; | |
| FDCount = 0; | |
| HDCount = 0; | |
| CDCount = 0; | |
| NETCount = 0; | |
| BEVCount = 0; | |
| TotalSize = 0; | |
| HeaderSize = sizeof (BBS_TYPE) + sizeof (UINT16); | |
| FDIndex = 0; | |
| HDIndex = 0; | |
| CDIndex = 0; | |
| NETIndex = 0; | |
| BEVIndex = 0; | |
| NewDevPtr = NULL; | |
| Status = gBS->LocateProtocol (&gEfiLegacyBiosProtocolGuid, NULL, (VOID **) &LegacyBios); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| Status = LegacyBios->GetBbsInfo ( | |
| LegacyBios, | |
| &HddCount, | |
| &LocalHddInfo, | |
| &BbsCount, | |
| &LocalBbsTable | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| DevOrder = BdsLibGetVariableAndSize ( | |
| VAR_LEGACY_DEV_ORDER, | |
| &gEfiLegacyDevOrderVariableGuid, | |
| &DevOrderSize | |
| ); | |
| if (NULL == DevOrder) { | |
| return BdsCreateDevOrder (LocalBbsTable, BbsCount); | |
| } | |
| // | |
| // First we figure out how many boot devices with same device type respectively | |
| // | |
| for (Index = 0; Index < BbsCount; Index++) { | |
| if ((LocalBbsTable[Index].BootPriority == BBS_IGNORE_ENTRY) || | |
| (LocalBbsTable[Index].BootPriority == BBS_DO_NOT_BOOT_FROM) | |
| ) { | |
| continue; | |
| } | |
| switch (LocalBbsTable[Index].DeviceType) { | |
| case BBS_FLOPPY: | |
| FDCount++; | |
| break; | |
| case BBS_HARDDISK: | |
| HDCount++; | |
| break; | |
| case BBS_CDROM: | |
| CDCount++; | |
| break; | |
| case BBS_EMBED_NETWORK: | |
| NETCount++; | |
| break; | |
| case BBS_BEV_DEVICE: | |
| BEVCount++; | |
| break; | |
| default: | |
| break; | |
| } | |
| } | |
| TotalSize += (HeaderSize + FDCount * sizeof (UINT16)); | |
| TotalSize += (HeaderSize + HDCount * sizeof (UINT16)); | |
| TotalSize += (HeaderSize + CDCount * sizeof (UINT16)); | |
| TotalSize += (HeaderSize + NETCount * sizeof (UINT16)); | |
| TotalSize += (HeaderSize + BEVCount * sizeof (UINT16)); | |
| NewDevOrder = AllocateZeroPool (TotalSize); | |
| if (NULL == NewDevOrder) { | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| // | |
| // copy FD | |
| // | |
| Ptr = DevOrder; | |
| NewPtr = NewDevOrder; | |
| NewPtr->BbsType = Ptr->BbsType; | |
| NewPtr->Length = (UINT16) (sizeof (UINT16) + FDCount * sizeof (UINT16)); | |
| for (Index = 0; Index < Ptr->Length / sizeof (UINT16) - 1; Index++) { | |
| if (LocalBbsTable[Ptr->Data[Index] & 0xFF].BootPriority == BBS_IGNORE_ENTRY || | |
| LocalBbsTable[Ptr->Data[Index] & 0xFF].BootPriority == BBS_DO_NOT_BOOT_FROM || | |
| LocalBbsTable[Ptr->Data[Index] & 0xFF].DeviceType != BBS_FLOPPY | |
| ) { | |
| continue; | |
| } | |
| NewPtr->Data[FDIndex] = Ptr->Data[Index]; | |
| FDIndex++; | |
| } | |
| NewFDPtr = NewPtr->Data; | |
| // | |
| // copy HD | |
| // | |
| Ptr = (LEGACY_DEV_ORDER_ENTRY *) (&Ptr->Data[Ptr->Length / sizeof (UINT16) - 1]); | |
| NewPtr = (LEGACY_DEV_ORDER_ENTRY *) (&NewPtr->Data[NewPtr->Length / sizeof (UINT16) -1]); | |
| NewPtr->BbsType = Ptr->BbsType; | |
| NewPtr->Length = (UINT16) (sizeof (UINT16) + HDCount * sizeof (UINT16)); | |
| for (Index = 0; Index < Ptr->Length / sizeof (UINT16) - 1; Index++) { | |
| if (LocalBbsTable[Ptr->Data[Index] & 0xFF].BootPriority == BBS_IGNORE_ENTRY || | |
| LocalBbsTable[Ptr->Data[Index] & 0xFF].BootPriority == BBS_DO_NOT_BOOT_FROM || | |
| LocalBbsTable[Ptr->Data[Index] & 0xFF].BootPriority == BBS_LOWEST_PRIORITY || | |
| LocalBbsTable[Ptr->Data[Index] & 0xFF].DeviceType != BBS_HARDDISK | |
| ) { | |
| continue; | |
| } | |
| NewPtr->Data[HDIndex] = Ptr->Data[Index]; | |
| HDIndex++; | |
| } | |
| NewHDPtr = NewPtr->Data; | |
| // | |
| // copy CD | |
| // | |
| Ptr = (LEGACY_DEV_ORDER_ENTRY *) (&Ptr->Data[Ptr->Length / sizeof (UINT16) - 1]); | |
| NewPtr = (LEGACY_DEV_ORDER_ENTRY *) (&NewPtr->Data[NewPtr->Length / sizeof (UINT16) -1]); | |
| NewPtr->BbsType = Ptr->BbsType; | |
| NewPtr->Length = (UINT16) (sizeof (UINT16) + CDCount * sizeof (UINT16)); | |
| for (Index = 0; Index < Ptr->Length / sizeof (UINT16) - 1; Index++) { | |
| if (LocalBbsTable[Ptr->Data[Index] & 0xFF].BootPriority == BBS_IGNORE_ENTRY || | |
| LocalBbsTable[Ptr->Data[Index] & 0xFF].BootPriority == BBS_DO_NOT_BOOT_FROM || | |
| LocalBbsTable[Ptr->Data[Index] & 0xFF].BootPriority == BBS_LOWEST_PRIORITY || | |
| LocalBbsTable[Ptr->Data[Index] & 0xFF].DeviceType != BBS_CDROM | |
| ) { | |
| continue; | |
| } | |
| NewPtr->Data[CDIndex] = Ptr->Data[Index]; | |
| CDIndex++; | |
| } | |
| NewCDPtr = NewPtr->Data; | |
| // | |
| // copy NET | |
| // | |
| Ptr = (LEGACY_DEV_ORDER_ENTRY *) (&Ptr->Data[Ptr->Length / sizeof (UINT16) - 1]); | |
| NewPtr = (LEGACY_DEV_ORDER_ENTRY *) (&NewPtr->Data[NewPtr->Length / sizeof (UINT16) -1]); | |
| NewPtr->BbsType = Ptr->BbsType; | |
| NewPtr->Length = (UINT16) (sizeof (UINT16) + NETCount * sizeof (UINT16)); | |
| for (Index = 0; Index < Ptr->Length / sizeof (UINT16) - 1; Index++) { | |
| if (LocalBbsTable[Ptr->Data[Index] & 0xFF].BootPriority == BBS_IGNORE_ENTRY || | |
| LocalBbsTable[Ptr->Data[Index] & 0xFF].BootPriority == BBS_DO_NOT_BOOT_FROM || | |
| LocalBbsTable[Ptr->Data[Index] & 0xFF].BootPriority == BBS_LOWEST_PRIORITY || | |
| LocalBbsTable[Ptr->Data[Index] & 0xFF].DeviceType != BBS_EMBED_NETWORK | |
| ) { | |
| continue; | |
| } | |
| NewPtr->Data[NETIndex] = Ptr->Data[Index]; | |
| NETIndex++; | |
| } | |
| NewNETPtr = NewPtr->Data; | |
| // | |
| // copy BEV | |
| // | |
| Ptr = (LEGACY_DEV_ORDER_ENTRY *) (&Ptr->Data[Ptr->Length / sizeof (UINT16) - 1]); | |
| NewPtr = (LEGACY_DEV_ORDER_ENTRY *) (&NewPtr->Data[NewPtr->Length / sizeof (UINT16) -1]); | |
| NewPtr->BbsType = Ptr->BbsType; | |
| NewPtr->Length = (UINT16) (sizeof (UINT16) + BEVCount * sizeof (UINT16)); | |
| for (Index = 0; Index < Ptr->Length / sizeof (UINT16) - 1; Index++) { | |
| if (LocalBbsTable[Ptr->Data[Index] & 0xFF].BootPriority == BBS_IGNORE_ENTRY || | |
| LocalBbsTable[Ptr->Data[Index] & 0xFF].BootPriority == BBS_DO_NOT_BOOT_FROM || | |
| LocalBbsTable[Ptr->Data[Index] & 0xFF].BootPriority == BBS_LOWEST_PRIORITY || | |
| LocalBbsTable[Ptr->Data[Index] & 0xFF].DeviceType != BBS_BEV_DEVICE | |
| ) { | |
| continue; | |
| } | |
| NewPtr->Data[BEVIndex] = Ptr->Data[Index]; | |
| BEVIndex++; | |
| } | |
| NewBEVPtr = NewPtr->Data; | |
| for (Index = 0; Index < BbsCount; Index++) { | |
| if ((LocalBbsTable[Index].BootPriority == BBS_IGNORE_ENTRY) || | |
| (LocalBbsTable[Index].BootPriority == BBS_DO_NOT_BOOT_FROM) | |
| ) { | |
| continue; | |
| } | |
| switch (LocalBbsTable[Index].DeviceType) { | |
| case BBS_FLOPPY: | |
| Idx = &FDIndex; | |
| NewDevPtr = NewFDPtr; | |
| break; | |
| case BBS_HARDDISK: | |
| Idx = &HDIndex; | |
| NewDevPtr = NewHDPtr; | |
| break; | |
| case BBS_CDROM: | |
| Idx = &CDIndex; | |
| NewDevPtr = NewCDPtr; | |
| break; | |
| case BBS_EMBED_NETWORK: | |
| Idx = &NETIndex; | |
| NewDevPtr = NewNETPtr; | |
| break; | |
| case BBS_BEV_DEVICE: | |
| Idx = &BEVIndex; | |
| NewDevPtr = NewBEVPtr; | |
| break; | |
| default: | |
| Idx = NULL; | |
| break; | |
| } | |
| // | |
| // at this point we have copied those valid indexes to new buffer | |
| // and we should check if there is any new appeared boot device | |
| // | |
| if (Idx != NULL) { | |
| for (Index2 = 0; Index2 < *Idx; Index2++) { | |
| if ((NewDevPtr[Index2] & 0xFF) == (UINT16) Index) { | |
| break; | |
| } | |
| } | |
| if (Index2 == *Idx) { | |
| // | |
| // Index2 == *Idx means we didn't find Index | |
| // so Index is a new appeared device's index in BBS table | |
| // insert it before disabled indexes. | |
| // | |
| for (Index2 = 0; Index2 < *Idx; Index2++) { | |
| if ((NewDevPtr[Index2] & 0xFF00) == 0xFF00) { | |
| break; | |
| } | |
| } | |
| CopyMem (&NewDevPtr[Index2 + 1], &NewDevPtr[Index2], (*Idx - Index2) * sizeof (UINT16)); | |
| NewDevPtr[Index2] = (UINT16) (Index & 0xFF); | |
| (*Idx)++; | |
| } | |
| } | |
| } | |
| FreePool (DevOrder); | |
| Status = gRT->SetVariable ( | |
| VAR_LEGACY_DEV_ORDER, | |
| &gEfiLegacyDevOrderVariableGuid, | |
| EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE, | |
| TotalSize, | |
| NewDevOrder | |
| ); | |
| FreePool (NewDevOrder); | |
| return Status; | |
| } | |
| /** | |
| Set Boot Priority for specified device type. | |
| @param DeviceType The device type. | |
| @param BbsIndex The BBS index to set the highest priority. Ignore when -1. | |
| @param LocalBbsTable The BBS table. | |
| @param Priority The prority table. | |
| @retval EFI_SUCCESS The function completes successfully. | |
| @retval EFI_NOT_FOUND Failed to find device. | |
| @retval EFI_OUT_OF_RESOURCES Failed to get the efi variable of device order. | |
| **/ | |
| EFI_STATUS | |
| BdsSetBootPriority4SameTypeDev ( | |
| IN UINT16 DeviceType, | |
| IN UINTN BbsIndex, | |
| IN OUT BBS_TABLE *LocalBbsTable, | |
| IN OUT UINT16 *Priority | |
| ) | |
| { | |
| LEGACY_DEV_ORDER_ENTRY *DevOrder; | |
| LEGACY_DEV_ORDER_ENTRY *DevOrderPtr; | |
| UINTN DevOrderSize; | |
| UINTN Index; | |
| DevOrder = BdsLibGetVariableAndSize ( | |
| VAR_LEGACY_DEV_ORDER, | |
| &gEfiLegacyDevOrderVariableGuid, | |
| &DevOrderSize | |
| ); | |
| if (NULL == DevOrder) { | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| DevOrderPtr = DevOrder; | |
| while ((UINT8 *) DevOrderPtr < (UINT8 *) DevOrder + DevOrderSize) { | |
| if (DevOrderPtr->BbsType == DeviceType) { | |
| break; | |
| } | |
| DevOrderPtr = (LEGACY_DEV_ORDER_ENTRY *) ((UINTN) DevOrderPtr + sizeof (BBS_TYPE) + DevOrderPtr->Length); | |
| } | |
| if ((UINT8 *) DevOrderPtr >= (UINT8 *) DevOrder + DevOrderSize) { | |
| FreePool (DevOrder); | |
| return EFI_NOT_FOUND; | |
| } | |
| if (BbsIndex != (UINTN) -1) { | |
| LocalBbsTable[BbsIndex].BootPriority = *Priority; | |
| (*Priority)++; | |
| } | |
| // | |
| // If the high byte of the DevIndex is 0xFF, it indicates that this device has been disabled. | |
| // | |
| for (Index = 0; Index < DevOrderPtr->Length / sizeof (UINT16) - 1; Index++) { | |
| if ((DevOrderPtr->Data[Index] & 0xFF00) == 0xFF00) { | |
| // | |
| // LocalBbsTable[DevIndex[Index] & 0xFF].BootPriority = BBS_DISABLED_ENTRY; | |
| // | |
| } else if (DevOrderPtr->Data[Index] != BbsIndex) { | |
| LocalBbsTable[DevOrderPtr->Data[Index]].BootPriority = *Priority; | |
| (*Priority)++; | |
| } | |
| } | |
| FreePool (DevOrder); | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Print the BBS Table. | |
| @param LocalBbsTable The BBS table. | |
| @param BbsCount The count of entry in BBS table. | |
| **/ | |
| VOID | |
| PrintBbsTable ( | |
| IN BBS_TABLE *LocalBbsTable, | |
| IN UINT16 BbsCount | |
| ) | |
| { | |
| UINT16 Idx; | |
| DEBUG ((DEBUG_ERROR, "\n")); | |
| DEBUG ((DEBUG_ERROR, " NO Prio bb/dd/ff cl/sc Type Stat segm:offs\n")); | |
| DEBUG ((DEBUG_ERROR, "=============================================\n")); | |
| for (Idx = 0; Idx < BbsCount; Idx++) { | |
| if ((LocalBbsTable[Idx].BootPriority == BBS_IGNORE_ENTRY) || | |
| (LocalBbsTable[Idx].BootPriority == BBS_DO_NOT_BOOT_FROM) || | |
| (LocalBbsTable[Idx].BootPriority == BBS_LOWEST_PRIORITY) | |
| ) { | |
| continue; | |
| } | |
| DEBUG ( | |
| (DEBUG_ERROR, | |
| " %02x: %04x %02x/%02x/%02x %02x/%02x %04x %04x %04x:%04x\n", | |
| (UINTN) Idx, | |
| (UINTN) LocalBbsTable[Idx].BootPriority, | |
| (UINTN) LocalBbsTable[Idx].Bus, | |
| (UINTN) LocalBbsTable[Idx].Device, | |
| (UINTN) LocalBbsTable[Idx].Function, | |
| (UINTN) LocalBbsTable[Idx].Class, | |
| (UINTN) LocalBbsTable[Idx].SubClass, | |
| (UINTN) LocalBbsTable[Idx].DeviceType, | |
| (UINTN) * (UINT16 *) &LocalBbsTable[Idx].StatusFlags, | |
| (UINTN) LocalBbsTable[Idx].BootHandlerSegment, | |
| (UINTN) LocalBbsTable[Idx].BootHandlerOffset, | |
| (UINTN) ((LocalBbsTable[Idx].MfgStringSegment << 4) + LocalBbsTable[Idx].MfgStringOffset), | |
| (UINTN) ((LocalBbsTable[Idx].DescStringSegment << 4) + LocalBbsTable[Idx].DescStringOffset)) | |
| ); | |
| } | |
| DEBUG ((DEBUG_ERROR, "\n")); | |
| } | |
| /** | |
| Set the boot priority for BBS entries based on boot option entry and boot order. | |
| @param Entry The boot option is to be checked for refresh BBS table. | |
| @retval EFI_SUCCESS The boot priority for BBS entries is refreshed successfully. | |
| @retval EFI_NOT_FOUND BBS entries can't be found. | |
| @retval EFI_OUT_OF_RESOURCES Failed to get the legacy device boot order. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| BdsRefreshBbsTableForBoot ( | |
| IN BDS_COMMON_OPTION *Entry | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINT16 BbsIndex; | |
| UINT16 HddCount; | |
| UINT16 BbsCount; | |
| HDD_INFO *LocalHddInfo; | |
| BBS_TABLE *LocalBbsTable; | |
| UINT16 DevType; | |
| EFI_LEGACY_BIOS_PROTOCOL *LegacyBios; | |
| UINTN Index; | |
| UINT16 Priority; | |
| UINT16 *BootOrder; | |
| UINTN BootOrderSize; | |
| UINT8 *BootOptionVar; | |
| UINTN BootOptionSize; | |
| CHAR16 BootOption[9]; | |
| UINT8 *Ptr; | |
| UINT16 DevPathLen; | |
| EFI_DEVICE_PATH_PROTOCOL *DevPath; | |
| UINT16 *DeviceType; | |
| UINTN DeviceTypeCount; | |
| UINTN DeviceTypeIndex; | |
| HddCount = 0; | |
| BbsCount = 0; | |
| LocalHddInfo = NULL; | |
| LocalBbsTable = NULL; | |
| DevType = BBS_UNKNOWN; | |
| Status = gBS->LocateProtocol (&gEfiLegacyBiosProtocolGuid, NULL, (VOID **) &LegacyBios); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| LegacyBios->GetBbsInfo ( | |
| LegacyBios, | |
| &HddCount, | |
| &LocalHddInfo, | |
| &BbsCount, | |
| &LocalBbsTable | |
| ); | |
| // | |
| // First, set all the present devices' boot priority to BBS_UNPRIORITIZED_ENTRY | |
| // We will set them according to the settings setup by user | |
| // | |
| for (Index = 0; Index < BbsCount; Index++) { | |
| if (!((BBS_IGNORE_ENTRY == LocalBbsTable[Index].BootPriority) || | |
| (BBS_DO_NOT_BOOT_FROM == LocalBbsTable[Index].BootPriority) || | |
| (BBS_LOWEST_PRIORITY == LocalBbsTable[Index].BootPriority))) { | |
| LocalBbsTable[Index].BootPriority = BBS_UNPRIORITIZED_ENTRY; | |
| } | |
| } | |
| // | |
| // boot priority always starts at 0 | |
| // | |
| Priority = 0; | |
| if (Entry->LoadOptionsSize == sizeof (BBS_TABLE) + sizeof (UINT16)) { | |
| // | |
| // If Entry stands for a legacy boot option, we prioritize the devices with the same type first. | |
| // | |
| DevType = ((BBS_TABLE *) Entry->LoadOptions)->DeviceType; | |
| BbsIndex = *(UINT16 *) ((BBS_TABLE *) Entry->LoadOptions + 1); | |
| Status = BdsSetBootPriority4SameTypeDev ( | |
| DevType, | |
| BbsIndex, | |
| LocalBbsTable, | |
| &Priority | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| } | |
| // | |
| // we have to set the boot priority for other BBS entries with different device types | |
| // | |
| BootOrder = BdsLibGetVariableAndSize ( | |
| L"BootOrder", | |
| &gEfiGlobalVariableGuid, | |
| &BootOrderSize | |
| ); | |
| DeviceType = AllocatePool (BootOrderSize + sizeof (UINT16)); | |
| ASSERT (DeviceType != NULL); | |
| DeviceType[0] = DevType; | |
| DeviceTypeCount = 1; | |
| for (Index = 0; ((BootOrder != NULL) && (Index < BootOrderSize / sizeof (UINT16))); Index++) { | |
| UnicodeSPrint (BootOption, sizeof (BootOption), L"Boot%04x", BootOrder[Index]); | |
| BootOptionVar = BdsLibGetVariableAndSize ( | |
| BootOption, | |
| &gEfiGlobalVariableGuid, | |
| &BootOptionSize | |
| ); | |
| if (NULL == BootOptionVar) { | |
| continue; | |
| } | |
| Ptr = BootOptionVar; | |
| Ptr += sizeof (UINT32); | |
| DevPathLen = *(UINT16 *) Ptr; | |
| Ptr += sizeof (UINT16); | |
| Ptr += StrSize ((UINT16 *) Ptr); | |
| DevPath = (EFI_DEVICE_PATH_PROTOCOL *) Ptr; | |
| if (BBS_DEVICE_PATH != DevPath->Type || BBS_BBS_DP != DevPath->SubType) { | |
| FreePool (BootOptionVar); | |
| continue; | |
| } | |
| Ptr += DevPathLen; | |
| DevType = ((BBS_TABLE *) Ptr)->DeviceType; | |
| for (DeviceTypeIndex = 0; DeviceTypeIndex < DeviceTypeCount; DeviceTypeIndex++) { | |
| if (DeviceType[DeviceTypeIndex] == DevType) { | |
| break; | |
| } | |
| } | |
| if (DeviceTypeIndex < DeviceTypeCount) { | |
| // | |
| // We don't want to process twice for a device type | |
| // | |
| FreePool (BootOptionVar); | |
| continue; | |
| } | |
| DeviceType[DeviceTypeCount] = DevType; | |
| DeviceTypeCount++; | |
| Status = BdsSetBootPriority4SameTypeDev ( | |
| DevType, | |
| (UINTN) -1, | |
| LocalBbsTable, | |
| &Priority | |
| ); | |
| FreePool (BootOptionVar); | |
| if (EFI_ERROR (Status)) { | |
| break; | |
| } | |
| } | |
| FreePool (DeviceType); | |
| if (BootOrder != NULL) { | |
| FreePool (BootOrder); | |
| } | |
| DEBUG_CODE_BEGIN(); | |
| PrintBbsTable (LocalBbsTable, BbsCount); | |
| DEBUG_CODE_END(); | |
| return Status; | |
| } | |
| /** | |
| Boot the legacy system with the boot option | |
| @param Option The legacy boot option which have BBS device path | |
| @retval EFI_UNSUPPORTED There is no legacybios protocol, do not support | |
| legacy boot. | |
| @retval EFI_STATUS Return the status of LegacyBios->LegacyBoot (). | |
| **/ | |
| EFI_STATUS | |
| BdsLibDoLegacyBoot ( | |
| IN BDS_COMMON_OPTION *Option | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_LEGACY_BIOS_PROTOCOL *LegacyBios; | |
| EFI_EVENT LegacyBootEvent; | |
| Status = gBS->LocateProtocol (&gEfiLegacyBiosProtocolGuid, NULL, (VOID **) &LegacyBios); | |
| if (EFI_ERROR (Status)) { | |
| // | |
| // If no LegacyBios protocol we do not support legacy boot | |
| // | |
| return EFI_UNSUPPORTED; | |
| } | |
| // | |
| // Notes: if we separate the int 19, then we don't need to refresh BBS | |
| // | |
| BdsRefreshBbsTableForBoot (Option); | |
| // | |
| // Write boot to OS performance data for legacy boot. | |
| // | |
| PERF_CODE ( | |
| // | |
| // Create an event to be signalled when Legacy Boot occurs to write performance data. | |
| // | |
| Status = EfiCreateEventLegacyBootEx( | |
| TPL_NOTIFY, | |
| WriteBootToOsPerformanceData, | |
| NULL, | |
| &LegacyBootEvent | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| ); | |
| DEBUG ((DEBUG_INFO | DEBUG_LOAD, "Legacy Boot: %S\n", Option->Description)); | |
| return LegacyBios->LegacyBoot ( | |
| LegacyBios, | |
| (BBS_BBS_DEVICE_PATH *) Option->DevicePath, | |
| Option->LoadOptionsSize, | |
| Option->LoadOptions | |
| ); | |
| } | |
| /** | |
| Internal function to check if the input boot option is a valid EFI NV Boot####. | |
| @param OptionToCheck Boot option to be checked. | |
| @retval TRUE This boot option matches a valid EFI NV Boot####. | |
| @retval FALSE If not. | |
| **/ | |
| BOOLEAN | |
| IsBootOptionValidNVVarialbe ( | |
| IN BDS_COMMON_OPTION *OptionToCheck | |
| ) | |
| { | |
| LIST_ENTRY TempList; | |
| BDS_COMMON_OPTION *BootOption; | |
| BOOLEAN Valid; | |
| CHAR16 OptionName[20]; | |
| Valid = FALSE; | |
| InitializeListHead (&TempList); | |
| UnicodeSPrint (OptionName, sizeof (OptionName), L"Boot%04x", OptionToCheck->BootCurrent); | |
| BootOption = BdsLibVariableToOption (&TempList, OptionName); | |
| if (BootOption == NULL) { | |
| return FALSE; | |
| } | |
| // | |
| // If the Boot Option Number and Device Path matches, OptionToCheck matches a | |
| // valid EFI NV Boot####. | |
| // | |
| if ((OptionToCheck->BootCurrent == BootOption->BootCurrent) && | |
| (CompareMem (OptionToCheck->DevicePath, BootOption->DevicePath, GetDevicePathSize (OptionToCheck->DevicePath)) == 0)) | |
| { | |
| Valid = TRUE; | |
| } | |
| FreePool (BootOption); | |
| return Valid; | |
| } | |
| /** | |
| Check whether a USB device match the specified USB Class device path. This | |
| function follows "Load Option Processing" behavior in UEFI specification. | |
| @param UsbIo USB I/O protocol associated with the USB device. | |
| @param UsbClass The USB Class device path to match. | |
| @retval TRUE The USB device match the USB Class device path. | |
| @retval FALSE The USB device does not match the USB Class device path. | |
| **/ | |
| BOOLEAN | |
| BdsMatchUsbClass ( | |
| IN EFI_USB_IO_PROTOCOL *UsbIo, | |
| IN USB_CLASS_DEVICE_PATH *UsbClass | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_USB_DEVICE_DESCRIPTOR DevDesc; | |
| EFI_USB_INTERFACE_DESCRIPTOR IfDesc; | |
| UINT8 DeviceClass; | |
| UINT8 DeviceSubClass; | |
| UINT8 DeviceProtocol; | |
| if ((DevicePathType (UsbClass) != MESSAGING_DEVICE_PATH) || | |
| (DevicePathSubType (UsbClass) != MSG_USB_CLASS_DP)){ | |
| return FALSE; | |
| } | |
| // | |
| // Check Vendor Id and Product Id. | |
| // | |
| Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DevDesc); | |
| if (EFI_ERROR (Status)) { | |
| return FALSE; | |
| } | |
| if ((UsbClass->VendorId != 0xffff) && | |
| (UsbClass->VendorId != DevDesc.IdVendor)) { | |
| return FALSE; | |
| } | |
| if ((UsbClass->ProductId != 0xffff) && | |
| (UsbClass->ProductId != DevDesc.IdProduct)) { | |
| return FALSE; | |
| } | |
| DeviceClass = DevDesc.DeviceClass; | |
| DeviceSubClass = DevDesc.DeviceSubClass; | |
| DeviceProtocol = DevDesc.DeviceProtocol; | |
| if (DeviceClass == 0) { | |
| // | |
| // If Class in Device Descriptor is set to 0, use the Class, SubClass and | |
| // Protocol in Interface Descriptor instead. | |
| // | |
| Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &IfDesc); | |
| if (EFI_ERROR (Status)) { | |
| return FALSE; | |
| } | |
| DeviceClass = IfDesc.InterfaceClass; | |
| DeviceSubClass = IfDesc.InterfaceSubClass; | |
| DeviceProtocol = IfDesc.InterfaceProtocol; | |
| } | |
| // | |
| // Check Class, SubClass and Protocol. | |
| // | |
| if ((UsbClass->DeviceClass != 0xff) && | |
| (UsbClass->DeviceClass != DeviceClass)) { | |
| return FALSE; | |
| } | |
| if ((UsbClass->DeviceSubClass != 0xff) && | |
| (UsbClass->DeviceSubClass != DeviceSubClass)) { | |
| return FALSE; | |
| } | |
| if ((UsbClass->DeviceProtocol != 0xff) && | |
| (UsbClass->DeviceProtocol != DeviceProtocol)) { | |
| return FALSE; | |
| } | |
| return TRUE; | |
| } | |
| /** | |
| Check whether a USB device match the specified USB WWID device path. This | |
| function follows "Load Option Processing" behavior in UEFI specification. | |
| @param UsbIo USB I/O protocol associated with the USB device. | |
| @param UsbWwid The USB WWID device path to match. | |
| @retval TRUE The USB device match the USB WWID device path. | |
| @retval FALSE The USB device does not match the USB WWID device path. | |
| **/ | |
| BOOLEAN | |
| BdsMatchUsbWwid ( | |
| IN EFI_USB_IO_PROTOCOL *UsbIo, | |
| IN USB_WWID_DEVICE_PATH *UsbWwid | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_USB_DEVICE_DESCRIPTOR DevDesc; | |
| EFI_USB_INTERFACE_DESCRIPTOR IfDesc; | |
| UINT16 *LangIdTable; | |
| UINT16 TableSize; | |
| UINT16 Index; | |
| CHAR16 *CompareStr; | |
| UINTN CompareLen; | |
| CHAR16 *SerialNumberStr; | |
| UINTN Length; | |
| if ((DevicePathType (UsbWwid) != MESSAGING_DEVICE_PATH) || | |
| (DevicePathSubType (UsbWwid) != MSG_USB_WWID_DP )){ | |
| return FALSE; | |
| } | |
| // | |
| // Check Vendor Id and Product Id. | |
| // | |
| Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DevDesc); | |
| if (EFI_ERROR (Status)) { | |
| return FALSE; | |
| } | |
| if ((DevDesc.IdVendor != UsbWwid->VendorId) || | |
| (DevDesc.IdProduct != UsbWwid->ProductId)) { | |
| return FALSE; | |
| } | |
| // | |
| // Check Interface Number. | |
| // | |
| Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &IfDesc); | |
| if (EFI_ERROR (Status)) { | |
| return FALSE; | |
| } | |
| if (IfDesc.InterfaceNumber != UsbWwid->InterfaceNumber) { | |
| return FALSE; | |
| } | |
| // | |
| // Check Serial Number. | |
| // | |
| if (DevDesc.StrSerialNumber == 0) { | |
| return FALSE; | |
| } | |
| // | |
| // Get all supported languages. | |
| // | |
| TableSize = 0; | |
| LangIdTable = NULL; | |
| Status = UsbIo->UsbGetSupportedLanguages (UsbIo, &LangIdTable, &TableSize); | |
| if (EFI_ERROR (Status) || (TableSize == 0) || (LangIdTable == NULL)) { | |
| return FALSE; | |
| } | |
| // | |
| // Serial number in USB WWID device path is the last 64-or-less UTF-16 characters. | |
| // | |
| CompareStr = (CHAR16 *) (UINTN) (UsbWwid + 1); | |
| CompareLen = (DevicePathNodeLength (UsbWwid) - sizeof (USB_WWID_DEVICE_PATH)) / sizeof (CHAR16); | |
| if (CompareStr[CompareLen - 1] == L'\0') { | |
| CompareLen--; | |
| } | |
| // | |
| // Compare serial number in each supported language. | |
| // | |
| for (Index = 0; Index < TableSize / sizeof (UINT16); Index++) { | |
| SerialNumberStr = NULL; | |
| Status = UsbIo->UsbGetStringDescriptor ( | |
| UsbIo, | |
| LangIdTable[Index], | |
| DevDesc.StrSerialNumber, | |
| &SerialNumberStr | |
| ); | |
| if (EFI_ERROR (Status) || (SerialNumberStr == NULL)) { | |
| continue; | |
| } | |
| Length = StrLen (SerialNumberStr); | |
| if ((Length >= CompareLen) && | |
| (CompareMem (SerialNumberStr + Length - CompareLen, CompareStr, CompareLen * sizeof (CHAR16)) == 0)) { | |
| FreePool (SerialNumberStr); | |
| return TRUE; | |
| } | |
| FreePool (SerialNumberStr); | |
| } | |
| return FALSE; | |
| } | |
| /** | |
| Find a USB device path which match the specified short-form device path start | |
| with USB Class or USB WWID device path and load the boot file then return the | |
| image handle. If ParentDevicePath is NULL, this function will search in all USB | |
| devices of the platform. If ParentDevicePath is not NULL,this function will only | |
| search in its child devices. | |
| @param ParentDevicePath The device path of the parent. | |
| @param ShortFormDevicePath The USB Class or USB WWID device path to match. | |
| @return The image Handle if find load file from specified short-form device path | |
| or NULL if not found. | |
| **/ | |
| EFI_HANDLE * | |
| BdsFindUsbDevice ( | |
| IN EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath, | |
| IN EFI_DEVICE_PATH_PROTOCOL *ShortFormDevicePath | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINTN UsbIoHandleCount; | |
| EFI_HANDLE *UsbIoHandleBuffer; | |
| EFI_DEVICE_PATH_PROTOCOL *UsbIoDevicePath; | |
| EFI_USB_IO_PROTOCOL *UsbIo; | |
| UINTN Index; | |
| UINTN ParentSize; | |
| UINTN Size; | |
| EFI_HANDLE ImageHandle; | |
| EFI_HANDLE Handle; | |
| EFI_DEVICE_PATH_PROTOCOL *FullDevicePath; | |
| EFI_DEVICE_PATH_PROTOCOL *NextDevicePath; | |
| FullDevicePath = NULL; | |
| ImageHandle = NULL; | |
| // | |
| // Get all UsbIo Handles. | |
| // | |
| UsbIoHandleCount = 0; | |
| UsbIoHandleBuffer = NULL; | |
| Status = gBS->LocateHandleBuffer ( | |
| ByProtocol, | |
| &gEfiUsbIoProtocolGuid, | |
| NULL, | |
| &UsbIoHandleCount, | |
| &UsbIoHandleBuffer | |
| ); | |
| if (EFI_ERROR (Status) || (UsbIoHandleCount == 0) || (UsbIoHandleBuffer == NULL)) { | |
| return NULL; | |
| } | |
| ParentSize = (ParentDevicePath == NULL) ? 0 : GetDevicePathSize (ParentDevicePath); | |
| for (Index = 0; Index < UsbIoHandleCount; Index++) { | |
| // | |
| // Get the Usb IO interface. | |
| // | |
| Status = gBS->HandleProtocol( | |
| UsbIoHandleBuffer[Index], | |
| &gEfiUsbIoProtocolGuid, | |
| (VOID **) &UsbIo | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| continue; | |
| } | |
| UsbIoDevicePath = DevicePathFromHandle (UsbIoHandleBuffer[Index]); | |
| if (UsbIoDevicePath == NULL) { | |
| continue; | |
| } | |
| if (ParentDevicePath != NULL) { | |
| // | |
| // Compare starting part of UsbIoHandle's device path with ParentDevicePath. | |
| // | |
| Size = GetDevicePathSize (UsbIoDevicePath); | |
| if ((Size < ParentSize) || | |
| (CompareMem (UsbIoDevicePath, ParentDevicePath, ParentSize - END_DEVICE_PATH_LENGTH) != 0)) { | |
| continue; | |
| } | |
| } | |
| if (BdsMatchUsbClass (UsbIo, (USB_CLASS_DEVICE_PATH *) ShortFormDevicePath) || | |
| BdsMatchUsbWwid (UsbIo, (USB_WWID_DEVICE_PATH *) ShortFormDevicePath)) { | |
| // | |
| // Try to find if there is the boot file in this DevicePath | |
| // | |
| NextDevicePath = NextDevicePathNode (ShortFormDevicePath); | |
| if (!IsDevicePathEnd (NextDevicePath)) { | |
| FullDevicePath = AppendDevicePath (UsbIoDevicePath, NextDevicePath); | |
| // | |
| // Connect the full device path, so that Simple File System protocol | |
| // could be installed for this USB device. | |
| // | |
| BdsLibConnectDevicePath (FullDevicePath); | |
| REPORT_STATUS_CODE (EFI_PROGRESS_CODE, PcdGet32 (PcdProgressCodeOsLoaderLoad)); | |
| Status = gBS->LoadImage ( | |
| TRUE, | |
| gImageHandle, | |
| FullDevicePath, | |
| NULL, | |
| 0, | |
| &ImageHandle | |
| ); | |
| FreePool (FullDevicePath); | |
| } else { | |
| FullDevicePath = UsbIoDevicePath; | |
| Status = EFI_NOT_FOUND; | |
| } | |
| // | |
| // If we didn't find an image directly, we need to try as if it is a removable device boot option | |
| // and load the image according to the default boot behavior for removable device. | |
| // | |
| if (EFI_ERROR (Status)) { | |
| // | |
| // check if there is a bootable removable media could be found in this device path , | |
| // and get the bootable media handle | |
| // | |
| Handle = BdsLibGetBootableHandle(UsbIoDevicePath); | |
| if (Handle == NULL) { | |
| continue; | |
| } | |
| // | |
| // Load the default boot file \EFI\BOOT\boot{machinename}.EFI from removable Media | |
| // machinename is ia32, ia64, x64, ... | |
| // | |
| FullDevicePath = FileDevicePath (Handle, EFI_REMOVABLE_MEDIA_FILE_NAME); | |
| if (FullDevicePath != NULL) { | |
| REPORT_STATUS_CODE (EFI_PROGRESS_CODE, PcdGet32 (PcdProgressCodeOsLoaderLoad)); | |
| Status = gBS->LoadImage ( | |
| TRUE, | |
| gImageHandle, | |
| FullDevicePath, | |
| NULL, | |
| 0, | |
| &ImageHandle | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| // | |
| // The DevicePath failed, and it's not a valid | |
| // removable media device. | |
| // | |
| continue; | |
| } | |
| } else { | |
| continue; | |
| } | |
| } | |
| break; | |
| } | |
| } | |
| FreePool (UsbIoHandleBuffer); | |
| return ImageHandle; | |
| } | |
| /** | |
| Expand USB Class or USB WWID device path node to be full device path of a USB | |
| device in platform then load the boot file on this full device path and return the | |
| image handle. | |
| This function support following 4 cases: | |
| 1) Boot Option device path starts with a USB Class or USB WWID device path, | |
| and there is no Media FilePath device path in the end. | |
| In this case, it will follow Removable Media Boot Behavior. | |
| 2) Boot Option device path starts with a USB Class or USB WWID device path, | |
| and ended with Media FilePath device path. | |
| 3) Boot Option device path starts with a full device path to a USB Host Controller, | |
| contains a USB Class or USB WWID device path node, while not ended with Media | |
| FilePath device path. In this case, it will follow Removable Media Boot Behavior. | |
| 4) Boot Option device path starts with a full device path to a USB Host Controller, | |
| contains a USB Class or USB WWID device path node, and ended with Media | |
| FilePath device path. | |
| @param DevicePath The Boot Option device path. | |
| @return The image handle of boot file, or NULL if there is no boot file found in | |
| the specified USB Class or USB WWID device path. | |
| **/ | |
| EFI_HANDLE * | |
| BdsExpandUsbShortFormDevicePath ( | |
| IN EFI_DEVICE_PATH_PROTOCOL *DevicePath | |
| ) | |
| { | |
| EFI_HANDLE *ImageHandle; | |
| EFI_DEVICE_PATH_PROTOCOL *TempDevicePath; | |
| EFI_DEVICE_PATH_PROTOCOL *ShortFormDevicePath; | |
| // | |
| // Search for USB Class or USB WWID device path node. | |
| // | |
| ShortFormDevicePath = NULL; | |
| ImageHandle = NULL; | |
| TempDevicePath = DevicePath; | |
| while (!IsDevicePathEnd (TempDevicePath)) { | |
| if ((DevicePathType (TempDevicePath) == MESSAGING_DEVICE_PATH) && | |
| ((DevicePathSubType (TempDevicePath) == MSG_USB_CLASS_DP) || | |
| (DevicePathSubType (TempDevicePath) == MSG_USB_WWID_DP))) { | |
| ShortFormDevicePath = TempDevicePath; | |
| break; | |
| } | |
| TempDevicePath = NextDevicePathNode (TempDevicePath); | |
| } | |
| if (ShortFormDevicePath == NULL) { | |
| // | |
| // No USB Class or USB WWID device path node found, do nothing. | |
| // | |
| return NULL; | |
| } | |
| if (ShortFormDevicePath == DevicePath) { | |
| // | |
| // Boot Option device path starts with USB Class or USB WWID device path. | |
| // | |
| ImageHandle = BdsFindUsbDevice (NULL, ShortFormDevicePath); | |
| if (ImageHandle == NULL) { | |
| // | |
| // Failed to find a match in existing devices, connect the short form USB | |
| // device path and try again. | |
| // | |
| BdsLibConnectUsbDevByShortFormDP (0xff, ShortFormDevicePath); | |
| ImageHandle = BdsFindUsbDevice (NULL, ShortFormDevicePath); | |
| } | |
| } else { | |
| // | |
| // Boot Option device path contains USB Class or USB WWID device path node. | |
| // | |
| // | |
| // Prepare the parent device path for search. | |
| // | |
| TempDevicePath = DuplicateDevicePath (DevicePath); | |
| ASSERT (TempDevicePath != NULL); | |
| SetDevicePathEndNode (((UINT8 *) TempDevicePath) + ((UINTN) ShortFormDevicePath - (UINTN) DevicePath)); | |
| // | |
| // The USB Host Controller device path is already in Boot Option device path | |
| // and USB Bus driver already support RemainingDevicePath starts with USB | |
| // Class or USB WWID device path, so just search in existing USB devices and | |
| // doesn't perform ConnectController here. | |
| // | |
| ImageHandle = BdsFindUsbDevice (TempDevicePath, ShortFormDevicePath); | |
| FreePool (TempDevicePath); | |
| } | |
| return ImageHandle; | |
| } | |
| /** | |
| Process the boot option follow the UEFI specification and | |
| special treat the legacy boot option with BBS_DEVICE_PATH. | |
| @param Option The boot option need to be processed | |
| @param DevicePath The device path which describe where to load the | |
| boot image or the legacy BBS device path to boot | |
| the legacy OS | |
| @param ExitDataSize The size of exit data. | |
| @param ExitData Data returned when Boot image failed. | |
| @retval EFI_SUCCESS Boot from the input boot option successfully. | |
| @retval EFI_NOT_FOUND If the Device Path is not found in the system | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| BdsLibBootViaBootOption ( | |
| IN BDS_COMMON_OPTION *Option, | |
| IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, | |
| OUT UINTN *ExitDataSize, | |
| OUT CHAR16 **ExitData OPTIONAL | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_STATUS StatusLogo; | |
| EFI_HANDLE Handle; | |
| EFI_HANDLE ImageHandle; | |
| EFI_DEVICE_PATH_PROTOCOL *FilePath; | |
| EFI_LOADED_IMAGE_PROTOCOL *ImageInfo; | |
| EFI_DEVICE_PATH_PROTOCOL *WorkingDevicePath; | |
| EFI_ACPI_S3_SAVE_PROTOCOL *AcpiS3Save; | |
| LIST_ENTRY TempBootLists; | |
| EFI_BOOT_LOGO_PROTOCOL *BootLogo; | |
| *ExitDataSize = 0; | |
| *ExitData = NULL; | |
| // | |
| // Notes: this code can be remove after the s3 script table | |
| // hook on the event EVT_SIGNAL_READY_TO_BOOT or | |
| // EVT_SIGNAL_LEGACY_BOOT | |
| // | |
| Status = gBS->LocateProtocol (&gEfiAcpiS3SaveProtocolGuid, NULL, (VOID **) &AcpiS3Save); | |
| if (!EFI_ERROR (Status)) { | |
| AcpiS3Save->S3Save (AcpiS3Save, NULL); | |
| } | |
| // | |
| // If it's Device Path that starts with a hard drive path, append it with the front part to compose a | |
| // full device path | |
| // | |
| WorkingDevicePath = NULL; | |
| if ((DevicePathType (DevicePath) == MEDIA_DEVICE_PATH) && | |
| (DevicePathSubType (DevicePath) == MEDIA_HARDDRIVE_DP)) { | |
| WorkingDevicePath = BdsExpandPartitionPartialDevicePathToFull ( | |
| (HARDDRIVE_DEVICE_PATH *)DevicePath | |
| ); | |
| if (WorkingDevicePath != NULL) { | |
| DevicePath = WorkingDevicePath; | |
| } | |
| } | |
| // | |
| // Set Boot Current | |
| // | |
| if (IsBootOptionValidNVVarialbe (Option)) { | |
| // | |
| // For a temporary boot (i.e. a boot by selected a EFI Shell using "Boot From File"), Boot Current is actually not valid. | |
| // In this case, "BootCurrent" is not created. | |
| // Only create the BootCurrent variable when it points to a valid Boot#### variable. | |
| // | |
| SetVariableAndReportStatusCodeOnError ( | |
| L"BootCurrent", | |
| &gEfiGlobalVariableGuid, | |
| EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, | |
| sizeof (UINT16), | |
| &Option->BootCurrent | |
| ); | |
| } | |
| // | |
| // Report Status Code to indicate ReadyToBoot event will be signalled | |
| // | |
| REPORT_STATUS_CODE (EFI_PROGRESS_CODE, (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_PC_READY_TO_BOOT_EVENT)); | |
| // | |
| // Signal the EVT_SIGNAL_READY_TO_BOOT event | |
| // | |
| EfiSignalEventReadyToBoot(); | |
| // | |
| // Expand USB Class or USB WWID device path node to be full device path of a USB | |
| // device in platform then load the boot file on this full device path and get the | |
| // image handle. | |
| // | |
| ImageHandle = BdsExpandUsbShortFormDevicePath (DevicePath); | |
| // | |
| // Adjust the different type memory page number just before booting | |
| // and save the updated info into the variable for next boot to use | |
| // | |
| BdsSetMemoryTypeInformationVariable (); | |
| // | |
| // By expanding the USB Class or WWID device path, the ImageHandle has returnned. | |
| // Here get the ImageHandle for the non USB class or WWID device path. | |
| // | |
| if (ImageHandle == NULL) { | |
| ASSERT (Option->DevicePath != NULL); | |
| if ((DevicePathType (Option->DevicePath) == BBS_DEVICE_PATH) && | |
| (DevicePathSubType (Option->DevicePath) == BBS_BBS_DP) | |
| ) { | |
| // | |
| // Check to see if we should legacy BOOT. If yes then do the legacy boot | |
| // | |
| return BdsLibDoLegacyBoot (Option); | |
| } | |
| // | |
| // If the boot option point to Internal FV shell, make sure it is valid | |
| // | |
| Status = BdsLibUpdateFvFileDevicePath (&DevicePath, PcdGetPtr(PcdShellFile)); | |
| if (!EFI_ERROR(Status)) { | |
| if (Option->DevicePath != NULL) { | |
| FreePool(Option->DevicePath); | |
| } | |
| Option->DevicePath = AllocateZeroPool (GetDevicePathSize (DevicePath)); | |
| ASSERT(Option->DevicePath != NULL); | |
| CopyMem (Option->DevicePath, DevicePath, GetDevicePathSize (DevicePath)); | |
| // | |
| // Update the shell boot option | |
| // | |
| InitializeListHead (&TempBootLists); | |
| BdsLibRegisterNewOption (&TempBootLists, DevicePath, L"EFI Internal Shell", L"BootOrder"); | |
| // | |
| // free the temporary device path created by BdsLibUpdateFvFileDevicePath() | |
| // | |
| FreePool (DevicePath); | |
| DevicePath = Option->DevicePath; | |
| } | |
| DEBUG_CODE_BEGIN(); | |
| if (Option->Description == NULL) { | |
| DEBUG ((DEBUG_INFO | DEBUG_LOAD, "Booting from unknown device path\n")); | |
| } else { | |
| DEBUG ((DEBUG_INFO | DEBUG_LOAD, "Booting %S\n", Option->Description)); | |
| } | |
| DEBUG_CODE_END(); | |
| // | |
| // Report status code for OS Loader LoadImage. | |
| // | |
| REPORT_STATUS_CODE (EFI_PROGRESS_CODE, PcdGet32 (PcdProgressCodeOsLoaderLoad)); | |
| Status = gBS->LoadImage ( | |
| TRUE, | |
| gImageHandle, | |
| DevicePath, | |
| NULL, | |
| 0, | |
| &ImageHandle | |
| ); | |
| // | |
| // If we didn't find an image directly, we need to try as if it is a removable device boot option | |
| // and load the image according to the default boot behavior for removable device. | |
| // | |
| if (EFI_ERROR (Status)) { | |
| // | |
| // check if there is a bootable removable media could be found in this device path , | |
| // and get the bootable media handle | |
| // | |
| Handle = BdsLibGetBootableHandle(DevicePath); | |
| if (Handle != NULL) { | |
| // | |
| // Load the default boot file \EFI\BOOT\boot{machinename}.EFI from removable Media | |
| // machinename is ia32, ia64, x64, ... | |
| // | |
| FilePath = FileDevicePath (Handle, EFI_REMOVABLE_MEDIA_FILE_NAME); | |
| if (FilePath != NULL) { | |
| REPORT_STATUS_CODE (EFI_PROGRESS_CODE, PcdGet32 (PcdProgressCodeOsLoaderLoad)); | |
| Status = gBS->LoadImage ( | |
| TRUE, | |
| gImageHandle, | |
| FilePath, | |
| NULL, | |
| 0, | |
| &ImageHandle | |
| ); | |
| } | |
| } | |
| } | |
| } | |
| // | |
| // Provide the image with it's load options | |
| // | |
| if ((ImageHandle == NULL) || (EFI_ERROR(Status))) { | |
| // | |
| // Report Status Code to indicate that the failure to load boot option | |
| // | |
| REPORT_STATUS_CODE ( | |
| EFI_ERROR_CODE | EFI_ERROR_MINOR, | |
| (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_EC_BOOT_OPTION_LOAD_ERROR) | |
| ); | |
| goto Done; | |
| } | |
| Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **) &ImageInfo); | |
| ASSERT_EFI_ERROR (Status); | |
| if (Option->LoadOptionsSize != 0) { | |
| ImageInfo->LoadOptionsSize = Option->LoadOptionsSize; | |
| ImageInfo->LoadOptions = Option->LoadOptions; | |
| } | |
| // | |
| // Clean to NULL because the image is loaded directly from the firmwares boot manager. | |
| // | |
| ImageInfo->ParentHandle = NULL; | |
| // | |
| // Before calling the image, enable the Watchdog Timer for | |
| // the 5 Minute period | |
| // | |
| gBS->SetWatchdogTimer (5 * 60, 0x0000, 0x00, NULL); | |
| // | |
| // Write boot to OS performance data for UEFI boot | |
| // | |
| PERF_CODE ( | |
| WriteBootToOsPerformanceData (NULL, NULL); | |
| ); | |
| // | |
| // Report status code for OS Loader StartImage. | |
| // | |
| REPORT_STATUS_CODE (EFI_PROGRESS_CODE, PcdGet32 (PcdProgressCodeOsLoaderStart)); | |
| Status = gBS->StartImage (ImageHandle, ExitDataSize, ExitData); | |
| DEBUG ((DEBUG_INFO | DEBUG_LOAD, "Image Return Status = %r\n", Status)); | |
| if (EFI_ERROR (Status)) { | |
| // | |
| // Report Status Code to indicate that boot failure | |
| // | |
| REPORT_STATUS_CODE ( | |
| EFI_ERROR_CODE | EFI_ERROR_MINOR, | |
| (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_EC_BOOT_OPTION_FAILED) | |
| ); | |
| } | |
| // | |
| // Clear the Watchdog Timer after the image returns | |
| // | |
| gBS->SetWatchdogTimer (0x0000, 0x0000, 0x0000, NULL); | |
| Done: | |
| // | |
| // Set Logo status invalid after trying one boot option | |
| // | |
| BootLogo = NULL; | |
| StatusLogo = gBS->LocateProtocol (&gEfiBootLogoProtocolGuid, NULL, (VOID **) &BootLogo); | |
| if (!EFI_ERROR (StatusLogo) && (BootLogo != NULL)) { | |
| BootLogo->SetBootLogo (BootLogo, NULL, 0, 0, 0, 0); | |
| } | |
| // | |
| // Clear Boot Current | |
| // Deleting variable with current implementation shouldn't fail. | |
| // | |
| gRT->SetVariable ( | |
| L"BootCurrent", | |
| &gEfiGlobalVariableGuid, | |
| EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, | |
| 0, | |
| NULL | |
| ); | |
| return Status; | |
| } | |
| /** | |
| Expand a device path that starts with a hard drive media device path node to be a | |
| full device path that includes the full hardware path to the device. We need | |
| to do this so it can be booted. As an optimization the front match (the part point | |
| to the partition node. E.g. ACPI() /PCI()/ATA()/Partition() ) is saved in a variable | |
| so a connect all is not required on every boot. All successful history device path | |
| which point to partition node (the front part) will be saved. | |
| @param HardDriveDevicePath EFI Device Path to boot, if it starts with a hard | |
| drive media device path. | |
| @return A Pointer to the full device path or NULL if a valid Hard Drive devic path | |
| cannot be found. | |
| **/ | |
| EFI_DEVICE_PATH_PROTOCOL * | |
| EFIAPI | |
| BdsExpandPartitionPartialDevicePathToFull ( | |
| IN HARDDRIVE_DEVICE_PATH *HardDriveDevicePath | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINTN BlockIoHandleCount; | |
| EFI_HANDLE *BlockIoBuffer; | |
| EFI_DEVICE_PATH_PROTOCOL *FullDevicePath; | |
| EFI_DEVICE_PATH_PROTOCOL *BlockIoDevicePath; | |
| EFI_DEVICE_PATH_PROTOCOL *DevicePath; | |
| UINTN Index; | |
| UINTN InstanceNum; | |
| EFI_DEVICE_PATH_PROTOCOL *CachedDevicePath; | |
| EFI_DEVICE_PATH_PROTOCOL *TempNewDevicePath; | |
| UINTN CachedDevicePathSize; | |
| BOOLEAN DeviceExist; | |
| BOOLEAN NeedAdjust; | |
| EFI_DEVICE_PATH_PROTOCOL *Instance; | |
| UINTN Size; | |
| FullDevicePath = NULL; | |
| // | |
| // Check if there is prestore HD_BOOT_DEVICE_PATH_VARIABLE_NAME variable. | |
| // If exist, search the front path which point to partition node in the variable instants. | |
| // If fail to find or HD_BOOT_DEVICE_PATH_VARIABLE_NAME not exist, reconnect all and search in all system | |
| // | |
| GetVariable2 ( | |
| HD_BOOT_DEVICE_PATH_VARIABLE_NAME, | |
| &gHdBootDevicePathVariablGuid, | |
| (VOID **) &CachedDevicePath, | |
| &CachedDevicePathSize | |
| ); | |
| // | |
| // Delete the invalid HD_BOOT_DEVICE_PATH_VARIABLE_NAME variable. | |
| // | |
| if ((CachedDevicePath != NULL) && !IsDevicePathValid (CachedDevicePath, CachedDevicePathSize)) { | |
| FreePool (CachedDevicePath); | |
| CachedDevicePath = NULL; | |
| Status = gRT->SetVariable ( | |
| HD_BOOT_DEVICE_PATH_VARIABLE_NAME, | |
| &gHdBootDevicePathVariablGuid, | |
| 0, | |
| 0, | |
| NULL | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| } | |
| if (CachedDevicePath != NULL) { | |
| TempNewDevicePath = CachedDevicePath; | |
| DeviceExist = FALSE; | |
| NeedAdjust = FALSE; | |
| do { | |
| // | |
| // Check every instance of the variable | |
| // First, check whether the instance contain the partition node, which is needed for distinguishing multi | |
| // partial partition boot option. Second, check whether the instance could be connected. | |
| // | |
| Instance = GetNextDevicePathInstance (&TempNewDevicePath, &Size); | |
| if (MatchPartitionDevicePathNode (Instance, HardDriveDevicePath)) { | |
| // | |
| // Connect the device path instance, the device path point to hard drive media device path node | |
| // e.g. ACPI() /PCI()/ATA()/Partition() | |
| // | |
| Status = BdsLibConnectDevicePath (Instance); | |
| if (!EFI_ERROR (Status)) { | |
| DeviceExist = TRUE; | |
| break; | |
| } | |
| } | |
| // | |
| // Come here means the first instance is not matched | |
| // | |
| NeedAdjust = TRUE; | |
| FreePool(Instance); | |
| } while (TempNewDevicePath != NULL); | |
| if (DeviceExist) { | |
| // | |
| // Find the matched device path. | |
| // Append the file path information from the boot option and return the fully expanded device path. | |
| // | |
| DevicePath = NextDevicePathNode ((EFI_DEVICE_PATH_PROTOCOL *) HardDriveDevicePath); | |
| FullDevicePath = AppendDevicePath (Instance, DevicePath); | |
| // | |
| // Adjust the HD_BOOT_DEVICE_PATH_VARIABLE_NAME instances sequence if the matched one is not first one. | |
| // | |
| if (NeedAdjust) { | |
| // | |
| // First delete the matched instance. | |
| // | |
| TempNewDevicePath = CachedDevicePath; | |
| CachedDevicePath = BdsLibDelPartMatchInstance (CachedDevicePath, Instance ); | |
| FreePool (TempNewDevicePath); | |
| // | |
| // Second, append the remaining path after the matched instance | |
| // | |
| TempNewDevicePath = CachedDevicePath; | |
| CachedDevicePath = AppendDevicePathInstance (Instance, CachedDevicePath ); | |
| FreePool (TempNewDevicePath); | |
| // | |
| // Save the matching Device Path so we don't need to do a connect all next time | |
| // Failure to set the variable only impacts the performance when next time expanding the short-form device path. | |
| // | |
| Status = gRT->SetVariable ( | |
| HD_BOOT_DEVICE_PATH_VARIABLE_NAME, | |
| &gHdBootDevicePathVariablGuid, | |
| EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE, | |
| GetDevicePathSize (CachedDevicePath), | |
| CachedDevicePath | |
| ); | |
| } | |
| FreePool (Instance); | |
| FreePool (CachedDevicePath); | |
| return FullDevicePath; | |
| } | |
| } | |
| // | |
| // If we get here we fail to find or HD_BOOT_DEVICE_PATH_VARIABLE_NAME not exist, and now we need | |
| // to search all devices in the system for a matched partition | |
| // | |
| BdsLibConnectAllDriversToAllControllers (); | |
| Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiBlockIoProtocolGuid, NULL, &BlockIoHandleCount, &BlockIoBuffer); | |
| if (EFI_ERROR (Status) || BlockIoHandleCount == 0 || BlockIoBuffer == NULL) { | |
| // | |
| // If there was an error or there are no device handles that support | |
| // the BLOCK_IO Protocol, then return. | |
| // | |
| return NULL; | |
| } | |
| // | |
| // Loop through all the device handles that support the BLOCK_IO Protocol | |
| // | |
| for (Index = 0; Index < BlockIoHandleCount; Index++) { | |
| Status = gBS->HandleProtocol (BlockIoBuffer[Index], &gEfiDevicePathProtocolGuid, (VOID *) &BlockIoDevicePath); | |
| if (EFI_ERROR (Status) || BlockIoDevicePath == NULL) { | |
| continue; | |
| } | |
| if (MatchPartitionDevicePathNode (BlockIoDevicePath, HardDriveDevicePath)) { | |
| // | |
| // Find the matched partition device path | |
| // | |
| DevicePath = NextDevicePathNode ((EFI_DEVICE_PATH_PROTOCOL *) HardDriveDevicePath); | |
| FullDevicePath = AppendDevicePath (BlockIoDevicePath, DevicePath); | |
| // | |
| // Save the matched partition device path in HD_BOOT_DEVICE_PATH_VARIABLE_NAME variable | |
| // | |
| if (CachedDevicePath != NULL) { | |
| // | |
| // Save the matched partition device path as first instance of HD_BOOT_DEVICE_PATH_VARIABLE_NAME variable | |
| // | |
| if (BdsLibMatchDevicePaths (CachedDevicePath, BlockIoDevicePath)) { | |
| TempNewDevicePath = CachedDevicePath; | |
| CachedDevicePath = BdsLibDelPartMatchInstance (CachedDevicePath, BlockIoDevicePath); | |
| FreePool(TempNewDevicePath); | |
| } | |
| if (CachedDevicePath != NULL) { | |
| TempNewDevicePath = CachedDevicePath; | |
| CachedDevicePath = AppendDevicePathInstance (BlockIoDevicePath, CachedDevicePath); | |
| FreePool(TempNewDevicePath); | |
| } else { | |
| CachedDevicePath = DuplicateDevicePath (BlockIoDevicePath); | |
| } | |
| // | |
| // Here limit the device path instance number to 12, which is max number for a system support 3 IDE controller | |
| // If the user try to boot many OS in different HDs or partitions, in theory, | |
| // the HD_BOOT_DEVICE_PATH_VARIABLE_NAME variable maybe become larger and larger. | |
| // | |
| InstanceNum = 0; | |
| ASSERT (CachedDevicePath != NULL); | |
| TempNewDevicePath = CachedDevicePath; | |
| while (!IsDevicePathEnd (TempNewDevicePath)) { | |
| TempNewDevicePath = NextDevicePathNode (TempNewDevicePath); | |
| // | |
| // Parse one instance | |
| // | |
| while (!IsDevicePathEndType (TempNewDevicePath)) { | |
| TempNewDevicePath = NextDevicePathNode (TempNewDevicePath); | |
| } | |
| InstanceNum++; | |
| // | |
| // If the CachedDevicePath variable contain too much instance, only remain 12 instances. | |
| // | |
| if (InstanceNum >= 12) { | |
| SetDevicePathEndNode (TempNewDevicePath); | |
| break; | |
| } | |
| } | |
| } else { | |
| CachedDevicePath = DuplicateDevicePath (BlockIoDevicePath); | |
| } | |
| // | |
| // Save the matching Device Path so we don't need to do a connect all next time | |
| // Failure to set the variable only impacts the performance when next time expanding the short-form device path. | |
| // | |
| Status = gRT->SetVariable ( | |
| HD_BOOT_DEVICE_PATH_VARIABLE_NAME, | |
| &gHdBootDevicePathVariablGuid, | |
| EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE, | |
| GetDevicePathSize (CachedDevicePath), | |
| CachedDevicePath | |
| ); | |
| break; | |
| } | |
| } | |
| if (CachedDevicePath != NULL) { | |
| FreePool (CachedDevicePath); | |
| } | |
| if (BlockIoBuffer != NULL) { | |
| FreePool (BlockIoBuffer); | |
| } | |
| return FullDevicePath; | |
| } | |
| /** | |
| Check whether there is a instance in BlockIoDevicePath, which contain multi device path | |
| instances, has the same partition node with HardDriveDevicePath device path | |
| @param BlockIoDevicePath Multi device path instances which need to check | |
| @param HardDriveDevicePath A device path which starts with a hard drive media | |
| device path. | |
| @retval TRUE There is a matched device path instance. | |
| @retval FALSE There is no matched device path instance. | |
| **/ | |
| BOOLEAN | |
| EFIAPI | |
| MatchPartitionDevicePathNode ( | |
| IN EFI_DEVICE_PATH_PROTOCOL *BlockIoDevicePath, | |
| IN HARDDRIVE_DEVICE_PATH *HardDriveDevicePath | |
| ) | |
| { | |
| HARDDRIVE_DEVICE_PATH *TmpHdPath; | |
| EFI_DEVICE_PATH_PROTOCOL *DevicePath; | |
| BOOLEAN Match; | |
| EFI_DEVICE_PATH_PROTOCOL *BlockIoHdDevicePathNode; | |
| if ((BlockIoDevicePath == NULL) || (HardDriveDevicePath == NULL)) { | |
| return FALSE; | |
| } | |
| // | |
| // Make PreviousDevicePath == the device path node before the end node | |
| // | |
| DevicePath = BlockIoDevicePath; | |
| BlockIoHdDevicePathNode = NULL; | |
| // | |
| // find the partition device path node | |
| // | |
| while (!IsDevicePathEnd (DevicePath)) { | |
| if ((DevicePathType (DevicePath) == MEDIA_DEVICE_PATH) && | |
| (DevicePathSubType (DevicePath) == MEDIA_HARDDRIVE_DP) | |
| ) { | |
| BlockIoHdDevicePathNode = DevicePath; | |
| break; | |
| } | |
| DevicePath = NextDevicePathNode (DevicePath); | |
| } | |
| if (BlockIoHdDevicePathNode == NULL) { | |
| return FALSE; | |
| } | |
| // | |
| // See if the harddrive device path in blockio matches the orig Hard Drive Node | |
| // | |
| TmpHdPath = (HARDDRIVE_DEVICE_PATH *) BlockIoHdDevicePathNode; | |
| Match = FALSE; | |
| // | |
| // Check for the match | |
| // | |
| if ((TmpHdPath->MBRType == HardDriveDevicePath->MBRType) && | |
| (TmpHdPath->SignatureType == HardDriveDevicePath->SignatureType)) { | |
| switch (TmpHdPath->SignatureType) { | |
| case SIGNATURE_TYPE_GUID: | |
| Match = CompareGuid ((EFI_GUID *)TmpHdPath->Signature, (EFI_GUID *)HardDriveDevicePath->Signature); | |
| break; | |
| case SIGNATURE_TYPE_MBR: | |
| Match = (BOOLEAN)(*((UINT32 *)(&(TmpHdPath->Signature[0]))) == ReadUnaligned32((UINT32 *)(&(HardDriveDevicePath->Signature[0])))); | |
| break; | |
| default: | |
| Match = FALSE; | |
| break; | |
| } | |
| } | |
| return Match; | |
| } | |
| /** | |
| Delete the boot option associated with the handle passed in. | |
| @param Handle The handle which present the device path to create | |
| boot option | |
| @retval EFI_SUCCESS Delete the boot option success | |
| @retval EFI_NOT_FOUND If the Device Path is not found in the system | |
| @retval EFI_OUT_OF_RESOURCES Lack of memory resource | |
| @retval Other Error return value from SetVariable() | |
| **/ | |
| EFI_STATUS | |
| BdsLibDeleteOptionFromHandle ( | |
| IN EFI_HANDLE Handle | |
| ) | |
| { | |
| UINT16 *BootOrder; | |
| UINT8 *BootOptionVar; | |
| UINTN BootOrderSize; | |
| UINTN BootOptionSize; | |
| EFI_STATUS Status; | |
| UINTN Index; | |
| UINT16 BootOption[BOOT_OPTION_MAX_CHAR]; | |
| UINTN DevicePathSize; | |
| UINTN OptionDevicePathSize; | |
| EFI_DEVICE_PATH_PROTOCOL *DevicePath; | |
| EFI_DEVICE_PATH_PROTOCOL *OptionDevicePath; | |
| UINT8 *TempPtr; | |
| Status = EFI_SUCCESS; | |
| BootOrder = NULL; | |
| BootOrderSize = 0; | |
| // | |
| // Check "BootOrder" variable, if no, means there is no any boot order. | |
| // | |
| BootOrder = BdsLibGetVariableAndSize ( | |
| L"BootOrder", | |
| &gEfiGlobalVariableGuid, | |
| &BootOrderSize | |
| ); | |
| if (BootOrder == NULL) { | |
| return EFI_NOT_FOUND; | |
| } | |
| // | |
| // Convert device handle to device path protocol instance | |
| // | |
| DevicePath = DevicePathFromHandle (Handle); | |
| if (DevicePath == NULL) { | |
| return EFI_NOT_FOUND; | |
| } | |
| DevicePathSize = GetDevicePathSize (DevicePath); | |
| // | |
| // Loop all boot order variable and find the matching device path | |
| // | |
| Index = 0; | |
| while (Index < BootOrderSize / sizeof (UINT16)) { | |
| UnicodeSPrint (BootOption, sizeof (BootOption), L"Boot%04x", BootOrder[Index]); | |
| BootOptionVar = BdsLibGetVariableAndSize ( | |
| BootOption, | |
| &gEfiGlobalVariableGuid, | |
| &BootOptionSize | |
| ); | |
| if (BootOptionVar == NULL) { | |
| FreePool (BootOrder); | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| if (!ValidateOption(BootOptionVar, BootOptionSize)) { | |
| BdsDeleteBootOption (BootOrder[Index], BootOrder, &BootOrderSize); | |
| FreePool (BootOptionVar); | |
| Index++; | |
| continue; | |
| } | |
| TempPtr = BootOptionVar; | |
| TempPtr += sizeof (UINT32) + sizeof (UINT16); | |
| TempPtr += StrSize ((CHAR16 *) TempPtr); | |
| OptionDevicePath = (EFI_DEVICE_PATH_PROTOCOL *) TempPtr; | |
| OptionDevicePathSize = GetDevicePathSize (OptionDevicePath); | |
| // | |
| // Check whether the device path match | |
| // | |
| if ((OptionDevicePathSize == DevicePathSize) && | |
| (CompareMem (DevicePath, OptionDevicePath, DevicePathSize) == 0)) { | |
| BdsDeleteBootOption (BootOrder[Index], BootOrder, &BootOrderSize); | |
| FreePool (BootOptionVar); | |
| break; | |
| } | |
| FreePool (BootOptionVar); | |
| Index++; | |
| } | |
| // | |
| // Adjust number of boot option for "BootOrder" variable. | |
| // | |
| Status = gRT->SetVariable ( | |
| L"BootOrder", | |
| &gEfiGlobalVariableGuid, | |
| EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE, | |
| BootOrderSize, | |
| BootOrder | |
| ); | |
| // | |
| // Shrinking variable with existing variable implementation shouldn't fail. | |
| // | |
| ASSERT_EFI_ERROR (Status); | |
| FreePool (BootOrder); | |
| return Status; | |
| } | |
| /** | |
| Delete all invalid EFI boot options. | |
| @retval EFI_SUCCESS Delete all invalid boot option success | |
| @retval EFI_NOT_FOUND Variable "BootOrder" is not found | |
| @retval EFI_OUT_OF_RESOURCES Lack of memory resource | |
| @retval Other Error return value from SetVariable() | |
| **/ | |
| EFI_STATUS | |
| BdsDeleteAllInvalidEfiBootOption ( | |
| VOID | |
| ) | |
| { | |
| UINT16 *BootOrder; | |
| UINT8 *BootOptionVar; | |
| UINTN BootOrderSize; | |
| UINTN BootOptionSize; | |
| EFI_STATUS Status; | |
| UINTN Index; | |
| UINTN Index2; | |
| UINT16 BootOption[BOOT_OPTION_MAX_CHAR]; | |
| EFI_DEVICE_PATH_PROTOCOL *OptionDevicePath; | |
| UINT8 *TempPtr; | |
| CHAR16 *Description; | |
| BOOLEAN Corrupted; | |
| Status = EFI_SUCCESS; | |
| BootOrder = NULL; | |
| Description = NULL; | |
| OptionDevicePath = NULL; | |
| BootOrderSize = 0; | |
| Corrupted = FALSE; | |
| // | |
| // Check "BootOrder" variable firstly, this variable hold the number of boot options | |
| // | |
| BootOrder = BdsLibGetVariableAndSize ( | |
| L"BootOrder", | |
| &gEfiGlobalVariableGuid, | |
| &BootOrderSize | |
| ); | |
| if (NULL == BootOrder) { | |
| return EFI_NOT_FOUND; | |
| } | |
| Index = 0; | |
| while (Index < BootOrderSize / sizeof (UINT16)) { | |
| UnicodeSPrint (BootOption, sizeof (BootOption), L"Boot%04x", BootOrder[Index]); | |
| BootOptionVar = BdsLibGetVariableAndSize ( | |
| BootOption, | |
| &gEfiGlobalVariableGuid, | |
| &BootOptionSize | |
| ); | |
| if (NULL == BootOptionVar) { | |
| FreePool (BootOrder); | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| if (!ValidateOption(BootOptionVar, BootOptionSize)) { | |
| Corrupted = TRUE; | |
| } else { | |
| TempPtr = BootOptionVar; | |
| TempPtr += sizeof (UINT32) + sizeof (UINT16); | |
| Description = (CHAR16 *) TempPtr; | |
| TempPtr += StrSize ((CHAR16 *) TempPtr); | |
| OptionDevicePath = (EFI_DEVICE_PATH_PROTOCOL *) TempPtr; | |
| // | |
| // Skip legacy boot option (BBS boot device) | |
| // | |
| if ((DevicePathType (OptionDevicePath) == BBS_DEVICE_PATH) && | |
| (DevicePathSubType (OptionDevicePath) == BBS_BBS_DP)) { | |
| FreePool (BootOptionVar); | |
| Index++; | |
| continue; | |
| } | |
| } | |
| if (Corrupted || !BdsLibIsValidEFIBootOptDevicePathExt (OptionDevicePath, FALSE, Description)) { | |
| // | |
| // Delete this invalid boot option "Boot####" | |
| // | |
| Status = gRT->SetVariable ( | |
| BootOption, | |
| &gEfiGlobalVariableGuid, | |
| EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE, | |
| 0, | |
| NULL | |
| ); | |
| // | |
| // Deleting variable with current variable implementation shouldn't fail. | |
| // | |
| ASSERT_EFI_ERROR (Status); | |
| // | |
| // Mark this boot option in boot order as deleted | |
| // | |
| BootOrder[Index] = 0xffff; | |
| Corrupted = FALSE; | |
| } | |
| FreePool (BootOptionVar); | |
| Index++; | |
| } | |
| // | |
| // Adjust boot order array | |
| // | |
| Index2 = 0; | |
| for (Index = 0; Index < BootOrderSize / sizeof (UINT16); Index++) { | |
| if (BootOrder[Index] != 0xffff) { | |
| BootOrder[Index2] = BootOrder[Index]; | |
| Index2 ++; | |
| } | |
| } | |
| Status = gRT->SetVariable ( | |
| L"BootOrder", | |
| &gEfiGlobalVariableGuid, | |
| EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE, | |
| Index2 * sizeof (UINT16), | |
| BootOrder | |
| ); | |
| // | |
| // Shrinking variable with current variable implementation shouldn't fail. | |
| // | |
| ASSERT_EFI_ERROR (Status); | |
| FreePool (BootOrder); | |
| return Status; | |
| } | |
| /** | |
| For EFI boot option, BDS separate them as six types: | |
| 1. Network - The boot option points to the SimpleNetworkProtocol device. | |
| Bds will try to automatically create this type boot option when enumerate. | |
| 2. Shell - The boot option points to internal flash shell. | |
| Bds will try to automatically create this type boot option when enumerate. | |
| 3. Removable BlockIo - The boot option only points to the removable media | |
| device, like USB flash disk, DVD, Floppy etc. | |
| These device should contain a *removable* blockIo | |
| protocol in their device handle. | |
| Bds will try to automatically create this type boot option | |
| when enumerate. | |
| 4. Fixed BlockIo - The boot option only points to a Fixed blockIo device, | |
| like HardDisk. | |
| These device should contain a *fixed* blockIo | |
| protocol in their device handle. | |
| BDS will skip fixed blockIo devices, and NOT | |
| automatically create boot option for them. But BDS | |
| will help to delete those fixed blockIo boot option, | |
| whose description rule conflict with other auto-created | |
| boot options. | |
| 5. Non-BlockIo Simplefile - The boot option points to a device whose handle | |
| has SimpleFileSystem Protocol, but has no blockio | |
| protocol. These devices do not offer blockIo | |
| protocol, but BDS still can get the | |
| \EFI\BOOT\boot{machinename}.EFI by SimpleFileSystem | |
| Protocol. | |
| 6. File - The boot option points to a file. These boot options are usually | |
| created by user manually or OS loader. BDS will not delete or modify | |
| these boot options. | |
| This function will enumerate all possible boot device in the system, and | |
| automatically create boot options for Network, Shell, Removable BlockIo, | |
| and Non-BlockIo Simplefile devices. | |
| It will only execute once of every boot. | |
| @param BdsBootOptionList The header of the link list which indexed all | |
| current boot options | |
| @retval EFI_SUCCESS Finished all the boot device enumerate and create | |
| the boot option base on that boot device | |
| @retval EFI_OUT_OF_RESOURCES Failed to enumerate the boot device and create the boot option list | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| BdsLibEnumerateAllBootOption ( | |
| IN OUT LIST_ENTRY *BdsBootOptionList | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINT16 FloppyNumber; | |
| UINT16 HarddriveNumber; | |
| UINT16 CdromNumber; | |
| UINT16 UsbNumber; | |
| UINT16 MiscNumber; | |
| UINT16 ScsiNumber; | |
| UINT16 NonBlockNumber; | |
| UINTN NumberBlockIoHandles; | |
| EFI_HANDLE *BlockIoHandles; | |
| EFI_BLOCK_IO_PROTOCOL *BlkIo; | |
| BOOLEAN Removable[2]; | |
| UINTN RemovableIndex; | |
| UINTN Index; | |
| UINTN NumOfLoadFileHandles; | |
| EFI_HANDLE *LoadFileHandles; | |
| UINTN FvHandleCount; | |
| EFI_HANDLE *FvHandleBuffer; | |
| EFI_FV_FILETYPE Type; | |
| UINTN Size; | |
| EFI_FV_FILE_ATTRIBUTES Attributes; | |
| UINT32 AuthenticationStatus; | |
| EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv; | |
| EFI_DEVICE_PATH_PROTOCOL *DevicePath; | |
| UINTN DevicePathType; | |
| CHAR16 Buffer[40]; | |
| EFI_HANDLE *FileSystemHandles; | |
| UINTN NumberFileSystemHandles; | |
| BOOLEAN NeedDelete; | |
| EFI_IMAGE_DOS_HEADER DosHeader; | |
| CHAR8 *PlatLang; | |
| CHAR8 *LastLang; | |
| EFI_IMAGE_OPTIONAL_HEADER_UNION HdrData; | |
| EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr; | |
| CHAR16 *MacStr; | |
| CHAR16 *IPverStr; | |
| EFI_HANDLE *NetworkHandles; | |
| UINTN BufferSize; | |
| FloppyNumber = 0; | |
| HarddriveNumber = 0; | |
| CdromNumber = 0; | |
| UsbNumber = 0; | |
| MiscNumber = 0; | |
| ScsiNumber = 0; | |
| PlatLang = NULL; | |
| LastLang = NULL; | |
| ZeroMem (Buffer, sizeof (Buffer)); | |
| // | |
| // If the boot device enumerate happened, just get the boot | |
| // device from the boot order variable | |
| // | |
| if (mEnumBootDevice) { | |
| GetVariable2 (LAST_ENUM_LANGUAGE_VARIABLE_NAME, &gLastEnumLangGuid, (VOID**)&LastLang, NULL); | |
| GetEfiGlobalVariable2 (L"PlatformLang", (VOID**)&PlatLang, NULL); | |
| ASSERT (PlatLang != NULL); | |
| if ((LastLang != NULL) && (AsciiStrCmp (LastLang, PlatLang) == 0)) { | |
| Status = BdsLibBuildOptionFromVar (BdsBootOptionList, L"BootOrder"); | |
| FreePool (LastLang); | |
| FreePool (PlatLang); | |
| return Status; | |
| } else { | |
| Status = gRT->SetVariable ( | |
| LAST_ENUM_LANGUAGE_VARIABLE_NAME, | |
| &gLastEnumLangGuid, | |
| EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE, | |
| AsciiStrSize (PlatLang), | |
| PlatLang | |
| ); | |
| // | |
| // Failure to set the variable only impacts the performance next time enumerating the boot options. | |
| // | |
| if (LastLang != NULL) { | |
| FreePool (LastLang); | |
| } | |
| FreePool (PlatLang); | |
| } | |
| } | |
| // | |
| // Notes: this dirty code is to get the legacy boot option from the | |
| // BBS table and create to variable as the EFI boot option, it should | |
| // be removed after the CSM can provide legacy boot option directly | |
| // | |
| REFRESH_LEGACY_BOOT_OPTIONS; | |
| // | |
| // Delete invalid boot option | |
| // | |
| BdsDeleteAllInvalidEfiBootOption (); | |
| // | |
| // Parse removable media followed by fixed media. | |
| // The Removable[] array is used by the for-loop below to create removable media boot options | |
| // at first, and then to create fixed media boot options. | |
| // | |
| Removable[0] = FALSE; | |
| Removable[1] = TRUE; | |
| gBS->LocateHandleBuffer ( | |
| ByProtocol, | |
| &gEfiBlockIoProtocolGuid, | |
| NULL, | |
| &NumberBlockIoHandles, | |
| &BlockIoHandles | |
| ); | |
| for (RemovableIndex = 0; RemovableIndex < 2; RemovableIndex++) { | |
| for (Index = 0; Index < NumberBlockIoHandles; Index++) { | |
| Status = gBS->HandleProtocol ( | |
| BlockIoHandles[Index], | |
| &gEfiBlockIoProtocolGuid, | |
| (VOID **) &BlkIo | |
| ); | |
| // | |
| // skip the logical partition | |
| // | |
| if (EFI_ERROR (Status) || BlkIo->Media->LogicalPartition) { | |
| continue; | |
| } | |
| // | |
| // firstly fixed block io then the removable block io | |
| // | |
| if (BlkIo->Media->RemovableMedia == Removable[RemovableIndex]) { | |
| continue; | |
| } | |
| DevicePath = DevicePathFromHandle (BlockIoHandles[Index]); | |
| DevicePathType = BdsGetBootTypeFromDevicePath (DevicePath); | |
| switch (DevicePathType) { | |
| case BDS_EFI_ACPI_FLOPPY_BOOT: | |
| if (FloppyNumber != 0) { | |
| UnicodeSPrint (Buffer, sizeof (Buffer), L"%s %d", BdsLibGetStringById (STRING_TOKEN (STR_DESCRIPTION_FLOPPY)), FloppyNumber); | |
| } else { | |
| UnicodeSPrint (Buffer, sizeof (Buffer), L"%s", BdsLibGetStringById (STRING_TOKEN (STR_DESCRIPTION_FLOPPY))); | |
| } | |
| BdsLibBuildOptionFromHandle (BlockIoHandles[Index], BdsBootOptionList, Buffer); | |
| FloppyNumber++; | |
| break; | |
| // | |
| // Assume a removable SATA device should be the DVD/CD device, a fixed SATA device should be the Hard Drive device. | |
| // | |
| case BDS_EFI_MESSAGE_ATAPI_BOOT: | |
| case BDS_EFI_MESSAGE_SATA_BOOT: | |
| if (BlkIo->Media->RemovableMedia) { | |
| if (CdromNumber != 0) { | |
| UnicodeSPrint (Buffer, sizeof (Buffer), L"%s %d", BdsLibGetStringById (STRING_TOKEN (STR_DESCRIPTION_CD_DVD)), CdromNumber); | |
| } else { | |
| UnicodeSPrint (Buffer, sizeof (Buffer), L"%s", BdsLibGetStringById (STRING_TOKEN (STR_DESCRIPTION_CD_DVD))); | |
| } | |
| CdromNumber++; | |
| } else { | |
| if (HarddriveNumber != 0) { | |
| UnicodeSPrint (Buffer, sizeof (Buffer), L"%s %d", BdsLibGetStringById (STRING_TOKEN (STR_DESCRIPTION_HARDDRIVE)), HarddriveNumber); | |
| } else { | |
| UnicodeSPrint (Buffer, sizeof (Buffer), L"%s", BdsLibGetStringById (STRING_TOKEN (STR_DESCRIPTION_HARDDRIVE))); | |
| } | |
| HarddriveNumber++; | |
| } | |
| DEBUG ((DEBUG_INFO | DEBUG_LOAD, "Buffer: %S\n", Buffer)); | |
| BdsLibBuildOptionFromHandle (BlockIoHandles[Index], BdsBootOptionList, Buffer); | |
| break; | |
| case BDS_EFI_MESSAGE_USB_DEVICE_BOOT: | |
| if (UsbNumber != 0) { | |
| UnicodeSPrint (Buffer, sizeof (Buffer), L"%s %d", BdsLibGetStringById (STRING_TOKEN (STR_DESCRIPTION_USB)), UsbNumber); | |
| } else { | |
| UnicodeSPrint (Buffer, sizeof (Buffer), L"%s", BdsLibGetStringById (STRING_TOKEN (STR_DESCRIPTION_USB))); | |
| } | |
| BdsLibBuildOptionFromHandle (BlockIoHandles[Index], BdsBootOptionList, Buffer); | |
| UsbNumber++; | |
| break; | |
| case BDS_EFI_MESSAGE_SCSI_BOOT: | |
| if (ScsiNumber != 0) { | |
| UnicodeSPrint (Buffer, sizeof (Buffer), L"%s %d", BdsLibGetStringById (STRING_TOKEN (STR_DESCRIPTION_SCSI)), ScsiNumber); | |
| } else { | |
| UnicodeSPrint (Buffer, sizeof (Buffer), L"%s", BdsLibGetStringById (STRING_TOKEN (STR_DESCRIPTION_SCSI))); | |
| } | |
| BdsLibBuildOptionFromHandle (BlockIoHandles[Index], BdsBootOptionList, Buffer); | |
| ScsiNumber++; | |
| break; | |
| case BDS_EFI_MESSAGE_MISC_BOOT: | |
| default: | |
| if (MiscNumber != 0) { | |
| UnicodeSPrint (Buffer, sizeof (Buffer), L"%s %d", BdsLibGetStringById (STRING_TOKEN (STR_DESCRIPTION_MISC)), MiscNumber); | |
| } else { | |
| UnicodeSPrint (Buffer, sizeof (Buffer), L"%s", BdsLibGetStringById (STRING_TOKEN (STR_DESCRIPTION_MISC))); | |
| } | |
| BdsLibBuildOptionFromHandle (BlockIoHandles[Index], BdsBootOptionList, Buffer); | |
| MiscNumber++; | |
| break; | |
| } | |
| } | |
| } | |
| if (NumberBlockIoHandles != 0) { | |
| FreePool (BlockIoHandles); | |
| } | |
| // | |
| // If there is simple file protocol which does not consume block Io protocol, create a boot option for it here. | |
| // | |
| NonBlockNumber = 0; | |
| gBS->LocateHandleBuffer ( | |
| ByProtocol, | |
| &gEfiSimpleFileSystemProtocolGuid, | |
| NULL, | |
| &NumberFileSystemHandles, | |
| &FileSystemHandles | |
| ); | |
| for (Index = 0; Index < NumberFileSystemHandles; Index++) { | |
| Status = gBS->HandleProtocol ( | |
| FileSystemHandles[Index], | |
| &gEfiBlockIoProtocolGuid, | |
| (VOID **) &BlkIo | |
| ); | |
| if (!EFI_ERROR (Status)) { | |
| // | |
| // Skip if the file system handle supports a BlkIo protocol, | |
| // | |
| continue; | |
| } | |
| // | |
| // Do the removable Media thing. \EFI\BOOT\boot{machinename}.EFI | |
| // machinename is ia32, ia64, x64, ... | |
| // | |
| Hdr.Union = &HdrData; | |
| NeedDelete = TRUE; | |
| Status = BdsLibGetImageHeader ( | |
| FileSystemHandles[Index], | |
| EFI_REMOVABLE_MEDIA_FILE_NAME, | |
| &DosHeader, | |
| Hdr | |
| ); | |
| if (!EFI_ERROR (Status) && | |
| EFI_IMAGE_MACHINE_TYPE_SUPPORTED (Hdr.Pe32->FileHeader.Machine) && | |
| Hdr.Pe32->OptionalHeader.Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION) { | |
| NeedDelete = FALSE; | |
| } | |
| if (NeedDelete) { | |
| // | |
| // No such file or the file is not a EFI application, delete this boot option | |
| // | |
| BdsLibDeleteOptionFromHandle (FileSystemHandles[Index]); | |
| } else { | |
| if (NonBlockNumber != 0) { | |
| UnicodeSPrint (Buffer, sizeof (Buffer), L"%s %d", BdsLibGetStringById (STRING_TOKEN (STR_DESCRIPTION_NON_BLOCK)), NonBlockNumber); | |
| } else { | |
| UnicodeSPrint (Buffer, sizeof (Buffer), L"%s", BdsLibGetStringById (STRING_TOKEN (STR_DESCRIPTION_NON_BLOCK))); | |
| } | |
| BdsLibBuildOptionFromHandle (FileSystemHandles[Index], BdsBootOptionList, Buffer); | |
| NonBlockNumber++; | |
| } | |
| } | |
| if (NumberFileSystemHandles != 0) { | |
| FreePool (FileSystemHandles); | |
| } | |
| // | |
| // Parse Network Boot Device | |
| // | |
| NumOfLoadFileHandles = 0; | |
| // | |
| // Search Load File protocol for PXE boot option. | |
| // | |
| gBS->LocateHandleBuffer ( | |
| ByProtocol, | |
| &gEfiLoadFileProtocolGuid, | |
| NULL, | |
| &NumOfLoadFileHandles, | |
| &LoadFileHandles | |
| ); | |
| for (Index = 0; Index < NumOfLoadFileHandles; Index++) { | |
| // | |
| //Locate EFI_DEVICE_PATH_PROTOCOL to dynamically get IPv4/IPv6 protocol information. | |
| // | |
| Status = gBS->HandleProtocol ( | |
| LoadFileHandles[Index], | |
| &gEfiDevicePathProtocolGuid, | |
| (VOID **) &DevicePath | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| while (!IsDevicePathEnd (DevicePath)) { | |
| if ((DevicePath->Type == MESSAGING_DEVICE_PATH) && | |
| (DevicePath->SubType == MSG_IPv4_DP)) { | |
| // | |
| //Get handle infomation | |
| // | |
| BufferSize = 0; | |
| NetworkHandles = NULL; | |
| Status = gBS->LocateHandle ( | |
| ByProtocol, | |
| &gEfiSimpleNetworkProtocolGuid, | |
| NULL, | |
| &BufferSize, | |
| NetworkHandles | |
| ); | |
| if (Status == EFI_BUFFER_TOO_SMALL) { | |
| NetworkHandles = AllocateZeroPool(BufferSize); | |
| if (NetworkHandles == NULL) { | |
| return (EFI_OUT_OF_RESOURCES); | |
| } | |
| Status = gBS->LocateHandle( | |
| ByProtocol, | |
| &gEfiSimpleNetworkProtocolGuid, | |
| NULL, | |
| &BufferSize, | |
| NetworkHandles | |
| ); | |
| } | |
| // | |
| //Get the MAC string | |
| // | |
| Status = NetLibGetMacString ( | |
| *NetworkHandles, | |
| NULL, | |
| &MacStr | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| IPverStr = L" IPv4"; | |
| UnicodeSPrint (Buffer, sizeof (Buffer), L"%s%s%s", BdsLibGetStringById (STRING_TOKEN (STR_DESCRIPTION_NETWORK)),MacStr,IPverStr); | |
| break; | |
| } | |
| if((DevicePath->Type == MESSAGING_DEVICE_PATH) && | |
| (DevicePath->SubType == MSG_IPv6_DP)) { | |
| // | |
| //Get handle infomation | |
| // | |
| BufferSize = 0; | |
| NetworkHandles = NULL; | |
| Status = gBS->LocateHandle ( | |
| ByProtocol, | |
| &gEfiSimpleNetworkProtocolGuid, | |
| NULL, | |
| &BufferSize, | |
| NetworkHandles | |
| ); | |
| if (Status == EFI_BUFFER_TOO_SMALL) { | |
| NetworkHandles = AllocateZeroPool(BufferSize); | |
| if (NetworkHandles == NULL) { | |
| return (EFI_OUT_OF_RESOURCES); | |
| } | |
| Status = gBS->LocateHandle( | |
| ByProtocol, | |
| &gEfiSimpleNetworkProtocolGuid, | |
| NULL, | |
| &BufferSize, | |
| NetworkHandles | |
| ); | |
| } | |
| // | |
| //Get the MAC string | |
| // | |
| Status = NetLibGetMacString ( | |
| *NetworkHandles, | |
| NULL, | |
| &MacStr | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| IPverStr = L" IPv6"; | |
| UnicodeSPrint (Buffer, sizeof (Buffer), L"%s%s%s", BdsLibGetStringById (STRING_TOKEN (STR_DESCRIPTION_NETWORK)),MacStr,IPverStr); | |
| break; | |
| } | |
| DevicePath = NextDevicePathNode (DevicePath); | |
| } | |
| BdsLibBuildOptionFromHandle (LoadFileHandles[Index], BdsBootOptionList, Buffer); | |
| } | |
| if (NumOfLoadFileHandles != 0) { | |
| FreePool (LoadFileHandles); | |
| } | |
| // | |
| // Check if we have on flash shell | |
| // | |
| gBS->LocateHandleBuffer ( | |
| ByProtocol, | |
| &gEfiFirmwareVolume2ProtocolGuid, | |
| NULL, | |
| &FvHandleCount, | |
| &FvHandleBuffer | |
| ); | |
| for (Index = 0; Index < FvHandleCount; Index++) { | |
| gBS->HandleProtocol ( | |
| FvHandleBuffer[Index], | |
| &gEfiFirmwareVolume2ProtocolGuid, | |
| (VOID **) &Fv | |
| ); | |
| Status = Fv->ReadFile ( | |
| Fv, | |
| PcdGetPtr(PcdShellFile), | |
| NULL, | |
| &Size, | |
| &Type, | |
| &Attributes, | |
| &AuthenticationStatus | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| // | |
| // Skip if no shell file in the FV | |
| // | |
| continue; | |
| } | |
| // | |
| // Build the shell boot option | |
| // | |
| BdsLibBuildOptionFromShell (FvHandleBuffer[Index], BdsBootOptionList); | |
| } | |
| if (FvHandleCount != 0) { | |
| FreePool (FvHandleBuffer); | |
| } | |
| // | |
| // Make sure every boot only have one time | |
| // boot device enumerate | |
| // | |
| Status = BdsLibBuildOptionFromVar (BdsBootOptionList, L"BootOrder"); | |
| mEnumBootDevice = TRUE; | |
| return Status; | |
| } | |
| /** | |
| Build the boot option with the handle parsed in | |
| @param Handle The handle which present the device path to create | |
| boot option | |
| @param BdsBootOptionList The header of the link list which indexed all | |
| current boot options | |
| @param String The description of the boot option. | |
| **/ | |
| VOID | |
| EFIAPI | |
| BdsLibBuildOptionFromHandle ( | |
| IN EFI_HANDLE Handle, | |
| IN LIST_ENTRY *BdsBootOptionList, | |
| IN CHAR16 *String | |
| ) | |
| { | |
| EFI_DEVICE_PATH_PROTOCOL *DevicePath; | |
| DevicePath = DevicePathFromHandle (Handle); | |
| // | |
| // Create and register new boot option | |
| // | |
| BdsLibRegisterNewOption (BdsBootOptionList, DevicePath, String, L"BootOrder"); | |
| } | |
| /** | |
| Build the on flash shell boot option with the handle parsed in. | |
| @param Handle The handle which present the device path to create | |
| on flash shell boot option | |
| @param BdsBootOptionList The header of the link list which indexed all | |
| current boot options | |
| **/ | |
| VOID | |
| EFIAPI | |
| BdsLibBuildOptionFromShell ( | |
| IN EFI_HANDLE Handle, | |
| IN OUT LIST_ENTRY *BdsBootOptionList | |
| ) | |
| { | |
| EFI_DEVICE_PATH_PROTOCOL *DevicePath; | |
| MEDIA_FW_VOL_FILEPATH_DEVICE_PATH ShellNode; | |
| DevicePath = DevicePathFromHandle (Handle); | |
| // | |
| // Build the shell device path | |
| // | |
| EfiInitializeFwVolDevicepathNode (&ShellNode, PcdGetPtr(PcdShellFile)); | |
| DevicePath = AppendDevicePathNode (DevicePath, (EFI_DEVICE_PATH_PROTOCOL *) &ShellNode); | |
| // | |
| // Create and register the shell boot option | |
| // | |
| BdsLibRegisterNewOption (BdsBootOptionList, DevicePath, L"EFI Internal Shell", L"BootOrder"); | |
| } | |
| /** | |
| Boot from the UEFI spec defined "BootNext" variable. | |
| **/ | |
| VOID | |
| EFIAPI | |
| BdsLibBootNext ( | |
| VOID | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINT16 *BootNext; | |
| UINTN BootNextSize; | |
| CHAR16 Buffer[20]; | |
| BDS_COMMON_OPTION *BootOption; | |
| LIST_ENTRY TempList; | |
| UINTN ExitDataSize; | |
| CHAR16 *ExitData; | |
| // | |
| // Init the boot option name buffer and temp link list | |
| // | |
| InitializeListHead (&TempList); | |
| ZeroMem (Buffer, sizeof (Buffer)); | |
| BootNext = BdsLibGetVariableAndSize ( | |
| L"BootNext", | |
| &gEfiGlobalVariableGuid, | |
| &BootNextSize | |
| ); | |
| // | |
| // Clear the boot next variable first | |
| // | |
| if (BootNext != NULL) { | |
| Status = gRT->SetVariable ( | |
| L"BootNext", | |
| &gEfiGlobalVariableGuid, | |
| EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE, | |
| 0, | |
| NULL | |
| ); | |
| // | |
| // Deleting variable with current variable implementation shouldn't fail. | |
| // | |
| ASSERT_EFI_ERROR (Status); | |
| // | |
| // Start to build the boot option and try to boot | |
| // | |
| UnicodeSPrint (Buffer, sizeof (Buffer), L"Boot%04x", *BootNext); | |
| BootOption = BdsLibVariableToOption (&TempList, Buffer); | |
| ASSERT (BootOption != NULL); | |
| BdsLibConnectDevicePath (BootOption->DevicePath); | |
| BdsLibBootViaBootOption (BootOption, BootOption->DevicePath, &ExitDataSize, &ExitData); | |
| FreePool(BootOption); | |
| FreePool(BootNext); | |
| } | |
| } | |
| /** | |
| Return the bootable media handle. | |
| First, check the device is connected | |
| Second, check whether the device path point to a device which support SimpleFileSystemProtocol, | |
| Third, detect the the default boot file in the Media, and return the removable Media handle. | |
| @param DevicePath Device Path to a bootable device | |
| @return The bootable media handle. If the media on the DevicePath is not bootable, NULL will return. | |
| **/ | |
| EFI_HANDLE | |
| EFIAPI | |
| BdsLibGetBootableHandle ( | |
| IN EFI_DEVICE_PATH_PROTOCOL *DevicePath | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_TPL OldTpl; | |
| EFI_DEVICE_PATH_PROTOCOL *UpdatedDevicePath; | |
| EFI_DEVICE_PATH_PROTOCOL *DupDevicePath; | |
| EFI_HANDLE Handle; | |
| EFI_BLOCK_IO_PROTOCOL *BlockIo; | |
| VOID *Buffer; | |
| EFI_DEVICE_PATH_PROTOCOL *TempDevicePath; | |
| UINTN Size; | |
| UINTN TempSize; | |
| EFI_HANDLE ReturnHandle; | |
| EFI_HANDLE *SimpleFileSystemHandles; | |
| UINTN NumberSimpleFileSystemHandles; | |
| UINTN Index; | |
| EFI_IMAGE_DOS_HEADER DosHeader; | |
| EFI_IMAGE_OPTIONAL_HEADER_UNION HdrData; | |
| EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr; | |
| UpdatedDevicePath = DevicePath; | |
| // | |
| // Enter to critical section to protect the acquired BlockIo instance | |
| // from getting released due to the USB mass storage hotplug event | |
| // | |
| OldTpl = gBS->RaiseTPL (TPL_CALLBACK); | |
| // | |
| // Check whether the device is connected | |
| // | |
| Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &UpdatedDevicePath, &Handle); | |
| if (EFI_ERROR (Status)) { | |
| // | |
| // Skip the case that the boot option point to a simple file protocol which does not consume block Io protocol, | |
| // | |
| Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &UpdatedDevicePath, &Handle); | |
| if (EFI_ERROR (Status)) { | |
| // | |
| // Fail to find the proper BlockIo and simple file protocol, maybe because device not present, we need to connect it firstly | |
| // | |
| UpdatedDevicePath = DevicePath; | |
| Status = gBS->LocateDevicePath (&gEfiDevicePathProtocolGuid, &UpdatedDevicePath, &Handle); | |
| gBS->ConnectController (Handle, NULL, NULL, TRUE); | |
| } | |
| } else { | |
| // | |
| // For removable device boot option, its contained device path only point to the removable device handle, | |
| // should make sure all its children handles (its child partion or media handles) are created and connected. | |
| // | |
| gBS->ConnectController (Handle, NULL, NULL, TRUE); | |
| // | |
| // Get BlockIo protocol and check removable attribute | |
| // | |
| Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **)&BlockIo); | |
| ASSERT_EFI_ERROR (Status); | |
| // | |
| // Issue a dummy read to the device to check for media change. | |
| // When the removable media is changed, any Block IO read/write will | |
| // cause the BlockIo protocol be reinstalled and EFI_MEDIA_CHANGED is | |
| // returned. After the Block IO protocol is reinstalled, subsequent | |
| // Block IO read/write will success. | |
| // | |
| Buffer = AllocatePool (BlockIo->Media->BlockSize); | |
| if (Buffer != NULL) { | |
| BlockIo->ReadBlocks ( | |
| BlockIo, | |
| BlockIo->Media->MediaId, | |
| 0, | |
| BlockIo->Media->BlockSize, | |
| Buffer | |
| ); | |
| FreePool(Buffer); | |
| } | |
| } | |
| // | |
| // Detect the the default boot file from removable Media | |
| // | |
| // | |
| // If fail to get bootable handle specified by a USB boot option, the BDS should try to find other bootable device in the same USB bus | |
| // Try to locate the USB node device path first, if fail then use its previous PCI node to search | |
| // | |
| DupDevicePath = DuplicateDevicePath (DevicePath); | |
| ASSERT (DupDevicePath != NULL); | |
| UpdatedDevicePath = DupDevicePath; | |
| Status = gBS->LocateDevicePath (&gEfiDevicePathProtocolGuid, &UpdatedDevicePath, &Handle); | |
| // | |
| // if the resulting device path point to a usb node, and the usb node is a dummy node, should only let device path only point to the previous Pci node | |
| // Acpi()/Pci()/Usb() --> Acpi()/Pci() | |
| // | |
| if ((DevicePathType (UpdatedDevicePath) == MESSAGING_DEVICE_PATH) && | |
| (DevicePathSubType (UpdatedDevicePath) == MSG_USB_DP)) { | |
| // | |
| // Remove the usb node, let the device path only point to PCI node | |
| // | |
| SetDevicePathEndNode (UpdatedDevicePath); | |
| UpdatedDevicePath = DupDevicePath; | |
| } else { | |
| UpdatedDevicePath = DevicePath; | |
| } | |
| // | |
| // Get the device path size of boot option | |
| // | |
| Size = GetDevicePathSize(UpdatedDevicePath) - sizeof (EFI_DEVICE_PATH_PROTOCOL); // minus the end node | |
| ReturnHandle = NULL; | |
| gBS->LocateHandleBuffer ( | |
| ByProtocol, | |
| &gEfiSimpleFileSystemProtocolGuid, | |
| NULL, | |
| &NumberSimpleFileSystemHandles, | |
| &SimpleFileSystemHandles | |
| ); | |
| for (Index = 0; Index < NumberSimpleFileSystemHandles; Index++) { | |
| // | |
| // Get the device path size of SimpleFileSystem handle | |
| // | |
| TempDevicePath = DevicePathFromHandle (SimpleFileSystemHandles[Index]); | |
| TempSize = GetDevicePathSize (TempDevicePath)- sizeof (EFI_DEVICE_PATH_PROTOCOL); // minus the end node | |
| // | |
| // Check whether the device path of boot option is part of the SimpleFileSystem handle's device path | |
| // | |
| if (Size <= TempSize && CompareMem (TempDevicePath, UpdatedDevicePath, Size)==0) { | |
| // | |
| // Load the default boot file \EFI\BOOT\boot{machinename}.EFI from removable Media | |
| // machinename is ia32, ia64, x64, ... | |
| // | |
| Hdr.Union = &HdrData; | |
| Status = BdsLibGetImageHeader ( | |
| SimpleFileSystemHandles[Index], | |
| EFI_REMOVABLE_MEDIA_FILE_NAME, | |
| &DosHeader, | |
| Hdr | |
| ); | |
| if (!EFI_ERROR (Status) && | |
| EFI_IMAGE_MACHINE_TYPE_SUPPORTED (Hdr.Pe32->FileHeader.Machine) && | |
| Hdr.Pe32->OptionalHeader.Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION) { | |
| ReturnHandle = SimpleFileSystemHandles[Index]; | |
| break; | |
| } | |
| } | |
| } | |
| FreePool(DupDevicePath); | |
| if (SimpleFileSystemHandles != NULL) { | |
| FreePool(SimpleFileSystemHandles); | |
| } | |
| gBS->RestoreTPL (OldTpl); | |
| return ReturnHandle; | |
| } | |
| /** | |
| Check to see if the network cable is plugged in. If the DevicePath is not | |
| connected it will be connected. | |
| @param DevicePath Device Path to check | |
| @retval TRUE DevicePath points to an Network that is connected | |
| @retval FALSE DevicePath does not point to a bootable network | |
| **/ | |
| BOOLEAN | |
| BdsLibNetworkBootWithMediaPresent ( | |
| IN EFI_DEVICE_PATH_PROTOCOL *DevicePath | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_DEVICE_PATH_PROTOCOL *UpdatedDevicePath; | |
| EFI_HANDLE Handle; | |
| EFI_SIMPLE_NETWORK_PROTOCOL *Snp; | |
| BOOLEAN MediaPresent; | |
| UINT32 InterruptStatus; | |
| MediaPresent = FALSE; | |
| UpdatedDevicePath = DevicePath; | |
| // | |
| // Locate Load File Protocol for PXE boot option first | |
| // | |
| Status = gBS->LocateDevicePath (&gEfiLoadFileProtocolGuid, &UpdatedDevicePath, &Handle); | |
| if (EFI_ERROR (Status)) { | |
| // | |
| // Device not present so see if we need to connect it | |
| // | |
| Status = BdsLibConnectDevicePath (DevicePath); | |
| if (!EFI_ERROR (Status)) { | |
| // | |
| // This one should work after we did the connect | |
| // | |
| Status = gBS->LocateDevicePath (&gEfiLoadFileProtocolGuid, &UpdatedDevicePath, &Handle); | |
| } | |
| } | |
| if (!EFI_ERROR (Status)) { | |
| Status = gBS->HandleProtocol (Handle, &gEfiSimpleNetworkProtocolGuid, (VOID **)&Snp); | |
| if (EFI_ERROR (Status)) { | |
| // | |
| // Failed to open SNP from this handle, try to get SNP from parent handle | |
| // | |
| UpdatedDevicePath = DevicePathFromHandle (Handle); | |
| if (UpdatedDevicePath != NULL) { | |
| Status = gBS->LocateDevicePath (&gEfiSimpleNetworkProtocolGuid, &UpdatedDevicePath, &Handle); | |
| if (!EFI_ERROR (Status)) { | |
| // | |
| // SNP handle found, get SNP from it | |
| // | |
| Status = gBS->HandleProtocol (Handle, &gEfiSimpleNetworkProtocolGuid, (VOID **) &Snp); | |
| } | |
| } | |
| } | |
| if (!EFI_ERROR (Status)) { | |
| if (Snp->Mode->MediaPresentSupported) { | |
| if (Snp->Mode->State == EfiSimpleNetworkInitialized) { | |
| // | |
| // Invoke Snp->GetStatus() to refresh the media status | |
| // | |
| Snp->GetStatus (Snp, &InterruptStatus, NULL); | |
| // | |
| // In case some one else is using the SNP check to see if it's connected | |
| // | |
| MediaPresent = Snp->Mode->MediaPresent; | |
| } else { | |
| // | |
| // No one is using SNP so we need to Start and Initialize so | |
| // MediaPresent will be valid. | |
| // | |
| Status = Snp->Start (Snp); | |
| if (!EFI_ERROR (Status)) { | |
| Status = Snp->Initialize (Snp, 0, 0); | |
| if (!EFI_ERROR (Status)) { | |
| MediaPresent = Snp->Mode->MediaPresent; | |
| Snp->Shutdown (Snp); | |
| } | |
| Snp->Stop (Snp); | |
| } | |
| } | |
| } else { | |
| MediaPresent = TRUE; | |
| } | |
| } | |
| } | |
| return MediaPresent; | |
| } | |
| /** | |
| For a bootable Device path, return its boot type. | |
| @param DevicePath The bootable device Path to check | |
| @retval BDS_EFI_MEDIA_HD_BOOT If given device path contains MEDIA_DEVICE_PATH type device path node | |
| which subtype is MEDIA_HARDDRIVE_DP | |
| @retval BDS_EFI_MEDIA_CDROM_BOOT If given device path contains MEDIA_DEVICE_PATH type device path node | |
| which subtype is MEDIA_CDROM_DP | |
| @retval BDS_EFI_ACPI_FLOPPY_BOOT If given device path contains ACPI_DEVICE_PATH type device path node | |
| which HID is floppy device. | |
| @retval BDS_EFI_MESSAGE_ATAPI_BOOT If given device path contains MESSAGING_DEVICE_PATH type device path node | |
| and its last device path node's subtype is MSG_ATAPI_DP. | |
| @retval BDS_EFI_MESSAGE_SCSI_BOOT If given device path contains MESSAGING_DEVICE_PATH type device path node | |
| and its last device path node's subtype is MSG_SCSI_DP. | |
| @retval BDS_EFI_MESSAGE_USB_DEVICE_BOOT If given device path contains MESSAGING_DEVICE_PATH type device path node | |
| and its last device path node's subtype is MSG_USB_DP. | |
| @retval BDS_EFI_MESSAGE_MISC_BOOT If the device path not contains any media device path node, and | |
| its last device path node point to a message device path node. | |
| @retval BDS_LEGACY_BBS_BOOT If given device path contains BBS_DEVICE_PATH type device path node. | |
| @retval BDS_EFI_UNSUPPORT An EFI Removable BlockIO device path not point to a media and message device, | |
| **/ | |
| UINT32 | |
| EFIAPI | |
| BdsGetBootTypeFromDevicePath ( | |
| IN EFI_DEVICE_PATH_PROTOCOL *DevicePath | |
| ) | |
| { | |
| ACPI_HID_DEVICE_PATH *Acpi; | |
| EFI_DEVICE_PATH_PROTOCOL *TempDevicePath; | |
| EFI_DEVICE_PATH_PROTOCOL *LastDeviceNode; | |
| UINT32 BootType; | |
| if (NULL == DevicePath) { | |
| return BDS_EFI_UNSUPPORT; | |
| } | |
| TempDevicePath = DevicePath; | |
| while (!IsDevicePathEndType (TempDevicePath)) { | |
| switch (DevicePathType (TempDevicePath)) { | |
| case BBS_DEVICE_PATH: | |
| return BDS_LEGACY_BBS_BOOT; | |
| case MEDIA_DEVICE_PATH: | |
| if (DevicePathSubType (TempDevicePath) == MEDIA_HARDDRIVE_DP) { | |
| return BDS_EFI_MEDIA_HD_BOOT; | |
| } else if (DevicePathSubType (TempDevicePath) == MEDIA_CDROM_DP) { | |
| return BDS_EFI_MEDIA_CDROM_BOOT; | |
| } | |
| break; | |
| case ACPI_DEVICE_PATH: | |
| Acpi = (ACPI_HID_DEVICE_PATH *) TempDevicePath; | |
| if (EISA_ID_TO_NUM (Acpi->HID) == 0x0604) { | |
| return BDS_EFI_ACPI_FLOPPY_BOOT; | |
| } | |
| break; | |
| case MESSAGING_DEVICE_PATH: | |
| // | |
| // Get the last device path node | |
| // | |
| LastDeviceNode = NextDevicePathNode (TempDevicePath); | |
| if (DevicePathSubType(LastDeviceNode) == MSG_DEVICE_LOGICAL_UNIT_DP) { | |
| // | |
| // if the next node type is Device Logical Unit, which specify the Logical Unit Number (LUN), | |
| // skip it | |
| // | |
| LastDeviceNode = NextDevicePathNode (LastDeviceNode); | |
| } | |
| // | |
| // if the device path not only point to driver device, it is not a messaging device path, | |
| // | |
| if (!IsDevicePathEndType (LastDeviceNode)) { | |
| break; | |
| } | |
| switch (DevicePathSubType (TempDevicePath)) { | |
| case MSG_ATAPI_DP: | |
| BootType = BDS_EFI_MESSAGE_ATAPI_BOOT; | |
| break; | |
| case MSG_USB_DP: | |
| BootType = BDS_EFI_MESSAGE_USB_DEVICE_BOOT; | |
| break; | |
| case MSG_SCSI_DP: | |
| BootType = BDS_EFI_MESSAGE_SCSI_BOOT; | |
| break; | |
| case MSG_SATA_DP: | |
| BootType = BDS_EFI_MESSAGE_SATA_BOOT; | |
| break; | |
| case MSG_MAC_ADDR_DP: | |
| case MSG_VLAN_DP: | |
| case MSG_IPv4_DP: | |
| case MSG_IPv6_DP: | |
| BootType = BDS_EFI_MESSAGE_MAC_BOOT; | |
| break; | |
| default: | |
| BootType = BDS_EFI_MESSAGE_MISC_BOOT; | |
| break; | |
| } | |
| return BootType; | |
| default: | |
| break; | |
| } | |
| TempDevicePath = NextDevicePathNode (TempDevicePath); | |
| } | |
| return BDS_EFI_UNSUPPORT; | |
| } | |
| /** | |
| Check whether the Device path in a boot option point to a valid bootable device, | |
| And if CheckMedia is true, check the device is ready to boot now. | |
| @param DevPath the Device path in a boot option | |
| @param CheckMedia if true, check the device is ready to boot now. | |
| @retval TRUE the Device path is valid | |
| @retval FALSE the Device path is invalid . | |
| **/ | |
| BOOLEAN | |
| EFIAPI | |
| BdsLibIsValidEFIBootOptDevicePath ( | |
| IN EFI_DEVICE_PATH_PROTOCOL *DevPath, | |
| IN BOOLEAN CheckMedia | |
| ) | |
| { | |
| return BdsLibIsValidEFIBootOptDevicePathExt (DevPath, CheckMedia, NULL); | |
| } | |
| /** | |
| Check whether the Device path in a boot option point to a valid bootable device, | |
| And if CheckMedia is true, check the device is ready to boot now. | |
| If Description is not NULL and the device path point to a fixed BlockIo | |
| device, check the description whether conflict with other auto-created | |
| boot options. | |
| @param DevPath the Device path in a boot option | |
| @param CheckMedia if true, check the device is ready to boot now. | |
| @param Description the description in a boot option | |
| @retval TRUE the Device path is valid | |
| @retval FALSE the Device path is invalid . | |
| **/ | |
| BOOLEAN | |
| EFIAPI | |
| BdsLibIsValidEFIBootOptDevicePathExt ( | |
| IN EFI_DEVICE_PATH_PROTOCOL *DevPath, | |
| IN BOOLEAN CheckMedia, | |
| IN CHAR16 *Description | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_HANDLE Handle; | |
| EFI_DEVICE_PATH_PROTOCOL *TempDevicePath; | |
| EFI_DEVICE_PATH_PROTOCOL *LastDeviceNode; | |
| EFI_BLOCK_IO_PROTOCOL *BlockIo; | |
| TempDevicePath = DevPath; | |
| LastDeviceNode = DevPath; | |
| // | |
| // Check if it's a valid boot option for network boot device. | |
| // Check if there is EfiLoadFileProtocol installed. | |
| // If yes, that means there is a boot option for network. | |
| // | |
| Status = gBS->LocateDevicePath ( | |
| &gEfiLoadFileProtocolGuid, | |
| &TempDevicePath, | |
| &Handle | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| // | |
| // Device not present so see if we need to connect it | |
| // | |
| TempDevicePath = DevPath; | |
| BdsLibConnectDevicePath (TempDevicePath); | |
| Status = gBS->LocateDevicePath ( | |
| &gEfiLoadFileProtocolGuid, | |
| &TempDevicePath, | |
| &Handle | |
| ); | |
| } | |
| if (!EFI_ERROR (Status)) { | |
| if (!IsDevicePathEnd (TempDevicePath)) { | |
| // | |
| // LoadFile protocol is not installed on handle with exactly the same DevPath | |
| // | |
| return FALSE; | |
| } | |
| if (CheckMedia) { | |
| // | |
| // Test if it is ready to boot now | |
| // | |
| if (BdsLibNetworkBootWithMediaPresent(DevPath)) { | |
| return TRUE; | |
| } | |
| } else { | |
| return TRUE; | |
| } | |
| } | |
| // | |
| // If the boot option point to a file, it is a valid EFI boot option, | |
| // and assume it is ready to boot now | |
| // | |
| while (!IsDevicePathEnd (TempDevicePath)) { | |
| // | |
| // If there is USB Class or USB WWID device path node, treat it as valid EFI | |
| // Boot Option. BdsExpandUsbShortFormDevicePath () will be used to expand it | |
| // to full device path. | |
| // | |
| if ((DevicePathType (TempDevicePath) == MESSAGING_DEVICE_PATH) && | |
| ((DevicePathSubType (TempDevicePath) == MSG_USB_CLASS_DP) || | |
| (DevicePathSubType (TempDevicePath) == MSG_USB_WWID_DP))) { | |
| return TRUE; | |
| } | |
| LastDeviceNode = TempDevicePath; | |
| TempDevicePath = NextDevicePathNode (TempDevicePath); | |
| } | |
| if ((DevicePathType (LastDeviceNode) == MEDIA_DEVICE_PATH) && | |
| (DevicePathSubType (LastDeviceNode) == MEDIA_FILEPATH_DP)) { | |
| return TRUE; | |
| } | |
| // | |
| // Check if it's a valid boot option for internal FV application | |
| // | |
| if (EfiGetNameGuidFromFwVolDevicePathNode ((MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *) LastDeviceNode) != NULL) { | |
| // | |
| // If the boot option point to internal FV application, make sure it is valid | |
| // | |
| TempDevicePath = DevPath; | |
| Status = BdsLibUpdateFvFileDevicePath ( | |
| &TempDevicePath, | |
| EfiGetNameGuidFromFwVolDevicePathNode ((MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *) LastDeviceNode) | |
| ); | |
| if (Status == EFI_ALREADY_STARTED) { | |
| return TRUE; | |
| } else { | |
| if (Status == EFI_SUCCESS) { | |
| FreePool (TempDevicePath); | |
| } | |
| return FALSE; | |
| } | |
| } | |
| // | |
| // If the boot option point to a blockIO device: | |
| // if it is a removable blockIo device, it is valid. | |
| // if it is a fixed blockIo device, check its description confliction. | |
| // | |
| TempDevicePath = DevPath; | |
| Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &TempDevicePath, &Handle); | |
| if (EFI_ERROR (Status)) { | |
| // | |
| // Device not present so see if we need to connect it | |
| // | |
| Status = BdsLibConnectDevicePath (DevPath); | |
| if (!EFI_ERROR (Status)) { | |
| // | |
| // Try again to get the Block Io protocol after we did the connect | |
| // | |
| TempDevicePath = DevPath; | |
| Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &TempDevicePath, &Handle); | |
| } | |
| } | |
| if (!EFI_ERROR (Status)) { | |
| Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **)&BlockIo); | |
| if (!EFI_ERROR (Status)) { | |
| if (CheckMedia) { | |
| // | |
| // Test if it is ready to boot now | |
| // | |
| if (BdsLibGetBootableHandle (DevPath) != NULL) { | |
| return TRUE; | |
| } | |
| } else { | |
| return TRUE; | |
| } | |
| } | |
| } else { | |
| // | |
| // if the boot option point to a simple file protocol which does not consume block Io protocol, it is also a valid EFI boot option, | |
| // | |
| Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &TempDevicePath, &Handle); | |
| if (!EFI_ERROR (Status)) { | |
| if (CheckMedia) { | |
| // | |
| // Test if it is ready to boot now | |
| // | |
| if (BdsLibGetBootableHandle (DevPath) != NULL) { | |
| return TRUE; | |
| } | |
| } else { | |
| return TRUE; | |
| } | |
| } | |
| } | |
| return FALSE; | |
| } | |
| /** | |
| According to a file guild, check a Fv file device path is valid. If it is invalid, | |
| try to return the valid device path. | |
| FV address maybe changes for memory layout adjust from time to time, use this function | |
| could promise the Fv file device path is right. | |
| @param DevicePath on input, the Fv file device path need to check on | |
| output, the updated valid Fv file device path | |
| @param FileGuid the Fv file guild | |
| @retval EFI_INVALID_PARAMETER the input DevicePath or FileGuid is invalid | |
| parameter | |
| @retval EFI_UNSUPPORTED the input DevicePath does not contain Fv file | |
| guild at all | |
| @retval EFI_ALREADY_STARTED the input DevicePath has pointed to Fv file, it is | |
| valid | |
| @retval EFI_SUCCESS has successfully updated the invalid DevicePath, | |
| and return the updated device path in DevicePath | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| BdsLibUpdateFvFileDevicePath ( | |
| IN OUT EFI_DEVICE_PATH_PROTOCOL ** DevicePath, | |
| IN EFI_GUID *FileGuid | |
| ) | |
| { | |
| EFI_DEVICE_PATH_PROTOCOL *TempDevicePath; | |
| EFI_DEVICE_PATH_PROTOCOL *LastDeviceNode; | |
| EFI_STATUS Status; | |
| EFI_GUID *GuidPoint; | |
| UINTN Index; | |
| UINTN FvHandleCount; | |
| EFI_HANDLE *FvHandleBuffer; | |
| EFI_FV_FILETYPE Type; | |
| UINTN Size; | |
| EFI_FV_FILE_ATTRIBUTES Attributes; | |
| UINT32 AuthenticationStatus; | |
| BOOLEAN FindFvFile; | |
| EFI_LOADED_IMAGE_PROTOCOL *LoadedImage; | |
| EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv; | |
| MEDIA_FW_VOL_FILEPATH_DEVICE_PATH FvFileNode; | |
| EFI_HANDLE FoundFvHandle; | |
| EFI_DEVICE_PATH_PROTOCOL *NewDevicePath; | |
| if ((DevicePath == NULL) || (*DevicePath == NULL)) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| if (FileGuid == NULL) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| // | |
| // Check whether the device path point to the default the input Fv file | |
| // | |
| TempDevicePath = *DevicePath; | |
| LastDeviceNode = TempDevicePath; | |
| while (!IsDevicePathEnd (TempDevicePath)) { | |
| LastDeviceNode = TempDevicePath; | |
| TempDevicePath = NextDevicePathNode (TempDevicePath); | |
| } | |
| GuidPoint = EfiGetNameGuidFromFwVolDevicePathNode ( | |
| (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *) LastDeviceNode | |
| ); | |
| if (GuidPoint == NULL) { | |
| // | |
| // if this option does not points to a Fv file, just return EFI_UNSUPPORTED | |
| // | |
| return EFI_UNSUPPORTED; | |
| } | |
| if (!CompareGuid (GuidPoint, FileGuid)) { | |
| // | |
| // If the Fv file is not the input file guid, just return EFI_UNSUPPORTED | |
| // | |
| return EFI_UNSUPPORTED; | |
| } | |
| // | |
| // Check whether the input Fv file device path is valid | |
| // | |
| TempDevicePath = *DevicePath; | |
| FoundFvHandle = NULL; | |
| Status = gBS->LocateDevicePath ( | |
| &gEfiFirmwareVolume2ProtocolGuid, | |
| &TempDevicePath, | |
| &FoundFvHandle | |
| ); | |
| if (!EFI_ERROR (Status)) { | |
| Status = gBS->HandleProtocol ( | |
| FoundFvHandle, | |
| &gEfiFirmwareVolume2ProtocolGuid, | |
| (VOID **) &Fv | |
| ); | |
| if (!EFI_ERROR (Status)) { | |
| // | |
| // Set FV ReadFile Buffer as NULL, only need to check whether input Fv file exist there | |
| // | |
| Status = Fv->ReadFile ( | |
| Fv, | |
| FileGuid, | |
| NULL, | |
| &Size, | |
| &Type, | |
| &Attributes, | |
| &AuthenticationStatus | |
| ); | |
| if (!EFI_ERROR (Status)) { | |
| return EFI_ALREADY_STARTED; | |
| } | |
| } | |
| } | |
| // | |
| // Look for the input wanted FV file in current FV | |
| // First, try to look for in Bds own FV. Bds and input wanted FV file usually are in the same FV | |
| // | |
| FindFvFile = FALSE; | |
| FoundFvHandle = NULL; | |
| Status = gBS->HandleProtocol ( | |
| gImageHandle, | |
| &gEfiLoadedImageProtocolGuid, | |
| (VOID **) &LoadedImage | |
| ); | |
| if (!EFI_ERROR (Status)) { | |
| Status = gBS->HandleProtocol ( | |
| LoadedImage->DeviceHandle, | |
| &gEfiFirmwareVolume2ProtocolGuid, | |
| (VOID **) &Fv | |
| ); | |
| if (!EFI_ERROR (Status)) { | |
| Status = Fv->ReadFile ( | |
| Fv, | |
| FileGuid, | |
| NULL, | |
| &Size, | |
| &Type, | |
| &Attributes, | |
| &AuthenticationStatus | |
| ); | |
| if (!EFI_ERROR (Status)) { | |
| FindFvFile = TRUE; | |
| FoundFvHandle = LoadedImage->DeviceHandle; | |
| } | |
| } | |
| } | |
| // | |
| // Second, if fail to find, try to enumerate all FV | |
| // | |
| if (!FindFvFile) { | |
| FvHandleBuffer = NULL; | |
| gBS->LocateHandleBuffer ( | |
| ByProtocol, | |
| &gEfiFirmwareVolume2ProtocolGuid, | |
| NULL, | |
| &FvHandleCount, | |
| &FvHandleBuffer | |
| ); | |
| for (Index = 0; Index < FvHandleCount; Index++) { | |
| gBS->HandleProtocol ( | |
| FvHandleBuffer[Index], | |
| &gEfiFirmwareVolume2ProtocolGuid, | |
| (VOID **) &Fv | |
| ); | |
| Status = Fv->ReadFile ( | |
| Fv, | |
| FileGuid, | |
| NULL, | |
| &Size, | |
| &Type, | |
| &Attributes, | |
| &AuthenticationStatus | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| // | |
| // Skip if input Fv file not in the FV | |
| // | |
| continue; | |
| } | |
| FindFvFile = TRUE; | |
| FoundFvHandle = FvHandleBuffer[Index]; | |
| break; | |
| } | |
| if (FvHandleBuffer != NULL) { | |
| FreePool (FvHandleBuffer); | |
| } | |
| } | |
| if (FindFvFile) { | |
| // | |
| // Build the shell device path | |
| // | |
| NewDevicePath = DevicePathFromHandle (FoundFvHandle); | |
| EfiInitializeFwVolDevicepathNode (&FvFileNode, FileGuid); | |
| NewDevicePath = AppendDevicePathNode (NewDevicePath, (EFI_DEVICE_PATH_PROTOCOL *) &FvFileNode); | |
| ASSERT (NewDevicePath != NULL); | |
| *DevicePath = NewDevicePath; | |
| return EFI_SUCCESS; | |
| } | |
| return EFI_NOT_FOUND; | |
| } |