summaryrefslogtreecommitdiff
path: root/MdeModulePkg
diff options
context:
space:
mode:
authorFeng Tian <feng.tian@intel.com>2013-08-12 06:13:54 +0000
committererictian <erictian@6f19259b-4bc3-4df7-8a09-765794883524>2013-08-12 06:13:54 +0000
commiteb290d0257814701eefd93ee71d46efb6dcc320b (patch)
treee4038d731493637b70ee19fd64228a71a50d67aa /MdeModulePkg
parent17625930ba14ec785dbc221d79967d398593cc32 (diff)
MdeMdeModulePkg/NvmExpressDxe: Add NVM Express support.
Signed-off-by: Feng Tian <feng.tian@intel.com> Reviewed-by: Star Zeng <star.zeng@intel.com> git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@14545 6f19259b-4bc3-4df7-8a09-765794883524
Diffstat (limited to 'MdeModulePkg')
-rw-r--r--MdeModulePkg/Bus/Pci/NvmExpressDxe/ComponentName.c233
-rw-r--r--MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpress.c1057
-rw-r--r--MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpress.h614
-rw-r--r--MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressBlockIo.c559
-rw-r--r--MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressBlockIo.h111
-rw-r--r--MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressDiskInfo.c162
-rw-r--r--MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressDiskInfo.h129
-rw-r--r--MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressDxe.inf68
-rw-r--r--MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressHci.c926
-rw-r--r--MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressHci.h790
-rw-r--r--MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressPassthru.c884
-rw-r--r--MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressPassthru.h299
-rw-r--r--MdeModulePkg/MdeModulePkg.dsc1
13 files changed, 5833 insertions, 0 deletions
diff --git a/MdeModulePkg/Bus/Pci/NvmExpressDxe/ComponentName.c b/MdeModulePkg/Bus/Pci/NvmExpressDxe/ComponentName.c
new file mode 100644
index 0000000000..60849aea03
--- /dev/null
+++ b/MdeModulePkg/Bus/Pci/NvmExpressDxe/ComponentName.c
@@ -0,0 +1,233 @@
+/** @file
+ NvmExpressDxe driver is used to manage non-volatile memory subsystem which follows
+ NVM Express specification.
+
+ Copyright (c) 2013, Intel Corporation. All rights reserved.<BR>
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "NvmExpress.h"
+
+//
+// EFI Component Name Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gNvmExpressComponentName = {
+ NvmExpressComponentNameGetDriverName,
+ NvmExpressComponentNameGetControllerName,
+ "eng"
+};
+
+//
+// EFI Component Name 2 Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gNvmExpressComponentName2 = {
+ (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) NvmExpressComponentNameGetDriverName,
+ (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) NvmExpressComponentNameGetControllerName,
+ "en"
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mNvmExpressDriverNameTable[] = {
+ { "eng;en", L"NVM Express Driver" },
+ { NULL, NULL }
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mNvmExpressControllerNameTable[] = {
+ { "eng;en", L"NVM Express Controller" },
+ { NULL, NULL }
+};
+
+/**
+ Retrieves a Unicode string that is the user readable name of the driver.
+
+ This function retrieves the user readable name of a driver in the form of a
+ Unicode string. If the driver specified by This has a user readable name in
+ the language specified by Language, then a pointer to the driver name is
+ returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
+ by This does not support the language specified by Language,
+ then EFI_UNSUPPORTED is returned.
+
+ @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param Language[in] A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up
+ to the driver writer. Language is specified
+ in RFC 4646 or ISO 639-2 language code format.
+
+ @param DriverName[out] A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ driver specified by This in the language
+ specified by Language.
+
+ @retval EFI_SUCCESS The Unicode string for the Driver specified by
+ This and the language specified by Language was
+ returned in DriverName.
+
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+
+ @retval EFI_INVALID_PARAMETER DriverName is NULL.
+
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+NvmExpressComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ )
+{
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ mNvmExpressDriverNameTable,
+ DriverName,
+ (BOOLEAN)(This == &gNvmExpressComponentName)
+ );
+}
+
+/**
+ Retrieves a Unicode string that is the user readable name of the controller
+ that is being managed by a driver.
+
+ This function retrieves the user readable name of the controller specified by
+ ControllerHandle and ChildHandle in the form of a Unicode string. If the
+ driver specified by This has a user readable name in the language specified by
+ Language, then a pointer to the controller name is returned in ControllerName,
+ and EFI_SUCCESS is returned. If the driver specified by This is not currently
+ managing the controller specified by ControllerHandle and ChildHandle,
+ then EFI_UNSUPPORTED is returned. If the driver specified by This does not
+ support the language specified by Language, then EFI_UNSUPPORTED is returned.
+
+ @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param ControllerHandle[in] The handle of a controller that the driver
+ specified by This is managing. This handle
+ specifies the controller whose name is to be
+ returned.
+
+ @param ChildHandle[in] The handle of the child controller to retrieve
+ the name of. This is an optional parameter that
+ may be NULL. It will be NULL for device
+ drivers. It will also be NULL for a bus drivers
+ that wish to retrieve the name of the bus
+ controller. It will not be NULL for a bus
+ driver that wishes to retrieve the name of a
+ child controller.
+
+ @param Language[in] A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up
+ to the driver writer. Language is specified in
+ RFC 4646 or ISO 639-2 language code format.
+
+ @param ControllerName[out] A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ controller specified by ControllerHandle and
+ ChildHandle in the language specified by
+ Language from the point of view of the driver
+ specified by This.
+
+ @retval EFI_SUCCESS The Unicode string for the user readable name in
+ the language specified by Language for the
+ driver specified by This was returned in
+ DriverName.
+
+ @retval EFI_INVALID_PARAMETER ControllerHandle is NULL.
+
+ @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid
+ EFI_HANDLE.
+
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+
+ @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+
+ @retval EFI_UNSUPPORTED The driver specified by This is not currently
+ managing the controller specified by
+ ControllerHandle and ChildHandle.
+
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+NvmExpressComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ )
+{
+ EFI_STATUS Status;
+ EFI_BLOCK_IO_PROTOCOL *BlockIo;
+ NVME_DEVICE_PRIVATE_DATA *Device;
+ EFI_UNICODE_STRING_TABLE *ControllerNameTable;
+
+ //
+ // Make sure this driver is currently managing ControllHandle
+ //
+ Status = EfiTestManagedDevice (
+ ControllerHandle,
+ gNvmExpressDriverBinding.DriverBindingHandle,
+ &gEfiPciIoProtocolGuid
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ ControllerNameTable = mNvmExpressControllerNameTable;
+ if (ChildHandle != NULL) {
+ Status = EfiTestChildHandle (
+ ControllerHandle,
+ ChildHandle,
+ &gEfiPciIoProtocolGuid
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ //
+ // Get the child context
+ //
+ Status = gBS->OpenProtocol (
+ ChildHandle,
+ &gEfiBlockIoProtocolGuid,
+ (VOID **) &BlockIo,
+ gNvmExpressDriverBinding.DriverBindingHandle,
+ ChildHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_UNSUPPORTED;
+ }
+ Device = NVME_DEVICE_PRIVATE_DATA_FROM_BLOCK_IO (BlockIo);
+ ControllerNameTable = Device->ControllerNameTable;
+ }
+
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ ControllerNameTable,
+ ControllerName,
+ (BOOLEAN)(This == &gNvmExpressComponentName)
+ );
+
+}
diff --git a/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpress.c b/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpress.c
new file mode 100644
index 0000000000..b6729ce711
--- /dev/null
+++ b/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpress.c
@@ -0,0 +1,1057 @@
+/** @file
+ NvmExpressDxe driver is used to manage non-volatile memory subsystem which follows
+ NVM Express specification.
+
+ Copyright (c) 2013, Intel Corporation. All rights reserved.<BR>
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "NvmExpress.h"
+
+//
+// NVM Express Driver Binding Protocol Instance
+//
+EFI_DRIVER_BINDING_PROTOCOL gNvmExpressDriverBinding = {
+ NvmExpressDriverBindingSupported,
+ NvmExpressDriverBindingStart,
+ NvmExpressDriverBindingStop,
+ 0x10,
+ NULL,
+ NULL
+};
+
+//
+// NVM Express EFI Driver Supported EFI Version Protocol Instance
+//
+EFI_DRIVER_SUPPORTED_EFI_VERSION_PROTOCOL gNvmExpressDriverSupportedEfiVersion = {
+ sizeof (EFI_DRIVER_SUPPORTED_EFI_VERSION_PROTOCOL), // Size of Protocol structure.
+ 0 // Version number to be filled at start up.
+};
+
+/**
+ Check if the specified Nvm Express device namespace is active, and create child handles
+ for them with BlockIo and DiskInfo protocol instances.
+
+ @param[in] Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure.
+ @param[in] NamespaceId The NVM Express namespace ID for which a device path node is to be
+ allocated and built. Caller must set the NamespaceId to zero if the
+ device path node will contain a valid UUID.
+ @param[in] NamespaceUuid The NVM Express namespace UUID for which a device path node is to be
+ allocated and built. UUID will only be valid of the Namespace ID is zero.
+
+ @retval EFI_SUCCESS All the namespaces in the device are successfully enumerated.
+ @return Others Some error occurs when enumerating the namespaces.
+
+**/
+EFI_STATUS
+EnumerateNvmeDevNamespace (
+ IN NVME_CONTROLLER_PRIVATE_DATA *Private,
+ UINT32 NamespaceId,
+ UINT64 NamespaceUuid
+ )
+{
+ NVME_ADMIN_NAMESPACE_DATA *NamespaceData;
+ EFI_DEVICE_PATH_PROTOCOL *NewDevicePathNode;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ EFI_HANDLE DeviceHandle;
+ EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath;
+ EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath;
+ NVME_DEVICE_PRIVATE_DATA *Device;
+ EFI_STATUS Status;
+ UINT32 Lbads;
+ UINT32 Flbas;
+ UINT32 LbaFmtIdx;
+
+ NewDevicePathNode = NULL;
+ DevicePath = NULL;
+ Device = NULL;
+
+ //
+ // Allocate a buffer for Identify Namespace data
+ //
+ NamespaceData = AllocateZeroPool(sizeof (NVME_ADMIN_NAMESPACE_DATA));
+ if(NamespaceData == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ ParentDevicePath = Private->ParentDevicePath;
+ //
+ // Identify Namespace
+ //
+ Status = NvmeIdentifyNamespace (
+ Private,
+ NamespaceId,
+ (VOID *)NamespaceData
+ );
+ if (EFI_ERROR(Status)) {
+ goto Exit;
+ }
+ //
+ // Validate Namespace
+ //
+ if (NamespaceData->Ncap == 0) {
+ Status = EFI_DEVICE_ERROR;
+ } else {
+ //
+ // allocate device private data for each discovered namespace
+ //
+ Device = AllocateZeroPool(sizeof(NVME_DEVICE_PRIVATE_DATA));
+ if (Device == NULL) {
+ goto Exit;
+ }
+
+ //
+ // Initialize SSD namespace instance data
+ //
+ Device->Signature = NVME_DEVICE_PRIVATE_DATA_SIGNATURE;
+ Device->NamespaceId = NamespaceId;
+ Device->NamespaceUuid = NamespaceData->Eui64;
+
+ Device->ControllerHandle = Private->ControllerHandle;
+ Device->DriverBindingHandle = Private->DriverBindingHandle;
+ Device->Controller = Private;
+
+ //
+ // Build BlockIo media structure
+ //
+ Device->Media.MediaId = 0;
+ Device->Media.RemovableMedia = FALSE;
+ Device->Media.MediaPresent = TRUE;
+ Device->Media.LogicalPartition = FALSE;
+ Device->Media.ReadOnly = FALSE;
+ Device->Media.WriteCaching = FALSE;
+
+ Flbas = NamespaceData->Flbas;
+ LbaFmtIdx = Flbas & 3;
+ Lbads = NamespaceData->LbaFormat[LbaFmtIdx].Lbads;
+ Device->Media.BlockSize = (UINT32)1 << Lbads;
+
+ Device->Media.LastBlock = NamespaceData->Nsze - 1;
+ Device->Media.LogicalBlocksPerPhysicalBlock = 1;
+ Device->Media.LowestAlignedLba = 1;
+
+ //
+ // Create BlockIo Protocol instance
+ //
+ Device->BlockIo.Revision = EFI_BLOCK_IO_PROTOCOL_REVISION2;
+ Device->BlockIo.Media = &Device->Media;
+ Device->BlockIo.Reset = NvmeBlockIoReset;
+ Device->BlockIo.ReadBlocks = NvmeBlockIoReadBlocks;
+ Device->BlockIo.WriteBlocks = NvmeBlockIoWriteBlocks;
+ Device->BlockIo.FlushBlocks = NvmeBlockIoFlushBlocks;
+
+ //
+ // Create DiskInfo Protocol instance
+ //
+ InitializeDiskInfo (Device);
+
+ //
+ // Create a Nvm Express Namespace Device Path Node
+ //
+ Status = Private->Passthru.BuildDevicePath (
+ &Private->Passthru,
+ Device->NamespaceId,
+ Device->NamespaceUuid,
+ &NewDevicePathNode
+ );
+
+ if (EFI_ERROR(Status)) {
+ goto Exit;
+ }
+
+ //
+ // Append the SSD node to the controller's device path
+ //
+ DevicePath = AppendDevicePathNode (ParentDevicePath, NewDevicePathNode);
+ if (DevicePath == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Exit;
+ }
+
+ DeviceHandle = NULL;
+ RemainingDevicePath = DevicePath;
+ Status = gBS->LocateDevicePath (&gEfiDevicePathProtocolGuid, &RemainingDevicePath, &DeviceHandle);
+ if (!EFI_ERROR (Status) && (DeviceHandle != NULL) && IsDevicePathEnd(RemainingDevicePath)) {
+ Status = EFI_ALREADY_STARTED;
+ FreePool (DevicePath);
+ goto Exit;
+ }
+
+ Device->DevicePath = DevicePath;
+
+ //
+ // Make sure the handle is NULL so we create a new handle
+ //
+ Device->DeviceHandle = NULL;
+
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &Device->DeviceHandle,
+ &gEfiDevicePathProtocolGuid,
+ Device->DevicePath,
+ &gEfiBlockIoProtocolGuid,
+ &Device->BlockIo,
+ &gEfiDiskInfoProtocolGuid,
+ &Device->DiskInfo,
+ NULL
+ );
+
+ if(EFI_ERROR(Status)) {
+ goto Exit;
+ }
+ gBS->OpenProtocol (
+ Private->ControllerHandle,
+ &gEfiPciIoProtocolGuid,
+ (VOID **) &Private->PciIo,
+ Private->DriverBindingHandle,
+ Device->DeviceHandle,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+ );
+
+ //
+ // Build controller name for Component Name (2) protocol.
+ //
+ UnicodeSPrintAsciiFormat (Device->ModelName, sizeof (Device->ModelName), "%a-%a-%x", Private->ControllerData->Sn, Private->ControllerData->Mn, NamespaceData->Eui64);
+
+ AddUnicodeString2 (
+ "eng",
+ gNvmExpressComponentName.SupportedLanguages,
+ &Device->ControllerNameTable,
+ Device->ModelName,
+ TRUE
+ );
+
+ AddUnicodeString2 (
+ "en",
+ gNvmExpressComponentName2.SupportedLanguages,
+ &Device->ControllerNameTable,
+ Device->ModelName,
+ FALSE
+ );
+ }
+
+Exit:
+ if(NamespaceData != NULL) {
+ FreePool (NamespaceData);
+ }
+
+ if(EFI_ERROR(Status) && (Device != NULL) && (Device->DevicePath != NULL)) {
+ FreePool (Device->DevicePath);
+ }
+ if(EFI_ERROR(Status) && (Device != NULL)) {
+ FreePool (Device);
+ }
+ return Status;
+}
+
+/**
+ Discover all Nvm Express device namespaces, and create child handles for them with BlockIo
+ and DiskInfo protocol instances.
+
+ @param[in] Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure.
+
+ @retval EFI_SUCCESS All the namespaces in the device are successfully enumerated.
+ @return Others Some error occurs when enumerating the namespaces.
+
+**/
+EFI_STATUS
+DiscoverAllNamespaces (
+ IN NVME_CONTROLLER_PRIVATE_DATA *Private
+ )
+{
+ EFI_STATUS Status;
+ UINT32 NamespaceId;
+ UINT64 NamespaceUuid;
+ NVM_EXPRESS_PASS_THRU_PROTOCOL *Passthru;
+
+ NamespaceId = 0xFFFFFFFF;
+ NamespaceUuid = 0;
+ Passthru = &Private->Passthru;
+
+ while (TRUE) {
+ Status = Passthru->GetNextNamespace (
+ Passthru,
+ (UINT32 *)&NamespaceId,
+ (UINT64 *)&NamespaceUuid
+ );
+
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+
+ Status = EnumerateNvmeDevNamespace (
+ Private,
+ NamespaceId,
+ NamespaceUuid
+ );
+
+ if (EFI_ERROR(Status)) {
+ continue;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Unregisters a Nvm Express device namespace.
+
+ This function removes the protocols installed on the controller handle and
+ frees the resources allocated for the namespace.
+
+ @param This The pointer to EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param Controller The controller handle of the namespace.
+ @param Handle The child handle.
+
+ @retval EFI_SUCCESS The namespace is successfully unregistered.
+ @return Others Some error occurs when unregistering the namespace.
+
+**/
+EFI_STATUS
+UnregisterNvmeNamespace (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_HANDLE Handle
+ )
+{
+ EFI_STATUS Status;
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ EFI_BLOCK_IO_PROTOCOL *BlockIo;
+ NVME_DEVICE_PRIVATE_DATA *Device;
+
+ BlockIo = NULL;
+
+ Status = gBS->OpenProtocol (
+ Handle,
+ &gEfiBlockIoProtocolGuid,
+ (VOID **) &BlockIo,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Device = NVME_DEVICE_PRIVATE_DATA_FROM_BLOCK_IO (BlockIo);
+
+ //
+ // Close the child handle
+ //
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiPciIoProtocolGuid,
+ This->DriverBindingHandle,
+ Handle
+ );
+
+ //
+ // The Nvm Express driver installs the BlockIo and DiskInfo in the DriverBindingStart().
+ // Here should uninstall both of them.
+ //
+ Status = gBS->UninstallMultipleProtocolInterfaces (
+ Handle,
+ &gEfiDevicePathProtocolGuid,
+ Device->DevicePath,
+ &gEfiBlockIoProtocolGuid,
+ &Device->BlockIo,
+ &gEfiDiskInfoProtocolGuid,
+ &Device->DiskInfo,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ gBS->OpenProtocol (
+ Controller,
+ &gEfiPciIoProtocolGuid,
+ (VOID **) &PciIo,
+ This->DriverBindingHandle,
+ Handle,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+ );
+ return Status;
+ }
+
+ if(Device->DevicePath != NULL) {
+ FreePool (Device->DevicePath);
+ }
+
+ if (Device->ControllerNameTable != NULL) {
+ FreeUnicodeStringTable (Device->ControllerNameTable);
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Tests to see if this driver supports a given controller. If a child device is provided,
+ it further tests to see if this driver supports creating a handle for the specified child device.
+
+ This function checks to see if the driver specified by This supports the device specified by
+ ControllerHandle. Drivers will typically use the device path attached to
+ ControllerHandle and/or the services from the bus I/O abstraction attached to
+ ControllerHandle to determine if the driver supports ControllerHandle. This function
+ may be called many times during platform initialization. In order to reduce boot times, the tests
+ performed by this function must be very small, and take as little time as possible to execute. This
+ function must not change the state of any hardware devices, and this function must be aware that the
+ device specified by ControllerHandle may already be managed by the same driver or a
+ different driver. This function must match its calls to AllocatePages() with FreePages(),
+ AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol().
+ Since ControllerHandle may have been previously started by the same driver, if a protocol is
+ already in the opened state, then it must not be closed with CloseProtocol(). This is required
+ to guarantee the state of ControllerHandle is not modified by this function.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param[in] ControllerHandle The handle of the controller to test. This handle
+ must support a protocol interface that supplies
+ an I/O abstraction to the driver.
+ @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This
+ parameter is ignored by device drivers, and is optional for bus
+ drivers. For bus drivers, if this parameter is not NULL, then
+ the bus driver must determine if the bus controller specified
+ by ControllerHandle and the child controller specified
+ by RemainingDevicePath are both supported by this
+ bus driver.
+
+ @retval EFI_SUCCESS The device specified by ControllerHandle and
+ RemainingDevicePath is supported by the driver specified by This.
+ @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and
+ RemainingDevicePath is already being managed by the driver
+ specified by This.
+ @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and
+ RemainingDevicePath is already being managed by a different
+ driver or an application that requires exclusive access.
+ Currently not implemented.
+ @retval EFI_UNSUPPORTED The device specified by ControllerHandle and
+ RemainingDevicePath is not supported by the driver specified by This.
+**/
+EFI_STATUS
+EFIAPI
+NvmExpressDriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ )
+{
+ EFI_STATUS Status;
+ EFI_DEV_PATH_PTR DevicePathNode;
+ EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath;
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ UINT8 ClassCode[3];
+
+ //
+ // Check whether device path is valid
+ //
+ if (RemainingDevicePath != NULL) {
+ //
+ // Check if RemainingDevicePath is the End of Device Path Node,
+ // if yes, go on checking other conditions
+ //
+ if (!IsDevicePathEnd (RemainingDevicePath)) {
+ //
+ // If RemainingDevicePath isn't the End of Device Path Node,
+ // check its validation
+ //
+ DevicePathNode.DevPath = RemainingDevicePath;
+
+ if ((DevicePathNode.DevPath->Type != MESSAGING_DEVICE_PATH) ||
+ (DevicePathNode.DevPath->SubType != MSG_NVME_NAMESPACE_DP) ||
+ DevicePathNodeLength(DevicePathNode.DevPath) != sizeof(NVME_NAMESPACE_DEVICE_PATH)) {
+ return EFI_UNSUPPORTED;
+ }
+ }
+ }
+
+ //
+ // Open the EFI Device Path protocol needed to perform the supported test
+ //
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiDevicePathProtocolGuid,
+ (VOID **) &ParentDevicePath,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (Status == EFI_ALREADY_STARTED) {
+ return EFI_SUCCESS;
+ }
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Close protocol, don't use device path protocol in the Support() function
+ //
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiDevicePathProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+
+ //
+ // Attempt to Open PCI I/O Protocol
+ //
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiPciIoProtocolGuid,
+ (VOID **) &PciIo,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (Status == EFI_ALREADY_STARTED) {
+ return EFI_SUCCESS;
+ }
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Now further check the PCI header: Base class (offset 0x0B) and Sub Class (offset 0x0A).
+ // This controller should be a Nvm Express controller.
+ //
+ Status = PciIo->Pci.Read (
+ PciIo,
+ EfiPciIoWidthUint8,
+ PCI_CLASSCODE_OFFSET,
+ sizeof (ClassCode),
+ ClassCode
+ );
+ if (EFI_ERROR (Status)) {
+ goto Done;
+ }
+
+ //
+ // Examine Nvm Express controller PCI Configuration table fields
+ //
+ if ((ClassCode[0] != PCI_IF_NVMHCI) || (ClassCode[1] != PCI_CLASS_MASS_STORAGE_NVM) || (ClassCode[2] != PCI_CLASS_MASS_STORAGE)) {
+ Status = EFI_UNSUPPORTED;
+ }
+
+Done:
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiPciIoProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+
+ return Status;
+}
+
+
+/**
+ Starts a device controller or a bus controller.
+
+ The Start() function is designed to be invoked from the EFI boot service ConnectController().
+ As a result, much of the error checking on the parameters to Start() has been moved into this
+ common boot service. It is legal to call Start() from other locations,
+ but the following calling restrictions must be followed or the system behavior will not be deterministic.
+ 1. ControllerHandle must be a valid EFI_HANDLE.
+ 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned
+ EFI_DEVICE_PATH_PROTOCOL.
+ 3. Prior to calling Start(), the Supported() function for the driver specified by This must
+ have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param[in] ControllerHandle The handle of the controller to start. This handle
+ must support a protocol interface that supplies
+ an I/O abstraction to the driver.
+ @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This
+ parameter is ignored by device drivers, and is optional for bus
+ drivers. For a bus driver, if this parameter is NULL, then handles
+ for all the children of Controller are created by this driver.
+ If this parameter is not NULL and the first Device Path Node is
+ not the End of Device Path Node, then only the handle for the
+ child device specified by the first Device Path Node of
+ RemainingDevicePath is created by this driver.
+ If the first Device Path Node of RemainingDevicePath is
+ the End of Device Path Node, no child handle is created by this
+ driver.
+
+ @retval EFI_SUCCESS The device was started.
+ @retval EFI_DEVICE_ERROR The device could not be started due to a device error.Currently not implemented.
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.
+ @retval Others The driver failded to start the device.
+
+**/
+EFI_STATUS
+EFIAPI
+NvmExpressDriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ )
+{
+ EFI_STATUS Status;
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ NVME_CONTROLLER_PRIVATE_DATA *Private;
+ EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath;
+ UINT32 NamespaceId;
+ UINT64 NamespaceUuid;
+ EFI_PHYSICAL_ADDRESS MappedAddr;
+ UINTN Bytes;
+
+ DEBUG ((EFI_D_INFO, "NvmExpressDriverBindingStart: start\n"));
+
+ Private = NULL;
+ ParentDevicePath = NULL;
+
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiDevicePathProtocolGuid,
+ (VOID **) &ParentDevicePath,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if ((EFI_ERROR (Status)) && (Status != EFI_ALREADY_STARTED)) {
+ return Status;
+ }
+
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiPciIoProtocolGuid,
+ (VOID **) &PciIo,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+
+ if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {
+ return Status;
+ }
+
+ //
+ // Check EFI_ALREADY_STARTED to reuse the original NVME_CONTROLLER_PRIVATE_DATA.
+ //
+ if (Status != EFI_ALREADY_STARTED) {
+ Private = AllocateZeroPool (sizeof (NVME_CONTROLLER_PRIVATE_DATA));
+
+ if (Private == NULL) {
+ DEBUG ((EFI_D_ERROR, "NvmExpressDriverBindingStart: allocating pool for Nvme Private Data failed!\n"));
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Exit2;
+ }
+
+ //
+ // 4 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 #1.
+ // 4th 4kB boundary is the start of I/O completion queue #1.
+ //
+ // Allocate 4 pages of memory, then map it for bus master read and write.
+ //
+ Status = PciIo->AllocateBuffer (
+ PciIo,
+ AllocateAnyPages,
+ EfiBootServicesData,
+ 6,
+ (VOID**)&Private->Buffer,
+ 0
+ );
+ if (EFI_ERROR (Status)) {
+ goto Exit2;
+ }
+
+ Bytes = EFI_PAGES_TO_SIZE (4);
+ Status = PciIo->Map (
+ PciIo,
+ EfiPciIoOperationBusMasterCommonBuffer,
+ Private->Buffer,
+ &Bytes,
+ &MappedAddr,
+ &Private->Mapping
+ );
+
+ if (EFI_ERROR (Status) || (Bytes != EFI_PAGES_TO_SIZE (4))) {
+ goto Exit2;
+ }
+
+ Private->BufferPciAddr = (UINT8 *)(UINTN)MappedAddr;
+ ZeroMem (Private->Buffer, EFI_PAGES_TO_SIZE (4));
+
+ Private->Signature = NVME_CONTROLLER_PRIVATE_DATA_SIGNATURE;
+ Private->ControllerHandle = Controller;
+ Private->ImageHandle = This->DriverBindingHandle;
+ Private->DriverBindingHandle = This->DriverBindingHandle;
+ Private->PciIo = PciIo;
+ Private->ParentDevicePath = ParentDevicePath;
+ Private->Passthru.Mode = &Private->PassThruMode;
+ Private->Passthru.PassThru = NvmExpressPassThru;
+ Private->Passthru.GetNextNamespace = NvmExpressGetNextNamespace;
+ Private->Passthru.BuildDevicePath = NvmExpressBuildDevicePath;
+ Private->Passthru.GetNamespace = NvmExpressGetNamespace;
+ Private->PassThruMode.Attributes = NVM_EXPRESS_PASS_THRU_ATTRIBUTES_PHYSICAL;
+
+ Status = NvmeControllerInit (Private);
+
+ if (EFI_ERROR(Status)) {
+ goto Exit2;
+ }
+
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &Controller,
+ &gEfiCallerIdGuid,
+ Private,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ goto Exit2;
+ }
+ } else {
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiCallerIdGuid,
+ (VOID **) &Private,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ Private = NULL;
+ goto Exit1;
+ }
+ }
+
+ if (RemainingDevicePath == NULL) {
+ //
+ // Enumerate all NVME namespaces in the controller
+ //
+ Status = DiscoverAllNamespaces (
+ Private
+ );
+
+ } else if (!IsDevicePathEnd (RemainingDevicePath)) {
+ //
+ // Enumerate the specified NVME namespace
+ //
+ Status = Private->Passthru.GetNamespace (
+ &Private->Passthru,
+ RemainingDevicePath,
+ &NamespaceId,
+ &NamespaceUuid
+ );
+
+ if (!EFI_ERROR (Status)) {
+ Status = EnumerateNvmeDevNamespace (
+ Private,
+ NamespaceId,
+ NamespaceUuid
+ );
+ }
+ }
+
+ DEBUG ((EFI_D_INFO, "NvmExpressDriverBindingStart: end successfully\n"));
+ return EFI_SUCCESS;
+
+Exit1:
+ gBS->UninstallMultipleProtocolInterfaces (
+ Controller,
+ &gEfiCallerIdGuid,
+ Private,
+ NULL
+ );
+Exit2:
+ if ((Private != NULL) && (Private->Mapping != NULL)) {
+ PciIo->Unmap (PciIo, Private->Mapping);
+ }
+
+ if ((Private != NULL) && (Private->Buffer != NULL)) {
+ PciIo->FreeBuffer (PciIo, 4, Private->Buffer);
+ }
+
+ if (Private != NULL) {
+ FreePool (Private);
+ }
+
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiPciIoProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiDevicePathProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+
+ DEBUG ((EFI_D_INFO, "NvmExpressDriverBindingStart: end with %r\n", Status));
+
+ return Status;
+}
+
+
+/**
+ Stops a device controller or a bus controller.
+
+ The Stop() function is designed to be invoked from the EFI boot service DisconnectController().
+ As a result, much of the error checking on the parameters to Stop() has been moved
+ into this common boot service. It is legal to call Stop() from other locations,
+ but the following calling restrictions must be followed or the system behavior will not be deterministic.
+ 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this
+ same driver's Start() function.
+ 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid
+ EFI_HANDLE. In addition, all of these handles must have been created in this driver's
+ Start() function, and the Start() function must have called OpenProtocol() on
+ ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param[in] ControllerHandle A handle to the device being stopped. The handle must
+ support a bus specific I/O protocol for the driver
+ to use to stop the device.
+ @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer.
+ @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL
+ if NumberOfChildren is 0.
+
+ @retval EFI_SUCCESS The device was stopped.
+ @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error.
+
+**/
+EFI_STATUS
+EFIAPI
+NvmExpressDriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ )
+{
+ EFI_STATUS Status;
+ BOOLEAN AllChildrenStopped;
+ UINTN Index;
+ NVME_CONTROLLER_PRIVATE_DATA *Private;
+
+ if (NumberOfChildren == 0) {
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiCallerIdGuid,
+ (VOID **) &Private,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (!EFI_ERROR (Status)) {
+ gBS->UninstallMultipleProtocolInterfaces (
+ Controller,
+ &gEfiCallerIdGuid,
+ Private,
+ NULL
+ );
+
+ if (Private->Mapping != NULL) {
+ Private->PciIo->Unmap (Private->PciIo, Private->Mapping);
+ }
+
+ if (Private->Buffer != NULL) {
+ Private->PciIo->FreeBuffer (Private->PciIo, 4, Private->Buffer);
+ }
+
+ FreePool (Private->ControllerData);
+ FreePool (Private);
+ }
+
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiPciIoProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiDevicePathProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+ return EFI_SUCCESS;
+ }
+
+ AllChildrenStopped = TRUE;
+
+ for (Index = 0; Index < NumberOfChildren; Index++) {
+ Status = UnregisterNvmeNamespace (This, Controller, ChildHandleBuffer[Index]);
+ if (EFI_ERROR (Status)) {
+ AllChildrenStopped = FALSE;
+ }
+ }
+
+ if (!AllChildrenStopped) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ This is the unload handle for the NVM Express driver.
+
+ Disconnect the driver specified by ImageHandle from the NVMe device in the handle database.
+ Uninstall all the protocols installed in the driver.
+
+ @param[in] ImageHandle The drivers' driver image.
+
+ @retval EFI_SUCCESS The image is unloaded.
+ @retval Others Failed to unload the image.
+
+**/
+EFI_STATUS
+EFIAPI
+NvmExpressUnload (
+ IN EFI_HANDLE ImageHandle
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE *DeviceHandleBuffer;
+ UINTN DeviceHandleCount;
+ UINTN Index;
+ EFI_DRIVER_BINDING_PROTOCOL *DriverBinding;
+ EFI_COMPONENT_NAME_PROTOCOL *ComponentName;
+ EFI_COMPONENT_NAME2_PROTOCOL *ComponentName2;
+
+ //
+ // Get the list of all the handles in the handle database.
+ // If there is an error getting the list, then the unload
+ // operation fails.
+ //
+ Status = gBS->LocateHandleBuffer (
+ AllHandles,
+ NULL,
+ NULL,
+ &DeviceHandleCount,
+ &DeviceHandleBuffer
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Disconnect the driver specified by ImageHandle from all
+ // the devices in the handle database.
+ //
+ for (Index = 0; Index < DeviceHandleCount; Index++) {
+ Status = gBS->DisconnectController (
+ DeviceHandleBuffer[Index],
+ ImageHandle,
+ NULL
+ );
+ }
+
+ //
+ // Uninstall all the protocols installed in the driver entry point
+ //
+ for (Index = 0; Index < DeviceHandleCount; Index++) {
+ Status = gBS->HandleProtocol (
+ DeviceHandleBuffer[Index],
+ &gEfiDriverBindingProtocolGuid,
+ (VOID **) &DriverBinding
+ );
+
+ if (EFI_ERROR (Status)) {
+ continue;
+ }
+
+ if (DriverBinding->ImageHandle != ImageHandle) {
+ continue;
+ }
+
+ gBS->UninstallProtocolInterface (
+ ImageHandle,
+ &gEfiDriverBindingProtocolGuid,
+ DriverBinding
+ );
+
+ Status = gBS->HandleProtocol (
+ DeviceHandleBuffer[Index],
+ &gEfiComponentNameProtocolGuid,
+ (VOID **) &ComponentName
+ );
+ if (!EFI_ERROR (Status)) {
+ gBS->UninstallProtocolInterface (
+ ImageHandle,
+ &gEfiComponentNameProtocolGuid,
+ ComponentName
+ );
+ }
+
+ Status = gBS->HandleProtocol (
+ DeviceHandleBuffer[Index],
+ &gEfiComponentName2ProtocolGuid,
+ (VOID **) &ComponentName2
+ );
+ if (!EFI_ERROR (Status)) {
+ gBS->UninstallProtocolInterface (
+ ImageHandle,
+ &gEfiComponentName2ProtocolGuid,
+ ComponentName2
+ );
+ }
+ }
+
+ //
+ // Free the buffer containing the list of handles from the handle database
+ //
+ if (DeviceHandleBuffer != NULL) {
+ gBS->FreePool (DeviceHandleBuffer);
+ }
+ return EFI_SUCCESS;
+}
+
+/**
+ The entry point for Nvm Express driver, used to install Nvm Express driver on the ImageHandle.
+
+ @param ImageHandle The firmware allocated handle for this driver image.
+ @param SystemTable Pointer to the EFI system table.
+
+ @retval EFI_SUCCESS Driver loaded.
+ @retval other Driver not loaded.
+
+**/
+EFI_STATUS
+EFIAPI
+NvmExpressDriverEntry (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+
+ Status = EfiLibInstallDriverBindingComponentName2 (
+ ImageHandle,
+ SystemTable,
+ &gNvmExpressDriverBinding,
+ ImageHandle,
+ &gNvmExpressComponentName,
+ &gNvmExpressComponentName2
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ //
+ // Install EFI Driver Supported EFI Version Protocol required for
+ // EFI drivers that are on PCI and other plug in cards.
+ //
+ gNvmExpressDriverSupportedEfiVersion.FirmwareVersion = 0x00020028;
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &ImageHandle,
+ &gEfiDriverSupportedEfiVersionProtocolGuid,
+ &gNvmExpressDriverSupportedEfiVersion,
+ NULL
+ );
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+}
diff --git a/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpress.h b/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpress.h
new file mode 100644
index 0000000000..d39b62e102
--- /dev/null
+++ b/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpress.h
@@ -0,0 +1,614 @@
+/** @file
+ NvmExpressDxe driver is used to manage non-volatile memory subsystem which follows
+ NVM Express specification.
+
+ Copyright (c) 2013, Intel Corporation. All rights reserved.<BR>
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _EFI_NVM_EXPRESS_H_
+#define _EFI_NVM_EXPRESS_H_
+
+#include <Uefi.h>
+
+#include <IndustryStandard/Pci.h>
+
+#include <Protocol/ComponentName.h>
+#include <Protocol/ComponentName2.h>
+#include <Protocol/DriverBinding.h>
+#include <Protocol/LoadedImage.h>
+#include <Protocol/DevicePath.h>
+#include <Protocol/PciIo.h>
+#include <Protocol/BlockIo.h>
+#include <Protocol/DiskInfo.h>
+#include <Protocol/DriverSupportedEfiVersion.h>
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/PrintLib.h>
+#include <Library/UefiLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+
+typedef struct _NVME_CONTROLLER_PRIVATE_DATA NVME_CONTROLLER_PRIVATE_DATA;
+typedef struct _NVME_DEVICE_PRIVATE_DATA NVME_DEVICE_PRIVATE_DATA;
+
+#include "NvmExpressPassthru.h"
+#include "NvmExpressBlockIo.h"
+#include "NvmExpressDiskInfo.h"
+#include "NvmExpressHci.h"
+
+extern EFI_DRIVER_BINDING_PROTOCOL gNvmExpressDriverBinding;
+extern EFI_COMPONENT_NAME_PROTOCOL gNvmExpressComponentName;
+extern EFI_COMPONENT_NAME2_PROTOCOL gNvmExpressComponentName2;
+extern EFI_DRIVER_SUPPORTED_EFI_VERSION_PROTOCOL gNvmExpressDriverSupportedEfiVersion;
+
+#define PCI_CLASS_MASS_STORAGE_NVM 0x08 // mass storage sub-class non-volatile memory.
+#define PCI_IF_NVMHCI 0x02 // mass storage programming interface NVMHCI.
+
+#define NVME_ASQ_SIZE 2 // Number of admin submission queue entries
+#define NVME_ACQ_SIZE 2 // Number of admin completion queue entries
+
+#define NVME_CSQ_SIZE 2 // Number of I/O submission queue entries
+#define NVME_CCQ_SIZE 2 // Number of I/O completion queue entries
+
+#define NVME_MAX_IO_QUEUES 2 // Number of I/O queues supported by the driver
+
+#define NVME_CONTROLLER_ID 0
+
+//
+// Time out value for Nvme transaction execution
+//
+#define NVME_GENERIC_TIMEOUT EFI_TIMER_PERIOD_SECONDS (5)
+
+//
+// Unique signature for private data structure.
+//
+#define NVME_CONTROLLER_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('N','V','M','E')
+
+//
+// Nvme private data structure.
+//
+struct _NVME_CONTROLLER_PRIVATE_DATA {
+ UINT32 Signature;
+
+ EFI_HANDLE ControllerHandle;
+ EFI_HANDLE ImageHandle;
+ EFI_HANDLE DriverBindingHandle;
+
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ UINT64 PciAttributes;
+
+ EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath;
+
+ NVM_EXPRESS_PASS_THRU_MODE PassThruMode;
+ NVM_EXPRESS_PASS_THRU_PROTOCOL Passthru;
+
+ //
+ // pointer to identify controller data
+ //
+ NVME_ADMIN_CONTROLLER_DATA *ControllerData;
+
+ //
+ // 6 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 submission queue #1.
+ // 3rd 4kB boundary is the start of the admin completion queue.
+ // 4th 4kB boundary is the start of completion queue #1.
+ // 5th 4kB boundary is the start of the first PRP list page.
+ // 6th 4kB boundary is the start of the second PRP list page.
+ //
+ UINT8 *Buffer;
+ UINT8 *BufferPciAddr;
+
+ //
+ // Pointers to 4kB aligned submission & completion queues.
+ //
+ NVME_SQ *SqBuffer[NVME_MAX_IO_QUEUES];
+ NVME_CQ *CqBuffer[NVME_MAX_IO_QUEUES];
+ NVME_SQ *SqBufferPciAddr[NVME_MAX_IO_QUEUES];
+ NVME_CQ *CqBufferPciAddr[NVME_MAX_IO_QUEUES];
+
+ //
+ // Submission and completion queue indices.
+ //
+ NVME_SQTDBL SqTdbl[NVME_MAX_IO_QUEUES];
+ NVME_CQHDBL CqHdbl[NVME_MAX_IO_QUEUES];
+
+ UINT8 Pt[2];
+ UINT16 Cid[2];
+
+ //
+ // Nvme controller capabilities
+ //
+ NVME_CAP Cap;
+
+ VOID *Mapping;
+};
+
+#define NVME_CONTROLLER_PRIVATE_DATA_FROM_PASS_THRU(a) \
+ CR (a, \
+ NVME_CONTROLLER_PRIVATE_DATA, \
+ Passthru, \
+ NVME_CONTROLLER_PRIVATE_DATA_SIGNATURE \
+ )
+
+//
+// Unique signature for private data structure.
+//
+#define NVME_DEVICE_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('X','S','S','D')
+
+//
+// Nvme device private data structure
+//
+struct _NVME_DEVICE_PRIVATE_DATA {
+ UINT32 Signature;
+
+ EFI_HANDLE DeviceHandle;
+ EFI_HANDLE ControllerHandle;
+ EFI_HANDLE DriverBindingHandle;
+
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+
+ EFI_UNICODE_STRING_TABLE *ControllerNameTable;
+
+ UINT32 NamespaceId;
+ UINT64 NamespaceUuid;
+
+ EFI_BLOCK_IO_MEDIA Media;
+ EFI_BLOCK_IO_PROTOCOL BlockIo;
+ EFI_DISK_INFO_PROTOCOL DiskInfo;
+
+ EFI_LBA NumBlocks;
+
+ CHAR16 ModelName[80];
+ NVME_ADMIN_NAMESPACE_DATA NamespaceData;
+
+ NVME_CONTROLLER_PRIVATE_DATA *Controller;
+
+};
+
+//
+// Statments to retrieve the private data from produced protocols.
+//
+#define NVME_DEVICE_PRIVATE_DATA_FROM_BLOCK_IO(a) \
+ CR (a, \
+ NVME_DEVICE_PRIVATE_DATA, \
+ BlockIo, \
+ NVME_DEVICE_PRIVATE_DATA_SIGNATURE \
+ )
+
+#define NVME_DEVICE_PRIVATE_DATA_FROM_DISK_INFO(a) \
+ CR (a, \
+ NVME_DEVICE_PRIVATE_DATA, \
+ DiskInfo, \
+ NVME_DEVICE_PRIVATE_DATA_SIGNATURE \
+ )
+
+/**
+ Retrieves a Unicode string that is the user readable name of the driver.
+
+ This function retrieves the user readable name of a driver in the form of a
+ Unicode string. If the driver specified by This has a user readable name in
+ the language specified by Language, then a pointer to the driver name is
+ returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
+ by This does not support the language specified by Language,
+ then EFI_UNSUPPORTED is returned.
+
+ @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param Language[in] A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up
+ to the driver writer. Language is specified
+ in RFC 4646 or ISO 639-2 language code format.
+
+ @param DriverName[out] A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ driver specified by This in the language
+ specified by Language.
+
+ @retval EFI_SUCCESS The Unicode string for the Driver specified by
+ This and the language specified by Language was
+ returned in DriverName.
+
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+
+ @retval EFI_INVALID_PARAMETER DriverName is NULL.
+
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+NvmExpressComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ );
+
+/**
+ Retrieves a Unicode string that is the user readable name of the controller
+ that is being managed by a driver.
+
+ This function retrieves the user readable name of the controller specified by
+ ControllerHandle and ChildHandle in the form of a Unicode string. If the
+ driver specified by This has a user readable name in the language specified by
+ Language, then a pointer to the controller name is returned in ControllerName,
+ and EFI_SUCCESS is returned. If the driver specified by This is not currently
+ managing the controller specified by ControllerHandle and ChildHandle,
+ then EFI_UNSUPPORTED is returned. If the driver specified by This does not
+ support the language specified by Language, then EFI_UNSUPPORTED is returned.
+
+ @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param ControllerHandle[in] The handle of a controller that the driver
+ specified by This is managing. This handle
+ specifies the controller whose name is to be
+ returned.
+
+ @param ChildHandle[in] The handle of the child controller to retrieve
+ the name of. This is an optional parameter that
+ may be NULL. It will be NULL for device
+ drivers. It will also be NULL for a bus drivers
+ that wish to retrieve the name of the bus
+ controller. It will not be NULL for a bus
+ driver that wishes to retrieve the name of a
+ child controller.
+
+ @param Language[in] A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up
+ to the driver writer. Language is specified in
+ RFC 4646 or ISO 639-2 language code format.
+
+ @param ControllerName[out] A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ controller specified by ControllerHandle and
+ ChildHandle in the language specified by
+ Language from the point of view of the driver
+ specified by This.
+
+ @retval EFI_SUCCESS The Unicode string for the user readable name in
+ the language specified by Language for the
+ driver specified by This was returned in
+ DriverName.
+
+ @retval EFI_INVALID_PARAMETER ControllerHandle is NULL.
+
+ @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid
+ EFI_HANDLE.
+
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+
+ @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+
+ @retval EFI_UNSUPPORTED The driver specified by This is not currently
+ managing the controller specified by
+ ControllerHandle and ChildHandle.
+
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+NvmExpressComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ );
+
+/**
+ Tests to see if this driver supports a given controller. If a child device is provided,
+ it further tests to see if this driver supports creating a handle for the specified child device.
+
+ This function checks to see if the driver specified by This supports the device specified by
+ ControllerHandle. Drivers will typically use the device path attached to
+ ControllerHandle and/or the services from the bus I/O abstraction attached to
+ ControllerHandle to determine if the driver supports ControllerHandle. This function
+ may be called many times during platform initialization. In order to reduce boot times, the tests
+ performed by this function must be very small, and take as little time as possible to execute. This
+ function must not change the state of any hardware devices, and this function must be aware that the
+ device specified by ControllerHandle may already be managed by the same driver or a
+ different driver. This function must match its calls to AllocatePages() with FreePages(),
+ AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol().
+ Since ControllerHandle may have been previously started by the same driver, if a protocol is
+ already in the opened state, then it must not be closed with CloseProtocol(). This is required
+ to guarantee the state of ControllerHandle is not modified by this function.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param[in] ControllerHandle The handle of the controller to test. This handle
+ must support a protocol interface that supplies
+ an I/O abstraction to the driver.
+ @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This
+ parameter is ignored by device drivers, and is optional for bus
+ drivers. For bus drivers, if this parameter is not NULL, then
+ the bus driver must determine if the bus controller specified
+ by ControllerHandle and the child controller specified
+ by RemainingDevicePath are both supported by this
+ bus driver.
+
+ @retval EFI_SUCCESS The device specified by ControllerHandle and
+ RemainingDevicePath is supported by the driver specified by This.
+ @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and
+ RemainingDevicePath is already being managed by the driver
+ specified by This.
+ @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and
+ RemainingDevicePath is already being managed by a different
+ driver or an application that requires exclusive access.
+ Currently not implemented.
+ @retval EFI_UNSUPPORTED The device specified by ControllerHandle and
+ RemainingDevicePath is not supported by the driver specified by This.
+**/
+EFI_STATUS
+EFIAPI
+NvmExpressDriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ );
+
+/**
+ Starts a device controller or a bus controller.
+
+ The Start() function is designed to be invoked from the EFI boot service ConnectController().
+ As a result, much of the error checking on the parameters to Start() has been moved into this
+ common boot service. It is legal to call Start() from other locations,
+ but the following calling restrictions must be followed or the system behavior will not be deterministic.
+ 1. ControllerHandle must be a valid EFI_HANDLE.
+ 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned
+ EFI_DEVICE_PATH_PROTOCOL.
+ 3. Prior to calling Start(), the Supported() function for the driver specified by This must
+ have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param[in] ControllerHandle The handle of the controller to start. This handle
+ must support a protocol interface that supplies
+ an I/O abstraction to the driver.
+ @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This
+ parameter is ignored by device drivers, and is optional for bus
+ drivers. For a bus driver, if this parameter is NULL, then handles
+ for all the children of Controller are created by this driver.
+ If this parameter is not NULL and the first Device Path Node is
+ not the End of Device Path Node, then only the handle for the
+ child device specified by the first Device Path Node of
+ RemainingDevicePath is created by this driver.
+ If the first Device Path Node of RemainingDevicePath is
+ the End of Device Path Node, no child handle is created by this
+ driver.
+
+ @retval EFI_SUCCESS The device was started.
+ @retval EFI_DEVICE_ERROR The device could not be started due to a device error.Currently not implemented.
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.
+ @retval Others The driver failded to start the device.
+
+**/
+EFI_STATUS
+EFIAPI
+NvmExpressDriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ );
+
+/**
+ Stops a device controller or a bus controller.
+
+ The Stop() function is designed to be invoked from the EFI boot service DisconnectController().
+ As a result, much of the error checking on the parameters to Stop() has been moved
+ into this common boot service. It is legal to call Stop() from other locations,
+ but the following calling restrictions must be followed or the system behavior will not be deterministic.
+ 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this
+ same driver's Start() function.
+ 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid
+ EFI_HANDLE. In addition, all of these handles must have been created in this driver's
+ Start() function, and the Start() function must have called OpenProtocol() on
+ ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param[in] ControllerHandle A handle to the device being stopped. The handle must
+ support a bus specific I/O protocol for the driver
+ to use to stop the device.
+ @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer.
+ @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL
+ if NumberOfChildren is 0.
+
+ @retval EFI_SUCCESS The device was stopped.
+ @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error.
+
+**/
+EFI_STATUS
+EFIAPI
+NvmExpressDriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ );
+
+/**
+ Sends an NVM Express Command Packet to an NVM Express controller or namespace. This function supports
+ both blocking I/O and nonblocking I/O. The blocking I/O functionality is required, and the nonblocking
+ I/O functionality is optional.
+
+ @param[in] This A pointer to the NVM_EXPRESS_PASS_THRU_PROTOCOL instance.
+ @param[in] NamespaceId Is a 32 bit Namespace ID to which the Express HCI command packet will be sent.
+ A value of 0 denotes the NVM Express controller, a value of all 0FFh in the namespace
+ ID specifies that the command packet should be sent to all valid namespaces.
+ @param[in] NamespaceUuid Is a 64 bit Namespace UUID to which the Express HCI command packet will be sent.
+ A value of 0 denotes the NVM Express controller, a value of all 0FFh in the namespace
+ UUID specifies that the command packet should be sent to all valid namespaces.
+ @param[in,out] Packet A pointer to the NVM Express HCI Command Packet to send to the NVMe namespace specified
+ by NamespaceId.
+ @param[in] Event If nonblocking I/O is not supported then Event is ignored, and blocking I/O is performed.
+ If Event is NULL, then blocking I/O is performed. If Event is not NULL and non blocking I/O
+ is supported, then nonblocking I/O is performed, and Event will be signaled when the NVM
+ Express Command Packet completes.
+
+ @retval EFI_SUCCESS The NVM Express Command Packet was sent by the host. TransferLength bytes were transferred
+ to, or from DataBuffer.
+ @retval EFI_BAD_BUFFER_SIZE The NVM Express Command Packet was not executed. The number of bytes that could be transferred
+ is returned in TransferLength.
+ @retval EFI_NOT_READY The NVM Express Command Packet could not be sent because the controller is not ready. The caller
+ may retry again later.
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to send the NVM Express Command Packet.
+ @retval EFI_INVALID_PARAMETER Namespace, or the contents of NVM_EXPRESS_PASS_THRU_COMMAND_PACKET are invalid. The NVM
+ Express Command Packet was not sent, so no additional status information is available.
+ @retval EFI_UNSUPPORTED The command described by the NVM Express Command Packet is not supported by the host adapter.
+ The NVM Express Command Packet was not sent, so no additional status information is available.
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the NVM Express Command Packet to execute.
+
+**/
+EFI_STATUS
+EFIAPI
+NvmExpressPassThru (
+ IN NVM_EXPRESS_PASS_THRU_PROTOCOL *This,
+ IN UINT32 NamespaceId,
+ IN UINT64 NamespaceUuid,
+ IN OUT NVM_EXPRESS_PASS_THRU_COMMAND_PACKET *Packet,
+ IN EFI_EVENT Event OPTIONAL
+ );
+
+/**
+ Used to retrieve the list of namespaces defined on an NVM Express controller.
+
+ The NVM_EXPRESS_PASS_THRU_PROTOCOL.GetNextNamespace() function retrieves a list of namespaces
+ defined on an NVM Express controller. If on input a NamespaceID is specified by all 0xFF in the
+ namespace buffer, then the first namespace defined on the NVM Express controller is returned in
+ NamespaceID, and a status of EFI_SUCCESS is returned.
+
+ If NamespaceId is a Namespace value that was returned on a previous call to GetNextNamespace(),
+ then the next valid NamespaceId for an NVM Express SSD namespace on the NVM Express controller
+ is returned in NamespaceId, and EFI_SUCCESS is returned.
+
+ If Namespace array is not a 0xFFFFFFFF and NamespaceId was not returned on a previous call to
+ GetNextNamespace(), then EFI_INVALID_PARAMETER is returned.
+
+ If NamespaceId is the NamespaceId of the last SSD namespace on the NVM Express controller, then
+ EFI_NOT_FOUND is returned
+
+ @param[in] This A pointer to the NVM_EXPRESS_PASS_THRU_PROTOCOL instance.
+ @param[in,out] NamespaceId On input, a pointer to a legal NamespaceId for an NVM Express
+ namespace present on the NVM Express controller. On output, a
+ pointer to the next NamespaceId of an NVM Express namespace on
+ an NVM Express controller. An input value of 0xFFFFFFFF retrieves
+ the first NamespaceId for an NVM Express namespace present on an
+ NVM Express controller.
+ @param[out] NamespaceUuid On output, the UUID associated with the next namespace, if a UUID
+ is defined for that NamespaceId, otherwise, zero is returned in
+ this parameter. If the caller does not require a UUID, then a NULL
+ pointer may be passed.
+
+ @retval EFI_SUCCESS The NamespaceId of the next Namespace was returned.
+ @retval EFI_NOT_FOUND There are no more namespaces defined on this controller.
+ @retval EFI_INVALID_PARAMETER Namespace array is not a 0xFFFFFFFF and NamespaceId was not returned
+ on a previous call to GetNextNamespace().
+
+**/
+EFI_STATUS
+EFIAPI
+NvmExpressGetNextNamespace (
+ IN NVM_EXPRESS_PASS_THRU_PROTOCOL *This,
+ IN OUT UINT32 *NamespaceId,
+ OUT UINT64 *NamespaceUuid OPTIONAL
+ );
+
+/**
+ Used to translate a device path node to a Target ID and LUN.
+
+ The NVM_EXPRESS_PASS_THRU_PROTOCOL.GetNamwspace() function determines the Namespace ID and Namespace UUID
+ associated with the NVM Express SSD namespace described by DevicePath. If DevicePath is a device path node type
+ that the NVM Express Pass Thru driver supports, then the NVM Express Pass Thru driver will attempt to translate
+ the contents DevicePath into a Namespace ID and UUID. If this translation is successful, then that Namespace ID
+ and UUID are returned in NamespaceID and NamespaceUUID, and EFI_SUCCESS is returned.
+
+ @param[in] This A pointer to the NVM_EXPRESS_PASS_THRU_PROTOCOL instance.
+ @param[in] DevicePath A pointer to the device path node that describes an NVM Express namespace on
+ the NVM Express controller.
+ @param[out] NamespaceId The NVM Express namespace ID contained in the device path node.
+ @param[out] NamespaceUuid The NVM Express namespace contained in the device path node.
+
+ @retval EFI_SUCCESS DevicePath was successfully translated to NamespaceId and NamespaceUuid.
+ @retval EFI_INVALID_PARAMETER If DevicePath, NamespaceId, or NamespaceUuid are NULL, then EFI_INVALID_PARAMETER
+ is returned.
+ @retval EFI_UNSUPPORTED If DevicePath is not a device path node type that the NVM Express Pass Thru driver
+ supports, then EFI_UNSUPPORTED is returned.
+ @retval EFI_NOT_FOUND If DevicePath is a device path node type that the Nvm Express Pass Thru driver
+ supports, but there is not a valid translation from DevicePath to a NamespaceID
+ and NamespaceUuid, then EFI_NOT_FOUND is returned.
+**/
+EFI_STATUS
+EFIAPI
+NvmExpressGetNamespace (
+ IN NVM_EXPRESS_PASS_THRU_PROTOCOL *This,
+ IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
+ OUT UINT32 *NamespaceId,
+ OUT UINT64 *NamespaceUuid
+ );
+
+/**
+ Used to allocate and build a device path node for an NVM Express namespace on an NVM Express controller.
+
+ The NVM_EXPRESS_PASS_THRU_PROTOCOL.BuildDevicePath() function allocates and builds a single device
+ path node for the NVM Express namespace specified by NamespaceId.
+
+ If the namespace device specified by NamespaceId is not valid , then EFI_NOT_FOUND is returned.
+
+ If DevicePath is NULL, then EFI_INVALID_PARAMETER is returned.
+
+ If there are not enough resources to allocate the device path node, then EFI_OUT_OF_RESOURCES is returned.
+
+ Otherwise, DevicePath is allocated with the boot service AllocatePool(), the contents of DevicePath are
+ initialized to describe the NVM Express namespace specified by NamespaceId, and EFI_SUCCESS is returned.
+
+ @param[in] This A pointer to the NVM_EXPRESS_PASS_THRU_PROTOCOL instance.
+ @param[in] NamespaceId The NVM Express namespace ID for which a device path node is to be
+ allocated and built. Caller must set the NamespaceId to zero if the
+ device path node will contain a valid UUID.
+ @param[in] NamespaceUuid The NVM Express namespace UUID for which a device path node is to be
+ allocated and built. UUID will only be valid of the Namespace ID is zero.
+ @param[in,out] DevicePath A pointer to a single device path node that describes the NVM Express
+ namespace specified by NamespaceId. This function is responsible for
+ allocating the buffer DevicePath with the boot service AllocatePool().
+ It is the caller's responsibility to free DevicePath when the caller
+ is finished with DevicePath.
+ @retval EFI_SUCCESS The device path node that describes the NVM Express namespace specified
+ by NamespaceId was allocated and returned in DevicePath.
+ @retval EFI_NOT_FOUND The NVM Express namespace specified by NamespaceId does not exist on the
+ NVM Express controller.
+ @retval EFI_INVALID_PARAMETER DevicePath is NULL.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources to allocate the DevicePath node.
+
+**/
+EFI_STATUS
+EFIAPI
+NvmExpressBuildDevicePath (
+ IN NVM_EXPRESS_PASS_THRU_PROTOCOL *This,
+ IN UINT32 NamespaceId,
+ IN UINT64 NamespaceUuid,
+ IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath
+ );
+
+#endif
diff --git a/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressBlockIo.c b/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressBlockIo.c
new file mode 100644
index 0000000000..458afd232b
--- /dev/null
+++ b/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressBlockIo.c
@@ -0,0 +1,559 @@
+/** @file
+ NvmExpressDxe driver is used to manage non-volatile memory subsystem which follows
+ NVM Express specification.
+
+ Copyright (c) 2013, Intel Corporation. All rights reserved.<BR>
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "NvmExpress.h"
+
+/**
+ Read some sectors from the device.
+
+ @param Device The pointer to the NVME_DEVICE_PRIVATE_DATA 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 Datum are read from the device.
+ @retval Others Fail to read all the datum.
+
+**/
+EFI_STATUS
+ReadSectors (
+ IN NVME_DEVICE_PRIVATE_DATA *Device,
+ IN UINT64 Buffer,
+ IN UINT64 Lba,
+ IN UINT32 Blocks
+ )
+{
+ NVME_CONTROLLER_PRIVATE_DATA *Controller;
+ UINT32 Bytes;
+ NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket;
+ NVM_EXPRESS_COMMAND Command;
+ NVM_EXPRESS_RESPONSE Response;
+ EFI_STATUS Status;
+ UINT32 BlockSize;
+
+ Controller = Device->Controller;
+ BlockSize = Device->Media.BlockSize;
+ Bytes = Blocks * BlockSize;
+
+ ZeroMem (&CommandPacket, sizeof(NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));
+ ZeroMem (&Command, sizeof(NVM_EXPRESS_COMMAND));
+ ZeroMem (&Response, sizeof(NVM_EXPRESS_RESPONSE));
+
+ CommandPacket.NvmeCmd = &Command;
+ CommandPacket.NvmeResponse = &Response;
+
+ CommandPacket.NvmeCmd->Cdw0.Opcode = NVME_IO_READ_OPC;
+ CommandPacket.NvmeCmd->Cdw0.Cid = Controller->Cid[1]++;
+ CommandPacket.NvmeCmd->Nsid = Device->NamespaceId;
+ CommandPacket.TransferBuffer = (VOID *)(UINTN)Buffer;
+
+ CommandPacket.TransferLength = Bytes;
+ CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;
+ CommandPacket.QueueId = NVME_IO_QUEUE;
+
+ CommandPacket.NvmeCmd->Cdw10 = (UINT32)Lba;
+ CommandPacket.NvmeCmd->Cdw11 = (UINT32)(Lba >> 32);
+ CommandPacket.NvmeCmd->Cdw12 = (Blocks - 1) & 0xFFFF;
+
+ CommandPacket.NvmeCmd->Flags = CDW10_VALID | CDW11_VALID | CDW12_VALID;
+
+ Status = Controller->Passthru.PassThru (
+ &Controller->Passthru,
+ Device->NamespaceId,
+ 0,
+ &CommandPacket,
+ NULL
+ );
+
+ return Status;
+}
+
+/**
+ Write some sectors to the device.
+
+ @param Device The pointer to the NVME_DEVICE_PRIVATE_DATA data structure.
+ @param Buffer The buffer to be written into the device.
+ @param Lba The start block number.
+ @param Blocks Total block number to be written.
+
+ @retval EFI_SUCCESS Datum are written into the buffer.
+ @retval Others Fail to write all the datum.
+
+**/
+EFI_STATUS
+WriteSectors (
+ IN NVME_DEVICE_PRIVATE_DATA *Device,
+ IN UINT64 Buffer,
+ IN UINT64 Lba,
+ IN UINT32 Blocks
+ )
+{
+ NVME_CONTROLLER_PRIVATE_DATA *Controller;
+ NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket;
+ NVM_EXPRESS_COMMAND Command;
+ NVM_EXPRESS_RESPONSE Response;
+ EFI_STATUS Status;
+ UINT32 Bytes;
+ UINT32 BlockSize;
+
+ Controller = Device->Controller;
+ BlockSize = Device->Media.BlockSize;
+ Bytes = Blocks * BlockSize;
+
+ ZeroMem (&CommandPacket, sizeof(NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));
+ ZeroMem (&Command, sizeof(NVM_EXPRESS_COMMAND));
+ ZeroMem (&Response, sizeof(NVM_EXPRESS_RESPONSE));
+
+ CommandPacket.NvmeCmd = &Command;
+ CommandPacket.NvmeResponse = &Response;
+
+ CommandPacket.NvmeCmd->Cdw0.Opcode = NVME_IO_WRITE_OPC;
+ CommandPacket.NvmeCmd->Cdw0.Cid = Controller->Cid[1]++;
+ CommandPacket.NvmeCmd->Nsid = Device->NamespaceId;
+ CommandPacket.TransferBuffer = (VOID *)(UINTN)Buffer;
+
+ CommandPacket.TransferLength = Bytes;
+ CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;
+ CommandPacket.QueueId = NVME_IO_QUEUE;
+
+ CommandPacket.NvmeCmd->Cdw10 = (UINT32)Lba;
+ CommandPacket.NvmeCmd->Cdw11 = (UINT32)(Lba >> 32);
+ CommandPacket.NvmeCmd->Cdw12 = (Blocks - 1) & 0xFFFF;
+
+ CommandPacket.MetadataBuffer = NULL;
+ CommandPacket.MetadataLength = 0;
+
+ CommandPacket.NvmeCmd->Flags = CDW10_VALID | CDW11_VALID | CDW12_VALID;
+
+ Status = Controller->Passthru.PassThru (
+ &Controller->Passthru,
+ Device->NamespaceId,
+ 0,
+ &CommandPacket,
+ NULL
+ );
+
+ return Status;
+}
+
+/**
+ Read some blocks from the device.
+
+ @param Device The pointer to the NVME_DEVICE_PRIVATE_DATA 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 Datum are read from the device.
+ @retval Others Fail to read all the datum.
+
+**/
+EFI_STATUS
+NvmeRead (
+ IN NVME_DEVICE_PRIVATE_DATA *Device,
+ OUT VOID *Buffer,
+ IN UINT64 Lba,
+ IN UINTN Blocks
+ )
+{
+ EFI_STATUS Status;
+ UINT32 BlockSize;
+ NVME_CONTROLLER_PRIVATE_DATA *Controller;
+ UINT32 MaxTransferBlocks;
+
+ Status = EFI_SUCCESS;
+ Controller = Device->Controller;
+ BlockSize = Device->Media.BlockSize;
+
+ if (Controller->ControllerData->Mdts != 0) {
+ MaxTransferBlocks = (1 << (Controller->ControllerData->Mdts)) * (1 << (Controller->Cap.Mpsmin + 12)) / BlockSize;
+ } else {
+ MaxTransferBlocks = 1024;
+ }
+
+ while (Blocks > 0) {
+ if (Blocks > MaxTransferBlocks) {
+ Status = ReadSectors (Device, (UINT64)(UINTN)Buffer, Lba, MaxTransferBlocks);
+
+ Blocks -= MaxTransferBlocks;
+ Buffer = (VOID *)(UINTN)((UINT64)(UINTN)Buffer + MaxTransferBlocks * BlockSize);
+ Lba += MaxTransferBlocks;
+ } else {
+ Status = ReadSectors (Device, (UINT64)(UINTN)Buffer, Lba, (UINT32)Blocks);
+ Blocks = 0;
+ }
+
+ if (EFI_ERROR(Status)) {
+ break;
+ }
+ }
+
+ DEBUG ((EFI_D_INFO, "NvmeRead() Lba = %8d, Blocks = %8d, BlockSize = %d Status = %r\n", Lba, Blocks, BlockSize, Status));
+
+ return Status;
+}
+
+/**
+ Write some blocks to the device.
+
+ @param Device The pointer to the NVME_DEVICE_PRIVATE_DATA data structure.
+ @param Buffer The buffer to be written into the device.
+ @param Lba The start block number.
+ @param Blocks Total block number to be written.
+
+ @retval EFI_SUCCESS Datum are written into the buffer.
+ @retval Others Fail to write all the datum.
+
+**/
+EFI_STATUS
+NvmeWrite (
+ IN NVME_DEVICE_PRIVATE_DATA *Device,
+ IN VOID *Buffer,
+ IN UINT64 Lba,
+ IN UINTN Blocks
+ )
+{
+ EFI_STATUS Status;
+ UINT32 BlockSize;
+ NVME_CONTROLLER_PRIVATE_DATA *Controller;
+ UINT32 MaxTransferBlocks;
+
+ Status = EFI_SUCCESS;
+ Controller = Device->Controller;
+ BlockSize = Device->Media.BlockSize;
+
+ if (Controller->ControllerData->Mdts != 0) {
+ MaxTransferBlocks = (1 << (Controller->ControllerData->Mdts)) * (1 << (Controller->Cap.Mpsmin + 12)) / BlockSize;
+ } else {
+ MaxTransferBlocks = 1024;
+ }
+
+ while (Blocks > 0) {
+ if (Blocks > MaxTransferBlocks) {
+ Status = WriteSectors (Device, (UINT64)(UINTN)Buffer, Lba, MaxTransferBlocks);
+
+ Blocks -= MaxTransferBlocks;
+ Buffer = (VOID *)(UINTN)((UINT64)(UINTN)Buffer + MaxTransferBlocks * BlockSize);
+ Lba += MaxTransferBlocks;
+ } else {
+ Status = WriteSectors (Device, (UINT64)(UINTN)Buffer, Lba, (UINT32)Blocks);
+ Blocks = 0;
+ }
+
+ if (EFI_ERROR(Status)) {
+ break;
+ }
+ }
+
+ DEBUG ((EFI_D_INFO, "NvmeWrite() Lba = %8d, Blocks = %8d, BlockSize = %d Status = %r\n", Lba, Blocks, BlockSize, Status));
+
+ return Status;
+}
+
+/**
+ Flushes all modified data to the device.
+
+ @param Device The pointer to the NVME_DEVICE_PRIVATE_DATA data structure.
+
+ @retval EFI_SUCCESS Datum are written into the buffer.
+ @retval Others Fail to write all the datum.
+
+**/
+EFI_STATUS
+NvmeFlush (
+ IN NVME_DEVICE_PRIVATE_DATA *Device
+ )
+{
+ NVME_CONTROLLER_PRIVATE_DATA *Controller;
+ NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket;
+ NVM_EXPRESS_COMMAND Command;
+ NVM_EXPRESS_RESPONSE Response;
+ EFI_STATUS Status;
+
+ Controller = Device->Controller;
+
+ ZeroMem (&CommandPacket, sizeof(NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));
+ ZeroMem (&Command, sizeof(NVM_EXPRESS_COMMAND));
+ ZeroMem (&Response, sizeof(NVM_EXPRESS_RESPONSE));
+
+ CommandPacket.NvmeCmd = &Command;
+ CommandPacket.NvmeResponse = &Response;
+
+ CommandPacket.NvmeCmd->Cdw0.Opcode = NVME_IO_FLUSH_OPC;
+ CommandPacket.NvmeCmd->Cdw0.Cid = Controller->Cid[1]++;
+ CommandPacket.NvmeCmd->Nsid = Device->NamespaceId;
+ CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;
+ CommandPacket.QueueId = NVME_IO_QUEUE;
+
+ Status = Controller->Passthru.PassThru (
+ &Controller->Passthru,
+ Device->NamespaceId,
+ 0,
+ &CommandPacket,
+ NULL
+ );
+
+ return Status;
+}
+
+
+/**
+ Reset the Block Device.
+
+ @param This Indicates a pointer to the calling context.
+ @param ExtendedVerification Driver may perform diagnostics on reset.
+
+ @retval EFI_SUCCESS The device was reset.
+ @retval EFI_DEVICE_ERROR The device is not functioning properly and could
+ not be reset.
+
+**/
+EFI_STATUS
+EFIAPI
+NvmeBlockIoReset (
+ IN EFI_BLOCK_IO_PROTOCOL *This,
+ IN BOOLEAN ExtendedVerification
+ )
+{
+ EFI_TPL OldTpl;
+ NVME_CONTROLLER_PRIVATE_DATA *Private;
+ NVME_DEVICE_PRIVATE_DATA *Device;
+ EFI_STATUS Status;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // For Nvm Express subsystem, reset block device means reset controller.
+ //
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ Device = NVME_DEVICE_PRIVATE_DATA_FROM_BLOCK_IO (This);
+
+ Private = Device->Controller;
+
+ Status = NvmeControllerInit (Private);
+
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
+
+/**
+ Read BufferSize bytes from Lba into Buffer.
+
+ @param This Indicates a pointer to the calling context.
+ @param MediaId Id of the media, changes every time the media is replaced.
+ @param Lba The starting Logical Block Address to read from.
+ @param BufferSize Size of Buffer, must be a multiple of device block size.
+ @param Buffer A pointer to the destination buffer for the data. The caller is
+ responsible for either having implicit or explicit ownership of the buffer.
+
+ @retval EFI_SUCCESS The data was read correctly from the device.
+ @retval EFI_DEVICE_ERROR The device reported an error while performing the read.
+ @retval EFI_NO_MEDIA There is no media in the device.
+ @retval EFI_MEDIA_CHANGED The MediaId does not matched the current device.
+ @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device.
+ @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid,
+ or the buffer is not on proper alignment.
+
+**/
+EFI_STATUS
+EFIAPI
+NvmeBlockIoReadBlocks (
+ IN EFI_BLOCK_IO_PROTOCOL *This,
+ IN UINT32 MediaId,
+ IN EFI_LBA Lba,
+ IN UINTN BufferSize,
+ OUT VOID *Buffer
+ )
+{
+ NVME_DEVICE_PRIVATE_DATA *Device;
+ EFI_STATUS Status;
+ EFI_BLOCK_IO_MEDIA *Media;
+ UINTN BlockSize;
+ UINTN NumberOfBlocks;
+ UINTN IoAlign;
+ EFI_TPL OldTpl;
+
+ //
+ // Check parameters.
+ //
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Media = This->Media;
+
+ if (MediaId != Media->MediaId) {
+ return EFI_MEDIA_CHANGED;
+ }
+
+ if (Buffer == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (BufferSize == 0) {
+ return EFI_SUCCESS;
+ }
+
+ BlockSize = Media->BlockSize;
+ if ((BufferSize % BlockSize) != 0) {
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+ NumberOfBlocks = BufferSize / BlockSize;
+ if ((Lba + NumberOfBlocks - 1) > Media->LastBlock) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ IoAlign = Media->IoAlign;
+ if (IoAlign > 0 && (((UINTN) Buffer & (IoAlign - 1)) != 0)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ Device = NVME_DEVICE_PRIVATE_DATA_FROM_BLOCK_IO (This);
+
+ Status = NvmeRead (Device, Buffer, Lba, NumberOfBlocks);
+
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
+/**
+ Write BufferSize bytes from Lba into Buffer.
+
+ @param This Indicates a pointer to the calling context.
+ @param MediaId The media ID that the write request is for.
+ @param Lba The starting logical block address to be written. The caller is
+ responsible for writing to only legitimate locations.
+ @param BufferSize Size of Buffer, must be a multiple of device block size.
+ @param Buffer A pointer to the source buffer for the data.
+
+ @retval EFI_SUCCESS The data was written correctly to the device.
+ @retval EFI_WRITE_PROTECTED The device can not be written to.
+ @retval EFI_DEVICE_ERROR The device reported an error while performing the write.
+ @retval EFI_NO_MEDIA There is no media in the device.
+ @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device.
+ @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device.
+ @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not valid,
+ or the buffer is not on proper alignment.
+
+**/
+EFI_STATUS
+EFIAPI
+NvmeBlockIoWriteBlocks (
+ IN EFI_BLOCK_IO_PROTOCOL *This,
+ IN UINT32 MediaId,
+ IN EFI_LBA Lba,
+ IN UINTN BufferSize,
+ IN VOID *Buffer
+ )
+{
+ NVME_DEVICE_PRIVATE_DATA *Device;
+ EFI_STATUS Status;
+ EFI_BLOCK_IO_MEDIA *Media;
+ UINTN BlockSize;
+ UINTN NumberOfBlocks;
+ UINTN IoAlign;
+ EFI_TPL OldTpl;
+
+ //
+ // Check parameters.
+ //
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Media = This->Media;
+
+ if (MediaId != Media->MediaId) {
+ return EFI_MEDIA_CHANGED;
+ }
+
+ if (Buffer == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (BufferSize == 0) {
+ return EFI_SUCCESS;
+ }
+
+ BlockSize = Media->BlockSize;
+ if ((BufferSize % BlockSize) != 0) {
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+ NumberOfBlocks = BufferSize / BlockSize;
+ if ((Lba + NumberOfBlocks - 1) > Media->LastBlock) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ IoAlign = Media->IoAlign;
+ if (IoAlign > 0 && (((UINTN) Buffer & (IoAlign - 1)) != 0)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ Device = NVME_DEVICE_PRIVATE_DATA_FROM_BLOCK_IO (This);
+
+ Status = NvmeWrite (Device, Buffer, Lba, NumberOfBlocks);
+
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
+
+/**
+ Flush the Block Device.
+
+ @param This Indicates a pointer to the calling context.
+
+ @retval EFI_SUCCESS All outstanding data was written to the device.
+ @retval EFI_DEVICE_ERROR The device reported an error while writing back the data.
+ @retval EFI_NO_MEDIA There is no media in the device.
+
+**/
+EFI_STATUS
+EFIAPI
+NvmeBlockIoFlushBlocks (
+ IN EFI_BLOCK_IO_PROTOCOL *This
+ )
+{
+ NVME_DEVICE_PRIVATE_DATA *Device;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+
+ //
+ // Check parameters.
+ //
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ Device = NVME_DEVICE_PRIVATE_DATA_FROM_BLOCK_IO (This);
+
+ Status = NvmeFlush (Device);
+
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
diff --git a/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressBlockIo.h b/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressBlockIo.h
new file mode 100644
index 0000000000..5f896fed81
--- /dev/null
+++ b/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressBlockIo.h
@@ -0,0 +1,111 @@
+/** @file
+ Header file for EFI_BLOCK_IO_PROTOCOL interface.
+
+Copyright (c) 2013, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _EFI_NVME_BLOCKIO_H_
+#define _EFI_NVME_BLOCKIO_H_
+
+/**
+ Reset the Block Device.
+
+ @param This Indicates a pointer to the calling context.
+ @param ExtendedVerification Driver may perform diagnostics on reset.
+
+ @retval EFI_SUCCESS The device was reset.
+ @retval EFI_DEVICE_ERROR The device is not functioning properly and could
+ not be reset.
+
+**/
+EFI_STATUS
+EFIAPI
+NvmeBlockIoReset (
+ IN EFI_BLOCK_IO_PROTOCOL *This,
+ IN BOOLEAN ExtendedVerification
+ );
+
+/**
+ Read BufferSize bytes from Lba into Buffer.
+
+ @param This Indicates a pointer to the calling context.
+ @param MediaId Id of the media, changes every time the media is replaced.
+ @param Lba The starting Logical Block Address to read from
+ @param BufferSize Size of Buffer, must be a multiple of device block size.
+ @param Buffer A pointer to the destination buffer for the data. The caller is
+ responsible for either having implicit or explicit ownership of the buffer.
+
+ @retval EFI_SUCCESS The data was read correctly from the device.
+ @retval EFI_DEVICE_ERROR The device reported an error while performing the read.
+ @retval EFI_NO_MEDIA There is no media in the device.
+ @retval EFI_MEDIA_CHANGED The MediaId does not matched the current device.
+ @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device.
+ @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid,
+ or the buffer is not on proper alignment.
+
+**/
+EFI_STATUS
+EFIAPI
+NvmeBlockIoReadBlocks (
+ IN EFI_BLOCK_IO_PROTOCOL *This,
+ IN UINT32 MediaId,
+ IN EFI_LBA Lba,
+ IN UINTN BufferSize,
+ OUT VOID *Buffer
+ );
+
+/**
+ Write BufferSize bytes from Lba into Buffer.
+
+ @param This Indicates a pointer to the calling context.
+ @param MediaId The media ID that the write request is for.
+ @param Lba The starting logical block address to be written. The caller is
+ responsible for writing to only legitimate locations.
+ @param BufferSize Size of Buffer, must be a multiple of device block size.
+ @param Buffer A pointer to the source buffer for the data.
+
+ @retval EFI_SUCCESS The data was written correctly to the device.
+ @retval EFI_WRITE_PROTECTED The device can not be written to.
+ @retval EFI_DEVICE_ERROR The device reported an error while performing the write.
+ @retval EFI_NO_MEDIA There is no media in the device.
+ @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device.
+ @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device.
+ @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not valid,
+ or the buffer is not on proper alignment.
+
+**/
+EFI_STATUS
+EFIAPI
+NvmeBlockIoWriteBlocks (
+ IN EFI_BLOCK_IO_PROTOCOL *This,
+ IN UINT32 MediaId,
+ IN EFI_LBA Lba,
+ IN UINTN BufferSize,
+ IN VOID *Buffer
+ );
+
+/**
+ Flush the Block Device.
+
+ @param This Indicates a pointer to the calling context.
+
+ @retval EFI_SUCCESS All outstanding data was written to the device
+ @retval EFI_DEVICE_ERROR The device reported an error while writing back the data
+ @retval EFI_NO_MEDIA There is no media in the device.
+
+**/
+EFI_STATUS
+EFIAPI
+NvmeBlockIoFlushBlocks (
+ IN EFI_BLOCK_IO_PROTOCOL *This
+ );
+
+#endif
diff --git a/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressDiskInfo.c b/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressDiskInfo.c
new file mode 100644
index 0000000000..66af1906ce
--- /dev/null
+++ b/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressDiskInfo.c
@@ -0,0 +1,162 @@
+/** @file
+ This file is used to implement the EFI_DISK_INFO_PROTOCOL interface..
+
+ Copyright (c) 2013, Intel Corporation. All rights reserved.<BR>
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "NvmExpress.h"
+
+EFI_DISK_INFO_PROTOCOL gNvmExpressDiskInfoProtocolTemplate = {
+ EFI_DISK_INFO_NVME_INTERFACE_GUID,
+ NvmExpressDiskInfoInquiry,
+ NvmExpressDiskInfoIdentify,
+ NvmExpressDiskInfoSenseData,
+ NvmExpressDiskInfoWhichIde
+};
+
+/**
+ Initialize the installation of DiskInfo protocol.
+
+ This function prepares for the installation of DiskInfo protocol on the child handle.
+ By default, it installs DiskInfo protocol with NVME interface GUID.
+
+ @param[in] Device The pointer of NVME_DEVICE_PRIVATE_DATA.
+
+**/
+VOID
+InitializeDiskInfo (
+ IN NVME_DEVICE_PRIVATE_DATA *Device
+ )
+{
+ CopyMem (&Device->DiskInfo, &gNvmExpressDiskInfoProtocolTemplate, sizeof (EFI_DISK_INFO_PROTOCOL));
+}
+
+
+/**
+ Provides inquiry information for the controller type.
+
+ This function is used to get inquiry data. Data format
+ of Identify data is defined by the Interface GUID.
+
+ @param[in] This Pointer to the EFI_DISK_INFO_PROTOCOL instance.
+ @param[in, out] InquiryData Pointer to a buffer for the inquiry data.
+ @param[in, out] InquiryDataSize Pointer to the value for the inquiry data size.
+
+ @retval EFI_SUCCESS The command was accepted without any errors.
+ @retval EFI_NOT_FOUND Device does not support this data class
+ @retval EFI_DEVICE_ERROR Error reading InquiryData from device
+ @retval EFI_BUFFER_TOO_SMALL InquiryDataSize not big enough
+
+**/
+EFI_STATUS
+EFIAPI
+NvmExpressDiskInfoInquiry (
+ IN EFI_DISK_INFO_PROTOCOL *This,
+ IN OUT VOID *InquiryData,
+ IN OUT UINT32 *InquiryDataSize
+ )
+{
+ return EFI_NOT_FOUND;
+}
+
+
+/**
+ Provides identify information for the controller type.
+
+ This function is used to get identify data. Data format
+ of Identify data is defined by the Interface GUID.
+
+ @param[in] This Pointer to the EFI_DISK_INFO_PROTOCOL
+ instance.
+ @param[in, out] IdentifyData Pointer to a buffer for the identify data.
+ @param[in, out] IdentifyDataSize Pointer to the value for the identify data
+ size.
+
+ @retval EFI_SUCCESS The command was accepted without any errors.
+ @retval EFI_NOT_FOUND Device does not support this data class
+ @retval EFI_DEVICE_ERROR Error reading IdentifyData from device
+ @retval EFI_BUFFER_TOO_SMALL IdentifyDataSize not big enough
+
+**/
+EFI_STATUS
+EFIAPI
+NvmExpressDiskInfoIdentify (
+ IN EFI_DISK_INFO_PROTOCOL *This,
+ IN OUT VOID *IdentifyData,
+ IN OUT UINT32 *IdentifyDataSize
+ )
+{
+ EFI_STATUS Status;
+ NVME_DEVICE_PRIVATE_DATA *Device;
+
+ Device = NVME_DEVICE_PRIVATE_DATA_FROM_DISK_INFO (This);
+
+ Status = EFI_BUFFER_TOO_SMALL;
+ if (*IdentifyDataSize >= sizeof (Device->NamespaceData)) {
+ Status = EFI_SUCCESS;
+ CopyMem (IdentifyData, &Device->NamespaceData, sizeof (Device->NamespaceData));
+ }
+ *IdentifyDataSize = sizeof (Device->NamespaceData);
+ return Status;
+}
+
+/**
+ Provides sense data information for the controller type.
+
+ This function is used to get sense data.
+ Data format of Sense data is defined by the Interface GUID.
+
+ @param[in] This Pointer to the EFI_DISK_INFO_PROTOCOL instance.
+ @param[in, out] SenseData Pointer to the SenseData.
+ @param[in, out] SenseDataSize Size of SenseData in bytes.
+ @param[out] SenseDataNumber Pointer to the value for the sense data size.
+
+ @retval EFI_SUCCESS The command was accepted without any errors.
+ @retval EFI_NOT_FOUND Device does not support this data class.
+ @retval EFI_DEVICE_ERROR Error reading SenseData from device.
+ @retval EFI_BUFFER_TOO_SMALL SenseDataSize not big enough.
+
+**/
+EFI_STATUS
+EFIAPI
+NvmExpressDiskInfoSenseData (
+ IN EFI_DISK_INFO_PROTOCOL *This,
+ IN OUT VOID *SenseData,
+ IN OUT UINT32 *SenseDataSize,
+ OUT UINT8 *SenseDataNumber
+ )
+{
+ return EFI_NOT_FOUND;
+}
+
+
+/**
+ This function is used to get controller information.
+
+ @param[in] This Pointer to the EFI_DISK_INFO_PROTOCOL instance.
+ @param[out] IdeChannel Pointer to the Ide Channel number. Primary or secondary.
+ @param[out] IdeDevice Pointer to the Ide Device number. Master or slave.
+
+ @retval EFI_SUCCESS IdeChannel and IdeDevice are valid.
+ @retval EFI_UNSUPPORTED This is not an IDE device.
+
+**/
+EFI_STATUS
+EFIAPI
+NvmExpressDiskInfoWhichIde (
+ IN EFI_DISK_INFO_PROTOCOL *This,
+ OUT UINT32 *IdeChannel,
+ OUT UINT32 *IdeDevice
+ )
+{
+ return EFI_UNSUPPORTED;
+}
+
diff --git a/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressDiskInfo.h b/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressDiskInfo.h
new file mode 100644
index 0000000000..7db3dff47a
--- /dev/null
+++ b/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressDiskInfo.h
@@ -0,0 +1,129 @@
+/** @file
+ Header file for EFI_DISK_INFO_PROTOCOL interface.
+
+Copyright (c) 2013, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _NVME_DISKINFO_H_
+#define _NVME_DISKINFO_H_
+
+/**
+ Initialize the installation of DiskInfo protocol.
+
+ This function prepares for the installation of DiskInfo protocol on the child handle.
+ By default, it installs DiskInfo protocol with NVME interface GUID.
+
+ @param[in] Device The pointer of NVME_DEVICE_PRIVATE_DATA.
+
+**/
+VOID
+InitializeDiskInfo (
+ IN NVME_DEVICE_PRIVATE_DATA *Device
+ );
+
+
+/**
+ Provides inquiry information for the controller type.
+
+ This function is used to get inquiry data. Data format
+ of Identify data is defined by the Interface GUID.
+
+ @param[in] This Pointer to the EFI_DISK_INFO_PROTOCOL instance.
+ @param[in, out] InquiryData Pointer to a buffer for the inquiry data.
+ @param[in, out] InquiryDataSize Pointer to the value for the inquiry data size.
+
+ @retval EFI_SUCCESS The command was accepted without any errors.
+ @retval EFI_NOT_FOUND Device does not support this data class
+ @retval EFI_DEVICE_ERROR Error reading InquiryData from device
+ @retval EFI_BUFFER_TOO_SMALL InquiryDataSize not big enough
+
+**/
+EFI_STATUS
+EFIAPI
+NvmExpressDiskInfoInquiry (
+ IN EFI_DISK_INFO_PROTOCOL *This,
+ IN OUT VOID *InquiryData,
+ IN OUT UINT32 *InquiryDataSize
+ );
+
+/**
+ Provides identify information for the controller type.
+
+ This function is used to get identify data. Data format
+ of Identify data is defined by the Interface GUID.
+
+ @param[in] This Pointer to the EFI_DISK_INFO_PROTOCOL
+ instance.
+ @param[in, out] IdentifyData Pointer to a buffer for the identify data.
+ @param[in, out] IdentifyDataSize Pointer to the value for the identify data
+ size.
+
+ @retval EFI_SUCCESS The command was accepted without any errors.
+ @retval EFI_NOT_FOUND Device does not support this data class
+ @retval EFI_DEVICE_ERROR Error reading IdentifyData from device
+ @retval EFI_BUFFER_TOO_SMALL IdentifyDataSize not big enough
+
+**/
+EFI_STATUS
+EFIAPI
+NvmExpressDiskInfoIdentify (
+ IN EFI_DISK_INFO_PROTOCOL *This,
+ IN OUT VOID *IdentifyData,
+ IN OUT UINT32 *IdentifyDataSize
+ );
+
+/**
+ Provides sense data information for the controller type.
+
+ This function is used to get sense data.
+ Data format of Sense data is defined by the Interface GUID.
+
+ @param[in] This Pointer to the EFI_DISK_INFO_PROTOCOL instance.
+ @param[in, out] SenseData Pointer to the SenseData.
+ @param[in, out] SenseDataSize Size of SenseData in bytes.
+ @param[out] SenseDataNumber Pointer to the value for the sense data size.
+
+ @retval EFI_SUCCESS The command was accepted without any errors.
+ @retval EFI_NOT_FOUND Device does not support this data class.
+ @retval EFI_DEVICE_ERROR Error reading SenseData from device.
+ @retval EFI_BUFFER_TOO_SMALL SenseDataSize not big enough.
+
+**/
+EFI_STATUS
+EFIAPI
+NvmExpressDiskInfoSenseData (
+ IN EFI_DISK_INFO_PROTOCOL *This,
+ IN OUT VOID *SenseData,
+ IN OUT UINT32 *SenseDataSize,
+ OUT UINT8 *SenseDataNumber
+ );
+
+
+/**
+ This function is used to get controller information.
+
+ @param[in] This Pointer to the EFI_DISK_INFO_PROTOCOL instance.
+ @param[out] IdeChannel Pointer to the Ide Channel number. Primary or secondary.
+ @param[out] IdeDevice Pointer to the Ide Device number. Master or slave.
+
+ @retval EFI_SUCCESS IdeChannel and IdeDevice are valid.
+ @retval EFI_UNSUPPORTED This is not an IDE device.
+
+**/
+EFI_STATUS
+EFIAPI
+NvmExpressDiskInfoWhichIde (
+ IN EFI_DISK_INFO_PROTOCOL *This,
+ OUT UINT32 *IdeChannel,
+ OUT UINT32 *IdeDevice
+ );
+
+#endif
diff --git a/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressDxe.inf b/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressDxe.inf
new file mode 100644
index 0000000000..3ed185a1f0
--- /dev/null
+++ b/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressDxe.inf
@@ -0,0 +1,68 @@
+## @file
+# Component Description File For NVM Express Host Controller Module.
+#
+# NvmExpressDxe driver is used to manage non-volatile memory subsystem which follows
+# NVM Express specification.
+#
+# Copyright (c) 2013, Intel Corporation. All rights reserved.<BR>
+#
+# This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php.
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = NvmExpressDxe
+ FILE_GUID = 5BE3BDF4-53CF-46a3-A6A9-73C34A6E5EE3
+ MODULE_TYPE = UEFI_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = NvmExpressDriverEntry
+ UNLOAD_IMAGE = NvmExpressUnload
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC
+#
+# DRIVER_BINDING = gNvmExpressDriverBinding
+# COMPONENT_NAME = gNvmExpressComponentName
+# COMPONENT_NAME2 = gNvmExpressComponentName2
+
+[Sources]
+ NvmExpressBlockIo.c
+ NvmExpressBlockIo.h
+ ComponentName.c
+ NvmExpress.c
+ NvmExpress.h
+ NvmExpressDiskInfo.c
+ NvmExpressDiskInfo.h
+ NvmExpressHci.c
+ NvmExpressHci.h
+ NvmExpressPassthru.c
+ NvmExpressPassthru.h
+
+[Packages]
+ MdePkg/MdePkg.dec
+
+[LibraryClasses]
+ BaseMemoryLib
+ BaseLib
+ DebugLib
+ DevicePathLib
+ MemoryAllocationLib
+ UefiDriverEntryPoint
+ UefiBootServicesTableLib
+ UefiLib
+ PrintLib
+
+[Protocols]
+ gEfiPciIoProtocolGuid ## TO_START
+ gEfiDevicePathProtocolGuid ## TO_START
+ gEfiBlockIoProtocolGuid ## BY_START
+ gEfiDiskInfoProtocolGuid ## BY_START
+ gEfiDriverSupportedEfiVersionProtocolGuid ## BY_START \ No newline at end of file
diff --git a/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressHci.c b/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressHci.c
new file mode 100644
index 0000000000..157e10127a
--- /dev/null
+++ b/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressHci.c
@@ -0,0 +1,926 @@
+/** @file
+ NvmExpressDxe driver is used to manage non-volatile memory subsystem which follows
+ NVM Express specification.
+
+ Copyright (c) 2013, Intel Corporation. All rights reserved.<BR>
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "NvmExpress.h"
+
+/**
+ Read Nvm Express controller capability register.
+
+ @param Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure.
+ @param Cap The buffer used to store capability register content.
+
+ @return EFI_SUCCESS Successfully read the controller capability register content.
+ @return EFI_DEVICE_ERROR Fail to read the controller capability register.
+
+**/
+EFI_STATUS
+ReadNvmeControllerCapabilities (
+ IN NVME_CONTROLLER_PRIVATE_DATA *Private,
+ IN NVME_CAP *Cap
+ )
+{
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ EFI_STATUS Status;
+
+ PciIo = Private->PciIo;
+ Status = PciIo->Mem.Read (
+ PciIo,
+ EfiPciIoWidthUint64,
+ NVME_BAR,
+ NVME_CAP_OFFSET,
+ 1,
+ Cap
+ );
+
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Read Nvm Express controller configuration register.
+
+ @param Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure.
+ @param Cc The buffer used to store configuration register content.
+
+ @return EFI_SUCCESS Successfully read the controller configuration register content.
+ @return EFI_DEVICE_ERROR Fail to read the controller configuration register.
+
+**/
+EFI_STATUS
+ReadNvmeControllerConfiguration (
+ IN NVME_CONTROLLER_PRIVATE_DATA *Private,
+ IN NVME_CC *Cc
+ )
+{
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ EFI_STATUS Status;
+
+ PciIo = Private->PciIo;
+ Status = PciIo->Mem.Read (
+ PciIo,
+ EfiPciIoWidthUint32,
+ NVME_BAR,
+ NVME_CC_OFFSET,
+ 1,
+ Cc
+ );
+
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Write Nvm Express controller configuration register.
+
+ @param Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure.
+ @param Cc The buffer used to store the content to be written into configuration register.
+
+ @return EFI_SUCCESS Successfully write data into the controller configuration register.
+ @return EFI_DEVICE_ERROR Fail to write data into the controller configuration register.
+
+**/
+EFI_STATUS
+WriteNvmeControllerConfiguration (
+ IN NVME_CONTROLLER_PRIVATE_DATA *Private,
+ IN NVME_CC *Cc
+ )
+{
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ EFI_STATUS Status;
+
+ PciIo = Private->PciIo;
+ Status = PciIo->Mem.Write (
+ PciIo,
+ EfiPciIoWidthUint32,
+ NVME_BAR,
+ NVME_CC_OFFSET,
+ 1,
+ Cc
+ );
+
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+
+ DEBUG ((EFI_D_INFO, "Cc.En: %d\n", Cc->En));
+ DEBUG ((EFI_D_INFO, "Cc.Css: %d\n", Cc->Css));
+ DEBUG ((EFI_D_INFO, "Cc.Mps: %d\n", Cc->Mps));
+ DEBUG ((EFI_D_INFO, "Cc.Ams: %d\n", Cc->Ams));
+ DEBUG ((EFI_D_INFO, "Cc.Shn: %d\n", Cc->Shn));
+ DEBUG ((EFI_D_INFO, "Cc.Iosqes: %d\n", Cc->Iosqes));
+ DEBUG ((EFI_D_INFO, "Cc.Iocqes: %d\n", Cc->Iocqes));
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Read Nvm Express controller status register.
+
+ @param Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure.
+ @param Csts The buffer used to store status register content.
+
+ @return EFI_SUCCESS Successfully read the controller status register content.
+ @return EFI_DEVICE_ERROR Fail to read the controller status register.
+
+**/
+EFI_STATUS
+ReadNvmeControllerStatus (
+ IN NVME_CONTROLLER_PRIVATE_DATA *Private,
+ IN NVME_CSTS *Csts
+ )
+{
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ EFI_STATUS Status;
+
+ PciIo = Private->PciIo;
+ Status = PciIo->Mem.Read (
+ PciIo,
+ EfiPciIoWidthUint32,
+ NVME_BAR,
+ NVME_CSTS_OFFSET,
+ 1,
+ Csts
+ );
+
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Read Nvm Express admin queue attributes register.
+
+ @param Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure.
+ @param Aqa The buffer used to store admin queue attributes register content.
+
+ @return EFI_SUCCESS Successfully read the admin queue attributes register content.
+ @return EFI_DEVICE_ERROR Fail to read the admin queue attributes register.
+
+**/
+EFI_STATUS
+ReadNvmeAdminQueueAttributes (
+ IN NVME_CONTROLLER_PRIVATE_DATA *Private,
+ IN NVME_AQA *Aqa
+ )
+{
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ EFI_STATUS Status;
+
+ PciIo = Private->PciIo;
+ Status = PciIo->Mem.Read (
+ PciIo,
+ EfiPciIoWidthUint32,
+ NVME_BAR,
+ NVME_AQA_OFFSET,
+ 1,
+ Aqa
+ );
+
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Write Nvm Express admin queue attributes register.
+
+ @param Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure.
+ @param Aqa The buffer used to store the content to be written into admin queue attributes register.
+
+ @return EFI_SUCCESS Successfully write data into the admin queue attributes register.
+ @return EFI_DEVICE_ERROR Fail to write data into the admin queue attributes register.
+
+**/
+EFI_STATUS
+WriteNvmeAdminQueueAttributes (
+ IN NVME_CONTROLLER_PRIVATE_DATA *Private,
+ IN NVME_AQA *Aqa
+ )
+{
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ EFI_STATUS Status;
+
+ PciIo = Private->PciIo;
+ Status = PciIo->Mem.Write (
+ PciIo,
+ EfiPciIoWidthUint32,
+ NVME_BAR,
+ NVME_AQA_OFFSET,
+ 1,
+ Aqa
+ );
+
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+
+ DEBUG ((EFI_D_INFO, "Aqa.Asqs: %d\n", Aqa->Asqs));
+ DEBUG ((EFI_D_INFO, "Aqa.Acqs: %d\n", Aqa->Acqs));
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Read Nvm Express admin submission queue base address register.
+
+ @param Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure.
+ @param Asq The buffer used to store admin submission queue base address register content.
+
+ @return EFI_SUCCESS Successfully read the admin submission queue base address register content.
+ @return EFI_DEVICE_ERROR Fail to read the admin submission queue base address register.
+
+**/
+EFI_STATUS
+ReadNvmeAdminSubmissionQueueBaseAddress (
+ IN NVME_CONTROLLER_PRIVATE_DATA *Private,
+ IN NVME_ASQ *Asq
+ )
+{
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ EFI_STATUS Status;
+
+ PciIo = Private->PciIo;
+ Status = PciIo->Mem.Read (
+ PciIo,
+ EfiPciIoWidthUint64,
+ NVME_BAR,
+ NVME_ASQ_OFFSET,
+ 1,
+ Asq
+ );
+
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Write Nvm Express admin submission queue base address register.
+
+ @param Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure.
+ @param Asq The buffer used to store the content to be written into admin submission queue base address register.
+
+ @return EFI_SUCCESS Successfully write data into the admin submission queue base address register.
+ @return EFI_DEVICE_ERROR Fail to write data into the admin submission queue base address register.
+
+**/
+EFI_STATUS
+WriteNvmeAdminSubmissionQueueBaseAddress (
+ IN NVME_CONTROLLER_PRIVATE_DATA *Private,
+ IN NVME_ASQ *Asq
+ )
+{
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ EFI_STATUS Status;
+
+ PciIo = Private->PciIo;
+ Status = PciIo->Mem.Write (
+ PciIo,
+ EfiPciIoWidthUint64,
+ NVME_BAR,
+ NVME_ASQ_OFFSET,
+ 1,
+ Asq
+ );
+
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+
+ DEBUG ((EFI_D_INFO, "Asq.Asqb: %lx\n", Asq->Asqb));
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Read Nvm Express admin completion queue base address register.
+
+ @param Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure.
+ @param Acq The buffer used to store admin completion queue base address register content.
+
+ @return EFI_SUCCESS Successfully read the admin completion queue base address register content.
+ @return EFI_DEVICE_ERROR Fail to read the admin completion queue base address register.
+
+**/
+EFI_STATUS
+ReadNvmeAdminCompletionQueueBaseAddress (
+ IN NVME_CONTROLLER_PRIVATE_DATA *Private,
+ IN NVME_ACQ *Acq
+ )
+{
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ EFI_STATUS Status;
+
+ PciIo = Private->PciIo;
+ Status = PciIo->Mem.Read (
+ PciIo,
+ EfiPciIoWidthUint64,
+ NVME_BAR,
+ NVME_ACQ_OFFSET,
+ 1,
+ Acq
+ );
+
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Write Nvm Express admin completion queue base address register.
+
+ @param Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure.
+ @param Acq The buffer used to store the content to be written into admin completion queue base address register.
+
+ @return EFI_SUCCESS Successfully write data into the admin completion queue base address register.
+ @return EFI_DEVICE_ERROR Fail to write data into the admin completion queue base address register.
+
+**/
+EFI_STATUS
+WriteNvmeAdminCompletionQueueBaseAddress (
+ IN NVME_CONTROLLER_PRIVATE_DATA *Private,
+ IN NVME_ACQ *Acq
+ )
+{
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ EFI_STATUS Status;
+
+ PciIo = Private->PciIo;
+ Status = PciIo->Mem.Write (
+ PciIo,
+ EfiPciIoWidthUint64,
+ NVME_BAR,
+ NVME_ACQ_OFFSET,
+ 1,
+ Acq
+ );
+
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+
+ DEBUG ((EFI_D_INFO, "Acq.Acqb: %lxh\n", Acq->Acqb));
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Disable the Nvm Express controller.
+
+ @param Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure.
+
+ @return EFI_SUCCESS Successfully disable the controller.
+ @return EFI_DEVICE_ERROR Fail to disable the controller.
+
+**/
+EFI_STATUS
+NvmeDisableController (
+ IN NVME_CONTROLLER_PRIVATE_DATA *Private
+ )
+{
+ NVME_CC Cc;
+ NVME_CSTS Csts;
+ EFI_STATUS Status;
+
+ //
+ // Read Controller Configuration Register.
+ //
+ Status = ReadNvmeControllerConfiguration (Private, &Cc);
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+
+ Cc.En = 0;
+
+ //
+ // Disable the controller.
+ //
+ Status = WriteNvmeControllerConfiguration (Private, &Cc);
+
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+
+ gBS->Stall(10000);
+
+ //
+ // Check if the controller is reset
+ //
+ Status = ReadNvmeControllerStatus (Private, &Csts);
+
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+
+ if (Csts.Rdy != 0) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ DEBUG ((EFI_D_INFO, "NVMe controller is disabled with status [%r].\n", Status));
+ return Status;
+}
+
+/**
+ Enable the Nvm Express controller.
+
+ @param Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure.
+
+ @return EFI_SUCCESS Successfully enable the controller.
+ @return EFI_DEVICE_ERROR Fail to enable the controller.
+ @return EFI_TIMEOUT Fail to enable the controller in given time slot.
+
+**/
+EFI_STATUS
+NvmeEnableController (
+ IN NVME_CONTROLLER_PRIVATE_DATA *Private
+ )
+{
+ NVME_CC Cc;
+ NVME_CSTS Csts;
+ EFI_STATUS Status;
+ UINT32 Index;
+ UINT8 Timeout;
+
+ //
+ // Enable the controller
+ //
+ ZeroMem (&Cc, sizeof (NVME_CC));
+ Cc.En = 1;
+ Cc.Iosqes = 6;
+ Cc.Iocqes = 4;
+ Status = WriteNvmeControllerConfiguration (Private, &Cc);
+
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+
+ //
+ // Cap.To specifies max delay time in 500ms increments for Csts.Rdy to set after
+ // Cc.Enable. Loop produces a 1 millisecond delay per itteration, up to 500 * Cap.To.
+ //
+ if (Private->Cap.To == 0) {
+ Timeout = 1;
+ } else {
+ Timeout = Private->Cap.To;
+ }
+
+ for(Index = (Timeout * 500); Index != 0; --Index) {
+ gBS->Stall(1000);
+
+ //
+ // Check if the controller is initialized
+ //
+ Status = ReadNvmeControllerStatus (Private, &Csts);
+
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+
+ if (Csts.Rdy) {
+ break;
+ }
+ }
+
+ if (Index == 0) {
+ Status = EFI_TIMEOUT;
+ }
+
+ DEBUG ((EFI_D_INFO, "NVMe controller is enabled with status [%r].\n", Status));
+ return Status;
+}
+
+/**
+ Get identify controller data.
+
+ @param Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure.
+ @param Buffer The buffer used to store the identify controller data.
+
+ @return EFI_SUCCESS Successfully get the identify controller data.
+ @return EFI_DEVICE_ERROR Fail to get the identify controller data.
+
+**/
+EFI_STATUS
+NvmeIdentifyController (
+ IN NVME_CONTROLLER_PRIVATE_DATA *Private,
+ IN VOID *Buffer
+ )
+{
+ NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket;
+ NVM_EXPRESS_COMMAND Command;
+ NVM_EXPRESS_RESPONSE Response;
+ EFI_STATUS Status;
+
+ ZeroMem (&CommandPacket, sizeof(NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));
+ ZeroMem (&Command, sizeof(NVM_EXPRESS_COMMAND));
+ ZeroMem (&Response, sizeof(NVM_EXPRESS_RESPONSE));
+
+ Command.Cdw0.Opcode = NVME_ADMIN_IDENTIFY_OPC;
+ Command.Cdw0.Cid = Private->Cid[0]++;
+ //
+ // According to Nvm Express 1.1 spec Figure 38, When not used, the field shall be cleared to 0h.
+ // For the Identify command, the Namespace Identifier is only used for the Namespace data structure.
+ //
+ Command.Nsid = 0;
+
+ CommandPacket.NvmeCmd = &Command;
+ CommandPacket.NvmeResponse = &Response;
+ CommandPacket.TransferBuffer = Buffer;
+ CommandPacket.TransferLength = sizeof (NVME_ADMIN_CONTROLLER_DATA);
+ CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;
+ CommandPacket.QueueId = NVME_ADMIN_QUEUE;
+ //
+ // Set bit 0 (Cns bit) to 1 to identify a controller
+ //
+ Command.Cdw10 = 1;
+ Command.Flags = CDW10_VALID;
+
+ Status = Private->Passthru.PassThru (
+ &Private->Passthru,
+ NVME_CONTROLLER_ID,
+ 0,
+ &CommandPacket,
+ NULL
+ );
+
+ return Status;
+}
+
+/**
+ Get specified identify namespace data.
+
+ @param Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure.
+ @param NamespaceId The specified namespace identifier.
+ @param Buffer The buffer used to store the identify namespace data.
+
+ @return EFI_SUCCESS Successfully get the identify namespace data.
+ @return EFI_DEVICE_ERROR Fail to get the identify namespace data.
+
+**/
+EFI_STATUS
+NvmeIdentifyNamespace (
+ IN NVME_CONTROLLER_PRIVATE_DATA *Private,
+ IN UINT32 NamespaceId,
+ IN VOID *Buffer
+ )
+{
+ NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket;
+ NVM_EXPRESS_COMMAND Command;
+ NVM_EXPRESS_RESPONSE Response;
+ EFI_STATUS Status;
+
+ ZeroMem (&CommandPacket, sizeof(NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));
+ ZeroMem (&Command, sizeof(NVM_EXPRESS_COMMAND));
+ ZeroMem (&Response, sizeof(NVM_EXPRESS_RESPONSE));
+
+ CommandPacket.NvmeCmd = &Command;
+ CommandPacket.NvmeResponse = &Response;
+
+ Command.Cdw0.Opcode = NVME_ADMIN_IDENTIFY_OPC;
+ Command.Cdw0.Cid = Private->Cid[0]++;
+ Command.Nsid = NamespaceId;
+ CommandPacket.TransferBuffer = Buffer;
+ CommandPacket.TransferLength = sizeof (NVME_ADMIN_NAMESPACE_DATA);
+ CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;
+ CommandPacket.QueueId = NVME_ADMIN_QUEUE;
+ //
+ // Set bit 0 (Cns bit) to 1 to identify a namespace
+ //
+ CommandPacket.NvmeCmd->Cdw10 = 0;
+ CommandPacket.NvmeCmd->Flags = CDW10_VALID;
+
+ Status = Private->Passthru.PassThru (
+ &Private->Passthru,
+ NamespaceId,
+ 0,
+ &CommandPacket,
+ NULL
+ );
+
+ return Status;
+}
+
+/**
+ Create io completion queue.
+
+ @param Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure.
+
+ @return EFI_SUCCESS Successfully create io completion queue.
+ @return EFI_DEVICE_ERROR Fail to create io completion queue.
+
+**/
+EFI_STATUS
+NvmeCreateIoCompletionQueue (
+ IN NVME_CONTROLLER_PRIVATE_DATA *Private
+ )
+{
+ NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket;
+ NVM_EXPRESS_COMMAND Command;
+ NVM_EXPRESS_RESPONSE Response;
+ EFI_STATUS Status;
+ NVME_ADMIN_CRIOCQ CrIoCq;
+
+ ZeroMem (&CommandPacket, sizeof(NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));
+ ZeroMem (&Command, sizeof(NVM_EXPRESS_COMMAND));
+ ZeroMem (&Response, sizeof(NVM_EXPRESS_RESPONSE));
+ ZeroMem (&CrIoCq, sizeof(NVME_ADMIN_CRIOCQ));
+
+ CommandPacket.NvmeCmd = &Command;
+ CommandPacket.NvmeResponse = &Response;
+
+ Command.Cdw0.Opcode = NVME_ADMIN_CRIOCQ_OPC;
+ Command.Cdw0.Cid = Private->Cid[0]++;
+ CommandPacket.TransferBuffer = Private->CqBufferPciAddr[1];
+ CommandPacket.TransferLength = EFI_PAGE_SIZE;
+ CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;
+ CommandPacket.QueueId = NVME_ADMIN_QUEUE;
+
+ CrIoCq.Qid = NVME_IO_QUEUE;
+ CrIoCq.Qsize = NVME_CCQ_SIZE;
+ CrIoCq.Pc = 1;
+ CopyMem (&CommandPacket.NvmeCmd->Cdw10, &CrIoCq, sizeof (NVME_ADMIN_CRIOCQ));
+ CommandPacket.NvmeCmd->Flags = CDW10_VALID | CDW11_VALID;
+
+ Status = Private->Passthru.PassThru (
+ &Private->Passthru,
+ 0,
+ 0,
+ &CommandPacket,
+ NULL
+ );
+
+ return Status;
+}
+
+/**
+ Create io submission queue.
+
+ @param Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure.
+
+ @return EFI_SUCCESS Successfully create io submission queue.
+ @return EFI_DEVICE_ERROR Fail to create io submission queue.
+
+**/
+EFI_STATUS
+NvmeCreateIoSubmissionQueue (
+ IN NVME_CONTROLLER_PRIVATE_DATA *Private
+ )
+{
+ NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket;
+ NVM_EXPRESS_COMMAND Command;
+ NVM_EXPRESS_RESPONSE Response;
+ EFI_STATUS Status;
+ NVME_ADMIN_CRIOSQ CrIoSq;
+
+ ZeroMem (&CommandPacket, sizeof(NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));
+ ZeroMem (&Command, sizeof(NVM_EXPRESS_COMMAND));
+ ZeroMem (&Response, sizeof(NVM_EXPRESS_RESPONSE));
+ ZeroMem (&CrIoSq, sizeof(NVME_ADMIN_CRIOSQ));
+
+ CommandPacket.NvmeCmd = &Command;
+ CommandPacket.NvmeResponse = &Response;
+
+ Command.Cdw0.Opcode = NVME_ADMIN_CRIOSQ_OPC;
+ Command.Cdw0.Cid = Private->Cid[0]++;
+ CommandPacket.TransferBuffer = Private->SqBufferPciAddr[1];
+ CommandPacket.TransferLength = EFI_PAGE_SIZE;
+ CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;
+ CommandPacket.QueueId = NVME_ADMIN_QUEUE;
+
+ CrIoSq.Qid = NVME_IO_QUEUE;
+ CrIoSq.Qsize = NVME_CSQ_SIZE;
+ CrIoSq.Pc = 1;
+ CrIoSq.Cqid = NVME_IO_QUEUE;
+ CrIoSq.Qprio = 0;
+ CopyMem (&CommandPacket.NvmeCmd->Cdw10, &CrIoSq, sizeof (NVME_ADMIN_CRIOSQ));
+ CommandPacket.NvmeCmd->Flags = CDW10_VALID | CDW11_VALID;
+
+ Status = Private->Passthru.PassThru (
+ &Private->Passthru,
+ 0,
+ 0,
+ &CommandPacket,
+ NULL
+ );
+
+ return Status;
+}
+
+/**
+ Initialize the Nvm Express controller.
+
+ @param[in] Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure.
+
+ @retval EFI_SUCCESS The NVM Express Controller is initialized successfully.
+ @retval Others A device error occurred while initializing the controller.
+
+**/
+EFI_STATUS
+NvmeControllerInit (
+ IN NVME_CONTROLLER_PRIVATE_DATA *Private
+ )
+{
+ EFI_STATUS Status;
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ UINT64 Supports;
+ NVME_AQA Aqa;
+ NVME_ASQ Asq;
+ NVME_ACQ Acq;
+
+ //
+ // Save original PCI attributes and enable this controller.
+ //
+ PciIo = Private->PciIo;
+ Status = PciIo->Attributes (
+ PciIo,
+ EfiPciIoAttributeOperationGet,
+ 0,
+ &Private->PciAttributes
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = PciIo->Attributes (
+ PciIo,
+ EfiPciIoAttributeOperationSupported,
+ 0,
+ &Supports
+ );
+
+ if (!EFI_ERROR (Status)) {
+ Supports &= EFI_PCI_DEVICE_ENABLE;
+ Status = PciIo->Attributes (
+ PciIo,
+ EfiPciIoAttributeOperationEnable,
+ Supports,
+ NULL
+ );
+ }
+
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_INFO, "NvmeControllerInit: failed to enable controller\n"));
+ return Status;
+ }
+
+ //
+ // Read the Controller Capabilities register and verify that the NVM command set is supported
+ //
+ Status = ReadNvmeControllerCapabilities (Private, &Private->Cap);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (Private->Cap.Css != 0x01) {
+ DEBUG ((EFI_D_INFO, "NvmeControllerInit: the controller doesn't support NVMe command set\n"));
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // Currently the driver only supports 4k page size.
+ //
+ ASSERT ((Private->Cap.Mpsmin + 12) <= EFI_PAGE_SHIFT);
+
+ Private->Cid[0] = 0;
+ Private->Cid[1] = 0;
+
+ Status = NvmeDisableController (Private);
+
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+
+ //
+ // set number of entries admin submission & completion queues.
+ //
+ Aqa.Asqs = NVME_ASQ_SIZE;
+ Aqa.Acqs = NVME_ACQ_SIZE;
+
+ //
+ // Address of admin submission queue.
+ //
+ Asq.Rsvd1 = 0;
+ Asq.Asqb = (UINT64)(UINTN)(Private->BufferPciAddr) >> 12;
+
+ //
+ // Address of admin completion queue.
+ //
+ Acq.Rsvd1 = 0;
+ Acq.Acqb = (UINT64)(UINTN)(Private->BufferPciAddr + EFI_PAGE_SIZE) >> 12;
+
+ //
+ // Address of I/O submission & completion queue.
+ //
+ Private->SqBuffer[0] = (NVME_SQ *)(UINTN)(Private->Buffer);
+ Private->SqBufferPciAddr[0] = (NVME_SQ *)(UINTN)(Private->BufferPciAddr);
+ Private->CqBuffer[0] = (NVME_CQ *)(UINTN)(Private->Buffer + 1 * EFI_PAGE_SIZE);
+ Private->CqBufferPciAddr[0] = (NVME_CQ *)(UINTN)(Private->BufferPciAddr + 1 * EFI_PAGE_SIZE);
+ Private->SqBuffer[1] = (NVME_SQ *)(UINTN)(Private->Buffer + 2 * EFI_PAGE_SIZE);
+ Private->SqBufferPciAddr[1] = (NVME_SQ *)(UINTN)(Private->BufferPciAddr + 2 * EFI_PAGE_SIZE);
+ Private->CqBuffer[1] = (NVME_CQ *)(UINTN)(Private->Buffer + 3 * EFI_PAGE_SIZE);
+ Private->CqBufferPciAddr[1] = (NVME_CQ *)(UINTN)(Private->BufferPciAddr + 3 * EFI_PAGE_SIZE);
+
+ DEBUG ((EFI_D_INFO, "Private->Buffer = [%016X]\n", (UINT64)(UINTN)Private->Buffer));
+ DEBUG ((EFI_D_INFO, "Admin Submission Queue size (Aqa.Asqs) = [%08X]\n", Aqa.Asqs));
+ DEBUG ((EFI_D_INFO, "Admin Completion Queue size (Aqa.Acqs) = [%08X]\n", Aqa.Acqs));
+ DEBUG ((EFI_D_INFO, "Admin Submission Queue (SqBuffer[0]) = [%016X]\n", Private->SqBuffer[0]));
+ DEBUG ((EFI_D_INFO, "Admin Completion Queue (CqBuffer[0]) = [%016X]\n", Private->CqBuffer[0]));
+ DEBUG ((EFI_D_INFO, "I/O Submission Queue (SqBuffer[1]) = [%016X]\n", Private->SqBuffer[1]));
+ DEBUG ((EFI_D_INFO, "I/O Completion Queue (CqBuffer[1]) = [%016X]\n", Private->CqBuffer[1]));
+
+ //
+ // Program admin queue attributes.
+ //
+ Status = WriteNvmeAdminQueueAttributes (Private, &Aqa);
+
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+
+ //
+ // Program admin submission queue address.
+ //
+ Status = WriteNvmeAdminSubmissionQueueBaseAddress (Private, &Asq);
+
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+
+ //
+ // Program admin completion queue address.
+ //
+ Status = WriteNvmeAdminCompletionQueueBaseAddress (Private, &Acq);
+
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+
+ Status = NvmeEnableController (Private);
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+
+ //
+ // Create one I/O completion queue.
+ //
+ Status = NvmeCreateIoCompletionQueue (Private);
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+
+ //
+ // Create one I/O Submission queue.
+ //
+ Status = NvmeCreateIoSubmissionQueue (Private);
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+
+ //
+ // Allocate buffer for Identify Controller data
+ //
+ Private->ControllerData = (NVME_ADMIN_CONTROLLER_DATA *)AllocateZeroPool (sizeof(NVME_ADMIN_CONTROLLER_DATA));
+
+ if (Private->ControllerData == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Get current Identify Controller Data
+ //
+ Status = NvmeIdentifyController (Private, Private->ControllerData);
+
+ if (EFI_ERROR(Status)) {
+ FreePool(Private->ControllerData);
+ Private->ControllerData = NULL;
+ return EFI_NOT_FOUND;
+ }
+ return Status;
+}
+
diff --git a/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressHci.h b/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressHci.h
new file mode 100644
index 0000000000..406cef4f8e
--- /dev/null
+++ b/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressHci.h
@@ -0,0 +1,790 @@
+/** @file
+ NvmExpressDxe driver is used to manage non-volatile memory subsystem which follows
+ NVM Express specification.
+
+ Copyright (c) 2013, Intel Corporation. All rights reserved.<BR>
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _NVME_HCI_H_
+#define _NVME_HCI_H_
+
+#define NVME_BAR 0
+
+//
+// controller register offsets
+//
+#define NVME_CAP_OFFSET 0x0000 // Controller Capabilities
+#define NVME_VER_OFFSET 0x0008 // Version
+#define NVME_INTMS_OFFSET 0x000c // Interrupt Mask Set
+#define NVME_INTMC_OFFSET 0x0010 // Interrupt Mask Clear
+#define NVME_CC_OFFSET 0x0014 // Controller Configuration
+#define NVME_CSTS_OFFSET 0x001c // Controller Status
+#define NVME_AQA_OFFSET 0x0024 // Admin Queue Attributes
+#define NVME_ASQ_OFFSET 0x0028 // Admin Submission Queue Base Address
+#define NVME_ACQ_OFFSET 0x0030 // Admin Completion Queue Base Address
+#define NVME_SQ0_OFFSET 0x1000 // Submission Queue 0 (admin) Tail Doorbell
+#define NVME_CQ0_OFFSET 0x1004 // Completion Queue 0 (admin) Head Doorbell
+
+//
+// These register offsets are defined as 0x1000 + (N * (4 << CAP.DSTRD))
+// Get the doorbell stride bit shift value from the controller capabilities.
+//
+#define NVME_SQTDBL_OFFSET(QID, DSTRD) 0x1000 + ((2 * (QID)) * (4 << (DSTRD))) // Submission Queue y (NVM) Tail Doorbell
+#define NVME_CQHDBL_OFFSET(QID, DSTRD) 0x1000 + (((2 * (QID)) + 1) * (4 << (DSTRD))) // Completion Queue y (NVM) Head Doorbell
+
+
+#pragma pack(1)
+
+//
+// 3.1.1 Offset 00h: CAP - Controller Capabilities
+//
+typedef struct {
+ UINT16 Mqes; // Maximum Queue Entries Supported
+ UINT8 Cqr:1; // Contiguous Queues Required
+ UINT8 Ams:2; // Arbitration Mechanism Supported
+ UINT8 Rsvd1:5;
+ UINT8 To; // Timeout
+ UINT16 Dstrd:4;
+ UINT16 Rsvd2:1;
+ UINT16 Css:4; // Command Sets Supported
+ UINT16 Rsvd3:7;
+ UINT8 Mpsmin:4;
+ UINT8 Mpsmax:4;
+ UINT8 Rsvd4;
+} NVME_CAP;
+
+//
+// 3.1.2 Offset 08h: VS - Version
+//
+typedef struct {
+ UINT16 Mnr; // Minor version number
+ UINT16 Mjr; // Major version number
+} NVME_VER;
+
+//
+// 3.1.5 Offset 14h: CC - Controller Configuration
+//
+typedef struct {
+ UINT16 En:1; // Enable
+ UINT16 Rsvd1:3;
+ UINT16 Css:3; // Command Set Selected
+ UINT16 Mps:4; // Memory Page Size
+ UINT16 Ams:3; // Arbitration Mechanism Selected
+ UINT16 Shn:2; // Shutdown Notification
+ UINT8 Iosqes:4; // I/O Submission Queue Entry Size
+ UINT8 Iocqes:4; // I/O Completion Queue Entry Size
+ UINT8 Rsvd2;
+} NVME_CC;
+
+//
+// 3.1.6 Offset 1Ch: CSTS - Controller Status
+//
+typedef struct {
+ UINT32 Rdy:1; // Ready
+ UINT32 Cfs:1; // Controller Fatal Status
+ UINT32 Shst:2; // Shutdown Status
+ UINT32 Nssro:1; // NVM Subsystem Reset Occurred
+ UINT32 Rsvd1:27;
+} NVME_CSTS;
+
+//
+// 3.1.8 Offset 24h: AQA - Admin Queue Attributes
+//
+typedef struct {
+ UINT16 Asqs:12; // Submission Queue Size
+ UINT16 Rsvd1:4;
+ UINT16 Acqs:12; // Completion Queue Size
+ UINT16 Rsvd2:4;
+} NVME_AQA;
+
+//
+// 3.1.9 Offset 28h: ASQ - Admin Submission Queue Base Address
+//
+typedef struct {
+ UINT64 Rsvd1:12;
+ UINT64 Asqb:52; // Admin Submission Queue Base Address
+} NVME_ASQ;
+
+//
+// 3.1.10 Offset 30h: ACQ - Admin Completion Queue Base Address
+//
+typedef struct {
+ UINT64 Rsvd1:12;
+ UINT64 Acqb:52; // Admin Completion Queue Base Address
+} NVME_ACQ;
+
+//
+// 3.1.11 Offset (1000h + ((2y) * (4 << CAP.DSTRD))): SQyTDBL - Submission Queue y Tail Doorbell
+//
+typedef struct {
+ UINT16 Sqt;
+ UINT16 Rsvd1;
+} NVME_SQTDBL;
+
+//
+// 3.1.12 Offset (1000h + ((2y + 1) * (4 << CAP.DSTRD))): CQyHDBL - Completion Queue y Head Doorbell
+//
+typedef struct {
+ UINT16 Cqh;
+ UINT16 Rsvd1;
+} NVME_CQHDBL;
+
+//
+// NVM command set structures
+//
+// Read Command
+//
+typedef struct {
+ //
+ // CDW 10, 11
+ //
+ UINT64 Slba; /* Starting Sector Address */
+ //
+ // CDW 12
+ //
+ UINT16 Nlb; /* Number of Sectors */
+ UINT16 Rsvd1:10;
+ UINT16 Prinfo:4; /* Protection Info Check */
+ UINT16 Fua:1; /* Force Unit Access */
+ UINT16 Lr:1; /* Limited Retry */
+ //
+ // CDW 13
+ //
+ UINT32 Af:4; /* Access Frequency */
+ UINT32 Al:2; /* Access Latency */
+ UINT32 Sr:1; /* Sequential Request */
+ UINT32 In:1; /* Incompressible */
+ UINT32 Rsvd2:24;
+ //
+ // CDW 14
+ //
+ UINT32 Eilbrt; /* Expected Initial Logical Block Reference Tag */
+ //
+ // CDW 15
+ //
+ UINT16 Elbat; /* Expected Logical Block Application Tag */
+ UINT16 Elbatm; /* Expected Logical Block Application Tag Mask */
+} NVME_READ;
+
+//
+// Write Command
+//
+typedef struct {
+ //
+ // CDW 10, 11
+ //
+ UINT64 Slba; /* Starting Sector Address */
+ //
+ // CDW 12
+ //
+ UINT16 Nlb; /* Number of Sectors */
+ UINT16 Rsvd1:10;
+ UINT16 Prinfo:4; /* Protection Info Check */
+ UINT16 Fua:1; /* Force Unit Access */
+ UINT16 Lr:1; /* Limited Retry */
+ //
+ // CDW 13
+ //
+ UINT32 Af:4; /* Access Frequency */
+ UINT32 Al:2; /* Access Latency */
+ UINT32 Sr:1; /* Sequential Request */
+ UINT32 In:1; /* Incompressible */
+ UINT32 Rsvd2:24;
+ //
+ // CDW 14
+ //
+ UINT32 Ilbrt; /* Initial Logical Block Reference Tag */
+ //
+ // CDW 15
+ //
+ UINT16 Lbat; /* Logical Block Application Tag */
+ UINT16 Lbatm; /* Logical Block Application Tag Mask */
+} NVME_WRITE;
+
+//
+// Flush
+//
+typedef struct {
+ //
+ // CDW 10
+ //
+ UINT32 Flush; /* Flush */
+} NVME_FLUSH;
+
+//
+// Write Uncorrectable command
+//
+typedef struct {
+ //
+ // CDW 10, 11
+ //
+ UINT64 Slba; /* Starting LBA */
+ //
+ // CDW 12
+ //
+ UINT32 Nlb:16; /* Number of Logical Blocks */
+ UINT32 Rsvd1:16;
+} NVME_WRITE_UNCORRECTABLE;
+
+//
+// Write Zeroes command
+//
+typedef struct {
+ //
+ // CDW 10, 11
+ //
+ UINT64 Slba; /* Starting LBA */
+ //
+ // CDW 12
+ //
+ UINT16 Nlb; /* Number of Logical Blocks */
+ UINT16 Rsvd1:10;
+ UINT16 Prinfo:4; /* Protection Info Check */
+ UINT16 Fua:1; /* Force Unit Access */
+ UINT16 Lr:1; /* Limited Retry */
+ //
+ // CDW 13
+ //
+ UINT32 Rsvd2;
+ //
+ // CDW 14
+ //
+ UINT32 Ilbrt; /* Initial Logical Block Reference Tag */
+ //
+ // CDW 15
+ //
+ UINT16 Lbat; /* Logical Block Application Tag */
+ UINT16 Lbatm; /* Logical Block Application Tag Mask */
+} NVME_WRITE_ZEROES;
+
+//
+// Compare command
+//
+typedef struct {
+ //
+ // CDW 10, 11
+ //
+ UINT64 Slba; /* Starting LBA */
+ //
+ // CDW 12
+ //
+ UINT16 Nlb; /* Number of Logical Blocks */
+ UINT16 Rsvd1:10;
+ UINT16 Prinfo:4; /* Protection Info Check */
+ UINT16 Fua:1; /* Force Unit Access */
+ UINT16 Lr:1; /* Limited Retry */
+ //
+ // CDW 13
+ //
+ UINT32 Rsvd2;
+ //
+ // CDW 14
+ //
+ UINT32 Eilbrt; /* Expected Initial Logical Block Reference Tag */
+ //
+ // CDW 15
+ //
+ UINT16 Elbat; /* Expected Logical Block Application Tag */
+ UINT16 Elbatm; /* Expected Logical Block Application Tag Mask */
+} NVME_COMPARE;
+
+typedef union {
+ NVME_READ Read;
+ NVME_WRITE Write;
+ NVME_FLUSH Flush;
+ NVME_WRITE_UNCORRECTABLE WriteUncorrectable;
+ NVME_WRITE_ZEROES WriteZeros;
+ NVME_COMPARE Compare;
+} NVME_CMD;
+
+typedef struct {
+ UINT16 Mp; /* Maximum Power */
+ UINT8 Rsvd1; /* Reserved as of Nvm Express 1.1 Spec */
+ UINT8 Mps:1; /* Max Power Scale */
+ UINT8 Nops:1; /* Non-Operational State */
+ UINT8 Rsvd2:6; /* Reserved as of Nvm Express 1.1 Spec */
+ UINT32 Enlat; /* Entry Latency */
+ UINT32 Exlat; /* Exit Latency */
+ UINT8 Rrt:5; /* Relative Read Throughput */
+ UINT8 Rsvd3:3; /* Reserved as of Nvm Express 1.1 Spec */
+ UINT8 Rrl:5; /* Relative Read Leatency */
+ UINT8 Rsvd4:3; /* Reserved as of Nvm Express 1.1 Spec */
+ UINT8 Rwt:5; /* Relative Write Throughput */
+ UINT8 Rsvd5:3; /* Reserved as of Nvm Express 1.1 Spec */
+ UINT8 Rwl:5; /* Relative Write Leatency */
+ UINT8 Rsvd6:3; /* Reserved as of Nvm Express 1.1 Spec */
+ UINT8 Rsvd7[16]; /* Reserved as of Nvm Express 1.1 Spec */
+} NVME_PSDESCRIPTOR;
+
+//
+// Identify Controller Data
+//
+typedef struct {
+ //
+ // Controller Capabilities and Features 0-255
+ //
+ UINT16 Vid; /* PCI Vendor ID */
+ UINT16 Ssvid; /* PCI sub-system vendor ID */
+ UINT8 Sn[20]; /* Produce serial number */
+
+ UINT8 Mn[40]; /* Proeduct model number */
+ UINT8 Fr[8]; /* Firmware Revision */
+ UINT8 Rab; /* Recommended Arbitration Burst */
+ UINT8 Ieee_oiu[3]; /* Organization Unique Identifier */
+ UINT8 Cmic; /* Multi-interface Capabilities */
+ UINT8 Mdts; /* Maximum Data Transfer Size */
+ UINT8 Cntlid[2]; /* Controller ID */
+ UINT8 Rsvd1[176]; /* Reserved as of Nvm Express 1.1 Spec */
+ //
+ // Admin Command Set Attributes
+ //
+ UINT16 Oacs; /* Optional Admin Command Support */
+ UINT8 Acl; /* Abort Command Limit */
+ UINT8 Aerl; /* Async Event Request Limit */
+ UINT8 Frmw; /* Firmware updates */
+ UINT8 Lpa; /* Log Page Attributes */
+ UINT8 Elpe; /* Error Log Page Entries */
+ UINT8 Npss; /* Number of Power States Support */
+ UINT8 Avscc; /* Admin Vendor Specific Command Configuration */
+ UINT8 Apsta; /* Autonomous Power State Transition Attributes */
+ UINT8 Rsvd2[246]; /* Reserved as of Nvm Express 1.1 Spec */
+ //
+ // NVM Command Set Attributes
+ //
+ UINT8 Sqes; /* Submission Queue Entry Size */
+ UINT8 Cqes; /* Completion Queue Entry Size */
+ UINT16 Rsvd3; /* Reserved as of Nvm Express 1.1 Spec */
+ UINT32 Nn; /* Number of Namespaces */
+ UINT16 Oncs; /* Optional NVM Command Support */
+ UINT16 Fuses; /* Fused Operation Support */
+ UINT8 Fna; /* Format NVM Attributes */
+ UINT8 Vwc; /* Volatile Write Cache */
+ UINT16 Awun; /* Atomic Write Unit Normal */
+ UINT16 Awupf; /* Atomic Write Unit Power Fail */
+ UINT8 Nvscc; /* NVM Vendor Specific Command Configuration */
+ UINT8 Rsvd4; /* Reserved as of Nvm Express 1.1 Spec */
+ UINT16 Acwu; /* Atomic Compare & Write Unit */
+ UINT16 Rsvd5; /* Reserved as of Nvm Express 1.1 Spec */
+ UINT32 Sgls; /* SGL Support */
+ UINT8 Rsvd6[164]; /* Reserved as of Nvm Express 1.1 Spec */
+ //
+ // I/O Command set Attributes
+ //
+ UINT8 Rsvd7[1344]; /* Reserved as of Nvm Express 1.1 Spec */
+ //
+ // Power State Descriptors
+ //
+ NVME_PSDESCRIPTOR PsDescriptor[32];
+
+ UINT8 VendorData[1024]; /* Vendor specific data */
+} NVME_ADMIN_CONTROLLER_DATA;
+
+typedef struct {
+ UINT16 Ms; /* Metadata Size */
+ UINT8 Lbads; /* LBA Data Size */
+ UINT8 Rp:2; /* Relative Performance */
+ #define LBAF_RP_BEST 00b
+ #define LBAF_RP_BETTER 01b
+ #define LBAF_RP_GOOD 10b
+ #define LBAF_RP_DEGRADED 11b
+ UINT8 Rsvd1:6; /* Reserved as of Nvm Express 1.1 Spec */
+} NVME_LBAFORMAT;
+
+//
+// Identify Namespace Data
+//
+typedef struct {
+ //
+ // NVM Command Set Specific
+ //
+ UINT64 Nsze; /* Namespace Size (total number of blocks in formatted namespace) */
+ UINT64 Ncap; /* Namespace Capacity (max number of logical blocks) */
+ UINT64 Nuse; /* Namespace Utilization */
+ UINT8 Nsfeat; /* Namespace Features */
+ UINT8 Nlbaf; /* Number of LBA Formats */
+ UINT8 Flbas; /* Formatted LBA size */
+ UINT8 Mc; /* Metadata Capabilities */
+ UINT8 Dpc; /* End-to-end Data Protection capabilities */
+ UINT8 Dps; /* End-to-end Data Protection Type Settings */
+ UINT8 Nmic; /* Namespace Multi-path I/O and Namespace Sharing Capabilities */
+ UINT8 Rescap; /* Reservation Capabilities */
+ UINT8 Rsvd1[88]; /* Reserved as of Nvm Express 1.1 Spec */
+ UINT64 Eui64; /* IEEE Extended Unique Identifier */
+ //
+ // LBA Format
+ //
+ NVME_LBAFORMAT LbaFormat[16];
+
+ UINT8 Rsvd2[192]; /* Reserved as of Nvm Express 1.1 Spec */
+ UINT8 VendorData[3712]; /* Vendor specific data */
+} NVME_ADMIN_NAMESPACE_DATA;
+
+//
+// NvmExpress Admin Identify Cmd
+//
+typedef struct {
+ //
+ // CDW 10
+ //
+ UINT32 Cns:2;
+ UINT32 Rsvd1:30;
+} NVME_ADMIN_IDENTIFY;
+
+//
+// NvmExpress Admin Create I/O Completion Queue
+//
+typedef struct {
+ //
+ // CDW 10
+ //
+ UINT32 Qid:16; /* Queue Identifier */
+ UINT32 Qsize:16; /* Queue Size */
+
+ //
+ // CDW 11
+ //
+ UINT32 Pc:1; /* Physically Contiguous */
+ UINT32 Ien:1; /* Interrupts Enabled */
+ UINT32 Rsvd1:14; /* reserved as of Nvm Express 1.1 Spec */
+ UINT32 Iv:16; /* Interrupt Vector */
+} NVME_ADMIN_CRIOCQ;
+
+//
+// NvmExpress Admin Create I/O Submission Queue
+//
+typedef struct {
+ //
+ // CDW 10
+ //
+ UINT32 Qid:16; /* Queue Identifier */
+ UINT32 Qsize:16; /* Queue Size */
+
+ //
+ // CDW 11
+ //
+ UINT32 Pc:1; /* Physically Contiguous */
+ UINT32 Qprio:2; /* Queue Priority */
+ UINT32 Rsvd1:13; /* Reserved as of Nvm Express 1.1 Spec */
+ UINT32 Cqid:16; /* Completion Queue ID */
+} NVME_ADMIN_CRIOSQ;
+
+//
+// NvmExpress Admin Delete I/O Completion Queue
+//
+typedef struct {
+ //
+ // CDW 10
+ //
+ UINT16 Qid;
+ UINT16 Rsvd1;
+} NVME_ADMIN_DEIOCQ;
+
+//
+// NvmExpress Admin Delete I/O Submission Queue
+//
+typedef struct {
+ //
+ // CDW 10
+ //
+ UINT16 Qid;
+ UINT16 Rsvd1;
+} NVME_ADMIN_DEIOSQ;
+
+//
+// NvmExpress Admin Abort Command
+//
+typedef struct {
+ //
+ // CDW 10
+ //
+ UINT32 Sqid:16; /* Submission Queue identifier */
+ UINT32 Cid:16; /* Command Identifier */
+} NVME_ADMIN_ABORT;
+
+//
+// NvmExpress Admin Firmware Activate Command
+//
+typedef struct {
+ //
+ // CDW 10
+ //
+ UINT32 Fs:3; /* Submission Queue identifier */
+ UINT32 Aa:2; /* Command Identifier */
+ UINT32 Rsvd1:27;
+} NVME_ADMIN_FIRMWARE_ACTIVATE;
+
+//
+// NvmExpress Admin Firmware Image Download Command
+//
+typedef struct {
+ //
+ // CDW 10
+ //
+ UINT32 Numd; /* Number of Dwords */
+ //
+ // CDW 11
+ //
+ UINT32 Ofst; /* Offset */
+} NVME_ADMIN_FIRMWARE_IMAGE_DOWNLOAD;
+
+//
+// NvmExpress Admin Get Features Command
+//
+typedef struct {
+ //
+ // CDW 10
+ //
+ UINT32 Fid:8; /* Feature Identifier */
+ UINT32 Sel:3; /* Select */
+ UINT32 Rsvd1:21;
+} NVME_ADMIN_GET_FEATURES;
+
+//
+// NvmExpress Admin Get Log Page Command
+//
+typedef struct {
+ //
+ // CDW 10
+ //
+ UINT32 Lid:8; /* Log Page Identifier */
+ #define LID_ERROR_INFO
+ #define LID_SMART_INFO
+ #define LID_FW_SLOT_INFO
+ UINT32 Rsvd1:8;
+ UINT32 Numd:12; /* Number of Dwords */
+ UINT32 Rsvd2:4; /* Reserved as of Nvm Express 1.1 Spec */
+} NVME_ADMIN_GET_LOG_PAGE;
+
+//
+// NvmExpress Admin Set Features Command
+//
+typedef struct {
+ //
+ // CDW 10
+ //
+ UINT32 Fid:8; /* Feature Identifier */
+ UINT32 Rsvd1:23;
+ UINT32 Sv:1; /* Save */
+} NVME_ADMIN_SET_FEATURES;
+
+//
+// NvmExpress Admin Format NVM Command
+//
+typedef struct {
+ //
+ // CDW 10
+ //
+ UINT32 Lbaf:4; /* LBA Format */
+ UINT32 Ms:1; /* Metadata Settings */
+ UINT32 Pi:3; /* Protection Information */
+ UINT32 Pil:1; /* Protection Information Location */
+ UINT32 Ses:3; /* Secure Erase Settings */
+ UINT32 Rsvd1:20;
+} NVME_ADMIN_FORMAT_NVM;
+
+//
+// NvmExpress Admin Security Receive Command
+//
+typedef struct {
+ //
+ // CDW 10
+ //
+ UINT32 Rsvd1:8;
+ UINT32 Spsp:16; /* SP Specific */
+ UINT32 Secp:8; /* Security Protocol */
+ //
+ // CDW 11
+ //
+ UINT32 Al; /* Allocation Length */
+} NVME_ADMIN_SECURITY_RECEIVE;
+
+//
+// NvmExpress Admin Security Send Command
+//
+typedef struct {
+ //
+ // CDW 10
+ //
+ UINT32 Rsvd1:8;
+ UINT32 Spsp:16; /* SP Specific */
+ UINT32 Secp:8; /* Security Protocol */
+ //
+ // CDW 11
+ //
+ UINT32 Tl; /* Transfer Length */
+} NVME_ADMIN_SECURITY_SEND;
+
+typedef union {
+ NVME_ADMIN_IDENTIFY Identify;
+ NVME_ADMIN_CRIOCQ CrIoCq;
+ NVME_ADMIN_CRIOSQ CrIoSq;
+ NVME_ADMIN_DEIOCQ DeIoCq;
+ NVME_ADMIN_DEIOSQ DeIoSq;
+ NVME_ADMIN_ABORT Abort;
+ NVME_ADMIN_FIRMWARE_ACTIVATE Activate;
+ NVME_ADMIN_FIRMWARE_IMAGE_DOWNLOAD FirmwareImageDownload;
+ NVME_ADMIN_GET_FEATURES GetFeatures;
+ NVME_ADMIN_GET_LOG_PAGE GetLogPage;
+ NVME_ADMIN_SET_FEATURES SetFeatures;
+ NVME_ADMIN_FORMAT_NVM FormatNvm;
+ NVME_ADMIN_SECURITY_RECEIVE SecurityReceive;
+ NVME_ADMIN_SECURITY_SEND SecuritySend;
+} NVME_ADMIN_CMD;
+
+typedef struct {
+ UINT32 Cdw10;
+ UINT32 Cdw11;
+ UINT32 Cdw12;
+ UINT32 Cdw13;
+ UINT32 Cdw14;
+ UINT32 Cdw15;
+} NVME_RAW;
+
+typedef union {
+ NVME_ADMIN_CMD Admin; // Union of Admin commands
+ NVME_CMD Nvm; // Union of Nvm commands
+ NVME_RAW Raw;
+} NVME_PAYLOAD;
+
+//
+// Submission Queue
+//
+typedef struct {
+ //
+ // CDW 0, Common to all comnmands
+ //
+ UINT8 Opc; // Opcode
+ UINT8 Fuse:2; // Fused Operation
+ UINT8 Rsvd1:5;
+ UINT8 Psdt:1; // PRP or SGL for Data Transfer
+ UINT16 Cid; // Command Identifier
+
+ //
+ // CDW 1
+ //
+ UINT32 Nsid; // Namespace Identifier
+
+ //
+ // CDW 2,3
+ //
+ UINT64 Rsvd2;
+
+ //
+ // CDW 4,5
+ //
+ UINT64 Mptr; // Metadata Pointer
+
+ //
+ // CDW 6-9
+ //
+ UINT64 Prp[2]; // First and second PRP entries
+
+ NVME_PAYLOAD Payload;
+
+} NVME_SQ;
+
+//
+// Completion Queue
+//
+typedef struct {
+ //
+ // CDW 0
+ //
+ UINT32 Dword0;
+ //
+ // CDW 1
+ //
+ UINT32 Rsvd1;
+ //
+ // CDW 2
+ //
+ UINT16 Sqhd; // Submission Queue Head Pointer
+ UINT16 Sqid; // Submission Queue Identifier
+ //
+ // CDW 3
+ //
+ UINT16 Cid; // Command Identifier
+ UINT16 Pt:1; // Phase Tag
+ UINT16 Sc:8; // Status Code
+ UINT16 Sct:3; // Status Code Type
+ UINT16 Rsvd2:2;
+ UINT16 Mo:1; // More
+ UINT16 Dnr:1; // Retry
+} NVME_CQ;
+
+//
+// Nvm Express Admin cmd opcodes
+//
+#define NVME_ADMIN_CRIOSQ_OPC 1
+#define NVME_ADMIN_CRIOCQ_OPC 5
+#define NVME_ADMIN_IDENTIFY_OPC 6
+
+#define NVME_IO_FLUSH_OPC 0
+#define NVME_IO_WRITE_OPC 1
+#define NVME_IO_READ_OPC 2
+
+//
+// Offset from the beginning of private data queue buffer
+//
+#define NVME_ASQ_BUF_OFFSET EFI_PAGE_SIZE
+
+/**
+ Initialize the Nvm Express controller.
+
+ @param[in] Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure.
+
+ @retval EFI_SUCCESS The NVM Express Controller is initialized successfully.
+ @retval Others A device error occurred while initializing the controller.
+
+**/
+EFI_STATUS
+NvmeControllerInit (
+ IN NVME_CONTROLLER_PRIVATE_DATA *Private
+ );
+
+/**
+ Get identify controller data.
+
+ @param Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure.
+ @param Buffer The buffer used to store the identify controller data.
+
+ @return EFI_SUCCESS Successfully get the identify controller data.
+ @return EFI_DEVICE_ERROR Fail to get the identify controller data.
+
+**/
+EFI_STATUS
+NvmeIdentifyController (
+ IN NVME_CONTROLLER_PRIVATE_DATA *Private,
+ IN VOID *Buffer
+ );
+
+/**
+ Get specified identify namespace data.
+
+ @param Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure.
+ @param NamespaceId The specified namespace identifier.
+ @param Buffer The buffer used to store the identify namespace data.
+
+ @return EFI_SUCCESS Successfully get the identify namespace data.
+ @return EFI_DEVICE_ERROR Fail to get the identify namespace data.
+
+**/
+EFI_STATUS
+NvmeIdentifyNamespace (
+ IN NVME_CONTROLLER_PRIVATE_DATA *Private,
+ IN UINT32 NamespaceId,
+ IN VOID *Buffer
+ );
+
+#pragma pack()
+
+#endif
+
diff --git a/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressPassthru.c b/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressPassthru.c
new file mode 100644
index 0000000000..d1231aca89
--- /dev/null
+++ b/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressPassthru.c
@@ -0,0 +1,884 @@
+/** @file
+ NvmExpressDxe driver is used to manage non-volatile memory subsystem which follows
+ NVM Express specification.
+
+ Copyright (c) 2013, Intel Corporation. All rights reserved.<BR>
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "NvmExpress.h"
+
+//
+// Page size should be set in the Controller Configuration register
+// during controller init, and the controller configuration save in
+// the controller's private data. The Max and Min supported page sizes
+// for the controller are specified in the Controller Capabilities register.
+//
+
+GLOBAL_REMOVE_IF_UNREFERENCED NVM_EXPRESS_PASS_THRU_MODE gNvmExpressPassThruMode = {
+ 0,
+ NVM_EXPRESS_PASS_THRU_ATTRIBUTES_PHYSICAL | NVM_EXPRESS_PASS_THRU_ATTRIBUTES_CMD_SET_NVME,
+ sizeof (UINTN),
+ 0x10000,
+ 0,
+ 0
+};
+
+
+/**
+ Dump the execution status from a given completion queue entry.
+
+ @param[in] Cq A pointer to the NVME_CQ item.
+
+**/
+VOID
+NvmeDumpStatus (
+ IN NVME_CQ *Cq
+ )
+{
+ DEBUG ((EFI_D_VERBOSE, "Dump NVMe Completion Entry Status from [0x%x]:\n", Cq));
+
+ DEBUG ((EFI_D_VERBOSE, " SQ Identifier : [0x%x], Phase Tag : [%d], Cmd Identifier : [0x%x]\n", Cq->Sqid, Cq->Pt, Cq->Cid));
+
+ DEBUG ((EFI_D_VERBOSE, " NVMe Cmd Execution Result - "));
+
+ switch (Cq->Sct) {
+ case 0x0:
+ switch (Cq->Sc) {
+ case 0x0:
+ DEBUG ((EFI_D_VERBOSE, "Successful Completion\n"));
+ break;
+ case 0x1:
+ DEBUG ((EFI_D_VERBOSE, "Invalid Command Opcode\n"));
+ break;
+ case 0x2:
+ DEBUG ((EFI_D_VERBOSE, "Invalid Field in Command\n"));
+ break;
+ case 0x3:
+ DEBUG ((EFI_D_VERBOSE, "Command ID Conflict\n"));
+ break;
+ case 0x4:
+ DEBUG ((EFI_D_VERBOSE, "Data Transfer Error\n"));
+ break;
+ case 0x5:
+ DEBUG ((EFI_D_VERBOSE, "Commands Aborted due to Power Loss Notification\n"));
+ break;
+ case 0x6:
+ DEBUG ((EFI_D_VERBOSE, "Internal Device Error\n"));
+ break;
+ case 0x7:
+ DEBUG ((EFI_D_VERBOSE, "Command Abort Requested\n"));
+ break;
+ case 0x8:
+ DEBUG ((EFI_D_VERBOSE, "Command Aborted due to SQ Deletion\n"));
+ break;
+ case 0x9:
+ DEBUG ((EFI_D_VERBOSE, "Command Aborted due to Failed Fused Command\n"));
+ break;
+ case 0xA:
+ DEBUG ((EFI_D_VERBOSE, "Command Aborted due to Missing Fused Command\n"));
+ break;
+ case 0xB:
+ DEBUG ((EFI_D_VERBOSE, "Invalid Namespace or Format\n"));
+ break;
+ case 0xC:
+ DEBUG ((EFI_D_VERBOSE, "Command Sequence Error\n"));
+ break;
+ case 0xD:
+ DEBUG ((EFI_D_VERBOSE, "Invalid SGL Last Segment Descriptor\n"));
+ break;
+ case 0xE:
+ DEBUG ((EFI_D_VERBOSE, "Invalid Number of SGL Descriptors\n"));
+ break;
+ case 0xF:
+ DEBUG ((EFI_D_VERBOSE, "Data SGL Length Invalid\n"));
+ break;
+ case 0x10:
+ DEBUG ((EFI_D_VERBOSE, "Metadata SGL Length Invalid\n"));
+ break;
+ case 0x11:
+ DEBUG ((EFI_D_VERBOSE, "SGL Descriptor Type Invalid\n"));
+ break;
+ case 0x80:
+ DEBUG ((EFI_D_VERBOSE, "LBA Out of Range\n"));
+ break;
+ case 0x81:
+ DEBUG ((EFI_D_VERBOSE, "Capacity Exceeded\n"));
+ break;
+ case 0x82:
+ DEBUG ((EFI_D_VERBOSE, "Namespace Not Ready\n"));
+ break;
+ case 0x83:
+ DEBUG ((EFI_D_VERBOSE, "Reservation Conflict\n"));
+ break;
+ }
+ break;
+
+ case 0x1:
+ switch (Cq->Sc) {
+ case 0x0:
+ DEBUG ((EFI_D_VERBOSE, "Completion Queue Invalid\n"));
+ break;
+ case 0x1:
+ DEBUG ((EFI_D_VERBOSE, "Invalid Queue Identifier\n"));
+ break;
+ case 0x2:
+ DEBUG ((EFI_D_VERBOSE, "Maximum Queue Size Exceeded\n"));
+ break;
+ case 0x3:
+ DEBUG ((EFI_D_VERBOSE, "Abort Command Limit Exceeded\n"));
+ break;
+ case 0x5:
+ DEBUG ((EFI_D_VERBOSE, "Asynchronous Event Request Limit Exceeded\n"));
+ break;
+ case 0x6:
+ DEBUG ((EFI_D_VERBOSE, "Invalid Firmware Slot\n"));
+ break;
+ case 0x7:
+ DEBUG ((EFI_D_VERBOSE, "Invalid Firmware Image\n"));
+ break;
+ case 0x8:
+ DEBUG ((EFI_D_VERBOSE, "Invalid Interrupt Vector\n"));
+ break;
+ case 0x9:
+ DEBUG ((EFI_D_VERBOSE, "Invalid Log Page\n"));
+ break;
+ case 0xA:
+ DEBUG ((EFI_D_VERBOSE, "Invalid Format\n"));
+ break;
+ case 0xB:
+ DEBUG ((EFI_D_VERBOSE, "Firmware Application Requires Conventional Reset\n"));
+ break;
+ case 0xC:
+ DEBUG ((EFI_D_VERBOSE, "Invalid Queue Deletion\n"));
+ break;
+ case 0xD:
+ DEBUG ((EFI_D_VERBOSE, "Feature Identifier Not Saveable\n"));
+ break;
+ case 0xE:
+ DEBUG ((EFI_D_VERBOSE, "Feature Not Changeable\n"));
+ break;
+ case 0xF:
+ DEBUG ((EFI_D_VERBOSE, "Feature Not Namespace Specific\n"));
+ break;
+ case 0x10:
+ DEBUG ((EFI_D_VERBOSE, "Firmware Application Requires NVM Subsystem Reset\n"));
+ break;
+ case 0x80:
+ DEBUG ((EFI_D_VERBOSE, "Conflicting Attributes\n"));
+ break;
+ case 0x81:
+ DEBUG ((EFI_D_VERBOSE, "Invalid Protection Information\n"));
+ break;
+ case 0x82:
+ DEBUG ((EFI_D_VERBOSE, "Attempted Write to Read Only Range\n"));
+ break;
+ }
+ break;
+
+ case 0x2:
+ switch (Cq->Sc) {
+ case 0x80:
+ DEBUG ((EFI_D_VERBOSE, "Write Fault\n"));
+ break;
+ case 0x81:
+ DEBUG ((EFI_D_VERBOSE, "Unrecovered Read Error\n"));
+ break;
+ case 0x82:
+ DEBUG ((EFI_D_VERBOSE, "End-to-end Guard Check Error\n"));
+ break;
+ case 0x83:
+ DEBUG ((EFI_D_VERBOSE, "End-to-end Application Tag Check Error\n"));
+ break;
+ case 0x84:
+ DEBUG ((EFI_D_VERBOSE, "End-to-end Reference Tag Check Error\n"));
+ break;
+ case 0x85:
+ DEBUG ((EFI_D_VERBOSE, "Compare Failure\n"));
+ break;
+ case 0x86:
+ DEBUG ((EFI_D_VERBOSE, "Access Denied\n"));
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+/**
+ Create PRP lists for data transfer which is larger than 2 memory pages.
+ Note here we calcuate the number of required PRP lists and allocate them at one time.
+
+ @param[in] PciIo A pointer to the EFI_PCI_IO_PROTOCOL instance.
+ @param[in] PhysicalAddr The physical base address of data buffer.
+ @param[in] Pages The number of pages to be transfered.
+ @param[out] PrpListHost The host base address of PRP lists.
+ @param[in,out] PrpListNo The number of PRP List.
+ @param[out] Mapping The mapping value returned from PciIo.Map().
+
+ @retval The pointer to the first PRP List of the PRP lists.
+
+**/
+VOID*
+NvmeCreatePrpList (
+ IN EFI_PCI_IO_PROTOCOL *PciIo,
+ IN EFI_PHYSICAL_ADDRESS PhysicalAddr,
+ IN UINTN Pages,
+ OUT VOID **PrpListHost,
+ IN OUT UINTN *PrpListNo,
+ OUT VOID **Mapping
+ )
+{
+ UINTN PrpEntryNo;
+ UINT64 PrpListBase;
+ UINTN PrpListIndex;
+ UINTN PrpEntryIndex;
+ UINT64 Remainder;
+ EFI_PHYSICAL_ADDRESS PrpListPhyAddr;
+ UINTN Bytes;
+ EFI_STATUS Status;
+
+ //
+ // The number of Prp Entry in a memory page.
+ //
+ PrpEntryNo = EFI_PAGE_SIZE / sizeof (UINT64);
+
+ //
+ // Calculate total PrpList number.
+ //
+ *PrpListNo = (UINTN)DivU64x64Remainder ((UINT64)Pages, (UINT64)PrpEntryNo, &Remainder);
+ if (Remainder != 0) {
+ *PrpListNo += 1;
+ }
+
+ Status = PciIo->AllocateBuffer (
+ PciIo,
+ AllocateAnyPages,
+ EfiBootServicesData,
+ *PrpListNo,
+ PrpListHost,
+ 0
+ );
+
+ if (EFI_ERROR (Status)) {
+ return NULL;
+ }
+
+ Bytes = EFI_PAGES_TO_SIZE (*PrpListNo);
+ Status = PciIo->Map (
+ PciIo,
+ EfiPciIoOperationBusMasterCommonBuffer,
+ *PrpListHost,
+ &Bytes,
+ &PrpListPhyAddr,
+ Mapping
+ );
+
+ if (EFI_ERROR (Status) || (Bytes != EFI_PAGES_TO_SIZE (*PrpListNo))) {
+ DEBUG ((EFI_D_ERROR, "NvmeCreatePrpList: create PrpList failure!\n"));
+ goto EXIT;
+ }
+ //
+ // Fill all PRP lists except of last one.
+ //
+ ZeroMem (*PrpListHost, Bytes);
+ for (PrpListIndex = 0; PrpListIndex < *PrpListNo - 1; ++PrpListIndex) {
+ PrpListBase = *(UINT8*)PrpListHost + PrpListIndex * EFI_PAGE_SIZE;
+
+ for (PrpEntryIndex = 0; PrpEntryIndex < PrpEntryNo; ++PrpEntryIndex) {
+ if (PrpEntryIndex != PrpEntryNo - 1) {
+ //
+ // Fill all PRP entries except of last one.
+ //
+ *((UINT64*)(UINTN)PrpListBase + PrpEntryIndex) = PhysicalAddr;
+ PhysicalAddr += EFI_PAGE_SIZE;
+ } else {
+ //
+ // Fill last PRP entries with next PRP List pointer.
+ //
+ *((UINT64*)(UINTN)PrpListBase + PrpEntryIndex) = PrpListPhyAddr + (PrpListIndex + 1) * EFI_PAGE_SIZE;
+ }
+ }
+ }
+ //
+ // Fill last PRP list.
+ //
+ PrpListBase = *(UINT64*)PrpListHost + PrpListIndex * EFI_PAGE_SIZE;
+ for (PrpEntryIndex = 0; PrpEntryIndex < ((Remainder != 0) ? Remainder : PrpEntryNo); ++PrpEntryIndex) {
+ *((UINT64*)(UINTN)PrpListBase + PrpEntryIndex) = PhysicalAddr;
+ PhysicalAddr += EFI_PAGE_SIZE;
+ }
+
+ return (VOID*)(UINTN)PrpListPhyAddr;
+
+EXIT:
+ PciIo->FreeBuffer (PciIo, *PrpListNo, *PrpListHost);
+ return NULL;
+}
+
+
+/**
+ Sends an NVM Express Command Packet to an NVM Express controller or namespace. This function supports
+ both blocking I/O and nonblocking I/O. The blocking I/O functionality is required, and the nonblocking
+ I/O functionality is optional.
+
+ @param[in] This A pointer to the NVM_EXPRESS_PASS_THRU_PROTOCOL instance.
+ @param[in] NamespaceId Is a 32 bit Namespace ID to which the Express HCI command packet will be sent.
+ A value of 0 denotes the NVM Express controller, a value of all 0FFh in the namespace
+ ID specifies that the command packet should be sent to all valid namespaces.
+ @param[in] NamespaceUuid Is a 64 bit Namespace UUID to which the Express HCI command packet will be sent.
+ A value of 0 denotes the NVM Express controller, a value of all 0FFh in the namespace
+ UUID specifies that the command packet should be sent to all valid namespaces.
+ @param[in,out] Packet A pointer to the NVM Express HCI Command Packet to send to the NVMe namespace specified
+ by NamespaceId.
+ @param[in] Event If nonblocking I/O is not supported then Event is ignored, and blocking I/O is performed.
+ If Event is NULL, then blocking I/O is performed. If Event is not NULL and non blocking I/O
+ is supported, then nonblocking I/O is performed, and Event will be signaled when the NVM
+ Express Command Packet completes.
+
+ @retval EFI_SUCCESS The NVM Express Command Packet was sent by the host. TransferLength bytes were transferred
+ to, or from DataBuffer.
+ @retval EFI_BAD_BUFFER_SIZE The NVM Express Command Packet was not executed. The number of bytes that could be transferred
+ is returned in TransferLength.
+ @retval EFI_NOT_READY The NVM Express Command Packet could not be sent because the controller is not ready. The caller
+ may retry again later.
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to send the NVM Express Command Packet.
+ @retval EFI_INVALID_PARAMETER Namespace, or the contents of NVM_EXPRESS_PASS_THRU_COMMAND_PACKET are invalid. The NVM
+ Express Command Packet was not sent, so no additional status information is available.
+ @retval EFI_UNSUPPORTED The command described by the NVM Express Command Packet is not supported by the host adapter.
+ The NVM Express Command Packet was not sent, so no additional status information is available.
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the NVM Express Command Packet to execute.
+
+**/
+EFI_STATUS
+EFIAPI
+NvmExpressPassThru (
+ IN NVM_EXPRESS_PASS_THRU_PROTOCOL *This,
+ IN UINT32 NamespaceId,
+ IN UINT64 NamespaceUuid,
+ IN OUT NVM_EXPRESS_PASS_THRU_COMMAND_PACKET *Packet,
+ IN EFI_EVENT Event OPTIONAL
+ )
+{
+ NVME_CONTROLLER_PRIVATE_DATA *Private;
+ EFI_STATUS Status;
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ NVME_SQ *Sq;
+ NVME_CQ *Cq;
+ UINT8 Qid;
+ UINT32 Bytes;
+ UINT16 Offset;
+ EFI_EVENT TimerEvent;
+ EFI_PCI_IO_PROTOCOL_OPERATION Flag;
+ EFI_PHYSICAL_ADDRESS PhyAddr;
+ VOID *MapData;
+ VOID *MapMeta;
+ VOID *MapPrpList;
+ UINTN MapLength;
+ UINT64 *Prp;
+ VOID *PrpListHost;
+ UINTN PrpListNo;
+
+ //
+ // check the data fields in Packet parameter.
+ //
+ if ((This == NULL) || (Packet == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((Packet->NvmeCmd == NULL) || (Packet->NvmeResponse == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Packet->QueueId != NVME_ADMIN_QUEUE && Packet->QueueId != NVME_IO_QUEUE) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = NVME_CONTROLLER_PRIVATE_DATA_FROM_PASS_THRU (This);
+ PciIo = Private->PciIo;
+ MapData = NULL;
+ MapMeta = NULL;
+ MapPrpList = NULL;
+ PrpListHost = NULL;
+ PrpListNo = 0;
+ Prp = NULL;
+ TimerEvent = NULL;
+ Status = EFI_SUCCESS;
+
+ Qid = Packet->QueueId;
+ Sq = Private->SqBuffer[Qid] + Private->SqTdbl[Qid].Sqt;
+ Cq = Private->CqBuffer[Qid] + Private->CqHdbl[Qid].Cqh;
+
+ if (Packet->NvmeCmd->Nsid != NamespaceId) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ZeroMem (Sq, sizeof (NVME_SQ));
+ Sq->Opc = Packet->NvmeCmd->Cdw0.Opcode;
+ Sq->Fuse = Packet->NvmeCmd->Cdw0.FusedOperation;
+ Sq->Cid = Packet->NvmeCmd->Cdw0.Cid;
+ Sq->Nsid = Packet->NvmeCmd->Nsid;
+
+ //
+ // Currently we only support PRP for data transfer, SGL is NOT supported.
+ //
+ ASSERT ((Sq->Opc & BIT15) == 0);
+ if ((Sq->Opc & BIT15) != 0) {
+ DEBUG ((EFI_D_ERROR, "NvmExpressPassThru: doesn't support SGL mechanism\n"));
+ return EFI_UNSUPPORTED;
+ }
+
+ Sq->Prp[0] = (UINT64)(UINTN)Packet->TransferBuffer;
+ //
+ // If the NVMe cmd has data in or out, then mapping the user buffer to the PCI controller specific addresses.
+ // Note here we don't handle data buffer for CreateIOSubmitionQueue and CreateIOCompletionQueue cmds because
+ // these two cmds are special which requires their data buffer must support simultaneous access by both the
+ // processor and a PCI Bus Master. It's caller's responsbility to ensure this.
+ //
+ if (((Sq->Opc & (BIT0 | BIT1)) != 0) && (Sq->Opc != NVME_ADMIN_CRIOCQ_OPC) && (Sq->Opc != NVME_ADMIN_CRIOSQ_OPC)) {
+ if ((Sq->Opc & BIT0) != 0) {
+ Flag = EfiPciIoOperationBusMasterRead;
+ } else {
+ Flag = EfiPciIoOperationBusMasterWrite;
+ }
+
+ MapLength = Packet->TransferLength;
+ Status = PciIo->Map (
+ PciIo,
+ Flag,
+ Packet->TransferBuffer,
+ &MapLength,
+ &PhyAddr,
+ &MapData
+ );
+ if (EFI_ERROR (Status) || (Packet->TransferLength != MapLength)) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Sq->Prp[0] = PhyAddr;
+ Sq->Prp[1] = 0;
+
+ MapLength = Packet->MetadataLength;
+ if(Packet->MetadataBuffer != NULL) {
+ MapLength = Packet->MetadataLength;
+ Status = PciIo->Map (
+ PciIo,
+ Flag,
+ Packet->MetadataBuffer,
+ &MapLength,
+ &PhyAddr,
+ &MapMeta
+ );
+ if (EFI_ERROR (Status) || (Packet->MetadataLength != MapLength)) {
+ PciIo->Unmap (
+ PciIo,
+ MapData
+ );
+
+ return EFI_OUT_OF_RESOURCES;
+ }
+ Sq->Mptr = PhyAddr;
+ }
+ }
+ //
+ // If the buffer size spans more than two memory pages (page size as defined in CC.Mps),
+ // then build a PRP list in the second PRP submission queue entry.
+ //
+ Offset = ((UINT16)Sq->Prp[0]) & (EFI_PAGE_SIZE - 1);
+ Bytes = Packet->TransferLength;
+
+ if ((Offset + Bytes) > (EFI_PAGE_SIZE * 2)) {
+ //
+ // Create PrpList for remaining data buffer.
+ //
+ PhyAddr = (Sq->Prp[0] + EFI_PAGE_SIZE) & ~(EFI_PAGE_SIZE - 1);
+ Prp = NvmeCreatePrpList (PciIo, PhyAddr, EFI_SIZE_TO_PAGES(Offset + Bytes) - 1, &PrpListHost, &PrpListNo, &MapPrpList);
+ if (Prp == NULL) {
+ goto EXIT;
+ }
+
+ Sq->Prp[1] = (UINT64)(UINTN)Prp;
+ } else if ((Offset + Bytes) > EFI_PAGE_SIZE) {
+ Sq->Prp[1] = (Sq->Prp[0] + EFI_PAGE_SIZE) & ~(EFI_PAGE_SIZE - 1);
+ }
+
+ if(Packet->NvmeCmd->Flags & CDW10_VALID) {
+ Sq->Payload.Raw.Cdw10 = Packet->NvmeCmd->Cdw10;
+ }
+ if(Packet->NvmeCmd->Flags & CDW11_VALID) {
+ Sq->Payload.Raw.Cdw11 = Packet->NvmeCmd->Cdw11;
+ }
+ if(Packet->NvmeCmd->Flags & CDW12_VALID) {
+ Sq->Payload.Raw.Cdw12 = Packet->NvmeCmd->Cdw12;
+ }
+ if(Packet->NvmeCmd->Flags & CDW13_VALID) {
+ Sq->Payload.Raw.Cdw13 = Packet->NvmeCmd->Cdw13;
+ }
+ if(Packet->NvmeCmd->Flags & CDW14_VALID) {
+ Sq->Payload.Raw.Cdw14 = Packet->NvmeCmd->Cdw14;
+ }
+ if(Packet->NvmeCmd->Flags & CDW15_VALID) {
+ Sq->Payload.Raw.Cdw15 = Packet->NvmeCmd->Cdw15;
+ }
+
+ //
+ // Ring the submission queue doorbell.
+ //
+ Private->SqTdbl[Qid].Sqt ^= 1;
+
+ PciIo->Mem.Write (
+ PciIo,
+ EfiPciIoWidthUint32,
+ NVME_BAR,
+ NVME_SQTDBL_OFFSET(Qid, Private->Cap.Dstrd),
+ 1,
+ &Private->SqTdbl[Qid]
+ );
+
+ Status = gBS->CreateEvent (
+ EVT_TIMER,
+ TPL_CALLBACK,
+ NULL,
+ NULL,
+ &TimerEvent
+ );
+ if (EFI_ERROR (Status)) {
+ goto EXIT;
+ }
+
+ Status = gBS->SetTimer(TimerEvent, TimerRelative, Packet->CommandTimeout);
+
+ if (EFI_ERROR(Status)) {
+ Packet->ControllerStatus = NVM_EXPRESS_STATUS_CONTROLLER_DEVICE_ERROR;
+ goto EXIT;
+ }
+
+ //
+ // Wait for completion queue to get filled in.
+ //
+ Status = EFI_TIMEOUT;
+ Packet->ControllerStatus = NVM_EXPRESS_STATUS_CONTROLLER_TIMEOUT_COMMAND;
+ while (EFI_ERROR (gBS->CheckEvent (TimerEvent))) {
+ if (Cq->Pt != Private->Pt[Qid]) {
+ Status = EFI_SUCCESS;
+ Packet->ControllerStatus = NVM_EXPRESS_STATUS_CONTROLLER_READY;
+ break;
+ }
+ }
+
+ if ((Private->CqHdbl[Qid].Cqh ^= 1) == 0) {
+ Private->Pt[Qid] ^= 1;
+ }
+
+ //
+ // Copy the Respose Queue entry for this command to the callers response buffer
+ //
+ CopyMem(Packet->NvmeResponse, Cq, sizeof(NVM_EXPRESS_RESPONSE));
+
+ //
+ // Dump every completion entry status for debugging.
+ //
+ DEBUG_CODE_BEGIN();
+ NvmeDumpStatus(Cq);
+ DEBUG_CODE_END();
+
+ PciIo->Mem.Write (
+ PciIo,
+ EfiPciIoWidthUint32,
+ NVME_BAR,
+ NVME_CQHDBL_OFFSET(Qid, Private->Cap.Dstrd),
+ 1,
+ &Private->CqHdbl[Qid]
+ );
+
+EXIT:
+ if (MapData != NULL) {
+ PciIo->Unmap (
+ PciIo,
+ MapData
+ );
+ }
+
+ if (MapMeta != NULL) {
+ PciIo->Unmap (
+ PciIo,
+ MapMeta
+ );
+ }
+
+ if (MapPrpList != NULL) {
+ PciIo->Unmap (
+ PciIo,
+ MapPrpList
+ );
+ }
+
+ if (Prp != NULL) {
+ PciIo->FreeBuffer (PciIo, PrpListNo, PrpListHost);
+ }
+
+ if (TimerEvent != NULL) {
+ gBS->CloseEvent (TimerEvent);
+ }
+ return Status;
+}
+
+/**
+ Used to retrieve the list of namespaces defined on an NVM Express controller.
+
+ The NVM_EXPRESS_PASS_THRU_PROTOCOL.GetNextNamespace() function retrieves a list of namespaces
+ defined on an NVM Express controller. If on input a NamespaceID is specified by all 0xFF in the
+ namespace buffer, then the first namespace defined on the NVM Express controller is returned in
+ NamespaceID, and a status of EFI_SUCCESS is returned.
+
+ If NamespaceId is a Namespace value that was returned on a previous call to GetNextNamespace(),
+ then the next valid NamespaceId for an NVM Express SSD namespace on the NVM Express controller
+ is returned in NamespaceId, and EFI_SUCCESS is returned.
+
+ If Namespace array is not a 0xFFFFFFFF and NamespaceId was not returned on a previous call to
+ GetNextNamespace(), then EFI_INVALID_PARAMETER is returned.
+
+ If NamespaceId is the NamespaceId of the last SSD namespace on the NVM Express controller, then
+ EFI_NOT_FOUND is returned
+
+ @param[in] This A pointer to the NVM_EXPRESS_PASS_THRU_PROTOCOL instance.
+ @param[in,out] NamespaceId On input, a pointer to a legal NamespaceId for an NVM Express
+ namespace present on the NVM Express controller. On output, a
+ pointer to the next NamespaceId of an NVM Express namespace on
+ an NVM Express controller. An input value of 0xFFFFFFFF retrieves
+ the first NamespaceId for an NVM Express namespace present on an
+ NVM Express controller.
+ @param[out] NamespaceUuid On output, the UUID associated with the next namespace, if a UUID
+ is defined for that NamespaceId, otherwise, zero is returned in
+ this parameter. If the caller does not require a UUID, then a NULL
+ pointer may be passed.
+
+ @retval EFI_SUCCESS The NamespaceId of the next Namespace was returned.
+ @retval EFI_NOT_FOUND There are no more namespaces defined on this controller.
+ @retval EFI_INVALID_PARAMETER Namespace array is not a 0xFFFFFFFF and NamespaceId was not returned
+ on a previous call to GetNextNamespace().
+
+**/
+EFI_STATUS
+EFIAPI
+NvmExpressGetNextNamespace (
+ IN NVM_EXPRESS_PASS_THRU_PROTOCOL *This,
+ IN OUT UINT32 *NamespaceId,
+ OUT UINT64 *NamespaceUuid OPTIONAL
+ )
+{
+ NVME_CONTROLLER_PRIVATE_DATA *Private;
+ NVME_ADMIN_NAMESPACE_DATA *NamespaceData;
+ UINT32 NextNamespaceId;
+ EFI_STATUS Status;
+
+ if ((This == NULL) || (NamespaceId == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ NamespaceData = NULL;
+ Status = EFI_NOT_FOUND;
+
+ Private = NVME_CONTROLLER_PRIVATE_DATA_FROM_PASS_THRU (This);
+ //
+ // If the NamespaceId input value is 0xFFFFFFFF, then get the first valid namespace ID
+ //
+ if (*NamespaceId == 0xFFFFFFFF) {
+ //
+ // Start with the first namespace ID
+ //
+ NextNamespaceId = 1;
+ //
+ // Allocate buffer for Identify Namespace data.
+ //
+ NamespaceData = (NVME_ADMIN_NAMESPACE_DATA *)AllocateZeroPool (sizeof (NVME_ADMIN_NAMESPACE_DATA));
+
+ if (NamespaceData == NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ Status = NvmeIdentifyNamespace (Private, NextNamespaceId, NamespaceData);
+ if (EFI_ERROR(Status)) {
+ goto Done;
+ }
+
+ *NamespaceId = NextNamespaceId;
+ if (NamespaceUuid != NULL) {
+ *NamespaceUuid = NamespaceData->Eui64;
+ }
+ } else {
+ if (*NamespaceId >= Private->ControllerData->Nn) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ NextNamespaceId = *NamespaceId + 1;
+ //
+ // Allocate buffer for Identify Namespace data.
+ //
+ NamespaceData = (NVME_ADMIN_NAMESPACE_DATA *)AllocateZeroPool (sizeof (NVME_ADMIN_NAMESPACE_DATA));
+ if (NamespaceData == NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ Status = NvmeIdentifyNamespace (Private, NextNamespaceId, NamespaceData);
+ if (EFI_ERROR(Status)) {
+ goto Done;
+ }
+
+ *NamespaceId = NextNamespaceId;
+ if (NamespaceUuid != NULL) {
+ *NamespaceUuid = NamespaceData->Eui64;
+ }
+ }
+
+Done:
+ if (NamespaceData != NULL) {
+ FreePool(NamespaceData);
+ }
+
+ return Status;
+}
+
+/**
+ Used to translate a device path node to a Namespace ID and Namespace UUID.
+
+ The NVM_EXPRESS_PASS_THRU_PROTOCOL.GetNamwspace() function determines the Namespace ID and Namespace UUID
+ associated with the NVM Express SSD namespace described by DevicePath. If DevicePath is a device path node type
+ that the NVM Express Pass Thru driver supports, then the NVM Express Pass Thru driver will attempt to translate
+ the contents DevicePath into a Namespace ID and UUID. If this translation is successful, then that Namespace ID
+ and UUID are returned in NamespaceID and NamespaceUUID, and EFI_SUCCESS is returned.
+
+ @param[in] This A pointer to the NVM_EXPRESS_PASS_THRU_PROTOCOL instance.
+ @param[in] DevicePath A pointer to the device path node that describes an NVM Express namespace on
+ the NVM Express controller.
+ @param[out] NamespaceId The NVM Express namespace ID contained in the device path node.
+ @param[out] NamespaceUuid The NVM Express namespace contained in the device path node.
+
+ @retval EFI_SUCCESS DevicePath was successfully translated to NamespaceId and NamespaceUuid.
+ @retval EFI_INVALID_PARAMETER If DevicePath, NamespaceId, or NamespaceUuid are NULL, then EFI_INVALID_PARAMETER
+ is returned.
+ @retval EFI_UNSUPPORTED If DevicePath is not a device path node type that the NVM Express Pass Thru driver
+ supports, then EFI_UNSUPPORTED is returned.
+ @retval EFI_NOT_FOUND If DevicePath is a device path node type that the Nvm Express Pass Thru driver
+ supports, but there is not a valid translation from DevicePath to a NamespaceID
+ and NamespaceUuid, then EFI_NOT_FOUND is returned.
+**/
+EFI_STATUS
+EFIAPI
+NvmExpressGetNamespace (
+ IN NVM_EXPRESS_PASS_THRU_PROTOCOL *This,
+ IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
+ OUT UINT32 *NamespaceId,
+ OUT UINT64 *NamespaceUuid
+ )
+{
+ NVME_NAMESPACE_DEVICE_PATH *Node;
+
+ if ((This == NULL) || (DevicePath == NULL) || (NamespaceId == NULL) || (NamespaceUuid == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (DevicePath->Type != MESSAGING_DEVICE_PATH) {
+ return EFI_UNSUPPORTED;
+ }
+
+ Node = (NVME_NAMESPACE_DEVICE_PATH *)DevicePath;
+
+ if (DevicePath->SubType == MSG_NVME_NAMESPACE_DP) {
+ if (DevicePathNodeLength(DevicePath) != sizeof(NVME_NAMESPACE_DEVICE_PATH)) {
+ return EFI_NOT_FOUND;
+ }
+
+ *NamespaceId = Node->NamespaceId;
+ *NamespaceUuid = Node->NamespaceUuid;
+
+ return EFI_SUCCESS;
+ } else {
+ return EFI_UNSUPPORTED;
+ }
+}
+
+/**
+ Used to allocate and build a device path node for an NVM Express namespace on an NVM Express controller.
+
+ The NVM_EXPRESS_PASS_THRU_PROTOCOL.BuildDevicePath() function allocates and builds a single device
+ path node for the NVM Express namespace specified by NamespaceId.
+
+ If the namespace device specified by NamespaceId is not valid , then EFI_NOT_FOUND is returned.
+
+ If DevicePath is NULL, then EFI_INVALID_PARAMETER is returned.
+
+ If there are not enough resources to allocate the device path node, then EFI_OUT_OF_RESOURCES is returned.
+
+ Otherwise, DevicePath is allocated with the boot service AllocatePool(), the contents of DevicePath are
+ initialized to describe the NVM Express namespace specified by NamespaceId, and EFI_SUCCESS is returned.
+
+ @param[in] This A pointer to the NVM_EXPRESS_PASS_THRU_PROTOCOL instance.
+ @param[in] NamespaceId The NVM Express namespace ID for which a device path node is to be
+ allocated and built. Caller must set the NamespaceId to zero if the
+ device path node will contain a valid UUID.
+ @param[in] NamespaceUuid The NVM Express namespace UUID for which a device path node is to be
+ allocated and built. UUID will only be valid of the Namespace ID is zero.
+ @param[in,out] DevicePath A pointer to a single device path node that describes the NVM Express
+ namespace specified by NamespaceId. This function is responsible for
+ allocating the buffer DevicePath with the boot service AllocatePool().
+ It is the caller's responsibility to free DevicePath when the caller
+ is finished with DevicePath.
+ @retval EFI_SUCCESS The device path node that describes the NVM Express namespace specified
+ by NamespaceId was allocated and returned in DevicePath.
+ @retval EFI_NOT_FOUND The NVM Express namespace specified by NamespaceId does not exist on the
+ NVM Express controller.
+ @retval EFI_INVALID_PARAMETER DevicePath is NULL.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources to allocate the DevicePath node.
+
+**/
+EFI_STATUS
+EFIAPI
+NvmExpressBuildDevicePath (
+ IN NVM_EXPRESS_PASS_THRU_PROTOCOL *This,
+ IN UINT32 NamespaceId,
+ IN UINT64 NamespaceUuid,
+ IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath
+ )
+{
+ NVME_CONTROLLER_PRIVATE_DATA *Private;
+ NVME_NAMESPACE_DEVICE_PATH *Node;
+
+ //
+ // Validate parameters
+ //
+ if ((This == NULL) || (DevicePath == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = NVME_CONTROLLER_PRIVATE_DATA_FROM_PASS_THRU (This);
+
+ if (NamespaceId == 0) {
+ return EFI_NOT_FOUND;
+ }
+
+ Node = (NVME_NAMESPACE_DEVICE_PATH *)AllocateZeroPool (sizeof (NVME_NAMESPACE_DEVICE_PATH));
+
+ if (Node == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Node->Header.Type = MESSAGING_DEVICE_PATH;
+ Node->Header.SubType = MSG_NVME_NAMESPACE_DP;
+ SetDevicePathNodeLength (&Node->Header, sizeof (NVME_NAMESPACE_DEVICE_PATH));
+ Node->NamespaceId = NamespaceId;
+ Node->NamespaceUuid = NamespaceUuid;
+
+ *DevicePath = (EFI_DEVICE_PATH_PROTOCOL *)Node;
+ return EFI_SUCCESS;
+}
+
diff --git a/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressPassthru.h b/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressPassthru.h
new file mode 100644
index 0000000000..cf05bffb84
--- /dev/null
+++ b/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressPassthru.h
@@ -0,0 +1,299 @@
+/** @file
+ NvmExpressDxe driver is used to manage non-volatile memory subsystem which follows
+ NVM Express specification.
+
+ Copyright (c) 2013, Intel Corporation. All rights reserved.<BR>
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _NVM_EXPRESS_PASS_THRU_H_
+#define _NVM_EXPRESS_PASS_THRU_H_
+
+#define NVM_EXPRESS_PASS_THRU_PROTOCOL_GUID \
+ { \
+ 0xec51ef5c, 0x2cf3, 0x4a55, {0xbf, 0x85, 0xb6, 0x3c, 0xa3, 0xb1, 0x3f, 0x44 } \
+ }
+
+typedef struct _NVM_EXPRESS_PASS_THRU_PROTOCOL NVM_EXPRESS_PASS_THRU_PROTOCOL;
+
+typedef struct {
+ UINT32 AdapterId;
+ UINT32 Attributes;
+ UINT32 IoAlign;
+ UINT32 HciVersion;
+ UINT64 Timeout;
+ UINT32 MaxNamespace;
+} NVM_EXPRESS_PASS_THRU_MODE;
+
+//
+// If this bit is set, then the NVM_EXPRESS_PASS_THRU_PROTOCOL interface is for directly addressable namespaces.
+//
+#define NVM_EXPRESS_PASS_THRU_ATTRIBUTES_PHYSICAL 0x0001
+//
+// If this bit is set, then the NVM_EXPRESS_PASS_THRU_PROTOCOL interface is for a single volume logical namespace
+// comprised of multiple namespaces.
+//
+#define NVM_EXPRESS_PASS_THRU_ATTRIBUTES_LOGICAL 0x0002
+//
+// If this bit is set, then the NVM_EXPRESS_PASS_THRU_PROTOCOL interface supports non blocking I/O.
+//
+#define NVM_EXPRESS_PASS_THRU_ATTRIBUTES_NONBLOCKIO 0x0004
+//
+// If this bit is set, then the NVM_EXPRESS_PASS_THRU_PROTOCOL interface supports NVM command set commands.
+//
+#define NVM_EXPRESS_PASS_THRU_ATTRIBUTES_CMD_SET_NVME 0x0008
+
+//
+// QueueId
+//
+#define NVME_ADMIN_QUEUE 0x00
+#define NVME_IO_QUEUE 0x01
+
+//
+// ControllerStatus
+//
+#define NVM_EXPRESS_STATUS_CONTROLLER_READY 0x00
+#define NVM_EXPRESS_STATUS_CONTROLLER_CMD_ERROR 0x01
+#define NVM_EXPRESS_STATUS_CONTROLLER_FATAL 0x02
+#define NVM_EXPRESS_STATUS_CONTROLLER_CMD_DATA_ERROR 0x04
+#define NVM_EXPRESS_STATUS_CONTROLLER_CMD_ABORT 0x05
+#define NVM_EXPRESS_STATUS_CONTROLLER_DEVICE_ERROR 0x06
+#define NVM_EXPRESS_STATUS_CONTROLLER_TIMEOUT_COMMAND 0x09
+#define NVM_EXPRESS_STATUS_CONTROLLER_INVALID_NAMESPACE 0x0B
+#define NVM_EXPRESS_STATUS_CONTROLLER_NOT_READY 0x0C
+#define NVM_EXPRESS_STATUS_CONTROLLER_OTHER 0x7F
+
+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;
+} NVM_EXPRESS_COMMAND;
+
+typedef struct {
+ UINT32 Cdw0;
+ UINT32 Cdw1;
+ UINT32 Cdw2;
+ UINT32 Cdw3;
+} NVM_EXPRESS_RESPONSE;
+
+typedef struct {
+ UINT64 CommandTimeout;
+ VOID *TransferBuffer;
+ UINT32 TransferLength;
+ VOID *MetadataBuffer;
+ UINT32 MetadataLength;
+ UINT8 QueueId;
+ NVM_EXPRESS_COMMAND *NvmeCmd;
+ NVM_EXPRESS_RESPONSE *NvmeResponse;
+ UINT8 ControllerStatus;
+} NVM_EXPRESS_PASS_THRU_COMMAND_PACKET;
+
+//
+// Protocol funtion prototypes
+//
+/**
+ Sends an NVM Express Command Packet to an NVM Express controller or namespace. This function supports
+ both blocking I/O and nonblocking I/O. The blocking I/O functionality is required, and the nonblocking
+ I/O functionality is optional.
+
+ @param[in] This A pointer to the NVM_EXPRESS_PASS_THRU_PROTOCOL instance.
+ @param[in] NamespaceId Is a 32 bit Namespace ID to which the Express HCI command packet will be sent.
+ A value of 0 denotes the NVM Express controller, a value of all 0FFh in the namespace
+ ID specifies that the command packet should be sent to all valid namespaces.
+ @param[in] NamespaceUuid Is a 64 bit Namespace UUID to which the Express HCI command packet will be sent.
+ A value of 0 denotes the NVM Express controller, a value of all 0FFh in the namespace
+ UUID specifies that the command packet should be sent to all valid namespaces.
+ @param[in,out] Packet A pointer to the NVM Express HCI Command Packet to send to the NVMe namespace specified
+ by NamespaceId.
+ @param[in] Event If nonblocking I/O is not supported then Event is ignored, and blocking I/O is performed.
+ If Event is NULL, then blocking I/O is performed. If Event is not NULL and non blocking I/O
+ is supported, then nonblocking I/O is performed, and Event will be signaled when the NVM
+ Express Command Packet completes.
+
+ @retval EFI_SUCCESS The NVM Express Command Packet was sent by the host. TransferLength bytes were transferred
+ to, or from DataBuffer.
+ @retval EFI_BAD_BUFFER_SIZE The NVM Express Command Packet was not executed. The number of bytes that could be transferred
+ is returned in TransferLength.
+ @retval EFI_NOT_READY The NVM Express Command Packet could not be sent because the controller is not ready. The caller
+ may retry again later.
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to send the NVM Express Command Packet.
+ @retval EFI_INVALID_PARAMETER Namespace, or the contents of NVM_EXPRESS_PASS_THRU_COMMAND_PACKET are invalid. The NVM
+ Express Command Packet was not sent, so no additional status information is available.
+ @retval EFI_UNSUPPORTED The command described by the NVM Express Command Packet is not supported by the host adapter.
+ The NVM Express Command Packet was not sent, so no additional status information is available.
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the NVM Express Command Packet to execute.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *NVM_EXPRESS_PASS_THRU_PASSTHRU)(
+ IN NVM_EXPRESS_PASS_THRU_PROTOCOL *This,
+ IN UINT32 NamespaceId,
+ IN UINT64 NamespaceUuid,
+ IN OUT NVM_EXPRESS_PASS_THRU_COMMAND_PACKET *Packet,
+ IN EFI_EVENT Event OPTIONAL
+ );
+
+/**
+ Used to retrieve the list of namespaces defined on an NVM Express controller.
+
+ The NVM_EXPRESS_PASS_THRU_PROTOCOL.GetNextNamespace() function retrieves a list of namespaces
+ defined on an NVM Express controller. If on input a NamespaceID is specified by all 0xFF in the
+ namespace buffer, then the first namespace defined on the NVM Express controller is returned in
+ NamespaceID, and a status of EFI_SUCCESS is returned.
+
+ If NamespaceId is a Namespace value that was returned on a previous call to GetNextNamespace(),
+ then the next valid NamespaceId for an NVM Express SSD namespace on the NVM Express controller
+ is returned in NamespaceId, and EFI_SUCCESS is returned.
+
+ If Namespace array is not a 0xFFFFFFFF and NamespaceId was not returned on a previous call to
+ GetNextNamespace(), then EFI_INVALID_PARAMETER is returned.
+
+ If NamespaceId is the NamespaceId of the last SSD namespace on the NVM Express controller, then
+ EFI_NOT_FOUND is returned
+
+ @param[in] This A pointer to the NVM_EXPRESS_PASS_THRU_PROTOCOL instance.
+ @param[in,out] NamespaceId On input, a pointer to a legal NamespaceId for an NVM Express
+ namespace present on the NVM Express controller. On output, a
+ pointer to the next NamespaceId of an NVM Express namespace on
+ an NVM Express controller. An input value of 0xFFFFFFFF retrieves
+ the first NamespaceId for an NVM Express namespace present on an
+ NVM Express controller.
+ @param[out] NamespaceUuid On output, the UUID associated with the next namespace, if a UUID
+ is defined for that NamespaceId, otherwise, zero is returned in
+ this parameter. If the caller does not require a UUID, then a NULL
+ pointer may be passed.
+
+ @retval EFI_SUCCESS The NamespaceId of the next Namespace was returned.
+ @retval EFI_NOT_FOUND There are no more namespaces defined on this controller.
+ @retval EFI_INVALID_PARAMETER Namespace array is not a 0xFFFFFFFF and NamespaceId was not returned
+ on a previous call to GetNextNamespace().
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *NVM_EXPRESS_PASS_THRU_GET_NEXT_NAMESPACE)(
+ IN NVM_EXPRESS_PASS_THRU_PROTOCOL *This,
+ IN OUT UINT32 *NamespaceId,
+ OUT UINT64 *NamespaceUuid OPTIONAL
+ );
+
+/**
+ Used to allocate and build a device path node for an NVM Express namespace on an NVM Express controller.
+
+ The NVM_EXPRESS_PASS_THRU_PROTOCOL.BuildDevicePath() function allocates and builds a single device
+ path node for the NVM Express namespace specified by NamespaceId.
+
+ If the namespace device specified by NamespaceId is not valid , then EFI_NOT_FOUND is returned.
+
+ If DevicePath is NULL, then EFI_INVALID_PARAMETER is returned.
+
+ If there are not enough resources to allocate the device path node, then EFI_OUT_OF_RESOURCES is returned.
+
+ Otherwise, DevicePath is allocated with the boot service AllocatePool(), the contents of DevicePath are
+ initialized to describe the NVM Express namespace specified by NamespaceId, and EFI_SUCCESS is returned.
+
+ @param[in] This A pointer to the NVM_EXPRESS_PASS_THRU_PROTOCOL instance.
+ @param[in] NamespaceId The NVM Express namespace ID for which a device path node is to be
+ allocated and built. Caller must set the NamespaceId to zero if the
+ device path node will contain a valid UUID.
+ @param[in] NamespaceUuid The NVM Express namespace UUID for which a device path node is to be
+ allocated and built. UUID will only be valid of the Namespace ID is zero.
+ @param[in,out] DevicePath A pointer to a single device path node that describes the NVM Express
+ namespace specified by NamespaceId. This function is responsible for
+ allocating the buffer DevicePath with the boot service AllocatePool().
+ It is the caller's responsibility to free DevicePath when the caller
+ is finished with DevicePath.
+ @retval EFI_SUCCESS The device path node that describes the NVM Express namespace specified
+ by NamespaceId was allocated and returned in DevicePath.
+ @retval EFI_NOT_FOUND The NVM Express namespace specified by NamespaceId does not exist on the
+ NVM Express controller.
+ @retval EFI_INVALID_PARAMETER DevicePath is NULL.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources to allocate the DevicePath node.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *NVM_EXPRESS_PASS_THRU_BUILD_DEVICE_PATH)(
+ IN NVM_EXPRESS_PASS_THRU_PROTOCOL *This,
+ IN UINT32 NamespaceId,
+ IN UINT64 NamespaceUuid,
+ IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath
+ );
+
+/**
+ Used to translate a device path node to a Namespace ID and Namespace UUID.
+
+ The NVM_EXPRESS_PASS_THRU_PROTOCOL.GetNamwspace() function determines the Namespace ID and Namespace UUID
+ associated with the NVM Express SSD namespace described by DevicePath. If DevicePath is a device path node type
+ that the NVM Express Pass Thru driver supports, then the NVM Express Pass Thru driver will attempt to translate
+ the contents DevicePath into a Namespace ID and UUID. If this translation is successful, then that Namespace ID
+ and UUID are returned in NamespaceID and NamespaceUUID, and EFI_SUCCESS is returned.
+
+ @param[in] This A pointer to the NVM_EXPRESS_PASS_THRU_PROTOCOL instance.
+ @param[in] DevicePath A pointer to the device path node that describes an NVM Express namespace on
+ the NVM Express controller.
+ @param[out] NamespaceId The NVM Express namespace ID contained in the device path node.
+ @param[out] NamespaceUuid The NVM Express namespace contained in the device path node.
+
+ @retval EFI_SUCCESS DevicePath was successfully translated to NamespaceId and NamespaceUuid.
+ @retval EFI_INVALID_PARAMETER If DevicePath, NamespaceId, or NamespaceUuid are NULL, then EFI_INVALID_PARAMETER
+ is returned.
+ @retval EFI_UNSUPPORTED If DevicePath is not a device path node type that the NVM Express Pass Thru driver
+ supports, then EFI_UNSUPPORTED is returned.
+ @retval EFI_NOT_FOUND If DevicePath is a device path node type that the Nvm Express Pass Thru driver
+ supports, but there is not a valid translation from DevicePath to a NamespaceID
+ and NamespaceUuid, then EFI_NOT_FOUND is returned.
+**/
+typedef
+EFI_STATUS
+(EFIAPI *NVM_EXPRESS_PASS_THRU_GET_NAMESPACE)(
+ IN NVM_EXPRESS_PASS_THRU_PROTOCOL *This,
+ IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
+ OUT UINT32 *NamespaceId,
+ OUT UINT64 *NamespaceUuid
+ );
+
+//
+// Protocol Interface Structure
+//
+struct _NVM_EXPRESS_PASS_THRU_PROTOCOL {
+ NVM_EXPRESS_PASS_THRU_MODE *Mode;
+ NVM_EXPRESS_PASS_THRU_PASSTHRU PassThru;
+ NVM_EXPRESS_PASS_THRU_GET_NEXT_NAMESPACE GetNextNamespace;
+ NVM_EXPRESS_PASS_THRU_BUILD_DEVICE_PATH BuildDevicePath;
+ NVM_EXPRESS_PASS_THRU_GET_NAMESPACE GetNamespace;
+};
+
+//extern EFI_GUID gNvmExpressPassThruProtocolGuid;
+
+#endif
+
diff --git a/MdeModulePkg/MdeModulePkg.dsc b/MdeModulePkg/MdeModulePkg.dsc
index 7895834c24..2ff9dc5fae 100644
--- a/MdeModulePkg/MdeModulePkg.dsc
+++ b/MdeModulePkg/MdeModulePkg.dsc
@@ -196,6 +196,7 @@
MdeModulePkg/Bus/Pci/PciBusDxe/PciBusDxe.inf
MdeModulePkg/Bus/Pci/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupportDxe.inf
+ MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressDxe.inf
MdeModulePkg/Bus/Pci/XhciDxe/XhciDxe.inf
MdeModulePkg/Bus/Pci/EhciDxe/EhciDxe.inf
MdeModulePkg/Bus/Pci/UhciDxe/UhciDxe.inf