2 The NvmExpressPei driver is used to manage non-volatile memory subsystem
3 which follows NVM Express specification at PEI phase.
5 Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>
7 SPDX-License-Identifier: BSD-2-Clause-Patent
11 #include "NvmExpressPei.h"
14 Create PRP lists for Data transfer which is larger than 2 memory pages.
16 @param[in] Private The pointer to the PEI_NVME_CONTROLLER_PRIVATE_DATA data structure.
17 @param[in] PhysicalAddr The physical base address of Data Buffer.
18 @param[in] Pages The number of pages to be transfered.
20 @retval The pointer Value to the first PRP List of the PRP lists.
25 IN PEI_NVME_CONTROLLER_PRIVATE_DATA
*Private
,
26 IN EFI_PHYSICAL_ADDRESS PhysicalAddr
,
37 EFI_PHYSICAL_ADDRESS PrpListPhyAddr
;
40 EFI_PHYSICAL_ADDRESS NewPhyAddr
;
43 // The number of Prp Entry in a memory page.
45 PrpEntryNo
= EFI_PAGE_SIZE
/ sizeof (UINT64
);
48 // Calculate total PrpList number.
50 PrpListNo
= (UINTN
) DivU64x64Remainder ((UINT64
)Pages
, (UINT64
)PrpEntryNo
, &Remainder
);
55 if (PrpListNo
> NVME_PRP_SIZE
) {
58 "%a: The implementation only supports PrpList number up to 4."
59 " But %d are needed here.\n",
65 PrpListHost
= (VOID
*)(UINTN
) NVME_PRP_BASE (Private
);
67 Bytes
= EFI_PAGES_TO_SIZE (PrpListNo
);
68 PrpListPhyAddr
= (UINT64
)(UINTN
)(PrpListHost
);
71 // Fill all PRP lists except of last one.
73 ZeroMem (PrpListHost
, Bytes
);
74 for (PrpListIndex
= 0; PrpListIndex
< PrpListNo
- 1; ++PrpListIndex
) {
75 PrpListBase
= (UINTN
)PrpListHost
+ PrpListIndex
* EFI_PAGE_SIZE
;
77 for (PrpEntryIndex
= 0; PrpEntryIndex
< PrpEntryNo
; ++PrpEntryIndex
) {
78 PrpEntry
= (UINT8
*)(UINTN
) (PrpListBase
+ PrpEntryIndex
* sizeof(UINT64
));
79 if (PrpEntryIndex
!= PrpEntryNo
- 1) {
81 // Fill all PRP entries except of last one.
83 CopyMem (PrpEntry
, (VOID
*)(UINTN
) (&PhysicalAddr
), sizeof (UINT64
));
84 PhysicalAddr
+= EFI_PAGE_SIZE
;
87 // Fill last PRP entries with next PRP List pointer.
89 NewPhyAddr
= (PrpListPhyAddr
+ (PrpListIndex
+ 1) * EFI_PAGE_SIZE
);
90 CopyMem (PrpEntry
, (VOID
*)(UINTN
) (&NewPhyAddr
), sizeof (UINT64
));
96 // Fill last PRP list.
98 PrpListBase
= (UINTN
)PrpListHost
+ PrpListIndex
* EFI_PAGE_SIZE
;
99 for (PrpEntryIndex
= 0; PrpEntryIndex
< ((Remainder
!= 0) ? Remainder
: PrpEntryNo
); ++PrpEntryIndex
) {
100 PrpEntry
= (UINT8
*)(UINTN
) (PrpListBase
+ PrpEntryIndex
* sizeof(UINT64
));
101 CopyMem (PrpEntry
, (VOID
*)(UINTN
) (&PhysicalAddr
), sizeof (UINT64
));
103 PhysicalAddr
+= EFI_PAGE_SIZE
;
106 return PrpListPhyAddr
;
110 Check the execution status from a given completion queue entry.
112 @param[in] Cq A pointer to the NVME_CQ item.
120 if (Cq
->Sct
== 0x0 && Cq
->Sc
== 0x0) {
124 DEBUG ((DEBUG_INFO
, "Dump NVMe Completion Entry Status from [0x%x]:\n", (UINTN
)Cq
));
127 " SQ Identifier : [0x%x], Phase Tag : [%d], Cmd Identifier : [0x%x]\n",
132 DEBUG ((DEBUG_INFO
, " Status Code Type : [0x%x], Status Code : [0x%x]\n", Cq
->Sct
, Cq
->Sc
));
133 DEBUG ((DEBUG_INFO
, " NVMe Cmd Execution Result - "));
139 DEBUG ((DEBUG_INFO
, "Successful Completion\n"));
142 DEBUG ((DEBUG_INFO
, "Invalid Command Opcode\n"));
145 DEBUG ((DEBUG_INFO
, "Invalid Field in Command\n"));
148 DEBUG ((DEBUG_INFO
, "Command ID Conflict\n"));
151 DEBUG ((DEBUG_INFO
, "Data Transfer Error\n"));
154 DEBUG ((DEBUG_INFO
, "Commands Aborted due to Power Loss Notification\n"));
157 DEBUG ((DEBUG_INFO
, "Internal Device Error\n"));
160 DEBUG ((DEBUG_INFO
, "Command Abort Requested\n"));
163 DEBUG ((DEBUG_INFO
, "Command Aborted due to SQ Deletion\n"));
166 DEBUG ((DEBUG_INFO
, "Command Aborted due to Failed Fused Command\n"));
169 DEBUG ((DEBUG_INFO
, "Command Aborted due to Missing Fused Command\n"));
172 DEBUG ((DEBUG_INFO
, "Invalid Namespace or Format\n"));
175 DEBUG ((DEBUG_INFO
, "Command Sequence Error\n"));
178 DEBUG ((DEBUG_INFO
, "Invalid SGL Last Segment Descriptor\n"));
181 DEBUG ((DEBUG_INFO
, "Invalid Number of SGL Descriptors\n"));
184 DEBUG ((DEBUG_INFO
, "Data SGL Length Invalid\n"));
187 DEBUG ((DEBUG_INFO
, "Metadata SGL Length Invalid\n"));
190 DEBUG ((DEBUG_INFO
, "SGL Descriptor Type Invalid\n"));
193 DEBUG ((DEBUG_INFO
, "LBA Out of Range\n"));
196 DEBUG ((DEBUG_INFO
, "Capacity Exceeded\n"));
199 DEBUG ((DEBUG_INFO
, "Namespace Not Ready\n"));
202 DEBUG ((DEBUG_INFO
, "Reservation Conflict\n"));
210 DEBUG ((DEBUG_INFO
, "Completion Queue Invalid\n"));
213 DEBUG ((DEBUG_INFO
, "Invalid Queue Identifier\n"));
216 DEBUG ((DEBUG_INFO
, "Maximum Queue Size Exceeded\n"));
219 DEBUG ((DEBUG_INFO
, "Abort Command Limit Exceeded\n"));
222 DEBUG ((DEBUG_INFO
, "Asynchronous Event Request Limit Exceeded\n"));
225 DEBUG ((DEBUG_INFO
, "Invalid Firmware Slot\n"));
228 DEBUG ((DEBUG_INFO
, "Invalid Firmware Image\n"));
231 DEBUG ((DEBUG_INFO
, "Invalid Interrupt Vector\n"));
234 DEBUG ((DEBUG_INFO
, "Invalid Log Page\n"));
237 DEBUG ((DEBUG_INFO
, "Invalid Format\n"));
240 DEBUG ((DEBUG_INFO
, "Firmware Application Requires Conventional Reset\n"));
243 DEBUG ((DEBUG_INFO
, "Invalid Queue Deletion\n"));
246 DEBUG ((DEBUG_INFO
, "Feature Identifier Not Saveable\n"));
249 DEBUG ((DEBUG_INFO
, "Feature Not Changeable\n"));
252 DEBUG ((DEBUG_INFO
, "Feature Not Namespace Specific\n"));
255 DEBUG ((DEBUG_INFO
, "Firmware Application Requires NVM Subsystem Reset\n"));
258 DEBUG ((DEBUG_INFO
, "Conflicting Attributes\n"));
261 DEBUG ((DEBUG_INFO
, "Invalid Protection Information\n"));
264 DEBUG ((DEBUG_INFO
, "Attempted Write to Read Only Range\n"));
272 DEBUG ((DEBUG_INFO
, "Write Fault\n"));
275 DEBUG ((DEBUG_INFO
, "Unrecovered Read Error\n"));
278 DEBUG ((DEBUG_INFO
, "End-to-end Guard Check Error\n"));
281 DEBUG ((DEBUG_INFO
, "End-to-end Application Tag Check Error\n"));
284 DEBUG ((DEBUG_INFO
, "End-to-end Reference Tag Check Error\n"));
287 DEBUG ((DEBUG_INFO
, "Compare Failure\n"));
290 DEBUG ((DEBUG_INFO
, "Access Denied\n"));
296 DEBUG ((DEBUG_INFO
, "Unknown error\n"));
300 return EFI_DEVICE_ERROR
;
304 Sends an NVM Express Command Packet to an NVM Express controller or namespace. This function only
305 supports blocking execution of the command.
307 @param[in] Private The pointer to the NVME_CONTEXT Data structure.
308 @param[in] NamespaceId Is a 32 bit Namespace ID to which the Express HCI command packet will
310 A Value of 0 denotes the NVM Express controller, a Value of all 0FFh in
311 the namespace ID specifies that the command packet should be sent to all
313 @param[in,out] Packet A pointer to the EDKII PEI NVM Express PassThru Command Packet to send
314 to the NVMe namespace specified by NamespaceId.
316 @retval EFI_SUCCESS The EDKII PEI NVM Express Command Packet was sent by the host.
317 TransferLength bytes were transferred to, or from DataBuffer.
318 @retval EFI_NOT_READY The EDKII PEI NVM Express Command Packet could not be sent because
319 the controller is not ready. The caller may retry again later.
320 @retval EFI_DEVICE_ERROR A device error occurred while attempting to send the EDKII PEI NVM
321 Express Command Packet.
322 @retval EFI_INVALID_PARAMETER Namespace, or the contents of EDKII_PEI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET
324 The EDKII PEI NVM Express Command Packet was not sent, so no
325 additional status information is available.
326 @retval EFI_UNSUPPORTED The command described by the EDKII PEI NVM Express Command Packet
327 is not supported by the host adapter.
328 The EDKII PEI NVM Express Command Packet was not sent, so no
329 additional status information is available.
330 @retval EFI_TIMEOUT A timeout occurred while waiting for the EDKII PEI NVM Express Command
336 IN PEI_NVME_CONTROLLER_PRIVATE_DATA
*Private
,
337 IN UINT32 NamespaceId
,
338 IN OUT EDKII_PEI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET
*Packet
347 EDKII_IOMMU_OPERATION MapOp
;
349 EFI_PHYSICAL_ADDRESS PhyAddr
;
358 // Check the data fields in Packet parameter
360 if (Packet
== NULL
) {
363 "%a, Invalid parameter: Packet(%lx)\n",
367 return EFI_INVALID_PARAMETER
;
370 if ((Packet
->NvmeCmd
== NULL
) || (Packet
->NvmeCompletion
== NULL
)) {
373 "%a, Invalid parameter: NvmeCmd (%lx)/NvmeCompletion(%lx)\n",
375 (UINTN
)Packet
->NvmeCmd
,
376 (UINTN
)Packet
->NvmeCompletion
378 return EFI_INVALID_PARAMETER
;
381 if (Packet
->QueueType
!= NVME_ADMIN_QUEUE
&& Packet
->QueueType
!= NVME_IO_QUEUE
) {
384 "%a, Invalid parameter: QueueId(%lx)\n",
386 (UINTN
)Packet
->QueueType
388 return EFI_INVALID_PARAMETER
;
391 QueueId
= Packet
->QueueType
;
392 Sq
= Private
->SqBuffer
[QueueId
] + Private
->SqTdbl
[QueueId
].Sqt
;
393 Cq
= Private
->CqBuffer
[QueueId
] + Private
->CqHdbl
[QueueId
].Cqh
;
394 if (QueueId
== NVME_ADMIN_QUEUE
) {
395 SqSize
= NVME_ASQ_SIZE
+ 1;
396 CqSize
= NVME_ACQ_SIZE
+ 1;
398 SqSize
= NVME_CSQ_SIZE
+ 1;
399 CqSize
= NVME_CCQ_SIZE
+ 1;
402 if (Packet
->NvmeCmd
->Nsid
!= NamespaceId
) {
405 "%a: Nsid mismatch (%x, %x)\n",
407 Packet
->NvmeCmd
->Nsid
,
410 return EFI_INVALID_PARAMETER
;
413 ZeroMem (Sq
, sizeof (NVME_SQ
));
414 Sq
->Opc
= Packet
->NvmeCmd
->Cdw0
.Opcode
;
415 Sq
->Fuse
= Packet
->NvmeCmd
->Cdw0
.FusedOperation
;
416 Sq
->Cid
= Packet
->NvmeCmd
->Cdw0
.Cid
;
417 Sq
->Nsid
= Packet
->NvmeCmd
->Nsid
;
420 // Currently we only support PRP for data transfer, SGL is NOT supported
422 ASSERT (Sq
->Psdt
== 0);
424 DEBUG ((DEBUG_ERROR
, "%a: Does not support SGL mechanism.\n", __FUNCTION__
));
425 return EFI_UNSUPPORTED
;
428 Sq
->Prp
[0] = (UINT64
)(UINTN
)Packet
->TransferBuffer
;
432 Status
= EFI_SUCCESS
;
434 // If the NVMe cmd has data in or out, then mapping the user buffer to the PCI controller
435 // specific addresses.
437 if ((Sq
->Opc
& (BIT0
| BIT1
)) != 0) {
438 if (((Packet
->TransferLength
!= 0) && (Packet
->TransferBuffer
== NULL
)) ||
439 ((Packet
->TransferLength
== 0) && (Packet
->TransferBuffer
!= NULL
))) {
440 return EFI_INVALID_PARAMETER
;
444 // Currently, we only support creating IO submission/completion queues that are
445 // allocated internally by the driver.
447 if ((Packet
->QueueType
== NVME_ADMIN_QUEUE
) &&
448 ((Sq
->Opc
== NVME_ADMIN_CRIOCQ_CMD
) || (Sq
->Opc
== NVME_ADMIN_CRIOSQ_CMD
))) {
449 if ((Packet
->TransferBuffer
!= Private
->SqBuffer
[NVME_IO_QUEUE
]) &&
450 (Packet
->TransferBuffer
!= Private
->CqBuffer
[NVME_IO_QUEUE
])) {
453 "%a: Does not support external IO queues creation request.\n",
456 return EFI_UNSUPPORTED
;
459 if ((Sq
->Opc
& BIT0
) != 0) {
460 MapOp
= EdkiiIoMmuOperationBusMasterRead
;
462 MapOp
= EdkiiIoMmuOperationBusMasterWrite
;
465 if ((Packet
->TransferLength
!= 0) && (Packet
->TransferBuffer
!= NULL
)) {
466 MapLength
= Packet
->TransferLength
;
469 Packet
->TransferBuffer
,
474 if (EFI_ERROR (Status
) || (MapLength
!= Packet
->TransferLength
)) {
475 Status
= EFI_OUT_OF_RESOURCES
;
476 DEBUG ((DEBUG_ERROR
, "%a: Fail to map data buffer.\n", __FUNCTION__
));
480 Sq
->Prp
[0] = PhyAddr
;
483 if((Packet
->MetadataLength
!= 0) && (Packet
->MetadataBuffer
!= NULL
)) {
484 MapLength
= Packet
->MetadataLength
;
487 Packet
->MetadataBuffer
,
492 if (EFI_ERROR (Status
) || (MapLength
!= Packet
->MetadataLength
)) {
493 Status
= EFI_OUT_OF_RESOURCES
;
494 DEBUG ((DEBUG_ERROR
, "%a: Fail to map meta data buffer.\n", __FUNCTION__
));
503 // If the Buffer Size spans more than two memory pages (page Size as defined in CC.Mps),
504 // then build a PRP list in the second PRP submission queue entry.
506 Offset
= ((UINT32
)Sq
->Prp
[0]) & (EFI_PAGE_SIZE
- 1);
507 Bytes
= Packet
->TransferLength
;
509 if ((Offset
+ Bytes
) > (EFI_PAGE_SIZE
* 2)) {
511 // Create PrpList for remaining Data Buffer.
513 PhyAddr
= (Sq
->Prp
[0] + EFI_PAGE_SIZE
) & ~(EFI_PAGE_SIZE
- 1);
514 Sq
->Prp
[1] = NvmeCreatePrpList (
517 EFI_SIZE_TO_PAGES(Offset
+ Bytes
) - 1
519 if (Sq
->Prp
[1] == 0) {
520 Status
= EFI_OUT_OF_RESOURCES
;
521 DEBUG ((DEBUG_ERROR
, "%a: Create PRP list fail, Status - %r\n", __FUNCTION__
, Status
));
525 } else if ((Offset
+ Bytes
) > EFI_PAGE_SIZE
) {
526 Sq
->Prp
[1] = (Sq
->Prp
[0] + EFI_PAGE_SIZE
) & ~(EFI_PAGE_SIZE
- 1);
529 if (Packet
->NvmeCmd
->Flags
& CDW10_VALID
) {
530 Sq
->Payload
.Raw
.Cdw10
= Packet
->NvmeCmd
->Cdw10
;
532 if (Packet
->NvmeCmd
->Flags
& CDW11_VALID
) {
533 Sq
->Payload
.Raw
.Cdw11
= Packet
->NvmeCmd
->Cdw11
;
535 if (Packet
->NvmeCmd
->Flags
& CDW12_VALID
) {
536 Sq
->Payload
.Raw
.Cdw12
= Packet
->NvmeCmd
->Cdw12
;
538 if (Packet
->NvmeCmd
->Flags
& CDW13_VALID
) {
539 Sq
->Payload
.Raw
.Cdw13
= Packet
->NvmeCmd
->Cdw13
;
541 if (Packet
->NvmeCmd
->Flags
& CDW14_VALID
) {
542 Sq
->Payload
.Raw
.Cdw14
= Packet
->NvmeCmd
->Cdw14
;
544 if (Packet
->NvmeCmd
->Flags
& CDW15_VALID
) {
545 Sq
->Payload
.Raw
.Cdw15
= Packet
->NvmeCmd
->Cdw15
;
549 // Ring the submission queue doorbell.
551 Private
->SqTdbl
[QueueId
].Sqt
++;
552 if (Private
->SqTdbl
[QueueId
].Sqt
== SqSize
) {
553 Private
->SqTdbl
[QueueId
].Sqt
= 0;
555 Data32
= ReadUnaligned32 ((UINT32
*)&Private
->SqTdbl
[QueueId
]);
556 Status
= NVME_SET_SQTDBL (Private
, QueueId
, &Data32
);
557 if (EFI_ERROR (Status
)) {
558 DEBUG ((DEBUG_ERROR
, "%a: NVME_SET_SQTDBL fail, Status - %r\n", __FUNCTION__
, Status
));
563 // Wait for completion queue to get filled in.
565 Status
= EFI_TIMEOUT
;
567 while (Timer
< Packet
->CommandTimeout
) {
568 if (Cq
->Pt
!= Private
->Pt
[QueueId
]) {
569 Status
= EFI_SUCCESS
;
573 MicroSecondDelay (NVME_POLL_INTERVAL
);
574 Timer
+= NVME_POLL_INTERVAL
;
577 if (Status
== EFI_TIMEOUT
) {
579 // Timeout occurs for an NVMe command, reset the controller to abort the outstanding command
581 DEBUG ((DEBUG_ERROR
, "%a: Timeout occurs for the PassThru command.\n", __FUNCTION__
));
582 Status
= NvmeControllerInit (Private
);
583 if (EFI_ERROR (Status
)) {
584 Status
= EFI_DEVICE_ERROR
;
587 // Return EFI_TIMEOUT to indicate a timeout occurs for PassThru command
589 Status
= EFI_TIMEOUT
;
595 // Move forward the Completion Queue head
597 Private
->CqHdbl
[QueueId
].Cqh
++;
598 if (Private
->CqHdbl
[QueueId
].Cqh
== CqSize
) {
599 Private
->CqHdbl
[QueueId
].Cqh
= 0;
600 Private
->Pt
[QueueId
] ^= 1;
604 // Copy the Respose Queue entry for this command to the callers response buffer
606 CopyMem (Packet
->NvmeCompletion
, Cq
, sizeof (EDKII_PEI_NVM_EXPRESS_COMPLETION
));
609 // Check the NVMe cmd execution result
611 Status
= NvmeCheckCqStatus (Cq
);
612 NVME_SET_CQHDBL (Private
, QueueId
, &Private
->CqHdbl
[QueueId
]);
615 if (MapMeta
!= NULL
) {
616 IoMmuUnmap (MapMeta
);
619 if (MapData
!= NULL
) {
620 IoMmuUnmap (MapData
);