]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Library/UefiBootManagerLib/BmBootDescription.c
MdeModulePkg/UefiBootManagerLib: Generate boot description for NVME
[mirror_edk2.git] / MdeModulePkg / Library / UefiBootManagerLib / BmBootDescription.c
index 5c77e86006057178b1ea42ad08999aeaa4918a23..501a0cc25520ec4b569f937767110eef42395102 100644 (file)
@@ -1,7 +1,7 @@
 /** @file\r
   Library functions which relate with boot option description.\r
 \r
-Copyright (c) 2011 - 2016, Intel Corporation. All rights reserved.<BR>\r
+Copyright (c) 2011 - 2017, 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
@@ -28,24 +28,19 @@ LIST_ENTRY mPlatformBootDescriptionHandlers = INITIALIZE_LIST_HEAD_VARIABLE (mPl
 /**\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
+  @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 BmMiscBoot        If tiven device path doesn't match the above condition.\r
 \r
 **/\r
 BM_BOOT_TYPE\r
@@ -108,17 +103,6 @@ BmDevicePathType (
         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
@@ -231,7 +215,7 @@ BmGetDescriptionFromDiskInfo (
       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
+      AsciiStrToUnicodeStrS (StrPtr, Description, VENDOR_IDENTIFICATION_LENGTH + 1);\r
       StrPtr[VENDOR_IDENTIFICATION_LENGTH] = Temp;\r
 \r
       //\r
@@ -241,7 +225,7 @@ BmGetDescriptionFromDiskInfo (
 \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
+      AsciiStrToUnicodeStrS (StrPtr, Description + VENDOR_IDENTIFICATION_LENGTH + 1, PRODUCT_IDENTIFICATION_LENGTH + 1);\r
 \r
       BmEliminateExtraSpaces (Description);\r
     }\r
@@ -351,6 +335,272 @@ BmGetUsbDescription (
   return Description;\r
 }\r
 \r
+/**\r
+  Return the description for network boot device.\r
+\r
+  @param Handle                Controller handle.\r
+\r
+  @return  The description string.\r
+**/\r
+CHAR16 *\r
+BmGetNetworkDescription (\r
+  IN EFI_HANDLE                  Handle\r
+  )\r
+{\r
+  EFI_STATUS                     Status;\r
+  EFI_DEVICE_PATH_PROTOCOL       *DevicePath;\r
+  MAC_ADDR_DEVICE_PATH           *Mac;\r
+  VLAN_DEVICE_PATH               *Vlan;\r
+  EFI_DEVICE_PATH_PROTOCOL       *Ip;\r
+  EFI_DEVICE_PATH_PROTOCOL       *Uri;\r
+  CHAR16                         *Description;\r
+  UINTN                          DescriptionSize;\r
+\r
+  Status = gBS->OpenProtocol (\r
+                  Handle,\r
+                  &gEfiLoadFileProtocolGuid,\r
+                  NULL,\r
+                  gImageHandle,\r
+                  Handle,\r
+                  EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    return NULL;\r
+  }\r
+\r
+  Status = gBS->OpenProtocol (\r
+                  Handle,\r
+                  &gEfiDevicePathProtocolGuid,\r
+                  (VOID **) &DevicePath,\r
+                  gImageHandle,\r
+                  Handle,\r
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+                  );\r
+  if (EFI_ERROR (Status) || (DevicePath == NULL)) {\r
+    return NULL;\r
+  }\r
+\r
+  //\r
+  // The PXE device path is like:\r
+  //   ....../Mac(...)[/Vlan(...)]\r
+  //   ....../Mac(...)[/Vlan(...)]/IPv4(...)\r
+  //   ....../Mac(...)[/Vlan(...)]/IPv6(...)\r
+  //\r
+  // The HTTP device path is like:\r
+  //   ....../Mac(...)[/Vlan(...)]/IPv4(...)/Uri(...)\r
+  //   ....../Mac(...)[/Vlan(...)]/IPv6(...)/Uri(...)\r
+  //\r
+  while (!IsDevicePathEnd (DevicePath) &&\r
+         ((DevicePathType (DevicePath) != MESSAGING_DEVICE_PATH) ||\r
+          (DevicePathSubType (DevicePath) != MSG_MAC_ADDR_DP))\r
+         ) {\r
+    DevicePath = NextDevicePathNode (DevicePath);\r
+  }\r
+\r
+  if (IsDevicePathEnd (DevicePath)) {\r
+    return NULL;\r
+  }\r
+\r
+  Mac = (MAC_ADDR_DEVICE_PATH *) DevicePath;\r
+  DevicePath = NextDevicePathNode (DevicePath);\r
+\r
+  if ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) &&\r
+      (DevicePathSubType (DevicePath) == MSG_VLAN_DP)\r
+      ) {\r
+    Vlan = (VLAN_DEVICE_PATH *) DevicePath;\r
+    DevicePath = NextDevicePathNode (DevicePath);\r
+  } else {\r
+    Vlan = NULL;\r
+  }\r
+\r
+  if ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) &&\r
+      ((DevicePathSubType (DevicePath) == MSG_IPv4_DP) ||\r
+       (DevicePathSubType (DevicePath) == MSG_IPv6_DP))\r
+      ) {\r
+    Ip = DevicePath;\r
+    DevicePath = NextDevicePathNode (DevicePath);\r
+  } else {\r
+    Ip = NULL;\r
+  }\r
+\r
+  if ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) &&\r
+      (DevicePathSubType (DevicePath) == MSG_URI_DP)\r
+      ) {\r
+    Uri = DevicePath;\r
+    DevicePath = NextDevicePathNode (DevicePath);\r
+  } else {\r
+    Uri = NULL;\r
+  }\r
+\r
+  //\r
+  // Build description like below:\r
+  //   "PXEv6 (MAC:112233445566 VLAN1)"\r
+  //   "HTTPv4 (MAC:112233445566)"\r
+  //\r
+  DescriptionSize = sizeof (L"HTTPv6 (MAC:112233445566 VLAN65535)");\r
+  Description     = AllocatePool (DescriptionSize);\r
+  ASSERT (Description != NULL);\r
+  UnicodeSPrint (\r
+    Description, DescriptionSize,\r
+    (Vlan == NULL) ?\r
+    L"%sv%d (MAC:%02x%02x%02x%02x%02x%02x)" :\r
+    L"%sv%d (MAC:%02x%02x%02x%02x%02x%02x VLAN%d)",\r
+    (Uri == NULL) ? L"PXE" : L"HTTP",\r
+    ((Ip == NULL) || (DevicePathSubType (Ip) == MSG_IPv4_DP)) ? 4 : 6,\r
+    Mac->MacAddress.Addr[0], Mac->MacAddress.Addr[1], Mac->MacAddress.Addr[2],\r
+    Mac->MacAddress.Addr[3], Mac->MacAddress.Addr[4], Mac->MacAddress.Addr[5],\r
+    (Vlan == NULL) ? 0 : Vlan->VlanId\r
+    );\r
+  return Description;\r
+}\r
+\r
+/**\r
+  Return the boot description for LoadFile\r
+\r
+  @param Handle                Controller handle.\r
+\r
+  @return  The description string.\r
+**/\r
+CHAR16 *\r
+BmGetLoadFileDescription (\r
+  IN EFI_HANDLE                  Handle\r
+  )\r
+{\r
+  EFI_STATUS                            Status;\r
+  EFI_DEVICE_PATH_PROTOCOL              *FilePath;\r
+  EFI_DEVICE_PATH_PROTOCOL              *DevicePathNode;\r
+  CHAR16                                *Description;\r
+  EFI_LOAD_FILE_PROTOCOL                *LoadFile;\r
+\r
+  Status = gBS->HandleProtocol (Handle, &gEfiLoadFileProtocolGuid, (VOID **)&LoadFile);\r
+  if (EFI_ERROR (Status)) {\r
+    return NULL;\r
+  }\r
+\r
+  //\r
+  // Get the file name\r
+  //\r
+  Description = NULL;\r
+  Status = gBS->HandleProtocol (Handle, &gEfiDevicePathProtocolGuid, (VOID **)&FilePath);\r
+  if (!EFI_ERROR (Status)) {\r
+    DevicePathNode = FilePath;\r
+    while (!IsDevicePathEnd (DevicePathNode)) {\r
+      if (DevicePathNode->Type == MEDIA_DEVICE_PATH && DevicePathNode->SubType == MEDIA_FILEPATH_DP) {\r
+        Description = (CHAR16 *)(DevicePathNode + 1);\r
+        break;\r
+      }\r
+      DevicePathNode = NextDevicePathNode (DevicePathNode);\r
+    }\r
+  }\r
+\r
+  if (Description != NULL) {\r
+    return AllocateCopyPool (StrSize (Description), Description);\r
+  }\r
+\r
+  return NULL;\r
+}\r
+\r
+/**\r
+  Return the boot description for NVME boot device.\r
+\r
+  @param Handle                Controller handle.\r
+\r
+  @return  The description string.\r
+**/\r
+CHAR16 *\r
+BmGetNvmeDescription (\r
+  IN EFI_HANDLE                      Handle\r
+  )\r
+{\r
+  EFI_STATUS                               Status;\r
+  EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL       *NvmePassthru;\r
+  EFI_DEV_PATH_PTR                         DevicePath;\r
+  EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket;\r
+  EFI_NVM_EXPRESS_COMMAND                  Command;\r
+  EFI_NVM_EXPRESS_COMPLETION               Completion;\r
+  NVME_ADMIN_CONTROLLER_DATA               ControllerData;\r
+  CHAR16                                   *Description;\r
+  CHAR16                                   *Char;\r
+  UINTN                                    Index;\r
+\r
+  Status = gBS->HandleProtocol (Handle, &gEfiDevicePathProtocolGuid, (VOID **) &DevicePath.DevPath);\r
+  if (EFI_ERROR (Status)) {\r
+    return NULL;\r
+  }\r
+\r
+  Status = gBS->LocateDevicePath (&gEfiNvmExpressPassThruProtocolGuid, &DevicePath.DevPath, &Handle);\r
+  if (EFI_ERROR (Status) ||\r
+      (DevicePathType (DevicePath.DevPath) != MESSAGING_DEVICE_PATH) ||\r
+      (DevicePathSubType (DevicePath.DevPath) != MSG_NVME_NAMESPACE_DP)) {\r
+    //\r
+    // Do not return description when the Handle is not a child of NVME controller.\r
+    //\r
+    return NULL;\r
+  }\r
+\r
+  //\r
+  // Send ADMIN_IDENTIFY command to NVME controller to get the model and serial number.\r
+  //\r
+  Status = gBS->HandleProtocol (Handle, &gEfiNvmExpressPassThruProtocolGuid, (VOID **) &NvmePassthru);\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  ZeroMem (&CommandPacket, sizeof(EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));\r
+  ZeroMem (&Command, sizeof(EFI_NVM_EXPRESS_COMMAND));\r
+  ZeroMem (&Completion, sizeof(EFI_NVM_EXPRESS_COMPLETION));\r
+\r
+  Command.Cdw0.Opcode = NVME_ADMIN_IDENTIFY_CMD;\r
+  //\r
+  // According to Nvm Express 1.1 spec Figure 38, When not used, the field shall be cleared to 0h.\r
+  // For the Identify command, the Namespace Identifier is only used for the Namespace data structure.\r
+  //\r
+  Command.Nsid        = 0;\r
+  CommandPacket.NvmeCmd        = &Command;\r
+  CommandPacket.NvmeCompletion = &Completion;\r
+  CommandPacket.TransferBuffer = &ControllerData;\r
+  CommandPacket.TransferLength = sizeof (ControllerData);\r
+  CommandPacket.CommandTimeout = EFI_TIMER_PERIOD_SECONDS (5);\r
+  CommandPacket.QueueType      = NVME_ADMIN_QUEUE;\r
+  //\r
+  // Set bit 0 (Cns bit) to 1 to identify a controller\r
+  //\r
+  Command.Cdw10                = 1;\r
+  Command.Flags                = CDW10_VALID;\r
+\r
+  Status = NvmePassthru->PassThru (\r
+                               NvmePassthru,\r
+                               0,\r
+                               &CommandPacket,\r
+                               NULL\r
+                               );\r
+  if (EFI_ERROR (Status)) {\r
+    return NULL;\r
+  }\r
+\r
+  Description = AllocateZeroPool (\r
+                  (ARRAY_SIZE (ControllerData.Mn) + 1\r
+                   + ARRAY_SIZE (ControllerData.Sn) + 1\r
+                   + MAXIMUM_VALUE_CHARACTERS + 1\r
+                   ) * sizeof (CHAR16));\r
+  if (Description != NULL) {\r
+    Char = Description;\r
+    for (Index = 0; Index < ARRAY_SIZE (ControllerData.Mn); Index++) {\r
+      *(Char++) = (CHAR16) ControllerData.Mn[Index];\r
+    }\r
+    *(Char++) = L' ';\r
+    for (Index = 0; Index < ARRAY_SIZE (ControllerData.Sn); Index++) {\r
+      *(Char++) = (CHAR16) ControllerData.Sn[Index];\r
+    }\r
+    *(Char++) = L' ';\r
+    UnicodeValueToStringS (\r
+      Char, sizeof (CHAR16) * (MAXIMUM_VALUE_CHARACTERS + 1),\r
+      0, DevicePath.NvmeNamespace->NamespaceId, 0\r
+      );\r
+    BmEliminateExtraSpaces (Description);\r
+  }\r
+\r
+  return Description;\r
+}\r
+\r
 /**\r
   Return the boot description for the controller based on the type.\r
 \r
@@ -400,14 +650,6 @@ BmGetMiscDescription (
     }\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
@@ -463,6 +705,9 @@ EfiBootManagerRegisterBootDescriptionHandler (
 BM_GET_BOOT_DESCRIPTION mBmBootDescriptionHandlers[] = {\r
   BmGetUsbDescription,\r
   BmGetDescriptionFromDiskInfo,\r
+  BmGetNetworkDescription,\r
+  BmGetLoadFileDescription,\r
+  BmGetNvmeDescription,\r
   BmGetMiscDescription\r
 };\r
 \r
@@ -489,7 +734,7 @@ BmGetBootDescription (
   // Firstly get the default boot description\r
   //\r
   DefaultDescription = NULL;\r
-  for (Index = 0; Index < sizeof (mBmBootDescriptionHandlers) / sizeof (mBmBootDescriptionHandlers[0]); Index++) {\r
+  for (Index = 0; Index < ARRAY_SIZE (mBmBootDescriptionHandlers); Index++) {\r
     DefaultDescription = mBmBootDescriptionHandlers[Index] (Handle);\r
     if (DefaultDescription != NULL) {\r
       //\r