For async commands, the buffer allocated for Prp list is not getting freed, which will cause memory leak for async read write command. For example testing async command flow with custom application to send multiple read write commands were resulting in decrease of available memory page in memmap, which eventually resulted in system hang. Hence freeing AsyncRequest->MapData, AsyncRequest->MapMeta, AsyncRequest->MapPrpList and AsyncRequest->PrpListHost when async command is completed. Cc: Feng Tian <feng.tian@intel.com> Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Suman Prakash <suman.p@samsung.com.com> Reviewed-by: Hao Wu <hao.a.wu@intel.com>
		
			
				
	
	
		
			1424 lines
		
	
	
		
			48 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1424 lines
		
	
	
		
			48 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
|   NvmExpressDxe driver is used to manage non-volatile memory subsystem which follows
 | |
|   NVM Express specification.
 | |
| 
 | |
|   Copyright (c) 2013 - 2016, Intel Corporation. All rights reserved.<BR>
 | |
|   This program and the accompanying materials
 | |
|   are licensed and made available under the terms and conditions of the BSD License
 | |
|   which accompanies this distribution.  The full text of the license may be found at
 | |
|   http://opensource.org/licenses/bsd-license.php.
 | |
| 
 | |
|   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
 | |
|   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 | |
| 
 | |
| **/
 | |
| 
 | |
| #include "NvmExpress.h"
 | |
| 
 | |
| //
 | |
| // NVM Express Driver Binding Protocol Instance
 | |
| //
 | |
| EFI_DRIVER_BINDING_PROTOCOL gNvmExpressDriverBinding = {
 | |
|   NvmExpressDriverBindingSupported,
 | |
|   NvmExpressDriverBindingStart,
 | |
|   NvmExpressDriverBindingStop,
 | |
|   0x10,
 | |
|   NULL,
 | |
|   NULL
 | |
| };
 | |
| 
 | |
| //
 | |
| // NVM Express EFI Driver Supported EFI Version Protocol Instance
 | |
| //
 | |
| EFI_DRIVER_SUPPORTED_EFI_VERSION_PROTOCOL gNvmExpressDriverSupportedEfiVersion = {
 | |
|   sizeof (EFI_DRIVER_SUPPORTED_EFI_VERSION_PROTOCOL), // Size of Protocol structure.
 | |
|   0                                                   // Version number to be filled at start up.
 | |
| };
 | |
| 
 | |
| //
 | |
| // Template for NVM Express Pass Thru Mode data structure.
 | |
| //
 | |
| GLOBAL_REMOVE_IF_UNREFERENCED EFI_NVM_EXPRESS_PASS_THRU_MODE gEfiNvmExpressPassThruMode = {
 | |
|   EFI_NVM_EXPRESS_PASS_THRU_ATTRIBUTES_PHYSICAL   |
 | |
|   EFI_NVM_EXPRESS_PASS_THRU_ATTRIBUTES_LOGICAL    |
 | |
|   EFI_NVM_EXPRESS_PASS_THRU_ATTRIBUTES_NONBLOCKIO |
 | |
|   EFI_NVM_EXPRESS_PASS_THRU_ATTRIBUTES_CMD_SET_NVM,
 | |
|   sizeof (UINTN),
 | |
|   0x10100
 | |
| };
 | |
| 
 | |
