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