From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received-SPF: Pass (sender SPF authorized) identity=mailfrom; client-ip=192.55.52.136; helo=mga12.intel.com; envelope-from=hao.a.wu@intel.com; receiver=edk2-devel@lists.01.org Received: from mga12.intel.com (mga12.intel.com [192.55.52.136]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id 26364210EB10A for ; Thu, 21 Jun 2018 01:31:30 -0700 (PDT) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from fmsmga001.fm.intel.com ([10.253.24.23]) by fmsmga106.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 21 Jun 2018 01:31:29 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.51,251,1526367600"; d="scan'208";a="66003632" Received: from fmsmsx107.amr.corp.intel.com ([10.18.124.205]) by fmsmga001.fm.intel.com with ESMTP; 21 Jun 2018 01:31:29 -0700 Received: from shsmsx103.ccr.corp.intel.com (10.239.4.69) by fmsmsx107.amr.corp.intel.com (10.18.124.205) with Microsoft SMTP Server (TLS) id 14.3.319.2; Thu, 21 Jun 2018 01:31:28 -0700 Received: from shsmsx104.ccr.corp.intel.com ([169.254.5.87]) by SHSMSX103.ccr.corp.intel.com ([169.254.4.51]) with mapi id 14.03.0319.002; Thu, 21 Jun 2018 16:31:26 +0800 From: "Wu, Hao A" To: "Ni, Ruiyu" , "edk2-devel@lists.01.org" CC: "Zeng, Star" , "Dong, Eric" , "Yao, Jiewen" Thread-Topic: [PATCH 2/4] MdeModulePkg/NvmExpressPei: Add the NVME device PEI BlockIo support Thread-Index: AQHUBHcFaY6Ej/s2nEaQ41XLnsSxR6Rp2LwAgACSwXA= Date: Thu, 21 Jun 2018 08:31:25 +0000 Message-ID: References: <20180615070342.13388-1-hao.a.wu@intel.com> <20180615070342.13388-3-hao.a.wu@intel.com> <734D49CCEBEEF84792F5B80ED585239D5BD40C10@SHSMSX104.ccr.corp.intel.com> In-Reply-To: <734D49CCEBEEF84792F5B80ED585239D5BD40C10@SHSMSX104.ccr.corp.intel.com> Accept-Language: zh-CN, en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-originating-ip: [10.239.127.40] MIME-Version: 1.0 Subject: Re: [PATCH 2/4] MdeModulePkg/NvmExpressPei: Add the NVME device PEI BlockIo support X-BeenThere: edk2-devel@lists.01.org X-Mailman-Version: 2.1.26 Precedence: list List-Id: EDK II Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 21 Jun 2018 08:31:30 -0000 Content-Language: en-US Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: quoted-printable > -----Original Message----- > From: Ni, Ruiyu > Sent: Thursday, June 21, 2018 3:46 PM > To: Wu, Hao A; edk2-devel@lists.01.org > Cc: Zeng, Star; Dong, Eric; Yao, Jiewen > Subject: RE: [PATCH 2/4] MdeModulePkg/NvmExpressPei: Add the NVME > device PEI BlockIo support >=20 > 2 minor comments. Ray, Thanks for the feedbacks. I will propose a V2 version of the series according to your comments. Best Regards, Hao Wu >=20 > Thanks/Ray >=20 > > -----Original Message----- > > From: Wu, Hao A > > Sent: Friday, June 15, 2018 3:04 PM > > To: edk2-devel@lists.01.org > > Cc: Wu, Hao A ; Zeng, Star ; > > Dong, Eric ; Ni, Ruiyu ; Yao, > > Jiewen > > Subject: [PATCH 2/4] MdeModulePkg/NvmExpressPei: Add the NVME > > device PEI BlockIo support > > > > REF: https://bugzilla.tianocore.org/show_bug.cgi?id=3D256 > > > > This commit adds the PEI BlockIo support for NVM Express devices. > > > > The driver will consume the EDKII_NVM_EXPRESS_HOST_CONTROLLER_PPI > > for NVM > > Express host controllers within the system. And then produces the > > BlockIo(2) PPIs for each controller. > > > > The implementation of this driver is currently based on the NVM Express= 1.1 > > Specification, which is available at: > > http://nvmexpress.org/resources/specifications/ > > > > Cc: Star Zeng > > Cc: Eric Dong > > Cc: Ruiyu Ni > > Cc: Jiewen Yao > > Contributed-under: TianoCore Contribution Agreement 1.1 > > Signed-off-by: Hao Wu > > --- > > MdeModulePkg/Bus/Pci/NvmExpressPei/DmaMem.c | 249 > +++++++ > > MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.c | 368 > > ++++++++++ > > MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.h | 265 > > +++++++ > > MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.inf | 70 ++ > > MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.uni | 21 + > > MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiBlockIo.c | 531 > > ++++++++++++++ > > MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiBlockIo.h | 266 > > +++++++ > > MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiExtra.uni | 19 + > > MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiHci.c | 748 > > ++++++++++++++++++++ > > MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiHci.h | 166 > > +++++ > > MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiPassThru.c | 628 > > ++++++++++++++++ > > MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiPassThru.h | 107 > > +++ > > MdeModulePkg/MdeModulePkg.dsc | 1 + > > 13 files changed, 3439 insertions(+) > > > > diff --git a/MdeModulePkg/Bus/Pci/NvmExpressPei/DmaMem.c > > b/MdeModulePkg/Bus/Pci/NvmExpressPei/DmaMem.c > > new file mode 100644 > > index 0000000000..51b48d38dd > > --- /dev/null > > +++ b/MdeModulePkg/Bus/Pci/NvmExpressPei/DmaMem.c > > @@ -0,0 +1,249 @@ > > +/** @file > > + The DMA memory help function. > > + > > + Copyright (c) 2018, Intel Corporation. All rights reserved.
> > + > > + This program and the accompanying materials > > + are licensed and made available under the terms and conditions > > + of the BSD License which accompanies this distribution. The > > + full text of the license may be found at > > + http://opensource.org/licenses/bsd-license.php > > + > > + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" > > BASIS, > > + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER > > EXPRESS OR IMPLIED. > > + > > +**/ > > + > > +#include "NvmExpressPei.h" > > + > > +EDKII_IOMMU_PPI *mIoMmu; > > + > > +/** > > + Provides the controller-specific addresses required to access system > > memory from a > > + DMA bus master. > > + > > + @param Operation Indicates if the bus master is going t= o read or > > write to system memory. > > + @param HostAddress The system memory address to map to th= e > PCI > > controller. > > + @param NumberOfBytes On input the number of bytes to map. O= n > > output the number of bytes > > + that were mapped. > > + @param DeviceAddress The resulting map address for the bus > master > > PCI controller to use to > > + access the hosts HostAddress. > > + @param Mapping A resulting value to pass to Unmap(). > > + > > + @retval EFI_SUCCESS The range was mapped for the returned > > NumberOfBytes. > > + @retval EFI_UNSUPPORTED The HostAddress cannot be mapped as a > > common buffer. > > + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. > > + @retval EFI_OUT_OF_RESOURCES The request could not be completed > > due to a lack of resources. > > + @retval EFI_DEVICE_ERROR The system hardware could not map the > > requested address. > > + > > +**/ > > +EFI_STATUS > > +IoMmuMap ( > > + IN EDKII_IOMMU_OPERATION Operation, > > + IN VOID *HostAddress, > > + IN OUT UINTN *NumberOfBytes, > > + OUT EFI_PHYSICAL_ADDRESS *DeviceAddress, > > + OUT VOID **Mapping > > + ) > > +{ > > + EFI_STATUS Status; > > + UINT64 Attribute; > > + > > + if (mIoMmu !=3D NULL) { > > + Status =3D mIoMmu->Map ( > > + mIoMmu, > > + Operation, > > + HostAddress, > > + NumberOfBytes, > > + DeviceAddress, > > + Mapping > > + ); > > + if (EFI_ERROR (Status)) { > > + return EFI_OUT_OF_RESOURCES; > > + } > > + switch (Operation) { > > + case EdkiiIoMmuOperationBusMasterRead: > > + case EdkiiIoMmuOperationBusMasterRead64: > > + Attribute =3D EDKII_IOMMU_ACCESS_READ; > > + break; > > + case EdkiiIoMmuOperationBusMasterWrite: > > + case EdkiiIoMmuOperationBusMasterWrite64: > > + Attribute =3D EDKII_IOMMU_ACCESS_WRITE; > > + break; > > + case EdkiiIoMmuOperationBusMasterCommonBuffer: > > + case EdkiiIoMmuOperationBusMasterCommonBuffer64: > > + Attribute =3D EDKII_IOMMU_ACCESS_READ | > > EDKII_IOMMU_ACCESS_WRITE; > > + break; > > + default: > > + ASSERT(FALSE); > > + return EFI_INVALID_PARAMETER; > > + } > > + Status =3D mIoMmu->SetAttribute ( > > + mIoMmu, > > + *Mapping, > > + Attribute > > + ); > > + if (EFI_ERROR (Status)) { > > + return Status; > > + } > > + } else { > > + *DeviceAddress =3D (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress; > > + *Mapping =3D NULL; > > + Status =3D EFI_SUCCESS; > > + } > > + return Status; > > +} > > + > > +/** > > + Completes the Map() operation and releases any corresponding resourc= es. > > + > > + @param Mapping The mapping value returned from Map(). > > + > > + @retval EFI_SUCCESS The range was unmapped. > > + @retval EFI_INVALID_PARAMETER Mapping is not a value that was > > returned by Map(). > > + @retval EFI_DEVICE_ERROR The data was not committed to the targ= et > > system memory. > > +**/ > > +EFI_STATUS > > +IoMmuUnmap ( > > + IN VOID *Mapping > > + ) > > +{ > > + EFI_STATUS Status; > > + > > + if (mIoMmu !=3D NULL) { > > + Status =3D mIoMmu->SetAttribute (mIoMmu, Mapping, 0); > > + Status =3D mIoMmu->Unmap (mIoMmu, Mapping); > > + } else { > > + Status =3D EFI_SUCCESS; > > + } > > + return Status; > > +} > > + > > +/** > > + Allocates pages that are suitable for an > > OperationBusMasterCommonBuffer or > > + OperationBusMasterCommonBuffer64 mapping. > > + > > + @param Pages The number of pages to allocate. > > + @param HostAddress A pointer to store the base system mem= ory > > address of the > > + allocated range. > > + @param DeviceAddress The resulting map address for the bus > master > > PCI controller to use to > > + access the hosts HostAddress. > > + @param Mapping A resulting value to pass to Unmap(). > > + > > + @retval EFI_SUCCESS The requested memory pages were alloca= ted. > > + @retval EFI_UNSUPPORTED Attributes is unsupported. The only le= gal > > attribute bits are > > + MEMORY_WRITE_COMBINE and MEMORY_CACHED= . > > + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. > > + @retval EFI_OUT_OF_RESOURCES The memory pages could not be > > allocated. > > + > > +**/ > > +EFI_STATUS > > +IoMmuAllocateBuffer ( > > + IN UINTN Pages, > > + OUT VOID **HostAddress, > > + OUT EFI_PHYSICAL_ADDRESS *DeviceAddress, > > + OUT VOID **Mapping > > + ) > > +{ > > + EFI_STATUS Status; > > + UINTN NumberOfBytes; > > + EFI_PHYSICAL_ADDRESS HostPhyAddress; > > + > > + *HostAddress =3D NULL; > > + *DeviceAddress =3D 0; > > + > > + if (mIoMmu !=3D NULL) { > > + Status =3D mIoMmu->AllocateBuffer ( > > + mIoMmu, > > + EfiBootServicesData, > > + Pages, > > + HostAddress, > > + 0 > > + ); > > + if (EFI_ERROR (Status)) { > > + return EFI_OUT_OF_RESOURCES; > > + } > > + > > + NumberOfBytes =3D EFI_PAGES_TO_SIZE(Pages); > > + Status =3D mIoMmu->Map ( > > + mIoMmu, > > + EdkiiIoMmuOperationBusMasterCommonBuffer, > > + *HostAddress, > > + &NumberOfBytes, > > + DeviceAddress, > > + Mapping > > + ); > > + if (EFI_ERROR (Status)) { > > + return EFI_OUT_OF_RESOURCES; > > + } > > + Status =3D mIoMmu->SetAttribute ( > > + mIoMmu, > > + *Mapping, > > + EDKII_IOMMU_ACCESS_READ | > EDKII_IOMMU_ACCESS_WRITE > > + ); > > + if (EFI_ERROR (Status)) { > > + return Status; > > + } > > + } else { > > + Status =3D PeiServicesAllocatePages ( > > + EfiBootServicesData, > > + Pages, > > + &HostPhyAddress > > + ); > > + if (EFI_ERROR (Status)) { > > + return EFI_OUT_OF_RESOURCES; > > + } > > + *HostAddress =3D (VOID *)(UINTN)HostPhyAddress; > > + *DeviceAddress =3D HostPhyAddress; > > + *Mapping =3D NULL; > > + } > > + return Status; > > +} > > + > > +/** > > + Frees memory that was allocated with AllocateBuffer(). > > + > > + @param Pages The number of pages to free. > > + @param HostAddress The base system memory address of the > > allocated range. > > + @param Mapping The mapping value returned from Map(). > > + > > + @retval EFI_SUCCESS The requested memory pages were freed. > > + @retval EFI_INVALID_PARAMETER The memory range specified by > > HostAddress and Pages > > + was not allocated with AllocateBuffer(= ). > > + > > +**/ > > +EFI_STATUS > > +IoMmuFreeBuffer ( > > + IN UINTN Pages, > > + IN VOID *HostAddress, > > + IN VOID *Mapping > > + ) > > +{ > > + EFI_STATUS Status; > > + > > + if (mIoMmu !=3D NULL) { > > + Status =3D mIoMmu->SetAttribute (mIoMmu, Mapping, 0); > > + Status =3D mIoMmu->Unmap (mIoMmu, Mapping); > > + Status =3D mIoMmu->FreeBuffer (mIoMmu, Pages, HostAddress); > > + } else { > > + Status =3D EFI_SUCCESS; > > + } > > + return Status; > > +} > > + > > +/** > > + Initialize IOMMU. > > +**/ > > +VOID > > +IoMmuInit ( > > + VOID > > + ) > > +{ > > + PeiServicesLocatePpi ( > > + &gEdkiiIoMmuPpiGuid, > > + 0, > > + NULL, > > + (VOID **)&mIoMmu > > + ); > > +} > > + > > diff --git a/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.c > > b/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.c > > new file mode 100644 > > index 0000000000..0ba88385c9 > > --- /dev/null > > +++ b/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.c > > @@ -0,0 +1,368 @@ > > +/** @file > > + The NvmExpressPei driver is used to manage non-volatile memory > > subsystem > > + which follows NVM Express specification at PEI phase. > > + > > + Copyright (c) 2018, Intel Corporation. All rights reserved.
> > + > > + This program and the accompanying materials > > + are licensed and made available under the terms and conditions > > + of the BSD License which accompanies this distribution. The > > + full text of the license may be found at > > + http://opensource.org/licenses/bsd-license.php > > + > > + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" > > BASIS, > > + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER > > EXPRESS OR IMPLIED. > > + > > +**/ > > + > > +#include "NvmExpressPei.h" > > + > > +EFI_PEI_PPI_DESCRIPTOR mNvmeBlkIoPpiListTemplate =3D { > > + EFI_PEI_PPI_DESCRIPTOR_PPI, > > + &gEfiPeiVirtualBlockIoPpiGuid, > > + NULL > > +}; > > + > > +EFI_PEI_PPI_DESCRIPTOR mNvmeBlkIo2PpiListTemplate =3D { > > + EFI_PEI_PPI_DESCRIPTOR_PPI | > > EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST, > > + &gEfiPeiVirtualBlockIo2PpiGuid, > > + NULL > > +}; > > + > > +EFI_PEI_NOTIFY_DESCRIPTOR mNvmeEndOfPeiNotifyListTemplate =3D { > > + (EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | > > EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), > > + &gEfiEndOfPeiSignalPpiGuid, > > + NvmePeimEndOfPei > > +}; > > + > > +/** > > + Check if the specified Nvm Express device namespace is active, and t= hen > > get the Identify > > + Namespace data. > > + > > + @param[in,out] Private The pointer to the > > PEI_NVME_CONTROLLER_PRIVATE_DATA data structure. > > + @param[in] NamespaceId The specified namespace identifier. > > + > > + @retval EFI_SUCCESS The specified namespace in the device is > > successfully enumerated. > > + @return Others Error occurs when enumerating the namespace. > > + > > +**/ > > +EFI_STATUS > > +EnumerateNvmeDevNamespace ( > > + IN OUT PEI_NVME_CONTROLLER_PRIVATE_DATA *Private, > > + IN UINT32 NamespaceId > > + ) > > +{ > > + EFI_STATUS Status; > > + NVME_ADMIN_NAMESPACE_DATA *NamespaceData; > > + PEI_NVME_NAMESPACE_INFO *NamespaceInfo; > > + UINT32 DeviceIndex; > > + UINT32 Lbads; > > + UINT32 Flbas; > > + UINT32 LbaFmtIdx; > > + > > + NamespaceData =3D (NVME_ADMIN_NAMESPACE_DATA *) > > AllocateZeroPool (sizeof (NVME_ADMIN_NAMESPACE_DATA)); > > + if (NamespaceData =3D=3D NULL) { > > + return EFI_OUT_OF_RESOURCES; > > + } > > + > > + // > > + // Identify Namespace > > + // > > + Status =3D NvmeIdentifyNamespace ( > > + Private, > > + NamespaceId, > > + NamespaceData > > + ); > > + if (EFI_ERROR (Status)) { > > + DEBUG ((DEBUG_ERROR, "%a: NvmeIdentifyNamespace fail, Status - > > %r\n", __FUNCTION__, Status)); > > + goto Exit; > > + } > > + > > + // > > + // Validate Namespace > > + // > > + if (NamespaceData->Ncap =3D=3D 0) { > > + DEBUG ((DEBUG_INFO, "%a: Namespace ID %d is an inactive one.\n", > > __FUNCTION__, NamespaceId)); > > + Status =3D EFI_DEVICE_ERROR; > > + goto Exit; > > + } > > + > > + DeviceIndex =3D Private->ActiveNamespaceNum; > > + NamespaceInfo =3D &Private->NamespaceInfo[DeviceIndex]; > > + NamespaceInfo->NamespaceId =3D NamespaceId; > > + NamespaceInfo->NamespaceUuid =3D NamespaceData->Eui64; > > + NamespaceInfo->Controller =3D Private; > > + Private->ActiveNamespaceNum++; > > + > > + // > > + // Build BlockIo media structure > > + // > > + Flbas =3D NamespaceData->Flbas; > > + LbaFmtIdx =3D Flbas & 0xF; > > + Lbads =3D NamespaceData->LbaFormat[LbaFmtIdx].Lbads; > > + > > + NamespaceInfo->Media.InterfaceType =3D MSG_NVME_NAMESPACE_DP; > > + NamespaceInfo->Media.RemovableMedia =3D FALSE; > > + NamespaceInfo->Media.MediaPresent =3D TRUE; > > + NamespaceInfo->Media.ReadOnly =3D FALSE; > > + NamespaceInfo->Media.BlockSize =3D (UINT32) 1 << Lbads; > > + NamespaceInfo->Media.LastBlock =3D (EFI_PEI_LBA) NamespaceData- > > >Nsze - 1; > > + DEBUG (( > > + DEBUG_INFO, > > + "%a: Namespace ID %d - BlockSize =3D 0x%x, LastBlock =3D 0x%lx\n", > > + __FUNCTION__, > > + NamespaceId, > > + NamespaceInfo->Media.BlockSize, > > + NamespaceInfo->Media.LastBlock > > + )); > > + > > +Exit: > > + if (NamespaceData !=3D NULL) { > > + FreePool (NamespaceData); > > + } > > + > > + return Status; > > +} > > + > > +/** > > + Discover all Nvm Express device active namespaces. > > + > > + @param[in,out] Private The pointer to the > > PEI_NVME_CONTROLLER_PRIVATE_DATA data structure. > > + > > + @retval EFI_SUCCESS All the namespaces in the device are succe= ssfully > > enumerated. > > + @return EFI_NOT_FOUND No active namespaces can be found. > > + > > +**/ > > +EFI_STATUS > > +NvmeDiscoverNamespaces ( > > + IN OUT PEI_NVME_CONTROLLER_PRIVATE_DATA *Private > > + ) > > +{ > > + UINT32 NamespaceId; > > + > > + Private->ActiveNamespaceNum =3D 0; > > + Private->NamespaceInfo =3D AllocateZeroPool (Private->Controlle= rData- > > >Nn * sizeof (PEI_NVME_NAMESPACE_INFO)); > > + > > + // > > + // According to Nvm Express 1.1 spec Figure 82, the field 'Nn' of th= e > > identify > > + // controller data defines the number of valid namespaces present fo= r the > > + // controller. Namespaces shall be allocated in order (starting with= 1) and > > + // packed sequentially. > > + // > > + for (NamespaceId =3D 1; NamespaceId <=3D Private->ControllerData->Nn= ; > > NamespaceId++) { > > + // > > + // For now, we do not care the return status. Since if a valid nam= espace is > > inactive, > > + // error status will be returned. But we continue to enumerate oth= er valid > > namespaces. > > + // > > + EnumerateNvmeDevNamespace (Private, NamespaceId); > > + } > > + if (Private->ActiveNamespaceNum =3D=3D 0) { > > + return EFI_NOT_FOUND; > > + } > > + > > + return EFI_SUCCESS; > > +} > > + > > +/** > > + One notified function to cleanup the allocated resources at the end = of PEI. > > + > > + @param[in] PeiServices Pointer to PEI Services Table. > > + @param[in] NotifyDescriptor Pointer to the descriptor for the > > Notification > > + event that caused this function to ex= ecute. > > + @param[in] Ppi Pointer to the PPI data associated wi= th this > > function. > > + > > + @retval EFI_SUCCESS The function completes successfully > > + > > +**/ > > +EFI_STATUS > > +EFIAPI > > +NvmePeimEndOfPei ( > > + IN EFI_PEI_SERVICES **PeiServices, > > + IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor, > > + IN VOID *Ppi > > + ) > > +{ > > + PEI_NVME_CONTROLLER_PRIVATE_DATA *Private; > > + > > + Private =3D GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_NOTIFY > > (NotifyDescriptor); > > + NvmeDisableController (Private); > > + NvmeFreeControllerResource (Private); > > + > > + return EFI_SUCCESS; > > +} > > + > > +/** > > + Entry point of the PEIM. > > + > > + @param[in] FileHandle Handle of the file being invoked. > > + @param[in] PeiServices Describes the list of possible PEI Service= s. > > + > > + @retval EFI_SUCCESS PPI successfully installed. > > + > > +**/ > > +EFI_STATUS > > +EFIAPI > > +NvmExpressPeimEntry ( > > + IN EFI_PEI_FILE_HANDLE FileHandle, > > + IN CONST EFI_PEI_SERVICES **PeiServices > > + ) > > +{ > > + EFI_STATUS Status; > > + EFI_BOOT_MODE BootMode; > > + EDKII_NVM_EXPRESS_HOST_CONTROLLER_PPI *NvmeHcPpi; > > + UINT8 Controller; > > + UINTN MmioBase; > > + PEI_NVME_CONTROLLER_PRIVATE_DATA *Private; > > + EFI_PHYSICAL_ADDRESS DeviceAddress; > > + > > + // > > + // Shadow this PEIM to run from memory > > + // > > + if (!EFI_ERROR (PeiServicesRegisterForShadow (FileHandle))) { > > + return EFI_SUCCESS; > > + } > > + > > + Status =3D PeiServicesGetBootMode (&BootMode); > > + // > > + // Currently, the driver does not produce any PPI in S3 boot path > > + // > > + if (BootMode =3D=3D BOOT_ON_S3_RESUME) { > > + return EFI_SUCCESS; > > + } > > + > > + // > > + // Locate the NVME host controller PPI > > + // > > + Status =3D PeiServicesLocatePpi ( > > + &gEdkiiPeiNvmExpressHostControllerPpiGuid, > > + 0, > > + NULL, > > + (VOID **) &NvmeHcPpi > > + ); > > + if (EFI_ERROR (Status)) { > > + DEBUG ((DEBUG_ERROR, "%a: Fail to locate NvmeHostControllerPpi.\n"= , > > __FUNCTION__)); > > + return EFI_UNSUPPORTED; > > + } > > + > > + IoMmuInit (); > > + > > + Controller =3D 0; > > + MmioBase =3D 0; > > + while (TRUE) { > > + Status =3D NvmeHcPpi->GetNvmeHcMmioBar ( > > + (EFI_PEI_SERVICES **) PeiServices, > > + NvmeHcPpi, > > + Controller, > > + &MmioBase > > + ); > > + // > > + // When status is error, meant no controller is found > > + // > > + if (EFI_ERROR (Status)) { > > + break; > > + } > > + > > + // > > + // Memory allocation for controller private data > > + // > > + Private =3D AllocateZeroPool (sizeof > > (PEI_NVME_CONTROLLER_PRIVATE_DATA)); > > + if (Private =3D=3D NULL) { > > + DEBUG (( > > + DEBUG_ERROR, > > + "%a: Fail to allocate private data for Controller %d.\n", > > + __FUNCTION__, > > + Controller > > + )); > > + return EFI_OUT_OF_RESOURCES; > > + } > > + > > + // > > + // Memory allocation for transfer-related data > > + // > > + Status =3D IoMmuAllocateBuffer ( > > + NVME_MEM_MAX_PAGES, > > + &Private->Buffer, > > + &DeviceAddress, > > + &Private->BufferMapping > > + ); > > + if (EFI_ERROR (Status)) { > > + DEBUG (( > > + DEBUG_ERROR, > > + "%a: Fail to allocate DMA buffers for Controller %d.\n", > > + __FUNCTION__, > > + Controller > > + )); > > + NvmeFreeControllerResource (Private); > > + return Status; > > + } > > + ASSERT (DeviceAddress =3D=3D ((EFI_PHYSICAL_ADDRESS) (UINTN) Priva= te- > > >Buffer)); > > + DEBUG ((DEBUG_INFO, "%a: DMA buffer base at 0x%x\n", > > __FUNCTION__, Private->Buffer)); > > + > > + // > > + // Initialize controller private data > > + // > > + Private->Signature =3D > > NVME_PEI_CONTROLLER_PRIVATE_DATA_SIGNATURE; > > + Private->MmioBase =3D MmioBase; > > + Private->BlkIoPpi.GetNumberOfBlockDevices =3D > > NvmeBlockIoPeimGetDeviceNo; > > + Private->BlkIoPpi.GetBlockDeviceMediaInfo =3D > > NvmeBlockIoPeimGetMediaInfo; > > + Private->BlkIoPpi.ReadBlocks =3D NvmeBlockIoPeimRead= Blocks; > > + Private->BlkIo2Ppi.Revision =3D > > EFI_PEI_RECOVERY_BLOCK_IO2_PPI_REVISION; > > + Private->BlkIo2Ppi.GetNumberOfBlockDevices =3D > > NvmeBlockIoPeimGetDeviceNo2; > > + Private->BlkIo2Ppi.GetBlockDeviceMediaInfo =3D > > NvmeBlockIoPeimGetMediaInfo2; > > + Private->BlkIo2Ppi.ReadBlocks =3D NvmeBlockIoPeimRead= Blocks2; > > + Private->BlkIoPpiList =3D mNvmeBlkIoPpiListTemplate; > > + Private->BlkIo2PpiList =3D mNvmeBlkIo2PpiListTemplate; > > + Private->EndOfPeiNotifyList =3D mNvmeEndOfPeiNotifyListTemplate; >=20 > 1. CopyMem() should be used for structure assignment. >=20 > > + Private->BlkIoPpiList.Ppi =3D &Private->BlkIoPpi; > > + Private->BlkIo2PpiList.Ppi =3D &Private->BlkIo2Ppi; > > + > > + // > > + // Initialize the NVME controller > > + // > > + Status =3D NvmeControllerInit (Private); > > + if (EFI_ERROR (Status)) { > > + DEBUG (( > > + DEBUG_ERROR, > > + "%a: Controller initialization fail for Controller %d with Sta= tus - %r.\n", > > + __FUNCTION__, > > + Controller, > > + Status > > + )); > > + NvmeFreeControllerResource (Private); > > + Controller++; > > + continue; > > + } > > + > > + // > > + // Enumerate the NVME namespaces on the controller > > + // > > + Status =3D NvmeDiscoverNamespaces (Private); > > + if (EFI_ERROR (Status)) { > > + // > > + // No active namespace was found on the controller > > + // > > + DEBUG (( > > + DEBUG_ERROR, > > + "%a: Namespaces discovery fail for Controller %d with Status -= %r.\n", > > + __FUNCTION__, > > + Controller, > > + Status > > + )); > > + NvmeFreeControllerResource (Private); > > + Controller++; > > + continue; > > + } > > + > > + PeiServicesInstallPpi (&Private->BlkIoPpiList); > > + PeiServicesNotifyPpi (&Private->EndOfPeiNotifyList); > > + DEBUG (( > > + DEBUG_INFO, > > + "%a: BlockIO PPI has been installed on Controller %d.\n", > > + __FUNCTION__, > > + Controller > > + )); > > + Controller++; > > + } > > + > > + return EFI_SUCCESS; > > +} > > diff --git a/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.h > > b/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.h > > new file mode 100644 > > index 0000000000..5e6f66892f > > --- /dev/null > > +++ b/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.h > > @@ -0,0 +1,265 @@ > > +/** @file > > + The NvmExpressPei driver is used to manage non-volatile memory > > subsystem > > + which follows NVM Express specification at PEI phase. > > + > > + Copyright (c) 2018, Intel Corporation. All rights reserved.
> > + > > + This program and the accompanying materials > > + are licensed and made available under the terms and conditions > > + of the BSD License which accompanies this distribution. The > > + full text of the license may be found at > > + http://opensource.org/licenses/bsd-license.php > > + > > + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" > > BASIS, > > + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER > > EXPRESS OR IMPLIED. > > + > > +**/ > > + > > +#ifndef _NVM_EXPRESS_PEI_H_ > > +#define _NVM_EXPRESS_PEI_H_ > > + > > +#include > > + > > +#include > > + > > +#include > > +#include > > +#include > > +#include > > +#include > > + > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > + > > +// > > +// Structure forward declarations > > +// > > +typedef struct _PEI_NVME_NAMESPACE_INFO > > PEI_NVME_NAMESPACE_INFO; > > +typedef struct _PEI_NVME_CONTROLLER_PRIVATE_DATA > > PEI_NVME_CONTROLLER_PRIVATE_DATA; > > + > > +#include "NvmExpressPeiHci.h" > > +#include "NvmExpressPeiPassThru.h" > > +#include "NvmExpressPeiBlockIo.h" > > + > > +// > > +// NVME PEI driver implementation related definitions > > +// > > +#define NVME_MAX_QUEUES 2 // Number = of I/O > queues > > supported by the driver, 1 for AQ, 1 for CQ > > +#define NVME_ASQ_SIZE 1 // Number = of admin > > submission queue entries, which is 0-based > > +#define NVME_ACQ_SIZE 1 // Number = of admin > > completion queue entries, which is 0-based > > +#define NVME_CSQ_SIZE 63 // Number = of I/O > submission > > queue entries, which is 0-based > > +#define NVME_CCQ_SIZE 63 // Number = of I/O > completion > > queue entries, which is 0-based > > +#define NVME_PRP_SIZE (8) // Pages o= f PRP list > > + > > +#define NVME_MEM_MAX_PAGES \ > > + ( \ > > + 1 /* ASQ */ + \ > > + 1 /* ACQ */ + \ > > + 1 /* SQs */ + \ > > + 1 /* CQs */ + \ > > + NVME_PRP_SIZE) /* PRPs */ > > + > > +#define NVME_ADMIN_QUEUE 0x00 > > +#define NVME_IO_QUEUE 0x01 > > +#define NVME_GENERIC_TIMEOUT 5000000 // Gen= eric > > PassThru command timeout value, in us unit > > +#define NVME_POLL_INTERVAL 100 // Pol= l interval for > > PassThru command, in us unit > > + > > +// > > +// Nvme namespace data structure. > > +// > > +struct _PEI_NVME_NAMESPACE_INFO { > > + UINT32 NamespaceId; > > + UINT64 NamespaceUuid; > > + EFI_PEI_BLOCK_IO2_MEDIA Media; > > + > > + PEI_NVME_CONTROLLER_PRIVATE_DATA *Controller; > > +}; > > + > > +// > > +// Unique signature for private data structure. > > +// > > +#define NVME_PEI_CONTROLLER_PRIVATE_DATA_SIGNATURE > > SIGNATURE_32 ('N','V','P','C') > > + > > +// > > +// Nvme controller private data structure. > > +// > > +struct _PEI_NVME_CONTROLLER_PRIVATE_DATA { > > + UINT32 Signature; > > + UINTN MmioBase; > > + EFI_PEI_RECOVERY_BLOCK_IO_PPI BlkIoPpi; > > + EFI_PEI_RECOVERY_BLOCK_IO2_PPI BlkIo2Ppi; > > + EFI_PEI_PPI_DESCRIPTOR BlkIoPpiList; > > + EFI_PEI_PPI_DESCRIPTOR BlkIo2PpiList; > > + EFI_PEI_NOTIFY_DESCRIPTOR EndOfPeiNotifyList; > > + > > + // > > + // Pointer to identify controller data > > + // > > + NVME_ADMIN_CONTROLLER_DATA *ControllerData; > > + > > + // > > + // (4 + NVME_PRP_SIZE) x 4kB aligned buffers will be carved out of t= his > > buffer > > + // 1st 4kB boundary is the start of the admin submission queue > > + // 2nd 4kB boundary is the start of the admin completion queue > > + // 3rd 4kB boundary is the start of I/O submission queue > > + // 4th 4kB boundary is the start of I/O completion queue > > + // 5th 4kB boundary is the start of PRP list buffers > > + // > > + VOID *Buffer; > > + VOID *BufferMapping; > > + > > + // > > + // Pointers to 4kB aligned submission & completion queues > > + // > > + NVME_SQ *SqBuffer[NVME_MAX_QUEUES]= ; > > + NVME_CQ *CqBuffer[NVME_MAX_QUEUES]= ; > > + > > + // > > + // Submission and completion queue indices > > + // > > + NVME_SQTDBL SqTdbl[NVME_MAX_QUEUES]; > > + NVME_CQHDBL CqHdbl[NVME_MAX_QUEUES]; > > + > > + UINT8 Pt[NVME_MAX_QUEUES]; > > + UINT16 Cid[NVME_MAX_QUEUES]; > > + > > + // > > + // Nvme controller capabilities > > + // > > + NVME_CAP Cap; > > + > > + // > > + // Namespaces information on the controller > > + // > > + UINT32 ActiveNamespaceNum; > > + PEI_NVME_NAMESPACE_INFO *NamespaceInfo; > > +}; > > + > > +#define GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_BLKIO(a) \ > > + CR (a, PEI_NVME_CONTROLLER_PRIVATE_DATA, BlkIoPpi, > > NVME_PEI_CONTROLLER_PRIVATE_DATA_SIGNATURE) > > +#define GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_BLKIO2(a) \ > > + CR (a, PEI_NVME_CONTROLLER_PRIVATE_DATA, BlkIo2Ppi, > > NVME_PEI_CONTROLLER_PRIVATE_DATA_SIGNATURE) > > +#define GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_NOTIFY(a) \ > > + CR (a, PEI_NVME_CONTROLLER_PRIVATE_DATA, EndOfPeiNotifyList, > > NVME_PEI_CONTROLLER_PRIVATE_DATA_SIGNATURE) > > + > > + > > +/** > > + Initialize IOMMU. > > +**/ > > +VOID > > +IoMmuInit ( > > + VOID > > + ); > > + > > +/** > > + Allocates pages that are suitable for an > > OperationBusMasterCommonBuffer or > > + OperationBusMasterCommonBuffer64 mapping. > > + > > + @param Pages The number of pages to allocate. > > + @param HostAddress A pointer to store the base system mem= ory > > address of the > > + allocated range. > > + @param DeviceAddress The resulting map address for the bus > master > > PCI controller to use to > > + access the hosts HostAddress. > > + @param Mapping A resulting value to pass to Unmap(). > > + > > + @retval EFI_SUCCESS The requested memory pages were alloca= ted. > > + @retval EFI_UNSUPPORTED Attributes is unsupported. The only le= gal > > attribute bits are > > + MEMORY_WRITE_COMBINE and MEMORY_CACHED= . > > + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. > > + @retval EFI_OUT_OF_RESOURCES The memory pages could not be > > allocated. > > + > > +**/ > > +EFI_STATUS > > +IoMmuAllocateBuffer ( > > + IN UINTN Pages, > > + OUT VOID **HostAddress, > > + OUT EFI_PHYSICAL_ADDRESS *DeviceAddress, > > + OUT VOID **Mapping > > + ); > > + > > +/** > > + Frees memory that was allocated with AllocateBuffer(). > > + > > + @param Pages The number of pages to free. > > + @param HostAddress The base system memory address of the > > allocated range. > > + @param Mapping The mapping value returned from Map(). > > + > > + @retval EFI_SUCCESS The requested memory pages were freed. > > + @retval EFI_INVALID_PARAMETER The memory range specified by > > HostAddress and Pages > > + was not allocated with AllocateBuffer(= ). > > + > > +**/ > > +EFI_STATUS > > +IoMmuFreeBuffer ( > > + IN UINTN Pages, > > + IN VOID *HostAddress, > > + IN VOID *Mapping > > + ); > > + > > +/** > > + Provides the controller-specific addresses required to access system > > memory from a > > + DMA bus master. > > + > > + @param Operation Indicates if the bus master is going t= o read or > > write to system memory. > > + @param HostAddress The system memory address to map to th= e > PCI > > controller. > > + @param NumberOfBytes On input the number of bytes to map. O= n > > output the number of bytes > > + that were mapped. > > + @param DeviceAddress The resulting map address for the bus > master > > PCI controller to use to > > + access the hosts HostAddress. > > + @param Mapping A resulting value to pass to Unmap(). > > + > > + @retval EFI_SUCCESS The range was mapped for the returned > > NumberOfBytes. > > + @retval EFI_UNSUPPORTED The HostAddress cannot be mapped as a > > common buffer. > > + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. > > + @retval EFI_OUT_OF_RESOURCES The request could not be completed > > due to a lack of resources. > > + @retval EFI_DEVICE_ERROR The system hardware could not map the > > requested address. > > + > > +**/ > > +EFI_STATUS > > +IoMmuMap ( > > + IN EDKII_IOMMU_OPERATION Operation, > > + IN VOID *HostAddress, > > + IN OUT UINTN *NumberOfBytes, > > + OUT EFI_PHYSICAL_ADDRESS *DeviceAddress, > > + OUT VOID **Mapping > > + ); > > + > > +/** > > + Completes the Map() operation and releases any corresponding resourc= es. > > + > > + @param Mapping The mapping value returned from Map(). > > + > > + @retval EFI_SUCCESS The range was unmapped. > > + @retval EFI_INVALID_PARAMETER Mapping is not a value that was > > returned by Map(). > > + @retval EFI_DEVICE_ERROR The data was not committed to the targ= et > > system memory. > > +**/ > > +EFI_STATUS > > +IoMmuUnmap ( > > + IN VOID *Mapping > > + ); > > + > > +/** > > + One notified function to cleanup the allocated resources at the end = of PEI. > > + > > + @param[in] PeiServices Pointer to PEI Services Table. > > + @param[in] NotifyDescriptor Pointer to the descriptor for the > > Notification > > + event that caused this function to ex= ecute. > > + @param[in] Ppi Pointer to the PPI data associated wi= th this > > function. > > + > > + @retval EFI_SUCCESS The function completes successfully > > + > > +**/ > > +EFI_STATUS > > +EFIAPI > > +NvmePeimEndOfPei ( > > + IN EFI_PEI_SERVICES **PeiServices, > > + IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor, > > + IN VOID *Ppi > > + ); > > + > > +#endif > > diff --git a/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.inf > > b/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.inf > > new file mode 100644 > > index 0000000000..8437c815fa > > --- /dev/null > > +++ b/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.inf > > @@ -0,0 +1,70 @@ > > +## @file > > +# The NvmExpressPei driver is used to manage non-volatile memory > > subsystem > > +# which follows NVM Express specification at PEI phase. > > +# > > +# Copyright (c) 2018, Intel Corporation. All rights reserved.
> > +# > > +# This program and the accompanying materials > > +# are licensed and made available under the terms and conditions of t= he > > BSD License > > +# which accompanies this distribution. The full text of the license = may be > > found at > > +# http://opensource.org/licenses/bsd-license.php > > +# > > +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" > > BASIS, > > +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER > > EXPRESS OR IMPLIED. > > +# > > +## > > + > > +[Defines] > > + INF_VERSION =3D 0x00010005 > > + BASE_NAME =3D NvmExpressPei > > + MODULE_UNI_FILE =3D NvmExpressPei.uni > > + FILE_GUID =3D 94813714-E10A-4798-9909-8C904F66B= 4D9 > > + MODULE_TYPE =3D PEIM > > + VERSION_STRING =3D 1.0 > > + ENTRY_POINT =3D NvmExpressPeimEntry > > + > > +# > > +# The following information is for reference only and not required by = the > > build tools. > > +# > > +# VALID_ARCHITECTURES =3D IA32 X64 IPF EBC >=20 > 2. IPF can be removed. >=20 > > +# > > + > > +[Sources] > > + DmaMem.c > > + NvmExpressPei.c > > + NvmExpressPei.h > > + NvmExpressPeiBlockIo.c > > + NvmExpressPeiBlockIo.h > > + NvmExpressPeiHci.c > > + NvmExpressPeiHci.h > > + NvmExpressPeiPassThru.c > > + NvmExpressPeiPassThru.h > > + > > +[Packages] > > + MdePkg/MdePkg.dec > > + MdeModulePkg/MdeModulePkg.dec > > + > > +[LibraryClasses] > > + DebugLib > > + PeiServicesLib > > + MemoryAllocationLib > > + BaseMemoryLib > > + IoLib > > + PciLib > > + TimerLib > > + PeimEntryPoint > > + > > +[Ppis] > > + gEfiPeiVirtualBlockIoPpiGuid ## PRODUCES > > + gEfiPeiVirtualBlockIo2PpiGuid ## PRODUCES > > + gEdkiiPeiNvmExpressHostControllerPpiGuid ## CONSUMES > > + gEdkiiIoMmuPpiGuid ## CONSUMES > > + gEfiEndOfPeiSignalPpiGuid ## CONSUMES > > + > > +[Depex] > > + gEfiPeiMemoryDiscoveredPpiGuid AND > > + gEfiPeiMasterBootModePpiGuid AND > > + gEdkiiPeiNvmExpressHostControllerPpiGuid > > + > > +[UserExtensions.TianoCore."ExtraFiles"] > > + NvmExpressPeiExtra.uni > > diff --git a/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.uni > > b/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.uni > > new file mode 100644 > > index 0000000000..1956800faf > > --- /dev/null > > +++ b/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.uni > > @@ -0,0 +1,21 @@ > > +// /** @file > > +// The NvmExpressPei driver is used to manage non-volatile memory > > subsystem > > +// which follows NVM Express specification at PEI phase. > > +// > > +// Copyright (c) 2018, Intel Corporation. All rights reserved.
> > +// > > +// This program and the accompanying materials > > +// are licensed and made available under the terms and conditions > > +// of the BSD License which accompanies this distribution. The > > +// full text of the license may be found at > > +// http://opensource.org/licenses/bsd-license.php > > +// > > +// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" > > BASIS, > > +// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER > > EXPRESS OR IMPLIED. > > +// > > +// **/ > > + > > + > > +#string STR_MODULE_ABSTRACT #language en-US "Manage non- > > volatile memory subsystem at PEI phase" > > + > > +#string STR_MODULE_DESCRIPTION #language en-US "The > > NvmExpressPei driver is used to manage non-volatile memory subsystem > > which follows NVM Express specification at PEI phase." > > diff --git > > a/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiBlockIo.c > > b/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiBlockIo.c > > new file mode 100644 > > index 0000000000..033d263c91 > > --- /dev/null > > +++ b/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiBlockIo.c > > @@ -0,0 +1,531 @@ > > +/** @file > > + The NvmExpressPei driver is used to manage non-volatile memory > > subsystem > > + which follows NVM Express specification at PEI phase. > > + > > + Copyright (c) 2018, Intel Corporation. All rights reserved.
> > + > > + This program and the accompanying materials > > + are licensed and made available under the terms and conditions > > + of the BSD License which accompanies this distribution. The > > + full text of the license may be found at > > + http://opensource.org/licenses/bsd-license.php > > + > > + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" > > BASIS, > > + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER > > EXPRESS OR IMPLIED. > > + > > +**/ > > + > > +#include "NvmExpressPei.h" > > + > > +/** > > + Read some sectors from the device. > > + > > + @param NamespaceInfo The pointer to the > > PEI_NVME_NAMESPACE_INFO data structure. > > + @param Buffer The buffer used to store the data read from= the > > device. > > + @param Lba The start block number. > > + @param Blocks Total block number to be read. > > + > > + @retval EFI_SUCCESS Data are read from the device. > > + @retval Others Fail to read all the data. > > + > > +**/ > > +EFI_STATUS > > +ReadSectors ( > > + IN PEI_NVME_NAMESPACE_INFO *NamespaceInfo, > > + OUT UINTN Buffer, > > + IN UINT64 Lba, > > + IN UINT32 Blocks > > + ) > > +{ > > + EFI_STATUS Status; > > + UINT32 BlockSize; > > + PEI_NVME_CONTROLLER_PRIVATE_DATA *Private; > > + UINT32 Bytes; > > + EDKII_PEI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET > > CommandPacket; > > + EDKII_PEI_NVM_EXPRESS_COMMAND Command; > > + EDKII_PEI_NVM_EXPRESS_COMPLETION Completion; > > + > > + Private =3D NamespaceInfo->Controller; > > + BlockSize =3D NamespaceInfo->Media.BlockSize; > > + Bytes =3D Blocks * BlockSize; > > + > > + ZeroMem (&CommandPacket, > > sizeof(EDKII_PEI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET)); > > + ZeroMem (&Command, sizeof(EDKII_PEI_NVM_EXPRESS_COMMAND)); > > + ZeroMem (&Completion, > > sizeof(EDKII_PEI_NVM_EXPRESS_COMPLETION)); > > + > > + CommandPacket.NvmeCmd =3D &Command; > > + CommandPacket.NvmeCompletion =3D &Completion; > > + > > + CommandPacket.NvmeCmd->Cdw0.Opcode =3D NVME_IO_READ_OPC; > > + CommandPacket.NvmeCmd->Nsid =3D NamespaceInfo->NamespaceId; > > + CommandPacket.TransferBuffer =3D (VOID *)Buffer; > > + > > + CommandPacket.TransferLength =3D Bytes; > > + CommandPacket.CommandTimeout =3D NVME_GENERIC_TIMEOUT; > > + CommandPacket.QueueType =3D NVME_IO_QUEUE; > > + > > + CommandPacket.NvmeCmd->Cdw10 =3D (UINT32)Lba; > > + CommandPacket.NvmeCmd->Cdw11 =3D (UINT32)RShiftU64(Lba, 32); > > + CommandPacket.NvmeCmd->Cdw12 =3D (Blocks - 1) & 0xFFFF; > > + > > + CommandPacket.NvmeCmd->Flags =3D CDW10_VALID | CDW11_VALID | > > CDW12_VALID; > > + > > + Status =3D NvmePassThru ( > > + Private, > > + NamespaceInfo->NamespaceId, > > + &CommandPacket > > + ); > > + return Status; > > +} > > + > > +/** > > + Read some blocks from the device. > > + > > + @param[in] NamespaceInfo The pointer to the > > PEI_NVME_NAMESPACE_INFO data structure. > > + @param[out] Buffer The Buffer used to store the Data read = from the > > device. > > + @param[in] Lba The start block number. > > + @param[in] Blocks Total block number to be read. > > + > > + @retval EFI_SUCCESS Data are read from the device. > > + @retval Others Fail to read all the data. > > + > > +**/ > > +EFI_STATUS > > +NvmeRead ( > > + IN PEI_NVME_NAMESPACE_INFO *NamespaceInfo, > > + OUT UINTN Buffer, > > + IN UINT64 Lba, > > + IN UINTN Blocks > > + ) > > +{ > > + EFI_STATUS Status; > > + UINT32 Retries; > > + UINT32 BlockSize; > > + PEI_NVME_CONTROLLER_PRIVATE_DATA *Private; > > + UINT32 MaxTransferBlocks; > > + UINTN OrginalBlocks; > > + > > + Status =3D EFI_SUCCESS; > > + Retries =3D 0; > > + Private =3D NamespaceInfo->Controller; > > + BlockSize =3D NamespaceInfo->Media.BlockSize; > > + OrginalBlocks =3D Blocks; > > + > > + if (Private->ControllerData->Mdts !=3D 0) { > > + MaxTransferBlocks =3D (1 << (Private->ControllerData->Mdts)) * (1 = << > > (Private->Cap.Mpsmin + 12)) / BlockSize; > > + } else { > > + MaxTransferBlocks =3D 1024; > > + } > > + // > > + // > > + // > > + DEBUG ((DEBUG_INFO, "%a: MaxTransferBlocks =3D 0x%x.\n", > > __FUNCTION__, MaxTransferBlocks)); > > + > > + while (Blocks > 0) { > > + Status =3D ReadSectors ( > > + NamespaceInfo, > > + Buffer, > > + Lba, > > + Blocks > MaxTransferBlocks ? MaxTransferBlocks : (UINT3= 2)Blocks > > + ); > > + if (EFI_ERROR(Status)) { > > + Retries++; > > + MaxTransferBlocks =3D MaxTransferBlocks >> 1; > > + > > + if (Retries > NVME_READ_MAX_RETRY || MaxTransferBlocks < 1) { > > + DEBUG ((DEBUG_ERROR, "%a: ReadSectors fail, Status - %r\n", > > __FUNCTION__, Status)); > > + break; > > + } > > + DEBUG (( > > + DEBUG_INFO, > > + "%a: ReadSectors fail, retry with smaller transfer block numbe= r - > > 0x%x\n", > > + __FUNCTION__, > > + MaxTransferBlocks > > + )); > > + continue; > > + } > > + > > + if (Blocks > MaxTransferBlocks) { > > + Blocks -=3D MaxTransferBlocks; > > + Buffer +=3D (MaxTransferBlocks * BlockSize); > > + Lba +=3D MaxTransferBlocks; > > + } else { > > + Blocks =3D 0; > > + } > > + } > > + > > + DEBUG ((DEBUG_INFO, "%a: Lba =3D 0x%08Lx, Original =3D 0x%08Lx, " > > + "Remaining =3D 0x%08Lx, BlockSize =3D 0x%x, Status =3D %r\n", __FU= NCTION__, > > Lba, > > + (UINT64)OrginalBlocks, (UINT64)Blocks, BlockSize, Status)); > > + return Status; > > +} > > + > > +/** > > + Gets the count of block I/O devices that one specific block driver d= etects. > > + > > + This function is used for getting the count of block I/O devices tha= t one > > + specific block driver detects. If no device is detected, then the fu= nction > > + will return zero. > > + > > + @param[in] PeiServices General-purpose services that are a= vailable > > + to every PEIM. > > + @param[in] This Indicates the > EFI_PEI_RECOVERY_BLOCK_IO_PPI > > + instance. > > + @param[out] NumberBlockDevices The number of block I/O devices > > discovered. > > + > > + @retval EFI_SUCCESS The operation performed successfull= y. > > + > > +**/ > > +EFI_STATUS > > +EFIAPI > > +NvmeBlockIoPeimGetDeviceNo ( > > + IN EFI_PEI_SERVICES **PeiServices, > > + IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This, > > + OUT UINTN *NumberBlockDevices > > + ) > > +{ > > + PEI_NVME_CONTROLLER_PRIVATE_DATA *Private; > > + > > + if (This =3D=3D NULL || NumberBlockDevices =3D=3D NULL) { > > + return EFI_INVALID_PARAMETER; > > + } > > + > > + Private =3D GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_BLKIO > > (This); > > + *NumberBlockDevices =3D Private->ActiveNamespaceNum; > > + > > + return EFI_SUCCESS; > > +} > > + > > +/** > > + Gets a block device's media information. > > + > > + This function will provide the caller with the specified block devic= e's media > > + information. If the media changes, calling this function will update= the > > media > > + information accordingly. > > + > > + @param[in] PeiServices General-purpose services that are availabl= e to > > every > > + PEIM > > + @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PP= I > > instance. > > + @param[in] DeviceIndex Specifies the block device to which the fu= nction > > wants > > + to talk. Because the driver that implement= s Block I/O > > + PPIs will manage multiple block devices, t= he PPIs that > > + want to talk to a single device must speci= fy the > > + device index that was assigned during the = enumeration > > + process. This index is a number from one t= o > > + NumberBlockDevices. > > + @param[out] MediaInfo The media information of the specified blo= ck > > media. > > + The caller is responsible for the ownershi= p of this > > + data structure. > > + > > + @par Note: > > + The MediaInfo structure describes an enumeration of possible blo= ck > > device > > + types. This enumeration exists because no device paths are actu= ally > > passed > > + across interfaces that describe the type or class of hardware th= at is > > publishing > > + the block I/O interface. This enumeration will allow for policy = decisions > > + in the Recovery PEIM, such as "Try to recover from legacy floppy= first, > > + LS-120 second, CD-ROM third." If there are multiple partitions > abstracted > > + by a given device type, they should be reported in ascending ord= er; this > > + order also applies to nested partitions, such as legacy MBR, whe= re the > > + outermost partitions would have precedence in the reporting orde= r. The > > + same logic applies to systems such as IDE that have precedence > > relationships > > + like "Master/Slave" or "Primary/Secondary". The master device sh= ould > > be > > + reported first, the slave second. > > + > > + @retval EFI_SUCCESS Media information about the specified blo= ck > > device > > + was obtained successfully. > > + @retval EFI_DEVICE_ERROR Cannot get the media information due to a > > hardware > > + error. > > + > > +**/ > > +EFI_STATUS > > +EFIAPI > > +NvmeBlockIoPeimGetMediaInfo ( > > + IN EFI_PEI_SERVICES **PeiServices, > > + IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This, > > + IN UINTN DeviceIndex, > > + OUT EFI_PEI_BLOCK_IO_MEDIA *MediaInfo > > + ) > > +{ > > + PEI_NVME_CONTROLLER_PRIVATE_DATA *Private; > > + > > + if (This =3D=3D NULL || MediaInfo =3D=3D NULL) { > > + return EFI_INVALID_PARAMETER; > > + } > > + > > + Private =3D GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_BLKIO > > (This); > > + > > + if ((DeviceIndex =3D=3D 0) || (DeviceIndex > Private->ActiveNamespac= eNum)) > > { > > + return EFI_INVALID_PARAMETER; > > + } > > + > > + MediaInfo->DeviceType =3D (EFI_PEI_BLOCK_DEVICE_TYPE) > > EDKII_PEI_BLOCK_DEVICE_TYPE_NVME; > > + MediaInfo->MediaPresent =3D TRUE; > > + MediaInfo->LastBlock =3D (UINTN)Private->NamespaceInfo[DeviceInde= x- > > 1].Media.LastBlock; > > + MediaInfo->BlockSize =3D Private->NamespaceInfo[DeviceIndex- > > 1].Media.BlockSize; > > + > > + return EFI_SUCCESS; > > +} > > + > > +/** > > + Reads the requested number of blocks from the specified block device= . > > + > > + The function reads the requested number of blocks from the device. A= ll > > the > > + blocks are read, or an error is returned. If there is no media in th= e device, > > + the function returns EFI_NO_MEDIA. > > + > > + @param[in] PeiServices General-purpose services that are availabl= e to > > + every PEIM. > > + @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PP= I > > instance. > > + @param[in] DeviceIndex Specifies the block device to which the fu= nction > > wants > > + to talk. Because the driver that implement= s Block I/O > > + PPIs will manage multiple block devices, P= PIs that > > + want to talk to a single device must speci= fy the device > > + index that was assigned during the enumera= tion process. > > + This index is a number from one to NumberB= lockDevices. > > + @param[in] StartLBA The starting logical block address (LBA) t= o read > > from > > + on the device > > + @param[in] BufferSize The size of the Buffer in bytes. This numb= er must > > be > > + a multiple of the intrinsic block size of = the device. > > + @param[out] Buffer A pointer to the destination buffer for th= e data. > > + The caller is responsible for the ownershi= p of the > > + buffer. > > + > > + @retval EFI_SUCCESS The data was read correctly from the= device. > > + @retval EFI_DEVICE_ERROR The device reported an error while > > attempting > > + to perform the read operation. > > + @retval EFI_INVALID_PARAMETER The read request contains LBAs that > > are not > > + valid, or the buffer is not properly= aligned. > > + @retval EFI_NO_MEDIA There is no media in the device. > > + @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a > > multiple of > > + the intrinsic block size of the devi= ce. > > + > > +**/ > > +EFI_STATUS > > +EFIAPI > > +NvmeBlockIoPeimReadBlocks ( > > + IN EFI_PEI_SERVICES **PeiServices, > > + IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This, > > + IN UINTN DeviceIndex, > > + IN EFI_PEI_LBA StartLBA, > > + IN UINTN BufferSize, > > + OUT VOID *Buffer > > + ) > > +{ > > + PEI_NVME_CONTROLLER_PRIVATE_DATA *Private; > > + PEI_NVME_NAMESPACE_INFO *NamespaceInfo; > > + UINT32 BlockSize; > > + UINTN NumberOfBlocks; > > + > > + Private =3D GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_BLKIO > > (This); > > + > > + // > > + // Check parameters > > + // > > + if (This =3D=3D NULL || Buffer =3D=3D NULL) { > > + return EFI_INVALID_PARAMETER; > > + } > > + > > + if (BufferSize =3D=3D 0) { > > + return EFI_SUCCESS; > > + } > > + > > + if ((DeviceIndex =3D=3D 0) || (DeviceIndex > Private->ActiveNamespac= eNum)) > > { > > + return EFI_INVALID_PARAMETER; > > + } > > + > > + // > > + // Check BufferSize and StartLBA > > + // > > + NamespaceInfo =3D &(Private->NamespaceInfo[DeviceIndex - 1]); > > + BlockSize =3D NamespaceInfo->Media.BlockSize; > > + if (BufferSize % BlockSize !=3D 0) { > > + return EFI_BAD_BUFFER_SIZE; > > + } > > + > > + if (StartLBA > NamespaceInfo->Media.LastBlock) { > > + return EFI_INVALID_PARAMETER; > > + } > > + NumberOfBlocks =3D BufferSize / BlockSize; > > + if (NumberOfBlocks - 1 > NamespaceInfo->Media.LastBlock - StartLBA) = { > > + return EFI_INVALID_PARAMETER; > > + } > > + > > + return NvmeRead (NamespaceInfo, (UINTN)Buffer, StartLBA, > > NumberOfBlocks); > > +} > > + > > +/** > > + Gets the count of block I/O devices that one specific block driver d= etects. > > + > > + This function is used for getting the count of block I/O devices tha= t one > > + specific block driver detects. If no device is detected, then the fu= nction > > + will return zero. > > + > > + @param[in] PeiServices General-purpose services that are a= vailable > > + to every PEIM. > > + @param[in] This Indicates the > > EFI_PEI_RECOVERY_BLOCK_IO2_PPI > > + instance. > > + @param[out] NumberBlockDevices The number of block I/O devices > > discovered. > > + > > + @retval EFI_SUCCESS The operation performed successfull= y. > > + > > +**/ > > +EFI_STATUS > > +EFIAPI > > +NvmeBlockIoPeimGetDeviceNo2 ( > > + IN EFI_PEI_SERVICES **PeiServices, > > + IN EFI_PEI_RECOVERY_BLOCK_IO2_PPI *This, > > + OUT UINTN *NumberBlockDevices > > + ) > > +{ > > + PEI_NVME_CONTROLLER_PRIVATE_DATA *Private; > > + > > + if (This =3D=3D NULL || NumberBlockDevices =3D=3D NULL) { > > + return EFI_INVALID_PARAMETER; > > + } > > + > > + Private =3D GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_BLKIO2 > > (This); > > + *NumberBlockDevices =3D Private->ActiveNamespaceNum; > > + > > + return EFI_SUCCESS; > > +} > > + > > +/** > > + Gets a block device's media information. > > + > > + This function will provide the caller with the specified block devic= e's media > > + information. If the media changes, calling this function will update= the > > media > > + information accordingly. > > + > > + @param[in] PeiServices General-purpose services that are availabl= e to > > every > > + PEIM > > + @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO2_P= PI > > instance. > > + @param[in] DeviceIndex Specifies the block device to which the fu= nction > > wants > > + to talk. Because the driver that implement= s Block I/O > > + PPIs will manage multiple block devices, t= he PPIs that > > + want to talk to a single device must speci= fy the > > + device index that was assigned during the = enumeration > > + process. This index is a number from one t= o > > + NumberBlockDevices. > > + @param[out] MediaInfo The media information of the specified blo= ck > > media. > > + The caller is responsible for the ownershi= p of this > > + data structure. > > + > > + @par Note: > > + The MediaInfo structure describes an enumeration of possible blo= ck > > device > > + types. This enumeration exists because no device paths are actu= ally > > passed > > + across interfaces that describe the type or class of hardware th= at is > > publishing > > + the block I/O interface. This enumeration will allow for policy = decisions > > + in the Recovery PEIM, such as "Try to recover from legacy floppy= first, > > + LS-120 second, CD-ROM third." If there are multiple partitions > abstracted > > + by a given device type, they should be reported in ascending ord= er; this > > + order also applies to nested partitions, such as legacy MBR, whe= re the > > + outermost partitions would have precedence in the reporting orde= r. The > > + same logic applies to systems such as IDE that have precedence > > relationships > > + like "Master/Slave" or "Primary/Secondary". The master device sh= ould > > be > > + reported first, the slave second. > > + > > + @retval EFI_SUCCESS Media information about the specified blo= ck > > device > > + was obtained successfully. > > + @retval EFI_DEVICE_ERROR Cannot get the media information due to a > > hardware > > + error. > > + > > +**/ > > +EFI_STATUS > > +EFIAPI > > +NvmeBlockIoPeimGetMediaInfo2 ( > > + IN EFI_PEI_SERVICES **PeiServices, > > + IN EFI_PEI_RECOVERY_BLOCK_IO2_PPI *This, > > + IN UINTN DeviceIndex, > > + OUT EFI_PEI_BLOCK_IO2_MEDIA *MediaInfo > > + ) > > +{ > > + EFI_STATUS Status; > > + PEI_NVME_CONTROLLER_PRIVATE_DATA *Private; > > + EFI_PEI_BLOCK_IO_MEDIA Media; > > + > > + if (This =3D=3D NULL || MediaInfo =3D=3D NULL) { > > + return EFI_INVALID_PARAMETER; > > + } > > + > > + Private =3D GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_BLKIO2 > > (This); > > + > > + Status =3D NvmeBlockIoPeimGetMediaInfo ( > > + PeiServices, > > + &Private->BlkIoPpi, > > + DeviceIndex, > > + &Media > > + ); > > + if (EFI_ERROR (Status)) { > > + return Status; > > + } > > + > > + CopyMem ( > > + MediaInfo, > > + &(Private->NamespaceInfo[DeviceIndex - 1].Media), > > + sizeof (EFI_PEI_BLOCK_IO2_MEDIA) > > + ); > > + > > + return EFI_SUCCESS; > > +} > > + > > +/** > > + Reads the requested number of blocks from the specified block device= . > > + > > + The function reads the requested number of blocks from the device. A= ll > > the > > + blocks are read, or an error is returned. If there is no media in th= e device, > > + the function returns EFI_NO_MEDIA. > > + > > + @param[in] PeiServices General-purpose services that are availabl= e to > > + every PEIM. > > + @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO2_P= PI > > instance. > > + @param[in] DeviceIndex Specifies the block device to which the fu= nction > > wants > > + to talk. Because the driver that implement= s Block I/O > > + PPIs will manage multiple block devices, P= PIs that > > + want to talk to a single device must speci= fy the device > > + index that was assigned during the enumera= tion process. > > + This index is a number from one to NumberB= lockDevices. > > + @param[in] StartLBA The starting logical block address (LBA) t= o read > > from > > + on the device > > + @param[in] BufferSize The size of the Buffer in bytes. This numb= er must > > be > > + a multiple of the intrinsic block size of = the device. > > + @param[out] Buffer A pointer to the destination buffer for th= e data. > > + The caller is responsible for the ownershi= p of the > > + buffer. > > + > > + @retval EFI_SUCCESS The data was read correctly from the= device. > > + @retval EFI_DEVICE_ERROR The device reported an error while > > attempting > > + to perform the read operation. > > + @retval EFI_INVALID_PARAMETER The read request contains LBAs that > > are not > > + valid, or the buffer is not properly= aligned. > > + @retval EFI_NO_MEDIA There is no media in the device. > > + @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a > > multiple of > > + the intrinsic block size of the devi= ce. > > + > > +**/ > > +EFI_STATUS > > +EFIAPI > > +NvmeBlockIoPeimReadBlocks2 ( > > + IN EFI_PEI_SERVICES **PeiServices, > > + IN EFI_PEI_RECOVERY_BLOCK_IO2_PPI *This, > > + IN UINTN DeviceIndex, > > + IN EFI_PEI_LBA StartLBA, > > + IN UINTN BufferSize, > > + OUT VOID *Buffer > > + ) > > +{ > > + PEI_NVME_CONTROLLER_PRIVATE_DATA *Private; > > + > > + if (This =3D=3D NULL) { > > + return EFI_INVALID_PARAMETER; > > + } > > + > > + Private =3D GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_BLKIO2 > > (This); > > + return NvmeBlockIoPeimReadBlocks ( > > + PeiServices, > > + &Private->BlkIoPpi, > > + DeviceIndex, > > + StartLBA, > > + BufferSize, > > + Buffer > > + ); > > +} > > diff --git > > a/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiBlockIo.h > > b/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiBlockIo.h > > new file mode 100644 > > index 0000000000..76e5970fe7 > > --- /dev/null > > +++ b/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiBlockIo.h > > @@ -0,0 +1,266 @@ > > +/** @file > > + The NvmExpressPei driver is used to manage non-volatile memory > > subsystem > > + which follows NVM Express specification at PEI phase. > > + > > + Copyright (c) 2018, Intel Corporation. All rights reserved.
> > + > > + This program and the accompanying materials > > + are licensed and made available under the terms and conditions > > + of the BSD License which accompanies this distribution. The > > + full text of the license may be found at > > + http://opensource.org/licenses/bsd-license.php > > + > > + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" > > BASIS, > > + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER > > EXPRESS OR IMPLIED. > > + > > +**/ > > + > > +#ifndef _NVM_EXPRESS_PEI_BLOCKIO_H_ > > +#define _NVM_EXPRESS_PEI_BLOCKIO_H_ > > + > > +// > > +// Nvme device for EFI_PEI_BLOCK_DEVICE_TYPE > > +// > > +#define EDKII_PEI_BLOCK_DEVICE_TYPE_NVME 7 > > + > > +#define NVME_READ_MAX_RETRY 3 > > + > > +/** > > + Gets the count of block I/O devices that one specific block driver d= etects. > > + > > + This function is used for getting the count of block I/O devices tha= t one > > + specific block driver detects. If no device is detected, then the fu= nction > > + will return zero. > > + > > + @param[in] PeiServices General-purpose services that are a= vailable > > + to every PEIM. > > + @param[in] This Indicates the > EFI_PEI_RECOVERY_BLOCK_IO_PPI > > + instance. > > + @param[out] NumberBlockDevices The number of block I/O devices > > discovered. > > + > > + @retval EFI_SUCCESS The operation performed successfull= y. > > + > > +**/ > > +EFI_STATUS > > +EFIAPI > > +NvmeBlockIoPeimGetDeviceNo ( > > + IN EFI_PEI_SERVICES **PeiServices, > > + IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This, > > + OUT UINTN *NumberBlockDevices > > + ); > > + > > +/** > > + Gets a block device's media information. > > + > > + This function will provide the caller with the specified block devic= e's media > > + information. If the media changes, calling this function will update= the > > media > > + information accordingly. > > + > > + @param[in] PeiServices General-purpose services that are availabl= e to > > every > > + PEIM > > + @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PP= I > > instance. > > + @param[in] DeviceIndex Specifies the block device to which the fu= nction > > wants > > + to talk. Because the driver that implement= s Block I/O > > + PPIs will manage multiple block devices, t= he PPIs that > > + want to talk to a single device must speci= fy the > > + device index that was assigned during the = enumeration > > + process. This index is a number from one t= o > > + NumberBlockDevices. > > + @param[out] MediaInfo The media information of the specified blo= ck > > media. > > + The caller is responsible for the ownershi= p of this > > + data structure. > > + > > + @par Note: > > + The MediaInfo structure describes an enumeration of possible blo= ck > > device > > + types. This enumeration exists because no device paths are actu= ally > > passed > > + across interfaces that describe the type or class of hardware th= at is > > publishing > > + the block I/O interface. This enumeration will allow for policy = decisions > > + in the Recovery PEIM, such as "Try to recover from legacy floppy= first, > > + LS-120 second, CD-ROM third." If there are multiple partitions > abstracted > > + by a given device type, they should be reported in ascending ord= er; this > > + order also applies to nested partitions, such as legacy MBR, whe= re the > > + outermost partitions would have precedence in the reporting orde= r. The > > + same logic applies to systems such as IDE that have precedence > > relationships > > + like "Master/Slave" or "Primary/Secondary". The master device sh= ould > > be > > + reported first, the slave second. > > + > > + @retval EFI_SUCCESS Media information about the specified blo= ck > > device > > + was obtained successfully. > > + @retval EFI_DEVICE_ERROR Cannot get the media information due to a > > hardware > > + error. > > + > > +**/ > > +EFI_STATUS > > +EFIAPI > > +NvmeBlockIoPeimGetMediaInfo ( > > + IN EFI_PEI_SERVICES **PeiServices, > > + IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This, > > + IN UINTN DeviceIndex, > > + OUT EFI_PEI_BLOCK_IO_MEDIA *MediaInfo > > + ); > > + > > +/** > > + Reads the requested number of blocks from the specified block device= . > > + > > + The function reads the requested number of blocks from the device. A= ll > > the > > + blocks are read, or an error is returned. If there is no media in th= e device, > > + the function returns EFI_NO_MEDIA. > > + > > + @param[in] PeiServices General-purpose services that are availabl= e to > > + every PEIM. > > + @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PP= I > > instance. > > + @param[in] DeviceIndex Specifies the block device to which the fu= nction > > wants > > + to talk. Because the driver that implement= s Block I/O > > + PPIs will manage multiple block devices, P= PIs that > > + want to talk to a single device must speci= fy the device > > + index that was assigned during the enumera= tion process. > > + This index is a number from one to NumberB= lockDevices. > > + @param[in] StartLBA The starting logical block address (LBA) t= o read > > from > > + on the device > > + @param[in] BufferSize The size of the Buffer in bytes. This numb= er must > > be > > + a multiple of the intrinsic block size of = the device. > > + @param[out] Buffer A pointer to the destination buffer for th= e data. > > + The caller is responsible for the ownershi= p of the > > + buffer. > > + > > + @retval EFI_SUCCESS The data was read correctly from the= device. > > + @retval EFI_DEVICE_ERROR The device reported an error while > > attempting > > + to perform the read operation. > > + @retval EFI_INVALID_PARAMETER The read request contains LBAs that > > are not > > + valid, or the buffer is not properly= aligned. > > + @retval EFI_NO_MEDIA There is no media in the device. > > + @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a > > multiple of > > + the intrinsic block size of the devi= ce. > > + > > +**/ > > +EFI_STATUS > > +EFIAPI > > +NvmeBlockIoPeimReadBlocks ( > > + IN EFI_PEI_SERVICES **PeiServices, > > + IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This, > > + IN UINTN DeviceIndex, > > + IN EFI_PEI_LBA StartLBA, > > + IN UINTN BufferSize, > > + OUT VOID *Buffer > > + ); > > + > > +/** > > + Gets the count of block I/O devices that one specific block driver d= etects. > > + > > + This function is used for getting the count of block I/O devices tha= t one > > + specific block driver detects. If no device is detected, then the fu= nction > > + will return zero. > > + > > + @param[in] PeiServices General-purpose services that are a= vailable > > + to every PEIM. > > + @param[in] This Indicates the > > EFI_PEI_RECOVERY_BLOCK_IO2_PPI > > + instance. > > + @param[out] NumberBlockDevices The number of block I/O devices > > discovered. > > + > > + @retval EFI_SUCCESS The operation performed successfull= y. > > + > > +**/ > > +EFI_STATUS > > +EFIAPI > > +NvmeBlockIoPeimGetDeviceNo2 ( > > + IN EFI_PEI_SERVICES **PeiServices, > > + IN EFI_PEI_RECOVERY_BLOCK_IO2_PPI *This, > > + OUT UINTN *NumberBlockDevices > > + ); > > + > > +/** > > + Gets a block device's media information. > > + > > + This function will provide the caller with the specified block devic= e's media > > + information. If the media changes, calling this function will update= the > > media > > + information accordingly. > > + > > + @param[in] PeiServices General-purpose services that are availabl= e to > > every > > + PEIM > > + @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO2_P= PI > > instance. > > + @param[in] DeviceIndex Specifies the block device to which the fu= nction > > wants > > + to talk. Because the driver that implement= s Block I/O > > + PPIs will manage multiple block devices, t= he PPIs that > > + want to talk to a single device must speci= fy the > > + device index that was assigned during the = enumeration > > + process. This index is a number from one t= o > > + NumberBlockDevices. > > + @param[out] MediaInfo The media information of the specified blo= ck > > media. > > + The caller is responsible for the ownershi= p of this > > + data structure. > > + > > + @par Note: > > + The MediaInfo structure describes an enumeration of possible blo= ck > > device > > + types. This enumeration exists because no device paths are actu= ally > > passed > > + across interfaces that describe the type or class of hardware th= at is > > publishing > > + the block I/O interface. This enumeration will allow for policy = decisions > > + in the Recovery PEIM, such as "Try to recover from legacy floppy= first, > > + LS-120 second, CD-ROM third." If there are multiple partitions > abstracted > > + by a given device type, they should be reported in ascending ord= er; this > > + order also applies to nested partitions, such as legacy MBR, whe= re the > > + outermost partitions would have precedence in the reporting orde= r. The > > + same logic applies to systems such as IDE that have precedence > > relationships > > + like "Master/Slave" or "Primary/Secondary". The master device sh= ould > > be > > + reported first, the slave second. > > + > > + @retval EFI_SUCCESS Media information about the specified blo= ck > > device > > + was obtained successfully. > > + @retval EFI_DEVICE_ERROR Cannot get the media information due to a > > hardware > > + error. > > + > > +**/ > > +EFI_STATUS > > +EFIAPI > > +NvmeBlockIoPeimGetMediaInfo2 ( > > + IN EFI_PEI_SERVICES **PeiServices, > > + IN EFI_PEI_RECOVERY_BLOCK_IO2_PPI *This, > > + IN UINTN DeviceIndex, > > + OUT EFI_PEI_BLOCK_IO2_MEDIA *MediaInfo > > + ); > > + > > +/** > > + Reads the requested number of blocks from the specified block device= . > > + > > + The function reads the requested number of blocks from the device. A= ll > > the > > + blocks are read, or an error is returned. If there is no media in th= e device, > > + the function returns EFI_NO_MEDIA. > > + > > + @param[in] PeiServices General-purpose services that are availabl= e to > > + every PEIM. > > + @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO2_P= PI > > instance. > > + @param[in] DeviceIndex Specifies the block device to which the fu= nction > > wants > > + to talk. Because the driver that implement= s Block I/O > > + PPIs will manage multiple block devices, P= PIs that > > + want to talk to a single device must speci= fy the device > > + index that was assigned during the enumera= tion process. > > + This index is a number from one to NumberB= lockDevices. > > + @param[in] StartLBA The starting logical block address (LBA) t= o read > > from > > + on the device > > + @param[in] BufferSize The size of the Buffer in bytes. This numb= er must > > be > > + a multiple of the intrinsic block size of = the device. > > + @param[out] Buffer A pointer to the destination buffer for th= e data. > > + The caller is responsible for the ownershi= p of the > > + buffer. > > + > > + @retval EFI_SUCCESS The data was read correctly from the= device. > > + @retval EFI_DEVICE_ERROR The device reported an error while > > attempting > > + to perform the read operation. > > + @retval EFI_INVALID_PARAMETER The read request contains LBAs that > > are not > > + valid, or the buffer is not properly= aligned. > > + @retval EFI_NO_MEDIA There is no media in the device. > > + @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a > > multiple of > > + the intrinsic block size of the devi= ce. > > + > > +**/ > > +EFI_STATUS > > +EFIAPI > > +NvmeBlockIoPeimReadBlocks2 ( > > + IN EFI_PEI_SERVICES **PeiServices, > > + IN EFI_PEI_RECOVERY_BLOCK_IO2_PPI *This, > > + IN UINTN DeviceIndex, > > + IN EFI_PEI_LBA StartLBA, > > + IN UINTN BufferSize, > > + OUT VOID *Buffer > > + ); > > + > > +#endif > > diff --git > > a/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiExtra.uni > > b/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiExtra.uni > > new file mode 100644 > > index 0000000000..8c97c0a8a9 > > --- /dev/null > > +++ b/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiExtra.uni > > @@ -0,0 +1,19 @@ > > +// /** @file > > +// NvmExpressPei Localized Strings and Content > > +// > > +// Copyright (c) 2018, Intel Corporation. All rights reserved.
> > +// > > +// This program and the accompanying materials > > +// are licensed and made available under the terms and conditions > > +// of the BSD License which accompanies this distribution. The > > +// full text of the license may be found at > > +// http://opensource.org/licenses/bsd-license.php > > +// > > +// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" > > BASIS, > > +// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER > > EXPRESS OR IMPLIED. > > +// > > +// **/ > > + > > +#string STR_PROPERTIES_MODULE_NAME > > +#language en-US > > +"NVM Express Peim" > > diff --git a/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiHci.c > > b/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiHci.c > > new file mode 100644 > > index 0000000000..d4056a2a5b > > --- /dev/null > > +++ b/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiHci.c > > @@ -0,0 +1,748 @@ > > +/** @file > > + The NvmExpressPei driver is used to manage non-volatile memory > > subsystem > > + which follows NVM Express specification at PEI phase. > > + > > + Copyright (c) 2018, Intel Corporation. All rights reserved.
> > + > > + This program and the accompanying materials > > + are licensed and made available under the terms and conditions > > + of the BSD License which accompanies this distribution. The > > + full text of the license may be found at > > + http://opensource.org/licenses/bsd-license.php > > + > > + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" > > BASIS, > > + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER > > EXPRESS OR IMPLIED. > > + > > +**/ > > + > > +#include "NvmExpressPei.h" > > + > > +/** > > + Transfer MMIO Data to memory. > > + > > + @param[in,out] MemBuffer Destination: Memory address. > > + @param[in] MmioAddr Source: MMIO address. > > + @param[in] Size Size for read. > > + > > + @retval EFI_SUCCESS MMIO read sucessfully. > > + > > +**/ > > +EFI_STATUS > > +NvmeMmioRead ( > > + IN OUT VOID *MemBuffer, > > + IN UINTN MmioAddr, > > + IN UINTN Size > > + ) > > +{ > > + UINTN Offset; > > + UINT8 Data; > > + UINT8 *Ptr; > > + > > + // priority has adjusted > > + switch (Size) { > > + case 4: > > + *((UINT32 *)MemBuffer) =3D MmioRead32 (MmioAddr); > > + break; > > + > > + case 8: > > + *((UINT64 *)MemBuffer) =3D MmioRead64 (MmioAddr); > > + break; > > + > > + case 2: > > + *((UINT16 *)MemBuffer) =3D MmioRead16 (MmioAddr); > > + break; > > + > > + case 1: > > + *((UINT8 *)MemBuffer) =3D MmioRead8 (MmioAddr); > > + break; > > + > > + default: > > + Ptr =3D (UINT8 *)MemBuffer; > > + for (Offset =3D 0; Offset < Size; Offset +=3D 1) { > > + Data =3D MmioRead8 (MmioAddr + Offset); > > + Ptr[Offset] =3D Data; > > + } > > + break; > > + } > > + > > + return EFI_SUCCESS; > > +} > > + > > +/** > > + Transfer memory data to MMIO. > > + > > + @param[in,out] MmioAddr Destination: MMIO address. > > + @param[in] MemBuffer Source: Memory address. > > + @param[in] Size Size for write. > > + > > + @retval EFI_SUCCESS MMIO write sucessfully. > > + > > +**/ > > +EFI_STATUS > > +NvmeMmioWrite ( > > + IN OUT UINTN MmioAddr, > > + IN VOID *MemBuffer, > > + IN UINTN Size > > + ) > > +{ > > + UINTN Offset; > > + UINT8 Data; > > + UINT8 *Ptr; > > + > > + // priority has adjusted > > + switch (Size) { > > + case 4: > > + MmioWrite32 (MmioAddr, *((UINT32 *)MemBuffer)); > > + break; > > + > > + case 8: > > + MmioWrite64 (MmioAddr, *((UINT64 *)MemBuffer)); > > + break; > > + > > + case 2: > > + MmioWrite16 (MmioAddr, *((UINT16 *)MemBuffer)); > > + break; > > + > > + case 1: > > + MmioWrite8 (MmioAddr, *((UINT8 *)MemBuffer)); > > + break; > > + > > + default: > > + Ptr =3D (UINT8 *)MemBuffer; > > + for (Offset =3D 0; Offset < Size; Offset +=3D 1) { > > + Data =3D Ptr[Offset]; > > + MmioWrite8 (MmioAddr + Offset, Data); > > + } > > + break; > > + } > > + > > + return EFI_SUCCESS; > > +} > > + > > +/** > > + Get the page offset for specific NVME based memory. > > + > > + @param[in] BaseMemIndex The Index of BaseMem (0-based). > > + > > + @retval - The page count for specific BaseMem Index > > + > > +**/ > > +UINT32 > > +NvmeBaseMemPageOffset ( > > + IN UINTN BaseMemIndex > > + ) > > +{ > > + UINT32 Pages; > > + UINTN Index; > > + UINT32 PageSizeList[5]; > > + > > + PageSizeList[0] =3D 1; /* ASQ */ > > + PageSizeList[1] =3D 1; /* ACQ */ > > + PageSizeList[2] =3D 1; /* SQs */ > > + PageSizeList[3] =3D 1; /* CQs */ > > + PageSizeList[4] =3D NVME_PRP_SIZE; /* PRPs */ > > + > > + if (BaseMemIndex > MAX_BASEMEM_COUNT) { > > + DEBUG ((DEBUG_ERROR, "%a: The input BaseMem index is invalid.\n", > > __FUNCTION__)); > > + ASSERT (FALSE); > > + return 0; > > + } > > + > > + Pages =3D 0; > > + for (Index =3D 0; Index < BaseMemIndex; Index++) { > > + Pages +=3D PageSizeList[Index]; > > + } > > + > > + return Pages; > > +} > > + > > +/** > > + Wait for NVME controller status to be ready or not. > > + > > + @param[in] Private The pointer to the > > PEI_NVME_CONTROLLER_PRIVATE_DATA data structure. > > + @param[in] WaitReady Flag for waitting status ready or not. > > + > > + @return EFI_SUCCESS Successfully to wait specific status. > > + @return others Fail to wait for specific controller status. > > + > > +**/ > > +EFI_STATUS > > +NvmeWaitController ( > > + IN PEI_NVME_CONTROLLER_PRIVATE_DATA *Private, > > + IN BOOLEAN WaitReady > > + ) > > +{ > > + NVME_CSTS Csts; > > + EFI_STATUS Status; > > + UINT32 Index; > > + UINT8 Timeout; > > + > > + // > > + // Cap.To specifies max delay time in 500ms increments for Csts.Rdy = to set > > after > > + // Cc.Enable. Loop produces a 1 millisecond delay per itteration, up= to 500 > * > > Cap.To. > > + // > > + if (Private->Cap.To =3D=3D 0) { > > + Timeout =3D 1; > > + } else { > > + Timeout =3D Private->Cap.To; > > + } > > + > > + Status =3D EFI_SUCCESS; > > + for(Index =3D (Timeout * 500); Index !=3D 0; --Index) { > > + MicroSecondDelay (1000); > > + > > + // > > + // Check if the controller is initialized > > + // > > + Status =3D NVME_GET_CSTS (Private, &Csts); > > + if (EFI_ERROR(Status)) { > > + DEBUG ((DEBUG_ERROR, "%a: NVME_GET_CSTS fail, Status - %r\n", > > __FUNCTION__, Status)); > > + return Status; > > + } > > + > > + if ((BOOLEAN) Csts.Rdy =3D=3D WaitReady) { > > + break; > > + } > > + } > > + > > + if (Index =3D=3D 0) { > > + Status =3D EFI_TIMEOUT; > > + } > > + > > + return Status; > > +} > > + > > +/** > > + Disable the Nvm Express controller. > > + > > + @param[in] Private The pointer to the > > PEI_NVME_CONTROLLER_PRIVATE_DATA data structure. > > + > > + @return EFI_SUCCESS Successfully disable the controller. > > + @return others Fail to disable the controller. > > + > > +**/ > > +EFI_STATUS > > +NvmeDisableController ( > > + IN PEI_NVME_CONTROLLER_PRIVATE_DATA *Private > > + ) > > +{ > > + NVME_CC Cc; > > + NVME_CSTS Csts; > > + EFI_STATUS Status; > > + > > + Status =3D NVME_GET_CSTS (Private, &Csts); > > + > > + // > > + // Read Controller Configuration Register. > > + // > > + Status =3D NVME_GET_CC (Private, &Cc); > > + if (EFI_ERROR (Status)) { > > + DEBUG ((DEBUG_ERROR, "%a: NVME_GET_CC fail, Status - %r\n", > > __FUNCTION__, Status)); > > + goto ErrorExit; > > + } > > + > > + if (Cc.En =3D=3D 1) { > > + Cc.En =3D 0; > > + // > > + // Disable the controller. > > + // > > + Status =3D NVME_SET_CC (Private, &Cc); > > + if (EFI_ERROR (Status)) { > > + DEBUG ((DEBUG_ERROR, "%a: NVME_SET_CC fail, Status - %r\n", > > __FUNCTION__, Status)); > > + goto ErrorExit; > > + } > > + } > > + > > + Status =3D NvmeWaitController (Private, FALSE); > > + if (EFI_ERROR (Status)) { > > + DEBUG ((DEBUG_ERROR, "%a: NvmeWaitController fail, Status - %r\n", > > __FUNCTION__, Status)); > > + goto ErrorExit; > > + } > > + > > + return EFI_SUCCESS; > > + > > +ErrorExit: > > + DEBUG ((DEBUG_ERROR, "%a fail, Status - %r\n", __FUNCTION__, Status)= ); > > + return Status; > > +} > > + > > +/** > > + Enable the Nvm Express controller. > > + > > + @param[in] Private The pointer to the > > PEI_NVME_CONTROLLER_PRIVATE_DATA data structure. > > + > > + @return EFI_SUCCESS Successfully enable the controller. > > + @return EFI_DEVICE_ERROR Fail to enable the controller. > > + @return EFI_TIMEOUT Fail to enable the controller in given t= ime slot. > > + > > +**/ > > +EFI_STATUS > > +NvmeEnableController ( > > + IN PEI_NVME_CONTROLLER_PRIVATE_DATA *Private > > + ) > > +{ > > + NVME_CC Cc; > > + EFI_STATUS Status; > > + > > + // > > + // Enable the controller > > + // CC.AMS, CC.MPS and CC.CSS are all set to 0 > > + // > > + ZeroMem (&Cc, sizeof (NVME_CC)); > > + Cc.En =3D 1; > > + Cc.Iosqes =3D 6; > > + Cc.Iocqes =3D 4; > > + Status =3D NVME_SET_CC (Private, &Cc); > > + if (EFI_ERROR (Status)) { > > + DEBUG ((DEBUG_ERROR, "%a: NVME_SET_CC fail, Status - %r\n", > > __FUNCTION__, Status)); > > + goto ErrorExit; > > + } > > + > > + Status =3D NvmeWaitController (Private, TRUE); > > + if (EFI_ERROR (Status)) { > > + DEBUG ((DEBUG_ERROR, "%a: NvmeWaitController fail, Status - %r\n", > > __FUNCTION__, Status)); > > + goto ErrorExit; > > + } > > + > > + return EFI_SUCCESS; > > + > > +ErrorExit: > > + DEBUG ((DEBUG_ERROR, "%a fail, Status: %r\n", __FUNCTION__, Status))= ; > > + return Status; > > +} > > + > > +/** > > + Get the Identify Controller data. > > + > > + @param[in] Private The pointer to the > > PEI_NVME_CONTROLLER_PRIVATE_DATA data structure. > > + @param[in] Buffer The Buffer used to store the Identify Control= ler data. > > + > > + @return EFI_SUCCESS Successfully get the Identify Controller data= . > > + @return others Fail to get the Identify Controller data. > > + > > +**/ > > +EFI_STATUS > > +NvmeIdentifyController ( > > + IN PEI_NVME_CONTROLLER_PRIVATE_DATA *Private, > > + IN VOID *Buffer > > + ) > > +{ > > + EDKII_PEI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET > > CommandPacket; > > + EDKII_PEI_NVM_EXPRESS_COMMAND Command; > > + EDKII_PEI_NVM_EXPRESS_COMPLETION Completion; > > + EFI_STATUS Status; > > + > > + ZeroMem (&CommandPacket, > > sizeof(EDKII_PEI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET)); > > + ZeroMem (&Command, sizeof(EDKII_PEI_NVM_EXPRESS_COMMAND)); > > + ZeroMem (&Completion, > > sizeof(EDKII_PEI_NVM_EXPRESS_COMPLETION)); > > + > > + Command.Cdw0.Opcode =3D NVME_ADMIN_IDENTIFY_CMD; > > + // > > + // According to Nvm Express 1.1 spec Figure 38, When not used, the f= ield > > shall be cleared to 0h. > > + // For the Identify command, the Namespace Identifier is only used f= or the > > Namespace Data structure. > > + // > > + Command.Nsid =3D 0; > > + > > + CommandPacket.NvmeCmd =3D &Command; > > + CommandPacket.NvmeCompletion =3D &Completion; > > + CommandPacket.TransferBuffer =3D Buffer; > > + CommandPacket.TransferLength =3D sizeof > > (NVME_ADMIN_CONTROLLER_DATA); > > + CommandPacket.CommandTimeout =3D NVME_GENERIC_TIMEOUT; > > + CommandPacket.QueueType =3D NVME_ADMIN_QUEUE; > > + // > > + // Set bit 0 (Cns bit) to 1 to identify the controller > > + // > > + CommandPacket.NvmeCmd->Cdw10 =3D 1; > > + CommandPacket.NvmeCmd->Flags =3D CDW10_VALID; > > + > > + Status =3D NvmePassThru ( > > + Private, > > + NVME_CONTROLLER_NSID, > > + &CommandPacket > > + ); > > + return Status; > > +} > > + > > +/** > > + Get specified identify namespace data. > > + > > + @param[in] Private The pointer to the > > PEI_NVME_CONTROLLER_PRIVATE_DATA data structure. > > + @param[in] NamespaceId The specified namespace identifier. > > + @param[in] Buffer The buffer used to store the identify name= space > > data. > > + > > + @return EFI_SUCCESS Successfully get the identify namespace = data. > > + @return EFI_DEVICE_ERROR Fail to get the identify namespace data. > > + > > +**/ > > +EFI_STATUS > > +NvmeIdentifyNamespace ( > > + IN PEI_NVME_CONTROLLER_PRIVATE_DATA *Private, > > + IN UINT32 NamespaceId, > > + IN VOID *Buffer > > + ) > > +{ > > + EDKII_PEI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET > > CommandPacket; > > + EDKII_PEI_NVM_EXPRESS_COMMAND Command; > > + EDKII_PEI_NVM_EXPRESS_COMPLETION Completion; > > + EFI_STATUS Status; > > + > > + ZeroMem (&CommandPacket, > > sizeof(EDKII_PEI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET)); > > + ZeroMem (&Command, sizeof(EDKII_PEI_NVM_EXPRESS_COMMAND)); > > + ZeroMem (&Completion, > > sizeof(EDKII_PEI_NVM_EXPRESS_COMPLETION)); > > + > > + Command.Cdw0.Opcode =3D NVME_ADMIN_IDENTIFY_CMD; > > + Command.Nsid =3D NamespaceId; > > + > > + CommandPacket.NvmeCmd =3D &Command; > > + CommandPacket.NvmeCompletion =3D &Completion; > > + CommandPacket.TransferBuffer =3D Buffer; > > + CommandPacket.TransferLength =3D sizeof > > (NVME_ADMIN_NAMESPACE_DATA); > > + CommandPacket.CommandTimeout =3D NVME_GENERIC_TIMEOUT; > > + CommandPacket.QueueType =3D NVME_ADMIN_QUEUE; > > + // > > + // Set bit 0 (Cns bit) to 1 to identify a namespace > > + // > > + CommandPacket.NvmeCmd->Cdw10 =3D 0; > > + CommandPacket.NvmeCmd->Flags =3D CDW10_VALID; > > + > > + Status =3D NvmePassThru ( > > + Private, > > + NamespaceId, > > + &CommandPacket > > + ); > > + return Status; > > +} > > + > > +/** > > + Dump the Identify Controller data. > > + > > + @param[in] ControllerData The pointer to the > > NVME_ADMIN_CONTROLLER_DATA data structure. > > + > > +**/ > > +VOID > > +NvmeDumpControllerData ( > > + IN NVME_ADMIN_CONTROLLER_DATA *ControllerData > > + ) > > +{ > > + UINT8 Sn[21]; > > + UINT8 Mn[41]; > > + > > + CopyMem (Sn, ControllerData->Sn, sizeof (ControllerData->Sn)); > > + Sn[20] =3D 0; > > + CopyMem (Mn, ControllerData->Mn, sizeof (ControllerData->Mn)); > > + Mn[40] =3D 0; > > + > > + DEBUG ((DEBUG_INFO, " =3D=3D NVME IDENTIFY CONTROLLER DATA =3D=3D\n"= )); > > + DEBUG ((DEBUG_INFO, " PCI VID : 0x%x\n", ControllerData->Vid)); > > + DEBUG ((DEBUG_INFO, " PCI SSVID : 0x%x\n", ControllerData->Ssvid)= ); > > + DEBUG ((DEBUG_INFO, " SN : %a\n", Sn)); > > + DEBUG ((DEBUG_INFO, " MN : %a\n", Mn)); > > + DEBUG ((DEBUG_INFO, " FR : 0x%lx\n", *((UINT64*)Controller= Data- > > >Fr))); > > + DEBUG ((DEBUG_INFO, " RAB : 0x%x\n", ControllerData->Rab)); > > + DEBUG ((DEBUG_INFO, " IEEE : 0x%x\n", *(UINT32*)ControllerDa= ta- > > >Ieee_oui)); > > + DEBUG ((DEBUG_INFO, " AERL : 0x%x\n", ControllerData->Aerl))= ; > > + DEBUG ((DEBUG_INFO, " SQES : 0x%x\n", ControllerData->Sqes))= ; > > + DEBUG ((DEBUG_INFO, " CQES : 0x%x\n", ControllerData->Cqes))= ; > > + DEBUG ((DEBUG_INFO, " NN : 0x%x\n", ControllerData->Nn)); > > + return; > > +} > > + > > +/** > > + Create IO completion queue. > > + > > + @param[in] Private The pointer to the > > PEI_NVME_CONTROLLER_PRIVATE_DATA data structure. > > + > > + @return EFI_SUCCESS Successfully create io completion queue. > > + @return others Fail to create io completion queue. > > + > > +**/ > > +EFI_STATUS > > +NvmeCreateIoCompletionQueue ( > > + IN PEI_NVME_CONTROLLER_PRIVATE_DATA *Private > > + ) > > +{ > > + EDKII_PEI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET > > CommandPacket; > > + EDKII_PEI_NVM_EXPRESS_COMMAND Command; > > + EDKII_PEI_NVM_EXPRESS_COMPLETION Completion; > > + EFI_STATUS Status; > > + NVME_ADMIN_CRIOCQ CrIoCq; > > + > > + ZeroMem (&CommandPacket, > > sizeof(EDKII_PEI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET)); > > + ZeroMem (&Command, sizeof(EDKII_PEI_NVM_EXPRESS_COMMAND)); > > + ZeroMem (&Completion, > > sizeof(EDKII_PEI_NVM_EXPRESS_COMPLETION)); > > + ZeroMem (&CrIoCq, sizeof(NVME_ADMIN_CRIOCQ)); > > + > > + CommandPacket.NvmeCmd =3D &Command; > > + CommandPacket.NvmeCompletion =3D &Completion; > > + > > + Command.Cdw0.Opcode =3D NVME_ADMIN_CRIOCQ_CMD; > > + Command.Cdw0.Cid =3D Private->Cid[NVME_ADMIN_QUEUE]++; > > + CommandPacket.TransferBuffer =3D Private->CqBuffer[NVME_IO_QUEUE]; > > + CommandPacket.TransferLength =3D EFI_PAGE_SIZE; > > + CommandPacket.CommandTimeout =3D NVME_GENERIC_TIMEOUT; > > + CommandPacket.QueueType =3D NVME_ADMIN_QUEUE; > > + > > + CrIoCq.Qid =3D NVME_IO_QUEUE; > > + CrIoCq.Qsize =3D NVME_CCQ_SIZE; > > + CrIoCq.Pc =3D 1; > > + CopyMem (&CommandPacket.NvmeCmd->Cdw10, &CrIoCq, sizeof > > (NVME_ADMIN_CRIOCQ)); > > + CommandPacket.NvmeCmd->Flags =3D CDW10_VALID | CDW11_VALID; > > + > > + Status =3D NvmePassThru ( > > + Private, > > + NVME_CONTROLLER_NSID, > > + &CommandPacket > > + ); > > + return Status; > > +} > > + > > +/** > > + Create IO submission queue. > > + > > + @param[in] Private The pointer to the > > PEI_NVME_CONTROLLER_PRIVATE_DATA data structure. > > + > > + @return EFI_SUCCESS Successfully create io submission queue. > > + @return others Fail to create io submission queue. > > + > > +**/ > > +EFI_STATUS > > +NvmeCreateIoSubmissionQueue ( > > + IN PEI_NVME_CONTROLLER_PRIVATE_DATA *Private > > + ) > > +{ > > + EDKII_PEI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET > > CommandPacket; > > + EDKII_PEI_NVM_EXPRESS_COMMAND Command; > > + EDKII_PEI_NVM_EXPRESS_COMPLETION Completion; > > + EFI_STATUS Status; > > + NVME_ADMIN_CRIOSQ CrIoSq; > > + > > + ZeroMem (&CommandPacket, > > sizeof(EDKII_PEI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET)); > > + ZeroMem (&Command, sizeof(EDKII_PEI_NVM_EXPRESS_COMMAND)); > > + ZeroMem (&Completion, > > sizeof(EDKII_PEI_NVM_EXPRESS_COMPLETION)); > > + ZeroMem (&CrIoSq, sizeof(NVME_ADMIN_CRIOSQ)); > > + > > + CommandPacket.NvmeCmd =3D &Command; > > + CommandPacket.NvmeCompletion =3D &Completion; > > + > > + Command.Cdw0.Opcode =3D NVME_ADMIN_CRIOSQ_CMD; > > + Command.Cdw0.Cid =3D Private->Cid[NVME_ADMIN_QUEUE]++; > > + CommandPacket.TransferBuffer =3D Private->SqBuffer[NVME_IO_QUEUE]; > > + CommandPacket.TransferLength =3D EFI_PAGE_SIZE; > > + CommandPacket.CommandTimeout =3D NVME_GENERIC_TIMEOUT; > > + CommandPacket.QueueType =3D NVME_ADMIN_QUEUE; > > + > > + CrIoSq.Qid =3D NVME_IO_QUEUE; > > + CrIoSq.Qsize =3D NVME_CSQ_SIZE; > > + CrIoSq.Pc =3D 1; > > + CrIoSq.Cqid =3D NVME_IO_QUEUE; > > + CrIoSq.Qprio =3D 0; > > + CopyMem (&CommandPacket.NvmeCmd->Cdw10, &CrIoSq, sizeof > > (NVME_ADMIN_CRIOSQ)); > > + CommandPacket.NvmeCmd->Flags =3D CDW10_VALID | CDW11_VALID; > > + > > + Status =3D NvmePassThru ( > > + Private, > > + NVME_CONTROLLER_NSID, > > + &CommandPacket > > + ); > > + return Status; > > +} > > + > > +/** > > + Initialize the Nvm Express controller. > > + > > + @param[in] Private The pointer to the > > PEI_NVME_CONTROLLER_PRIVATE_DATA data structure. > > + > > + @retval EFI_SUCCESS The NVM Express Controller is initialized > > successfully. > > + @retval Others A device error occurred while initializing th= e > controller. > > + > > +**/ > > +EFI_STATUS > > +NvmeControllerInit ( > > + IN PEI_NVME_CONTROLLER_PRIVATE_DATA *Private > > + ) > > +{ > > + EFI_STATUS Status; > > + UINTN Index; > > + NVME_AQA Aqa; > > + NVME_ASQ Asq; > > + NVME_ACQ Acq; > > + NVME_VER Ver; > > + > > + // > > + // Dump the NVME controller implementation version > > + // > > + NVME_GET_VER (Private, &Ver); > > + DEBUG ((DEBUG_INFO, "NVME controller implementation > > version: %d.%d\n", Ver.Mjr, Ver.Mnr)); > > + > > + // > > + // Read the controller Capabilities register and verify that the NVM > > command set is supported > > + // > > + NVME_GET_CAP (Private, &Private->Cap); > > + if (Private->Cap.Css !=3D 0x01) { > > + DEBUG ((DEBUG_ERROR, "%a: The NVME controller doesn't support > > NVMe command set.\n", __FUNCTION__)); > > + return EFI_UNSUPPORTED; > > + } > > + > > + // > > + // Currently, the driver only supports 4k page size > > + // > > + if ((Private->Cap.Mpsmin + 12) > EFI_PAGE_SHIFT) { > > + DEBUG ((DEBUG_ERROR, "%a: The driver doesn't support page size oth= er > > than 4K.\n", __FUNCTION__)); > > + ASSERT (FALSE); > > + return EFI_UNSUPPORTED; > > + } > > + > > + for (Index =3D 0; Index < NVME_MAX_QUEUES; Index++) { > > + Private->Pt[Index] =3D 0; > > + Private->Cid[Index] =3D 0; > > + ZeroMem ((VOID *)(UINTN)(&Private->SqTdbl[Index]), sizeof > > (NVME_SQTDBL)); > > + ZeroMem ((VOID *)(UINTN)(&Private->CqHdbl[Index]), sizeof > > (NVME_CQHDBL)); > > + } > > + ZeroMem (Private->Buffer, EFI_PAGE_SIZE * NVME_MEM_MAX_PAGES); > > + > > + // > > + // Disable the NVME controller first > > + // > > + Status =3D NvmeDisableController (Private); > > + if (EFI_ERROR (Status)) { > > + DEBUG ((DEBUG_ERROR, "%a: NvmeDisableController fail, Status - %r\= n", > > __FUNCTION__, Status)); > > + return Status; > > + } > > + > > + // > > + // Set the number of entries in admin submission & completion queues > > + // > > + Aqa.Asqs =3D NVME_ASQ_SIZE; > > + Aqa.Rsvd1 =3D 0; > > + Aqa.Acqs =3D NVME_ACQ_SIZE; > > + Aqa.Rsvd2 =3D 0; > > + > > + // > > + // Address of admin submission & completion queues > > + // > > + Asq =3D (UINT64)(UINTN)(NVME_ASQ_BASE (Private) & ~0xFFF); > > + Acq =3D (UINT64)(UINTN)(NVME_ACQ_BASE (Private) & ~0xFFF); > > + > > + // > > + // Address of I/O submission & completion queues > > + // > > + Private->SqBuffer[0] =3D (NVME_SQ *)(UINTN)NVME_ASQ_BASE (Private); > > // NVME_ADMIN_QUEUE > > + Private->CqBuffer[0] =3D (NVME_CQ *)(UINTN)NVME_ACQ_BASE (Private); > > // NVME_ADMIN_QUEUE > > + Private->SqBuffer[1] =3D (NVME_SQ *)(UINTN)NVME_SQ_BASE (Private, 0)= ; > > // NVME_IO_QUEUE > > + Private->CqBuffer[1] =3D (NVME_CQ *)(UINTN)NVME_CQ_BASE (Private, 0)= ; > > // NVME_IO_QUEUE > > + DEBUG ((DEBUG_INFO, "Admin Submission Queue Size (Aqa.Asqs) =3D > > [%08X]\n", Aqa.Asqs)); > > + DEBUG ((DEBUG_INFO, "Admin Completion Queue Size (Aqa.Acqs) =3D > > [%08X]\n", Aqa.Acqs)); > > + DEBUG ((DEBUG_INFO, "Admin Submission Queue (SqBuffer[0]) =3D > > [%08X]\n", Private->SqBuffer[0])); > > + DEBUG ((DEBUG_INFO, "Admin Completion Queue (CqBuffer[0]) =3D > > [%08X]\n", Private->CqBuffer[0])); > > + DEBUG ((DEBUG_INFO, "I/O Submission Queue (SqBuffer[1]) =3D > > [%08X]\n", Private->SqBuffer[1])); > > + DEBUG ((DEBUG_INFO, "I/O Completion Queue (CqBuffer[1]) =3D > > [%08X]\n", Private->CqBuffer[1])); > > + > > + // > > + // Program admin queue attributes > > + // > > + NVME_SET_AQA (Private, &Aqa); > > + > > + // > > + // Program admin submission & completion queues address > > + // > > + NVME_SET_ASQ (Private, &Asq); > > + NVME_SET_ACQ (Private, &Acq); > > + > > + // > > + // Enable the NVME controller > > + // > > + Status =3D NvmeEnableController (Private); > > + if (EFI_ERROR (Status)) { > > + DEBUG ((DEBUG_ERROR, "%a: NvmeEnableController fail, Status - %r\n= ", > > __FUNCTION__, Status)); > > + return Status; > > + } > > + > > + // > > + // Get the Identify Controller data > > + // > > + if (Private->ControllerData =3D=3D NULL) { > > + Private->ControllerData =3D (NVME_ADMIN_CONTROLLER_DATA > > *)AllocateZeroPool (sizeof (NVME_ADMIN_CONTROLLER_DATA)); > > + if (Private->ControllerData =3D=3D NULL) { > > + return EFI_OUT_OF_RESOURCES; > > + } > > + } > > + Status =3D NvmeIdentifyController (Private, Private->ControllerData)= ; > > + if (EFI_ERROR (Status)) { > > + DEBUG ((DEBUG_ERROR, "%a: NvmeIdentifyController fail, Status - %r= \n", > > __FUNCTION__, Status)); > > + return Status; > > + } > > + NvmeDumpControllerData (Private->ControllerData); > > + > > + // > > + // Check the namespace number for storing the namespaces information > > + // > > + if (Private->ControllerData->Nn > MAX_UINT32 / sizeof > > (PEI_NVME_NAMESPACE_INFO)) { > > + DEBUG (( > > + DEBUG_ERROR, > > + "%a: Number of Namespaces field in Identify Controller data not > > supported by the driver.\n", > > + __FUNCTION__ > > + )); > > + return EFI_UNSUPPORTED; > > + } > > + > > + // > > + // Create one I/O completion queue and one I/O submission queue > > + // > > + Status =3D NvmeCreateIoCompletionQueue (Private); > > + if (EFI_ERROR (Status)) { > > + DEBUG ((DEBUG_ERROR, "%a: Create IO completion queue fail, Status = - > > %r\n", __FUNCTION__, Status)); > > + return Status; > > + } > > + Status =3D NvmeCreateIoSubmissionQueue (Private); > > + if (EFI_ERROR (Status)) { > > + DEBUG ((DEBUG_ERROR, "%a: Create IO submission queue fail, Status = - > > %r\n", __FUNCTION__, Status)); > > + } > > + > > + return Status; > > +} > > + > > +/** > > + Free the resources allocated by an NVME controller. > > + > > + @param[in] Private The pointer to the > > PEI_NVME_CONTROLLER_PRIVATE_DATA data structure. > > + > > +**/ > > +VOID > > +NvmeFreeControllerResource ( > > + IN PEI_NVME_CONTROLLER_PRIVATE_DATA *Private > > + ) > > +{ > > + // > > + // Free the controller data buffer > > + // > > + if (Private->ControllerData !=3D NULL) { > > + FreePool (Private->ControllerData); > > + Private->ControllerData =3D NULL; > > + } > > + > > + // > > + // Free the DMA buffers > > + // > > + if (Private->Buffer !=3D NULL) { > > + IoMmuFreeBuffer ( > > + NVME_MEM_MAX_PAGES, > > + Private->Buffer, > > + Private->BufferMapping > > + ); > > + Private->Buffer =3D NULL; > > + } > > + > > + // > > + // Free the namespaces information buffer > > + // > > + if (Private->NamespaceInfo !=3D NULL) { > > + FreePool (Private->NamespaceInfo); > > + Private->NamespaceInfo =3D NULL; > > + } > > + > > + // > > + // Free the controller private data structure > > + // > > + FreePool (Private); > > + return; > > +} > > diff --git a/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiHci.h > > b/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiHci.h > > new file mode 100644 > > index 0000000000..ff334e3e17 > > --- /dev/null > > +++ b/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiHci.h > > @@ -0,0 +1,166 @@ > > +/** @file > > + The NvmExpressPei driver is used to manage non-volatile memory > > subsystem > > + which follows NVM Express specification at PEI phase. > > + > > + Copyright (c) 2018, Intel Corporation. All rights reserved.
> > + > > + This program and the accompanying materials > > + are licensed and made available under the terms and conditions > > + of the BSD License which accompanies this distribution. The > > + full text of the license may be found at > > + http://opensource.org/licenses/bsd-license.php > > + > > + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" > > BASIS, > > + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER > > EXPRESS OR IMPLIED. > > + > > +**/ > > + > > +#ifndef _NVM_EXPRESS_PEI_HCI_H_ > > +#define _NVM_EXPRESS_PEI_HCI_H_ > > + > > +// > > +// NVME host controller registers operation definitions > > +// > > +#define NVME_GET_CAP(Private, Cap) NvmeMmioRead (Cap, > > Private->MmioBase + NVME_CAP_OFFSET, sizeof (NVME_CAP)) > > +#define NVME_GET_CC(Private, Cc) NvmeMmioRead (Cc, Priv= ate- > > >MmioBase + NVME_CC_OFFSET, sizeof (NVME_CC)) > > +#define NVME_SET_CC(Private, Cc) NvmeMmioWrite (Private- > > >MmioBase + NVME_CC_OFFSET, Cc, sizeof (NVME_CC)) > > +#define NVME_GET_CSTS(Private, Csts) NvmeMmioRead (Csts, > > Private->MmioBase + NVME_CSTS_OFFSET, sizeof (NVME_CSTS)) > > +#define NVME_GET_AQA(Private, Aqa) NvmeMmioRead (Aqa, > > Private->MmioBase + NVME_AQA_OFFSET, sizeof (NVME_AQA)) > > +#define NVME_SET_AQA(Private, Aqa) NvmeMmioWrite (Private- > > >MmioBase + NVME_AQA_OFFSET, Aqa, sizeof (NVME_AQA)) > > +#define NVME_GET_ASQ(Private, Asq) NvmeMmioRead (Asq, > > Private->MmioBase + NVME_ASQ_OFFSET, sizeof (NVME_ASQ)) > > +#define NVME_SET_ASQ(Private, Asq) NvmeMmioWrite (Private- > > >MmioBase + NVME_ASQ_OFFSET, Asq, sizeof (NVME_ASQ)) > > +#define NVME_GET_ACQ(Private, Acq) NvmeMmioRead (Acq, > > Private->MmioBase + NVME_ACQ_OFFSET, sizeof (NVME_ACQ)) > > +#define NVME_SET_ACQ(Private, Acq) NvmeMmioWrite (Private- > > >MmioBase + NVME_ACQ_OFFSET, Acq, sizeof (NVME_ACQ)) > > +#define NVME_GET_VER(Private, Ver) NvmeMmioRead (Ver, > > Private->MmioBase + NVME_VER_OFFSET, sizeof (NVME_VER)) > > +#define NVME_SET_SQTDBL(Private, Qid, Sqtdbl) NvmeMmioWrite > > (Private->MmioBase + NVME_SQTDBL_OFFSET(Qid, Private->Cap.Dstrd), > > Sqtdbl, sizeof (NVME_SQTDBL)) > > +#define NVME_SET_CQHDBL(Private, Qid, Cqhdbl) NvmeMmioWrite > > (Private->MmioBase + NVME_CQHDBL_OFFSET(Qid, Private->Cap.Dstrd), > > Cqhdbl, sizeof (NVME_CQHDBL)) > > + > > +// > > +// Base memory address enum types > > +// > > +enum { > > + BASEMEM_ASQ, > > + BASEMEM_ACQ, > > + BASEMEM_SQ, > > + BASEMEM_CQ, > > + BASEMEM_PRP, > > + MAX_BASEMEM_COUNT > > +}; > > + > > +// > > +// All of base memories are 4K(0x1000) alignment > > +// > > +#define ALIGN(v, a) (UINTN)((((v) - 1) | ((a)= - 1)) + 1) > > +#define NVME_MEM_BASE(Private) ((UINTN)(Private->Buffer)= ) > > +#define NVME_ASQ_BASE(Private) (ALIGN > > (NVME_MEM_BASE(Private) + ((NvmeBaseMemPageOffset > > (BASEMEM_ASQ)) * EFI_PAGE_SIZE), EFI_PAG= E_SIZE)) > > +#define NVME_ACQ_BASE(Private) (ALIGN > > (NVME_MEM_BASE(Private) + ((NvmeBaseMemPageOffset > > (BASEMEM_ACQ)) * EFI_PAGE_SIZE), EFI_PAG= E_SIZE)) > > +#define NVME_SQ_BASE(Private, Index) (ALIGN > > (NVME_MEM_BASE(Private) + ((NvmeBaseMemPageOffset (BASEMEM_SQ) > > + ((Index)*(NVME_MAX_QUEUES-1))) * EFI_PAGE_SIZE), EFI_PAGE_SIZE)) > > +#define NVME_CQ_BASE(Private, Index) (ALIGN > > (NVME_MEM_BASE(Private) + ((NvmeBaseMemPageOffset (BASEMEM_CQ) > > + ((Index)*(NVME_MAX_QUEUES-1))) * EFI_PAGE_SIZE), EFI_PAGE_SIZE)) > > +#define NVME_PRP_BASE(Private) (ALIGN > > (NVME_MEM_BASE(Private) + ((NvmeBaseMemPageOffset > > (BASEMEM_PRP)) * EFI_PAGE_SIZE), EFI_PAG= E_SIZE)) > > + > > + > > +/** > > + Transfer MMIO Data to memory. > > + > > + @param[in,out] MemBuffer Destination: Memory address. > > + @param[in] MmioAddr Source: MMIO address. > > + @param[in] Size Size for read. > > + > > + @retval EFI_SUCCESS MMIO read sucessfully. > > + > > +**/ > > +EFI_STATUS > > +NvmeMmioRead ( > > + IN OUT VOID *MemBuffer, > > + IN UINTN MmioAddr, > > + IN UINTN Size > > + ); > > + > > +/** > > + Transfer memory data to MMIO. > > + > > + @param[in,out] MmioAddr Destination: MMIO address. > > + @param[in] MemBuffer Source: Memory address. > > + @param[in] Size Size for write. > > + > > + @retval EFI_SUCCESS MMIO write sucessfully. > > + > > +**/ > > +EFI_STATUS > > +NvmeMmioWrite ( > > + IN OUT UINTN MmioAddr, > > + IN VOID *MemBuffer, > > + IN UINTN Size > > + ); > > + > > +/** > > + Get the page offset for specific NVME based memory. > > + > > + @param[in] BaseMemIndex The Index of BaseMem (0-based). > > + > > + @retval - The page count for specific BaseMem Index > > + > > +**/ > > +UINT32 > > +NvmeBaseMemPageOffset ( > > + IN UINTN BaseMemIndex > > + ); > > + > > +/** > > + Disable the Nvm Express controller. > > + > > + @param[in] Private The pointer to the > > PEI_NVME_CONTROLLER_PRIVATE_DATA data structure. > > + > > + @return EFI_SUCCESS Successfully disable the controller. > > + @return others Fail to disable the controller. > > + > > +**/ > > +EFI_STATUS > > +NvmeDisableController ( > > + IN PEI_NVME_CONTROLLER_PRIVATE_DATA *Private > > + ); > > + > > +/** > > + Initialize the Nvm Express controller. > > + > > + @param[in] Private The pointer to the > > PEI_NVME_CONTROLLER_PRIVATE_DATA data structure. > > + > > + @retval EFI_SUCCESS The NVM Express Controller is initialized > > successfully. > > + @retval Others A device error occurred while initializing th= e > controller. > > + > > +**/ > > +EFI_STATUS > > +NvmeControllerInit ( > > + IN PEI_NVME_CONTROLLER_PRIVATE_DATA *Private > > + ); > > + > > +/** > > + Get specified identify namespace data. > > + > > + @param[in] Private The pointer to the > > PEI_NVME_CONTROLLER_PRIVATE_DATA data structure. > > + @param[in] NamespaceId The specified namespace identifier. > > + @param[in] Buffer The buffer used to store the identify name= space > > data. > > + > > + @return EFI_SUCCESS Successfully get the identify namespace = data. > > + @return EFI_DEVICE_ERROR Fail to get the identify namespace data. > > + > > +**/ > > +EFI_STATUS > > +NvmeIdentifyNamespace ( > > + IN PEI_NVME_CONTROLLER_PRIVATE_DATA *Private, > > + IN UINT32 NamespaceId, > > + IN VOID *Buffer > > + ); > > + > > +/** > > + Free the resources allocated by an NVME controller. > > + > > + @param[in] Private The pointer to the > > PEI_NVME_CONTROLLER_PRIVATE_DATA data structure. > > + > > +**/ > > +VOID > > +NvmeFreeControllerResource ( > > + IN PEI_NVME_CONTROLLER_PRIVATE_DATA *Private > > + ); > > + > > +#endif > > diff --git > > a/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiPassThru.c > > b/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiPassThru.c > > new file mode 100644 > > index 0000000000..81ad01b7ee > > --- /dev/null > > +++ b/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiPassThru.c > > @@ -0,0 +1,628 @@ > > +/** @file > > + The NvmExpressPei driver is used to manage non-volatile memory > > subsystem > > + which follows NVM Express specification at PEI phase. > > + > > + Copyright (c) 2018, Intel Corporation. All rights reserved.
> > + > > + This program and the accompanying materials > > + are licensed and made available under the terms and conditions > > + of the BSD License which accompanies this distribution. The > > + full text of the license may be found at > > + http://opensource.org/licenses/bsd-license.php > > + > > + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" > > BASIS, > > + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER > > EXPRESS OR IMPLIED. > > + > > +**/ > > + > > +#include "NvmExpressPei.h" > > + > > +/** > > + Create PRP lists for Data transfer which is larger than 2 memory pag= es. > > + > > + @param[in] Private The pointer to the > > PEI_NVME_CONTROLLER_PRIVATE_DATA data structure. > > + @param[in] PhysicalAddr The physical base address of Data Buffer= . > > + @param[in] Pages The number of pages to be transfered. > > + > > + @retval The pointer Value to the first PRP List of the PRP lists. > > + > > +**/ > > +UINT64 > > +NvmeCreatePrpList ( > > + IN PEI_NVME_CONTROLLER_PRIVATE_DATA *Private, > > + IN EFI_PHYSICAL_ADDRESS PhysicalAddr, > > + IN UINTN Pages > > + ) > > +{ > > + UINTN PrpEntryNo; > > + UINTN PrpListNo; > > + UINT64 PrpListBase; > > + VOID *PrpListHost; > > + UINTN PrpListIndex; > > + UINTN PrpEntryIndex; > > + UINT64 Remainder; > > + EFI_PHYSICAL_ADDRESS PrpListPhyAddr; > > + UINTN Bytes; > > + UINT8 *PrpEntry; > > + EFI_PHYSICAL_ADDRESS NewPhyAddr; > > + > > + // > > + // The number of Prp Entry in a memory page. > > + // > > + PrpEntryNo =3D EFI_PAGE_SIZE / sizeof (UINT64); > > + > > + // > > + // Calculate total PrpList number. > > + // > > + PrpListNo =3D (UINTN) DivU64x64Remainder ((UINT64)Pages, > > (UINT64)PrpEntryNo, &Remainder); > > + if (Remainder !=3D 0) { > > + PrpListNo +=3D 1; > > + } > > + > > + if (PrpListNo > NVME_PRP_SIZE) { > > + DEBUG (( > > + DEBUG_ERROR, > > + "%a: The implementation only supports PrpList number up to 4." > > + " But %d are needed here.\n", > > + __FUNCTION__, > > + PrpListNo > > + )); > > + return 0; > > + } > > + PrpListHost =3D (VOID *)(UINTN) NVME_PRP_BASE (Private); > > + > > + Bytes =3D EFI_PAGES_TO_SIZE (PrpListNo); > > + PrpListPhyAddr =3D (UINT64)(UINTN)(PrpListHost); > > + > > + // > > + // Fill all PRP lists except of last one. > > + // > > + ZeroMem (PrpListHost, Bytes); > > + for (PrpListIndex =3D 0; PrpListIndex < PrpListNo - 1; ++PrpListInde= x) { > > + PrpListBase =3D (UINTN)PrpListHost + PrpListIndex * EFI_PAGE_SIZE; > > + > > + for (PrpEntryIndex =3D 0; PrpEntryIndex < PrpEntryNo; ++PrpEntryIn= dex) { > > + PrpEntry =3D (UINT8 *)(UINTN) (PrpListBase + PrpEntryIndex * > > sizeof(UINT64)); > > + if (PrpEntryIndex !=3D PrpEntryNo - 1) { > > + // > > + // Fill all PRP entries except of last one. > > + // > > + CopyMem (PrpEntry, (VOID *)(UINTN) (&PhysicalAddr), sizeof > > (UINT64)); > > + PhysicalAddr +=3D EFI_PAGE_SIZE; > > + } else { > > + // > > + // Fill last PRP entries with next PRP List pointer. > > + // > > + NewPhyAddr =3D (PrpListPhyAddr + (PrpListIndex + 1) * EFI_PAGE= _SIZE); > > + CopyMem (PrpEntry, (VOID *)(UINTN) (&NewPhyAddr), sizeof > > (UINT64)); > > + } > > + } > > + } > > + > > + // > > + // Fill last PRP list. > > + // > > + PrpListBase =3D (UINTN)PrpListHost + PrpListIndex * EFI_PAGE_SIZE; > > + for (PrpEntryIndex =3D 0; PrpEntryIndex < ((Remainder !=3D 0) ? Rema= inder : > > PrpEntryNo); ++PrpEntryIndex) { > > + PrpEntry =3D (UINT8 *)(UINTN) (PrpListBase + PrpEntryIndex * > > sizeof(UINT64)); > > + CopyMem (PrpEntry, (VOID *)(UINTN) (&PhysicalAddr), sizeof (UINT64= )); > > + > > + PhysicalAddr +=3D EFI_PAGE_SIZE; > > + } > > + > > + return PrpListPhyAddr; > > +} > > + > > +/** > > + Check the execution status from a given completion queue entry. > > + > > + @param[in] Cq A pointer to the NVME_CQ item. > > + > > +**/ > > +EFI_STATUS > > +NvmeCheckCqStatus ( > > + IN NVME_CQ *Cq > > + ) > > +{ > > + if (Cq->Sct =3D=3D 0x0 && Cq->Sc =3D=3D 0x0) { > > + return EFI_SUCCESS; > > + } > > + > > + DEBUG ((DEBUG_INFO, "Dump NVMe Completion Entry Status from > > [0x%x]:\n", (UINTN)Cq)); > > + DEBUG (( > > + DEBUG_INFO, > > + " SQ Identifier : [0x%x], Phase Tag : [%d], Cmd Identifier : [0x%= x]\n", > > + Cq->Sqid, > > + Cq->Pt, > > + Cq->Cid > > + )); > > + DEBUG ((DEBUG_INFO, " Status Code Type : [0x%x], Status Code : > > [0x%x]\n", Cq->Sct, Cq->Sc)); > > + DEBUG ((DEBUG_INFO, " NVMe Cmd Execution Result - ")); > > + > > + switch (Cq->Sct) { > > + case 0x0: > > + switch (Cq->Sc) { > > + case 0x0: > > + DEBUG ((DEBUG_INFO, "Successful Completion\n")); > > + return EFI_SUCCESS; > > + case 0x1: > > + DEBUG ((DEBUG_INFO, "Invalid Command Opcode\n")); > > + break; > > + case 0x2: > > + DEBUG ((DEBUG_INFO, "Invalid Field in Command\n")); > > + break; > > + case 0x3: > > + DEBUG ((DEBUG_INFO, "Command ID Conflict\n")); > > + break; > > + case 0x4: > > + DEBUG ((DEBUG_INFO, "Data Transfer Error\n")); > > + break; > > + case 0x5: > > + DEBUG ((DEBUG_INFO, "Commands Aborted due to Power Loss > > Notification\n")); > > + break; > > + case 0x6: > > + DEBUG ((DEBUG_INFO, "Internal Device Error\n")); > > + break; > > + case 0x7: > > + DEBUG ((DEBUG_INFO, "Command Abort Requested\n")); > > + break; > > + case 0x8: > > + DEBUG ((DEBUG_INFO, "Command Aborted due to SQ Deletion\n"))= ; > > + break; > > + case 0x9: > > + DEBUG ((DEBUG_INFO, "Command Aborted due to Failed Fused > > Command\n")); > > + break; > > + case 0xA: > > + DEBUG ((DEBUG_INFO, "Command Aborted due to Missing Fused > > Command\n")); > > + break; > > + case 0xB: > > + DEBUG ((DEBUG_INFO, "Invalid Namespace or Format\n")); > > + break; > > + case 0xC: > > + DEBUG ((DEBUG_INFO, "Command Sequence Error\n")); > > + break; > > + case 0xD: > > + DEBUG ((DEBUG_INFO, "Invalid SGL Last Segment Descriptor\n")= ); > > + break; > > + case 0xE: > > + DEBUG ((DEBUG_INFO, "Invalid Number of SGL Descriptors\n")); > > + break; > > + case 0xF: > > + DEBUG ((DEBUG_INFO, "Data SGL Length Invalid\n")); > > + break; > > + case 0x10: > > + DEBUG ((DEBUG_INFO, "Metadata SGL Length Invalid\n")); > > + break; > > + case 0x11: > > + DEBUG ((DEBUG_INFO, "SGL Descriptor Type Invalid\n")); > > + break; > > + case 0x80: > > + DEBUG ((DEBUG_INFO, "LBA Out of Range\n")); > > + break; > > + case 0x81: > > + DEBUG ((DEBUG_INFO, "Capacity Exceeded\n")); > > + break; > > + case 0x82: > > + DEBUG ((DEBUG_INFO, "Namespace Not Ready\n")); > > + break; > > + case 0x83: > > + DEBUG ((DEBUG_INFO, "Reservation Conflict\n")); > > + break; > > + } > > + break; > > + > > + case 0x1: > > + switch (Cq->Sc) { > > + case 0x0: > > + DEBUG ((DEBUG_INFO, "Completion Queue Invalid\n")); > > + break; > > + case 0x1: > > + DEBUG ((DEBUG_INFO, "Invalid Queue Identifier\n")); > > + break; > > + case 0x2: > > + DEBUG ((DEBUG_INFO, "Maximum Queue Size Exceeded\n")); > > + break; > > + case 0x3: > > + DEBUG ((DEBUG_INFO, "Abort Command Limit Exceeded\n")); > > + break; > > + case 0x5: > > + DEBUG ((DEBUG_INFO, "Asynchronous Event Request Limit > > Exceeded\n")); > > + break; > > + case 0x6: > > + DEBUG ((DEBUG_INFO, "Invalid Firmware Slot\n")); > > + break; > > + case 0x7: > > + DEBUG ((DEBUG_INFO, "Invalid Firmware Image\n")); > > + break; > > + case 0x8: > > + DEBUG ((DEBUG_INFO, "Invalid Interrupt Vector\n")); > > + break; > > + case 0x9: > > + DEBUG ((DEBUG_INFO, "Invalid Log Page\n")); > > + break; > > + case 0xA: > > + DEBUG ((DEBUG_INFO, "Invalid Format\n")); > > + break; > > + case 0xB: > > + DEBUG ((DEBUG_INFO, "Firmware Application Requires Conventio= nal > > Reset\n")); > > + break; > > + case 0xC: > > + DEBUG ((DEBUG_INFO, "Invalid Queue Deletion\n")); > > + break; > > + case 0xD: > > + DEBUG ((DEBUG_INFO, "Feature Identifier Not Saveable\n")); > > + break; > > + case 0xE: > > + DEBUG ((DEBUG_INFO, "Feature Not Changeable\n")); > > + break; > > + case 0xF: > > + DEBUG ((DEBUG_INFO, "Feature Not Namespace Specific\n")); > > + break; > > + case 0x10: > > + DEBUG ((DEBUG_INFO, "Firmware Application Requires NVM > > Subsystem Reset\n")); > > + break; > > + case 0x80: > > + DEBUG ((DEBUG_INFO, "Conflicting Attributes\n")); > > + break; > > + case 0x81: > > + DEBUG ((DEBUG_INFO, "Invalid Protection Information\n")); > > + break; > > + case 0x82: > > + DEBUG ((DEBUG_INFO, "Attempted Write to Read Only Range\n"))= ; > > + break; > > + } > > + break; > > + > > + case 0x2: > > + switch (Cq->Sc) { > > + case 0x80: > > + DEBUG ((DEBUG_INFO, "Write Fault\n")); > > + break; > > + case 0x81: > > + DEBUG ((DEBUG_INFO, "Unrecovered Read Error\n")); > > + break; > > + case 0x82: > > + DEBUG ((DEBUG_INFO, "End-to-end Guard Check Error\n")); > > + break; > > + case 0x83: > > + DEBUG ((DEBUG_INFO, "End-to-end Application Tag Check Error\= n")); > > + break; > > + case 0x84: > > + DEBUG ((DEBUG_INFO, "End-to-end Reference Tag Check Error\n"= )); > > + break; > > + case 0x85: > > + DEBUG ((DEBUG_INFO, "Compare Failure\n")); > > + break; > > + case 0x86: > > + DEBUG ((DEBUG_INFO, "Access Denied\n")); > > + break; > > + } > > + break; > > + > > + default: > > + DEBUG ((DEBUG_INFO, "Unknown error\n")); > > + break; > > + } > > + > > + return EFI_DEVICE_ERROR; > > +} > > + > > +/** > > + Sends an NVM Express Command Packet to an NVM Express controller or > > namespace. This function only > > + supports blocking execution of the command. > > + > > + @param[in] Private The pointer to the NVME_CONTEXT Data struc= ture. > > + @param[in] NamespaceId Is a 32 bit Namespace ID to which the Expr= ess > > HCI command packet will > > + be sent. > > + A Value of 0 denotes the NVM Express contr= oller, a Value > of > > all 0FFh in > > + the namespace ID specifies that the comman= d packet > should > > be sent to all > > + valid namespaces. > > + @param[in,out] Packet A pointer to the EDKII PEI NVM Express Pas= sThru > > Command Packet to send > > + to the NVMe namespace specified by Namespa= ceId. > > + > > + @retval EFI_SUCCESS The EDKII PEI NVM Express Command P= acket > > was sent by the host. > > + TransferLength bytes were transferr= ed to, or from > > DataBuffer. > > + @retval EFI_NOT_READY The EDKII PEI NVM Express Command > > Packet could not be sent because > > + the controller is not ready. The ca= ller may retry again > later. > > + @retval EFI_DEVICE_ERROR A device error occurred while attem= pting > > to send the EDKII PEI NVM > > + Express Command Packet. > > + @retval EFI_INVALID_PARAMETER Namespace, or the contents of > > EDKII_PEI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET > > + are invalid. > > + The EDKII PEI NVM Express Command P= acket was not > sent, > > so no > > + additional status information is av= ailable. > > + @retval EFI_UNSUPPORTED The command described by the EDKII = PEI > > NVM Express Command Packet > > + is not supported by the host adapte= r. > > + The EDKII PEI NVM Express Command P= acket was not > sent, > > so no > > + additional status information is av= ailable. > > + @retval EFI_TIMEOUT A timeout occurred while waiting fo= r the > > EDKII PEI NVM Express Command > > + Packet to execute. > > + > > +**/ > > +EFI_STATUS > > +NvmePassThru ( > > + IN PEI_NVME_CONTROLLER_PRIVATE_DATA *Private, > > + IN UINT32 NamespaceId= , > > + IN OUT EDKII_PEI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET > > *Packet > > + ) > > +{ > > + EFI_STATUS Status; > > + NVME_SQ *Sq; > > + NVME_CQ *Cq; > > + UINT8 QueueId; > > + UINTN SqSize; > > + UINTN CqSize; > > + EDKII_IOMMU_OPERATION MapOp; > > + UINTN MapLength; > > + EFI_PHYSICAL_ADDRESS PhyAddr; > > + VOID *MapData; > > + VOID *MapMeta; > > + UINT32 Bytes; > > + UINT32 Offset; > > + UINT32 Data32; > > + UINT64 Timer; > > + > > + // > > + // Check the data fields in Packet parameter > > + // > > + if (Packet =3D=3D NULL) { > > + DEBUG (( > > + DEBUG_ERROR, > > + "%a, Invalid parameter: Packet(%lx)\n", > > + __FUNCTION__, > > + (UINTN)Packet > > + )); > > + return EFI_INVALID_PARAMETER; > > + } > > + > > + if ((Packet->NvmeCmd =3D=3D NULL) || (Packet->NvmeCompletion =3D=3D = NULL)) > > { > > + DEBUG (( > > + DEBUG_ERROR, > > + "%a, Invalid parameter: NvmeCmd (%lx)/NvmeCompletion(%lx)\n", > > + __FUNCTION__, > > + (UINTN)Packet->NvmeCmd, > > + (UINTN)Packet->NvmeCompletion > > + )); > > + return EFI_INVALID_PARAMETER; > > + } > > + > > + if (Packet->QueueType !=3D NVME_ADMIN_QUEUE && Packet- > > >QueueType !=3D NVME_IO_QUEUE) { > > + DEBUG (( > > + DEBUG_ERROR, > > + "%a, Invalid parameter: QueueId(%lx)\n", > > + __FUNCTION__, > > + (UINTN)Packet->QueueType > > + )); > > + return EFI_INVALID_PARAMETER; > > + } > > + > > + QueueId =3D Packet->QueueType; > > + Sq =3D Private->SqBuffer[QueueId] + Private->SqTdbl[QueueId].Sq= t; > > + Cq =3D Private->CqBuffer[QueueId] + Private->CqHdbl[QueueId].Cq= h; > > + if (QueueId =3D=3D NVME_ADMIN_QUEUE) { > > + SqSize =3D NVME_ASQ_SIZE + 1; > > + CqSize =3D NVME_ACQ_SIZE + 1; > > + } else { > > + SqSize =3D NVME_CSQ_SIZE + 1; > > + CqSize =3D NVME_CCQ_SIZE + 1; > > + } > > + > > + if (Packet->NvmeCmd->Nsid !=3D NamespaceId) { > > + DEBUG (( > > + DEBUG_ERROR, > > + "%a: Nsid mismatch (%x, %x)\n", > > + __FUNCTION__, > > + Packet->NvmeCmd->Nsid, > > + NamespaceId > > + )); > > + return EFI_INVALID_PARAMETER; > > + } > > + > > + ZeroMem (Sq, sizeof (NVME_SQ)); > > + Sq->Opc =3D Packet->NvmeCmd->Cdw0.Opcode; > > + Sq->Fuse =3D Packet->NvmeCmd->Cdw0.FusedOperation; > > + Sq->Cid =3D Packet->NvmeCmd->Cdw0.Cid; > > + Sq->Nsid =3D Packet->NvmeCmd->Nsid; > > + > > + // > > + // Currently we only support PRP for data transfer, SGL is NOT suppo= rted > > + // > > + ASSERT (Sq->Psdt =3D=3D 0); > > + if (Sq->Psdt !=3D 0) { > > + DEBUG ((DEBUG_ERROR, "%a: Does not support SGL mechanism.\n", > > __FUNCTION__)); > > + return EFI_UNSUPPORTED; > > + } > > + > > + Sq->Prp[0] =3D (UINT64)(UINTN)Packet->TransferBuffer; > > + Sq->Prp[1] =3D 0; > > + MapData =3D NULL; > > + MapMeta =3D NULL; > > + Status =3D EFI_SUCCESS; > > + // > > + // If the NVMe cmd has data in or out, then mapping the user buffer = to the > > PCI controller > > + // specific addresses. > > + // > > + if ((Sq->Opc & (BIT0 | BIT1)) !=3D 0) { > > + if ((Packet->TransferLength =3D=3D 0) || (Packet->TransferBuffer = =3D=3D NULL)) { > > + return EFI_INVALID_PARAMETER; > > + } > > + > > + // > > + // Currently, we only support creating IO submission/completion qu= eues > > that are > > + // allocated internally by the driver. > > + // > > + if ((Packet->QueueType =3D=3D NVME_ADMIN_QUEUE) && > > + ((Sq->Opc =3D=3D NVME_ADMIN_CRIOCQ_CMD) || (Sq->Opc =3D=3D > > NVME_ADMIN_CRIOSQ_CMD))) { > > + if ((Packet->TransferBuffer !=3D Private->SqBuffer[NVME_IO_QUEUE= ]) > > && > > + (Packet->TransferBuffer !=3D Private->CqBuffer[NVME_IO_QUEUE= ])) { > > + DEBUG (( > > + DEBUG_ERROR, > > + "%a: Does not support external IO queues creation request.\n= ", > > + __FUNCTION__ > > + )); > > + return EFI_UNSUPPORTED; > > + } > > + } else { > > + if ((Sq->Opc & BIT0) !=3D 0) { > > + MapOp =3D EdkiiIoMmuOperationBusMasterRead; > > + } else { > > + MapOp =3D EdkiiIoMmuOperationBusMasterWrite; > > + } > > + > > + MapLength =3D Packet->TransferLength; > > + Status =3D IoMmuMap ( > > + MapOp, > > + Packet->TransferBuffer, > > + &MapLength, > > + &PhyAddr, > > + &MapData > > + ); > > + if (EFI_ERROR (Status) || (MapLength !=3D Packet->TransferLength= )) { > > + Status =3D EFI_OUT_OF_RESOURCES; > > + DEBUG ((DEBUG_ERROR, "%a: Fail to map data buffer.\n", > > __FUNCTION__)); > > + goto Exit; > > + } > > + > > + Sq->Prp[0] =3D PhyAddr; > > + > > + if((Packet->MetadataLength !=3D 0) && (Packet->MetadataBuffer != =3D > > NULL)) { > > + MapLength =3D Packet->MetadataLength; > > + Status =3D IoMmuMap ( > > + MapOp, > > + Packet->MetadataBuffer, > > + &MapLength, > > + &PhyAddr, > > + &MapMeta > > + ); > > + if (EFI_ERROR (Status) || (MapLength !=3D Packet->MetadataLeng= th)) { > > + Status =3D EFI_OUT_OF_RESOURCES; > > + DEBUG ((DEBUG_ERROR, "%a: Fail to map meta data buffer.\n", > > __FUNCTION__)); > > + goto Exit; > > + } > > + Sq->Mptr =3D PhyAddr; > > + } > > + } > > + } > > + > > + // > > + // If the Buffer Size spans more than two memory pages (page Size as > > defined in CC.Mps), > > + // then build a PRP list in the second PRP submission queue entry. > > + // > > + Offset =3D ((UINT32)Sq->Prp[0]) & (EFI_PAGE_SIZE - 1); > > + Bytes =3D Packet->TransferLength; > > + > > + if ((Offset + Bytes) > (EFI_PAGE_SIZE * 2)) { > > + // > > + // Create PrpList for remaining Data Buffer. > > + // > > + PhyAddr =3D (Sq->Prp[0] + EFI_PAGE_SIZE) & ~(EFI_PAGE_SIZE - 1); > > + Sq->Prp[1] =3D NvmeCreatePrpList ( > > + Private, > > + PhyAddr, > > + EFI_SIZE_TO_PAGES(Offset + Bytes) - 1 > > + ); > > + if (Sq->Prp[1] =3D=3D 0) { > > + Status =3D EFI_OUT_OF_RESOURCES; > > + DEBUG ((DEBUG_ERROR, "%a: Create PRP list fail, Status - %r\n", > > __FUNCTION__, Status)); > > + goto Exit; > > + } > > + > > + } else if ((Offset + Bytes) > EFI_PAGE_SIZE) { > > + Sq->Prp[1] =3D (Sq->Prp[0] + EFI_PAGE_SIZE) & ~(EFI_PAGE_SIZE - 1)= ; > > + } > > + > > + if (Packet->NvmeCmd->Flags & CDW10_VALID) { > > + Sq->Payload.Raw.Cdw10 =3D Packet->NvmeCmd->Cdw10; > > + } > > + if (Packet->NvmeCmd->Flags & CDW11_VALID) { > > + Sq->Payload.Raw.Cdw11 =3D Packet->NvmeCmd->Cdw11; > > + } > > + if (Packet->NvmeCmd->Flags & CDW12_VALID) { > > + Sq->Payload.Raw.Cdw12 =3D Packet->NvmeCmd->Cdw12; > > + } > > + if (Packet->NvmeCmd->Flags & CDW13_VALID) { > > + Sq->Payload.Raw.Cdw13 =3D Packet->NvmeCmd->Cdw13; > > + } > > + if (Packet->NvmeCmd->Flags & CDW14_VALID) { > > + Sq->Payload.Raw.Cdw14 =3D Packet->NvmeCmd->Cdw14; > > + } > > + if (Packet->NvmeCmd->Flags & CDW15_VALID) { > > + Sq->Payload.Raw.Cdw15 =3D Packet->NvmeCmd->Cdw15; > > + } > > + > > + // > > + // Ring the submission queue doorbell. > > + // > > + Private->SqTdbl[QueueId].Sqt++; > > + if (Private->SqTdbl[QueueId].Sqt =3D=3D SqSize) { > > + Private->SqTdbl[QueueId].Sqt =3D 0; > > + } > > + Data32 =3D ReadUnaligned32 ((UINT32 *)&Private->SqTdbl[QueueId]); > > + Status =3D NVME_SET_SQTDBL (Private, QueueId, &Data32); > > + if (EFI_ERROR (Status)) { > > + DEBUG ((DEBUG_ERROR, "%a: NVME_SET_SQTDBL fail, Status - %r\n", > > __FUNCTION__, Status)); > > + goto Exit; > > + } > > + > > + // > > + // Wait for completion queue to get filled in. > > + // > > + Status =3D EFI_TIMEOUT; > > + Timer =3D 0; > > + while (Timer < Packet->CommandTimeout) { > > + if (Cq->Pt !=3D Private->Pt[QueueId]) { > > + Status =3D EFI_SUCCESS; > > + break; > > + } > > + > > + MicroSecondDelay (NVME_POLL_INTERVAL); > > + Timer +=3D NVME_POLL_INTERVAL; > > + } > > + > > + if (Status =3D=3D EFI_TIMEOUT) { > > + // > > + // Timeout occurs for an NVMe command, reset the controller to abo= rt > > the outstanding command > > + // > > + DEBUG ((DEBUG_ERROR, "%a: Timeout occurs for the PassThru > > command.\n", __FUNCTION__)); > > + Status =3D NvmeControllerInit (Private); > > + if (EFI_ERROR (Status)) { > > + Status =3D EFI_DEVICE_ERROR; > > + } else { > > + // > > + // Return EFI_TIMEOUT to indicate a timeout occurs for PassThru > > command > > + // > > + Status =3D EFI_TIMEOUT; > > + } > > + goto Exit; > > + } > > + > > + // > > + // Move forward the Completion Queue head > > + // > > + Private->CqHdbl[QueueId].Cqh++; > > + if (Private->CqHdbl[QueueId].Cqh =3D=3D CqSize) { > > + Private->CqHdbl[QueueId].Cqh =3D 0; > > + Private->Pt[QueueId] ^=3D 1; > > + } > > + > > + // > > + // Copy the Respose Queue entry for this command to the callers > > response buffer > > + // > > + CopyMem (Packet->NvmeCompletion, Cq, sizeof > > (EDKII_PEI_NVM_EXPRESS_COMPLETION)); > > + > > + // > > + // Check the NVMe cmd execution result > > + // > > + Status =3D NvmeCheckCqStatus (Cq); > > + NVME_SET_CQHDBL (Private, QueueId, &Private->CqHdbl[QueueId]); > > + > > +Exit: > > + if (MapMeta !=3D NULL) { > > + IoMmuUnmap (MapMeta); > > + } > > + > > + if (MapData !=3D NULL) { > > + IoMmuUnmap (MapData); > > + } > > + > > + return Status; > > +} > > diff --git > > a/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiPassThru.h > > b/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiPassThru.h > > new file mode 100644 > > index 0000000000..96c748e1bf > > --- /dev/null > > +++ b/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiPassThru.h > > @@ -0,0 +1,107 @@ > > +/** @file > > + The NvmExpressPei driver is used to manage non-volatile memory > > subsystem > > + which follows NVM Express specification at PEI phase. > > + > > + Copyright (c) 2018, Intel Corporation. All rights reserved.
> > + > > + This program and the accompanying materials > > + are licensed and made available under the terms and conditions > > + of the BSD License which accompanies this distribution. The > > + full text of the license may be found at > > + http://opensource.org/licenses/bsd-license.php > > + > > + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" > > BASIS, > > + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER > > EXPRESS OR IMPLIED. > > + > > +**/ > > + > > +#ifndef _NVM_EXPRESS_PEI_PASSTHRU_H_ > > +#define _NVM_EXPRESS_PEI_PASSTHRU_H_ > > + > > +#define NVME_CONTROLLER_NSID 0 > > + > > +typedef struct { > > + UINT8 Opcode; > > + UINT8 FusedOperation; > > + #define NORMAL_CMD 0x00 > > + #define FUSED_FIRST_CMD 0x01 > > + #define FUSED_SECOND_CMD 0x02 > > + UINT16 Cid; > > +} NVME_CDW0; > > + > > +typedef struct { > > + NVME_CDW0 Cdw0; > > + UINT8 Flags; > > + #define CDW10_VALID 0x01 > > + #define CDW11_VALID 0x02 > > + #define CDW12_VALID 0x04 > > + #define CDW13_VALID 0x08 > > + #define CDW14_VALID 0x10 > > + #define CDW15_VALID 0x20 > > + UINT32 Nsid; > > + UINT32 Cdw10; > > + UINT32 Cdw11; > > + UINT32 Cdw12; > > + UINT32 Cdw13; > > + UINT32 Cdw14; > > + UINT32 Cdw15; > > +} EDKII_PEI_NVM_EXPRESS_COMMAND; > > + > > +typedef struct { > > + UINT32 Cdw0; > > + UINT32 Cdw1; > > + UINT32 Cdw2; > > + UINT32 Cdw3; > > +} EDKII_PEI_NVM_EXPRESS_COMPLETION; > > + > > +typedef struct { > > + UINT64 CommandTimeout; > > + VOID *TransferBuffer; > > + UINT32 TransferLength; > > + VOID *MetadataBuffer; > > + UINT32 MetadataLength; > > + UINT8 QueueType; > > + EDKII_PEI_NVM_EXPRESS_COMMAND *NvmeCmd; > > + EDKII_PEI_NVM_EXPRESS_COMPLETION *NvmeCompletion; > > +} EDKII_PEI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET; > > + > > + > > +/** > > + Sends an NVM Express Command Packet to an NVM Express controller or > > namespace. This function only > > + supports blocking execution of the command. > > + > > + @param[in] Private The pointer to the NVME_CONTEXT Data struc= ture. > > + @param[in] NamespaceId Is a 32 bit Namespace ID to which the Expr= ess > > HCI command packet will > > + be sent. > > + A Value of 0 denotes the NVM Express contr= oller, a Value > of > > all 0FFh in > > + the namespace ID specifies that the comman= d packet > should > > be sent to all > > + valid namespaces. > > + @param[in,out] Packet A pointer to the EDKII PEI NVM Express Pas= sThru > > Command Packet to send > > + to the NVMe namespace specified by Namespa= ceId. > > + > > + @retval EFI_SUCCESS The EDKII PEI NVM Express Command P= acket > > was sent by the host. > > + TransferLength bytes were transferr= ed to, or from > > DataBuffer. > > + @retval EFI_NOT_READY The EDKII PEI NVM Express Command > > Packet could not be sent because > > + the controller is not ready. The ca= ller may retry again > later. > > + @retval EFI_DEVICE_ERROR A device error occurred while attem= pting > > to send the EDKII PEI NVM > > + Express Command Packet. > > + @retval EFI_INVALID_PARAMETER Namespace, or the contents of > > EDKII_PEI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET > > + are invalid. > > + The EDKII PEI NVM Express Command P= acket was not > sent, > > so no > > + additional status information is av= ailable. > > + @retval EFI_UNSUPPORTED The command described by the EDKII = PEI > > NVM Express Command Packet > > + is not supported by the host adapte= r. > > + The EDKII PEI NVM Express Command P= acket was not > sent, > > so no > > + additional status information is av= ailable. > > + @retval EFI_TIMEOUT A timeout occurred while waiting fo= r the > > EDKII PEI NVM Express Command > > + Packet to execute. > > + > > +**/ > > +EFI_STATUS > > +NvmePassThru ( > > + IN PEI_NVME_CONTROLLER_PRIVATE_DATA *Private, > > + IN UINT32 NamespaceId= , > > + IN OUT EDKII_PEI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET > > *Packet > > + ); > > + > > +#endif > > diff --git a/MdeModulePkg/MdeModulePkg.dsc > > b/MdeModulePkg/MdeModulePkg.dsc > > index 18928f96d8..09b0f9f13d 100644 > > --- a/MdeModulePkg/MdeModulePkg.dsc > > +++ b/MdeModulePkg/MdeModulePkg.dsc > > @@ -233,6 +233,7 @@ > > MdeModulePkg/Bus/Pci/PciBusDxe/PciBusDxe.inf > > > > MdeModulePkg/Bus/Pci/IncompatiblePciDeviceSupportDxe/IncompatiblePci > > DeviceSupportDxe.inf > > MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressDxe.inf > > + MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.inf > > MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/SdMmcPciHcDxe.inf > > MdeModulePkg/Bus/Pci/SdMmcPciHcPei/SdMmcPciHcPei.inf > > MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcBlockIoPei.inf > > -- > > 2.12.0.windows.1