+/** @file\r
+ NvmExpressDxe driver is used to manage non-volatile memory subsystem which follows\r
+ NVM Express specification.\r
+\r
+ Copyright (c) 2013, Intel Corporation. All rights reserved.<BR>\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "NvmExpress.h"\r
+\r
+//\r
+// NVM Express Driver Binding Protocol Instance\r
+//\r
+EFI_DRIVER_BINDING_PROTOCOL gNvmExpressDriverBinding = {\r
+ NvmExpressDriverBindingSupported,\r
+ NvmExpressDriverBindingStart,\r
+ NvmExpressDriverBindingStop,\r
+ 0x10,\r
+ NULL,\r
+ NULL\r
+};\r
+\r
+//\r
+// NVM Express EFI Driver Supported EFI Version Protocol Instance\r
+//\r
+EFI_DRIVER_SUPPORTED_EFI_VERSION_PROTOCOL gNvmExpressDriverSupportedEfiVersion = {\r
+ sizeof (EFI_DRIVER_SUPPORTED_EFI_VERSION_PROTOCOL), // Size of Protocol structure.\r
+ 0 // Version number to be filled at start up.\r
+};\r
+\r
+/**\r
+ Check if the specified Nvm Express device namespace is active, and create child handles\r
+ for them with BlockIo and DiskInfo protocol instances.\r
+\r
+ @param[in] Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure.\r
+ @param[in] NamespaceId The NVM Express namespace ID for which a device path node is to be\r
+ allocated and built. Caller must set the NamespaceId to zero if the\r
+ device path node will contain a valid UUID.\r
+ @param[in] NamespaceUuid The NVM Express namespace UUID for which a device path node is to be\r
+ allocated and built. UUID will only be valid of the Namespace ID is zero.\r
+\r
+ @retval EFI_SUCCESS All the namespaces in the device are successfully enumerated.\r
+ @return Others Some error occurs when enumerating the namespaces.\r
+\r
+**/\r
+EFI_STATUS\r
+EnumerateNvmeDevNamespace (\r
+ IN NVME_CONTROLLER_PRIVATE_DATA *Private,\r
+ UINT32 NamespaceId,\r
+ UINT64 NamespaceUuid\r
+ )\r
+{\r
+ NVME_ADMIN_NAMESPACE_DATA *NamespaceData;\r
+ EFI_DEVICE_PATH_PROTOCOL *NewDevicePathNode;\r
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;\r
+ EFI_HANDLE DeviceHandle;\r
+ EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath;\r
+ EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath;\r
+ NVME_DEVICE_PRIVATE_DATA *Device;\r
+ EFI_STATUS Status;\r
+ UINT32 Lbads;\r
+ UINT32 Flbas;\r
+ UINT32 LbaFmtIdx;\r
+\r
+ NewDevicePathNode = NULL;\r
+ DevicePath = NULL;\r
+ Device = NULL;\r
+\r
+ //\r
+ // Allocate a buffer for Identify Namespace data\r
+ //\r
+ NamespaceData = AllocateZeroPool(sizeof (NVME_ADMIN_NAMESPACE_DATA));\r
+ if(NamespaceData == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ ParentDevicePath = Private->ParentDevicePath;\r
+ //\r
+ // Identify Namespace\r
+ //\r
+ Status = NvmeIdentifyNamespace (\r
+ Private,\r
+ NamespaceId,\r
+ (VOID *)NamespaceData\r
+ );\r
+ if (EFI_ERROR(Status)) {\r
+ goto Exit;\r
+ }\r
+ //\r
+ // Validate Namespace\r
+ //\r
+ if (NamespaceData->Ncap == 0) {\r
+ Status = EFI_DEVICE_ERROR;\r
+ } else {\r
+ //\r
+ // allocate device private data for each discovered namespace\r
+ //\r
+ Device = AllocateZeroPool(sizeof(NVME_DEVICE_PRIVATE_DATA));\r
+ if (Device == NULL) {\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // Initialize SSD namespace instance data\r
+ //\r
+ Device->Signature = NVME_DEVICE_PRIVATE_DATA_SIGNATURE;\r
+ Device->NamespaceId = NamespaceId;\r
+ Device->NamespaceUuid = NamespaceData->Eui64;\r
+\r
+ Device->ControllerHandle = Private->ControllerHandle;\r
+ Device->DriverBindingHandle = Private->DriverBindingHandle;\r
+ Device->Controller = Private;\r
+\r
+ //\r
+ // Build BlockIo media structure\r
+ //\r
+ Device->Media.MediaId = 0;\r
+ Device->Media.RemovableMedia = FALSE;\r
+ Device->Media.MediaPresent = TRUE;\r
+ Device->Media.LogicalPartition = FALSE;\r
+ Device->Media.ReadOnly = FALSE;\r
+ Device->Media.WriteCaching = FALSE;\r
+\r
+ Flbas = NamespaceData->Flbas;\r
+ LbaFmtIdx = Flbas & 3;\r
+ Lbads = NamespaceData->LbaFormat[LbaFmtIdx].Lbads;\r
+ Device->Media.BlockSize = (UINT32)1 << Lbads;\r
+\r
+ Device->Media.LastBlock = NamespaceData->Nsze - 1;\r
+ Device->Media.LogicalBlocksPerPhysicalBlock = 1;\r
+ Device->Media.LowestAlignedLba = 1;\r
+\r
+ //\r
+ // Create BlockIo Protocol instance\r
+ //\r
+ Device->BlockIo.Revision = EFI_BLOCK_IO_PROTOCOL_REVISION2;\r
+ Device->BlockIo.Media = &Device->Media;\r
+ Device->BlockIo.Reset = NvmeBlockIoReset;\r
+ Device->BlockIo.ReadBlocks = NvmeBlockIoReadBlocks;\r
+ Device->BlockIo.WriteBlocks = NvmeBlockIoWriteBlocks;\r
+ Device->BlockIo.FlushBlocks = NvmeBlockIoFlushBlocks;\r
+\r
+ //\r
+ // Create DiskInfo Protocol instance\r
+ //\r
+ InitializeDiskInfo (Device);\r
+\r
+ //\r
+ // Create a Nvm Express Namespace Device Path Node\r
+ //\r
+ Status = Private->Passthru.BuildDevicePath (\r
+ &Private->Passthru,\r
+ Device->NamespaceId,\r
+ Device->NamespaceUuid,\r
+ &NewDevicePathNode\r
+ );\r
+\r
+ if (EFI_ERROR(Status)) {\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // Append the SSD node to the controller's device path\r
+ //\r
+ DevicePath = AppendDevicePathNode (ParentDevicePath, NewDevicePathNode);\r
+ if (DevicePath == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto Exit;\r
+ }\r
+\r
+ DeviceHandle = NULL;\r
+ RemainingDevicePath = DevicePath;\r
+ Status = gBS->LocateDevicePath (&gEfiDevicePathProtocolGuid, &RemainingDevicePath, &DeviceHandle);\r
+ if (!EFI_ERROR (Status) && (DeviceHandle != NULL) && IsDevicePathEnd(RemainingDevicePath)) {\r
+ Status = EFI_ALREADY_STARTED;\r
+ FreePool (DevicePath);\r
+ goto Exit;\r
+ }\r
+\r
+ Device->DevicePath = DevicePath;\r
+\r
+ //\r
+ // Make sure the handle is NULL so we create a new handle\r
+ //\r
+ Device->DeviceHandle = NULL;\r
+\r
+ Status = gBS->InstallMultipleProtocolInterfaces (\r
+ &Device->DeviceHandle,\r
+ &gEfiDevicePathProtocolGuid,\r
+ Device->DevicePath,\r
+ &gEfiBlockIoProtocolGuid,\r
+ &Device->BlockIo,\r
+ &gEfiDiskInfoProtocolGuid,\r
+ &Device->DiskInfo,\r
+ NULL\r
+ );\r
+\r
+ if(EFI_ERROR(Status)) {\r
+ goto Exit;\r
+ }\r
+ gBS->OpenProtocol (\r
+ Private->ControllerHandle,\r
+ &gEfiPciIoProtocolGuid,\r
+ (VOID **) &Private->PciIo,\r
+ Private->DriverBindingHandle,\r
+ Device->DeviceHandle,\r
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER\r
+ );\r
+\r
+ //\r
+ // Build controller name for Component Name (2) protocol.\r
+ //\r
+ UnicodeSPrintAsciiFormat (Device->ModelName, sizeof (Device->ModelName), "%a-%a-%x", Private->ControllerData->Sn, Private->ControllerData->Mn, NamespaceData->Eui64);\r
+\r
+ AddUnicodeString2 (\r
+ "eng",\r
+ gNvmExpressComponentName.SupportedLanguages,\r
+ &Device->ControllerNameTable,\r
+ Device->ModelName,\r
+ TRUE\r
+ );\r
+\r
+ AddUnicodeString2 (\r
+ "en",\r
+ gNvmExpressComponentName2.SupportedLanguages,\r
+ &Device->ControllerNameTable,\r
+ Device->ModelName,\r
+ FALSE\r
+ );\r
+ }\r
+\r
+Exit:\r
+ if(NamespaceData != NULL) {\r
+ FreePool (NamespaceData);\r
+ }\r
+\r
+ if(EFI_ERROR(Status) && (Device != NULL) && (Device->DevicePath != NULL)) {\r
+ FreePool (Device->DevicePath);\r
+ }\r
+ if(EFI_ERROR(Status) && (Device != NULL)) {\r
+ FreePool (Device);\r
+ }\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Discover all Nvm Express device namespaces, and create child handles for them with BlockIo\r
+ and DiskInfo protocol instances.\r
+\r
+ @param[in] Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure.\r
+\r
+ @retval EFI_SUCCESS All the namespaces in the device are successfully enumerated.\r
+ @return Others Some error occurs when enumerating the namespaces.\r
+\r
+**/\r
+EFI_STATUS\r
+DiscoverAllNamespaces (\r
+ IN NVME_CONTROLLER_PRIVATE_DATA *Private\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT32 NamespaceId;\r
+ UINT64 NamespaceUuid;\r
+ NVM_EXPRESS_PASS_THRU_PROTOCOL *Passthru;\r
+\r
+ NamespaceId = 0xFFFFFFFF;\r
+ NamespaceUuid = 0;\r
+ Passthru = &Private->Passthru;\r
+\r
+ while (TRUE) {\r
+ Status = Passthru->GetNextNamespace (\r
+ Passthru,\r
+ (UINT32 *)&NamespaceId,\r
+ (UINT64 *)&NamespaceUuid\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ break;\r
+ }\r
+\r
+ Status = EnumerateNvmeDevNamespace (\r
+ Private,\r
+ NamespaceId,\r
+ NamespaceUuid\r
+ );\r
+\r
+ if (EFI_ERROR(Status)) {\r
+ continue;\r
+ }\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Unregisters a Nvm Express device namespace.\r
+\r
+ This function removes the protocols installed on the controller handle and\r
+ frees the resources allocated for the namespace.\r
+\r
+ @param This The pointer to EFI_DRIVER_BINDING_PROTOCOL instance.\r
+ @param Controller The controller handle of the namespace.\r
+ @param Handle The child handle.\r
+\r
+ @retval EFI_SUCCESS The namespace is successfully unregistered.\r
+ @return Others Some error occurs when unregistering the namespace.\r
+\r
+**/\r
+EFI_STATUS\r
+UnregisterNvmeNamespace (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE Controller,\r
+ IN EFI_HANDLE Handle\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_PCI_IO_PROTOCOL *PciIo;\r
+ EFI_BLOCK_IO_PROTOCOL *BlockIo;\r
+ NVME_DEVICE_PRIVATE_DATA *Device;\r
+\r
+ BlockIo = NULL;\r
+\r
+ Status = gBS->OpenProtocol (\r
+ Handle,\r
+ &gEfiBlockIoProtocolGuid,\r
+ (VOID **) &BlockIo,\r
+ This->DriverBindingHandle,\r
+ Controller,\r
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Device = NVME_DEVICE_PRIVATE_DATA_FROM_BLOCK_IO (BlockIo);\r
+\r
+ //\r
+ // Close the child handle\r
+ //\r
+ gBS->CloseProtocol (\r
+ Controller,\r
+ &gEfiPciIoProtocolGuid,\r
+ This->DriverBindingHandle,\r
+ Handle\r
+ );\r
+\r
+ //\r
+ // The Nvm Express driver installs the BlockIo and DiskInfo in the DriverBindingStart().\r
+ // Here should uninstall both of them.\r
+ //\r
+ Status = gBS->UninstallMultipleProtocolInterfaces (\r
+ Handle,\r
+ &gEfiDevicePathProtocolGuid,\r
+ Device->DevicePath,\r
+ &gEfiBlockIoProtocolGuid,\r
+ &Device->BlockIo,\r
+ &gEfiDiskInfoProtocolGuid,\r
+ &Device->DiskInfo,\r
+ NULL\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ gBS->OpenProtocol (\r
+ Controller,\r
+ &gEfiPciIoProtocolGuid,\r
+ (VOID **) &PciIo,\r
+ This->DriverBindingHandle,\r
+ Handle,\r
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER\r
+ );\r
+ return Status;\r
+ }\r
+\r
+ if(Device->DevicePath != NULL) {\r
+ FreePool (Device->DevicePath);\r
+ }\r
+\r
+ if (Device->ControllerNameTable != NULL) {\r
+ FreeUnicodeStringTable (Device->ControllerNameTable);\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Tests to see if this driver supports a given controller. If a child device is provided,\r
+ it further tests to see if this driver supports creating a handle for the specified child device.\r
+\r
+ This function checks to see if the driver specified by This supports the device specified by\r
+ ControllerHandle. Drivers will typically use the device path attached to\r
+ ControllerHandle and/or the services from the bus I/O abstraction attached to\r
+ ControllerHandle to determine if the driver supports ControllerHandle. This function\r
+ may be called many times during platform initialization. In order to reduce boot times, the tests\r
+ performed by this function must be very small, and take as little time as possible to execute. This\r
+ function must not change the state of any hardware devices, and this function must be aware that the\r
+ device specified by ControllerHandle may already be managed by the same driver or a\r
+ different driver. This function must match its calls to AllocatePages() with FreePages(),\r
+ AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol().\r
+ Since ControllerHandle may have been previously started by the same driver, if a protocol is\r
+ already in the opened state, then it must not be closed with CloseProtocol(). This is required\r
+ to guarantee the state of ControllerHandle is not modified by this function.\r
+\r
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.\r
+ @param[in] ControllerHandle The handle of the controller to test. This handle\r
+ must support a protocol interface that supplies\r
+ an I/O abstraction to the driver.\r
+ @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This\r
+ parameter is ignored by device drivers, and is optional for bus\r
+ drivers. For bus drivers, if this parameter is not NULL, then\r
+ the bus driver must determine if the bus controller specified\r
+ by ControllerHandle and the child controller specified\r
+ by RemainingDevicePath are both supported by this\r
+ bus driver.\r
+\r
+ @retval EFI_SUCCESS The device specified by ControllerHandle and\r
+ RemainingDevicePath is supported by the driver specified by This.\r
+ @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and\r
+ RemainingDevicePath is already being managed by the driver\r
+ specified by This.\r
+ @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and\r
+ RemainingDevicePath is already being managed by a different\r
+ driver or an application that requires exclusive access.\r
+ Currently not implemented.\r
+ @retval EFI_UNSUPPORTED The device specified by ControllerHandle and\r
+ RemainingDevicePath is not supported by the driver specified by This.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+NvmExpressDriverBindingSupported (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE Controller,\r
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_DEV_PATH_PTR DevicePathNode;\r
+ EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath;\r
+ EFI_PCI_IO_PROTOCOL *PciIo;\r
+ UINT8 ClassCode[3];\r
+\r
+ //\r
+ // Check whether device path is valid\r
+ //\r
+ if (RemainingDevicePath != NULL) {\r
+ //\r
+ // Check if RemainingDevicePath is the End of Device Path Node,\r
+ // if yes, go on checking other conditions\r
+ //\r
+ if (!IsDevicePathEnd (RemainingDevicePath)) {\r
+ //\r
+ // If RemainingDevicePath isn't the End of Device Path Node,\r
+ // check its validation\r
+ //\r
+ DevicePathNode.DevPath = RemainingDevicePath;\r
+\r
+ if ((DevicePathNode.DevPath->Type != MESSAGING_DEVICE_PATH) ||\r
+ (DevicePathNode.DevPath->SubType != MSG_NVME_NAMESPACE_DP) ||\r
+ DevicePathNodeLength(DevicePathNode.DevPath) != sizeof(NVME_NAMESPACE_DEVICE_PATH)) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+ }\r
+ }\r
+\r
+ //\r
+ // Open the EFI Device Path protocol needed to perform the supported test\r
+ //\r
+ Status = gBS->OpenProtocol (\r
+ Controller,\r
+ &gEfiDevicePathProtocolGuid,\r
+ (VOID **) &ParentDevicePath,\r
+ This->DriverBindingHandle,\r
+ Controller,\r
+ EFI_OPEN_PROTOCOL_BY_DRIVER\r
+ );\r
+ if (Status == EFI_ALREADY_STARTED) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Close protocol, don't use device path protocol in the Support() function\r
+ //\r
+ gBS->CloseProtocol (\r
+ Controller,\r
+ &gEfiDevicePathProtocolGuid,\r
+ This->DriverBindingHandle,\r
+ Controller\r
+ );\r
+\r
+ //\r
+ // Attempt to Open PCI I/O Protocol\r
+ //\r
+ Status = gBS->OpenProtocol (\r
+ Controller,\r
+ &gEfiPciIoProtocolGuid,\r
+ (VOID **) &PciIo,\r
+ This->DriverBindingHandle,\r
+ Controller,\r
+ EFI_OPEN_PROTOCOL_BY_DRIVER\r
+ );\r
+ if (Status == EFI_ALREADY_STARTED) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Now further check the PCI header: Base class (offset 0x0B) and Sub Class (offset 0x0A).\r
+ // This controller should be a Nvm Express controller.\r
+ //\r
+ Status = PciIo->Pci.Read (\r
+ PciIo,\r
+ EfiPciIoWidthUint8,\r
+ PCI_CLASSCODE_OFFSET,\r
+ sizeof (ClassCode),\r
+ ClassCode\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto Done;\r
+ }\r
+\r
+ //\r
+ // Examine Nvm Express controller PCI Configuration table fields\r
+ //\r
+ if ((ClassCode[0] != PCI_IF_NVMHCI) || (ClassCode[1] != PCI_CLASS_MASS_STORAGE_NVM) || (ClassCode[2] != PCI_CLASS_MASS_STORAGE)) {\r
+ Status = EFI_UNSUPPORTED;\r
+ }\r
+\r
+Done:\r
+ gBS->CloseProtocol (\r
+ Controller,\r
+ &gEfiPciIoProtocolGuid,\r
+ This->DriverBindingHandle,\r
+ Controller\r
+ );\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Starts a device controller or a bus controller.\r
+\r
+ The Start() function is designed to be invoked from the EFI boot service ConnectController().\r
+ As a result, much of the error checking on the parameters to Start() has been moved into this\r
+ common boot service. It is legal to call Start() from other locations,\r
+ but the following calling restrictions must be followed or the system behavior will not be deterministic.\r
+ 1. ControllerHandle must be a valid EFI_HANDLE.\r
+ 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned\r
+ EFI_DEVICE_PATH_PROTOCOL.\r
+ 3. Prior to calling Start(), the Supported() function for the driver specified by This must\r
+ have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS.\r
+\r
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.\r
+ @param[in] ControllerHandle The handle of the controller to start. This handle\r
+ must support a protocol interface that supplies\r
+ an I/O abstraction to the driver.\r
+ @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This\r
+ parameter is ignored by device drivers, and is optional for bus\r
+ drivers. For a bus driver, if this parameter is NULL, then handles\r
+ for all the children of Controller are created by this driver.\r
+ If this parameter is not NULL and the first Device Path Node is\r
+ not the End of Device Path Node, then only the handle for the\r
+ child device specified by the first Device Path Node of\r
+ RemainingDevicePath is created by this driver.\r
+ If the first Device Path Node of RemainingDevicePath is\r
+ the End of Device Path Node, no child handle is created by this\r
+ driver.\r
+\r
+ @retval EFI_SUCCESS The device was started.\r
+ @retval EFI_DEVICE_ERROR The device could not be started due to a device error.Currently not implemented.\r
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.\r
+ @retval Others The driver failded to start the device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+NvmExpressDriverBindingStart (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE Controller,\r
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_PCI_IO_PROTOCOL *PciIo;\r
+ NVME_CONTROLLER_PRIVATE_DATA *Private;\r
+ EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath;\r
+ UINT32 NamespaceId;\r
+ UINT64 NamespaceUuid;\r
+ EFI_PHYSICAL_ADDRESS MappedAddr;\r
+ UINTN Bytes;\r
+\r
+ DEBUG ((EFI_D_INFO, "NvmExpressDriverBindingStart: start\n"));\r
+\r
+ Private = NULL;\r
+ ParentDevicePath = NULL;\r
+\r
+ Status = gBS->OpenProtocol (\r
+ Controller,\r
+ &gEfiDevicePathProtocolGuid,\r
+ (VOID **) &ParentDevicePath,\r
+ This->DriverBindingHandle,\r
+ Controller,\r
+ EFI_OPEN_PROTOCOL_BY_DRIVER\r
+ );\r
+ if ((EFI_ERROR (Status)) && (Status != EFI_ALREADY_STARTED)) {\r
+ return Status;\r
+ }\r
+\r
+ Status = gBS->OpenProtocol (\r
+ Controller,\r
+ &gEfiPciIoProtocolGuid,\r
+ (VOID **) &PciIo,\r
+ This->DriverBindingHandle,\r
+ Controller,\r
+ EFI_OPEN_PROTOCOL_BY_DRIVER\r
+ );\r
+\r
+ if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Check EFI_ALREADY_STARTED to reuse the original NVME_CONTROLLER_PRIVATE_DATA.\r
+ //\r
+ if (Status != EFI_ALREADY_STARTED) {\r
+ Private = AllocateZeroPool (sizeof (NVME_CONTROLLER_PRIVATE_DATA));\r
+\r
+ if (Private == NULL) {\r
+ DEBUG ((EFI_D_ERROR, "NvmExpressDriverBindingStart: allocating pool for Nvme Private Data failed!\n"));\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto Exit2;\r
+ }\r
+\r
+ //\r
+ // 4 x 4kB aligned buffers will be carved out of this buffer.\r
+ // 1st 4kB boundary is the start of the admin submission queue.\r
+ // 2nd 4kB boundary is the start of the admin completion queue.\r
+ // 3rd 4kB boundary is the start of I/O submission queue #1.\r
+ // 4th 4kB boundary is the start of I/O completion queue #1.\r
+ //\r
+ // Allocate 4 pages of memory, then map it for bus master read and write.\r
+ //\r
+ Status = PciIo->AllocateBuffer (\r
+ PciIo,\r
+ AllocateAnyPages,\r
+ EfiBootServicesData,\r
+ 6,\r
+ (VOID**)&Private->Buffer,\r
+ 0\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto Exit2;\r
+ }\r
+\r
+ Bytes = EFI_PAGES_TO_SIZE (4);\r
+ Status = PciIo->Map (\r
+ PciIo,\r
+ EfiPciIoOperationBusMasterCommonBuffer,\r
+ Private->Buffer,\r
+ &Bytes,\r
+ &MappedAddr,\r
+ &Private->Mapping\r
+ );\r
+\r
+ if (EFI_ERROR (Status) || (Bytes != EFI_PAGES_TO_SIZE (4))) {\r
+ goto Exit2;\r
+ }\r
+\r
+ Private->BufferPciAddr = (UINT8 *)(UINTN)MappedAddr;\r
+ ZeroMem (Private->Buffer, EFI_PAGES_TO_SIZE (4));\r
+\r
+ Private->Signature = NVME_CONTROLLER_PRIVATE_DATA_SIGNATURE;\r
+ Private->ControllerHandle = Controller;\r
+ Private->ImageHandle = This->DriverBindingHandle;\r
+ Private->DriverBindingHandle = This->DriverBindingHandle;\r
+ Private->PciIo = PciIo;\r
+ Private->ParentDevicePath = ParentDevicePath;\r
+ Private->Passthru.Mode = &Private->PassThruMode;\r
+ Private->Passthru.PassThru = NvmExpressPassThru;\r
+ Private->Passthru.GetNextNamespace = NvmExpressGetNextNamespace;\r
+ Private->Passthru.BuildDevicePath = NvmExpressBuildDevicePath;\r
+ Private->Passthru.GetNamespace = NvmExpressGetNamespace;\r
+ Private->PassThruMode.Attributes = NVM_EXPRESS_PASS_THRU_ATTRIBUTES_PHYSICAL;\r
+\r
+ Status = NvmeControllerInit (Private);\r
+\r
+ if (EFI_ERROR(Status)) {\r
+ goto Exit2;\r
+ }\r
+\r
+ Status = gBS->InstallMultipleProtocolInterfaces (\r
+ &Controller,\r
+ &gEfiCallerIdGuid,\r
+ Private,\r
+ NULL\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto Exit2;\r
+ }\r
+ } else {\r
+ Status = gBS->OpenProtocol (\r
+ Controller,\r
+ &gEfiCallerIdGuid,\r
+ (VOID **) &Private,\r
+ This->DriverBindingHandle,\r
+ Controller,\r
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ Private = NULL;\r
+ goto Exit1;\r
+ }\r
+ }\r
+\r
+ if (RemainingDevicePath == NULL) {\r
+ //\r
+ // Enumerate all NVME namespaces in the controller\r
+ //\r
+ Status = DiscoverAllNamespaces (\r
+ Private\r
+ );\r
+\r
+ } else if (!IsDevicePathEnd (RemainingDevicePath)) {\r
+ //\r
+ // Enumerate the specified NVME namespace\r
+ //\r
+ Status = Private->Passthru.GetNamespace (\r
+ &Private->Passthru,\r
+ RemainingDevicePath,\r
+ &NamespaceId,\r
+ &NamespaceUuid\r
+ );\r
+\r
+ if (!EFI_ERROR (Status)) {\r
+ Status = EnumerateNvmeDevNamespace (\r
+ Private,\r
+ NamespaceId,\r
+ NamespaceUuid\r
+ );\r
+ }\r
+ }\r
+\r
+ DEBUG ((EFI_D_INFO, "NvmExpressDriverBindingStart: end successfully\n"));\r
+ return EFI_SUCCESS;\r
+\r
+Exit1:\r
+ gBS->UninstallMultipleProtocolInterfaces (\r
+ Controller,\r
+ &gEfiCallerIdGuid,\r
+ Private,\r
+ NULL\r
+ );\r
+Exit2:\r
+ if ((Private != NULL) && (Private->Mapping != NULL)) {\r
+ PciIo->Unmap (PciIo, Private->Mapping);\r
+ }\r
+\r
+ if ((Private != NULL) && (Private->Buffer != NULL)) {\r
+ PciIo->FreeBuffer (PciIo, 4, Private->Buffer);\r
+ }\r
+\r
+ if (Private != NULL) {\r
+ FreePool (Private);\r
+ }\r
+\r
+ gBS->CloseProtocol (\r
+ Controller,\r
+ &gEfiPciIoProtocolGuid,\r
+ This->DriverBindingHandle,\r
+ Controller\r
+ );\r
+\r
+ gBS->CloseProtocol (\r
+ Controller,\r
+ &gEfiDevicePathProtocolGuid,\r
+ This->DriverBindingHandle,\r
+ Controller\r
+ );\r
+\r
+ DEBUG ((EFI_D_INFO, "NvmExpressDriverBindingStart: end with %r\n", Status));\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Stops a device controller or a bus controller.\r
+\r
+ The Stop() function is designed to be invoked from the EFI boot service DisconnectController().\r
+ As a result, much of the error checking on the parameters to Stop() has been moved\r
+ into this common boot service. It is legal to call Stop() from other locations,\r
+ but the following calling restrictions must be followed or the system behavior will not be deterministic.\r
+ 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this\r
+ same driver's Start() function.\r
+ 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid\r
+ EFI_HANDLE. In addition, all of these handles must have been created in this driver's\r
+ Start() function, and the Start() function must have called OpenProtocol() on\r
+ ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER.\r
+\r
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.\r
+ @param[in] ControllerHandle A handle to the device being stopped. The handle must\r
+ support a bus specific I/O protocol for the driver\r
+ to use to stop the device.\r
+ @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer.\r
+ @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL\r
+ if NumberOfChildren is 0.\r
+\r
+ @retval EFI_SUCCESS The device was stopped.\r
+ @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+NvmExpressDriverBindingStop (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE Controller,\r
+ IN UINTN NumberOfChildren,\r
+ IN EFI_HANDLE *ChildHandleBuffer\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ BOOLEAN AllChildrenStopped;\r
+ UINTN Index;\r
+ NVME_CONTROLLER_PRIVATE_DATA *Private;\r
+\r
+ if (NumberOfChildren == 0) {\r
+ Status = gBS->OpenProtocol (\r
+ Controller,\r
+ &gEfiCallerIdGuid,\r
+ (VOID **) &Private,\r
+ This->DriverBindingHandle,\r
+ Controller,\r
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+ );\r
+\r
+ if (!EFI_ERROR (Status)) {\r
+ gBS->UninstallMultipleProtocolInterfaces (\r
+ Controller,\r
+ &gEfiCallerIdGuid,\r
+ Private,\r
+ NULL\r
+ );\r
+\r
+ if (Private->Mapping != NULL) {\r
+ Private->PciIo->Unmap (Private->PciIo, Private->Mapping);\r
+ }\r
+\r
+ if (Private->Buffer != NULL) {\r
+ Private->PciIo->FreeBuffer (Private->PciIo, 4, Private->Buffer);\r
+ }\r
+\r
+ FreePool (Private->ControllerData);\r
+ FreePool (Private);\r
+ }\r
+\r
+ gBS->CloseProtocol (\r
+ Controller,\r
+ &gEfiPciIoProtocolGuid,\r
+ This->DriverBindingHandle,\r
+ Controller\r
+ );\r
+ gBS->CloseProtocol (\r
+ Controller,\r
+ &gEfiDevicePathProtocolGuid,\r
+ This->DriverBindingHandle,\r
+ Controller\r
+ );\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ AllChildrenStopped = TRUE;\r
+\r
+ for (Index = 0; Index < NumberOfChildren; Index++) {\r
+ Status = UnregisterNvmeNamespace (This, Controller, ChildHandleBuffer[Index]);\r
+ if (EFI_ERROR (Status)) {\r
+ AllChildrenStopped = FALSE;\r
+ }\r
+ }\r
+\r
+ if (!AllChildrenStopped) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ This is the unload handle for the NVM Express driver.\r
+\r
+ Disconnect the driver specified by ImageHandle from the NVMe device in the handle database.\r
+ Uninstall all the protocols installed in the driver.\r
+\r
+ @param[in] ImageHandle The drivers' driver image.\r
+\r
+ @retval EFI_SUCCESS The image is unloaded.\r
+ @retval Others Failed to unload the image.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+NvmExpressUnload (\r
+ IN EFI_HANDLE ImageHandle\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_HANDLE *DeviceHandleBuffer;\r
+ UINTN DeviceHandleCount;\r
+ UINTN Index;\r
+ EFI_DRIVER_BINDING_PROTOCOL *DriverBinding;\r
+ EFI_COMPONENT_NAME_PROTOCOL *ComponentName;\r
+ EFI_COMPONENT_NAME2_PROTOCOL *ComponentName2;\r
+\r
+ //\r
+ // Get the list of all the handles in the handle database.\r
+ // If there is an error getting the list, then the unload\r
+ // operation fails.\r
+ //\r
+ Status = gBS->LocateHandleBuffer (\r
+ AllHandles,\r
+ NULL,\r
+ NULL,\r
+ &DeviceHandleCount,\r
+ &DeviceHandleBuffer\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Disconnect the driver specified by ImageHandle from all\r
+ // the devices in the handle database.\r
+ //\r
+ for (Index = 0; Index < DeviceHandleCount; Index++) {\r
+ Status = gBS->DisconnectController (\r
+ DeviceHandleBuffer[Index],\r
+ ImageHandle,\r
+ NULL\r
+ );\r
+ }\r
+\r
+ //\r
+ // Uninstall all the protocols installed in the driver entry point\r
+ //\r
+ for (Index = 0; Index < DeviceHandleCount; Index++) {\r
+ Status = gBS->HandleProtocol (\r
+ DeviceHandleBuffer[Index],\r
+ &gEfiDriverBindingProtocolGuid,\r
+ (VOID **) &DriverBinding\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ continue;\r
+ }\r
+\r
+ if (DriverBinding->ImageHandle != ImageHandle) {\r
+ continue;\r
+ }\r
+\r
+ gBS->UninstallProtocolInterface (\r
+ ImageHandle,\r
+ &gEfiDriverBindingProtocolGuid,\r
+ DriverBinding\r
+ );\r
+\r
+ Status = gBS->HandleProtocol (\r
+ DeviceHandleBuffer[Index],\r
+ &gEfiComponentNameProtocolGuid,\r
+ (VOID **) &ComponentName\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ gBS->UninstallProtocolInterface (\r
+ ImageHandle,\r
+ &gEfiComponentNameProtocolGuid,\r
+ ComponentName\r
+ );\r
+ }\r
+\r
+ Status = gBS->HandleProtocol (\r
+ DeviceHandleBuffer[Index],\r
+ &gEfiComponentName2ProtocolGuid,\r
+ (VOID **) &ComponentName2\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ gBS->UninstallProtocolInterface (\r
+ ImageHandle,\r
+ &gEfiComponentName2ProtocolGuid,\r
+ ComponentName2\r
+ );\r
+ }\r
+ }\r
+\r
+ //\r
+ // Free the buffer containing the list of handles from the handle database\r
+ //\r
+ if (DeviceHandleBuffer != NULL) {\r
+ gBS->FreePool (DeviceHandleBuffer);\r
+ }\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ The entry point for Nvm Express driver, used to install Nvm Express driver on the ImageHandle.\r
+\r
+ @param ImageHandle The firmware allocated handle for this driver image.\r
+ @param SystemTable Pointer to the EFI system table.\r
+\r
+ @retval EFI_SUCCESS Driver loaded.\r
+ @retval other Driver not loaded.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+NvmExpressDriverEntry (\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_SYSTEM_TABLE *SystemTable\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ Status = EfiLibInstallDriverBindingComponentName2 (\r
+ ImageHandle,\r
+ SystemTable,\r
+ &gNvmExpressDriverBinding,\r
+ ImageHandle,\r
+ &gNvmExpressComponentName,\r
+ &gNvmExpressComponentName2\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ //\r
+ // Install EFI Driver Supported EFI Version Protocol required for\r
+ // EFI drivers that are on PCI and other plug in cards.\r
+ //\r
+ gNvmExpressDriverSupportedEfiVersion.FirmwareVersion = 0x00020028;\r
+ Status = gBS->InstallMultipleProtocolInterfaces (\r
+ &ImageHandle,\r
+ &gEfiDriverSupportedEfiVersionProtocolGuid,\r
+ &gNvmExpressDriverSupportedEfiVersion,\r
+ NULL\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+ return Status;\r
+}\r