aboutsummaryrefslogtreecommitdiffstats
path: root/roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei
diff options
context:
space:
mode:
Diffstat (limited to 'roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei')
-rw-r--r--roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/DevicePath.c277
-rw-r--r--roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/DmaMem.c263
-rw-r--r--roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.c462
-rw-r--r--roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.h349
-rw-r--r--roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.inf73
-rw-r--r--roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.uni14
-rw-r--r--roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiBlockIo.c523
-rw-r--r--roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiBlockIo.h259
-rw-r--r--roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiExtra.uni12
-rw-r--r--roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiHci.c717
-rw-r--r--roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiHci.h145
-rw-r--r--roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiPassThru.c828
-rw-r--r--roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiPassThru.h161
-rw-r--r--roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiS3.c107
-rw-r--r--roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiStorageSecurity.c418
-rw-r--r--roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiStorageSecurity.h240
16 files changed, 4848 insertions, 0 deletions
diff --git a/roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/DevicePath.c b/roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/DevicePath.c
new file mode 100644
index 000000000..6f9ff4b7d
--- /dev/null
+++ b/roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/DevicePath.c
@@ -0,0 +1,277 @@
+/** @file
+ The device path help function.
+
+ Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "NvmExpressPei.h"
+
+//
+// Template for an Nvm Express Device Path node
+//
+NVME_NAMESPACE_DEVICE_PATH mNvmeDevicePathNodeTemplate = {
+ { // Header
+ MESSAGING_DEVICE_PATH,
+ MSG_NVME_NAMESPACE_DP,
+ {
+ (UINT8) (sizeof (NVME_NAMESPACE_DEVICE_PATH)),
+ (UINT8) ((sizeof (NVME_NAMESPACE_DEVICE_PATH)) >> 8)
+ }
+ },
+ 0x0, // NamespaceId
+ 0x0 // NamespaceUuid
+};
+
+//
+// Template for an End of entire Device Path node
+//
+EFI_DEVICE_PATH_PROTOCOL mNvmeEndDevicePathNodeTemplate = {
+ END_DEVICE_PATH_TYPE,
+ END_ENTIRE_DEVICE_PATH_SUBTYPE,
+ {
+ (UINT8) (sizeof (EFI_DEVICE_PATH_PROTOCOL)),
+ (UINT8) ((sizeof (EFI_DEVICE_PATH_PROTOCOL)) >> 8)
+ }
+};
+
+/**
+ Returns the 16-bit Length field of a device path node.
+
+ Returns the 16-bit Length field of the device path node specified by Node.
+ Node is not required to be aligned on a 16-bit boundary, so it is recommended
+ that a function such as ReadUnaligned16() be used to extract the contents of
+ the Length field.
+
+ If Node is NULL, then ASSERT().
+
+ @param Node A pointer to a device path node data structure.
+
+ @return The 16-bit Length field of the device path node specified by Node.
+
+**/
+UINTN
+DevicePathNodeLength (
+ IN CONST VOID *Node
+ )
+{
+ ASSERT (Node != NULL);
+ return ReadUnaligned16 ((UINT16 *)&((EFI_DEVICE_PATH_PROTOCOL *)(Node))->Length[0]);
+}
+
+/**
+ Returns a pointer to the next node in a device path.
+
+ If Node is NULL, then ASSERT().
+
+ @param Node A pointer to a device path node data structure.
+
+ @return a pointer to the device path node that follows the device path node
+ specified by Node.
+
+**/
+EFI_DEVICE_PATH_PROTOCOL *
+NextDevicePathNode (
+ IN CONST VOID *Node
+ )
+{
+ ASSERT (Node != NULL);
+ return (EFI_DEVICE_PATH_PROTOCOL *)((UINT8 *)(Node) + DevicePathNodeLength(Node));
+}
+
+/**
+ Get the size of the current device path instance.
+
+ @param[in] DevicePath A pointer to the EFI_DEVICE_PATH_PROTOCOL
+ structure.
+ @param[out] InstanceSize The size of the current device path instance.
+ @param[out] EntireDevicePathEnd Indicate whether the instance is the last
+ one in the device path strucure.
+
+ @retval EFI_SUCCESS The size of the current device path instance is fetched.
+ @retval Others Fails to get the size of the current device path instance.
+
+**/
+EFI_STATUS
+GetDevicePathInstanceSize (
+ IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
+ OUT UINTN *InstanceSize,
+ OUT BOOLEAN *EntireDevicePathEnd
+ )
+{
+ EFI_DEVICE_PATH_PROTOCOL *Walker;
+
+ if (DevicePath == NULL || InstanceSize == NULL || EntireDevicePathEnd == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Find the end of the device path instance
+ //
+ Walker = DevicePath;
+ while (Walker->Type != END_DEVICE_PATH_TYPE) {
+ Walker = NextDevicePathNode (Walker);
+ }
+
+ //
+ // Check if 'Walker' points to the end of an entire device path
+ //
+ if (Walker->SubType == END_ENTIRE_DEVICE_PATH_SUBTYPE) {
+ *EntireDevicePathEnd = TRUE;
+ } else if (Walker->SubType == END_INSTANCE_DEVICE_PATH_SUBTYPE) {
+ *EntireDevicePathEnd = FALSE;
+ } else {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Compute the size of the device path instance
+ //
+ *InstanceSize = ((UINTN) Walker - (UINTN) (DevicePath)) + sizeof (EFI_DEVICE_PATH_PROTOCOL);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Check the validity of the device path of a NVM Express host controller.
+
+ @param[in] DevicePath A pointer to the EFI_DEVICE_PATH_PROTOCOL
+ structure.
+ @param[in] DevicePathLength The length of the device path.
+
+ @retval EFI_SUCCESS The device path is valid.
+ @retval EFI_INVALID_PARAMETER The device path is invalid.
+
+**/
+EFI_STATUS
+NvmeIsHcDevicePathValid (
+ IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
+ IN UINTN DevicePathLength
+ )
+{
+ EFI_DEVICE_PATH_PROTOCOL *Start;
+ UINTN Size;
+
+ if (DevicePath == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Validate the DevicePathLength is big enough to touch the first node.
+ //
+ if (DevicePathLength < sizeof (EFI_DEVICE_PATH_PROTOCOL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Start = DevicePath;
+ while (!(DevicePath->Type == END_DEVICE_PATH_TYPE &&
+ DevicePath->SubType == END_ENTIRE_DEVICE_PATH_SUBTYPE)) {
+ DevicePath = NextDevicePathNode (DevicePath);
+
+ //
+ // Prevent overflow and invalid zero in the 'Length' field of a device path
+ // node.
+ //
+ if ((UINTN) DevicePath <= (UINTN) Start) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Prevent touching memory beyond given DevicePathLength.
+ //
+ if ((UINTN) DevicePath - (UINTN) Start >
+ DevicePathLength - sizeof (EFI_DEVICE_PATH_PROTOCOL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ //
+ // Check if the device path and its size match exactly with each other.
+ //
+ Size = ((UINTN) DevicePath - (UINTN) Start) + sizeof (EFI_DEVICE_PATH_PROTOCOL);
+ if (Size != DevicePathLength) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Build the device path for an Nvm Express device with given namespace identifier
+ and namespace extended unique identifier.
+
+ @param[in] Private A pointer to the PEI_NVME_CONTROLLER_PRIVATE_DATA
+ data structure.
+ @param[in] NamespaceId The given namespace identifier.
+ @param[in] NamespaceUuid The given namespace extended unique identifier.
+ @param[out] DevicePathLength The length of the device path in bytes specified
+ by DevicePath.
+ @param[out] DevicePath The device path of Nvm Express device.
+
+ @retval EFI_SUCCESS The operation succeeds.
+ @retval EFI_INVALID_PARAMETER The parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES The operation fails due to lack of resources.
+
+**/
+EFI_STATUS
+NvmeBuildDevicePath (
+ IN PEI_NVME_CONTROLLER_PRIVATE_DATA *Private,
+ IN UINT32 NamespaceId,
+ IN UINT64 NamespaceUuid,
+ OUT UINTN *DevicePathLength,
+ OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath
+ )
+{
+ EFI_DEVICE_PATH_PROTOCOL *DevicePathWalker;
+ NVME_NAMESPACE_DEVICE_PATH *NvmeDeviceNode;
+
+ if (DevicePathLength == NULL || DevicePath == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *DevicePathLength = Private->DevicePathLength + sizeof (NVME_NAMESPACE_DEVICE_PATH);
+ *DevicePath = AllocatePool (*DevicePathLength);
+ if (*DevicePath == NULL) {
+ *DevicePathLength = 0;
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Construct the host controller part device nodes
+ //
+ DevicePathWalker = *DevicePath;
+ CopyMem (
+ DevicePathWalker,
+ Private->DevicePath,
+ Private->DevicePathLength - sizeof (EFI_DEVICE_PATH_PROTOCOL)
+ );
+
+ //
+ // Construct the Nvm Express device node
+ //
+ DevicePathWalker = (EFI_DEVICE_PATH_PROTOCOL *) ((UINT8 *)DevicePathWalker +
+ (Private->DevicePathLength - sizeof (EFI_DEVICE_PATH_PROTOCOL)));
+ CopyMem (
+ DevicePathWalker,
+ &mNvmeDevicePathNodeTemplate,
+ sizeof (mNvmeDevicePathNodeTemplate)
+ );
+ NvmeDeviceNode = (NVME_NAMESPACE_DEVICE_PATH *)DevicePathWalker;
+ NvmeDeviceNode->NamespaceId = NamespaceId;
+ NvmeDeviceNode->NamespaceUuid = NamespaceUuid;
+
+ //
+ // Construct the end device node
+ //
+ DevicePathWalker = (EFI_DEVICE_PATH_PROTOCOL *) ((UINT8 *)DevicePathWalker +
+ sizeof (NVME_NAMESPACE_DEVICE_PATH));
+ CopyMem (
+ DevicePathWalker,
+ &mNvmeEndDevicePathNodeTemplate,
+ sizeof (mNvmeEndDevicePathNodeTemplate)
+ );
+
+ return EFI_SUCCESS;
+}
diff --git a/roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/DmaMem.c b/roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/DmaMem.c
new file mode 100644
index 000000000..7b049b9e4
--- /dev/null
+++ b/roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/DmaMem.c
@@ -0,0 +1,263 @@
+/** @file
+ The DMA memory help function.
+
+ Copyright (c) 2018 - 2019, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "NvmExpressPei.h"
+
+/**
+ Get IOMMU PPI.
+
+ @return Pointer to IOMMU PPI.
+
+**/
+EDKII_IOMMU_PPI *
+GetIoMmu (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ EDKII_IOMMU_PPI *IoMmu;
+
+ IoMmu = NULL;
+ Status = PeiServicesLocatePpi (
+ &gEdkiiIoMmuPpiGuid,
+ 0,
+ NULL,
+ (VOID **) &IoMmu
+ );
+ if (!EFI_ERROR (Status) && (IoMmu != NULL)) {
+ return IoMmu;
+ }
+
+ return NULL;
+}
+
+/**
+ Provides the controller-specific addresses required to access system memory from a
+ DMA bus master.
+
+ @param Operation Indicates if the bus master is going to read or write to system memory.
+ @param HostAddress The system memory address to map to the PCI controller.
+ @param NumberOfBytes On input the number of bytes to map. On output the number of bytes
+ that were mapped.
+ @param DeviceAddress The resulting map address for the bus master PCI controller to use to
+ access the hosts HostAddress.
+ @param Mapping A resulting value to pass to Unmap().
+
+ @retval EFI_SUCCESS The range was mapped for the returned NumberOfBytes.
+ @retval EFI_UNSUPPORTED The HostAddress cannot be mapped as a common buffer.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.
+ @retval EFI_DEVICE_ERROR The system hardware could not map the requested address.
+
+**/
+EFI_STATUS
+IoMmuMap (
+ IN EDKII_IOMMU_OPERATION Operation,
+ IN VOID *HostAddress,
+ IN OUT UINTN *NumberOfBytes,
+ OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,
+ OUT VOID **Mapping
+ )
+{
+ EFI_STATUS Status;
+ UINT64 Attribute;
+ EDKII_IOMMU_PPI *IoMmu;
+
+ IoMmu = GetIoMmu ();
+
+ if (IoMmu != NULL) {
+ Status = IoMmu->Map (
+ IoMmu,
+ Operation,
+ HostAddress,
+ NumberOfBytes,
+ DeviceAddress,
+ Mapping
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ switch (Operation) {
+ case EdkiiIoMmuOperationBusMasterRead:
+ case EdkiiIoMmuOperationBusMasterRead64:
+ Attribute = EDKII_IOMMU_ACCESS_READ;
+ break;
+ case EdkiiIoMmuOperationBusMasterWrite:
+ case EdkiiIoMmuOperationBusMasterWrite64:
+ Attribute = EDKII_IOMMU_ACCESS_WRITE;
+ break;
+ case EdkiiIoMmuOperationBusMasterCommonBuffer:
+ case EdkiiIoMmuOperationBusMasterCommonBuffer64:
+ Attribute = EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE;
+ break;
+ default:
+ ASSERT(FALSE);
+ return EFI_INVALID_PARAMETER;
+ }
+ Status = IoMmu->SetAttribute (
+ IoMmu,
+ *Mapping,
+ Attribute
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ } else {
+ *DeviceAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress;
+ *Mapping = NULL;
+ Status = EFI_SUCCESS;
+ }
+ return Status;
+}
+
+/**
+ Completes the Map() operation and releases any corresponding resources.
+
+ @param Mapping The mapping value returned from Map().
+
+ @retval EFI_SUCCESS The range was unmapped.
+ @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by Map().
+ @retval EFI_DEVICE_ERROR The data was not committed to the target system memory.
+**/
+EFI_STATUS
+IoMmuUnmap (
+ IN VOID *Mapping
+ )
+{
+ EFI_STATUS Status;
+ EDKII_IOMMU_PPI *IoMmu;
+
+ IoMmu = GetIoMmu ();
+
+ if (IoMmu != NULL) {
+ Status = IoMmu->SetAttribute (IoMmu, Mapping, 0);
+ Status = IoMmu->Unmap (IoMmu, Mapping);
+ } else {
+ Status = EFI_SUCCESS;
+ }
+ return Status;
+}
+
+/**
+ Allocates pages that are suitable for an OperationBusMasterCommonBuffer or
+ OperationBusMasterCommonBuffer64 mapping.
+
+ @param Pages The number of pages to allocate.
+ @param HostAddress A pointer to store the base system memory address of the
+ allocated range.
+ @param DeviceAddress The resulting map address for the bus master PCI controller to use to
+ access the hosts HostAddress.
+ @param Mapping A resulting value to pass to Unmap().
+
+ @retval EFI_SUCCESS The requested memory pages were allocated.
+ @retval EFI_UNSUPPORTED Attributes is unsupported. The only legal attribute bits are
+ MEMORY_WRITE_COMBINE and MEMORY_CACHED.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated.
+
+**/
+EFI_STATUS
+IoMmuAllocateBuffer (
+ IN UINTN Pages,
+ OUT VOID **HostAddress,
+ OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,
+ OUT VOID **Mapping
+ )
+{
+ EFI_STATUS Status;
+ UINTN NumberOfBytes;
+ EFI_PHYSICAL_ADDRESS HostPhyAddress;
+ EDKII_IOMMU_PPI *IoMmu;
+
+ *HostAddress = NULL;
+ *DeviceAddress = 0;
+
+ IoMmu = GetIoMmu ();
+
+ if (IoMmu != NULL) {
+ Status = IoMmu->AllocateBuffer (
+ IoMmu,
+ EfiBootServicesData,
+ Pages,
+ HostAddress,
+ 0
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ NumberOfBytes = EFI_PAGES_TO_SIZE(Pages);
+ Status = IoMmu->Map (
+ IoMmu,
+ EdkiiIoMmuOperationBusMasterCommonBuffer,
+ *HostAddress,
+ &NumberOfBytes,
+ DeviceAddress,
+ Mapping
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ Status = IoMmu->SetAttribute (
+ IoMmu,
+ *Mapping,
+ EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ } else {
+ Status = PeiServicesAllocatePages (
+ EfiBootServicesData,
+ Pages,
+ &HostPhyAddress
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ *HostAddress = (VOID *)(UINTN)HostPhyAddress;
+ *DeviceAddress = HostPhyAddress;
+ *Mapping = NULL;
+ }
+ return Status;
+}
+
+/**
+ Frees memory that was allocated with AllocateBuffer().
+
+ @param Pages The number of pages to free.
+ @param HostAddress The base system memory address of the allocated range.
+ @param Mapping The mapping value returned from Map().
+
+ @retval EFI_SUCCESS The requested memory pages were freed.
+ @retval EFI_INVALID_PARAMETER The memory range specified by HostAddress and Pages
+ was not allocated with AllocateBuffer().
+
+**/
+EFI_STATUS
+IoMmuFreeBuffer (
+ IN UINTN Pages,
+ IN VOID *HostAddress,
+ IN VOID *Mapping
+ )
+{
+ EFI_STATUS Status;
+ EDKII_IOMMU_PPI *IoMmu;
+
+ IoMmu = GetIoMmu ();
+
+ if (IoMmu != NULL) {
+ Status = IoMmu->SetAttribute (IoMmu, Mapping, 0);
+ Status = IoMmu->Unmap (IoMmu, Mapping);
+ Status = IoMmu->FreeBuffer (IoMmu, Pages, HostAddress);
+ } else {
+ Status = EFI_SUCCESS;
+ }
+ return Status;
+}
diff --git a/roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.c b/roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.c
new file mode 100644
index 000000000..a8cb7f3a6
--- /dev/null
+++ b/roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.c
@@ -0,0 +1,462 @@
+/** @file
+ The NvmExpressPei driver is used to manage non-volatile memory subsystem
+ which follows NVM Express specification at PEI phase.
+
+ Copyright (c) 2018 - 2019, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "NvmExpressPei.h"
+
+EFI_PEI_PPI_DESCRIPTOR mNvmeBlkIoPpiListTemplate = {
+ EFI_PEI_PPI_DESCRIPTOR_PPI,
+ &gEfiPeiVirtualBlockIoPpiGuid,
+ NULL
+};
+
+EFI_PEI_PPI_DESCRIPTOR mNvmeBlkIo2PpiListTemplate = {
+ (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
+ &gEfiPeiVirtualBlockIo2PpiGuid,
+ NULL
+};
+
+EFI_PEI_PPI_DESCRIPTOR mNvmeStorageSecurityPpiListTemplate = {
+ (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
+ &gEdkiiPeiStorageSecurityCommandPpiGuid,
+ NULL
+};
+
+EFI_PEI_PPI_DESCRIPTOR mNvmePassThruPpiListTemplate = {
+ (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
+ &gEdkiiPeiNvmExpressPassThruPpiGuid,
+ NULL
+};
+
+EFI_PEI_NOTIFY_DESCRIPTOR mNvmeEndOfPeiNotifyListTemplate = {
+ (EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
+ &gEfiEndOfPeiSignalPpiGuid,
+ NvmePeimEndOfPei
+};
+
+/**
+ Check if the specified Nvm Express device namespace is active, and then get the Identify
+ Namespace data.
+
+ @param[in,out] Private The pointer to the PEI_NVME_CONTROLLER_PRIVATE_DATA data structure.
+ @param[in] NamespaceId The specified namespace identifier.
+
+ @retval EFI_SUCCESS The specified namespace in the device is successfully enumerated.
+ @return Others Error occurs when enumerating the namespace.
+
+**/
+EFI_STATUS
+EnumerateNvmeDevNamespace (
+ IN OUT PEI_NVME_CONTROLLER_PRIVATE_DATA *Private,
+ IN UINT32 NamespaceId
+ )
+{
+ EFI_STATUS Status;
+ NVME_ADMIN_NAMESPACE_DATA *NamespaceData;
+ PEI_NVME_NAMESPACE_INFO *NamespaceInfo;
+ UINT32 DeviceIndex;
+ UINT32 Lbads;
+ UINT32 Flbas;
+ UINT32 LbaFmtIdx;
+
+ NamespaceData = (NVME_ADMIN_NAMESPACE_DATA *) AllocateZeroPool (sizeof (NVME_ADMIN_NAMESPACE_DATA));
+ if (NamespaceData == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Identify Namespace
+ //
+ Status = NvmeIdentifyNamespace (
+ Private,
+ NamespaceId,
+ NamespaceData
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: NvmeIdentifyNamespace fail, Status - %r\n", __FUNCTION__, Status));
+ goto Exit;
+ }
+
+ //
+ // Validate Namespace
+ //
+ if (NamespaceData->Ncap == 0) {
+ DEBUG ((DEBUG_INFO, "%a: Namespace ID %d is an inactive one.\n", __FUNCTION__, NamespaceId));
+ Status = EFI_DEVICE_ERROR;
+ goto Exit;
+ }
+
+ DeviceIndex = Private->ActiveNamespaceNum;
+ NamespaceInfo = &Private->NamespaceInfo[DeviceIndex];
+ NamespaceInfo->NamespaceId = NamespaceId;
+ NamespaceInfo->NamespaceUuid = NamespaceData->Eui64;
+ NamespaceInfo->Controller = Private;
+ Private->ActiveNamespaceNum++;
+
+ //
+ // Build BlockIo media structure
+ //
+ Flbas = NamespaceData->Flbas;
+ LbaFmtIdx = Flbas & 0xF;
+ Lbads = NamespaceData->LbaFormat[LbaFmtIdx].Lbads;
+
+ NamespaceInfo->Media.InterfaceType = MSG_NVME_NAMESPACE_DP;
+ NamespaceInfo->Media.RemovableMedia = FALSE;
+ NamespaceInfo->Media.MediaPresent = TRUE;
+ NamespaceInfo->Media.ReadOnly = FALSE;
+ NamespaceInfo->Media.BlockSize = (UINT32) 1 << Lbads;
+ NamespaceInfo->Media.LastBlock = (EFI_PEI_LBA) NamespaceData->Nsze - 1;
+ DEBUG ((
+ DEBUG_INFO,
+ "%a: Namespace ID %d - BlockSize = 0x%x, LastBlock = 0x%lx\n",
+ __FUNCTION__,
+ NamespaceId,
+ NamespaceInfo->Media.BlockSize,
+ NamespaceInfo->Media.LastBlock
+ ));
+
+Exit:
+ if (NamespaceData != NULL) {
+ FreePool (NamespaceData);
+ }
+
+ return Status;
+}
+
+/**
+ Discover all Nvm Express device active namespaces.
+
+ @param[in,out] Private The pointer to the PEI_NVME_CONTROLLER_PRIVATE_DATA data structure.
+
+ @retval EFI_SUCCESS All the namespaces in the device are successfully enumerated.
+ @return EFI_NOT_FOUND No active namespaces can be found.
+
+**/
+EFI_STATUS
+NvmeDiscoverNamespaces (
+ IN OUT PEI_NVME_CONTROLLER_PRIVATE_DATA *Private
+ )
+{
+ UINT32 NamespaceId;
+
+ Private->ActiveNamespaceNum = 0;
+ Private->NamespaceInfo = AllocateZeroPool (Private->ControllerData->Nn * sizeof (PEI_NVME_NAMESPACE_INFO));
+
+ //
+ // According to Nvm Express 1.1 spec Figure 82, the field 'Nn' of the identify
+ // controller data defines the number of valid namespaces present for the
+ // controller. Namespaces shall be allocated in order (starting with 1) and
+ // packed sequentially.
+ //
+ for (NamespaceId = 1; NamespaceId <= Private->ControllerData->Nn; NamespaceId++) {
+ //
+ // For now, we do not care the return status. Since if a valid namespace is inactive,
+ // error status will be returned. But we continue to enumerate other valid namespaces.
+ //
+ EnumerateNvmeDevNamespace (Private, NamespaceId);
+ }
+ if (Private->ActiveNamespaceNum == 0) {
+ return EFI_NOT_FOUND;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ One notified function to cleanup the allocated resources at the end of PEI.
+
+ @param[in] PeiServices Pointer to PEI Services Table.
+ @param[in] NotifyDescriptor Pointer to the descriptor for the Notification
+ event that caused this function to execute.
+ @param[in] Ppi Pointer to the PPI data associated with this function.
+
+ @retval EFI_SUCCESS The function completes successfully
+
+**/
+EFI_STATUS
+EFIAPI
+NvmePeimEndOfPei (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor,
+ IN VOID *Ppi
+ )
+{
+ PEI_NVME_CONTROLLER_PRIVATE_DATA *Private;
+
+ Private = GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_NOTIFY (NotifyDescriptor);
+ NvmeFreeDmaResource (Private);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Entry point of the PEIM.
+
+ @param[in] FileHandle Handle of the file being invoked.
+ @param[in] PeiServices Describes the list of possible PEI Services.
+
+ @retval EFI_SUCCESS PPI successfully installed.
+
+**/
+EFI_STATUS
+EFIAPI
+NvmExpressPeimEntry (
+ IN EFI_PEI_FILE_HANDLE FileHandle,
+ IN CONST EFI_PEI_SERVICES **PeiServices
+ )
+{
+ EFI_STATUS Status;
+ EFI_BOOT_MODE BootMode;
+ EDKII_NVM_EXPRESS_HOST_CONTROLLER_PPI *NvmeHcPpi;
+ UINT8 Controller;
+ UINTN MmioBase;
+ UINTN DevicePathLength;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ PEI_NVME_CONTROLLER_PRIVATE_DATA *Private;
+ EFI_PHYSICAL_ADDRESS DeviceAddress;
+
+ DEBUG ((DEBUG_INFO, "%a: Enters.\n", __FUNCTION__));
+
+ //
+ // Get the current boot mode.
+ //
+ Status = PeiServicesGetBootMode (&BootMode);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: Fail to get the current boot mode.\n", __FUNCTION__));
+ return Status;
+ }
+
+ //
+ // Locate the NVME host controller PPI
+ //
+ Status = PeiServicesLocatePpi (
+ &gEdkiiPeiNvmExpressHostControllerPpiGuid,
+ 0,
+ NULL,
+ (VOID **) &NvmeHcPpi
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: Fail to locate NvmeHostControllerPpi.\n", __FUNCTION__));
+ return EFI_UNSUPPORTED;
+ }
+
+ Controller = 0;
+ MmioBase = 0;
+ while (TRUE) {
+ Status = NvmeHcPpi->GetNvmeHcMmioBar (
+ NvmeHcPpi,
+ Controller,
+ &MmioBase
+ );
+ //
+ // When status is error, meant no controller is found
+ //
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+
+ Status = NvmeHcPpi->GetNvmeHcDevicePath (
+ NvmeHcPpi,
+ Controller,
+ &DevicePathLength,
+ &DevicePath
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR, "%a: Fail to allocate get the device path for Controller %d.\n",
+ __FUNCTION__, Controller
+ ));
+ return Status;
+ }
+
+ //
+ // Check validity of the device path of the NVM Express controller.
+ //
+ Status = NvmeIsHcDevicePathValid (DevicePath, DevicePathLength);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR, "%a: The device path is invalid for Controller %d.\n",
+ __FUNCTION__, Controller
+ ));
+ Controller++;
+ continue;
+ }
+
+ //
+ // For S3 resume performance consideration, not all NVM Express controllers
+ // will be initialized. The driver consumes the content within
+ // S3StorageDeviceInitList LockBox to see if a controller will be skipped
+ // during S3 resume.
+ //
+ if ((BootMode == BOOT_ON_S3_RESUME) &&
+ (NvmeS3SkipThisController (DevicePath, DevicePathLength))) {
+ DEBUG ((
+ DEBUG_ERROR, "%a: Controller %d is skipped during S3.\n",
+ __FUNCTION__, Controller
+ ));
+ Controller++;
+ continue;
+ }
+
+ //
+ // Memory allocation for controller private data
+ //
+ Private = AllocateZeroPool (sizeof (PEI_NVME_CONTROLLER_PRIVATE_DATA));
+ if (Private == NULL) {
+ DEBUG ((
+ DEBUG_ERROR, "%a: Fail to allocate private data for Controller %d.\n",
+ __FUNCTION__, Controller
+ ));
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Memory allocation for transfer-related data
+ //
+ Status = IoMmuAllocateBuffer (
+ NVME_MEM_MAX_PAGES,
+ &Private->Buffer,
+ &DeviceAddress,
+ &Private->BufferMapping
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR, "%a: Fail to allocate DMA buffers for Controller %d.\n",
+ __FUNCTION__, Controller
+ ));
+ return Status;
+ }
+ ASSERT (DeviceAddress == ((EFI_PHYSICAL_ADDRESS) (UINTN) Private->Buffer));
+ DEBUG ((DEBUG_INFO, "%a: DMA buffer base at 0x%x\n", __FUNCTION__, Private->Buffer));
+
+ //
+ // Initialize controller private data
+ //
+ Private->Signature = NVME_PEI_CONTROLLER_PRIVATE_DATA_SIGNATURE;
+ Private->MmioBase = MmioBase;
+ Private->DevicePathLength = DevicePathLength;
+ Private->DevicePath = DevicePath;
+
+ //
+ // Initialize the NVME controller
+ //
+ Status = NvmeControllerInit (Private);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "%a: Controller initialization fail for Controller %d with Status - %r.\n",
+ __FUNCTION__, Controller, Status
+ ));
+ NvmeFreeDmaResource (Private);
+ Controller++;
+ continue;
+ }
+
+ //
+ // Enumerate the NVME namespaces on the controller
+ //
+ Status = NvmeDiscoverNamespaces (Private);
+ if (EFI_ERROR (Status)) {
+ //
+ // No active namespace was found on the controller
+ //
+ DEBUG ((
+ DEBUG_ERROR,
+ "%a: Namespaces discovery fail for Controller %d with Status - %r.\n",
+ __FUNCTION__, Controller, Status
+ ));
+ NvmeFreeDmaResource (Private);
+ Controller++;
+ continue;
+ }
+
+ //
+ // Nvm Express Pass Thru PPI
+ //
+ Private->PassThruMode.Attributes = EFI_NVM_EXPRESS_PASS_THRU_ATTRIBUTES_PHYSICAL |
+ EFI_NVM_EXPRESS_PASS_THRU_ATTRIBUTES_LOGICAL |
+ EFI_NVM_EXPRESS_PASS_THRU_ATTRIBUTES_CMD_SET_NVM;
+ Private->PassThruMode.IoAlign = sizeof (UINTN);
+ Private->PassThruMode.NvmeVersion = EDKII_PEI_NVM_EXPRESS_PASS_THRU_PPI_REVISION;
+ Private->NvmePassThruPpi.Mode = &Private->PassThruMode;
+ Private->NvmePassThruPpi.GetDevicePath = NvmePassThruGetDevicePath;
+ Private->NvmePassThruPpi.GetNextNameSpace = NvmePassThruGetNextNameSpace;
+ Private->NvmePassThruPpi.PassThru = NvmePassThru;
+ CopyMem (
+ &Private->NvmePassThruPpiList,
+ &mNvmePassThruPpiListTemplate,
+ sizeof (EFI_PEI_PPI_DESCRIPTOR)
+ );
+ Private->NvmePassThruPpiList.Ppi = &Private->NvmePassThruPpi;
+ PeiServicesInstallPpi (&Private->NvmePassThruPpiList);
+
+ //
+ // Block Io PPI
+ //
+ Private->BlkIoPpi.GetNumberOfBlockDevices = NvmeBlockIoPeimGetDeviceNo;
+ Private->BlkIoPpi.GetBlockDeviceMediaInfo = NvmeBlockIoPeimGetMediaInfo;
+ Private->BlkIoPpi.ReadBlocks = NvmeBlockIoPeimReadBlocks;
+ CopyMem (
+ &Private->BlkIoPpiList,
+ &mNvmeBlkIoPpiListTemplate,
+ sizeof (EFI_PEI_PPI_DESCRIPTOR)
+ );
+ Private->BlkIoPpiList.Ppi = &Private->BlkIoPpi;
+
+ Private->BlkIo2Ppi.Revision = EFI_PEI_RECOVERY_BLOCK_IO2_PPI_REVISION;
+ Private->BlkIo2Ppi.GetNumberOfBlockDevices = NvmeBlockIoPeimGetDeviceNo2;
+ Private->BlkIo2Ppi.GetBlockDeviceMediaInfo = NvmeBlockIoPeimGetMediaInfo2;
+ Private->BlkIo2Ppi.ReadBlocks = NvmeBlockIoPeimReadBlocks2;
+ CopyMem (
+ &Private->BlkIo2PpiList,
+ &mNvmeBlkIo2PpiListTemplate,
+ sizeof (EFI_PEI_PPI_DESCRIPTOR)
+ );
+ Private->BlkIo2PpiList.Ppi = &Private->BlkIo2Ppi;
+ PeiServicesInstallPpi (&Private->BlkIoPpiList);
+
+ //
+ // Check if the NVME controller supports the Security Receive/Send commands
+ //
+ if ((Private->ControllerData->Oacs & SECURITY_SEND_RECEIVE_SUPPORTED) != 0) {
+ DEBUG ((
+ DEBUG_INFO,
+ "%a: Security Security Command PPI will be produced for Controller %d.\n",
+ __FUNCTION__, Controller
+ ));
+ Private->StorageSecurityPpi.Revision = EDKII_STORAGE_SECURITY_PPI_REVISION;
+ Private->StorageSecurityPpi.GetNumberofDevices = NvmeStorageSecurityGetDeviceNo;
+ Private->StorageSecurityPpi.GetDevicePath = NvmeStorageSecurityGetDevicePath;
+ Private->StorageSecurityPpi.ReceiveData = NvmeStorageSecurityReceiveData;
+ Private->StorageSecurityPpi.SendData = NvmeStorageSecuritySendData;
+ CopyMem (
+ &Private->StorageSecurityPpiList,
+ &mNvmeStorageSecurityPpiListTemplate,
+ sizeof (EFI_PEI_PPI_DESCRIPTOR)
+ );
+ Private->StorageSecurityPpiList.Ppi = &Private->StorageSecurityPpi;
+ PeiServicesInstallPpi (&Private->StorageSecurityPpiList);
+ }
+
+ CopyMem (
+ &Private->EndOfPeiNotifyList,
+ &mNvmeEndOfPeiNotifyListTemplate,
+ sizeof (EFI_PEI_NOTIFY_DESCRIPTOR)
+ );
+ PeiServicesNotifyPpi (&Private->EndOfPeiNotifyList);
+
+ DEBUG ((
+ DEBUG_INFO, "%a: Controller %d has been successfully initialized.\n",
+ __FUNCTION__, Controller
+ ));
+ Controller++;
+ }
+
+ return EFI_SUCCESS;
+}
diff --git a/roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.h b/roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.h
new file mode 100644
index 000000000..8cd905191
--- /dev/null
+++ b/roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.h
@@ -0,0 +1,349 @@
+/** @file
+ The NvmExpressPei driver is used to manage non-volatile memory subsystem
+ which follows NVM Express specification at PEI phase.
+
+ Copyright (c) 2018 - 2019, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef _NVM_EXPRESS_PEI_H_
+#define _NVM_EXPRESS_PEI_H_
+
+#include <PiPei.h>
+
+#include <IndustryStandard/Nvme.h>
+
+#include <Ppi/NvmExpressHostController.h>
+#include <Ppi/BlockIo.h>
+#include <Ppi/BlockIo2.h>
+#include <Ppi/StorageSecurityCommand.h>
+#include <Ppi/NvmExpressPassThru.h>
+#include <Ppi/IoMmu.h>
+#include <Ppi/EndOfPeiPhase.h>
+
+#include <Library/DebugLib.h>
+#include <Library/PeiServicesLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/IoLib.h>
+#include <Library/TimerLib.h>
+
+//
+// Structure forward declarations
+//
+typedef struct _PEI_NVME_NAMESPACE_INFO PEI_NVME_NAMESPACE_INFO;
+typedef struct _PEI_NVME_CONTROLLER_PRIVATE_DATA PEI_NVME_CONTROLLER_PRIVATE_DATA;
+
+#include "NvmExpressPeiHci.h"
+#include "NvmExpressPeiPassThru.h"
+#include "NvmExpressPeiBlockIo.h"
+#include "NvmExpressPeiStorageSecurity.h"
+
+//
+// NVME PEI driver implementation related definitions
+//
+#define NVME_MAX_QUEUES 2 // Number of I/O queues supported by the driver, 1 for AQ, 1 for CQ
+#define NVME_ASQ_SIZE 1 // Number of admin submission queue entries, which is 0-based
+#define NVME_ACQ_SIZE 1 // Number of admin completion queue entries, which is 0-based
+#define NVME_CSQ_SIZE 63 // Number of I/O submission queue entries, which is 0-based
+#define NVME_CCQ_SIZE 63 // Number of I/O completion queue entries, which is 0-based
+#define NVME_PRP_SIZE (8) // Pages of PRP list
+
+#define NVME_MEM_MAX_PAGES \
+ ( \
+ 1 /* ASQ */ + \
+ 1 /* ACQ */ + \
+ 1 /* SQs */ + \
+ 1 /* CQs */ + \
+ NVME_PRP_SIZE) /* PRPs */
+
+#define NVME_ADMIN_QUEUE 0x00
+#define NVME_IO_QUEUE 0x01
+#define NVME_GENERIC_TIMEOUT 5000000 // Generic PassThru command timeout value, in us unit
+#define NVME_POLL_INTERVAL 100 // Poll interval for PassThru command, in us unit
+
+//
+// Nvme namespace data structure.
+//
+struct _PEI_NVME_NAMESPACE_INFO {
+ UINT32 NamespaceId;
+ UINT64 NamespaceUuid;
+ EFI_PEI_BLOCK_IO2_MEDIA Media;
+
+ PEI_NVME_CONTROLLER_PRIVATE_DATA *Controller;
+};
+
+#define NVME_CONTROLLER_NSID 0
+
+//
+// Unique signature for private data structure.
+//
+#define NVME_PEI_CONTROLLER_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('N','V','P','C')
+
+//
+// Nvme controller private data structure.
+//
+struct _PEI_NVME_CONTROLLER_PRIVATE_DATA {
+ UINT32 Signature;
+ UINTN MmioBase;
+ EFI_NVM_EXPRESS_PASS_THRU_MODE PassThruMode;
+ UINTN DevicePathLength;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+
+ EFI_PEI_RECOVERY_BLOCK_IO_PPI BlkIoPpi;
+ EFI_PEI_RECOVERY_BLOCK_IO2_PPI BlkIo2Ppi;
+ EDKII_PEI_STORAGE_SECURITY_CMD_PPI StorageSecurityPpi;
+ EDKII_PEI_NVM_EXPRESS_PASS_THRU_PPI NvmePassThruPpi;
+ EFI_PEI_PPI_DESCRIPTOR BlkIoPpiList;
+ EFI_PEI_PPI_DESCRIPTOR BlkIo2PpiList;
+ EFI_PEI_PPI_DESCRIPTOR StorageSecurityPpiList;
+ EFI_PEI_PPI_DESCRIPTOR NvmePassThruPpiList;
+ EFI_PEI_NOTIFY_DESCRIPTOR EndOfPeiNotifyList;
+
+ //
+ // Pointer to identify controller data
+ //
+ NVME_ADMIN_CONTROLLER_DATA *ControllerData;
+
+ //
+ // (4 + NVME_PRP_SIZE) x 4kB aligned buffers will be carved out of this buffer
+ // 1st 4kB boundary is the start of the admin submission queue
+ // 2nd 4kB boundary is the start of the admin completion queue
+ // 3rd 4kB boundary is the start of I/O submission queue
+ // 4th 4kB boundary is the start of I/O completion queue
+ // 5th 4kB boundary is the start of PRP list buffers
+ //
+ VOID *Buffer;
+ VOID *BufferMapping;
+
+ //
+ // Pointers to 4kB aligned submission & completion queues
+ //
+ NVME_SQ *SqBuffer[NVME_MAX_QUEUES];
+ NVME_CQ *CqBuffer[NVME_MAX_QUEUES];
+
+ //
+ // Submission and completion queue indices
+ //
+ NVME_SQTDBL SqTdbl[NVME_MAX_QUEUES];
+ NVME_CQHDBL CqHdbl[NVME_MAX_QUEUES];
+
+ UINT8 Pt[NVME_MAX_QUEUES];
+ UINT16 Cid[NVME_MAX_QUEUES];
+
+ //
+ // Nvme controller capabilities
+ //
+ NVME_CAP Cap;
+
+ //
+ // Namespaces information on the controller
+ //
+ UINT32 ActiveNamespaceNum;
+ PEI_NVME_NAMESPACE_INFO *NamespaceInfo;
+};
+
+#define GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_BLKIO(a) \
+ CR (a, PEI_NVME_CONTROLLER_PRIVATE_DATA, BlkIoPpi, NVME_PEI_CONTROLLER_PRIVATE_DATA_SIGNATURE)
+#define GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_BLKIO2(a) \
+ CR (a, PEI_NVME_CONTROLLER_PRIVATE_DATA, BlkIo2Ppi, NVME_PEI_CONTROLLER_PRIVATE_DATA_SIGNATURE)
+#define GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_STROAGE_SECURITY(a) \
+ CR (a, PEI_NVME_CONTROLLER_PRIVATE_DATA, StorageSecurityPpi, NVME_PEI_CONTROLLER_PRIVATE_DATA_SIGNATURE)
+#define GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_NVME_PASSTHRU(a) \
+ CR (a, PEI_NVME_CONTROLLER_PRIVATE_DATA, NvmePassThruPpi, NVME_PEI_CONTROLLER_PRIVATE_DATA_SIGNATURE)
+#define GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_NOTIFY(a) \
+ CR (a, PEI_NVME_CONTROLLER_PRIVATE_DATA, EndOfPeiNotifyList, NVME_PEI_CONTROLLER_PRIVATE_DATA_SIGNATURE)
+
+
+//
+// Internal functions
+//
+
+/**
+ Allocates pages that are suitable for an OperationBusMasterCommonBuffer or
+ OperationBusMasterCommonBuffer64 mapping.
+
+ @param Pages The number of pages to allocate.
+ @param HostAddress A pointer to store the base system memory address of the
+ allocated range.
+ @param DeviceAddress The resulting map address for the bus master PCI controller to use to
+ access the hosts HostAddress.
+ @param Mapping A resulting value to pass to Unmap().
+
+ @retval EFI_SUCCESS The requested memory pages were allocated.
+ @retval EFI_UNSUPPORTED Attributes is unsupported. The only legal attribute bits are
+ MEMORY_WRITE_COMBINE and MEMORY_CACHED.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated.
+
+**/
+EFI_STATUS
+IoMmuAllocateBuffer (
+ IN UINTN Pages,
+ OUT VOID **HostAddress,
+ OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,
+ OUT VOID **Mapping
+ );
+
+/**
+ Frees memory that was allocated with AllocateBuffer().
+
+ @param Pages The number of pages to free.
+ @param HostAddress The base system memory address of the allocated range.
+ @param Mapping The mapping value returned from Map().
+
+ @retval EFI_SUCCESS The requested memory pages were freed.
+ @retval EFI_INVALID_PARAMETER The memory range specified by HostAddress and Pages
+ was not allocated with AllocateBuffer().
+
+**/
+EFI_STATUS
+IoMmuFreeBuffer (
+ IN UINTN Pages,
+ IN VOID *HostAddress,
+ IN VOID *Mapping
+ );
+
+/**
+ Provides the controller-specific addresses required to access system memory from a
+ DMA bus master.
+
+ @param Operation Indicates if the bus master is going to read or write to system memory.
+ @param HostAddress The system memory address to map to the PCI controller.
+ @param NumberOfBytes On input the number of bytes to map. On output the number of bytes
+ that were mapped.
+ @param DeviceAddress The resulting map address for the bus master PCI controller to use to
+ access the hosts HostAddress.
+ @param Mapping A resulting value to pass to Unmap().
+
+ @retval EFI_SUCCESS The range was mapped for the returned NumberOfBytes.
+ @retval EFI_UNSUPPORTED The HostAddress cannot be mapped as a common buffer.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.
+ @retval EFI_DEVICE_ERROR The system hardware could not map the requested address.
+
+**/
+EFI_STATUS
+IoMmuMap (
+ IN EDKII_IOMMU_OPERATION Operation,
+ IN VOID *HostAddress,
+ IN OUT UINTN *NumberOfBytes,
+ OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,
+ OUT VOID **Mapping
+ );
+
+/**
+ Completes the Map() operation and releases any corresponding resources.
+
+ @param Mapping The mapping value returned from Map().
+
+ @retval EFI_SUCCESS The range was unmapped.
+ @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by Map().
+ @retval EFI_DEVICE_ERROR The data was not committed to the target system memory.
+**/
+EFI_STATUS
+IoMmuUnmap (
+ IN VOID *Mapping
+ );
+
+/**
+ One notified function to cleanup the allocated resources at the end of PEI.
+
+ @param[in] PeiServices Pointer to PEI Services Table.
+ @param[in] NotifyDescriptor Pointer to the descriptor for the Notification
+ event that caused this function to execute.
+ @param[in] Ppi Pointer to the PPI data associated with this function.
+
+ @retval EFI_SUCCESS The function completes successfully
+
+**/
+EFI_STATUS
+EFIAPI
+NvmePeimEndOfPei (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor,
+ IN VOID *Ppi
+ );
+
+/**
+ Get the size of the current device path instance.
+
+ @param[in] DevicePath A pointer to the EFI_DEVICE_PATH_PROTOCOL
+ structure.
+ @param[out] InstanceSize The size of the current device path instance.
+ @param[out] EntireDevicePathEnd Indicate whether the instance is the last
+ one in the device path strucure.
+
+ @retval EFI_SUCCESS The size of the current device path instance is fetched.
+ @retval Others Fails to get the size of the current device path instance.
+
+**/
+EFI_STATUS
+GetDevicePathInstanceSize (
+ IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
+ OUT UINTN *InstanceSize,
+ OUT BOOLEAN *EntireDevicePathEnd
+ );
+
+/**
+ Check the validity of the device path of a NVM Express host controller.
+
+ @param[in] DevicePath A pointer to the EFI_DEVICE_PATH_PROTOCOL
+ structure.
+ @param[in] DevicePathLength The length of the device path.
+
+ @retval EFI_SUCCESS The device path is valid.
+ @retval EFI_INVALID_PARAMETER The device path is invalid.
+
+**/
+EFI_STATUS
+NvmeIsHcDevicePathValid (
+ IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
+ IN UINTN DevicePathLength
+ );
+
+/**
+ Build the device path for an Nvm Express device with given namespace identifier
+ and namespace extended unique identifier.
+
+ @param[in] Private A pointer to the PEI_NVME_CONTROLLER_PRIVATE_DATA
+ data structure.
+ @param[in] NamespaceId The given namespace identifier.
+ @param[in] NamespaceUuid The given namespace extended unique identifier.
+ @param[out] DevicePathLength The length of the device path in bytes specified
+ by DevicePath.
+ @param[out] DevicePath The device path of Nvm Express device.
+
+ @retval EFI_SUCCESS The operation succeeds.
+ @retval EFI_INVALID_PARAMETER The parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES The operation fails due to lack of resources.
+
+**/
+EFI_STATUS
+NvmeBuildDevicePath (
+ IN PEI_NVME_CONTROLLER_PRIVATE_DATA *Private,
+ IN UINT32 NamespaceId,
+ IN UINT64 NamespaceUuid,
+ OUT UINTN *DevicePathLength,
+ OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath
+ );
+
+/**
+ Determine if a specific NVM Express controller can be skipped for S3 phase.
+
+ @param[in] HcDevicePath Device path of the controller.
+ @param[in] HcDevicePathLength Length of the device path specified by
+ HcDevicePath.
+
+ @retval The number of ports that need to be enumerated.
+
+**/
+BOOLEAN
+NvmeS3SkipThisController (
+ IN EFI_DEVICE_PATH_PROTOCOL *HcDevicePath,
+ IN UINTN HcDevicePathLength
+ );
+
+#endif
diff --git a/roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.inf b/roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.inf
new file mode 100644
index 000000000..4dd6c5704
--- /dev/null
+++ b/roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.inf
@@ -0,0 +1,73 @@
+## @file
+# The NvmExpressPei driver is used to manage non-volatile memory subsystem
+# which follows NVM Express specification at PEI phase.
+#
+# Copyright (c) 2018 - 2019, Intel Corporation. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = NvmExpressPei
+ MODULE_UNI_FILE = NvmExpressPei.uni
+ FILE_GUID = 94813714-E10A-4798-9909-8C904F66B4D9
+ MODULE_TYPE = PEIM
+ VERSION_STRING = 1.0
+ ENTRY_POINT = NvmExpressPeimEntry
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 EBC
+#
+
+[Sources]
+ DevicePath.c
+ DmaMem.c
+ NvmExpressPei.c
+ NvmExpressPei.h
+ NvmExpressPeiBlockIo.c
+ NvmExpressPeiBlockIo.h
+ NvmExpressPeiHci.c
+ NvmExpressPeiHci.h
+ NvmExpressPeiPassThru.c
+ NvmExpressPeiPassThru.h
+ NvmExpressPeiS3.c
+ NvmExpressPeiStorageSecurity.c
+ NvmExpressPeiStorageSecurity.h
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+[LibraryClasses]
+ DebugLib
+ PeiServicesLib
+ MemoryAllocationLib
+ BaseMemoryLib
+ IoLib
+ TimerLib
+ LockBoxLib
+ PeimEntryPoint
+
+[Ppis]
+ gEdkiiPeiNvmExpressHostControllerPpiGuid ## CONSUMES
+ gEdkiiIoMmuPpiGuid ## CONSUMES
+ gEfiEndOfPeiSignalPpiGuid ## CONSUMES
+ gEdkiiPeiNvmExpressPassThruPpiGuid ## SOMETIMES_PRODUCES
+ gEfiPeiVirtualBlockIoPpiGuid ## SOMETIMES_PRODUCES
+ gEfiPeiVirtualBlockIo2PpiGuid ## SOMETIMES_PRODUCES
+ gEdkiiPeiStorageSecurityCommandPpiGuid ## SOMETIMES_PRODUCES
+
+[Guids]
+ gS3StorageDeviceInitListGuid ## SOMETIMES_CONSUMES ## UNDEFINED
+
+[Depex]
+ gEfiPeiMemoryDiscoveredPpiGuid AND
+ gEdkiiPeiNvmExpressHostControllerPpiGuid AND
+ gEfiPeiMasterBootModePpiGuid
+
+[UserExtensions.TianoCore."ExtraFiles"]
+ NvmExpressPeiExtra.uni
diff --git a/roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.uni b/roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.uni
new file mode 100644
index 000000000..e1e98ba83
--- /dev/null
+++ b/roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.uni
@@ -0,0 +1,14 @@
+// /** @file
+// The NvmExpressPei driver is used to manage non-volatile memory subsystem
+// which follows NVM Express specification at PEI phase.
+//
+// Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Manage non-volatile memory subsystem at PEI phase"
+
+#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."
diff --git a/roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiBlockIo.c b/roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiBlockIo.c
new file mode 100644
index 000000000..a9bf4f819
--- /dev/null
+++ b/roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiBlockIo.c
@@ -0,0 +1,523 @@
+/** @file
+ The NvmExpressPei driver is used to manage non-volatile memory subsystem
+ which follows NVM Express specification at PEI phase.
+
+ Copyright (c) 2018 - 2019, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "NvmExpressPei.h"
+
+/**
+ Read some sectors from the device.
+
+ @param NamespaceInfo The pointer to the PEI_NVME_NAMESPACE_INFO data structure.
+ @param Buffer The buffer used to store the data read from the device.
+ @param Lba The start block number.
+ @param Blocks Total block number to be read.
+
+ @retval EFI_SUCCESS Data are read from the device.
+ @retval Others Fail to read all the data.
+
+**/
+EFI_STATUS
+ReadSectors (
+ IN PEI_NVME_NAMESPACE_INFO *NamespaceInfo,
+ OUT UINTN Buffer,
+ IN UINT64 Lba,
+ IN UINT32 Blocks
+ )
+{
+ EFI_STATUS Status;
+ UINT32 BlockSize;
+ PEI_NVME_CONTROLLER_PRIVATE_DATA *Private;
+ UINT32 Bytes;
+ EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket;
+ EFI_NVM_EXPRESS_COMMAND Command;
+ EFI_NVM_EXPRESS_COMPLETION Completion;
+ EDKII_PEI_NVM_EXPRESS_PASS_THRU_PPI *NvmePassThru;
+
+ Private = NamespaceInfo->Controller;
+ NvmePassThru = &Private->NvmePassThruPpi;
+ BlockSize = NamespaceInfo->Media.BlockSize;
+ Bytes = Blocks * BlockSize;
+
+ ZeroMem (&CommandPacket, sizeof(EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));
+ ZeroMem (&Command, sizeof(EFI_NVM_EXPRESS_COMMAND));
+ ZeroMem (&Completion, sizeof(EFI_NVM_EXPRESS_COMPLETION));
+
+ CommandPacket.NvmeCmd = &Command;
+ CommandPacket.NvmeCompletion = &Completion;
+
+ CommandPacket.NvmeCmd->Cdw0.Opcode = NVME_IO_READ_OPC;
+ CommandPacket.NvmeCmd->Nsid = NamespaceInfo->NamespaceId;
+ CommandPacket.TransferBuffer = (VOID *)Buffer;
+
+ CommandPacket.TransferLength = Bytes;
+ CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;
+ CommandPacket.QueueType = NVME_IO_QUEUE;
+
+ CommandPacket.NvmeCmd->Cdw10 = (UINT32)Lba;
+ CommandPacket.NvmeCmd->Cdw11 = (UINT32)RShiftU64(Lba, 32);
+ CommandPacket.NvmeCmd->Cdw12 = (Blocks - 1) & 0xFFFF;
+
+ CommandPacket.NvmeCmd->Flags = CDW10_VALID | CDW11_VALID | CDW12_VALID;
+
+ Status = NvmePassThru->PassThru (
+ NvmePassThru,
+ NamespaceInfo->NamespaceId,
+ &CommandPacket
+ );
+
+ return Status;
+}
+
+/**
+ Read some blocks from the device.
+
+ @param[in] NamespaceInfo The pointer to the PEI_NVME_NAMESPACE_INFO data structure.
+ @param[out] Buffer The Buffer used to store the Data read from the device.
+ @param[in] Lba The start block number.
+ @param[in] Blocks Total block number to be read.
+
+ @retval EFI_SUCCESS Data are read from the device.
+ @retval Others Fail to read all the data.
+
+**/
+EFI_STATUS
+NvmeRead (
+ IN PEI_NVME_NAMESPACE_INFO *NamespaceInfo,
+ OUT UINTN Buffer,
+ IN UINT64 Lba,
+ IN UINTN Blocks
+ )
+{
+ EFI_STATUS Status;
+ UINT32 Retries;
+ UINT32 BlockSize;
+ PEI_NVME_CONTROLLER_PRIVATE_DATA *Private;
+ UINT32 MaxTransferBlocks;
+ UINTN OrginalBlocks;
+
+ Status = EFI_SUCCESS;
+ Retries = 0;
+ Private = NamespaceInfo->Controller;
+ BlockSize = NamespaceInfo->Media.BlockSize;
+ OrginalBlocks = Blocks;
+
+ if (Private->ControllerData->Mdts != 0) {
+ MaxTransferBlocks = (1 << (Private->ControllerData->Mdts)) * (1 << (Private->Cap.Mpsmin + 12)) / BlockSize;
+ } else {
+ MaxTransferBlocks = 1024;
+ }
+
+ while (Blocks > 0) {
+ Status = ReadSectors (
+ NamespaceInfo,
+ Buffer,
+ Lba,
+ Blocks > MaxTransferBlocks ? MaxTransferBlocks : (UINT32)Blocks
+ );
+ if (EFI_ERROR(Status)) {
+ Retries++;
+ MaxTransferBlocks = MaxTransferBlocks >> 1;
+
+ if (Retries > NVME_READ_MAX_RETRY || MaxTransferBlocks < 1) {
+ DEBUG ((DEBUG_ERROR, "%a: ReadSectors fail, Status - %r\n", __FUNCTION__, Status));
+ break;
+ }
+ DEBUG ((
+ DEBUG_BLKIO,
+ "%a: ReadSectors fail, retry with smaller transfer block number - 0x%x\n",
+ __FUNCTION__,
+ MaxTransferBlocks
+ ));
+ continue;
+ }
+
+ if (Blocks > MaxTransferBlocks) {
+ Blocks -= MaxTransferBlocks;
+ Buffer += (MaxTransferBlocks * BlockSize);
+ Lba += MaxTransferBlocks;
+ } else {
+ Blocks = 0;
+ }
+ }
+
+ DEBUG ((DEBUG_BLKIO, "%a: Lba = 0x%08Lx, Original = 0x%08Lx, "
+ "Remaining = 0x%08Lx, BlockSize = 0x%x, Status = %r\n", __FUNCTION__, Lba,
+ (UINT64)OrginalBlocks, (UINT64)Blocks, BlockSize, Status));
+ return Status;
+}
+
+/**
+ Gets the count of block I/O devices that one specific block driver detects.
+
+ This function is used for getting the count of block I/O devices that one
+ specific block driver detects. If no device is detected, then the function
+ will return zero.
+
+ @param[in] PeiServices General-purpose services that are available
+ to every PEIM.
+ @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI
+ instance.
+ @param[out] NumberBlockDevices The number of block I/O devices discovered.
+
+ @retval EFI_SUCCESS The operation performed successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+NvmeBlockIoPeimGetDeviceNo (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This,
+ OUT UINTN *NumberBlockDevices
+ )
+{
+ PEI_NVME_CONTROLLER_PRIVATE_DATA *Private;
+
+ if (This == NULL || NumberBlockDevices == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_BLKIO (This);
+ *NumberBlockDevices = Private->ActiveNamespaceNum;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Gets a block device's media information.
+
+ This function will provide the caller with the specified block device's media
+ information. If the media changes, calling this function will update the media
+ information accordingly.
+
+ @param[in] PeiServices General-purpose services that are available to every
+ PEIM
+ @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance.
+ @param[in] DeviceIndex Specifies the block device to which the function wants
+ to talk. Because the driver that implements Block I/O
+ PPIs will manage multiple block devices, the PPIs that
+ want to talk to a single device must specify the
+ device index that was assigned during the enumeration
+ process. This index is a number from one to
+ NumberBlockDevices.
+ @param[out] MediaInfo The media information of the specified block media.
+ The caller is responsible for the ownership of this
+ data structure.
+
+ @par Note:
+ The MediaInfo structure describes an enumeration of possible block device
+ types. This enumeration exists because no device paths are actually passed
+ across interfaces that describe the type or class of hardware that is publishing
+ the block I/O interface. This enumeration will allow for policy decisions
+ in the Recovery PEIM, such as "Try to recover from legacy floppy first,
+ LS-120 second, CD-ROM third." If there are multiple partitions abstracted
+ by a given device type, they should be reported in ascending order; this
+ order also applies to nested partitions, such as legacy MBR, where the
+ outermost partitions would have precedence in the reporting order. The
+ same logic applies to systems such as IDE that have precedence relationships
+ like "Master/Slave" or "Primary/Secondary". The master device should be
+ reported first, the slave second.
+
+ @retval EFI_SUCCESS Media information about the specified block device
+ was obtained successfully.
+ @retval EFI_DEVICE_ERROR Cannot get the media information due to a hardware
+ error.
+
+**/
+EFI_STATUS
+EFIAPI
+NvmeBlockIoPeimGetMediaInfo (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This,
+ IN UINTN DeviceIndex,
+ OUT EFI_PEI_BLOCK_IO_MEDIA *MediaInfo
+ )
+{
+ PEI_NVME_CONTROLLER_PRIVATE_DATA *Private;
+
+ if (This == NULL || MediaInfo == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_BLKIO (This);
+
+ if ((DeviceIndex == 0) || (DeviceIndex > Private->ActiveNamespaceNum)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ MediaInfo->DeviceType = (EFI_PEI_BLOCK_DEVICE_TYPE) EDKII_PEI_BLOCK_DEVICE_TYPE_NVME;
+ MediaInfo->MediaPresent = TRUE;
+ MediaInfo->LastBlock = (UINTN)Private->NamespaceInfo[DeviceIndex-1].Media.LastBlock;
+ MediaInfo->BlockSize = Private->NamespaceInfo[DeviceIndex-1].Media.BlockSize;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Reads the requested number of blocks from the specified block device.
+
+ The function reads the requested number of blocks from the device. All the
+ blocks are read, or an error is returned. If there is no media in the device,
+ the function returns EFI_NO_MEDIA.
+
+ @param[in] PeiServices General-purpose services that are available to
+ every PEIM.
+ @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance.
+ @param[in] DeviceIndex Specifies the block device to which the function wants
+ to talk. Because the driver that implements Block I/O
+ PPIs will manage multiple block devices, PPIs that
+ want to talk to a single device must specify the device
+ index that was assigned during the enumeration process.
+ This index is a number from one to NumberBlockDevices.
+ @param[in] StartLBA The starting logical block address (LBA) to read from
+ on the device
+ @param[in] BufferSize The size of the Buffer in bytes. This number must be
+ a multiple of the intrinsic block size of the device.
+ @param[out] Buffer A pointer to the destination buffer for the data.
+ The caller is responsible for the ownership of the
+ buffer.
+
+ @retval EFI_SUCCESS The data was read correctly from the device.
+ @retval EFI_DEVICE_ERROR The device reported an error while attempting
+ to perform the read operation.
+ @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not
+ valid, or the buffer is not properly aligned.
+ @retval EFI_NO_MEDIA There is no media in the device.
+ @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of
+ the intrinsic block size of the device.
+
+**/
+EFI_STATUS
+EFIAPI
+NvmeBlockIoPeimReadBlocks (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This,
+ IN UINTN DeviceIndex,
+ IN EFI_PEI_LBA StartLBA,
+ IN UINTN BufferSize,
+ OUT VOID *Buffer
+ )
+{
+ PEI_NVME_CONTROLLER_PRIVATE_DATA *Private;
+ PEI_NVME_NAMESPACE_INFO *NamespaceInfo;
+ UINT32 BlockSize;
+ UINTN NumberOfBlocks;
+
+ Private = GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_BLKIO (This);
+
+ //
+ // Check parameters
+ //
+ if (This == NULL || Buffer == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (BufferSize == 0) {
+ return EFI_SUCCESS;
+ }
+
+ if ((DeviceIndex == 0) || (DeviceIndex > Private->ActiveNamespaceNum)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Check BufferSize and StartLBA
+ //
+ NamespaceInfo = &(Private->NamespaceInfo[DeviceIndex - 1]);
+ BlockSize = NamespaceInfo->Media.BlockSize;
+ if (BufferSize % BlockSize != 0) {
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+ if (StartLBA > NamespaceInfo->Media.LastBlock) {
+ return EFI_INVALID_PARAMETER;
+ }
+ NumberOfBlocks = BufferSize / BlockSize;
+ if (NumberOfBlocks - 1 > NamespaceInfo->Media.LastBlock - StartLBA) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ return NvmeRead (NamespaceInfo, (UINTN)Buffer, StartLBA, NumberOfBlocks);
+}
+
+/**
+ Gets the count of block I/O devices that one specific block driver detects.
+
+ This function is used for getting the count of block I/O devices that one
+ specific block driver detects. If no device is detected, then the function
+ will return zero.
+
+ @param[in] PeiServices General-purpose services that are available
+ to every PEIM.
+ @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO2_PPI
+ instance.
+ @param[out] NumberBlockDevices The number of block I/O devices discovered.
+
+ @retval EFI_SUCCESS The operation performed successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+NvmeBlockIoPeimGetDeviceNo2 (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN EFI_PEI_RECOVERY_BLOCK_IO2_PPI *This,
+ OUT UINTN *NumberBlockDevices
+ )
+{
+ PEI_NVME_CONTROLLER_PRIVATE_DATA *Private;
+
+ if (This == NULL || NumberBlockDevices == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_BLKIO2 (This);
+ *NumberBlockDevices = Private->ActiveNamespaceNum;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Gets a block device's media information.
+
+ This function will provide the caller with the specified block device's media
+ information. If the media changes, calling this function will update the media
+ information accordingly.
+
+ @param[in] PeiServices General-purpose services that are available to every
+ PEIM
+ @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO2_PPI instance.
+ @param[in] DeviceIndex Specifies the block device to which the function wants
+ to talk. Because the driver that implements Block I/O
+ PPIs will manage multiple block devices, the PPIs that
+ want to talk to a single device must specify the
+ device index that was assigned during the enumeration
+ process. This index is a number from one to
+ NumberBlockDevices.
+ @param[out] MediaInfo The media information of the specified block media.
+ The caller is responsible for the ownership of this
+ data structure.
+
+ @par Note:
+ The MediaInfo structure describes an enumeration of possible block device
+ types. This enumeration exists because no device paths are actually passed
+ across interfaces that describe the type or class of hardware that is publishing
+ the block I/O interface. This enumeration will allow for policy decisions
+ in the Recovery PEIM, such as "Try to recover from legacy floppy first,
+ LS-120 second, CD-ROM third." If there are multiple partitions abstracted
+ by a given device type, they should be reported in ascending order; this
+ order also applies to nested partitions, such as legacy MBR, where the
+ outermost partitions would have precedence in the reporting order. The
+ same logic applies to systems such as IDE that have precedence relationships
+ like "Master/Slave" or "Primary/Secondary". The master device should be
+ reported first, the slave second.
+
+ @retval EFI_SUCCESS Media information about the specified block device
+ was obtained successfully.
+ @retval EFI_DEVICE_ERROR Cannot get the media information due to a hardware
+ error.
+
+**/
+EFI_STATUS
+EFIAPI
+NvmeBlockIoPeimGetMediaInfo2 (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN EFI_PEI_RECOVERY_BLOCK_IO2_PPI *This,
+ IN UINTN DeviceIndex,
+ OUT EFI_PEI_BLOCK_IO2_MEDIA *MediaInfo
+ )
+{
+ EFI_STATUS Status;
+ PEI_NVME_CONTROLLER_PRIVATE_DATA *Private;
+ EFI_PEI_BLOCK_IO_MEDIA Media;
+
+ if (This == NULL || MediaInfo == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_BLKIO2 (This);
+
+ Status = NvmeBlockIoPeimGetMediaInfo (
+ PeiServices,
+ &Private->BlkIoPpi,
+ DeviceIndex,
+ &Media
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ CopyMem (
+ MediaInfo,
+ &(Private->NamespaceInfo[DeviceIndex - 1].Media),
+ sizeof (EFI_PEI_BLOCK_IO2_MEDIA)
+ );
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Reads the requested number of blocks from the specified block device.
+
+ The function reads the requested number of blocks from the device. All the
+ blocks are read, or an error is returned. If there is no media in the device,
+ the function returns EFI_NO_MEDIA.
+
+ @param[in] PeiServices General-purpose services that are available to
+ every PEIM.
+ @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO2_PPI instance.
+ @param[in] DeviceIndex Specifies the block device to which the function wants
+ to talk. Because the driver that implements Block I/O
+ PPIs will manage multiple block devices, PPIs that
+ want to talk to a single device must specify the device
+ index that was assigned during the enumeration process.
+ This index is a number from one to NumberBlockDevices.
+ @param[in] StartLBA The starting logical block address (LBA) to read from
+ on the device
+ @param[in] BufferSize The size of the Buffer in bytes. This number must be
+ a multiple of the intrinsic block size of the device.
+ @param[out] Buffer A pointer to the destination buffer for the data.
+ The caller is responsible for the ownership of the
+ buffer.
+
+ @retval EFI_SUCCESS The data was read correctly from the device.
+ @retval EFI_DEVICE_ERROR The device reported an error while attempting
+ to perform the read operation.
+ @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not
+ valid, or the buffer is not properly aligned.
+ @retval EFI_NO_MEDIA There is no media in the device.
+ @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of
+ the intrinsic block size of the device.
+
+**/
+EFI_STATUS
+EFIAPI
+NvmeBlockIoPeimReadBlocks2 (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN EFI_PEI_RECOVERY_BLOCK_IO2_PPI *This,
+ IN UINTN DeviceIndex,
+ IN EFI_PEI_LBA StartLBA,
+ IN UINTN BufferSize,
+ OUT VOID *Buffer
+ )
+{
+ PEI_NVME_CONTROLLER_PRIVATE_DATA *Private;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_BLKIO2 (This);
+ return NvmeBlockIoPeimReadBlocks (
+ PeiServices,
+ &Private->BlkIoPpi,
+ DeviceIndex,
+ StartLBA,
+ BufferSize,
+ Buffer
+ );
+}
diff --git a/roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiBlockIo.h b/roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiBlockIo.h
new file mode 100644
index 000000000..2c7065cb7
--- /dev/null
+++ b/roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiBlockIo.h
@@ -0,0 +1,259 @@
+/** @file
+ The NvmExpressPei driver is used to manage non-volatile memory subsystem
+ which follows NVM Express specification at PEI phase.
+
+ Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef _NVM_EXPRESS_PEI_BLOCKIO_H_
+#define _NVM_EXPRESS_PEI_BLOCKIO_H_
+
+//
+// Nvme device for EFI_PEI_BLOCK_DEVICE_TYPE
+//
+#define EDKII_PEI_BLOCK_DEVICE_TYPE_NVME 7
+
+#define NVME_READ_MAX_RETRY 3
+
+/**
+ Gets the count of block I/O devices that one specific block driver detects.
+
+ This function is used for getting the count of block I/O devices that one
+ specific block driver detects. If no device is detected, then the function
+ will return zero.
+
+ @param[in] PeiServices General-purpose services that are available
+ to every PEIM.
+ @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI
+ instance.
+ @param[out] NumberBlockDevices The number of block I/O devices discovered.
+
+ @retval EFI_SUCCESS The operation performed successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+NvmeBlockIoPeimGetDeviceNo (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This,
+ OUT UINTN *NumberBlockDevices
+ );
+
+/**
+ Gets a block device's media information.
+
+ This function will provide the caller with the specified block device's media
+ information. If the media changes, calling this function will update the media
+ information accordingly.
+
+ @param[in] PeiServices General-purpose services that are available to every
+ PEIM
+ @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance.
+ @param[in] DeviceIndex Specifies the block device to which the function wants
+ to talk. Because the driver that implements Block I/O
+ PPIs will manage multiple block devices, the PPIs that
+ want to talk to a single device must specify the
+ device index that was assigned during the enumeration
+ process. This index is a number from one to
+ NumberBlockDevices.
+ @param[out] MediaInfo The media information of the specified block media.
+ The caller is responsible for the ownership of this
+ data structure.
+
+ @par Note:
+ The MediaInfo structure describes an enumeration of possible block device
+ types. This enumeration exists because no device paths are actually passed
+ across interfaces that describe the type or class of hardware that is publishing
+ the block I/O interface. This enumeration will allow for policy decisions
+ in the Recovery PEIM, such as "Try to recover from legacy floppy first,
+ LS-120 second, CD-ROM third." If there are multiple partitions abstracted
+ by a given device type, they should be reported in ascending order; this
+ order also applies to nested partitions, such as legacy MBR, where the
+ outermost partitions would have precedence in the reporting order. The
+ same logic applies to systems such as IDE that have precedence relationships
+ like "Master/Slave" or "Primary/Secondary". The master device should be
+ reported first, the slave second.
+
+ @retval EFI_SUCCESS Media information about the specified block device
+ was obtained successfully.
+ @retval EFI_DEVICE_ERROR Cannot get the media information due to a hardware
+ error.
+
+**/
+EFI_STATUS
+EFIAPI
+NvmeBlockIoPeimGetMediaInfo (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This,
+ IN UINTN DeviceIndex,
+ OUT EFI_PEI_BLOCK_IO_MEDIA *MediaInfo
+ );
+
+/**
+ Reads the requested number of blocks from the specified block device.
+
+ The function reads the requested number of blocks from the device. All the
+ blocks are read, or an error is returned. If there is no media in the device,
+ the function returns EFI_NO_MEDIA.
+
+ @param[in] PeiServices General-purpose services that are available to
+ every PEIM.
+ @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance.
+ @param[in] DeviceIndex Specifies the block device to which the function wants
+ to talk. Because the driver that implements Block I/O
+ PPIs will manage multiple block devices, PPIs that
+ want to talk to a single device must specify the device
+ index that was assigned during the enumeration process.
+ This index is a number from one to NumberBlockDevices.
+ @param[in] StartLBA The starting logical block address (LBA) to read from
+ on the device
+ @param[in] BufferSize The size of the Buffer in bytes. This number must be
+ a multiple of the intrinsic block size of the device.
+ @param[out] Buffer A pointer to the destination buffer for the data.
+ The caller is responsible for the ownership of the
+ buffer.
+
+ @retval EFI_SUCCESS The data was read correctly from the device.
+ @retval EFI_DEVICE_ERROR The device reported an error while attempting
+ to perform the read operation.
+ @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not
+ valid, or the buffer is not properly aligned.
+ @retval EFI_NO_MEDIA There is no media in the device.
+ @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of
+ the intrinsic block size of the device.
+
+**/
+EFI_STATUS
+EFIAPI
+NvmeBlockIoPeimReadBlocks (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This,
+ IN UINTN DeviceIndex,
+ IN EFI_PEI_LBA StartLBA,
+ IN UINTN BufferSize,
+ OUT VOID *Buffer
+ );
+
+/**
+ Gets the count of block I/O devices that one specific block driver detects.
+
+ This function is used for getting the count of block I/O devices that one
+ specific block driver detects. If no device is detected, then the function
+ will return zero.
+
+ @param[in] PeiServices General-purpose services that are available
+ to every PEIM.
+ @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO2_PPI
+ instance.
+ @param[out] NumberBlockDevices The number of block I/O devices discovered.
+
+ @retval EFI_SUCCESS The operation performed successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+NvmeBlockIoPeimGetDeviceNo2 (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN EFI_PEI_RECOVERY_BLOCK_IO2_PPI *This,
+ OUT UINTN *NumberBlockDevices
+ );
+
+/**
+ Gets a block device's media information.
+
+ This function will provide the caller with the specified block device's media
+ information. If the media changes, calling this function will update the media
+ information accordingly.
+
+ @param[in] PeiServices General-purpose services that are available to every
+ PEIM
+ @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO2_PPI instance.
+ @param[in] DeviceIndex Specifies the block device to which the function wants
+ to talk. Because the driver that implements Block I/O
+ PPIs will manage multiple block devices, the PPIs that
+ want to talk to a single device must specify the
+ device index that was assigned during the enumeration
+ process. This index is a number from one to
+ NumberBlockDevices.
+ @param[out] MediaInfo The media information of the specified block media.
+ The caller is responsible for the ownership of this
+ data structure.
+
+ @par Note:
+ The MediaInfo structure describes an enumeration of possible block device
+ types. This enumeration exists because no device paths are actually passed
+ across interfaces that describe the type or class of hardware that is publishing
+ the block I/O interface. This enumeration will allow for policy decisions
+ in the Recovery PEIM, such as "Try to recover from legacy floppy first,
+ LS-120 second, CD-ROM third." If there are multiple partitions abstracted
+ by a given device type, they should be reported in ascending order; this
+ order also applies to nested partitions, such as legacy MBR, where the
+ outermost partitions would have precedence in the reporting order. The
+ same logic applies to systems such as IDE that have precedence relationships
+ like "Master/Slave" or "Primary/Secondary". The master device should be
+ reported first, the slave second.
+
+ @retval EFI_SUCCESS Media information about the specified block device
+ was obtained successfully.
+ @retval EFI_DEVICE_ERROR Cannot get the media information due to a hardware
+ error.
+
+**/
+EFI_STATUS
+EFIAPI
+NvmeBlockIoPeimGetMediaInfo2 (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN EFI_PEI_RECOVERY_BLOCK_IO2_PPI *This,
+ IN UINTN DeviceIndex,
+ OUT EFI_PEI_BLOCK_IO2_MEDIA *MediaInfo
+ );
+
+/**
+ Reads the requested number of blocks from the specified block device.
+
+ The function reads the requested number of blocks from the device. All the
+ blocks are read, or an error is returned. If there is no media in the device,
+ the function returns EFI_NO_MEDIA.
+
+ @param[in] PeiServices General-purpose services that are available to
+ every PEIM.
+ @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO2_PPI instance.
+ @param[in] DeviceIndex Specifies the block device to which the function wants
+ to talk. Because the driver that implements Block I/O
+ PPIs will manage multiple block devices, PPIs that
+ want to talk to a single device must specify the device
+ index that was assigned during the enumeration process.
+ This index is a number from one to NumberBlockDevices.
+ @param[in] StartLBA The starting logical block address (LBA) to read from
+ on the device
+ @param[in] BufferSize The size of the Buffer in bytes. This number must be
+ a multiple of the intrinsic block size of the device.
+ @param[out] Buffer A pointer to the destination buffer for the data.
+ The caller is responsible for the ownership of the
+ buffer.
+
+ @retval EFI_SUCCESS The data was read correctly from the device.
+ @retval EFI_DEVICE_ERROR The device reported an error while attempting
+ to perform the read operation.
+ @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not
+ valid, or the buffer is not properly aligned.
+ @retval EFI_NO_MEDIA There is no media in the device.
+ @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of
+ the intrinsic block size of the device.
+
+**/
+EFI_STATUS
+EFIAPI
+NvmeBlockIoPeimReadBlocks2 (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN EFI_PEI_RECOVERY_BLOCK_IO2_PPI *This,
+ IN UINTN DeviceIndex,
+ IN EFI_PEI_LBA StartLBA,
+ IN UINTN BufferSize,
+ OUT VOID *Buffer
+ );
+
+#endif
diff --git a/roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiExtra.uni b/roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiExtra.uni
new file mode 100644
index 000000000..e486fef8b
--- /dev/null
+++ b/roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiExtra.uni
@@ -0,0 +1,12 @@
+// /** @file
+// NvmExpressPei Localized Strings and Content
+//
+// Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"NVM Express Peim"
diff --git a/roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiHci.c b/roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiHci.c
new file mode 100644
index 000000000..1d7e3d26e
--- /dev/null
+++ b/roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiHci.c
@@ -0,0 +1,717 @@
+/** @file
+ The NvmExpressPei driver is used to manage non-volatile memory subsystem
+ which follows NVM Express specification at PEI phase.
+
+ Copyright (c) 2018 - 2019, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "NvmExpressPei.h"
+
+/**
+ Transfer MMIO Data to memory.
+
+ @param[in,out] MemBuffer Destination: Memory address.
+ @param[in] MmioAddr Source: MMIO address.
+ @param[in] Size Size for read.
+
+ @retval EFI_SUCCESS MMIO read sucessfully.
+
+**/
+EFI_STATUS
+NvmeMmioRead (
+ IN OUT VOID *MemBuffer,
+ IN UINTN MmioAddr,
+ IN UINTN Size
+ )
+{
+ UINTN Offset;
+ UINT8 Data;
+ UINT8 *Ptr;
+
+ // priority has adjusted
+ switch (Size) {
+ case 4:
+ *((UINT32 *)MemBuffer) = MmioRead32 (MmioAddr);
+ break;
+
+ case 8:
+ *((UINT64 *)MemBuffer) = MmioRead64 (MmioAddr);
+ break;
+
+ case 2:
+ *((UINT16 *)MemBuffer) = MmioRead16 (MmioAddr);
+ break;
+
+ case 1:
+ *((UINT8 *)MemBuffer) = MmioRead8 (MmioAddr);
+ break;
+
+ default:
+ Ptr = (UINT8 *)MemBuffer;
+ for (Offset = 0; Offset < Size; Offset += 1) {
+ Data = MmioRead8 (MmioAddr + Offset);
+ Ptr[Offset] = Data;
+ }
+ break;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Transfer memory data to MMIO.
+
+ @param[in,out] MmioAddr Destination: MMIO address.
+ @param[in] MemBuffer Source: Memory address.
+ @param[in] Size Size for write.
+
+ @retval EFI_SUCCESS MMIO write sucessfully.
+
+**/
+EFI_STATUS
+NvmeMmioWrite (
+ IN OUT UINTN MmioAddr,
+ IN VOID *MemBuffer,
+ IN UINTN Size
+ )
+{
+ UINTN Offset;
+ UINT8 Data;
+ UINT8 *Ptr;
+
+ // priority has adjusted
+ switch (Size) {
+ case 4:
+ MmioWrite32 (MmioAddr, *((UINT32 *)MemBuffer));
+ break;
+
+ case 8:
+ MmioWrite64 (MmioAddr, *((UINT64 *)MemBuffer));
+ break;
+
+ case 2:
+ MmioWrite16 (MmioAddr, *((UINT16 *)MemBuffer));
+ break;
+
+ case 1:
+ MmioWrite8 (MmioAddr, *((UINT8 *)MemBuffer));
+ break;
+
+ default:
+ Ptr = (UINT8 *)MemBuffer;
+ for (Offset = 0; Offset < Size; Offset += 1) {
+ Data = Ptr[Offset];
+ MmioWrite8 (MmioAddr + Offset, Data);
+ }
+ break;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Get the page offset for specific NVME based memory.
+
+ @param[in] BaseMemIndex The Index of BaseMem (0-based).
+
+ @retval - The page count for specific BaseMem Index
+
+**/
+UINT32
+NvmeBaseMemPageOffset (
+ IN UINTN BaseMemIndex
+ )
+{
+ UINT32 Pages;
+ UINTN Index;
+ UINT32 PageSizeList[5];
+
+ PageSizeList[0] = 1; /* ASQ */
+ PageSizeList[1] = 1; /* ACQ */
+ PageSizeList[2] = 1; /* SQs */
+ PageSizeList[3] = 1; /* CQs */
+ PageSizeList[4] = NVME_PRP_SIZE; /* PRPs */
+
+ if (BaseMemIndex > MAX_BASEMEM_COUNT) {
+ DEBUG ((DEBUG_ERROR, "%a: The input BaseMem index is invalid.\n", __FUNCTION__));
+ ASSERT (FALSE);
+ return 0;
+ }
+
+ Pages = 0;
+ for (Index = 0; Index < BaseMemIndex; Index++) {
+ Pages += PageSizeList[Index];
+ }
+
+ return Pages;
+}
+
+/**
+ Wait for NVME controller status to be ready or not.
+
+ @param[in] Private The pointer to the PEI_NVME_CONTROLLER_PRIVATE_DATA data structure.
+ @param[in] WaitReady Flag for waitting status ready or not.
+
+ @return EFI_SUCCESS Successfully to wait specific status.
+ @return others Fail to wait for specific controller status.
+
+**/
+EFI_STATUS
+NvmeWaitController (
+ IN PEI_NVME_CONTROLLER_PRIVATE_DATA *Private,
+ IN BOOLEAN WaitReady
+ )
+{
+ NVME_CSTS Csts;
+ EFI_STATUS Status;
+ UINT32 Index;
+ UINT8 Timeout;
+
+ //
+ // Cap.To specifies max delay time in 500ms increments for Csts.Rdy to set after
+ // Cc.Enable. Loop produces a 1 millisecond delay per itteration, up to 500 * Cap.To.
+ //
+ if (Private->Cap.To == 0) {
+ Timeout = 1;
+ } else {
+ Timeout = Private->Cap.To;
+ }
+
+ Status = EFI_SUCCESS;
+ for(Index = (Timeout * 500); Index != 0; --Index) {
+ MicroSecondDelay (1000);
+
+ //
+ // Check if the controller is initialized
+ //
+ Status = NVME_GET_CSTS (Private, &Csts);
+ if (EFI_ERROR(Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: NVME_GET_CSTS fail, Status - %r\n", __FUNCTION__, Status));
+ return Status;
+ }
+
+ if ((BOOLEAN) Csts.Rdy == WaitReady) {
+ break;
+ }
+ }
+
+ if (Index == 0) {
+ Status = EFI_TIMEOUT;
+ }
+
+ return Status;
+}
+
+/**
+ Disable the Nvm Express controller.
+
+ @param[in] Private The pointer to the PEI_NVME_CONTROLLER_PRIVATE_DATA data structure.
+
+ @return EFI_SUCCESS Successfully disable the controller.
+ @return others Fail to disable the controller.
+
+**/
+EFI_STATUS
+NvmeDisableController (
+ IN PEI_NVME_CONTROLLER_PRIVATE_DATA *Private
+ )
+{
+ NVME_CC Cc;
+ NVME_CSTS Csts;
+ EFI_STATUS Status;
+
+ Status = NVME_GET_CSTS (Private, &Csts);
+
+ //
+ // Read Controller Configuration Register.
+ //
+ Status = NVME_GET_CC (Private, &Cc);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: NVME_GET_CC fail, Status - %r\n", __FUNCTION__, Status));
+ goto ErrorExit;
+ }
+
+ if (Cc.En == 1) {
+ Cc.En = 0;
+ //
+ // Disable the controller.
+ //
+ Status = NVME_SET_CC (Private, &Cc);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: NVME_SET_CC fail, Status - %r\n", __FUNCTION__, Status));
+ goto ErrorExit;
+ }
+ }
+
+ Status = NvmeWaitController (Private, FALSE);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: NvmeWaitController fail, Status - %r\n", __FUNCTION__, Status));
+ goto ErrorExit;
+ }
+
+ return EFI_SUCCESS;
+
+ErrorExit:
+ DEBUG ((DEBUG_ERROR, "%a fail, Status - %r\n", __FUNCTION__, Status));
+ return Status;
+}
+
+/**
+ Enable the Nvm Express controller.
+
+ @param[in] Private The pointer to the PEI_NVME_CONTROLLER_PRIVATE_DATA data structure.
+
+ @return EFI_SUCCESS Successfully enable the controller.
+ @return EFI_DEVICE_ERROR Fail to enable the controller.
+ @return EFI_TIMEOUT Fail to enable the controller in given time slot.
+
+**/
+EFI_STATUS
+NvmeEnableController (
+ IN PEI_NVME_CONTROLLER_PRIVATE_DATA *Private
+ )
+{
+ NVME_CC Cc;
+ EFI_STATUS Status;
+
+ //
+ // Enable the controller
+ // CC.AMS, CC.MPS and CC.CSS are all set to 0
+ //
+ ZeroMem (&Cc, sizeof (NVME_CC));
+ Cc.En = 1;
+ Cc.Iosqes = 6;
+ Cc.Iocqes = 4;
+ Status = NVME_SET_CC (Private, &Cc);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: NVME_SET_CC fail, Status - %r\n", __FUNCTION__, Status));
+ goto ErrorExit;
+ }
+
+ Status = NvmeWaitController (Private, TRUE);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: NvmeWaitController fail, Status - %r\n", __FUNCTION__, Status));
+ goto ErrorExit;
+ }
+
+ return EFI_SUCCESS;
+
+ErrorExit:
+ DEBUG ((DEBUG_ERROR, "%a fail, Status: %r\n", __FUNCTION__, Status));
+ return Status;
+}
+
+/**
+ Get the Identify Controller data.
+
+ @param[in] Private The pointer to the PEI_NVME_CONTROLLER_PRIVATE_DATA data structure.
+ @param[in] Buffer The Buffer used to store the Identify Controller data.
+
+ @return EFI_SUCCESS Successfully get the Identify Controller data.
+ @return others Fail to get the Identify Controller data.
+
+**/
+EFI_STATUS
+NvmeIdentifyController (
+ IN PEI_NVME_CONTROLLER_PRIVATE_DATA *Private,
+ IN VOID *Buffer
+ )
+{
+ EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket;
+ EFI_NVM_EXPRESS_COMMAND Command;
+ EFI_NVM_EXPRESS_COMPLETION Completion;
+ EFI_STATUS Status;
+
+ ZeroMem (&CommandPacket, sizeof(EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));
+ ZeroMem (&Command, sizeof(EFI_NVM_EXPRESS_COMMAND));
+ ZeroMem (&Completion, sizeof(EFI_NVM_EXPRESS_COMPLETION));
+
+ Command.Cdw0.Opcode = NVME_ADMIN_IDENTIFY_CMD;
+ //
+ // According to Nvm Express 1.1 spec Figure 38, When not used, the field shall be cleared to 0h.
+ // For the Identify command, the Namespace Identifier is only used for the Namespace Data structure.
+ //
+ Command.Nsid = 0;
+
+ CommandPacket.NvmeCmd = &Command;
+ CommandPacket.NvmeCompletion = &Completion;
+ CommandPacket.TransferBuffer = Buffer;
+ CommandPacket.TransferLength = sizeof (NVME_ADMIN_CONTROLLER_DATA);
+ CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;
+ CommandPacket.QueueType = NVME_ADMIN_QUEUE;
+ //
+ // Set bit 0 (Cns bit) to 1 to identify the controller
+ //
+ CommandPacket.NvmeCmd->Cdw10 = 1;
+ CommandPacket.NvmeCmd->Flags = CDW10_VALID;
+
+ Status = NvmePassThruExecute (
+ Private,
+ NVME_CONTROLLER_NSID,
+ &CommandPacket
+ );
+ return Status;
+}
+
+/**
+ Get specified identify namespace data.
+
+ @param[in] Private The pointer to the PEI_NVME_CONTROLLER_PRIVATE_DATA data structure.
+ @param[in] NamespaceId The specified namespace identifier.
+ @param[in] Buffer The buffer used to store the identify namespace data.
+
+ @return EFI_SUCCESS Successfully get the identify namespace data.
+ @return EFI_DEVICE_ERROR Fail to get the identify namespace data.
+
+**/
+EFI_STATUS
+NvmeIdentifyNamespace (
+ IN PEI_NVME_CONTROLLER_PRIVATE_DATA *Private,
+ IN UINT32 NamespaceId,
+ IN VOID *Buffer
+ )
+{
+ EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket;
+ EFI_NVM_EXPRESS_COMMAND Command;
+ EFI_NVM_EXPRESS_COMPLETION Completion;
+ EFI_STATUS Status;
+
+ ZeroMem (&CommandPacket, sizeof(EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));
+ ZeroMem (&Command, sizeof(EFI_NVM_EXPRESS_COMMAND));
+ ZeroMem (&Completion, sizeof(EFI_NVM_EXPRESS_COMPLETION));
+
+ Command.Cdw0.Opcode = NVME_ADMIN_IDENTIFY_CMD;
+ Command.Nsid = NamespaceId;
+
+ CommandPacket.NvmeCmd = &Command;
+ CommandPacket.NvmeCompletion = &Completion;
+ CommandPacket.TransferBuffer = Buffer;
+ CommandPacket.TransferLength = sizeof (NVME_ADMIN_NAMESPACE_DATA);
+ CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;
+ CommandPacket.QueueType = NVME_ADMIN_QUEUE;
+ //
+ // Set bit 0 (Cns bit) to 1 to identify a namespace
+ //
+ CommandPacket.NvmeCmd->Cdw10 = 0;
+ CommandPacket.NvmeCmd->Flags = CDW10_VALID;
+
+ Status = NvmePassThruExecute (
+ Private,
+ NamespaceId,
+ &CommandPacket
+ );
+ return Status;
+}
+
+/**
+ Dump the Identify Controller data.
+
+ @param[in] ControllerData The pointer to the NVME_ADMIN_CONTROLLER_DATA data structure.
+
+**/
+VOID
+NvmeDumpControllerData (
+ IN NVME_ADMIN_CONTROLLER_DATA *ControllerData
+ )
+{
+ UINT8 Sn[21];
+ UINT8 Mn[41];
+
+ CopyMem (Sn, ControllerData->Sn, sizeof (ControllerData->Sn));
+ Sn[20] = 0;
+ CopyMem (Mn, ControllerData->Mn, sizeof (ControllerData->Mn));
+ Mn[40] = 0;
+
+ DEBUG ((DEBUG_INFO, " == NVME IDENTIFY CONTROLLER DATA ==\n"));
+ DEBUG ((DEBUG_INFO, " PCI VID : 0x%x\n", ControllerData->Vid));
+ DEBUG ((DEBUG_INFO, " PCI SSVID : 0x%x\n", ControllerData->Ssvid));
+ DEBUG ((DEBUG_INFO, " SN : %a\n", Sn));
+ DEBUG ((DEBUG_INFO, " MN : %a\n", Mn));
+ DEBUG ((DEBUG_INFO, " FR : 0x%lx\n", *((UINT64*)ControllerData->Fr)));
+ DEBUG ((DEBUG_INFO, " RAB : 0x%x\n", ControllerData->Rab));
+ DEBUG ((DEBUG_INFO, " IEEE : 0x%x\n", *(UINT32*)ControllerData->Ieee_oui));
+ DEBUG ((DEBUG_INFO, " AERL : 0x%x\n", ControllerData->Aerl));
+ DEBUG ((DEBUG_INFO, " SQES : 0x%x\n", ControllerData->Sqes));
+ DEBUG ((DEBUG_INFO, " CQES : 0x%x\n", ControllerData->Cqes));
+ DEBUG ((DEBUG_INFO, " NN : 0x%x\n", ControllerData->Nn));
+ return;
+}
+
+/**
+ Create IO completion queue.
+
+ @param[in] Private The pointer to the PEI_NVME_CONTROLLER_PRIVATE_DATA data structure.
+
+ @return EFI_SUCCESS Successfully create io completion queue.
+ @return others Fail to create io completion queue.
+
+**/
+EFI_STATUS
+NvmeCreateIoCompletionQueue (
+ IN PEI_NVME_CONTROLLER_PRIVATE_DATA *Private
+ )
+{
+ EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket;
+ EFI_NVM_EXPRESS_COMMAND Command;
+ EFI_NVM_EXPRESS_COMPLETION Completion;
+ EFI_STATUS Status;
+ NVME_ADMIN_CRIOCQ CrIoCq;
+
+ ZeroMem (&CommandPacket, sizeof(EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));
+ ZeroMem (&Command, sizeof(EFI_NVM_EXPRESS_COMMAND));
+ ZeroMem (&Completion, sizeof(EFI_NVM_EXPRESS_COMPLETION));
+ ZeroMem (&CrIoCq, sizeof(NVME_ADMIN_CRIOCQ));
+
+ CommandPacket.NvmeCmd = &Command;
+ CommandPacket.NvmeCompletion = &Completion;
+
+ Command.Cdw0.Opcode = NVME_ADMIN_CRIOCQ_CMD;
+ CommandPacket.TransferBuffer = Private->CqBuffer[NVME_IO_QUEUE];
+ CommandPacket.TransferLength = EFI_PAGE_SIZE;
+ CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;
+ CommandPacket.QueueType = NVME_ADMIN_QUEUE;
+
+ CrIoCq.Qid = NVME_IO_QUEUE;
+ CrIoCq.Qsize = NVME_CCQ_SIZE;
+ CrIoCq.Pc = 1;
+ CopyMem (&CommandPacket.NvmeCmd->Cdw10, &CrIoCq, sizeof (NVME_ADMIN_CRIOCQ));
+ CommandPacket.NvmeCmd->Flags = CDW10_VALID | CDW11_VALID;
+
+ Status = NvmePassThruExecute (
+ Private,
+ NVME_CONTROLLER_NSID,
+ &CommandPacket
+ );
+ return Status;
+}
+
+/**
+ Create IO submission queue.
+
+ @param[in] Private The pointer to the PEI_NVME_CONTROLLER_PRIVATE_DATA data structure.
+
+ @return EFI_SUCCESS Successfully create io submission queue.
+ @return others Fail to create io submission queue.
+
+**/
+EFI_STATUS
+NvmeCreateIoSubmissionQueue (
+ IN PEI_NVME_CONTROLLER_PRIVATE_DATA *Private
+ )
+{
+ EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket;
+ EFI_NVM_EXPRESS_COMMAND Command;
+ EFI_NVM_EXPRESS_COMPLETION Completion;
+ EFI_STATUS Status;
+ NVME_ADMIN_CRIOSQ CrIoSq;
+
+ ZeroMem (&CommandPacket, sizeof(EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));
+ ZeroMem (&Command, sizeof(EFI_NVM_EXPRESS_COMMAND));
+ ZeroMem (&Completion, sizeof(EFI_NVM_EXPRESS_COMPLETION));
+ ZeroMem (&CrIoSq, sizeof(NVME_ADMIN_CRIOSQ));
+
+ CommandPacket.NvmeCmd = &Command;
+ CommandPacket.NvmeCompletion = &Completion;
+
+ Command.Cdw0.Opcode = NVME_ADMIN_CRIOSQ_CMD;
+ CommandPacket.TransferBuffer = Private->SqBuffer[NVME_IO_QUEUE];
+ CommandPacket.TransferLength = EFI_PAGE_SIZE;
+ CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;
+ CommandPacket.QueueType = NVME_ADMIN_QUEUE;
+
+ CrIoSq.Qid = NVME_IO_QUEUE;
+ CrIoSq.Qsize = NVME_CSQ_SIZE;
+ CrIoSq.Pc = 1;
+ CrIoSq.Cqid = NVME_IO_QUEUE;
+ CrIoSq.Qprio = 0;
+ CopyMem (&CommandPacket.NvmeCmd->Cdw10, &CrIoSq, sizeof (NVME_ADMIN_CRIOSQ));
+ CommandPacket.NvmeCmd->Flags = CDW10_VALID | CDW11_VALID;
+
+ Status = NvmePassThruExecute (
+ Private,
+ NVME_CONTROLLER_NSID,
+ &CommandPacket
+ );
+ return Status;
+}
+
+/**
+ Initialize the Nvm Express controller.
+
+ @param[in] Private The pointer to the PEI_NVME_CONTROLLER_PRIVATE_DATA data structure.
+
+ @retval EFI_SUCCESS The NVM Express Controller is initialized successfully.
+ @retval Others A device error occurred while initializing the controller.
+
+**/
+EFI_STATUS
+NvmeControllerInit (
+ IN PEI_NVME_CONTROLLER_PRIVATE_DATA *Private
+ )
+{
+ EFI_STATUS Status;
+ UINTN Index;
+ NVME_AQA Aqa;
+ NVME_ASQ Asq;
+ NVME_ACQ Acq;
+ NVME_VER Ver;
+
+ //
+ // Dump the NVME controller implementation version
+ //
+ NVME_GET_VER (Private, &Ver);
+ DEBUG ((DEBUG_INFO, "NVME controller implementation version: %d.%d\n", Ver.Mjr, Ver.Mnr));
+
+ //
+ // Read the controller Capabilities register and verify that the NVM command set is supported
+ //
+ NVME_GET_CAP (Private, &Private->Cap);
+ if (Private->Cap.Css != 0x01) {
+ DEBUG ((DEBUG_ERROR, "%a: The NVME controller doesn't support NVMe command set.\n", __FUNCTION__));
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // Currently, the driver only supports 4k page size
+ //
+ if ((Private->Cap.Mpsmin + 12) > EFI_PAGE_SHIFT) {
+ DEBUG ((DEBUG_ERROR, "%a: The driver doesn't support page size other than 4K.\n", __FUNCTION__));
+ ASSERT (FALSE);
+ return EFI_UNSUPPORTED;
+ }
+
+ for (Index = 0; Index < NVME_MAX_QUEUES; Index++) {
+ Private->Pt[Index] = 0;
+ Private->Cid[Index] = 0;
+ ZeroMem ((VOID *)(UINTN)(&Private->SqTdbl[Index]), sizeof (NVME_SQTDBL));
+ ZeroMem ((VOID *)(UINTN)(&Private->CqHdbl[Index]), sizeof (NVME_CQHDBL));
+ }
+ ZeroMem (Private->Buffer, EFI_PAGE_SIZE * NVME_MEM_MAX_PAGES);
+
+ //
+ // Disable the NVME controller first
+ //
+ Status = NvmeDisableController (Private);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: NvmeDisableController fail, Status - %r\n", __FUNCTION__, Status));
+ return Status;
+ }
+
+ //
+ // Set the number of entries in admin submission & completion queues
+ //
+ Aqa.Asqs = NVME_ASQ_SIZE;
+ Aqa.Rsvd1 = 0;
+ Aqa.Acqs = NVME_ACQ_SIZE;
+ Aqa.Rsvd2 = 0;
+
+ //
+ // Address of admin submission & completion queues
+ //
+ Asq = (UINT64)(UINTN)(NVME_ASQ_BASE (Private) & ~0xFFF);
+ Acq = (UINT64)(UINTN)(NVME_ACQ_BASE (Private) & ~0xFFF);
+
+ //
+ // Address of I/O submission & completion queues
+ //
+ Private->SqBuffer[0] = (NVME_SQ *)(UINTN)NVME_ASQ_BASE (Private); // NVME_ADMIN_QUEUE
+ Private->CqBuffer[0] = (NVME_CQ *)(UINTN)NVME_ACQ_BASE (Private); // NVME_ADMIN_QUEUE
+ Private->SqBuffer[1] = (NVME_SQ *)(UINTN)NVME_SQ_BASE (Private, 0); // NVME_IO_QUEUE
+ Private->CqBuffer[1] = (NVME_CQ *)(UINTN)NVME_CQ_BASE (Private, 0); // NVME_IO_QUEUE
+ DEBUG ((DEBUG_INFO, "Admin Submission Queue Size (Aqa.Asqs) = [%08X]\n", Aqa.Asqs));
+ DEBUG ((DEBUG_INFO, "Admin Completion Queue Size (Aqa.Acqs) = [%08X]\n", Aqa.Acqs));
+ DEBUG ((DEBUG_INFO, "Admin Submission Queue (SqBuffer[0]) = [%08X]\n", Private->SqBuffer[0]));
+ DEBUG ((DEBUG_INFO, "Admin Completion Queue (CqBuffer[0]) = [%08X]\n", Private->CqBuffer[0]));
+ DEBUG ((DEBUG_INFO, "I/O Submission Queue (SqBuffer[1]) = [%08X]\n", Private->SqBuffer[1]));
+ DEBUG ((DEBUG_INFO, "I/O Completion Queue (CqBuffer[1]) = [%08X]\n", Private->CqBuffer[1]));
+
+ //
+ // Program admin queue attributes
+ //
+ NVME_SET_AQA (Private, &Aqa);
+
+ //
+ // Program admin submission & completion queues address
+ //
+ NVME_SET_ASQ (Private, &Asq);
+ NVME_SET_ACQ (Private, &Acq);
+
+ //
+ // Enable the NVME controller
+ //
+ Status = NvmeEnableController (Private);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: NvmeEnableController fail, Status - %r\n", __FUNCTION__, Status));
+ return Status;
+ }
+
+ //
+ // Get the Identify Controller data
+ //
+ if (Private->ControllerData == NULL) {
+ Private->ControllerData = (NVME_ADMIN_CONTROLLER_DATA *)AllocateZeroPool (sizeof (NVME_ADMIN_CONTROLLER_DATA));
+ if (Private->ControllerData == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ }
+ Status = NvmeIdentifyController (Private, Private->ControllerData);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: NvmeIdentifyController fail, Status - %r\n", __FUNCTION__, Status));
+ return Status;
+ }
+ NvmeDumpControllerData (Private->ControllerData);
+
+ //
+ // Check the namespace number for storing the namespaces information
+ //
+ if (Private->ControllerData->Nn > MAX_UINT32 / sizeof (PEI_NVME_NAMESPACE_INFO)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "%a: Number of Namespaces field in Identify Controller data not supported by the driver.\n",
+ __FUNCTION__
+ ));
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // Create one I/O completion queue and one I/O submission queue
+ //
+ Status = NvmeCreateIoCompletionQueue (Private);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: Create IO completion queue fail, Status - %r\n", __FUNCTION__, Status));
+ return Status;
+ }
+ Status = NvmeCreateIoSubmissionQueue (Private);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: Create IO submission queue fail, Status - %r\n", __FUNCTION__, Status));
+ }
+
+ return Status;
+}
+
+/**
+ Free the DMA resources allocated by an NVME controller.
+
+ @param[in] Private The pointer to the PEI_NVME_CONTROLLER_PRIVATE_DATA data structure.
+
+**/
+VOID
+NvmeFreeDmaResource (
+ IN PEI_NVME_CONTROLLER_PRIVATE_DATA *Private
+ )
+{
+ ASSERT (Private != NULL);
+
+ if (Private->BufferMapping != NULL) {
+ IoMmuFreeBuffer (
+ NVME_MEM_MAX_PAGES,
+ Private->Buffer,
+ Private->BufferMapping
+ );
+ }
+
+ return;
+}
diff --git a/roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiHci.h b/roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiHci.h
new file mode 100644
index 000000000..89fee735f
--- /dev/null
+++ b/roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiHci.h
@@ -0,0 +1,145 @@
+/** @file
+ The NvmExpressPei driver is used to manage non-volatile memory subsystem
+ which follows NVM Express specification at PEI phase.
+
+ Copyright (c) 2018 - 2019, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef _NVM_EXPRESS_PEI_HCI_H_
+#define _NVM_EXPRESS_PEI_HCI_H_
+
+//
+// NVME host controller registers operation definitions
+//
+#define NVME_GET_CAP(Private, Cap) NvmeMmioRead (Cap, Private->MmioBase + NVME_CAP_OFFSET, sizeof (NVME_CAP))
+#define NVME_GET_CC(Private, Cc) NvmeMmioRead (Cc, Private->MmioBase + NVME_CC_OFFSET, sizeof (NVME_CC))
+#define NVME_SET_CC(Private, Cc) NvmeMmioWrite (Private->MmioBase + NVME_CC_OFFSET, Cc, sizeof (NVME_CC))
+#define NVME_GET_CSTS(Private, Csts) NvmeMmioRead (Csts, Private->MmioBase + NVME_CSTS_OFFSET, sizeof (NVME_CSTS))
+#define NVME_GET_AQA(Private, Aqa) NvmeMmioRead (Aqa, Private->MmioBase + NVME_AQA_OFFSET, sizeof (NVME_AQA))
+#define NVME_SET_AQA(Private, Aqa) NvmeMmioWrite (Private->MmioBase + NVME_AQA_OFFSET, Aqa, sizeof (NVME_AQA))
+#define NVME_GET_ASQ(Private, Asq) NvmeMmioRead (Asq, Private->MmioBase + NVME_ASQ_OFFSET, sizeof (NVME_ASQ))
+#define NVME_SET_ASQ(Private, Asq) NvmeMmioWrite (Private->MmioBase + NVME_ASQ_OFFSET, Asq, sizeof (NVME_ASQ))
+#define NVME_GET_ACQ(Private, Acq) NvmeMmioRead (Acq, Private->MmioBase + NVME_ACQ_OFFSET, sizeof (NVME_ACQ))
+#define NVME_SET_ACQ(Private, Acq) NvmeMmioWrite (Private->MmioBase + NVME_ACQ_OFFSET, Acq, sizeof (NVME_ACQ))
+#define NVME_GET_VER(Private, Ver) NvmeMmioRead (Ver, Private->MmioBase + NVME_VER_OFFSET, sizeof (NVME_VER))
+#define NVME_SET_SQTDBL(Private, Qid, Sqtdbl) NvmeMmioWrite (Private->MmioBase + NVME_SQTDBL_OFFSET(Qid, Private->Cap.Dstrd), Sqtdbl, sizeof (NVME_SQTDBL))
+#define NVME_SET_CQHDBL(Private, Qid, Cqhdbl) NvmeMmioWrite (Private->MmioBase + NVME_CQHDBL_OFFSET(Qid, Private->Cap.Dstrd), Cqhdbl, sizeof (NVME_CQHDBL))
+
+//
+// Base memory address enum types
+//
+enum {
+ BASEMEM_ASQ,
+ BASEMEM_ACQ,
+ BASEMEM_SQ,
+ BASEMEM_CQ,
+ BASEMEM_PRP,
+ MAX_BASEMEM_COUNT
+};
+
+//
+// All of base memories are 4K(0x1000) alignment
+//
+#define ALIGN(v, a) (UINTN)((((v) - 1) | ((a) - 1)) + 1)
+#define NVME_MEM_BASE(Private) ((UINTN)(Private->Buffer))
+#define NVME_ASQ_BASE(Private) (ALIGN (NVME_MEM_BASE(Private) + ((NvmeBaseMemPageOffset (BASEMEM_ASQ)) * EFI_PAGE_SIZE), EFI_PAGE_SIZE))
+#define NVME_ACQ_BASE(Private) (ALIGN (NVME_MEM_BASE(Private) + ((NvmeBaseMemPageOffset (BASEMEM_ACQ)) * EFI_PAGE_SIZE), EFI_PAGE_SIZE))
+#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))
+#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))
+#define NVME_PRP_BASE(Private) (ALIGN (NVME_MEM_BASE(Private) + ((NvmeBaseMemPageOffset (BASEMEM_PRP)) * EFI_PAGE_SIZE), EFI_PAGE_SIZE))
+
+
+/**
+ Transfer MMIO Data to memory.
+
+ @param[in,out] MemBuffer Destination: Memory address.
+ @param[in] MmioAddr Source: MMIO address.
+ @param[in] Size Size for read.
+
+ @retval EFI_SUCCESS MMIO read sucessfully.
+
+**/
+EFI_STATUS
+NvmeMmioRead (
+ IN OUT VOID *MemBuffer,
+ IN UINTN MmioAddr,
+ IN UINTN Size
+ );
+
+/**
+ Transfer memory data to MMIO.
+
+ @param[in,out] MmioAddr Destination: MMIO address.
+ @param[in] MemBuffer Source: Memory address.
+ @param[in] Size Size for write.
+
+ @retval EFI_SUCCESS MMIO write sucessfully.
+
+**/
+EFI_STATUS
+NvmeMmioWrite (
+ IN OUT UINTN MmioAddr,
+ IN VOID *MemBuffer,
+ IN UINTN Size
+ );
+
+/**
+ Get the page offset for specific NVME based memory.
+
+ @param[in] BaseMemIndex The Index of BaseMem (0-based).
+
+ @retval - The page count for specific BaseMem Index
+
+**/
+UINT32
+NvmeBaseMemPageOffset (
+ IN UINTN BaseMemIndex
+ );
+
+/**
+ Initialize the Nvm Express controller.
+
+ @param[in] Private The pointer to the PEI_NVME_CONTROLLER_PRIVATE_DATA data structure.
+
+ @retval EFI_SUCCESS The NVM Express Controller is initialized successfully.
+ @retval Others A device error occurred while initializing the controller.
+
+**/
+EFI_STATUS
+NvmeControllerInit (
+ IN PEI_NVME_CONTROLLER_PRIVATE_DATA *Private
+ );
+
+/**
+ Get specified identify namespace data.
+
+ @param[in] Private The pointer to the PEI_NVME_CONTROLLER_PRIVATE_DATA data structure.
+ @param[in] NamespaceId The specified namespace identifier.
+ @param[in] Buffer The buffer used to store the identify namespace data.
+
+ @return EFI_SUCCESS Successfully get the identify namespace data.
+ @return EFI_DEVICE_ERROR Fail to get the identify namespace data.
+
+**/
+EFI_STATUS
+NvmeIdentifyNamespace (
+ IN PEI_NVME_CONTROLLER_PRIVATE_DATA *Private,
+ IN UINT32 NamespaceId,
+ IN VOID *Buffer
+ );
+
+/**
+ Free the DMA resources allocated by an NVME controller.
+
+ @param[in] Private The pointer to the PEI_NVME_CONTROLLER_PRIVATE_DATA data structure.
+
+**/
+VOID
+NvmeFreeDmaResource (
+ IN PEI_NVME_CONTROLLER_PRIVATE_DATA *Private
+ );
+
+#endif
diff --git a/roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiPassThru.c b/roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiPassThru.c
new file mode 100644
index 000000000..370a54e5a
--- /dev/null
+++ b/roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiPassThru.c
@@ -0,0 +1,828 @@
+/** @file
+ The NvmExpressPei driver is used to manage non-volatile memory subsystem
+ which follows NVM Express specification at PEI phase.
+
+ Copyright (c) 2018 - 2019, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "NvmExpressPei.h"
+
+/**
+ Create PRP lists for Data transfer which is larger than 2 memory pages.
+
+ @param[in] Private The pointer to the PEI_NVME_CONTROLLER_PRIVATE_DATA data structure.
+ @param[in] PhysicalAddr The physical base address of Data Buffer.
+ @param[in] Pages The number of pages to be transfered.
+
+ @retval The pointer Value to the first PRP List of the PRP lists.
+
+**/
+UINT64
+NvmeCreatePrpList (
+ IN PEI_NVME_CONTROLLER_PRIVATE_DATA *Private,
+ IN EFI_PHYSICAL_ADDRESS PhysicalAddr,
+ IN UINTN Pages
+ )
+{
+ UINTN PrpEntryNo;
+ UINTN PrpListNo;
+ UINT64 PrpListBase;
+ VOID *PrpListHost;
+ UINTN PrpListIndex;
+ UINTN PrpEntryIndex;
+ UINT64 Remainder;
+ EFI_PHYSICAL_ADDRESS PrpListPhyAddr;
+ UINTN Bytes;
+ UINT8 *PrpEntry;
+ EFI_PHYSICAL_ADDRESS NewPhyAddr;
+
+ //
+ // The number of Prp Entry in a memory page.
+ //
+ PrpEntryNo = EFI_PAGE_SIZE / sizeof (UINT64);
+
+ //
+ // Calculate total PrpList number.
+ //
+ PrpListNo = (UINTN) DivU64x64Remainder ((UINT64)Pages, (UINT64)PrpEntryNo, &Remainder);
+ if (Remainder != 0) {
+ PrpListNo += 1;
+ }
+
+ if (PrpListNo > NVME_PRP_SIZE) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "%a: The implementation only supports PrpList number up to 4."
+ " But %d are needed here.\n",
+ __FUNCTION__,
+ PrpListNo
+ ));
+ return 0;
+ }
+ PrpListHost = (VOID *)(UINTN) NVME_PRP_BASE (Private);
+
+ Bytes = EFI_PAGES_TO_SIZE (PrpListNo);
+ PrpListPhyAddr = (UINT64)(UINTN)(PrpListHost);
+
+ //
+ // Fill all PRP lists except of last one.
+ //
+ ZeroMem (PrpListHost, Bytes);
+ for (PrpListIndex = 0; PrpListIndex < PrpListNo - 1; ++PrpListIndex) {
+ PrpListBase = (UINTN)PrpListHost + PrpListIndex * EFI_PAGE_SIZE;
+
+ for (PrpEntryIndex = 0; PrpEntryIndex < PrpEntryNo; ++PrpEntryIndex) {
+ PrpEntry = (UINT8 *)(UINTN) (PrpListBase + PrpEntryIndex * sizeof(UINT64));
+ if (PrpEntryIndex != PrpEntryNo - 1) {
+ //
+ // Fill all PRP entries except of last one.
+ //
+ CopyMem (PrpEntry, (VOID *)(UINTN) (&PhysicalAddr), sizeof (UINT64));
+ PhysicalAddr += EFI_PAGE_SIZE;
+ } else {
+ //
+ // Fill last PRP entries with next PRP List pointer.
+ //
+ NewPhyAddr = (PrpListPhyAddr + (PrpListIndex + 1) * EFI_PAGE_SIZE);
+ CopyMem (PrpEntry, (VOID *)(UINTN) (&NewPhyAddr), sizeof (UINT64));
+ }
+ }
+ }
+
+ //
+ // Fill last PRP list.
+ //
+ PrpListBase = (UINTN)PrpListHost + PrpListIndex * EFI_PAGE_SIZE;
+ for (PrpEntryIndex = 0; PrpEntryIndex < ((Remainder != 0) ? Remainder : PrpEntryNo); ++PrpEntryIndex) {
+ PrpEntry = (UINT8 *)(UINTN) (PrpListBase + PrpEntryIndex * sizeof(UINT64));
+ CopyMem (PrpEntry, (VOID *)(UINTN) (&PhysicalAddr), sizeof (UINT64));
+
+ PhysicalAddr += EFI_PAGE_SIZE;
+ }
+
+ return PrpListPhyAddr;
+}
+
+/**
+ Check the execution status from a given completion queue entry.
+
+ @param[in] Cq A pointer to the NVME_CQ item.
+
+**/
+EFI_STATUS
+NvmeCheckCqStatus (
+ IN NVME_CQ *Cq
+ )
+{
+ if (Cq->Sct == 0x0 && Cq->Sc == 0x0) {
+ return EFI_SUCCESS;
+ }
+
+ DEBUG ((DEBUG_INFO, "Dump NVMe Completion Entry Status from [0x%x]:\n", (UINTN)Cq));
+ DEBUG ((
+ DEBUG_INFO,
+ " SQ Identifier : [0x%x], Phase Tag : [%d], Cmd Identifier : [0x%x]\n",
+ Cq->Sqid,
+ Cq->Pt,
+ Cq->Cid
+ ));
+ DEBUG ((DEBUG_INFO, " Status Code Type : [0x%x], Status Code : [0x%x]\n", Cq->Sct, Cq->Sc));
+ DEBUG ((DEBUG_INFO, " NVMe Cmd Execution Result - "));
+
+ switch (Cq->Sct) {
+ case 0x0:
+ switch (Cq->Sc) {
+ case 0x0:
+ DEBUG ((DEBUG_INFO, "Successful Completion\n"));
+ return EFI_SUCCESS;
+ case 0x1:
+ DEBUG ((DEBUG_INFO, "Invalid Command Opcode\n"));
+ break;
+ case 0x2:
+ DEBUG ((DEBUG_INFO, "Invalid Field in Command\n"));
+ break;
+ case 0x3:
+ DEBUG ((DEBUG_INFO, "Command ID Conflict\n"));
+ break;
+ case 0x4:
+ DEBUG ((DEBUG_INFO, "Data Transfer Error\n"));
+ break;
+ case 0x5:
+ DEBUG ((DEBUG_INFO, "Commands Aborted due to Power Loss Notification\n"));
+ break;
+ case 0x6:
+ DEBUG ((DEBUG_INFO, "Internal Device Error\n"));
+ break;
+ case 0x7:
+ DEBUG ((DEBUG_INFO, "Command Abort Requested\n"));
+ break;
+ case 0x8:
+ DEBUG ((DEBUG_INFO, "Command Aborted due to SQ Deletion\n"));
+ break;
+ case 0x9:
+ DEBUG ((DEBUG_INFO, "Command Aborted due to Failed Fused Command\n"));
+ break;
+ case 0xA:
+ DEBUG ((DEBUG_INFO, "Command Aborted due to Missing Fused Command\n"));
+ break;
+ case 0xB:
+ DEBUG ((DEBUG_INFO, "Invalid Namespace or Format\n"));
+ break;
+ case 0xC:
+ DEBUG ((DEBUG_INFO, "Command Sequence Error\n"));
+ break;
+ case 0xD:
+ DEBUG ((DEBUG_INFO, "Invalid SGL Last Segment Descriptor\n"));
+ break;
+ case 0xE:
+ DEBUG ((DEBUG_INFO, "Invalid Number of SGL Descriptors\n"));
+ break;
+ case 0xF:
+ DEBUG ((DEBUG_INFO, "Data SGL Length Invalid\n"));
+ break;
+ case 0x10:
+ DEBUG ((DEBUG_INFO, "Metadata SGL Length Invalid\n"));
+ break;
+ case 0x11:
+ DEBUG ((DEBUG_INFO, "SGL Descriptor Type Invalid\n"));
+ break;
+ case 0x80:
+ DEBUG ((DEBUG_INFO, "LBA Out of Range\n"));
+ break;
+ case 0x81:
+ DEBUG ((DEBUG_INFO, "Capacity Exceeded\n"));
+ break;
+ case 0x82:
+ DEBUG ((DEBUG_INFO, "Namespace Not Ready\n"));
+ break;
+ case 0x83:
+ DEBUG ((DEBUG_INFO, "Reservation Conflict\n"));
+ break;
+ }
+ break;
+
+ case 0x1:
+ switch (Cq->Sc) {
+ case 0x0:
+ DEBUG ((DEBUG_INFO, "Completion Queue Invalid\n"));
+ break;
+ case 0x1:
+ DEBUG ((DEBUG_INFO, "Invalid Queue Identifier\n"));
+ break;
+ case 0x2:
+ DEBUG ((DEBUG_INFO, "Maximum Queue Size Exceeded\n"));
+ break;
+ case 0x3:
+ DEBUG ((DEBUG_INFO, "Abort Command Limit Exceeded\n"));
+ break;
+ case 0x5:
+ DEBUG ((DEBUG_INFO, "Asynchronous Event Request Limit Exceeded\n"));
+ break;
+ case 0x6:
+ DEBUG ((DEBUG_INFO, "Invalid Firmware Slot\n"));
+ break;
+ case 0x7:
+ DEBUG ((DEBUG_INFO, "Invalid Firmware Image\n"));
+ break;
+ case 0x8:
+ DEBUG ((DEBUG_INFO, "Invalid Interrupt Vector\n"));
+ break;
+ case 0x9:
+ DEBUG ((DEBUG_INFO, "Invalid Log Page\n"));
+ break;
+ case 0xA:
+ DEBUG ((DEBUG_INFO, "Invalid Format\n"));
+ break;
+ case 0xB:
+ DEBUG ((DEBUG_INFO, "Firmware Application Requires Conventional Reset\n"));
+ break;
+ case 0xC:
+ DEBUG ((DEBUG_INFO, "Invalid Queue Deletion\n"));
+ break;
+ case 0xD:
+ DEBUG ((DEBUG_INFO, "Feature Identifier Not Saveable\n"));
+ break;
+ case 0xE:
+ DEBUG ((DEBUG_INFO, "Feature Not Changeable\n"));
+ break;
+ case 0xF:
+ DEBUG ((DEBUG_INFO, "Feature Not Namespace Specific\n"));
+ break;
+ case 0x10:
+ DEBUG ((DEBUG_INFO, "Firmware Application Requires NVM Subsystem Reset\n"));
+ break;
+ case 0x80:
+ DEBUG ((DEBUG_INFO, "Conflicting Attributes\n"));
+ break;
+ case 0x81:
+ DEBUG ((DEBUG_INFO, "Invalid Protection Information\n"));
+ break;
+ case 0x82:
+ DEBUG ((DEBUG_INFO, "Attempted Write to Read Only Range\n"));
+ break;
+ }
+ break;
+
+ case 0x2:
+ switch (Cq->Sc) {
+ case 0x80:
+ DEBUG ((DEBUG_INFO, "Write Fault\n"));
+ break;
+ case 0x81:
+ DEBUG ((DEBUG_INFO, "Unrecovered Read Error\n"));
+ break;
+ case 0x82:
+ DEBUG ((DEBUG_INFO, "End-to-end Guard Check Error\n"));
+ break;
+ case 0x83:
+ DEBUG ((DEBUG_INFO, "End-to-end Application Tag Check Error\n"));
+ break;
+ case 0x84:
+ DEBUG ((DEBUG_INFO, "End-to-end Reference Tag Check Error\n"));
+ break;
+ case 0x85:
+ DEBUG ((DEBUG_INFO, "Compare Failure\n"));
+ break;
+ case 0x86:
+ DEBUG ((DEBUG_INFO, "Access Denied\n"));
+ break;
+ }
+ break;
+
+ default:
+ DEBUG ((DEBUG_INFO, "Unknown error\n"));
+ break;
+ }
+
+ return EFI_DEVICE_ERROR;
+}
+
+/**
+ Sends an NVM Express Command Packet to an NVM Express controller or namespace. This function only
+ supports blocking execution of the command.
+
+ @param[in] Private The pointer to the NVME_CONTEXT Data structure.
+ @param[in] NamespaceId Is a 32 bit Namespace ID to which the Express HCI command packet will
+ be sent.
+ A Value of 0 denotes the NVM Express controller, a Value of all 0FFh in
+ the namespace ID specifies that the command packet should be sent to all
+ valid namespaces.
+ @param[in,out] Packet A pointer to the EDKII PEI NVM Express PassThru Command Packet to send
+ to the NVMe namespace specified by NamespaceId.
+
+ @retval EFI_SUCCESS The EDKII PEI NVM Express Command Packet was sent by the host.
+ TransferLength bytes were transferred to, or from DataBuffer.
+ @retval EFI_NOT_READY The EDKII PEI NVM Express Command Packet could not be sent because
+ the controller is not ready. The caller may retry again later.
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to send the EDKII PEI NVM
+ Express Command Packet.
+ @retval EFI_INVALID_PARAMETER Namespace, or the contents of EDKII_PEI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET
+ are invalid.
+ The EDKII PEI NVM Express Command Packet was not sent, so no
+ additional status information is available.
+ @retval EFI_UNSUPPORTED The command described by the EDKII PEI NVM Express Command Packet
+ is not supported by the host adapter.
+ The EDKII PEI NVM Express Command Packet was not sent, so no
+ additional status information is available.
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the EDKII PEI NVM Express Command
+ Packet to execute.
+
+**/
+EFI_STATUS
+NvmePassThruExecute (
+ IN PEI_NVME_CONTROLLER_PRIVATE_DATA *Private,
+ IN UINT32 NamespaceId,
+ IN OUT EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET *Packet
+ )
+{
+ EFI_STATUS Status;
+ NVME_SQ *Sq;
+ NVME_CQ *Cq;
+ UINT8 QueueId;
+ UINTN SqSize;
+ UINTN CqSize;
+ EDKII_IOMMU_OPERATION MapOp;
+ UINTN MapLength;
+ EFI_PHYSICAL_ADDRESS PhyAddr;
+ VOID *MapData;
+ VOID *MapMeta;
+ UINT32 Bytes;
+ UINT32 Offset;
+ UINT32 Data32;
+ UINT64 Timer;
+
+ //
+ // Check the data fields in Packet parameter
+ //
+ if (Packet == NULL) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "%a, Invalid parameter: Packet(%lx)\n",
+ __FUNCTION__,
+ (UINTN)Packet
+ ));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((Packet->NvmeCmd == NULL) || (Packet->NvmeCompletion == NULL)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "%a, Invalid parameter: NvmeCmd (%lx)/NvmeCompletion(%lx)\n",
+ __FUNCTION__,
+ (UINTN)Packet->NvmeCmd,
+ (UINTN)Packet->NvmeCompletion
+ ));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Packet->QueueType != NVME_ADMIN_QUEUE && Packet->QueueType != NVME_IO_QUEUE) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "%a, Invalid parameter: QueueId(%lx)\n",
+ __FUNCTION__,
+ (UINTN)Packet->QueueType
+ ));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ QueueId = Packet->QueueType;
+ Sq = Private->SqBuffer[QueueId] + Private->SqTdbl[QueueId].Sqt;
+ Cq = Private->CqBuffer[QueueId] + Private->CqHdbl[QueueId].Cqh;
+ if (QueueId == NVME_ADMIN_QUEUE) {
+ SqSize = NVME_ASQ_SIZE + 1;
+ CqSize = NVME_ACQ_SIZE + 1;
+ } else {
+ SqSize = NVME_CSQ_SIZE + 1;
+ CqSize = NVME_CCQ_SIZE + 1;
+ }
+
+ if (Packet->NvmeCmd->Nsid != NamespaceId) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "%a: Nsid mismatch (%x, %x)\n",
+ __FUNCTION__,
+ Packet->NvmeCmd->Nsid,
+ NamespaceId
+ ));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ZeroMem (Sq, sizeof (NVME_SQ));
+ Sq->Opc = (UINT8)Packet->NvmeCmd->Cdw0.Opcode;
+ Sq->Fuse = (UINT8)Packet->NvmeCmd->Cdw0.FusedOperation;
+ Sq->Cid = Private->Cid[QueueId]++;;
+ Sq->Nsid = Packet->NvmeCmd->Nsid;
+
+ //
+ // Currently we only support PRP for data transfer, SGL is NOT supported
+ //
+ ASSERT (Sq->Psdt == 0);
+ if (Sq->Psdt != 0) {
+ DEBUG ((DEBUG_ERROR, "%a: Does not support SGL mechanism.\n", __FUNCTION__));
+ return EFI_UNSUPPORTED;
+ }
+
+ Sq->Prp[0] = (UINT64)(UINTN)Packet->TransferBuffer;
+ Sq->Prp[1] = 0;
+ MapData = NULL;
+ MapMeta = NULL;
+ Status = EFI_SUCCESS;
+ //
+ // If the NVMe cmd has data in or out, then mapping the user buffer to the PCI controller
+ // specific addresses.
+ //
+ if ((Sq->Opc & (BIT0 | BIT1)) != 0) {
+ if (((Packet->TransferLength != 0) && (Packet->TransferBuffer == NULL)) ||
+ ((Packet->TransferLength == 0) && (Packet->TransferBuffer != NULL))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Currently, we only support creating IO submission/completion queues that are
+ // allocated internally by the driver.
+ //
+ if ((Packet->QueueType == NVME_ADMIN_QUEUE) &&
+ ((Sq->Opc == NVME_ADMIN_CRIOCQ_CMD) || (Sq->Opc == NVME_ADMIN_CRIOSQ_CMD))) {
+ if ((Packet->TransferBuffer != Private->SqBuffer[NVME_IO_QUEUE]) &&
+ (Packet->TransferBuffer != Private->CqBuffer[NVME_IO_QUEUE])) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "%a: Does not support external IO queues creation request.\n",
+ __FUNCTION__
+ ));
+ return EFI_UNSUPPORTED;
+ }
+ } else {
+ if ((Sq->Opc & BIT0) != 0) {
+ MapOp = EdkiiIoMmuOperationBusMasterRead;
+ } else {
+ MapOp = EdkiiIoMmuOperationBusMasterWrite;
+ }
+
+ if ((Packet->TransferLength != 0) && (Packet->TransferBuffer != NULL)) {
+ MapLength = Packet->TransferLength;
+ Status = IoMmuMap (
+ MapOp,
+ Packet->TransferBuffer,
+ &MapLength,
+ &PhyAddr,
+ &MapData
+ );
+ if (EFI_ERROR (Status) || (MapLength != Packet->TransferLength)) {
+ Status = EFI_OUT_OF_RESOURCES;
+ DEBUG ((DEBUG_ERROR, "%a: Fail to map data buffer.\n", __FUNCTION__));
+ goto Exit;
+ }
+
+ Sq->Prp[0] = PhyAddr;
+ }
+
+ if((Packet->MetadataLength != 0) && (Packet->MetadataBuffer != NULL)) {
+ MapLength = Packet->MetadataLength;
+ Status = IoMmuMap (
+ MapOp,
+ Packet->MetadataBuffer,
+ &MapLength,
+ &PhyAddr,
+ &MapMeta
+ );
+ if (EFI_ERROR (Status) || (MapLength != Packet->MetadataLength)) {
+ Status = EFI_OUT_OF_RESOURCES;
+ DEBUG ((DEBUG_ERROR, "%a: Fail to map meta data buffer.\n", __FUNCTION__));
+ goto Exit;
+ }
+ Sq->Mptr = PhyAddr;
+ }
+ }
+ }
+
+ //
+ // If the Buffer Size spans more than two memory pages (page Size as defined in CC.Mps),
+ // then build a PRP list in the second PRP submission queue entry.
+ //
+ Offset = ((UINT32)Sq->Prp[0]) & (EFI_PAGE_SIZE - 1);
+ Bytes = Packet->TransferLength;
+
+ if ((Offset + Bytes) > (EFI_PAGE_SIZE * 2)) {
+ //
+ // Create PrpList for remaining Data Buffer.
+ //
+ PhyAddr = (Sq->Prp[0] + EFI_PAGE_SIZE) & ~(EFI_PAGE_SIZE - 1);
+ Sq->Prp[1] = NvmeCreatePrpList (
+ Private,
+ PhyAddr,
+ EFI_SIZE_TO_PAGES(Offset + Bytes) - 1
+ );
+ if (Sq->Prp[1] == 0) {
+ Status = EFI_OUT_OF_RESOURCES;
+ DEBUG ((DEBUG_ERROR, "%a: Create PRP list fail, Status - %r\n", __FUNCTION__, Status));
+ goto Exit;
+ }
+
+ } else if ((Offset + Bytes) > EFI_PAGE_SIZE) {
+ Sq->Prp[1] = (Sq->Prp[0] + EFI_PAGE_SIZE) & ~(EFI_PAGE_SIZE - 1);
+ }
+
+ if (Packet->NvmeCmd->Flags & CDW10_VALID) {
+ Sq->Payload.Raw.Cdw10 = Packet->NvmeCmd->Cdw10;
+ }
+ if (Packet->NvmeCmd->Flags & CDW11_VALID) {
+ Sq->Payload.Raw.Cdw11 = Packet->NvmeCmd->Cdw11;
+ }
+ if (Packet->NvmeCmd->Flags & CDW12_VALID) {
+ Sq->Payload.Raw.Cdw12 = Packet->NvmeCmd->Cdw12;
+ }
+ if (Packet->NvmeCmd->Flags & CDW13_VALID) {
+ Sq->Payload.Raw.Cdw13 = Packet->NvmeCmd->Cdw13;
+ }
+ if (Packet->NvmeCmd->Flags & CDW14_VALID) {
+ Sq->Payload.Raw.Cdw14 = Packet->NvmeCmd->Cdw14;
+ }
+ if (Packet->NvmeCmd->Flags & CDW15_VALID) {
+ Sq->Payload.Raw.Cdw15 = Packet->NvmeCmd->Cdw15;
+ }
+
+ //
+ // Ring the submission queue doorbell.
+ //
+ Private->SqTdbl[QueueId].Sqt++;
+ if (Private->SqTdbl[QueueId].Sqt == SqSize) {
+ Private->SqTdbl[QueueId].Sqt = 0;
+ }
+ Data32 = ReadUnaligned32 ((UINT32 *)&Private->SqTdbl[QueueId]);
+ Status = NVME_SET_SQTDBL (Private, QueueId, &Data32);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: NVME_SET_SQTDBL fail, Status - %r\n", __FUNCTION__, Status));
+ goto Exit;
+ }
+
+ //
+ // Wait for completion queue to get filled in.
+ //
+ Status = EFI_TIMEOUT;
+ Timer = 0;
+ while (Timer < Packet->CommandTimeout) {
+ if (Cq->Pt != Private->Pt[QueueId]) {
+ Status = EFI_SUCCESS;
+ break;
+ }
+
+ MicroSecondDelay (NVME_POLL_INTERVAL);
+ Timer += NVME_POLL_INTERVAL;
+ }
+
+ if (Status == EFI_TIMEOUT) {
+ //
+ // Timeout occurs for an NVMe command, reset the controller to abort the outstanding command
+ //
+ DEBUG ((DEBUG_ERROR, "%a: Timeout occurs for the PassThru command.\n", __FUNCTION__));
+ Status = NvmeControllerInit (Private);
+ if (EFI_ERROR (Status)) {
+ Status = EFI_DEVICE_ERROR;
+ } else {
+ //
+ // Return EFI_TIMEOUT to indicate a timeout occurs for PassThru command
+ //
+ Status = EFI_TIMEOUT;
+ }
+ goto Exit;
+ }
+
+ //
+ // Move forward the Completion Queue head
+ //
+ Private->CqHdbl[QueueId].Cqh++;
+ if (Private->CqHdbl[QueueId].Cqh == CqSize) {
+ Private->CqHdbl[QueueId].Cqh = 0;
+ Private->Pt[QueueId] ^= 1;
+ }
+
+ //
+ // Copy the Respose Queue entry for this command to the callers response buffer
+ //
+ CopyMem (Packet->NvmeCompletion, Cq, sizeof (EFI_NVM_EXPRESS_COMPLETION));
+
+ //
+ // Check the NVMe cmd execution result
+ //
+ Status = NvmeCheckCqStatus (Cq);
+ NVME_SET_CQHDBL (Private, QueueId, &Private->CqHdbl[QueueId]);
+
+Exit:
+ if (MapMeta != NULL) {
+ IoMmuUnmap (MapMeta);
+ }
+
+ if (MapData != NULL) {
+ IoMmuUnmap (MapData);
+ }
+
+ return Status;
+}
+
+/**
+ Gets the device path information of the underlying NVM Express host controller.
+
+ @param[in] This The PPI instance pointer.
+ @param[out] DevicePathLength The length of the device path in bytes specified
+ by DevicePath.
+ @param[out] DevicePath The device path of the underlying NVM Express
+ host controller.
+ This field re-uses EFI Device Path Protocol as
+ defined by Section 10.2 EFI Device Path Protocol
+ of UEFI 2.7 Specification.
+
+ @retval EFI_SUCCESS The operation succeeds.
+ @retval EFI_INVALID_PARAMETER DevicePathLength or DevicePath is NULL.
+ @retval EFI_OUT_OF_RESOURCES The operation fails due to lack of resources.
+
+**/
+EFI_STATUS
+EFIAPI
+NvmePassThruGetDevicePath (
+ IN EDKII_PEI_NVM_EXPRESS_PASS_THRU_PPI *This,
+ OUT UINTN *DevicePathLength,
+ OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath
+ )
+{
+ PEI_NVME_CONTROLLER_PRIVATE_DATA *Private;
+
+ if (This == NULL || DevicePathLength == NULL || DevicePath == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_NVME_PASSTHRU (This);
+
+ *DevicePathLength = Private->DevicePathLength;
+ *DevicePath = AllocateCopyPool (Private->DevicePathLength, Private->DevicePath);
+ if (*DevicePath == NULL) {
+ *DevicePathLength = 0;
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Used to retrieve the next namespace ID for this NVM Express controller.
+
+ If on input the value pointed to by NamespaceId is 0xFFFFFFFF, then the first
+ valid namespace ID defined on the NVM Express controller is returned in the
+ location pointed to by NamespaceId and a status of EFI_SUCCESS is returned.
+
+ If on input the value pointed to by NamespaceId is an invalid namespace ID
+ other than 0xFFFFFFFF, then EFI_INVALID_PARAMETER is returned.
+
+ If on input the value pointed to by NamespaceId is a valid namespace ID, then
+ the next valid namespace ID on the NVM Express controller is returned in the
+ location pointed to by NamespaceId, and EFI_SUCCESS is returned.
+
+ If the value pointed to by NamespaceId is the namespace ID of the last
+ namespace on the NVM Express controller, then EFI_NOT_FOUND is returned.
+
+ @param[in] This The PPI instance pointer.
+ @param[in,out] NamespaceId On input, a pointer to a legal NamespaceId
+ for an NVM Express namespace present on the
+ NVM Express controller. On output, a pointer
+ to the next NamespaceId of an NVM Express
+ namespace on an NVM Express controller. An
+ input value of 0xFFFFFFFF retrieves the
+ first NamespaceId for an NVM Express
+ namespace present on an NVM Express
+ controller.
+
+ @retval EFI_SUCCESS The Namespace ID of the next Namespace was
+ returned.
+ @retval EFI_NOT_FOUND There are no more namespaces defined on this
+ controller.
+ @retval EFI_INVALID_PARAMETER NamespaceId is an invalid value other than
+ 0xFFFFFFFF.
+
+**/
+EFI_STATUS
+EFIAPI
+NvmePassThruGetNextNameSpace (
+ IN EDKII_PEI_NVM_EXPRESS_PASS_THRU_PPI *This,
+ IN OUT UINT32 *NamespaceId
+ )
+{
+ PEI_NVME_CONTROLLER_PRIVATE_DATA *Private;
+ UINT32 DeviceIndex;
+ EFI_STATUS Status;
+
+ if (This == NULL || NamespaceId == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_NVME_PASSTHRU (This);
+
+ Status = EFI_NOT_FOUND;
+
+ //
+ // If active namespace number is 0, then valid namespace ID is unavailable
+ //
+ if (Private->ActiveNamespaceNum == 0) {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // If the NamespaceId input value is 0xFFFFFFFF, then get the first valid namespace ID
+ //
+ if (*NamespaceId == 0xFFFFFFFF) {
+ //
+ // Start with the first namespace ID
+ //
+ *NamespaceId = Private->NamespaceInfo[0].NamespaceId;
+ Status = EFI_SUCCESS;
+ } else {
+ if (*NamespaceId > Private->ControllerData->Nn) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((*NamespaceId + 1) > Private->ControllerData->Nn) {
+ return EFI_NOT_FOUND;
+ }
+
+ for (DeviceIndex = 0; DeviceIndex < Private->ActiveNamespaceNum; DeviceIndex++) {
+ if (*NamespaceId == Private->NamespaceInfo[DeviceIndex].NamespaceId) {
+ if ((DeviceIndex + 1) < Private->ActiveNamespaceNum) {
+ *NamespaceId = Private->NamespaceInfo[DeviceIndex + 1].NamespaceId;
+ Status = EFI_SUCCESS;
+ }
+ break;
+ }
+ }
+ }
+
+ return Status;
+
+}
+
+/**
+ Sends an NVM Express Command Packet to an NVM Express controller or namespace. This function only
+ supports blocking execution of the command.
+
+ @param[in] This The PPI instance pointer.
+ @param[in] NamespaceId Is a 32 bit Namespace ID to which the Nvm Express command packet will
+ be sent.
+ A Value of 0 denotes the NVM Express controller, a Value of all 0FFh in
+ the namespace ID specifies that the command packet should be sent to all
+ valid namespaces.
+ @param[in,out] Packet A pointer to the EDKII PEI NVM Express PassThru Command Packet to send
+ to the NVMe namespace specified by NamespaceId.
+
+ @retval EFI_SUCCESS The EDKII PEI NVM Express Command Packet was sent by the host.
+ TransferLength bytes were transferred to, or from DataBuffer.
+ @retval EFI_NOT_READY The EDKII PEI NVM Express Command Packet could not be sent because
+ the controller is not ready. The caller may retry again later.
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to send the EDKII PEI NVM
+ Express Command Packet.
+ @retval EFI_INVALID_PARAMETER Namespace, or the contents of EDKII_PEI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET
+ are invalid.
+ The EDKII PEI NVM Express Command Packet was not sent, so no
+ additional status information is available.
+ @retval EFI_UNSUPPORTED The command described by the EDKII PEI NVM Express Command Packet
+ is not supported by the host adapter.
+ The EDKII PEI NVM Express Command Packet was not sent, so no
+ additional status information is available.
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the EDKII PEI NVM Express Command
+ Packet to execute.
+
+**/
+EFI_STATUS
+EFIAPI
+NvmePassThru (
+ IN EDKII_PEI_NVM_EXPRESS_PASS_THRU_PPI *This,
+ IN UINT32 NamespaceId,
+ IN OUT EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET *Packet
+ )
+{
+ PEI_NVME_CONTROLLER_PRIVATE_DATA *Private;
+ EFI_STATUS Status;
+
+ if (This == NULL || Packet == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_NVME_PASSTHRU (This);
+ //
+ // Check NamespaceId is valid or not.
+ //
+ if ((NamespaceId > Private->ControllerData->Nn) &&
+ (NamespaceId != (UINT32) -1)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = NvmePassThruExecute (
+ Private,
+ NamespaceId,
+ Packet
+ );
+
+ return Status;
+
+}
+
diff --git a/roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiPassThru.h b/roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiPassThru.h
new file mode 100644
index 000000000..00e8f0abd
--- /dev/null
+++ b/roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiPassThru.h
@@ -0,0 +1,161 @@
+/** @file
+ The NvmExpressPei driver is used to manage non-volatile memory subsystem
+ which follows NVM Express specification at PEI phase.
+
+ Copyright (c) 2018 - 2019, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef _NVM_EXPRESS_PEI_PASSTHRU_H_
+#define _NVM_EXPRESS_PEI_PASSTHRU_H_
+
+
+
+/**
+ Sends an NVM Express Command Packet to an NVM Express controller or namespace. This function only
+ supports blocking execution of the command.
+
+ @param[in] Private The pointer to the NVME_CONTEXT Data structure.
+ @param[in] NamespaceId Is a 32 bit Namespace ID to which the Express HCI command packet will
+ be sent.
+ A Value of 0 denotes the NVM Express controller, a Value of all 0FFh in
+ the namespace ID specifies that the command packet should be sent to all
+ valid namespaces.
+ @param[in,out] Packet A pointer to the EDKII PEI NVM Express PassThru Command Packet to send
+ to the NVMe namespace specified by NamespaceId.
+
+ @retval EFI_SUCCESS The EDKII PEI NVM Express Command Packet was sent by the host.
+ TransferLength bytes were transferred to, or from DataBuffer.
+ @retval EFI_NOT_READY The EDKII PEI NVM Express Command Packet could not be sent because
+ the controller is not ready. The caller may retry again later.
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to send the EDKII PEI NVM
+ Express Command Packet.
+ @retval EFI_INVALID_PARAMETER Namespace, or the contents of EDKII_PEI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET
+ are invalid.
+ The EDKII PEI NVM Express Command Packet was not sent, so no
+ additional status information is available.
+ @retval EFI_UNSUPPORTED The command described by the EDKII PEI NVM Express Command Packet
+ is not supported by the host adapter.
+ The EDKII PEI NVM Express Command Packet was not sent, so no
+ additional status information is available.
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the EDKII PEI NVM Express Command
+ Packet to execute.
+
+**/
+EFI_STATUS
+NvmePassThruExecute (
+ IN PEI_NVME_CONTROLLER_PRIVATE_DATA *Private,
+ IN UINT32 NamespaceId,
+ IN OUT EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET *Packet
+ );
+
+/**
+ Gets the device path information of the underlying NVM Express host controller.
+
+ @param[in] This The PPI instance pointer.
+ @param[out] DevicePathLength The length of the device path in bytes specified
+ by DevicePath.
+ @param[out] DevicePath The device path of the underlying NVM Express
+ host controller.
+ This field re-uses EFI Device Path Protocol as
+ defined by Section 10.2 EFI Device Path Protocol
+ of UEFI 2.7 Specification.
+
+ @retval EFI_SUCCESS The operation succeeds.
+ @retval EFI_INVALID_PARAMETER DevicePathLength or DevicePath is NULL.
+ @retval EFI_OUT_OF_RESOURCES The operation fails due to lack of resources.
+
+**/
+EFI_STATUS
+EFIAPI
+NvmePassThruGetDevicePath (
+ IN EDKII_PEI_NVM_EXPRESS_PASS_THRU_PPI *This,
+ OUT UINTN *DevicePathLength,
+ OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath
+ );
+
+/**
+ Used to retrieve the next namespace ID for this NVM Express controller.
+
+ If on input the value pointed to by NamespaceId is 0xFFFFFFFF, then the first
+ valid namespace ID defined on the NVM Express controller is returned in the
+ location pointed to by NamespaceId and a status of EFI_SUCCESS is returned.
+
+ If on input the value pointed to by NamespaceId is an invalid namespace ID
+ other than 0xFFFFFFFF, then EFI_INVALID_PARAMETER is returned.
+
+ If on input the value pointed to by NamespaceId is a valid namespace ID, then
+ the next valid namespace ID on the NVM Express controller is returned in the
+ location pointed to by NamespaceId, and EFI_SUCCESS is returned.
+
+ If the value pointed to by NamespaceId is the namespace ID of the last
+ namespace on the NVM Express controller, then EFI_NOT_FOUND is returned.
+
+ @param[in] This The PPI instance pointer.
+ @param[in,out] NamespaceId On input, a pointer to a legal NamespaceId
+ for an NVM Express namespace present on the
+ NVM Express controller. On output, a pointer
+ to the next NamespaceId of an NVM Express
+ namespace on an NVM Express controller. An
+ input value of 0xFFFFFFFF retrieves the
+ first NamespaceId for an NVM Express
+ namespace present on an NVM Express
+ controller.
+
+ @retval EFI_SUCCESS The Namespace ID of the next Namespace was
+ returned.
+ @retval EFI_NOT_FOUND There are no more namespaces defined on this
+ controller.
+ @retval EFI_INVALID_PARAMETER NamespaceId is an invalid value other than
+ 0xFFFFFFFF.
+
+**/
+EFI_STATUS
+EFIAPI
+NvmePassThruGetNextNameSpace (
+ IN EDKII_PEI_NVM_EXPRESS_PASS_THRU_PPI *This,
+ IN OUT UINT32 *NamespaceId
+ );
+
+/**
+ Sends an NVM Express Command Packet to an NVM Express controller or namespace. This function only
+ supports blocking execution of the command.
+
+ @param[in] This The PPI instance pointer.
+ @param[in] NamespaceId Is a 32 bit Namespace ID to which the Nvm Express command packet will
+ be sent.
+ A Value of 0 denotes the NVM Express controller, a Value of all 0FFh in
+ the namespace ID specifies that the command packet should be sent to all
+ valid namespaces.
+ @param[in,out] Packet A pointer to the EDKII PEI NVM Express PassThru Command Packet to send
+ to the NVMe namespace specified by NamespaceId.
+
+ @retval EFI_SUCCESS The EDKII PEI NVM Express Command Packet was sent by the host.
+ TransferLength bytes were transferred to, or from DataBuffer.
+ @retval EFI_NOT_READY The EDKII PEI NVM Express Command Packet could not be sent because
+ the controller is not ready. The caller may retry again later.
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to send the EDKII PEI NVM
+ Express Command Packet.
+ @retval EFI_INVALID_PARAMETER Namespace, or the contents of EDKII_PEI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET
+ are invalid.
+ The EDKII PEI NVM Express Command Packet was not sent, so no
+ additional status information is available.
+ @retval EFI_UNSUPPORTED The command described by the EDKII PEI NVM Express Command Packet
+ is not supported by the host adapter.
+ The EDKII PEI NVM Express Command Packet was not sent, so no
+ additional status information is available.
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the EDKII PEI NVM Express Command
+ Packet to execute.
+
+**/
+EFI_STATUS
+EFIAPI
+NvmePassThru (
+ IN EDKII_PEI_NVM_EXPRESS_PASS_THRU_PPI *This,
+ IN UINT32 NamespaceId,
+ IN OUT EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET *Packet
+ );
+
+#endif
diff --git a/roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiS3.c b/roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiS3.c
new file mode 100644
index 000000000..f409285d5
--- /dev/null
+++ b/roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiS3.c
@@ -0,0 +1,107 @@
+/** @file
+ The NvmExpressPei driver is used to manage non-volatile memory subsystem
+ which follows NVM Express specification at PEI phase.
+
+ Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "NvmExpressPei.h"
+
+#include <Guid/S3StorageDeviceInitList.h>
+
+#include <Library/LockBoxLib.h>
+
+/**
+ Determine if a specific NVM Express controller can be skipped for S3 phase.
+
+ @param[in] HcDevicePath Device path of the controller.
+ @param[in] HcDevicePathLength Length of the device path specified by
+ HcDevicePath.
+
+ @retval The number of ports that need to be enumerated.
+
+**/
+BOOLEAN
+NvmeS3SkipThisController (
+ IN EFI_DEVICE_PATH_PROTOCOL *HcDevicePath,
+ IN UINTN HcDevicePathLength
+ )
+{
+ EFI_STATUS Status;
+ UINT8 DummyData;
+ UINTN S3InitDevicesLength;
+ EFI_DEVICE_PATH_PROTOCOL *S3InitDevices;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePathInst;
+ UINTN DevicePathInstLength;
+ BOOLEAN EntireEnd;
+ BOOLEAN Skip;
+
+ //
+ // From the LockBox, get the list of device paths for devices need to be
+ // initialized in S3.
+ //
+ S3InitDevices = NULL;
+ S3InitDevicesLength = sizeof (DummyData);
+ EntireEnd = FALSE;
+ Skip = TRUE;
+ Status = RestoreLockBox (&gS3StorageDeviceInitListGuid, &DummyData, &S3InitDevicesLength);
+ if (Status != EFI_BUFFER_TOO_SMALL) {
+ return Skip;
+ } else {
+ S3InitDevices = AllocatePool (S3InitDevicesLength);
+ if (S3InitDevices == NULL) {
+ return Skip;
+ }
+
+ Status = RestoreLockBox (&gS3StorageDeviceInitListGuid, S3InitDevices, &S3InitDevicesLength);
+ if (EFI_ERROR (Status)) {
+ return Skip;
+ }
+ }
+
+ if (S3InitDevices == NULL) {
+ return Skip;
+ }
+
+ //
+ // Only need to initialize the controllers that exist in the device list.
+ //
+ do {
+ //
+ // Fetch the size of current device path instance.
+ //
+ Status = GetDevicePathInstanceSize (
+ S3InitDevices,
+ &DevicePathInstLength,
+ &EntireEnd
+ );
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+
+ DevicePathInst = S3InitDevices;
+ S3InitDevices = (EFI_DEVICE_PATH_PROTOCOL *)((UINTN) S3InitDevices + DevicePathInstLength);
+
+ if (HcDevicePathLength >= DevicePathInstLength) {
+ continue;
+ }
+
+ //
+ // Compare the device paths to determine if the device is managed by this
+ // controller.
+ //
+ if (CompareMem (
+ DevicePathInst,
+ HcDevicePath,
+ HcDevicePathLength - sizeof (EFI_DEVICE_PATH_PROTOCOL)
+ ) == 0) {
+ Skip = FALSE;
+ break;
+ }
+ } while (!EntireEnd);
+
+ return Skip;
+}
diff --git a/roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiStorageSecurity.c b/roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiStorageSecurity.c
new file mode 100644
index 000000000..094d61bb8
--- /dev/null
+++ b/roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiStorageSecurity.c
@@ -0,0 +1,418 @@
+/** @file
+ The NvmExpressPei driver is used to manage non-volatile memory subsystem
+ which follows NVM Express specification at PEI phase.
+
+ Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "NvmExpressPei.h"
+
+/**
+ Trust transfer data from/to NVM Express device.
+
+ This function performs one NVMe transaction to do a trust transfer from/to NVM
+ Express device.
+
+ @param[in] Private The pointer to the PEI_NVME_CONTROLLER_PRIVATE_DATA
+ data structure.
+ @param[in,out] Buffer The pointer to the current transaction buffer.
+ @param[in] SecurityProtocolId
+ The value of the "Security Protocol" parameter
+ of the security protocol command to be sent.
+ @param[in] SecurityProtocolSpecificData
+ The value of the "Security Protocol Specific"
+ parameter of the security protocol command to
+ be sent.
+ @param[in] TransferLength The block number or sector count of the transfer.
+ @param[in] IsTrustSend Indicates whether it is a trust send operation
+ or not.
+ @param[in] Timeout The timeout, in 100ns units, to use for the
+ execution of the security protocol command.
+ A Timeout value of 0 means that this function
+ will wait indefinitely for the security protocol
+ command to execute. If Timeout is greater than
+ zero, then this function will return EFI_TIMEOUT
+ if the time required to execute the receive
+ data command is greater than Timeout.
+ @param[out] TransferLengthOut A pointer to a buffer to store the size in bytes
+ of the data written to the buffer. Ignore it
+ when IsTrustSend is TRUE.
+
+ @retval EFI_SUCCESS The data transfer is complete successfully.
+ @return others Some error occurs when transferring data.
+
+**/
+EFI_STATUS
+TrustTransferNvmeDevice (
+ IN PEI_NVME_CONTROLLER_PRIVATE_DATA *Private,
+ IN OUT VOID *Buffer,
+ IN UINT8 SecurityProtocolId,
+ IN UINT16 SecurityProtocolSpecificData,
+ IN UINTN TransferLength,
+ IN BOOLEAN IsTrustSend,
+ IN UINT64 Timeout,
+ OUT UINTN *TransferLengthOut
+ )
+{
+ EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket;
+ EFI_NVM_EXPRESS_COMMAND Command;
+ EFI_NVM_EXPRESS_COMPLETION Completion;
+ EFI_STATUS Status;
+ UINT16 SpecificData;
+ EDKII_PEI_NVM_EXPRESS_PASS_THRU_PPI *NvmePassThru;
+
+ NvmePassThru = &Private->NvmePassThruPpi;
+ ZeroMem (&CommandPacket, sizeof(EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));
+ ZeroMem (&Command, sizeof(EFI_NVM_EXPRESS_COMMAND));
+ ZeroMem (&Completion, sizeof(EFI_NVM_EXPRESS_COMPLETION));
+
+ CommandPacket.NvmeCmd = &Command;
+ CommandPacket.NvmeCompletion = &Completion;
+
+ //
+ // Change Endianness of SecurityProtocolSpecificData
+ //
+ SpecificData = (((SecurityProtocolSpecificData << 8) & 0xFF00) | (SecurityProtocolSpecificData >> 8));
+
+ if (IsTrustSend) {
+ Command.Cdw0.Opcode = NVME_ADMIN_SECURITY_SEND_CMD;
+ CommandPacket.TransferBuffer = Buffer;
+ CommandPacket.TransferLength = (UINT32)TransferLength;
+ CommandPacket.NvmeCmd->Cdw10 = (UINT32)((SecurityProtocolId << 24) | (SpecificData << 8));
+ CommandPacket.NvmeCmd->Cdw11 = (UINT32)TransferLength;
+ } else {
+ Command.Cdw0.Opcode = NVME_ADMIN_SECURITY_RECEIVE_CMD;
+ CommandPacket.TransferBuffer = Buffer;
+ CommandPacket.TransferLength = (UINT32)TransferLength;
+ CommandPacket.NvmeCmd->Cdw10 = (UINT32)((SecurityProtocolId << 24) | (SpecificData << 8));
+ CommandPacket.NvmeCmd->Cdw11 = (UINT32)TransferLength;
+ }
+
+ CommandPacket.NvmeCmd->Flags = CDW10_VALID | CDW11_VALID;
+ CommandPacket.NvmeCmd->Nsid = NVME_CONTROLLER_NSID;
+ CommandPacket.CommandTimeout = Timeout;
+ CommandPacket.QueueType = NVME_ADMIN_QUEUE;
+
+ Status = NvmePassThru->PassThru (
+ NvmePassThru,
+ NVME_CONTROLLER_NSID,
+ &CommandPacket
+ );
+
+ if (!IsTrustSend) {
+ if (EFI_ERROR (Status)) {
+ *TransferLengthOut = 0;
+ } else {
+ *TransferLengthOut = (UINTN) TransferLength;
+ }
+ }
+
+ return Status;
+}
+
+/**
+ Gets the count of storage security devices that one specific driver detects.
+
+ @param[in] This The PPI instance pointer.
+ @param[out] NumberofDevices The number of storage security devices discovered.
+
+ @retval EFI_SUCCESS The operation performed successfully.
+ @retval EFI_INVALID_PARAMETER The parameters are invalid.
+
+**/
+EFI_STATUS
+EFIAPI
+NvmeStorageSecurityGetDeviceNo (
+ IN EDKII_PEI_STORAGE_SECURITY_CMD_PPI *This,
+ OUT UINTN *NumberofDevices
+ )
+{
+ PEI_NVME_CONTROLLER_PRIVATE_DATA *Private;
+
+ if (This == NULL || NumberofDevices == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_STROAGE_SECURITY (This);
+ *NumberofDevices = Private->ActiveNamespaceNum;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Gets the device path of a specific storage security device.
+
+ @param[in] This The PPI instance pointer.
+ @param[in] DeviceIndex Specifies the storage security device to which
+ the function wants to talk. Because the driver
+ that implements Storage Security Command PPIs
+ will manage multiple storage devices, the PPIs
+ that want to talk to a single device must specify
+ the device index that was assigned during the
+ enumeration process. This index is a number from
+ one to NumberofDevices.
+ @param[out] DevicePathLength The length of the device path in bytes specified
+ by DevicePath.
+ @param[out] DevicePath The device path of storage security device.
+ This field re-uses EFI Device Path Protocol as
+ defined by Section 10.2 EFI Device Path Protocol
+ of UEFI 2.7 Specification.
+
+ @retval EFI_SUCCESS The operation succeeds.
+ @retval EFI_INVALID_PARAMETER DevicePathLength or DevicePath is NULL.
+ @retval EFI_NOT_FOUND The specified storage security device not found.
+ @retval EFI_OUT_OF_RESOURCES The operation fails due to lack of resources.
+
+**/
+EFI_STATUS
+EFIAPI
+NvmeStorageSecurityGetDevicePath (
+ IN EDKII_PEI_STORAGE_SECURITY_CMD_PPI *This,
+ IN UINTN DeviceIndex,
+ OUT UINTN *DevicePathLength,
+ OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath
+ )
+{
+ PEI_NVME_CONTROLLER_PRIVATE_DATA *Private;
+
+ if (This == NULL || DevicePathLength == NULL || DevicePath == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_STROAGE_SECURITY (This);
+ if ((DeviceIndex == 0) || (DeviceIndex > Private->ActiveNamespaceNum)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ return NvmeBuildDevicePath (
+ Private,
+ Private->NamespaceInfo[DeviceIndex-1].NamespaceId,
+ Private->NamespaceInfo[DeviceIndex-1].NamespaceUuid,
+ DevicePathLength,
+ DevicePath
+ );
+}
+
+/**
+ Send a security protocol command to a device that receives data and/or the result
+ of one or more commands sent by SendData.
+
+ The ReceiveData function sends a security protocol command to the given DeviceIndex.
+ The security protocol command sent is defined by SecurityProtocolId and contains
+ the security protocol specific data SecurityProtocolSpecificData. The function
+ returns the data from the security protocol command in PayloadBuffer.
+
+ For devices supporting the SCSI command set, the security protocol command is sent
+ using the SECURITY PROTOCOL IN command defined in SPC-4.
+
+ For devices supporting the ATA command set, the security protocol command is sent
+ using one of the TRUSTED RECEIVE commands defined in ATA8-ACS if PayloadBufferSize
+ is non-zero.
+
+ If the PayloadBufferSize is zero, the security protocol command is sent using the
+ Trusted Non-Data command defined in ATA8-ACS.
+
+ If PayloadBufferSize is too small to store the available data from the security
+ protocol command, the function shall copy PayloadBufferSize bytes into the
+ PayloadBuffer and return EFI_WARN_BUFFER_TOO_SMALL.
+
+ If PayloadBuffer or PayloadTransferSize is NULL and PayloadBufferSize is non-zero,
+ the function shall return EFI_INVALID_PARAMETER.
+
+ If the given DeviceIndex does not support security protocol commands, the function
+ shall return EFI_UNSUPPORTED.
+
+ If the security protocol fails to complete within the Timeout period, the function
+ shall return EFI_TIMEOUT.
+
+ If the security protocol command completes without an error, the function shall
+ return EFI_SUCCESS. If the security protocol command completes with an error, the
+ function shall return EFI_DEVICE_ERROR.
+
+ @param[in] This The PPI instance pointer.
+ @param[in] DeviceIndex Specifies the storage security device to which the
+ function wants to talk. Because the driver that
+ implements Storage Security Command PPIs will manage
+ multiple storage devices, the PPIs that want to talk
+ to a single device must specify the device index
+ that was assigned during the enumeration process.
+ This index is a number from one to NumberofDevices.
+ @param[in] Timeout The timeout, in 100ns units, to use for the execution
+ of the security protocol command. A Timeout value
+ of 0 means that this function will wait indefinitely
+ for the security protocol command to execute. If
+ Timeout is greater than zero, then this function
+ will return EFI_TIMEOUT if the time required to
+ execute the receive data command is greater than
+ Timeout.
+ @param[in] SecurityProtocolId
+ The value of the "Security Protocol" parameter of
+ the security protocol command to be sent.
+ @param[in] SecurityProtocolSpecificData
+ The value of the "Security Protocol Specific"
+ parameter of the security protocol command to be
+ sent.
+ @param[in] PayloadBufferSize
+ Size in bytes of the payload data buffer.
+ @param[out] PayloadBuffer A pointer to a destination buffer to store the
+ security protocol command specific payload data
+ for the security protocol command. The caller is
+ responsible for having either implicit or explicit
+ ownership of the buffer.
+ @param[out] PayloadTransferSize
+ A pointer to a buffer to store the size in bytes
+ of the data written to the payload data buffer.
+
+ @retval EFI_SUCCESS The security protocol command completed
+ successfully.
+ @retval EFI_WARN_BUFFER_TOO_SMALL The PayloadBufferSize was too small to
+ store the available data from the device.
+ The PayloadBuffer contains the truncated
+ data.
+ @retval EFI_UNSUPPORTED The given DeviceIndex does not support
+ security protocol commands.
+ @retval EFI_DEVICE_ERROR The security protocol command completed
+ with an error.
+ @retval EFI_INVALID_PARAMETER The PayloadBuffer or PayloadTransferSize
+ is NULL and PayloadBufferSize is non-zero.
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the
+ security protocol command to execute.
+
+**/
+EFI_STATUS
+EFIAPI
+NvmeStorageSecurityReceiveData (
+ IN EDKII_PEI_STORAGE_SECURITY_CMD_PPI *This,
+ IN UINTN DeviceIndex,
+ IN UINT64 Timeout,
+ IN UINT8 SecurityProtocolId,
+ IN UINT16 SecurityProtocolSpecificData,
+ IN UINTN PayloadBufferSize,
+ OUT VOID *PayloadBuffer,
+ OUT UINTN *PayloadTransferSize
+ )
+{
+ PEI_NVME_CONTROLLER_PRIVATE_DATA *Private;
+ EFI_STATUS Status;
+
+ if ((PayloadBuffer == NULL) || (PayloadTransferSize == NULL) || (PayloadBufferSize == 0)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_STROAGE_SECURITY (This);
+
+ Status = TrustTransferNvmeDevice (
+ Private,
+ PayloadBuffer,
+ SecurityProtocolId,
+ SecurityProtocolSpecificData,
+ PayloadBufferSize,
+ FALSE,
+ Timeout,
+ PayloadTransferSize
+ );
+
+ return Status;
+}
+
+/**
+ Send a security protocol command to a device.
+
+ The SendData function sends a security protocol command containing the payload
+ PayloadBuffer to the given DeviceIndex. The security protocol command sent is
+ defined by SecurityProtocolId and contains the security protocol specific data
+ SecurityProtocolSpecificData. If the underlying protocol command requires a
+ specific padding for the command payload, the SendData function shall add padding
+ bytes to the command payload to satisfy the padding requirements.
+
+ For devices supporting the SCSI command set, the security protocol command is
+ sent using the SECURITY PROTOCOL OUT command defined in SPC-4.
+
+ For devices supporting the ATA command set, the security protocol command is
+ sent using one of the TRUSTED SEND commands defined in ATA8-ACS if PayloadBufferSize
+ is non-zero. If the PayloadBufferSize is zero, the security protocol command
+ is sent using the Trusted Non-Data command defined in ATA8-ACS.
+
+ If PayloadBuffer is NULL and PayloadBufferSize is non-zero, the function shall
+ return EFI_INVALID_PARAMETER.
+
+ If the given DeviceIndex does not support security protocol commands, the function
+ shall return EFI_UNSUPPORTED.
+
+ If the security protocol fails to complete within the Timeout period, the function
+ shall return EFI_TIMEOUT.
+
+ If the security protocol command completes without an error, the function shall
+ return EFI_SUCCESS. If the security protocol command completes with an error,
+ the functio shall return EFI_DEVICE_ERROR.
+
+ @param[in] This The PPI instance pointer.
+ @param[in] DeviceIndex The ID of the device.
+ @param[in] Timeout The timeout, in 100ns units, to use for the execution
+ of the security protocol command. A Timeout value
+ of 0 means that this function will wait indefinitely
+ for the security protocol command to execute. If
+ Timeout is greater than zero, then this function
+ will return EFI_TIMEOUT if the time required to
+ execute the receive data command is greater than
+ Timeout.
+ @param[in] SecurityProtocolId
+ The value of the "Security Protocol" parameter of
+ the security protocol command to be sent.
+ @param[in] SecurityProtocolSpecificData
+ The value of the "Security Protocol Specific"
+ parameter of the security protocol command to be
+ sent.
+ @param[in] PayloadBufferSize Size in bytes of the payload data buffer.
+ @param[in] PayloadBuffer A pointer to a destination buffer to store the
+ security protocol command specific payload data
+ for the security protocol command.
+
+ @retval EFI_SUCCESS The security protocol command completed successfully.
+ @retval EFI_UNSUPPORTED The given DeviceIndex does not support security
+ protocol commands.
+ @retval EFI_DEVICE_ERROR The security protocol command completed with
+ an error.
+ @retval EFI_INVALID_PARAMETER The PayloadBuffer is NULL and PayloadBufferSize
+ is non-zero.
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the security
+ protocol command to execute.
+
+**/
+EFI_STATUS
+EFIAPI
+NvmeStorageSecuritySendData (
+ IN EDKII_PEI_STORAGE_SECURITY_CMD_PPI *This,
+ IN UINTN DeviceIndex,
+ IN UINT64 Timeout,
+ IN UINT8 SecurityProtocolId,
+ IN UINT16 SecurityProtocolSpecificData,
+ IN UINTN PayloadBufferSize,
+ IN VOID *PayloadBuffer
+ )
+{
+ PEI_NVME_CONTROLLER_PRIVATE_DATA *Private;
+ EFI_STATUS Status;
+
+ if ((PayloadBuffer == NULL) && (PayloadBufferSize != 0)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_STROAGE_SECURITY (This);
+
+ Status = TrustTransferNvmeDevice (
+ Private,
+ PayloadBuffer,
+ SecurityProtocolId,
+ SecurityProtocolSpecificData,
+ PayloadBufferSize,
+ TRUE,
+ Timeout,
+ NULL
+ );
+
+ return Status;
+}
diff --git a/roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiStorageSecurity.h b/roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiStorageSecurity.h
new file mode 100644
index 000000000..18f3e1ce2
--- /dev/null
+++ b/roms/edk2/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiStorageSecurity.h
@@ -0,0 +1,240 @@
+/** @file
+ The NvmExpressPei driver is used to manage non-volatile memory subsystem
+ which follows NVM Express specification at PEI phase.
+
+ Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef _NVM_EXPRESS_PEI_STORAGE_SECURITY_H_
+#define _NVM_EXPRESS_PEI_STORAGE_SECURITY_H_
+
+/**
+ Gets the count of storage security devices that one specific driver detects.
+
+ @param[in] This The PPI instance pointer.
+ @param[out] NumberofDevices The number of storage security devices discovered.
+
+ @retval EFI_SUCCESS The operation performed successfully.
+ @retval EFI_INVALID_PARAMETER The parameters are invalid.
+
+**/
+EFI_STATUS
+EFIAPI
+NvmeStorageSecurityGetDeviceNo (
+ IN EDKII_PEI_STORAGE_SECURITY_CMD_PPI *This,
+ OUT UINTN *NumberofDevices
+ );
+
+/**
+ Gets the device path of a specific storage security device.
+
+ @param[in] This The PPI instance pointer.
+ @param[in] DeviceIndex Specifies the storage security device to which
+ the function wants to talk. Because the driver
+ that implements Storage Security Command PPIs
+ will manage multiple storage devices, the PPIs
+ that want to talk to a single device must specify
+ the device index that was assigned during the
+ enumeration process. This index is a number from
+ one to NumberofDevices.
+ @param[out] DevicePathLength The length of the device path in bytes specified
+ by DevicePath.
+ @param[out] DevicePath The device path of storage security device.
+ This field re-uses EFI Device Path Protocol as
+ defined by Section 10.2 EFI Device Path Protocol
+ of UEFI 2.7 Specification.
+
+ @retval EFI_SUCCESS The operation succeeds.
+ @retval EFI_INVALID_PARAMETER DevicePathLength or DevicePath is NULL.
+ @retval EFI_NOT_FOUND The specified storage security device not found.
+ @retval EFI_OUT_OF_RESOURCES The operation fails due to lack of resources.
+
+**/
+EFI_STATUS
+EFIAPI
+NvmeStorageSecurityGetDevicePath (
+ IN EDKII_PEI_STORAGE_SECURITY_CMD_PPI *This,
+ IN UINTN DeviceIndex,
+ OUT UINTN *DevicePathLength,
+ OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath
+ );
+
+/**
+ Send a security protocol command to a device that receives data and/or the result
+ of one or more commands sent by SendData.
+
+ The ReceiveData function sends a security protocol command to the given DeviceIndex.
+ The security protocol command sent is defined by SecurityProtocolId and contains
+ the security protocol specific data SecurityProtocolSpecificData. The function
+ returns the data from the security protocol command in PayloadBuffer.
+
+ For devices supporting the SCSI command set, the security protocol command is sent
+ using the SECURITY PROTOCOL IN command defined in SPC-4.
+
+ For devices supporting the ATA command set, the security protocol command is sent
+ using one of the TRUSTED RECEIVE commands defined in ATA8-ACS if PayloadBufferSize
+ is non-zero.
+
+ If the PayloadBufferSize is zero, the security protocol command is sent using the
+ Trusted Non-Data command defined in ATA8-ACS.
+
+ If PayloadBufferSize is too small to store the available data from the security
+ protocol command, the function shall copy PayloadBufferSize bytes into the
+ PayloadBuffer and return EFI_WARN_BUFFER_TOO_SMALL.
+
+ If PayloadBuffer or PayloadTransferSize is NULL and PayloadBufferSize is non-zero,
+ the function shall return EFI_INVALID_PARAMETER.
+
+ If the given DeviceIndex does not support security protocol commands, the function
+ shall return EFI_UNSUPPORTED.
+
+ If the security protocol fails to complete within the Timeout period, the function
+ shall return EFI_TIMEOUT.
+
+ If the security protocol command completes without an error, the function shall
+ return EFI_SUCCESS. If the security protocol command completes with an error, the
+ function shall return EFI_DEVICE_ERROR.
+
+ @param[in] This The PPI instance pointer.
+ @param[in] DeviceIndex Specifies the storage security device to which the
+ function wants to talk. Because the driver that
+ implements Storage Security Command PPIs will manage
+ multiple storage devices, the PPIs that want to talk
+ to a single device must specify the device index
+ that was assigned during the enumeration process.
+ This index is a number from one to NumberofDevices.
+ @param[in] Timeout The timeout, in 100ns units, to use for the execution
+ of the security protocol command. A Timeout value
+ of 0 means that this function will wait indefinitely
+ for the security protocol command to execute. If
+ Timeout is greater than zero, then this function
+ will return EFI_TIMEOUT if the time required to
+ execute the receive data command is greater than
+ Timeout.
+ @param[in] SecurityProtocolId
+ The value of the "Security Protocol" parameter of
+ the security protocol command to be sent.
+ @param[in] SecurityProtocolSpecificData
+ The value of the "Security Protocol Specific"
+ parameter of the security protocol command to be
+ sent.
+ @param[in] PayloadBufferSize
+ Size in bytes of the payload data buffer.
+ @param[out] PayloadBuffer A pointer to a destination buffer to store the
+ security protocol command specific payload data
+ for the security protocol command. The caller is
+ responsible for having either implicit or explicit
+ ownership of the buffer.
+ @param[out] PayloadTransferSize
+ A pointer to a buffer to store the size in bytes
+ of the data written to the payload data buffer.
+
+ @retval EFI_SUCCESS The security protocol command completed
+ successfully.
+ @retval EFI_WARN_BUFFER_TOO_SMALL The PayloadBufferSize was too small to
+ store the available data from the device.
+ The PayloadBuffer contains the truncated
+ data.
+ @retval EFI_UNSUPPORTED The given DeviceIndex does not support
+ security protocol commands.
+ @retval EFI_DEVICE_ERROR The security protocol command completed
+ with an error.
+ @retval EFI_INVALID_PARAMETER The PayloadBuffer or PayloadTransferSize
+ is NULL and PayloadBufferSize is non-zero.
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the
+ security protocol command to execute.
+
+**/
+EFI_STATUS
+EFIAPI
+NvmeStorageSecurityReceiveData (
+ IN EDKII_PEI_STORAGE_SECURITY_CMD_PPI *This,
+ IN UINTN DeviceIndex,
+ IN UINT64 Timeout,
+ IN UINT8 SecurityProtocolId,
+ IN UINT16 SecurityProtocolSpecificData,
+ IN UINTN PayloadBufferSize,
+ OUT VOID *PayloadBuffer,
+ OUT UINTN *PayloadTransferSize
+ );
+
+/**
+ Send a security protocol command to a device.
+
+ The SendData function sends a security protocol command containing the payload
+ PayloadBuffer to the given DeviceIndex. The security protocol command sent is
+ defined by SecurityProtocolId and contains the security protocol specific data
+ SecurityProtocolSpecificData. If the underlying protocol command requires a
+ specific padding for the command payload, the SendData function shall add padding
+ bytes to the command payload to satisfy the padding requirements.
+
+ For devices supporting the SCSI command set, the security protocol command is
+ sent using the SECURITY PROTOCOL OUT command defined in SPC-4.
+
+ For devices supporting the ATA command set, the security protocol command is
+ sent using one of the TRUSTED SEND commands defined in ATA8-ACS if PayloadBufferSize
+ is non-zero. If the PayloadBufferSize is zero, the security protocol command
+ is sent using the Trusted Non-Data command defined in ATA8-ACS.
+
+ If PayloadBuffer is NULL and PayloadBufferSize is non-zero, the function shall
+ return EFI_INVALID_PARAMETER.
+
+ If the given DeviceIndex does not support security protocol commands, the function
+ shall return EFI_UNSUPPORTED.
+
+ If the security protocol fails to complete within the Timeout period, the function
+ shall return EFI_TIMEOUT.
+
+ If the security protocol command completes without an error, the function shall
+ return EFI_SUCCESS. If the security protocol command completes with an error,
+ the functio shall return EFI_DEVICE_ERROR.
+
+ @param[in] This The PPI instance pointer.
+ @param[in] DeviceIndex The ID of the device.
+ @param[in] Timeout The timeout, in 100ns units, to use for the execution
+ of the security protocol command. A Timeout value
+ of 0 means that this function will wait indefinitely
+ for the security protocol command to execute. If
+ Timeout is greater than zero, then this function
+ will return EFI_TIMEOUT if the time required to
+ execute the receive data command is greater than
+ Timeout.
+ @param[in] SecurityProtocolId
+ The value of the "Security Protocol" parameter of
+ the security protocol command to be sent.
+ @param[in] SecurityProtocolSpecificData
+ The value of the "Security Protocol Specific"
+ parameter of the security protocol command to be
+ sent.
+ @param[in] PayloadBufferSize Size in bytes of the payload data buffer.
+ @param[in] PayloadBuffer A pointer to a destination buffer to store the
+ security protocol command specific payload data
+ for the security protocol command.
+
+ @retval EFI_SUCCESS The security protocol command completed successfully.
+ @retval EFI_UNSUPPORTED The given DeviceIndex does not support security
+ protocol commands.
+ @retval EFI_DEVICE_ERROR The security protocol command completed with
+ an error.
+ @retval EFI_INVALID_PARAMETER The PayloadBuffer is NULL and PayloadBufferSize
+ is non-zero.
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the security
+ protocol command to execute.
+
+**/
+EFI_STATUS
+EFIAPI
+NvmeStorageSecuritySendData (
+ IN EDKII_PEI_STORAGE_SECURITY_CMD_PPI *This,
+ IN UINTN DeviceIndex,
+ IN UINT64 Timeout,
+ IN UINT8 SecurityProtocolId,
+ IN UINT16 SecurityProtocolSpecificData,
+ IN UINTN PayloadBufferSize,
+ IN VOID *PayloadBuffer
+ );
+
+#endif