/** @file This function deal with the legacy boot option, it create, delete and manage the legacy boot option, all legacy boot option is getting from the legacy BBS table. Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "InternalLegacyBm.h" #define LEGACY_BM_BOOT_DESCRIPTION_LENGTH 32 /** Initialize legacy boot manager library by call EfiBootManagerRegisterLegacyBootSupport function to export two function pointer. @param ImageHandle The image handle. @param SystemTable The system table. @retval EFI_SUCCESS The legacy boot manager library is initialized correctly. @return Other value if failed to initialize the legacy boot manager library. **/ EFI_STATUS EFIAPI LegacyBootManagerLibConstructor ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { EfiBootManagerRegisterLegacyBootSupport ( LegacyBmRefreshAllBootOption, LegacyBmBoot ); return EFI_SUCCESS; } /** Get the device type from the input legacy device path. @param DevicePath The legacy device path. @retval The legacy device type. **/ UINT16 LegacyBmDeviceType ( EFI_DEVICE_PATH_PROTOCOL *DevicePath ) { ASSERT ((DevicePathType (DevicePath) == BBS_DEVICE_PATH) && (DevicePathSubType (DevicePath) == BBS_BBS_DP)); return ((BBS_BBS_DEVICE_PATH *) DevicePath)->DeviceType; } /** Validate the BbsEntry base on the Boot Priority info in the BbsEntry. @param BbsEntry The input bbs entry info. @retval TRUE The BbsEntry is valid. @retval FALSE The BbsEntry is invalid. **/ BOOLEAN LegacyBmValidBbsEntry ( IN BBS_TABLE *BbsEntry ) { switch (BbsEntry->BootPriority) { case BBS_IGNORE_ENTRY: case BBS_DO_NOT_BOOT_FROM: case BBS_LOWEST_PRIORITY: return FALSE; default: return TRUE; } } /** Build Legacy Device Name String according. @param CurBBSEntry BBS Table. @param Index Index. @param BufSize The buffer size. @param BootString The output string. **/ VOID LegacyBmBuildLegacyDevNameString ( IN BBS_TABLE *CurBBSEntry, IN UINTN Index, IN UINTN BufSize, OUT CHAR16 *BootString ) { CHAR16 *Fmt; CHAR16 *Type; CHAR8 *StringDesc; CHAR8 StringBufferA[LEGACY_BM_BOOT_DESCRIPTION_LENGTH + 1]; CHAR16 StringBufferU[LEGACY_BM_BOOT_DESCRIPTION_LENGTH + 1]; 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 = (CHAR8 *) (((UINTN) CurBBSEntry->DescStringSegment << 4) + CurBBSEntry->DescStringOffset); if (NULL != StringDesc) { // // Only get first 32 characters, this is suggested by BBS spec // CopyMem (StringBufferA, StringDesc, LEGACY_BM_BOOT_DESCRIPTION_LENGTH); StringBufferA[LEGACY_BM_BOOT_DESCRIPTION_LENGTH] = 0; AsciiStrToUnicodeStrS (StringBufferA, StringBufferU, ARRAY_SIZE (StringBufferU)); Fmt = L"%s"; Type = StringBufferU; } // // 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); } } /** Get the Bbs index for the input boot option. @param BootOption The input boot option info. @param BbsTable The input Bbs table. @param BbsCount The input total bbs entry number. @param BbsIndexUsed The array shows how many BBS table indexs have been used. @retval The index for the input boot option. **/ UINT16 LegacyBmFuzzyMatch ( EFI_BOOT_MANAGER_LOAD_OPTION *BootOption, BBS_TABLE *BbsTable, UINT16 BbsCount, BOOLEAN *BbsIndexUsed ) { UINT16 Index; LEGACY_BM_BOOT_OPTION_BBS_DATA *BbsData; CHAR16 Description[LEGACY_BM_BOOT_DESCRIPTION_LENGTH + 1]; BbsData = (LEGACY_BM_BOOT_OPTION_BBS_DATA *) BootOption->OptionalData; // // Directly check the BBS index stored in BootOption // if ((BbsData->BbsIndex < BbsCount) && (LegacyBmDeviceType (BootOption->FilePath) == BbsTable[BbsData->BbsIndex].DeviceType)) { LegacyBmBuildLegacyDevNameString ( &BbsTable[BbsData->BbsIndex], BbsData->BbsIndex, sizeof (Description), Description ); if ((StrCmp (Description, BootOption->Description) == 0) && !BbsIndexUsed[BbsData->BbsIndex]) { // // If devices with the same description string are connected, // the BbsIndex of the first device is returned for the other device also. // So, check if the BbsIndex is already being used, before assigning the BbsIndex. // BbsIndexUsed[BbsData->BbsIndex] = TRUE; return BbsData->BbsIndex; } } // // BBS table could be changed (entry removed/moved) // find the correct BBS index // for (Index = 0; Index < BbsCount; Index++) { if (!LegacyBmValidBbsEntry (&BbsTable[Index]) || (BbsTable[Index].DeviceType != LegacyBmDeviceType (BootOption->FilePath))) { continue; } LegacyBmBuildLegacyDevNameString ( &BbsTable[Index], Index, sizeof (Description), Description ); if ((StrCmp (Description, BootOption->Description) == 0) && !BbsIndexUsed[Index]) { // // If devices with the same description string are connected, // the BbsIndex of the first device is assigned for the other device also. // So, check if the BbsIndex is already being used, before assigning the corrected BbsIndex. // break; } } // // Add the corrected BbsIndex in the UsedBbsIndex Buffer // if (Index != BbsCount) { BbsIndexUsed[Index] = TRUE; } return Index; } /** Update legacy device order base on the input info. @param LegacyDevOrder Legacy device order data buffer. @param LegacyDevOrderSize Legacy device order data buffer size. @param DeviceType Device type which need to check. @param OldBbsIndex Old Bds Index. @param NewBbsIndex New Bds Index, if it is -1,means remove this option. **/ VOID LegacyBmUpdateBbsIndex ( LEGACY_DEV_ORDER_ENTRY *LegacyDevOrder, UINTN *LegacyDevOrderSize, UINT16 DeviceType, UINT16 OldBbsIndex, UINT16 NewBbsIndex // Delete entry if -1 ) { LEGACY_DEV_ORDER_ENTRY *Entry; UINTN Index; ASSERT (((LegacyDevOrder == NULL) && (*LegacyDevOrderSize == 0)) || ((LegacyDevOrder != NULL) && (*LegacyDevOrderSize != 0)) ); for (Entry = LegacyDevOrder; Entry < (LEGACY_DEV_ORDER_ENTRY *) ((UINT8 *) LegacyDevOrder + *LegacyDevOrderSize); Entry = (LEGACY_DEV_ORDER_ENTRY *) ((UINTN) Entry + sizeof (BBS_TYPE) + Entry->Length) ) { if (Entry->BbsType == DeviceType) { for (Index = 0; Index < Entry->Length / sizeof (UINT16) - 1; Index++) { if (Entry->Data[Index] == OldBbsIndex) { if (NewBbsIndex == (UINT16) -1) { // // Delete the old entry // CopyMem ( &Entry->Data[Index], &Entry->Data[Index + 1], (UINT8 *) LegacyDevOrder + *LegacyDevOrderSize - (UINT8 *) &Entry->Data[Index + 1] ); Entry->Length -= sizeof (UINT16); *LegacyDevOrderSize -= sizeof(UINT16); } else { Entry->Data[Index] = NewBbsIndex; } break; } } break; } } } /** Delete all the legacy boot options. @retval EFI_SUCCESS All legacy boot options are deleted. **/ EFI_STATUS LegacyBmDeleteAllBootOptions ( VOID ) { EFI_STATUS Status; UINTN Index; EFI_BOOT_MANAGER_LOAD_OPTION *BootOption; UINTN BootOptionCount; BootOption = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot); for (Index = 0; Index < BootOptionCount; Index++) { if ((DevicePathType (BootOption[Index].FilePath) == BBS_DEVICE_PATH) && (DevicePathSubType (BootOption[Index].FilePath) == BBS_BBS_DP)) { Status = EfiBootManagerDeleteLoadOptionVariable (BootOption[Index].OptionNumber, BootOption[Index].OptionType); // // Deleting variable with current variable implementation shouldn't fail. // ASSERT_EFI_ERROR (Status); } } Status = gRT->SetVariable ( VAR_LEGACY_DEV_ORDER, &gEfiLegacyDevOrderVariableGuid, 0, 0, NULL ); // // Deleting variable with current variable implementation shouldn't fail. // ASSERT (Status == EFI_SUCCESS || Status == EFI_NOT_FOUND); return EFI_SUCCESS; } /** Delete all the invalid legacy boot options. @retval EFI_SUCCESS All invalid legacy boot options are deleted. @retval EFI_OUT_OF_RESOURCES Fail to allocate necessary memory. @retval EFI_NOT_FOUND Fail to retrieve variable of boot order. **/ EFI_STATUS LegacyBmDeleteAllInvalidBootOptions ( VOID ) { EFI_STATUS Status; UINT16 HddCount; UINT16 BbsCount; HDD_INFO *HddInfo; BBS_TABLE *BbsTable; UINT16 BbsIndex; EFI_LEGACY_BIOS_PROTOCOL *LegacyBios; UINTN Index; EFI_BOOT_MANAGER_LOAD_OPTION *BootOption; UINTN BootOptionCount; LEGACY_DEV_ORDER_ENTRY *LegacyDevOrder; UINTN LegacyDevOrderSize; BOOLEAN *BbsIndexUsed; HddCount = 0; BbsCount = 0; HddInfo = NULL; BbsTable = NULL; Status = gBS->LocateProtocol (&gEfiLegacyBiosProtocolGuid, NULL, (VOID **) &LegacyBios); if (EFI_ERROR (Status)) { return Status; } Status = LegacyBios->GetBbsInfo ( LegacyBios, &HddCount, &HddInfo, &BbsCount, &BbsTable ); if (EFI_ERROR (Status)) { return Status; } GetVariable2 (VAR_LEGACY_DEV_ORDER, &gEfiLegacyDevOrderVariableGuid, (VOID **) &LegacyDevOrder, &LegacyDevOrderSize); BootOption = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot); BbsIndexUsed = AllocateZeroPool (BbsCount * sizeof (BOOLEAN)); ASSERT (BbsIndexUsed != NULL); for (Index = 0; Index < BootOptionCount; Index++) { // // Skip non legacy boot option // if ((DevicePathType (BootOption[Index].FilePath) != BBS_DEVICE_PATH) || (DevicePathSubType (BootOption[Index].FilePath) != BBS_BBS_DP)) { continue; } BbsIndex = LegacyBmFuzzyMatch (&BootOption[Index], BbsTable, BbsCount, BbsIndexUsed); if (BbsIndex == BbsCount) { DEBUG ((DEBUG_INFO, "[LegacyBds] Delete Boot Option Boot%04x: %s\n", (UINTN) BootOption[Index].OptionNumber, BootOption[Index].Description)); // // Delete entry from LegacyDevOrder // LegacyBmUpdateBbsIndex ( LegacyDevOrder, &LegacyDevOrderSize, LegacyBmDeviceType (BootOption[Index].FilePath), ((LEGACY_BM_BOOT_OPTION_BBS_DATA *) BootOption[Index].OptionalData)->BbsIndex, (UINT16) -1 ); EfiBootManagerDeleteLoadOptionVariable (BootOption[Index].OptionNumber, BootOption[Index].OptionType); } else { if (((LEGACY_BM_BOOT_OPTION_BBS_DATA *) BootOption[Index].OptionalData)->BbsIndex != BbsIndex) { DEBUG ((DEBUG_INFO, "[LegacyBds] Update Boot Option Boot%04x: %s Bbs0x%04x->Bbs0x%04x\n", (UINTN) BootOption[Index].OptionNumber, BootOption[Index].Description, (UINTN) ((LEGACY_BM_BOOT_OPTION_BBS_DATA *) BootOption[Index].OptionalData)->BbsIndex, (UINTN) BbsIndex)); // // Update the BBS index in LegacyDevOrder // LegacyBmUpdateBbsIndex ( LegacyDevOrder, &LegacyDevOrderSize, LegacyBmDeviceType (BootOption[Index].FilePath), ((LEGACY_BM_BOOT_OPTION_BBS_DATA *) BootOption[Index].OptionalData)->BbsIndex, BbsIndex ); // // Update the OptionalData in the Boot#### variable // ((LEGACY_BM_BOOT_OPTION_BBS_DATA *) BootOption[Index].OptionalData)->BbsIndex = BbsIndex; EfiBootManagerLoadOptionToVariable (&BootOption[Index]); } } } EfiBootManagerFreeLoadOptions (BootOption, BootOptionCount); if (LegacyDevOrder != NULL) { Status = gRT->SetVariable ( VAR_LEGACY_DEV_ORDER, &gEfiLegacyDevOrderVariableGuid, EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE, LegacyDevOrderSize, LegacyDevOrder ); // // Shrink variable with current variable implementation shouldn't fail. // ASSERT_EFI_ERROR (Status); FreePool (LegacyDevOrder); } FreePool(BbsIndexUsed); return Status; } /** Create legacy boot option. @param BootOption Pointer to the boot option which will be crated. @param BbsEntry The input bbs entry info. @param BbsIndex The BBS index. @retval EFI_SUCCESS Create legacy boot option successfully. @retval EFI_INVALID_PARAMETER Invalid input parameter. **/ EFI_STATUS LegacyBmCreateLegacyBootOption ( IN OUT EFI_BOOT_MANAGER_LOAD_OPTION *BootOption, IN BBS_TABLE *BbsEntry, IN UINT16 BbsIndex ) { EFI_STATUS Status; EFI_DEVICE_PATH_PROTOCOL *DevicePath; CHAR16 Description[LEGACY_BM_BOOT_DESCRIPTION_LENGTH + 1]; CHAR8 HelpString[LEGACY_BM_BOOT_DESCRIPTION_LENGTH + 1]; UINTN StringLen; LEGACY_BM_BOOT_OPTION_BBS_DATA *OptionalData; BBS_BBS_DEVICE_PATH *BbsNode; if ((BootOption == NULL) || (BbsEntry == NULL)) { return EFI_INVALID_PARAMETER; } LegacyBmBuildLegacyDevNameString (BbsEntry, BbsIndex, sizeof (Description), Description); // // Create the BBS device path with description string // UnicodeStrToAsciiStrS (Description, HelpString, sizeof (HelpString)); StringLen = AsciiStrLen (HelpString); DevicePath = AllocatePool (sizeof (BBS_BBS_DEVICE_PATH) + StringLen + END_DEVICE_PATH_LENGTH); ASSERT (DevicePath != NULL); BbsNode = (BBS_BBS_DEVICE_PATH *) DevicePath; SetDevicePathNodeLength (BbsNode, sizeof (BBS_BBS_DEVICE_PATH) + StringLen); BbsNode->Header.Type = BBS_DEVICE_PATH; BbsNode->Header.SubType = BBS_BBS_DP; BbsNode->DeviceType = BbsEntry->DeviceType; CopyMem (&BbsNode->StatusFlag, &BbsEntry->StatusFlags, sizeof (BBS_STATUS_FLAGS)); CopyMem (BbsNode->String, HelpString, StringLen + 1); SetDevicePathEndNode (NextDevicePathNode (BbsNode)); // // Create the OptionalData // OptionalData = AllocatePool (sizeof (LEGACY_BM_BOOT_OPTION_BBS_DATA)); ASSERT (OptionalData != NULL); OptionalData->BbsIndex = BbsIndex; // // Create the BootOption // Status = EfiBootManagerInitializeLoadOption ( BootOption, LoadOptionNumberUnassigned, LoadOptionTypeBoot, LOAD_OPTION_ACTIVE, Description, DevicePath, (UINT8 *) OptionalData, sizeof (LEGACY_BM_BOOT_OPTION_BBS_DATA) ); FreePool (DevicePath); FreePool (OptionalData); 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 * LegacyBmFillDevOrderBuf ( IN BBS_TABLE *BbsTable, IN BBS_TYPE BbsType, IN UINTN BbsCount, OUT UINT16 *Buf ) { UINTN Index; for (Index = 0; Index < BbsCount; Index++) { if (!LegacyBmValidBbsEntry (&BbsTable[Index])) { 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_SUCCESS The buffer is created and the EFI variable named VAR_LEGACY_DEV_ORDER and EfiLegacyDevOrderGuid is set correctly. @retval EFI_OUT_OF_RESOURCES Memory 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 LegacyBmCreateDevOrder ( 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 (!LegacyBmValidBbsEntry (&BbsTable[Index])) { 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 *) LegacyBmFillDevOrderBuf (BbsTable, BBS_FLOPPY, BbsCount, DevOrderPtr->Data); DevOrderPtr->BbsType = BBS_HARDDISK; DevOrderPtr->Length = (UINT16) (sizeof (UINT16) + HDCount * sizeof (UINT16)); DevOrderPtr = (LEGACY_DEV_ORDER_ENTRY *) LegacyBmFillDevOrderBuf (BbsTable, BBS_HARDDISK, BbsCount, DevOrderPtr->Data); DevOrderPtr->BbsType = BBS_CDROM; DevOrderPtr->Length = (UINT16) (sizeof (UINT16) + CDCount * sizeof (UINT16)); DevOrderPtr = (LEGACY_DEV_ORDER_ENTRY *) LegacyBmFillDevOrderBuf (BbsTable, BBS_CDROM, BbsCount, DevOrderPtr->Data); DevOrderPtr->BbsType = BBS_EMBED_NETWORK; DevOrderPtr->Length = (UINT16) (sizeof (UINT16) + NETCount * sizeof (UINT16)); DevOrderPtr = (LEGACY_DEV_ORDER_ENTRY *) LegacyBmFillDevOrderBuf (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 *) LegacyBmFillDevOrderBuf (BbsTable, BBS_BEV_DEVICE, BbsCount, DevOrderPtr->Data); ASSERT (TotalSize == ((UINTN) DevOrderPtr - (UINTN) DevOrder)); // // Save device order for legacy boot device to variable. // Status = gRT->SetVariable ( VAR_LEGACY_DEV_ORDER, &gEfiLegacyDevOrderVariableGuid, EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE, TotalSize, DevOrder ); FreePool (DevOrder); return Status; } /** Add the legacy boot devices from BBS table into the legacy device boot order. @retval EFI_SUCCESS The boot devices are added successfully. @retval EFI_NOT_FOUND The legacy boot devices are not found. @retval EFI_OUT_OF_RESOURCES Memory 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 LegacyBmUpdateDevOrder ( VOID ) { LEGACY_DEV_ORDER_ENTRY *DevOrder; LEGACY_DEV_ORDER_ENTRY *NewDevOrder; LEGACY_DEV_ORDER_ENTRY *Ptr; LEGACY_DEV_ORDER_ENTRY *NewPtr; 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; } GetVariable2 (VAR_LEGACY_DEV_ORDER, &gEfiLegacyDevOrderVariableGuid, (VOID **) &DevOrder, NULL); if (NULL == DevOrder) { return LegacyBmCreateDevOrder (LocalBbsTable, BbsCount); } // // First we figure out how many boot devices with same device type respectively // for (Index = 0; Index < BbsCount; Index++) { if (!LegacyBmValidBbsEntry (&LocalBbsTable[Index])) { 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 (!LegacyBmValidBbsEntry (&LocalBbsTable[Ptr->Data[Index] & 0xFF]) || 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 (!LegacyBmValidBbsEntry (&LocalBbsTable[Ptr->Data[Index] & 0xFF]) || 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 (!LegacyBmValidBbsEntry (&LocalBbsTable[Ptr->Data[Index] & 0xFF]) || 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 (!LegacyBmValidBbsEntry (&LocalBbsTable[Ptr->Data[Index] & 0xFF]) || 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 (!LegacyBmValidBbsEntry (&LocalBbsTable[Ptr->Data[Index] & 0xFF]) || 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 (!LegacyBmValidBbsEntry (&LocalBbsTable[Index])) { 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 priority 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 LegacyBmSetPriorityForSameTypeDev ( 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; GetVariable2 (VAR_LEGACY_DEV_ORDER, &gEfiLegacyDevOrderVariableGuid, (VOID **) &DevOrder, &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) { // // In case the BBS entry isn't valid because devices were plugged or removed. // if (!LegacyBmValidBbsEntry (&LocalBbsTable[BbsIndex]) || (LocalBbsTable[BbsIndex].DeviceType != DeviceType)) { FreePool (DevOrder); return EFI_NOT_FOUND; } 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 LegacyBmPrintBbsTable ( IN BBS_TABLE *LocalBbsTable, IN UINT16 BbsCount ) { UINT16 Index; DEBUG ((DEBUG_INFO, "\n")); DEBUG ((DEBUG_INFO, " NO Prio bb/dd/ff cl/sc Type Stat segm:offs\n")); DEBUG ((DEBUG_INFO, "=============================================\n")); for (Index = 0; Index < BbsCount; Index++) { if (!LegacyBmValidBbsEntry (&LocalBbsTable[Index])) { continue; } DEBUG ( (DEBUG_INFO, " %02x: %04x %02x/%02x/%02x %02x/%02x %04x %04x %04x:%04x\n", (UINTN) Index, (UINTN) LocalBbsTable[Index].BootPriority, (UINTN) LocalBbsTable[Index].Bus, (UINTN) LocalBbsTable[Index].Device, (UINTN) LocalBbsTable[Index].Function, (UINTN) LocalBbsTable[Index].Class, (UINTN) LocalBbsTable[Index].SubClass, (UINTN) LocalBbsTable[Index].DeviceType, (UINTN) * (UINT16 *) &LocalBbsTable[Index].StatusFlags, (UINTN) LocalBbsTable[Index].BootHandlerSegment, (UINTN) LocalBbsTable[Index].BootHandlerOffset, (UINTN) ((LocalBbsTable[Index].MfgStringSegment << 4) + LocalBbsTable[Index].MfgStringOffset), (UINTN) ((LocalBbsTable[Index].DescStringSegment << 4) + LocalBbsTable[Index].DescStringOffset)) ); } DEBUG ((DEBUG_INFO, "\n")); } /** Set the boot priority for BBS entries based on boot option entry and boot order. @param BootOption 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 LegacyBmRefreshBbsTableForBoot ( IN EFI_BOOT_MANAGER_LOAD_OPTION *BootOption ) { 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 *DeviceType; UINTN DeviceTypeCount; UINTN DeviceTypeIndex; EFI_BOOT_MANAGER_LOAD_OPTION *Option; UINTN OptionCount; HddCount = 0; BbsCount = 0; LocalHddInfo = NULL; LocalBbsTable = NULL; DevType = BBS_UNKNOWN; 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; } // // 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 (LegacyBmValidBbsEntry (&LocalBbsTable[Index])) { LocalBbsTable[Index].BootPriority = BBS_UNPRIORITIZED_ENTRY; } } // // boot priority always starts at 0 // Priority = 0; if ((DevicePathType (BootOption->FilePath) == BBS_DEVICE_PATH) && (DevicePathSubType (BootOption->FilePath) == BBS_BBS_DP)) { // // If BootOption stands for a legacy boot option, we prioritize the devices with the same type first. // DevType = LegacyBmDeviceType (BootOption->FilePath); BbsIndex = ((LEGACY_BM_BOOT_OPTION_BBS_DATA *) BootOption->OptionalData)->BbsIndex; Status = LegacyBmSetPriorityForSameTypeDev ( 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 // Option = EfiBootManagerGetLoadOptions (&OptionCount, LoadOptionTypeBoot); DeviceType = AllocatePool (sizeof (UINT16) * OptionCount); ASSERT (DeviceType != NULL); DeviceType[0] = DevType; DeviceTypeCount = 1; for (Index = 0; Index < OptionCount; Index++) { if ((DevicePathType (Option[Index].FilePath) != BBS_DEVICE_PATH) || (DevicePathSubType (Option[Index].FilePath) != BBS_BBS_DP)) { continue; } DevType = LegacyBmDeviceType (Option[Index].FilePath); 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 // continue; } DeviceType[DeviceTypeCount] = DevType; DeviceTypeCount++; Status = LegacyBmSetPriorityForSameTypeDev ( DevType, (UINTN) -1, LocalBbsTable, &Priority ); } EfiBootManagerFreeLoadOptions (Option, OptionCount); DEBUG_CODE_BEGIN(); LegacyBmPrintBbsTable (LocalBbsTable, BbsCount); DEBUG_CODE_END(); return Status; } /** Boot the legacy system with the boot option. @param BootOption The legacy boot option which have BBS device path On return, BootOption->Status contains the boot status. EFI_UNSUPPORTED There is no legacybios protocol, do not support legacy boot. EFI_STATUS The status of LegacyBios->LegacyBoot (). **/ VOID EFIAPI LegacyBmBoot ( IN EFI_BOOT_MANAGER_LOAD_OPTION *BootOption ) { EFI_STATUS Status; EFI_LEGACY_BIOS_PROTOCOL *LegacyBios; Status = gBS->LocateProtocol (&gEfiLegacyBiosProtocolGuid, NULL, (VOID **) &LegacyBios); if (EFI_ERROR (Status)) { // // If no LegacyBios protocol we do not support legacy boot // BootOption->Status = EFI_UNSUPPORTED; return; } // // Notes: if we separate the int 19, then we don't need to refresh BBS // Status = LegacyBmRefreshBbsTableForBoot (BootOption); if (EFI_ERROR (Status)) { BootOption->Status = Status; return; } BootOption->Status = LegacyBios->LegacyBoot ( LegacyBios, (BBS_BBS_DEVICE_PATH *) BootOption->FilePath, BootOption->OptionalDataSize, BootOption->OptionalData ); } /** This function enumerates all the legacy boot options. @param BootOptionCount Return the legacy boot option count. @retval Pointer to the legacy boot option buffer. **/ EFI_BOOT_MANAGER_LOAD_OPTION * LegacyBmEnumerateAllBootOptions ( UINTN *BootOptionCount ) { EFI_STATUS Status; UINT16 HddCount; UINT16 BbsCount; HDD_INFO *HddInfo; BBS_TABLE *BbsTable; EFI_LEGACY_BIOS_PROTOCOL *LegacyBios; UINT16 Index; EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions; ASSERT (BootOptionCount != NULL); BootOptions = NULL; *BootOptionCount = 0; BbsCount = 0; Status = gBS->LocateProtocol (&gEfiLegacyBiosProtocolGuid, NULL, (VOID **) &LegacyBios); if (EFI_ERROR (Status)) { return NULL; } Status = LegacyBios->GetBbsInfo ( LegacyBios, &HddCount, &HddInfo, &BbsCount, &BbsTable ); if (EFI_ERROR (Status)) { return NULL; } for (Index = 0; Index < BbsCount; Index++) { if (!LegacyBmValidBbsEntry (&BbsTable[Index])) { continue; } BootOptions = ReallocatePool ( sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount), sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount + 1), BootOptions ); ASSERT (BootOptions != NULL); Status = LegacyBmCreateLegacyBootOption (&BootOptions[(*BootOptionCount)++], &BbsTable[Index], Index); ASSERT_EFI_ERROR (Status); } return BootOptions; } /** Return the index of the boot option in the boot option array. The function compares the Description, FilePath, OptionalData. @param Key The input boot option which is compared with. @param Array The input boot option array. @param Count The count of the input boot options. @retval The index of the input boot option in the array. **/ INTN LegacyBmFindBootOption ( IN CONST EFI_BOOT_MANAGER_LOAD_OPTION *Key, IN CONST EFI_BOOT_MANAGER_LOAD_OPTION *Array, IN UINTN Count ) { UINTN Index; for (Index = 0; Index < Count; Index++) { if ((StrCmp (Key->Description, Array[Index].Description) == 0) && (CompareMem (Key->FilePath, Array[Index].FilePath, GetDevicePathSize (Key->FilePath)) == 0) && (Key->OptionalDataSize == Array[Index].OptionalDataSize) && (CompareMem (Key->OptionalData, Array[Index].OptionalData, Key->OptionalDataSize) == 0)) { return (INTN) Index; } } return -1; } /** Refresh all legacy boot options. **/ VOID EFIAPI LegacyBmRefreshAllBootOption ( VOID ) { EFI_STATUS Status; EFI_LEGACY_BIOS_PROTOCOL *LegacyBios; UINTN RootBridgeHandleCount; EFI_HANDLE *RootBridgeHandleBuffer; UINTN HandleCount; EFI_HANDLE *HandleBuffer; UINTN RootBridgeIndex; UINTN Index; UINTN Flags; EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions; UINTN BootOptionCount; EFI_BOOT_MANAGER_LOAD_OPTION *ExistingBootOptions; UINTN ExistingBootOptionCount; Status = gBS->LocateProtocol (&gEfiLegacyBiosProtocolGuid, NULL, (VOID **) &LegacyBios); if (EFI_ERROR (Status)) { LegacyBmDeleteAllBootOptions (); return; } PERF_START (NULL, "LegacyBootOptionEnum", "BDS", 0); // // Before enumerating the legacy boot option, we need to dispatch all the legacy option roms // to ensure the GetBbsInfo() counts all the legacy devices. // gBS->LocateHandleBuffer ( ByProtocol, &gEfiPciRootBridgeIoProtocolGuid, NULL, &RootBridgeHandleCount, &RootBridgeHandleBuffer ); for (RootBridgeIndex = 0; RootBridgeIndex < RootBridgeHandleCount; RootBridgeIndex++) { gBS->ConnectController (RootBridgeHandleBuffer[RootBridgeIndex], NULL, NULL, FALSE); gBS->LocateHandleBuffer ( ByProtocol, &gEfiPciIoProtocolGuid, NULL, &HandleCount, &HandleBuffer ); for (Index = 0; Index < HandleCount; Index++) { // // Start the thunk driver so that the legacy option rom gets dispatched. // Note: We don't directly call InstallPciRom because some thunk drivers // (e.g. BlockIo thunk driver) depend on the immediate result after dispatching // Status = LegacyBios->CheckPciRom ( LegacyBios, HandleBuffer[Index], NULL, NULL, &Flags ); if (!EFI_ERROR (Status)) { gBS->ConnectController (HandleBuffer[Index], NULL, NULL, FALSE); } } } // // Same algorithm pattern as the EfiBootManagerRefreshAllBootOption // Firstly delete the invalid legacy boot options, // then enumerate and save the newly appeared legacy boot options // the last step is legacy boot option special action to refresh the LegacyDevOrder variable // LegacyBmDeleteAllInvalidBootOptions (); ExistingBootOptions = EfiBootManagerGetLoadOptions (&ExistingBootOptionCount, LoadOptionTypeBoot); BootOptions = LegacyBmEnumerateAllBootOptions (&BootOptionCount); for (Index = 0; Index < BootOptionCount; Index++) { if (LegacyBmFindBootOption (&BootOptions[Index], ExistingBootOptions, ExistingBootOptionCount) == -1) { Status = EfiBootManagerAddLoadOptionVariable (&BootOptions[Index], (UINTN) -1); DEBUG (( DEBUG_INFO, "[LegacyBds] New Boot Option: Boot%04x Bbs0x%04x %s %r\n", (UINTN) BootOptions[Index].OptionNumber, (UINTN) ((LEGACY_BM_BOOT_OPTION_BBS_DATA *) BootOptions[Index].OptionalData)->BbsIndex, BootOptions[Index].Description, Status )); // // Continue upon failure to add boot option. // } } EfiBootManagerFreeLoadOptions (ExistingBootOptions, ExistingBootOptionCount); EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount); // // Failure to create LegacyDevOrder variable only impacts the boot order. // LegacyBmUpdateDevOrder (); PERF_END (NULL, "LegacyBootOptionEnum", "BDS", 0); }