REF:https://bugzilla.tianocore.org/show_bug.cgi?id=620 Adds the support for SD/eMMC device path to show as a boot option. The CID register content (returned from DiskInfo->Inquiry) seems do not provide very useful/readable 'OEM/Application ID' and 'Product name' field. For SD devices, the OID is a 2-character ASCII string and the Product name is a 5-character ASCII string. For eMMC devices, the OID is an 8-bit binary number and the Product name is a 6-character ASCII string. These strings are relatively short and do not provide a very readable description. Hence, this commit uses general 'SD (eMMC) Device' for the boot option description. Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Hao Wu <hao.a.wu@intel.com> Reviewed-by: Ruiyu Ni <ruiyu.ni@intel.com>
		
			
				
	
	
		
			882 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			882 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
|   Library functions which relate with boot option description.
 | |
| 
 | |
| Copyright (c) 2011 - 2017, Intel Corporation. All rights reserved.<BR>
 | |
| (C) Copyright 2015 Hewlett Packard Enterprise Development LP<BR>
 | |
| This program and the accompanying materials
 | |
| are licensed and made available under the terms and conditions of the BSD License
 | |
| which accompanies this distribution.  The full text of the license may be found at
 | |
| http://opensource.org/licenses/bsd-license.php
 | |
| 
 | |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
 | |
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 | |
| 
 | |
| **/
 | |
| 
 | |
| #include "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);
 | |
| }
 |