CloverBootloader/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.c
2019-09-03 12:58:42 +03:00

460 lines
15 KiB
C

/** @file
The NvmExpressPei driver is used to manage non-volatile memory subsystem
which follows NVM Express specification at PEI phase.
Copyright (c) 2018 - 2019, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "NvmExpressPei.h"
EFI_PEI_PPI_DESCRIPTOR mNvmeBlkIoPpiListTemplate = {
EFI_PEI_PPI_DESCRIPTOR_PPI,
&gEfiPeiVirtualBlockIoPpiGuid,
NULL
};
EFI_PEI_PPI_DESCRIPTOR mNvmeBlkIo2PpiListTemplate = {
(EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
&gEfiPeiVirtualBlockIo2PpiGuid,
NULL
};
EFI_PEI_PPI_DESCRIPTOR mNvmeStorageSecurityPpiListTemplate = {
(EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
&gEdkiiPeiStorageSecurityCommandPpiGuid,
NULL
};
EFI_PEI_PPI_DESCRIPTOR mNvmePassThruPpiListTemplate = {
(EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
&gEdkiiPeiNvmExpressPassThruPpiGuid,
NULL
};
EFI_PEI_NOTIFY_DESCRIPTOR mNvmeEndOfPeiNotifyListTemplate = {
(EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
&gEfiEndOfPeiSignalPpiGuid,
NvmePeimEndOfPei
};
/**
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;
}
/**
Discover all Nvm Express device active namespaces.
@param[in,out] Private The pointer to the PEI_NVME_CONTROLLER_PRIVATE_DATA data structure.
@retval EFI_SUCCESS All the namespaces in the device are successfully enumerated.
@return EFI_NOT_FOUND No active namespaces can be found.
**/
EFI_STATUS
NvmeDiscoverNamespaces (
IN OUT PEI_NVME_CONTROLLER_PRIVATE_DATA *Private
)
{
UINT32 NamespaceId;
Private->ActiveNamespaceNum = 0;
Private->NamespaceInfo = AllocateZeroPool (Private->ControllerData->Nn * sizeof (PEI_NVME_NAMESPACE_INFO));
//
// According to Nvm Express 1.1 spec Figure 82, the field 'Nn' of the identify
// controller data defines the number of valid namespaces present for the
// controller. Namespaces shall be allocated in order (starting with 1) and
// packed sequentially.
//
for (NamespaceId = 1; NamespaceId <= Private->ControllerData->Nn; NamespaceId++) {
//
// For now, we do not care the return status. Since if a valid namespace is inactive,
// error status will be returned. But we continue to enumerate other valid namespaces.
//
EnumerateNvmeDevNamespace (Private, NamespaceId);
}
if (Private->ActiveNamespaceNum == 0) {
return EFI_NOT_FOUND;
}
return EFI_SUCCESS;
}
/**
One notified function to cleanup the allocated resources at the end of PEI.
@param[in] PeiServices Pointer to PEI Services Table.
@param[in] NotifyDescriptor Pointer to the descriptor for the Notification
event that caused this function to execute.
@param[in] Ppi Pointer to the PPI data associated with this function.
@retval EFI_SUCCESS The function completes successfully
**/
EFI_STATUS
EFIAPI
NvmePeimEndOfPei (
IN EFI_PEI_SERVICES **PeiServices,
IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor,
IN VOID *Ppi
)
{
PEI_NVME_CONTROLLER_PRIVATE_DATA *Private;
Private = GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_NOTIFY (NotifyDescriptor);
NvmeFreeDmaResource (Private);
return EFI_SUCCESS;
}
/**
Entry point of the PEIM.
@param[in] FileHandle Handle of the file being invoked.
@param[in] PeiServices Describes the list of possible PEI Services.
@retval EFI_SUCCESS PPI successfully installed.
**/
EFI_STATUS
EFIAPI
NvmExpressPeimEntry (
IN EFI_PEI_FILE_HANDLE FileHandle,
IN CONST EFI_PEI_SERVICES **PeiServices
)
{
EFI_STATUS Status;
EFI_BOOT_MODE BootMode;
EDKII_NVM_EXPRESS_HOST_CONTROLLER_PPI *NvmeHcPpi;
UINT8 Controller;
UINTN MmioBase;
UINTN DevicePathLength;
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
PEI_NVME_CONTROLLER_PRIVATE_DATA *Private;
EFI_PHYSICAL_ADDRESS DeviceAddress;
DEBUG ((DEBUG_INFO, "%a: Enters.\n", __FUNCTION__));
//
// Get the current boot mode.
//
Status = PeiServicesGetBootMode (&BootMode);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "%a: Fail to get the current boot mode.\n", __FUNCTION__));
return Status;
}
//
// Locate the NVME host controller PPI
//
Status = PeiServicesLocatePpi (
&gEdkiiPeiNvmExpressHostControllerPpiGuid,
0,
NULL,
(VOID **) &NvmeHcPpi
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "%a: Fail to locate NvmeHostControllerPpi.\n", __FUNCTION__));
return EFI_UNSUPPORTED;
}
Controller = 0;
MmioBase = 0;
while (TRUE) {
Status = NvmeHcPpi->GetNvmeHcMmioBar (
NvmeHcPpi,
Controller,
&MmioBase
);
//
// When status is error, meant no controller is found
//
if (EFI_ERROR (Status)) {
break;
}
Status = NvmeHcPpi->GetNvmeHcDevicePath (
NvmeHcPpi,
Controller,
&DevicePathLength,
&DevicePath
);
if (EFI_ERROR (Status)) {
DEBUG ((
DEBUG_ERROR, "%a: Fail to allocate get the device path for Controller %d.\n",
__FUNCTION__, Controller
));
return Status;
}
//
// Check validity of the device path of the NVM Express controller.
//
Status = NvmeIsHcDevicePathValid (DevicePath, DevicePathLength);
if (EFI_ERROR (Status)) {
DEBUG ((
DEBUG_ERROR, "%a: The device path is invalid for Controller %d.\n",
__FUNCTION__, Controller
));
Controller++;
continue;
}
//
// For S3 resume performance consideration, not all NVM Express controllers
// will be initialized. The driver consumes the content within
// S3StorageDeviceInitList LockBox to see if a controller will be skipped
// during S3 resume.
//
if ((BootMode == BOOT_ON_S3_RESUME) &&
(NvmeS3SkipThisController (DevicePath, DevicePathLength))) {
DEBUG ((
DEBUG_ERROR, "%a: Controller %d is skipped during S3.\n",
__FUNCTION__, Controller
));
Controller++;
continue;
}
//
// Memory allocation for controller private data
//
Private = AllocateZeroPool (sizeof (PEI_NVME_CONTROLLER_PRIVATE_DATA));
if (Private == NULL) {
DEBUG ((
DEBUG_ERROR, "%a: Fail to allocate private data for Controller %d.\n",
__FUNCTION__, Controller
));
return EFI_OUT_OF_RESOURCES;
}
//
// Memory allocation for transfer-related data
//
Status = IoMmuAllocateBuffer (
NVME_MEM_MAX_PAGES,
&Private->Buffer,
&DeviceAddress,
&Private->BufferMapping
);
if (EFI_ERROR (Status)) {
DEBUG ((
DEBUG_ERROR, "%a: Fail to allocate DMA buffers for Controller %d.\n",
__FUNCTION__, Controller
));
return Status;
}
ASSERT (DeviceAddress == ((EFI_PHYSICAL_ADDRESS) (UINTN) Private->Buffer));
DEBUG ((DEBUG_INFO, "%a: DMA buffer base at 0x%x\n", __FUNCTION__, Private->Buffer));
//
// Initialize controller private data
//
Private->Signature = NVME_PEI_CONTROLLER_PRIVATE_DATA_SIGNATURE;
Private->MmioBase = MmioBase;
Private->DevicePathLength = DevicePathLength;
Private->DevicePath = DevicePath;
//
// Initialize the NVME controller
//
Status = NvmeControllerInit (Private);
if (EFI_ERROR (Status)) {
DEBUG ((
DEBUG_ERROR,
"%a: Controller initialization fail for Controller %d with Status - %r.\n",
__FUNCTION__, Controller, Status
));
NvmeFreeDmaResource (Private);
Controller++;
continue;
}
//
// Enumerate the NVME namespaces on the controller
//
Status = NvmeDiscoverNamespaces (Private);
if (EFI_ERROR (Status)) {
//
// No active namespace was found on the controller
//
DEBUG ((
DEBUG_ERROR,
"%a: Namespaces discovery fail for Controller %d with Status - %r.\n",
__FUNCTION__, Controller, Status
));
NvmeFreeDmaResource (Private);
Controller++;
continue;
}
Private->BlkIoPpi.GetNumberOfBlockDevices = NvmeBlockIoPeimGetDeviceNo;
Private->BlkIoPpi.GetBlockDeviceMediaInfo = NvmeBlockIoPeimGetMediaInfo;
Private->BlkIoPpi.ReadBlocks = NvmeBlockIoPeimReadBlocks;
CopyMem (
&Private->BlkIoPpiList,
&mNvmeBlkIoPpiListTemplate,
sizeof (EFI_PEI_PPI_DESCRIPTOR)
);
Private->BlkIoPpiList.Ppi = &Private->BlkIoPpi;
Private->BlkIo2Ppi.Revision = EFI_PEI_RECOVERY_BLOCK_IO2_PPI_REVISION;
Private->BlkIo2Ppi.GetNumberOfBlockDevices = NvmeBlockIoPeimGetDeviceNo2;
Private->BlkIo2Ppi.GetBlockDeviceMediaInfo = NvmeBlockIoPeimGetMediaInfo2;
Private->BlkIo2Ppi.ReadBlocks = NvmeBlockIoPeimReadBlocks2;
CopyMem (
&Private->BlkIo2PpiList,
&mNvmeBlkIo2PpiListTemplate,
sizeof (EFI_PEI_PPI_DESCRIPTOR)
);
Private->BlkIo2PpiList.Ppi = &Private->BlkIo2Ppi;
PeiServicesInstallPpi (&Private->BlkIoPpiList);
//
// Nvm Express Pass Thru PPI
//
Private->PassThruMode.Attributes = EFI_NVM_EXPRESS_PASS_THRU_ATTRIBUTES_PHYSICAL |
EFI_NVM_EXPRESS_PASS_THRU_ATTRIBUTES_LOGICAL |
EFI_NVM_EXPRESS_PASS_THRU_ATTRIBUTES_CMD_SET_NVM;
Private->PassThruMode.IoAlign = sizeof (UINTN);
Private->PassThruMode.NvmeVersion = EDKII_PEI_NVM_EXPRESS_PASS_THRU_PPI_REVISION;
Private->NvmePassThruPpi.Mode = &Private->PassThruMode;
Private->NvmePassThruPpi.GetDevicePath = NvmePassThruGetDevicePath;
Private->NvmePassThruPpi.GetNextNameSpace = NvmePassThruGetNextNameSpace;
Private->NvmePassThruPpi.PassThru = NvmePassThru;
CopyMem (
&Private->NvmePassThruPpiList,
&mNvmePassThruPpiListTemplate,
sizeof (EFI_PEI_PPI_DESCRIPTOR)
);
Private->NvmePassThruPpiList.Ppi = &Private->NvmePassThruPpi;
PeiServicesInstallPpi (&Private->NvmePassThruPpiList);
//
// Check if the NVME controller supports the Security Receive/Send commands
//
if ((Private->ControllerData->Oacs & SECURITY_SEND_RECEIVE_SUPPORTED) != 0) {
DEBUG ((
DEBUG_INFO,
"%a: Security Security Command PPI will be produced for Controller %d.\n",
__FUNCTION__, Controller
));
Private->StorageSecurityPpi.Revision = EDKII_STORAGE_SECURITY_PPI_REVISION;
Private->StorageSecurityPpi.GetNumberofDevices = NvmeStorageSecurityGetDeviceNo;
Private->StorageSecurityPpi.GetDevicePath = NvmeStorageSecurityGetDevicePath;
Private->StorageSecurityPpi.ReceiveData = NvmeStorageSecurityReceiveData;
Private->StorageSecurityPpi.SendData = NvmeStorageSecuritySendData;
CopyMem (
&Private->StorageSecurityPpiList,
&mNvmeStorageSecurityPpiListTemplate,
sizeof (EFI_PEI_PPI_DESCRIPTOR)
);
Private->StorageSecurityPpiList.Ppi = &Private->StorageSecurityPpi;
PeiServicesInstallPpi (&Private->StorageSecurityPpiList);
}
CopyMem (
&Private->EndOfPeiNotifyList,
&mNvmeEndOfPeiNotifyListTemplate,
sizeof (EFI_PEI_NOTIFY_DESCRIPTOR)
);
PeiServicesNotifyPpi (&Private->EndOfPeiNotifyList);
DEBUG ((
DEBUG_INFO, "%a: Controller %d has been successfully initialized.\n",
__FUNCTION__, Controller
));
Controller++;
}
return EFI_SUCCESS;
}