]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressBlockIo.c
MdeModulePkg NvmExpressDxe: Add BlockIo2 support
[mirror_edk2.git] / MdeModulePkg / Bus / Pci / NvmExpressDxe / NvmExpressBlockIo.c
index 92d7d923df437cb8c6f998a2aa627464deac943f..734e1286c61e501cf39c5d4410e28cef5dea01a5 100644 (file)
@@ -172,6 +172,23 @@ NvmeRead (
   NVME_CONTROLLER_PRIVATE_DATA     *Private;\r
   UINT32                           MaxTransferBlocks;\r
   UINTN                            OrginalBlocks;\r
+  BOOLEAN                          IsEmpty;\r
+  EFI_TPL                          OldTpl;\r
+\r
+  //\r
+  // Wait for the device's asynchronous I/O queue to become empty.\r
+  //\r
+  while (TRUE) {\r
+    OldTpl  = gBS->RaiseTPL (TPL_NOTIFY);\r
+    IsEmpty = IsListEmpty (&Device->AsyncQueue);\r
+    gBS->RestoreTPL (OldTpl);\r
+\r
+    if (IsEmpty) {\r
+      break;\r
+    }\r
+\r
+    gBS->Stall (100);\r
+  }\r
 \r
   Status        = EFI_SUCCESS;\r
   Private       = Device->Controller;\r
@@ -233,6 +250,23 @@ NvmeWrite (
   NVME_CONTROLLER_PRIVATE_DATA     *Private;\r
   UINT32                           MaxTransferBlocks;\r
   UINTN                            OrginalBlocks;\r
+  BOOLEAN                          IsEmpty;\r
+  EFI_TPL                          OldTpl;\r
+\r
+  //\r
+  // Wait for the device's asynchronous I/O queue to become empty.\r
+  //\r
+  while (TRUE) {\r
+    OldTpl  = gBS->RaiseTPL (TPL_NOTIFY);\r
+    IsEmpty = IsListEmpty (&Device->AsyncQueue);\r
+    gBS->RestoreTPL (OldTpl);\r
+\r
+    if (IsEmpty) {\r
+      break;\r
+    }\r
+\r
+    gBS->Stall (100);\r
+  }\r
 \r
   Status        = EFI_SUCCESS;\r
   Private       = Device->Controller;\r
@@ -313,6 +347,587 @@ NvmeFlush (
   return Status;\r
 }\r
 \r
+/**\r
+  Nonblocking I/O callback funtion when the event is signaled.\r
+\r
+  @param[in]  Event     The Event this notify function registered to.\r
+  @param[in]  Context   Pointer to the context data registered to the\r
+                        Event.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+AsyncIoCallback (\r
+  IN EFI_EVENT                Event,\r
+  IN VOID                     *Context\r
+  )\r
+{\r
+  NVME_BLKIO2_SUBTASK         *Subtask;\r
+  NVME_BLKIO2_REQUEST         *Request;\r
+  NVME_CQ                     *Completion;\r
+  EFI_BLOCK_IO2_TOKEN         *Token;\r
+\r
+  gBS->CloseEvent (Event);\r
+\r
+  Subtask    = (NVME_BLKIO2_SUBTASK *) Context;\r
+  Completion = (NVME_CQ *) Subtask->CommandPacket->NvmeCompletion;\r
+  Request    = Subtask->BlockIo2Request;\r
+  Token      = Request->Token;\r
+\r
+  if (Token->TransactionStatus == EFI_SUCCESS) {\r
+    //\r
+    // If previous subtask already fails, do not check the result of\r
+    // subsequent subtasks.\r
+    //\r
+    if ((Completion->Sct != 0) || (Completion->Sc != 0)) {\r
+      Token->TransactionStatus = EFI_DEVICE_ERROR;\r
+\r
+      //\r
+      // Dump completion entry status for debugging.\r
+      //\r
+      DEBUG_CODE_BEGIN();\r
+        NvmeDumpStatus (Completion);\r
+      DEBUG_CODE_END();\r
+    }\r
+  }\r
+\r
+  //\r
+  // Remove the subtask from the BlockIo2 subtasks list.\r
+  //\r
+  RemoveEntryList (&Subtask->Link);\r
+\r
+  if (IsListEmpty (&Request->SubtasksQueue) && Request->LastSubtaskSubmitted) {\r
+    //\r
+    // Remove the BlockIo2 request from the device asynchronous queue.\r
+    //\r
+    RemoveEntryList (&Request->Link);\r
+    FreePool (Request);\r
+    gBS->SignalEvent (Token->Event);\r
+  }\r
+\r
+  FreePool (Subtask->CommandPacket->NvmeCmd);\r
+  FreePool (Subtask->CommandPacket->NvmeCompletion);\r
+  FreePool (Subtask->CommandPacket);\r
+  FreePool (Subtask);\r
+}\r
+\r
+/**\r
+  Read some sectors from the device in an asynchronous manner.\r
+\r
+  @param  Device        The pointer to the NVME_DEVICE_PRIVATE_DATA data\r
+                        structure.\r
+  @param  Request       The pointer to the NVME_BLKIO2_REQUEST data structure.\r
+  @param  Buffer        The buffer used to store the data read from the device.\r
+  @param  Lba           The start block number.\r
+  @param  Blocks        Total block number to be read.\r
+  @param  IsLast        The last subtask of an asynchronous read request.\r
+\r
+  @retval EFI_SUCCESS   Asynchronous read request has been queued.\r
+  @retval Others        Fail to send the asynchronous request.\r
+\r
+**/\r
+EFI_STATUS\r
+AsyncReadSectors (\r
+  IN NVME_DEVICE_PRIVATE_DATA           *Device,\r
+  IN NVME_BLKIO2_REQUEST                *Request,\r
+  IN UINT64                             Buffer,\r
+  IN UINT64                             Lba,\r
+  IN UINT32                             Blocks,\r
+  IN BOOLEAN                            IsLast\r
+  )\r
+{\r
+  NVME_CONTROLLER_PRIVATE_DATA             *Private;\r
+  UINT32                                   Bytes;\r
+  NVME_BLKIO2_SUBTASK                      *Subtask;\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
+  UINT32                                   BlockSize;\r
+  EFI_TPL                                  OldTpl;\r
+\r
+  Private       = Device->Controller;\r
+  BlockSize     = Device->Media.BlockSize;\r
+  Bytes         = Blocks * BlockSize;\r
+  CommandPacket = NULL;\r
+  Command       = NULL;\r
+  Completion    = NULL;\r
+\r
+  Subtask = AllocateZeroPool (sizeof (NVME_BLKIO2_SUBTASK));\r
+  if (Subtask == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ErrorExit;\r
+  }\r
+\r
+  Subtask->Signature       = NVME_BLKIO2_SUBTASK_SIGNATURE;\r
+  Subtask->IsLast          = IsLast;\r
+  Subtask->NamespaceId     = Device->NamespaceId;\r
+  Subtask->BlockIo2Request = Request;\r
+\r
+  CommandPacket = AllocateZeroPool (sizeof (EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));\r
+  if (CommandPacket == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ErrorExit;\r
+  } else {\r
+    Subtask->CommandPacket = CommandPacket;\r
+  }\r
+\r
+  Command = AllocateZeroPool (sizeof (EFI_NVM_EXPRESS_COMMAND));\r
+  if (Command == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ErrorExit;\r
+  }\r
+\r
+  Completion = AllocateZeroPool (sizeof (EFI_NVM_EXPRESS_COMPLETION));\r
+  if (Completion == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ErrorExit;\r
+  }\r
+\r
+  //\r
+  // Create Event\r
+  //\r
+  Status = gBS->CreateEvent (\r
+                  EVT_NOTIFY_SIGNAL,\r
+                  TPL_NOTIFY,\r
+                  AsyncIoCallback,\r
+                  Subtask,\r
+                  &Subtask->Event\r
+                  );\r
+  if (EFI_ERROR(Status)) {\r
+    goto ErrorExit;\r
+  }\r
+\r
+  CommandPacket->NvmeCmd        = Command;\r
+  CommandPacket->NvmeCompletion = Completion;\r
+\r
+  CommandPacket->NvmeCmd->Cdw0.Opcode = NVME_IO_READ_OPC;\r
+  CommandPacket->NvmeCmd->Nsid        = Device->NamespaceId;\r
+  CommandPacket->TransferBuffer       = (VOID *)(UINTN)Buffer;\r
+\r
+  CommandPacket->TransferLength = Bytes;\r
+  CommandPacket->CommandTimeout = NVME_GENERIC_TIMEOUT;\r
+  CommandPacket->QueueType      = 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
+  OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
+  InsertTailList (&Private->UnsubmittedSubtasks, &Subtask->Link);\r
+  Request->UnsubmittedSubtaskNum++;\r
+  gBS->RestoreTPL (OldTpl);\r
+\r
+  return EFI_SUCCESS;\r
+\r
+ErrorExit:\r
+  //\r
+  // Resource cleanup if asynchronous read request has not been queued.\r
+  //\r
+  if (Completion != NULL) {\r
+    FreePool (Completion);\r
+  }\r
+\r
+  if (Command != NULL) {\r
+    FreePool (Command);\r
+  }\r
+\r
+  if (CommandPacket != NULL) {\r
+    FreePool (CommandPacket);\r
+  }\r
+\r
+  if (Subtask != NULL) {\r
+    if (Subtask->Event != NULL) {\r
+      gBS->CloseEvent (Subtask->Event);\r
+    }\r
+\r
+    FreePool (Subtask);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Write some sectors from the device in an asynchronous manner.\r
+\r
+  @param  Device        The pointer to the NVME_DEVICE_PRIVATE_DATA data\r
+                        structure.\r
+  @param  Request       The pointer to the NVME_BLKIO2_REQUEST data structure.\r
+  @param  Buffer        The buffer used to store the data written to the\r
+                        device.\r
+  @param  Lba           The start block number.\r
+  @param  Blocks        Total block number to be written.\r
+  @param  IsLast        The last subtask of an asynchronous write request.\r
+\r
+  @retval EFI_SUCCESS   Asynchronous write request has been queued.\r
+  @retval Others        Fail to send the asynchronous request.\r
+\r
+**/\r
+EFI_STATUS\r
+AsyncWriteSectors (\r
+  IN NVME_DEVICE_PRIVATE_DATA           *Device,\r
+  IN NVME_BLKIO2_REQUEST                *Request,\r
+  IN UINT64                             Buffer,\r
+  IN UINT64                             Lba,\r
+  IN UINT32                             Blocks,\r
+  IN BOOLEAN                            IsLast\r
+  )\r
+{\r
+  NVME_CONTROLLER_PRIVATE_DATA             *Private;\r
+  UINT32                                   Bytes;\r
+  NVME_BLKIO2_SUBTASK                      *Subtask;\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
+  UINT32                                   BlockSize;\r
+  EFI_TPL                                  OldTpl;\r
+\r
+  Private       = Device->Controller;\r
+  BlockSize     = Device->Media.BlockSize;\r
+  Bytes         = Blocks * BlockSize;\r
+  CommandPacket = NULL;\r
+  Command       = NULL;\r
+  Completion    = NULL;\r
+\r
+  Subtask = AllocateZeroPool (sizeof (NVME_BLKIO2_SUBTASK));\r
+  if (Subtask == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ErrorExit;\r
+  }\r
+\r
+  Subtask->Signature       = NVME_BLKIO2_SUBTASK_SIGNATURE;\r
+  Subtask->IsLast          = IsLast;\r
+  Subtask->NamespaceId     = Device->NamespaceId;\r
+  Subtask->BlockIo2Request = Request;\r
+\r
+  CommandPacket = AllocateZeroPool (sizeof (EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));\r
+  if (CommandPacket == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ErrorExit;\r
+  } else {\r
+    Subtask->CommandPacket = CommandPacket;\r
+  }\r
+\r
+  Command = AllocateZeroPool (sizeof (EFI_NVM_EXPRESS_COMMAND));\r
+  if (Command == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ErrorExit;\r
+  }\r
+\r
+  Completion = AllocateZeroPool (sizeof (EFI_NVM_EXPRESS_COMPLETION));\r
+  if (Completion == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ErrorExit;\r
+  }\r
+\r
+  //\r
+  // Create Event\r
+  //\r
+  Status = gBS->CreateEvent (\r
+                  EVT_NOTIFY_SIGNAL,\r
+                  TPL_NOTIFY,\r
+                  AsyncIoCallback,\r
+                  Subtask,\r
+                  &Subtask->Event\r
+                  );\r
+  if (EFI_ERROR(Status)) {\r
+    goto ErrorExit;\r
+  }\r
+\r
+  CommandPacket->NvmeCmd        = Command;\r
+  CommandPacket->NvmeCompletion = Completion;\r
+\r
+  CommandPacket->NvmeCmd->Cdw0.Opcode = NVME_IO_WRITE_OPC;\r
+  CommandPacket->NvmeCmd->Nsid        = Device->NamespaceId;\r
+  CommandPacket->TransferBuffer       = (VOID *)(UINTN)Buffer;\r
+\r
+  CommandPacket->TransferLength = Bytes;\r
+  CommandPacket->CommandTimeout = NVME_GENERIC_TIMEOUT;\r
+  CommandPacket->QueueType      = NVME_IO_QUEUE;\r
+\r
+  CommandPacket->NvmeCmd->Cdw10 = (UINT32)Lba;\r
+  CommandPacket->NvmeCmd->Cdw11 = (UINT32)RShiftU64(Lba, 32);\r
+  //\r
+  // Set Force Unit Access bit (bit 30) to use write-through behaviour\r
+  //\r
+  CommandPacket->NvmeCmd->Cdw12 = ((Blocks - 1) & 0xFFFF) | BIT30;\r
+\r
+  CommandPacket->NvmeCmd->Flags = CDW10_VALID | CDW11_VALID | CDW12_VALID;\r
+\r
+  OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
+  InsertTailList (&Private->UnsubmittedSubtasks, &Subtask->Link);\r
+  Request->UnsubmittedSubtaskNum++;\r
+  gBS->RestoreTPL (OldTpl);\r
+\r
+  return EFI_SUCCESS;\r
+\r
+ErrorExit:\r
+  //\r
+  // Resource cleanup if asynchronous read request has not been queued.\r
+  //\r
+  if (Completion != NULL) {\r
+    FreePool (Completion);\r
+  }\r
+\r
+  if (Command != NULL) {\r
+    FreePool (Command);\r
+  }\r
+\r
+  if (CommandPacket != NULL) {\r
+    FreePool (CommandPacket);\r
+  }\r
+\r
+  if (Subtask != NULL) {\r
+    if (Subtask->Event != NULL) {\r
+      gBS->CloseEvent (Subtask->Event);\r
+    }\r
+\r
+    FreePool (Subtask);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Read some blocks from the device in an asynchronous manner.\r
+\r
+  @param  Device        The pointer to the NVME_DEVICE_PRIVATE_DATA data\r
+                        structure.\r
+  @param  Buffer        The buffer used to store the data read from the device.\r
+  @param  Lba           The start block number.\r
+  @param  Blocks        Total block number to be read.\r
+  @param  Token         A pointer to the token associated with the transaction.\r
+\r
+  @retval EFI_SUCCESS   Data are read from the device.\r
+  @retval Others        Fail to read all the data.\r
+\r
+**/\r
+EFI_STATUS\r
+NvmeAsyncRead (\r
+  IN     NVME_DEVICE_PRIVATE_DATA       *Device,\r
+     OUT VOID                           *Buffer,\r
+  IN     UINT64                         Lba,\r
+  IN     UINTN                          Blocks,\r
+  IN     EFI_BLOCK_IO2_TOKEN            *Token\r
+  )\r
+{\r
+  EFI_STATUS                       Status;\r
+  UINT32                           BlockSize;\r
+  NVME_CONTROLLER_PRIVATE_DATA     *Private;\r
+  NVME_BLKIO2_REQUEST              *BlkIo2Req;\r
+  UINT32                           MaxTransferBlocks;\r
+  UINTN                            OrginalBlocks;\r
+  BOOLEAN                          IsEmpty;\r
+  EFI_TPL                          OldTpl;\r
+\r
+  Status        = EFI_SUCCESS;\r
+  Private       = Device->Controller;\r
+  BlockSize     = Device->Media.BlockSize;\r
+  OrginalBlocks = Blocks;\r
+  BlkIo2Req     = AllocateZeroPool (sizeof (NVME_BLKIO2_REQUEST));\r
+  if (BlkIo2Req == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  BlkIo2Req->Signature = NVME_BLKIO2_REQUEST_SIGNATURE;\r
+  BlkIo2Req->Token     = Token;\r
+\r
+  OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
+  InsertTailList (&Device->AsyncQueue, &BlkIo2Req->Link);\r
+  gBS->RestoreTPL (OldTpl);\r
+\r
+  InitializeListHead (&BlkIo2Req->SubtasksQueue);\r
+\r
+  if (Private->ControllerData->Mdts != 0) {\r
+    MaxTransferBlocks = (1 << (Private->ControllerData->Mdts)) * (1 << (Private->Cap.Mpsmin + 12)) / BlockSize;\r
+  } else {\r
+    MaxTransferBlocks = 1024;\r
+  }\r
+\r
+  while (Blocks > 0) {\r
+    if (Blocks > MaxTransferBlocks) {\r
+      Status = AsyncReadSectors (\r
+                 Device,\r
+                 BlkIo2Req, (UINT64)(UINTN)Buffer,\r
+                 Lba,\r
+                 MaxTransferBlocks,\r
+                 FALSE\r
+                 );\r
+\r
+      Blocks -= MaxTransferBlocks;\r
+      Buffer  = (VOID *)(UINTN)((UINT64)(UINTN)Buffer + MaxTransferBlocks * BlockSize);\r
+      Lba    += MaxTransferBlocks;\r
+    } else {\r
+      Status = AsyncReadSectors (\r
+                 Device,\r
+                 BlkIo2Req,\r
+                 (UINT64)(UINTN)Buffer,\r
+                 Lba,\r
+                 (UINT32)Blocks,\r
+                 TRUE\r
+                 );\r
+\r
+      Blocks = 0;\r
+    }\r
+\r
+    if (EFI_ERROR(Status)) {\r
+      OldTpl  = gBS->RaiseTPL (TPL_NOTIFY);\r
+      IsEmpty = IsListEmpty (&BlkIo2Req->SubtasksQueue) &&\r
+                (BlkIo2Req->UnsubmittedSubtaskNum == 0);\r
+\r
+      if (IsEmpty) {\r
+        //\r
+        // Remove the BlockIo2 request from the device asynchronous queue.\r
+        //\r
+        RemoveEntryList (&BlkIo2Req->Link);\r
+        FreePool (BlkIo2Req);\r
+        Status = EFI_DEVICE_ERROR;\r
+      } else {\r
+        //\r
+        // There are previous BlockIo2 subtasks still running, EFI_SUCCESS\r
+        // should be returned to make sure that the caller does not free\r
+        // resources still using by these requests.\r
+        //\r
+        Status = EFI_SUCCESS;\r
+        Token->TransactionStatus = EFI_DEVICE_ERROR;\r
+        BlkIo2Req->LastSubtaskSubmitted = TRUE;\r
+      }\r
+\r
+      gBS->RestoreTPL (OldTpl);\r
+\r
+      break;\r
+    }\r
+  }\r
+\r
+  DEBUG ((EFI_D_VERBOSE, "%a: Lba = 0x%08Lx, Original = 0x%08Lx, "\r
+    "Remaining = 0x%08Lx, BlockSize = 0x%x, Status = %r\n", __FUNCTION__, Lba,\r
+    (UINT64)OrginalBlocks, (UINT64)Blocks, BlockSize, Status));\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Write some blocks from the device in an asynchronous manner.\r
+\r
+  @param  Device        The pointer to the NVME_DEVICE_PRIVATE_DATA data\r
+                        structure.\r
+  @param  Buffer        The buffer used to store the data written to the\r
+                        device.\r
+  @param  Lba           The start block number.\r
+  @param  Blocks        Total block number to be written.\r
+  @param  Token         A pointer to the token associated with the transaction.\r
+\r
+  @retval EFI_SUCCESS   Data are written to the device.\r
+  @retval Others        Fail to write all the data.\r
+\r
+**/\r
+EFI_STATUS\r
+NvmeAsyncWrite (\r
+  IN NVME_DEVICE_PRIVATE_DATA           *Device,\r
+  IN VOID                               *Buffer,\r
+  IN UINT64                             Lba,\r
+  IN UINTN                              Blocks,\r
+  IN EFI_BLOCK_IO2_TOKEN                *Token\r
+  )\r
+{\r
+  EFI_STATUS                       Status;\r
+  UINT32                           BlockSize;\r
+  NVME_CONTROLLER_PRIVATE_DATA     *Private;\r
+  NVME_BLKIO2_REQUEST              *BlkIo2Req;\r
+  UINT32                           MaxTransferBlocks;\r
+  UINTN                            OrginalBlocks;\r
+  BOOLEAN                          IsEmpty;\r
+  EFI_TPL                          OldTpl;\r
+\r
+  Status        = EFI_SUCCESS;\r
+  Private       = Device->Controller;\r
+  BlockSize     = Device->Media.BlockSize;\r
+  OrginalBlocks = Blocks;\r
+  BlkIo2Req     = AllocateZeroPool (sizeof (NVME_BLKIO2_REQUEST));\r
+  if (BlkIo2Req == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  BlkIo2Req->Signature = NVME_BLKIO2_REQUEST_SIGNATURE;\r
+  BlkIo2Req->Token     = Token;\r
+\r
+  OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
+  InsertTailList (&Device->AsyncQueue, &BlkIo2Req->Link);\r
+  gBS->RestoreTPL (OldTpl);\r
+\r
+  InitializeListHead (&BlkIo2Req->SubtasksQueue);\r
+\r
+  if (Private->ControllerData->Mdts != 0) {\r
+    MaxTransferBlocks = (1 << (Private->ControllerData->Mdts)) * (1 << (Private->Cap.Mpsmin + 12)) / BlockSize;\r
+  } else {\r
+    MaxTransferBlocks = 1024;\r
+  }\r
+\r
+  while (Blocks > 0) {\r
+    if (Blocks > MaxTransferBlocks) {\r
+      Status  = AsyncWriteSectors (\r
+                  Device,\r
+                  BlkIo2Req,\r
+                  (UINT64)(UINTN)Buffer,\r
+                  Lba,\r
+                  MaxTransferBlocks,\r
+                  FALSE\r
+                  );\r
+\r
+      Blocks -= MaxTransferBlocks;\r
+      Buffer  = (VOID *)(UINTN)((UINT64)(UINTN)Buffer + MaxTransferBlocks * BlockSize);\r
+      Lba    += MaxTransferBlocks;\r
+    } else {\r
+      Status = AsyncWriteSectors (\r
+                 Device,\r
+                 BlkIo2Req,\r
+                 (UINT64)(UINTN)Buffer,\r
+                 Lba,\r
+                 (UINT32)Blocks,\r
+                 TRUE\r
+                 );\r
+\r
+      Blocks = 0;\r
+    }\r
+\r
+    if (EFI_ERROR(Status)) {\r
+      OldTpl  = gBS->RaiseTPL (TPL_NOTIFY);\r
+      IsEmpty = IsListEmpty (&BlkIo2Req->SubtasksQueue) &&\r
+                (BlkIo2Req->UnsubmittedSubtaskNum == 0);\r
+\r
+      if (IsEmpty) {\r
+        //\r
+        // Remove the BlockIo2 request from the device asynchronous queue.\r
+        //\r
+        RemoveEntryList (&BlkIo2Req->Link);\r
+        FreePool (BlkIo2Req);\r
+        Status = EFI_DEVICE_ERROR;\r
+      } else {\r
+        //\r
+        // There are previous BlockIo2 subtasks still running, EFI_SUCCESS\r
+        // should be returned to make sure that the caller does not free\r
+        // resources still using by these requests.\r
+        //\r
+        Status = EFI_SUCCESS;\r
+        Token->TransactionStatus = EFI_DEVICE_ERROR;\r
+        BlkIo2Req->LastSubtaskSubmitted = TRUE;\r
+      }\r
+\r
+      gBS->RestoreTPL (OldTpl);\r
+\r
+      break;\r
+    }\r
+  }\r
+\r
+  DEBUG ((EFI_D_VERBOSE, "%a: Lba = 0x%08Lx, Original = 0x%08Lx, "\r
+    "Remaining = 0x%08Lx, BlockSize = 0x%x, Status = %r\n", __FUNCTION__, Lba,\r
+    (UINT64)OrginalBlocks, (UINT64)Blocks, BlockSize, Status));\r
+\r
+  return Status;\r
+}\r
 \r
 /**\r
   Reset the Block Device.\r
@@ -567,6 +1182,361 @@ NvmeBlockIoFlushBlocks (
   return Status;\r
 }\r
 \r
+/**\r
+  Reset the block device hardware.\r
+\r
+  @param[in]  This                 Indicates a pointer to the calling context.\r
+  @param[in]  ExtendedVerification Indicates that the driver may perform a more\r
+                                   exhausive verfication operation of the\r
+                                   device during reset.\r
+\r
+  @retval EFI_SUCCESS          The device was reset.\r
+  @retval EFI_DEVICE_ERROR     The device is not functioning properly and could\r
+                               not be reset.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+NvmeBlockIoResetEx (\r
+  IN EFI_BLOCK_IO2_PROTOCOL  *This,\r
+  IN BOOLEAN                 ExtendedVerification\r
+  )\r
+{\r
+  EFI_STATUS                      Status;\r
+  NVME_DEVICE_PRIVATE_DATA        *Device;\r
+  NVME_CONTROLLER_PRIVATE_DATA    *Private;\r
+  BOOLEAN                         IsEmpty;\r
+  EFI_TPL                         OldTpl;\r
+\r
+  if (This == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Device  = NVME_DEVICE_PRIVATE_DATA_FROM_BLOCK_IO2 (This);\r
+  Private = Device->Controller;\r
+\r
+  //\r
+  // Wait for the asynchronous PassThru queue to become empty.\r
+  //\r
+  while (TRUE) {\r
+    OldTpl  = gBS->RaiseTPL (TPL_NOTIFY);\r
+    IsEmpty = IsListEmpty (&Private->AsyncPassThruQueue) &&\r
+              IsListEmpty (&Private->UnsubmittedSubtasks);\r
+    gBS->RestoreTPL (OldTpl);\r
+\r
+    if (IsEmpty) {\r
+      break;\r
+    }\r
+\r
+    gBS->Stall (100);\r
+  }\r
+\r
+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+  Status  = NvmeControllerInit (Private);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    Status = EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  gBS->RestoreTPL (OldTpl);\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Read BufferSize bytes from Lba into Buffer.\r
+\r
+  This function reads the requested number of blocks from the device. All the\r
+  blocks are read, or an error is returned.\r
+  If EFI_DEVICE_ERROR, EFI_NO_MEDIA,_or EFI_MEDIA_CHANGED is returned and\r
+  non-blocking I/O is being used, the Event associated with this request will\r
+  not be signaled.\r
+\r
+  @param[in]       This       Indicates a pointer to the calling context.\r
+  @param[in]       MediaId    Id of the media, changes every time the media is\r
+                              replaced.\r
+  @param[in]       Lba        The starting Logical Block Address to read from.\r
+  @param[in, out]  Token      A pointer to the token associated with the\r
+                              transaction.\r
+  @param[in]       BufferSize Size of Buffer, must be a multiple of device\r
+                              block size.\r
+  @param[out]      Buffer     A pointer to the destination buffer for the data.\r
+                              The caller is responsible for either having\r
+                              implicit or explicit ownership of the buffer.\r
+\r
+  @retval EFI_SUCCESS           The read request was queued if Token->Event is\r
+                                not NULL.The data was read correctly from the\r
+                                device if the Token->Event is NULL.\r
+  @retval EFI_DEVICE_ERROR      The device reported an error while performing\r
+                                the read.\r
+  @retval EFI_NO_MEDIA          There is no media in the device.\r
+  @retval EFI_MEDIA_CHANGED     The MediaId is not for the current media.\r
+  @retval EFI_BAD_BUFFER_SIZE   The BufferSize parameter is not a multiple of\r
+                                the intrinsic block size of the device.\r
+  @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not\r
+                                valid, or the buffer is not on proper\r
+                                alignment.\r
+  @retval EFI_OUT_OF_RESOURCES  The request could not be completed due to a\r
+                                lack of resources.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+NvmeBlockIoReadBlocksEx (\r
+  IN     EFI_BLOCK_IO2_PROTOCOL *This,\r
+  IN     UINT32                 MediaId,\r
+  IN     EFI_LBA                Lba,\r
+  IN OUT EFI_BLOCK_IO2_TOKEN    *Token,\r
+  IN     UINTN                  BufferSize,\r
+     OUT VOID                  *Buffer\r
+  )\r
+{\r
+  NVME_DEVICE_PRIVATE_DATA        *Device;\r
+  EFI_STATUS                      Status;\r
+  EFI_BLOCK_IO_MEDIA              *Media;\r
+  UINTN                           BlockSize;\r
+  UINTN                           NumberOfBlocks;\r
+  UINTN                           IoAlign;\r
+  EFI_TPL                         OldTpl;\r
+\r
+  //\r
+  // Check parameters.\r
+  //\r
+  if (This == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Media = This->Media;\r
+\r
+  if (MediaId != Media->MediaId) {\r
+    return EFI_MEDIA_CHANGED;\r
+  }\r
+\r
+  if (Buffer == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (BufferSize == 0) {\r
+    if ((Token != NULL) && (Token->Event != NULL)) {\r
+      Token->TransactionStatus = EFI_SUCCESS;\r
+      gBS->SignalEvent (Token->Event);\r
+    }\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  BlockSize = Media->BlockSize;\r
+  if ((BufferSize % BlockSize) != 0) {\r
+    return EFI_BAD_BUFFER_SIZE;\r
+  }\r
+\r
+  NumberOfBlocks  = BufferSize / BlockSize;\r
+  if ((Lba + NumberOfBlocks - 1) > Media->LastBlock) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  IoAlign = Media->IoAlign;\r
+  if (IoAlign > 0 && (((UINTN) Buffer & (IoAlign - 1)) != 0)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+  Device = NVME_DEVICE_PRIVATE_DATA_FROM_BLOCK_IO2 (This);\r
+\r
+  if ((Token != NULL) && (Token->Event != NULL)) {\r
+    Token->TransactionStatus = EFI_SUCCESS;\r
+    Status = NvmeAsyncRead (Device, Buffer, Lba, NumberOfBlocks, Token);\r
+  } else {\r
+    Status = NvmeRead (Device, Buffer, Lba, NumberOfBlocks);\r
+  }\r
+\r
+  gBS->RestoreTPL (OldTpl);\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Write BufferSize bytes from Lba into Buffer.\r
+\r
+  This function writes the requested number of blocks to the device. All blocks\r
+  are written, or an error is returned.If EFI_DEVICE_ERROR, EFI_NO_MEDIA,\r
+  EFI_WRITE_PROTECTED or EFI_MEDIA_CHANGED is returned and non-blocking I/O is\r
+  being used, the Event associated with this request will not be signaled.\r
+\r
+  @param[in]       This       Indicates a pointer to the calling context.\r
+  @param[in]       MediaId    The media ID that the write request is for.\r
+  @param[in]       Lba        The starting logical block address to be written.\r
+                              The caller is responsible for writing to only\r
+                              legitimate locations.\r
+  @param[in, out]  Token      A pointer to the token associated with the\r
+                              transaction.\r
+  @param[in]       BufferSize Size of Buffer, must be a multiple of device\r
+                              block size.\r
+  @param[in]       Buffer     A pointer to the source buffer for the data.\r
+\r
+  @retval EFI_SUCCESS           The write request was queued if Event is not\r
+                                NULL.\r
+                                The data was written correctly to the device if\r
+                                the Event is NULL.\r
+  @retval EFI_WRITE_PROTECTED   The device can not be written to.\r
+  @retval EFI_NO_MEDIA          There is no media in the device.\r
+  @retval EFI_MEDIA_CHNAGED     The MediaId does not matched the current\r
+                                device.\r
+  @retval EFI_DEVICE_ERROR      The device reported an error while performing\r
+                                the write.\r
+  @retval EFI_BAD_BUFFER_SIZE   The Buffer was not a multiple of the block size\r
+                                of the device.\r
+  @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not\r
+                                valid, or the buffer is not on proper\r
+                                alignment.\r
+  @retval EFI_OUT_OF_RESOURCES  The request could not be completed due to a\r
+                                lack of resources.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+NvmeBlockIoWriteBlocksEx (\r
+  IN     EFI_BLOCK_IO2_PROTOCOL  *This,\r
+  IN     UINT32                 MediaId,\r
+  IN     EFI_LBA                Lba,\r
+  IN OUT EFI_BLOCK_IO2_TOKEN    *Token,\r
+  IN     UINTN                  BufferSize,\r
+  IN     VOID                   *Buffer\r
+  )\r
+{\r
+  NVME_DEVICE_PRIVATE_DATA        *Device;\r
+  EFI_STATUS                      Status;\r
+  EFI_BLOCK_IO_MEDIA              *Media;\r
+  UINTN                           BlockSize;\r
+  UINTN                           NumberOfBlocks;\r
+  UINTN                           IoAlign;\r
+  EFI_TPL                         OldTpl;\r
+\r
+  //\r
+  // Check parameters.\r
+  //\r
+  if (This == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Media = This->Media;\r
+\r
+  if (MediaId != Media->MediaId) {\r
+    return EFI_MEDIA_CHANGED;\r
+  }\r
+\r
+  if (Buffer == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (BufferSize == 0) {\r
+    if ((Token != NULL) && (Token->Event != NULL)) {\r
+      Token->TransactionStatus = EFI_SUCCESS;\r
+      gBS->SignalEvent (Token->Event);\r
+    }\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  BlockSize = Media->BlockSize;\r
+  if ((BufferSize % BlockSize) != 0) {\r
+    return EFI_BAD_BUFFER_SIZE;\r
+  }\r
+\r
+  NumberOfBlocks  = BufferSize / BlockSize;\r
+  if ((Lba + NumberOfBlocks - 1) > Media->LastBlock) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  IoAlign = Media->IoAlign;\r
+  if (IoAlign > 0 && (((UINTN) Buffer & (IoAlign - 1)) != 0)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+  Device = NVME_DEVICE_PRIVATE_DATA_FROM_BLOCK_IO2 (This);\r
+\r
+  if ((Token != NULL) && (Token->Event != NULL)) {\r
+    Token->TransactionStatus = EFI_SUCCESS;\r
+    Status = NvmeAsyncWrite (Device, Buffer, Lba, NumberOfBlocks, Token);\r
+  } else {\r
+    Status = NvmeWrite (Device, Buffer, Lba, NumberOfBlocks);\r
+  }\r
+\r
+  gBS->RestoreTPL (OldTpl);\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Flush the Block Device.\r
+\r
+  If EFI_DEVICE_ERROR, EFI_NO_MEDIA,_EFI_WRITE_PROTECTED or EFI_MEDIA_CHANGED\r
+  is returned and non-blocking I/O is being used, the Event associated with\r
+  this request will not be signaled.\r
+\r
+  @param[in]      This     Indicates a pointer to the calling context.\r
+  @param[in,out]  Token    A pointer to the token associated with the\r
+                           transaction.\r
+\r
+  @retval EFI_SUCCESS          The flush request was queued if Event is not\r
+                               NULL.\r
+                               All outstanding data was written correctly to\r
+                               the device if the Event is NULL.\r
+  @retval EFI_DEVICE_ERROR     The device reported an error while writting back\r
+                               the data.\r
+  @retval EFI_WRITE_PROTECTED  The device cannot be written to.\r
+  @retval EFI_NO_MEDIA         There is no media in the device.\r
+  @retval EFI_MEDIA_CHANGED    The MediaId is not for the current media.\r
+  @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack\r
+                               of resources.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+NvmeBlockIoFlushBlocksEx (\r
+  IN     EFI_BLOCK_IO2_PROTOCOL   *This,\r
+  IN OUT EFI_BLOCK_IO2_TOKEN      *Token\r
+  )\r
+{\r
+  NVME_DEVICE_PRIVATE_DATA        *Device;\r
+  BOOLEAN                         IsEmpty;\r
+  EFI_TPL                         OldTpl;\r
+\r
+  //\r
+  // Check parameters.\r
+  //\r
+  if (This == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Device = NVME_DEVICE_PRIVATE_DATA_FROM_BLOCK_IO2 (This);\r
+\r
+  //\r
+  // Wait for the asynchronous I/O queue to become empty.\r
+  //\r
+  while (TRUE) {\r
+    OldTpl  = gBS->RaiseTPL (TPL_NOTIFY);\r
+    IsEmpty = IsListEmpty (&Device->AsyncQueue);\r
+    gBS->RestoreTPL (OldTpl);\r
+\r
+    if (IsEmpty) {\r
+      break;\r
+    }\r
+\r
+    gBS->Stall (100);\r
+  }\r
+\r
+  //\r
+  // Signal caller event\r
+  //\r
+  if ((Token != NULL) && (Token->Event != NULL)) {\r
+    Token->TransactionStatus = EFI_SUCCESS;\r
+    gBS->SignalEvent (Token->Event);\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
 /**\r
   Trust transfer data from/to NVMe device.\r
 \r
@@ -619,7 +1589,7 @@ TrustTransferNvmeDevice (
   CommandPacket.NvmeCmd        = &Command;\r
   CommandPacket.NvmeCompletion = &Completion;\r
 \r
-  // \r
+  //\r
   // Change Endianness of SecurityProtocolSpecificData\r
   //\r
   SpecificData = (((SecurityProtocolSpecificData << 8) & 0xFF00) | (SecurityProtocolSpecificData >> 8));\r
@@ -748,7 +1718,7 @@ NvmeStorageSecurityReceiveData (
 {\r
   EFI_STATUS                       Status;\r
   NVME_DEVICE_PRIVATE_DATA         *Device;\r
-  \r
+\r
   Status  = EFI_SUCCESS;\r
 \r
   if ((PayloadBuffer == NULL) || (PayloadTransferSize == NULL) || (PayloadBufferSize == 0)) {\r
@@ -852,7 +1822,7 @@ NvmeStorageSecuritySendData (
   IN VOID                                     *PayloadBuffer\r
   )\r
 {\r
-  EFI_STATUS                       Status; \r
+  EFI_STATUS                       Status;\r
   NVME_DEVICE_PRIVATE_DATA         *Device;\r
 \r
   Status  = EFI_SUCCESS;\r