]> git.proxmox.com Git - mirror_edk2.git/commitdiff
MdeModulePkg/NvmExpressPei: Add the NVME device PEI BlockIo support
authorHao Wu <hao.a.wu@intel.com>
Mon, 21 May 2018 05:28:24 +0000 (13:28 +0800)
committerHao Wu <hao.a.wu@intel.com>
Mon, 25 Jun 2018 01:13:12 +0000 (09:13 +0800)
REF: https://bugzilla.tianocore.org/show_bug.cgi?id=256

This commit adds the PEI BlockIo support for NVM Express devices.

The driver will consume the EDKII_NVM_EXPRESS_HOST_CONTROLLER_PPI for NVM
Express host controllers within the system. And then produces the
BlockIo(2) PPIs for each controller.

The implementation of this driver is currently based on the NVM Express 1.1
Specification, which is available at:
http://nvmexpress.org/resources/specifications/

Cc: Star Zeng <star.zeng@intel.com>
Cc: Eric Dong <eric.dong@intel.com>
Cc: Ruiyu Ni <ruiyu.ni@intel.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Hao Wu <hao.a.wu@intel.com>
Reviewed-by: Ruiyu Ni <ruiyu.ni@intel.com>
Reviewed-by: Star Zeng <star.zeng@intel.com>
13 files changed:
MdeModulePkg/Bus/Pci/NvmExpressPei/DmaMem.c [new file with mode: 0644]
MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.c [new file with mode: 0644]
MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.h [new file with mode: 0644]
MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.inf [new file with mode: 0644]
MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.uni [new file with mode: 0644]
MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiBlockIo.c [new file with mode: 0644]
MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiBlockIo.h [new file with mode: 0644]
MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiExtra.uni [new file with mode: 0644]
MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiHci.c [new file with mode: 0644]
MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiHci.h [new file with mode: 0644]
MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiPassThru.c [new file with mode: 0644]
MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiPassThru.h [new file with mode: 0644]
MdeModulePkg/MdeModulePkg.dsc

