1523 lines
56 KiB
C
1523 lines
56 KiB
C
//***********************************************************************
|
|
//* *
|
|
//* Copyright (c) 1985-2021, American Megatrends International LLC. *
|
|
//* *
|
|
//* All rights reserved. Subject to AMI licensing agreement. *
|
|
//* *
|
|
//***********************************************************************
|
|
/** @file NvmeController.c
|
|
Provides Access to Nvme Controller
|
|
**/
|
|
//---------------------------------------------------------------------------
|
|
|
|
#include "NvmeIncludes.h"
|
|
#include "NvmeBus.h"
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
/**
|
|
Creates Submission and Completion Queue
|
|
|
|
|
|
@param NVMeController
|
|
@param NvmeCmdWrapper
|
|
@param QueueNumber
|
|
@param QueueSize
|
|
|
|
@retval EFI_STATUS
|
|
|
|
@note Can be called recursively
|
|
|
|
**/
|
|
|
|
EFI_STATUS
|
|
CreateAdditionalSubmissionCompletionQueue (
|
|
IN AMI_NVME_CONTROLLER_PROTOCOL *NvmeController,
|
|
IN NVME_COMMAND_WRAPPER *NvmeCmdWrapper,
|
|
IN UINT16 QueueNumber,
|
|
IN UINT32 QueueSize
|
|
)
|
|
{
|
|
EFI_STATUS Status=EFI_SUCCESS;
|
|
COMPLETION_QUEUE_ENTRY CompletionData;
|
|
NVME_DXE_SMM_DATA_AREA *NvmeDxeSmmDataArea = NvmeController->NvmeDxeSmmDataArea;
|
|
|
|
if ((NvmeController->Queue1CompletionAligned == 0) ||
|
|
(NvmeController->Queue1SubmissionAligned == 0)) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
// Create I/O completion queue.
|
|
// Clear memory
|
|
ZeroMem(NvmeCmdWrapper,sizeof(NVME_COMMAND_WRAPPER));
|
|
ZeroMem((VOID *)NvmeController->Queue1CompletionAligned, \
|
|
(NvmeController->Queue1CompletionQueueSize+1) * sizeof (COMPLETION_QUEUE_ENTRY));
|
|
|
|
// If FATAL error occurred for CREATE_IO_COMPLETION_QUEUE nvme command, Return from here.
|
|
if( (NvmeController->DeviceState == Fatal_Recovery_state) && \
|
|
NvmeController->CreateIoCompletionQueueInProgress ) {
|
|
goto CreateAdditionalSubmissionCompletionQueue_Exit;
|
|
}
|
|
|
|
NvmeController->CreateIoCompletionQueueInProgress = TRUE;
|
|
|
|
// Build NVME command
|
|
NvmeCmdWrapper->NvmCmd.CMD0.Opcode = CREATE_IO_COMPLETION_QUEUE;
|
|
NvmeCmdWrapper->NvmCmd.CMD0.FusedOperation = 0;
|
|
NvmeCmdWrapper->NvmCmd.CMD0.PSDT = 0;
|
|
NvmeCmdWrapper->NvmCmd.CMD0.CommandIdentifier = NvmeDxeSmmDataArea->CommandIdentifierAdmin;
|
|
NvmeCmdWrapper->NvmCmd.NSID = 0;
|
|
|
|
NvmeCmdWrapper->NvmCmd.PRP1 = (UINT64)NvmeController->Queue1CompletionQueueMappedAddr;
|
|
NvmeCmdWrapper->NvmCmd.PRP2 = 0;
|
|
NvmeCmdWrapper->NvmCmd.CDW10 = (QueueSize << 16 )+ QueueNumber;
|
|
NvmeCmdWrapper->NvmCmd.CDW11 = 1; // Contiguous
|
|
|
|
NvmeCmdWrapper->AdminOrNVMCmdSet = TRUE;
|
|
NvmeCmdWrapper->SQIdentifier = 0; // Cmd issued in Queue0
|
|
NvmeCmdWrapper->CmdTimeOut = NVME_COMMAND_TIMEOUT * 1000;
|
|
|
|
Status = ExecuteNvmeCmd (NvmeController, NvmeCmdWrapper, &CompletionData);
|
|
|
|
NvmeController->CreateIoCompletionQueueInProgress = FALSE;
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
goto CreateAdditionalSubmissionCompletionQueue_Exit;
|
|
}
|
|
|
|
// Create I/O submission queue.
|
|
// Clear memory
|
|
ZeroMem(NvmeCmdWrapper,sizeof(NVME_COMMAND_WRAPPER));
|
|
ZeroMem((VOID *)NvmeController->Queue1SubmissionAligned, \
|
|
(NvmeController->Queue1SubmissionQueueSize+1) * sizeof(NVME_ADMIN_COMMAND));
|
|
|
|
// If Nvme FATAL error occurred for CREATE_IO_SUBMISSION_QUEUE nvme command, Return from here.
|
|
if( (NvmeController->DeviceState == Fatal_Recovery_state) && \
|
|
NvmeController->CreateIoSubmissionQueueInProgress ) {
|
|
goto CreateAdditionalSubmissionCompletionQueue_Exit;
|
|
}
|
|
|
|
NvmeController->CreateIoSubmissionQueueInProgress = TRUE;
|
|
|
|
// Build NVME command
|
|
NvmeCmdWrapper->NvmCmd.CMD0.Opcode = CREATE_IO_SUBMISSION_QUEUE;
|
|
NvmeCmdWrapper->NvmCmd.CMD0.FusedOperation = 0;
|
|
NvmeCmdWrapper->NvmCmd.CMD0.PSDT = 0;
|
|
NvmeCmdWrapper->NvmCmd.CMD0.CommandIdentifier = NvmeDxeSmmDataArea->CommandIdentifierAdmin;
|
|
NvmeCmdWrapper->NvmCmd.NSID = 0;
|
|
|
|
NvmeCmdWrapper->NvmCmd.PRP1 = (UINT64)NvmeController->Queue1SubmissionQueueMappedAddr;
|
|
NvmeCmdWrapper->NvmCmd.PRP2 = 0;
|
|
NvmeCmdWrapper->NvmCmd.CDW10 = (QueueSize << 16 )+ QueueNumber;
|
|
NvmeCmdWrapper->NvmCmd.CDW11 = (QueueNumber << 16) + 1; // Contiguous
|
|
|
|
NvmeCmdWrapper->AdminOrNVMCmdSet = TRUE;
|
|
NvmeCmdWrapper->SQIdentifier = 0; // Cmd issued to admin queue
|
|
NvmeCmdWrapper->CmdTimeOut = NVME_COMMAND_TIMEOUT * 1000;
|
|
|
|
Status = ExecuteNvmeCmd (NvmeController, NvmeCmdWrapper, &CompletionData);
|
|
|
|
NvmeController->CreateIoSubmissionQueueInProgress = FALSE;
|
|
|
|
CreateAdditionalSubmissionCompletionQueue_Exit:
|
|
|
|
NvmeDxeSmmDataArea->Queue1PhaseTag = FALSE;
|
|
|
|
NvmeDxeSmmDataArea->Queue1SubmissionQueueTailPtr = 0;
|
|
NvmeDxeSmmDataArea->Queue1SubmissionQueueHeadPtr = 0;
|
|
|
|
NvmeDxeSmmDataArea->Queue1CompletionQueueTailPtr = 0;
|
|
NvmeDxeSmmDataArea->Queue1CompletionQueueHeadPtr = 0;
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
/**@internal
|
|
Routine to get the Active NameSpace
|
|
|
|
@param NvmeController
|
|
@param NamespaceId
|
|
|
|
@retval Returns ACTIVE_NAMESPACE_DATA pointer for the NamespaceId
|
|
@endinternal
|
|
**/
|
|
|
|
ACTIVE_NAMESPACE_DATA*
|
|
GetActiveNameSpace (
|
|
AMI_NVME_CONTROLLER_PROTOCOL *NvmeController,
|
|
UINT32 NamespaceId
|
|
)
|
|
{
|
|
LIST_ENTRY *LinkData;
|
|
ACTIVE_NAMESPACE_DATA *ActiveNameSpace = NULL;
|
|
|
|
if(!NvmeController) {
|
|
return NULL;
|
|
}
|
|
|
|
for (LinkData = NvmeController->ActiveNameSpaceList.ForwardLink;
|
|
LinkData != &NvmeController->ActiveNameSpaceList;
|
|
LinkData = LinkData->ForwardLink) {
|
|
|
|
ActiveNameSpace = BASE_CR(LinkData, ACTIVE_NAMESPACE_DATA, Link);
|
|
|
|
// If NamespaceId is 0xFFFFFFFF, then return the first valid ActiveNameSpace
|
|
if( NamespaceId == 0xFFFFFFFF ) {
|
|
return ActiveNameSpace;
|
|
}
|
|
|
|
if(ActiveNameSpace->ActiveNameSpaceID == NamespaceId) {
|
|
return ActiveNameSpace;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
/**
|
|
Programs PRP2 with the list of page address for data transfer
|
|
|
|
@param This
|
|
|
|
@retval EFI_STATUS
|
|
|
|
**/
|
|
|
|
EFI_STATUS
|
|
ProgramPRP2List (
|
|
IN UINT64 *PRP2List,
|
|
IN UINT32 PageSize,
|
|
IN UINTN BufferAddress,
|
|
IN UINTN BufferSize,
|
|
IN UINTN *PRP2TransferSize
|
|
)
|
|
{
|
|
UINTN TotalNumberOfEntries = PageSize / 8; // Each entry 64 bytes long
|
|
|
|
*PRP2TransferSize = 0;
|
|
do {
|
|
*PRP2List++ = BufferAddress;
|
|
if (BufferSize >= PageSize) {
|
|
*PRP2TransferSize += PageSize;
|
|
BufferAddress += PageSize;
|
|
BufferSize -= PageSize;
|
|
} else {
|
|
*PRP2TransferSize = *PRP2TransferSize + (UINT32)BufferSize;
|
|
BufferAddress += BufferSize;
|
|
BufferSize = 0;
|
|
}
|
|
} while (--TotalNumberOfEntries && (BufferSize > 0));
|
|
return EFI_SUCCESS;
|
|
|
|
}
|
|
/**@internal
|
|
For the corresponding controller version, check whether the command supports PRP2 list
|
|
or not.
|
|
|
|
@param ControllerVersion Specifies the Nvme spec version
|
|
@param NvmeCmdWrapper Pointer to hold NvmeCommandWrapper
|
|
|
|
@retval TRUE PRP2 can be a pointer.(i.e PRP2List can be programmed)
|
|
@retval FALSE PRP2 should not be a pointer
|
|
|
|
@endinternal
|
|
**/
|
|
BOOLEAN
|
|
IsPrp2ListSupported (
|
|
IN UINT32 ControllerVersion,
|
|
IN NVME_COMMAND_WRAPPER *NvmeCmdWrapper
|
|
) {
|
|
UINT8 Prp2ListUnSupportedAdminCommands_1_1[] = { PRP2LIST_UNSUPPORTED_ADMIN_CMDS_1_1,0xFF };
|
|
UINT8 Prp2ListUnSupportedNvmCommands_1_1[] = { PRP2LIST_UNSUPPORTED_NVM_CMDS_1_1,0xFF };
|
|
UINT8 Prp2ListUnSupportedAdminCommands[] = { PRP2LIST_UNSUPPORTED_ADMIN_CMDS, 0xFF };
|
|
UINT8 Index;
|
|
|
|
//PRP2 should not be a pointer for Admin commands like Identity, Set feature,NameSpace attachment
|
|
if( NvmeCmdWrapper->AdminOrNVMCmdSet ) {
|
|
for (Index = 0; Prp2ListUnSupportedAdminCommands [Index] != 0xFF; Index++ ) {
|
|
if ( NvmeCmdWrapper->NvmCmd.CMD0.Opcode == Prp2ListUnSupportedAdminCommands[Index] ) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
//Device which supports controller version 1.1 and less has limitation on PRP2list usage for some additional commands.
|
|
if ( ControllerVersion <= NVME_SPEC_VS_1_1 ) {
|
|
if( NvmeCmdWrapper->AdminOrNVMCmdSet ) {
|
|
for ( Index = 0; Prp2ListUnSupportedAdminCommands_1_1 [Index] != 0xFF; Index++ ) {
|
|
if ( NvmeCmdWrapper->NvmCmd.CMD0.Opcode == Prp2ListUnSupportedAdminCommands_1_1[Index] ) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
} else {
|
|
for ( Index = 0; Prp2ListUnSupportedNvmCommands_1_1 [Index] != 0xFF; Index++ ) {
|
|
if ( NvmeCmdWrapper->NvmCmd.CMD0.Opcode == Prp2ListUnSupportedNvmCommands_1_1[Index] ) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
/**@internal
|
|
Program PRP1 and PRP2 entry in the NvmeCommandWrapper input.If ActiveNameSpace is found, then
|
|
PRP2List address will be obtained from ActiveNameSpaceList. If no ActiveNameSpace is found then
|
|
PRP2List address will be allocated and mapped inside this function.
|
|
|
|
@param NvmeController Pointer which holds AMI_NVME_CONTROLLER_PROTOCOL instance
|
|
@param NvmeCmdWrapper Pointer which holds NVME_COMMAND_WRAPPER instance
|
|
@param TransferLength Size of transfer buffer, which is transferred
|
|
to or from the NVM Express controller or namespace
|
|
@param TransferBuffer Pointer to hold the data buffer transferred.
|
|
@param PRP2List Holds the PRP2List address which is either obtained from ActiveNameSpaceList
|
|
or the newly allocated one.
|
|
@param PRP2ListUnMap If PRP2List address is newly allocated one, then it hold
|
|
@param BufferSize Holds the sum of PRP1 and PRP2 transfer size
|
|
|
|
@retval EFI_STATUS
|
|
|
|
Note: Only when ActiveNameSpace is NULL, PRP2List and PRP2ListUnMap output parameters
|
|
should be used by the caller function to unmap and free the allocated memory.
|
|
@endinternal
|
|
**/
|
|
EFI_STATUS
|
|
ProgramPRPEntry (
|
|
IN AMI_NVME_CONTROLLER_PROTOCOL *NvmeController,
|
|
IN NVME_COMMAND_WRAPPER *NvmeCmdWrapper,
|
|
IN UINTN TransferLength,
|
|
IN UINTN *TransferBuffer,
|
|
IN ACTIVE_NAMESPACE_DATA *ActiveNameSpace,
|
|
OUT UINT64 **PRP2List,
|
|
OUT VOID **PRP2ListUnMap,
|
|
OUT UINTN *BufferSize
|
|
) {
|
|
EFI_STATUS Status;
|
|
UINTN PRP1TransferSize = 0;
|
|
UINTN PRP2TransferSize = 0;
|
|
EFI_PHYSICAL_ADDRESS PRP2ListMappedAddr;
|
|
UINTN AllocatePageSize;
|
|
|
|
NvmeCmdWrapper->NvmCmd.PRP1 = (UINT64)TransferBuffer;
|
|
PRP1TransferSize = NvmeController->MemoryPageSize -
|
|
((UINTN)(TransferBuffer) & ((UINTN)(NvmeController->MemoryPageSize) - 1));
|
|
|
|
// If all data can be transferred using only PRP1 then do that.
|
|
if (PRP1TransferSize >= TransferLength) {
|
|
PRP1TransferSize = TransferLength;
|
|
}
|
|
|
|
// Do we need PRP2
|
|
if (TransferLength - PRP1TransferSize) {
|
|
// Do we need either a PRP2 pointer or a List
|
|
if ((TransferLength - PRP1TransferSize) <= NvmeController->MemoryPageSize) {
|
|
NvmeCmdWrapper->NvmCmd.PRP2 = NvmeCmdWrapper->NvmCmd.PRP1 + PRP1TransferSize;
|
|
PRP2TransferSize = TransferLength - PRP1TransferSize;
|
|
} else {
|
|
if(!IsPrp2ListSupported(NvmeController->ControllerVersion,NvmeCmdWrapper)) {
|
|
*BufferSize = PRP1TransferSize + NvmeController->MemoryPageSize;
|
|
return EFI_BAD_BUFFER_SIZE;
|
|
}
|
|
if( !ActiveNameSpace ) {
|
|
AllocatePageSize = NvmeController->MemoryPageSize;
|
|
Status = NvmeController->PciIO->AllocateBuffer (NvmeController->PciIO,
|
|
AllocateAnyPages,
|
|
EfiRuntimeServicesData,
|
|
EFI_SIZE_TO_PAGES(AllocatePageSize),
|
|
(VOID**)PRP2List,
|
|
EFI_PCI_IO_ATTRIBUTE_MEMORY_CACHED | EFI_PCI_IO_ATTRIBUTE_MEMORY_WRITE_COMBINE
|
|
);
|
|
if (EFI_ERROR(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Status = NvmeController->PciIO->Map ( NvmeController->PciIO,
|
|
EfiPciIoOperationBusMasterCommonBuffer,
|
|
(VOID *)*PRP2List,
|
|
&AllocatePageSize,
|
|
&PRP2ListMappedAddr,
|
|
PRP2ListUnMap
|
|
);
|
|
if (EFI_ERROR(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
} else {
|
|
*PRP2List = ActiveNameSpace->PRP2List;
|
|
PRP2ListMappedAddr = ActiveNameSpace->PRP2ListMappedAddr;
|
|
}
|
|
|
|
// We need PRP2 List
|
|
Status = ProgramPRP2List (*PRP2List, NvmeController->MemoryPageSize, \
|
|
(UINTN)(TransferBuffer) + PRP1TransferSize, \
|
|
TransferLength - PRP1TransferSize, &PRP2TransferSize
|
|
);
|
|
if (EFI_ERROR(Status)) {
|
|
return Status;
|
|
}
|
|
NvmeCmdWrapper->NvmCmd.PRP2 = (UINT64) PRP2ListMappedAddr;
|
|
}
|
|
}
|
|
*BufferSize = PRP1TransferSize + PRP2TransferSize;
|
|
return EFI_SUCCESS;
|
|
}
|
|
/**
|
|
Read/Write data from the given LBA address
|
|
|
|
|
|
@param This
|
|
@param MediaId
|
|
@param LBA
|
|
@param BufferSize
|
|
@param Buffer
|
|
@param ReadWriteOpCode
|
|
|
|
@retval EFI_STATUS
|
|
|
|
**/
|
|
|
|
EFI_STATUS
|
|
NvmeReadWriteBlocks (
|
|
IN ACTIVE_NAMESPACE_DATA *ActiveNameSpace,
|
|
IN UINT32 MediaId,
|
|
IN EFI_LBA LBA,
|
|
IN UINTN BufferSize,
|
|
OUT VOID *Buffer,
|
|
OUT COMPLETION_QUEUE_ENTRY *NvmeCompletionData,
|
|
IN UINT8 ReadWriteOpCode
|
|
)
|
|
{
|
|
EFI_STATUS Status = EFI_SUCCESS;
|
|
AMI_NVME_CONTROLLER_PROTOCOL *NvmeController = ActiveNameSpace->NvmeController;
|
|
NVME_DXE_SMM_DATA_AREA *NvmeDxeSmmDataArea = NvmeController->NvmeDxeSmmDataArea;
|
|
EFI_BLOCK_IO_MEDIA *BlkMedia = ActiveNameSpace->NvmeBlockIO.Media;
|
|
NVME_COMMAND_WRAPPER *NvmeCmdWrapper = NvmeController->NvmeCmdWrapper;
|
|
COMPLETION_QUEUE_ENTRY CompletionData;
|
|
COMPLETION_QUEUE_ENTRY *pCompletionData = &CompletionData;
|
|
UINTN DataN;
|
|
UINTN LBACountInOneTransfer;
|
|
UINTN MappedBufferSize = BufferSize;
|
|
UINTN PRPTransferSize;
|
|
EFI_PHYSICAL_ADDRESS MappedBuffer;
|
|
VOID *BufferUnMap;
|
|
EFI_PHYSICAL_ADDRESS UserMappedBufferPtr = 0;
|
|
UINTN Count;
|
|
UINTN TotalMetaDataSize = 0;
|
|
UINT16 MetaDataSize = 0;
|
|
UINTN MaxDataTransferSize = 0;
|
|
|
|
if (!NvmeController->NvmeInSmm) {
|
|
DEBUG((DEBUG_BLKIO,"LBA : %lx BufferSize : %lx Buffer : %lx Opcode : %x", LBA, BufferSize, Buffer, ReadWriteOpCode));
|
|
}
|
|
|
|
// Check if Media ID matches
|
|
if (BlkMedia->MediaId != MediaId) {
|
|
return EFI_MEDIA_CHANGED;
|
|
}
|
|
|
|
if (BufferSize == 0) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// If IoAlign values is 0 or 1, means that the buffer can be placed
|
|
// anywhere in memory or else IoAlign value should be power of 2. To be
|
|
// properly aligned the buffer address should be divisible by IoAlign
|
|
// with no remainder.
|
|
//
|
|
if((BlkMedia->IoAlign > 1 ) && ((UINTN)Buffer % BlkMedia->IoAlign)) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
// Check whether the block size is multiple of BlkMedia->BlockSize
|
|
DataN = BufferSize % BlkMedia->BlockSize;
|
|
if (DataN){
|
|
return EFI_BAD_BUFFER_SIZE;
|
|
}
|
|
|
|
// Check for Valid start LBA #
|
|
if (LBA > BlkMedia->LastBlock) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
// Check for Valid End LBA #
|
|
DataN = BufferSize / BlkMedia->BlockSize;
|
|
if (LBA + DataN > BlkMedia->LastBlock + 1) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
MetaDataSize = ActiveNameSpace->IdentifyNamespaceData.LBAF[ActiveNameSpace->IdentifyNamespaceData.FLBAS & 0xF].MS;
|
|
// Map pre-allocated buffer for MetaData
|
|
if (!NvmeController->NvmeInSmm ) {
|
|
|
|
if (ActiveNameSpace->MetaDataBlock) {
|
|
|
|
ActiveNameSpace->MappedMetaDataBufferSize = ActiveNameSpace->MetaDataBufferSize;
|
|
|
|
Status = NvmeController->PciIO->Map ( NvmeController->PciIO,
|
|
EfiPciIoOperationBusMasterCommonBuffer,
|
|
(VOID *)ActiveNameSpace->MetaDataBlock,
|
|
(UINTN *)&ActiveNameSpace->MappedMetaDataBufferSize,
|
|
&ActiveNameSpace->MetaDataBlockMappedAddr,
|
|
(VOID**)&ActiveNameSpace->MetaDataBlockUnMap
|
|
);
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
}
|
|
|
|
if (ActiveNameSpace->ExtendedLBA) {
|
|
|
|
ActiveNameSpace->MappedExtendedLBABufferSize = ActiveNameSpace->ExtendedLBABufferSize;
|
|
|
|
Status = NvmeController->PciIO->Map ( NvmeController->PciIO,
|
|
EfiPciIoOperationBusMasterCommonBuffer,
|
|
(VOID *)ActiveNameSpace->ExtendedLBA,
|
|
(UINTN *)&ActiveNameSpace->MappedExtendedLBABufferSize,
|
|
&ActiveNameSpace->ExtendedLBAMappedAddr,
|
|
(VOID**)&ActiveNameSpace->ExtendedLBAUnMap
|
|
);
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
return Status;;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
do {
|
|
MappedBuffer = (EFI_PHYSICAL_ADDRESS) Buffer;
|
|
|
|
// Calculate the maximum transfer size using MDTS value.
|
|
if (NvmeController->IdentifyControllerData->MDTS) {
|
|
MaxDataTransferSize = (UINTN)MultU64x32(LShiftU64(1, NvmeController->IdentifyControllerData->MDTS),
|
|
NvmeController->MemoryPageSizeMin);
|
|
} else {
|
|
// If MDTS value is zero, limit maximum data transfer size
|
|
// with size of 1024 blocks.
|
|
MaxDataTransferSize = (UINTN)MultU64x32(1024 , BlkMedia->BlockSize);
|
|
}
|
|
|
|
// Limit the transfer size based on the MaxDataTransferSize calculated.
|
|
if ( MappedBufferSize > MaxDataTransferSize ) {
|
|
MappedBufferSize = MaxDataTransferSize;
|
|
}
|
|
|
|
// Reduce MappedBuffer size if local buffer available is less when MetaData is enabled
|
|
if (ActiveNameSpace->ExtendedLBA) {
|
|
|
|
TotalMetaDataSize = (MappedBufferSize / BlkMedia->BlockSize) * MetaDataSize;
|
|
|
|
// Make sure MappedExtendedLBABufferSize can hold LBA Data + MetaData
|
|
MappedBufferSize += TotalMetaDataSize;
|
|
MappedBufferSize = (UINTN)(MappedBufferSize > (ActiveNameSpace->MappedExtendedLBABufferSize) ? \
|
|
(ActiveNameSpace->MappedExtendedLBABufferSize) : MappedBufferSize);
|
|
UserMappedBufferPtr = MappedBuffer;
|
|
|
|
// Use the local temp. buffer instead of user provided buffer
|
|
MappedBuffer = ActiveNameSpace->ExtendedLBAMappedAddr;
|
|
|
|
} else if (ActiveNameSpace->MetaDataBlock) {
|
|
// MetaData is a separate buffer. Data will be transferred based on the size of meta data buffer allocated locally
|
|
MappedBufferSize = (UINTN)(MappedBufferSize > (ActiveNameSpace->MappedMetaDataBufferSize) ? \
|
|
(ActiveNameSpace->MappedMetaDataBufferSize) : MappedBufferSize);
|
|
}
|
|
|
|
// After MAP, Mapped BufferSize may not be same as input.
|
|
// So need to complete the transfer in multiple loops.
|
|
// When the call originates from SMM, don't call Map function.
|
|
// Also don't need to map it if ExtLBA is used. The buffer is already mapped.
|
|
if (!NvmeController->NvmeInSmm && !ActiveNameSpace->ExtendedLBA) {
|
|
|
|
Status = NvmeController->PciIO->Map ( NvmeController->PciIO,
|
|
(ReadWriteOpCode == NVME_READ) ? \
|
|
EfiPciIoOperationBusMasterWrite : EfiPciIoOperationBusMasterRead,
|
|
Buffer,
|
|
&MappedBufferSize,
|
|
&MappedBuffer,
|
|
&BufferUnMap
|
|
);
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
SetMem(NvmeCmdWrapper, sizeof(NVME_COMMAND_WRAPPER), 0);
|
|
NvmeCmdWrapper->NvmCmd.CMD0.Opcode = ReadWriteOpCode;
|
|
NvmeCmdWrapper->AdminOrNVMCmdSet = FALSE;
|
|
Status = ProgramPRPEntry(NvmeController,
|
|
NvmeCmdWrapper,
|
|
MappedBufferSize,
|
|
(VOID*)MappedBuffer,
|
|
ActiveNameSpace,
|
|
&(ActiveNameSpace->PRP2List),
|
|
NULL,
|
|
&PRPTransferSize);
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
break;
|
|
}
|
|
LBACountInOneTransfer = (UINTN)((PRPTransferSize - TotalMetaDataSize) / BlkMedia->BlockSize);
|
|
// Is metaData enabled AND is it a WRITE?
|
|
if ((ReadWriteOpCode == NVME_WRITE) || (ReadWriteOpCode == NVME_COMPARE)) {
|
|
|
|
// For Write operations, Data need to be moved from user buffer to local buffer.
|
|
if (ActiveNameSpace->ExtendedLBA) {
|
|
|
|
// Transfer data from user buffer to local buffer
|
|
for (Count = 0; Count < LBACountInOneTransfer; Count++) {
|
|
|
|
|
|
CopyMem ((VOID *)(ActiveNameSpace->ExtendedLBAMappedAddr + ((BlkMedia->BlockSize + MetaDataSize)* Count)), \
|
|
(VOID *)(UserMappedBufferPtr + (BlkMedia->BlockSize * Count)), BlkMedia->BlockSize);
|
|
|
|
//MetaData part of extLBA. So clear memory at the end of LBA size
|
|
SetMem((VOID *)(ActiveNameSpace->ExtendedLBAMappedAddr + ((BlkMedia->BlockSize + MetaDataSize) * Count ) + BlkMedia->BlockSize), \
|
|
MetaDataSize, 0);
|
|
}
|
|
|
|
} else if (ActiveNameSpace->MetaDataBlock) {
|
|
|
|
// Clear out the metadata alone
|
|
for (Count = 0; Count < LBACountInOneTransfer; Count++) {
|
|
SetMem((VOID *)(ActiveNameSpace->MetaDataBlockMappedAddr + (MetaDataSize * Count)), \
|
|
MetaDataSize, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Build NVME command
|
|
NvmeCmdWrapper->NvmCmd.CMD0.FusedOperation = 0;
|
|
NvmeCmdWrapper->NvmCmd.CMD0.PSDT = 0;
|
|
NvmeCmdWrapper->NvmCmd.CMD0.CommandIdentifier = NvmeDxeSmmDataArea->CommandIdentifierQueue1;
|
|
NvmeCmdWrapper->NvmCmd.NSID = ActiveNameSpace->ActiveNameSpaceID;
|
|
|
|
NvmeCmdWrapper->NvmCmd.MPTR = ActiveNameSpace->MetaDataBlock ? ActiveNameSpace->MetaDataBlockMappedAddr : 0;
|
|
NvmeCmdWrapper->NvmCmd.CDW10 = (UINT32)LBA;
|
|
NvmeCmdWrapper->NvmCmd.CDW11 = (UINT32) RShiftU64 (LBA, 32);
|
|
NvmeCmdWrapper->NvmCmd.CDW12 = 0x80000000 + (UINT32)(LBACountInOneTransfer - 1);
|
|
NvmeCmdWrapper->NvmCmd.CDW13 = 0;
|
|
NvmeCmdWrapper->NvmCmd.CDW14 = 0;
|
|
|
|
NvmeCmdWrapper->SQIdentifier = NvmeController->NVMQueueNumber;
|
|
NvmeCmdWrapper->CmdTimeOut = NVME_COMMAND_TIMEOUT * 1000;
|
|
|
|
// If caller has passed buffer to return controller status, use it.
|
|
if (NvmeCompletionData) {
|
|
pCompletionData = NvmeCompletionData;
|
|
}
|
|
|
|
// Is Protection enabled?
|
|
if (ActiveNameSpace->IdentifyNamespaceData.DPS & 0x07) {
|
|
// Protection is stripped/inserted for Read/write by the controller. Set PRACT
|
|
NvmeCmdWrapper->NvmCmd.CDW12 |= 0x20000000;
|
|
}
|
|
|
|
Status = ExecuteNvmeCmd (NvmeController, NvmeCmdWrapper, pCompletionData);
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
if (!NvmeController->NvmeInSmm && !ActiveNameSpace->ExtendedLBA) {
|
|
NvmeController->PciIO->Unmap(NvmeController->PciIO, BufferUnMap);
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Reduce the LBA and Buffer size to reflect only the DATA and exclude MetaData
|
|
if (ActiveNameSpace->ExtendedLBA) {
|
|
|
|
MappedBufferSize = MappedBufferSize - TotalMetaDataSize;
|
|
|
|
if (ReadWriteOpCode == NVME_READ) {
|
|
// Copy data from local buffer to user buffer and skip the meta data
|
|
for (Count = 0; Count < LBACountInOneTransfer; Count++) {
|
|
CopyMem ((VOID *)(UserMappedBufferPtr + (BlkMedia->BlockSize * Count)), \
|
|
(VOID *)(ActiveNameSpace->ExtendedLBAMappedAddr + ((BlkMedia->BlockSize + MetaDataSize ) * Count)), BlkMedia->BlockSize);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Remaining Bytes to be transferred
|
|
MappedBufferSize -= (LBACountInOneTransfer * BlkMedia->BlockSize);
|
|
|
|
// Update LBA # for next transfer if needed
|
|
LBA += LBACountInOneTransfer;
|
|
|
|
// Adjust the Buffer address
|
|
Buffer =(VOID*) ((UINTN) Buffer + (LBACountInOneTransfer * BlkMedia->BlockSize));
|
|
BufferSize -= (LBACountInOneTransfer * BlkMedia->BlockSize);
|
|
|
|
// When PciIO->Map is called, it might not map the complete buffer.
|
|
// After the complete MappedBufferSize is transferred, if there is a left over do that transfer also
|
|
if (MappedBufferSize == 0) {
|
|
MappedBufferSize = BufferSize;
|
|
}
|
|
|
|
if (!NvmeController->NvmeInSmm && !ActiveNameSpace->ExtendedLBA) {
|
|
NvmeController->PciIO->Unmap(NvmeController->PciIO, BufferUnMap);
|
|
}
|
|
|
|
} while (MappedBufferSize);
|
|
|
|
if (!NvmeController->NvmeInSmm) {
|
|
|
|
if (ActiveNameSpace->MetaDataBlock) {
|
|
NvmeController->PciIO->Unmap(NvmeController->PciIO, ActiveNameSpace->MetaDataBlockUnMap);
|
|
}
|
|
|
|
if (ActiveNameSpace->ExtendedLBA) {
|
|
NvmeController->PciIO->Unmap(NvmeController->PciIO, ActiveNameSpace->ExtendedLBAUnMap);
|
|
}
|
|
DEBUG((DEBUG_BLKIO,"NvmeReadWriteBlocks : %r \n", Status));
|
|
}
|
|
return Status;
|
|
|
|
}
|
|
|
|
/**
|
|
Execute Admin and Nvme cmds
|
|
|
|
@param NvmeController
|
|
@param NvmeCmdWrapper
|
|
@param CmdCompletionData
|
|
|
|
@retval EFI_STATUS
|
|
|
|
@note If the cmd needs to be retried due to a failure, caller can
|
|
initialize the RetryCount.Can be called recursively.
|
|
|
|
|
|
**/
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
ExecuteNvmeCmd (
|
|
IN AMI_NVME_CONTROLLER_PROTOCOL *NvmeController,
|
|
IN NVME_COMMAND_WRAPPER *NvmeCmdWrapper,
|
|
OUT COMPLETION_QUEUE_ENTRY *CmdCompletionData
|
|
)
|
|
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_STATUS RecreateQueueStatus;
|
|
NVME_DXE_SMM_DATA_AREA *NvmeDxeSmmDataArea = NvmeController->NvmeDxeSmmDataArea;
|
|
UINT8 CmdRetryCount = MAX_RETRY_COUNT_FOR_FATAL_ERROR;
|
|
|
|
//controller in FATAL error condition state. Can't be recovered
|
|
if (NvmeController->DeviceState == Fatal_UnRecovered ) {
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
do {
|
|
|
|
if (!NvmeController->NvmeInSmm) {
|
|
PrintNvmeCmdWrapper (NvmeCmdWrapper);
|
|
}
|
|
|
|
RecreateQueueStatus = EFI_DEVICE_ERROR;
|
|
|
|
// Because of recursive nature and retry mechanism, cmd identifier needs to be updated just before giving the call.
|
|
NvmeCmdWrapper->NvmCmd.CMD0.CommandIdentifier = NvmeCmdWrapper->AdminOrNVMCmdSet ? NvmeDxeSmmDataArea->CommandIdentifierAdmin : \
|
|
NvmeDxeSmmDataArea->CommandIdentifierQueue1;
|
|
|
|
NvmeCmdWrapper->AdminOrNVMCmdSet ? (Status = AddToAdminSubmissionQueue(NvmeController, NvmeCmdWrapper)) : \
|
|
(Status = AddToQueue1SubmissionQueue(NvmeController, NvmeCmdWrapper));
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
continue;
|
|
}
|
|
|
|
Status = UpdateDoorBellRegister(NvmeController,
|
|
NvmeCmdWrapper->SQIdentifier,
|
|
NvmeCmdWrapper->AdminOrNVMCmdSet ? NvmeDxeSmmDataArea->AdminSubmissionQueueTailPtr :
|
|
NvmeDxeSmmDataArea->Queue1SubmissionQueueTailPtr
|
|
);
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
continue;
|
|
}
|
|
|
|
//Wait for cmd to complete
|
|
if (NvmeCmdWrapper->NvmCmd.CMD0.Opcode != ASYNC_EVENT_REQUEST) {
|
|
Status = WaitForCompletionQueueUpdate(NvmeController, NvmeCmdWrapper, CmdCompletionData);
|
|
}
|
|
|
|
// Follow Fatal Error Mechanism if WaitForCompletionQueueUpdate returns EFI_ABORTED/EFI_TIMEOUT Status
|
|
if ((Status == EFI_ABORTED || Status == EFI_TIMEOUT) && (NvmeController->DeviceState==Functional)) {
|
|
|
|
RecreateQueueStatus = RecreateAllQueues(NvmeController );
|
|
|
|
if ( EFI_ERROR(RecreateQueueStatus) ){
|
|
NvmeController->DeviceState = Fatal_UnRecovered;
|
|
} else {
|
|
NvmeController->DeviceState = Functional;
|
|
}
|
|
}
|
|
|
|
} while (EFI_ERROR(Status) && (!EFI_ERROR(RecreateQueueStatus) && CmdRetryCount--) );
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
/**
|
|
Submits the cmd to the Admin Submission queue
|
|
|
|
|
|
@param NvmeController
|
|
@param NvmeCmdWrapper
|
|
|
|
@retval EFI_STATUS
|
|
|
|
**/
|
|
|
|
EFI_STATUS
|
|
AddToAdminSubmissionQueue (
|
|
IN AMI_NVME_CONTROLLER_PROTOCOL *NvmeController,
|
|
IN NVME_COMMAND_WRAPPER *NvmeCmdWrapper
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT64 DestinationAddress;
|
|
NVME_DXE_SMM_DATA_AREA *NvmeDxeSmmDataArea = NvmeController->NvmeDxeSmmDataArea;
|
|
|
|
// Is Admin Queue full?
|
|
if (NvmeDxeSmmDataArea->AdminSubmissionQueueHeadPtr) { // Non-zero value
|
|
if ((NvmeDxeSmmDataArea->AdminSubmissionQueueTailPtr - 1) == NvmeDxeSmmDataArea->AdminSubmissionQueueHeadPtr){
|
|
|
|
// In this design, queue should never get filled up.
|
|
// If it does something is wrong. Delete and start all over again.
|
|
|
|
Status = RecreateAllQueues (NvmeController);
|
|
if (EFI_ERROR(Status)) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
}
|
|
} else {
|
|
// If Head is at the start and Tail is at the end, then queue is full
|
|
if (NvmeDxeSmmDataArea->AdminSubmissionQueueTailPtr == (NvmeDxeSmmDataArea->AdminSubmissionQueueHeadPtr +
|
|
NvmeController->AdminSubmissionQueueSize)) {
|
|
|
|
// In this design, queue should never get filled up.
|
|
// If it does something is wrong. Delete and start all over again.
|
|
Status = RecreateAllQueues (NvmeController);
|
|
if (EFI_ERROR(Status)) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Copy cmd to Admin Queue
|
|
DestinationAddress = NvmeController->AdminSubmissionAligned + (
|
|
NvmeDxeSmmDataArea->AdminSubmissionQueueTailPtr * sizeof(NVME_ADMIN_COMMAND));
|
|
|
|
if (!NvmeController->NvmeInSmm) {
|
|
DEBUG((DEBUG_BLKIO, "Destination Address for Cmd : %016lx\n", DestinationAddress));
|
|
}
|
|
|
|
|
|
CopyMem ((VOID *)DestinationAddress, &(NvmeCmdWrapper->NvmCmd), sizeof(NVME_ADMIN_COMMAND));
|
|
|
|
NvmeDxeSmmDataArea->AdminSubmissionQueueTailPtr++;
|
|
|
|
// Check if there is a roller over
|
|
if (NvmeDxeSmmDataArea->AdminSubmissionQueueTailPtr > NvmeController->AdminSubmissionQueueSize) {
|
|
NvmeDxeSmmDataArea->AdminSubmissionQueueTailPtr = 0;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
|
|
}
|
|
|
|
/**
|
|
Submits the Nvme cmd to the Queue1 Submission queue
|
|
|
|
@param NvmeController
|
|
@param NvmeCmdWrapper
|
|
|
|
@retval EFI_STATUS
|
|
|
|
**/
|
|
|
|
EFI_STATUS
|
|
AddToQueue1SubmissionQueue (
|
|
IN AMI_NVME_CONTROLLER_PROTOCOL *NvmeController,
|
|
IN NVME_COMMAND_WRAPPER *NvmeCmdWrapper
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT64 DestinationAddress;
|
|
NVME_DXE_SMM_DATA_AREA *NvmeDxeSmmDataArea = NvmeController->NvmeDxeSmmDataArea;
|
|
|
|
// Is Queue full? If Tail is one less than the Head queue is full.
|
|
if (NvmeDxeSmmDataArea->Queue1SubmissionQueueHeadPtr) { // Non-zero value
|
|
if ((NvmeDxeSmmDataArea->Queue1SubmissionQueueTailPtr - 1) == NvmeDxeSmmDataArea->Queue1SubmissionQueueHeadPtr){
|
|
|
|
// In this design, queue should never get filled up.
|
|
// If it does something is wrong. Delete and start all over again.
|
|
Status = RecreateAllQueues (NvmeController);
|
|
if (EFI_ERROR(Status)) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
}
|
|
} else {
|
|
// If Head is at the start and Tail is at the end, then queue is full
|
|
if (NvmeDxeSmmDataArea->Queue1SubmissionQueueTailPtr == (NvmeDxeSmmDataArea->Queue1SubmissionQueueHeadPtr +
|
|
NvmeController->Queue1SubmissionQueueSize)) {
|
|
|
|
// In this design, queue should never get filled up.
|
|
// If it does something is wrong. Delete and start all over again.
|
|
Status = RecreateAllQueues (NvmeController);
|
|
if (EFI_ERROR(Status)) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Copy cmd to I/O Queue
|
|
DestinationAddress = NvmeController->Queue1SubmissionAligned +
|
|
(NvmeDxeSmmDataArea->Queue1SubmissionQueueTailPtr * sizeof(NVME_ADMIN_COMMAND));
|
|
|
|
if (!NvmeController->NvmeInSmm) {
|
|
DEBUG((DEBUG_BLKIO, "Destination Address for Cmd : %016lx\n", DestinationAddress));
|
|
}
|
|
|
|
|
|
CopyMem ((VOID *)DestinationAddress, &(NvmeCmdWrapper->NvmCmd), sizeof(NVME_ADMIN_COMMAND));
|
|
|
|
NvmeDxeSmmDataArea->Queue1SubmissionQueueTailPtr++;
|
|
|
|
// Check if there is a roller over
|
|
if (NvmeDxeSmmDataArea->Queue1SubmissionQueueTailPtr > NvmeController->Queue1SubmissionQueueSize) {
|
|
NvmeDxeSmmDataArea->Queue1SubmissionQueueTailPtr = 0;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
|
|
}
|
|
|
|
/**
|
|
Update door bell register for the controller to start executing the cmd
|
|
|
|
@param NvmeController
|
|
@param QueueNumber
|
|
@param Value
|
|
|
|
@retval EFI_STATUS
|
|
|
|
**/
|
|
|
|
EFI_STATUS
|
|
UpdateDoorBellRegister (
|
|
IN AMI_NVME_CONTROLLER_PROTOCOL *NvmeController,
|
|
IN UINT16 QueueNumber,
|
|
IN UINT32 Value
|
|
)
|
|
{
|
|
|
|
UINT32 Offset;
|
|
|
|
// Update Door Bell Register
|
|
Offset = QUEUE_DOORBELL_OFFSET(QueueNumber, 0, NvmeController->DoorBellStride);
|
|
|
|
if (!NvmeController->NvmeInSmm) {
|
|
DEBUG((DEBUG_BLKIO, "\nDoorBell Offset %016lx Value %08X\n", NvmeController->NvmeBarOffset + Offset, Value));
|
|
}
|
|
|
|
CONTROLLER_WRITE_REG32(NvmeController, Offset, Value);
|
|
return EFI_SUCCESS;
|
|
|
|
}
|
|
|
|
/**
|
|
Checks for the completion queue for the correct PhaseTag,
|
|
Queue Identified and Cmd Identifier.
|
|
|
|
@param NvmeController
|
|
@param NvmeCmdWrapper
|
|
@param CmdCompletionData
|
|
|
|
@retval EFI_STATUS
|
|
|
|
**/
|
|
|
|
EFI_STATUS
|
|
WaitForCompletionQueueUpdate (
|
|
IN AMI_NVME_CONTROLLER_PROTOCOL *NvmeController,
|
|
IN NVME_COMMAND_WRAPPER *NvmeCmdWrapper,
|
|
OUT COMPLETION_QUEUE_ENTRY *CmdCompletionData
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT64 TimeOut = MultU64x32((UINT64)NvmeCmdWrapper->CmdTimeOut,100); // Convert the delay unit from 1ms to 10us
|
|
UINT32 Offset;
|
|
COMPLETION_QUEUE_ENTRY *pCmdCompletionData;
|
|
NVME_DXE_SMM_DATA_AREA *NvmeDxeSmmDataArea = NvmeController->NvmeDxeSmmDataArea;
|
|
|
|
UINT16 CommandIdentifier = NvmeCmdWrapper->AdminOrNVMCmdSet ? NvmeDxeSmmDataArea->CommandIdentifierAdmin : \
|
|
NvmeDxeSmmDataArea->CommandIdentifierQueue1;
|
|
|
|
UINT64 CompletionQueueStart = NvmeCmdWrapper->AdminOrNVMCmdSet ? NvmeController->AdminCompletionAligned : \
|
|
NvmeController->Queue1CompletionAligned;
|
|
|
|
UINT16 CompletionQueueHeadPtr = NvmeCmdWrapper->AdminOrNVMCmdSet ? NvmeDxeSmmDataArea->AdminCompletionQueueHeadPtr :\
|
|
NvmeDxeSmmDataArea->Queue1CompletionQueueHeadPtr;
|
|
|
|
// Toggle expected phase tag on every roll over
|
|
if (CompletionQueueHeadPtr == 0) {
|
|
NvmeCmdWrapper->AdminOrNVMCmdSet ? (NvmeDxeSmmDataArea->AdminPhaseTag = ~NvmeDxeSmmDataArea->AdminPhaseTag) : \
|
|
(NvmeDxeSmmDataArea->Queue1PhaseTag = ~NvmeDxeSmmDataArea->Queue1PhaseTag);
|
|
}
|
|
|
|
// Get the offset to the Command Completion Queue Head Pointer
|
|
pCmdCompletionData = (COMPLETION_QUEUE_ENTRY *)(CompletionQueueStart + CompletionQueueHeadPtr * sizeof(COMPLETION_QUEUE_ENTRY));
|
|
|
|
do {
|
|
// Check whether Command Identifier, SQ ID matches and Phase Tag matches with the cmd issued.
|
|
if ((pCmdCompletionData->CommandIdentifier == CommandIdentifier) && \
|
|
(pCmdCompletionData->SQIdentifier == NvmeCmdWrapper->SQIdentifier) && \
|
|
(pCmdCompletionData->PhaseTag == (NvmeCmdWrapper->AdminOrNVMCmdSet ? \
|
|
NvmeDxeSmmDataArea->AdminPhaseTag : NvmeDxeSmmDataArea->Queue1PhaseTag)))
|
|
{
|
|
break;
|
|
}
|
|
|
|
// check if there are any fatal errors
|
|
if (CONTROLLER_REG32(NvmeController, Offset_CSTS) & CSTS_CFS){
|
|
if (!NvmeController->NvmeInSmm) {
|
|
DEBUG((DEBUG_ERROR, "Nvme Fatal Error\n"));
|
|
}
|
|
return EFI_ABORTED;
|
|
}
|
|
|
|
MicroSecondDelay (10); // 10us delay
|
|
|
|
} while (--TimeOut);
|
|
|
|
if (!NvmeController->NvmeInSmm) {
|
|
PrintCommandCompletionData (pCmdCompletionData);
|
|
}
|
|
|
|
if (!TimeOut) {
|
|
if (!NvmeController->NvmeInSmm) {
|
|
DEBUG((DEBUG_ERROR, "Nvme TimeOut\n"));
|
|
}
|
|
|
|
return EFI_TIMEOUT;
|
|
}
|
|
|
|
// Update HeadPtr from Completion Queue.
|
|
// Check what Queue was cmd posted to and then update the corresponding Head/Tail pointer
|
|
if (NvmeCmdWrapper->AdminOrNVMCmdSet) {
|
|
NvmeDxeSmmDataArea->AdminSubmissionQueueHeadPtr = pCmdCompletionData->SQHeadPointer;
|
|
NvmeDxeSmmDataArea->AdminCompletionQueueHeadPtr = NvmeDxeSmmDataArea->AdminSubmissionQueueHeadPtr;
|
|
Offset = QUEUE_DOORBELL_OFFSET(NvmeCmdWrapper->SQIdentifier, 1, NvmeController->DoorBellStride);
|
|
CONTROLLER_WRITE_REG32(NvmeController, Offset, NvmeDxeSmmDataArea->AdminCompletionQueueHeadPtr);
|
|
} else {
|
|
NvmeDxeSmmDataArea->Queue1SubmissionQueueHeadPtr = pCmdCompletionData->SQHeadPointer;
|
|
NvmeDxeSmmDataArea->Queue1CompletionQueueHeadPtr = NvmeDxeSmmDataArea->Queue1SubmissionQueueHeadPtr;
|
|
Offset = QUEUE_DOORBELL_OFFSET(NvmeCmdWrapper->SQIdentifier, 1, NvmeController->DoorBellStride);
|
|
CONTROLLER_WRITE_REG32(NvmeController, Offset, NvmeDxeSmmDataArea->Queue1CompletionQueueHeadPtr);
|
|
}
|
|
|
|
// Check whether all cmds submitted has been completed. CompletionQueue Head pointer should give a clue on
|
|
// how many cmds where executed.
|
|
if (pCmdCompletionData->StatusCode || pCmdCompletionData->StatusCodeType) {
|
|
DEBUG ((DEBUG_BLKIO, "NVMe command with opcode - 0x%x failed with Status Code Type - 0x%x and Status Code - 0x%x\n",
|
|
NvmeCmdWrapper->NvmCmd.CMD0.Opcode, pCmdCompletionData->StatusCodeType, pCmdCompletionData->StatusCode));
|
|
Status = EFI_DEVICE_ERROR;
|
|
} else {
|
|
Status = EFI_SUCCESS;
|
|
}
|
|
|
|
#if NVME_VERBOSE_PRINT
|
|
if (!NvmeController->NvmeInSmm) {
|
|
if (NvmeCmdWrapper->AdminOrNVMCmdSet) {
|
|
DEBUG((DEBUG_BLKIO, "AdminSubmissionQueueHeadPtr %08X\n", NvmeDxeSmmDataArea->AdminSubmissionQueueHeadPtr));
|
|
DEBUG((DEBUG_BLKIO, "AdminSubmissionQueueTailPtr %08X\n", NvmeDxeSmmDataArea->AdminSubmissionQueueTailPtr));
|
|
DEBUG((DEBUG_BLKIO, "AdminCompletionQueueHeadPtr %08X\n", NvmeDxeSmmDataArea->AdminCompletionQueueHeadPtr));
|
|
DEBUG((DEBUG_BLKIO, "AdminCompletionQueueTailPtr %08X\n", NvmeDxeSmmDataArea->AdminCompletionQueueTailPtr));
|
|
} else {
|
|
DEBUG((DEBUG_BLKIO, "Queue1SubmissionQueueHeadPtr %08X\n", NvmeDxeSmmDataArea->Queue1SubmissionQueueHeadPtr));
|
|
DEBUG((DEBUG_BLKIO, "Queue1SubmissionQueueTailPtr %08X\n", NvmeDxeSmmDataArea->Queue1SubmissionQueueTailPtr));
|
|
DEBUG((DEBUG_BLKIO, "Queue1CompletionQueueHeadPtr %08X\n", NvmeDxeSmmDataArea->Queue1CompletionQueueHeadPtr));
|
|
DEBUG((DEBUG_BLKIO, "Queue1CompletionQueueTailPtr %08X\n", NvmeDxeSmmDataArea->Queue1CompletionQueueTailPtr));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Update o/p buffer
|
|
CopyMem ((VOID *)CmdCompletionData, pCmdCompletionData, sizeof(COMPLETION_QUEUE_ENTRY));
|
|
NvmeCmdWrapper->AdminOrNVMCmdSet ? NvmeDxeSmmDataArea->CommandIdentifierAdmin++ : NvmeDxeSmmDataArea->CommandIdentifierQueue1++;
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
/**
|
|
Delete Admin and other Completion/Submission queue and
|
|
create it back again.
|
|
|
|
@param NvmeController
|
|
|
|
@retval EFI_STATUS
|
|
|
|
@note This procedure will be called if the queue gets filled up.
|
|
This situation shouldn't generally happen as each cmd is completed in
|
|
our case. So both Head and Tail should point to the same location
|
|
before and after cmd is executed.One possibility for calling this
|
|
routine will be when the cmd doesn't get completed and Completion
|
|
Queue doesn't get updated by the controller. Also any Set Feature
|
|
cmd that was issued during initialization should be re-issued here.
|
|
|
|
**/
|
|
|
|
EFI_STATUS
|
|
RecreateAllQueues (
|
|
IN AMI_NVME_CONTROLLER_PROTOCOL *NvmeController
|
|
)
|
|
{
|
|
|
|
EFI_STATUS Status = EFI_SUCCESS;
|
|
UINT32 ProgramCC = 0;
|
|
UINT32 Delay;
|
|
NVME_DXE_SMM_DATA_AREA *NvmeDxeSmmDataArea = NvmeController->NvmeDxeSmmDataArea;
|
|
NVME_COMMAND_WRAPPER LocalNvmeCmdWrapper;
|
|
NVME_COMMAND_WRAPPER *NvmeCmdWrapper = &LocalNvmeCmdWrapper;
|
|
|
|
// In SMM, this function is not supported
|
|
if (NvmeController->NvmeInSmm) {
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
if (!NvmeController->NvmeInSmm) {
|
|
DEBUG((DEBUG_BLKIO, "Recreate Admin/IO Queue : "));
|
|
}
|
|
|
|
// Device is already in Re-Configuring State, Exit with Error
|
|
if ( NvmeController->DeviceState == Fatal_Recovery_state ) {
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
NvmeController->DeviceState = Fatal_Recovery_state;
|
|
|
|
ProgramCC = CONTROLLER_REG32(NvmeController, Offset_CC);
|
|
|
|
// Check if the controller is already running. If yes stop it.
|
|
Delay = NvmeController->TimeOut * 500;
|
|
if (CONTROLLER_REG32(NvmeController, Offset_CC) & 0x1) {
|
|
|
|
// Clear Control register
|
|
CONTROLLER_WRITE_REG32 (NvmeController, Offset_CC, 0);
|
|
do {
|
|
if (!(CONTROLLER_REG32(NvmeController, Offset_CSTS) & 0x1)) {
|
|
break;
|
|
}
|
|
MicroSecondDelay (1000);
|
|
} while (--Delay);
|
|
|
|
}
|
|
|
|
if (!Delay) {
|
|
goto RecreateAllQueues_Error;
|
|
}
|
|
|
|
// Program Admin Queue Size and Base Address
|
|
CONTROLLER_WRITE_REG32(NvmeController, Offset_Aqa ,
|
|
((UINT32)NvmeController->AdminCompletionQueueSize << 16) + NvmeController->AdminSubmissionQueueSize);
|
|
|
|
CONTROLLER_WRITE_REG32(NvmeController, Offset_Asq,
|
|
(UINT32) NvmeController->AdminSubmissionQueueMappedAddr);
|
|
|
|
CONTROLLER_WRITE_REG32(NvmeController, Offset_Asq + 4,
|
|
(UINT32) RShiftU64(NvmeController->AdminSubmissionQueueMappedAddr, 32) );
|
|
|
|
CONTROLLER_WRITE_REG32(NvmeController, Offset_Acq,
|
|
(UINT32)NvmeController->AdminCompletionQueueMappedAddr);
|
|
|
|
CONTROLLER_WRITE_REG32(NvmeController, Offset_Acq + 4,
|
|
(UINT32) RShiftU64(NvmeController->AdminCompletionQueueMappedAddr, 32) );
|
|
|
|
// Enable Controller
|
|
CONTROLLER_WRITE_REG32(NvmeController, Offset_CC, (ProgramCC | 1) );
|
|
|
|
// Wait for the controller to get ready
|
|
// Check if the controller is already running. If yes stop it.
|
|
Delay = NvmeController->TimeOut * 500;
|
|
do {
|
|
if ((CONTROLLER_REG32(NvmeController, Offset_CSTS) & 0x1)) {
|
|
break;
|
|
}
|
|
|
|
MicroSecondDelay (1000);
|
|
} while (--Delay);
|
|
|
|
RecreateAllQueues_Error:
|
|
if (!Delay) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
if (!NvmeController->NvmeInSmm) {
|
|
DEBUG((DEBUG_ERROR, "Status %r\n", Status));
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
NvmeDxeSmmDataArea->AdminSubmissionQueueHeadPtr = 0;
|
|
NvmeDxeSmmDataArea->AdminSubmissionQueueTailPtr = 0;
|
|
NvmeDxeSmmDataArea->AdminCompletionQueueHeadPtr = 0;
|
|
NvmeDxeSmmDataArea->AdminCompletionQueueTailPtr = 0;
|
|
NvmeDxeSmmDataArea->AdminPhaseTag = FALSE;
|
|
|
|
Status = SetNumberOfQueues ( NvmeController,
|
|
NvmeCmdWrapper );
|
|
|
|
if (!EFI_ERROR(Status) && (NvmeController->NVMQueueNumber == 1) ) {
|
|
|
|
// Create Submission and Completion Queue1
|
|
Status = CreateAdditionalSubmissionCompletionQueue(
|
|
NvmeController,
|
|
NvmeCmdWrapper,
|
|
NvmeController->NVMQueueNumber,
|
|
NvmeController->Queue1SubmissionQueueSize
|
|
);
|
|
}
|
|
|
|
if (!NvmeController->NvmeInSmm) {
|
|
DEBUG((DEBUG_BLKIO, "Recreate Admin/IO Queues Status %r\n", Status));
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Issue Set feature command to allocate I/O queues
|
|
|
|
@param NvmeController
|
|
@param NvmeCmdWrapper
|
|
|
|
@retval EFI_STATUS
|
|
|
|
**/
|
|
|
|
EFI_STATUS
|
|
SetNumberOfQueues (
|
|
IN AMI_NVME_CONTROLLER_PROTOCOL *NvmeController,
|
|
IN NVME_COMMAND_WRAPPER *NvmeCmdWrapper
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
NVME_DXE_SMM_DATA_AREA *NvmeDxeSmmDataArea = NvmeController->NvmeDxeSmmDataArea;
|
|
COMPLETION_QUEUE_ENTRY CompletionData;
|
|
|
|
// If FATAL error occurred for SET_FEATURES nvme command, Return from here.
|
|
if( (NvmeController->DeviceState == Fatal_Recovery_state) && \
|
|
NvmeController->SetNumberOfQueuesInProgress ) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
NvmeController->SetNumberOfQueuesInProgress = TRUE;
|
|
|
|
// Issue Set Feature cmd to initialize # of queues to 1
|
|
gBS->SetMem(NvmeCmdWrapper, sizeof(NVME_COMMAND_WRAPPER), 0);
|
|
|
|
// Build NVME command
|
|
NvmeCmdWrapper->NvmCmd.CMD0.Opcode = SET_FEATURES;
|
|
NvmeCmdWrapper->NvmCmd.CMD0.CommandIdentifier = NvmeDxeSmmDataArea->CommandIdentifierAdmin;
|
|
|
|
NvmeCmdWrapper->NvmCmd.CDW10 = 0x7;
|
|
NvmeCmdWrapper->NvmCmd.CDW11 = 0x10001;
|
|
|
|
NvmeCmdWrapper->AdminOrNVMCmdSet = TRUE;
|
|
NvmeCmdWrapper->SQIdentifier = 0; // Queue 0 for Admin cmds
|
|
NvmeCmdWrapper->CmdTimeOut = NVME_COMMAND_TIMEOUT * 1000;
|
|
|
|
Status = ExecuteNvmeCmd (NvmeController, NvmeCmdWrapper, &CompletionData);
|
|
|
|
NvmeController->SetNumberOfQueuesInProgress = FALSE;
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
/**
|
|
Prints the cmd completion status
|
|
|
|
@param pCmdCompletionData
|
|
|
|
@retval VOID
|
|
|
|
@note The amount of data that will get printed can be controlled
|
|
using DEBUG_ERROR_LEVEL_MASK SDL token.
|
|
Make sure PcdDebugPrintErrorLevel is properly cloned and set to
|
|
PcdsPatchableInModule in the project.
|
|
|
|
**/
|
|
|
|
VOID
|
|
PrintCommandCompletionData (
|
|
IN COMPLETION_QUEUE_ENTRY *pCmdCompletionData
|
|
)
|
|
{
|
|
|
|
#if NVME_VERBOSE_PRINT
|
|
// Fig 25 NVM Express 1.1 spec
|
|
// Print Completion Cmd Data
|
|
DEBUG((DEBUG_BLKIO, "Completion Queue DW2 : %08X\n", *((UINT32 *)pCmdCompletionData + 2)));
|
|
DEBUG((DEBUG_BLKIO, "Completion Queue DW3 : %08X\n", *((UINT32 *)pCmdCompletionData + 3)));
|
|
DEBUG((DEBUG_BLKIO, "Completion Queue DW0 : %08X\n", pCmdCompletionData->DW0));
|
|
DEBUG((DEBUG_BLKIO, "Completion Queue Reserved : %08X\n", pCmdCompletionData->DW1));
|
|
#endif
|
|
|
|
}
|
|
|
|
/**
|
|
Prints Nvme cmd parameters
|
|
|
|
@param NvmeCmdWrapper
|
|
|
|
@retval VOID
|
|
|
|
@note The amount of data that will get printed can be controlled
|
|
using DEBUG_ERROR_LEVEL_MASK SDL token.
|
|
Make sure PcdDebugPrintErrorLevel is properly cloned and set to
|
|
PcdsPatchableInModule in the project.
|
|
|
|
**/
|
|
|
|
VOID
|
|
PrintNvmeCmdWrapper (
|
|
NVME_COMMAND_WRAPPER *NvmeCmdWrapper
|
|
)
|
|
{
|
|
|
|
#if NVME_VERBOSE_PRINT
|
|
DEBUG((DEBUG_BLKIO,"\nCMD DW0 : %08X\n", *(UINT32 *)&(NvmeCmdWrapper->NvmCmd)));
|
|
DEBUG((DEBUG_BLKIO, "NSID : %08X\n", NvmeCmdWrapper->NvmCmd.NSID));
|
|
DEBUG((DEBUG_BLKIO, "MPTR : %016lX\n", NvmeCmdWrapper->NvmCmd.MPTR));
|
|
DEBUG((DEBUG_BLKIO, "PRP1 : %016lX\n", NvmeCmdWrapper->NvmCmd.PRP1));
|
|
DEBUG((DEBUG_BLKIO, "PRP2 : %016lX\n", NvmeCmdWrapper->NvmCmd.PRP2));
|
|
DEBUG((DEBUG_BLKIO, "CDW10 : %08X\n", NvmeCmdWrapper->NvmCmd.CDW10));
|
|
DEBUG((DEBUG_BLKIO, "CDW11 : %08X\n", NvmeCmdWrapper->NvmCmd.CDW11));
|
|
DEBUG((DEBUG_BLKIO, "CDW12 : %08X\n", NvmeCmdWrapper->NvmCmd.CDW12));
|
|
DEBUG((DEBUG_BLKIO, "CDW13 : %08X\n", NvmeCmdWrapper->NvmCmd.CDW13));
|
|
DEBUG((DEBUG_BLKIO, "CDW14 : %08X\n", NvmeCmdWrapper->NvmCmd.CDW14));
|
|
DEBUG((DEBUG_BLKIO, "CDW15 : %08X\n", NvmeCmdWrapper->NvmCmd.CDW15));
|
|
DEBUG((DEBUG_BLKIO, "Cmd sent to Queue : %08X\n", NvmeCmdWrapper->SQIdentifier));
|
|
#endif
|
|
|
|
}
|
|
|
|
/**
|
|
Read the Dword Data
|
|
|
|
@param NvmeController - Pointer to AMI_AHCI_BUS_PROTOCOL of AHCI Controller
|
|
@param Index - Index/Offset address to read
|
|
|
|
@retval Value Read
|
|
|
|
**/
|
|
UINT32
|
|
ReadDataDword(
|
|
IN AMI_NVME_CONTROLLER_PROTOCOL *NvmeController,
|
|
IN UINTN Offset
|
|
)
|
|
{
|
|
UINT32 Data = 0;
|
|
|
|
if( NvmeController == NULL ) {
|
|
return Data;
|
|
}
|
|
|
|
if( !NvmeController->NvmeInSmm && NvmeController->PciIO != NULL) {
|
|
NvmeController->PciIO->Mem.Read (
|
|
NvmeController->PciIO,
|
|
EfiPciIoWidthUint32,
|
|
NVME_BAR0_INDEX,
|
|
Offset,
|
|
1,
|
|
&Data);
|
|
} else {
|
|
Data = (*NvmeMm32Ptr ((NvmeController->NvmeBarOffset), (Offset)));
|
|
}
|
|
return Data;
|
|
}
|
|
/**
|
|
Write Read the Dword Data
|
|
|
|
@param NvmeController - Pointer to AMI_AHCI_BUS_PROTOCOL of AHCI Controller
|
|
@param Offset - Index/Offset address to Write
|
|
@param Data - Data to be written
|
|
|
|
@retval Nothing
|
|
|
|
**/
|
|
VOID
|
|
WriteDataDword(
|
|
IN AMI_NVME_CONTROLLER_PROTOCOL *NvmeController,
|
|
IN UINTN Offset,
|
|
IN UINTN Data
|
|
)
|
|
{
|
|
|
|
if( NvmeController == NULL ) {
|
|
return;
|
|
}
|
|
|
|
if( !NvmeController->NvmeInSmm && NvmeController->PciIO != NULL) {
|
|
NvmeController->PciIO->Mem.Write (
|
|
NvmeController->PciIO,
|
|
EfiPciIoWidthUint32,
|
|
NVME_BAR0_INDEX,
|
|
Offset,
|
|
1,
|
|
&Data);
|
|
} else {
|
|
(*NvmeMm32Ptr ((NvmeController->NvmeBarOffset), (Offset))) = ((UINT32) (Data));
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
Read the Word Data
|
|
|
|
@param NvmeController - Pointer to AMI_AHCI_BUS_PROTOCOL of AHCI Controller
|
|
@param Offset - Index/Offset address to read
|
|
|
|
@retval Value Read
|
|
|
|
**/
|
|
UINT16
|
|
ReadDataWord(
|
|
IN AMI_NVME_CONTROLLER_PROTOCOL *NvmeController,
|
|
IN UINTN Offset
|
|
)
|
|
{
|
|
UINT16 Data = 0;
|
|
|
|
if( NvmeController == NULL ) {
|
|
return Data;
|
|
}
|
|
|
|
if( !NvmeController->NvmeInSmm && NvmeController->PciIO != NULL) {
|
|
NvmeController->PciIO->Mem.Read (
|
|
NvmeController->PciIO,
|
|
EfiPciIoWidthUint16,
|
|
NVME_BAR0_INDEX,
|
|
Offset,
|
|
1,
|
|
&Data);
|
|
} else {
|
|
Data = (*NvmeMm16Ptr ((NvmeController->NvmeBarOffset), (Offset)));
|
|
}
|
|
|
|
return Data;
|
|
}
|
|
|
|
/**
|
|
WriteRead the word Data
|
|
|
|
@param NvmeController - Pointer to AMI_AHCI_BUS_PROTOCOL of AHCI Controller
|
|
@param Offset - Index/Offset address to Write
|
|
@param Data - Data to be written
|
|
|
|
@retval Nothing
|
|
|
|
**/
|
|
VOID
|
|
WriteDataWord(
|
|
IN AMI_NVME_CONTROLLER_PROTOCOL *NvmeController,
|
|
IN UINTN Offset,
|
|
IN UINTN Data
|
|
)
|
|
{
|
|
|
|
if( NvmeController == NULL ) {
|
|
return;
|
|
}
|
|
|
|
if( !NvmeController->NvmeInSmm && NvmeController->PciIO != NULL) {
|
|
NvmeController->PciIO->Mem.Write (
|
|
NvmeController->PciIO,
|
|
EfiPciIoWidthUint16,
|
|
NVME_BAR0_INDEX,
|
|
Offset,
|
|
1,
|
|
&Data);
|
|
} else {
|
|
(*NvmeMm16Ptr ((NvmeController->NvmeBarOffset), (Offset))) = ((UINT16) (Data));
|
|
}
|
|
|
|
}
|
|
/**
|
|
Read the Byte Data
|
|
|
|
@param NvmeController - Pointer to AMI_AHCI_BUS_PROTOCOL of AHCI Controller
|
|
@param Offset - Index/Offset address to read
|
|
|
|
@retval Value Read
|
|
|
|
**/
|
|
UINT8
|
|
ReadDataByte(
|
|
IN AMI_NVME_CONTROLLER_PROTOCOL *NvmeController,
|
|
IN UINTN Offset
|
|
)
|
|
{
|
|
UINT8 Data = 0;
|
|
|
|
if( NvmeController == NULL ) {
|
|
return Data;
|
|
}
|
|
|
|
if( !NvmeController->NvmeInSmm && NvmeController->PciIO != NULL) {
|
|
NvmeController->PciIO->Mem.Read (
|
|
NvmeController->PciIO,
|
|
EfiPciIoWidthUint8,
|
|
NVME_BAR0_INDEX,
|
|
Offset,
|
|
1,
|
|
&Data);
|
|
} else {
|
|
Data = (*NvmeMm8Ptr ((NvmeController->NvmeBarOffset), (Offset)));
|
|
}
|
|
|
|
return Data;
|
|
}
|
|
/**
|
|
WriteRead the Byte Data
|
|
|
|
@param NvmeController - Pointer to AMI_AHCI_BUS_PROTOCOL of AHCI Controller
|
|
@param Offset - Index/Offset address to Write
|
|
@param Data - Data to be written
|
|
|
|
@retval Nothing
|
|
|
|
**/
|
|
VOID
|
|
WriteDataByte(
|
|
IN AMI_NVME_CONTROLLER_PROTOCOL *NvmeController,
|
|
IN UINTN Offset,
|
|
IN UINTN Data
|
|
)
|
|
{
|
|
|
|
if( NvmeController == NULL ) {
|
|
return;
|
|
}
|
|
|
|
if( !NvmeController->NvmeInSmm && NvmeController->PciIO != NULL) {
|
|
NvmeController->PciIO->Mem.Write (
|
|
NvmeController->PciIO,
|
|
EfiPciIoWidthUint8,
|
|
NVME_BAR0_INDEX,
|
|
Offset,
|
|
1,
|
|
&Data);
|
|
} else {
|
|
(*NvmeMm8Ptr ((NvmeController->NvmeBarOffset), (Offset))) = ((UINT8) (Data));
|
|
}
|
|
}
|