--- /dev/null
+/** @file\r
+ Provide functions to initialize NVME controller and perform NVME commands\r
+\r
+Copyright (c) 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
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "OpalPasswordSmm.h"\r
+\r
+\r
+#define ALIGN(v, a) (UINTN)((((v) - 1) | ((a) - 1)) + 1)\r
+\r
+///\r
+/// NVME Host controller registers operation\r
+///\r
+#define NVME_GET_CAP(Nvme, Cap) NvmeMmioRead (Cap, Nvme->Nbar + NVME_CAP_OFFSET, sizeof (NVME_CAP))\r
+#define NVME_GET_CC(Nvme, Cc) NvmeMmioRead (Cc, Nvme->Nbar + NVME_CC_OFFSET, sizeof (NVME_CC))\r
+#define NVME_SET_CC(Nvme, Cc) NvmeMmioWrite (Nvme->Nbar + NVME_CC_OFFSET, Cc, sizeof (NVME_CC))\r
+#define NVME_GET_CSTS(Nvme, Csts) NvmeMmioRead (Csts, Nvme->Nbar + NVME_CSTS_OFFSET, sizeof (NVME_CSTS))\r
+#define NVME_GET_AQA(Nvme, Aqa) NvmeMmioRead (Aqa, Nvme->Nbar + NVME_AQA_OFFSET, sizeof (NVME_AQA))\r
+#define NVME_SET_AQA(Nvme, Aqa) NvmeMmioWrite (Nvme->Nbar + NVME_AQA_OFFSET, Aqa, sizeof (NVME_AQA))\r
+#define NVME_GET_ASQ(Nvme, Asq) NvmeMmioRead (Asq, Nvme->Nbar + NVME_ASQ_OFFSET, sizeof (NVME_ASQ))\r
+#define NVME_SET_ASQ(Nvme, Asq) NvmeMmioWrite (Nvme->Nbar + NVME_ASQ_OFFSET, Asq, sizeof (NVME_ASQ))\r
+#define NVME_GET_ACQ(Nvme, Acq) NvmeMmioRead (Acq, Nvme->Nbar + NVME_ACQ_OFFSET, sizeof (NVME_ACQ))\r
+#define NVME_SET_ACQ(Nvme, Acq) NvmeMmioWrite (Nvme->Nbar + NVME_ACQ_OFFSET, Acq, sizeof (NVME_ACQ))\r
+#define NVME_GET_VER(Nvme, Ver) NvmeMmioRead (Ver, Nvme->Nbar + NVME_VER_OFFSET, sizeof (NVME_VER))\r
+#define NVME_SET_SQTDBL(Nvme, Qid, Sqtdbl) NvmeMmioWrite (Nvme->Nbar + NVME_SQTDBL_OFFSET(Qid, Nvme->Cap.Dstrd), Sqtdbl, sizeof (NVME_SQTDBL))\r
+#define NVME_SET_CQHDBL(Nvme, Qid, Cqhdbl) NvmeMmioWrite (Nvme->Nbar + NVME_CQHDBL_OFFSET(Qid, Nvme->Cap.Dstrd), Cqhdbl, sizeof (NVME_CQHDBL))\r
+\r
+///\r
+/// Base memory address\r
+///\r
+enum {\r
+ BASEMEM_CONTROLLER_DATA,\r
+ BASEMEM_IDENTIFY_DATA,\r
+ BASEMEM_ASQ,\r
+ BASEMEM_ACQ,\r
+ BASEMEM_SQ,\r
+ BASEMEM_CQ,\r
+ BASEMEM_PRP,\r
+ BASEMEM_SECURITY,\r
+ MAX_BASEMEM_COUNT\r
+};\r
+\r
+///\r
+/// All of base memories are 4K(0x1000) alignment\r
+///\r
+#define NVME_MEM_BASE(Nvme) (Nvme->BaseMem)\r
+#define NVME_CONTROL_DATA_BASE(Nvme) (ALIGN (NVME_MEM_BASE(Nvme) + ((NvmeGetBaseMemPages (BASEMEM_CONTROLLER_DATA)) * EFI_PAGE_SIZE), EFI_PAGE_SIZE))\r
+#define NVME_NAMESPACE_DATA_BASE(Nvme) (ALIGN (NVME_MEM_BASE(Nvme) + ((NvmeGetBaseMemPages (BASEMEM_IDENTIFY_DATA)) * EFI_PAGE_SIZE), EFI_PAGE_SIZE))\r
+#define NVME_ASQ_BASE(Nvme) (ALIGN (NVME_MEM_BASE(Nvme) + ((NvmeGetBaseMemPages (BASEMEM_ASQ)) * EFI_PAGE_SIZE), EFI_PAGE_SIZE))\r
+#define NVME_ACQ_BASE(Nvme) (ALIGN (NVME_MEM_BASE(Nvme) + ((NvmeGetBaseMemPages (BASEMEM_ACQ)) * EFI_PAGE_SIZE), EFI_PAGE_SIZE))\r
+#define NVME_SQ_BASE(Nvme, index) (ALIGN (NVME_MEM_BASE(Nvme) + ((NvmeGetBaseMemPages (BASEMEM_SQ) + ((index)*(NVME_MAX_IO_QUEUES-1))) * EFI_PAGE_SIZE), EFI_PAGE_SIZE))\r
+#define NVME_CQ_BASE(Nvme, index) (ALIGN (NVME_MEM_BASE(Nvme) + ((NvmeGetBaseMemPages (BASEMEM_CQ) + ((index)*(NVME_MAX_IO_QUEUES-1))) * EFI_PAGE_SIZE), EFI_PAGE_SIZE))\r
+#define NVME_PRP_BASE(Nvme, index) (ALIGN (NVME_MEM_BASE(Nvme) + ((NvmeGetBaseMemPages (BASEMEM_PRP) + ((index)*NVME_PRP_SIZE)) * EFI_PAGE_SIZE), EFI_PAGE_SIZE))\r
+#define NVME_SEC_BASE(Nvme) (ALIGN (NVME_MEM_BASE(Nvme) + ((NvmeGetBaseMemPages (BASEMEM_SECURITY)) * EFI_PAGE_SIZE), EFI_PAGE_SIZE))\r
+\r
+/**\r
+ Transfer MMIO Data to memory.\r
+\r
+ @param[in,out] MemBuffer - Destination: Memory address\r
+ @param[in] MmioAddr - Source: MMIO address\r
+ @param[in] Size - Size for read\r
+\r
+ @retval EFI_SUCCESS - MMIO read sucessfully\r
+**/\r
+EFI_STATUS\r
+NvmeMmioRead (\r
+ IN OUT VOID *MemBuffer,\r
+ IN UINTN MmioAddr,\r
+ IN UINTN Size\r
+ )\r
+{\r
+ UINTN Offset;\r
+ UINT8 Data;\r
+ UINT8 *Ptr;\r
+\r
+ // priority has adjusted\r
+ switch (Size) {\r
+ case 4:\r
+ *((UINT32 *)MemBuffer) = MmioRead32 (MmioAddr);\r
+ break;\r
+\r
+ case 8:\r
+ *((UINT64 *)MemBuffer) = MmioRead64 (MmioAddr);\r
+ break;\r
+\r
+ case 2:\r
+ *((UINT16 *)MemBuffer) = MmioRead16 (MmioAddr);\r
+ break;\r
+\r
+ case 1:\r
+ *((UINT8 *)MemBuffer) = MmioRead8 (MmioAddr);\r
+ break;\r
+\r
+ default:\r
+ Ptr = (UINT8 *)MemBuffer;\r
+ for (Offset = 0; Offset < Size; Offset += 1) {\r
+ Data = MmioRead8 (MmioAddr + Offset);\r
+ Ptr[Offset] = Data;\r
+ }\r
+ break;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Transfer memory data to MMIO.\r
+\r
+ @param[in,out] MmioAddr - Destination: MMIO address\r
+ @param[in] MemBuffer - Source: Memory address\r
+ @param[in] Size - Size for write\r
+\r
+ @retval EFI_SUCCESS - MMIO write sucessfully\r
+**/\r
+EFI_STATUS\r
+NvmeMmioWrite (\r
+ IN OUT UINTN MmioAddr,\r
+ IN VOID *MemBuffer,\r
+ IN UINTN Size\r
+ )\r
+{\r
+ UINTN Offset;\r
+ UINT8 Data;\r
+ UINT8 *Ptr;\r
+\r
+ // priority has adjusted\r
+ switch (Size) {\r
+ case 4:\r
+ MmioWrite32 (MmioAddr, *((UINT32 *)MemBuffer));\r
+ break;\r
+\r
+ case 8:\r
+ MmioWrite64 (MmioAddr, *((UINT64 *)MemBuffer));\r
+ break;\r
+\r
+ case 2:\r
+ MmioWrite16 (MmioAddr, *((UINT16 *)MemBuffer));\r
+ break;\r
+\r
+ case 1:\r
+ MmioWrite8 (MmioAddr, *((UINT8 *)MemBuffer));\r
+ break;\r
+\r
+ default:\r
+ Ptr = (UINT8 *)MemBuffer;\r
+ for (Offset = 0; Offset < Size; Offset += 1) {\r
+ Data = Ptr[Offset];\r
+ MmioWrite8 (MmioAddr + Offset, Data);\r
+ }\r
+ break;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Transfer MMIO data to memory.\r
+\r
+ @param[in,out] MemBuffer - Destination: Memory address\r
+ @param[in] MmioAddr - Source: MMIO address\r
+ @param[in] Size - Size for read\r
+\r
+ @retval EFI_SUCCESS - MMIO read sucessfully\r
+**/\r
+EFI_STATUS\r
+OpalPciRead (\r
+ IN OUT VOID *MemBuffer,\r
+ IN UINTN MmioAddr,\r
+ IN UINTN Size\r
+ )\r
+{\r
+ UINTN Offset;\r
+ UINT8 Data;\r
+ UINT8 *Ptr;\r
+\r
+ // priority has adjusted\r
+ switch (Size) {\r
+ case 4:\r
+ *((UINT32 *)MemBuffer) = PciRead32 (MmioAddr);\r
+ break;\r
+\r
+ case 2:\r
+ *((UINT16 *)MemBuffer) = PciRead16 (MmioAddr);\r
+ break;\r
+\r
+ case 1:\r
+ *((UINT8 *)MemBuffer) = PciRead8 (MmioAddr);\r
+ break;\r
+\r
+ default:\r
+ Ptr = (UINT8 *)MemBuffer;\r
+ for (Offset = 0; Offset < Size; Offset += 1) {\r
+ Data = PciRead8 (MmioAddr + Offset);\r
+ Ptr[Offset] = Data;\r
+ }\r
+ break;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Transfer memory data to MMIO.\r
+\r
+ @param[in,out] MmioAddr - Destination: MMIO address\r
+ @param[in] MemBuffer - Source: Memory address\r
+ @param[in] Size - Size for write\r
+\r
+ @retval EFI_SUCCESS - MMIO write sucessfully\r
+**/\r
+EFI_STATUS\r
+OpalPciWrite (\r
+ IN OUT UINTN MmioAddr,\r
+ IN VOID *MemBuffer,\r
+ IN UINTN Size\r
+ )\r
+{\r
+ UINTN Offset;\r
+ UINT8 Data;\r
+ UINT8 *Ptr;\r
+\r
+ // priority has adjusted\r
+ switch (Size) {\r
+ case 4:\r
+ PciWrite32 (MmioAddr, *((UINT32 *)MemBuffer));\r
+ break;\r
+\r
+ case 2:\r
+ PciWrite16 (MmioAddr, *((UINT16 *)MemBuffer));\r
+ break;\r
+\r
+ case 1:\r
+ PciWrite8 (MmioAddr, *((UINT8 *)MemBuffer));\r
+ break;\r
+\r
+ default:\r
+ Ptr = (UINT8 *)MemBuffer;\r
+ for (Offset = 0; Offset < Size; Offset += 1) {\r
+ Data = Ptr[Offset];\r
+ PciWrite8 (MmioAddr + Offset, Data);\r
+ }\r
+ break;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Get total pages for specific NVME based memory.\r
+\r
+ @param[in] BaseMemIndex - The Index of BaseMem (0-based).\r
+\r
+ @retval - The page count for specific BaseMem Index\r
+\r
+**/\r
+UINT32\r
+NvmeGetBaseMemPages (\r
+ IN UINTN BaseMemIndex\r
+ )\r
+{\r
+ UINT32 Pages;\r
+ UINTN Index;\r
+ UINT32 PageSizeList[8];\r
+\r
+ PageSizeList[0] = 1; /* Controller Data */\r
+ PageSizeList[1] = 1; /* Identify Data */\r
+ PageSizeList[2] = 1; /* ASQ */\r
+ PageSizeList[3] = 1; /* ACQ */\r
+ PageSizeList[4] = 1; /* SQs */\r
+ PageSizeList[5] = 1; /* CQs */\r
+ PageSizeList[6] = NVME_PRP_SIZE * NVME_CSQ_DEPTH; /* PRPs */\r
+ PageSizeList[7] = 1; /* Security Commands */\r
+\r
+ if (BaseMemIndex > MAX_BASEMEM_COUNT) {\r
+ ASSERT (FALSE);\r
+ return 0;\r
+ }\r
+\r
+ Pages = 0;\r
+ for (Index = 0; Index < BaseMemIndex; Index++) {\r
+ Pages += PageSizeList[Index];\r
+ }\r
+\r
+ return Pages;\r
+}\r
+\r
+/**\r
+ Wait for NVME controller status to be ready or not.\r
+\r
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.\r
+ @param[in] WaitReady - Flag for waitting status ready or not\r
+\r
+ @return EFI_SUCCESS - Successfully to wait specific status.\r
+ @return others - Fail to wait for specific controller status.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+NvmeWaitController (\r
+ IN NVME_CONTEXT *Nvme,\r
+ IN BOOLEAN WaitReady\r
+ )\r
+{\r
+ NVME_CSTS Csts;\r
+ EFI_STATUS Status;\r
+ UINT32 Index;\r
+ UINT8 Timeout;\r
+\r
+ //\r
+ // Cap.To specifies max delay time in 500ms increments for Csts.Rdy to set after\r
+ // Cc.Enable. Loop produces a 1 millisecond delay per itteration, up to 500 * Cap.To.\r
+ //\r
+ if (Nvme->Cap.To == 0) {\r
+ Timeout = 1;\r
+ } else {\r
+ Timeout = Nvme->Cap.To;\r
+ }\r
+\r
+ Status = EFI_SUCCESS;\r
+ for(Index = (Timeout * 500); Index != 0; --Index) {\r
+ MicroSecondDelay (1000);\r
+\r
+ //\r
+ // Check if the controller is initialized\r
+ //\r
+ Status = NVME_GET_CSTS (Nvme, &Csts);\r
+ if (EFI_ERROR(Status)) {\r
+ DEBUG ((DEBUG_ERROR, "NVME_GET_CSTS fail, Status = %r\n", Status));\r
+ return Status;\r
+ }\r
+\r
+ if ((BOOLEAN) Csts.Rdy == WaitReady) {\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (Index == 0) {\r
+ Status = EFI_TIMEOUT;\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Disable the Nvm Express controller.\r
+\r
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.\r
+\r
+ @return EFI_SUCCESS - Successfully disable the controller.\r
+ @return others - Fail to disable the controller.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+NvmeDisableController (\r
+ IN NVME_CONTEXT *Nvme\r
+ )\r
+{\r
+ NVME_CC Cc;\r
+ NVME_CSTS Csts;\r
+ EFI_STATUS Status;\r
+\r
+ Status = NVME_GET_CSTS (Nvme, &Csts);\r
+\r
+ ///\r
+ /// Read Controller Configuration Register.\r
+ ///\r
+ Status = NVME_GET_CC (Nvme, &Cc);\r
+ if (EFI_ERROR(Status)) {\r
+ DEBUG ((DEBUG_ERROR, "NVME_GET_CC fail, Status = %r\n", Status));\r
+ goto Done;\r
+ }\r
+\r
+ if (Cc.En == 1) {\r
+ Cc.En = 0;\r
+ ///\r
+ /// Disable the controller.\r
+ ///\r
+ Status = NVME_SET_CC (Nvme, &Cc);\r
+ if (EFI_ERROR(Status)) {\r
+ DEBUG ((DEBUG_ERROR, "NVME_SET_CC fail, Status = %r\n", Status));\r
+ goto Done;\r
+ }\r
+ }\r
+\r
+ Status = NvmeWaitController (Nvme, FALSE);\r
+ if (EFI_ERROR(Status)) {\r
+ DEBUG ((DEBUG_ERROR, "NvmeWaitController fail, Status = %r\n", Status));\r
+ goto Done;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+\r
+Done:\r
+ DEBUG ((DEBUG_INFO, "NvmeDisableController fail, Status: %r\n", Status));\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Enable the Nvm Express controller.\r
+\r
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.\r
+\r
+ @return EFI_SUCCESS - Successfully enable the controller.\r
+ @return EFI_DEVICE_ERROR - Fail to enable the controller.\r
+ @return EFI_TIMEOUT - Fail to enable the controller in given time slot.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+NvmeEnableController (\r
+ IN NVME_CONTEXT *Nvme\r
+ )\r
+{\r
+ NVME_CC Cc;\r
+ EFI_STATUS Status;\r
+\r
+ //\r
+ // Enable the controller\r
+ //\r
+ ZeroMem (&Cc, sizeof (NVME_CC));\r
+ Cc.En = 1;\r
+ Cc.Iosqes = 6;\r
+ Cc.Iocqes = 4;\r
+ Status = NVME_SET_CC (Nvme, &Cc);\r
+ if (EFI_ERROR(Status)) {\r
+ DEBUG ((DEBUG_ERROR, "NVME_SET_CC fail, Status = %r\n", Status));\r
+ goto Done;\r
+ }\r
+\r
+ Status = NvmeWaitController (Nvme, TRUE);\r
+ if (EFI_ERROR(Status)) {\r
+ DEBUG ((DEBUG_ERROR, "NvmeWaitController fail, Status = %r\n", Status));\r
+ goto Done;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+\r
+Done:\r
+ DEBUG ((DEBUG_INFO, "NvmeEnableController fail, Status: %r\n", Status));\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Shutdown the Nvm Express controller.\r
+\r
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.\r
+\r
+ @return EFI_SUCCESS - Successfully shutdown the controller.\r
+ @return EFI_DEVICE_ERROR - Fail to shutdown the controller.\r
+ @return EFI_TIMEOUT - Fail to shutdown the controller in given time slot.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+NvmeShutdownController (\r
+ IN NVME_CONTEXT *Nvme\r
+ )\r
+{\r
+ NVME_CC Cc;\r
+ NVME_CSTS Csts;\r
+ EFI_STATUS Status;\r
+ UINT32 Index;\r
+ UINTN Timeout;\r
+\r
+ Status = NVME_GET_CC (Nvme, &Cc);\r
+ if (EFI_ERROR(Status)) {\r
+ DEBUG ((DEBUG_ERROR, "NVME_GET_CC fail, Status = %r\n", Status));\r
+ return Status;\r
+ }\r
+\r
+ Cc.Shn = 1; // Normal shutdown\r
+\r
+ Status = NVME_SET_CC (Nvme, &Cc);\r
+ if (EFI_ERROR(Status)) {\r
+ DEBUG ((DEBUG_ERROR, "NVME_SET_CC fail, Status = %r\n", Status));\r
+ return Status;\r
+ }\r
+\r
+ Timeout = NVME_GENERIC_TIMEOUT/1000; // ms\r
+ for(Index = (UINT32)(Timeout); Index != 0; --Index) {\r
+ MicroSecondDelay (1000);\r
+\r
+ Status = NVME_GET_CSTS (Nvme, &Csts);\r
+ if (EFI_ERROR(Status)) {\r
+ DEBUG ((DEBUG_ERROR, "NVME_GET_CSTS fail, Status = %r\n", Status));\r
+ return Status;\r
+ }\r
+\r
+ if (Csts.Shst == 2) { // Shutdown processing complete\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (Index == 0) {\r
+ Status = EFI_TIMEOUT;\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Check the execution status from a given completion queue entry.\r
+\r
+ @param[in] Cq - A pointer to the NVME_CQ item.\r
+\r
+**/\r
+EFI_STATUS\r
+NvmeCheckCqStatus (\r
+ IN NVME_CQ *Cq\r
+ )\r
+{\r
+ if (Cq->Sct == 0x0 && Cq->Sc == 0x0) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ DEBUG ((DEBUG_INFO, "Dump NVMe Completion Entry Status from [0x%x]:\n", (UINTN)Cq));\r
+ DEBUG ((DEBUG_INFO, " SQ Identifier : [0x%x], Phase Tag : [%d], Cmd Identifier : [0x%x]\n", Cq->Sqid, Cq->Pt, Cq->Cid));\r
+ DEBUG ((DEBUG_INFO, " NVMe Cmd Execution Result - "));\r
+\r
+ switch (Cq->Sct) {\r
+ case 0x0:\r
+ switch (Cq->Sc) {\r
+ case 0x0:\r
+ DEBUG ((DEBUG_INFO, "Successful Completion\n"));\r
+ return EFI_SUCCESS;\r
+ case 0x1:\r
+ DEBUG ((DEBUG_INFO, "Invalid Command Opcode\n"));\r
+ break;\r
+ case 0x2:\r
+ DEBUG ((DEBUG_INFO, "Invalid Field in Command\n"));\r
+ break;\r
+ case 0x3:\r
+ DEBUG ((DEBUG_INFO, "Command ID Conflict\n"));\r
+ break;\r
+ case 0x4:\r
+ DEBUG ((DEBUG_INFO, "Data Transfer Error\n"));\r
+ break;\r
+ case 0x5:\r
+ DEBUG ((DEBUG_INFO, "Commands Aborted due to Power Loss Notification\n"));\r
+ break;\r
+ case 0x6:\r
+ DEBUG ((DEBUG_INFO, "Internal Device Error\n"));\r
+ break;\r
+ case 0x7:\r
+ DEBUG ((DEBUG_INFO, "Command Abort Requested\n"));\r
+ break;\r
+ case 0x8:\r
+ DEBUG ((DEBUG_INFO, "Command Aborted due to SQ Deletion\n"));\r
+ break;\r
+ case 0x9:\r
+ DEBUG ((DEBUG_INFO, "Command Aborted due to Failed Fused Command\n"));\r
+ break;\r
+ case 0xA:\r
+ DEBUG ((DEBUG_INFO, "Command Aborted due to Missing Fused Command\n"));\r
+ break;\r
+ case 0xB:\r
+ DEBUG ((DEBUG_INFO, "Invalid Namespace or Format\n"));\r
+ break;\r
+ case 0xC:\r
+ DEBUG ((DEBUG_INFO, "Command Sequence Error\n"));\r
+ break;\r
+ case 0xD:\r
+ DEBUG ((DEBUG_INFO, "Invalid SGL Last Segment Descriptor\n"));\r
+ break;\r
+ case 0xE:\r
+ DEBUG ((DEBUG_INFO, "Invalid Number of SGL Descriptors\n"));\r
+ break;\r
+ case 0xF:\r
+ DEBUG ((DEBUG_INFO, "Data SGL Length Invalid\n"));\r
+ break;\r
+ case 0x10:\r
+ DEBUG ((DEBUG_INFO, "Metadata SGL Length Invalid\n"));\r
+ break;\r
+ case 0x11:\r
+ DEBUG ((DEBUG_INFO, "SGL Descriptor Type Invalid\n"));\r
+ break;\r
+ case 0x80:\r
+ DEBUG ((DEBUG_INFO, "LBA Out of Range\n"));\r
+ break;\r
+ case 0x81:\r
+ DEBUG ((DEBUG_INFO, "Capacity Exceeded\n"));\r
+ break;\r
+ case 0x82:\r
+ DEBUG ((DEBUG_INFO, "Namespace Not Ready\n"));\r
+ break;\r
+ case 0x83:\r
+ DEBUG ((DEBUG_INFO, "Reservation Conflict\n"));\r
+ break;\r
+ }\r
+ break;\r
+\r
+ case 0x1:\r
+ switch (Cq->Sc) {\r
+ case 0x0:\r
+ DEBUG ((DEBUG_INFO, "Completion Queue Invalid\n"));\r
+ break;\r
+ case 0x1:\r
+ DEBUG ((DEBUG_INFO, "Invalid Queue Identifier\n"));\r
+ break;\r
+ case 0x2:\r
+ DEBUG ((DEBUG_INFO, "Maximum Queue Size Exceeded\n"));\r
+ break;\r
+ case 0x3:\r
+ DEBUG ((DEBUG_INFO, "Abort Command Limit Exceeded\n"));\r
+ break;\r
+ case 0x5:\r
+ DEBUG ((DEBUG_INFO, "Asynchronous Event Request Limit Exceeded\n"));\r
+ break;\r
+ case 0x6:\r
+ DEBUG ((DEBUG_INFO, "Invalid Firmware Slot\n"));\r
+ break;\r
+ case 0x7:\r
+ DEBUG ((DEBUG_INFO, "Invalid Firmware Image\n"));\r
+ break;\r
+ case 0x8:\r
+ DEBUG ((DEBUG_INFO, "Invalid Interrupt Vector\n"));\r
+ break;\r
+ case 0x9:\r
+ DEBUG ((DEBUG_INFO, "Invalid Log Page\n"));\r
+ break;\r
+ case 0xA:\r
+ DEBUG ((DEBUG_INFO, "Invalid Format\n"));\r
+ break;\r
+ case 0xB:\r
+ DEBUG ((DEBUG_INFO, "Firmware Application Requires Conventional Reset\n"));\r
+ break;\r
+ case 0xC:\r
+ DEBUG ((DEBUG_INFO, "Invalid Queue Deletion\n"));\r
+ break;\r
+ case 0xD:\r
+ DEBUG ((DEBUG_INFO, "Feature Identifier Not Saveable\n"));\r
+ break;\r
+ case 0xE:\r
+ DEBUG ((DEBUG_INFO, "Feature Not Changeable\n"));\r
+ break;\r
+ case 0xF:\r
+ DEBUG ((DEBUG_INFO, "Feature Not Namespace Specific\n"));\r
+ break;\r
+ case 0x10:\r
+ DEBUG ((DEBUG_INFO, "Firmware Application Requires NVM Subsystem Reset\n"));\r
+ break;\r
+ case 0x80:\r
+ DEBUG ((DEBUG_INFO, "Conflicting Attributes\n"));\r
+ break;\r
+ case 0x81:\r
+ DEBUG ((DEBUG_INFO, "Invalid Protection Information\n"));\r
+ break;\r
+ case 0x82:\r
+ DEBUG ((DEBUG_INFO, "Attempted Write to Read Only Range\n"));\r
+ break;\r
+ }\r
+ break;\r
+\r
+ case 0x2:\r
+ switch (Cq->Sc) {\r
+ case 0x80:\r
+ DEBUG ((DEBUG_INFO, "Write Fault\n"));\r
+ break;\r
+ case 0x81:\r
+ DEBUG ((DEBUG_INFO, "Unrecovered Read Error\n"));\r
+ break;\r
+ case 0x82:\r
+ DEBUG ((DEBUG_INFO, "End-to-end Guard Check Error\n"));\r
+ break;\r
+ case 0x83:\r
+ DEBUG ((DEBUG_INFO, "End-to-end Application Tag Check Error\n"));\r
+ break;\r
+ case 0x84:\r
+ DEBUG ((DEBUG_INFO, "End-to-end Reference Tag Check Error\n"));\r
+ break;\r
+ case 0x85:\r
+ DEBUG ((DEBUG_INFO, "Compare Failure\n"));\r
+ break;\r
+ case 0x86:\r
+ DEBUG ((DEBUG_INFO, "Access Denied\n"));\r
+ break;\r
+ }\r
+ break;\r
+\r
+ default:\r
+ DEBUG ((DEBUG_INFO, "Unknown error\n"));\r
+ break;\r
+ }\r
+\r
+ return EFI_DEVICE_ERROR;\r
+}\r
+\r
+/**\r
+ Create PRP lists for Data transfer which is larger than 2 memory pages.\r
+ Note here we calcuate the number of required PRP lists and allocate them at one time.\r
+\r
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.\r
+ @param[in] SqId - The SQ index for this PRP\r
+ @param[in] PhysicalAddr - The physical base address of Data Buffer.\r
+ @param[in] Pages - The number of pages to be transfered.\r
+ @param[out] PrpListHost - The host base address of PRP lists.\r
+ @param[in,out] PrpListNo - The number of PRP List.\r
+\r
+ @retval The pointer Value to the first PRP List of the PRP lists.\r
+\r
+**/\r
+STATIC\r
+UINT64\r
+NvmeCreatePrpList (\r
+ IN NVME_CONTEXT *Nvme,\r
+ IN UINT16 SqId,\r
+ IN EFI_PHYSICAL_ADDRESS PhysicalAddr,\r
+ IN UINTN Pages,\r
+ OUT VOID **PrpListHost,\r
+ IN OUT UINTN *PrpListNo\r
+ )\r
+{\r
+ UINTN PrpEntryNo;\r
+ UINT64 PrpListBase;\r
+ UINTN PrpListIndex;\r
+ UINTN PrpEntryIndex;\r
+ UINT64 Remainder;\r
+ EFI_PHYSICAL_ADDRESS PrpListPhyAddr;\r
+ UINTN Bytes;\r
+ UINT8 *PrpEntry;\r
+ EFI_PHYSICAL_ADDRESS NewPhyAddr;\r
+\r
+ ///\r
+ /// The number of Prp Entry in a memory page.\r
+ ///\r
+ PrpEntryNo = EFI_PAGE_SIZE / sizeof (UINT64);\r
+\r
+ ///\r
+ /// Calculate total PrpList number.\r
+ ///\r
+ *PrpListNo = (UINTN) DivU64x64Remainder ((UINT64)Pages, (UINT64)PrpEntryNo, &Remainder);\r
+ if (Remainder != 0) {\r
+ *PrpListNo += 1;\r
+ }\r
+\r
+ if (*PrpListNo > NVME_PRP_SIZE) {\r
+ DEBUG ((DEBUG_INFO, "NvmeCreatePrpList (PhysicalAddr: %lx, Pages: %x) PrpEntryNo: %x\n",\r
+ PhysicalAddr, Pages, PrpEntryNo));\r
+ DEBUG ((DEBUG_INFO, "*PrpListNo: %x, Remainder: %lx", *PrpListNo, Remainder));\r
+ ASSERT (FALSE);\r
+ }\r
+ *PrpListHost = (VOID *)(UINTN) NVME_PRP_BASE (Nvme, SqId);\r
+\r
+ Bytes = EFI_PAGES_TO_SIZE (*PrpListNo);\r
+ PrpListPhyAddr = (UINT64)(UINTN)(*PrpListHost);\r
+\r
+ ///\r
+ /// Fill all PRP lists except of last one.\r
+ ///\r
+ ZeroMem (*PrpListHost, Bytes);\r
+ for (PrpListIndex = 0; PrpListIndex < *PrpListNo - 1; ++PrpListIndex) {\r
+ PrpListBase = *(UINT64*)PrpListHost + PrpListIndex * EFI_PAGE_SIZE;\r
+\r
+ for (PrpEntryIndex = 0; PrpEntryIndex < PrpEntryNo; ++PrpEntryIndex) {\r
+ PrpEntry = (UINT8 *)(UINTN) (PrpListBase + PrpEntryIndex * sizeof(UINT64));\r
+ if (PrpEntryIndex != PrpEntryNo - 1) {\r
+ ///\r
+ /// Fill all PRP entries except of last one.\r
+ ///\r
+ CopyMem (PrpEntry, (VOID *)(UINTN) (&PhysicalAddr), sizeof (UINT64));\r
+ PhysicalAddr += EFI_PAGE_SIZE;\r
+ } else {\r
+ ///\r
+ /// Fill last PRP entries with next PRP List pointer.\r
+ ///\r
+ NewPhyAddr = (PrpListPhyAddr + (PrpListIndex + 1) * EFI_PAGE_SIZE);\r
+ CopyMem (PrpEntry, (VOID *)(UINTN) (&NewPhyAddr), sizeof (UINT64));\r
+ }\r
+ }\r
+ }\r
+\r
+ ///\r
+ /// Fill last PRP list.\r
+ ///\r
+ PrpListBase = *(UINT64*)PrpListHost + PrpListIndex * EFI_PAGE_SIZE;\r
+ for (PrpEntryIndex = 0; PrpEntryIndex < ((Remainder != 0) ? Remainder : PrpEntryNo); ++PrpEntryIndex) {\r
+ PrpEntry = (UINT8 *)(UINTN) (PrpListBase + PrpEntryIndex * sizeof(UINT64));\r
+ CopyMem (PrpEntry, (VOID *)(UINTN) (&PhysicalAddr), sizeof (UINT64));\r
+\r
+ PhysicalAddr += EFI_PAGE_SIZE;\r
+ }\r
+\r
+ return PrpListPhyAddr;\r
+}\r
+\r
+/**\r
+ Check whether there are available command slots.\r
+\r
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.\r
+ @param[in] Qid - Queue index\r
+\r
+ @retval EFI_SUCCESS - Available command slot is found\r
+ @retval EFI_NOT_READY - No available command slot is found\r
+ @retval EFI_DEVICE_ERROR - Error occurred on device side.\r
+\r
+**/\r
+EFI_STATUS\r
+NvmeHasFreeCmdSlot (\r
+ IN NVME_CONTEXT *Nvme,\r
+ IN UINT8 Qid\r
+ )\r
+{\r
+ return TRUE;\r
+}\r
+\r
+/**\r
+ Check whether all command slots are clean.\r
+\r
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.\r
+ @param[in] Qid - Queue index\r
+\r
+ @retval EFI_SUCCESS - All command slots are clean\r
+ @retval EFI_NOT_READY - Not all command slots are clean\r
+ @retval EFI_DEVICE_ERROR - Error occurred on device side.\r
+\r
+**/\r
+EFI_STATUS\r
+NvmeIsAllCmdSlotClean (\r
+ IN NVME_CONTEXT *Nvme,\r
+ IN UINT8 Qid\r
+ )\r
+{\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Waits until all NVME commands completed.\r
+\r
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.\r
+ @param[in] Qid - Queue index\r
+\r
+ @retval EFI_SUCCESS - All NVME commands have completed\r
+ @retval EFI_TIMEOUT - Timeout occured\r
+ @retval EFI_NOT_READY - Not all NVME commands have completed\r
+ @retval others - Error occurred on device side.\r
+**/\r
+EFI_STATUS\r
+NvmeWaitAllComplete (\r
+ IN NVME_CONTEXT *Nvme,\r
+ IN UINT8 Qid\r
+ )\r
+{\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Sends an NVM Express Command Packet to an NVM Express controller or namespace. This function supports\r
+ both blocking I/O and nonblocking I/O. The blocking I/O functionality is required, and the nonblocking\r
+ I/O functionality is optional.\r
+\r
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.\r
+ @param[in] NamespaceId - Is a 32 bit Namespace ID to which the Express HCI command packet will be sent.\r
+ A Value of 0 denotes the NVM Express controller, a Value of all 0FFh in the namespace\r
+ ID specifies that the command packet should be sent to all valid namespaces.\r
+ @param[in] NamespaceUuid - Is a 64 bit Namespace UUID to which the Express HCI command packet will be sent.\r
+ A Value of 0 denotes the NVM Express controller, a Value of all 0FFh in the namespace\r
+ UUID specifies that the command packet should be sent to all valid namespaces.\r
+ @param[in,out] Packet - A pointer to the NVM Express HCI Command Packet to send to the NVMe namespace specified\r
+ by NamespaceId.\r
+\r
+ @retval EFI_SUCCESS - The NVM Express Command Packet was sent by the host. TransferLength bytes were transferred\r
+ to, or from DataBuffer.\r
+ @retval EFI_NOT_READY - The NVM Express Command Packet could not be sent because the controller is not ready. The caller\r
+ may retry again later.\r
+ @retval EFI_DEVICE_ERROR - A device error occurred while attempting to send the NVM Express Command Packet.\r
+ @retval EFI_INVALID_PARAMETER - Namespace, or the contents of NVM_EXPRESS_PASS_THRU_COMMAND_PACKET are invalid. The NVM\r
+ Express Command Packet was not sent, so no additional status information is available.\r
+ @retval EFI_UNSUPPORTED - The command described by the NVM Express Command Packet is not supported by the host adapter.\r
+ The NVM Express Command Packet was not sent, so no additional status information is available.\r
+ @retval EFI_TIMEOUT - A timeout occurred while waiting for the NVM Express Command Packet to execute.\r
+\r
+**/\r
+EFI_STATUS\r
+NvmePassThru (\r
+ IN NVME_CONTEXT *Nvme,\r
+ IN UINT32 NamespaceId,\r
+ IN UINT64 NamespaceUuid,\r
+ IN OUT NVM_EXPRESS_PASS_THRU_COMMAND_PACKET *Packet\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ NVME_SQ *Sq;\r
+ NVME_CQ *Cq;\r
+ UINT8 Qid;\r
+ UINT32 Bytes;\r
+ UINT32 Offset;\r
+ EFI_PHYSICAL_ADDRESS PhyAddr;\r
+ VOID *PrpListHost;\r
+ UINTN PrpListNo;\r
+ UINT32 Timer;\r
+ UINTN SqSize;\r
+ UINTN CqSize;\r
+\r
+ ///\r
+ /// check the Data fields in Packet parameter.\r
+ ///\r
+ if ((Nvme == NULL) || (Packet == NULL)) {\r
+ DEBUG ((DEBUG_ERROR, "NvmePassThru, invalid parameter: Nvme(%x)/Packet(%x)\n",\r
+ (UINTN)Nvme, (UINTN)Packet));\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if ((Packet->NvmeCmd == NULL) || (Packet->NvmeResponse == NULL)) {\r
+ DEBUG ((DEBUG_ERROR, "NvmePassThru, invalid parameter: NvmeCmd(%x)/NvmeResponse(%x)\n",\r
+ (UINTN)Packet->NvmeCmd, (UINTN)Packet->NvmeResponse));\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (Packet->QueueId != NVME_ADMIN_QUEUE && Packet->QueueId != NVME_IO_QUEUE) {\r
+ DEBUG ((DEBUG_ERROR, "NvmePassThru, invalid parameter: QueueId(%x)\n",\r
+ Packet->QueueId));\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ PrpListHost = NULL;\r
+ PrpListNo = 0;\r
+ Status = EFI_SUCCESS;\r
+\r
+ Qid = Packet->QueueId;\r
+ Sq = Nvme->SqBuffer[Qid] + Nvme->SqTdbl[Qid].Sqt;\r
+ Cq = Nvme->CqBuffer[Qid] + Nvme->CqHdbl[Qid].Cqh;\r
+ if (Qid == NVME_ADMIN_QUEUE) {\r
+ SqSize = NVME_ASQ_SIZE + 1;\r
+ CqSize = NVME_ACQ_SIZE + 1;\r
+ } else {\r
+ SqSize = NVME_CSQ_DEPTH;\r
+ CqSize = NVME_CCQ_DEPTH;\r
+ }\r
+\r
+ if (Packet->NvmeCmd->Nsid != NamespaceId) {\r
+ DEBUG ((DEBUG_ERROR, "NvmePassThru: Nsid mismatch (%x, %x)\n",\r
+ Packet->NvmeCmd->Nsid, NamespaceId));\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ ZeroMem (Sq, sizeof (NVME_SQ));\r
+ Sq->Opc = Packet->NvmeCmd->Cdw0.Opcode;\r
+ Sq->Fuse = Packet->NvmeCmd->Cdw0.FusedOperation;\r
+ Sq->Cid = Packet->NvmeCmd->Cdw0.Cid;\r
+ Sq->Nsid = Packet->NvmeCmd->Nsid;\r
+\r
+ ///\r
+ /// Currently we only support PRP for Data transfer, SGL is NOT supported.\r
+ ///\r
+ ASSERT (Sq->Psdt == 0);\r
+ if (Sq->Psdt != 0) {\r
+ DEBUG ((DEBUG_ERROR, "NvmePassThru: doesn't support SGL mechanism\n"));\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ Sq->Prp[0] = Packet->TransferBuffer;\r
+ Sq->Prp[1] = 0;\r
+\r
+ if(Packet->MetadataBuffer != (UINT64)(UINTN)NULL) {\r
+ Sq->Mptr = Packet->MetadataBuffer;\r
+ }\r
+\r
+ ///\r
+ /// If the Buffer Size spans more than two memory pages (page Size as defined in CC.Mps),\r
+ /// then build a PRP list in the second PRP submission queue entry.\r
+ ///\r
+ Offset = ((UINT32)Sq->Prp[0]) & (EFI_PAGE_SIZE - 1);\r
+ Bytes = Packet->TransferLength;\r
+\r
+ if ((Offset + Bytes) > (EFI_PAGE_SIZE * 2)) {\r
+ ///\r
+ /// Create PrpList for remaining Data Buffer.\r
+ ///\r
+ PhyAddr = (Sq->Prp[0] + EFI_PAGE_SIZE) & ~(EFI_PAGE_SIZE - 1);\r
+ Sq->Prp[1] = NvmeCreatePrpList (Nvme, Nvme->SqTdbl[Qid].Sqt, PhyAddr, EFI_SIZE_TO_PAGES(Offset + Bytes) - 1, &PrpListHost, &PrpListNo);\r
+ if (Sq->Prp[1] == 0) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ DEBUG ((DEBUG_ERROR, "NvmeCreatePrpList fail, Status: %r\n", Status));\r
+ goto EXIT;\r
+ }\r
+\r
+ } else if ((Offset + Bytes) > EFI_PAGE_SIZE) {\r
+ Sq->Prp[1] = (Sq->Prp[0] + EFI_PAGE_SIZE) & ~(EFI_PAGE_SIZE - 1);\r
+ }\r
+\r
+ if(Packet->NvmeCmd->Flags & CDW10_VALID) {\r
+ Sq->Payload.Raw.Cdw10 = Packet->NvmeCmd->Cdw10;\r
+ }\r
+ if(Packet->NvmeCmd->Flags & CDW11_VALID) {\r
+ Sq->Payload.Raw.Cdw11 = Packet->NvmeCmd->Cdw11;\r
+ }\r
+ if(Packet->NvmeCmd->Flags & CDW12_VALID) {\r
+ Sq->Payload.Raw.Cdw12 = Packet->NvmeCmd->Cdw12;\r
+ }\r
+ if(Packet->NvmeCmd->Flags & CDW13_VALID) {\r
+ Sq->Payload.Raw.Cdw13 = Packet->NvmeCmd->Cdw13;\r
+ }\r
+ if(Packet->NvmeCmd->Flags & CDW14_VALID) {\r
+ Sq->Payload.Raw.Cdw14 = Packet->NvmeCmd->Cdw14;\r
+ }\r
+ if(Packet->NvmeCmd->Flags & CDW15_VALID) {\r
+ Sq->Payload.Raw.Cdw15 = Packet->NvmeCmd->Cdw15;\r
+ }\r
+\r
+#if (EN_NVME_VERBOSE_DBINFO == ON)\r
+ //DumpMem (Sq, sizeof (NVME_SQ));\r
+#endif\r
+ ///\r
+ /// Ring the submission queue doorbell.\r
+ ///\r
+ Nvme->SqTdbl[Qid].Sqt++;\r
+ if(Nvme->SqTdbl[Qid].Sqt == SqSize) {\r
+ Nvme->SqTdbl[Qid].Sqt = 0;\r
+ }\r
+ Status = NVME_SET_SQTDBL (Nvme, Qid, &Nvme->SqTdbl[Qid]);\r
+ if (EFI_ERROR(Status)) {\r
+ DEBUG ((DEBUG_ERROR, "NVME_SET_SQTDBL fail, Status: %r\n", Status));\r
+ goto EXIT;\r
+ }\r
+\r
+ ///\r
+ /// Wait for completion queue to get filled in.\r
+ ///\r
+ Status = EFI_TIMEOUT;\r
+ Timer = 0;\r
+ while (Timer < NVME_CMD_TIMEOUT) {\r
+ //DEBUG ((DEBUG_VERBOSE, "Timer: %x, Cq:\n", Timer));\r
+ //DumpMem (Cq, sizeof (NVME_CQ));\r
+ if (Cq->Pt != Nvme->Pt[Qid]) {\r
+ Status = EFI_SUCCESS;\r
+ break;\r
+ }\r
+\r
+ MicroSecondDelay (NVME_CMD_WAIT);\r
+ Timer += NVME_CMD_WAIT;\r
+ }\r
+\r
+ Nvme->CqHdbl[Qid].Cqh++;\r
+ if (Nvme->CqHdbl[Qid].Cqh == CqSize) {\r
+ Nvme->CqHdbl[Qid].Cqh = 0;\r
+ Nvme->Pt[Qid] ^= 1;\r
+ }\r
+\r
+ ///\r
+ /// Copy the Respose Queue entry for this command to the callers response Buffer\r
+ ///\r
+ CopyMem (Packet->NvmeResponse, Cq, sizeof(NVM_EXPRESS_RESPONSE));\r
+\r
+ if (!EFI_ERROR(Status)) { // We still need to check CQ status if no timeout error occured\r
+ Status = NvmeCheckCqStatus (Cq);\r
+ }\r
+ NVME_SET_CQHDBL (Nvme, Qid, &Nvme->CqHdbl[Qid]);\r
+\r
+EXIT:\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Get identify controller Data.\r
+\r
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.\r
+ @param[in] Buffer - The Buffer used to store the identify controller Data.\r
+\r
+ @return EFI_SUCCESS - Successfully get the identify controller Data.\r
+ @return others - Fail to get the identify controller Data.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+NvmeIdentifyController (\r
+ IN NVME_CONTEXT *Nvme,\r
+ IN VOID *Buffer\r
+ )\r
+{\r
+ NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket;\r
+ NVM_EXPRESS_COMMAND Command;\r
+ NVM_EXPRESS_RESPONSE Response;\r
+ EFI_STATUS Status;\r
+\r
+ ZeroMem (&CommandPacket, sizeof(NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));\r
+ ZeroMem (&Command, sizeof(NVM_EXPRESS_COMMAND));\r
+ ZeroMem (&Response, sizeof(NVM_EXPRESS_RESPONSE));\r
+\r
+ Command.Cdw0.Opcode = NVME_ADMIN_IDENTIFY_OPC;\r
+ Command.Cdw0.Cid = Nvme->Cid[NVME_ADMIN_QUEUE]++;\r
+ //\r
+ // According to Nvm Express 1.1 spec Figure 38, When not used, the field shall be cleared to 0h.\r
+ // For the Identify command, the Namespace Identifier is only used for the Namespace Data structure.\r
+ //\r
+ Command.Nsid = 0;\r
+\r
+ CommandPacket.NvmeCmd = &Command;\r
+ CommandPacket.NvmeResponse = &Response;\r
+ CommandPacket.TransferBuffer = (UINT64)(UINTN)Buffer;\r
+ CommandPacket.TransferLength = sizeof (NVME_ADMIN_CONTROLLER_DATA);\r
+ CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;\r
+ CommandPacket.QueueId = NVME_ADMIN_QUEUE;\r
+ //\r
+ // Set bit 0 (Cns bit) to 1 to identify a controller\r
+ //\r
+ Command.Cdw10 = 1;\r
+ Command.Flags = CDW10_VALID;\r
+\r
+ Status = NvmePassThru (\r
+ Nvme,\r
+ NVME_CONTROLLER_ID,\r
+ 0,\r
+ &CommandPacket\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ Status = NvmeWaitAllComplete (Nvme, CommandPacket.QueueId);\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Get specified identify namespace Data.\r
+\r
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.\r
+ @param[in] NamespaceId - The specified namespace identifier.\r
+ @param[in] Buffer - The Buffer used to store the identify namespace Data.\r
+\r
+ @return EFI_SUCCESS - Successfully get the identify namespace Data.\r
+ @return others - Fail to get the identify namespace Data.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+NvmeIdentifyNamespace (\r
+ IN NVME_CONTEXT *Nvme,\r
+ IN UINT32 NamespaceId,\r
+ IN VOID *Buffer\r
+ )\r
+{\r
+ NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket;\r
+ NVM_EXPRESS_COMMAND Command;\r
+ NVM_EXPRESS_RESPONSE Response;\r
+ EFI_STATUS Status;\r
+\r
+ ZeroMem (&CommandPacket, sizeof(NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));\r
+ ZeroMem (&Command, sizeof(NVM_EXPRESS_COMMAND));\r
+ ZeroMem (&Response, sizeof(NVM_EXPRESS_RESPONSE));\r
+\r
+ CommandPacket.NvmeCmd = &Command;\r
+ CommandPacket.NvmeResponse = &Response;\r
+\r
+ Command.Cdw0.Opcode = NVME_ADMIN_IDENTIFY_OPC;\r
+ Command.Cdw0.Cid = Nvme->Cid[NVME_ADMIN_QUEUE]++;\r
+ Command.Nsid = NamespaceId;\r
+ CommandPacket.TransferBuffer = (UINT64)(UINTN)Buffer;\r
+ CommandPacket.TransferLength = sizeof (NVME_ADMIN_NAMESPACE_DATA);\r
+ CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;\r
+ CommandPacket.QueueId = NVME_ADMIN_QUEUE;\r
+ //\r
+ // Set bit 0 (Cns bit) to 1 to identify a namespace\r
+ //\r
+ CommandPacket.NvmeCmd->Cdw10 = 0;\r
+ CommandPacket.NvmeCmd->Flags = CDW10_VALID;\r
+\r
+ Status = NvmePassThru (\r
+ Nvme,\r
+ NamespaceId,\r
+ 0,\r
+ &CommandPacket\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ Status = NvmeWaitAllComplete (Nvme, CommandPacket.QueueId);\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Get Block Size for specific namespace of NVME.\r
+\r
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.\r
+\r
+ @return - Block Size in bytes\r
+\r
+**/\r
+STATIC\r
+UINT32\r
+NvmeGetBlockSize (\r
+ IN NVME_CONTEXT *Nvme\r
+ )\r
+{\r
+ UINT32 BlockSize;\r
+ UINT32 Lbads;\r
+ UINT32 Flbas;\r
+ UINT32 LbaFmtIdx;\r
+\r
+ Flbas = Nvme->NamespaceData->Flbas;\r
+ LbaFmtIdx = Flbas & 3;\r
+ Lbads = Nvme->NamespaceData->LbaFormat[LbaFmtIdx].Lbads;\r
+\r
+ BlockSize = (UINT32)1 << Lbads;\r
+ return BlockSize;\r
+}\r
+\r
+/**\r
+ Get last LBA for specific namespace of NVME.\r
+\r
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.\r
+\r
+ @return - Last LBA address\r
+\r
+**/\r
+STATIC\r
+EFI_LBA\r
+NvmeGetLastLba (\r
+ IN NVME_CONTEXT *Nvme\r
+ )\r
+{\r
+ EFI_LBA LastBlock;\r
+ LastBlock = Nvme->NamespaceData->Nsze - 1;\r
+ return LastBlock;\r
+}\r
+\r
+/**\r
+ Create io completion queue.\r
+\r
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.\r
+\r
+ @return EFI_SUCCESS - Successfully create io completion queue.\r
+ @return others - Fail to create io completion queue.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+NvmeCreateIoCompletionQueue (\r
+ IN NVME_CONTEXT *Nvme\r
+ )\r
+{\r
+ NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket;\r
+ NVM_EXPRESS_COMMAND Command;\r
+ NVM_EXPRESS_RESPONSE Response;\r
+ EFI_STATUS Status;\r
+ NVME_ADMIN_CRIOCQ CrIoCq;\r
+\r
+ ZeroMem (&CommandPacket, sizeof(NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));\r
+ ZeroMem (&Command, sizeof(NVM_EXPRESS_COMMAND));\r
+ ZeroMem (&Response, sizeof(NVM_EXPRESS_RESPONSE));\r
+ ZeroMem (&CrIoCq, sizeof(NVME_ADMIN_CRIOCQ));\r
+\r
+ CommandPacket.NvmeCmd = &Command;\r
+ CommandPacket.NvmeResponse = &Response;\r
+\r
+ Command.Cdw0.Opcode = NVME_ADMIN_CRIOCQ_OPC;\r
+ Command.Cdw0.Cid = Nvme->Cid[NVME_ADMIN_QUEUE]++;\r
+ CommandPacket.TransferBuffer = (UINT64)(UINTN)Nvme->CqBuffer[NVME_IO_QUEUE];\r
+ CommandPacket.TransferLength = EFI_PAGE_SIZE;\r
+ CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;\r
+ CommandPacket.QueueId = NVME_ADMIN_QUEUE;\r
+\r
+ CrIoCq.Qid = NVME_IO_QUEUE;\r
+ CrIoCq.Qsize = NVME_CCQ_SIZE;\r
+ CrIoCq.Pc = 1;\r
+ CopyMem (&CommandPacket.NvmeCmd->Cdw10, &CrIoCq, sizeof (NVME_ADMIN_CRIOCQ));\r
+ CommandPacket.NvmeCmd->Flags = CDW10_VALID | CDW11_VALID;\r
+\r
+ Status = NvmePassThru (\r
+ Nvme,\r
+ NVME_CONTROLLER_ID,\r
+ 0,\r
+ &CommandPacket\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ Status = NvmeWaitAllComplete (Nvme, CommandPacket.QueueId);\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Create io submission queue.\r
+\r
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.\r
+\r
+ @return EFI_SUCCESS - Successfully create io submission queue.\r
+ @return others - Fail to create io submission queue.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+NvmeCreateIoSubmissionQueue (\r
+ IN NVME_CONTEXT *Nvme\r
+ )\r
+{\r
+ NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket;\r
+ NVM_EXPRESS_COMMAND Command;\r
+ NVM_EXPRESS_RESPONSE Response;\r
+ EFI_STATUS Status;\r
+ NVME_ADMIN_CRIOSQ CrIoSq;\r
+\r
+ ZeroMem (&CommandPacket, sizeof(NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));\r
+ ZeroMem (&Command, sizeof(NVM_EXPRESS_COMMAND));\r
+ ZeroMem (&Response, sizeof(NVM_EXPRESS_RESPONSE));\r
+ ZeroMem (&CrIoSq, sizeof(NVME_ADMIN_CRIOSQ));\r
+\r
+ CommandPacket.NvmeCmd = &Command;\r
+ CommandPacket.NvmeResponse = &Response;\r
+\r
+ Command.Cdw0.Opcode = NVME_ADMIN_CRIOSQ_OPC;\r
+ Command.Cdw0.Cid = Nvme->Cid[NVME_ADMIN_QUEUE]++;\r
+ CommandPacket.TransferBuffer = (UINT64)(UINTN)Nvme->SqBuffer[NVME_IO_QUEUE];\r
+ CommandPacket.TransferLength = EFI_PAGE_SIZE;\r
+ CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;\r
+ CommandPacket.QueueId = NVME_ADMIN_QUEUE;\r
+\r
+ CrIoSq.Qid = NVME_IO_QUEUE;\r
+ CrIoSq.Qsize = NVME_CSQ_SIZE;\r
+ CrIoSq.Pc = 1;\r
+ CrIoSq.Cqid = NVME_IO_QUEUE;\r
+ CrIoSq.Qprio = 0;\r
+ CopyMem (&CommandPacket.NvmeCmd->Cdw10, &CrIoSq, sizeof (NVME_ADMIN_CRIOSQ));\r
+ CommandPacket.NvmeCmd->Flags = CDW10_VALID | CDW11_VALID;\r
+\r
+ Status = NvmePassThru (\r
+ Nvme,\r
+ NVME_CONTROLLER_ID,\r
+ 0,\r
+ &CommandPacket\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ Status = NvmeWaitAllComplete (Nvme, CommandPacket.QueueId);\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Security send and receive commands.\r
+\r
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.\r
+ @param[in] SendCommand - The flag to indicate the command type, TRUE for Send command and FALSE for receive command\r
+ @param[in] SecurityProtocol - Security Protocol\r
+ @param[in] SpSpecific - Security Protocol Specific\r
+ @param[in] TransferLength - Transfer Length of Buffer (in bytes) - always a multiple of 512\r
+ @param[in,out] TransferBuffer - Address of Data to transfer\r
+\r
+ @return EFI_SUCCESS - Successfully create io submission queue.\r
+ @return others - Fail to send/receive commands.\r
+\r
+**/\r
+EFI_STATUS\r
+NvmeSecuritySendReceive (\r
+ IN NVME_CONTEXT *Nvme,\r
+ IN BOOLEAN SendCommand,\r
+ IN UINT8 SecurityProtocol,\r
+ IN UINT16 SpSpecific,\r
+ IN UINTN TransferLength,\r
+ IN OUT VOID *TransferBuffer\r
+ )\r
+{\r
+ NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket;\r
+ NVM_EXPRESS_COMMAND Command;\r
+ NVM_EXPRESS_RESPONSE Response;\r
+ EFI_STATUS Status;\r
+ NVME_ADMIN_SECSEND SecSend;\r
+ OACS *Oacs;\r
+ UINT8 Opcode;\r
+ VOID* *SecBuff;\r
+\r
+ Oacs = (OACS *)&Nvme->ControllerData->Oacs;\r
+\r
+ //\r
+ // Verify security bit for Security Send/Receive commands\r
+ //\r
+ if (Oacs->Security == 0) {\r
+ DEBUG ((DEBUG_ERROR, "Security command doesn't support.\n"));\r
+ return EFI_NOT_READY;\r
+ }\r
+\r
+ SecBuff = (VOID *)(UINTN) NVME_SEC_BASE (Nvme);\r
+\r
+ //\r
+ // Actions for sending security command\r
+ //\r
+ if (SendCommand) {\r
+#if (EN_NVME_VERBOSE_DBINFO == ON)\r
+ //DumpMem (TransferBuffer, TransferLength);\r
+#endif\r
+ CopyMem (SecBuff, TransferBuffer, TransferLength);\r
+ }\r
+\r
+ ZeroMem (&CommandPacket, sizeof(NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));\r
+ ZeroMem (&Command, sizeof(NVM_EXPRESS_COMMAND));\r
+ ZeroMem (&Response, sizeof(NVM_EXPRESS_RESPONSE));\r
+ ZeroMem (&SecSend, sizeof(NVME_ADMIN_SECSEND));\r
+\r
+ CommandPacket.NvmeCmd = &Command;\r
+ CommandPacket.NvmeResponse = &Response;\r
+\r
+ Opcode = (UINT8)(SendCommand ? NVME_ADMIN_SECURITY_SEND_OPC : NVME_ADMIN_SECURITY_RECV_OPC);\r
+ Command.Cdw0.Opcode = Opcode;\r
+ Command.Cdw0.Cid = Nvme->Cid[NVME_ADMIN_QUEUE]++;\r
+ CommandPacket.TransferBuffer = (UINT64)(UINTN)SecBuff;\r
+ CommandPacket.TransferLength = (UINT32)TransferLength;\r
+ CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;\r
+ CommandPacket.QueueId = NVME_ADMIN_QUEUE;\r
+\r
+ SecSend.Spsp = SpSpecific;\r
+ SecSend.Secp = SecurityProtocol;\r
+ SecSend.Tl = (UINT32)TransferLength;\r
+\r
+ CopyMem (&CommandPacket.NvmeCmd->Cdw10, &SecSend, sizeof (NVME_ADMIN_SECSEND));\r
+ CommandPacket.NvmeCmd->Flags = CDW10_VALID | CDW11_VALID;\r
+\r
+ Status = NvmePassThru (\r
+ Nvme,\r
+ NVME_CONTROLLER_ID,\r
+ 0,\r
+ &CommandPacket\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ Status = NvmeWaitAllComplete (Nvme, CommandPacket.QueueId);\r
+ }\r
+\r
+ //\r
+ // Actions for receiving security command\r
+ //\r
+ if (!SendCommand) {\r
+ CopyMem (TransferBuffer, SecBuff, TransferLength);\r
+#if (EN_NVME_VERBOSE_DBINFO == ON)\r
+ //DumpMem (TransferBuffer, TransferLength);\r
+#endif\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Destroy io completion queue.\r
+\r
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.\r
+\r
+ @return EFI_SUCCESS - Successfully destroy io completion queue.\r
+ @return others - Fail to destroy io completion queue.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+NvmeDestroyIoCompletionQueue (\r
+ IN NVME_CONTEXT *Nvme\r
+ )\r
+{\r
+ NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket;\r
+ NVM_EXPRESS_COMMAND Command;\r
+ NVM_EXPRESS_RESPONSE Response;\r
+ EFI_STATUS Status;\r
+ NVME_ADMIN_DEIOCQ DelIoCq;\r
+\r
+ ZeroMem (&CommandPacket, sizeof(NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));\r
+ ZeroMem (&Command, sizeof(NVM_EXPRESS_COMMAND));\r
+ ZeroMem (&Response, sizeof(NVM_EXPRESS_RESPONSE));\r
+ ZeroMem (&DelIoCq, sizeof(NVME_ADMIN_DEIOCQ));\r
+\r
+ CommandPacket.NvmeCmd = &Command;\r
+ CommandPacket.NvmeResponse = &Response;\r
+\r
+ Command.Cdw0.Opcode = NVME_ADMIN_DELIOCQ_OPC;\r
+ Command.Cdw0.Cid = Nvme->Cid[NVME_ADMIN_QUEUE]++;\r
+ CommandPacket.TransferBuffer = (UINT64)(UINTN)Nvme->CqBuffer[NVME_IO_QUEUE];\r
+ CommandPacket.TransferLength = EFI_PAGE_SIZE;\r
+ CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;\r
+ CommandPacket.QueueId = NVME_ADMIN_QUEUE;\r
+\r
+ DelIoCq.Qid = NVME_IO_QUEUE;\r
+ CopyMem (&CommandPacket.NvmeCmd->Cdw10, &DelIoCq, sizeof (NVME_ADMIN_DEIOCQ));\r
+ CommandPacket.NvmeCmd->Flags = CDW10_VALID | CDW11_VALID;\r
+\r
+ Status = NvmePassThru (\r
+ Nvme,\r
+ NVME_CONTROLLER_ID,\r
+ 0,\r
+ &CommandPacket\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ Status = NvmeWaitAllComplete (Nvme, CommandPacket.QueueId);\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Destroy io submission queue.\r
+\r
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.\r
+\r
+ @return EFI_SUCCESS - Successfully destroy io submission queue.\r
+ @return others - Fail to destroy io submission queue.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+NvmeDestroyIoSubmissionQueue (\r
+ IN NVME_CONTEXT *Nvme\r
+ )\r
+{\r
+ NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket;\r
+ NVM_EXPRESS_COMMAND Command;\r
+ NVM_EXPRESS_RESPONSE Response;\r
+ EFI_STATUS Status;\r
+ NVME_ADMIN_DEIOSQ DelIoSq;\r
+\r
+ ZeroMem (&CommandPacket, sizeof(NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));\r
+ ZeroMem (&Command, sizeof(NVM_EXPRESS_COMMAND));\r
+ ZeroMem (&Response, sizeof(NVM_EXPRESS_RESPONSE));\r
+ ZeroMem (&DelIoSq, sizeof(NVME_ADMIN_DEIOSQ));\r
+\r
+ CommandPacket.NvmeCmd = &Command;\r
+ CommandPacket.NvmeResponse = &Response;\r
+\r
+ Command.Cdw0.Opcode = NVME_ADMIN_DELIOSQ_OPC;\r
+ Command.Cdw0.Cid = Nvme->Cid[NVME_ADMIN_QUEUE]++;\r
+ CommandPacket.TransferBuffer = (UINT64)(UINTN)Nvme->SqBuffer[NVME_IO_QUEUE];\r
+ CommandPacket.TransferLength = EFI_PAGE_SIZE;\r
+ CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;\r
+ CommandPacket.QueueId = NVME_ADMIN_QUEUE;\r
+\r
+ DelIoSq.Qid = NVME_IO_QUEUE;\r
+ CopyMem (&CommandPacket.NvmeCmd->Cdw10, &DelIoSq, sizeof (NVME_ADMIN_DEIOSQ));\r
+ CommandPacket.NvmeCmd->Flags = CDW10_VALID | CDW11_VALID;\r
+\r
+ Status = NvmePassThru (\r
+ Nvme,\r
+ NVME_CONTROLLER_ID,\r
+ 0,\r
+ &CommandPacket\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ Status = NvmeWaitAllComplete (Nvme, CommandPacket.QueueId);\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Allocate transfer-related Data struct which is used at Nvme.\r
+\r
+ @param[in] ImageHandle Image handle for this driver image\r
+ @param[in] Nvme The pointer to the NVME_CONTEXT Data structure.\r
+\r
+ @retval EFI_OUT_OF_RESOURCE The allocation is failure.\r
+ @retval EFI_SUCCESS Successful to allocate memory.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+NvmeAllocateResource (\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN NVME_CONTEXT *Nvme\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_PHYSICAL_ADDRESS Addr;\r
+ UINT32 Size;\r
+\r
+ //\r
+ // Allocate resources required by NVMe host controller.\r
+ //\r
+ // NBAR\r
+ Size = 0x10000;\r
+ Addr = 0xFFFFFFFF;\r
+ Status = gDS->AllocateMemorySpace (\r
+ EfiGcdAllocateMaxAddressSearchBottomUp,\r
+ EfiGcdMemoryTypeMemoryMappedIo,\r
+ 15, // 2^15: 32K Alignment\r
+ Size,\r
+ &Addr,\r
+ ImageHandle,\r
+ NULL\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ Nvme->Nbar = (UINT32) Addr;\r
+\r
+ // DMA Buffer\r
+ Size = NVME_MEM_MAX_SIZE;\r
+ Addr = 0xFFFFFFFF;\r
+ Status = gBS->AllocatePages (\r
+ AllocateMaxAddress,\r
+ EfiACPIMemoryNVS,\r
+ EFI_SIZE_TO_PAGES (Size),\r
+ (EFI_PHYSICAL_ADDRESS *)&Addr\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ Nvme->BaseMem = (UINT32) Addr;\r
+\r
+ // Clean up DMA Buffer before using\r
+ ZeroMem ((VOID *)(UINTN)Addr, NVME_MEM_MAX_SIZE);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Free allocated transfer-related Data struct which is used at NVMe.\r
+\r
+ @param[in] Nvme The pointer to the NVME_CONTEXT Data structure.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+NvmeFreeResource (\r
+ IN NVME_CONTEXT *Nvme\r
+ )\r
+{\r
+ UINT32 Size;\r
+\r
+ // NBAR\r
+ if (Nvme->BaseMem != 0) {\r
+ Size = 0x10000;\r
+ gDS->FreeMemorySpace (Nvme->Nbar, Size);\r
+ }\r
+\r
+ // DMA Buffer\r
+ if (Nvme->Nbar != 0) {\r
+ Size = NVME_MEM_MAX_SIZE;\r
+ gBS->FreePages ((EFI_PHYSICAL_ADDRESS)(UINTN) Nvme->Nbar, EFI_SIZE_TO_PAGES (Size));\r
+ }\r
+}\r
+\r
+\r
+/**\r
+ Initialize the Nvm Express controller.\r
+\r
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.\r
+\r
+ @retval EFI_SUCCESS - The NVM Express Controller is initialized successfully.\r
+ @retval Others - A device error occurred while initializing the controller.\r
+\r
+**/\r
+EFI_STATUS\r
+NvmeControllerInit (\r
+ IN NVME_CONTEXT *Nvme\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ NVME_AQA Aqa;\r
+ NVME_ASQ Asq;\r
+ NVME_ACQ Acq;\r
+ NVME_VER Ver;\r
+\r
+ UINT32 MlBAR;\r
+ UINT32 MuBAR;\r
+\r
+ ///\r
+ /// Update PCIE BAR0/1 for NVME device\r
+ ///\r
+ MlBAR = Nvme->Nbar;\r
+ MuBAR = 0;\r
+ PciWrite32 (Nvme->PciBase + 0x10, MlBAR); // MLBAR (BAR0)\r
+ PciWrite32 (Nvme->PciBase + 0x14, MuBAR); // MUBAR (BAR1)\r
+\r
+ ///\r
+ /// Enable PCIE decode\r
+ ///\r
+ PciWrite8 (Nvme->PciBase + NVME_PCIE_PCICMD, 0x6);\r
+\r
+ // Version\r
+ NVME_GET_VER (Nvme, &Ver);\r
+ if (!(Ver.Mjr == 0x0001) && (Ver.Mnr == 0x0000)) {\r
+ DEBUG ((DEBUG_INFO, "\n!!!\n!!! NVME Version mismatch for the implementation !!!\n!!!\n"));\r
+ }\r
+\r
+ ///\r
+ /// Read the Controller Capabilities register and verify that the NVM command set is supported\r
+ ///\r
+ Status = NVME_GET_CAP (Nvme, &Nvme->Cap);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((DEBUG_ERROR, "NVME_GET_CAP fail, Status: %r\n", Status));\r
+ goto Done;\r
+ }\r
+\r
+ if (Nvme->Cap.Css != 0x01) {\r
+ DEBUG ((DEBUG_ERROR, "NvmeControllerInit fail: the controller doesn't support NVMe command set\n"));\r
+ Status = EFI_UNSUPPORTED;\r
+ goto Done;\r
+ }\r
+\r
+ ///\r
+ /// Currently the driver only supports 4k page Size.\r
+ ///\r
+ if ((Nvme->Cap.Mpsmin + 12) > EFI_PAGE_SHIFT) {\r
+ DEBUG ((DEBUG_ERROR, "NvmeControllerInit fail: only supports 4k page Size\n"));\r
+ ASSERT (FALSE);\r
+ Status = EFI_UNSUPPORTED;\r
+ goto Done;\r
+ }\r
+\r
+ Nvme->Cid[0] = 0;\r
+ Nvme->Cid[1] = 0;\r
+\r
+ Status = NvmeDisableController (Nvme);\r
+ if (EFI_ERROR(Status)) {\r
+ DEBUG ((DEBUG_ERROR, "NvmeDisableController fail, Status: %r\n", Status));\r
+ goto Done;\r
+ }\r
+\r
+ ///\r
+ /// set number of entries admin submission & completion queues.\r
+ ///\r
+ Aqa.Asqs = NVME_ASQ_SIZE;\r
+ Aqa.Rsvd1 = 0;\r
+ Aqa.Acqs = NVME_ACQ_SIZE;\r
+ Aqa.Rsvd2 = 0;\r
+\r
+ ///\r
+ /// Address of admin submission queue.\r
+ ///\r
+ Asq = (UINT64)(UINTN)(NVME_ASQ_BASE (Nvme) & ~0xFFF);\r
+\r
+ ///\r
+ /// Address of admin completion queue.\r
+ ///\r
+ Acq = (UINT64)(UINTN)(NVME_ACQ_BASE (Nvme) & ~0xFFF);\r
+\r
+ ///\r
+ /// Address of I/O submission & completion queue.\r
+ ///\r
+ Nvme->SqBuffer[0] = (NVME_SQ *)(UINTN)NVME_ASQ_BASE (Nvme); // NVME_ADMIN_QUEUE\r
+ Nvme->CqBuffer[0] = (NVME_CQ *)(UINTN)NVME_ACQ_BASE (Nvme); // NVME_ADMIN_QUEUE\r
+ Nvme->SqBuffer[1] = (NVME_SQ *)(UINTN)NVME_SQ_BASE (Nvme, 0); // NVME_IO_QUEUE\r
+ Nvme->CqBuffer[1] = (NVME_CQ *)(UINTN)NVME_CQ_BASE (Nvme, 0); // NVME_IO_QUEUE\r
+\r
+ DEBUG ((DEBUG_INFO, "BaseMem = [%08X]\n", Nvme->BaseMem));\r
+ DEBUG ((DEBUG_INFO, "Admin Submission Queue Size (Aqa.Asqs) = [%08X]\n", Aqa.Asqs));\r
+ DEBUG ((DEBUG_INFO, "Admin Completion Queue Size (Aqa.Acqs) = [%08X]\n", Aqa.Acqs));\r
+ DEBUG ((DEBUG_INFO, "Admin Submission Queue (SqBuffer[0]) = [%08X]\n", Nvme->SqBuffer[0]));\r
+ DEBUG ((DEBUG_INFO, "Admin Completion Queue (CqBuffer[0]) = [%08X]\n", Nvme->CqBuffer[0]));\r
+ DEBUG ((DEBUG_INFO, "I/O Submission Queue (SqBuffer[1]) = [%08X]\n", Nvme->SqBuffer[1]));\r
+ DEBUG ((DEBUG_INFO, "I/O Completion Queue (CqBuffer[1]) = [%08X]\n", Nvme->CqBuffer[1]));\r
+\r
+ ///\r
+ /// Program admin queue attributes.\r
+ ///\r
+ Status = NVME_SET_AQA (Nvme, &Aqa);\r
+ if (EFI_ERROR(Status)) {\r
+ goto Done;\r
+ }\r
+\r
+ ///\r
+ /// Program admin submission queue address.\r
+ ///\r
+ Status = NVME_SET_ASQ (Nvme, &Asq);\r
+ if (EFI_ERROR(Status)) {\r
+ goto Done;\r
+ }\r
+\r
+ ///\r
+ /// Program admin completion queue address.\r
+ ///\r
+ Status = NVME_SET_ACQ (Nvme, &Acq);\r
+ if (EFI_ERROR(Status)) {\r
+ goto Done;\r
+ }\r
+\r
+ Status = NvmeEnableController (Nvme);\r
+ if (EFI_ERROR(Status)) {\r
+ goto Done;\r
+ }\r
+\r
+ ///\r
+ /// Create one I/O completion queue.\r
+ ///\r
+ Status = NvmeCreateIoCompletionQueue (Nvme);\r
+ if (EFI_ERROR(Status)) {\r
+ goto Done;\r
+ }\r
+\r
+ ///\r
+ /// Create one I/O Submission queue.\r
+ ///\r
+ Status = NvmeCreateIoSubmissionQueue (Nvme);\r
+ if (EFI_ERROR(Status)) {\r
+ goto Done;\r
+ }\r
+\r
+ ///\r
+ /// Get current Identify Controller Data\r
+ ///\r
+ Nvme->ControllerData = (NVME_ADMIN_CONTROLLER_DATA *)(UINTN) NVME_CONTROL_DATA_BASE (Nvme);\r
+ Status = NvmeIdentifyController (Nvme, Nvme->ControllerData);\r
+ if (EFI_ERROR(Status)) {\r
+ goto Done;\r
+ }\r
+\r
+ ///\r
+ /// Dump NvmExpress Identify Controller Data\r
+ ///\r
+ Nvme->ControllerData->Sn[19] = 0;\r
+ Nvme->ControllerData->Mn[39] = 0;\r
+ //NvmeDumpIdentifyController (Nvme->ControllerData);\r
+\r
+ ///\r
+ /// Get current Identify Namespace Data\r
+ ///\r
+ Nvme->NamespaceData = (NVME_ADMIN_NAMESPACE_DATA *)NVME_NAMESPACE_DATA_BASE (Nvme);\r
+ Status = NvmeIdentifyNamespace (Nvme, Nvme->Nsid, Nvme->NamespaceData);\r
+ if (EFI_ERROR(Status)) {\r
+ DEBUG ((DEBUG_ERROR, "NvmeIdentifyNamespace fail, Status = %r\n", Status));\r
+ goto Done;\r
+ }\r
+\r
+ ///\r
+ /// Dump NvmExpress Identify Namespace Data\r
+ ///\r
+ if (Nvme->NamespaceData->Ncap == 0) {\r
+ DEBUG ((DEBUG_ERROR, "Invalid Namespace, Ncap: %lx\n", Nvme->NamespaceData->Ncap));\r
+ Status = EFI_DEVICE_ERROR;\r
+ goto Done;\r
+ }\r
+\r
+ Nvme->BlockSize = NvmeGetBlockSize (Nvme);\r
+ Nvme->LastBlock = NvmeGetLastLba (Nvme);\r
+\r
+ Nvme->State = NvmeStatusInit;\r
+\r
+ return EFI_SUCCESS;\r
+\r
+Done:\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Un-initialize the Nvm Express controller.\r
+\r
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.\r
+\r
+ @retval EFI_SUCCESS - The NVM Express Controller is un-initialized successfully.\r
+ @retval Others - A device error occurred while un-initializing the controller.\r
+\r
+**/\r
+EFI_STATUS\r
+NvmeControllerExit (\r
+ IN NVME_CONTEXT *Nvme\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ Status = EFI_SUCCESS;\r
+ if (Nvme->State == NvmeStatusInit || Nvme->State == NvmeStatusMax) {\r
+ ///\r
+ /// Destroy I/O Submission queue.\r
+ ///\r
+ Status = NvmeDestroyIoSubmissionQueue (Nvme);\r
+ if (EFI_ERROR(Status)) {\r
+ DEBUG ((DEBUG_ERROR, "NvmeDestroyIoSubmissionQueue fail, Status = %r\n", Status));\r
+ return Status;\r
+ }\r
+\r
+ ///\r
+ /// Destroy I/O completion queue.\r
+ ///\r
+ Status = NvmeDestroyIoCompletionQueue (Nvme);\r
+ if (EFI_ERROR(Status)) {\r
+ DEBUG ((DEBUG_ERROR, "NvmeDestroyIoCompletionQueue fail, Status = %r\n", Status));\r
+ return Status;\r
+ }\r
+\r
+ Status = NvmeShutdownController (Nvme);\r
+ if (EFI_ERROR(Status)) {\r
+ DEBUG ((DEBUG_ERROR, "NvmeShutdownController fail, Status: %r\n", Status));\r
+ }\r
+ }\r
+\r
+ ///\r
+ /// Disable PCIE decode\r
+ ///\r
+ PciWrite8 (Nvme->PciBase + NVME_PCIE_PCICMD, 0x0);\r
+ PciWrite32 (Nvme->PciBase + 0x10, 0); // MLBAR (BAR0)\r
+ PciWrite32 (Nvme->PciBase + 0x14, 0); // MUBAR (BAR1)\r
+\r
+ Nvme->State = NvmeStatusUnknown;\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Read sector Data from the NVMe device.\r
+\r
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.\r
+ @param[in,out] Buffer - The Buffer used to store the Data read from the device.\r
+ @param[in] Lba - The start block number.\r
+ @param[in] Blocks - Total block number to be read.\r
+\r
+ @retval EFI_SUCCESS - Datum are read from the device.\r
+ @retval Others - Fail to read all the datum.\r
+\r
+**/\r
+EFI_STATUS\r
+NvmeReadSectors (\r
+ IN NVME_CONTEXT *Nvme,\r
+ IN OUT UINT64 Buffer,\r
+ IN UINT64 Lba,\r
+ IN UINT32 Blocks\r
+ )\r
+{\r
+ UINT32 Bytes;\r
+ NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket;\r
+ NVM_EXPRESS_COMMAND Command;\r
+ NVM_EXPRESS_RESPONSE Response;\r
+ EFI_STATUS Status;\r
+ UINT32 BlockSize;\r
+\r
+ BlockSize = Nvme->BlockSize;\r
+ Bytes = Blocks * BlockSize;\r
+\r
+ ZeroMem (&CommandPacket, sizeof(NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));\r
+ ZeroMem (&Command, sizeof(NVM_EXPRESS_COMMAND));\r
+ ZeroMem (&Response, sizeof(NVM_EXPRESS_RESPONSE));\r
+\r
+ CommandPacket.NvmeCmd = &Command;\r
+ CommandPacket.NvmeResponse = &Response;\r
+\r
+ CommandPacket.NvmeCmd->Cdw0.Opcode = NVME_IO_READ_OPC;\r
+ CommandPacket.NvmeCmd->Cdw0.Cid = Nvme->Cid[NVME_IO_QUEUE]++;\r
+ CommandPacket.NvmeCmd->Nsid = Nvme->Nsid;\r
+ CommandPacket.TransferBuffer = Buffer;\r
+\r
+ CommandPacket.TransferLength = Bytes;\r
+ CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;\r
+ CommandPacket.QueueId = NVME_IO_QUEUE;\r
+\r
+ CommandPacket.NvmeCmd->Cdw10 = (UINT32)Lba;\r
+ CommandPacket.NvmeCmd->Cdw11 = (UINT32)(RShiftU64 (Lba, 32));\r
+ CommandPacket.NvmeCmd->Cdw12 = (Blocks - 1) & 0xFFFF;\r
+\r
+ CommandPacket.NvmeCmd->Flags = CDW10_VALID | CDW11_VALID | CDW12_VALID;\r
+\r
+ Status = NvmePassThru (\r
+ Nvme,\r
+ Nvme->Nsid,\r
+ 0,\r
+ &CommandPacket\r
+ );\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Write sector Data to the NVMe device.\r
+\r
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.\r
+ @param[in] Buffer - The Buffer to be written into the device.\r
+ @param[in] Lba - The start block number.\r
+ @param[in] Blocks - Total block number to be written.\r
+\r
+ @retval EFI_SUCCESS - Datum are written into the Buffer.\r
+ @retval Others - Fail to write all the datum.\r
+\r
+**/\r
+EFI_STATUS\r
+NvmeWriteSectors (\r
+ IN NVME_CONTEXT *Nvme,\r
+ IN UINT64 Buffer,\r
+ IN UINT64 Lba,\r
+ IN UINT32 Blocks\r
+ )\r
+{\r
+ NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket;\r
+ NVM_EXPRESS_COMMAND Command;\r
+ NVM_EXPRESS_RESPONSE Response;\r
+ EFI_STATUS Status;\r
+ UINT32 Bytes;\r
+ UINT32 BlockSize;\r
+\r
+ BlockSize = Nvme->BlockSize;\r
+ Bytes = Blocks * BlockSize;\r
+\r
+ ZeroMem (&CommandPacket, sizeof(NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));\r
+ ZeroMem (&Command, sizeof(NVM_EXPRESS_COMMAND));\r
+ ZeroMem (&Response, sizeof(NVM_EXPRESS_RESPONSE));\r
+\r
+ CommandPacket.NvmeCmd = &Command;\r
+ CommandPacket.NvmeResponse = &Response;\r
+\r
+ CommandPacket.NvmeCmd->Cdw0.Opcode = NVME_IO_WRITE_OPC;\r
+ CommandPacket.NvmeCmd->Cdw0.Cid = Nvme->Cid[NVME_IO_QUEUE]++;\r
+ CommandPacket.NvmeCmd->Nsid = Nvme->Nsid;\r
+ CommandPacket.TransferBuffer = Buffer;\r
+\r
+ CommandPacket.TransferLength = Bytes;\r
+ CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;\r
+ CommandPacket.QueueId = NVME_IO_QUEUE;\r
+\r
+ CommandPacket.NvmeCmd->Cdw10 = (UINT32)Lba;\r
+ CommandPacket.NvmeCmd->Cdw11 = (UINT32)(RShiftU64 (Lba, 32));\r
+ CommandPacket.NvmeCmd->Cdw12 = (Blocks - 1) & 0xFFFF;\r
+\r
+ CommandPacket.MetadataBuffer = (UINT64)(UINTN)NULL;\r
+ CommandPacket.MetadataLength = 0;\r
+\r
+ CommandPacket.NvmeCmd->Flags = CDW10_VALID | CDW11_VALID | CDW12_VALID;\r
+\r
+ Status = NvmePassThru (\r
+ Nvme,\r
+ Nvme->Nsid,\r
+ 0,\r
+ &CommandPacket\r
+ );\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Flushes all modified Data to the device.\r
+\r
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.\r
+\r
+ @retval EFI_SUCCESS - Datum are written into the Buffer.\r
+ @retval Others - Fail to write all the datum.\r
+\r
+**/\r
+EFI_STATUS\r
+NvmeFlush (\r
+ IN NVME_CONTEXT *Nvme\r
+ )\r
+{\r
+ NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket;\r
+ NVM_EXPRESS_COMMAND Command;\r
+ NVM_EXPRESS_RESPONSE Response;\r
+ EFI_STATUS Status;\r
+\r
+ ZeroMem (&CommandPacket, sizeof(NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));\r
+ ZeroMem (&Command, sizeof(NVM_EXPRESS_COMMAND));\r
+ ZeroMem (&Response, sizeof(NVM_EXPRESS_RESPONSE));\r
+\r
+ CommandPacket.NvmeCmd = &Command;\r
+ CommandPacket.NvmeResponse = &Response;\r
+\r
+ CommandPacket.NvmeCmd->Cdw0.Opcode = NVME_IO_FLUSH_OPC;\r
+ CommandPacket.NvmeCmd->Cdw0.Cid = Nvme->Cid[NVME_IO_QUEUE]++;\r
+ CommandPacket.NvmeCmd->Nsid = Nvme->Nsid;\r
+ CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;\r
+ CommandPacket.QueueId = NVME_IO_QUEUE;\r
+\r
+ Status = NvmePassThru (\r
+ Nvme,\r
+ Nvme->Nsid,\r
+ 0,\r
+ &CommandPacket\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ Status = NvmeWaitAllComplete (Nvme, CommandPacket.QueueId);\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Read some blocks from the device.\r
+\r
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.\r
+ @param[out] Buffer - The Buffer used to store the Data read from the device.\r
+ @param[in] Lba - The start block number.\r
+ @param[in] Blocks - Total block number to be read.\r
+\r
+ @retval EFI_SUCCESS - Datum are read from the device.\r
+ @retval Others - Fail to read all the datum.\r
+\r
+**/\r
+EFI_STATUS\r
+NvmeRead (\r
+ IN NVME_CONTEXT *Nvme,\r
+ OUT UINT64 Buffer,\r
+ IN UINT64 Lba,\r
+ IN UINTN Blocks\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT32 BlockSize;\r
+ UINT32 MaxTransferBlocks;\r
+\r
+ ASSERT (Blocks <= NVME_MAX_SECTORS);\r
+ Status = EFI_SUCCESS;\r
+ BlockSize = Nvme->BlockSize;\r
+ if (Nvme->ControllerData->Mdts != 0) {\r
+ MaxTransferBlocks = (1 << (Nvme->ControllerData->Mdts)) * (1 << (Nvme->Cap.Mpsmin + 12)) / BlockSize;\r
+ } else {\r
+ MaxTransferBlocks = 1024;\r
+ }\r
+\r
+ while (Blocks > 0) {\r
+ if (Blocks > MaxTransferBlocks) {\r
+ Status = NvmeReadSectors (Nvme, Buffer, Lba, MaxTransferBlocks);\r
+\r
+ Blocks -= MaxTransferBlocks;\r
+ Buffer += (MaxTransferBlocks * BlockSize);\r
+ Lba += MaxTransferBlocks;\r
+ } else {\r
+ Status = NvmeReadSectors (Nvme, Buffer, Lba, (UINT32) Blocks);\r
+ Blocks = 0;\r
+ }\r
+\r
+ if (EFI_ERROR(Status)) {\r
+ DEBUG ((DEBUG_ERROR, "NvmeRead fail, Status = %r\n", Status));\r
+ break;\r
+ }\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Write some blocks to the device.\r
+\r
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.\r
+ @param[in] Buffer - The Buffer to be written into the device.\r
+ @param[in] Lba - The start block number.\r
+ @param[in] Blocks - Total block number to be written.\r
+\r
+ @retval EFI_SUCCESS - Datum are written into the Buffer.\r
+ @retval Others - Fail to write all the datum.\r
+\r
+**/\r
+EFI_STATUS\r
+NvmeWrite (\r
+ IN NVME_CONTEXT *Nvme,\r
+ IN UINT64 Buffer,\r
+ IN UINT64 Lba,\r
+ IN UINTN Blocks\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT32 BlockSize;\r
+ UINT32 MaxTransferBlocks;\r
+\r
+ ASSERT (Blocks <= NVME_MAX_SECTORS);\r
+ Status = EFI_SUCCESS;\r
+ BlockSize = Nvme->BlockSize;\r
+\r
+ if (Nvme->ControllerData->Mdts != 0) {\r
+ MaxTransferBlocks = (1 << (Nvme->ControllerData->Mdts)) * (1 << (Nvme->Cap.Mpsmin + 12)) / BlockSize;\r
+ } else {\r
+ MaxTransferBlocks = 1024;\r
+ }\r
+\r
+ while (Blocks > 0) {\r
+ if (Blocks > MaxTransferBlocks) {\r
+ Status = NvmeWriteSectors (Nvme, Buffer, Lba, MaxTransferBlocks);\r
+\r
+ Blocks -= MaxTransferBlocks;\r
+ Buffer += (MaxTransferBlocks * BlockSize);\r
+ Lba += MaxTransferBlocks;\r
+ } else {\r
+ Status = NvmeWriteSectors (Nvme, Buffer, Lba, (UINT32) Blocks);\r
+ Blocks = 0;\r
+ }\r
+\r
+ if (EFI_ERROR(Status)) {\r
+ DEBUG ((DEBUG_ERROR, "NvmeWrite fail, Status = %r\n", Status));\r
+ break;\r
+ }\r
+ }\r
+\r
+ return Status;\r
+}\r