summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib/BmBootDescription.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib/BmBootDescription.c')
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib/BmBootDescription.c875
1 files changed, 875 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib/BmBootDescription.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib/BmBootDescription.c
new file mode 100644
index 00000000..34d1f5a0
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib/BmBootDescription.c
@@ -0,0 +1,875 @@
+/** @file
+ Library functions which relate with boot option description.
+
+Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR>
+(C) Copyright 2015 Hewlett Packard Enterprise Development LP<BR>
+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);
+}