From nobody Mon Apr 29 02:15:30 2024 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; spf=none (zoho.com: 198.145.21.10 is neither permitted nor denied by domain of lists.01.org) smtp.mailfrom=edk2-devel-bounces@lists.01.org; dmarc=fail(p=none dis=none) header.from=intel.com Return-Path: Received: from ml01.01.org (ml01.01.org [198.145.21.10]) by mx.zohomail.com with SMTPS id 1529046230021532.8775925434956; Fri, 15 Jun 2018 00:03:50 -0700 (PDT) Received: from [127.0.0.1] (localhost [IPv6:::1]) by ml01.01.org (Postfix) with ESMTP id CBC88210FCF45; Fri, 15 Jun 2018 00:03:47 -0700 (PDT) Received: from mga17.intel.com (mga17.intel.com [192.55.52.151]) (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 3DE4D210FC38A for ; Fri, 15 Jun 2018 00:03:46 -0700 (PDT) Received: from fmsmga008.fm.intel.com ([10.253.24.58]) by fmsmga107.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 15 Jun 2018 00:03:45 -0700 Received: from shwdeopenpsi014.ccr.corp.intel.com ([10.239.9.19]) by fmsmga008.fm.intel.com with ESMTP; 15 Jun 2018 00:03:44 -0700 X-Original-To: edk2-devel@lists.01.org Received-SPF: none (zoho.com: 198.145.21.10 is neither permitted nor denied by domain of lists.01.org) client-ip=198.145.21.10; envelope-from=edk2-devel-bounces@lists.01.org; helo=ml01.01.org; Received-SPF: Pass (sender SPF authorized) identity=mailfrom; client-ip=192.55.52.151; helo=mga17.intel.com; envelope-from=hao.a.wu@intel.com; receiver=edk2-devel@lists.01.org X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.51,226,1526367600"; d="scan'208";a="47932591" From: Hao Wu To: edk2-devel@lists.01.org Date: Fri, 15 Jun 2018 15:03:39 +0800 Message-Id: <20180615070342.13388-2-hao.a.wu@intel.com> X-Mailer: git-send-email 2.12.0.windows.1 In-Reply-To: <20180615070342.13388-1-hao.a.wu@intel.com> References: <20180615070342.13388-1-hao.a.wu@intel.com> Subject: [edk2] [PATCH 1/4] MdeModulePkg: Add definitions for EDKII PEI NVME host controller PPI 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: , Cc: Hao Wu , Ruiyu Ni , Jiewen Yao , Eric Dong , Star Zeng MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Errors-To: edk2-devel-bounces@lists.01.org Sender: "edk2-devel" X-ZohoMail: RSF_4 Z_629925259 SPT_0 Content-Type: text/plain; charset="utf-8" Introduces the below PPI: struct EDKII_NVM_EXPRESS_HOST_CONTROLLER_PPI { EDKII_NVM_EXPRESS_HC_GET_MMIO_BAR GetNvmeHcMmioBar; EDKII_NVM_EXPRESS_HC_GET_DEVICE_PATH GetNvmeHcDevicePath; }; The GetNvmeHcMmioBar service will provide the caller with the MMIO BAR address of each NVMe HC within the system; The GetNvmeHcDevicePath service will provide the caller with the device path information of each NVMe HC. 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/Include/Ppi/NvmExpressHostController.h | 97 +++++++++++++++++= +++ MdeModulePkg/MdeModulePkg.dec | 3 + 2 files changed, 100 insertions(+) diff --git a/MdeModulePkg/Include/Ppi/NvmExpressHostController.h b/MdeModul= ePkg/Include/Ppi/NvmExpressHostController.h new file mode 100644 index 0000000000..de9ae4a59c --- /dev/null +++ b/MdeModulePkg/Include/Ppi/NvmExpressHostController.h @@ -0,0 +1,97 @@ +/** @file + + 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 BS= D License + which accompanies this distribution. The full text of the license may b= e 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 IMP= LIED. + +**/ + +#ifndef _EDKII_NVM_EXPRESS_HOST_CONTROLLER_PPI_H_ +#define _EDKII_NVM_EXPRESS_HOST_CONTROLLER_PPI_H_ + +#include + +/// +/// Global ID for the EDKII_NVM_EXPRESS_HOST_CONTROLLER_PPI. +/// +#define EDKII_NVME_EXPRESS_HOST_CONTROLLER_PPI_GUID \ + { \ + 0xcae3aa63, 0x676f, 0x4da3, { 0xbd, 0x50, 0x6c, 0xc5, 0xed, 0xde, 0x9a= , 0xad } \ + } + +// +// Forward declaration for the EDKII_NVM_EXPRESS_HOST_CONTROLLER_PPI. +// +typedef struct _EDKII_NVM_EXPRESS_HOST_CONTROLLER_PPI EDKII_NVM_EXPRESS_H= OST_CONTROLLER_PPI; + +/** + Get the MMIO base address of NVM Express host controller. + + @param[in] PeiServices Describes the list of possible PEI Serv= ices. + @param[in] This The PPI instance pointer. + @param[in] ControllerId The ID of the NVM Express host controll= er. + @param[out] MmioBar The MMIO base address of the controller. + + @retval EFI_SUCCESS The operation succeeds. + @retval EFI_INVALID_PARAMETER The parameters are invalid. + @retval EFI_NOT_FOUND The specified NVM Express host controll= er not + found. + +**/ +typedef +EFI_STATUS +(EFIAPI *EDKII_NVM_EXPRESS_HC_GET_MMIO_BAR) ( + IN EFI_PEI_SERVICES **PeiServices, + IN EDKII_NVM_EXPRESS_HOST_CONTROLLER_PPI *This, + IN UINT8 ControllerId, + OUT UINTN *MmioBar + ); + +/** + Get the device path of NVM Express host controller. + + @param[in] PeiServices Describes the list of possible PEI Serv= ices. + @param[in] This The PPI instance pointer. + @param[in] ControllerId The ID of the NVM Express host controll= er. + @param[out] DevicePathLenth The length of the device path in bytes = specified + by DevicePath. + @param[out] DevicePath The device path of NVM Express host con= troller. + The caller is responsible for freeing i= t. + This field re-uses EFI Device Path Prot= ocol as + defined by Section 10.2 EFI Device Path= Protocol + of UEFI 2.7 Specification. + + @retval EFI_SUCCESS The operation succeeds. + @retval EFI_INVALID_PARAMETER The parameters are invalid. + @retval EFI_NOT_FOUND The specified NVM Express host controll= er not + found. + @retval EFI_OUT_OF_RESOURCES The operation fails due to lack of reso= urces. + +**/ +typedef +EFI_STATUS +(EFIAPI *EDKII_NVM_EXPRESS_HC_GET_DEVICE_PATH) ( + IN EFI_PEI_SERVICES **PeiServices, + IN EDKII_NVM_EXPRESS_HOST_CONTROLLER_PPI *This, + IN UINT8 ControllerId, + OUT UINTN *DevicePathLenth, + OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath + ); + +// +// This PPI contains a set of services to interact with the NVM Express ho= st +// controller. +// +struct _EDKII_NVM_EXPRESS_HOST_CONTROLLER_PPI { + EDKII_NVM_EXPRESS_HC_GET_MMIO_BAR GetNvmeHcMmioBar; + EDKII_NVM_EXPRESS_HC_GET_DEVICE_PATH GetNvmeHcDevicePath; +}; + +extern EFI_GUID gEdkiiPeiNvmExpressHostControllerPpiGuid; + +#endif diff --git a/MdeModulePkg/MdeModulePkg.dec b/MdeModulePkg/MdeModulePkg.dec index 3802b6e0b8..8d7c97ee91 100644 --- a/MdeModulePkg/MdeModulePkg.dec +++ b/MdeModulePkg/MdeModulePkg.dec @@ -471,6 +471,9 @@ ## Include/Ppi/PlatformSpecificResetHandler.h gEdkiiPlatformSpecificResetHandlerPpiGuid =3D { 0x75cf14ae, 0x3441, 0x49= dc, { 0xaa, 0x10, 0xbb, 0x35, 0xa7, 0xba, 0x8b, 0xab } } =20 + ## Include/Ppi/NvmExpressHostController.h + gEdkiiPeiNvmExpressHostControllerPpiGuid =3D { 0xcae3aa63, 0x676f, 0x4d= a3, { 0xbd, 0x50, 0x6c, 0xc5, 0xed, 0xde, 0x9a, 0xad } } + [Protocols] ## Load File protocol provides capability to load and unload EFI image i= nto memory and execute it. # Include/Protocol/LoadPe32Image.h --=20 2.12.0.windows.1 _______________________________________________ edk2-devel mailing list edk2-devel@lists.01.org https://lists.01.org/mailman/listinfo/edk2-devel From nobody Mon Apr 29 02:15:30 2024 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; spf=none (zoho.com: 198.145.21.10 is neither permitted nor denied by domain of lists.01.org) smtp.mailfrom=edk2-devel-bounces@lists.01.org; dmarc=fail(p=none dis=none) header.from=intel.com Return-Path: Received: from ml01.01.org (ml01.01.org [198.145.21.10]) by mx.zohomail.com with SMTPS id 1529046232957583.0395747041246; Fri, 15 Jun 2018 00:03:52 -0700 (PDT) Received: from [127.0.0.1] (localhost [IPv6:::1]) by ml01.01.org (Postfix) with ESMTP id 174BD210FCF60; Fri, 15 Jun 2018 00:03:50 -0700 (PDT) Received: from mga17.intel.com (mga17.intel.com [192.55.52.151]) (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 5CF5B210D2255 for ; Fri, 15 Jun 2018 00:03:49 -0700 (PDT) Received: from fmsmga008.fm.intel.com ([10.253.24.58]) by fmsmga107.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 15 Jun 2018 00:03:49 -0700 Received: from shwdeopenpsi014.ccr.corp.intel.com ([10.239.9.19]) by fmsmga008.fm.intel.com with ESMTP; 15 Jun 2018 00:03:45 -0700 X-Original-To: edk2-devel@lists.01.org Received-SPF: none (zoho.com: 198.145.21.10 is neither permitted nor denied by domain of lists.01.org) client-ip=198.145.21.10; envelope-from=edk2-devel-bounces@lists.01.org; helo=ml01.01.org; Received-SPF: Pass (sender SPF authorized) identity=mailfrom; client-ip=192.55.52.151; helo=mga17.intel.com; envelope-from=hao.a.wu@intel.com; receiver=edk2-devel@lists.01.org X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.51,226,1526367600"; d="scan'208";a="47932598" From: Hao Wu To: edk2-devel@lists.01.org Date: Fri, 15 Jun 2018 15:03:40 +0800 Message-Id: <20180615070342.13388-3-hao.a.wu@intel.com> X-Mailer: git-send-email 2.12.0.windows.1 In-Reply-To: <20180615070342.13388-1-hao.a.wu@intel.com> References: <20180615070342.13388-1-hao.a.wu@intel.com> Subject: [edk2] [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: , Cc: Hao Wu , Ruiyu Ni , Jiewen Yao , Eric Dong , Star Zeng MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Errors-To: edk2-devel-bounces@lists.01.org Sender: "edk2-devel" X-ZohoMail: RSF_4 Z_629925259 SPT_0 Content-Type: text/plain; charset="utf-8" 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 IMP= LIED. + +**/ + +#include "NvmExpressPei.h" + +EDKII_IOMMU_PPI *mIoMmu; + +/** + Provides the controller-specific addresses required to access system mem= ory from a + DMA bus master. + + @param Operation Indicates if the bus master is going to re= ad or write to system memory. + @param HostAddress The system memory address to map to the PC= I controller. + @param NumberOfBytes On input the number of bytes to map. On ou= tput the number of bytes + that were mapped. + @param DeviceAddress The resulting map address for the bus mast= er 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 Numb= erOfBytes. + @retval EFI_UNSUPPORTED The HostAddress cannot be mapped as a comm= on 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 requ= ested 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 resources. + + @param Mapping The mapping value returned from Map(). + + @retval EFI_SUCCESS The range was unmapped. + @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned b= y Map(). + @retval EFI_DEVICE_ERROR The data was not committed to the target s= ystem 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 memory = address of the + allocated range. + @param DeviceAddress The resulting map address for the bus mast= er PCI controller to use to + access the hosts HostAddress. + @param Mapping A resulting value to pass to Unmap(). + + @retval EFI_SUCCESS The requested memory pages were allocated. + @retval EFI_UNSUPPORTED Attributes is unsupported. The only legal = attribute bits are + MEMORY_WRITE_COMBINE and MEMORY_CACHED. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated. + +**/ +EFI_STATUS +IoMmuAllocateBuffer ( + IN UINTN Pages, + OUT VOID **HostAddress, + OUT EFI_PHYSICAL_ADDRESS *DeviceAddress, + OUT VOID **Mapping + ) +{ + EFI_STATUS Status; + UINTN NumberOfBytes; + EFI_PHYSICAL_ADDRESS HostPhyAddress; + + *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 allo= cated 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/MdeModule= Pkg/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 IMP= LIED. + +**/ + +#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_TERMINA= TE_LIST), + &gEfiEndOfPeiSignalPpiGuid, + NvmePeimEndOfPei +}; + +/** + Check if the specified Nvm Express device namespace is active, and then = get the Identify + Namespace data. + + @param[in,out] Private The pointer to the PEI_NVME_CONTROLLER_PRI= VATE_DATA data structure. + @param[in] NamespaceId The specified namespace identifier. + + @retval EFI_SUCCESS The specified namespace in the device is successf= ully 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", __FUN= CTION__, 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->Nsz= e - 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 successfu= lly 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->ControllerDat= a->Nn * sizeof (PEI_NVME_NAMESPACE_INFO)); + + // + // According to Nvm Express 1.1 spec Figure 82, the field 'Nn' of the id= entify + // controller data defines the number of valid namespaces present for the + // controller. Namespaces shall be allocated in order (starting with 1) = and + // packed sequentially. + // + for (NamespaceId =3D 1; NamespaceId <=3D Private->ControllerData->Nn; Na= mespaceId++) { + // + // For now, we do not care the return status. Since if a valid namespa= ce is inactive, + // error status will be returned. But we continue to enumerate other v= alid 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 P= EI. + + @param[in] PeiServices Pointer to PEI Services Table. + @param[in] NotifyDescriptor Pointer to the descriptor for the Notific= ation + event that caused this function to execut= e. + @param[in] Ppi Pointer to the PPI data associated with t= his 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 (NotifyDescri= ptor); + 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 Services. + + @retval EFI_SUCCESS PPI successfully installed. + +**/ +EFI_STATUS +EFIAPI +NvmExpressPeimEntry ( + IN EFI_PEI_FILE_HANDLE FileHandle, + IN CONST EFI_PEI_SERVICES **PeiServices + ) +{ + EFI_STATUS Status; + EFI_BOOT_MODE BootMode; + EDKII_NVM_EXPRESS_HOST_CONTROLLER_PPI *NvmeHcPpi; + UINT8 Controller; + UINTN MmioBase; + 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) Private->= Buffer)); + DEBUG ((DEBUG_INFO, "%a: DMA buffer base at 0x%x\n", __FUNCTION__, Pri= vate->Buffer)); + + // + // Initialize controller private data + // + Private->Signature =3D NVME_PEI_CONTROLLER_PRIVATE_DATA_SIGNATURE; + Private->MmioBase =3D MmioBase; + Private->BlkIoPpi.GetNumberOfBlockDevices =3D NvmeBlockIoPeimGetDevic= eNo; + Private->BlkIoPpi.GetBlockDeviceMediaInfo =3D NvmeBlockIoPeimGetMedia= Info; + Private->BlkIoPpi.ReadBlocks =3D NvmeBlockIoPeimReadBloc= ks; + Private->BlkIo2Ppi.Revision =3D EFI_PEI_RECOVERY_BLOCK_= IO2_PPI_REVISION; + Private->BlkIo2Ppi.GetNumberOfBlockDevices =3D NvmeBlockIoPeimGetDevic= eNo2; + Private->BlkIo2Ppi.GetBlockDeviceMediaInfo =3D NvmeBlockIoPeimGetMedia= Info2; + Private->BlkIo2Ppi.ReadBlocks =3D NvmeBlockIoPeimReadBloc= ks2; + Private->BlkIoPpiList =3D mNvmeBlkIoPpiListTemplate; + Private->BlkIo2PpiList =3D mNvmeBlkIo2PpiListTemplate; + Private->EndOfPeiNotifyList =3D mNvmeEndOfPeiNotifyListTemplate; + 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 Status = - %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/MdeModule= Pkg/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 IMP= LIED. + +**/ + +#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_PRIV= ATE_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 a= dmin submission queue entries, which is 0-based +#define NVME_ACQ_SIZE 1 // Number of a= dmin completion queue entries, which is 0-based +#define NVME_CSQ_SIZE 63 // Number of I= /O submission queue entries, which is 0-based +#define NVME_CCQ_SIZE 63 // Number of I= /O completion queue entries, which is 0-based +#define NVME_PRP_SIZE (8) // Pages of PR= P list + +#define NVME_MEM_MAX_PAGES \ + ( \ + 1 /* ASQ */ + \ + 1 /* ACQ */ + \ + 1 /* SQs */ + \ + 1 /* CQs */ + \ + NVME_PRP_SIZE) /* PRPs */ + +#define NVME_ADMIN_QUEUE 0x00 +#define NVME_IO_QUEUE 0x01 +#define NVME_GENERIC_TIMEOUT 5000000 // Generic= PassThru command timeout value, in us unit +#define NVME_POLL_INTERVAL 100 // Poll in= terval 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 this = buffer + // 1st 4kB boundary is the start of the admin submission queue + // 2nd 4kB boundary is the start of the admin completion queue + // 3rd 4kB boundary is the start of I/O submission queue + // 4th 4kB boundary is the start of I/O completion queue + // 5th 4kB boundary is the start of PRP list buffers + // + VOID *Buffer; + VOID *BufferMapping; + + // + // Pointers to 4kB aligned submission & completion queues + // + NVME_SQ *SqBuffer[NVME_MAX_QUEUES]; + NVME_CQ *CqBuffer[NVME_MAX_QUEUES]; + + // + // Submission and completion queue indices + // + NVME_SQTDBL SqTdbl[NVME_MAX_QUEUES]; + NVME_CQHDBL CqHdbl[NVME_MAX_QUEUES]; + + UINT8 Pt[NVME_MAX_QUEUES]; + UINT16 Cid[NVME_MAX_QUEUES]; + + // + // Nvme controller capabilities + // + NVME_CAP Cap; + + // + // Namespaces information on the controller + // + UINT32 ActiveNamespaceNum; + PEI_NVME_NAMESPACE_INFO *NamespaceInfo; +}; + +#define GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_BLKIO(a) \ + CR (a, PEI_NVME_CONTROLLER_PRIVATE_DATA, BlkIoPpi, NVME_PEI_CONTROLLER_P= RIVATE_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_CO= NTROLLER_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 memory = address of the + allocated range. + @param DeviceAddress The resulting map address for the bus mast= er PCI controller to use to + access the hosts HostAddress. + @param Mapping A resulting value to pass to Unmap(). + + @retval EFI_SUCCESS The requested memory pages were allocated. + @retval EFI_UNSUPPORTED Attributes is unsupported. The only legal = attribute bits are + MEMORY_WRITE_COMBINE and MEMORY_CACHED. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated. + +**/ +EFI_STATUS +IoMmuAllocateBuffer ( + IN UINTN Pages, + OUT VOID **HostAddress, + OUT EFI_PHYSICAL_ADDRESS *DeviceAddress, + OUT VOID **Mapping + ); + +/** + Frees memory that was allocated with AllocateBuffer(). + + @param Pages The number of pages to free. + @param HostAddress The base system memory address of the allo= cated 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 mem= ory from a + DMA bus master. + + @param Operation Indicates if the bus master is going to re= ad or write to system memory. + @param HostAddress The system memory address to map to the PC= I controller. + @param NumberOfBytes On input the number of bytes to map. On ou= tput the number of bytes + that were mapped. + @param DeviceAddress The resulting map address for the bus mast= er 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 Numb= erOfBytes. + @retval EFI_UNSUPPORTED The HostAddress cannot be mapped as a comm= on 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 requ= ested address. + +**/ +EFI_STATUS +IoMmuMap ( + IN EDKII_IOMMU_OPERATION Operation, + IN VOID *HostAddress, + IN OUT UINTN *NumberOfBytes, + OUT EFI_PHYSICAL_ADDRESS *DeviceAddress, + OUT VOID **Mapping + ); + +/** + Completes the Map() operation and releases any corresponding resources. + + @param Mapping The mapping value returned from Map(). + + @retval EFI_SUCCESS The range was unmapped. + @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned b= y Map(). + @retval EFI_DEVICE_ERROR The data was not committed to the target s= ystem memory. +**/ +EFI_STATUS +IoMmuUnmap ( + IN VOID *Mapping + ); + +/** + One notified function to cleanup the allocated resources at the end of P= EI. + + @param[in] PeiServices Pointer to PEI Services Table. + @param[in] NotifyDescriptor Pointer to the descriptor for the Notific= ation + event that caused this function to execut= e. + @param[in] Ppi Pointer to the PPI data associated with t= his 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/MdeModu= lePkg/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 the B= SD 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 IM= PLIED. +# +## + +[Defines] + INF_VERSION =3D 0x00010005 + BASE_NAME =3D NvmExpressPei + MODULE_UNI_FILE =3D NvmExpressPei.uni + FILE_GUID =3D 94813714-E10A-4798-9909-8C904F66B4D9 + 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 +# + +[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/MdeModu= lePkg/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 IM= PLIED. +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Manage non-volati= le 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 E= xpress specification at PEI phase." diff --git a/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiBlockIo.c b/Md= eModulePkg/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 IMP= LIED. + +**/ + +#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__, M= axTransferBlocks)); + + while (Blocks > 0) { + Status =3D ReadSectors ( + NamespaceInfo, + Buffer, + Lba, + Blocks > MaxTransferBlocks ? MaxTransferBlocks : (UINT32)Bl= ocks + ); + 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", __FUNC= TION__, Status)); + break; + } + DEBUG (( + DEBUG_INFO, + "%a: ReadSectors fail, retry with smaller transfer block number - = 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", __FUNCTI= ON__, Lba, + (UINT64)OrginalBlocks, (UINT64)Blocks, BlockSize, Status)); + return Status; +} + +/** + Gets the count of block I/O devices that one specific block driver detec= ts. + + This function is used for getting the count of block I/O devices that one + specific block driver detects. If no device is detected, then the functi= on + will return zero. + + @param[in] PeiServices General-purpose services that are avail= able + 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 discove= red. + + @retval EFI_SUCCESS The operation performed successfully. + +**/ +EFI_STATUS +EFIAPI +NvmeBlockIoPeimGetDeviceNo ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This, + OUT UINTN *NumberBlockDevices + ) +{ + PEI_NVME_CONTROLLER_PRIVATE_DATA *Private; + + if (This =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 device's = media + information. If the media changes, calling this function will update the= media + information accordingly. + + @param[in] PeiServices General-purpose services that are available to= every + PEIM + @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI in= stance. + @param[in] DeviceIndex Specifies the block device to which the functi= on wants + to talk. Because the driver that implements Bl= ock I/O + PPIs will manage multiple block devices, the P= PIs that + want to talk to a single device must specify t= he + device index that was assigned during the enum= eration + process. This index is a number from one to + NumberBlockDevices. + @param[out] MediaInfo The media information of the specified block m= edia. + The caller is responsible for the ownership of= this + data structure. + + @par Note: + The MediaInfo structure describes an enumeration of possible block d= evice + types. This enumeration exists because no device paths are actually= passed + across interfaces that describe the type or class of hardware that i= s publishing + the block I/O interface. This enumeration will allow for policy deci= sions + in the Recovery PEIM, such as "Try to recover from legacy floppy fir= st, + LS-120 second, CD-ROM third." If there are multiple partitions abstr= acted + by a given device type, they should be reported in ascending order; = this + order also applies to nested partitions, such as legacy MBR, where t= he + outermost partitions would have precedence in the reporting order. T= he + same logic applies to systems such as IDE that have precedence relat= ionships + like "Master/Slave" or "Primary/Secondary". The master device should= be + reported first, the slave second. + + @retval EFI_SUCCESS Media information about the specified block d= evice + was obtained successfully. + @retval EFI_DEVICE_ERROR Cannot get the media information due to a har= dware + 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->ActiveNamespaceNum= )) { + 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[DeviceIndex-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. All t= he + blocks are read, or an error is returned. If there is no media in the de= vice, + the function returns EFI_NO_MEDIA. + + @param[in] PeiServices General-purpose services that are available to + every PEIM. + @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI in= stance. + @param[in] DeviceIndex Specifies the block device to which the functi= on wants + to talk. Because the driver that implements Bl= ock I/O + PPIs will manage multiple block devices, PPIs = that + want to talk to a single device must specify t= he device + index that was assigned during the enumeration= process. + This index is a number from one to NumberBlock= Devices. + @param[in] StartLBA The starting logical block address (LBA) to re= ad from + on the device + @param[in] BufferSize The size of the Buffer in bytes. This number m= ust be + a multiple of the intrinsic block size of the = device. + @param[out] Buffer A pointer to the destination buffer for the da= ta. + The caller is responsible for the ownership of= the + buffer. + + @retval EFI_SUCCESS The data was read correctly from the dev= ice. + @retval EFI_DEVICE_ERROR The device reported an error while attem= pting + to perform the read operation. + @retval EFI_INVALID_PARAMETER The read request contains LBAs that are = not + valid, or the buffer is not properly ali= gned. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multip= le of + the intrinsic block size of the device. + +**/ +EFI_STATUS +EFIAPI +NvmeBlockIoPeimReadBlocks ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This, + IN UINTN DeviceIndex, + IN EFI_PEI_LBA StartLBA, + IN UINTN BufferSize, + OUT VOID *Buffer + ) +{ + PEI_NVME_CONTROLLER_PRIVATE_DATA *Private; + PEI_NVME_NAMESPACE_INFO *NamespaceInfo; + UINT32 BlockSize; + UINTN NumberOfBlocks; + + Private =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->ActiveNamespaceNum= )) { + 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 detec= ts. + + This function is used for getting the count of block I/O devices that one + specific block driver detects. If no device is detected, then the functi= on + will return zero. + + @param[in] PeiServices General-purpose services that are avail= able + to every PEIM. + @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO= 2_PPI + instance. + @param[out] NumberBlockDevices The number of block I/O devices discove= red. + + @retval EFI_SUCCESS The operation performed successfully. + +**/ +EFI_STATUS +EFIAPI +NvmeBlockIoPeimGetDeviceNo2 ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_RECOVERY_BLOCK_IO2_PPI *This, + OUT UINTN *NumberBlockDevices + ) +{ + PEI_NVME_CONTROLLER_PRIVATE_DATA *Private; + + if (This =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 device's = media + information. If the media changes, calling this function will update the= media + information accordingly. + + @param[in] PeiServices General-purpose services that are available to= every + PEIM + @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO2_PPI i= nstance. + @param[in] DeviceIndex Specifies the block device to which the functi= on wants + to talk. Because the driver that implements Bl= ock I/O + PPIs will manage multiple block devices, the P= PIs that + want to talk to a single device must specify t= he + device index that was assigned during the enum= eration + process. This index is a number from one to + NumberBlockDevices. + @param[out] MediaInfo The media information of the specified block m= edia. + The caller is responsible for the ownership of= this + data structure. + + @par Note: + The MediaInfo structure describes an enumeration of possible block d= evice + types. This enumeration exists because no device paths are actually= passed + across interfaces that describe the type or class of hardware that i= s publishing + the block I/O interface. This enumeration will allow for policy deci= sions + in the Recovery PEIM, such as "Try to recover from legacy floppy fir= st, + LS-120 second, CD-ROM third." If there are multiple partitions abstr= acted + by a given device type, they should be reported in ascending order; = this + order also applies to nested partitions, such as legacy MBR, where t= he + outermost partitions would have precedence in the reporting order. T= he + same logic applies to systems such as IDE that have precedence relat= ionships + like "Master/Slave" or "Primary/Secondary". The master device should= be + reported first, the slave second. + + @retval EFI_SUCCESS Media information about the specified block d= evice + was obtained successfully. + @retval EFI_DEVICE_ERROR Cannot get the media information due to a har= dware + 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. All t= he + blocks are read, or an error is returned. If there is no media in the de= vice, + the function returns EFI_NO_MEDIA. + + @param[in] PeiServices General-purpose services that are available to + every PEIM. + @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO2_PPI i= nstance. + @param[in] DeviceIndex Specifies the block device to which the functi= on wants + to talk. Because the driver that implements Bl= ock I/O + PPIs will manage multiple block devices, PPIs = that + want to talk to a single device must specify t= he device + index that was assigned during the enumeration= process. + This index is a number from one to NumberBlock= Devices. + @param[in] StartLBA The starting logical block address (LBA) to re= ad from + on the device + @param[in] BufferSize The size of the Buffer in bytes. This number m= ust be + a multiple of the intrinsic block size of the = device. + @param[out] Buffer A pointer to the destination buffer for the da= ta. + The caller is responsible for the ownership of= the + buffer. + + @retval EFI_SUCCESS The data was read correctly from the dev= ice. + @retval EFI_DEVICE_ERROR The device reported an error while attem= pting + to perform the read operation. + @retval EFI_INVALID_PARAMETER The read request contains LBAs that are = not + valid, or the buffer is not properly ali= gned. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multip= le of + the intrinsic block size of the device. + +**/ +EFI_STATUS +EFIAPI +NvmeBlockIoPeimReadBlocks2 ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_RECOVERY_BLOCK_IO2_PPI *This, + IN UINTN DeviceIndex, + IN EFI_PEI_LBA StartLBA, + IN UINTN BufferSize, + OUT VOID *Buffer + ) +{ + PEI_NVME_CONTROLLER_PRIVATE_DATA *Private; + + if (This =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/Md= eModulePkg/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 IMP= LIED. + +**/ + +#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 detec= ts. + + This function is used for getting the count of block I/O devices that one + specific block driver detects. If no device is detected, then the functi= on + will return zero. + + @param[in] PeiServices General-purpose services that are avail= able + 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 discove= red. + + @retval EFI_SUCCESS The operation performed successfully. + +**/ +EFI_STATUS +EFIAPI +NvmeBlockIoPeimGetDeviceNo ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This, + OUT UINTN *NumberBlockDevices + ); + +/** + Gets a block device's media information. + + This function will provide the caller with the specified block device's = media + information. If the media changes, calling this function will update the= media + information accordingly. + + @param[in] PeiServices General-purpose services that are available to= every + PEIM + @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI in= stance. + @param[in] DeviceIndex Specifies the block device to which the functi= on wants + to talk. Because the driver that implements Bl= ock I/O + PPIs will manage multiple block devices, the P= PIs that + want to talk to a single device must specify t= he + device index that was assigned during the enum= eration + process. This index is a number from one to + NumberBlockDevices. + @param[out] MediaInfo The media information of the specified block m= edia. + The caller is responsible for the ownership of= this + data structure. + + @par Note: + The MediaInfo structure describes an enumeration of possible block d= evice + types. This enumeration exists because no device paths are actually= passed + across interfaces that describe the type or class of hardware that i= s publishing + the block I/O interface. This enumeration will allow for policy deci= sions + in the Recovery PEIM, such as "Try to recover from legacy floppy fir= st, + LS-120 second, CD-ROM third." If there are multiple partitions abstr= acted + by a given device type, they should be reported in ascending order; = this + order also applies to nested partitions, such as legacy MBR, where t= he + outermost partitions would have precedence in the reporting order. T= he + same logic applies to systems such as IDE that have precedence relat= ionships + like "Master/Slave" or "Primary/Secondary". The master device should= be + reported first, the slave second. + + @retval EFI_SUCCESS Media information about the specified block d= evice + was obtained successfully. + @retval EFI_DEVICE_ERROR Cannot get the media information due to a har= dware + error. + +**/ +EFI_STATUS +EFIAPI +NvmeBlockIoPeimGetMediaInfo ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This, + IN UINTN DeviceIndex, + OUT EFI_PEI_BLOCK_IO_MEDIA *MediaInfo + ); + +/** + Reads the requested number of blocks from the specified block device. + + The function reads the requested number of blocks from the device. All t= he + blocks are read, or an error is returned. If there is no media in the de= vice, + the function returns EFI_NO_MEDIA. + + @param[in] PeiServices General-purpose services that are available to + every PEIM. + @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI in= stance. + @param[in] DeviceIndex Specifies the block device to which the functi= on wants + to talk. Because the driver that implements Bl= ock I/O + PPIs will manage multiple block devices, PPIs = that + want to talk to a single device must specify t= he device + index that was assigned during the enumeration= process. + This index is a number from one to NumberBlock= Devices. + @param[in] StartLBA The starting logical block address (LBA) to re= ad from + on the device + @param[in] BufferSize The size of the Buffer in bytes. This number m= ust be + a multiple of the intrinsic block size of the = device. + @param[out] Buffer A pointer to the destination buffer for the da= ta. + The caller is responsible for the ownership of= the + buffer. + + @retval EFI_SUCCESS The data was read correctly from the dev= ice. + @retval EFI_DEVICE_ERROR The device reported an error while attem= pting + to perform the read operation. + @retval EFI_INVALID_PARAMETER The read request contains LBAs that are = not + valid, or the buffer is not properly ali= gned. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multip= le of + the intrinsic block size of the device. + +**/ +EFI_STATUS +EFIAPI +NvmeBlockIoPeimReadBlocks ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This, + IN UINTN DeviceIndex, + IN EFI_PEI_LBA StartLBA, + IN UINTN BufferSize, + OUT VOID *Buffer + ); + +/** + Gets the count of block I/O devices that one specific block driver detec= ts. + + This function is used for getting the count of block I/O devices that one + specific block driver detects. If no device is detected, then the functi= on + will return zero. + + @param[in] PeiServices General-purpose services that are avail= able + to every PEIM. + @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO= 2_PPI + instance. + @param[out] NumberBlockDevices The number of block I/O devices discove= red. + + @retval EFI_SUCCESS The operation performed successfully. + +**/ +EFI_STATUS +EFIAPI +NvmeBlockIoPeimGetDeviceNo2 ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_RECOVERY_BLOCK_IO2_PPI *This, + OUT UINTN *NumberBlockDevices + ); + +/** + Gets a block device's media information. + + This function will provide the caller with the specified block device's = media + information. If the media changes, calling this function will update the= media + information accordingly. + + @param[in] PeiServices General-purpose services that are available to= every + PEIM + @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO2_PPI i= nstance. + @param[in] DeviceIndex Specifies the block device to which the functi= on wants + to talk. Because the driver that implements Bl= ock I/O + PPIs will manage multiple block devices, the P= PIs that + want to talk to a single device must specify t= he + device index that was assigned during the enum= eration + process. This index is a number from one to + NumberBlockDevices. + @param[out] MediaInfo The media information of the specified block m= edia. + The caller is responsible for the ownership of= this + data structure. + + @par Note: + The MediaInfo structure describes an enumeration of possible block d= evice + types. This enumeration exists because no device paths are actually= passed + across interfaces that describe the type or class of hardware that i= s publishing + the block I/O interface. This enumeration will allow for policy deci= sions + in the Recovery PEIM, such as "Try to recover from legacy floppy fir= st, + LS-120 second, CD-ROM third." If there are multiple partitions abstr= acted + by a given device type, they should be reported in ascending order; = this + order also applies to nested partitions, such as legacy MBR, where t= he + outermost partitions would have precedence in the reporting order. T= he + same logic applies to systems such as IDE that have precedence relat= ionships + like "Master/Slave" or "Primary/Secondary". The master device should= be + reported first, the slave second. + + @retval EFI_SUCCESS Media information about the specified block d= evice + was obtained successfully. + @retval EFI_DEVICE_ERROR Cannot get the media information due to a har= dware + error. + +**/ +EFI_STATUS +EFIAPI +NvmeBlockIoPeimGetMediaInfo2 ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_RECOVERY_BLOCK_IO2_PPI *This, + IN UINTN DeviceIndex, + OUT EFI_PEI_BLOCK_IO2_MEDIA *MediaInfo + ); + +/** + Reads the requested number of blocks from the specified block device. + + The function reads the requested number of blocks from the device. All t= he + blocks are read, or an error is returned. If there is no media in the de= vice, + the function returns EFI_NO_MEDIA. + + @param[in] PeiServices General-purpose services that are available to + every PEIM. + @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO2_PPI i= nstance. + @param[in] DeviceIndex Specifies the block device to which the functi= on wants + to talk. Because the driver that implements Bl= ock I/O + PPIs will manage multiple block devices, PPIs = that + want to talk to a single device must specify t= he device + index that was assigned during the enumeration= process. + This index is a number from one to NumberBlock= Devices. + @param[in] StartLBA The starting logical block address (LBA) to re= ad from + on the device + @param[in] BufferSize The size of the Buffer in bytes. This number m= ust be + a multiple of the intrinsic block size of the = device. + @param[out] Buffer A pointer to the destination buffer for the da= ta. + The caller is responsible for the ownership of= the + buffer. + + @retval EFI_SUCCESS The data was read correctly from the dev= ice. + @retval EFI_DEVICE_ERROR The device reported an error while attem= pting + to perform the read operation. + @retval EFI_INVALID_PARAMETER The read request contains LBAs that are = not + valid, or the buffer is not properly ali= gned. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multip= le of + the intrinsic block size of the device. + +**/ +EFI_STATUS +EFIAPI +NvmeBlockIoPeimReadBlocks2 ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_RECOVERY_BLOCK_IO2_PPI *This, + IN UINTN DeviceIndex, + IN EFI_PEI_LBA StartLBA, + IN UINTN BufferSize, + OUT VOID *Buffer + ); + +#endif diff --git a/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiExtra.uni b/Md= eModulePkg/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 IM= PLIED. +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"NVM Express Peim" diff --git a/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiHci.c b/MdeMod= ulePkg/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 IMP= LIED. + +**/ + +#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", __FU= NCTION__)); + 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_D= ATA 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 s= et 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", __FUNC= TION__, 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_DA= TA 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", __FUNCTI= ON__, Status)); + goto ErrorExit; + } + } + + Status =3D NvmeWaitController (Private, FALSE); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: NvmeWaitController fail, Status - %r\n", __F= UNCTION__, 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_DAT= A data structure. + + @return EFI_SUCCESS Successfully enable the controller. + @return EFI_DEVICE_ERROR Fail to enable the controller. + @return EFI_TIMEOUT Fail to enable the controller in given time = slot. + +**/ +EFI_STATUS +NvmeEnableController ( + IN PEI_NVME_CONTROLLER_PRIVATE_DATA *Private + ) +{ + NVME_CC Cc; + EFI_STATUS Status; + + // + // Enable the controller + // CC.AMS, CC.MPS and CC.CSS are all set to 0 + // + ZeroMem (&Cc, sizeof (NVME_CC)); + Cc.En =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", __F= UNCTION__, 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_DA= TA data structure. + @param[in] Buffer The Buffer used to store the Identify Controller = data. + + @return EFI_SUCCESS Successfully get the Identify Controller data. + @return others Fail to get the Identify Controller data. + +**/ +EFI_STATUS +NvmeIdentifyController ( + IN PEI_NVME_CONTROLLER_PRIVATE_DATA *Private, + IN VOID *Buffer + ) +{ + 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 field= shall be cleared to 0h. + // For the Identify command, the Namespace Identifier is only used for t= he 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 namespac= e 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_DA= TA 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*)ControllerData= ->Fr))); + DEBUG ((DEBUG_INFO, " RAB : 0x%x\n", ControllerData->Rab)); + DEBUG ((DEBUG_INFO, " IEEE : 0x%x\n", *(UINT32*)ControllerData->= Ieee_oui)); + DEBUG ((DEBUG_INFO, " AERL : 0x%x\n", ControllerData->Aerl)); + DEBUG ((DEBUG_INFO, " SQES : 0x%x\n", ControllerData->Sqes)); + DEBUG ((DEBUG_INFO, " CQES : 0x%x\n", ControllerData->Cqes)); + DEBUG ((DEBUG_INFO, " NN : 0x%x\n", ControllerData->Nn)); + return; +} + +/** + Create IO completion queue. + + @param[in] Private The pointer to the PEI_NVME_CONTROLLER_PRIVATE_DA= TA 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_CRIO= CQ)); + 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_DA= TA 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_CRIO= SQ)); + 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_DA= TA data structure. + + @retval EFI_SUCCESS The NVM Express Controller is initialized success= fully. + @retval Others A device error occurred while initializing the co= ntroller. + +**/ +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", V= er.Mjr, Ver.Mnr)); + + // + // Read the controller Capabilities register and verify that the NVM com= mand 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 com= mand set.\n", __FUNCTION__)); + return EFI_UNSUPPORTED; + } + + // + // Currently, the driver only supports 4k page size + // + if ((Private->Cap.Mpsmin + 12) > EFI_PAGE_SHIFT) { + DEBUG ((DEBUG_ERROR, "%a: The driver doesn't support page size other t= han 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 *)AllocateZero= Pool (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_NAMESPAC= E_INFO)) { + DEBUG (( + DEBUG_ERROR, + "%a: Number of Namespaces field in Identify Controller data not supp= orted 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_DA= TA 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/MdeMod= ulePkg/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 IMP= LIED. + +**/ + +#ifndef _NVM_EXPRESS_PEI_HCI_H_ +#define _NVM_EXPRESS_PEI_HCI_H_ + +// +// NVME host controller registers operation definitions +// +#define NVME_GET_CAP(Private, Cap) NvmeMmioRead (Cap, Private= ->MmioBase + NVME_CAP_OFFSET, sizeof (NVME_CAP)) +#define NVME_GET_CC(Private, Cc) NvmeMmioRead (Cc, Private-= >MmioBase + NVME_CC_OFFSET, sizeof (NVME_CC)) +#define NVME_SET_CC(Private, Cc) NvmeMmioWrite (Private->Mmi= oBase + NVME_CC_OFFSET, Cc, sizeof (NVME_CC)) +#define NVME_GET_CSTS(Private, Csts) NvmeMmioRead (Csts, Privat= e->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->Mmi= oBase + 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->Mmi= oBase + 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->Mmi= oBase + 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->Mmi= oBase + NVME_SQTDBL_OFFSET(Qid, Private->Cap.Dstrd), Sqtdbl, sizeof (NVME_S= QTDBL)) +#define NVME_SET_CQHDBL(Private, Qid, Cqhdbl) NvmeMmioWrite (Private->Mmi= oBase + NVME_CQHDBL_OFFSET(Qid, Private->Cap.Dstrd), Cqhdbl, sizeof (NVME_C= QHDBL)) + +// +// Base memory address enum types +// +enum { + BASEMEM_ASQ, + BASEMEM_ACQ, + BASEMEM_SQ, + BASEMEM_CQ, + BASEMEM_PRP, + MAX_BASEMEM_COUNT +}; + +// +// All of base memories are 4K(0x1000) alignment +// +#define ALIGN(v, a) (UINTN)((((v) - 1) | ((a) - 1= )) + 1) +#define NVME_MEM_BASE(Private) ((UINTN)(Private->Buffer)) +#define NVME_ASQ_BASE(Private) (ALIGN (NVME_MEM_BASE(Private= ) + ((NvmeBaseMemPageOffset (BASEMEM_ASQ)) *= EFI_PAGE_SIZE), EFI_PAGE_SIZE)) +#define NVME_ACQ_BASE(Private) (ALIGN (NVME_MEM_BASE(Private= ) + ((NvmeBaseMemPageOffset (BASEMEM_ACQ)) *= EFI_PAGE_SIZE), EFI_PAGE_SIZE)) +#define NVME_SQ_BASE(Private, Index) (ALIGN (NVME_MEM_BASE(Private= ) + ((NvmeBaseMemPageOffset (BASEMEM_SQ) + ((Index)*(NVME_MAX_QUEUES-1))) *= EFI_PAGE_SIZE), EFI_PAGE_SIZE)) +#define NVME_CQ_BASE(Private, Index) (ALIGN (NVME_MEM_BASE(Private= ) + ((NvmeBaseMemPageOffset (BASEMEM_CQ) + ((Index)*(NVME_MAX_QUEUES-1))) *= EFI_PAGE_SIZE), EFI_PAGE_SIZE)) +#define NVME_PRP_BASE(Private) (ALIGN (NVME_MEM_BASE(Private= ) + ((NvmeBaseMemPageOffset (BASEMEM_PRP)) *= EFI_PAGE_SIZE), EFI_PAGE_SIZE)) + + +/** + Transfer MMIO Data to memory. + + @param[in,out] MemBuffer Destination: Memory address. + @param[in] MmioAddr Source: MMIO address. + @param[in] Size Size for read. + + @retval EFI_SUCCESS MMIO read sucessfully. + +**/ +EFI_STATUS +NvmeMmioRead ( + IN OUT VOID *MemBuffer, + IN UINTN MmioAddr, + IN UINTN Size + ); + +/** + Transfer memory data to MMIO. + + @param[in,out] MmioAddr Destination: MMIO address. + @param[in] MemBuffer Source: Memory address. + @param[in] Size Size for write. + + @retval EFI_SUCCESS MMIO write sucessfully. + +**/ +EFI_STATUS +NvmeMmioWrite ( + IN OUT UINTN MmioAddr, + IN VOID *MemBuffer, + IN UINTN Size + ); + +/** + Get the page offset for specific NVME based memory. + + @param[in] BaseMemIndex The Index of BaseMem (0-based). + + @retval - The page count for specific BaseMem Index + +**/ +UINT32 +NvmeBaseMemPageOffset ( + IN UINTN BaseMemIndex + ); + +/** + Disable the Nvm Express controller. + + @param[in] Private The pointer to the PEI_NVME_CONTROLLER_PRIVATE_DA= TA 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_DA= TA data structure. + + @retval EFI_SUCCESS The NVM Express Controller is initialized success= fully. + @retval Others A device error occurred while initializing the co= ntroller. + +**/ +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 namespac= e 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_DA= TA data structure. + +**/ +VOID +NvmeFreeControllerResource ( + IN PEI_NVME_CONTROLLER_PRIVATE_DATA *Private + ); + +#endif diff --git a/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiPassThru.c b/M= deModulePkg/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 IMP= LIED. + +**/ + +#include "NvmExpressPei.h" + +/** + Create PRP lists for Data transfer which is larger than 2 memory pages. + + @param[in] Private The pointer to the PEI_NVME_CONTROLLER_PRIVA= TE_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)PrpEntr= yNo, &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; ++PrpListIndex) { + PrpListBase =3D (UINTN)PrpListHost + PrpListIndex * EFI_PAGE_SIZE; + + for (PrpEntryIndex =3D 0; PrpEntryIndex < PrpEntryNo; ++PrpEntryIndex)= { + 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_SIZ= E); + 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) ? Remainde= r : PrpEntryNo); ++PrpEntryIndex) { + PrpEntry =3D (UINT8 *)(UINTN) (PrpListBase + PrpEntryIndex * sizeof(UI= NT64)); + 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 Notifica= tion\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 Comman= d\n")); + break; + case 0xB: + DEBUG ((DEBUG_INFO, "Invalid Namespace or Format\n")); + break; + case 0xC: + DEBUG ((DEBUG_INFO, "Command Sequence Error\n")); + break; + case 0xD: + DEBUG ((DEBUG_INFO, "Invalid SGL Last Segment Descriptor\n")); + break; + case 0xE: + DEBUG ((DEBUG_INFO, "Invalid Number of SGL Descriptors\n")); + break; + case 0xF: + DEBUG ((DEBUG_INFO, "Data SGL Length Invalid\n")); + break; + case 0x10: + DEBUG ((DEBUG_INFO, "Metadata SGL Length Invalid\n")); + break; + case 0x11: + DEBUG ((DEBUG_INFO, "SGL Descriptor Type Invalid\n")); + break; + case 0x80: + DEBUG ((DEBUG_INFO, "LBA Out of Range\n")); + break; + case 0x81: + DEBUG ((DEBUG_INFO, "Capacity Exceeded\n")); + break; + case 0x82: + DEBUG ((DEBUG_INFO, "Namespace Not Ready\n")); + break; + case 0x83: + DEBUG ((DEBUG_INFO, "Reservation Conflict\n")); + break; + } + break; + + case 0x1: + switch (Cq->Sc) { + case 0x0: + DEBUG ((DEBUG_INFO, "Completion Queue Invalid\n")); + break; + case 0x1: + DEBUG ((DEBUG_INFO, "Invalid Queue Identifier\n")); + break; + case 0x2: + DEBUG ((DEBUG_INFO, "Maximum Queue Size Exceeded\n")); + break; + case 0x3: + DEBUG ((DEBUG_INFO, "Abort Command Limit Exceeded\n")); + break; + case 0x5: + DEBUG ((DEBUG_INFO, "Asynchronous Event Request Limit Exceeded\n= ")); + break; + case 0x6: + DEBUG ((DEBUG_INFO, "Invalid Firmware Slot\n")); + break; + case 0x7: + DEBUG ((DEBUG_INFO, "Invalid Firmware Image\n")); + break; + case 0x8: + DEBUG ((DEBUG_INFO, "Invalid Interrupt Vector\n")); + break; + case 0x9: + DEBUG ((DEBUG_INFO, "Invalid Log Page\n")); + break; + case 0xA: + DEBUG ((DEBUG_INFO, "Invalid Format\n")); + break; + case 0xB: + DEBUG ((DEBUG_INFO, "Firmware Application Requires Conventional = Reset\n")); + break; + case 0xC: + DEBUG ((DEBUG_INFO, "Invalid Queue Deletion\n")); + break; + case 0xD: + DEBUG ((DEBUG_INFO, "Feature Identifier Not Saveable\n")); + break; + case 0xE: + DEBUG ((DEBUG_INFO, "Feature Not Changeable\n")); + break; + case 0xF: + DEBUG ((DEBUG_INFO, "Feature Not Namespace Specific\n")); + break; + case 0x10: + DEBUG ((DEBUG_INFO, "Firmware Application Requires NVM Subsystem= Reset\n")); + break; + case 0x80: + DEBUG ((DEBUG_INFO, "Conflicting Attributes\n")); + break; + case 0x81: + DEBUG ((DEBUG_INFO, "Invalid Protection Information\n")); + break; + case 0x82: + DEBUG ((DEBUG_INFO, "Attempted Write to Read Only Range\n")); + break; + } + break; + + case 0x2: + switch (Cq->Sc) { + case 0x80: + DEBUG ((DEBUG_INFO, "Write Fault\n")); + break; + case 0x81: + DEBUG ((DEBUG_INFO, "Unrecovered Read Error\n")); + break; + case 0x82: + DEBUG ((DEBUG_INFO, "End-to-end Guard Check Error\n")); + break; + case 0x83: + DEBUG ((DEBUG_INFO, "End-to-end Application Tag Check Error\n")); + break; + case 0x84: + DEBUG ((DEBUG_INFO, "End-to-end Reference Tag Check Error\n")); + break; + case 0x85: + DEBUG ((DEBUG_INFO, "Compare Failure\n")); + break; + case 0x86: + DEBUG ((DEBUG_INFO, "Access Denied\n")); + break; + } + break; + + default: + DEBUG ((DEBUG_INFO, "Unknown error\n")); + break; + } + + return EFI_DEVICE_ERROR; +} + +/** + Sends an NVM Express Command Packet to an NVM Express controller or name= space. This function only + supports blocking execution of the command. + + @param[in] Private The pointer to the NVME_CONTEXT Data structure. + @param[in] NamespaceId Is a 32 bit Namespace ID to which the Express = HCI command packet will + be sent. + A Value of 0 denotes the NVM Express controlle= r, a Value of all 0FFh in + the namespace ID specifies that the command pa= cket should be sent to all + valid namespaces. + @param[in,out] Packet A pointer to the EDKII PEI NVM Express PassThr= u Command Packet to send + to the NVMe namespace specified by NamespaceId. + + @retval EFI_SUCCESS The EDKII PEI NVM Express Command Packe= t was sent by the host. + TransferLength bytes were transferred t= o, or from DataBuffer. + @retval EFI_NOT_READY The EDKII PEI NVM Express Command Packe= t could not be sent because + the controller is not ready. The caller= may retry again later. + @retval EFI_DEVICE_ERROR A device error occurred while attemptin= g 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 Packe= t was not sent, so no + additional status information is availa= ble. + @retval EFI_UNSUPPORTED The command described by the EDKII PEI = NVM Express Command Packet + is not supported by the host adapter. + The EDKII PEI NVM Express Command Packe= t was not sent, so no + additional status information is availa= ble. + @retval EFI_TIMEOUT A timeout occurred while waiting for th= e 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 NV= ME_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].Sqt; + Cq =3D Private->CqBuffer[QueueId] + Private->CqHdbl[QueueId].Cqh; + 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 supported + // + ASSERT (Sq->Psdt =3D=3D 0); + if (Sq->Psdt !=3D 0) { + DEBUG ((DEBUG_ERROR, "%a: Does not support SGL mechanism.\n", __FUNCTI= ON__)); + 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 t= he 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 queues= 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_AD= MIN_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 N= ULL)) { + MapLength =3D Packet->MetadataLength; + Status =3D IoMmuMap ( + MapOp, + Packet->MetadataBuffer, + &MapLength, + &PhyAddr, + &MapMeta + ); + if (EFI_ERROR (Status) || (MapLength !=3D Packet->MetadataLength))= { + Status =3D EFI_OUT_OF_RESOURCES; + DEBUG ((DEBUG_ERROR, "%a: Fail to map meta data buffer.\n", __FU= NCTION__)); + goto Exit; + } + Sq->Mptr =3D PhyAddr; + } + } + } + + // + // If the Buffer Size spans more than two memory pages (page Size as def= ined 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", __FU= NCTION__, 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", __FUNC= TION__, 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 abort t= he 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 comm= and + // + 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_COMPL= ETION)); + + // + // 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/M= deModulePkg/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 IMP= LIED. + +**/ + +#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 name= space. This function only + supports blocking execution of the command. + + @param[in] Private The pointer to the NVME_CONTEXT Data structure. + @param[in] NamespaceId Is a 32 bit Namespace ID to which the Express = HCI command packet will + be sent. + A Value of 0 denotes the NVM Express controlle= r, a Value of all 0FFh in + the namespace ID specifies that the command pa= cket should be sent to all + valid namespaces. + @param[in,out] Packet A pointer to the EDKII PEI NVM Express PassThr= u Command Packet to send + to the NVMe namespace specified by NamespaceId. + + @retval EFI_SUCCESS The EDKII PEI NVM Express Command Packe= t was sent by the host. + TransferLength bytes were transferred t= o, or from DataBuffer. + @retval EFI_NOT_READY The EDKII PEI NVM Express Command Packe= t could not be sent because + the controller is not ready. The caller= may retry again later. + @retval EFI_DEVICE_ERROR A device error occurred while attemptin= g 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 Packe= t was not sent, so no + additional status information is availa= ble. + @retval EFI_UNSUPPORTED The command described by the EDKII PEI = NVM Express Command Packet + is not supported by the host adapter. + The EDKII PEI NVM Express Command Packe= t was not sent, so no + additional status information is availa= ble. + @retval EFI_TIMEOUT A timeout occurred while waiting for th= e 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/IncompatiblePciDevi= ceSupportDxe.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 --=20 2.12.0.windows.1 _______________________________________________ edk2-devel mailing list edk2-devel@lists.01.org https://lists.01.org/mailman/listinfo/edk2-devel From nobody Mon Apr 29 02:15:30 2024 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; spf=none (zoho.com: 198.145.21.10 is neither permitted nor denied by domain of lists.01.org) smtp.mailfrom=edk2-devel-bounces@lists.01.org; dmarc=fail(p=none dis=none) header.from=intel.com Return-Path: Received: from ml01.01.org (ml01.01.org [198.145.21.10]) by mx.zohomail.com with SMTPS id 1529046236252843.987240928233; Fri, 15 Jun 2018 00:03:56 -0700 (PDT) Received: from [127.0.0.1] (localhost [IPv6:::1]) by ml01.01.org (Postfix) with ESMTP id 57C8E21102D93; Fri, 15 Jun 2018 00:03:51 -0700 (PDT) Received: from mga17.intel.com (mga17.intel.com [192.55.52.151]) (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 42B21210FCF7A for ; Fri, 15 Jun 2018 00:03:50 -0700 (PDT) Received: from fmsmga008.fm.intel.com ([10.253.24.58]) by fmsmga107.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 15 Jun 2018 00:03:50 -0700 Received: from shwdeopenpsi014.ccr.corp.intel.com ([10.239.9.19]) by fmsmga008.fm.intel.com with ESMTP; 15 Jun 2018 00:03:49 -0700 X-Original-To: edk2-devel@lists.01.org Received-SPF: none (zoho.com: 198.145.21.10 is neither permitted nor denied by domain of lists.01.org) client-ip=198.145.21.10; envelope-from=edk2-devel-bounces@lists.01.org; helo=ml01.01.org; Received-SPF: Pass (sender SPF authorized) identity=mailfrom; client-ip=192.55.52.151; helo=mga17.intel.com; envelope-from=hao.a.wu@intel.com; receiver=edk2-devel@lists.01.org X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.51,226,1526367600"; d="scan'208";a="47932605" From: Hao Wu To: edk2-devel@lists.01.org Date: Fri, 15 Jun 2018 15:03:41 +0800 Message-Id: <20180615070342.13388-4-hao.a.wu@intel.com> X-Mailer: git-send-email 2.12.0.windows.1 In-Reply-To: <20180615070342.13388-1-hao.a.wu@intel.com> References: <20180615070342.13388-1-hao.a.wu@intel.com> Subject: [edk2] [PATCH 3/4] MdeModulePkg: Add GUID for recovery capsule on NVM Express devices 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: , Cc: Hao Wu , Ruiyu Ni , Jiewen Yao , Eric Dong , Star Zeng MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Errors-To: edk2-devel-bounces@lists.01.org Sender: "edk2-devel" X-ZohoMail: RSF_4 Z_629925259 SPT_0 Content-Type: text/plain; charset="utf-8" Cc: Star Zeng Cc: Eric Dong Cc: Ruiyu Ni Cc: Jiewen Yao Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Hao Wu Reviewed-by: Ruiyu Ni Reviewed-by: Star Zeng --- MdeModulePkg/Include/Guid/RecoveryDevice.h | 9 +++++++++ MdeModulePkg/MdeModulePkg.dec | 3 +++ 2 files changed, 12 insertions(+) diff --git a/MdeModulePkg/Include/Guid/RecoveryDevice.h b/MdeModulePkg/Incl= ude/Guid/RecoveryDevice.h index b79c51c4d1..2d2cea68ec 100644 --- a/MdeModulePkg/Include/Guid/RecoveryDevice.h +++ b/MdeModulePkg/Include/Guid/RecoveryDevice.h @@ -52,9 +52,18 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHE= R EXPRESS OR IMPLIED. 0x0ffbce19, 0x324c, 0x4690, {0xa0, 0x09, 0x98, 0xc6, 0xae, 0x2e, 0xb1,= 0x86 } \ } =20 +/// +/// The Global ID used to identify a recovery capsule that was loaded from= NVM Express device. +/// +#define RECOVERY_ON_FAT_NVME_DISK_GUID \ + { \ + 0xc770a27f, 0x956a, 0x497a, {0x85, 0x48, 0xe0, 0x61, 0x97, 0x58, 0x8b,= 0xf6 } \ + } + extern EFI_GUID gRecoveryOnDataCdGuid; extern EFI_GUID gRecoveryOnFatFloppyDiskGuid; extern EFI_GUID gRecoveryOnFatIdeDiskGuid; extern EFI_GUID gRecoveryOnFatUsbDiskGuid; +extern EFI_GUID gRecoveryOnFatNvmeDiskGuid; =20 #endif diff --git a/MdeModulePkg/MdeModulePkg.dec b/MdeModulePkg/MdeModulePkg.dec index 8d7c97ee91..492b67d251 100644 --- a/MdeModulePkg/MdeModulePkg.dec +++ b/MdeModulePkg/MdeModulePkg.dec @@ -300,6 +300,9 @@ ## Include/Guid/RecoveryDevice.h gRecoveryOnDataCdGuid =3D { 0x5CAC0099, 0x0DC9, 0x48E5, { 0= x80, 0x68, 0xBB, 0x95, 0xF5, 0x40, 0x0A, 0x9F }} =20 + ## Include/Guid/RecoveryDevice.h + gRecoveryOnFatNvmeDiskGuid =3D { 0xC770A27F, 0x956A, 0x497A, { 0= x85, 0x48, 0xE0, 0x61, 0x97, 0x58, 0x8B, 0xF6 }} + ## Include/Guid/SmmLockBox.h gEfiSmmLockBoxCommunicationGuid =3D { 0x2a3cfebd, 0x27e8, 0x4d0a, { 0= x8b, 0x79, 0xd6, 0x88, 0xc2, 0xa3, 0xe1, 0xc0 }} =20 --=20 2.12.0.windows.1 _______________________________________________ edk2-devel mailing list edk2-devel@lists.01.org https://lists.01.org/mailman/listinfo/edk2-devel From nobody Mon Apr 29 02:15:30 2024 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; spf=none (zoho.com: 198.145.21.10 is neither permitted nor denied by domain of lists.01.org) smtp.mailfrom=edk2-devel-bounces@lists.01.org; dmarc=fail(p=none dis=none) header.from=intel.com Return-Path: Received: from ml01.01.org (ml01.01.org [198.145.21.10]) by mx.zohomail.com with SMTPS id 1529046239989839.4796561914181; Fri, 15 Jun 2018 00:03:59 -0700 (PDT) Received: from [127.0.0.1] (localhost [IPv6:::1]) by ml01.01.org (Postfix) with ESMTP id 89B3921102D9D; Fri, 15 Jun 2018 00:03:52 -0700 (PDT) Received: from mga17.intel.com (mga17.intel.com [192.55.52.151]) (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 45533210FC391 for ; Fri, 15 Jun 2018 00:03:51 -0700 (PDT) Received: from fmsmga008.fm.intel.com ([10.253.24.58]) by fmsmga107.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 15 Jun 2018 00:03:51 -0700 Received: from shwdeopenpsi014.ccr.corp.intel.com ([10.239.9.19]) by fmsmga008.fm.intel.com with ESMTP; 15 Jun 2018 00:03:50 -0700 X-Original-To: edk2-devel@lists.01.org Received-SPF: none (zoho.com: 198.145.21.10 is neither permitted nor denied by domain of lists.01.org) client-ip=198.145.21.10; envelope-from=edk2-devel-bounces@lists.01.org; helo=ml01.01.org; Received-SPF: Pass (sender SPF authorized) identity=mailfrom; client-ip=192.55.52.151; helo=mga17.intel.com; envelope-from=hao.a.wu@intel.com; receiver=edk2-devel@lists.01.org X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.51,226,1526367600"; d="scan'208";a="47932611" From: Hao Wu To: edk2-devel@lists.01.org Date: Fri, 15 Jun 2018 15:03:42 +0800 Message-Id: <20180615070342.13388-5-hao.a.wu@intel.com> X-Mailer: git-send-email 2.12.0.windows.1 In-Reply-To: <20180615070342.13388-1-hao.a.wu@intel.com> References: <20180615070342.13388-1-hao.a.wu@intel.com> Subject: [edk2] [PATCH 4/4] FatPkg/FatPei: Add the recognition of recovery capsule on NVME device 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: , Cc: Hao Wu , Ruiyu Ni , Jiewen Yao MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Errors-To: edk2-devel-bounces@lists.01.org Sender: "edk2-devel" X-ZohoMail: RSF_4 Z_629925259 SPT_0 Content-Type: text/plain; charset="utf-8" The driver now can recognize the BlockIo2 PPI for NVM Express devices. And support identifying the recovery capsule on those devices. Cc: Ruiyu Ni Cc: Jiewen Yao Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Hao Wu Reviewed-by: Ruiyu Ni --- FatPkg/FatPei/FatLiteApi.c | 6 +++++- FatPkg/FatPei/FatPei.inf | 3 ++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/FatPkg/FatPei/FatLiteApi.c b/FatPkg/FatPei/FatLiteApi.c index e302657132..b455390610 100644 --- a/FatPkg/FatPei/FatLiteApi.c +++ b/FatPkg/FatPei/FatLiteApi.c @@ -1,7 +1,7 @@ /** @file FAT recovery PEIM entry point, Ppi Functions and FAT Api functions. =20 -Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.
+Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
=20 This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this @@ -485,6 +485,10 @@ GetRecoveryCapsuleInfo ( CopyGuid (CapsuleType, &gRecoveryOnFatUsbDiskGuid); break; =20 + case MSG_NVME_NAMESPACE_DP: + CopyGuid (CapsuleType, &gRecoveryOnFatNvmeDiskGuid); + break; + default: break; } diff --git a/FatPkg/FatPei/FatPei.inf b/FatPkg/FatPei/FatPei.inf index 273f72da2f..00b08df2b9 100644 --- a/FatPkg/FatPei/FatPei.inf +++ b/FatPkg/FatPei/FatPei.inf @@ -1,7 +1,7 @@ ## @file # Lite Fat driver only used in Pei Phase. # -# Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.
+# Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
# # This program and the accompanying materials are licensed and made avail= able # under the terms and conditions of the BSD License which accompanies this @@ -58,6 +58,7 @@ gRecoveryOnFatUsbDiskGuid ## SOMETIMES_CONSUMES ## U= NDEFINED gRecoveryOnFatIdeDiskGuid ## SOMETIMES_CONSUMES ## U= NDEFINED gRecoveryOnFatFloppyDiskGuid ## SOMETIMES_CONSUMES ## U= NDEFINED + gRecoveryOnFatNvmeDiskGuid ## SOMETIMES_CONSUMES ## U= NDEFINED =20 =20 [Ppis] --=20 2.12.0.windows.1 _______________________________________________ edk2-devel mailing list edk2-devel@lists.01.org https://lists.01.org/mailman/listinfo/edk2-devel