/** 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; }
/** Used to retrieve the next namespace ID for this NVM Express controller. The EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL.GetNextNamespace() function retrieves the next valid namespace ID on this NVM Express controller. If on input the value pointed to by NamespaceId is 0xFFFFFFFF, then the first valid namespace ID defined on the NVM Express controller is returned in the location pointed to by NamespaceId and a status of EFI_SUCCESS is returned. If on input the value pointed to by NamespaceId is an invalid namespace ID other than 0xFFFFFFFF, then EFI_INVALID_PARAMETER is returned. If on input the value pointed to by NamespaceId is a valid namespace ID, then the next valid namespace ID on the NVM Express controller is returned in the location pointed to by NamespaceId, and EFI_SUCCESS is returned. If the value pointed to by NamespaceId is the namespace ID of the last namespace on the NVM Express controller, then EFI_NOT_FOUND is returned. @param[in] This A pointer to the EFI_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. @retval EFI_SUCCESS The Namespace ID of the next Namespace was returned. @retval EFI_NOT_FOUND There are no more namespaces defined on this controller. @retval EFI_INVALID_PARAMETER NamespaceId is an invalid value other than 0xFFFFFFFF. **/ EFI_STATUS EFIAPI NvmExpressGetNextNamespace ( IN EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL *This, IN OUT UINT32 *NamespaceId ) { 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; } else { if (*NamespaceId > Private->ControllerData->Nn) { return EFI_INVALID_PARAMETER; } NextNamespaceId = *NamespaceId + 1; if (NextNamespaceId > Private->ControllerData->Nn) { return EFI_NOT_FOUND; } // // 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; } Done: if (NamespaceData != NULL) { FreePool(NamespaceData); } return Status; }
/** Check if the specified Nvm Express device namespace is active, and then get the Identify Namespace data. @param[in,out] Private The pointer to the PEI_NVME_CONTROLLER_PRIVATE_DATA data structure. @param[in] NamespaceId The specified namespace identifier. @retval EFI_SUCCESS The specified namespace in the device is successfully enumerated. @return Others Error occurs when enumerating the namespace. **/ EFI_STATUS EnumerateNvmeDevNamespace ( IN OUT PEI_NVME_CONTROLLER_PRIVATE_DATA *Private, IN UINT32 NamespaceId ) { EFI_STATUS Status; NVME_ADMIN_NAMESPACE_DATA *NamespaceData; PEI_NVME_NAMESPACE_INFO *NamespaceInfo; UINT32 DeviceIndex; UINT32 Lbads; UINT32 Flbas; UINT32 LbaFmtIdx; NamespaceData = (NVME_ADMIN_NAMESPACE_DATA *) AllocateZeroPool (sizeof (NVME_ADMIN_NAMESPACE_DATA)); if (NamespaceData == NULL) { return EFI_OUT_OF_RESOURCES; } // // Identify Namespace // Status = NvmeIdentifyNamespace ( Private, NamespaceId, NamespaceData ); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "%a: NvmeIdentifyNamespace fail, Status - %r\n", __FUNCTION__, Status)); goto Exit; } // // Validate Namespace // if (NamespaceData->Ncap == 0) { DEBUG ((DEBUG_INFO, "%a: Namespace ID %d is an inactive one.\n", __FUNCTION__, NamespaceId)); Status = EFI_DEVICE_ERROR; goto Exit; } DeviceIndex = Private->ActiveNamespaceNum; NamespaceInfo = &Private->NamespaceInfo[DeviceIndex]; NamespaceInfo->NamespaceId = NamespaceId; NamespaceInfo->NamespaceUuid = NamespaceData->Eui64; NamespaceInfo->Controller = Private; Private->ActiveNamespaceNum++; // // Build BlockIo media structure // Flbas = NamespaceData->Flbas; LbaFmtIdx = Flbas & 0xF; Lbads = NamespaceData->LbaFormat[LbaFmtIdx].Lbads; NamespaceInfo->Media.InterfaceType = MSG_NVME_NAMESPACE_DP; NamespaceInfo->Media.RemovableMedia = FALSE; NamespaceInfo->Media.MediaPresent = TRUE; NamespaceInfo->Media.ReadOnly = FALSE; NamespaceInfo->Media.BlockSize = (UINT32) 1 << Lbads; NamespaceInfo->Media.LastBlock = (EFI_PEI_LBA) NamespaceData->Nsze - 1; DEBUG (( DEBUG_INFO, "%a: Namespace ID %d - BlockSize = 0x%x, LastBlock = 0x%lx\n", __FUNCTION__, NamespaceId, NamespaceInfo->Media.BlockSize, NamespaceInfo->Media.LastBlock )); Exit: if (NamespaceData != NULL) { FreePool (NamespaceData); } return Status; }
/** Used to allocate and build a device path node for an NVM Express namespace on an NVM Express controller. The EFI_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 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 EFI_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,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 NamespaceId is not valid. @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 EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL *This, IN UINT32 NamespaceId, IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath ) { NVME_NAMESPACE_DEVICE_PATH *Node; NVME_CONTROLLER_PRIVATE_DATA *Private; EFI_STATUS Status; NVME_ADMIN_NAMESPACE_DATA *NamespaceData; // // Validate parameters // if ((This == NULL) || (DevicePath == NULL)) { return EFI_INVALID_PARAMETER; } Status = EFI_SUCCESS; Private = NVME_CONTROLLER_PRIVATE_DATA_FROM_PASS_THRU (This); // // Check NamespaceId is valid or not. // if ((NamespaceId == 0) || (NamespaceId > Private->ControllerData->Nn)) { 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; // // Allocate a buffer for Identify Namespace data. // NamespaceData = NULL; NamespaceData = AllocateZeroPool(sizeof (NVME_ADMIN_NAMESPACE_DATA)); if(NamespaceData == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Exit; } // // Get UUID from specified Identify Namespace data. // Status = NvmeIdentifyNamespace ( Private, NamespaceId, (VOID *)NamespaceData ); if (EFI_ERROR(Status)) { goto Exit; } Node->NamespaceUuid = NamespaceData->Eui64; *DevicePath = (EFI_DEVICE_PATH_PROTOCOL *)Node; Exit: if(NamespaceData != NULL) { FreePool (NamespaceData); } if (EFI_ERROR (Status)) { FreePool (Node); } return Status; }