Add support for getting disk info from UFS devices. Signed-off-by: Jeff Brasen <jbrasen@nvidia.com> Reviewed-by: Zhichao Gao <zhichao.gao@intel.com>
		
			
				
	
	
		
			912 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			912 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/** @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) ||
 | 
						|
             CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoUfsInterfaceGuid))
 | 
						|
  {
 | 
						|
    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);
 | 
						|
}
 |