]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressHci.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[mirror_edk2.git] / MdeModulePkg / Bus / Pci / NvmExpressDxe / NvmExpressHci.c
index 009ad7c43dcfdff29415e9ba779f271047e31256..b90c48731ca1824dd1a71268dd3ef77294ddd778 100644 (file)
@@ -2,19 +2,21 @@
   NvmExpressDxe driver is used to manage non-volatile memory subsystem which follows\r
   NVM Express specification.\r
 \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
-  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 - 2019, Intel Corporation. All rights reserved.<BR>\r
+  SPDX-License-Identifier: BSD-2-Clause-Patent\r
 \r
 **/\r
 \r
 #include "NvmExpress.h"\r
 \r
+#define NVME_SHUTDOWN_PROCESS_TIMEOUT  45\r
+\r
+//\r
+// The number of NVME controllers managed by this driver, used by\r
+// NvmeRegisterShutdownNotification() and NvmeUnregisterShutdownNotification().\r
+//\r
+UINTN  mNvmeControllerNumber = 0;\r
+\r
 /**\r
   Read Nvm Express controller capability register.\r
 \r
 **/\r
 EFI_STATUS\r
 ReadNvmeControllerCapabilities (\r
-  IN NVME_CONTROLLER_PRIVATE_DATA     *Private,\r
-  IN NVME_CAP                         *Cap\r
+  IN NVME_CONTROLLER_PRIVATE_DATA  *Private,\r
+  IN NVME_CAP                      *Cap\r
   )\r
 {\r
-  EFI_PCI_IO_PROTOCOL   *PciIo;\r
-  EFI_STATUS            Status;\r
-  UINT64                Data;\r
+  EFI_PCI_IO_PROTOCOL  *PciIo;\r
+  EFI_STATUS           Status;\r
+  UINT64               Data;\r
 \r
   PciIo  = Private->PciIo;\r
   Status = PciIo->Mem.Read (\r
@@ -45,11 +47,11 @@ ReadNvmeControllerCapabilities (
                         &Data\r
                         );\r
 \r
-  if (EFI_ERROR(Status)) {\r
+  if (EFI_ERROR (Status)) {\r
     return Status;\r
   }\r
 \r
-  WriteUnaligned64 ((UINT64*)Cap, Data);\r
+  WriteUnaligned64 ((UINT64 *)Cap, Data);\r
   return EFI_SUCCESS;\r
 }\r
 \r
@@ -65,13 +67,13 @@ ReadNvmeControllerCapabilities (
 **/\r
 EFI_STATUS\r
 ReadNvmeControllerConfiguration (\r
-  IN NVME_CONTROLLER_PRIVATE_DATA     *Private,\r
-  IN NVME_CC                          *Cc\r
+  IN NVME_CONTROLLER_PRIVATE_DATA  *Private,\r
+  IN NVME_CC                       *Cc\r
   )\r
 {\r
-  EFI_PCI_IO_PROTOCOL   *PciIo;\r
-  EFI_STATUS            Status;\r
-  UINT32                Data;\r
+  EFI_PCI_IO_PROTOCOL  *PciIo;\r
+  EFI_STATUS           Status;\r
+  UINT32               Data;\r
 \r
   PciIo  = Private->PciIo;\r
   Status = PciIo->Mem.Read (\r
@@ -83,11 +85,11 @@ ReadNvmeControllerConfiguration (
                         &Data\r
                         );\r
 \r
-  if (EFI_ERROR(Status)) {\r
+  if (EFI_ERROR (Status)) {\r
     return Status;\r
   }\r
 \r
-  WriteUnaligned32 ((UINT32*)Cc, Data);\r
+  WriteUnaligned32 ((UINT32 *)Cc, Data);\r
   return EFI_SUCCESS;\r
 }\r
 \r
@@ -103,16 +105,16 @@ ReadNvmeControllerConfiguration (
 **/\r
 EFI_STATUS\r
 WriteNvmeControllerConfiguration (\r
-  IN NVME_CONTROLLER_PRIVATE_DATA     *Private,\r
-  IN NVME_CC                          *Cc\r
+  IN NVME_CONTROLLER_PRIVATE_DATA  *Private,\r
+  IN NVME_CC                       *Cc\r
   )\r
 {\r
-  EFI_PCI_IO_PROTOCOL   *PciIo;\r
-  EFI_STATUS            Status;\r
-  UINT32                Data;\r
+  EFI_PCI_IO_PROTOCOL  *PciIo;\r
+  EFI_STATUS           Status;\r
+  UINT32               Data;\r
 \r
   PciIo  = Private->PciIo;\r
-  Data   = ReadUnaligned32 ((UINT32*)Cc);\r
+  Data   = ReadUnaligned32 ((UINT32 *)Cc);\r
   Status = PciIo->Mem.Write (\r
                         PciIo,\r
                         EfiPciIoWidthUint32,\r
@@ -122,17 +124,17 @@ WriteNvmeControllerConfiguration (
                         &Data\r
                         );\r
 \r
-  if (EFI_ERROR(Status)) {\r
+  if (EFI_ERROR (Status)) {\r
     return Status;\r
   }\r
 \r
-  DEBUG ((EFI_D_INFO, "Cc.En: %d\n", Cc->En));\r
-  DEBUG ((EFI_D_INFO, "Cc.Css: %d\n", Cc->Css));\r
-  DEBUG ((EFI_D_INFO, "Cc.Mps: %d\n", Cc->Mps));\r
-  DEBUG ((EFI_D_INFO, "Cc.Ams: %d\n", Cc->Ams));\r
-  DEBUG ((EFI_D_INFO, "Cc.Shn: %d\n", Cc->Shn));\r
-  DEBUG ((EFI_D_INFO, "Cc.Iosqes: %d\n", Cc->Iosqes));\r
-  DEBUG ((EFI_D_INFO, "Cc.Iocqes: %d\n", Cc->Iocqes));\r
+  DEBUG ((DEBUG_INFO, "Cc.En: %d\n", Cc->En));\r
+  DEBUG ((DEBUG_INFO, "Cc.Css: %d\n", Cc->Css));\r
+  DEBUG ((DEBUG_INFO, "Cc.Mps: %d\n", Cc->Mps));\r
+  DEBUG ((DEBUG_INFO, "Cc.Ams: %d\n", Cc->Ams));\r
+  DEBUG ((DEBUG_INFO, "Cc.Shn: %d\n", Cc->Shn));\r
+  DEBUG ((DEBUG_INFO, "Cc.Iosqes: %d\n", Cc->Iosqes));\r
+  DEBUG ((DEBUG_INFO, "Cc.Iocqes: %d\n", Cc->Iocqes));\r
 \r
   return EFI_SUCCESS;\r
 }\r
@@ -149,13 +151,13 @@ WriteNvmeControllerConfiguration (
 **/\r
 EFI_STATUS\r
 ReadNvmeControllerStatus (\r
-  IN NVME_CONTROLLER_PRIVATE_DATA     *Private,\r
-  IN NVME_CSTS                        *Csts\r
+  IN NVME_CONTROLLER_PRIVATE_DATA  *Private,\r
+  IN NVME_CSTS                     *Csts\r
   )\r
 {\r
-  EFI_PCI_IO_PROTOCOL   *PciIo;\r
-  EFI_STATUS            Status;\r
-  UINT32                Data;\r
+  EFI_PCI_IO_PROTOCOL  *PciIo;\r
+  EFI_STATUS           Status;\r
+  UINT32               Data;\r
 \r
   PciIo  = Private->PciIo;\r
   Status = PciIo->Mem.Read (\r
@@ -167,49 +169,11 @@ ReadNvmeControllerStatus (
                         &Data\r
                         );\r
 \r
-  if (EFI_ERROR(Status)) {\r
-    return Status;\r
-  }\r
-\r
-  WriteUnaligned32 ((UINT32*)Csts, Data);\r
-  return EFI_SUCCESS;\r
-}\r
-\r
-/**\r
-  Read Nvm Express admin queue attributes register.\r
-\r
-  @param  Private          The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure.\r
-  @param  Aqa              The buffer used to store admin queue attributes register content.\r
-\r
-  @return EFI_SUCCESS      Successfully read the admin queue attributes register content.\r
-  @return EFI_DEVICE_ERROR Fail to read the admin queue attributes register.\r
-\r
-**/\r
-EFI_STATUS\r
-ReadNvmeAdminQueueAttributes (\r
-  IN NVME_CONTROLLER_PRIVATE_DATA     *Private,\r
-  IN NVME_AQA                         *Aqa\r
-  )\r
-{\r
-  EFI_PCI_IO_PROTOCOL   *PciIo;\r
-  EFI_STATUS            Status;\r
-  UINT32                Data;\r
-\r
-  PciIo  = Private->PciIo;\r
-  Status = PciIo->Mem.Read (\r
-                        PciIo,\r
-                        EfiPciIoWidthUint32,\r
-                        NVME_BAR,\r
-                        NVME_AQA_OFFSET,\r
-                        1,\r
-                        &Data\r
-                        );\r
-\r
-  if (EFI_ERROR(Status)) {\r
+  if (EFI_ERROR (Status)) {\r
     return Status;\r
   }\r
 \r
-  WriteUnaligned32 ((UINT32*)Aqa, Data);\r
+  WriteUnaligned32 ((UINT32 *)Csts, Data);\r
   return EFI_SUCCESS;\r
 }\r
 \r
@@ -225,16 +189,16 @@ ReadNvmeAdminQueueAttributes (
 **/\r
 EFI_STATUS\r
 WriteNvmeAdminQueueAttributes (\r
-  IN NVME_CONTROLLER_PRIVATE_DATA     *Private,\r
-  IN NVME_AQA                         *Aqa\r
+  IN NVME_CONTROLLER_PRIVATE_DATA  *Private,\r
+  IN NVME_AQA                      *Aqa\r
   )\r
 {\r
-  EFI_PCI_IO_PROTOCOL   *PciIo;\r
-  EFI_STATUS            Status;\r
-  UINT32                Data;\r
+  EFI_PCI_IO_PROTOCOL  *PciIo;\r
+  EFI_STATUS           Status;\r
+  UINT32               Data;\r
 \r
   PciIo  = Private->PciIo;\r
-  Data   = ReadUnaligned32 ((UINT32*)Aqa);\r
+  Data   = ReadUnaligned32 ((UINT32 *)Aqa);\r
   Status = PciIo->Mem.Write (\r
                         PciIo,\r
                         EfiPciIoWidthUint32,\r
@@ -244,51 +208,13 @@ WriteNvmeAdminQueueAttributes (
                         &Data\r
                         );\r
 \r
-  if (EFI_ERROR(Status)) {\r
+  if (EFI_ERROR (Status)) {\r
     return Status;\r
   }\r
 \r
-  DEBUG ((EFI_D_INFO, "Aqa.Asqs: %d\n", Aqa->Asqs));\r
-  DEBUG ((EFI_D_INFO, "Aqa.Acqs: %d\n", Aqa->Acqs));\r
-\r
-  return EFI_SUCCESS;\r
-}\r
-\r
-/**\r
-  Read Nvm Express admin submission queue base address register.\r
-\r
-  @param  Private          The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure.\r
-  @param  Asq              The buffer used to store admin submission queue base address register content.\r
-\r
-  @return EFI_SUCCESS      Successfully read the admin submission queue base address register content.\r
-  @return EFI_DEVICE_ERROR Fail to read the admin submission queue base address register.\r
-\r
-**/\r
-EFI_STATUS\r
-ReadNvmeAdminSubmissionQueueBaseAddress (\r
-  IN NVME_CONTROLLER_PRIVATE_DATA     *Private,\r
-  IN NVME_ASQ                         *Asq\r
-  )\r
-{\r
-  EFI_PCI_IO_PROTOCOL   *PciIo;\r
-  EFI_STATUS            Status;\r
-  UINT64                Data;\r
-\r
-  PciIo  = Private->PciIo;\r
-  Status = PciIo->Mem.Read (\r
-                        PciIo,\r
-                        EfiPciIoWidthUint32,\r
-                        NVME_BAR,\r
-                        NVME_ASQ_OFFSET,\r
-                        2,\r
-                        &Data\r
-                        );\r
+  DEBUG ((DEBUG_INFO, "Aqa.Asqs: %d\n", Aqa->Asqs));\r
+  DEBUG ((DEBUG_INFO, "Aqa.Acqs: %d\n", Aqa->Acqs));\r
 \r
-  if (EFI_ERROR(Status)) {\r
-    return Status;\r
-  }\r
-\r
-  WriteUnaligned64 ((UINT64*)Asq, Data);\r
   return EFI_SUCCESS;\r
 }\r
 \r
@@ -304,16 +230,16 @@ ReadNvmeAdminSubmissionQueueBaseAddress (
 **/\r
 EFI_STATUS\r
 WriteNvmeAdminSubmissionQueueBaseAddress (\r
-  IN NVME_CONTROLLER_PRIVATE_DATA     *Private,\r
-  IN NVME_ASQ                         *Asq\r
+  IN NVME_CONTROLLER_PRIVATE_DATA  *Private,\r
+  IN NVME_ASQ                      *Asq\r
   )\r
 {\r
-  EFI_PCI_IO_PROTOCOL   *PciIo;\r
-  EFI_STATUS            Status;\r
-  UINT64                Data;\r
+  EFI_PCI_IO_PROTOCOL  *PciIo;\r
+  EFI_STATUS           Status;\r
+  UINT64               Data;\r
 \r
-  PciIo  = Private->PciIo;\r
-  Data   = ReadUnaligned64 ((UINT64*)Asq);\r
+  PciIo = Private->PciIo;\r
+  Data  = ReadUnaligned64 ((UINT64 *)Asq);\r
 \r
   Status = PciIo->Mem.Write (\r
                         PciIo,\r
@@ -324,51 +250,12 @@ WriteNvmeAdminSubmissionQueueBaseAddress (
                         &Data\r
                         );\r
 \r
-  if (EFI_ERROR(Status)) {\r
+  if (EFI_ERROR (Status)) {\r
     return Status;\r
   }\r
 \r
-  DEBUG ((EFI_D_INFO, "Asq: %lx\n", *Asq));\r
-\r
-  return EFI_SUCCESS;\r
-}\r
-\r
-/**\r
-  Read Nvm Express admin completion queue base address register.\r
-\r
-  @param  Private          The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure.\r
-  @param  Acq              The buffer used to store admin completion queue base address register content.\r
-\r
-  @return EFI_SUCCESS      Successfully read the admin completion queue base address register content.\r
-  @return EFI_DEVICE_ERROR Fail to read the admin completion queue base address register.\r
-\r
-**/\r
-EFI_STATUS\r
-ReadNvmeAdminCompletionQueueBaseAddress (\r
-  IN NVME_CONTROLLER_PRIVATE_DATA     *Private,\r
-  IN NVME_ACQ                         *Acq\r
-  )\r
-{\r
-  EFI_PCI_IO_PROTOCOL   *PciIo;\r
-  EFI_STATUS            Status;\r
-  UINT64                Data;\r
-\r
-  PciIo  = Private->PciIo;\r
+  DEBUG ((DEBUG_INFO, "Asq: %lx\n", *Asq));\r
 \r
-  Status = PciIo->Mem.Read (\r
-                        PciIo,\r
-                        EfiPciIoWidthUint32,\r
-                        NVME_BAR,\r
-                        NVME_ACQ_OFFSET,\r
-                        2,\r
-                        &Data\r
-                        );\r
-\r
-  if (EFI_ERROR(Status)) {\r
-    return Status;\r
-  }\r
-\r
-  WriteUnaligned64 ((UINT64*)Acq, Data);\r
   return EFI_SUCCESS;\r
 }\r
 \r
@@ -384,16 +271,16 @@ ReadNvmeAdminCompletionQueueBaseAddress (
 **/\r
 EFI_STATUS\r
 WriteNvmeAdminCompletionQueueBaseAddress (\r
-  IN NVME_CONTROLLER_PRIVATE_DATA     *Private,\r
-  IN NVME_ACQ                         *Acq\r
+  IN NVME_CONTROLLER_PRIVATE_DATA  *Private,\r
+  IN NVME_ACQ                      *Acq\r
   )\r
 {\r
-  EFI_PCI_IO_PROTOCOL   *PciIo;\r
-  EFI_STATUS            Status;\r
-  UINT64                Data;\r
+  EFI_PCI_IO_PROTOCOL  *PciIo;\r
+  EFI_STATUS           Status;\r
+  UINT64               Data;\r
 \r
-  PciIo  = Private->PciIo;\r
-  Data   = ReadUnaligned64 ((UINT64*)Acq);\r
+  PciIo = Private->PciIo;\r
+  Data  = ReadUnaligned64 ((UINT64 *)Acq);\r
 \r
   Status = PciIo->Mem.Write (\r
                         PciIo,\r
@@ -404,11 +291,11 @@ WriteNvmeAdminCompletionQueueBaseAddress (
                         &Data\r
                         );\r
 \r
-  if (EFI_ERROR(Status)) {\r
+  if (EFI_ERROR (Status)) {\r
     return Status;\r
   }\r
 \r
-  DEBUG ((EFI_D_INFO, "Acq: %lxh\n", *Acq));\r
+  DEBUG ((DEBUG_INFO, "Acq: %lxh\n", *Acq));\r
 \r
   return EFI_SUCCESS;\r
 }\r
@@ -424,20 +311,20 @@ WriteNvmeAdminCompletionQueueBaseAddress (
 **/\r
 EFI_STATUS\r
 NvmeDisableController (\r
-  IN NVME_CONTROLLER_PRIVATE_DATA     *Private\r
+  IN NVME_CONTROLLER_PRIVATE_DATA  *Private\r
   )\r
 {\r
-  NVME_CC                Cc;\r
-  NVME_CSTS              Csts;\r
-  EFI_STATUS             Status;\r
-  UINT32                 Index;\r
-  UINT8                  Timeout;\r
+  NVME_CC     Cc;\r
+  NVME_CSTS   Csts;\r
+  EFI_STATUS  Status;\r
+  UINT32      Index;\r
+  UINT8       Timeout;\r
 \r
   //\r
   // Read Controller Configuration Register.\r
   //\r
   Status = ReadNvmeControllerConfiguration (Private, &Cc);\r
-  if (EFI_ERROR(Status)) {\r
+  if (EFI_ERROR (Status)) {\r
     return Status;\r
   }\r
 \r
@@ -448,7 +335,7 @@ NvmeDisableController (
   //\r
   Status = WriteNvmeControllerConfiguration (Private, &Cc);\r
 \r
-  if (EFI_ERROR(Status)) {\r
+  if (EFI_ERROR (Status)) {\r
     return Status;\r
   }\r
 \r
@@ -462,15 +349,15 @@ NvmeDisableController (
     Timeout = Private->Cap.To;\r
   }\r
 \r
-  for(Index = (Timeout * 500); Index != 0; --Index) {\r
-    gBS->Stall(1000);\r
+  for (Index = (Timeout * 500); Index != 0; --Index) {\r
+    gBS->Stall (1000);\r
 \r
     //\r
     // Check if the controller is initialized\r
     //\r
     Status = ReadNvmeControllerStatus (Private, &Csts);\r
 \r
-    if (EFI_ERROR(Status)) {\r
+    if (EFI_ERROR (Status)) {\r
       return Status;\r
     }\r
 \r
@@ -481,9 +368,13 @@ NvmeDisableController (
 \r
   if (Index == 0) {\r
     Status = EFI_DEVICE_ERROR;\r
+    REPORT_STATUS_CODE (\r
+      (EFI_ERROR_CODE | EFI_ERROR_MAJOR),\r
+      (EFI_IO_BUS_SCSI | EFI_IOB_EC_INTERFACE_ERROR)\r
+      );\r
   }\r
 \r
-  DEBUG ((EFI_D_INFO, "NVMe controller is disabled with status [%r].\n", Status));\r
+  DEBUG ((DEBUG_INFO, "NVMe controller is disabled with status [%r].\n", Status));\r
   return Status;\r
 }\r
 \r
@@ -499,14 +390,14 @@ NvmeDisableController (
 **/\r
 EFI_STATUS\r
 NvmeEnableController (\r
-  IN NVME_CONTROLLER_PRIVATE_DATA     *Private\r
+  IN NVME_CONTROLLER_PRIVATE_DATA  *Private\r
   )\r
 {\r
-  NVME_CC                Cc;\r
-  NVME_CSTS              Csts;\r
-  EFI_STATUS             Status;\r
-  UINT32                 Index;\r
-  UINT8                  Timeout;\r
+  NVME_CC     Cc;\r
+  NVME_CSTS   Csts;\r
+  EFI_STATUS  Status;\r
+  UINT32      Index;\r
+  UINT8       Timeout;\r
 \r
   //\r
   // Enable the controller.\r
@@ -518,7 +409,7 @@ NvmeEnableController (
   Cc.Iocqes = 4;\r
 \r
   Status = WriteNvmeControllerConfiguration (Private, &Cc);\r
-  if (EFI_ERROR(Status)) {\r
+  if (EFI_ERROR (Status)) {\r
     return Status;\r
   }\r
 \r
@@ -532,15 +423,15 @@ NvmeEnableController (
     Timeout = Private->Cap.To;\r
   }\r
 \r
-  for(Index = (Timeout * 500); Index != 0; --Index) {\r
-    gBS->Stall(1000);\r
+  for (Index = (Timeout * 500); Index != 0; --Index) {\r
+    gBS->Stall (1000);\r
 \r
     //\r
     // Check if the controller is initialized\r
     //\r
     Status = ReadNvmeControllerStatus (Private, &Csts);\r
 \r
-    if (EFI_ERROR(Status)) {\r
+    if (EFI_ERROR (Status)) {\r
       return Status;\r
     }\r
 \r
@@ -551,9 +442,13 @@ NvmeEnableController (
 \r
   if (Index == 0) {\r
     Status = EFI_TIMEOUT;\r
+    REPORT_STATUS_CODE (\r
+      (EFI_ERROR_CODE | EFI_ERROR_MAJOR),\r
+      (EFI_IO_BUS_SCSI | EFI_IOB_EC_INTERFACE_ERROR)\r
+      );\r
   }\r
 \r
-  DEBUG ((EFI_D_INFO, "NVMe controller is enabled with status [%r].\n", Status));\r
+  DEBUG ((DEBUG_INFO, "NVMe controller is enabled with status [%r].\n", Status));\r
   return Status;\r
 }\r
 \r
@@ -569,25 +464,25 @@ NvmeEnableController (
 **/\r
 EFI_STATUS\r
 NvmeIdentifyController (\r
-  IN NVME_CONTROLLER_PRIVATE_DATA       *Private,\r
-  IN VOID                               *Buffer\r
+  IN NVME_CONTROLLER_PRIVATE_DATA  *Private,\r
+  IN VOID                          *Buffer\r
   )\r
 {\r
-  EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket;\r
-  EFI_NVM_EXPRESS_COMMAND                  Command;\r
-  EFI_NVM_EXPRESS_COMPLETION               Completion;\r
-  EFI_STATUS                               Status;\r
+  EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET  CommandPacket;\r
+  EFI_NVM_EXPRESS_COMMAND                   Command;\r
+  EFI_NVM_EXPRESS_COMPLETION                Completion;\r
+  EFI_STATUS                                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
+  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
+  Command.Nsid = 0;\r
 \r
   CommandPacket.NvmeCmd        = &Command;\r
   CommandPacket.NvmeCompletion = &Completion;\r
@@ -598,8 +493,8 @@ NvmeIdentifyController (
   //\r
   // Set bit 0 (Cns bit) to 1 to identify a controller\r
   //\r
-  Command.Cdw10                = 1;\r
-  Command.Flags                = CDW10_VALID;\r
+  Command.Cdw10 = 1;\r
+  Command.Flags = CDW10_VALID;\r
 \r
   Status = Private->Passthru.PassThru (\r
                                &Private->Passthru,\r
@@ -624,25 +519,25 @@ NvmeIdentifyController (
 **/\r
 EFI_STATUS\r
 NvmeIdentifyNamespace (\r
-  IN NVME_CONTROLLER_PRIVATE_DATA      *Private,\r
-  IN UINT32                            NamespaceId,\r
-  IN VOID                              *Buffer\r
+  IN NVME_CONTROLLER_PRIVATE_DATA  *Private,\r
+  IN UINT32                        NamespaceId,\r
+  IN VOID                          *Buffer\r
   )\r
 {\r
-  EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket;\r
-  EFI_NVM_EXPRESS_COMMAND                  Command;\r
-  EFI_NVM_EXPRESS_COMPLETION               Completion;\r
-  EFI_STATUS                               Status;\r
+  EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET  CommandPacket;\r
+  EFI_NVM_EXPRESS_COMMAND                   Command;\r
+  EFI_NVM_EXPRESS_COMPLETION                Completion;\r
+  EFI_STATUS                                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
+  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
   CommandPacket.NvmeCmd        = &Command;\r
   CommandPacket.NvmeCompletion = &Completion;\r
 \r
-  Command.Cdw0.Opcode = NVME_ADMIN_IDENTIFY_CMD;\r
-  Command.Nsid        = NamespaceId;\r
+  Command.Cdw0.Opcode          = NVME_ADMIN_IDENTIFY_CMD;\r
+  Command.Nsid                 = NamespaceId;\r
   CommandPacket.TransferBuffer = Buffer;\r
   CommandPacket.TransferLength = sizeof (NVME_ADMIN_NAMESPACE_DATA);\r
   CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;\r
@@ -674,41 +569,63 @@ NvmeIdentifyNamespace (
 **/\r
 EFI_STATUS\r
 NvmeCreateIoCompletionQueue (\r
-  IN NVME_CONTROLLER_PRIVATE_DATA      *Private\r
+  IN NVME_CONTROLLER_PRIVATE_DATA  *Private\r
   )\r
 {\r
-  EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket;\r
-  EFI_NVM_EXPRESS_COMMAND                  Command;\r
-  EFI_NVM_EXPRESS_COMPLETION               Completion;\r
-  EFI_STATUS                               Status;\r
-  NVME_ADMIN_CRIOCQ                        CrIoCq;\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
-  ZeroMem (&CrIoCq, sizeof(NVME_ADMIN_CRIOCQ));\r
-\r
-  CommandPacket.NvmeCmd        = &Command;\r
-  CommandPacket.NvmeCompletion = &Completion;\r
-\r
-  Command.Cdw0.Opcode = NVME_ADMIN_CRIOCQ_CMD;\r
-  CommandPacket.TransferBuffer = Private->CqBufferPciAddr[1];\r
-  CommandPacket.TransferLength = EFI_PAGE_SIZE;\r
-  CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;\r
-  CommandPacket.QueueType      = NVME_ADMIN_QUEUE;\r
+  EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET  CommandPacket;\r
+  EFI_NVM_EXPRESS_COMMAND                   Command;\r
+  EFI_NVM_EXPRESS_COMPLETION                Completion;\r
+  EFI_STATUS                                Status;\r
+  NVME_ADMIN_CRIOCQ                         CrIoCq;\r
+  UINT32                                    Index;\r
+  UINT16                                    QueueSize;\r
+\r
+  Status                 = EFI_SUCCESS;\r
+  Private->CreateIoQueue = TRUE;\r
+\r
+  for (Index = 1; Index < NVME_MAX_QUEUES; Index++) {\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
+    ZeroMem (&CrIoCq, sizeof (NVME_ADMIN_CRIOCQ));\r
+\r
+    CommandPacket.NvmeCmd        = &Command;\r
+    CommandPacket.NvmeCompletion = &Completion;\r
+\r
+    Command.Cdw0.Opcode          = NVME_ADMIN_CRIOCQ_CMD;\r
+    CommandPacket.TransferBuffer = Private->CqBufferPciAddr[Index];\r
+    CommandPacket.TransferLength = EFI_PAGE_SIZE;\r
+    CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;\r
+    CommandPacket.QueueType      = NVME_ADMIN_QUEUE;\r
+\r
+    if (Index == 1) {\r
+      QueueSize = NVME_CCQ_SIZE;\r
+    } else {\r
+      if (Private->Cap.Mqes > NVME_ASYNC_CCQ_SIZE) {\r
+        QueueSize = NVME_ASYNC_CCQ_SIZE;\r
+      } else {\r
+        QueueSize = Private->Cap.Mqes;\r
+      }\r
+    }\r
 \r
-  CrIoCq.Qid   = NVME_IO_QUEUE;\r
-  CrIoCq.Qsize = NVME_CCQ_SIZE;\r
-  CrIoCq.Pc    = 1;\r
-  CopyMem (&CommandPacket.NvmeCmd->Cdw10, &CrIoCq, sizeof (NVME_ADMIN_CRIOCQ));\r
-  CommandPacket.NvmeCmd->Flags = CDW10_VALID | CDW11_VALID;\r
+    CrIoCq.Qid   = Index;\r
+    CrIoCq.Qsize = QueueSize;\r
+    CrIoCq.Pc    = 1;\r
+    CopyMem (&CommandPacket.NvmeCmd->Cdw10, &CrIoCq, sizeof (NVME_ADMIN_CRIOCQ));\r
+    CommandPacket.NvmeCmd->Flags = CDW10_VALID | CDW11_VALID;\r
+\r
+    Status = Private->Passthru.PassThru (\r
+                                 &Private->Passthru,\r
+                                 0,\r
+                                 &CommandPacket,\r
+                                 NULL\r
+                                 );\r
+    if (EFI_ERROR (Status)) {\r
+      break;\r
+    }\r
+  }\r
 \r
-  Status = Private->Passthru.PassThru (\r
-                               &Private->Passthru,\r
-                               0,\r
-                               &CommandPacket,\r
-                               NULL\r
-                               );\r
+  Private->CreateIoQueue = FALSE;\r
 \r
   return Status;\r
 }\r
@@ -724,43 +641,65 @@ NvmeCreateIoCompletionQueue (
 **/\r
 EFI_STATUS\r
 NvmeCreateIoSubmissionQueue (\r
-  IN NVME_CONTROLLER_PRIVATE_DATA      *Private\r
+  IN NVME_CONTROLLER_PRIVATE_DATA  *Private\r
   )\r
 {\r
-  EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket;\r
-  EFI_NVM_EXPRESS_COMMAND                  Command;\r
-  EFI_NVM_EXPRESS_COMPLETION               Completion;\r
-  EFI_STATUS                               Status;\r
-  NVME_ADMIN_CRIOSQ                        CrIoSq;\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
-  ZeroMem (&CrIoSq, sizeof(NVME_ADMIN_CRIOSQ));\r
-\r
-  CommandPacket.NvmeCmd        = &Command;\r
-  CommandPacket.NvmeCompletion = &Completion;\r
-\r
-  Command.Cdw0.Opcode = NVME_ADMIN_CRIOSQ_CMD;\r
-  CommandPacket.TransferBuffer = Private->SqBufferPciAddr[1];\r
-  CommandPacket.TransferLength = EFI_PAGE_SIZE;\r
-  CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;\r
-  CommandPacket.QueueType      = NVME_ADMIN_QUEUE;\r
+  EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET  CommandPacket;\r
+  EFI_NVM_EXPRESS_COMMAND                   Command;\r
+  EFI_NVM_EXPRESS_COMPLETION                Completion;\r
+  EFI_STATUS                                Status;\r
+  NVME_ADMIN_CRIOSQ                         CrIoSq;\r
+  UINT32                                    Index;\r
+  UINT16                                    QueueSize;\r
+\r
+  Status                 = EFI_SUCCESS;\r
+  Private->CreateIoQueue = TRUE;\r
+\r
+  for (Index = 1; Index < NVME_MAX_QUEUES; Index++) {\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
+    ZeroMem (&CrIoSq, sizeof (NVME_ADMIN_CRIOSQ));\r
+\r
+    CommandPacket.NvmeCmd        = &Command;\r
+    CommandPacket.NvmeCompletion = &Completion;\r
+\r
+    Command.Cdw0.Opcode          = NVME_ADMIN_CRIOSQ_CMD;\r
+    CommandPacket.TransferBuffer = Private->SqBufferPciAddr[Index];\r
+    CommandPacket.TransferLength = EFI_PAGE_SIZE;\r
+    CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;\r
+    CommandPacket.QueueType      = NVME_ADMIN_QUEUE;\r
+\r
+    if (Index == 1) {\r
+      QueueSize = NVME_CSQ_SIZE;\r
+    } else {\r
+      if (Private->Cap.Mqes > NVME_ASYNC_CSQ_SIZE) {\r
+        QueueSize = NVME_ASYNC_CSQ_SIZE;\r
+      } else {\r
+        QueueSize = Private->Cap.Mqes;\r
+      }\r
+    }\r
 \r
-  CrIoSq.Qid   = NVME_IO_QUEUE;\r
-  CrIoSq.Qsize = NVME_CSQ_SIZE;\r
-  CrIoSq.Pc    = 1;\r
-  CrIoSq.Cqid  = NVME_IO_QUEUE;\r
-  CrIoSq.Qprio = 0;\r
-  CopyMem (&CommandPacket.NvmeCmd->Cdw10, &CrIoSq, sizeof (NVME_ADMIN_CRIOSQ));\r
-  CommandPacket.NvmeCmd->Flags = CDW10_VALID | CDW11_VALID;\r
+    CrIoSq.Qid   = Index;\r
+    CrIoSq.Qsize = QueueSize;\r
+    CrIoSq.Pc    = 1;\r
+    CrIoSq.Cqid  = Index;\r
+    CrIoSq.Qprio = 0;\r
+    CopyMem (&CommandPacket.NvmeCmd->Cdw10, &CrIoSq, sizeof (NVME_ADMIN_CRIOSQ));\r
+    CommandPacket.NvmeCmd->Flags = CDW10_VALID | CDW11_VALID;\r
+\r
+    Status = Private->Passthru.PassThru (\r
+                                 &Private->Passthru,\r
+                                 0,\r
+                                 &CommandPacket,\r
+                                 NULL\r
+                                 );\r
+    if (EFI_ERROR (Status)) {\r
+      break;\r
+    }\r
+  }\r
 \r
-  Status = Private->Passthru.PassThru (\r
-                               &Private->Passthru,\r
-                               0,\r
-                               &CommandPacket,\r
-                               NULL\r
-                               );\r
+  Private->CreateIoQueue = FALSE;\r
 \r
   return Status;\r
 }\r
@@ -776,32 +715,22 @@ NvmeCreateIoSubmissionQueue (
 **/\r
 EFI_STATUS\r
 NvmeControllerInit (\r
-  IN NVME_CONTROLLER_PRIVATE_DATA    *Private\r
+  IN NVME_CONTROLLER_PRIVATE_DATA  *Private\r
   )\r
 {\r
-  EFI_STATUS                      Status;\r
-  EFI_PCI_IO_PROTOCOL             *PciIo;\r
-  UINT64                          Supports;\r
-  NVME_AQA                        Aqa;\r
-  NVME_ASQ                        Asq;\r
-  NVME_ACQ                        Acq;\r
-  UINT8                           Sn[21];\r
-  UINT8                           Mn[41];\r
+  EFI_STATUS           Status;\r
+  EFI_PCI_IO_PROTOCOL  *PciIo;\r
+  UINT64               Supports;\r
+  NVME_AQA             Aqa;\r
+  NVME_ASQ             Asq;\r
+  NVME_ACQ             Acq;\r
+  UINT8                Sn[21];\r
+  UINT8                Mn[41];\r
+\r
   //\r
-  // Save original PCI attributes and enable this controller.\r
+  // Enable this controller.\r
   //\r
   PciIo  = Private->PciIo;\r
-  Status = PciIo->Attributes (\r
-                    PciIo,\r
-                    EfiPciIoAttributeOperationGet,\r
-                    0,\r
-                    &Private->PciAttributes\r
-                    );\r
-\r
-  if (EFI_ERROR (Status)) {\r
-    return Status;\r
-  }\r
-\r
   Status = PciIo->Attributes (\r
                     PciIo,\r
                     EfiPciIoAttributeOperationSupported,\r
@@ -820,7 +749,7 @@ NvmeControllerInit (
   }\r
 \r
   if (EFI_ERROR (Status)) {\r
-    DEBUG ((EFI_D_INFO, "NvmeControllerInit: failed to enable controller\n"));\r
+    DEBUG ((DEBUG_INFO, "NvmeControllerInit: failed to enable controller\n"));\r
     return Status;\r
   }\r
 \r
@@ -832,8 +761,8 @@ NvmeControllerInit (
     return Status;\r
   }\r
 \r
-  if (Private->Cap.Css != 0x01) {\r
-    DEBUG ((EFI_D_INFO, "NvmeControllerInit: the controller doesn't support NVMe command set\n"));\r
+  if ((Private->Cap.Css & BIT0) == 0) {\r
+    DEBUG ((DEBUG_INFO, "NvmeControllerInit: the controller doesn't support NVMe command set\n"));\r
     return EFI_UNSUPPORTED;\r
   }\r
 \r
@@ -842,12 +771,23 @@ NvmeControllerInit (
   //\r
   ASSERT ((Private->Cap.Mpsmin + 12) <= EFI_PAGE_SHIFT);\r
 \r
-  Private->Cid[0] = 0;\r
-  Private->Cid[1] = 0;\r
+  Private->Cid[0]        = 0;\r
+  Private->Cid[1]        = 0;\r
+  Private->Cid[2]        = 0;\r
+  Private->Pt[0]         = 0;\r
+  Private->Pt[1]         = 0;\r
+  Private->Pt[2]         = 0;\r
+  Private->SqTdbl[0].Sqt = 0;\r
+  Private->SqTdbl[1].Sqt = 0;\r
+  Private->SqTdbl[2].Sqt = 0;\r
+  Private->CqHdbl[0].Cqh = 0;\r
+  Private->CqHdbl[1].Cqh = 0;\r
+  Private->CqHdbl[2].Cqh = 0;\r
+  Private->AsyncSqHead   = 0;\r
 \r
   Status = NvmeDisableController (Private);\r
 \r
-  if (EFI_ERROR(Status)) {\r
+  if (EFI_ERROR (Status)) {\r
     return Status;\r
   }\r
 \r
@@ -872,6 +812,7 @@ NvmeControllerInit (
   //\r
   // Address of I/O submission & completion queue.\r
   //\r
+  ZeroMem (Private->Buffer, EFI_PAGES_TO_SIZE (6));\r
   Private->SqBuffer[0]        = (NVME_SQ *)(UINTN)(Private->Buffer);\r
   Private->SqBufferPciAddr[0] = (NVME_SQ *)(UINTN)(Private->BufferPciAddr);\r
   Private->CqBuffer[0]        = (NVME_CQ *)(UINTN)(Private->Buffer + 1 * EFI_PAGE_SIZE);\r
@@ -880,21 +821,27 @@ NvmeControllerInit (
   Private->SqBufferPciAddr[1] = (NVME_SQ *)(UINTN)(Private->BufferPciAddr + 2 * EFI_PAGE_SIZE);\r
   Private->CqBuffer[1]        = (NVME_CQ *)(UINTN)(Private->Buffer + 3 * EFI_PAGE_SIZE);\r
   Private->CqBufferPciAddr[1] = (NVME_CQ *)(UINTN)(Private->BufferPciAddr + 3 * EFI_PAGE_SIZE);\r
-\r
-  DEBUG ((EFI_D_INFO, "Private->Buffer = [%016X]\n", (UINT64)(UINTN)Private->Buffer));\r
-  DEBUG ((EFI_D_INFO, "Admin Submission Queue size (Aqa.Asqs) = [%08X]\n", Aqa.Asqs));\r
-  DEBUG ((EFI_D_INFO, "Admin Completion Queue size (Aqa.Acqs) = [%08X]\n", Aqa.Acqs));\r
-  DEBUG ((EFI_D_INFO, "Admin Submission Queue (SqBuffer[0]) = [%016X]\n", Private->SqBuffer[0]));\r
-  DEBUG ((EFI_D_INFO, "Admin Completion Queue (CqBuffer[0]) = [%016X]\n", Private->CqBuffer[0]));\r
-  DEBUG ((EFI_D_INFO, "I/O   Submission Queue (SqBuffer[1]) = [%016X]\n", Private->SqBuffer[1]));\r
-  DEBUG ((EFI_D_INFO, "I/O   Completion Queue (CqBuffer[1]) = [%016X]\n", Private->CqBuffer[1]));\r
+  Private->SqBuffer[2]        = (NVME_SQ *)(UINTN)(Private->Buffer + 4 * EFI_PAGE_SIZE);\r
+  Private->SqBufferPciAddr[2] = (NVME_SQ *)(UINTN)(Private->BufferPciAddr + 4 * EFI_PAGE_SIZE);\r
+  Private->CqBuffer[2]        = (NVME_CQ *)(UINTN)(Private->Buffer + 5 * EFI_PAGE_SIZE);\r
+  Private->CqBufferPciAddr[2] = (NVME_CQ *)(UINTN)(Private->BufferPciAddr + 5 * EFI_PAGE_SIZE);\r
+\r
+  DEBUG ((DEBUG_INFO, "Private->Buffer = [%016X]\n", (UINT64)(UINTN)Private->Buffer));\r
+  DEBUG ((DEBUG_INFO, "Admin     Submission Queue size (Aqa.Asqs) = [%08X]\n", Aqa.Asqs));\r
+  DEBUG ((DEBUG_INFO, "Admin     Completion Queue size (Aqa.Acqs) = [%08X]\n", Aqa.Acqs));\r
+  DEBUG ((DEBUG_INFO, "Admin     Submission Queue (SqBuffer[0]) = [%016X]\n", Private->SqBuffer[0]));\r
+  DEBUG ((DEBUG_INFO, "Admin     Completion Queue (CqBuffer[0]) = [%016X]\n", Private->CqBuffer[0]));\r
+  DEBUG ((DEBUG_INFO, "Sync  I/O Submission Queue (SqBuffer[1]) = [%016X]\n", Private->SqBuffer[1]));\r
+  DEBUG ((DEBUG_INFO, "Sync  I/O Completion Queue (CqBuffer[1]) = [%016X]\n", Private->CqBuffer[1]));\r
+  DEBUG ((DEBUG_INFO, "Async I/O Submission Queue (SqBuffer[2]) = [%016X]\n", Private->SqBuffer[2]));\r
+  DEBUG ((DEBUG_INFO, "Async I/O Completion Queue (CqBuffer[2]) = [%016X]\n", Private->CqBuffer[2]));\r
 \r
   //\r
   // Program admin queue attributes.\r
   //\r
   Status = WriteNvmeAdminQueueAttributes (Private, &Aqa);\r
 \r
-  if (EFI_ERROR(Status)) {\r
+  if (EFI_ERROR (Status)) {\r
     return Status;\r
   }\r
 \r
@@ -903,7 +850,7 @@ NvmeControllerInit (
   //\r
   Status = WriteNvmeAdminSubmissionQueueBaseAddress (Private, &Asq);\r
 \r
-  if (EFI_ERROR(Status)) {\r
+  if (EFI_ERROR (Status)) {\r
     return Status;\r
   }\r
 \r
@@ -912,22 +859,24 @@ NvmeControllerInit (
   //\r
   Status = WriteNvmeAdminCompletionQueueBaseAddress (Private, &Acq);\r
 \r
-  if (EFI_ERROR(Status)) {\r
+  if (EFI_ERROR (Status)) {\r
     return Status;\r
   }\r
 \r
   Status = NvmeEnableController (Private);\r
-  if (EFI_ERROR(Status)) {\r
+  if (EFI_ERROR (Status)) {\r
     return Status;\r
   }\r
 \r
   //\r
   // Allocate buffer for Identify Controller data\r
   //\r
-  Private->ControllerData = (NVME_ADMIN_CONTROLLER_DATA *)AllocateZeroPool (sizeof(NVME_ADMIN_CONTROLLER_DATA));\r
-\r
   if (Private->ControllerData == NULL) {\r
-    return EFI_OUT_OF_RESOURCES;\r
+    Private->ControllerData = (NVME_ADMIN_CONTROLLER_DATA *)AllocateZeroPool (sizeof (NVME_ADMIN_CONTROLLER_DATA));\r
+\r
+    if (Private->ControllerData == NULL) {\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
   }\r
 \r
   //\r
@@ -935,8 +884,8 @@ NvmeControllerInit (
   //\r
   Status = NvmeIdentifyController (Private, Private->ControllerData);\r
 \r
-  if (EFI_ERROR(Status)) {\r
-    FreePool(Private->ControllerData);\r
+  if (EFI_ERROR (Status)) {\r
+    FreePool (Private->ControllerData);\r
     Private->ControllerData = NULL;\r
     return EFI_NOT_FOUND;\r
   }\r
@@ -948,35 +897,208 @@ NvmeControllerInit (
   Sn[20] = 0;\r
   CopyMem (Mn, Private->ControllerData->Mn, sizeof (Private->ControllerData->Mn));\r
   Mn[40] = 0;\r
-  DEBUG ((EFI_D_INFO, " == NVME IDENTIFY CONTROLLER DATA ==\n"));\r
-  DEBUG ((EFI_D_INFO, "    PCI VID   : 0x%x\n", Private->ControllerData->Vid));\r
-  DEBUG ((EFI_D_INFO, "    PCI SSVID : 0x%x\n", Private->ControllerData->Ssvid));\r
-  DEBUG ((EFI_D_INFO, "    SN        : %a\n",   Sn));\r
-  DEBUG ((EFI_D_INFO, "    MN        : %a\n",   Mn));\r
-  DEBUG ((EFI_D_INFO, "    FR        : 0x%x\n", *((UINT64*)Private->ControllerData->Fr)));\r
-  DEBUG ((EFI_D_INFO, "    RAB       : 0x%x\n", Private->ControllerData->Rab));\r
-  DEBUG ((EFI_D_INFO, "    IEEE      : 0x%x\n", *(UINT32*)Private->ControllerData->Ieee_oui));\r
-  DEBUG ((EFI_D_INFO, "    AERL      : 0x%x\n", Private->ControllerData->Aerl));\r
-  DEBUG ((EFI_D_INFO, "    SQES      : 0x%x\n", Private->ControllerData->Sqes));\r
-  DEBUG ((EFI_D_INFO, "    CQES      : 0x%x\n", Private->ControllerData->Cqes));\r
-  DEBUG ((EFI_D_INFO, "    NN        : 0x%x\n", Private->ControllerData->Nn));\r
-\r
-  //\r
-  // Create one I/O completion queue.\r
+  DEBUG ((DEBUG_INFO, " == NVME IDENTIFY CONTROLLER DATA ==\n"));\r
+  DEBUG ((DEBUG_INFO, "    PCI VID   : 0x%x\n", Private->ControllerData->Vid));\r
+  DEBUG ((DEBUG_INFO, "    PCI SSVID : 0x%x\n", Private->ControllerData->Ssvid));\r
+  DEBUG ((DEBUG_INFO, "    SN        : %a\n", Sn));\r
+  DEBUG ((DEBUG_INFO, "    MN        : %a\n", Mn));\r
+  DEBUG ((DEBUG_INFO, "    FR        : 0x%x\n", *((UINT64 *)Private->ControllerData->Fr)));\r
+  DEBUG ((DEBUG_INFO, "    TNVMCAP (high 8-byte) : 0x%lx\n", *((UINT64 *)(Private->ControllerData->Tnvmcap + 8))));\r
+  DEBUG ((DEBUG_INFO, "    TNVMCAP (low 8-byte)  : 0x%lx\n", *((UINT64 *)Private->ControllerData->Tnvmcap)));\r
+  DEBUG ((DEBUG_INFO, "    RAB       : 0x%x\n", Private->ControllerData->Rab));\r
+  DEBUG ((DEBUG_INFO, "    IEEE      : 0x%x\n", *(UINT32 *)Private->ControllerData->Ieee_oui));\r
+  DEBUG ((DEBUG_INFO, "    AERL      : 0x%x\n", Private->ControllerData->Aerl));\r
+  DEBUG ((DEBUG_INFO, "    SQES      : 0x%x\n", Private->ControllerData->Sqes));\r
+  DEBUG ((DEBUG_INFO, "    CQES      : 0x%x\n", Private->ControllerData->Cqes));\r
+  DEBUG ((DEBUG_INFO, "    NN        : 0x%x\n", Private->ControllerData->Nn));\r
+\r
+  //\r
+  // Create two I/O completion queues.\r
+  // One for blocking I/O, one for non-blocking I/O.\r
   //\r
   Status = NvmeCreateIoCompletionQueue (Private);\r
-  if (EFI_ERROR(Status)) {\r
-   return Status;\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
   }\r
 \r
   //\r
-  // Create one I/O Submission queue.\r
+  // Create two I/O Submission queues.\r
+  // One for blocking I/O, one for non-blocking I/O.\r
   //\r
   Status = NvmeCreateIoSubmissionQueue (Private);\r
-  if (EFI_ERROR(Status)) {\r
-   return Status;\r
-  }\r
 \r
   return Status;\r
 }\r
 \r
+/**\r
+ This routine is called to properly shutdown the Nvm Express controller per NVMe spec.\r
+\r
+  @param[in]  ResetType         The type of reset to perform.\r
+  @param[in]  ResetStatus       The status code for the reset.\r
+  @param[in]  DataSize          The size, in bytes, of ResetData.\r
+  @param[in]  ResetData         For a ResetType of EfiResetCold, EfiResetWarm, or\r
+                                EfiResetShutdown the data buffer starts with a Null-terminated\r
+                                string, optionally followed by additional binary data.\r
+                                The string is a description that the caller may use to further\r
+                                indicate the reason for the system reset.\r
+                                For a ResetType of EfiResetPlatformSpecific the data buffer\r
+                                also starts with a Null-terminated string that is followed\r
+                                by an EFI_GUID that describes the specific type of reset to perform.\r
+**/\r
+VOID\r
+EFIAPI\r
+NvmeShutdownAllControllers (\r
+  IN EFI_RESET_TYPE  ResetType,\r
+  IN EFI_STATUS      ResetStatus,\r
+  IN UINTN           DataSize,\r
+  IN VOID            *ResetData OPTIONAL\r
+  )\r
+{\r
+  EFI_STATUS                           Status;\r
+  EFI_HANDLE                           *Handles;\r
+  UINTN                                HandleCount;\r
+  UINTN                                HandleIndex;\r
+  EFI_OPEN_PROTOCOL_INFORMATION_ENTRY  *OpenInfos;\r
+  UINTN                                OpenInfoCount;\r
+  UINTN                                OpenInfoIndex;\r
+  EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL   *NvmePassThru;\r
+  NVME_CC                              Cc;\r
+  NVME_CSTS                            Csts;\r
+  UINTN                                Index;\r
+  NVME_CONTROLLER_PRIVATE_DATA         *Private;\r
+\r
+  Status = gBS->LocateHandleBuffer (\r
+                  ByProtocol,\r
+                  &gEfiPciIoProtocolGuid,\r
+                  NULL,\r
+                  &HandleCount,\r
+                  &Handles\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    HandleCount = 0;\r
+  }\r
+\r
+  for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++) {\r
+    Status = gBS->OpenProtocolInformation (\r
+                    Handles[HandleIndex],\r
+                    &gEfiPciIoProtocolGuid,\r
+                    &OpenInfos,\r
+                    &OpenInfoCount\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      continue;\r
+    }\r
+\r
+    for (OpenInfoIndex = 0; OpenInfoIndex < OpenInfoCount; OpenInfoIndex++) {\r
+      //\r
+      // Find all the NVME controller managed by this driver.\r
+      // gImageHandle equals to DriverBinding handle for this driver.\r
+      //\r
+      if (((OpenInfos[OpenInfoIndex].Attributes & EFI_OPEN_PROTOCOL_BY_DRIVER) != 0) &&\r
+          (OpenInfos[OpenInfoIndex].AgentHandle == gImageHandle))\r
+      {\r
+        Status = gBS->OpenProtocol (\r
+                        OpenInfos[OpenInfoIndex].ControllerHandle,\r
+                        &gEfiNvmExpressPassThruProtocolGuid,\r
+                        (VOID **)&NvmePassThru,\r
+                        NULL,\r
+                        NULL,\r
+                        EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+                        );\r
+        if (EFI_ERROR (Status)) {\r
+          continue;\r
+        }\r
+\r
+        Private = NVME_CONTROLLER_PRIVATE_DATA_FROM_PASS_THRU (NvmePassThru);\r
+\r
+        //\r
+        // Read Controller Configuration Register.\r
+        //\r
+        Status = ReadNvmeControllerConfiguration (Private, &Cc);\r
+        if (EFI_ERROR (Status)) {\r
+          continue;\r
+        }\r
+\r
+        //\r
+        // The host should set the Shutdown Notification (CC.SHN) field to 01b\r
+        // to indicate a normal shutdown operation.\r
+        //\r
+        Cc.Shn = NVME_CC_SHN_NORMAL_SHUTDOWN;\r
+        Status = WriteNvmeControllerConfiguration (Private, &Cc);\r
+        if (EFI_ERROR (Status)) {\r
+          continue;\r
+        }\r
+\r
+        //\r
+        // The controller indicates when shutdown processing is completed by updating the\r
+        // Shutdown Status (CSTS.SHST) field to 10b.\r
+        // Wait up to 45 seconds (break down to 4500 x 10ms) for the shutdown to complete.\r
+        //\r
+        for (Index = 0; Index < NVME_SHUTDOWN_PROCESS_TIMEOUT * 100; Index++) {\r
+          Status = ReadNvmeControllerStatus (Private, &Csts);\r
+          if (!EFI_ERROR (Status) && (Csts.Shst == NVME_CSTS_SHST_SHUTDOWN_COMPLETED)) {\r
+            DEBUG ((DEBUG_INFO, "NvmeShutdownController: shutdown processing is completed after %dms.\n", Index * 10));\r
+            break;\r
+          }\r
+\r
+          //\r
+          // Stall for 10ms\r
+          //\r
+          gBS->Stall (10 * 1000);\r
+        }\r
+\r
+        if (Index == NVME_SHUTDOWN_PROCESS_TIMEOUT * 100) {\r
+          DEBUG ((DEBUG_ERROR, "NvmeShutdownController: shutdown processing is timed out\n"));\r
+        }\r
+      }\r
+    }\r
+  }\r
+}\r
+\r
+/**\r
+  Register the shutdown notification through the ResetNotification protocol.\r
+\r
+  Register the shutdown notification when mNvmeControllerNumber increased from 0 to 1.\r
+**/\r
+VOID\r
+NvmeRegisterShutdownNotification (\r
+  VOID\r
+  )\r
+{\r
+  EFI_STATUS                       Status;\r
+  EFI_RESET_NOTIFICATION_PROTOCOL  *ResetNotify;\r
+\r
+  mNvmeControllerNumber++;\r
+  if (mNvmeControllerNumber == 1) {\r
+    Status = gBS->LocateProtocol (&gEfiResetNotificationProtocolGuid, NULL, (VOID **)&ResetNotify);\r
+    if (!EFI_ERROR (Status)) {\r
+      Status = ResetNotify->RegisterResetNotify (ResetNotify, NvmeShutdownAllControllers);\r
+      ASSERT_EFI_ERROR (Status);\r
+    } else {\r
+      DEBUG ((DEBUG_WARN, "NVME: ResetNotification absent! Shutdown notification cannot be performed!\n"));\r
+    }\r
+  }\r
+}\r
+\r
+/**\r
+  Unregister the shutdown notification through the ResetNotification protocol.\r
+\r
+  Unregister the shutdown notification when mNvmeControllerNumber decreased from 1 to 0.\r
+**/\r
+VOID\r
+NvmeUnregisterShutdownNotification (\r
+  VOID\r
+  )\r
+{\r
+  EFI_STATUS                       Status;\r
+  EFI_RESET_NOTIFICATION_PROTOCOL  *ResetNotify;\r
+\r
+  mNvmeControllerNumber--;\r
+  if (mNvmeControllerNumber == 0) {\r
+    Status = gBS->LocateProtocol (&gEfiResetNotificationProtocolGuid, NULL, (VOID **)&ResetNotify);\r
+    if (!EFI_ERROR (Status)) {\r
+      Status = ResetNotify->UnregisterResetNotify (ResetNotify, NvmeShutdownAllControllers);\r
+      ASSERT_EFI_ERROR (Status);\r
+    }\r
+  }\r
+}\r