-/** @file\r
- Provide functions to initialize NVME controller and perform NVME commands\r
-\r
-Copyright (c) 2016 - 2017, 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) ((UINTN)(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
- ///\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
- 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
- }\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
- Nvme->Pt[0] = 0;\r
- Nvme->Pt[1] = 0;\r
-\r
- ZeroMem ((VOID *)(UINTN)(&(Nvme->SqTdbl[0])), sizeof (NVME_SQTDBL) * NVME_MAX_IO_QUEUES);\r
- ZeroMem ((VOID *)(UINTN)(&(Nvme->CqHdbl[0])), sizeof (NVME_CQHDBL) * NVME_MAX_IO_QUEUES);\r
-\r
- ZeroMem ((VOID *)(UINTN)Nvme->BaseMem, NVME_MEM_MAX_SIZE);\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