]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpress.c
MdeModulePkg NvmExpressDxe: Add BlockIo2 support
[mirror_edk2.git] / MdeModulePkg / Bus / Pci / NvmExpressDxe / NvmExpress.c
index ed1c4bf391a962b3f72b46b3d7bef0b5801aad68..cb25b3e08dc62f9a6b2db5fcb56520557406d07a 100644 (file)
@@ -2,7 +2,7 @@
   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
+  Copyright (c) 2013 - 2016, 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
@@ -35,6 +35,15 @@ 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 | EFI_NVM_EXPRESS_PASS_THRU_ATTRIBUTES_LOGICAL | 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 +52,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 +60,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 +74,8 @@ EnumerateNvmeDevNamespace (
   UINT32                                Lbads;\r
   UINT32                                Flbas;\r
   UINT32                                LbaFmtIdx;\r
+  UINT8                                 Sn[21];\r
+  UINT8                                 Mn[41];\r
 \r
   NewDevicePathNode = NULL;\r
   DevicePath        = NULL;\r
@@ -104,6 +112,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 +136,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 +157,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 +185,6 @@ EnumerateNvmeDevNamespace (
     Status = Private->Passthru.BuildDevicePath (\r
                                  &Private->Passthru,\r
                                  Device->NamespaceId,\r
-                                 Device->NamespaceUuid,\r
                                  &NewDevicePathNode\r
                                  );\r
 \r
@@ -197,6 +223,8 @@ EnumerateNvmeDevNamespace (
                     Device->DevicePath,\r
                     &gEfiBlockIoProtocolGuid,\r
                     &Device->BlockIo,\r
+                    &gEfiBlockIo2ProtocolGuid,\r
+                    &Device->BlockIo2,\r
                     &gEfiDiskInfoProtocolGuid,\r
                     &Device->DiskInfo,\r
                     NULL\r
@@ -205,10 +233,38 @@ 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 **) &Private->Passthru,\r
            Private->DriverBindingHandle,\r
            Device->DeviceHandle,\r
            EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER\r
@@ -226,7 +282,11 @@ EnumerateNvmeDevNamespace (
     //\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
@@ -250,6 +310,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
@@ -276,18 +340,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
@@ -296,8 +357,7 @@ DiscoverAllNamespaces (
 \r
     Status = EnumerateNvmeDevNamespace (\r
                Private,\r
-               NamespaceId,\r
-               NamespaceUuid\r
+               NamespaceId\r
                );\r
 \r
     if (EFI_ERROR(Status)) {\r
@@ -330,9 +390,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
+  NVME_CONTROLLER_PRIVATE_DATA             *Private;\r
+  EFI_STORAGE_SECURITY_COMMAND_PROTOCOL    *StorageSecurity;\r
+  BOOLEAN                                  IsEmpty;\r
+  EFI_TPL                                  OldTpl;\r
 \r
   BlockIo = NULL;\r
 \r
@@ -348,14 +411,30 @@ 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
+  Private = Device->Controller;\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
@@ -370,6 +449,8 @@ UnregisterNvmeNamespace (
                   Device->DevicePath,\r
                   &gEfiBlockIoProtocolGuid,\r
                   &Device->BlockIo,\r
+                  &gEfiBlockIo2ProtocolGuid,\r
+                  &Device->BlockIo2,\r
                   &gEfiDiskInfoProtocolGuid,\r
                   &Device->DiskInfo,\r
                   NULL\r
@@ -378,8 +459,8 @@ UnregisterNvmeNamespace (
   if (EFI_ERROR (Status)) {\r
     gBS->OpenProtocol (\r
            Controller,\r
-           &gEfiPciIoProtocolGuid,\r
-           (VOID **) &PciIo,\r
+           &gEfiNvmExpressPassThruProtocolGuid,\r
+           (VOID **) &Private->Passthru,\r
            This->DriverBindingHandle,\r
            Handle,\r
            EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER\r
@@ -387,6 +468,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 **) &Private->Passthru,\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
@@ -395,9 +507,175 @@ 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
+\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
+        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
+    PciIo = Private->PciIo;\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
@@ -471,8 +749,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
@@ -602,18 +880,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
@@ -650,31 +929,33 @@ 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
                       AllocateAnyPages,\r
                       EfiBootServicesData,\r
-                      4,\r
+                      6,\r
                       (VOID**)&Private->Buffer,\r
                       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
@@ -684,12 +965,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
@@ -702,36 +982,61 @@ 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
   } 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
@@ -749,39 +1054,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
@@ -844,31 +1148,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
@@ -928,97 +1257,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