+/** @file\r
+ Library functions which relate with boot option description.\r
+\r
+Copyright (c) 2011 - 2016, Intel Corporation. All rights reserved.<BR>\r
+(C) Copyright 2015 Hewlett Packard Enterprise Development LP<BR>\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution. The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "InternalBm.h"\r
+\r
+#define VENDOR_IDENTIFICATION_OFFSET 3\r
+#define VENDOR_IDENTIFICATION_LENGTH 8\r
+#define PRODUCT_IDENTIFICATION_OFFSET 11\r
+#define PRODUCT_IDENTIFICATION_LENGTH 16\r
+\r
+CONST UINT16 mBmUsbLangId = 0x0409; // English\r
+CHAR16 mBmUefiPrefix[] = L"UEFI ";\r
+\r
+LIST_ENTRY mPlatformBootDescriptionHandlers = INITIALIZE_LIST_HEAD_VARIABLE (mPlatformBootDescriptionHandlers);\r
+\r
+/**\r
+ For a bootable Device path, return its boot type.\r
+\r
+ @param DevicePath The bootable device Path to check\r
+\r
+ @retval AcpiFloppyBoot If given device path contains ACPI_DEVICE_PATH type device path node\r
+ which HID is floppy device.\r
+ @retval MessageAtapiBoot If given device path contains MESSAGING_DEVICE_PATH type device path node\r
+ and its last device path node's subtype is MSG_ATAPI_DP.\r
+ @retval MessageSataBoot If given device path contains MESSAGING_DEVICE_PATH type device path node\r
+ and its last device path node's subtype is MSG_SATA_DP.\r
+ @retval MessageScsiBoot If given device path contains MESSAGING_DEVICE_PATH type device path node\r
+ and its last device path node's subtype is MSG_SCSI_DP.\r
+ @retval MessageUsbBoot If given device path contains MESSAGING_DEVICE_PATH type device path node\r
+ and its last device path node's subtype is MSG_USB_DP.\r
+ @retval MessageNetworkBoot If given device path contains MESSAGING_DEVICE_PATH type device path node\r
+ and its last device path node's subtype is MSG_MAC_ADDR_DP, MSG_VLAN_DP,\r
+ MSG_IPv4_DP or MSG_IPv6_DP.\r
+ @retval MessageHttpBoot If given device path contains MESSAGING_DEVICE_PATH type device path node\r
+ and its last device path node's subtype is MSG_URI_DP.\r
+ @retval UnsupportedBoot If tiven device path doesn't match the above condition, it's not supported.\r
+\r
+**/\r
+BM_BOOT_TYPE\r
+BmDevicePathType (\r
+ IN EFI_DEVICE_PATH_PROTOCOL *DevicePath\r
+ )\r
+{\r
+ EFI_DEVICE_PATH_PROTOCOL *Node;\r
+ EFI_DEVICE_PATH_PROTOCOL *NextNode;\r
+\r
+ ASSERT (DevicePath != NULL);\r
+\r
+ for (Node = DevicePath; !IsDevicePathEndType (Node); Node = NextDevicePathNode (Node)) {\r
+ switch (DevicePathType (Node)) {\r
+\r
+ case ACPI_DEVICE_PATH:\r
+ if (EISA_ID_TO_NUM (((ACPI_HID_DEVICE_PATH *) Node)->HID) == 0x0604) {\r
+ return BmAcpiFloppyBoot;\r
+ }\r
+ break;\r
+\r
+ case HARDWARE_DEVICE_PATH:\r
+ if (DevicePathSubType (Node) == HW_CONTROLLER_DP) {\r
+ return BmHardwareDeviceBoot;\r
+ }\r
+ break;\r
+\r
+ case MESSAGING_DEVICE_PATH:\r
+ //\r
+ // Skip LUN device node\r
+ //\r
+ NextNode = Node;\r
+ do {\r
+ NextNode = NextDevicePathNode (NextNode);\r
+ } while (\r
+ (DevicePathType (NextNode) == MESSAGING_DEVICE_PATH) &&\r
+ (DevicePathSubType(NextNode) == MSG_DEVICE_LOGICAL_UNIT_DP)\r
+ );\r
+\r
+ //\r
+ // If the device path not only point to driver device, it is not a messaging device path,\r
+ //\r
+ if (!IsDevicePathEndType (NextNode)) {\r
+ continue;\r
+ }\r
+\r
+ switch (DevicePathSubType (Node)) {\r
+ case MSG_ATAPI_DP:\r
+ return BmMessageAtapiBoot;\r
+ break;\r
+\r
+ case MSG_SATA_DP:\r
+ return BmMessageSataBoot;\r
+ break;\r
+\r
+ case MSG_USB_DP:\r
+ return BmMessageUsbBoot;\r
+ break;\r
+\r
+ case MSG_SCSI_DP:\r
+ return BmMessageScsiBoot;\r
+ break;\r
+\r
+ case MSG_MAC_ADDR_DP:\r
+ case MSG_VLAN_DP:\r
+ case MSG_IPv4_DP:\r
+ case MSG_IPv6_DP:\r
+ return BmMessageNetworkBoot;\r
+ break;\r
+\r
+ case MSG_URI_DP:\r
+ return BmMessageHttpBoot;\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ return BmMiscBoot;\r
+}\r
+\r
+/**\r
+ Eliminate the extra spaces in the Str to one space.\r
+\r
+ @param Str Input string info.\r
+**/\r
+VOID\r
+BmEliminateExtraSpaces (\r
+ IN CHAR16 *Str\r
+ )\r
+{\r
+ UINTN Index;\r
+ UINTN ActualIndex;\r
+\r
+ for (Index = 0, ActualIndex = 0; Str[Index] != L'\0'; Index++) {\r
+ if ((Str[Index] != L' ') || ((ActualIndex > 0) && (Str[ActualIndex - 1] != L' '))) {\r
+ Str[ActualIndex++] = Str[Index];\r
+ }\r
+ }\r
+ Str[ActualIndex] = L'\0';\r
+}\r
+\r
+/**\r
+ Try to get the controller's ATA/ATAPI description.\r
+\r
+ @param Handle Controller handle.\r
+\r
+ @return The description string.\r
+**/\r
+CHAR16 *\r
+BmGetDescriptionFromDiskInfo (\r
+ IN EFI_HANDLE Handle\r
+ )\r
+{\r
+ UINTN Index;\r
+ EFI_STATUS Status;\r
+ EFI_DISK_INFO_PROTOCOL *DiskInfo;\r
+ UINT32 BufferSize;\r
+ EFI_ATAPI_IDENTIFY_DATA IdentifyData;\r
+ EFI_SCSI_INQUIRY_DATA InquiryData;\r
+ CHAR16 *Description;\r
+ UINTN Length;\r
+ CONST UINTN ModelNameLength = 40;\r
+ CONST UINTN SerialNumberLength = 20;\r
+ CHAR8 *StrPtr;\r
+ UINT8 Temp;\r
+\r
+ Description = NULL;\r
+\r
+ Status = gBS->HandleProtocol (\r
+ Handle,\r
+ &gEfiDiskInfoProtocolGuid,\r
+ (VOID **) &DiskInfo\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return NULL;\r
+ }\r
+\r
+ if (CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoAhciInterfaceGuid) ||\r
+ CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoIdeInterfaceGuid)) {\r
+ BufferSize = sizeof (EFI_ATAPI_IDENTIFY_DATA);\r
+ Status = DiskInfo->Identify (\r
+ DiskInfo,\r
+ &IdentifyData,\r
+ &BufferSize\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ Description = AllocateZeroPool ((ModelNameLength + SerialNumberLength + 2) * sizeof (CHAR16));\r
+ ASSERT (Description != NULL);\r
+ for (Index = 0; Index + 1 < ModelNameLength; Index += 2) {\r
+ Description[Index] = (CHAR16) IdentifyData.ModelName[Index + 1];\r
+ Description[Index + 1] = (CHAR16) IdentifyData.ModelName[Index];\r
+ }\r
+\r
+ Length = Index;\r
+ Description[Length++] = L' ';\r
+\r
+ for (Index = 0; Index + 1 < SerialNumberLength; Index += 2) {\r
+ Description[Length + Index] = (CHAR16) IdentifyData.SerialNo[Index + 1];\r
+ Description[Length + Index + 1] = (CHAR16) IdentifyData.SerialNo[Index];\r
+ }\r
+ Length += Index;\r
+ Description[Length++] = L'\0';\r
+ ASSERT (Length == ModelNameLength + SerialNumberLength + 2);\r
+\r
+ BmEliminateExtraSpaces (Description);\r
+ }\r
+ } else if (CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoScsiInterfaceGuid)) {\r
+ BufferSize = sizeof (EFI_SCSI_INQUIRY_DATA);\r
+ Status = DiskInfo->Inquiry (\r
+ DiskInfo,\r
+ &InquiryData,\r
+ &BufferSize\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ Description = AllocateZeroPool ((VENDOR_IDENTIFICATION_LENGTH + PRODUCT_IDENTIFICATION_LENGTH + 2) * sizeof (CHAR16));\r
+ ASSERT (Description != NULL);\r
+\r
+ //\r
+ // Per SCSI spec, EFI_SCSI_INQUIRY_DATA.Reserved_5_95[3 - 10] save the Verdor identification\r
+ // EFI_SCSI_INQUIRY_DATA.Reserved_5_95[11 - 26] save the product identification,\r
+ // Here combine the vendor identification and product identification to the description.\r
+ //\r
+ StrPtr = (CHAR8 *) (&InquiryData.Reserved_5_95[VENDOR_IDENTIFICATION_OFFSET]);\r
+ Temp = StrPtr[VENDOR_IDENTIFICATION_LENGTH];\r
+ StrPtr[VENDOR_IDENTIFICATION_LENGTH] = '\0';\r
+ AsciiStrToUnicodeStr (StrPtr, Description);\r
+ StrPtr[VENDOR_IDENTIFICATION_LENGTH] = Temp;\r
+\r
+ //\r
+ // Add one space at the middle of vendor information and product information.\r
+ //\r
+ Description[VENDOR_IDENTIFICATION_LENGTH] = L' ';\r
+\r
+ StrPtr = (CHAR8 *) (&InquiryData.Reserved_5_95[PRODUCT_IDENTIFICATION_OFFSET]);\r
+ StrPtr[PRODUCT_IDENTIFICATION_LENGTH] = '\0';\r
+ AsciiStrToUnicodeStr (StrPtr, Description + VENDOR_IDENTIFICATION_LENGTH + 1);\r
+\r
+ BmEliminateExtraSpaces (Description);\r
+ }\r
+ }\r
+\r
+ return Description;\r
+}\r
+\r
+/**\r
+ Try to get the controller's USB description.\r
+\r
+ @param Handle Controller handle.\r
+\r
+ @return The description string.\r
+**/\r
+CHAR16 *\r
+BmGetUsbDescription (\r
+ IN EFI_HANDLE Handle\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_USB_IO_PROTOCOL *UsbIo;\r
+ CHAR16 NullChar;\r
+ CHAR16 *Manufacturer;\r
+ CHAR16 *Product;\r
+ CHAR16 *SerialNumber;\r
+ CHAR16 *Description;\r
+ EFI_USB_DEVICE_DESCRIPTOR DevDesc;\r
+ UINTN DescMaxSize;\r
+\r
+ Status = gBS->HandleProtocol (\r
+ Handle,\r
+ &gEfiUsbIoProtocolGuid,\r
+ (VOID **) &UsbIo\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return NULL;\r
+ }\r
+\r
+ NullChar = L'\0';\r
+\r
+ Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DevDesc);\r
+ if (EFI_ERROR (Status)) {\r
+ return NULL;\r
+ }\r
+\r
+ Status = UsbIo->UsbGetStringDescriptor (\r
+ UsbIo,\r
+ mBmUsbLangId,\r
+ DevDesc.StrManufacturer,\r
+ &Manufacturer\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ Manufacturer = &NullChar;\r
+ }\r
+\r
+ Status = UsbIo->UsbGetStringDescriptor (\r
+ UsbIo,\r
+ mBmUsbLangId,\r
+ DevDesc.StrProduct,\r
+ &Product\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ Product = &NullChar;\r
+ }\r
+\r
+ Status = UsbIo->UsbGetStringDescriptor (\r
+ UsbIo,\r
+ mBmUsbLangId,\r
+ DevDesc.StrSerialNumber,\r
+ &SerialNumber\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ SerialNumber = &NullChar;\r
+ }\r
+\r
+ if ((Manufacturer == &NullChar) &&\r
+ (Product == &NullChar) &&\r
+ (SerialNumber == &NullChar)\r
+ ) {\r
+ return NULL;\r
+ }\r
+\r
+ DescMaxSize = StrSize (Manufacturer) + StrSize (Product) + StrSize (SerialNumber);\r
+ Description = AllocateZeroPool (DescMaxSize);\r
+ ASSERT (Description != NULL);\r
+ StrCatS (Description, DescMaxSize/sizeof(CHAR16), Manufacturer);\r
+ StrCatS (Description, DescMaxSize/sizeof(CHAR16), L" ");\r
+\r
+ StrCatS (Description, DescMaxSize/sizeof(CHAR16), Product);\r
+ StrCatS (Description, DescMaxSize/sizeof(CHAR16), L" ");\r
+\r
+ StrCatS (Description, DescMaxSize/sizeof(CHAR16), SerialNumber);\r
+\r
+ if (Manufacturer != &NullChar) {\r
+ FreePool (Manufacturer);\r
+ }\r
+ if (Product != &NullChar) {\r
+ FreePool (Product);\r
+ }\r
+ if (SerialNumber != &NullChar) {\r
+ FreePool (SerialNumber);\r
+ }\r
+\r
+ BmEliminateExtraSpaces (Description);\r
+\r
+ return Description;\r
+}\r
+\r
+/**\r
+ Return the boot description for the controller based on the type.\r
+\r
+ @param Handle Controller handle.\r
+\r
+ @return The description string.\r
+**/\r
+CHAR16 *\r
+BmGetMiscDescription (\r
+ IN EFI_HANDLE Handle\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ CHAR16 *Description;\r
+ EFI_BLOCK_IO_PROTOCOL *BlockIo;\r
+ EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs;\r
+\r
+ switch (BmDevicePathType (DevicePathFromHandle (Handle))) {\r
+ case BmAcpiFloppyBoot:\r
+ Description = L"Floppy";\r
+ break;\r
+\r
+ case BmMessageAtapiBoot:\r
+ case BmMessageSataBoot:\r
+ Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **) &BlockIo);\r
+ ASSERT_EFI_ERROR (Status);\r
+ //\r
+ // Assume a removable SATA device should be the DVD/CD device\r
+ //\r
+ Description = BlockIo->Media->RemovableMedia ? L"DVD/CDROM" : L"Hard Drive";\r
+ break;\r
+\r
+ case BmMessageUsbBoot:\r
+ Description = L"USB Device";\r
+ break;\r
+\r
+ case BmMessageScsiBoot:\r
+ Description = L"SCSI Device";\r
+ break;\r
+\r
+ case BmHardwareDeviceBoot:\r
+ Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **) &BlockIo);\r
+ if (!EFI_ERROR (Status)) {\r
+ Description = BlockIo->Media->RemovableMedia ? L"Removable Disk" : L"Hard Drive";\r
+ } else {\r
+ Description = L"Misc Device";\r
+ }\r
+ break;\r
+\r
+ case BmMessageNetworkBoot:\r
+ Description = L"Network";\r
+ break;\r
+\r
+ case BmMessageHttpBoot:\r
+ Description = L"Http";\r
+ break;\r
+\r
+ default:\r
+ Status = gBS->HandleProtocol (Handle, &gEfiSimpleFileSystemProtocolGuid, (VOID **) &Fs);\r
+ if (!EFI_ERROR (Status)) {\r
+ Description = L"Non-Block Boot Device";\r
+ } else {\r
+ Description = L"Misc Device";\r
+ }\r
+ break;\r
+ }\r
+\r
+ return AllocateCopyPool (StrSize (Description), Description);\r
+}\r
+\r
+/**\r
+ Register the platform provided boot description handler.\r
+\r
+ @param Handler The platform provided boot description handler\r
+\r
+ @retval EFI_SUCCESS The handler was registered successfully.\r
+ @retval EFI_ALREADY_STARTED The handler was already registered.\r
+ @retval EFI_OUT_OF_RESOURCES There is not enough resource to perform the registration.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiBootManagerRegisterBootDescriptionHandler (\r
+ IN EFI_BOOT_MANAGER_BOOT_DESCRIPTION_HANDLER Handler\r
+ )\r
+{\r
+ LIST_ENTRY *Link;\r
+ BM_BOOT_DESCRIPTION_ENTRY *Entry;\r
+\r
+ for ( Link = GetFirstNode (&mPlatformBootDescriptionHandlers)\r
+ ; !IsNull (&mPlatformBootDescriptionHandlers, Link)\r
+ ; Link = GetNextNode (&mPlatformBootDescriptionHandlers, Link)\r
+ ) {\r
+ Entry = CR (Link, BM_BOOT_DESCRIPTION_ENTRY, Link, BM_BOOT_DESCRIPTION_ENTRY_SIGNATURE);\r
+ if (Entry->Handler == Handler) {\r
+ return EFI_ALREADY_STARTED;\r
+ }\r
+ }\r
+\r
+ Entry = AllocatePool (sizeof (BM_BOOT_DESCRIPTION_ENTRY));\r
+ if (Entry == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Entry->Signature = BM_BOOT_DESCRIPTION_ENTRY_SIGNATURE;\r
+ Entry->Handler = Handler;\r
+ InsertTailList (&mPlatformBootDescriptionHandlers, &Entry->Link);\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+BM_GET_BOOT_DESCRIPTION mBmBootDescriptionHandlers[] = {\r
+ BmGetUsbDescription,\r
+ BmGetDescriptionFromDiskInfo,\r
+ BmGetMiscDescription\r
+};\r
+\r
+/**\r
+ Return the boot description for the controller.\r
+\r
+ @param Handle Controller handle.\r
+\r
+ @return The description string.\r
+**/\r
+CHAR16 *\r
+BmGetBootDescription (\r
+ IN EFI_HANDLE Handle\r
+ )\r
+{\r
+ LIST_ENTRY *Link;\r
+ BM_BOOT_DESCRIPTION_ENTRY *Entry;\r
+ CHAR16 *Description;\r
+ CHAR16 *DefaultDescription;\r
+ CHAR16 *Temp;\r
+ UINTN Index;\r
+\r
+ //\r
+ // Firstly get the default boot description\r
+ //\r
+ DefaultDescription = NULL;\r
+ for (Index = 0; Index < sizeof (mBmBootDescriptionHandlers) / sizeof (mBmBootDescriptionHandlers[0]); Index++) {\r
+ DefaultDescription = mBmBootDescriptionHandlers[Index] (Handle);\r
+ if (DefaultDescription != NULL) {\r
+ //\r
+ // Avoid description confusion between UEFI & Legacy boot option by adding "UEFI " prefix\r
+ // ONLY for core provided boot description handler.\r
+ //\r
+ Temp = AllocatePool (StrSize (DefaultDescription) + sizeof (mBmUefiPrefix));\r
+ ASSERT (Temp != NULL);\r
+ StrCpyS (Temp, (StrSize (DefaultDescription) + sizeof (mBmUefiPrefix)) / sizeof (CHAR16), mBmUefiPrefix);\r
+ StrCatS (Temp, (StrSize (DefaultDescription) + sizeof (mBmUefiPrefix)) / sizeof (CHAR16), DefaultDescription);\r
+ FreePool (DefaultDescription);\r
+ DefaultDescription = Temp;\r
+ break;\r
+ }\r
+ }\r
+ ASSERT (DefaultDescription != NULL);\r
+\r
+ //\r
+ // Secondly query platform for the better boot description\r
+ //\r
+ for ( Link = GetFirstNode (&mPlatformBootDescriptionHandlers)\r
+ ; !IsNull (&mPlatformBootDescriptionHandlers, Link)\r
+ ; Link = GetNextNode (&mPlatformBootDescriptionHandlers, Link)\r
+ ) {\r
+ Entry = CR (Link, BM_BOOT_DESCRIPTION_ENTRY, Link, BM_BOOT_DESCRIPTION_ENTRY_SIGNATURE);\r
+ Description = Entry->Handler (Handle, DefaultDescription);\r
+ if (Description != NULL) {\r
+ FreePool (DefaultDescription);\r
+ return Description;\r
+ }\r
+ }\r
+\r
+ return DefaultDescription;\r
+}\r
+\r
+/**\r
+ Enumerate all boot option descriptions and append " 2"/" 3"/... to make\r
+ unique description.\r
+\r
+ @param BootOptions Array of boot options.\r
+ @param BootOptionCount Count of boot options.\r
+**/\r
+VOID\r
+BmMakeBootOptionDescriptionUnique (\r
+ EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions,\r
+ UINTN BootOptionCount\r
+ )\r
+{\r
+ UINTN Base;\r
+ UINTN Index;\r
+ UINTN DescriptionSize;\r
+ UINTN MaxSuffixSize;\r
+ BOOLEAN *Visited;\r
+ UINTN MatchCount;\r
+\r
+ if (BootOptionCount == 0) {\r
+ return;\r
+ }\r
+\r
+ //\r
+ // Calculate the maximum buffer size for the number suffix.\r
+ // The initial sizeof (CHAR16) is for the blank space before the number.\r
+ //\r
+ MaxSuffixSize = sizeof (CHAR16);\r
+ for (Index = BootOptionCount; Index != 0; Index = Index / 10) {\r
+ MaxSuffixSize += sizeof (CHAR16);\r
+ }\r
+\r
+ Visited = AllocateZeroPool (sizeof (BOOLEAN) * BootOptionCount);\r
+ ASSERT (Visited != NULL);\r
+\r
+ for (Base = 0; Base < BootOptionCount; Base++) {\r
+ if (!Visited[Base]) {\r
+ MatchCount = 1;\r
+ Visited[Base] = TRUE;\r
+ DescriptionSize = StrSize (BootOptions[Base].Description);\r
+ for (Index = Base + 1; Index < BootOptionCount; Index++) {\r
+ if (!Visited[Index] && StrCmp (BootOptions[Base].Description, BootOptions[Index].Description) == 0) {\r
+ Visited[Index] = TRUE;\r
+ MatchCount++;\r
+ FreePool (BootOptions[Index].Description);\r
+ BootOptions[Index].Description = AllocatePool (DescriptionSize + MaxSuffixSize);\r
+ UnicodeSPrint (\r
+ BootOptions[Index].Description, DescriptionSize + MaxSuffixSize,\r
+ L"%s %d",\r
+ BootOptions[Base].Description, MatchCount\r
+ );\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ FreePool (Visited);\r
+}\r