| /**
 | |
|   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.
 | |
| 
 | |
|   @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
 | |
|   )
 | |
| {
 | |
|   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;
 | |
|   UINT8                                 Sn[21];
 | |
|   UINT8                                 Mn[41];
 | |
|   VOID                                  *DummyInterface;
 | |
| 
 | |
|   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) {
 | |
|       Status = EFI_OUT_OF_RESOURCES;
 | |
|       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;
 | |
|     Device->Media.IoAlign        = Private->PassThruMode.IoAlign;
 | |
| 
 | |
|     Flbas     = NamespaceData->Flbas;
 | |
|     LbaFmtIdx = Flbas & 0xF;
 | |
|     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 BlockIo2 Protocol instance
 | |
|     //
 | |
|     Device->BlockIo2.Media          = &Device->Media;
 | |
|     Device->BlockIo2.Reset          = NvmeBlockIoResetEx;
 | |
|     Device->BlockIo2.ReadBlocksEx   = NvmeBlockIoReadBlocksEx;
 | |
|     Device->BlockIo2.WriteBlocksEx  = NvmeBlockIoWriteBlocksEx;
 | |
|     Device->BlockIo2.FlushBlocksEx  = NvmeBlockIoFlushBlocksEx;
 | |
|     InitializeListHead (&Device->AsyncQueue);
 | |
| 
 | |
|     //
 | |
|     // Create StorageSecurityProtocol Instance
 | |
|     //
 | |
|     Device->StorageSecurity.ReceiveData = NvmeStorageSecurityReceiveData;
 | |
|     Device->StorageSecurity.SendData    = NvmeStorageSecuritySendData;
 | |
| 
 | |
|     //
 | |
|     // Create DiskInfo Protocol instance
 | |
|     //
 | |
|     CopyMem (&Device->NamespaceData, NamespaceData, sizeof (NVME_ADMIN_NAMESPACE_DATA));
 | |
|     InitializeDiskInfo (Device);
 | |
| 
 | |
|     //
 | |
|     // Create a Nvm Express Namespace Device Path Node
 | |
|     //
 | |
|     Status = Private->Passthru.BuildDevicePath (
 | |
|                                  &Private->Passthru,
 | |
|                                  Device->NamespaceId,
 | |
|                                  &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,
 | |
|                     &gEfiBlockIo2ProtocolGuid,
 | |
|                     &Device->BlockIo2,
 | |
|                     &gEfiDiskInfoProtocolGuid,
 | |
|                     &Device->DiskInfo,
 | |
|                     NULL
 | |
|                     );
 | |
| 
 | |
|     if(EFI_ERROR(Status)) {
 | |
|       goto Exit;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Check if the NVMe controller supports the Security Send and Security Receive commands
 | |
|     //
 | |
|     if ((Private->ControllerData->Oacs & SECURITY_SEND_RECEIVE_SUPPORTED) != 0) {
 | |
|       Status = gBS->InstallProtocolInterface (
 | |
|                       &Device->DeviceHandle,
 | |
|                       &gEfiStorageSecurityCommandProtocolGuid,
 | |
|                       EFI_NATIVE_INTERFACE,
 | |
|                       &Device->StorageSecurity
 | |
|                       );
 | |
|       if(EFI_ERROR(Status)) {
 | |
|         gBS->UninstallMultipleProtocolInterfaces (
 | |
|                &Device->DeviceHandle,
 | |
|                &gEfiDevicePathProtocolGuid,
 | |
|                Device->DevicePath,
 | |
|                &gEfiBlockIoProtocolGuid,
 | |
|                &Device->BlockIo,
 | |
|                &gEfiBlockIo2ProtocolGuid,
 | |
|                &Device->BlockIo2,
 | |
|                &gEfiDiskInfoProtocolGuid,
 | |
|                &Device->DiskInfo,
 | |
|                NULL
 | |
|                );
 | |
|         goto Exit;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     gBS->OpenProtocol (
 | |
|            Private->ControllerHandle,
 | |
|            &gEfiNvmExpressPassThruProtocolGuid,
 | |
|            (VOID **) &DummyInterface,
 | |
|            Private->DriverBindingHandle,
 | |
|            Device->DeviceHandle,
 | |
|            EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
 | |
|            );
 | |
| 
 | |
|     //
 | |
|     // Dump NvmExpress Identify Namespace Data
 | |
|     //
 | |
|     DEBUG ((EFI_D_INFO, " == NVME IDENTIFY NAMESPACE [%d] DATA ==\n", NamespaceId));
 | |
|     DEBUG ((EFI_D_INFO, "    NSZE        : 0x%x\n", NamespaceData->Nsze));
 | |
|     DEBUG ((EFI_D_INFO, "    NCAP        : 0x%x\n", NamespaceData->Ncap));
 | |
|     DEBUG ((EFI_D_INFO, "    NUSE        : 0x%x\n", NamespaceData->Nuse));
 | |
|     DEBUG ((EFI_D_INFO, "    LBAF0.LBADS : 0x%x\n", (NamespaceData->LbaFormat[0].Lbads)));
 | |
| 
 | |
|     //
 | |
|     // Build controller name for Component Name (2) protocol.
 | |
|     //
 | |
|     CopyMem (Sn, Private->ControllerData->Sn, sizeof (Private->ControllerData->Sn));
 | |
|     Sn[20] = 0;
 | |
|     CopyMem (Mn, Private->ControllerData->Mn, sizeof (Private->ControllerData->Mn));
 | |
|     Mn[40] = 0;
 | |
|     UnicodeSPrintAsciiFormat (Device->ModelName, sizeof (Device->ModelName), "%a-%a-%x", Sn, 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 (NewDevicePathNode != NULL) {
 | |
|     FreePool (NewDevicePathNode);
 | |
|   }
 | |
| 
 | |
|   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;
 | |
|   EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL    *Passthru;
 | |
| 
 | |
|   NamespaceId   = 0xFFFFFFFF;
 | |
|   Passthru      = &Private->Passthru;
 | |
| 
 | |
|   while (TRUE) {
 | |
|     Status = Passthru->GetNextNamespace (
 | |
|                          Passthru,
 | |
|                          (UINT32 *)&NamespaceId
 | |
|                          );
 | |
| 
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     Status = EnumerateNvmeDevNamespace (
 | |
|                Private,
 | |
|                NamespaceId
 | |
|                );
 | |
| 
 | |
|     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_BLOCK_IO_PROTOCOL                    *BlockIo;
 | |
|   NVME_DEVICE_PRIVATE_DATA                 *Device;
 | |
|   EFI_STORAGE_SECURITY_COMMAND_PROTOCOL    *StorageSecurity;
 | |
|   BOOLEAN                                  IsEmpty;
 | |
|   EFI_TPL                                  OldTpl;
 | |
|   VOID                                     *DummyInterface;
 | |
| 
 | |
|   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);
 | |
| 
 | |
|   //
 | |
|   // Wait for the device's asynchronous I/O queue to become empty.
 | |
|   //
 | |
|   while (TRUE) {
 | |
|     OldTpl  = gBS->RaiseTPL (TPL_NOTIFY);
 | |
|     IsEmpty = IsListEmpty (&Device->AsyncQueue);
 | |
|     gBS->RestoreTPL (OldTpl);
 | |
| 
 | |
|     if (IsEmpty) {
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     gBS->Stall (100);
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Close the child handle
 | |
|   //
 | |
|   gBS->CloseProtocol (
 | |
|          Controller,
 | |
|          &gEfiNvmExpressPassThruProtocolGuid,
 | |
|          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,
 | |
|                   &gEfiBlockIo2ProtocolGuid,
 | |
|                   &Device->BlockIo2,
 | |
|                   &gEfiDiskInfoProtocolGuid,
 | |
|                   &Device->DiskInfo,
 | |
|                   NULL
 | |
|                   );
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     gBS->OpenProtocol (
 | |
|            Controller,
 | |
|            &gEfiNvmExpressPassThruProtocolGuid,
 | |
|            (VOID **) &DummyInterface,
 | |
|            This->DriverBindingHandle,
 | |
|            Handle,
 | |
|            EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
 | |
|            );
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // If Storage Security Command Protocol is installed, then uninstall this protocol.
 | |
|   //
 | |
|   Status = gBS->OpenProtocol (
 | |
|                   Handle,
 | |
|                   &gEfiStorageSecurityCommandProtocolGuid,
 | |
|                   (VOID **) &StorageSecurity,
 | |
|                   This->DriverBindingHandle,
 | |
|                   Controller,
 | |
|                   EFI_OPEN_PROTOCOL_GET_PROTOCOL
 | |
|                   );
 | |
| 
 | |
|   if (!EFI_ERROR (Status)) {
 | |
|     Status = gBS->UninstallProtocolInterface (
 | |
|                     Handle,
 | |
|                     &gEfiStorageSecurityCommandProtocolGuid,
 | |
|                     &Device->StorageSecurity
 | |
|                     );
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       gBS->OpenProtocol (
 | |
|         Controller,
 | |
|         &gEfiNvmExpressPassThruProtocolGuid,
 | |
|         (VOID **) &DummyInterface,
 | |
|         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);
 | |
|   }
 | |
| 
 | |
|   FreePool (Device);
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Call back function when the timer event is signaled.
 | |
| 
 | |
|   @param[in]  Event     The Event this notify function registered to.
 | |
|   @param[in]  Context   Pointer to the context data registered to the
 | |
|                         Event.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| EFIAPI
 | |
| ProcessAsyncTaskList (
 | |
|   IN EFI_EVENT                    Event,
 | |
|   IN VOID*                        Context
 | |
|   )
 | |
| {
 | |
|   NVME_CONTROLLER_PRIVATE_DATA         *Private;
 | |
|   EFI_PCI_IO_PROTOCOL                  *PciIo;
 | |
|   NVME_CQ                              *Cq;
 | |
|   UINT16                               QueueId;
 | |
|   UINT32                               Data;
 | |
|   LIST_ENTRY                           *Link;
 | |
|   LIST_ENTRY                           *NextLink;
 | |
|   NVME_PASS_THRU_ASYNC_REQ             *AsyncRequest;
 | |
|   NVME_BLKIO2_SUBTASK                  *Subtask;
 | |
|   NVME_BLKIO2_REQUEST                  *BlkIo2Request;
 | |
|   EFI_BLOCK_IO2_TOKEN                  *Token;
 | |
|   BOOLEAN                              HasNewItem;
 | |
|   EFI_STATUS                           Status;
 | |
| 
 | |
|   Private    = (NVME_CONTROLLER_PRIVATE_DATA*)Context;
 | |
|   QueueId    = 2;
 | |
|   Cq         = Private->CqBuffer[QueueId] + Private->CqHdbl[QueueId].Cqh;
 | |
|   HasNewItem = FALSE;
 | |
|   PciIo      = Private->PciIo;
 | |
| 
 | |
|   //
 | |
|   // Submit asynchronous subtasks to the NVMe Submission Queue
 | |
|   //
 | |
|   for (Link = GetFirstNode (&Private->UnsubmittedSubtasks);
 | |
|        !IsNull (&Private->UnsubmittedSubtasks, Link);
 | |
|        Link = NextLink) {
 | |
|     NextLink      = GetNextNode (&Private->UnsubmittedSubtasks, Link);
 | |
|     Subtask       = NVME_BLKIO2_SUBTASK_FROM_LINK (Link);
 | |
|     BlkIo2Request = Subtask->BlockIo2Request;
 | |
|     Token         = BlkIo2Request->Token;
 | |
|     RemoveEntryList (Link);
 | |
|     BlkIo2Request->UnsubmittedSubtaskNum--;
 | |
| 
 | |
|     //
 | |
|     // If any previous subtask fails, do not process subsequent ones.
 | |
|     //
 | |
|     if (Token->TransactionStatus != EFI_SUCCESS) {
 | |
|       if (IsListEmpty (&BlkIo2Request->SubtasksQueue) &&
 | |
|           BlkIo2Request->LastSubtaskSubmitted &&
 | |
|           (BlkIo2Request->UnsubmittedSubtaskNum == 0)) {
 | |
|         //
 | |
|         // Remove the BlockIo2 request from the device asynchronous queue.
 | |
|         //
 | |
|         RemoveEntryList (&BlkIo2Request->Link);
 | |
|         FreePool (BlkIo2Request);
 | |
|         gBS->SignalEvent (Token->Event);
 | |
|       }
 | |
| 
 | |
|       FreePool (Subtask->CommandPacket->NvmeCmd);
 | |
|       FreePool (Subtask->CommandPacket->NvmeCompletion);
 | |
|       FreePool (Subtask->CommandPacket);
 | |
|       FreePool (Subtask);
 | |
| 
 | |
|       continue;
 | |
|     }
 | |
| 
 | |
|     Status = Private->Passthru.PassThru (
 | |
|                                  &Private->Passthru,
 | |
|                                  Subtask->NamespaceId,
 | |
|                                  Subtask->CommandPacket,
 | |
|                                  Subtask->Event
 | |
|                                  );
 | |
|     if (Status == EFI_NOT_READY) {
 | |
|       InsertHeadList (&Private->UnsubmittedSubtasks, Link);
 | |
|       BlkIo2Request->UnsubmittedSubtaskNum++;
 | |
|       break;
 | |
|     } else if (EFI_ERROR (Status)) {
 | |
|       Token->TransactionStatus = EFI_DEVICE_ERROR;
 | |
| 
 | |
|       if (IsListEmpty (&BlkIo2Request->SubtasksQueue) &&
 | |
|           Subtask->IsLast) {
 | |
|         //
 | |
|         // Remove the BlockIo2 request from the device asynchronous queue.
 | |
|         //
 | |
|         RemoveEntryList (&BlkIo2Request->Link);
 | |
|         FreePool (BlkIo2Request);
 | |
|         gBS->SignalEvent (Token->Event);
 | |
|       }
 | |
| 
 | |
|       FreePool (Subtask->CommandPacket->NvmeCmd);
 | |
|       FreePool (Subtask->CommandPacket->NvmeCompletion);
 | |
|       FreePool (Subtask->CommandPacket);
 | |
|       FreePool (Subtask);
 | |
|     } else {
 | |
|       InsertTailList (&BlkIo2Request->SubtasksQueue, Link);
 | |
|       if (Subtask->IsLast) {
 | |
|         BlkIo2Request->LastSubtaskSubmitted = TRUE;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   while (Cq->Pt != Private->Pt[QueueId]) {
 | |
|     ASSERT (Cq->Sqid == QueueId);
 | |
| 
 | |
|     HasNewItem = TRUE;
 | |
| 
 | |
|     //
 | |
|     // Find the command with given Command Id.
 | |
|     //
 | |
|     for (Link = GetFirstNode (&Private->AsyncPassThruQueue);
 | |
|          !IsNull (&Private->AsyncPassThruQueue, Link);
 | |
|          Link = NextLink) {
 | |
|       NextLink = GetNextNode (&Private->AsyncPassThruQueue, Link);
 | |
|       AsyncRequest = NVME_PASS_THRU_ASYNC_REQ_FROM_THIS (Link);
 | |
|       if (AsyncRequest->CommandId == Cq->Cid) {
 | |
|         //
 | |
|         // Copy the Respose Queue entry for this command to the callers
 | |
|         // response buffer.
 | |
|         //
 | |
|         CopyMem (
 | |
|           AsyncRequest->Packet->NvmeCompletion,
 | |
|           Cq,
 | |
|           sizeof(EFI_NVM_EXPRESS_COMPLETION)
 | |
|           );
 | |
| 
 | |
|         //
 | |
|         // Free the resources allocated before cmd submission
 | |
|         //
 | |
|         if (AsyncRequest->MapData != NULL) {
 | |
|           PciIo->Unmap (PciIo, AsyncRequest->MapData);
 | |
|         }
 | |
|         if (AsyncRequest->MapMeta != NULL) {
 | |
|           PciIo->Unmap (PciIo, AsyncRequest->MapMeta);
 | |
|         }
 | |
|         if (AsyncRequest->MapPrpList != NULL) {
 | |
|           PciIo->Unmap (PciIo, AsyncRequest->MapPrpList);
 | |
|         }
 | |
|         if (AsyncRequest->PrpListHost != NULL) {
 | |
|           PciIo->FreeBuffer (
 | |
|                    PciIo,
 | |
|                    AsyncRequest->PrpListNo,
 | |
|                    AsyncRequest->PrpListHost
 | |
|                    );
 | |
|         }
 | |
| 
 | |
|         RemoveEntryList (Link);
 | |
|         gBS->SignalEvent (AsyncRequest->CallerEvent);
 | |
|         FreePool (AsyncRequest);
 | |
| 
 | |
|         //
 | |
|         // Update submission queue head.
 | |
|         //
 | |
|         Private->AsyncSqHead = Cq->Sqhd;
 | |
|         break;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     Private->CqHdbl[QueueId].Cqh++;
 | |
|     if (Private->CqHdbl[QueueId].Cqh > NVME_ASYNC_CCQ_SIZE) {
 | |
|       Private->CqHdbl[QueueId].Cqh = 0;
 | |
|       Private->Pt[QueueId] ^= 1;
 | |
|     }
 | |
| 
 | |
|     Cq = Private->CqBuffer[QueueId] + Private->CqHdbl[QueueId].Cqh;
 | |
|   }
 | |
| 
 | |
|   if (HasNewItem) {
 | |
|     Data  = ReadUnaligned32 ((UINT32*)&Private->CqHdbl[QueueId]);
 | |
|     PciIo->Mem.Write (
 | |
|                  PciIo,
 | |
|                  EfiPciIoWidthUint32,
 | |
|                  NVME_BAR,
 | |
|                  NVME_CQHDBL_OFFSET(QueueId, Private->Cap.Dstrd),
 | |
|                  1,
 | |
|                  &Data
 | |
|                  );
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|   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;
 | |
|   EFI_PHYSICAL_ADDRESS                MappedAddr;
 | |
|   UINTN                               Bytes;
 | |
|   EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL  *Passthru;
 | |
| 
 | |
|   DEBUG ((EFI_D_INFO, "NvmExpressDriverBindingStart: start\n"));
 | |
| 
 | |
|   Private          = NULL;
 | |
|   Passthru         = 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 Exit;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // 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 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.
 | |
|     // 5th 4kB boundary is the start of I/O submission queue #2.
 | |
|     // 6th 4kB boundary is the start of I/O completion queue #2.
 | |
|     //
 | |
|     // Allocate 6 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 Exit;
 | |
|     }
 | |
| 
 | |
|     Bytes = EFI_PAGES_TO_SIZE (6);
 | |
|     Status = PciIo->Map (
 | |
|                       PciIo,
 | |
|                       EfiPciIoOperationBusMasterCommonBuffer,
 | |
|                       Private->Buffer,
 | |
|                       &Bytes,
 | |
|                       &MappedAddr,
 | |
|                       &Private->Mapping
 | |
|                       );
 | |
| 
 | |
|     if (EFI_ERROR (Status) || (Bytes != EFI_PAGES_TO_SIZE (6))) {
 | |
|       goto Exit;
 | |
|     }
 | |
| 
 | |
|     Private->BufferPciAddr = (UINT8 *)(UINTN)MappedAddr;
 | |
| 
 | |
|     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;
 | |
|     CopyMem (&Private->PassThruMode, &gEfiNvmExpressPassThruMode, sizeof (EFI_NVM_EXPRESS_PASS_THRU_MODE));
 | |
|     InitializeListHead (&Private->AsyncPassThruQueue);
 | |
|     InitializeListHead (&Private->UnsubmittedSubtasks);
 | |
| 
 | |
|     Status = NvmeControllerInit (Private);
 | |
|     if (EFI_ERROR(Status)) {
 | |
|       goto Exit;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Start the asynchronous I/O completion monitor
 | |
|     //
 | |
|     Status = gBS->CreateEvent (
 | |
|                     EVT_TIMER | EVT_NOTIFY_SIGNAL,
 | |
|                     TPL_NOTIFY,
 | |
|                     ProcessAsyncTaskList,
 | |
|                     Private,
 | |
|                     &Private->TimerEvent
 | |
|                     );
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       goto Exit;
 | |
|     }
 | |
| 
 | |
|     Status = gBS->SetTimer (
 | |
|                     Private->TimerEvent,
 | |
|                     TimerPeriodic,
 | |
|                     NVME_HC_ASYNC_TIMER
 | |
|                     );
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       goto Exit;
 | |
|     }
 | |
| 
 | |
|     Status = gBS->InstallMultipleProtocolInterfaces (
 | |
|                     &Controller,
 | |
|                     &gEfiNvmExpressPassThruProtocolGuid,
 | |
|                     &Private->Passthru,
 | |
|                     NULL
 | |
|                     );
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       goto Exit;
 | |
|     }
 | |
|   } else {
 | |
|     Status = gBS->OpenProtocol (
 | |
|                     Controller,
 | |
|                     &gEfiNvmExpressPassThruProtocolGuid,
 | |
|                     (VOID **) &Passthru,
 | |
|                     This->DriverBindingHandle,
 | |
|                     Controller,
 | |
|                     EFI_OPEN_PROTOCOL_GET_PROTOCOL
 | |
|                     );
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       goto Exit;
 | |
|     }
 | |
| 
 | |
|     Private = NVME_CONTROLLER_PRIVATE_DATA_FROM_PASS_THRU (Passthru);
 | |
|   }
 | |
| 
 | |
|   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
 | |
|                                  );
 | |
| 
 | |
|     if (!EFI_ERROR (Status)) {
 | |
|       Status = EnumerateNvmeDevNamespace (
 | |
|                  Private,
 | |
|                  NamespaceId
 | |
|                  );
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   DEBUG ((EFI_D_INFO, "NvmExpressDriverBindingStart: end successfully\n"));
 | |
|   return EFI_SUCCESS;
 | |
| 
 | |
| Exit:
 | |
|   if ((Private != NULL) && (Private->Mapping != NULL)) {
 | |
|     PciIo->Unmap (PciIo, Private->Mapping);
 | |
|   }
 | |
| 
 | |
|   if ((Private != NULL) && (Private->Buffer != NULL)) {
 | |
|     PciIo->FreeBuffer (PciIo, 6, Private->Buffer);
 | |
|   }
 | |
| 
 | |
|   if ((Private != NULL) && (Private->ControllerData != NULL)) {
 | |
|     FreePool (Private->ControllerData);
 | |
|   }
 | |
| 
 | |
|   if (Private != NULL) {
 | |
|     if (Private->TimerEvent != NULL) {
 | |
|       gBS->CloseEvent (Private->TimerEvent);
 | |
|     }
 | |
| 
 | |
|     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;
 | |
|   EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL  *PassThru;
 | |
|   BOOLEAN                             IsEmpty;
 | |
|   EFI_TPL                             OldTpl;
 | |
| 
 | |
|   if (NumberOfChildren == 0) {
 | |
|     Status = gBS->OpenProtocol (
 | |
|                     Controller,
 | |
|                     &gEfiNvmExpressPassThruProtocolGuid,
 | |
|                     (VOID **) &PassThru,
 | |
|                     This->DriverBindingHandle,
 | |
|                     Controller,
 | |
|                     EFI_OPEN_PROTOCOL_GET_PROTOCOL
 | |
|                     );
 | |
| 
 | |
|     if (!EFI_ERROR (Status)) {
 | |
|       Private = NVME_CONTROLLER_PRIVATE_DATA_FROM_PASS_THRU (PassThru);
 | |
| 
 | |
|       //
 | |
|       // Wait for the asynchronous PassThru queue to become empty.
 | |
|       //
 | |
|       while (TRUE) {
 | |
|         OldTpl  = gBS->RaiseTPL (TPL_NOTIFY);
 | |
|         IsEmpty = IsListEmpty (&Private->AsyncPassThruQueue) &&
 | |
|                   IsListEmpty (&Private->UnsubmittedSubtasks);
 | |
|         gBS->RestoreTPL (OldTpl);
 | |
| 
 | |
|         if (IsEmpty) {
 | |
|           break;
 | |
|         }
 | |
| 
 | |
|         gBS->Stall (100);
 | |
|       }
 | |
| 
 | |
|       gBS->UninstallMultipleProtocolInterfaces (
 | |
|             Controller,
 | |
|             &gEfiNvmExpressPassThruProtocolGuid,
 | |
|             PassThru,
 | |
|             NULL
 | |
|             );
 | |
| 
 | |
|       if (Private->TimerEvent != NULL) {
 | |
|         gBS->CloseEvent (Private->TimerEvent);
 | |
|       }
 | |
| 
 | |
|       if (Private->Mapping != NULL) {
 | |
|         Private->PciIo->Unmap (Private->PciIo, Private->Mapping);
 | |
|       }
 | |
| 
 | |
|       if (Private->Buffer != NULL) {
 | |
|         Private->PciIo->FreeBuffer (Private->PciIo, 6, 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_COMPONENT_NAME_PROTOCOL       *ComponentName;
 | |
|   EFI_COMPONENT_NAME2_PROTOCOL      *ComponentName2;
 | |
| 
 | |
|   //
 | |
|   // Get the list of the device handles managed by this driver.
 | |
|   // If there is an error getting the list, then means the driver
 | |
|   // doesn't manage any device. At this way, we would only close
 | |
|   // those protocols installed at image handle.
 | |
|   //
 | |
|   DeviceHandleBuffer = NULL;
 | |
|   Status = gBS->LocateHandleBuffer (
 | |
|                   ByProtocol,
 | |
|                   &gEfiNvmExpressPassThruProtocolGuid,
 | |
|                   NULL,
 | |
|                   &DeviceHandleCount,
 | |
|                   &DeviceHandleBuffer
 | |
|                   );
 | |
| 
 | |
|   if (!EFI_ERROR (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
 | |
|                       );
 | |
|       if (EFI_ERROR (Status)) {
 | |
|         goto EXIT;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Uninstall all the protocols installed in the driver entry point
 | |
|   //
 | |
|   Status = gBS->UninstallMultipleProtocolInterfaces (
 | |
|                   ImageHandle,
 | |
|                   &gEfiDriverBindingProtocolGuid,
 | |
|                   &gNvmExpressDriverBinding,
 | |
|                   &gEfiDriverSupportedEfiVersionProtocolGuid,
 | |
|                   &gNvmExpressDriverSupportedEfiVersion,
 | |
|                   NULL
 | |
|                   );
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     goto EXIT;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Note we have to one by one uninstall the following protocols.
 | |
|   // It's because some of them are optionally installed based on
 | |
|   // the following PCD settings.
 | |
|   //   gEfiMdePkgTokenSpaceGuid.PcdDriverDiagnosticsDisable
 | |
|   //   gEfiMdePkgTokenSpaceGuid.PcdComponentNameDisable
 | |
|   //   gEfiMdePkgTokenSpaceGuid.PcdDriverDiagnostics2Disable
 | |
|   //   gEfiMdePkgTokenSpaceGuid.PcdComponentName2Disable
 | |
|   //
 | |
|   Status = gBS->HandleProtocol (
 | |
|                   ImageHandle,
 | |
|                   &gEfiComponentNameProtocolGuid,
 | |
|                   (VOID **) &ComponentName
 | |
|                   );
 | |
|   if (!EFI_ERROR (Status)) {
 | |
|     gBS->UninstallProtocolInterface (
 | |
|            ImageHandle,
 | |
|            &gEfiComponentNameProtocolGuid,
 | |
|            ComponentName
 | |
|            );
 | |
|   }
 | |
| 
 | |
|   Status = gBS->HandleProtocol (
 | |
|                   ImageHandle,
 | |
|                   &gEfiComponentName2ProtocolGuid,
 | |
|                   (VOID **) &ComponentName2
 | |
|                   );
 | |
|   if (!EFI_ERROR (Status)) {
 | |
|     gBS->UninstallProtocolInterface (
 | |
|            ImageHandle,
 | |
|            &gEfiComponentName2ProtocolGuid,
 | |
|            ComponentName2
 | |
|            );
 | |
|   }
 | |
| 
 | |
|   Status = EFI_SUCCESS;
 | |
| 
 | |
| EXIT:
 | |
|   //
 | |
|   // Free the buffer containing the list of handles from the handle database
 | |
|   //
 | |
|   if (DeviceHandleBuffer != NULL) {
 | |
|     gBS->FreePool (DeviceHandleBuffer);
 | |
|   }
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   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;
 | |
| }
 |