/** @file Library functions which relate with boot option description. Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.
(C) Copyright 2015 Hewlett Packard Enterprise Development LP
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "InternalBm.h" #define VENDOR_IDENTIFICATION_OFFSET 3 #define VENDOR_IDENTIFICATION_LENGTH 8 #define PRODUCT_IDENTIFICATION_OFFSET 11 #define PRODUCT_IDENTIFICATION_LENGTH 16 CONST UINT16 mBmUsbLangId = 0x0409; // English CHAR16 mBmUefiPrefix[] = L"UEFI "; LIST_ENTRY mPlatformBootDescriptionHandlers = INITIALIZE_LIST_HEAD_VARIABLE (mPlatformBootDescriptionHandlers); /** For a bootable Device path, return its boot type. @param DevicePath The bootable device Path to check @retval AcpiFloppyBoot If given device path contains ACPI_DEVICE_PATH type device path node which HID is floppy device. @retval MessageAtapiBoot 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 MessageSataBoot If given device path contains MESSAGING_DEVICE_PATH type device path node and its last device path node's subtype is MSG_SATA_DP. @retval MessageScsiBoot 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 MessageUsbBoot 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 BmMiscBoot If tiven device path doesn't match the above condition. **/ BM_BOOT_TYPE BmDevicePathType ( IN EFI_DEVICE_PATH_PROTOCOL *DevicePath ) { EFI_DEVICE_PATH_PROTOCOL *Node; EFI_DEVICE_PATH_PROTOCOL *NextNode; ASSERT (DevicePath != NULL); for (Node = DevicePath; !IsDevicePathEndType (Node); Node = NextDevicePathNode (Node)) { switch (DevicePathType (Node)) { case ACPI_DEVICE_PATH: if (EISA_ID_TO_NUM (((ACPI_HID_DEVICE_PATH *) Node)->HID) == 0x0604) { return BmAcpiFloppyBoot; } break; case HARDWARE_DEVICE_PATH: if (DevicePathSubType (Node) == HW_CONTROLLER_DP) { return BmHardwareDeviceBoot; } break; case MESSAGING_DEVICE_PATH: // // Skip LUN device node // NextNode = Node; do { NextNode = NextDevicePathNode (NextNode); } while ( (DevicePathType (NextNode) == MESSAGING_DEVICE_PATH) && (DevicePathSubType(NextNode) == MSG_DEVICE_LOGICAL_UNIT_DP) ); // // If the device path not only point to driver device, it is not a messaging device path, // if (!IsDevicePathEndType (NextNode)) { continue; } switch (DevicePathSubType (Node)) { case MSG_ATAPI_DP: return BmMessageAtapiBoot; break; case MSG_SATA_DP: return BmMessageSataBoot; break; case MSG_USB_DP: return BmMessageUsbBoot; break; case MSG_SCSI_DP: return BmMessageScsiBoot; break; } } } return BmMiscBoot; } /** Eliminate the extra spaces in the Str to one space. @param Str Input string info. **/ VOID BmEliminateExtraSpaces ( IN CHAR16 *Str ) { UINTN Index; UINTN ActualIndex; for (Index = 0, ActualIndex = 0; Str[Index] != L'\0'; Index++) { if ((Str[Index] != L' ') || ((ActualIndex > 0) && (Str[ActualIndex - 1] != L' '))) { Str[ActualIndex++] = Str[Index]; } } Str[ActualIndex] = L'\0'; } /** Try to get the controller's ATA/ATAPI description. @param Handle Controller handle. @return The description string. **/ CHAR16 * BmGetDescriptionFromDiskInfo ( IN EFI_HANDLE Handle ) { UINTN Index; EFI_STATUS Status; EFI_DISK_INFO_PROTOCOL *DiskInfo; UINT32 BufferSize; EFI_ATAPI_IDENTIFY_DATA IdentifyData; EFI_SCSI_INQUIRY_DATA InquiryData; CHAR16 *Description; UINTN Length; CONST UINTN ModelNameLength = 40; CONST UINTN SerialNumberLength = 20; CHAR8 *StrPtr; UINT8 Temp; EFI_DEVICE_PATH_PROTOCOL *DevicePath; Description = NULL; Status = gBS->HandleProtocol ( Handle, &gEfiDiskInfoProtocolGuid, (VOID **) &DiskInfo ); if (EFI_ERROR(Status)) { return NULL; } if (CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoAhciInterfaceGuid) || CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoIdeInterfaceGuid)) { BufferSize = sizeof (EFI_ATAPI_IDENTIFY_DATA); Status = DiskInfo->Identify ( DiskInfo, &IdentifyData, &BufferSize ); if (!EFI_ERROR(Status)) { Description = AllocateZeroPool((ModelNameLength + SerialNumberLength + 2) * sizeof (CHAR16)); ASSERT (Description != NULL); for (Index = 0; Index + 1 < ModelNameLength; Index += 2) { Description[Index] = (CHAR16) IdentifyData.ModelName[Index + 1]; Description[Index + 1] = (CHAR16) IdentifyData.ModelName[Index]; } Length = Index; Description[Length++] = L' '; for (Index = 0; Index + 1 < SerialNumberLength; Index += 2) { Description[Length + Index] = (CHAR16) IdentifyData.SerialNo[Index + 1]; Description[Length + Index + 1] = (CHAR16) IdentifyData.SerialNo[Index]; } Length += Index; Description[Length++] = L'\0'; ASSERT (Length == ModelNameLength + SerialNumberLength + 2); BmEliminateExtraSpaces (Description); } } else if (CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoScsiInterfaceGuid)) { BufferSize = sizeof (EFI_SCSI_INQUIRY_DATA); Status = DiskInfo->Inquiry ( DiskInfo, &InquiryData, &BufferSize ); if (!EFI_ERROR(Status)) { Description = AllocateZeroPool((VENDOR_IDENTIFICATION_LENGTH + PRODUCT_IDENTIFICATION_LENGTH + 2) * sizeof (CHAR16)); ASSERT (Description != NULL); // // Per SCSI spec, EFI_SCSI_INQUIRY_DATA.Reserved_5_95[3 - 10] save the Verdor identification // EFI_SCSI_INQUIRY_DATA.Reserved_5_95[11 - 26] save the product identification, // Here combine the vendor identification and product identification to the description. // StrPtr = (CHAR8 *) (&InquiryData.Reserved_5_95[VENDOR_IDENTIFICATION_OFFSET]); Temp = StrPtr[VENDOR_IDENTIFICATION_LENGTH]; StrPtr[VENDOR_IDENTIFICATION_LENGTH] = '\0'; AsciiStrToUnicodeStrS (StrPtr, Description, VENDOR_IDENTIFICATION_LENGTH + 1); StrPtr[VENDOR_IDENTIFICATION_LENGTH] = Temp; // // Add one space at the middle of vendor information and product information. // Description[VENDOR_IDENTIFICATION_LENGTH] = L' '; StrPtr = (CHAR8 *) (&InquiryData.Reserved_5_95[PRODUCT_IDENTIFICATION_OFFSET]); StrPtr[PRODUCT_IDENTIFICATION_LENGTH] = '\0'; AsciiStrToUnicodeStrS (StrPtr, Description + VENDOR_IDENTIFICATION_LENGTH + 1, PRODUCT_IDENTIFICATION_LENGTH + 1); BmEliminateExtraSpaces (Description); } } else if (CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoSdMmcInterfaceGuid)) { DevicePath = DevicePathFromHandle (Handle); if (DevicePath == NULL) { return NULL; } while (!IsDevicePathEnd (DevicePath) && (DevicePathType (DevicePath) != MESSAGING_DEVICE_PATH)) { DevicePath = NextDevicePathNode (DevicePath); } if (IsDevicePathEnd (DevicePath)) { return NULL; } if (DevicePathSubType (DevicePath) == MSG_SD_DP) { Description = L"SD Device"; } else if (DevicePathSubType (DevicePath) == MSG_EMMC_DP) { Description = L"eMMC Device"; } else { return NULL; } Description = AllocateCopyPool(StrSize (Description), Description); } return Description; } /** Try to get the controller's USB description. @param Handle Controller handle. @return The description string. **/ CHAR16 * BmGetUsbDescription ( IN EFI_HANDLE Handle ) { EFI_STATUS Status; EFI_USB_IO_PROTOCOL *UsbIo; CHAR16 NullChar; CHAR16 *Manufacturer; CHAR16 *Product; CHAR16 *SerialNumber; CHAR16 *Description; EFI_USB_DEVICE_DESCRIPTOR DevDesc; UINTN DescMaxSize; Status = gBS->HandleProtocol ( Handle, &gEfiUsbIoProtocolGuid, (VOID **) &UsbIo ); if (EFI_ERROR(Status)) { return NULL; } NullChar = L'\0'; Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DevDesc); if (EFI_ERROR(Status)) { return NULL; } Status = UsbIo->UsbGetStringDescriptor ( UsbIo, mBmUsbLangId, DevDesc.StrManufacturer, &Manufacturer ); if (EFI_ERROR(Status)) { Manufacturer = &NullChar; } Status = UsbIo->UsbGetStringDescriptor ( UsbIo, mBmUsbLangId, DevDesc.StrProduct, &Product ); if (EFI_ERROR(Status)) { Product = &NullChar; } Status = UsbIo->UsbGetStringDescriptor ( UsbIo, mBmUsbLangId, DevDesc.StrSerialNumber, &SerialNumber ); if (EFI_ERROR(Status)) { SerialNumber = &NullChar; } if ((Manufacturer == &NullChar) && (Product == &NullChar) && (SerialNumber == &NullChar) ) { return NULL; } DescMaxSize = StrSize (Manufacturer) + StrSize (Product) + StrSize (SerialNumber); Description = AllocateZeroPool(DescMaxSize); ASSERT (Description != NULL); StrCatS (Description, DescMaxSize/sizeof(CHAR16), Manufacturer); StrCatS (Description, DescMaxSize/sizeof(CHAR16), L" "); StrCatS (Description, DescMaxSize/sizeof(CHAR16), Product); StrCatS (Description, DescMaxSize/sizeof(CHAR16), L" "); StrCatS (Description, DescMaxSize/sizeof(CHAR16), SerialNumber); if (Manufacturer != &NullChar) { FreePool(Manufacturer); } if (Product != &NullChar) { FreePool(Product); } if (SerialNumber != &NullChar) { FreePool(SerialNumber); } BmEliminateExtraSpaces (Description); return Description; } /** Return the description for network boot device. @param Handle Controller handle. @return The description string. **/ CHAR16 * BmGetNetworkDescription ( IN EFI_HANDLE Handle ) { EFI_STATUS Status; EFI_DEVICE_PATH_PROTOCOL *DevicePath; MAC_ADDR_DEVICE_PATH *Mac; VLAN_DEVICE_PATH *Vlan; EFI_DEVICE_PATH_PROTOCOL *Ip; EFI_DEVICE_PATH_PROTOCOL *Uri; CHAR16 *Description; UINTN DescriptionSize; Status = gBS->OpenProtocol ( Handle, &gEfiLoadFileProtocolGuid, NULL, gImageHandle, Handle, EFI_OPEN_PROTOCOL_TEST_PROTOCOL ); if (EFI_ERROR(Status)) { return NULL; } Status = gBS->OpenProtocol ( Handle, &gEfiDevicePathProtocolGuid, (VOID **) &DevicePath, gImageHandle, Handle, EFI_OPEN_PROTOCOL_GET_PROTOCOL ); if (EFI_ERROR(Status) || (DevicePath == NULL)) { return NULL; } // // The PXE device path is like: // ....../Mac(...)[/Vlan(...)][/Wi-Fi(...)] // ....../Mac(...)[/Vlan(...)][/Wi-Fi(...)]/IPv4(...) // ....../Mac(...)[/Vlan(...)][/Wi-Fi(...)]/IPv6(...) // // The HTTP device path is like: // ....../Mac(...)[/Vlan(...)][/Wi-Fi(...)]/IPv4(...)[/Dns(...)]/Uri(...) // ....../Mac(...)[/Vlan(...)][/Wi-Fi(...)]/IPv6(...)[/Dns(...)]/Uri(...) // while (!IsDevicePathEnd (DevicePath) && ((DevicePathType (DevicePath) != MESSAGING_DEVICE_PATH) || (DevicePathSubType (DevicePath) != MSG_MAC_ADDR_DP)) ) { DevicePath = NextDevicePathNode (DevicePath); } if (IsDevicePathEnd (DevicePath)) { return NULL; } Mac = (MAC_ADDR_DEVICE_PATH *) DevicePath; DevicePath = NextDevicePathNode (DevicePath); // // Locate the optional Vlan node // if ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) && (DevicePathSubType (DevicePath) == MSG_VLAN_DP) ) { Vlan = (VLAN_DEVICE_PATH *) DevicePath; DevicePath = NextDevicePathNode (DevicePath); } else { Vlan = NULL; } // // Skip the optional Wi-Fi node // if ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) && (DevicePathSubType (DevicePath) == MSG_WIFI_DP) ) { DevicePath = NextDevicePathNode (DevicePath); } // // Locate the IP node // if ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) && ((DevicePathSubType (DevicePath) == MSG_IPv4_DP) || (DevicePathSubType (DevicePath) == MSG_IPv6_DP)) ) { Ip = DevicePath; DevicePath = NextDevicePathNode (DevicePath); } else { Ip = NULL; } // // Skip the optional DNS node // if ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) && (DevicePathSubType (DevicePath) == MSG_DNS_DP) ) { DevicePath = NextDevicePathNode (DevicePath); } // // Locate the URI node // if ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) && (DevicePathSubType (DevicePath) == MSG_URI_DP) ) { Uri = DevicePath; DevicePath = NextDevicePathNode (DevicePath); } else { Uri = NULL; } // // Build description like below: // "PXEv6 (MAC:112233445566 VLAN1)" // "HTTPv4 (MAC:112233445566)" // DescriptionSize = sizeof (L"HTTPv6 (MAC:112233445566 VLAN65535)"); Description = AllocatePool (DescriptionSize); ASSERT (Description != NULL); UnicodeSPrint ( Description, DescriptionSize, (Vlan == NULL) ? L"%sv%d (MAC:%02x%02x%02x%02x%02x%02x)" : L"%sv%d (MAC:%02x%02x%02x%02x%02x%02x VLAN%d)", (Uri == NULL) ? L"PXE" : L"HTTP", ((Ip == NULL) || (DevicePathSubType (Ip) == MSG_IPv4_DP)) ? 4 : 6, Mac->MacAddress.Addr[0], Mac->MacAddress.Addr[1], Mac->MacAddress.Addr[2], Mac->MacAddress.Addr[3], Mac->MacAddress.Addr[4], Mac->MacAddress.Addr[5], (Vlan == NULL) ? 0 : Vlan->VlanId ); return Description; } /** Return the boot description for LoadFile @param Handle Controller handle. @return The description string. **/ CHAR16 * BmGetLoadFileDescription ( IN EFI_HANDLE Handle ) { EFI_STATUS Status; EFI_DEVICE_PATH_PROTOCOL *FilePath; EFI_DEVICE_PATH_PROTOCOL *DevicePathNode; CHAR16 *Description; EFI_LOAD_FILE_PROTOCOL *LoadFile; Status = gBS->HandleProtocol (Handle, &gEfiLoadFileProtocolGuid, (VOID **)&LoadFile); if (EFI_ERROR(Status)) { return NULL; } // // Get the file name // Description = NULL; Status = gBS->HandleProtocol (Handle, &gEfiDevicePathProtocolGuid, (VOID **)&FilePath); if (!EFI_ERROR(Status)) { DevicePathNode = FilePath; while (!IsDevicePathEnd (DevicePathNode)) { if (DevicePathNode->Type == MEDIA_DEVICE_PATH && DevicePathNode->SubType == MEDIA_FILEPATH_DP) { Description = (CHAR16 *)(DevicePathNode + 1); break; } DevicePathNode = NextDevicePathNode (DevicePathNode); } } if (Description != NULL) { return AllocateCopyPool(StrSize (Description), Description); } return NULL; } /** Return the boot description for NVME boot device. @param Handle Controller handle. @return The description string. **/ CHAR16 * BmGetNvmeDescription ( IN EFI_HANDLE Handle ) { EFI_STATUS Status; EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL *NvmePassthru; EFI_DEV_PATH_PTR DevicePath; EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket; EFI_NVM_EXPRESS_COMMAND Command; EFI_NVM_EXPRESS_COMPLETION Completion; NVME_ADMIN_CONTROLLER_DATA ControllerData; CHAR16 *Description; CHAR16 *Char; UINTN Index; Status = gBS->HandleProtocol (Handle, &gEfiDevicePathProtocolGuid, (VOID **) &DevicePath.DevPath); if (EFI_ERROR(Status)) { return NULL; } Status = gBS->LocateDevicePath (&gEfiNvmExpressPassThruProtocolGuid, &DevicePath.DevPath, &Handle); if (EFI_ERROR(Status) || (DevicePathType (DevicePath.DevPath) != MESSAGING_DEVICE_PATH) || (DevicePathSubType (DevicePath.DevPath) != MSG_NVME_NAMESPACE_DP)) { // // Do not return description when the Handle is not a child of NVME controller. // return NULL; } // // Send ADMIN_IDENTIFY command to NVME controller to get the model and serial number. // Status = gBS->HandleProtocol (Handle, &gEfiNvmExpressPassThruProtocolGuid, (VOID **) &NvmePassthru); ASSERT_EFI_ERROR(Status); ZeroMem (&CommandPacket, sizeof(EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET)); ZeroMem (&Command, sizeof(EFI_NVM_EXPRESS_COMMAND)); ZeroMem (&Completion, sizeof(EFI_NVM_EXPRESS_COMPLETION)); Command.Cdw0.Opcode = NVME_ADMIN_IDENTIFY_CMD; // // According to Nvm Express 1.1 spec Figure 38, When not used, the field shall be cleared to 0h. // For the Identify command, the Namespace Identifier is only used for the Namespace data structure. // Command.Nsid = 0; CommandPacket.NvmeCmd = &Command; CommandPacket.NvmeCompletion = &Completion; CommandPacket.TransferBuffer = &ControllerData; CommandPacket.TransferLength = sizeof (ControllerData); CommandPacket.CommandTimeout = EFI_TIMER_PERIOD_SECONDS (5); CommandPacket.QueueType = NVME_ADMIN_QUEUE; // // Set bit 0 (Cns bit) to 1 to identify a controller // Command.Cdw10 = 1; Command.Flags = CDW10_VALID; Status = NvmePassthru->PassThru ( NvmePassthru, 0, &CommandPacket, NULL ); if (EFI_ERROR(Status)) { return NULL; } Description = AllocateZeroPool( (ARRAY_SIZE (ControllerData.Mn) + 1 + ARRAY_SIZE (ControllerData.Sn) + 1 + MAXIMUM_VALUE_CHARACTERS + 1 ) * sizeof (CHAR16)); if (Description != NULL) { Char = Description; for (Index = 0; Index < ARRAY_SIZE (ControllerData.Mn); Index++) { *(Char++) = (CHAR16) ControllerData.Mn[Index]; } *(Char++) = L' '; for (Index = 0; Index < ARRAY_SIZE (ControllerData.Sn); Index++) { *(Char++) = (CHAR16) ControllerData.Sn[Index]; } *(Char++) = L' '; UnicodeValueToStringS ( Char, sizeof (CHAR16) * (MAXIMUM_VALUE_CHARACTERS + 1), 0, DevicePath.NvmeNamespace->NamespaceId, 0 ); BmEliminateExtraSpaces (Description); } return Description; } /** Return the boot description for the controller based on the type. @param Handle Controller handle. @return The description string. **/ CHAR16 * BmGetMiscDescription ( IN EFI_HANDLE Handle ) { EFI_STATUS Status; CHAR16 *Description; EFI_BLOCK_IO_PROTOCOL *BlockIo; EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs; switch (BmDevicePathType (DevicePathFromHandle (Handle))) { case BmAcpiFloppyBoot: Description = L"Floppy"; break; case BmMessageAtapiBoot: case BmMessageSataBoot: Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **) &BlockIo); ASSERT_EFI_ERROR(Status); // // Assume a removable SATA device should be the DVD/CD device // Description = BlockIo->Media->RemovableMedia ? L"DVD/CDROM" : L"Hard Drive"; break; case BmMessageUsbBoot: Description = L"USB Device"; break; case BmMessageScsiBoot: Description = L"SCSI Device"; break; case BmHardwareDeviceBoot: Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **) &BlockIo); if (!EFI_ERROR(Status)) { Description = BlockIo->Media->RemovableMedia ? L"Removable Disk" : L"Hard Drive"; } else { Description = L"Misc Device"; } break; default: Status = gBS->HandleProtocol (Handle, &gEfiSimpleFileSystemProtocolGuid, (VOID **) &Fs); if (!EFI_ERROR(Status)) { Description = L"Non-Block Boot Device"; } else { Description = L"Misc Device"; } break; } return AllocateCopyPool(StrSize (Description), Description); } /** Register the platform provided boot description handler. @param Handler The platform provided boot description handler @retval EFI_SUCCESS The handler was registered successfully. @retval EFI_ALREADY_STARTED The handler was already registered. @retval EFI_OUT_OF_RESOURCES There is not enough resource to perform the registration. **/ EFI_STATUS EFIAPI EfiBootManagerRegisterBootDescriptionHandler ( IN EFI_BOOT_MANAGER_BOOT_DESCRIPTION_HANDLER Handler ) { LIST_ENTRY *Link; BM_BOOT_DESCRIPTION_ENTRY *Entry; for ( Link = GetFirstNode (&mPlatformBootDescriptionHandlers) ; !IsNull (&mPlatformBootDescriptionHandlers, Link) ; Link = GetNextNode (&mPlatformBootDescriptionHandlers, Link) ) { Entry = CR (Link, BM_BOOT_DESCRIPTION_ENTRY, Link, BM_BOOT_DESCRIPTION_ENTRY_SIGNATURE); if (Entry->Handler == Handler) { return EFI_ALREADY_STARTED; } } Entry = AllocatePool (sizeof (BM_BOOT_DESCRIPTION_ENTRY)); if (Entry == NULL) { return EFI_OUT_OF_RESOURCES; } Entry->Signature = BM_BOOT_DESCRIPTION_ENTRY_SIGNATURE; Entry->Handler = Handler; InsertTailList (&mPlatformBootDescriptionHandlers, &Entry->Link); return EFI_SUCCESS; } BM_GET_BOOT_DESCRIPTION mBmBootDescriptionHandlers[] = { BmGetUsbDescription, BmGetDescriptionFromDiskInfo, BmGetNetworkDescription, BmGetLoadFileDescription, BmGetNvmeDescription, BmGetMiscDescription }; /** Return the boot description for the controller. @param Handle Controller handle. @return The description string. **/ CHAR16 * BmGetBootDescription ( IN EFI_HANDLE Handle ) { LIST_ENTRY *Link; BM_BOOT_DESCRIPTION_ENTRY *Entry; CHAR16 *Description; CHAR16 *DefaultDescription; CHAR16 *Temp; UINTN Index; // // Firstly get the default boot description // DefaultDescription = NULL; for (Index = 0; Index < ARRAY_SIZE (mBmBootDescriptionHandlers); Index++) { DefaultDescription = mBmBootDescriptionHandlers[Index] (Handle); if (DefaultDescription != NULL) { // // Avoid description confusion between UEFI & Legacy boot option by adding "UEFI " prefix // ONLY for core provided boot description handler. // Temp = AllocatePool (StrSize (DefaultDescription) + sizeof (mBmUefiPrefix)); ASSERT (Temp != NULL); StrCpyS (Temp, (StrSize (DefaultDescription) + sizeof (mBmUefiPrefix)) / sizeof (CHAR16), mBmUefiPrefix); StrCatS (Temp, (StrSize (DefaultDescription) + sizeof (mBmUefiPrefix)) / sizeof (CHAR16), DefaultDescription); FreePool(DefaultDescription); DefaultDescription = Temp; break; } } ASSERT (DefaultDescription != NULL); // // Secondly query platform for the better boot description // for ( Link = GetFirstNode (&mPlatformBootDescriptionHandlers) ; !IsNull (&mPlatformBootDescriptionHandlers, Link) ; Link = GetNextNode (&mPlatformBootDescriptionHandlers, Link) ) { Entry = CR (Link, BM_BOOT_DESCRIPTION_ENTRY, Link, BM_BOOT_DESCRIPTION_ENTRY_SIGNATURE); Description = Entry->Handler (Handle, DefaultDescription); if (Description != NULL) { FreePool(DefaultDescription); return Description; } } return DefaultDescription; } /** Enumerate all boot option descriptions and append " 2"/" 3"/... to make unique description. @param BootOptions Array of boot options. @param BootOptionCount Count of boot options. **/ VOID BmMakeBootOptionDescriptionUnique ( EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions, UINTN BootOptionCount ) { UINTN Base; UINTN Index; UINTN DescriptionSize; UINTN MaxSuffixSize; BOOLEAN *Visited; UINTN MatchCount; if (BootOptionCount == 0) { return; } // // Calculate the maximum buffer size for the number suffix. // The initial sizeof (CHAR16) is for the blank space before the number. // MaxSuffixSize = sizeof (CHAR16); for (Index = BootOptionCount; Index != 0; Index = Index / 10) { MaxSuffixSize += sizeof (CHAR16); } Visited = AllocateZeroPool(sizeof (BOOLEAN) * BootOptionCount); ASSERT (Visited != NULL); for (Base = 0; Base < BootOptionCount; Base++) { if (!Visited[Base]) { MatchCount = 1; Visited[Base] = TRUE; DescriptionSize = StrSize (BootOptions[Base].Description); for (Index = Base + 1; Index < BootOptionCount; Index++) { if (!Visited[Index] && StrCmp (BootOptions[Base].Description, BootOptions[Index].Description) == 0) { Visited[Index] = TRUE; MatchCount++; FreePool(BootOptions[Index].Description); BootOptions[Index].Description = AllocatePool (DescriptionSize + MaxSuffixSize); UnicodeSPrint ( BootOptions[Index].Description, DescriptionSize + MaxSuffixSize, L"%s %d", BootOptions[Base].Description, MatchCount ); } } } } FreePool(Visited); }