]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpress.c
MdeModulePkg: Replace BSD License with BSD+Patent License
[mirror_edk2.git] / MdeModulePkg / Bus / Pci / NvmExpressDxe / NvmExpress.c
index b6729ce711b311a116efd59a3aed2ffbdbe8d3e3..c6e401176a4b3b1359a41d2fdfff6bfbfa14970d 100644 (file)
@@ -2,14 +2,8 @@
   NvmExpressDxe driver is used to manage non-volatile memory subsystem which follows\r
   NVM Express specification.\r
 \r
-  Copyright (c) 2013, Intel Corporation. All rights reserved.<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
+  Copyright (c) 2013 - 2017, Intel Corporation. All rights reserved.<BR>\r
+  SPDX-License-Identifier: BSD-2-Clause-Patent\r
 \r
 **/\r
 \r
@@ -35,6 +29,18 @@ EFI_DRIVER_SUPPORTED_EFI_VERSION_PROTOCOL gNvmExpressDriverSupportedEfiVersion =
   0                                                   // Version number to be filled at start up.\r
 };\r
 \r
+//\r
+// Template for NVM Express Pass Thru Mode data structure.\r
+//\r
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_NVM_EXPRESS_PASS_THRU_MODE gEfiNvmExpressPassThruMode = {\r
+  EFI_NVM_EXPRESS_PASS_THRU_ATTRIBUTES_PHYSICAL   |\r
+  EFI_NVM_EXPRESS_PASS_THRU_ATTRIBUTES_LOGICAL    |\r
+  EFI_NVM_EXPRESS_PASS_THRU_ATTRIBUTES_NONBLOCKIO |\r
+  EFI_NVM_EXPRESS_PASS_THRU_ATTRIBUTES_CMD_SET_NVM,\r
+  sizeof (UINTN),\r
+  0x10100\r
+};\r
+\r
 /**\r
   Check if the specified Nvm Express device namespace is active, and create child handles\r
   for them with BlockIo and DiskInfo protocol instances.\r
@@ -43,8 +49,6 @@ EFI_DRIVER_SUPPORTED_EFI_VERSION_PROTOCOL gNvmExpressDriverSupportedEfiVersion =
   @param[in] NamespaceId     The NVM Express namespace ID  for which a device path node is to be\r
                              allocated and built. Caller must set the NamespaceId to zero if the\r
                              device path node will contain a valid UUID.\r
-  @param[in] NamespaceUuid   The NVM Express namespace UUID for which a device path node is to be\r
-                             allocated and built. UUID will only be valid of the Namespace ID is zero.\r
 \r
   @retval EFI_SUCCESS        All the namespaces in the device are successfully enumerated.\r
   @return Others             Some error occurs when enumerating the namespaces.\r
@@ -53,8 +57,7 @@ EFI_DRIVER_SUPPORTED_EFI_VERSION_PROTOCOL gNvmExpressDriverSupportedEfiVersion =
 EFI_STATUS\r
 EnumerateNvmeDevNamespace (\r
   IN NVME_CONTROLLER_PRIVATE_DATA       *Private,\r
-  UINT32                                NamespaceId,\r
-  UINT64                                NamespaceUuid\r
+  UINT32                                NamespaceId\r
   )\r
 {\r
   NVME_ADMIN_NAMESPACE_DATA             *NamespaceData;\r
@@ -68,6 +71,9 @@ EnumerateNvmeDevNamespace (
   UINT32                                Lbads;\r
   UINT32                                Flbas;\r
   UINT32                                LbaFmtIdx;\r
+  UINT8                                 Sn[21];\r
+  UINT8                                 Mn[41];\r
+  VOID                                  *DummyInterface;\r
 \r
   NewDevicePathNode = NULL;\r
   DevicePath        = NULL;\r
@@ -104,6 +110,7 @@ EnumerateNvmeDevNamespace (
     //\r
     Device = AllocateZeroPool(sizeof(NVME_DEVICE_PRIVATE_DATA));\r
     if (Device == NULL) {\r
+      Status = EFI_OUT_OF_RESOURCES;\r
       goto Exit;\r
     }\r
 \r
@@ -127,9 +134,10 @@ EnumerateNvmeDevNamespace (
     Device->Media.LogicalPartition = FALSE;\r
     Device->Media.ReadOnly       = FALSE;\r
     Device->Media.WriteCaching   = FALSE;\r
+    Device->Media.IoAlign        = Private->PassThruMode.IoAlign;\r
 \r
     Flbas     = NamespaceData->Flbas;\r
-    LbaFmtIdx = Flbas & 3;\r
+    LbaFmtIdx = Flbas & 0xF;\r
     Lbads     = NamespaceData->LbaFormat[LbaFmtIdx].Lbads;\r
     Device->Media.BlockSize = (UINT32)1 << Lbads;\r
 \r
@@ -147,9 +155,26 @@ EnumerateNvmeDevNamespace (
     Device->BlockIo.WriteBlocks  = NvmeBlockIoWriteBlocks;\r
     Device->BlockIo.FlushBlocks  = NvmeBlockIoFlushBlocks;\r
 \r
+    //\r
+    // Create BlockIo2 Protocol instance\r
+    //\r
+    Device->BlockIo2.Media          = &Device->Media;\r
+    Device->BlockIo2.Reset          = NvmeBlockIoResetEx;\r
+    Device->BlockIo2.ReadBlocksEx   = NvmeBlockIoReadBlocksEx;\r
+    Device->BlockIo2.WriteBlocksEx  = NvmeBlockIoWriteBlocksEx;\r
+    Device->BlockIo2.FlushBlocksEx  = NvmeBlockIoFlushBlocksEx;\r
+    InitializeListHead (&Device->AsyncQueue);\r
+\r
+    //\r
+    // Create StorageSecurityProtocol Instance\r
+    //\r
+    Device->StorageSecurity.ReceiveData = NvmeStorageSecurityReceiveData;\r
+    Device->StorageSecurity.SendData    = NvmeStorageSecuritySendData;\r
+\r
     //\r
     // Create DiskInfo Protocol instance\r
     //\r
+    CopyMem (&Device->NamespaceData, NamespaceData, sizeof (NVME_ADMIN_NAMESPACE_DATA));\r
     InitializeDiskInfo (Device);\r
 \r
     //\r
@@ -158,7 +183,6 @@ EnumerateNvmeDevNamespace (
     Status = Private->Passthru.BuildDevicePath (\r
                                  &Private->Passthru,\r
                                  Device->NamespaceId,\r
-                                 Device->NamespaceUuid,\r
                                  &NewDevicePathNode\r
                                  );\r
 \r
@@ -197,6 +221,8 @@ EnumerateNvmeDevNamespace (
                     Device->DevicePath,\r
                     &gEfiBlockIoProtocolGuid,\r
                     &Device->BlockIo,\r
+                    &gEfiBlockIo2ProtocolGuid,\r
+                    &Device->BlockIo2,\r
                     &gEfiDiskInfoProtocolGuid,\r
                     &Device->DiskInfo,\r
                     NULL\r
@@ -205,19 +231,60 @@ EnumerateNvmeDevNamespace (
     if(EFI_ERROR(Status)) {\r
       goto Exit;\r
     }\r
+\r
+    //\r
+    // Check if the NVMe controller supports the Security Send and Security Receive commands\r
+    //\r
+    if ((Private->ControllerData->Oacs & SECURITY_SEND_RECEIVE_SUPPORTED) != 0) {\r
+      Status = gBS->InstallProtocolInterface (\r
+                      &Device->DeviceHandle,\r
+                      &gEfiStorageSecurityCommandProtocolGuid,\r
+                      EFI_NATIVE_INTERFACE,\r
+                      &Device->StorageSecurity\r
+                      );\r
+      if(EFI_ERROR(Status)) {\r
+        gBS->UninstallMultipleProtocolInterfaces (\r
+               &Device->DeviceHandle,\r
+               &gEfiDevicePathProtocolGuid,\r
+               Device->DevicePath,\r
+               &gEfiBlockIoProtocolGuid,\r
+               &Device->BlockIo,\r
+               &gEfiBlockIo2ProtocolGuid,\r
+               &Device->BlockIo2,\r
+               &gEfiDiskInfoProtocolGuid,\r
+               &Device->DiskInfo,\r
+               NULL\r
+               );\r
+        goto Exit;\r
+      }\r
+    }\r
+\r
     gBS->OpenProtocol (\r
            Private->ControllerHandle,\r
-           &gEfiPciIoProtocolGuid,\r
-           (VOID **) &Private->PciIo,\r
+           &gEfiNvmExpressPassThruProtocolGuid,\r
+           (VOID **) &DummyInterface,\r
            Private->DriverBindingHandle,\r
            Device->DeviceHandle,\r
            EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER\r
            );\r
 \r
+    //\r
+    // Dump NvmExpress Identify Namespace Data\r
+    //\r
+    DEBUG ((EFI_D_INFO, " == NVME IDENTIFY NAMESPACE [%d] DATA ==\n", NamespaceId));\r
+    DEBUG ((EFI_D_INFO, "    NSZE        : 0x%x\n", NamespaceData->Nsze));\r
+    DEBUG ((EFI_D_INFO, "    NCAP        : 0x%x\n", NamespaceData->Ncap));\r
+    DEBUG ((EFI_D_INFO, "    NUSE        : 0x%x\n", NamespaceData->Nuse));\r
+    DEBUG ((EFI_D_INFO, "    LBAF0.LBADS : 0x%x\n", (NamespaceData->LbaFormat[0].Lbads)));\r
+\r
     //\r
     // Build controller name for Component Name (2) protocol.\r
     //\r
-    UnicodeSPrintAsciiFormat (Device->ModelName, sizeof (Device->ModelName), "%a-%a-%x", Private->ControllerData->Sn, Private->ControllerData->Mn, NamespaceData->Eui64);\r
+    CopyMem (Sn, Private->ControllerData->Sn, sizeof (Private->ControllerData->Sn));\r
+    Sn[20] = 0;\r
+    CopyMem (Mn, Private->ControllerData->Mn, sizeof (Private->ControllerData->Mn));\r
+    Mn[40] = 0;\r
+    UnicodeSPrintAsciiFormat (Device->ModelName, sizeof (Device->ModelName), "%a-%a-%x", Sn, Mn, NamespaceData->Eui64);\r
 \r
     AddUnicodeString2 (\r
       "eng",\r
@@ -241,6 +308,10 @@ Exit:
     FreePool (NamespaceData);\r
   }\r
 \r
+  if (NewDevicePathNode != NULL) {\r
+    FreePool (NewDevicePathNode);\r
+  }\r
+\r
   if(EFI_ERROR(Status) && (Device != NULL) && (Device->DevicePath != NULL)) {\r
     FreePool (Device->DevicePath);\r
   }\r
@@ -267,18 +338,15 @@ DiscoverAllNamespaces (
 {\r
   EFI_STATUS                            Status;\r
   UINT32                                NamespaceId;\r
-  UINT64                                NamespaceUuid;\r
-  NVM_EXPRESS_PASS_THRU_PROTOCOL        *Passthru;\r
+  EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL    *Passthru;\r
 \r
   NamespaceId   = 0xFFFFFFFF;\r
-  NamespaceUuid = 0;\r
   Passthru      = &Private->Passthru;\r
 \r
   while (TRUE) {\r
     Status = Passthru->GetNextNamespace (\r
                          Passthru,\r
-                         (UINT32 *)&NamespaceId,\r
-                         (UINT64 *)&NamespaceUuid\r
+                         (UINT32 *)&NamespaceId\r
                          );\r
 \r
     if (EFI_ERROR (Status)) {\r
@@ -287,8 +355,7 @@ DiscoverAllNamespaces (
 \r
     Status = EnumerateNvmeDevNamespace (\r
                Private,\r
-               NamespaceId,\r
-               NamespaceUuid\r
+               NamespaceId\r
                );\r
 \r
     if (EFI_ERROR(Status)) {\r
@@ -321,9 +388,12 @@ UnregisterNvmeNamespace (
   )\r
 {\r
   EFI_STATUS                               Status;\r
-  EFI_PCI_IO_PROTOCOL                      *PciIo;\r
   EFI_BLOCK_IO_PROTOCOL                    *BlockIo;\r
   NVME_DEVICE_PRIVATE_DATA                 *Device;\r
+  EFI_STORAGE_SECURITY_COMMAND_PROTOCOL    *StorageSecurity;\r
+  BOOLEAN                                  IsEmpty;\r
+  EFI_TPL                                  OldTpl;\r
+  VOID                                     *DummyInterface;\r
 \r
   BlockIo = NULL;\r
 \r
@@ -339,14 +409,29 @@ UnregisterNvmeNamespace (
     return Status;\r
   }\r
 \r
-  Device = NVME_DEVICE_PRIVATE_DATA_FROM_BLOCK_IO (BlockIo);\r
+  Device  = NVME_DEVICE_PRIVATE_DATA_FROM_BLOCK_IO (BlockIo);\r
+\r
+  //\r
+  // Wait for the device's asynchronous I/O queue to become empty.\r
+  //\r
+  while (TRUE) {\r
+    OldTpl  = gBS->RaiseTPL (TPL_NOTIFY);\r
+    IsEmpty = IsListEmpty (&Device->AsyncQueue);\r
+    gBS->RestoreTPL (OldTpl);\r
+\r
+    if (IsEmpty) {\r
+      break;\r
+    }\r
+\r
+    gBS->Stall (100);\r
+  }\r
 \r
   //\r
   // Close the child handle\r
   //\r
   gBS->CloseProtocol (\r
          Controller,\r
-         &gEfiPciIoProtocolGuid,\r
+         &gEfiNvmExpressPassThruProtocolGuid,\r
          This->DriverBindingHandle,\r
          Handle\r
          );\r
@@ -361,6 +446,8 @@ UnregisterNvmeNamespace (
                   Device->DevicePath,\r
                   &gEfiBlockIoProtocolGuid,\r
                   &Device->BlockIo,\r
+                  &gEfiBlockIo2ProtocolGuid,\r
+                  &Device->BlockIo2,\r
                   &gEfiDiskInfoProtocolGuid,\r
                   &Device->DiskInfo,\r
                   NULL\r
@@ -369,8 +456,8 @@ UnregisterNvmeNamespace (
   if (EFI_ERROR (Status)) {\r
     gBS->OpenProtocol (\r
            Controller,\r
-           &gEfiPciIoProtocolGuid,\r
-           (VOID **) &PciIo,\r
+           &gEfiNvmExpressPassThruProtocolGuid,\r
+           (VOID **) &DummyInterface,\r
            This->DriverBindingHandle,\r
            Handle,\r
            EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER\r
@@ -378,6 +465,37 @@ UnregisterNvmeNamespace (
     return Status;\r
   }\r
 \r
+  //\r
+  // If Storage Security Command Protocol is installed, then uninstall this protocol.\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+                  Handle,\r
+                  &gEfiStorageSecurityCommandProtocolGuid,\r
+                  (VOID **) &StorageSecurity,\r
+                  This->DriverBindingHandle,\r
+                  Controller,\r
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+                  );\r
+\r
+  if (!EFI_ERROR (Status)) {\r
+    Status = gBS->UninstallProtocolInterface (\r
+                    Handle,\r
+                    &gEfiStorageSecurityCommandProtocolGuid,\r
+                    &Device->StorageSecurity\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      gBS->OpenProtocol (\r
+        Controller,\r
+        &gEfiNvmExpressPassThruProtocolGuid,\r
+        (VOID **) &DummyInterface,\r
+        This->DriverBindingHandle,\r
+        Handle,\r
+        EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER\r
+        );\r
+      return Status;\r
+    }\r
+  }\r
+\r
   if(Device->DevicePath != NULL) {\r
     FreePool (Device->DevicePath);\r
   }\r
@@ -386,9 +504,195 @@ UnregisterNvmeNamespace (
     FreeUnicodeStringTable (Device->ControllerNameTable);\r
   }\r
 \r
+  FreePool (Device);\r
+\r
   return EFI_SUCCESS;\r
 }\r
 \r
+/**\r
+  Call back function when the timer event is signaled.\r
+\r
+  @param[in]  Event     The Event this notify function registered to.\r
+  @param[in]  Context   Pointer to the context data registered to the\r
+                        Event.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+ProcessAsyncTaskList (\r
+  IN EFI_EVENT                    Event,\r
+  IN VOID*                        Context\r
+  )\r
+{\r
+  NVME_CONTROLLER_PRIVATE_DATA         *Private;\r
+  EFI_PCI_IO_PROTOCOL                  *PciIo;\r
+  NVME_CQ                              *Cq;\r
+  UINT16                               QueueId;\r
+  UINT32                               Data;\r
+  LIST_ENTRY                           *Link;\r
+  LIST_ENTRY                           *NextLink;\r
+  NVME_PASS_THRU_ASYNC_REQ             *AsyncRequest;\r
+  NVME_BLKIO2_SUBTASK                  *Subtask;\r
+  NVME_BLKIO2_REQUEST                  *BlkIo2Request;\r
+  EFI_BLOCK_IO2_TOKEN                  *Token;\r
+  BOOLEAN                              HasNewItem;\r
+  EFI_STATUS                           Status;\r
+\r
+  Private    = (NVME_CONTROLLER_PRIVATE_DATA*)Context;\r
+  QueueId    = 2;\r
+  Cq         = Private->CqBuffer[QueueId] + Private->CqHdbl[QueueId].Cqh;\r
+  HasNewItem = FALSE;\r
+  PciIo      = Private->PciIo;\r
+\r
+  //\r
+  // Submit asynchronous subtasks to the NVMe Submission Queue\r
+  //\r
+  for (Link = GetFirstNode (&Private->UnsubmittedSubtasks);\r
+       !IsNull (&Private->UnsubmittedSubtasks, Link);\r
+       Link = NextLink) {\r
+    NextLink      = GetNextNode (&Private->UnsubmittedSubtasks, Link);\r
+    Subtask       = NVME_BLKIO2_SUBTASK_FROM_LINK (Link);\r
+    BlkIo2Request = Subtask->BlockIo2Request;\r
+    Token         = BlkIo2Request->Token;\r
+    RemoveEntryList (Link);\r
+    BlkIo2Request->UnsubmittedSubtaskNum--;\r
+\r
+    //\r
+    // If any previous subtask fails, do not process subsequent ones.\r
+    //\r
+    if (Token->TransactionStatus != EFI_SUCCESS) {\r
+      if (IsListEmpty (&BlkIo2Request->SubtasksQueue) &&\r
+          BlkIo2Request->LastSubtaskSubmitted &&\r
+          (BlkIo2Request->UnsubmittedSubtaskNum == 0)) {\r
+        //\r
+        // Remove the BlockIo2 request from the device asynchronous queue.\r
+        //\r
+        RemoveEntryList (&BlkIo2Request->Link);\r
+        FreePool (BlkIo2Request);\r
+        gBS->SignalEvent (Token->Event);\r
+      }\r
+\r
+      FreePool (Subtask->CommandPacket->NvmeCmd);\r
+      FreePool (Subtask->CommandPacket->NvmeCompletion);\r
+      FreePool (Subtask->CommandPacket);\r
+      FreePool (Subtask);\r
+\r
+      continue;\r
+    }\r
+\r
+    Status = Private->Passthru.PassThru (\r
+                                 &Private->Passthru,\r
+                                 Subtask->NamespaceId,\r
+                                 Subtask->CommandPacket,\r
+                                 Subtask->Event\r
+                                 );\r
+    if (Status == EFI_NOT_READY) {\r
+      InsertHeadList (&Private->UnsubmittedSubtasks, Link);\r
+      BlkIo2Request->UnsubmittedSubtaskNum++;\r
+      break;\r
+    } else if (EFI_ERROR (Status)) {\r
+      Token->TransactionStatus = EFI_DEVICE_ERROR;\r
+\r
+      if (IsListEmpty (&BlkIo2Request->SubtasksQueue) &&\r
+          Subtask->IsLast) {\r
+        //\r
+        // Remove the BlockIo2 request from the device asynchronous queue.\r
+        //\r
+        RemoveEntryList (&BlkIo2Request->Link);\r
+        FreePool (BlkIo2Request);\r
+        gBS->SignalEvent (Token->Event);\r
+      }\r
+\r
+      FreePool (Subtask->CommandPacket->NvmeCmd);\r
+      FreePool (Subtask->CommandPacket->NvmeCompletion);\r
+      FreePool (Subtask->CommandPacket);\r
+      FreePool (Subtask);\r
+    } else {\r
+      InsertTailList (&BlkIo2Request->SubtasksQueue, Link);\r
+      if (Subtask->IsLast) {\r
+        BlkIo2Request->LastSubtaskSubmitted = TRUE;\r
+      }\r
+    }\r
+  }\r
+\r
+  while (Cq->Pt != Private->Pt[QueueId]) {\r
+    ASSERT (Cq->Sqid == QueueId);\r
+\r
+    HasNewItem = TRUE;\r
+\r
+    //\r
+    // Find the command with given Command Id.\r
+    //\r
+    for (Link = GetFirstNode (&Private->AsyncPassThruQueue);\r
+         !IsNull (&Private->AsyncPassThruQueue, Link);\r
+         Link = NextLink) {\r
+      NextLink = GetNextNode (&Private->AsyncPassThruQueue, Link);\r
+      AsyncRequest = NVME_PASS_THRU_ASYNC_REQ_FROM_THIS (Link);\r
+      if (AsyncRequest->CommandId == Cq->Cid) {\r
+        //\r
+        // Copy the Respose Queue entry for this command to the callers\r
+        // response buffer.\r
+        //\r
+        CopyMem (\r
+          AsyncRequest->Packet->NvmeCompletion,\r
+          Cq,\r
+          sizeof(EFI_NVM_EXPRESS_COMPLETION)\r
+          );\r
+\r
+        //\r
+        // Free the resources allocated before cmd submission\r
+        //\r
+        if (AsyncRequest->MapData != NULL) {\r
+          PciIo->Unmap (PciIo, AsyncRequest->MapData);\r
+        }\r
+        if (AsyncRequest->MapMeta != NULL) {\r
+          PciIo->Unmap (PciIo, AsyncRequest->MapMeta);\r
+        }\r
+        if (AsyncRequest->MapPrpList != NULL) {\r
+          PciIo->Unmap (PciIo, AsyncRequest->MapPrpList);\r
+        }\r
+        if (AsyncRequest->PrpListHost != NULL) {\r
+          PciIo->FreeBuffer (\r
+                   PciIo,\r
+                   AsyncRequest->PrpListNo,\r
+                   AsyncRequest->PrpListHost\r
+                   );\r
+        }\r
+\r
+        RemoveEntryList (Link);\r
+        gBS->SignalEvent (AsyncRequest->CallerEvent);\r
+        FreePool (AsyncRequest);\r
+\r
+        //\r
+        // Update submission queue head.\r
+        //\r
+        Private->AsyncSqHead = Cq->Sqhd;\r
+        break;\r
+      }\r
+    }\r
+\r
+    Private->CqHdbl[QueueId].Cqh++;\r
+    if (Private->CqHdbl[QueueId].Cqh > NVME_ASYNC_CCQ_SIZE) {\r
+      Private->CqHdbl[QueueId].Cqh = 0;\r
+      Private->Pt[QueueId] ^= 1;\r
+    }\r
+\r
+    Cq = Private->CqBuffer[QueueId] + Private->CqHdbl[QueueId].Cqh;\r
+  }\r
+\r
+  if (HasNewItem) {\r
+    Data  = ReadUnaligned32 ((UINT32*)&Private->CqHdbl[QueueId]);\r
+    PciIo->Mem.Write (\r
+                 PciIo,\r
+                 EfiPciIoWidthUint32,\r
+                 NVME_BAR,\r
+                 NVME_CQHDBL_OFFSET(QueueId, Private->Cap.Dstrd),\r
+                 1,\r
+                 &Data\r
+                 );\r
+  }\r
+}\r
+\r
 /**\r
   Tests to see if this driver supports a given controller. If a child device is provided,\r
   it further tests to see if this driver supports creating a handle for the specified child device.\r
@@ -462,8 +766,8 @@ NvmExpressDriverBindingSupported (
 \r
       if ((DevicePathNode.DevPath->Type    != MESSAGING_DEVICE_PATH) ||\r
           (DevicePathNode.DevPath->SubType != MSG_NVME_NAMESPACE_DP) ||\r
-           DevicePathNodeLength(DevicePathNode.DevPath) != sizeof(NVME_NAMESPACE_DEVICE_PATH)) {\r
-        return EFI_UNSUPPORTED;\r
+          (DevicePathNodeLength(DevicePathNode.DevPath) != sizeof(NVME_NAMESPACE_DEVICE_PATH))) {\r
+         return EFI_UNSUPPORTED;\r
       }\r
     }\r
   }\r
@@ -593,18 +897,19 @@ NvmExpressDriverBindingStart (
   IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath\r
   )\r
 {\r
-  EFI_STATUS                        Status;\r
-  EFI_PCI_IO_PROTOCOL               *PciIo;\r
-  NVME_CONTROLLER_PRIVATE_DATA      *Private;\r
-  EFI_DEVICE_PATH_PROTOCOL          *ParentDevicePath;\r
-  UINT32                            NamespaceId;\r
-  UINT64                            NamespaceUuid;\r
-  EFI_PHYSICAL_ADDRESS              MappedAddr;\r
-  UINTN                             Bytes;\r
+  EFI_STATUS                          Status;\r
+  EFI_PCI_IO_PROTOCOL                 *PciIo;\r
+  NVME_CONTROLLER_PRIVATE_DATA        *Private;\r
+  EFI_DEVICE_PATH_PROTOCOL            *ParentDevicePath;\r
+  UINT32                              NamespaceId;\r
+  EFI_PHYSICAL_ADDRESS                MappedAddr;\r
+  UINTN                               Bytes;\r
+  EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL  *Passthru;\r
 \r
   DEBUG ((EFI_D_INFO, "NvmExpressDriverBindingStart: start\n"));\r
 \r
-  Private = NULL;\r
+  Private          = NULL;\r
+  Passthru         = NULL;\r
   ParentDevicePath = NULL;\r
 \r
   Status = gBS->OpenProtocol (\r
@@ -641,17 +946,19 @@ NvmExpressDriverBindingStart (
     if (Private == NULL) {\r
       DEBUG ((EFI_D_ERROR, "NvmExpressDriverBindingStart: allocating pool for Nvme Private Data failed!\n"));\r
       Status = EFI_OUT_OF_RESOURCES;\r
-      goto Exit2;\r
+      goto Exit;\r
     }\r
 \r
     //\r
-    // 4 x 4kB aligned buffers will be carved out of this buffer.\r
+    // 6 x 4kB aligned buffers will be carved out of this buffer.\r
     // 1st 4kB boundary is the start of the admin submission queue.\r
     // 2nd 4kB boundary is the start of the admin completion queue.\r
     // 3rd 4kB boundary is the start of I/O submission queue #1.\r
     // 4th 4kB boundary is the start of I/O completion queue #1.\r
+    // 5th 4kB boundary is the start of I/O submission queue #2.\r
+    // 6th 4kB boundary is the start of I/O completion queue #2.\r
     //\r
-    // Allocate 4 pages of memory, then map it for bus master read and write.\r
+    // Allocate 6 pages of memory, then map it for bus master read and write.\r
     //\r
     Status = PciIo->AllocateBuffer (\r
                       PciIo,\r
@@ -662,10 +969,10 @@ NvmExpressDriverBindingStart (
                       0\r
                       );\r
     if (EFI_ERROR (Status)) {\r
-      goto Exit2;\r
+      goto Exit;\r
     }\r
 \r
-    Bytes = EFI_PAGES_TO_SIZE (4);\r
+    Bytes = EFI_PAGES_TO_SIZE (6);\r
     Status = PciIo->Map (\r
                       PciIo,\r
                       EfiPciIoOperationBusMasterCommonBuffer,\r
@@ -675,12 +982,11 @@ NvmExpressDriverBindingStart (
                       &Private->Mapping\r
                       );\r
 \r
-    if (EFI_ERROR (Status) || (Bytes != EFI_PAGES_TO_SIZE (4))) {\r
-      goto Exit2;\r
+    if (EFI_ERROR (Status) || (Bytes != EFI_PAGES_TO_SIZE (6))) {\r
+      goto Exit;\r
     }\r
 \r
     Private->BufferPciAddr = (UINT8 *)(UINTN)MappedAddr;\r
-    ZeroMem (Private->Buffer, EFI_PAGES_TO_SIZE (4));\r
 \r
     Private->Signature = NVME_CONTROLLER_PRIVATE_DATA_SIGNATURE;\r
     Private->ControllerHandle          = Controller;\r
@@ -693,36 +999,63 @@ NvmExpressDriverBindingStart (
     Private->Passthru.GetNextNamespace = NvmExpressGetNextNamespace;\r
     Private->Passthru.BuildDevicePath  = NvmExpressBuildDevicePath;\r
     Private->Passthru.GetNamespace     = NvmExpressGetNamespace;\r
-    Private->PassThruMode.Attributes   = NVM_EXPRESS_PASS_THRU_ATTRIBUTES_PHYSICAL;\r
+    CopyMem (&Private->PassThruMode, &gEfiNvmExpressPassThruMode, sizeof (EFI_NVM_EXPRESS_PASS_THRU_MODE));\r
+    InitializeListHead (&Private->AsyncPassThruQueue);\r
+    InitializeListHead (&Private->UnsubmittedSubtasks);\r
 \r
     Status = NvmeControllerInit (Private);\r
-\r
     if (EFI_ERROR(Status)) {\r
-      goto Exit2;\r
+      goto Exit;\r
+    }\r
+\r
+    //\r
+    // Start the asynchronous I/O completion monitor\r
+    //\r
+    Status = gBS->CreateEvent (\r
+                    EVT_TIMER | EVT_NOTIFY_SIGNAL,\r
+                    TPL_NOTIFY,\r
+                    ProcessAsyncTaskList,\r
+                    Private,\r
+                    &Private->TimerEvent\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      goto Exit;\r
+    }\r
+\r
+    Status = gBS->SetTimer (\r
+                    Private->TimerEvent,\r
+                    TimerPeriodic,\r
+                    NVME_HC_ASYNC_TIMER\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      goto Exit;\r
     }\r
 \r
     Status = gBS->InstallMultipleProtocolInterfaces (\r
                     &Controller,\r
-                    &gEfiCallerIdGuid,\r
-                    Private,\r
+                    &gEfiNvmExpressPassThruProtocolGuid,\r
+                    &Private->Passthru,\r
                     NULL\r
                     );\r
     if (EFI_ERROR (Status)) {\r
-      goto Exit2;\r
+      goto Exit;\r
     }\r
+\r
+    NvmeRegisterShutdownNotification ();\r
   } else {\r
     Status = gBS->OpenProtocol (\r
                     Controller,\r
-                    &gEfiCallerIdGuid,\r
-                    (VOID **) &Private,\r
+                    &gEfiNvmExpressPassThruProtocolGuid,\r
+                    (VOID **) &Passthru,\r
                     This->DriverBindingHandle,\r
                     Controller,\r
                     EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
                     );\r
     if (EFI_ERROR (Status)) {\r
-      Private = NULL;\r
-      goto Exit1;\r
+      goto Exit;\r
     }\r
+\r
+    Private = NVME_CONTROLLER_PRIVATE_DATA_FROM_PASS_THRU (Passthru);\r
   }\r
 \r
   if (RemainingDevicePath == NULL) {\r
@@ -740,39 +1073,38 @@ NvmExpressDriverBindingStart (
     Status = Private->Passthru.GetNamespace (\r
                                  &Private->Passthru,\r
                                  RemainingDevicePath,\r
-                                 &NamespaceId,\r
-                                 &NamespaceUuid\r
+                                 &NamespaceId\r
                                  );\r
 \r
     if (!EFI_ERROR (Status)) {\r
-        Status = EnumerateNvmeDevNamespace (\r
-                   Private,\r
-                   NamespaceId,\r
-                   NamespaceUuid\r
-                   );\r
+      Status = EnumerateNvmeDevNamespace (\r
+                 Private,\r
+                 NamespaceId\r
+                 );\r
     }\r
   }\r
 \r
   DEBUG ((EFI_D_INFO, "NvmExpressDriverBindingStart: end successfully\n"));\r
   return EFI_SUCCESS;\r
 \r
-Exit1:\r
-  gBS->UninstallMultipleProtocolInterfaces (\r
-         Controller,\r
-         &gEfiCallerIdGuid,\r
-         Private,\r
-         NULL\r
-         );\r
-Exit2:\r
+Exit:\r
   if ((Private != NULL) && (Private->Mapping != NULL)) {\r
     PciIo->Unmap (PciIo, Private->Mapping);\r
   }\r
 \r
   if ((Private != NULL) && (Private->Buffer != NULL)) {\r
-    PciIo->FreeBuffer (PciIo, 4, Private->Buffer);\r
+    PciIo->FreeBuffer (PciIo, 6, Private->Buffer);\r
+  }\r
+\r
+  if ((Private != NULL) && (Private->ControllerData != NULL)) {\r
+    FreePool (Private->ControllerData);\r
   }\r
 \r
   if (Private != NULL) {\r
+    if (Private->TimerEvent != NULL) {\r
+      gBS->CloseEvent (Private->TimerEvent);\r
+    }\r
+\r
     FreePool (Private);\r
   }\r
 \r
@@ -835,31 +1167,56 @@ NvmExpressDriverBindingStop (
   BOOLEAN                             AllChildrenStopped;\r
   UINTN                               Index;\r
   NVME_CONTROLLER_PRIVATE_DATA        *Private;\r
+  EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL  *PassThru;\r
+  BOOLEAN                             IsEmpty;\r
+  EFI_TPL                             OldTpl;\r
 \r
   if (NumberOfChildren == 0) {\r
     Status = gBS->OpenProtocol (\r
                     Controller,\r
-                    &gEfiCallerIdGuid,\r
-                    (VOID **) &Private,\r
+                    &gEfiNvmExpressPassThruProtocolGuid,\r
+                    (VOID **) &PassThru,\r
                     This->DriverBindingHandle,\r
                     Controller,\r
                     EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
                     );\r
 \r
     if (!EFI_ERROR (Status)) {\r
+      Private = NVME_CONTROLLER_PRIVATE_DATA_FROM_PASS_THRU (PassThru);\r
+\r
+      //\r
+      // Wait for the asynchronous PassThru queue to become empty.\r
+      //\r
+      while (TRUE) {\r
+        OldTpl  = gBS->RaiseTPL (TPL_NOTIFY);\r
+        IsEmpty = IsListEmpty (&Private->AsyncPassThruQueue) &&\r
+                  IsListEmpty (&Private->UnsubmittedSubtasks);\r
+        gBS->RestoreTPL (OldTpl);\r
+\r
+        if (IsEmpty) {\r
+          break;\r
+        }\r
+\r
+        gBS->Stall (100);\r
+      }\r
+\r
       gBS->UninstallMultipleProtocolInterfaces (\r
             Controller,\r
-            &gEfiCallerIdGuid,\r
-            Private,\r
+            &gEfiNvmExpressPassThruProtocolGuid,\r
+            PassThru,\r
             NULL\r
             );\r
 \r
+      if (Private->TimerEvent != NULL) {\r
+        gBS->CloseEvent (Private->TimerEvent);\r
+      }\r
+\r
       if (Private->Mapping != NULL) {\r
         Private->PciIo->Unmap (Private->PciIo, Private->Mapping);\r
       }\r
 \r
       if (Private->Buffer != NULL) {\r
-        Private->PciIo->FreeBuffer (Private->PciIo, 4, Private->Buffer);\r
+        Private->PciIo->FreeBuffer (Private->PciIo, 6, Private->Buffer);\r
       }\r
 \r
       FreePool (Private->ControllerData);\r
@@ -878,6 +1235,9 @@ NvmExpressDriverBindingStop (
           This->DriverBindingHandle,\r
           Controller\r
           );\r
+\r
+    NvmeUnregisterShutdownNotification ();\r
+\r
     return EFI_SUCCESS;\r
   }\r
 \r
@@ -919,97 +1279,102 @@ NvmExpressUnload (
   EFI_HANDLE                        *DeviceHandleBuffer;\r
   UINTN                             DeviceHandleCount;\r
   UINTN                             Index;\r
-  EFI_DRIVER_BINDING_PROTOCOL       *DriverBinding;\r
   EFI_COMPONENT_NAME_PROTOCOL       *ComponentName;\r
   EFI_COMPONENT_NAME2_PROTOCOL      *ComponentName2;\r
 \r
   //\r
-  // Get the list of all the handles in the handle database.\r
-  // If there is an error getting the list, then the unload\r
-  // operation fails.\r
+  // Get the list of the device handles managed by this driver.\r
+  // If there is an error getting the list, then means the driver\r
+  // doesn't manage any device. At this way, we would only close\r
+  // those protocols installed at image handle.\r
   //\r
+  DeviceHandleBuffer = NULL;\r
   Status = gBS->LocateHandleBuffer (\r
-                  AllHandles,\r
-                  NULL,\r
+                  ByProtocol,\r
+                  &gEfiNvmExpressPassThruProtocolGuid,\r
                   NULL,\r
                   &DeviceHandleCount,\r
                   &DeviceHandleBuffer\r
                   );\r
 \r
-  if (EFI_ERROR (Status)) {\r
-    return Status;\r
+  if (!EFI_ERROR (Status)) {\r
+    //\r
+    // Disconnect the driver specified by ImageHandle from all\r
+    // the devices in the handle database.\r
+    //\r
+    for (Index = 0; Index < DeviceHandleCount; Index++) {\r
+      Status = gBS->DisconnectController (\r
+                      DeviceHandleBuffer[Index],\r
+                      ImageHandle,\r
+                      NULL\r
+                      );\r
+      if (EFI_ERROR (Status)) {\r
+        goto EXIT;\r
+      }\r
+    }\r
   }\r
 \r
   //\r
-  // Disconnect the driver specified by ImageHandle from all\r
-  // the devices in the handle database.\r
+  // Uninstall all the protocols installed in the driver entry point\r
   //\r
-  for (Index = 0; Index < DeviceHandleCount; Index++) {\r
-    Status = gBS->DisconnectController (\r
-                    DeviceHandleBuffer[Index],\r
-                    ImageHandle,\r
-                    NULL\r
-                    );\r
+  Status = gBS->UninstallMultipleProtocolInterfaces (\r
+                  ImageHandle,\r
+                  &gEfiDriverBindingProtocolGuid,\r
+                  &gNvmExpressDriverBinding,\r
+                  &gEfiDriverSupportedEfiVersionProtocolGuid,\r
+                  &gNvmExpressDriverSupportedEfiVersion,\r
+                  NULL\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto EXIT;\r
   }\r
 \r
   //\r
-  // Uninstall all the protocols installed in the driver entry point\r
+  // Note we have to one by one uninstall the following protocols.\r
+  // It's because some of them are optionally installed based on\r
+  // the following PCD settings.\r
+  //   gEfiMdePkgTokenSpaceGuid.PcdDriverDiagnosticsDisable\r
+  //   gEfiMdePkgTokenSpaceGuid.PcdComponentNameDisable\r
+  //   gEfiMdePkgTokenSpaceGuid.PcdDriverDiagnostics2Disable\r
+  //   gEfiMdePkgTokenSpaceGuid.PcdComponentName2Disable\r
   //\r
-  for (Index = 0; Index < DeviceHandleCount; Index++) {\r
-    Status = gBS->HandleProtocol (\r
-                    DeviceHandleBuffer[Index],\r
-                    &gEfiDriverBindingProtocolGuid,\r
-                    (VOID **) &DriverBinding\r
-                    );\r
-\r
-    if (EFI_ERROR (Status)) {\r
-      continue;\r
-    }\r
-\r
-    if (DriverBinding->ImageHandle != ImageHandle) {\r
-      continue;\r
-    }\r
-\r
+  Status = gBS->HandleProtocol (\r
+                  ImageHandle,\r
+                  &gEfiComponentNameProtocolGuid,\r
+                  (VOID **) &ComponentName\r
+                  );\r
+  if (!EFI_ERROR (Status)) {\r
     gBS->UninstallProtocolInterface (\r
            ImageHandle,\r
-           &gEfiDriverBindingProtocolGuid,\r
-           DriverBinding\r
+           &gEfiComponentNameProtocolGuid,\r
+           ComponentName\r
            );\r
+  }\r
 \r
-    Status = gBS->HandleProtocol (\r
-                    DeviceHandleBuffer[Index],\r
-                    &gEfiComponentNameProtocolGuid,\r
-                    (VOID **) &ComponentName\r
-                    );\r
-    if (!EFI_ERROR (Status)) {\r
-      gBS->UninstallProtocolInterface (\r
-             ImageHandle,\r
-             &gEfiComponentNameProtocolGuid,\r
-             ComponentName\r
-             );\r
-    }\r
-\r
-    Status = gBS->HandleProtocol (\r
-                    DeviceHandleBuffer[Index],\r
-                    &gEfiComponentName2ProtocolGuid,\r
-                    (VOID **) &ComponentName2\r
-                    );\r
-    if (!EFI_ERROR (Status)) {\r
-      gBS->UninstallProtocolInterface (\r
-             ImageHandle,\r
-             &gEfiComponentName2ProtocolGuid,\r
-             ComponentName2\r
-             );\r
-    }\r
+  Status = gBS->HandleProtocol (\r
+                  ImageHandle,\r
+                  &gEfiComponentName2ProtocolGuid,\r
+                  (VOID **) &ComponentName2\r
+                  );\r
+  if (!EFI_ERROR (Status)) {\r
+    gBS->UninstallProtocolInterface (\r
+           ImageHandle,\r
+           &gEfiComponentName2ProtocolGuid,\r
+           ComponentName2\r
+           );\r
   }\r
 \r
+  Status = EFI_SUCCESS;\r
+\r
+EXIT:\r
   //\r
   // Free the buffer containing the list of handles from the handle database\r
   //\r
   if (DeviceHandleBuffer != NULL) {\r
     gBS->FreePool (DeviceHandleBuffer);\r
   }\r
-  return EFI_SUCCESS;\r
+  return Status;\r
 }\r
 \r
 /**\r