NVM Express specification.\r
\r
(C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>\r
- Copyright (c) 2013 - 2015, Intel Corporation. All rights reserved.<BR>\r
+ Copyright (c) 2013 - 2016, Intel Corporation. All rights reserved.<BR>\r
This program and the accompanying materials\r
are licensed and made available under the terms and conditions of the BSD License\r
which accompanies this distribution. The full text of the license may be found at\r
IN EFI_EVENT Event OPTIONAL\r
)\r
{\r
- NVME_CONTROLLER_PRIVATE_DATA *Private;\r
- EFI_STATUS Status;\r
- EFI_PCI_IO_PROTOCOL *PciIo;\r
- NVME_SQ *Sq;\r
- NVME_CQ *Cq;\r
- UINT8 QueueType;\r
- UINT32 Bytes;\r
- UINT16 Offset;\r
- EFI_EVENT TimerEvent;\r
- EFI_PCI_IO_PROTOCOL_OPERATION Flag;\r
- EFI_PHYSICAL_ADDRESS PhyAddr;\r
- VOID *MapData;\r
- VOID *MapMeta;\r
- VOID *MapPrpList;\r
- UINTN MapLength;\r
- UINT64 *Prp;\r
- VOID *PrpListHost;\r
- UINTN PrpListNo;\r
- UINT32 Data;\r
+ NVME_CONTROLLER_PRIVATE_DATA *Private;\r
+ EFI_STATUS Status;\r
+ EFI_PCI_IO_PROTOCOL *PciIo;\r
+ NVME_SQ *Sq;\r
+ NVME_CQ *Cq;\r
+ UINT16 QueueId;\r
+ UINT32 Bytes;\r
+ UINT16 Offset;\r
+ EFI_EVENT TimerEvent;\r
+ EFI_PCI_IO_PROTOCOL_OPERATION Flag;\r
+ EFI_PHYSICAL_ADDRESS PhyAddr;\r
+ VOID *MapData;\r
+ VOID *MapMeta;\r
+ VOID *MapPrpList;\r
+ UINTN MapLength;\r
+ UINT64 *Prp;\r
+ VOID *PrpListHost;\r
+ UINTN PrpListNo;\r
+ UINT32 Attributes;\r
+ UINT32 IoAlign;\r
+ UINT32 MaxTransLen;\r
+ UINT32 Data;\r
+ NVME_PASS_THRU_ASYNC_REQ *AsyncRequest;\r
+ EFI_TPL OldTpl;\r
\r
//\r
// check the data fields in Packet parameter.\r
return EFI_INVALID_PARAMETER;\r
}\r
\r
+ //\r
+ // 'Attributes' with neither EFI_NVM_EXPRESS_PASS_THRU_ATTRIBUTES_LOGICAL nor\r
+ // EFI_NVM_EXPRESS_PASS_THRU_ATTRIBUTES_PHYSICAL set is an illegal\r
+ // configuration.\r
+ //\r
+ Attributes = This->Mode->Attributes;\r
+ if ((Attributes & (EFI_NVM_EXPRESS_PASS_THRU_ATTRIBUTES_PHYSICAL |\r
+ EFI_NVM_EXPRESS_PASS_THRU_ATTRIBUTES_LOGICAL)) == 0) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Buffer alignment check for TransferBuffer & MetadataBuffer.\r
+ //\r
+ IoAlign = This->Mode->IoAlign;\r
+ if (IoAlign > 0 && (((UINTN) Packet->TransferBuffer & (IoAlign - 1)) != 0)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (IoAlign > 0 && (((UINTN) Packet->MetadataBuffer & (IoAlign - 1)) != 0)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
Private = NVME_CONTROLLER_PRIVATE_DATA_FROM_PASS_THRU (This);\r
+\r
+ //\r
+ // Check NamespaceId is valid or not.\r
+ //\r
+ if ((NamespaceId > Private->ControllerData->Nn) &&\r
+ (NamespaceId != (UINT32) -1)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Check whether TransferLength exceeds the maximum data transfer size.\r
+ //\r
+ if (Private->ControllerData->Mdts != 0) {\r
+ MaxTransLen = (1 << (Private->ControllerData->Mdts)) *\r
+ (1 << (Private->Cap.Mpsmin + 12));\r
+ if (Packet->TransferLength > MaxTransLen) {\r
+ Packet->TransferLength = MaxTransLen;\r
+ return EFI_BAD_BUFFER_SIZE;\r
+ }\r
+ }\r
+\r
PciIo = Private->PciIo;\r
MapData = NULL;\r
MapMeta = NULL;\r
TimerEvent = NULL;\r
Status = EFI_SUCCESS;\r
\r
- QueueType = Packet->QueueType;\r
- Sq = Private->SqBuffer[QueueType] + Private->SqTdbl[QueueType].Sqt;\r
- Cq = Private->CqBuffer[QueueType] + Private->CqHdbl[QueueType].Cqh;\r
+ if (Packet->QueueType == NVME_ADMIN_QUEUE) {\r
+ QueueId = 0;\r
+ } else {\r
+ if (Event == NULL) {\r
+ QueueId = 1;\r
+ } else {\r
+ QueueId = 2;\r
+\r
+ //\r
+ // Submission queue full check.\r
+ //\r
+ if ((Private->SqTdbl[QueueId].Sqt + 1) % (NVME_ASYNC_CSQ_SIZE + 1) ==\r
+ Private->AsyncSqHead) {\r
+ return EFI_NOT_READY;\r
+ }\r
+ }\r
+ }\r
+ Sq = Private->SqBuffer[QueueId] + Private->SqTdbl[QueueId].Sqt;\r
+ Cq = Private->CqBuffer[QueueId] + Private->CqHdbl[QueueId].Cqh;\r
\r
if (Packet->NvmeCmd->Nsid != NamespaceId) {\r
return EFI_INVALID_PARAMETER;\r
ZeroMem (Sq, sizeof (NVME_SQ));\r
Sq->Opc = (UINT8)Packet->NvmeCmd->Cdw0.Opcode;\r
Sq->Fuse = (UINT8)Packet->NvmeCmd->Cdw0.FusedOperation;\r
- Sq->Cid = Private->Cid[QueueType]++;\r
+ Sq->Cid = Private->Cid[QueueId]++;\r
Sq->Nsid = Packet->NvmeCmd->Nsid;\r
\r
//\r
// these two cmds are special which requires their data buffer must support simultaneous access by both the\r
// processor and a PCI Bus Master. It's caller's responsbility to ensure this.\r
//\r
- if (((Sq->Opc & (BIT0 | BIT1)) != 0) && (Sq->Opc != NVME_ADMIN_CRIOCQ_OPC) && (Sq->Opc != NVME_ADMIN_CRIOSQ_OPC)) {\r
+ if (((Sq->Opc & (BIT0 | BIT1)) != 0) && (Sq->Opc != NVME_ADMIN_CRIOCQ_CMD) && (Sq->Opc != NVME_ADMIN_CRIOSQ_CMD)) {\r
+ if ((Packet->TransferLength == 0) || (Packet->TransferBuffer == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
if ((Sq->Opc & BIT0) != 0) {\r
Flag = EfiPciIoOperationBusMasterRead;\r
} else {\r
Sq->Prp[0] = PhyAddr;\r
Sq->Prp[1] = 0;\r
\r
- MapLength = Packet->MetadataLength;\r
- if(Packet->MetadataBuffer != NULL) {\r
+ if((Packet->MetadataLength != 0) && (Packet->MetadataBuffer != NULL)) {\r
MapLength = Packet->MetadataLength;\r
Status = PciIo->Map (\r
PciIo,\r
//\r
// Ring the submission queue doorbell.\r
//\r
- Private->SqTdbl[QueueType].Sqt ^= 1;\r
- Data = ReadUnaligned32 ((UINT32*)&Private->SqTdbl[QueueType]);\r
+ if (Event != NULL) {\r
+ Private->SqTdbl[QueueId].Sqt =\r
+ (Private->SqTdbl[QueueId].Sqt + 1) % (NVME_ASYNC_CSQ_SIZE + 1);\r
+ } else {\r
+ Private->SqTdbl[QueueId].Sqt ^= 1;\r
+ }\r
+ Data = ReadUnaligned32 ((UINT32*)&Private->SqTdbl[QueueId]);\r
PciIo->Mem.Write (\r
PciIo,\r
EfiPciIoWidthUint32,\r
NVME_BAR,\r
- NVME_SQTDBL_OFFSET(QueueType, Private->Cap.Dstrd),\r
+ NVME_SQTDBL_OFFSET(QueueId, Private->Cap.Dstrd),\r
1,\r
&Data\r
);\r
\r
+ //\r
+ // For non-blocking requests, return directly if the command is placed\r
+ // in the submission queue.\r
+ //\r
+ if (Event != NULL) {\r
+ AsyncRequest = AllocateZeroPool (sizeof (NVME_PASS_THRU_ASYNC_REQ));\r
+ if (AsyncRequest == NULL) {\r
+ Status = EFI_DEVICE_ERROR;\r
+ goto EXIT;\r
+ }\r
+\r
+ AsyncRequest->Signature = NVME_PASS_THRU_ASYNC_REQ_SIG;\r
+ AsyncRequest->Packet = Packet;\r
+ AsyncRequest->CommandId = Sq->Cid;\r
+ AsyncRequest->CallerEvent = Event;\r
+\r
+ OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
+ InsertTailList (&Private->AsyncPassThruQueue, &AsyncRequest->Link);\r
+ gBS->RestoreTPL (OldTpl);\r
+\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
Status = gBS->CreateEvent (\r
EVT_TIMER,\r
TPL_CALLBACK,\r
//\r
Status = EFI_TIMEOUT;\r
while (EFI_ERROR (gBS->CheckEvent (TimerEvent))) {\r
- if (Cq->Pt != Private->Pt[QueueType]) {\r
+ if (Cq->Pt != Private->Pt[QueueId]) {\r
Status = EFI_SUCCESS;\r
break;\r
}\r
}\r
\r
- if ((Private->CqHdbl[QueueType].Cqh ^= 1) == 0) {\r
- Private->Pt[QueueType] ^= 1;\r
- }\r
-\r
//\r
- // Copy the Respose Queue entry for this command to the callers response buffer\r
+ // Check the NVMe cmd execution result\r
//\r
- CopyMem(Packet->NvmeCompletion, Cq, sizeof(EFI_NVM_EXPRESS_COMPLETION));\r
+ if (Status != EFI_TIMEOUT) {\r
+ if ((Cq->Sct == 0) && (Cq->Sc == 0)) {\r
+ Status = EFI_SUCCESS;\r
+ } else {\r
+ Status = EFI_DEVICE_ERROR;\r
+ //\r
+ // Copy the Respose Queue entry for this command to the callers response buffer\r
+ //\r
+ CopyMem(Packet->NvmeCompletion, Cq, sizeof(EFI_NVM_EXPRESS_COMPLETION));\r
+ \r
+ //\r
+ // Dump every completion entry status for debugging.\r
+ //\r
+ DEBUG_CODE_BEGIN();\r
+ NvmeDumpStatus(Cq);\r
+ DEBUG_CODE_END();\r
+ }\r
+ }\r
\r
- //\r
- // Dump every completion entry status for debugging.\r
- //\r
- DEBUG_CODE_BEGIN();\r
- NvmeDumpStatus(Cq);\r
- DEBUG_CODE_END();\r
+ if ((Private->CqHdbl[QueueId].Cqh ^= 1) == 0) {\r
+ Private->Pt[QueueId] ^= 1;\r
+ }\r
\r
- Data = ReadUnaligned32 ((UINT32*)&Private->CqHdbl[QueueType]);\r
+ Data = ReadUnaligned32 ((UINT32*)&Private->CqHdbl[QueueId]);\r
PciIo->Mem.Write (\r
PciIo,\r
EfiPciIoWidthUint32,\r
NVME_BAR,\r
- NVME_CQHDBL_OFFSET(QueueType, Private->Cap.Dstrd),\r
+ NVME_CQHDBL_OFFSET(QueueId, Private->Cap.Dstrd),\r
1,\r
&Data\r
);\r
\r
*NamespaceId = NextNamespaceId;\r
} else {\r
- if (*NamespaceId >= Private->ControllerData->Nn) {\r
+ if (*NamespaceId > Private->ControllerData->Nn) {\r
return EFI_INVALID_PARAMETER;\r
}\r
\r
NextNamespaceId = *NamespaceId + 1;\r
+ if (NextNamespaceId > Private->ControllerData->Nn) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
//\r
// Allocate buffer for Identify Namespace data.\r
//\r
)\r
{\r
NVME_NAMESPACE_DEVICE_PATH *Node;\r
+ NVME_CONTROLLER_PRIVATE_DATA *Private;\r
\r
if ((This == NULL) || (DevicePath == NULL) || (NamespaceId == NULL)) {\r
return EFI_INVALID_PARAMETER;\r
return EFI_UNSUPPORTED;\r
}\r
\r
- Node = (NVME_NAMESPACE_DEVICE_PATH *)DevicePath;\r
+ Node = (NVME_NAMESPACE_DEVICE_PATH *)DevicePath;\r
+ Private = NVME_CONTROLLER_PRIVATE_DATA_FROM_PASS_THRU (This);\r
\r
if (DevicePath->SubType == MSG_NVME_NAMESPACE_DP) {\r
if (DevicePathNodeLength(DevicePath) != sizeof(NVME_NAMESPACE_DEVICE_PATH)) {\r
return EFI_NOT_FOUND;\r
}\r
\r
+ //\r
+ // Check NamespaceId in the device path node is valid or not.\r
+ //\r
+ if ((Node->NamespaceId == 0) ||\r
+ (Node->NamespaceId > Private->ControllerData->Nn)) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
*NamespaceId = Node->NamespaceId;\r
\r
return EFI_SUCCESS;\r
return EFI_INVALID_PARAMETER;\r
}\r
\r
- if (NamespaceId == 0) {\r
- return EFI_NOT_FOUND;\r
- }\r
-\r
Status = EFI_SUCCESS;\r
Private = NVME_CONTROLLER_PRIVATE_DATA_FROM_PASS_THRU (This);\r
\r
+ //\r
+ // Check NamespaceId is valid or not.\r
+ //\r
+ if ((NamespaceId == 0) ||\r
+ (NamespaceId > Private->ControllerData->Nn)) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
Node = (NVME_NAMESPACE_DEVICE_PATH *)AllocateZeroPool (sizeof (NVME_NAMESPACE_DEVICE_PATH));\r
if (Node == NULL) {\r
return EFI_OUT_OF_RESOURCES;\r