From eb290d0257814701eefd93ee71d46efb6dcc320b Mon Sep 17 00:00:00 2001 From: Feng Tian Date: Mon, 12 Aug 2013 06:13:54 +0000 Subject: [PATCH] MdeMdeModulePkg/NvmExpressDxe: Add NVM Express support. Signed-off-by: Feng Tian Reviewed-by: Star Zeng git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@14545 6f19259b-4bc3-4df7-8a09-765794883524 --- .../Bus/Pci/NvmExpressDxe/ComponentName.c | 233 ++++ .../Bus/Pci/NvmExpressDxe/NvmExpress.c | 1057 +++++++++++++++++ .../Bus/Pci/NvmExpressDxe/NvmExpress.h | 614 ++++++++++ .../Bus/Pci/NvmExpressDxe/NvmExpressBlockIo.c | 559 +++++++++ .../Bus/Pci/NvmExpressDxe/NvmExpressBlockIo.h | 111 ++ .../Pci/NvmExpressDxe/NvmExpressDiskInfo.c | 162 +++ .../Pci/NvmExpressDxe/NvmExpressDiskInfo.h | 129 ++ .../Bus/Pci/NvmExpressDxe/NvmExpressDxe.inf | 68 ++ .../Bus/Pci/NvmExpressDxe/NvmExpressHci.c | 926 +++++++++++++++ .../Bus/Pci/NvmExpressDxe/NvmExpressHci.h | 790 ++++++++++++ .../Pci/NvmExpressDxe/NvmExpressPassthru.c | 884 ++++++++++++++ .../Pci/NvmExpressDxe/NvmExpressPassthru.h | 299 +++++ MdeModulePkg/MdeModulePkg.dsc | 1 + 13 files changed, 5833 insertions(+) create mode 100644 MdeModulePkg/Bus/Pci/NvmExpressDxe/ComponentName.c create mode 100644 MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpress.c create mode 100644 MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpress.h create mode 100644 MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressBlockIo.c create mode 100644 MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressBlockIo.h create mode 100644 MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressDiskInfo.c create mode 100644 MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressDiskInfo.h create mode 100644 MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressDxe.inf create mode 100644 MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressHci.c create mode 100644 MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressHci.h create mode 100644 MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressPassthru.c create mode 100644 MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressPassthru.h 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.
+ 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.
+ 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.
+ 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 + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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.
+ 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.
+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.
+ 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.
+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.
+# +# 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.
+ 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.
+ 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.
+ 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.
+ 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 -- 2.39.2