diff --git a/MdeModulePkg/Bus/Pci/NvmExpressPei/DmaMem.c b/MdeModulePkg/Bus/Pci/NvmExpressPei/DmaMem.c
new file mode 100644 (file)
index 0000000..51b48d3
--- /dev/null
@@ -0,0 +1,249 @@
+/** @file\r
+  The DMA memory help function.\r
+\r
+  Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>\r
+\r
+  This program and the accompanying materials\r
+  are licensed and made available under the terms and conditions\r
+  of the BSD License which accompanies this distribution.  The\r
+  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 "NvmExpressPei.h"\r
+\r
+EDKII_IOMMU_PPI  *mIoMmu;\r
+\r
+/**\r
+  Provides the controller-specific addresses required to access system memory from a\r
+  DMA bus master.\r
+\r
+  @param  Operation             Indicates if the bus master is going to read or write to system memory.\r
+  @param  HostAddress           The system memory address to map to the PCI controller.\r
+  @param  NumberOfBytes         On input the number of bytes to map. On output the number of bytes\r
+                                that were mapped.\r
+  @param  DeviceAddress         The resulting map address for the bus master PCI controller to use to\r
+                                access the hosts HostAddress.\r
+  @param  Mapping               A resulting value to pass to Unmap().\r
+\r
+  @retval EFI_SUCCESS           The range was mapped for the returned NumberOfBytes.\r
+  @retval EFI_UNSUPPORTED       The HostAddress cannot be mapped as a common buffer.\r
+  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
+  @retval EFI_OUT_OF_RESOURCES  The request could not be completed due to a lack of resources.\r
+  @retval EFI_DEVICE_ERROR      The system hardware could not map the requested address.\r
+\r
+**/\r
+EFI_STATUS\r
+IoMmuMap (\r
+  IN  EDKII_IOMMU_OPERATION Operation,\r
+  IN VOID                   *HostAddress,\r
+  IN  OUT UINTN             *NumberOfBytes,\r
+  OUT EFI_PHYSICAL_ADDRESS  *DeviceAddress,\r
+  OUT VOID                  **Mapping\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+  UINT64      Attribute;\r
+\r
+  if (mIoMmu != NULL) {\r
+    Status = mIoMmu->Map (\r
+                       mIoMmu,\r
+                       Operation,\r
+                       HostAddress,\r
+                       NumberOfBytes,\r
+                       DeviceAddress,\r
+                       Mapping\r
+                       );\r
+    if (EFI_ERROR (Status)) {\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+    switch (Operation) {\r
+    case EdkiiIoMmuOperationBusMasterRead:\r
+    case EdkiiIoMmuOperationBusMasterRead64:\r
+      Attribute = EDKII_IOMMU_ACCESS_READ;\r
+      break;\r
+    case EdkiiIoMmuOperationBusMasterWrite:\r
+    case EdkiiIoMmuOperationBusMasterWrite64:\r
+      Attribute = EDKII_IOMMU_ACCESS_WRITE;\r
+      break;\r
+    case EdkiiIoMmuOperationBusMasterCommonBuffer:\r
+    case EdkiiIoMmuOperationBusMasterCommonBuffer64:\r
+      Attribute = EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE;\r
+      break;\r
+    default:\r
+      ASSERT(FALSE);\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+    Status = mIoMmu->SetAttribute (\r
+                       mIoMmu,\r
+                       *Mapping,\r
+                       Attribute\r
+                       );\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+  } else {\r
+    *DeviceAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress;\r
+    *Mapping = NULL;\r
+    Status = EFI_SUCCESS;\r
+  }\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Completes the Map() operation and releases any corresponding resources.\r
+\r
+  @param  Mapping               The mapping value returned from Map().\r
+\r
+  @retval EFI_SUCCESS           The range was unmapped.\r
+  @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by Map().\r
+  @retval EFI_DEVICE_ERROR      The data was not committed to the target system memory.\r
+**/\r
+EFI_STATUS\r
+IoMmuUnmap (\r
+  IN VOID                  *Mapping\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+\r
+  if (mIoMmu != NULL) {\r
+    Status = mIoMmu->SetAttribute (mIoMmu, Mapping, 0);\r
+    Status = mIoMmu->Unmap (mIoMmu, Mapping);\r
+  } else {\r
+    Status = EFI_SUCCESS;\r
+  }\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Allocates pages that are suitable for an OperationBusMasterCommonBuffer or\r
+  OperationBusMasterCommonBuffer64 mapping.\r
+\r
+  @param  Pages                 The number of pages to allocate.\r
+  @param  HostAddress           A pointer to store the base system memory address of the\r
+                                allocated range.\r
+  @param  DeviceAddress         The resulting map address for the bus master PCI controller to use to\r
+                                access the hosts HostAddress.\r
+  @param  Mapping               A resulting value to pass to Unmap().\r
+\r
+  @retval EFI_SUCCESS           The requested memory pages were allocated.\r
+  @retval EFI_UNSUPPORTED       Attributes is unsupported. The only legal attribute bits are\r
+                                MEMORY_WRITE_COMBINE and MEMORY_CACHED.\r
+  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
+  @retval EFI_OUT_OF_RESOURCES  The memory pages could not be allocated.\r
+\r
+**/\r
+EFI_STATUS\r
+IoMmuAllocateBuffer (\r
+  IN UINTN                  Pages,\r
+  OUT VOID                  **HostAddress,\r
+  OUT EFI_PHYSICAL_ADDRESS  *DeviceAddress,\r
+  OUT VOID                  **Mapping\r
+  )\r
+{\r
+  EFI_STATUS            Status;\r
+  UINTN                 NumberOfBytes;\r
+  EFI_PHYSICAL_ADDRESS  HostPhyAddress;\r
+\r
+  *HostAddress = NULL;\r
+  *DeviceAddress = 0;\r
+\r
+  if (mIoMmu != NULL) {\r
+    Status = mIoMmu->AllocateBuffer (\r
+                       mIoMmu,\r
+                       EfiBootServicesData,\r
+                       Pages,\r
+                       HostAddress,\r
+                       0\r
+                       );\r
+    if (EFI_ERROR (Status)) {\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+\r
+    NumberOfBytes = EFI_PAGES_TO_SIZE(Pages);\r
+    Status = mIoMmu->Map (\r
+                       mIoMmu,\r
+                       EdkiiIoMmuOperationBusMasterCommonBuffer,\r
+                       *HostAddress,\r
+                       &NumberOfBytes,\r
+                       DeviceAddress,\r
+                       Mapping\r
+                       );\r
+    if (EFI_ERROR (Status)) {\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+    Status = mIoMmu->SetAttribute (\r
+                       mIoMmu,\r
+                       *Mapping,\r
+                       EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE\r
+                       );\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+  } else {\r
+    Status = PeiServicesAllocatePages (\r
+               EfiBootServicesData,\r
+               Pages,\r
+               &HostPhyAddress\r
+               );\r
+    if (EFI_ERROR (Status)) {\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+    *HostAddress = (VOID *)(UINTN)HostPhyAddress;\r
+    *DeviceAddress = HostPhyAddress;\r
+    *Mapping = NULL;\r
+  }\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Frees memory that was allocated with AllocateBuffer().\r
+\r
+  @param  Pages                 The number of pages to free.\r
+  @param  HostAddress           The base system memory address of the allocated range.\r
+  @param  Mapping               The mapping value returned from Map().\r
+\r
+  @retval EFI_SUCCESS           The requested memory pages were freed.\r
+  @retval EFI_INVALID_PARAMETER The memory range specified by HostAddress and Pages\r
+                                was not allocated with AllocateBuffer().\r
+\r
+**/\r
+EFI_STATUS\r
+IoMmuFreeBuffer (\r
+  IN UINTN                  Pages,\r
+  IN VOID                   *HostAddress,\r
+  IN VOID                   *Mapping\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+\r
+  if (mIoMmu != NULL) {\r
+    Status = mIoMmu->SetAttribute (mIoMmu, Mapping, 0);\r
+    Status = mIoMmu->Unmap (mIoMmu, Mapping);\r
+    Status = mIoMmu->FreeBuffer (mIoMmu, Pages, HostAddress);\r
+  } else {\r
+    Status = EFI_SUCCESS;\r
+  }\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Initialize IOMMU.\r
+**/\r
+VOID\r
+IoMmuInit (\r
+  VOID\r
+  )\r
+{\r
+  PeiServicesLocatePpi (\r
+    &gEdkiiIoMmuPpiGuid,\r
+    0,\r
+    NULL,\r
+    (VOID **)&mIoMmu\r
+    );\r
+}\r
+\r
diff --git a/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.c b/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.c
new file mode 100644 (file)
index 0000000..fabec37
--- /dev/null
@@ -0,0 +1,358 @@
+/** @file\r
+  The NvmExpressPei driver is used to manage non-volatile memory subsystem\r
+  which follows NVM Express specification at PEI phase.\r
+\r
+  Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>\r
+\r
+  This program and the accompanying materials\r
+  are licensed and made available under the terms and conditions\r
+  of the BSD License which accompanies this distribution.  The\r
+  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 "NvmExpressPei.h"\r
+\r
+EFI_PEI_PPI_DESCRIPTOR  mNvmeBlkIoPpiListTemplate = {\r
+  EFI_PEI_PPI_DESCRIPTOR_PPI,\r
+  &gEfiPeiVirtualBlockIoPpiGuid,\r
+  NULL\r
+};\r
+\r
+EFI_PEI_PPI_DESCRIPTOR  mNvmeBlkIo2PpiListTemplate = {\r
+  EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST,\r
+  &gEfiPeiVirtualBlockIo2PpiGuid,\r
+  NULL\r
+};\r
+\r
+EFI_PEI_NOTIFY_DESCRIPTOR  mNvmeEndOfPeiNotifyListTemplate = {\r
+  (EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),\r
+  &gEfiEndOfPeiSignalPpiGuid,\r
+  NvmePeimEndOfPei\r
+};\r
+\r
+/**\r
+  Check if the specified Nvm Express device namespace is active, and then get the Identify\r
+  Namespace data.\r
+\r
+  @param[in,out] Private        The pointer to the PEI_NVME_CONTROLLER_PRIVATE_DATA data structure.\r
+  @param[in]     NamespaceId    The specified namespace identifier.\r
+\r
+  @retval EFI_SUCCESS    The specified namespace in the device is successfully enumerated.\r
+  @return Others         Error occurs when enumerating the namespace.\r
+\r
+**/\r
+EFI_STATUS\r
+EnumerateNvmeDevNamespace (\r
+  IN OUT PEI_NVME_CONTROLLER_PRIVATE_DATA    *Private,\r
+  IN UINT32                                  NamespaceId\r
+  )\r
+{\r
+  EFI_STATUS                   Status;\r
+  NVME_ADMIN_NAMESPACE_DATA    *NamespaceData;\r
+  PEI_NVME_NAMESPACE_INFO      *NamespaceInfo;\r
+  UINT32                       DeviceIndex;\r
+  UINT32                       Lbads;\r
+  UINT32                       Flbas;\r
+  UINT32                       LbaFmtIdx;\r
+\r
+  NamespaceData = (NVME_ADMIN_NAMESPACE_DATA *) AllocateZeroPool (sizeof (NVME_ADMIN_NAMESPACE_DATA));\r
+  if (NamespaceData == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  //\r
+  // Identify Namespace\r
+  //\r
+  Status = NvmeIdentifyNamespace (\r
+             Private,\r
+             NamespaceId,\r
+             NamespaceData\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((DEBUG_ERROR, "%a: NvmeIdentifyNamespace fail, Status - %r\n", __FUNCTION__, Status));\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // Validate Namespace\r
+  //\r
+  if (NamespaceData->Ncap == 0) {\r
+    DEBUG ((DEBUG_INFO, "%a: Namespace ID %d is an inactive one.\n", __FUNCTION__, NamespaceId));\r
+    Status = EFI_DEVICE_ERROR;\r
+    goto Exit;\r
+  }\r
+\r
+  DeviceIndex   = Private->ActiveNamespaceNum;\r
+  NamespaceInfo = &Private->NamespaceInfo[DeviceIndex];\r
+  NamespaceInfo->NamespaceId   = NamespaceId;\r
+  NamespaceInfo->NamespaceUuid = NamespaceData->Eui64;\r
+  NamespaceInfo->Controller    = Private;\r
+  Private->ActiveNamespaceNum++;\r
+\r
+  //\r
+  // Build BlockIo media structure\r
+  //\r
+  Flbas     = NamespaceData->Flbas;\r
+  LbaFmtIdx = Flbas & 0xF;\r
+  Lbads     = NamespaceData->LbaFormat[LbaFmtIdx].Lbads;\r
+\r
+  NamespaceInfo->Media.InterfaceType  = MSG_NVME_NAMESPACE_DP;\r
+  NamespaceInfo->Media.RemovableMedia = FALSE;\r
+  NamespaceInfo->Media.MediaPresent   = TRUE;\r
+  NamespaceInfo->Media.ReadOnly       = FALSE;\r
+  NamespaceInfo->Media.BlockSize      = (UINT32) 1 << Lbads;\r
+  NamespaceInfo->Media.LastBlock      = (EFI_PEI_LBA) NamespaceData->Nsze - 1;\r
+  DEBUG ((\r
+    DEBUG_INFO,\r
+    "%a: Namespace ID %d - BlockSize = 0x%x, LastBlock = 0x%lx\n",\r
+    __FUNCTION__,\r
+    NamespaceId,\r
+    NamespaceInfo->Media.BlockSize,\r
+    NamespaceInfo->Media.LastBlock\r
+    ));\r
+\r
+Exit:\r
+  if (NamespaceData != NULL) {\r
+    FreePool (NamespaceData);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Discover all Nvm Express device active namespaces.\r
+\r
+  @param[in,out] Private    The pointer to the PEI_NVME_CONTROLLER_PRIVATE_DATA data structure.\r
+\r
+  @retval EFI_SUCCESS       All the namespaces in the device are successfully enumerated.\r
+  @return EFI_NOT_FOUND     No active namespaces can be found.\r
+\r
+**/\r
+EFI_STATUS\r
+NvmeDiscoverNamespaces (\r
+  IN OUT PEI_NVME_CONTROLLER_PRIVATE_DATA    *Private\r
+  )\r
+{\r
+  UINT32    NamespaceId;\r
+\r
+  Private->ActiveNamespaceNum = 0;\r
+  Private->NamespaceInfo      = AllocateZeroPool (Private->ControllerData->Nn * sizeof (PEI_NVME_NAMESPACE_INFO));\r
+\r
+  //\r
+  // According to Nvm Express 1.1 spec Figure 82, the field 'Nn' of the identify\r
+  // controller data defines the number of valid namespaces present for the\r
+  // controller. Namespaces shall be allocated in order (starting with 1) and\r
+  // packed sequentially.\r
+  //\r
+  for (NamespaceId = 1; NamespaceId <= Private->ControllerData->Nn; NamespaceId++) {\r
+    //\r
+    // For now, we do not care the return status. Since if a valid namespace is inactive,\r
+    // error status will be returned. But we continue to enumerate other valid namespaces.\r
+    //\r
+    EnumerateNvmeDevNamespace (Private, NamespaceId);\r
+  }\r
+  if (Private->ActiveNamespaceNum == 0) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  One notified function to cleanup the allocated resources at the end of PEI.\r
+\r
+  @param[in] PeiServices         Pointer to PEI Services Table.\r
+  @param[in] NotifyDescriptor    Pointer to the descriptor for the Notification\r
+                                 event that caused this function to execute.\r
+  @param[in] Ppi                 Pointer to the PPI data associated with this function.\r
+\r
+  @retval     EFI_SUCCESS  The function completes successfully\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+NvmePeimEndOfPei (\r
+  IN EFI_PEI_SERVICES           **PeiServices,\r
+  IN EFI_PEI_NOTIFY_DESCRIPTOR  *NotifyDescriptor,\r
+  IN VOID                       *Ppi\r
+  )\r
+{\r
+  PEI_NVME_CONTROLLER_PRIVATE_DATA    *Private;\r
+\r
+  Private = GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_NOTIFY (NotifyDescriptor);\r
+  NvmeDisableController (Private);\r
+  NvmeFreeControllerResource (Private);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Entry point of the PEIM.\r
+\r
+  @param[in] FileHandle     Handle of the file being invoked.\r
+  @param[in] PeiServices    Describes the list of possible PEI Services.\r
+\r
+  @retval EFI_SUCCESS    PPI successfully installed.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+NvmExpressPeimEntry (\r
+  IN EFI_PEI_FILE_HANDLE    FileHandle,\r
+  IN CONST EFI_PEI_SERVICES **PeiServices\r
+  )\r
+{\r
+  EFI_STATUS                               Status;\r
+  EDKII_NVM_EXPRESS_HOST_CONTROLLER_PPI    *NvmeHcPpi;\r
+  UINT8                                    Controller;\r
+  UINTN                                    MmioBase;\r
+  PEI_NVME_CONTROLLER_PRIVATE_DATA         *Private;\r
+  EFI_PHYSICAL_ADDRESS                     DeviceAddress;\r
+\r
+  //\r
+  // Shadow this PEIM to run from memory\r
+  //\r
+  if (!EFI_ERROR (PeiServicesRegisterForShadow (FileHandle))) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  //\r
+  // Locate the NVME host controller PPI\r
+  //\r
+  Status = PeiServicesLocatePpi (\r
+             &gEdkiiPeiNvmExpressHostControllerPpiGuid,\r
+             0,\r
+             NULL,\r
+             (VOID **) &NvmeHcPpi\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((DEBUG_ERROR, "%a: Fail to locate NvmeHostControllerPpi.\n", __FUNCTION__));\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  IoMmuInit ();\r
+\r
+  Controller = 0;\r
+  MmioBase   = 0;\r
+  while (TRUE) {\r
+    Status = NvmeHcPpi->GetNvmeHcMmioBar (\r
+                          NvmeHcPpi,\r
+                          Controller,\r
+                          &MmioBase\r
+                          );\r
+    //\r
+    // When status is error, meant no controller is found\r
+    //\r
+    if (EFI_ERROR (Status)) {\r
+      break;\r
+    }\r
+\r
+    //\r
+    // Memory allocation for controller private data\r
+    //\r
+    Private = AllocateZeroPool (sizeof (PEI_NVME_CONTROLLER_PRIVATE_DATA));\r
+    if (Private == NULL) {\r
+      DEBUG ((\r
+        DEBUG_ERROR,\r
+        "%a: Fail to allocate private data for Controller %d.\n",\r
+        __FUNCTION__,\r
+        Controller\r
+        ));\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+\r
+    //\r
+    // Memory allocation for transfer-related data\r
+    //\r
+    Status = IoMmuAllocateBuffer (\r
+               NVME_MEM_MAX_PAGES,\r
+               &Private->Buffer,\r
+               &DeviceAddress,\r
+               &Private->BufferMapping\r
+               );\r
+    if (EFI_ERROR (Status)) {\r
+      DEBUG ((\r
+        DEBUG_ERROR,\r
+        "%a: Fail to allocate DMA buffers for Controller %d.\n",\r
+        __FUNCTION__,\r
+        Controller\r
+        ));\r
+      NvmeFreeControllerResource (Private);\r
+      return Status;\r
+    }\r
+    ASSERT (DeviceAddress == ((EFI_PHYSICAL_ADDRESS) (UINTN) Private->Buffer));\r
+    DEBUG ((DEBUG_INFO, "%a: DMA buffer base at 0x%x\n", __FUNCTION__, Private->Buffer));\r
+\r
+    //\r
+    // Initialize controller private data\r
+    //\r
+    Private->Signature = NVME_PEI_CONTROLLER_PRIVATE_DATA_SIGNATURE;\r
+    Private->MmioBase  = MmioBase;\r
+    Private->BlkIoPpi.GetNumberOfBlockDevices  = NvmeBlockIoPeimGetDeviceNo;\r
+    Private->BlkIoPpi.GetBlockDeviceMediaInfo  = NvmeBlockIoPeimGetMediaInfo;\r
+    Private->BlkIoPpi.ReadBlocks               = NvmeBlockIoPeimReadBlocks;\r
+    Private->BlkIo2Ppi.Revision                = EFI_PEI_RECOVERY_BLOCK_IO2_PPI_REVISION;\r
+    Private->BlkIo2Ppi.GetNumberOfBlockDevices = NvmeBlockIoPeimGetDeviceNo2;\r
+    Private->BlkIo2Ppi.GetBlockDeviceMediaInfo = NvmeBlockIoPeimGetMediaInfo2;\r
+    Private->BlkIo2Ppi.ReadBlocks              = NvmeBlockIoPeimReadBlocks2;\r
+    CopyMem (&Private->BlkIoPpiList, &mNvmeBlkIoPpiListTemplate, sizeof (EFI_PEI_PPI_DESCRIPTOR));\r
+    CopyMem (&Private->BlkIo2PpiList, &mNvmeBlkIo2PpiListTemplate, sizeof (EFI_PEI_PPI_DESCRIPTOR));\r
+    CopyMem (&Private->EndOfPeiNotifyList, &mNvmeEndOfPeiNotifyListTemplate, sizeof (EFI_PEI_NOTIFY_DESCRIPTOR));\r
+    Private->BlkIoPpiList.Ppi   = &Private->BlkIoPpi;\r
+    Private->BlkIo2PpiList.Ppi  = &Private->BlkIo2Ppi;\r
+\r
+    //\r
+    // Initialize the NVME controller\r
+    //\r
+    Status = NvmeControllerInit (Private);\r
+    if (EFI_ERROR (Status)) {\r
+      DEBUG ((\r
+        DEBUG_ERROR,\r
+        "%a: Controller initialization fail for Controller %d with Status - %r.\n",\r
+        __FUNCTION__,\r
+        Controller,\r
+        Status\r
+        ));\r
+      NvmeFreeControllerResource (Private);\r
+      Controller++;\r
+      continue;\r
+    }\r
+\r
+    //\r
+    // Enumerate the NVME namespaces on the controller\r
+    //\r
+    Status = NvmeDiscoverNamespaces (Private);\r
+    if (EFI_ERROR (Status)) {\r
+      //\r
+      // No active namespace was found on the controller\r
+      //\r
+      DEBUG ((\r
+        DEBUG_ERROR,\r
+        "%a: Namespaces discovery fail for Controller %d with Status - %r.\n",\r
+        __FUNCTION__,\r
+        Controller,\r
+        Status\r
+        ));\r
+      NvmeFreeControllerResource (Private);\r
+      Controller++;\r
+      continue;\r
+    }\r
+\r
+    PeiServicesInstallPpi (&Private->BlkIoPpiList);\r
+    PeiServicesNotifyPpi (&Private->EndOfPeiNotifyList);\r
+    DEBUG ((\r
+      DEBUG_INFO,\r
+      "%a: BlockIO PPI has been installed on Controller %d.\n",\r
+      __FUNCTION__,\r
+      Controller\r
+      ));\r
+    Controller++;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
diff --git a/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.h b/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.h
new file mode 100644 (file)
index 0000000..5e6f668
--- /dev/null
@@ -0,0 +1,265 @@
+/** @file\r
+  The NvmExpressPei driver is used to manage non-volatile memory subsystem\r
+  which follows NVM Express specification at PEI phase.\r
+\r
+  Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>\r
+\r
+  This program and the accompanying materials\r
+  are licensed and made available under the terms and conditions\r
+  of the BSD License which accompanies this distribution.  The\r
+  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
+#ifndef _NVM_EXPRESS_PEI_H_\r
+#define _NVM_EXPRESS_PEI_H_\r
+\r
+#include <PiPei.h>\r
+\r
+#include <IndustryStandard/Nvme.h>\r
+\r
+#include <Ppi/NvmExpressHostController.h>\r
+#include <Ppi/BlockIo.h>\r
+#include <Ppi/BlockIo2.h>\r
+#include <Ppi/IoMmu.h>\r
+#include <Ppi/EndOfPeiPhase.h>\r
+\r
+#include <Library/DebugLib.h>\r
+#include <Library/PeiServicesLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/IoLib.h>\r
+#include <Library/PciLib.h>\r
+#include <Library/TimerLib.h>\r
+\r
+//\r
+// Structure forward declarations\r
+//\r
+typedef struct _PEI_NVME_NAMESPACE_INFO           PEI_NVME_NAMESPACE_INFO;\r
+typedef struct _PEI_NVME_CONTROLLER_PRIVATE_DATA  PEI_NVME_CONTROLLER_PRIVATE_DATA;\r
+\r
+#include "NvmExpressPeiHci.h"\r
+#include "NvmExpressPeiPassThru.h"\r
+#include "NvmExpressPeiBlockIo.h"\r
+\r
+//\r
+// NVME PEI driver implementation related definitions\r
+//\r
+#define NVME_MAX_QUEUES                               2     // Number of I/O queues supported by the driver, 1 for AQ, 1 for CQ\r
+#define NVME_ASQ_SIZE                                 1     // Number of admin submission queue entries, which is 0-based\r
+#define NVME_ACQ_SIZE                                 1     // Number of admin completion queue entries, which is 0-based\r
+#define NVME_CSQ_SIZE                                 63    // Number of I/O submission queue entries, which is 0-based\r
+#define NVME_CCQ_SIZE                                 63    // Number of I/O completion queue entries, which is 0-based\r
+#define NVME_PRP_SIZE                                 (8)   // Pages of PRP list\r
+\r
+#define NVME_MEM_MAX_PAGES                                           \\r
+  (                                                                  \\r
+  1                                         /* ASQ */             +  \\r
+  1                                         /* ACQ */             +  \\r
+  1                                         /* SQs */             +  \\r
+  1                                         /* CQs */             +  \\r
+  NVME_PRP_SIZE)                            /* PRPs */\r
+\r
+#define NVME_ADMIN_QUEUE                              0x00\r
+#define NVME_IO_QUEUE                                 0x01\r
+#define NVME_GENERIC_TIMEOUT                          5000000   // Generic PassThru command timeout value, in us unit\r
+#define NVME_POLL_INTERVAL                            100       // Poll interval for PassThru command, in us unit\r
+\r
+//\r
+// Nvme namespace data structure.\r
+//\r
+struct _PEI_NVME_NAMESPACE_INFO {\r
+  UINT32                                    NamespaceId;\r
+  UINT64                                    NamespaceUuid;\r
+  EFI_PEI_BLOCK_IO2_MEDIA                   Media;\r
+\r
+  PEI_NVME_CONTROLLER_PRIVATE_DATA          *Controller;\r
+};\r
+\r
+//\r
+// Unique signature for private data structure.\r
+//\r
+#define NVME_PEI_CONTROLLER_PRIVATE_DATA_SIGNATURE    SIGNATURE_32 ('N','V','P','C')\r
+\r
+//\r
+// Nvme controller private data structure.\r
+//\r
+struct _PEI_NVME_CONTROLLER_PRIVATE_DATA {\r
+  UINT32                                    Signature;\r
+  UINTN                                     MmioBase;\r
+  EFI_PEI_RECOVERY_BLOCK_IO_PPI             BlkIoPpi;\r
+  EFI_PEI_RECOVERY_BLOCK_IO2_PPI            BlkIo2Ppi;\r
+  EFI_PEI_PPI_DESCRIPTOR                    BlkIoPpiList;\r
+  EFI_PEI_PPI_DESCRIPTOR                    BlkIo2PpiList;\r
+  EFI_PEI_NOTIFY_DESCRIPTOR                 EndOfPeiNotifyList;\r
+\r
+  //\r
+  // Pointer to identify controller data\r
+  //\r
+  NVME_ADMIN_CONTROLLER_DATA                *ControllerData;\r
+\r
+  //\r
+  // (4 + NVME_PRP_SIZE) x 4kB aligned buffers will be carved out of this buffer\r
+  // 1st 4kB boundary is the start of the admin submission queue\r
+  // 2nd 4kB boundary is the start of the admin completion queue\r
+  // 3rd 4kB boundary is the start of I/O submission queue\r
+  // 4th 4kB boundary is the start of I/O completion queue\r
+  // 5th 4kB boundary is the start of PRP list buffers\r
+  //\r
+  VOID                                      *Buffer;\r
+  VOID                                      *BufferMapping;\r
+\r
+  //\r
+  // Pointers to 4kB aligned submission & completion queues\r
+  //\r
+  NVME_SQ                                   *SqBuffer[NVME_MAX_QUEUES];\r
+  NVME_CQ                                   *CqBuffer[NVME_MAX_QUEUES];\r
+\r
+  //\r
+  // Submission and completion queue indices\r
+  //\r
+  NVME_SQTDBL                               SqTdbl[NVME_MAX_QUEUES];\r
+  NVME_CQHDBL                               CqHdbl[NVME_MAX_QUEUES];\r
+\r
+  UINT8                                     Pt[NVME_MAX_QUEUES];\r
+  UINT16                                    Cid[NVME_MAX_QUEUES];\r
+\r
+  //\r
+  // Nvme controller capabilities\r
+  //\r
+  NVME_CAP                                  Cap;\r
+\r
+  //\r
+  // Namespaces information on the controller\r
+  //\r
+  UINT32                                    ActiveNamespaceNum;\r
+  PEI_NVME_NAMESPACE_INFO                   *NamespaceInfo;\r
+};\r
+\r
+#define GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_BLKIO(a)     \\r
+  CR (a, PEI_NVME_CONTROLLER_PRIVATE_DATA, BlkIoPpi, NVME_PEI_CONTROLLER_PRIVATE_DATA_SIGNATURE)\r
+#define GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_BLKIO2(a)    \\r
+  CR (a, PEI_NVME_CONTROLLER_PRIVATE_DATA, BlkIo2Ppi, NVME_PEI_CONTROLLER_PRIVATE_DATA_SIGNATURE)\r
+#define GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_NOTIFY(a)    \\r
+  CR (a, PEI_NVME_CONTROLLER_PRIVATE_DATA, EndOfPeiNotifyList, NVME_PEI_CONTROLLER_PRIVATE_DATA_SIGNATURE)\r
+\r
+\r
+/**\r
+  Initialize IOMMU.\r
+**/\r
+VOID\r
+IoMmuInit (\r
+  VOID\r
+  );\r
+\r
+/**\r
+  Allocates pages that are suitable for an OperationBusMasterCommonBuffer or\r
+  OperationBusMasterCommonBuffer64 mapping.\r
+\r
+  @param  Pages                 The number of pages to allocate.\r
+  @param  HostAddress           A pointer to store the base system memory address of the\r
+                                allocated range.\r
+  @param  DeviceAddress         The resulting map address for the bus master PCI controller to use to\r
+                                access the hosts HostAddress.\r
+  @param  Mapping               A resulting value to pass to Unmap().\r
+\r
+  @retval EFI_SUCCESS           The requested memory pages were allocated.\r
+  @retval EFI_UNSUPPORTED       Attributes is unsupported. The only legal attribute bits are\r
+                                MEMORY_WRITE_COMBINE and MEMORY_CACHED.\r
+  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
+  @retval EFI_OUT_OF_RESOURCES  The memory pages could not be allocated.\r
+\r
+**/\r
+EFI_STATUS\r
+IoMmuAllocateBuffer (\r
+  IN UINTN                  Pages,\r
+  OUT VOID                  **HostAddress,\r
+  OUT EFI_PHYSICAL_ADDRESS  *DeviceAddress,\r
+  OUT VOID                  **Mapping\r
+  );\r
+\r
+/**\r
+  Frees memory that was allocated with AllocateBuffer().\r
+\r
+  @param  Pages                 The number of pages to free.\r
+  @param  HostAddress           The base system memory address of the allocated range.\r
+  @param  Mapping               The mapping value returned from Map().\r
+\r
+  @retval EFI_SUCCESS           The requested memory pages were freed.\r
+  @retval EFI_INVALID_PARAMETER The memory range specified by HostAddress and Pages\r
+                                was not allocated with AllocateBuffer().\r
+\r
+**/\r
+EFI_STATUS\r
+IoMmuFreeBuffer (\r
+  IN UINTN                  Pages,\r
+  IN VOID                   *HostAddress,\r
+  IN VOID                   *Mapping\r
+  );\r
+\r
+/**\r
+  Provides the controller-specific addresses required to access system memory from a\r
+  DMA bus master.\r
+\r
+  @param  Operation             Indicates if the bus master is going to read or write to system memory.\r
+  @param  HostAddress           The system memory address to map to the PCI controller.\r
+  @param  NumberOfBytes         On input the number of bytes to map. On output the number of bytes\r
+                                that were mapped.\r
+  @param  DeviceAddress         The resulting map address for the bus master PCI controller to use to\r
+                                access the hosts HostAddress.\r
+  @param  Mapping               A resulting value to pass to Unmap().\r
+\r
+  @retval EFI_SUCCESS           The range was mapped for the returned NumberOfBytes.\r
+  @retval EFI_UNSUPPORTED       The HostAddress cannot be mapped as a common buffer.\r
+  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
+  @retval EFI_OUT_OF_RESOURCES  The request could not be completed due to a lack of resources.\r
+  @retval EFI_DEVICE_ERROR      The system hardware could not map the requested address.\r
+\r
+**/\r
+EFI_STATUS\r
+IoMmuMap (\r
+  IN  EDKII_IOMMU_OPERATION Operation,\r
+  IN VOID                   *HostAddress,\r
+  IN  OUT UINTN             *NumberOfBytes,\r
+  OUT EFI_PHYSICAL_ADDRESS  *DeviceAddress,\r
+  OUT VOID                  **Mapping\r
+  );\r
+\r
+/**\r
+  Completes the Map() operation and releases any corresponding resources.\r
+\r
+  @param  Mapping               The mapping value returned from Map().\r
+\r
+  @retval EFI_SUCCESS           The range was unmapped.\r
+  @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by Map().\r
+  @retval EFI_DEVICE_ERROR      The data was not committed to the target system memory.\r
+**/\r
+EFI_STATUS\r
+IoMmuUnmap (\r
+  IN VOID                  *Mapping\r
+  );\r
+\r
+/**\r
+  One notified function to cleanup the allocated resources at the end of PEI.\r
+\r
+  @param[in] PeiServices         Pointer to PEI Services Table.\r
+  @param[in] NotifyDescriptor    Pointer to the descriptor for the Notification\r
+                                 event that caused this function to execute.\r
+  @param[in] Ppi                 Pointer to the PPI data associated with this function.\r
+\r
+  @retval     EFI_SUCCESS  The function completes successfully\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+NvmePeimEndOfPei (\r
+  IN EFI_PEI_SERVICES           **PeiServices,\r
+  IN EFI_PEI_NOTIFY_DESCRIPTOR  *NotifyDescriptor,\r
+  IN VOID                       *Ppi\r
+  );\r
+\r
+#endif\r
diff --git a/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.inf b/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.inf
new file mode 100644 (file)
index 0000000..8b2523a
--- /dev/null
@@ -0,0 +1,69 @@
+## @file\r
+#  The NvmExpressPei driver is used to manage non-volatile memory subsystem\r
+#  which follows NVM Express specification at PEI phase.\r
+#\r
+#  Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>\r
+#\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
+[Defines]\r
+  INF_VERSION                    = 0x00010005\r
+  BASE_NAME                      = NvmExpressPei\r
+  MODULE_UNI_FILE                = NvmExpressPei.uni\r
+  FILE_GUID                      = 94813714-E10A-4798-9909-8C904F66B4D9\r
+  MODULE_TYPE                    = PEIM\r
+  VERSION_STRING                 = 1.0\r
+  ENTRY_POINT                    = NvmExpressPeimEntry\r
+\r
+#\r
+# The following information is for reference only and not required by the build tools.\r
+#\r
+#  VALID_ARCHITECTURES           = IA32 X64 EBC\r
+#\r
+\r
+[Sources]\r
+  DmaMem.c\r
+  NvmExpressPei.c\r
+  NvmExpressPei.h\r
+  NvmExpressPeiBlockIo.c\r
+  NvmExpressPeiBlockIo.h\r
+  NvmExpressPeiHci.c\r
+  NvmExpressPeiHci.h\r
+  NvmExpressPeiPassThru.c\r
+  NvmExpressPeiPassThru.h\r
+\r
+[Packages]\r
+  MdePkg/MdePkg.dec\r
+  MdeModulePkg/MdeModulePkg.dec\r
+\r
+[LibraryClasses]\r
+  DebugLib\r
+  PeiServicesLib\r
+  MemoryAllocationLib\r
+  BaseMemoryLib\r
+  IoLib\r
+  PciLib\r
+  TimerLib\r
+  PeimEntryPoint\r
+\r
+[Ppis]\r
+  gEfiPeiVirtualBlockIoPpiGuid                   ## PRODUCES\r
+  gEfiPeiVirtualBlockIo2PpiGuid                  ## PRODUCES\r
+  gEdkiiPeiNvmExpressHostControllerPpiGuid       ## CONSUMES\r
+  gEdkiiIoMmuPpiGuid                             ## CONSUMES\r
+  gEfiEndOfPeiSignalPpiGuid                      ## CONSUMES\r
+\r
+[Depex]\r
+  gEfiPeiMemoryDiscoveredPpiGuid AND\r
+  gEdkiiPeiNvmExpressHostControllerPpiGuid\r
+\r
+[UserExtensions.TianoCore."ExtraFiles"]\r
+  NvmExpressPeiExtra.uni\r
diff --git a/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.uni b/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.uni
new file mode 100644 (file)
index 0000000..1956800
--- /dev/null
@@ -0,0 +1,21 @@
+// /** @file\r
+// The NvmExpressPei driver is used to manage non-volatile memory subsystem\r
+// which follows NVM Express specification at PEI phase.\r
+//\r
+// Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>\r
+//\r
+// This program and the accompanying materials\r
+// are licensed and made available under the terms and conditions\r
+// of the BSD License which accompanies this distribution.  The\r
+// 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
+\r
+#string STR_MODULE_ABSTRACT             #language en-US "Manage non-volatile memory subsystem at PEI phase"\r
+\r
+#string STR_MODULE_DESCRIPTION          #language en-US "The NvmExpressPei driver is used to manage non-volatile memory subsystem which follows NVM Express specification at PEI phase."\r
diff --git a/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiBlockIo.c b/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiBlockIo.c
new file mode 100644 (file)
index 0000000..e5018d4
--- /dev/null
@@ -0,0 +1,527 @@
+/** @file\r
+  The NvmExpressPei driver is used to manage non-volatile memory subsystem\r
+  which follows NVM Express specification at PEI phase.\r
+\r
+  Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>\r
+\r
+  This program and the accompanying materials\r
+  are licensed and made available under the terms and conditions\r
+  of the BSD License which accompanies this distribution.  The\r
+  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 "NvmExpressPei.h"\r
+\r
+/**\r
+  Read some sectors from the device.\r
+\r
+  @param  NamespaceInfo    The pointer to the PEI_NVME_NAMESPACE_INFO 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
+\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
+ReadSectors (\r
+  IN  PEI_NVME_NAMESPACE_INFO    *NamespaceInfo,\r
+  OUT UINTN                      Buffer,\r
+  IN  UINT64                     Lba,\r
+  IN  UINT32                     Blocks\r
+  )\r
+{\r
+  EFI_STATUS                                        Status;\r
+  UINT32                                            BlockSize;\r
+  PEI_NVME_CONTROLLER_PRIVATE_DATA                  *Private;\r
+  UINT32                                            Bytes;\r
+  EDKII_PEI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET    CommandPacket;\r
+  EDKII_PEI_NVM_EXPRESS_COMMAND                     Command;\r
+  EDKII_PEI_NVM_EXPRESS_COMPLETION                  Completion;\r
+\r
+  Private   = NamespaceInfo->Controller;\r
+  BlockSize = NamespaceInfo->Media.BlockSize;\r
+  Bytes     = Blocks * BlockSize;\r
+\r
+  ZeroMem (&CommandPacket, sizeof(EDKII_PEI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));\r
+  ZeroMem (&Command, sizeof(EDKII_PEI_NVM_EXPRESS_COMMAND));\r
+  ZeroMem (&Completion, sizeof(EDKII_PEI_NVM_EXPRESS_COMPLETION));\r
+\r
+  CommandPacket.NvmeCmd        = &Command;\r
+  CommandPacket.NvmeCompletion = &Completion;\r
+\r
+  CommandPacket.NvmeCmd->Cdw0.Opcode = NVME_IO_READ_OPC;\r
+  CommandPacket.NvmeCmd->Nsid        = NamespaceInfo->NamespaceId;\r
+  CommandPacket.TransferBuffer       = (VOID *)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
+  Status = NvmePassThru (\r
+             Private,\r
+             NamespaceInfo->NamespaceId,\r
+             &CommandPacket\r
+             );\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Read some blocks from the device.\r
+\r
+  @param[in]  NamespaceInfo    The pointer to the PEI_NVME_NAMESPACE_INFO 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          Data are read from the device.\r
+  @retval Others               Fail to read all the data.\r
+\r
+**/\r
+EFI_STATUS\r
+NvmeRead (\r
+  IN  PEI_NVME_NAMESPACE_INFO    *NamespaceInfo,\r
+  OUT UINTN                      Buffer,\r
+  IN  UINT64                     Lba,\r
+  IN  UINTN                      Blocks\r
+  )\r
+{\r
+  EFI_STATUS                          Status;\r
+  UINT32                              Retries;\r
+  UINT32                              BlockSize;\r
+  PEI_NVME_CONTROLLER_PRIVATE_DATA    *Private;\r
+  UINT32                              MaxTransferBlocks;\r
+  UINTN                               OrginalBlocks;\r
+\r
+  Status        = EFI_SUCCESS;\r
+  Retries       = 0;\r
+  Private       = NamespaceInfo->Controller;\r
+  BlockSize     = NamespaceInfo->Media.BlockSize;\r
+  OrginalBlocks = Blocks;\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
+    Status = ReadSectors (\r
+               NamespaceInfo,\r
+               Buffer,\r
+               Lba,\r
+               Blocks > MaxTransferBlocks ? MaxTransferBlocks : (UINT32)Blocks\r
+               );\r
+    if (EFI_ERROR(Status)) {\r
+      Retries++;\r
+      MaxTransferBlocks = MaxTransferBlocks >> 1;\r
+\r
+      if (Retries > NVME_READ_MAX_RETRY || MaxTransferBlocks < 1) {\r
+        DEBUG ((DEBUG_ERROR, "%a: ReadSectors fail, Status - %r\n", __FUNCTION__, Status));\r
+        break;\r
+      }\r
+      DEBUG ((\r
+        DEBUG_BLKIO,\r
+        "%a: ReadSectors fail, retry with smaller transfer block number - 0x%x\n",\r
+        __FUNCTION__,\r
+        MaxTransferBlocks\r
+        ));\r
+      continue;\r
+    }\r
+\r
+    if (Blocks > MaxTransferBlocks) {\r
+      Blocks -= MaxTransferBlocks;\r
+      Buffer += (MaxTransferBlocks * BlockSize);\r
+      Lba    += MaxTransferBlocks;\r
+    } else {\r
+      Blocks  = 0;\r
+    }\r
+  }\r
+\r
+  DEBUG ((DEBUG_BLKIO, "%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
+  return Status;\r
+}\r
+\r
+/**\r
+  Gets the count of block I/O devices that one specific block driver detects.\r
+\r
+  This function is used for getting the count of block I/O devices that one\r
+  specific block driver detects. If no device is detected, then the function\r
+  will return zero.\r
+\r
+  @param[in]  PeiServices          General-purpose services that are available\r
+                                   to every PEIM.\r
+  @param[in]  This                 Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI\r
+                                   instance.\r
+  @param[out] NumberBlockDevices   The number of block I/O devices discovered.\r
+\r
+  @retval     EFI_SUCCESS          The operation performed successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+NvmeBlockIoPeimGetDeviceNo (\r
+  IN  EFI_PEI_SERVICES               **PeiServices,\r
+  IN  EFI_PEI_RECOVERY_BLOCK_IO_PPI  *This,\r
+  OUT UINTN                          *NumberBlockDevices\r
+  )\r
+{\r
+  PEI_NVME_CONTROLLER_PRIVATE_DATA    *Private;\r
+\r
+  if (This == NULL || NumberBlockDevices == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Private = GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_BLKIO (This);\r
+  *NumberBlockDevices = Private->ActiveNamespaceNum;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Gets a block device's media information.\r
+\r
+  This function will provide the caller with the specified block device's media\r
+  information. If the media changes, calling this function will update the media\r
+  information accordingly.\r
+\r
+  @param[in]  PeiServices   General-purpose services that are available to every\r
+                            PEIM\r
+  @param[in]  This          Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance.\r
+  @param[in]  DeviceIndex   Specifies the block device to which the function wants\r
+                            to talk. Because the driver that implements Block I/O\r
+                            PPIs will manage multiple block devices, the PPIs that\r
+                            want to talk to a single device must specify the\r
+                            device index that was assigned during the enumeration\r
+                            process. This index is a number from one to\r
+                            NumberBlockDevices.\r
+  @param[out] MediaInfo     The media information of the specified block media.\r
+                            The caller is responsible for the ownership of this\r
+                            data structure.\r
+\r
+  @par Note:\r
+      The MediaInfo structure describes an enumeration of possible block device\r
+      types.  This enumeration exists because no device paths are actually passed\r
+      across interfaces that describe the type or class of hardware that is publishing\r
+      the block I/O interface. This enumeration will allow for policy decisions\r
+      in the Recovery PEIM, such as "Try to recover from legacy floppy first,\r
+      LS-120 second, CD-ROM third." If there are multiple partitions abstracted\r
+      by a given device type, they should be reported in ascending order; this\r
+      order also applies to nested partitions, such as legacy MBR, where the\r
+      outermost partitions would have precedence in the reporting order. The\r
+      same logic applies to systems such as IDE that have precedence relationships\r
+      like "Master/Slave" or "Primary/Secondary". The master device should be\r
+      reported first, the slave second.\r
+\r
+  @retval EFI_SUCCESS        Media information about the specified block device\r
+                             was obtained successfully.\r
+  @retval EFI_DEVICE_ERROR   Cannot get the media information due to a hardware\r
+                             error.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+NvmeBlockIoPeimGetMediaInfo (\r
+  IN  EFI_PEI_SERVICES               **PeiServices,\r
+  IN  EFI_PEI_RECOVERY_BLOCK_IO_PPI  *This,\r
+  IN  UINTN                          DeviceIndex,\r
+  OUT EFI_PEI_BLOCK_IO_MEDIA         *MediaInfo\r
+  )\r
+{\r
+  PEI_NVME_CONTROLLER_PRIVATE_DATA    *Private;\r
+\r
+  if (This == NULL || MediaInfo == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Private = GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_BLKIO (This);\r
+\r
+  if ((DeviceIndex == 0) || (DeviceIndex > Private->ActiveNamespaceNum)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  MediaInfo->DeviceType   = (EFI_PEI_BLOCK_DEVICE_TYPE) EDKII_PEI_BLOCK_DEVICE_TYPE_NVME;\r
+  MediaInfo->MediaPresent = TRUE;\r
+  MediaInfo->LastBlock    = (UINTN)Private->NamespaceInfo[DeviceIndex-1].Media.LastBlock;\r
+  MediaInfo->BlockSize    = Private->NamespaceInfo[DeviceIndex-1].Media.BlockSize;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Reads the requested number of blocks from the specified block device.\r
+\r
+  The function reads the requested number of blocks from the device. All the\r
+  blocks are read, or an error is returned. If there is no media in the device,\r
+  the function returns EFI_NO_MEDIA.\r
+\r
+  @param[in]  PeiServices   General-purpose services that are available to\r
+                            every PEIM.\r
+  @param[in]  This          Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance.\r
+  @param[in]  DeviceIndex   Specifies the block device to which the function wants\r
+                            to talk. Because the driver that implements Block I/O\r
+                            PPIs will manage multiple block devices, PPIs that\r
+                            want to talk to a single device must specify the device\r
+                            index that was assigned during the enumeration process.\r
+                            This index is a number from one to NumberBlockDevices.\r
+  @param[in]  StartLBA      The starting logical block address (LBA) to read from\r
+                            on the device\r
+  @param[in]  BufferSize    The size of the Buffer in bytes. This number must be\r
+                            a multiple of the intrinsic block size of the device.\r
+  @param[out] Buffer        A pointer to the destination buffer for the data.\r
+                            The caller is responsible for the ownership of the\r
+                            buffer.\r
+\r
+  @retval EFI_SUCCESS             The data was read correctly from the device.\r
+  @retval EFI_DEVICE_ERROR        The device reported an error while attempting\r
+                                  to perform the read operation.\r
+  @retval EFI_INVALID_PARAMETER   The read request contains LBAs that are not\r
+                                  valid, or the buffer is not properly aligned.\r
+  @retval EFI_NO_MEDIA            There is no media in the device.\r
+  @retval EFI_BAD_BUFFER_SIZE     The BufferSize parameter is not a multiple of\r
+                                  the intrinsic block size of the device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+NvmeBlockIoPeimReadBlocks (\r
+  IN  EFI_PEI_SERVICES               **PeiServices,\r
+  IN  EFI_PEI_RECOVERY_BLOCK_IO_PPI  *This,\r
+  IN  UINTN                          DeviceIndex,\r
+  IN  EFI_PEI_LBA                    StartLBA,\r
+  IN  UINTN                          BufferSize,\r
+  OUT VOID                           *Buffer\r
+  )\r
+{\r
+  PEI_NVME_CONTROLLER_PRIVATE_DATA    *Private;\r
+  PEI_NVME_NAMESPACE_INFO             *NamespaceInfo;\r
+  UINT32                              BlockSize;\r
+  UINTN                               NumberOfBlocks;\r
+\r
+  Private = GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_BLKIO (This);\r
+\r
+  //\r
+  // Check parameters\r
+  //\r
+  if (This == NULL || Buffer == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (BufferSize == 0) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  if ((DeviceIndex == 0) || (DeviceIndex > Private->ActiveNamespaceNum)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // Check BufferSize and StartLBA\r
+  //\r
+  NamespaceInfo = &(Private->NamespaceInfo[DeviceIndex - 1]);\r
+  BlockSize = NamespaceInfo->Media.BlockSize;\r
+  if (BufferSize % BlockSize != 0) {\r
+    return EFI_BAD_BUFFER_SIZE;\r
+  }\r
+\r
+  if (StartLBA > NamespaceInfo->Media.LastBlock) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  NumberOfBlocks = BufferSize / BlockSize;\r
+  if (NumberOfBlocks - 1 > NamespaceInfo->Media.LastBlock - StartLBA) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  return NvmeRead (NamespaceInfo, (UINTN)Buffer, StartLBA, NumberOfBlocks);\r
+}\r
+\r
+/**\r
+  Gets the count of block I/O devices that one specific block driver detects.\r
+\r
+  This function is used for getting the count of block I/O devices that one\r
+  specific block driver detects. If no device is detected, then the function\r
+  will return zero.\r
+\r
+  @param[in]  PeiServices          General-purpose services that are available\r
+                                   to every PEIM.\r
+  @param[in]  This                 Indicates the EFI_PEI_RECOVERY_BLOCK_IO2_PPI\r
+                                   instance.\r
+  @param[out] NumberBlockDevices   The number of block I/O devices discovered.\r
+\r
+  @retval     EFI_SUCCESS          The operation performed successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+NvmeBlockIoPeimGetDeviceNo2 (\r
+  IN  EFI_PEI_SERVICES                **PeiServices,\r
+  IN  EFI_PEI_RECOVERY_BLOCK_IO2_PPI  *This,\r
+  OUT UINTN                           *NumberBlockDevices\r
+  )\r
+{\r
+  PEI_NVME_CONTROLLER_PRIVATE_DATA    *Private;\r
+\r
+  if (This == NULL || NumberBlockDevices == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Private = GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_BLKIO2 (This);\r
+  *NumberBlockDevices = Private->ActiveNamespaceNum;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Gets a block device's media information.\r
+\r
+  This function will provide the caller with the specified block device's media\r
+  information. If the media changes, calling this function will update the media\r
+  information accordingly.\r
+\r
+  @param[in]  PeiServices   General-purpose services that are available to every\r
+                            PEIM\r
+  @param[in]  This          Indicates the EFI_PEI_RECOVERY_BLOCK_IO2_PPI instance.\r
+  @param[in]  DeviceIndex   Specifies the block device to which the function wants\r
+                            to talk. Because the driver that implements Block I/O\r
+                            PPIs will manage multiple block devices, the PPIs that\r
+                            want to talk to a single device must specify the\r
+                            device index that was assigned during the enumeration\r
+                            process. This index is a number from one to\r
+                            NumberBlockDevices.\r
+  @param[out] MediaInfo     The media information of the specified block media.\r
+                            The caller is responsible for the ownership of this\r
+                            data structure.\r
+\r
+  @par Note:\r
+      The MediaInfo structure describes an enumeration of possible block device\r
+      types.  This enumeration exists because no device paths are actually passed\r
+      across interfaces that describe the type or class of hardware that is publishing\r
+      the block I/O interface. This enumeration will allow for policy decisions\r
+      in the Recovery PEIM, such as "Try to recover from legacy floppy first,\r
+      LS-120 second, CD-ROM third." If there are multiple partitions abstracted\r
+      by a given device type, they should be reported in ascending order; this\r
+      order also applies to nested partitions, such as legacy MBR, where the\r
+      outermost partitions would have precedence in the reporting order. The\r
+      same logic applies to systems such as IDE that have precedence relationships\r
+      like "Master/Slave" or "Primary/Secondary". The master device should be\r
+      reported first, the slave second.\r
+\r
+  @retval EFI_SUCCESS        Media information about the specified block device\r
+                             was obtained successfully.\r
+  @retval EFI_DEVICE_ERROR   Cannot get the media information due to a hardware\r
+                             error.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+NvmeBlockIoPeimGetMediaInfo2 (\r
+  IN  EFI_PEI_SERVICES                **PeiServices,\r
+  IN  EFI_PEI_RECOVERY_BLOCK_IO2_PPI  *This,\r
+  IN  UINTN                           DeviceIndex,\r
+  OUT EFI_PEI_BLOCK_IO2_MEDIA         *MediaInfo\r
+  )\r
+{\r
+  EFI_STATUS                          Status;\r
+  PEI_NVME_CONTROLLER_PRIVATE_DATA    *Private;\r
+  EFI_PEI_BLOCK_IO_MEDIA              Media;\r
+\r
+  if (This == NULL || MediaInfo == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Private = GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_BLKIO2 (This);\r
+\r
+  Status  = NvmeBlockIoPeimGetMediaInfo (\r
+              PeiServices,\r
+              &Private->BlkIoPpi,\r
+              DeviceIndex,\r
+              &Media\r
+              );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  CopyMem (\r
+    MediaInfo,\r
+    &(Private->NamespaceInfo[DeviceIndex - 1].Media),\r
+    sizeof (EFI_PEI_BLOCK_IO2_MEDIA)\r
+    );\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Reads the requested number of blocks from the specified block device.\r
+\r
+  The function reads the requested number of blocks from the device. All the\r
+  blocks are read, or an error is returned. If there is no media in the device,\r
+  the function returns EFI_NO_MEDIA.\r
+\r
+  @param[in]  PeiServices   General-purpose services that are available to\r
+                            every PEIM.\r
+  @param[in]  This          Indicates the EFI_PEI_RECOVERY_BLOCK_IO2_PPI instance.\r
+  @param[in]  DeviceIndex   Specifies the block device to which the function wants\r
+                            to talk. Because the driver that implements Block I/O\r
+                            PPIs will manage multiple block devices, PPIs that\r
+                            want to talk to a single device must specify the device\r
+                            index that was assigned during the enumeration process.\r
+                            This index is a number from one to NumberBlockDevices.\r
+  @param[in]  StartLBA      The starting logical block address (LBA) to read from\r
+                            on the device\r
+  @param[in]  BufferSize    The size of the Buffer in bytes. This number must be\r
+                            a multiple of the intrinsic block size of the device.\r
+  @param[out] Buffer        A pointer to the destination buffer for the data.\r
+                            The caller is responsible for the ownership of the\r
+                            buffer.\r
+\r
+  @retval EFI_SUCCESS             The data was read correctly from the device.\r
+  @retval EFI_DEVICE_ERROR        The device reported an error while attempting\r
+                                  to perform the read operation.\r
+  @retval EFI_INVALID_PARAMETER   The read request contains LBAs that are not\r
+                                  valid, or the buffer is not properly aligned.\r
+  @retval EFI_NO_MEDIA            There is no media in the device.\r
+  @retval EFI_BAD_BUFFER_SIZE     The BufferSize parameter is not a multiple of\r
+                                  the intrinsic block size of the device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+NvmeBlockIoPeimReadBlocks2 (\r
+  IN  EFI_PEI_SERVICES                **PeiServices,\r
+  IN  EFI_PEI_RECOVERY_BLOCK_IO2_PPI  *This,\r
+  IN  UINTN                           DeviceIndex,\r
+  IN  EFI_PEI_LBA                     StartLBA,\r
+  IN  UINTN                           BufferSize,\r
+  OUT VOID                            *Buffer\r
+  )\r
+{\r
+  PEI_NVME_CONTROLLER_PRIVATE_DATA    *Private;\r
+\r
+  if (This == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Private = GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_BLKIO2 (This);\r
+  return NvmeBlockIoPeimReadBlocks (\r
+           PeiServices,\r
+           &Private->BlkIoPpi,\r
+           DeviceIndex,\r
+           StartLBA,\r
+           BufferSize,\r
+           Buffer\r
+           );\r
+}\r
diff --git a/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiBlockIo.h b/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiBlockIo.h
new file mode 100644 (file)
index 0000000..76e5970
--- /dev/null
@@ -0,0 +1,266 @@
+/** @file\r
+  The NvmExpressPei driver is used to manage non-volatile memory subsystem\r
+  which follows NVM Express specification at PEI phase.\r
+\r
+  Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>\r
+\r
+  This program and the accompanying materials\r
+  are licensed and made available under the terms and conditions\r
+  of the BSD License which accompanies this distribution.  The\r
+  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
+#ifndef _NVM_EXPRESS_PEI_BLOCKIO_H_\r
+#define _NVM_EXPRESS_PEI_BLOCKIO_H_\r
+\r
+//\r
+// Nvme device for EFI_PEI_BLOCK_DEVICE_TYPE\r
+//\r
+#define EDKII_PEI_BLOCK_DEVICE_TYPE_NVME    7\r
+\r
+#define NVME_READ_MAX_RETRY                 3\r
+\r
+/**\r
+  Gets the count of block I/O devices that one specific block driver detects.\r
+\r
+  This function is used for getting the count of block I/O devices that one\r
+  specific block driver detects. If no device is detected, then the function\r
+  will return zero.\r
+\r
+  @param[in]  PeiServices          General-purpose services that are available\r
+                                   to every PEIM.\r
+  @param[in]  This                 Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI\r
+                                   instance.\r
+  @param[out] NumberBlockDevices   The number of block I/O devices discovered.\r
+\r
+  @retval     EFI_SUCCESS          The operation performed successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+NvmeBlockIoPeimGetDeviceNo (\r
+  IN  EFI_PEI_SERVICES               **PeiServices,\r
+  IN  EFI_PEI_RECOVERY_BLOCK_IO_PPI  *This,\r
+  OUT UINTN                          *NumberBlockDevices\r
+  );\r
+\r
+/**\r
+  Gets a block device's media information.\r
+\r
+  This function will provide the caller with the specified block device's media\r
+  information. If the media changes, calling this function will update the media\r
+  information accordingly.\r
+\r
+  @param[in]  PeiServices   General-purpose services that are available to every\r
+                            PEIM\r
+  @param[in]  This          Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance.\r
+  @param[in]  DeviceIndex   Specifies the block device to which the function wants\r
+                            to talk. Because the driver that implements Block I/O\r
+                            PPIs will manage multiple block devices, the PPIs that\r
+                            want to talk to a single device must specify the\r
+                            device index that was assigned during the enumeration\r
+                            process. This index is a number from one to\r
+                            NumberBlockDevices.\r
+  @param[out] MediaInfo     The media information of the specified block media.\r
+                            The caller is responsible for the ownership of this\r
+                            data structure.\r
+\r
+  @par Note:\r
+      The MediaInfo structure describes an enumeration of possible block device\r
+      types.  This enumeration exists because no device paths are actually passed\r
+      across interfaces that describe the type or class of hardware that is publishing\r
+      the block I/O interface. This enumeration will allow for policy decisions\r
+      in the Recovery PEIM, such as "Try to recover from legacy floppy first,\r
+      LS-120 second, CD-ROM third." If there are multiple partitions abstracted\r
+      by a given device type, they should be reported in ascending order; this\r
+      order also applies to nested partitions, such as legacy MBR, where the\r
+      outermost partitions would have precedence in the reporting order. The\r
+      same logic applies to systems such as IDE that have precedence relationships\r
+      like "Master/Slave" or "Primary/Secondary". The master device should be\r
+      reported first, the slave second.\r
+\r
+  @retval EFI_SUCCESS        Media information about the specified block device\r
+                             was obtained successfully.\r
+  @retval EFI_DEVICE_ERROR   Cannot get the media information due to a hardware\r
+                             error.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+NvmeBlockIoPeimGetMediaInfo (\r
+  IN  EFI_PEI_SERVICES               **PeiServices,\r
+  IN  EFI_PEI_RECOVERY_BLOCK_IO_PPI  *This,\r
+  IN  UINTN                          DeviceIndex,\r
+  OUT EFI_PEI_BLOCK_IO_MEDIA         *MediaInfo\r
+  );\r
+\r
+/**\r
+  Reads the requested number of blocks from the specified block device.\r
+\r
+  The function reads the requested number of blocks from the device. All the\r
+  blocks are read, or an error is returned. If there is no media in the device,\r
+  the function returns EFI_NO_MEDIA.\r
+\r
+  @param[in]  PeiServices   General-purpose services that are available to\r
+                            every PEIM.\r
+  @param[in]  This          Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance.\r
+  @param[in]  DeviceIndex   Specifies the block device to which the function wants\r
+                            to talk. Because the driver that implements Block I/O\r
+                            PPIs will manage multiple block devices, PPIs that\r
+                            want to talk to a single device must specify the device\r
+                            index that was assigned during the enumeration process.\r
+                            This index is a number from one to NumberBlockDevices.\r
+  @param[in]  StartLBA      The starting logical block address (LBA) to read from\r
+                            on the device\r
+  @param[in]  BufferSize    The size of the Buffer in bytes. This number must be\r
+                            a multiple of the intrinsic block size of the device.\r
+  @param[out] Buffer        A pointer to the destination buffer for the data.\r
+                            The caller is responsible for the ownership of the\r
+                            buffer.\r
+\r
+  @retval EFI_SUCCESS             The data was read correctly from the device.\r
+  @retval EFI_DEVICE_ERROR        The device reported an error while attempting\r
+                                  to perform the read operation.\r
+  @retval EFI_INVALID_PARAMETER   The read request contains LBAs that are not\r
+                                  valid, or the buffer is not properly aligned.\r
+  @retval EFI_NO_MEDIA            There is no media in the device.\r
+  @retval EFI_BAD_BUFFER_SIZE     The BufferSize parameter is not a multiple of\r
+                                  the intrinsic block size of the device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+NvmeBlockIoPeimReadBlocks (\r
+  IN  EFI_PEI_SERVICES               **PeiServices,\r
+  IN  EFI_PEI_RECOVERY_BLOCK_IO_PPI  *This,\r
+  IN  UINTN                          DeviceIndex,\r
+  IN  EFI_PEI_LBA                    StartLBA,\r
+  IN  UINTN                          BufferSize,\r
+  OUT VOID                           *Buffer\r
+  );\r
+\r
+/**\r
+  Gets the count of block I/O devices that one specific block driver detects.\r
+\r
+  This function is used for getting the count of block I/O devices that one\r
+  specific block driver detects. If no device is detected, then the function\r
+  will return zero.\r
+\r
+  @param[in]  PeiServices          General-purpose services that are available\r
+                                   to every PEIM.\r
+  @param[in]  This                 Indicates the EFI_PEI_RECOVERY_BLOCK_IO2_PPI\r
+                                   instance.\r
+  @param[out] NumberBlockDevices   The number of block I/O devices discovered.\r
+\r
+  @retval     EFI_SUCCESS          The operation performed successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+NvmeBlockIoPeimGetDeviceNo2 (\r
+  IN  EFI_PEI_SERVICES                **PeiServices,\r
+  IN  EFI_PEI_RECOVERY_BLOCK_IO2_PPI  *This,\r
+  OUT UINTN                           *NumberBlockDevices\r
+  );\r
+\r
+/**\r
+  Gets a block device's media information.\r
+\r
+  This function will provide the caller with the specified block device's media\r
+  information. If the media changes, calling this function will update the media\r
+  information accordingly.\r
+\r
+  @param[in]  PeiServices   General-purpose services that are available to every\r
+                            PEIM\r
+  @param[in]  This          Indicates the EFI_PEI_RECOVERY_BLOCK_IO2_PPI instance.\r
+  @param[in]  DeviceIndex   Specifies the block device to which the function wants\r
+                            to talk. Because the driver that implements Block I/O\r
+                            PPIs will manage multiple block devices, the PPIs that\r
+                            want to talk to a single device must specify the\r
+                            device index that was assigned during the enumeration\r
+                            process. This index is a number from one to\r
+                            NumberBlockDevices.\r
+  @param[out] MediaInfo     The media information of the specified block media.\r
+                            The caller is responsible for the ownership of this\r
+                            data structure.\r
+\r
+  @par Note:\r
+      The MediaInfo structure describes an enumeration of possible block device\r
+      types.  This enumeration exists because no device paths are actually passed\r
+      across interfaces that describe the type or class of hardware that is publishing\r
+      the block I/O interface. This enumeration will allow for policy decisions\r
+      in the Recovery PEIM, such as "Try to recover from legacy floppy first,\r
+      LS-120 second, CD-ROM third." If there are multiple partitions abstracted\r
+      by a given device type, they should be reported in ascending order; this\r
+      order also applies to nested partitions, such as legacy MBR, where the\r
+      outermost partitions would have precedence in the reporting order. The\r
+      same logic applies to systems such as IDE that have precedence relationships\r
+      like "Master/Slave" or "Primary/Secondary". The master device should be\r
+      reported first, the slave second.\r
+\r
+  @retval EFI_SUCCESS        Media information about the specified block device\r
+                             was obtained successfully.\r
+  @retval EFI_DEVICE_ERROR   Cannot get the media information due to a hardware\r
+                             error.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+NvmeBlockIoPeimGetMediaInfo2 (\r
+  IN  EFI_PEI_SERVICES                **PeiServices,\r
+  IN  EFI_PEI_RECOVERY_BLOCK_IO2_PPI  *This,\r
+  IN  UINTN                           DeviceIndex,\r
+  OUT EFI_PEI_BLOCK_IO2_MEDIA         *MediaInfo\r
+  );\r
+\r
+/**\r
+  Reads the requested number of blocks from the specified block device.\r
+\r
+  The function reads the requested number of blocks from the device. All the\r
+  blocks are read, or an error is returned. If there is no media in the device,\r
+  the function returns EFI_NO_MEDIA.\r
+\r
+  @param[in]  PeiServices   General-purpose services that are available to\r
+                            every PEIM.\r
+  @param[in]  This          Indicates the EFI_PEI_RECOVERY_BLOCK_IO2_PPI instance.\r
+  @param[in]  DeviceIndex   Specifies the block device to which the function wants\r
+                            to talk. Because the driver that implements Block I/O\r
+                            PPIs will manage multiple block devices, PPIs that\r
+                            want to talk to a single device must specify the device\r
+                            index that was assigned during the enumeration process.\r
+                            This index is a number from one to NumberBlockDevices.\r
+  @param[in]  StartLBA      The starting logical block address (LBA) to read from\r
+                            on the device\r
+  @param[in]  BufferSize    The size of the Buffer in bytes. This number must be\r
+                            a multiple of the intrinsic block size of the device.\r
+  @param[out] Buffer        A pointer to the destination buffer for the data.\r
+                            The caller is responsible for the ownership of the\r
+                            buffer.\r
+\r
+  @retval EFI_SUCCESS             The data was read correctly from the device.\r
+  @retval EFI_DEVICE_ERROR        The device reported an error while attempting\r
+                                  to perform the read operation.\r
+  @retval EFI_INVALID_PARAMETER   The read request contains LBAs that are not\r
+                                  valid, or the buffer is not properly aligned.\r
+  @retval EFI_NO_MEDIA            There is no media in the device.\r
+  @retval EFI_BAD_BUFFER_SIZE     The BufferSize parameter is not a multiple of\r
+                                  the intrinsic block size of the device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+NvmeBlockIoPeimReadBlocks2 (\r
+  IN  EFI_PEI_SERVICES                **PeiServices,\r
+  IN  EFI_PEI_RECOVERY_BLOCK_IO2_PPI  *This,\r
+  IN  UINTN                           DeviceIndex,\r
+  IN  EFI_PEI_LBA                     StartLBA,\r
+  IN  UINTN                           BufferSize,\r
+  OUT VOID                            *Buffer\r
+  );\r
+\r
+#endif\r
diff --git a/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiExtra.uni b/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiExtra.uni
new file mode 100644 (file)
index 0000000..8c97c0a
--- /dev/null
@@ -0,0 +1,19 @@
+// /** @file\r
+// NvmExpressPei Localized Strings and Content\r
+//\r
+// Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>\r
+//\r
+// This program and the accompanying materials\r
+// are licensed and made available under the terms and conditions\r
+// of the BSD License which accompanies this distribution.  The\r
+// 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
+#string STR_PROPERTIES_MODULE_NAME\r
+#language en-US\r
+"NVM Express Peim"\r
diff --git a/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiHci.c b/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiHci.c
new file mode 100644 (file)
index 0000000..d4056a2
--- /dev/null
@@ -0,0 +1,748 @@
+/** @file\r
+  The NvmExpressPei driver is used to manage non-volatile memory subsystem\r
+  which follows NVM Express specification at PEI phase.\r
+\r
+  Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>\r
+\r
+  This program and the accompanying materials\r
+  are licensed and made available under the terms and conditions\r
+  of the BSD License which accompanies this distribution.  The\r
+  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 "NvmExpressPei.h"\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
+**/\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
+**/\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
+  Get the page offset 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
+NvmeBaseMemPageOffset (\r
+  IN UINTN              BaseMemIndex\r
+  )\r
+{\r
+  UINT32                Pages;\r
+  UINTN                 Index;\r
+  UINT32                PageSizeList[5];\r
+\r
+  PageSizeList[0] = 1;  /* ASQ */\r
+  PageSizeList[1] = 1;  /* ACQ */\r
+  PageSizeList[2] = 1;  /* SQs */\r
+  PageSizeList[3] = 1;  /* CQs */\r
+  PageSizeList[4] = NVME_PRP_SIZE;  /* PRPs */\r
+\r
+  if (BaseMemIndex > MAX_BASEMEM_COUNT) {\r
+    DEBUG ((DEBUG_ERROR, "%a: The input BaseMem index is invalid.\n", __FUNCTION__));\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] Private      The pointer to the PEI_NVME_CONTROLLER_PRIVATE_DATA 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
+EFI_STATUS\r
+NvmeWaitController (\r
+  IN PEI_NVME_CONTROLLER_PRIVATE_DATA    *Private,\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 (Private->Cap.To == 0) {\r
+    Timeout = 1;\r
+  } else {\r
+    Timeout = Private->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 (Private, &Csts);\r
+    if (EFI_ERROR(Status)) {\r
+      DEBUG ((DEBUG_ERROR, "%a: NVME_GET_CSTS fail, Status - %r\n", __FUNCTION__, 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] Private     The pointer to the PEI_NVME_CONTROLLER_PRIVATE_DATA data structure.\r
+\r
+  @return EFI_SUCCESS    Successfully disable the controller.\r
+  @return others         Fail to disable the controller.\r
+\r
+**/\r
+EFI_STATUS\r
+NvmeDisableController (\r
+  IN PEI_NVME_CONTROLLER_PRIVATE_DATA    *Private\r
+  )\r
+{\r
+  NVME_CC       Cc;\r
+  NVME_CSTS     Csts;\r
+  EFI_STATUS    Status;\r
+\r
+  Status = NVME_GET_CSTS (Private, &Csts);\r
+\r
+  //\r
+  // Read Controller Configuration Register.\r
+  //\r
+  Status = NVME_GET_CC (Private, &Cc);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((DEBUG_ERROR, "%a: NVME_GET_CC fail, Status - %r\n", __FUNCTION__, Status));\r
+    goto ErrorExit;\r
+  }\r
+\r
+  if (Cc.En == 1) {\r
+    Cc.En = 0;\r
+    //\r
+    // Disable the controller.\r
+    //\r
+    Status = NVME_SET_CC (Private, &Cc);\r
+    if (EFI_ERROR (Status)) {\r
+      DEBUG ((DEBUG_ERROR, "%a: NVME_SET_CC fail, Status - %r\n", __FUNCTION__, Status));\r
+      goto ErrorExit;\r
+    }\r
+  }\r
+\r
+  Status = NvmeWaitController (Private, FALSE);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((DEBUG_ERROR, "%a: NvmeWaitController fail, Status - %r\n", __FUNCTION__, Status));\r
+    goto ErrorExit;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+\r
+ErrorExit:\r
+  DEBUG ((DEBUG_ERROR, "%a fail, Status - %r\n", __FUNCTION__, Status));\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Enable the Nvm Express controller.\r
+\r
+  @param[in] Private    The pointer to the PEI_NVME_CONTROLLER_PRIVATE_DATA 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
+EFI_STATUS\r
+NvmeEnableController (\r
+  IN PEI_NVME_CONTROLLER_PRIVATE_DATA    *Private\r
+  )\r
+{\r
+  NVME_CC       Cc;\r
+  EFI_STATUS    Status;\r
+\r
+  //\r
+  // Enable the controller\r
+  // CC.AMS, CC.MPS and CC.CSS are all set to 0\r
+  //\r
+  ZeroMem (&Cc, sizeof (NVME_CC));\r
+  Cc.En     = 1;\r
+  Cc.Iosqes = 6;\r
+  Cc.Iocqes = 4;\r
+  Status    = NVME_SET_CC (Private, &Cc);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((DEBUG_ERROR, "%a: NVME_SET_CC fail, Status - %r\n", __FUNCTION__, Status));\r
+    goto ErrorExit;\r
+  }\r
+\r
+  Status = NvmeWaitController (Private, TRUE);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((DEBUG_ERROR, "%a: NvmeWaitController fail, Status - %r\n", __FUNCTION__, Status));\r
+    goto ErrorExit;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+\r
+ErrorExit:\r
+  DEBUG ((DEBUG_ERROR, "%a fail, Status: %r\n", __FUNCTION__, Status));\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Get the Identify Controller data.\r
+\r
+  @param[in] Private     The pointer to the PEI_NVME_CONTROLLER_PRIVATE_DATA 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
+EFI_STATUS\r
+NvmeIdentifyController (\r
+  IN PEI_NVME_CONTROLLER_PRIVATE_DATA    *Private,\r
+  IN VOID                                *Buffer\r
+  )\r
+{\r
+  EDKII_PEI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET    CommandPacket;\r
+  EDKII_PEI_NVM_EXPRESS_COMMAND                     Command;\r
+  EDKII_PEI_NVM_EXPRESS_COMPLETION                  Completion;\r
+  EFI_STATUS                                        Status;\r
+\r
+  ZeroMem (&CommandPacket, sizeof(EDKII_PEI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));\r
+  ZeroMem (&Command, sizeof(EDKII_PEI_NVM_EXPRESS_COMMAND));\r
+  ZeroMem (&Completion, sizeof(EDKII_PEI_NVM_EXPRESS_COMPLETION));\r
+\r
+  Command.Cdw0.Opcode = NVME_ADMIN_IDENTIFY_CMD;\r
+  //\r
+  // According to Nvm Express 1.1 spec Figure 38, When not used, the field shall be cleared to 0h.\r
+  // For the Identify command, the Namespace Identifier is only used for the Namespace Data structure.\r
+  //\r
+  Command.Nsid        = 0;\r
+\r
+  CommandPacket.NvmeCmd        = &Command;\r
+  CommandPacket.NvmeCompletion = &Completion;\r
+  CommandPacket.TransferBuffer = Buffer;\r
+  CommandPacket.TransferLength = sizeof (NVME_ADMIN_CONTROLLER_DATA);\r
+  CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;\r
+  CommandPacket.QueueType      = NVME_ADMIN_QUEUE;\r
+  //\r
+  // Set bit 0 (Cns bit) to 1 to identify the controller\r
+  //\r
+  CommandPacket.NvmeCmd->Cdw10 = 1;\r
+  CommandPacket.NvmeCmd->Flags = CDW10_VALID;\r
+\r
+  Status = NvmePassThru (\r
+             Private,\r
+             NVME_CONTROLLER_NSID,\r
+             &CommandPacket\r
+             );\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Get specified identify namespace data.\r
+\r
+  @param[in] Private        The pointer to the PEI_NVME_CONTROLLER_PRIVATE_DATA 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 EFI_DEVICE_ERROR    Fail to get the identify namespace data.\r
+\r
+**/\r
+EFI_STATUS\r
+NvmeIdentifyNamespace (\r
+  IN PEI_NVME_CONTROLLER_PRIVATE_DATA    *Private,\r
+  IN UINT32                              NamespaceId,\r
+  IN VOID                                *Buffer\r
+  )\r
+{\r
+  EDKII_PEI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET    CommandPacket;\r
+  EDKII_PEI_NVM_EXPRESS_COMMAND                     Command;\r
+  EDKII_PEI_NVM_EXPRESS_COMPLETION                  Completion;\r
+  EFI_STATUS                                        Status;\r
+\r
+  ZeroMem (&CommandPacket, sizeof(EDKII_PEI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));\r
+  ZeroMem (&Command, sizeof(EDKII_PEI_NVM_EXPRESS_COMMAND));\r
+  ZeroMem (&Completion, sizeof(EDKII_PEI_NVM_EXPRESS_COMPLETION));\r
+\r
+  Command.Cdw0.Opcode = NVME_ADMIN_IDENTIFY_CMD;\r
+  Command.Nsid        = NamespaceId;\r
+\r
+  CommandPacket.NvmeCmd        = &Command;\r
+  CommandPacket.NvmeCompletion = &Completion;\r
+  CommandPacket.TransferBuffer = Buffer;\r
+  CommandPacket.TransferLength = sizeof (NVME_ADMIN_NAMESPACE_DATA);\r
+  CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;\r
+  CommandPacket.QueueType      = 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
+             Private,\r
+             NamespaceId,\r
+             &CommandPacket\r
+             );\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Dump the Identify Controller data.\r
+\r
+  @param[in] ControllerData    The pointer to the NVME_ADMIN_CONTROLLER_DATA data structure.\r
+\r
+**/\r
+VOID\r
+NvmeDumpControllerData (\r
+  IN NVME_ADMIN_CONTROLLER_DATA    *ControllerData\r
+  )\r
+{\r
+  UINT8    Sn[21];\r
+  UINT8    Mn[41];\r
+\r
+  CopyMem (Sn, ControllerData->Sn, sizeof (ControllerData->Sn));\r
+  Sn[20] = 0;\r
+  CopyMem (Mn, ControllerData->Mn, sizeof (ControllerData->Mn));\r
+  Mn[40] = 0;\r
+\r
+  DEBUG ((DEBUG_INFO, " == NVME IDENTIFY CONTROLLER DATA ==\n"));\r
+  DEBUG ((DEBUG_INFO, "    PCI VID   : 0x%x\n", ControllerData->Vid));\r
+  DEBUG ((DEBUG_INFO, "    PCI SSVID : 0x%x\n", ControllerData->Ssvid));\r
+  DEBUG ((DEBUG_INFO, "    SN        : %a\n",   Sn));\r
+  DEBUG ((DEBUG_INFO, "    MN        : %a\n",   Mn));\r
+  DEBUG ((DEBUG_INFO, "    FR        : 0x%lx\n", *((UINT64*)ControllerData->Fr)));\r
+  DEBUG ((DEBUG_INFO, "    RAB       : 0x%x\n", ControllerData->Rab));\r
+  DEBUG ((DEBUG_INFO, "    IEEE      : 0x%x\n", *(UINT32*)ControllerData->Ieee_oui));\r
+  DEBUG ((DEBUG_INFO, "    AERL      : 0x%x\n", ControllerData->Aerl));\r
+  DEBUG ((DEBUG_INFO, "    SQES      : 0x%x\n", ControllerData->Sqes));\r
+  DEBUG ((DEBUG_INFO, "    CQES      : 0x%x\n", ControllerData->Cqes));\r
+  DEBUG ((DEBUG_INFO, "    NN        : 0x%x\n", ControllerData->Nn));\r
+  return;\r
+}\r
+\r
+/**\r
+  Create IO completion queue.\r
+\r
+  @param[in] Private     The pointer to the PEI_NVME_CONTROLLER_PRIVATE_DATA data structure.\r
+\r
+  @return EFI_SUCCESS    Successfully create io completion queue.\r
+  @return others         Fail to create io completion queue.\r
+\r
+**/\r
+EFI_STATUS\r
+NvmeCreateIoCompletionQueue (\r
+  IN PEI_NVME_CONTROLLER_PRIVATE_DATA    *Private\r
+  )\r
+{\r
+  EDKII_PEI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET    CommandPacket;\r
+  EDKII_PEI_NVM_EXPRESS_COMMAND                     Command;\r
+  EDKII_PEI_NVM_EXPRESS_COMPLETION                  Completion;\r
+  EFI_STATUS                                        Status;\r
+  NVME_ADMIN_CRIOCQ                                 CrIoCq;\r
+\r
+  ZeroMem (&CommandPacket, sizeof(EDKII_PEI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));\r
+  ZeroMem (&Command, sizeof(EDKII_PEI_NVM_EXPRESS_COMMAND));\r
+  ZeroMem (&Completion, sizeof(EDKII_PEI_NVM_EXPRESS_COMPLETION));\r
+  ZeroMem (&CrIoCq, sizeof(NVME_ADMIN_CRIOCQ));\r
+\r
+  CommandPacket.NvmeCmd        = &Command;\r
+  CommandPacket.NvmeCompletion = &Completion;\r
+\r
+  Command.Cdw0.Opcode = NVME_ADMIN_CRIOCQ_CMD;\r
+  Command.Cdw0.Cid    = Private->Cid[NVME_ADMIN_QUEUE]++;\r
+  CommandPacket.TransferBuffer = Private->CqBuffer[NVME_IO_QUEUE];\r
+  CommandPacket.TransferLength = EFI_PAGE_SIZE;\r
+  CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;\r
+  CommandPacket.QueueType      = 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
+             Private,\r
+             NVME_CONTROLLER_NSID,\r
+             &CommandPacket\r
+             );\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Create IO submission queue.\r
+\r
+  @param[in] Private     The pointer to the PEI_NVME_CONTROLLER_PRIVATE_DATA data structure.\r
+\r
+  @return EFI_SUCCESS    Successfully create io submission queue.\r
+  @return others         Fail to create io submission queue.\r
+\r
+**/\r
+EFI_STATUS\r
+NvmeCreateIoSubmissionQueue (\r
+  IN PEI_NVME_CONTROLLER_PRIVATE_DATA    *Private\r
+  )\r
+{\r
+  EDKII_PEI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET    CommandPacket;\r
+  EDKII_PEI_NVM_EXPRESS_COMMAND                     Command;\r
+  EDKII_PEI_NVM_EXPRESS_COMPLETION                  Completion;\r
+  EFI_STATUS                                        Status;\r
+  NVME_ADMIN_CRIOSQ                                 CrIoSq;\r
+\r
+  ZeroMem (&CommandPacket, sizeof(EDKII_PEI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));\r
+  ZeroMem (&Command, sizeof(EDKII_PEI_NVM_EXPRESS_COMMAND));\r
+  ZeroMem (&Completion, sizeof(EDKII_PEI_NVM_EXPRESS_COMPLETION));\r
+  ZeroMem (&CrIoSq, sizeof(NVME_ADMIN_CRIOSQ));\r
+\r
+  CommandPacket.NvmeCmd        = &Command;\r
+  CommandPacket.NvmeCompletion = &Completion;\r
+\r
+  Command.Cdw0.Opcode = NVME_ADMIN_CRIOSQ_CMD;\r
+  Command.Cdw0.Cid    = Private->Cid[NVME_ADMIN_QUEUE]++;\r
+  CommandPacket.TransferBuffer = Private->SqBuffer[NVME_IO_QUEUE];\r
+  CommandPacket.TransferLength = EFI_PAGE_SIZE;\r
+  CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;\r
+  CommandPacket.QueueType      = 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
+             Private,\r
+             NVME_CONTROLLER_NSID,\r
+             &CommandPacket\r
+             );\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Initialize the Nvm Express controller.\r
+\r
+  @param[in] Private     The pointer to the PEI_NVME_CONTROLLER_PRIVATE_DATA 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 PEI_NVME_CONTROLLER_PRIVATE_DATA    *Private\r
+  )\r
+{\r
+  EFI_STATUS    Status;\r
+  UINTN         Index;\r
+  NVME_AQA      Aqa;\r
+  NVME_ASQ      Asq;\r
+  NVME_ACQ      Acq;\r
+  NVME_VER      Ver;\r
+\r
+  //\r
+  // Dump the NVME controller implementation version\r
+  //\r
+  NVME_GET_VER (Private, &Ver);\r
+  DEBUG ((DEBUG_INFO, "NVME controller implementation version: %d.%d\n", Ver.Mjr, Ver.Mnr));\r
+\r
+  //\r
+  // Read the controller Capabilities register and verify that the NVM command set is supported\r
+  //\r
+  NVME_GET_CAP (Private, &Private->Cap);\r
+  if (Private->Cap.Css != 0x01) {\r
+    DEBUG ((DEBUG_ERROR, "%a: The NVME controller doesn't support NVMe command set.\n", __FUNCTION__));\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  //\r
+  // Currently, the driver only supports 4k page size\r
+  //\r
+  if ((Private->Cap.Mpsmin + 12) > EFI_PAGE_SHIFT) {\r
+    DEBUG ((DEBUG_ERROR, "%a: The driver doesn't support page size other than 4K.\n", __FUNCTION__));\r
+    ASSERT (FALSE);\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  for (Index = 0; Index < NVME_MAX_QUEUES; Index++) {\r
+    Private->Pt[Index]  = 0;\r
+    Private->Cid[Index] = 0;\r
+    ZeroMem ((VOID *)(UINTN)(&Private->SqTdbl[Index]), sizeof (NVME_SQTDBL));\r
+    ZeroMem ((VOID *)(UINTN)(&Private->CqHdbl[Index]), sizeof (NVME_CQHDBL));\r
+  }\r
+  ZeroMem (Private->Buffer, EFI_PAGE_SIZE * NVME_MEM_MAX_PAGES);\r
+\r
+  //\r
+  // Disable the NVME controller first\r
+  //\r
+  Status = NvmeDisableController (Private);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((DEBUG_ERROR, "%a: NvmeDisableController fail, Status - %r\n", __FUNCTION__, Status));\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Set the number of entries in 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 & completion queues\r
+  //\r
+  Asq = (UINT64)(UINTN)(NVME_ASQ_BASE (Private) & ~0xFFF);\r
+  Acq = (UINT64)(UINTN)(NVME_ACQ_BASE (Private) & ~0xFFF);\r
+\r
+  //\r
+  // Address of I/O submission & completion queues\r
+  //\r
+  Private->SqBuffer[0] = (NVME_SQ *)(UINTN)NVME_ASQ_BASE (Private);    // NVME_ADMIN_QUEUE\r
+  Private->CqBuffer[0] = (NVME_CQ *)(UINTN)NVME_ACQ_BASE (Private);    // NVME_ADMIN_QUEUE\r
+  Private->SqBuffer[1] = (NVME_SQ *)(UINTN)NVME_SQ_BASE (Private, 0);  // NVME_IO_QUEUE\r
+  Private->CqBuffer[1] = (NVME_CQ *)(UINTN)NVME_CQ_BASE (Private, 0);  // NVME_IO_QUEUE\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", Private->SqBuffer[0]));\r
+  DEBUG ((DEBUG_INFO, "Admin Completion Queue (CqBuffer[0]) =   [%08X]\n", Private->CqBuffer[0]));\r
+  DEBUG ((DEBUG_INFO, "I/O   Submission Queue (SqBuffer[1]) =   [%08X]\n", Private->SqBuffer[1]));\r
+  DEBUG ((DEBUG_INFO, "I/O   Completion Queue (CqBuffer[1]) =   [%08X]\n", Private->CqBuffer[1]));\r
+\r
+  //\r
+  // Program admin queue attributes\r
+  //\r
+  NVME_SET_AQA (Private, &Aqa);\r
+\r
+  //\r
+  // Program admin submission & completion queues address\r
+  //\r
+  NVME_SET_ASQ (Private, &Asq);\r
+  NVME_SET_ACQ (Private, &Acq);\r
+\r
+  //\r
+  // Enable the NVME controller\r
+  //\r
+  Status = NvmeEnableController (Private);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((DEBUG_ERROR, "%a: NvmeEnableController fail, Status - %r\n", __FUNCTION__, Status));\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Get the Identify Controller data\r
+  //\r
+  if (Private->ControllerData == NULL) {\r
+    Private->ControllerData = (NVME_ADMIN_CONTROLLER_DATA *)AllocateZeroPool (sizeof (NVME_ADMIN_CONTROLLER_DATA));\r
+    if (Private->ControllerData == NULL) {\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+  }\r
+  Status = NvmeIdentifyController (Private, Private->ControllerData);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((DEBUG_ERROR, "%a: NvmeIdentifyController fail, Status - %r\n", __FUNCTION__, Status));\r
+    return Status;\r
+  }\r
+  NvmeDumpControllerData (Private->ControllerData);\r
+\r
+  //\r
+  // Check the namespace number for storing the namespaces information\r
+  //\r
+  if (Private->ControllerData->Nn > MAX_UINT32 / sizeof (PEI_NVME_NAMESPACE_INFO)) {\r
+    DEBUG ((\r
+      DEBUG_ERROR,\r
+      "%a: Number of Namespaces field in Identify Controller data not supported by the driver.\n",\r
+      __FUNCTION__\r
+      ));\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  //\r
+  // Create one I/O completion queue and one I/O submission queue\r
+  //\r
+  Status = NvmeCreateIoCompletionQueue (Private);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((DEBUG_ERROR, "%a: Create IO completion queue fail, Status - %r\n", __FUNCTION__, Status));\r
+    return Status;\r
+  }\r
+  Status = NvmeCreateIoSubmissionQueue (Private);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((DEBUG_ERROR, "%a: Create IO submission queue fail, Status - %r\n", __FUNCTION__, Status));\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Free the resources allocated by an NVME controller.\r
+\r
+  @param[in] Private     The pointer to the PEI_NVME_CONTROLLER_PRIVATE_DATA data structure.\r
+\r
+**/\r
+VOID\r
+NvmeFreeControllerResource (\r
+  IN PEI_NVME_CONTROLLER_PRIVATE_DATA    *Private\r
+  )\r
+{\r
+  //\r
+  // Free the controller data buffer\r
+  //\r
+  if (Private->ControllerData != NULL) {\r
+    FreePool (Private->ControllerData);\r
+    Private->ControllerData = NULL;\r
+  }\r
+\r
+  //\r
+  // Free the DMA buffers\r
+  //\r
+  if (Private->Buffer != NULL) {\r
+    IoMmuFreeBuffer (\r
+       NVME_MEM_MAX_PAGES,\r
+       Private->Buffer,\r
+       Private->BufferMapping\r
+       );\r
+    Private->Buffer = NULL;\r
+  }\r
+\r
+  //\r
+  // Free the namespaces information buffer\r
+  //\r
+  if (Private->NamespaceInfo != NULL) {\r
+    FreePool (Private->NamespaceInfo);\r
+    Private->NamespaceInfo = NULL;\r
+  }\r
+\r
+  //\r
+  // Free the controller private data structure\r
+  //\r
+  FreePool (Private);\r
+  return;\r
+}\r
diff --git a/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiHci.h b/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiHci.h
new file mode 100644 (file)
index 0000000..ff334e3
--- /dev/null
@@ -0,0 +1,166 @@
+/** @file\r
+  The NvmExpressPei driver is used to manage non-volatile memory subsystem\r
+  which follows NVM Express specification at PEI phase.\r
+\r
+  Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>\r
+\r
+  This program and the accompanying materials\r
+  are licensed and made available under the terms and conditions\r
+  of the BSD License which accompanies this distribution.  The\r
+  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
+#ifndef _NVM_EXPRESS_PEI_HCI_H_\r
+#define _NVM_EXPRESS_PEI_HCI_H_\r
+\r
+//\r
+// NVME host controller registers operation definitions\r
+//\r
+#define NVME_GET_CAP(Private, Cap)             NvmeMmioRead  (Cap, Private->MmioBase + NVME_CAP_OFFSET, sizeof (NVME_CAP))\r
+#define NVME_GET_CC(Private, Cc)               NvmeMmioRead  (Cc, Private->MmioBase + NVME_CC_OFFSET, sizeof (NVME_CC))\r
+#define NVME_SET_CC(Private, Cc)               NvmeMmioWrite (Private->MmioBase + NVME_CC_OFFSET, Cc, sizeof (NVME_CC))\r
+#define NVME_GET_CSTS(Private, Csts)           NvmeMmioRead  (Csts, Private->MmioBase + NVME_CSTS_OFFSET, sizeof (NVME_CSTS))\r
+#define NVME_GET_AQA(Private, Aqa)             NvmeMmioRead  (Aqa, Private->MmioBase + NVME_AQA_OFFSET, sizeof (NVME_AQA))\r
+#define NVME_SET_AQA(Private, Aqa)             NvmeMmioWrite (Private->MmioBase + NVME_AQA_OFFSET, Aqa, sizeof (NVME_AQA))\r
+#define NVME_GET_ASQ(Private, Asq)             NvmeMmioRead  (Asq, Private->MmioBase + NVME_ASQ_OFFSET, sizeof (NVME_ASQ))\r
+#define NVME_SET_ASQ(Private, Asq)             NvmeMmioWrite (Private->MmioBase + NVME_ASQ_OFFSET, Asq, sizeof (NVME_ASQ))\r
+#define NVME_GET_ACQ(Private, Acq)             NvmeMmioRead  (Acq, Private->MmioBase + NVME_ACQ_OFFSET, sizeof (NVME_ACQ))\r
+#define NVME_SET_ACQ(Private, Acq)             NvmeMmioWrite (Private->MmioBase + NVME_ACQ_OFFSET, Acq, sizeof (NVME_ACQ))\r
+#define NVME_GET_VER(Private, Ver)             NvmeMmioRead  (Ver, Private->MmioBase + NVME_VER_OFFSET, sizeof (NVME_VER))\r
+#define NVME_SET_SQTDBL(Private, Qid, Sqtdbl)  NvmeMmioWrite (Private->MmioBase + NVME_SQTDBL_OFFSET(Qid, Private->Cap.Dstrd), Sqtdbl, sizeof (NVME_SQTDBL))\r
+#define NVME_SET_CQHDBL(Private, Qid, Cqhdbl)  NvmeMmioWrite (Private->MmioBase + NVME_CQHDBL_OFFSET(Qid, Private->Cap.Dstrd), Cqhdbl, sizeof (NVME_CQHDBL))\r
+\r
+//\r
+// Base memory address enum types\r
+//\r
+enum {\r
+  BASEMEM_ASQ,\r
+  BASEMEM_ACQ,\r
+  BASEMEM_SQ,\r
+  BASEMEM_CQ,\r
+  BASEMEM_PRP,\r
+  MAX_BASEMEM_COUNT\r
+};\r
+\r
+//\r
+// All of base memories are 4K(0x1000) alignment\r
+//\r
+#define ALIGN(v, a)                          (UINTN)((((v) - 1) | ((a) - 1)) + 1)\r
+#define NVME_MEM_BASE(Private)               ((UINTN)(Private->Buffer))\r
+#define NVME_ASQ_BASE(Private)               (ALIGN (NVME_MEM_BASE(Private) + ((NvmeBaseMemPageOffset (BASEMEM_ASQ))                                * EFI_PAGE_SIZE), EFI_PAGE_SIZE))\r
+#define NVME_ACQ_BASE(Private)               (ALIGN (NVME_MEM_BASE(Private) + ((NvmeBaseMemPageOffset (BASEMEM_ACQ))                                * EFI_PAGE_SIZE), EFI_PAGE_SIZE))\r
+#define NVME_SQ_BASE(Private, Index)         (ALIGN (NVME_MEM_BASE(Private) + ((NvmeBaseMemPageOffset (BASEMEM_SQ) + ((Index)*(NVME_MAX_QUEUES-1))) * EFI_PAGE_SIZE), EFI_PAGE_SIZE))\r
+#define NVME_CQ_BASE(Private, Index)         (ALIGN (NVME_MEM_BASE(Private) + ((NvmeBaseMemPageOffset (BASEMEM_CQ) + ((Index)*(NVME_MAX_QUEUES-1))) * EFI_PAGE_SIZE), EFI_PAGE_SIZE))\r
+#define NVME_PRP_BASE(Private)               (ALIGN (NVME_MEM_BASE(Private) + ((NvmeBaseMemPageOffset (BASEMEM_PRP))                                * EFI_PAGE_SIZE), EFI_PAGE_SIZE))\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
+**/\r
+EFI_STATUS\r
+NvmeMmioRead (\r
+  IN OUT VOID *MemBuffer,\r
+  IN     UINTN MmioAddr,\r
+  IN     UINTN Size\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
+**/\r
+EFI_STATUS\r
+NvmeMmioWrite (\r
+  IN OUT UINTN MmioAddr,\r
+  IN     VOID *MemBuffer,\r
+  IN     UINTN Size\r
+  );\r
+\r
+/**\r
+  Get the page offset 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
+NvmeBaseMemPageOffset (\r
+  IN UINTN              BaseMemIndex\r
+  );\r
+\r
+/**\r
+  Disable the Nvm Express controller.\r
+\r
+  @param[in] Private     The pointer to the PEI_NVME_CONTROLLER_PRIVATE_DATA data structure.\r
+\r
+  @return EFI_SUCCESS    Successfully disable the controller.\r
+  @return others         Fail to disable the controller.\r
+\r
+**/\r
+EFI_STATUS\r
+NvmeDisableController (\r
+  IN PEI_NVME_CONTROLLER_PRIVATE_DATA    *Private\r
+  );\r
+\r
+/**\r
+  Initialize the Nvm Express controller.\r
+\r
+  @param[in] Private     The pointer to the PEI_NVME_CONTROLLER_PRIVATE_DATA 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 PEI_NVME_CONTROLLER_PRIVATE_DATA    *Private\r
+  );\r
+\r
+/**\r
+  Get specified identify namespace data.\r
+\r
+  @param[in] Private        The pointer to the PEI_NVME_CONTROLLER_PRIVATE_DATA 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 EFI_DEVICE_ERROR    Fail to get the identify namespace data.\r
+\r
+**/\r
+EFI_STATUS\r
+NvmeIdentifyNamespace (\r
+  IN PEI_NVME_CONTROLLER_PRIVATE_DATA    *Private,\r
+  IN UINT32                              NamespaceId,\r
+  IN VOID                                *Buffer\r
+  );\r
+\r
+/**\r
+  Free the resources allocated by an NVME controller.\r
+\r
+  @param[in] Private     The pointer to the PEI_NVME_CONTROLLER_PRIVATE_DATA data structure.\r
+\r
+**/\r
+VOID\r
+NvmeFreeControllerResource (\r
+  IN PEI_NVME_CONTROLLER_PRIVATE_DATA    *Private\r
+  );\r
+\r
+#endif\r
diff --git a/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiPassThru.c b/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiPassThru.c
new file mode 100644 (file)
index 0000000..81ad01b
--- /dev/null
@@ -0,0 +1,628 @@
+/** @file\r
+  The NvmExpressPei driver is used to manage non-volatile memory subsystem\r
+  which follows NVM Express specification at PEI phase.\r
+\r
+  Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>\r
+\r
+  This program and the accompanying materials\r
+  are licensed and made available under the terms and conditions\r
+  of the BSD License which accompanies this distribution.  The\r
+  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 "NvmExpressPei.h"\r
+\r
+/**\r
+  Create PRP lists for Data transfer which is larger than 2 memory pages.\r
+\r
+  @param[in] Private          The pointer to the PEI_NVME_CONTROLLER_PRIVATE_DATA data structure.\r
+  @param[in] PhysicalAddr     The physical base address of Data Buffer.\r
+  @param[in] Pages            The number of pages to be transfered.\r
+\r
+  @retval The pointer Value to the first PRP List of the PRP lists.\r
+\r
+**/\r
+UINT64\r
+NvmeCreatePrpList (\r
+  IN     PEI_NVME_CONTROLLER_PRIVATE_DATA    *Private,\r
+  IN     EFI_PHYSICAL_ADDRESS                PhysicalAddr,\r
+  IN     UINTN                               Pages\r
+  )\r
+{\r
+  UINTN                   PrpEntryNo;\r
+  UINTN                   PrpListNo;\r
+  UINT64                  PrpListBase;\r
+  VOID                    *PrpListHost;\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 ((\r
+      DEBUG_ERROR,\r
+      "%a: The implementation only supports PrpList number up to 4."\r
+      " But %d are needed here.\n",\r
+      __FUNCTION__,\r
+      PrpListNo\r
+      ));\r
+    return 0;\r
+  }\r
+  PrpListHost = (VOID *)(UINTN) NVME_PRP_BASE (Private);\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 = (UINTN)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 = (UINTN)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 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 ((\r
+    DEBUG_INFO,\r
+    "  SQ Identifier : [0x%x], Phase Tag : [%d], Cmd Identifier : [0x%x]\n",\r
+    Cq->Sqid,\r
+    Cq->Pt,\r
+    Cq->Cid\r
+    ));\r
+  DEBUG ((DEBUG_INFO, "  Status Code Type : [0x%x], Status Code : [0x%x]\n", Cq->Sct, Cq->Sc));\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
+  Sends an NVM Express Command Packet to an NVM Express controller or namespace. This function only\r
+  supports blocking execution of the command.\r
+\r
+  @param[in] Private        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\r
+                            be sent.\r
+                            A Value of 0 denotes the NVM Express controller, a Value of all 0FFh in\r
+                            the namespace ID specifies that the command packet should be sent to all\r
+                            valid namespaces.\r
+  @param[in,out] Packet     A pointer to the EDKII PEI NVM Express PassThru Command Packet to send\r
+                            to the NVMe namespace specified by NamespaceId.\r
+\r
+  @retval EFI_SUCCESS              The EDKII PEI NVM Express Command Packet was sent by the host.\r
+                                   TransferLength bytes were transferred to, or from DataBuffer.\r
+  @retval EFI_NOT_READY            The EDKII PEI NVM Express Command Packet could not be sent because\r
+                                   the controller is not ready. The caller may retry again later.\r
+  @retval EFI_DEVICE_ERROR         A device error occurred while attempting to send the EDKII PEI NVM\r
+                                   Express Command Packet.\r
+  @retval EFI_INVALID_PARAMETER    Namespace, or the contents of EDKII_PEI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET\r
+                                   are invalid.\r
+                                   The EDKII PEI NVM Express Command Packet was not sent, so no\r
+                                   additional status information is available.\r
+  @retval EFI_UNSUPPORTED          The command described by the EDKII PEI NVM Express Command Packet\r
+                                   is not supported by the host adapter.\r
+                                   The EDKII PEI NVM Express Command Packet was not sent, so no\r
+                                   additional status information is available.\r
+  @retval EFI_TIMEOUT              A timeout occurred while waiting for the EDKII PEI NVM Express Command\r
+                                   Packet to execute.\r
+\r
+**/\r
+EFI_STATUS\r
+NvmePassThru (\r
+  IN     PEI_NVME_CONTROLLER_PRIVATE_DATA                  *Private,\r
+  IN     UINT32                                            NamespaceId,\r
+  IN OUT EDKII_PEI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET    *Packet\r
+  )\r
+{\r
+  EFI_STATUS               Status;\r
+  NVME_SQ                  *Sq;\r
+  NVME_CQ                  *Cq;\r
+  UINT8                    QueueId;\r
+  UINTN                    SqSize;\r
+  UINTN                    CqSize;\r
+  EDKII_IOMMU_OPERATION    MapOp;\r
+  UINTN                    MapLength;\r
+  EFI_PHYSICAL_ADDRESS     PhyAddr;\r
+  VOID                     *MapData;\r
+  VOID                     *MapMeta;\r
+  UINT32                   Bytes;\r
+  UINT32                   Offset;\r
+  UINT32                   Data32;\r
+  UINT64                   Timer;\r
+\r
+  //\r
+  // Check the data fields in Packet parameter\r
+  //\r
+  if (Packet == NULL) {\r
+    DEBUG ((\r
+      DEBUG_ERROR,\r
+      "%a, Invalid parameter: Packet(%lx)\n",\r
+      __FUNCTION__,\r
+      (UINTN)Packet\r
+      ));\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if ((Packet->NvmeCmd == NULL) || (Packet->NvmeCompletion == NULL)) {\r
+    DEBUG ((\r
+      DEBUG_ERROR,\r
+      "%a, Invalid parameter: NvmeCmd (%lx)/NvmeCompletion(%lx)\n",\r
+      __FUNCTION__,\r
+      (UINTN)Packet->NvmeCmd,\r
+      (UINTN)Packet->NvmeCompletion\r
+      ));\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (Packet->QueueType != NVME_ADMIN_QUEUE && Packet->QueueType != NVME_IO_QUEUE) {\r
+    DEBUG ((\r
+      DEBUG_ERROR,\r
+      "%a, Invalid parameter: QueueId(%lx)\n",\r
+      __FUNCTION__,\r
+      (UINTN)Packet->QueueType\r
+      ));\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  QueueId = Packet->QueueType;\r
+  Sq      = Private->SqBuffer[QueueId] + Private->SqTdbl[QueueId].Sqt;\r
+  Cq      = Private->CqBuffer[QueueId] + Private->CqHdbl[QueueId].Cqh;\r
+  if (QueueId == NVME_ADMIN_QUEUE) {\r
+    SqSize = NVME_ASQ_SIZE + 1;\r
+    CqSize = NVME_ACQ_SIZE + 1;\r
+  } else {\r
+    SqSize = NVME_CSQ_SIZE + 1;\r
+    CqSize = NVME_CCQ_SIZE + 1;\r
+  }\r
+\r
+  if (Packet->NvmeCmd->Nsid != NamespaceId) {\r
+    DEBUG ((\r
+      DEBUG_ERROR,\r
+      "%a: Nsid mismatch (%x, %x)\n",\r
+      __FUNCTION__,\r
+      Packet->NvmeCmd->Nsid,\r
+      NamespaceId\r
+      ));\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, "%a: Does not support SGL mechanism.\n", __FUNCTION__));\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  Sq->Prp[0] = (UINT64)(UINTN)Packet->TransferBuffer;\r
+  Sq->Prp[1] = 0;\r
+  MapData    = NULL;\r
+  MapMeta    = NULL;\r
+  Status     = EFI_SUCCESS;\r
+  //\r
+  // If the NVMe cmd has data in or out, then mapping the user buffer to the PCI controller\r
+  // specific addresses.\r
+  //\r
+  if ((Sq->Opc & (BIT0 | BIT1)) != 0) {\r
+    if ((Packet->TransferLength == 0) || (Packet->TransferBuffer == NULL)) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    //\r
+    // Currently, we only support creating IO submission/completion queues that are\r
+    // allocated internally by the driver.\r
+    //\r
+    if ((Packet->QueueType == NVME_ADMIN_QUEUE) &&\r
+        ((Sq->Opc == NVME_ADMIN_CRIOCQ_CMD) || (Sq->Opc == NVME_ADMIN_CRIOSQ_CMD))) {\r
+      if ((Packet->TransferBuffer != Private->SqBuffer[NVME_IO_QUEUE]) &&\r
+          (Packet->TransferBuffer != Private->CqBuffer[NVME_IO_QUEUE])) {\r
+        DEBUG ((\r
+          DEBUG_ERROR,\r
+          "%a: Does not support external IO queues creation request.\n",\r
+          __FUNCTION__\r
+          ));\r
+        return EFI_UNSUPPORTED;\r
+      }\r
+    } else {\r
+      if ((Sq->Opc & BIT0) != 0) {\r
+        MapOp = EdkiiIoMmuOperationBusMasterRead;\r
+      } else {\r
+        MapOp = EdkiiIoMmuOperationBusMasterWrite;\r
+      }\r
+\r
+      MapLength = Packet->TransferLength;\r
+      Status = IoMmuMap (\r
+                 MapOp,\r
+                 Packet->TransferBuffer,\r
+                 &MapLength,\r
+                 &PhyAddr,\r
+                 &MapData\r
+                 );\r
+      if (EFI_ERROR (Status) || (MapLength != Packet->TransferLength)) {\r
+        Status = EFI_OUT_OF_RESOURCES;\r
+        DEBUG ((DEBUG_ERROR, "%a: Fail to map data buffer.\n", __FUNCTION__));\r
+        goto Exit;\r
+      }\r
+\r
+      Sq->Prp[0] = PhyAddr;\r
+\r
+      if((Packet->MetadataLength != 0) && (Packet->MetadataBuffer != NULL)) {\r
+        MapLength = Packet->MetadataLength;\r
+        Status = IoMmuMap (\r
+                   MapOp,\r
+                   Packet->MetadataBuffer,\r
+                   &MapLength,\r
+                   &PhyAddr,\r
+                   &MapMeta\r
+                   );\r
+        if (EFI_ERROR (Status) || (MapLength != Packet->MetadataLength)) {\r
+          Status = EFI_OUT_OF_RESOURCES;\r
+          DEBUG ((DEBUG_ERROR, "%a: Fail to map meta data buffer.\n", __FUNCTION__));\r
+          goto Exit;\r
+        }\r
+        Sq->Mptr = PhyAddr;\r
+      }\r
+    }\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 (\r
+                   Private,\r
+                   PhyAddr,\r
+                   EFI_SIZE_TO_PAGES(Offset + Bytes) - 1\r
+                   );\r
+    if (Sq->Prp[1] == 0) {\r
+      Status = EFI_OUT_OF_RESOURCES;\r
+      DEBUG ((DEBUG_ERROR, "%a: Create PRP list fail, Status - %r\n", __FUNCTION__, 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
+  //\r
+  // Ring the submission queue doorbell.\r
+  //\r
+  Private->SqTdbl[QueueId].Sqt++;\r
+  if (Private->SqTdbl[QueueId].Sqt == SqSize) {\r
+    Private->SqTdbl[QueueId].Sqt = 0;\r
+  }\r
+  Data32 = ReadUnaligned32 ((UINT32 *)&Private->SqTdbl[QueueId]);\r
+  Status = NVME_SET_SQTDBL (Private, QueueId, &Data32);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((DEBUG_ERROR, "%a: NVME_SET_SQTDBL fail, Status - %r\n", __FUNCTION__, 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 < Packet->CommandTimeout) {\r
+    if (Cq->Pt != Private->Pt[QueueId]) {\r
+      Status = EFI_SUCCESS;\r
+      break;\r
+    }\r
+\r
+    MicroSecondDelay (NVME_POLL_INTERVAL);\r
+    Timer += NVME_POLL_INTERVAL;\r
+  }\r
+\r
+  if (Status == EFI_TIMEOUT) {\r
+    //\r
+    // Timeout occurs for an NVMe command, reset the controller to abort the outstanding command\r
+    //\r
+    DEBUG ((DEBUG_ERROR, "%a: Timeout occurs for the PassThru command.\n", __FUNCTION__));\r
+    Status = NvmeControllerInit (Private);\r
+    if (EFI_ERROR (Status)) {\r
+      Status = EFI_DEVICE_ERROR;\r
+    } else {\r
+      //\r
+      // Return EFI_TIMEOUT to indicate a timeout occurs for PassThru command\r
+      //\r
+      Status = EFI_TIMEOUT;\r
+    }\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // Move forward the Completion Queue head\r
+  //\r
+  Private->CqHdbl[QueueId].Cqh++;\r
+  if (Private->CqHdbl[QueueId].Cqh == CqSize) {\r
+    Private->CqHdbl[QueueId].Cqh = 0;\r
+    Private->Pt[QueueId] ^= 1;\r
+  }\r
+\r
+  //\r
+  // Copy the Respose Queue entry for this command to the callers response buffer\r
+  //\r
+  CopyMem (Packet->NvmeCompletion, Cq, sizeof (EDKII_PEI_NVM_EXPRESS_COMPLETION));\r
+\r
+  //\r
+  // Check the NVMe cmd execution result\r
+  //\r
+  Status = NvmeCheckCqStatus (Cq);\r
+  NVME_SET_CQHDBL (Private, QueueId, &Private->CqHdbl[QueueId]);\r
+\r
+Exit:\r
+  if (MapMeta != NULL) {\r
+    IoMmuUnmap (MapMeta);\r
+  }\r
+\r
+  if (MapData != NULL) {\r
+    IoMmuUnmap (MapData);\r
+  }\r
+\r
+  return Status;\r
+}\r
diff --git a/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiPassThru.h b/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiPassThru.h
new file mode 100644 (file)
index 0000000..96c748e
--- /dev/null
@@ -0,0 +1,107 @@
+/** @file\r
+  The NvmExpressPei driver is used to manage non-volatile memory subsystem\r
+  which follows NVM Express specification at PEI phase.\r
+\r
+  Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>\r
+\r
+  This program and the accompanying materials\r
+  are licensed and made available under the terms and conditions\r
+  of the BSD License which accompanies this distribution.  The\r
+  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
+#ifndef _NVM_EXPRESS_PEI_PASSTHRU_H_\r
+#define _NVM_EXPRESS_PEI_PASSTHRU_H_\r
+\r
+#define NVME_CONTROLLER_NSID        0\r
+\r
+typedef struct {\r
+  UINT8                             Opcode;\r
+  UINT8                             FusedOperation;\r
+    #define NORMAL_CMD              0x00\r
+    #define FUSED_FIRST_CMD         0x01\r
+    #define FUSED_SECOND_CMD        0x02\r
+  UINT16                            Cid;\r
+} NVME_CDW0;\r
+\r
+typedef struct {\r
+  NVME_CDW0                         Cdw0;\r
+  UINT8                             Flags;\r
+    #define CDW10_VALID             0x01\r
+    #define CDW11_VALID             0x02\r
+    #define CDW12_VALID             0x04\r
+    #define CDW13_VALID             0x08\r
+    #define CDW14_VALID             0x10\r
+    #define CDW15_VALID             0x20\r
+  UINT32                            Nsid;\r
+  UINT32                            Cdw10;\r
+  UINT32                            Cdw11;\r
+  UINT32                            Cdw12;\r
+  UINT32                            Cdw13;\r
+  UINT32                            Cdw14;\r
+  UINT32                            Cdw15;\r
+} EDKII_PEI_NVM_EXPRESS_COMMAND;\r
+\r
+typedef struct {\r
+  UINT32                            Cdw0;\r
+  UINT32                            Cdw1;\r
+  UINT32                            Cdw2;\r
+  UINT32                            Cdw3;\r
+} EDKII_PEI_NVM_EXPRESS_COMPLETION;\r
+\r
+typedef struct {\r
+  UINT64                            CommandTimeout;\r
+  VOID                              *TransferBuffer;\r
+  UINT32                            TransferLength;\r
+  VOID                              *MetadataBuffer;\r
+  UINT32                            MetadataLength;\r
+  UINT8                             QueueType;\r
+  EDKII_PEI_NVM_EXPRESS_COMMAND     *NvmeCmd;\r
+  EDKII_PEI_NVM_EXPRESS_COMPLETION  *NvmeCompletion;\r
+} EDKII_PEI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET;\r
+\r
+\r
+/**\r
+  Sends an NVM Express Command Packet to an NVM Express controller or namespace. This function only\r
+  supports blocking execution of the command.\r
+\r
+  @param[in] Private        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\r
+                            be sent.\r
+                            A Value of 0 denotes the NVM Express controller, a Value of all 0FFh in\r
+                            the namespace ID specifies that the command packet should be sent to all\r
+                            valid namespaces.\r
+  @param[in,out] Packet     A pointer to the EDKII PEI NVM Express PassThru Command Packet to send\r
+                            to the NVMe namespace specified by NamespaceId.\r
+\r
+  @retval EFI_SUCCESS              The EDKII PEI NVM Express Command Packet was sent by the host.\r
+                                   TransferLength bytes were transferred to, or from DataBuffer.\r
+  @retval EFI_NOT_READY            The EDKII PEI NVM Express Command Packet could not be sent because\r
+                                   the controller is not ready. The caller may retry again later.\r
+  @retval EFI_DEVICE_ERROR         A device error occurred while attempting to send the EDKII PEI NVM\r
+                                   Express Command Packet.\r
+  @retval EFI_INVALID_PARAMETER    Namespace, or the contents of EDKII_PEI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET\r
+                                   are invalid.\r
+                                   The EDKII PEI NVM Express Command Packet was not sent, so no\r
+                                   additional status information is available.\r
+  @retval EFI_UNSUPPORTED          The command described by the EDKII PEI NVM Express Command Packet\r
+                                   is not supported by the host adapter.\r
+                                   The EDKII PEI NVM Express Command Packet was not sent, so no\r
+                                   additional status information is available.\r
+  @retval EFI_TIMEOUT              A timeout occurred while waiting for the EDKII PEI NVM Express Command\r
+                                   Packet to execute.\r
+\r
+**/\r
+EFI_STATUS\r
+NvmePassThru (\r
+  IN     PEI_NVME_CONTROLLER_PRIVATE_DATA                  *Private,\r
+  IN     UINT32                                            NamespaceId,\r
+  IN OUT EDKII_PEI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET    *Packet\r
+  );\r
+\r
+#endif\r
index 18928f96d80a7a2a78a76c7ce07fb05aef443078..09b0f9f13dc9617fc4185baaacc3774de25e5a18 100644 (file)
   MdeModulePkg/Bus/Pci/PciBusDxe/PciBusDxe.inf\r
   MdeModulePkg/Bus/Pci/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupportDxe.inf\r
   MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressDxe.inf\r
+  MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.inf\r
   MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/SdMmcPciHcDxe.inf\r
   MdeModulePkg/Bus/Pci/SdMmcPciHcPei/SdMmcPciHcPei.inf\r
   MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcBlockIoPei.inf\r