]> git.proxmox.com Git - mirror_edk2.git/blobdiff - SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalNvmeMode.c
SecurityPkg: OpalPasswordSmm: Add Opal password Smm driver.
[mirror_edk2.git] / SecurityPkg / Tcg / Opal / OpalPasswordSmm / OpalNvmeMode.c
diff --git a/SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalNvmeMode.c b/SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalNvmeMode.c
new file mode 100644 (file)
index 0000000..ae9bfd9
--- /dev/null
@@ -0,0 +1,2166 @@
+/** @file\r
+  Provide functions to initialize NVME controller and perform NVME commands\r
+\r
+Copyright (c) 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
+\r
+**/\r
+\r
+#include "OpalPasswordSmm.h"\r
+\r
+\r
+#define ALIGN(v, a)                         (UINTN)((((v) - 1) | ((a) - 1)) + 1)\r
+\r
+///\r
+/// NVME Host controller registers operation\r
+///\r
+#define NVME_GET_CAP(Nvme, Cap)             NvmeMmioRead  (Cap, Nvme->Nbar + NVME_CAP_OFFSET, sizeof (NVME_CAP))\r
+#define NVME_GET_CC(Nvme, Cc)               NvmeMmioRead  (Cc, Nvme->Nbar + NVME_CC_OFFSET, sizeof (NVME_CC))\r
+#define NVME_SET_CC(Nvme, Cc)               NvmeMmioWrite (Nvme->Nbar + NVME_CC_OFFSET, Cc, sizeof (NVME_CC))\r
+#define NVME_GET_CSTS(Nvme, Csts)           NvmeMmioRead  (Csts, Nvme->Nbar + NVME_CSTS_OFFSET, sizeof (NVME_CSTS))\r
+#define NVME_GET_AQA(Nvme, Aqa)             NvmeMmioRead  (Aqa, Nvme->Nbar + NVME_AQA_OFFSET, sizeof (NVME_AQA))\r
+#define NVME_SET_AQA(Nvme, Aqa)             NvmeMmioWrite (Nvme->Nbar + NVME_AQA_OFFSET, Aqa, sizeof (NVME_AQA))\r
+#define NVME_GET_ASQ(Nvme, Asq)             NvmeMmioRead  (Asq, Nvme->Nbar + NVME_ASQ_OFFSET, sizeof (NVME_ASQ))\r
+#define NVME_SET_ASQ(Nvme, Asq)             NvmeMmioWrite (Nvme->Nbar + NVME_ASQ_OFFSET, Asq, sizeof (NVME_ASQ))\r
+#define NVME_GET_ACQ(Nvme, Acq)             NvmeMmioRead  (Acq, Nvme->Nbar + NVME_ACQ_OFFSET, sizeof (NVME_ACQ))\r
+#define NVME_SET_ACQ(Nvme, Acq)             NvmeMmioWrite (Nvme->Nbar + NVME_ACQ_OFFSET, Acq, sizeof (NVME_ACQ))\r
+#define NVME_GET_VER(Nvme, Ver)             NvmeMmioRead  (Ver, Nvme->Nbar + NVME_VER_OFFSET, sizeof (NVME_VER))\r
+#define NVME_SET_SQTDBL(Nvme, Qid, Sqtdbl)  NvmeMmioWrite (Nvme->Nbar + NVME_SQTDBL_OFFSET(Qid, Nvme->Cap.Dstrd), Sqtdbl, sizeof (NVME_SQTDBL))\r
+#define NVME_SET_CQHDBL(Nvme, Qid, Cqhdbl)  NvmeMmioWrite (Nvme->Nbar + NVME_CQHDBL_OFFSET(Qid, Nvme->Cap.Dstrd), Cqhdbl, sizeof (NVME_CQHDBL))\r
+\r
+///\r
+/// Base memory address\r
+///\r
+enum {\r
+  BASEMEM_CONTROLLER_DATA,\r
+  BASEMEM_IDENTIFY_DATA,\r
+  BASEMEM_ASQ,\r
+  BASEMEM_ACQ,\r
+  BASEMEM_SQ,\r
+  BASEMEM_CQ,\r
+  BASEMEM_PRP,\r
+  BASEMEM_SECURITY,\r
+  MAX_BASEMEM_COUNT\r
+};\r
+\r
+///\r
+/// All of base memories are 4K(0x1000) alignment\r
+///\r
+#define NVME_MEM_BASE(Nvme)                 (Nvme->BaseMem)\r
+#define NVME_CONTROL_DATA_BASE(Nvme)        (ALIGN (NVME_MEM_BASE(Nvme) + ((NvmeGetBaseMemPages (BASEMEM_CONTROLLER_DATA))                        * EFI_PAGE_SIZE), EFI_PAGE_SIZE))\r
+#define NVME_NAMESPACE_DATA_BASE(Nvme)      (ALIGN (NVME_MEM_BASE(Nvme) + ((NvmeGetBaseMemPages (BASEMEM_IDENTIFY_DATA))                          * EFI_PAGE_SIZE), EFI_PAGE_SIZE))\r
+#define NVME_ASQ_BASE(Nvme)                 (ALIGN (NVME_MEM_BASE(Nvme) + ((NvmeGetBaseMemPages (BASEMEM_ASQ))                                    * EFI_PAGE_SIZE), EFI_PAGE_SIZE))\r
+#define NVME_ACQ_BASE(Nvme)                 (ALIGN (NVME_MEM_BASE(Nvme) + ((NvmeGetBaseMemPages (BASEMEM_ACQ))                                    * EFI_PAGE_SIZE), EFI_PAGE_SIZE))\r
+#define NVME_SQ_BASE(Nvme, index)           (ALIGN (NVME_MEM_BASE(Nvme) + ((NvmeGetBaseMemPages (BASEMEM_SQ) + ((index)*(NVME_MAX_IO_QUEUES-1)))  * EFI_PAGE_SIZE), EFI_PAGE_SIZE))\r
+#define NVME_CQ_BASE(Nvme, index)           (ALIGN (NVME_MEM_BASE(Nvme) + ((NvmeGetBaseMemPages (BASEMEM_CQ) + ((index)*(NVME_MAX_IO_QUEUES-1)))  * EFI_PAGE_SIZE), EFI_PAGE_SIZE))\r
+#define NVME_PRP_BASE(Nvme, index)          (ALIGN (NVME_MEM_BASE(Nvme) + ((NvmeGetBaseMemPages (BASEMEM_PRP) + ((index)*NVME_PRP_SIZE))          * EFI_PAGE_SIZE), EFI_PAGE_SIZE))\r
+#define NVME_SEC_BASE(Nvme)                 (ALIGN (NVME_MEM_BASE(Nvme) + ((NvmeGetBaseMemPages (BASEMEM_SECURITY))                               * EFI_PAGE_SIZE), EFI_PAGE_SIZE))\r
+\r
+/**\r
+  Transfer MMIO Data to memory.\r
+\r
+  @param[in,out] MemBuffer - Destination: Memory address\r
+  @param[in] MmioAddr      - Source: MMIO address\r
+  @param[in] Size          - Size for read\r
+\r
+  @retval EFI_SUCCESS - MMIO read sucessfully\r
+**/\r
+EFI_STATUS\r
+NvmeMmioRead (\r
+  IN OUT VOID *MemBuffer,\r
+  IN     UINTN MmioAddr,\r
+  IN     UINTN Size\r
+  )\r
+{\r
+  UINTN  Offset;\r
+  UINT8  Data;\r
+  UINT8  *Ptr;\r
+\r
+  // priority has adjusted\r
+  switch (Size) {\r
+    case 4:\r
+      *((UINT32 *)MemBuffer) = MmioRead32 (MmioAddr);\r
+      break;\r
+\r
+    case 8:\r
+      *((UINT64 *)MemBuffer) = MmioRead64 (MmioAddr);\r
+      break;\r
+\r
+    case 2:\r
+      *((UINT16 *)MemBuffer) = MmioRead16 (MmioAddr);\r
+      break;\r
+\r
+    case 1:\r
+      *((UINT8 *)MemBuffer) = MmioRead8 (MmioAddr);\r
+      break;\r
+\r
+    default:\r
+      Ptr = (UINT8 *)MemBuffer;\r
+      for (Offset = 0; Offset < Size; Offset += 1) {\r
+        Data = MmioRead8 (MmioAddr + Offset);\r
+        Ptr[Offset] = Data;\r
+      }\r
+      break;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Transfer memory data to MMIO.\r
+\r
+  @param[in,out] MmioAddr - Destination: MMIO address\r
+  @param[in] MemBuffer    - Source: Memory address\r
+  @param[in] Size         - Size for write\r
+\r
+  @retval EFI_SUCCESS - MMIO write sucessfully\r
+**/\r
+EFI_STATUS\r
+NvmeMmioWrite (\r
+  IN OUT UINTN MmioAddr,\r
+  IN     VOID *MemBuffer,\r
+  IN     UINTN Size\r
+  )\r
+{\r
+  UINTN  Offset;\r
+  UINT8  Data;\r
+  UINT8  *Ptr;\r
+\r
+  // priority has adjusted\r
+  switch (Size) {\r
+    case 4:\r
+      MmioWrite32 (MmioAddr, *((UINT32 *)MemBuffer));\r
+      break;\r
+\r
+    case 8:\r
+      MmioWrite64 (MmioAddr, *((UINT64 *)MemBuffer));\r
+      break;\r
+\r
+    case 2:\r
+      MmioWrite16 (MmioAddr, *((UINT16 *)MemBuffer));\r
+      break;\r
+\r
+    case 1:\r
+      MmioWrite8 (MmioAddr, *((UINT8 *)MemBuffer));\r
+      break;\r
+\r
+    default:\r
+      Ptr = (UINT8 *)MemBuffer;\r
+      for (Offset = 0; Offset < Size; Offset += 1) {\r
+        Data = Ptr[Offset];\r
+        MmioWrite8 (MmioAddr + Offset, Data);\r
+      }\r
+      break;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Transfer MMIO data to memory.\r
+\r
+  @param[in,out] MemBuffer - Destination: Memory address\r
+  @param[in] MmioAddr      - Source: MMIO address\r
+  @param[in] Size          - Size for read\r
+\r
+  @retval EFI_SUCCESS - MMIO read sucessfully\r
+**/\r
+EFI_STATUS\r
+OpalPciRead (\r
+  IN OUT VOID *MemBuffer,\r
+  IN     UINTN MmioAddr,\r
+  IN     UINTN Size\r
+  )\r
+{\r
+  UINTN  Offset;\r
+  UINT8  Data;\r
+  UINT8  *Ptr;\r
+\r
+  // priority has adjusted\r
+  switch (Size) {\r
+    case 4:\r
+      *((UINT32 *)MemBuffer) = PciRead32 (MmioAddr);\r
+      break;\r
+\r
+    case 2:\r
+      *((UINT16 *)MemBuffer) = PciRead16 (MmioAddr);\r
+      break;\r
+\r
+    case 1:\r
+      *((UINT8 *)MemBuffer) = PciRead8 (MmioAddr);\r
+      break;\r
+\r
+    default:\r
+      Ptr = (UINT8 *)MemBuffer;\r
+      for (Offset = 0; Offset < Size; Offset += 1) {\r
+        Data = PciRead8 (MmioAddr + Offset);\r
+        Ptr[Offset] = Data;\r
+      }\r
+      break;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Transfer memory data to MMIO.\r
+\r
+  @param[in,out] MmioAddr - Destination: MMIO address\r
+  @param[in] MemBuffer    - Source: Memory address\r
+  @param[in] Size         - Size for write\r
+\r
+  @retval EFI_SUCCESS - MMIO write sucessfully\r
+**/\r
+EFI_STATUS\r
+OpalPciWrite (\r
+  IN OUT UINTN MmioAddr,\r
+  IN     VOID *MemBuffer,\r
+  IN     UINTN Size\r
+  )\r
+{\r
+  UINTN  Offset;\r
+  UINT8  Data;\r
+  UINT8  *Ptr;\r
+\r
+  // priority has adjusted\r
+  switch (Size) {\r
+    case 4:\r
+      PciWrite32 (MmioAddr, *((UINT32 *)MemBuffer));\r
+      break;\r
+\r
+    case 2:\r
+      PciWrite16 (MmioAddr, *((UINT16 *)MemBuffer));\r
+      break;\r
+\r
+    case 1:\r
+      PciWrite8 (MmioAddr, *((UINT8 *)MemBuffer));\r
+      break;\r
+\r
+    default:\r
+      Ptr = (UINT8 *)MemBuffer;\r
+      for (Offset = 0; Offset < Size; Offset += 1) {\r
+        Data = Ptr[Offset];\r
+        PciWrite8 (MmioAddr + Offset, Data);\r
+      }\r
+      break;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Get total pages for specific NVME based memory.\r
+\r
+  @param[in] BaseMemIndex           - The Index of BaseMem (0-based).\r
+\r
+  @retval - The page count for specific BaseMem Index\r
+\r
+**/\r
+UINT32\r
+NvmeGetBaseMemPages (\r
+  IN UINTN              BaseMemIndex\r
+  )\r
+{\r
+  UINT32                Pages;\r
+  UINTN                 Index;\r
+  UINT32                PageSizeList[8];\r
+\r
+  PageSizeList[0] = 1;  /* Controller Data */\r
+  PageSizeList[1] = 1;  /* Identify Data */\r
+  PageSizeList[2] = 1;  /* ASQ */\r
+  PageSizeList[3] = 1;  /* ACQ */\r
+  PageSizeList[4] = 1;  /* SQs */\r
+  PageSizeList[5] = 1;  /* CQs */\r
+  PageSizeList[6] = NVME_PRP_SIZE * NVME_CSQ_DEPTH;  /* PRPs */\r
+  PageSizeList[7] = 1;  /* Security Commands */\r
+\r
+  if (BaseMemIndex > MAX_BASEMEM_COUNT) {\r
+    ASSERT (FALSE);\r
+    return 0;\r
+  }\r
+\r
+  Pages = 0;\r
+  for (Index = 0; Index < BaseMemIndex; Index++) {\r
+    Pages += PageSizeList[Index];\r
+  }\r
+\r
+  return Pages;\r
+}\r
+\r
+/**\r
+  Wait for NVME controller status to be ready or not.\r
+\r
+  @param[in] Nvme                   - The pointer to the NVME_CONTEXT Data structure.\r
+  @param[in] WaitReady              - Flag for waitting status ready or not\r
+\r
+  @return EFI_SUCCESS               - Successfully to wait specific status.\r
+  @return others                    - Fail to wait for specific controller status.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+NvmeWaitController (\r
+  IN NVME_CONTEXT       *Nvme,\r
+  IN BOOLEAN            WaitReady\r
+  )\r
+{\r
+  NVME_CSTS              Csts;\r
+  EFI_STATUS             Status;\r
+  UINT32                 Index;\r
+  UINT8                  Timeout;\r
+\r
+  //\r
+  // Cap.To specifies max delay time in 500ms increments for Csts.Rdy to set after\r
+  // Cc.Enable. Loop produces a 1 millisecond delay per itteration, up to 500 * Cap.To.\r
+  //\r
+  if (Nvme->Cap.To == 0) {\r
+    Timeout = 1;\r
+  } else {\r
+    Timeout = Nvme->Cap.To;\r
+  }\r
+\r
+  Status = EFI_SUCCESS;\r
+  for(Index = (Timeout * 500); Index != 0; --Index) {\r
+    MicroSecondDelay (1000);\r
+\r
+    //\r
+    // Check if the controller is initialized\r
+    //\r
+    Status = NVME_GET_CSTS (Nvme, &Csts);\r
+    if (EFI_ERROR(Status)) {\r
+      DEBUG ((DEBUG_ERROR, "NVME_GET_CSTS fail, Status = %r\n", Status));\r
+      return Status;\r
+    }\r
+\r
+    if ((BOOLEAN) Csts.Rdy == WaitReady) {\r
+      break;\r
+    }\r
+  }\r
+\r
+  if (Index == 0) {\r
+    Status = EFI_TIMEOUT;\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Disable the Nvm Express controller.\r
+\r
+  @param[in] Nvme                   - The pointer to the NVME_CONTEXT Data structure.\r
+\r
+  @return EFI_SUCCESS               - Successfully disable the controller.\r
+  @return others                    - Fail to disable the controller.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+NvmeDisableController (\r
+  IN NVME_CONTEXT       *Nvme\r
+  )\r
+{\r
+  NVME_CC                Cc;\r
+  NVME_CSTS              Csts;\r
+  EFI_STATUS             Status;\r
+\r
+  Status = NVME_GET_CSTS (Nvme, &Csts);\r
+\r
+  ///\r
+  /// Read Controller Configuration Register.\r
+  ///\r
+  Status = NVME_GET_CC (Nvme, &Cc);\r
+  if (EFI_ERROR(Status)) {\r
+    DEBUG ((DEBUG_ERROR, "NVME_GET_CC fail, Status = %r\n", Status));\r
+    goto Done;\r
+  }\r
+\r
+  if (Cc.En == 1) {\r
+    Cc.En = 0;\r
+    ///\r
+    /// Disable the controller.\r
+    ///\r
+    Status = NVME_SET_CC (Nvme, &Cc);\r
+    if (EFI_ERROR(Status)) {\r
+      DEBUG ((DEBUG_ERROR, "NVME_SET_CC fail, Status = %r\n", Status));\r
+      goto Done;\r
+    }\r
+  }\r
+\r
+  Status = NvmeWaitController (Nvme, FALSE);\r
+  if (EFI_ERROR(Status)) {\r
+    DEBUG ((DEBUG_ERROR, "NvmeWaitController fail, Status = %r\n", Status));\r
+    goto Done;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+\r
+Done:\r
+  DEBUG ((DEBUG_INFO, "NvmeDisableController fail, Status: %r\n", Status));\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Enable the Nvm Express controller.\r
+\r
+  @param[in] Nvme                   - The pointer to the NVME_CONTEXT Data structure.\r
+\r
+  @return EFI_SUCCESS               - Successfully enable the controller.\r
+  @return EFI_DEVICE_ERROR          - Fail to enable the controller.\r
+  @return EFI_TIMEOUT               - Fail to enable the controller in given time slot.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+NvmeEnableController (\r
+  IN NVME_CONTEXT       *Nvme\r
+  )\r
+{\r
+  NVME_CC                Cc;\r
+  EFI_STATUS             Status;\r
+\r
+  //\r
+  // Enable the controller\r
+  //\r
+  ZeroMem (&Cc, sizeof (NVME_CC));\r
+  Cc.En     = 1;\r
+  Cc.Iosqes = 6;\r
+  Cc.Iocqes = 4;\r
+  Status    = NVME_SET_CC (Nvme, &Cc);\r
+  if (EFI_ERROR(Status)) {\r
+    DEBUG ((DEBUG_ERROR, "NVME_SET_CC fail, Status = %r\n", Status));\r
+    goto Done;\r
+  }\r
+\r
+  Status = NvmeWaitController (Nvme, TRUE);\r
+  if (EFI_ERROR(Status)) {\r
+    DEBUG ((DEBUG_ERROR, "NvmeWaitController fail, Status = %r\n", Status));\r
+    goto Done;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+\r
+Done:\r
+  DEBUG ((DEBUG_INFO, "NvmeEnableController fail, Status: %r\n", Status));\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Shutdown the Nvm Express controller.\r
+\r
+  @param[in] Nvme                   - The pointer to the NVME_CONTEXT Data structure.\r
+\r
+  @return EFI_SUCCESS               - Successfully shutdown the controller.\r
+  @return EFI_DEVICE_ERROR          - Fail to shutdown the controller.\r
+  @return EFI_TIMEOUT               - Fail to shutdown the controller in given time slot.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+NvmeShutdownController (\r
+  IN NVME_CONTEXT       *Nvme\r
+  )\r
+{\r
+  NVME_CC                Cc;\r
+  NVME_CSTS              Csts;\r
+  EFI_STATUS             Status;\r
+  UINT32                 Index;\r
+  UINTN                  Timeout;\r
+\r
+  Status    = NVME_GET_CC (Nvme, &Cc);\r
+  if (EFI_ERROR(Status)) {\r
+    DEBUG ((DEBUG_ERROR, "NVME_GET_CC fail, Status = %r\n", Status));\r
+    return Status;\r
+  }\r
+\r
+  Cc.Shn     = 1; // Normal shutdown\r
+\r
+  Status    = NVME_SET_CC (Nvme, &Cc);\r
+  if (EFI_ERROR(Status)) {\r
+    DEBUG ((DEBUG_ERROR, "NVME_SET_CC fail, Status = %r\n", Status));\r
+    return Status;\r
+  }\r
+\r
+  Timeout = NVME_GENERIC_TIMEOUT/1000; // ms\r
+  for(Index = (UINT32)(Timeout); Index != 0; --Index) {\r
+    MicroSecondDelay (1000);\r
+\r
+    Status = NVME_GET_CSTS (Nvme, &Csts);\r
+    if (EFI_ERROR(Status)) {\r
+      DEBUG ((DEBUG_ERROR, "NVME_GET_CSTS fail, Status = %r\n", Status));\r
+      return Status;\r
+    }\r
+\r
+    if (Csts.Shst == 2) { // Shutdown processing complete\r
+      break;\r
+    }\r
+  }\r
+\r
+  if (Index == 0) {\r
+    Status = EFI_TIMEOUT;\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Check the execution status from a given completion queue entry.\r
+\r
+  @param[in]     Cq                 - A pointer to the NVME_CQ item.\r
+\r
+**/\r
+EFI_STATUS\r
+NvmeCheckCqStatus (\r
+  IN NVME_CQ             *Cq\r
+  )\r
+{\r
+  if (Cq->Sct == 0x0 && Cq->Sc == 0x0) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  DEBUG ((DEBUG_INFO, "Dump NVMe Completion Entry Status from [0x%x]:\n", (UINTN)Cq));\r
+  DEBUG ((DEBUG_INFO, "  SQ Identifier : [0x%x], Phase Tag : [%d], Cmd Identifier : [0x%x]\n", Cq->Sqid, Cq->Pt, Cq->Cid));\r
+  DEBUG ((DEBUG_INFO, "  NVMe Cmd Execution Result - "));\r
+\r
+  switch (Cq->Sct) {\r
+    case 0x0:\r
+      switch (Cq->Sc) {\r
+        case 0x0:\r
+          DEBUG ((DEBUG_INFO, "Successful Completion\n"));\r
+          return EFI_SUCCESS;\r
+        case 0x1:\r
+          DEBUG ((DEBUG_INFO, "Invalid Command Opcode\n"));\r
+          break;\r
+        case 0x2:\r
+          DEBUG ((DEBUG_INFO, "Invalid Field in Command\n"));\r
+          break;\r
+        case 0x3:\r
+          DEBUG ((DEBUG_INFO, "Command ID Conflict\n"));\r
+          break;\r
+        case 0x4:\r
+          DEBUG ((DEBUG_INFO, "Data Transfer Error\n"));\r
+          break;\r
+        case 0x5:\r
+          DEBUG ((DEBUG_INFO, "Commands Aborted due to Power Loss Notification\n"));\r
+          break;\r
+        case 0x6:\r
+          DEBUG ((DEBUG_INFO, "Internal Device Error\n"));\r
+          break;\r
+        case 0x7:\r
+          DEBUG ((DEBUG_INFO, "Command Abort Requested\n"));\r
+          break;\r
+        case 0x8:\r
+          DEBUG ((DEBUG_INFO, "Command Aborted due to SQ Deletion\n"));\r
+          break;\r
+        case 0x9:\r
+          DEBUG ((DEBUG_INFO, "Command Aborted due to Failed Fused Command\n"));\r
+          break;\r
+        case 0xA:\r
+          DEBUG ((DEBUG_INFO, "Command Aborted due to Missing Fused Command\n"));\r
+          break;\r
+        case 0xB:\r
+          DEBUG ((DEBUG_INFO, "Invalid Namespace or Format\n"));\r
+          break;\r
+        case 0xC:\r
+          DEBUG ((DEBUG_INFO, "Command Sequence Error\n"));\r
+          break;\r
+        case 0xD:\r
+          DEBUG ((DEBUG_INFO, "Invalid SGL Last Segment Descriptor\n"));\r
+          break;\r
+        case 0xE:\r
+          DEBUG ((DEBUG_INFO, "Invalid Number of SGL Descriptors\n"));\r
+          break;\r
+        case 0xF:\r
+          DEBUG ((DEBUG_INFO, "Data SGL Length Invalid\n"));\r
+          break;\r
+        case 0x10:\r
+          DEBUG ((DEBUG_INFO, "Metadata SGL Length Invalid\n"));\r
+          break;\r
+        case 0x11:\r
+          DEBUG ((DEBUG_INFO, "SGL Descriptor Type Invalid\n"));\r
+          break;\r
+        case 0x80:\r
+          DEBUG ((DEBUG_INFO, "LBA Out of Range\n"));\r
+          break;\r
+        case 0x81:\r
+          DEBUG ((DEBUG_INFO, "Capacity Exceeded\n"));\r
+          break;\r
+        case 0x82:\r
+          DEBUG ((DEBUG_INFO, "Namespace Not Ready\n"));\r
+          break;\r
+        case 0x83:\r
+          DEBUG ((DEBUG_INFO, "Reservation Conflict\n"));\r
+          break;\r
+      }\r
+      break;\r
+\r
+    case 0x1:\r
+      switch (Cq->Sc) {\r
+        case 0x0:\r
+          DEBUG ((DEBUG_INFO, "Completion Queue Invalid\n"));\r
+          break;\r
+        case 0x1:\r
+          DEBUG ((DEBUG_INFO, "Invalid Queue Identifier\n"));\r
+          break;\r
+        case 0x2:\r
+          DEBUG ((DEBUG_INFO, "Maximum Queue Size Exceeded\n"));\r
+          break;\r
+        case 0x3:\r
+          DEBUG ((DEBUG_INFO, "Abort Command Limit Exceeded\n"));\r
+          break;\r
+        case 0x5:\r
+          DEBUG ((DEBUG_INFO, "Asynchronous Event Request Limit Exceeded\n"));\r
+          break;\r
+        case 0x6:\r
+          DEBUG ((DEBUG_INFO, "Invalid Firmware Slot\n"));\r
+          break;\r
+        case 0x7:\r
+          DEBUG ((DEBUG_INFO, "Invalid Firmware Image\n"));\r
+          break;\r
+        case 0x8:\r
+          DEBUG ((DEBUG_INFO, "Invalid Interrupt Vector\n"));\r
+          break;\r
+        case 0x9:\r
+          DEBUG ((DEBUG_INFO, "Invalid Log Page\n"));\r
+          break;\r
+        case 0xA:\r
+          DEBUG ((DEBUG_INFO, "Invalid Format\n"));\r
+          break;\r
+        case 0xB:\r
+          DEBUG ((DEBUG_INFO, "Firmware Application Requires Conventional Reset\n"));\r
+          break;\r
+        case 0xC:\r
+          DEBUG ((DEBUG_INFO, "Invalid Queue Deletion\n"));\r
+          break;\r
+        case 0xD:\r
+          DEBUG ((DEBUG_INFO, "Feature Identifier Not Saveable\n"));\r
+          break;\r
+        case 0xE:\r
+          DEBUG ((DEBUG_INFO, "Feature Not Changeable\n"));\r
+          break;\r
+        case 0xF:\r
+          DEBUG ((DEBUG_INFO, "Feature Not Namespace Specific\n"));\r
+          break;\r
+        case 0x10:\r
+          DEBUG ((DEBUG_INFO, "Firmware Application Requires NVM Subsystem Reset\n"));\r
+          break;\r
+        case 0x80:\r
+          DEBUG ((DEBUG_INFO, "Conflicting Attributes\n"));\r
+          break;\r
+        case 0x81:\r
+          DEBUG ((DEBUG_INFO, "Invalid Protection Information\n"));\r
+          break;\r
+        case 0x82:\r
+          DEBUG ((DEBUG_INFO, "Attempted Write to Read Only Range\n"));\r
+          break;\r
+      }\r
+      break;\r
+\r
+    case 0x2:\r
+      switch (Cq->Sc) {\r
+        case 0x80:\r
+          DEBUG ((DEBUG_INFO, "Write Fault\n"));\r
+          break;\r
+        case 0x81:\r
+          DEBUG ((DEBUG_INFO, "Unrecovered Read Error\n"));\r
+          break;\r
+        case 0x82:\r
+          DEBUG ((DEBUG_INFO, "End-to-end Guard Check Error\n"));\r
+          break;\r
+        case 0x83:\r
+          DEBUG ((DEBUG_INFO, "End-to-end Application Tag Check Error\n"));\r
+          break;\r
+        case 0x84:\r
+          DEBUG ((DEBUG_INFO, "End-to-end Reference Tag Check Error\n"));\r
+          break;\r
+        case 0x85:\r
+          DEBUG ((DEBUG_INFO, "Compare Failure\n"));\r
+          break;\r
+        case 0x86:\r
+          DEBUG ((DEBUG_INFO, "Access Denied\n"));\r
+          break;\r
+      }\r
+      break;\r
+\r
+    default:\r
+      DEBUG ((DEBUG_INFO, "Unknown error\n"));\r
+      break;\r
+  }\r
+\r
+  return EFI_DEVICE_ERROR;\r
+}\r
+\r
+/**\r
+  Create PRP lists for Data transfer which is larger than 2 memory pages.\r
+  Note here we calcuate the number of required PRP lists and allocate them at one time.\r
+\r
+  @param[in] Nvme                   - The pointer to the NVME_CONTEXT Data structure.\r
+  @param[in] SqId                   - The SQ index for this PRP\r
+  @param[in] PhysicalAddr           - The physical base address of Data Buffer.\r
+  @param[in] Pages                  - The number of pages to be transfered.\r
+  @param[out] PrpListHost           - The host base address of PRP lists.\r
+  @param[in,out] PrpListNo          - The number of PRP List.\r
+\r
+  @retval The pointer Value to the first PRP List of the PRP lists.\r
+\r
+**/\r
+STATIC\r
+UINT64\r
+NvmeCreatePrpList (\r
+  IN     NVME_CONTEXT                 *Nvme,\r
+  IN     UINT16                       SqId,\r
+  IN     EFI_PHYSICAL_ADDRESS         PhysicalAddr,\r
+  IN     UINTN                        Pages,\r
+     OUT VOID                         **PrpListHost,\r
+  IN OUT UINTN                        *PrpListNo\r
+  )\r
+{\r
+  UINTN                       PrpEntryNo;\r
+  UINT64                      PrpListBase;\r
+  UINTN                       PrpListIndex;\r
+  UINTN                       PrpEntryIndex;\r
+  UINT64                      Remainder;\r
+  EFI_PHYSICAL_ADDRESS        PrpListPhyAddr;\r
+  UINTN                       Bytes;\r
+  UINT8                       *PrpEntry;\r
+  EFI_PHYSICAL_ADDRESS        NewPhyAddr;\r
+\r
+  ///\r
+  /// The number of Prp Entry in a memory page.\r
+  ///\r
+  PrpEntryNo = EFI_PAGE_SIZE / sizeof (UINT64);\r
+\r
+  ///\r
+  /// Calculate total PrpList number.\r
+  ///\r
+  *PrpListNo = (UINTN) DivU64x64Remainder ((UINT64)Pages, (UINT64)PrpEntryNo, &Remainder);\r
+  if (Remainder != 0) {\r
+    *PrpListNo += 1;\r
+  }\r
+\r
+  if (*PrpListNo > NVME_PRP_SIZE) {\r
+    DEBUG ((DEBUG_INFO, "NvmeCreatePrpList (PhysicalAddr: %lx, Pages: %x) PrpEntryNo: %x\n",\r
+      PhysicalAddr, Pages, PrpEntryNo));\r
+    DEBUG ((DEBUG_INFO, "*PrpListNo: %x, Remainder: %lx", *PrpListNo, Remainder));\r
+    ASSERT (FALSE);\r
+  }\r
+  *PrpListHost = (VOID *)(UINTN) NVME_PRP_BASE (Nvme, SqId);\r
+\r
+  Bytes = EFI_PAGES_TO_SIZE (*PrpListNo);\r
+  PrpListPhyAddr = (UINT64)(UINTN)(*PrpListHost);\r
+\r
+  ///\r
+  /// Fill all PRP lists except of last one.\r
+  ///\r
+  ZeroMem (*PrpListHost, Bytes);\r
+  for (PrpListIndex = 0; PrpListIndex < *PrpListNo - 1; ++PrpListIndex) {\r
+    PrpListBase = *(UINT64*)PrpListHost + PrpListIndex * EFI_PAGE_SIZE;\r
+\r
+    for (PrpEntryIndex = 0; PrpEntryIndex < PrpEntryNo; ++PrpEntryIndex) {\r
+      PrpEntry = (UINT8 *)(UINTN) (PrpListBase + PrpEntryIndex * sizeof(UINT64));\r
+      if (PrpEntryIndex != PrpEntryNo - 1) {\r
+        ///\r
+        /// Fill all PRP entries except of last one.\r
+        ///\r
+        CopyMem (PrpEntry, (VOID *)(UINTN) (&PhysicalAddr), sizeof (UINT64));\r
+        PhysicalAddr += EFI_PAGE_SIZE;\r
+      } else {\r
+        ///\r
+        /// Fill last PRP entries with next PRP List pointer.\r
+        ///\r
+        NewPhyAddr = (PrpListPhyAddr + (PrpListIndex + 1) * EFI_PAGE_SIZE);\r
+        CopyMem (PrpEntry, (VOID *)(UINTN) (&NewPhyAddr), sizeof (UINT64));\r
+      }\r
+    }\r
+  }\r
+\r
+  ///\r
+  /// Fill last PRP list.\r
+  ///\r
+  PrpListBase = *(UINT64*)PrpListHost + PrpListIndex * EFI_PAGE_SIZE;\r
+  for (PrpEntryIndex = 0; PrpEntryIndex < ((Remainder != 0) ? Remainder : PrpEntryNo); ++PrpEntryIndex) {\r
+    PrpEntry = (UINT8 *)(UINTN) (PrpListBase + PrpEntryIndex * sizeof(UINT64));\r
+    CopyMem (PrpEntry, (VOID *)(UINTN) (&PhysicalAddr), sizeof (UINT64));\r
+\r
+    PhysicalAddr += EFI_PAGE_SIZE;\r
+  }\r
+\r
+  return PrpListPhyAddr;\r
+}\r
+\r
+/**\r
+  Check whether there are available command slots.\r
+\r
+  @param[in] Nvme                   - The pointer to the NVME_CONTEXT Data structure.\r
+  @param[in] Qid                    - Queue index\r
+\r
+  @retval EFI_SUCCESS               - Available command slot is found\r
+  @retval EFI_NOT_READY             - No available command slot is found\r
+  @retval EFI_DEVICE_ERROR          - Error occurred on device side.\r
+\r
+**/\r
+EFI_STATUS\r
+NvmeHasFreeCmdSlot (\r
+  IN NVME_CONTEXT       *Nvme,\r
+  IN UINT8              Qid\r
+  )\r
+{\r
+  return TRUE;\r
+}\r
+\r
+/**\r
+  Check whether all command slots are clean.\r
+\r
+  @param[in] Nvme                   - The pointer to the NVME_CONTEXT Data structure.\r
+  @param[in] Qid                    - Queue index\r
+\r
+  @retval EFI_SUCCESS               - All command slots are clean\r
+  @retval EFI_NOT_READY             - Not all command slots are clean\r
+  @retval EFI_DEVICE_ERROR          - Error occurred on device side.\r
+\r
+**/\r
+EFI_STATUS\r
+NvmeIsAllCmdSlotClean (\r
+  IN NVME_CONTEXT       *Nvme,\r
+  IN UINT8              Qid\r
+  )\r
+{\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Waits until all NVME commands completed.\r
+\r
+  @param[in] Nvme                   - The pointer to the NVME_CONTEXT Data structure.\r
+  @param[in] Qid                    - Queue index\r
+\r
+  @retval EFI_SUCCESS               - All NVME commands have completed\r
+  @retval EFI_TIMEOUT               - Timeout occured\r
+  @retval EFI_NOT_READY             - Not all NVME commands have completed\r
+  @retval others                    - Error occurred on device side.\r
+**/\r
+EFI_STATUS\r
+NvmeWaitAllComplete (\r
+  IN NVME_CONTEXT       *Nvme,\r
+  IN UINT8              Qid\r
+  )\r
+{\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Sends an NVM Express Command Packet to an NVM Express controller or namespace. This function supports\r
+  both blocking I/O and nonblocking I/O. The blocking I/O functionality is required, and the nonblocking\r
+  I/O functionality is optional.\r
+\r
+  @param[in] Nvme                   - The pointer to the NVME_CONTEXT Data structure.\r
+  @param[in] NamespaceId            - Is a 32 bit Namespace ID to which the Express HCI command packet will be sent.\r
+                                      A Value of 0 denotes the NVM Express controller, a Value of all 0FFh in the namespace\r
+                                      ID specifies that the command packet should be sent to all valid namespaces.\r
+  @param[in] NamespaceUuid          - Is a 64 bit Namespace UUID to which the Express HCI command packet will be sent.\r
+                                      A Value of 0 denotes the NVM Express controller, a Value of all 0FFh in the namespace\r
+                                      UUID specifies that the command packet should be sent to all valid namespaces.\r
+  @param[in,out] Packet             - A pointer to the NVM Express HCI Command Packet to send to the NVMe namespace specified\r
+                                      by NamespaceId.\r
+\r
+  @retval EFI_SUCCESS               - The NVM Express Command Packet was sent by the host. TransferLength bytes were transferred\r
+                                      to, or from DataBuffer.\r
+  @retval EFI_NOT_READY             - The NVM Express Command Packet could not be sent because the controller is not ready. The caller\r
+                                      may retry again later.\r
+  @retval EFI_DEVICE_ERROR          - A device error occurred while attempting to send the NVM Express Command Packet.\r
+  @retval EFI_INVALID_PARAMETER     - Namespace, or the contents of NVM_EXPRESS_PASS_THRU_COMMAND_PACKET are invalid. The NVM\r
+                                      Express Command Packet was not sent, so no additional status information is available.\r
+  @retval EFI_UNSUPPORTED           - The command described by the NVM Express Command Packet is not supported by the host adapter.\r
+                                      The NVM Express Command Packet was not sent, so no additional status information is available.\r
+  @retval EFI_TIMEOUT               - A timeout occurred while waiting for the NVM Express Command Packet to execute.\r
+\r
+**/\r
+EFI_STATUS\r
+NvmePassThru (\r
+  IN     NVME_CONTEXT                         *Nvme,\r
+  IN     UINT32                               NamespaceId,\r
+  IN     UINT64                               NamespaceUuid,\r
+  IN OUT NVM_EXPRESS_PASS_THRU_COMMAND_PACKET *Packet\r
+  )\r
+{\r
+  EFI_STATUS                    Status;\r
+  NVME_SQ                       *Sq;\r
+  NVME_CQ                       *Cq;\r
+  UINT8                         Qid;\r
+  UINT32                        Bytes;\r
+  UINT32                        Offset;\r
+  EFI_PHYSICAL_ADDRESS          PhyAddr;\r
+  VOID                          *PrpListHost;\r
+  UINTN                         PrpListNo;\r
+  UINT32                        Timer;\r
+  UINTN SqSize;\r
+  UINTN CqSize;\r
+\r
+  ///\r
+  /// check the Data fields in Packet parameter.\r
+  ///\r
+  if ((Nvme == NULL) || (Packet == NULL)) {\r
+    DEBUG ((DEBUG_ERROR, "NvmePassThru, invalid parameter: Nvme(%x)/Packet(%x)\n",\r
+      (UINTN)Nvme, (UINTN)Packet));\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if ((Packet->NvmeCmd == NULL) || (Packet->NvmeResponse == NULL)) {\r
+    DEBUG ((DEBUG_ERROR, "NvmePassThru, invalid parameter: NvmeCmd(%x)/NvmeResponse(%x)\n",\r
+      (UINTN)Packet->NvmeCmd, (UINTN)Packet->NvmeResponse));\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (Packet->QueueId != NVME_ADMIN_QUEUE && Packet->QueueId != NVME_IO_QUEUE) {\r
+    DEBUG ((DEBUG_ERROR, "NvmePassThru, invalid parameter: QueueId(%x)\n",\r
+      Packet->QueueId));\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  PrpListHost = NULL;\r
+  PrpListNo   = 0;\r
+  Status      = EFI_SUCCESS;\r
+\r
+  Qid = Packet->QueueId;\r
+  Sq  = Nvme->SqBuffer[Qid] + Nvme->SqTdbl[Qid].Sqt;\r
+  Cq  = Nvme->CqBuffer[Qid] + Nvme->CqHdbl[Qid].Cqh;\r
+  if (Qid == NVME_ADMIN_QUEUE) {\r
+    SqSize = NVME_ASQ_SIZE + 1;\r
+    CqSize = NVME_ACQ_SIZE + 1;\r
+  } else {\r
+    SqSize = NVME_CSQ_DEPTH;\r
+    CqSize = NVME_CCQ_DEPTH;\r
+  }\r
+\r
+  if (Packet->NvmeCmd->Nsid != NamespaceId) {\r
+    DEBUG ((DEBUG_ERROR, "NvmePassThru: Nsid mismatch (%x, %x)\n",\r
+      Packet->NvmeCmd->Nsid, NamespaceId));\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  ZeroMem (Sq, sizeof (NVME_SQ));\r
+  Sq->Opc  = Packet->NvmeCmd->Cdw0.Opcode;\r
+  Sq->Fuse = Packet->NvmeCmd->Cdw0.FusedOperation;\r
+  Sq->Cid  = Packet->NvmeCmd->Cdw0.Cid;\r
+  Sq->Nsid = Packet->NvmeCmd->Nsid;\r
+\r
+  ///\r
+  /// Currently we only support PRP for Data transfer, SGL is NOT supported.\r
+  ///\r
+  ASSERT (Sq->Psdt == 0);\r
+  if (Sq->Psdt != 0) {\r
+    DEBUG ((DEBUG_ERROR, "NvmePassThru: doesn't support SGL mechanism\n"));\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  Sq->Prp[0] = Packet->TransferBuffer;\r
+  Sq->Prp[1] = 0;\r
+\r
+  if(Packet->MetadataBuffer != (UINT64)(UINTN)NULL) {\r
+    Sq->Mptr = Packet->MetadataBuffer;\r
+  }\r
+\r
+  ///\r
+  /// If the Buffer Size spans more than two memory pages (page Size as defined in CC.Mps),\r
+  /// then build a PRP list in the second PRP submission queue entry.\r
+  ///\r
+  Offset = ((UINT32)Sq->Prp[0]) & (EFI_PAGE_SIZE - 1);\r
+  Bytes  = Packet->TransferLength;\r
+\r
+  if ((Offset + Bytes) > (EFI_PAGE_SIZE * 2)) {\r
+    ///\r
+    /// Create PrpList for remaining Data Buffer.\r
+    ///\r
+    PhyAddr = (Sq->Prp[0] + EFI_PAGE_SIZE) & ~(EFI_PAGE_SIZE - 1);\r
+    Sq->Prp[1] = NvmeCreatePrpList (Nvme, Nvme->SqTdbl[Qid].Sqt, PhyAddr, EFI_SIZE_TO_PAGES(Offset + Bytes) - 1, &PrpListHost, &PrpListNo);\r
+    if (Sq->Prp[1] == 0) {\r
+      Status = EFI_OUT_OF_RESOURCES;\r
+      DEBUG ((DEBUG_ERROR, "NvmeCreatePrpList fail, Status: %r\n", Status));\r
+      goto EXIT;\r
+    }\r
+\r
+  } else if ((Offset + Bytes) > EFI_PAGE_SIZE) {\r
+    Sq->Prp[1] = (Sq->Prp[0] + EFI_PAGE_SIZE) & ~(EFI_PAGE_SIZE - 1);\r
+  }\r
+\r
+  if(Packet->NvmeCmd->Flags & CDW10_VALID) {\r
+    Sq->Payload.Raw.Cdw10 = Packet->NvmeCmd->Cdw10;\r
+  }\r
+  if(Packet->NvmeCmd->Flags & CDW11_VALID) {\r
+    Sq->Payload.Raw.Cdw11 = Packet->NvmeCmd->Cdw11;\r
+  }\r
+  if(Packet->NvmeCmd->Flags & CDW12_VALID) {\r
+    Sq->Payload.Raw.Cdw12 = Packet->NvmeCmd->Cdw12;\r
+  }\r
+  if(Packet->NvmeCmd->Flags & CDW13_VALID) {\r
+    Sq->Payload.Raw.Cdw13 = Packet->NvmeCmd->Cdw13;\r
+  }\r
+  if(Packet->NvmeCmd->Flags & CDW14_VALID) {\r
+    Sq->Payload.Raw.Cdw14 = Packet->NvmeCmd->Cdw14;\r
+  }\r
+  if(Packet->NvmeCmd->Flags & CDW15_VALID) {\r
+    Sq->Payload.Raw.Cdw15 = Packet->NvmeCmd->Cdw15;\r
+  }\r
+\r
+#if (EN_NVME_VERBOSE_DBINFO == ON)\r
+  //DumpMem (Sq, sizeof (NVME_SQ));\r
+#endif\r
+  ///\r
+  /// Ring the submission queue doorbell.\r
+  ///\r
+  Nvme->SqTdbl[Qid].Sqt++;\r
+  if(Nvme->SqTdbl[Qid].Sqt == SqSize) {\r
+    Nvme->SqTdbl[Qid].Sqt = 0;\r
+  }\r
+  Status = NVME_SET_SQTDBL (Nvme, Qid, &Nvme->SqTdbl[Qid]);\r
+  if (EFI_ERROR(Status)) {\r
+    DEBUG ((DEBUG_ERROR, "NVME_SET_SQTDBL fail, Status: %r\n", Status));\r
+    goto EXIT;\r
+  }\r
+\r
+  ///\r
+  /// Wait for completion queue to get filled in.\r
+  ///\r
+  Status = EFI_TIMEOUT;\r
+  Timer   = 0;\r
+  while (Timer < NVME_CMD_TIMEOUT) {\r
+    //DEBUG ((DEBUG_VERBOSE, "Timer: %x, Cq:\n", Timer));\r
+    //DumpMem (Cq, sizeof (NVME_CQ));\r
+    if (Cq->Pt != Nvme->Pt[Qid]) {\r
+      Status = EFI_SUCCESS;\r
+      break;\r
+    }\r
+\r
+    MicroSecondDelay (NVME_CMD_WAIT);\r
+    Timer += NVME_CMD_WAIT;\r
+  }\r
+\r
+  Nvme->CqHdbl[Qid].Cqh++;\r
+  if (Nvme->CqHdbl[Qid].Cqh == CqSize) {\r
+    Nvme->CqHdbl[Qid].Cqh = 0;\r
+    Nvme->Pt[Qid] ^= 1;\r
+  }\r
+\r
+  ///\r
+  /// Copy the Respose Queue entry for this command to the callers response Buffer\r
+  ///\r
+  CopyMem (Packet->NvmeResponse, Cq, sizeof(NVM_EXPRESS_RESPONSE));\r
+\r
+  if (!EFI_ERROR(Status)) { // We still need to check CQ status if no timeout error occured\r
+    Status = NvmeCheckCqStatus (Cq);\r
+  }\r
+  NVME_SET_CQHDBL (Nvme, Qid, &Nvme->CqHdbl[Qid]);\r
+\r
+EXIT:\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Get identify controller Data.\r
+\r
+  @param[in] Nvme                   - The pointer to the NVME_CONTEXT Data structure.\r
+  @param[in] Buffer                 - The Buffer used to store the identify controller Data.\r
+\r
+  @return EFI_SUCCESS               - Successfully get the identify controller Data.\r
+  @return others                    - Fail to get the identify controller Data.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+NvmeIdentifyController (\r
+  IN NVME_CONTEXT                          *Nvme,\r
+  IN VOID                                  *Buffer\r
+  )\r
+{\r
+  NVM_EXPRESS_PASS_THRU_COMMAND_PACKET     CommandPacket;\r
+  NVM_EXPRESS_COMMAND                      Command;\r
+  NVM_EXPRESS_RESPONSE                     Response;\r
+  EFI_STATUS                               Status;\r
+\r
+  ZeroMem (&CommandPacket, sizeof(NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));\r
+  ZeroMem (&Command, sizeof(NVM_EXPRESS_COMMAND));\r
+  ZeroMem (&Response, sizeof(NVM_EXPRESS_RESPONSE));\r
+\r
+  Command.Cdw0.Opcode = NVME_ADMIN_IDENTIFY_OPC;\r
+  Command.Cdw0.Cid    = Nvme->Cid[NVME_ADMIN_QUEUE]++;\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
+\r
+  CommandPacket.NvmeCmd        = &Command;\r
+  CommandPacket.NvmeResponse   = &Response;\r
+  CommandPacket.TransferBuffer = (UINT64)(UINTN)Buffer;\r
+  CommandPacket.TransferLength = sizeof (NVME_ADMIN_CONTROLLER_DATA);\r
+  CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;\r
+  CommandPacket.QueueId        = NVME_ADMIN_QUEUE;\r
+  //\r
+  // Set bit 0 (Cns bit) to 1 to identify a controller\r
+  //\r
+  Command.Cdw10                = 1;\r
+  Command.Flags                = CDW10_VALID;\r
+\r
+  Status = NvmePassThru (\r
+              Nvme,\r
+              NVME_CONTROLLER_ID,\r
+              0,\r
+              &CommandPacket\r
+              );\r
+  if (!EFI_ERROR (Status)) {\r
+    Status = NvmeWaitAllComplete (Nvme, CommandPacket.QueueId);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Get specified identify namespace Data.\r
+\r
+  @param[in] Nvme                   - The pointer to the NVME_CONTEXT Data structure.\r
+  @param[in] NamespaceId            - The specified namespace identifier.\r
+  @param[in] Buffer                 - The Buffer used to store the identify namespace Data.\r
+\r
+  @return EFI_SUCCESS               - Successfully get the identify namespace Data.\r
+  @return others                    - Fail to get the identify namespace Data.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+NvmeIdentifyNamespace (\r
+  IN NVME_CONTEXT                          *Nvme,\r
+  IN UINT32                                NamespaceId,\r
+  IN VOID                                  *Buffer\r
+  )\r
+{\r
+  NVM_EXPRESS_PASS_THRU_COMMAND_PACKET     CommandPacket;\r
+  NVM_EXPRESS_COMMAND                      Command;\r
+  NVM_EXPRESS_RESPONSE                     Response;\r
+  EFI_STATUS                               Status;\r
+\r
+  ZeroMem (&CommandPacket, sizeof(NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));\r
+  ZeroMem (&Command, sizeof(NVM_EXPRESS_COMMAND));\r
+  ZeroMem (&Response, sizeof(NVM_EXPRESS_RESPONSE));\r
+\r
+  CommandPacket.NvmeCmd      = &Command;\r
+  CommandPacket.NvmeResponse = &Response;\r
+\r
+  Command.Cdw0.Opcode = NVME_ADMIN_IDENTIFY_OPC;\r
+  Command.Cdw0.Cid    = Nvme->Cid[NVME_ADMIN_QUEUE]++;\r
+  Command.Nsid        = NamespaceId;\r
+  CommandPacket.TransferBuffer = (UINT64)(UINTN)Buffer;\r
+  CommandPacket.TransferLength = sizeof (NVME_ADMIN_NAMESPACE_DATA);\r
+  CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;\r
+  CommandPacket.QueueId        = NVME_ADMIN_QUEUE;\r
+  //\r
+  // Set bit 0 (Cns bit) to 1 to identify a namespace\r
+  //\r
+  CommandPacket.NvmeCmd->Cdw10 = 0;\r
+  CommandPacket.NvmeCmd->Flags = CDW10_VALID;\r
+\r
+  Status = NvmePassThru (\r
+              Nvme,\r
+              NamespaceId,\r
+              0,\r
+              &CommandPacket\r
+              );\r
+  if (!EFI_ERROR (Status)) {\r
+    Status = NvmeWaitAllComplete (Nvme, CommandPacket.QueueId);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Get Block Size for specific namespace of NVME.\r
+\r
+  @param[in] Nvme                   - The pointer to the NVME_CONTEXT Data structure.\r
+\r
+  @return                           - Block Size in bytes\r
+\r
+**/\r
+STATIC\r
+UINT32\r
+NvmeGetBlockSize (\r
+  IN NVME_CONTEXT       *Nvme\r
+  )\r
+{\r
+  UINT32                BlockSize;\r
+  UINT32                Lbads;\r
+  UINT32                Flbas;\r
+  UINT32                LbaFmtIdx;\r
+\r
+  Flbas     = Nvme->NamespaceData->Flbas;\r
+  LbaFmtIdx = Flbas & 3;\r
+  Lbads     = Nvme->NamespaceData->LbaFormat[LbaFmtIdx].Lbads;\r
+\r
+  BlockSize = (UINT32)1 << Lbads;\r
+  return BlockSize;\r
+}\r
+\r
+/**\r
+  Get last LBA for specific namespace of NVME.\r
+\r
+  @param[in] Nvme                   - The pointer to the NVME_CONTEXT Data structure.\r
+\r
+  @return                           - Last LBA address\r
+\r
+**/\r
+STATIC\r
+EFI_LBA\r
+NvmeGetLastLba (\r
+  IN NVME_CONTEXT       *Nvme\r
+  )\r
+{\r
+  EFI_LBA               LastBlock;\r
+  LastBlock = Nvme->NamespaceData->Nsze - 1;\r
+  return LastBlock;\r
+}\r
+\r
+/**\r
+  Create io completion queue.\r
+\r
+  @param[in] Nvme                   - The pointer to the NVME_CONTEXT Data structure.\r
+\r
+  @return EFI_SUCCESS               - Successfully create io completion queue.\r
+  @return others                    - Fail to create io completion queue.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+NvmeCreateIoCompletionQueue (\r
+  IN     NVME_CONTEXT                      *Nvme\r
+  )\r
+{\r
+  NVM_EXPRESS_PASS_THRU_COMMAND_PACKET     CommandPacket;\r
+  NVM_EXPRESS_COMMAND                      Command;\r
+  NVM_EXPRESS_RESPONSE                     Response;\r
+  EFI_STATUS                               Status;\r
+  NVME_ADMIN_CRIOCQ                        CrIoCq;\r
+\r
+  ZeroMem (&CommandPacket, sizeof(NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));\r
+  ZeroMem (&Command, sizeof(NVM_EXPRESS_COMMAND));\r
+  ZeroMem (&Response, sizeof(NVM_EXPRESS_RESPONSE));\r
+  ZeroMem (&CrIoCq, sizeof(NVME_ADMIN_CRIOCQ));\r
+\r
+  CommandPacket.NvmeCmd      = &Command;\r
+  CommandPacket.NvmeResponse = &Response;\r
+\r
+  Command.Cdw0.Opcode = NVME_ADMIN_CRIOCQ_OPC;\r
+  Command.Cdw0.Cid    = Nvme->Cid[NVME_ADMIN_QUEUE]++;\r
+  CommandPacket.TransferBuffer = (UINT64)(UINTN)Nvme->CqBuffer[NVME_IO_QUEUE];\r
+  CommandPacket.TransferLength = EFI_PAGE_SIZE;\r
+  CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;\r
+  CommandPacket.QueueId        = NVME_ADMIN_QUEUE;\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
+\r
+  Status = NvmePassThru (\r
+              Nvme,\r
+              NVME_CONTROLLER_ID,\r
+              0,\r
+              &CommandPacket\r
+              );\r
+  if (!EFI_ERROR (Status)) {\r
+    Status = NvmeWaitAllComplete (Nvme, CommandPacket.QueueId);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Create io submission queue.\r
+\r
+  @param[in] Nvme                   - The pointer to the NVME_CONTEXT Data structure.\r
+\r
+  @return EFI_SUCCESS               - Successfully create io submission queue.\r
+  @return others                    - Fail to create io submission queue.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+NvmeCreateIoSubmissionQueue (\r
+  IN NVME_CONTEXT                          *Nvme\r
+  )\r
+{\r
+  NVM_EXPRESS_PASS_THRU_COMMAND_PACKET     CommandPacket;\r
+  NVM_EXPRESS_COMMAND                      Command;\r
+  NVM_EXPRESS_RESPONSE                     Response;\r
+  EFI_STATUS                               Status;\r
+  NVME_ADMIN_CRIOSQ                        CrIoSq;\r
+\r
+  ZeroMem (&CommandPacket, sizeof(NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));\r
+  ZeroMem (&Command, sizeof(NVM_EXPRESS_COMMAND));\r
+  ZeroMem (&Response, sizeof(NVM_EXPRESS_RESPONSE));\r
+  ZeroMem (&CrIoSq, sizeof(NVME_ADMIN_CRIOSQ));\r
+\r
+  CommandPacket.NvmeCmd      = &Command;\r
+  CommandPacket.NvmeResponse = &Response;\r
+\r
+  Command.Cdw0.Opcode = NVME_ADMIN_CRIOSQ_OPC;\r
+  Command.Cdw0.Cid    = Nvme->Cid[NVME_ADMIN_QUEUE]++;\r
+  CommandPacket.TransferBuffer = (UINT64)(UINTN)Nvme->SqBuffer[NVME_IO_QUEUE];\r
+  CommandPacket.TransferLength = EFI_PAGE_SIZE;\r
+  CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;\r
+  CommandPacket.QueueId        = NVME_ADMIN_QUEUE;\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
+\r
+  Status = NvmePassThru (\r
+              Nvme,\r
+              NVME_CONTROLLER_ID,\r
+              0,\r
+              &CommandPacket\r
+              );\r
+  if (!EFI_ERROR (Status)) {\r
+    Status = NvmeWaitAllComplete (Nvme, CommandPacket.QueueId);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Security send and receive commands.\r
+\r
+  @param[in]     Nvme                   - The pointer to the NVME_CONTEXT Data structure.\r
+  @param[in]     SendCommand            - The flag to indicate the command type, TRUE for Send command and FALSE for receive command\r
+  @param[in]     SecurityProtocol       - Security Protocol\r
+  @param[in]     SpSpecific             - Security Protocol Specific\r
+  @param[in]     TransferLength         - Transfer Length of Buffer (in bytes) - always a multiple of 512\r
+  @param[in,out] TransferBuffer         - Address of Data to transfer\r
+\r
+  @return EFI_SUCCESS               - Successfully create io submission queue.\r
+  @return others                    - Fail to send/receive commands.\r
+\r
+**/\r
+EFI_STATUS\r
+NvmeSecuritySendReceive (\r
+  IN NVME_CONTEXT                          *Nvme,\r
+  IN BOOLEAN                               SendCommand,\r
+  IN UINT8                                 SecurityProtocol,\r
+  IN UINT16                                SpSpecific,\r
+  IN UINTN                                 TransferLength,\r
+  IN OUT VOID                              *TransferBuffer\r
+  )\r
+{\r
+  NVM_EXPRESS_PASS_THRU_COMMAND_PACKET     CommandPacket;\r
+  NVM_EXPRESS_COMMAND                      Command;\r
+  NVM_EXPRESS_RESPONSE                     Response;\r
+  EFI_STATUS                               Status;\r
+  NVME_ADMIN_SECSEND                       SecSend;\r
+  OACS                                     *Oacs;\r
+  UINT8                                    Opcode;\r
+  VOID*                                    *SecBuff;\r
+\r
+  Oacs = (OACS *)&Nvme->ControllerData->Oacs;\r
+\r
+  //\r
+  // Verify security bit for Security Send/Receive commands\r
+  //\r
+  if (Oacs->Security == 0) {\r
+    DEBUG ((DEBUG_ERROR, "Security command doesn't support.\n"));\r
+    return EFI_NOT_READY;\r
+  }\r
+\r
+  SecBuff = (VOID *)(UINTN) NVME_SEC_BASE (Nvme);\r
+\r
+  //\r
+  // Actions for sending security command\r
+  //\r
+  if (SendCommand) {\r
+#if (EN_NVME_VERBOSE_DBINFO == ON)\r
+    //DumpMem (TransferBuffer, TransferLength);\r
+#endif\r
+    CopyMem (SecBuff, TransferBuffer, TransferLength);\r
+  }\r
+\r
+  ZeroMem (&CommandPacket, sizeof(NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));\r
+  ZeroMem (&Command, sizeof(NVM_EXPRESS_COMMAND));\r
+  ZeroMem (&Response, sizeof(NVM_EXPRESS_RESPONSE));\r
+  ZeroMem (&SecSend, sizeof(NVME_ADMIN_SECSEND));\r
+\r
+  CommandPacket.NvmeCmd      = &Command;\r
+  CommandPacket.NvmeResponse = &Response;\r
+\r
+  Opcode = (UINT8)(SendCommand ? NVME_ADMIN_SECURITY_SEND_OPC : NVME_ADMIN_SECURITY_RECV_OPC);\r
+  Command.Cdw0.Opcode = Opcode;\r
+  Command.Cdw0.Cid    = Nvme->Cid[NVME_ADMIN_QUEUE]++;\r
+  CommandPacket.TransferBuffer = (UINT64)(UINTN)SecBuff;\r
+  CommandPacket.TransferLength = (UINT32)TransferLength;\r
+  CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;\r
+  CommandPacket.QueueId        = NVME_ADMIN_QUEUE;\r
+\r
+  SecSend.Spsp = SpSpecific;\r
+  SecSend.Secp = SecurityProtocol;\r
+  SecSend.Tl   = (UINT32)TransferLength;\r
+\r
+  CopyMem (&CommandPacket.NvmeCmd->Cdw10, &SecSend, sizeof (NVME_ADMIN_SECSEND));\r
+  CommandPacket.NvmeCmd->Flags = CDW10_VALID | CDW11_VALID;\r
+\r
+  Status = NvmePassThru (\r
+              Nvme,\r
+              NVME_CONTROLLER_ID,\r
+              0,\r
+              &CommandPacket\r
+              );\r
+  if (!EFI_ERROR (Status)) {\r
+    Status = NvmeWaitAllComplete (Nvme, CommandPacket.QueueId);\r
+  }\r
+\r
+  //\r
+  // Actions for receiving security command\r
+  //\r
+  if (!SendCommand) {\r
+    CopyMem (TransferBuffer, SecBuff, TransferLength);\r
+#if (EN_NVME_VERBOSE_DBINFO == ON)\r
+    //DumpMem (TransferBuffer, TransferLength);\r
+#endif\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Destroy io completion queue.\r
+\r
+  @param[in] Nvme                   - The pointer to the NVME_CONTEXT Data structure.\r
+\r
+  @return EFI_SUCCESS               - Successfully destroy io completion queue.\r
+  @return others                    - Fail to destroy io completion queue.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+NvmeDestroyIoCompletionQueue (\r
+  IN     NVME_CONTEXT                      *Nvme\r
+  )\r
+{\r
+  NVM_EXPRESS_PASS_THRU_COMMAND_PACKET     CommandPacket;\r
+  NVM_EXPRESS_COMMAND                      Command;\r
+  NVM_EXPRESS_RESPONSE                     Response;\r
+  EFI_STATUS                               Status;\r
+  NVME_ADMIN_DEIOCQ                        DelIoCq;\r
+\r
+  ZeroMem (&CommandPacket, sizeof(NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));\r
+  ZeroMem (&Command, sizeof(NVM_EXPRESS_COMMAND));\r
+  ZeroMem (&Response, sizeof(NVM_EXPRESS_RESPONSE));\r
+  ZeroMem (&DelIoCq, sizeof(NVME_ADMIN_DEIOCQ));\r
+\r
+  CommandPacket.NvmeCmd      = &Command;\r
+  CommandPacket.NvmeResponse = &Response;\r
+\r
+  Command.Cdw0.Opcode = NVME_ADMIN_DELIOCQ_OPC;\r
+  Command.Cdw0.Cid    = Nvme->Cid[NVME_ADMIN_QUEUE]++;\r
+  CommandPacket.TransferBuffer = (UINT64)(UINTN)Nvme->CqBuffer[NVME_IO_QUEUE];\r
+  CommandPacket.TransferLength = EFI_PAGE_SIZE;\r
+  CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;\r
+  CommandPacket.QueueId        = NVME_ADMIN_QUEUE;\r
+\r
+  DelIoCq.Qid   = NVME_IO_QUEUE;\r
+  CopyMem (&CommandPacket.NvmeCmd->Cdw10, &DelIoCq, sizeof (NVME_ADMIN_DEIOCQ));\r
+  CommandPacket.NvmeCmd->Flags = CDW10_VALID | CDW11_VALID;\r
+\r
+  Status = NvmePassThru (\r
+              Nvme,\r
+              NVME_CONTROLLER_ID,\r
+              0,\r
+              &CommandPacket\r
+              );\r
+  if (!EFI_ERROR (Status)) {\r
+    Status = NvmeWaitAllComplete (Nvme, CommandPacket.QueueId);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Destroy io submission queue.\r
+\r
+  @param[in] Nvme                   - The pointer to the NVME_CONTEXT Data structure.\r
+\r
+  @return EFI_SUCCESS               - Successfully destroy io submission queue.\r
+  @return others                    - Fail to destroy io submission queue.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+NvmeDestroyIoSubmissionQueue (\r
+  IN NVME_CONTEXT                          *Nvme\r
+  )\r
+{\r
+  NVM_EXPRESS_PASS_THRU_COMMAND_PACKET     CommandPacket;\r
+  NVM_EXPRESS_COMMAND                      Command;\r
+  NVM_EXPRESS_RESPONSE                     Response;\r
+  EFI_STATUS                               Status;\r
+  NVME_ADMIN_DEIOSQ                        DelIoSq;\r
+\r
+  ZeroMem (&CommandPacket, sizeof(NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));\r
+  ZeroMem (&Command, sizeof(NVM_EXPRESS_COMMAND));\r
+  ZeroMem (&Response, sizeof(NVM_EXPRESS_RESPONSE));\r
+  ZeroMem (&DelIoSq, sizeof(NVME_ADMIN_DEIOSQ));\r
+\r
+  CommandPacket.NvmeCmd      = &Command;\r
+  CommandPacket.NvmeResponse = &Response;\r
+\r
+  Command.Cdw0.Opcode = NVME_ADMIN_DELIOSQ_OPC;\r
+  Command.Cdw0.Cid    = Nvme->Cid[NVME_ADMIN_QUEUE]++;\r
+  CommandPacket.TransferBuffer = (UINT64)(UINTN)Nvme->SqBuffer[NVME_IO_QUEUE];\r
+  CommandPacket.TransferLength = EFI_PAGE_SIZE;\r
+  CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;\r
+  CommandPacket.QueueId        = NVME_ADMIN_QUEUE;\r
+\r
+  DelIoSq.Qid   = NVME_IO_QUEUE;\r
+  CopyMem (&CommandPacket.NvmeCmd->Cdw10, &DelIoSq, sizeof (NVME_ADMIN_DEIOSQ));\r
+  CommandPacket.NvmeCmd->Flags = CDW10_VALID | CDW11_VALID;\r
+\r
+  Status = NvmePassThru (\r
+              Nvme,\r
+              NVME_CONTROLLER_ID,\r
+              0,\r
+              &CommandPacket\r
+              );\r
+  if (!EFI_ERROR (Status)) {\r
+    Status = NvmeWaitAllComplete (Nvme, CommandPacket.QueueId);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Allocate transfer-related Data struct which is used at Nvme.\r
+\r
+  @param[in] ImageHandle         Image handle for this driver image\r
+  @param[in] Nvme                The pointer to the NVME_CONTEXT Data structure.\r
+\r
+  @retval  EFI_OUT_OF_RESOURCE   The allocation is failure.\r
+  @retval  EFI_SUCCESS           Successful to allocate memory.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+NvmeAllocateResource (\r
+  IN EFI_HANDLE                         ImageHandle,\r
+  IN NVME_CONTEXT                       *Nvme\r
+  )\r
+{\r
+  EFI_STATUS            Status;\r
+  EFI_PHYSICAL_ADDRESS  Addr;\r
+  UINT32                Size;\r
+\r
+  //\r
+  // Allocate resources required by NVMe host controller.\r
+  //\r
+  // NBAR\r
+  Size = 0x10000;\r
+  Addr = 0xFFFFFFFF;\r
+  Status = gDS->AllocateMemorySpace (\r
+                  EfiGcdAllocateMaxAddressSearchBottomUp,\r
+                  EfiGcdMemoryTypeMemoryMappedIo,\r
+                  15,                             // 2^15: 32K Alignment\r
+                  Size,\r
+                  &Addr,\r
+                  ImageHandle,\r
+                  NULL\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+  Nvme->Nbar = (UINT32) Addr;\r
+\r
+  // DMA Buffer\r
+  Size = NVME_MEM_MAX_SIZE;\r
+  Addr = 0xFFFFFFFF;\r
+  Status = gBS->AllocatePages (\r
+                  AllocateMaxAddress,\r
+                  EfiACPIMemoryNVS,\r
+                  EFI_SIZE_TO_PAGES (Size),\r
+                  (EFI_PHYSICAL_ADDRESS *)&Addr\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+  Nvme->BaseMem = (UINT32) Addr;\r
+\r
+  // Clean up DMA Buffer before using\r
+  ZeroMem ((VOID *)(UINTN)Addr, NVME_MEM_MAX_SIZE);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Free allocated transfer-related Data struct which is used at NVMe.\r
+\r
+  @param[in] Nvme                The pointer to the NVME_CONTEXT Data structure.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+NvmeFreeResource (\r
+  IN NVME_CONTEXT                       *Nvme\r
+  )\r
+{\r
+  UINT32                Size;\r
+\r
+  // NBAR\r
+  if (Nvme->BaseMem != 0) {\r
+    Size = 0x10000;\r
+    gDS->FreeMemorySpace (Nvme->Nbar, Size);\r
+  }\r
+\r
+  // DMA Buffer\r
+  if (Nvme->Nbar != 0) {\r
+    Size = NVME_MEM_MAX_SIZE;\r
+    gBS->FreePages ((EFI_PHYSICAL_ADDRESS)(UINTN) Nvme->Nbar, EFI_SIZE_TO_PAGES (Size));\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Initialize the Nvm Express controller.\r
+\r
+  @param[in] Nvme                   - The pointer to the NVME_CONTEXT Data structure.\r
+\r
+  @retval EFI_SUCCESS               - The NVM Express Controller is initialized successfully.\r
+  @retval Others                    - A device error occurred while initializing the controller.\r
+\r
+**/\r
+EFI_STATUS\r
+NvmeControllerInit (\r
+  IN NVME_CONTEXT       *Nvme\r
+  )\r
+{\r
+  EFI_STATUS            Status;\r
+  NVME_AQA              Aqa;\r
+  NVME_ASQ              Asq;\r
+  NVME_ACQ              Acq;\r
+  NVME_VER              Ver;\r
+\r
+  UINT32                MlBAR;\r
+  UINT32                MuBAR;\r
+\r
+  ///\r
+  /// Update PCIE BAR0/1 for NVME device\r
+  ///\r
+  MlBAR = Nvme->Nbar;\r
+  MuBAR = 0;\r
+  PciWrite32 (Nvme->PciBase + 0x10, MlBAR); // MLBAR (BAR0)\r
+  PciWrite32 (Nvme->PciBase + 0x14, MuBAR); // MUBAR (BAR1)\r
+\r
+  ///\r
+  /// Enable PCIE decode\r
+  ///\r
+  PciWrite8 (Nvme->PciBase + NVME_PCIE_PCICMD, 0x6);\r
+\r
+  // Version\r
+  NVME_GET_VER (Nvme, &Ver);\r
+  if (!(Ver.Mjr == 0x0001) && (Ver.Mnr == 0x0000)) {\r
+    DEBUG ((DEBUG_INFO, "\n!!!\n!!! NVME Version mismatch for the implementation !!!\n!!!\n"));\r
+  }\r
+\r
+  ///\r
+  /// Read the Controller Capabilities register and verify that the NVM command set is supported\r
+  ///\r
+  Status = NVME_GET_CAP (Nvme, &Nvme->Cap);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((DEBUG_ERROR, "NVME_GET_CAP fail, Status: %r\n", Status));\r
+    goto Done;\r
+  }\r
+\r
+  if (Nvme->Cap.Css != 0x01) {\r
+    DEBUG ((DEBUG_ERROR, "NvmeControllerInit fail: the controller doesn't support NVMe command set\n"));\r
+    Status = EFI_UNSUPPORTED;\r
+    goto Done;\r
+  }\r
+\r
+  ///\r
+  /// Currently the driver only supports 4k page Size.\r
+  ///\r
+  if ((Nvme->Cap.Mpsmin + 12) > EFI_PAGE_SHIFT) {\r
+    DEBUG ((DEBUG_ERROR, "NvmeControllerInit fail: only supports 4k page Size\n"));\r
+    ASSERT (FALSE);\r
+    Status = EFI_UNSUPPORTED;\r
+    goto Done;\r
+  }\r
+\r
+  Nvme->Cid[0] = 0;\r
+  Nvme->Cid[1] = 0;\r
+\r
+  Status = NvmeDisableController (Nvme);\r
+  if (EFI_ERROR(Status)) {\r
+    DEBUG ((DEBUG_ERROR, "NvmeDisableController fail, Status: %r\n", Status));\r
+    goto Done;\r
+  }\r
+\r
+  ///\r
+  /// set number of entries admin submission & completion queues.\r
+  ///\r
+  Aqa.Asqs  = NVME_ASQ_SIZE;\r
+  Aqa.Rsvd1 = 0;\r
+  Aqa.Acqs  = NVME_ACQ_SIZE;\r
+  Aqa.Rsvd2 = 0;\r
+\r
+  ///\r
+  /// Address of admin submission queue.\r
+  ///\r
+  Asq = (UINT64)(UINTN)(NVME_ASQ_BASE (Nvme) & ~0xFFF);\r
+\r
+  ///\r
+  /// Address of admin completion queue.\r
+  ///\r
+  Acq = (UINT64)(UINTN)(NVME_ACQ_BASE (Nvme) & ~0xFFF);\r
+\r
+  ///\r
+  /// Address of I/O submission & completion queue.\r
+  ///\r
+  Nvme->SqBuffer[0] = (NVME_SQ *)(UINTN)NVME_ASQ_BASE (Nvme);   // NVME_ADMIN_QUEUE\r
+  Nvme->CqBuffer[0] = (NVME_CQ *)(UINTN)NVME_ACQ_BASE (Nvme);   // NVME_ADMIN_QUEUE\r
+  Nvme->SqBuffer[1] = (NVME_SQ *)(UINTN)NVME_SQ_BASE (Nvme, 0); // NVME_IO_QUEUE\r
+  Nvme->CqBuffer[1] = (NVME_CQ *)(UINTN)NVME_CQ_BASE (Nvme, 0); // NVME_IO_QUEUE\r
+\r
+  DEBUG ((DEBUG_INFO, "BaseMem = [%08X]\n", Nvme->BaseMem));\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]) =   [%08X]\n", Nvme->SqBuffer[0]));\r
+  DEBUG ((DEBUG_INFO, "Admin Completion Queue (CqBuffer[0]) =   [%08X]\n", Nvme->CqBuffer[0]));\r
+  DEBUG ((DEBUG_INFO, "I/O   Submission Queue (SqBuffer[1]) =   [%08X]\n", Nvme->SqBuffer[1]));\r
+  DEBUG ((DEBUG_INFO, "I/O   Completion Queue (CqBuffer[1]) =   [%08X]\n", Nvme->CqBuffer[1]));\r
+\r
+  ///\r
+  /// Program admin queue attributes.\r
+  ///\r
+  Status = NVME_SET_AQA (Nvme, &Aqa);\r
+  if (EFI_ERROR(Status)) {\r
+    goto Done;\r
+  }\r
+\r
+  ///\r
+  /// Program admin submission queue address.\r
+  ///\r
+  Status = NVME_SET_ASQ (Nvme, &Asq);\r
+  if (EFI_ERROR(Status)) {\r
+    goto Done;\r
+  }\r
+\r
+  ///\r
+  /// Program admin completion queue address.\r
+  ///\r
+  Status = NVME_SET_ACQ (Nvme, &Acq);\r
+  if (EFI_ERROR(Status)) {\r
+    goto Done;\r
+  }\r
+\r
+  Status = NvmeEnableController (Nvme);\r
+  if (EFI_ERROR(Status)) {\r
+    goto Done;\r
+  }\r
+\r
+  ///\r
+  /// Create one I/O completion queue.\r
+  ///\r
+  Status = NvmeCreateIoCompletionQueue (Nvme);\r
+  if (EFI_ERROR(Status)) {\r
+    goto Done;\r
+  }\r
+\r
+  ///\r
+  /// Create one I/O Submission queue.\r
+  ///\r
+  Status = NvmeCreateIoSubmissionQueue (Nvme);\r
+  if (EFI_ERROR(Status)) {\r
+    goto Done;\r
+  }\r
+\r
+  ///\r
+  /// Get current Identify Controller Data\r
+  ///\r
+  Nvme->ControllerData = (NVME_ADMIN_CONTROLLER_DATA *)(UINTN) NVME_CONTROL_DATA_BASE (Nvme);\r
+  Status = NvmeIdentifyController (Nvme, Nvme->ControllerData);\r
+  if (EFI_ERROR(Status)) {\r
+    goto Done;\r
+  }\r
+\r
+  ///\r
+  /// Dump NvmExpress Identify Controller Data\r
+  ///\r
+  Nvme->ControllerData->Sn[19] = 0;\r
+  Nvme->ControllerData->Mn[39] = 0;\r
+  //NvmeDumpIdentifyController (Nvme->ControllerData);\r
+\r
+  ///\r
+  /// Get current Identify Namespace Data\r
+  ///\r
+  Nvme->NamespaceData = (NVME_ADMIN_NAMESPACE_DATA *)NVME_NAMESPACE_DATA_BASE (Nvme);\r
+  Status = NvmeIdentifyNamespace (Nvme, Nvme->Nsid, Nvme->NamespaceData);\r
+  if (EFI_ERROR(Status)) {\r
+    DEBUG ((DEBUG_ERROR, "NvmeIdentifyNamespace fail, Status = %r\n", Status));\r
+    goto Done;\r
+  }\r
+\r
+  ///\r
+  /// Dump NvmExpress Identify Namespace Data\r
+  ///\r
+  if (Nvme->NamespaceData->Ncap == 0) {\r
+    DEBUG ((DEBUG_ERROR, "Invalid Namespace, Ncap: %lx\n", Nvme->NamespaceData->Ncap));\r
+    Status = EFI_DEVICE_ERROR;\r
+    goto Done;\r
+  }\r
+\r
+  Nvme->BlockSize = NvmeGetBlockSize (Nvme);\r
+  Nvme->LastBlock = NvmeGetLastLba (Nvme);\r
+\r
+  Nvme->State    = NvmeStatusInit;\r
+\r
+  return EFI_SUCCESS;\r
+\r
+Done:\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Un-initialize the Nvm Express controller.\r
+\r
+  @param[in] Nvme                   - The pointer to the NVME_CONTEXT Data structure.\r
+\r
+  @retval EFI_SUCCESS               - The NVM Express Controller is un-initialized successfully.\r
+  @retval Others                    - A device error occurred while un-initializing the controller.\r
+\r
+**/\r
+EFI_STATUS\r
+NvmeControllerExit (\r
+  IN NVME_CONTEXT       *Nvme\r
+  )\r
+{\r
+  EFI_STATUS            Status;\r
+\r
+  Status = EFI_SUCCESS;\r
+  if (Nvme->State == NvmeStatusInit || Nvme->State == NvmeStatusMax) {\r
+    ///\r
+    /// Destroy I/O Submission queue.\r
+    ///\r
+    Status = NvmeDestroyIoSubmissionQueue (Nvme);\r
+    if (EFI_ERROR(Status)) {\r
+      DEBUG ((DEBUG_ERROR, "NvmeDestroyIoSubmissionQueue fail, Status = %r\n", Status));\r
+      return Status;\r
+    }\r
+\r
+    ///\r
+    /// Destroy I/O completion queue.\r
+    ///\r
+    Status = NvmeDestroyIoCompletionQueue (Nvme);\r
+    if (EFI_ERROR(Status)) {\r
+      DEBUG ((DEBUG_ERROR, "NvmeDestroyIoCompletionQueue fail, Status = %r\n", Status));\r
+      return Status;\r
+    }\r
+\r
+    Status = NvmeShutdownController (Nvme);\r
+    if (EFI_ERROR(Status)) {\r
+      DEBUG ((DEBUG_ERROR, "NvmeShutdownController fail, Status: %r\n", Status));\r
+    }\r
+  }\r
+\r
+  ///\r
+  /// Disable PCIE decode\r
+  ///\r
+  PciWrite8  (Nvme->PciBase + NVME_PCIE_PCICMD, 0x0);\r
+  PciWrite32 (Nvme->PciBase + 0x10, 0); // MLBAR (BAR0)\r
+  PciWrite32 (Nvme->PciBase + 0x14, 0); // MUBAR (BAR1)\r
+\r
+  Nvme->State = NvmeStatusUnknown;\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Read sector Data from the NVMe device.\r
+\r
+  @param[in] Nvme                   - The pointer to the NVME_CONTEXT Data structure.\r
+  @param[in,out] Buffer             - The Buffer used to store the Data read from the device.\r
+  @param[in] Lba                    - The start block number.\r
+  @param[in] Blocks                 - Total block number to be read.\r
+\r
+  @retval EFI_SUCCESS               - Datum are read from the device.\r
+  @retval Others                    - Fail to read all the datum.\r
+\r
+**/\r
+EFI_STATUS\r
+NvmeReadSectors (\r
+  IN NVME_CONTEXT                          *Nvme,\r
+  IN OUT UINT64                            Buffer,\r
+  IN UINT64                                Lba,\r
+  IN UINT32                                Blocks\r
+  )\r
+{\r
+  UINT32                                   Bytes;\r
+  NVM_EXPRESS_PASS_THRU_COMMAND_PACKET     CommandPacket;\r
+  NVM_EXPRESS_COMMAND                      Command;\r
+  NVM_EXPRESS_RESPONSE                     Response;\r
+  EFI_STATUS                               Status;\r
+  UINT32                                   BlockSize;\r
+\r
+  BlockSize  = Nvme->BlockSize;\r
+  Bytes      = Blocks * BlockSize;\r
+\r
+  ZeroMem (&CommandPacket, sizeof(NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));\r
+  ZeroMem (&Command, sizeof(NVM_EXPRESS_COMMAND));\r
+  ZeroMem (&Response, sizeof(NVM_EXPRESS_RESPONSE));\r
+\r
+  CommandPacket.NvmeCmd      = &Command;\r
+  CommandPacket.NvmeResponse = &Response;\r
+\r
+  CommandPacket.NvmeCmd->Cdw0.Opcode = NVME_IO_READ_OPC;\r
+  CommandPacket.NvmeCmd->Cdw0.Cid    = Nvme->Cid[NVME_IO_QUEUE]++;\r
+  CommandPacket.NvmeCmd->Nsid        = Nvme->Nsid;\r
+  CommandPacket.TransferBuffer       = Buffer;\r
+\r
+  CommandPacket.TransferLength = Bytes;\r
+  CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;\r
+  CommandPacket.QueueId        = NVME_IO_QUEUE;\r
+\r
+  CommandPacket.NvmeCmd->Cdw10 = (UINT32)Lba;\r
+  CommandPacket.NvmeCmd->Cdw11 = (UINT32)(RShiftU64 (Lba, 32));\r
+  CommandPacket.NvmeCmd->Cdw12 = (Blocks - 1) & 0xFFFF;\r
+\r
+  CommandPacket.NvmeCmd->Flags = CDW10_VALID | CDW11_VALID | CDW12_VALID;\r
+\r
+  Status = NvmePassThru (\r
+              Nvme,\r
+              Nvme->Nsid,\r
+              0,\r
+              &CommandPacket\r
+              );\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Write sector Data to the NVMe device.\r
+\r
+  @param[in] Nvme                   - The pointer to the NVME_CONTEXT Data structure.\r
+  @param[in] Buffer                 - The Buffer to be written into the device.\r
+  @param[in] Lba                    - The start block number.\r
+  @param[in] Blocks                 - Total block number to be written.\r
+\r
+  @retval EFI_SUCCESS               - Datum are written into the Buffer.\r
+  @retval Others                    - Fail to write all the datum.\r
+\r
+**/\r
+EFI_STATUS\r
+NvmeWriteSectors (\r
+  IN NVME_CONTEXT                          *Nvme,\r
+  IN UINT64                                Buffer,\r
+  IN UINT64                                Lba,\r
+  IN UINT32                                Blocks\r
+  )\r
+{\r
+  NVM_EXPRESS_PASS_THRU_COMMAND_PACKET     CommandPacket;\r
+  NVM_EXPRESS_COMMAND                      Command;\r
+  NVM_EXPRESS_RESPONSE                     Response;\r
+  EFI_STATUS                               Status;\r
+  UINT32                                   Bytes;\r
+  UINT32                                   BlockSize;\r
+\r
+  BlockSize  = Nvme->BlockSize;\r
+  Bytes      = Blocks * BlockSize;\r
+\r
+  ZeroMem (&CommandPacket, sizeof(NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));\r
+  ZeroMem (&Command, sizeof(NVM_EXPRESS_COMMAND));\r
+  ZeroMem (&Response, sizeof(NVM_EXPRESS_RESPONSE));\r
+\r
+  CommandPacket.NvmeCmd      = &Command;\r
+  CommandPacket.NvmeResponse = &Response;\r
+\r
+  CommandPacket.NvmeCmd->Cdw0.Opcode = NVME_IO_WRITE_OPC;\r
+  CommandPacket.NvmeCmd->Cdw0.Cid    = Nvme->Cid[NVME_IO_QUEUE]++;\r
+  CommandPacket.NvmeCmd->Nsid  = Nvme->Nsid;\r
+  CommandPacket.TransferBuffer = Buffer;\r
+\r
+  CommandPacket.TransferLength = Bytes;\r
+  CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;\r
+  CommandPacket.QueueId        = NVME_IO_QUEUE;\r
+\r
+  CommandPacket.NvmeCmd->Cdw10 = (UINT32)Lba;\r
+  CommandPacket.NvmeCmd->Cdw11 = (UINT32)(RShiftU64 (Lba, 32));\r
+  CommandPacket.NvmeCmd->Cdw12 = (Blocks - 1) & 0xFFFF;\r
+\r
+  CommandPacket.MetadataBuffer = (UINT64)(UINTN)NULL;\r
+  CommandPacket.MetadataLength = 0;\r
+\r
+  CommandPacket.NvmeCmd->Flags = CDW10_VALID | CDW11_VALID | CDW12_VALID;\r
+\r
+  Status = NvmePassThru (\r
+              Nvme,\r
+              Nvme->Nsid,\r
+              0,\r
+              &CommandPacket\r
+              );\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Flushes all modified Data to the device.\r
+\r
+  @param[in] Nvme                   - The pointer to the NVME_CONTEXT Data structure.\r
+\r
+  @retval EFI_SUCCESS               - Datum are written into the Buffer.\r
+  @retval Others                    - Fail to write all the datum.\r
+\r
+**/\r
+EFI_STATUS\r
+NvmeFlush (\r
+  IN NVME_CONTEXT                          *Nvme\r
+  )\r
+{\r
+  NVM_EXPRESS_PASS_THRU_COMMAND_PACKET     CommandPacket;\r
+  NVM_EXPRESS_COMMAND                      Command;\r
+  NVM_EXPRESS_RESPONSE                     Response;\r
+  EFI_STATUS                               Status;\r
+\r
+  ZeroMem (&CommandPacket, sizeof(NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));\r
+  ZeroMem (&Command, sizeof(NVM_EXPRESS_COMMAND));\r
+  ZeroMem (&Response, sizeof(NVM_EXPRESS_RESPONSE));\r
+\r
+  CommandPacket.NvmeCmd      = &Command;\r
+  CommandPacket.NvmeResponse = &Response;\r
+\r
+  CommandPacket.NvmeCmd->Cdw0.Opcode = NVME_IO_FLUSH_OPC;\r
+  CommandPacket.NvmeCmd->Cdw0.Cid    = Nvme->Cid[NVME_IO_QUEUE]++;\r
+  CommandPacket.NvmeCmd->Nsid  = Nvme->Nsid;\r
+  CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;\r
+  CommandPacket.QueueId        = NVME_IO_QUEUE;\r
+\r
+  Status = NvmePassThru (\r
+              Nvme,\r
+              Nvme->Nsid,\r
+              0,\r
+              &CommandPacket\r
+              );\r
+  if (!EFI_ERROR (Status)) {\r
+    Status = NvmeWaitAllComplete (Nvme, CommandPacket.QueueId);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Read some blocks from the device.\r
+\r
+  @param[in] Nvme                   - The pointer to the NVME_CONTEXT Data structure.\r
+  @param[out] Buffer                - The Buffer used to store the Data read from the device.\r
+  @param[in] Lba                    - The start block number.\r
+  @param[in] Blocks                 - Total block number to be read.\r
+\r
+  @retval EFI_SUCCESS               - Datum are read from the device.\r
+  @retval Others                    - Fail to read all the datum.\r
+\r
+**/\r
+EFI_STATUS\r
+NvmeRead (\r
+  IN NVME_CONTEXT                  *Nvme,\r
+  OUT UINT64                       Buffer,\r
+  IN UINT64                        Lba,\r
+  IN UINTN                         Blocks\r
+  )\r
+{\r
+  EFI_STATUS                       Status;\r
+  UINT32                           BlockSize;\r
+  UINT32                           MaxTransferBlocks;\r
+\r
+  ASSERT (Blocks <= NVME_MAX_SECTORS);\r
+  Status        = EFI_SUCCESS;\r
+  BlockSize     = Nvme->BlockSize;\r
+  if (Nvme->ControllerData->Mdts != 0) {\r
+    MaxTransferBlocks = (1 << (Nvme->ControllerData->Mdts)) * (1 << (Nvme->Cap.Mpsmin + 12)) / BlockSize;\r
+  } else {\r
+    MaxTransferBlocks = 1024;\r
+  }\r
+\r
+  while (Blocks > 0) {\r
+    if (Blocks > MaxTransferBlocks) {\r
+      Status = NvmeReadSectors (Nvme, Buffer, Lba, MaxTransferBlocks);\r
+\r
+      Blocks -= MaxTransferBlocks;\r
+      Buffer += (MaxTransferBlocks * BlockSize);\r
+      Lba    += MaxTransferBlocks;\r
+    } else {\r
+      Status = NvmeReadSectors (Nvme, Buffer, Lba, (UINT32) Blocks);\r
+      Blocks = 0;\r
+    }\r
+\r
+    if (EFI_ERROR(Status)) {\r
+      DEBUG ((DEBUG_ERROR, "NvmeRead fail, Status = %r\n", Status));\r
+      break;\r
+    }\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Write some blocks to the device.\r
+\r
+  @param[in] Nvme                   - The pointer to the NVME_CONTEXT Data structure.\r
+  @param[in] Buffer                 - The Buffer to be written into the device.\r
+  @param[in] Lba                    - The start block number.\r
+  @param[in] Blocks                 - Total block number to be written.\r
+\r
+  @retval EFI_SUCCESS               - Datum are written into the Buffer.\r
+  @retval Others                    - Fail to write all the datum.\r
+\r
+**/\r
+EFI_STATUS\r
+NvmeWrite (\r
+  IN NVME_CONTEXT                  *Nvme,\r
+  IN UINT64                        Buffer,\r
+  IN UINT64                        Lba,\r
+  IN UINTN                         Blocks\r
+  )\r
+{\r
+  EFI_STATUS                       Status;\r
+  UINT32                           BlockSize;\r
+  UINT32                           MaxTransferBlocks;\r
+\r
+  ASSERT (Blocks <= NVME_MAX_SECTORS);\r
+  Status        = EFI_SUCCESS;\r
+  BlockSize     = Nvme->BlockSize;\r
+\r
+  if (Nvme->ControllerData->Mdts != 0) {\r
+    MaxTransferBlocks = (1 << (Nvme->ControllerData->Mdts)) * (1 << (Nvme->Cap.Mpsmin + 12)) / BlockSize;\r
+  } else {\r
+    MaxTransferBlocks = 1024;\r
+  }\r
+\r
+  while (Blocks > 0) {\r
+    if (Blocks > MaxTransferBlocks) {\r
+      Status = NvmeWriteSectors (Nvme, Buffer, Lba, MaxTransferBlocks);\r
+\r
+      Blocks -= MaxTransferBlocks;\r
+      Buffer += (MaxTransferBlocks * BlockSize);\r
+      Lba    += MaxTransferBlocks;\r
+    } else {\r
+      Status = NvmeWriteSectors (Nvme, Buffer, Lba, (UINT32) Blocks);\r
+      Blocks = 0;\r
+    }\r
+\r
+    if (EFI_ERROR(Status)) {\r
+      DEBUG ((DEBUG_ERROR, "NvmeWrite fail, Status = %r\n", Status));\r
+      break;\r
+    }\r
+  }\r
+\r
+  return Status;\r
+}\r