]> git.proxmox.com Git - mirror_edk2.git/commitdiff
SecurityPkg: OpalPasswordSmm: Add Opal password Smm driver.
authorEric Dong <eric.dong@intel.com>
Tue, 29 Mar 2016 06:49:25 +0000 (14:49 +0800)
committerFeng Tian <feng.tian@intel.com>
Tue, 29 Mar 2016 07:37:30 +0000 (15:37 +0800)
This driver used to unlock device in S3 resume phase.

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Eric Dong <eric.dong@intel.com>
Reviewed-by: Feng Tian <feng.tian@intel.com>
SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalAhciMode.c [new file with mode: 0644]
SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalAhciMode.h [new file with mode: 0644]
SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalIdeMode.c [new file with mode: 0644]
SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalIdeMode.h [new file with mode: 0644]
SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalNvmeMode.c [new file with mode: 0644]
SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalNvmeMode.h [new file with mode: 0644]
SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalNvmeReg.h [new file with mode: 0644]
SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalPasswordSmm.c [new file with mode: 0644]
SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalPasswordSmm.h [new file with mode: 0644]
SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalPasswordSmm.inf [new file with mode: 0644]

diff --git a/SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalAhciMode.c b/SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalAhciMode.c
new file mode 100644 (file)
index 0000000..0dc3490
--- /dev/null
@@ -0,0 +1,1267 @@
+/** @file\r
+  This driver is used for Opal Password Feature support at AHCI mode.\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
+\r
+#include "OpalPasswordSmm.h"\r
+\r
+/**\r
+  Start command for give slot on specific port.\r
+\r
+  @param  Port               The number of port.\r
+  @param  CommandSlot        The number of CommandSlot.\r
+  @param  Timeout            The timeout Value of start.\r
+\r
+  @retval EFI_DEVICE_ERROR   The command start unsuccessfully.\r
+  @retval EFI_TIMEOUT        The operation is time out.\r
+  @retval EFI_SUCCESS        The command start successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AhciStartCommand (\r
+  IN  UINT8                     Port,\r
+  IN  UINT8                     CommandSlot,\r
+  IN  UINT64                    Timeout\r
+  );\r
+\r
+/**\r
+  Stop command running for giving port\r
+\r
+  @param  Port               The number of port.\r
+  @param  Timeout            The timeout Value of stop.\r
+\r
+  @retval EFI_DEVICE_ERROR   The command stop unsuccessfully.\r
+  @retval EFI_TIMEOUT        The operation is time out.\r
+  @retval EFI_SUCCESS        The command stop successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AhciStopCommand (\r
+  IN  UINT8                     Port,\r
+  IN  UINT64                    Timeout\r
+  );\r
+\r
+/**\r
+  Read AHCI Operation register.\r
+\r
+  @param  Offset       The operation register offset.\r
+\r
+  @return The register content read.\r
+\r
+**/\r
+UINT32\r
+EFIAPI\r
+AhciReadReg (\r
+  IN  UINT32              Offset\r
+  )\r
+{\r
+  UINT32   Data;\r
+\r
+  Data = 0;\r
+\r
+  Data = MmioRead32 (mAhciBar + Offset);\r
+\r
+  return Data;\r
+}\r
+\r
+/**\r
+  Write AHCI Operation register.\r
+\r
+  @param  Offset       The operation register offset.\r
+  @param  Data         The Data used to write down.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+AhciWriteReg (\r
+  IN UINT32               Offset,\r
+  IN UINT32               Data\r
+  )\r
+{\r
+  MmioWrite32 (mAhciBar + Offset, Data);\r
+\r
+  return ;\r
+}\r
+\r
+/**\r
+  Do AND operation with the Value of AHCI Operation register.\r
+\r
+  @param  Offset       The operation register offset.\r
+  @param  AndData      The Data used to do AND operation.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+AhciAndReg (\r
+  IN UINT32               Offset,\r
+  IN UINT32               AndData\r
+  )\r
+{\r
+  UINT32 Data;\r
+\r
+  Data  = AhciReadReg (Offset);\r
+\r
+  Data &= AndData;\r
+\r
+  AhciWriteReg (Offset, Data);\r
+}\r
+\r
+/**\r
+  Do OR operation with the Value of AHCI Operation register.\r
+\r
+  @param  Offset       The operation register offset.\r
+  @param  OrData       The Data used to do OR operation.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+AhciOrReg (\r
+  IN UINT32               Offset,\r
+  IN UINT32               OrData\r
+  )\r
+{\r
+  UINT32 Data;\r
+\r
+  Data  = AhciReadReg (Offset);\r
+\r
+  Data |= OrData;\r
+\r
+  AhciWriteReg (Offset, Data);\r
+}\r
+\r
+/**\r
+  Wait for memory set to the test Value.\r
+\r
+  @param  Offset            The memory address to test.\r
+  @param  MaskValue         The mask Value of memory.\r
+  @param  TestValue         The test Value of memory.\r
+  @param  Timeout           The time out Value for wait memory set.\r
+\r
+  @retval EFI_DEVICE_ERROR  The memory is not set.\r
+  @retval EFI_TIMEOUT       The memory setting is time out.\r
+  @retval EFI_SUCCESS       The memory is correct set.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AhciWaitMmioSet (\r
+  IN  UINT32                    Offset,\r
+  IN  UINT32                    MaskValue,\r
+  IN  UINT32                    TestValue,\r
+  IN  UINT64                    Timeout\r
+  )\r
+{\r
+  UINT32     Value;\r
+  UINT32     Delay;\r
+\r
+  Delay = (UINT32) (DivU64x32(Timeout, 1000) + 1);\r
+\r
+  do {\r
+    Value = AhciReadReg (Offset) & MaskValue;\r
+\r
+    if (Value == TestValue) {\r
+      return EFI_SUCCESS;\r
+    }\r
+\r
+    //\r
+    // Stall for 100 microseconds.\r
+    //\r
+    MicroSecondDelay (100);\r
+\r
+    Delay--;\r
+\r
+  } while (Delay > 0);\r
+\r
+  return EFI_TIMEOUT;\r
+}\r
+/**\r
+  Wait for the Value of the specified system memory set to the test Value.\r
+\r
+  @param  Address           The system memory address to test.\r
+  @param  MaskValue         The mask Value of memory.\r
+  @param  TestValue         The test Value of memory.\r
+  @param  Timeout           The time out Value for wait memory set, uses 100ns as a unit.\r
+\r
+  @retval EFI_TIMEOUT       The system memory setting is time out.\r
+  @retval EFI_SUCCESS       The system memory is correct set.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AhciWaitMemSet (\r
+  IN  EFI_PHYSICAL_ADDRESS      Address,\r
+  IN  UINT32                    MaskValue,\r
+  IN  UINT32                    TestValue,\r
+  IN  UINT64                    Timeout\r
+  )\r
+{\r
+  UINT32     Value;\r
+  UINT32     Delay;\r
+\r
+  Delay = (UINT32) (DivU64x32 (Timeout, 1000) + 1);\r
+\r
+  do {\r
+    //\r
+    // Access sytem memory to see if the Value is the tested one.\r
+    //\r
+    // The system memory pointed by Address will be updated by the\r
+    // SATA Host Controller, "volatile" is introduced to prevent\r
+    // compiler from optimizing the access to the memory address\r
+    // to only read once.\r
+    //\r
+    Value  = *(volatile UINT32 *) (UINTN) Address;\r
+    Value &= MaskValue;\r
+\r
+    if (Value == TestValue) {\r
+      return EFI_SUCCESS;\r
+    }\r
+\r
+    //\r
+    // Stall for 100 microseconds.\r
+    //\r
+    MicroSecondDelay (100);\r
+\r
+    Delay--;\r
+\r
+  } while (Delay > 0);\r
+\r
+  return EFI_TIMEOUT;\r
+}\r
+\r
+/**\r
+  Check the memory status to the test Value.\r
+\r
+  @param[in]       Address           The memory address to test.\r
+  @param[in]       MaskValue         The mask Value of memory.\r
+  @param[in]       TestValue         The test Value of memory.\r
+  @param[in, out]  RetryTimes        The retry times Value for waitting memory set. If 0, then just try once.\r
+\r
+  @retval EFI_NOTREADY      The memory is not set.\r
+  @retval EFI_TIMEOUT       The memory setting retry times out.\r
+  @retval EFI_SUCCESS       The memory is correct set.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AhciCheckMemSet (\r
+  IN     UINTN                     Address,\r
+  IN     UINT32                    MaskValue,\r
+  IN     UINT32                    TestValue,\r
+  IN OUT UINTN                     *RetryTimes OPTIONAL\r
+  )\r
+{\r
+  UINT32     Value;\r
+\r
+  if (RetryTimes != NULL) {\r
+    (*RetryTimes)--;\r
+  }\r
+\r
+  Value  = *(volatile UINT32 *) Address;\r
+  Value &= MaskValue;\r
+\r
+  if (Value == TestValue) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  if ((RetryTimes != NULL) && (*RetryTimes == 0)) {\r
+    return EFI_TIMEOUT;\r
+  } else {\r
+    return EFI_NOT_READY;\r
+  }\r
+}\r
+\r
+/**\r
+  Clear the port interrupt and error status. It will also clear\r
+  HBA interrupt status.\r
+\r
+  @param      Port           The number of port.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+AhciClearPortStatus (\r
+  IN  UINT8                  Port\r
+  )\r
+{\r
+  UINT32 Offset;\r
+\r
+  //\r
+  // Clear any error status\r
+  //\r
+  Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SERR;\r
+  AhciWriteReg (Offset, AhciReadReg (Offset));\r
+\r
+  //\r
+  // Clear any port interrupt status\r
+  //\r
+  Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_IS;\r
+  AhciWriteReg (Offset, AhciReadReg (Offset));\r
+\r
+  //\r
+  // Clear any HBA interrupt status\r
+  //\r
+  AhciWriteReg (EFI_AHCI_IS_OFFSET, AhciReadReg (EFI_AHCI_IS_OFFSET));\r
+}\r
+\r
+/**\r
+  Enable the FIS running for giving port.\r
+\r
+  @param      Port           The number of port.\r
+  @param      Timeout        The timeout Value of enabling FIS.\r
+\r
+  @retval EFI_DEVICE_ERROR   The FIS enable setting fails.\r
+  @retval EFI_TIMEOUT        The FIS enable setting is time out.\r
+  @retval EFI_SUCCESS        The FIS enable successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AhciEnableFisReceive (\r
+  IN  UINT8                     Port,\r
+  IN  UINT64                    Timeout\r
+  )\r
+{\r
+  UINT32 Offset;\r
+\r
+  Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;\r
+  AhciOrReg (Offset, EFI_AHCI_PORT_CMD_FRE);\r
+\r
+  return AhciWaitMmioSet (\r
+           Offset,\r
+           EFI_AHCI_PORT_CMD_FR,\r
+           EFI_AHCI_PORT_CMD_FR,\r
+           Timeout\r
+           );\r
+}\r
+\r
+/**\r
+  Disable the FIS running for giving port.\r
+\r
+  @param      Port           The number of port.\r
+  @param      Timeout        The timeout Value of disabling FIS.\r
+\r
+  @retval EFI_DEVICE_ERROR   The FIS disable setting fails.\r
+  @retval EFI_TIMEOUT        The FIS disable setting is time out.\r
+  @retval EFI_UNSUPPORTED    The port is in running state.\r
+  @retval EFI_SUCCESS        The FIS disable successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AhciDisableFisReceive (\r
+  IN  UINT8                     Port,\r
+  IN  UINT64                    Timeout\r
+  )\r
+{\r
+  UINT32 Offset;\r
+  UINT32 Data;\r
+\r
+  Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;\r
+  Data   = AhciReadReg (Offset);\r
+\r
+  //\r
+  // Before disabling Fis receive, the DMA engine of the port should NOT be in running status.\r
+  //\r
+  if ((Data & (EFI_AHCI_PORT_CMD_ST | EFI_AHCI_PORT_CMD_CR)) != 0) {\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  //\r
+  // Check if the Fis receive DMA engine for the port is running.\r
+  //\r
+  if ((Data & EFI_AHCI_PORT_CMD_FR) != EFI_AHCI_PORT_CMD_FR) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  AhciAndReg (Offset, (UINT32)~(EFI_AHCI_PORT_CMD_FRE));\r
+\r
+  return AhciWaitMmioSet (\r
+           Offset,\r
+           EFI_AHCI_PORT_CMD_FR,\r
+           0,\r
+           Timeout\r
+           );\r
+}\r
+\r
+/**\r
+  Build the command list, command table and prepare the fis receiver.\r
+\r
+  @param    AhciRegisters         The pointer to the EFI_AHCI_REGISTERS.\r
+  @param    Port                  The number of port.\r
+  @param    PortMultiplier        The timeout Value of stop.\r
+  @param    CommandFis            The control fis will be used for the transfer.\r
+  @param    CommandList           The command list will be used for the transfer.\r
+  @param    AtapiCommand          The atapi command will be used for the transfer.\r
+  @param    AtapiCommandLength    The Length of the atapi command.\r
+  @param    CommandSlotNumber     The command slot will be used for the transfer.\r
+  @param    DataPhysicalAddr      The pointer to the Data Buffer pci bus master address.\r
+  @param    DataLength            The Data count to be transferred.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+AhciBuildCommand (\r
+  IN     EFI_AHCI_REGISTERS         *AhciRegisters,\r
+  IN     UINT8                      Port,\r
+  IN     UINT8                      PortMultiplier,\r
+  IN     EFI_AHCI_COMMAND_FIS       *CommandFis,\r
+  IN     EFI_AHCI_COMMAND_LIST      *CommandList,\r
+  IN     EFI_AHCI_ATAPI_COMMAND     *AtapiCommand OPTIONAL,\r
+  IN     UINT8                      AtapiCommandLength,\r
+  IN     UINT8                      CommandSlotNumber,\r
+  IN OUT VOID                       *DataPhysicalAddr,\r
+  IN     UINT64                     DataLength\r
+  )\r
+{\r
+  UINT64     BaseAddr;\r
+  UINT64     PrdtNumber;\r
+  UINTN      RemainedData;\r
+  UINTN      MemAddr;\r
+  DATA_64    Data64;\r
+  UINT32     Offset;\r
+\r
+  //\r
+  // Filling the PRDT\r
+  //\r
+  PrdtNumber = DivU64x32 (DataLength + EFI_AHCI_MAX_DATA_PER_PRDT - 1, EFI_AHCI_MAX_DATA_PER_PRDT);\r
+\r
+  //\r
+  // According to AHCI 1.3 spec, a PRDT entry can point to a maximum 4MB Data block.\r
+  // It also limits that the maximum amount of the PRDT entry in the command table\r
+  // is 65535.\r
+  //\r
+  ASSERT (PrdtNumber <= 1);\r
+\r
+  Data64.Uint64 = (UINTN) (AhciRegisters->AhciRFis);\r
+\r
+  BaseAddr = Data64.Uint64;\r
+\r
+  ZeroMem ((VOID *)((UINTN) BaseAddr), sizeof (EFI_AHCI_RECEIVED_FIS));\r
+\r
+  ZeroMem (AhciRegisters->AhciCommandTable, sizeof (EFI_AHCI_COMMAND_TABLE));\r
+\r
+  CommandFis->AhciCFisPmNum = PortMultiplier;\r
+\r
+  CopyMem (&AhciRegisters->AhciCommandTable->CommandFis, CommandFis, sizeof (EFI_AHCI_COMMAND_FIS));\r
+\r
+  Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;\r
+  if (AtapiCommand != NULL) {\r
+    CopyMem (\r
+      &AhciRegisters->AhciCommandTable->AtapiCmd,\r
+      AtapiCommand,\r
+      AtapiCommandLength\r
+      );\r
+\r
+    CommandList->AhciCmdA = 1;\r
+    CommandList->AhciCmdP = 1;\r
+\r
+    AhciOrReg (Offset, (EFI_AHCI_PORT_CMD_DLAE | EFI_AHCI_PORT_CMD_ATAPI));\r
+  } else {\r
+    AhciAndReg (Offset, (UINT32)~(EFI_AHCI_PORT_CMD_DLAE | EFI_AHCI_PORT_CMD_ATAPI));\r
+  }\r
+\r
+  RemainedData = (UINTN) DataLength;\r
+  MemAddr      = (UINTN) DataPhysicalAddr;\r
+  CommandList->AhciCmdPrdtl = (UINT32)PrdtNumber;\r
+\r
+  AhciRegisters->AhciCommandTable->PrdtTable.AhciPrdtDbc = (UINT32)RemainedData - 1;\r
+\r
+  Data64.Uint64 = (UINT64)MemAddr;\r
+  AhciRegisters->AhciCommandTable->PrdtTable.AhciPrdtDba  = Data64.Uint32.Lower32;\r
+  AhciRegisters->AhciCommandTable->PrdtTable.AhciPrdtDbau = Data64.Uint32.Upper32;\r
+\r
+  //\r
+  // Set the last PRDT to Interrupt On Complete\r
+  //\r
+  AhciRegisters->AhciCommandTable->PrdtTable.AhciPrdtIoc = 1;\r
+\r
+  CopyMem (\r
+    (VOID *) ((UINTN) AhciRegisters->AhciCmdList + (UINTN) CommandSlotNumber * sizeof (EFI_AHCI_COMMAND_LIST)),\r
+    CommandList,\r
+    sizeof (EFI_AHCI_COMMAND_LIST)\r
+    );\r
+\r
+  Data64.Uint64 = (UINT64)(UINTN) AhciRegisters->AhciCommandTable;\r
+  AhciRegisters->AhciCmdList[CommandSlotNumber].AhciCmdCtba  = Data64.Uint32.Lower32;\r
+  AhciRegisters->AhciCmdList[CommandSlotNumber].AhciCmdCtbau = Data64.Uint32.Upper32;\r
+  AhciRegisters->AhciCmdList[CommandSlotNumber].AhciCmdPmp   = PortMultiplier;\r
+\r
+}\r
+\r
+/**\r
+  Buid a command FIS.\r
+\r
+  @param  CmdFis            A pointer to the EFI_AHCI_COMMAND_FIS Data structure.\r
+  @param  AtaCommandBlock   A pointer to the AhciBuildCommandFis Data structure.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+AhciBuildCommandFis (\r
+  IN OUT EFI_AHCI_COMMAND_FIS    *CmdFis,\r
+  IN     EFI_ATA_COMMAND_BLOCK   *AtaCommandBlock\r
+  )\r
+{\r
+  ZeroMem (CmdFis, sizeof (EFI_AHCI_COMMAND_FIS));\r
+\r
+  CmdFis->AhciCFisType = EFI_AHCI_FIS_REGISTER_H2D;\r
+  //\r
+  // Indicator it's a command\r
+  //\r
+  CmdFis->AhciCFisCmdInd      = 0x1;\r
+  CmdFis->AhciCFisCmd         = AtaCommandBlock->AtaCommand;\r
+\r
+  CmdFis->AhciCFisFeature     = AtaCommandBlock->AtaFeatures;\r
+  CmdFis->AhciCFisFeatureExp  = AtaCommandBlock->AtaFeaturesExp;\r
+\r
+  CmdFis->AhciCFisSecNum      = AtaCommandBlock->AtaSectorNumber;\r
+  CmdFis->AhciCFisSecNumExp   = AtaCommandBlock->AtaSectorNumberExp;\r
+\r
+  CmdFis->AhciCFisClyLow      = AtaCommandBlock->AtaCylinderLow;\r
+  CmdFis->AhciCFisClyLowExp   = AtaCommandBlock->AtaCylinderLowExp;\r
+\r
+  CmdFis->AhciCFisClyHigh     = AtaCommandBlock->AtaCylinderHigh;\r
+  CmdFis->AhciCFisClyHighExp  = AtaCommandBlock->AtaCylinderHighExp;\r
+\r
+  CmdFis->AhciCFisSecCount    = AtaCommandBlock->AtaSectorCount;\r
+  CmdFis->AhciCFisSecCountExp = AtaCommandBlock->AtaSectorCountExp;\r
+\r
+  CmdFis->AhciCFisDevHead     = (UINT8) (AtaCommandBlock->AtaDeviceHead | 0xE0);\r
+}\r
+\r
+/**\r
+  Start a PIO Data transfer on specific port.\r
+\r
+  @param  AhciRegisters       The pointer to the EFI_AHCI_REGISTERS.\r
+  @param  Port                The number of port.\r
+  @param  PortMultiplier      The timeout Value of stop.\r
+  @param  AtapiCommand        The atapi command will be used for the transfer.\r
+  @param  AtapiCommandLength  The Length of the atapi command.\r
+  @param  Read                The transfer direction.\r
+  @param  AtaCommandBlock     The EFI_ATA_COMMAND_BLOCK Data.\r
+  @param  AtaStatusBlock      The EFI_ATA_STATUS_BLOCK Data.\r
+  @param  MemoryAddr          The pointer to the Data Buffer.\r
+  @param  DataCount           The Data count to be transferred.\r
+  @param  Timeout             The timeout Value of non Data transfer.\r
+\r
+  @retval EFI_DEVICE_ERROR    The PIO Data transfer abort with error occurs.\r
+  @retval EFI_TIMEOUT         The operation is time out.\r
+  @retval EFI_UNSUPPORTED     The device is not ready for transfer.\r
+  @retval EFI_SUCCESS         The PIO Data transfer executes successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AhciPioTransfer (\r
+  IN     EFI_AHCI_REGISTERS         *AhciRegisters,\r
+  IN     UINT8                      Port,\r
+  IN     UINT8                      PortMultiplier,\r
+  IN     EFI_AHCI_ATAPI_COMMAND     *AtapiCommand OPTIONAL,\r
+  IN     UINT8                      AtapiCommandLength,\r
+  IN     BOOLEAN                    Read,\r
+  IN     EFI_ATA_COMMAND_BLOCK      *AtaCommandBlock,\r
+  IN OUT EFI_ATA_STATUS_BLOCK       *AtaStatusBlock,\r
+  IN OUT VOID                       *MemoryAddr,\r
+  IN     UINT32                     DataCount,\r
+  IN     UINT64                     Timeout\r
+  )\r
+{\r
+  EFI_STATUS                    Status;\r
+  UINT32                        FisBaseAddr;\r
+  UINT32                        Offset;\r
+  UINT32                        Delay;\r
+  EFI_AHCI_COMMAND_FIS          CFis;\r
+  EFI_AHCI_COMMAND_LIST         CmdList;\r
+  UINT32                        PortTfd;\r
+  UINT32                        PrdCount;\r
+  UINT32                        OldRfisLo;\r
+  UINT32                        OldRfisHi;\r
+  UINT32                        OldCmdListLo;\r
+  UINT32                        OldCmdListHi;\r
+\r
+  Offset    = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FB;\r
+  OldRfisLo = AhciReadReg (Offset);\r
+  Offset    = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FBU;\r
+  OldRfisHi = AhciReadReg (Offset);\r
+  Offset    = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FB;\r
+  AhciWriteReg (Offset, (UINT32)(UINTN)AhciRegisters->AhciRFis);\r
+  Offset    = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FBU;\r
+  AhciWriteReg (Offset, 0);\r
+\r
+  //\r
+  // Single task envrionment, we only use one command table for all port\r
+  //\r
+  Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLB;\r
+  OldCmdListLo = AhciReadReg (Offset);\r
+  Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLBU;\r
+  OldCmdListHi = AhciReadReg (Offset);\r
+  Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLB;\r
+  AhciWriteReg (Offset, (UINT32)(UINTN)AhciRegisters->AhciCmdList);\r
+  Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLBU;\r
+  AhciWriteReg (Offset, 0);\r
+\r
+  //\r
+  // Package read needed\r
+  //\r
+  AhciBuildCommandFis (&CFis, AtaCommandBlock);\r
+\r
+  ZeroMem (&CmdList, sizeof (EFI_AHCI_COMMAND_LIST));\r
+\r
+  CmdList.AhciCmdCfl = EFI_AHCI_FIS_REGISTER_H2D_LENGTH / 4;\r
+  CmdList.AhciCmdW   = Read ? 0 : 1;\r
+\r
+  AhciBuildCommand (\r
+    AhciRegisters,\r
+    Port,\r
+    PortMultiplier,\r
+    &CFis,\r
+    &CmdList,\r
+    AtapiCommand,\r
+    AtapiCommandLength,\r
+    0,\r
+    (VOID *)(UINTN)MemoryAddr,\r
+    DataCount\r
+    );\r
+\r
+  Status = AhciStartCommand (\r
+             Port,\r
+             0,\r
+             Timeout\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // Checking the status and wait the driver sending Data\r
+  //\r
+  FisBaseAddr = (UINT32)(UINTN)AhciRegisters->AhciRFis;\r
+  if (Read && (AtapiCommand == 0)) {\r
+    //\r
+    // Wait device sends the PIO setup fis before Data transfer\r
+    //\r
+    Status = EFI_TIMEOUT;\r
+    Delay  = (UINT32) (DivU64x32 (Timeout, 1000) + 1);\r
+    do {\r
+      Offset = FisBaseAddr + EFI_AHCI_PIO_FIS_OFFSET;\r
+\r
+      Status = AhciCheckMemSet (Offset, EFI_AHCI_FIS_TYPE_MASK, EFI_AHCI_FIS_PIO_SETUP, 0);\r
+      if (!EFI_ERROR (Status)) {\r
+        Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;\r
+        PortTfd = AhciReadReg ((UINT32) Offset);\r
+        //\r
+        // PxTFD will be updated if there is a D2H or SetupFIS received.\r
+        // For PIO IN transfer, D2H means a device error. Therefore we only need to check the TFD after receiving a SetupFIS.\r
+        //\r
+        if ((PortTfd & EFI_AHCI_PORT_TFD_ERR) != 0) {\r
+          Status = EFI_DEVICE_ERROR;\r
+          break;\r
+        }\r
+\r
+        PrdCount = *(volatile UINT32 *) (&(AhciRegisters->AhciCmdList[0].AhciCmdPrdbc));\r
+        if (PrdCount == DataCount) {\r
+          break;\r
+        }\r
+      }\r
+\r
+      Offset = FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET;\r
+      Status = AhciCheckMemSet (Offset, EFI_AHCI_FIS_TYPE_MASK, EFI_AHCI_FIS_REGISTER_D2H, 0);\r
+      if (!EFI_ERROR (Status)) {\r
+        Status = EFI_DEVICE_ERROR;\r
+        break;\r
+      }\r
+\r
+      //\r
+      // Stall for 100 microseconds.\r
+      //\r
+      MicroSecondDelay(100);\r
+\r
+      Delay--;\r
+    } while (Delay > 0);\r
+  } else {\r
+    //\r
+    // Wait for D2H Fis is received\r
+    //\r
+    Offset = FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET;\r
+    Status = AhciWaitMemSet (\r
+               Offset,\r
+               EFI_AHCI_FIS_TYPE_MASK,\r
+               EFI_AHCI_FIS_REGISTER_D2H,\r
+               Timeout\r
+               );\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      goto Exit;\r
+    }\r
+\r
+    Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;\r
+    PortTfd = AhciReadReg ((UINT32) Offset);\r
+    if ((PortTfd & EFI_AHCI_PORT_TFD_ERR) != 0) {\r
+      Status = EFI_DEVICE_ERROR;\r
+    }\r
+  }\r
+\r
+Exit:\r
+  AhciStopCommand (\r
+    Port,\r
+    Timeout\r
+    );\r
+\r
+  AhciDisableFisReceive (\r
+    Port,\r
+    Timeout\r
+    );\r
+\r
+  Offset    = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FB;\r
+  AhciWriteReg (Offset, OldRfisLo);\r
+  Offset    = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FBU;\r
+  AhciWriteReg (Offset, OldRfisHi);\r
+\r
+  Offset    = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLB;\r
+  AhciWriteReg (Offset, OldCmdListLo);\r
+  Offset    = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLBU;\r
+  AhciWriteReg (Offset, OldCmdListHi);\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Stop command running for giving port\r
+\r
+  @param  Port               The number of port.\r
+  @param  Timeout            The timeout Value of stop.\r
+\r
+  @retval EFI_DEVICE_ERROR   The command stop unsuccessfully.\r
+  @retval EFI_TIMEOUT        The operation is time out.\r
+  @retval EFI_SUCCESS        The command stop successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AhciStopCommand (\r
+  IN  UINT8                     Port,\r
+  IN  UINT64                    Timeout\r
+  )\r
+{\r
+  UINT32 Offset;\r
+  UINT32 Data;\r
+\r
+  Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;\r
+  Data   = AhciReadReg (Offset);\r
+\r
+  if ((Data & (EFI_AHCI_PORT_CMD_ST |  EFI_AHCI_PORT_CMD_CR)) == 0) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  if ((Data & EFI_AHCI_PORT_CMD_ST) != 0) {\r
+    AhciAndReg (Offset, (UINT32)~(EFI_AHCI_PORT_CMD_ST));\r
+  }\r
+\r
+  return AhciWaitMmioSet (\r
+           Offset,\r
+           EFI_AHCI_PORT_CMD_CR,\r
+           0,\r
+           Timeout\r
+           );\r
+}\r
+\r
+/**\r
+  Start command for give slot on specific port.\r
+\r
+  @param  Port               The number of port.\r
+  @param  CommandSlot        The number of CommandSlot.\r
+  @param  Timeout            The timeout Value of start.\r
+\r
+  @retval EFI_DEVICE_ERROR   The command start unsuccessfully.\r
+  @retval EFI_TIMEOUT        The operation is time out.\r
+  @retval EFI_SUCCESS        The command start successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AhciStartCommand (\r
+  IN  UINT8                     Port,\r
+  IN  UINT8                     CommandSlot,\r
+  IN  UINT64                    Timeout\r
+  )\r
+{\r
+  UINT32                        CmdSlotBit;\r
+  EFI_STATUS                    Status;\r
+  UINT32                        PortStatus;\r
+  UINT32                        StartCmd;\r
+  UINT32                        PortTfd;\r
+  UINT32                        Offset;\r
+  UINT32                        Capability;\r
+\r
+  //\r
+  // Collect AHCI controller information\r
+  //\r
+  Capability = AhciReadReg(EFI_AHCI_CAPABILITY_OFFSET);\r
+\r
+  CmdSlotBit = (UINT32) (1 << CommandSlot);\r
+\r
+  AhciClearPortStatus (\r
+    Port\r
+    );\r
+\r
+  Status = AhciEnableFisReceive (\r
+             Port,\r
+             Timeout\r
+             );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;\r
+  PortStatus = AhciReadReg (Offset);\r
+\r
+  StartCmd = 0;\r
+  if ((PortStatus & EFI_AHCI_PORT_CMD_ALPE) != 0) {\r
+    StartCmd = AhciReadReg (Offset);\r
+    StartCmd &= ~EFI_AHCI_PORT_CMD_ICC_MASK;\r
+    StartCmd |= EFI_AHCI_PORT_CMD_ACTIVE;\r
+  }\r
+\r
+  Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;\r
+  PortTfd = AhciReadReg (Offset);\r
+\r
+  if ((PortTfd & (EFI_AHCI_PORT_TFD_BSY | EFI_AHCI_PORT_TFD_DRQ)) != 0) {\r
+    if ((Capability & BIT24) != 0) {\r
+      Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;\r
+      AhciOrReg (Offset, EFI_AHCI_PORT_CMD_COL);\r
+\r
+      AhciWaitMmioSet (\r
+        Offset,\r
+        EFI_AHCI_PORT_CMD_COL,\r
+        0,\r
+        Timeout\r
+        );\r
+    }\r
+  }\r
+\r
+  Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;\r
+  AhciOrReg (Offset, EFI_AHCI_PORT_CMD_ST | StartCmd);\r
+\r
+  //\r
+  // Setting the command\r
+  //\r
+  Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SACT;\r
+  AhciAndReg (Offset, 0);\r
+  AhciOrReg (Offset, CmdSlotBit);\r
+\r
+  Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CI;\r
+  AhciAndReg (Offset, 0);\r
+  AhciOrReg (Offset, CmdSlotBit);\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Do AHCI HBA reset.\r
+\r
+  @param[in]  Timeout        The timeout Value of reset.\r
+\r
+  @retval EFI_DEVICE_ERROR   AHCI controller is failed to complete hardware reset.\r
+  @retval EFI_TIMEOUT        The reset operation is time out.\r
+  @retval EFI_SUCCESS        AHCI controller is reset successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AhciReset (\r
+  IN  UINT64                    Timeout\r
+  )\r
+{\r
+  UINT32                 Delay;\r
+  UINT32                 Value;\r
+  UINT32                 Capability;\r
+\r
+  //\r
+  // Collect AHCI controller information\r
+  //\r
+  Capability = AhciReadReg (EFI_AHCI_CAPABILITY_OFFSET);\r
+\r
+  //\r
+  // Enable AE before accessing any AHCI registers if Supports AHCI Mode Only is not set\r
+  //\r
+  if ((Capability & EFI_AHCI_CAP_SAM) == 0) {\r
+    AhciOrReg (EFI_AHCI_GHC_OFFSET, EFI_AHCI_GHC_ENABLE);\r
+  }\r
+\r
+  AhciOrReg (EFI_AHCI_GHC_OFFSET, EFI_AHCI_GHC_RESET);\r
+\r
+  Delay = (UINT32) (DivU64x32(Timeout, 1000) + 1);\r
+\r
+  do {\r
+    Value = AhciReadReg(EFI_AHCI_GHC_OFFSET);\r
+    if ((Value & EFI_AHCI_GHC_RESET) == 0) {\r
+      return EFI_SUCCESS;\r
+    }\r
+\r
+    //\r
+    // Stall for 100 microseconds.\r
+    //\r
+    MicroSecondDelay(100);\r
+\r
+    Delay--;\r
+  } while (Delay > 0);\r
+\r
+  return EFI_TIMEOUT;\r
+\r
+\r
+}\r
+\r
+/**\r
+  Send Buffer cmd to specific device.\r
+\r
+  @param[in]  AhciRegisters       The pointer to the EFI_AHCI_REGISTERS.\r
+  @param[in]  Port                The port number of attached ATA device.\r
+  @param[in]  PortMultiplier      The port number of port multiplier of attached ATA device.\r
+  @param[in, out]  Buffer         The Data Buffer to store IDENTIFY PACKET Data.\r
+\r
+  @retval EFI_DEVICE_ERROR    The cmd abort with error occurs.\r
+  @retval EFI_TIMEOUT         The operation is time out.\r
+  @retval EFI_UNSUPPORTED     The device is not ready for executing.\r
+  @retval EFI_SUCCESS         The cmd executes successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AhciIdentify (\r
+  IN EFI_AHCI_REGISTERS       *AhciRegisters,\r
+  IN UINT8                    Port,\r
+  IN UINT8                    PortMultiplier,\r
+  IN OUT ATA_IDENTIFY_DATA    *Buffer\r
+  )\r
+{\r
+  EFI_STATUS                   Status;\r
+  EFI_ATA_COMMAND_BLOCK        AtaCommandBlock;\r
+\r
+  if (AhciRegisters == NULL || Buffer == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK));\r
+\r
+  AtaCommandBlock.AtaCommand     = ATA_CMD_IDENTIFY_DRIVE;\r
+  AtaCommandBlock.AtaSectorCount = 1;\r
+\r
+  Status = AhciPioTransfer (\r
+             AhciRegisters,\r
+             Port,\r
+             PortMultiplier,\r
+             NULL,\r
+             0,\r
+             TRUE,\r
+             &AtaCommandBlock,\r
+             NULL,\r
+             Buffer,\r
+             sizeof (ATA_IDENTIFY_DATA),\r
+             ATA_TIMEOUT\r
+             );\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Get AHCI mode MMIO Bar Size.\r
+\r
+  @param[in] Bus         The bus number of ata host controller.\r
+  @param[in] Device      The device number of ata host controller.\r
+  @param[in] Function    The function number of ata host controller.\r
+\r
+  @retval  The Size of AHCI MMIO BAR.\r
+\r
+**/\r
+UINT32\r
+EFIAPI\r
+GetAhciBarSize (\r
+  IN     UINTN                       Bus,\r
+  IN     UINTN                       Device,\r
+  IN     UINTN                       Function\r
+  )\r
+{\r
+  UINT32     Size;\r
+  UINT32     OldBar;\r
+\r
+  OldBar = PciRead32 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x24));\r
+  //\r
+  // Disable PCI CMD.MSE bit before calculating MMIO Bar Size as it needs write all 1 to BAR register.\r
+  //\r
+  PciAnd32 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x04), (UINT32)~BIT1);\r
+\r
+  //\r
+  // Get AHCI MMIO Bar Size.\r
+  //\r
+  PciWrite32 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x24), 0xFFFFFFFF);\r
+  Size = PciRead32 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x24));\r
+  Size = (~(Size & 0xFFFFFFF0)) + 1;\r
+\r
+  //\r
+  // Restore old MMIO Bar.\r
+  //\r
+  PciWrite32 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x24), OldBar);\r
+  //\r
+  // Enable PCI CMD.MSE bit after restoring MMIO Bar.\r
+  //\r
+  PciOr32 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x04), BIT1);\r
+\r
+  return Size;\r
+}\r
+\r
+/**\r
+  Get AHCI mode base address registers' Value.\r
+\r
+  @param[in] Bus         The bus number of ata host controller.\r
+  @param[in] Device      The device number of ata host controller.\r
+  @param[in] Function    The function number of ata host controller.\r
+\r
+  @retval EFI_UNSUPPORTED        Return this Value when the BARs is not IO type\r
+  @retval EFI_SUCCESS            Get the Base address successfully\r
+  @retval Other                  Read the pci configureation Data error\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+GetAhciBaseAddress (\r
+  IN     UINTN                       Bus,\r
+  IN     UINTN                       Device,\r
+  IN     UINTN                       Function\r
+  )\r
+{\r
+  UINT32  Size;\r
+\r
+  //\r
+  // Get AHCI MMIO Bar\r
+  //\r
+  mAhciBar = PciRead32 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x24));\r
+  //\r
+  // Get AHCI MMIO Bar Size\r
+  //\r
+  Size = GetAhciBarSize (Bus, Device, Function);\r
+  //\r
+  // Check if the AHCI Bar region is in SMRAM to avoid malicious attack by modifying MMIO Bar to point to SMRAM.\r
+  //\r
+  if (!SmmIsBufferOutsideSmmValid ((EFI_PHYSICAL_ADDRESS)mAhciBar, Size)) {\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Allocate transfer-related Data struct which is used at AHCI mode.\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
+AhciAllocateResource (\r
+  VOID\r
+  )\r
+{\r
+  EFI_STATUS            Status;\r
+  EFI_PHYSICAL_ADDRESS  Base;\r
+\r
+  //\r
+  // Allocate resources required by AHCI host controller.\r
+  //\r
+  Base = 0xFFFFFFFF;\r
+  Status = gBS->AllocatePages (\r
+                  AllocateMaxAddress,\r
+                  EfiACPIMemoryNVS,\r
+                  EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS)),\r
+                  &Base\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  ZeroMem ((VOID *)(UINTN)Base, EFI_PAGE_SIZE * EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS)));\r
+  mAhciRegisters.AhciRFis = (VOID *)(UINTN)Base;\r
+\r
+  Base = 0xFFFFFFFF;\r
+  Status = gBS->AllocatePages (\r
+                  AllocateMaxAddress,\r
+                  EfiACPIMemoryNVS,\r
+                  EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_LIST)),\r
+                  &Base\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    gBS->FreePages ((EFI_PHYSICAL_ADDRESS)(UINTN)mAhciRegisters.AhciRFis, EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS)));\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+  ZeroMem ((VOID *)(UINTN)Base, EFI_PAGE_SIZE * EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_LIST)));\r
+  mAhciRegisters.AhciCmdList = (VOID *)(UINTN)Base;\r
+\r
+  Base = 0xFFFFFFFF;\r
+  Status = gBS->AllocatePages (\r
+                  AllocateMaxAddress,\r
+                  EfiACPIMemoryNVS,\r
+                  EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_TABLE)),\r
+                  &Base\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    gBS->FreePages ((EFI_PHYSICAL_ADDRESS)(UINTN)mAhciRegisters.AhciRFis, EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS)));\r
+    gBS->FreePages ((EFI_PHYSICAL_ADDRESS)(UINTN)mAhciRegisters.AhciCmdList, EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_LIST)));\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+  ZeroMem ((VOID *)(UINTN)Base, EFI_PAGE_SIZE * EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_TABLE)));\r
+  mAhciRegisters.AhciCommandTable = (VOID *)(UINTN)Base;\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Free allocated transfer-related Data struct which is used at AHCI mode.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+AhciFreeResource (\r
+  VOID\r
+  )\r
+{\r
+  if (mAhciRegisters.AhciRFis != NULL) {\r
+    gBS->FreePages ((EFI_PHYSICAL_ADDRESS)(UINTN)mAhciRegisters.AhciRFis, EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS)));\r
+  }\r
+\r
+  if (mAhciRegisters.AhciCmdList != NULL) {\r
+    gBS->FreePages ((EFI_PHYSICAL_ADDRESS)(UINTN)mAhciRegisters.AhciCmdList, EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_LIST)));\r
+  }\r
+\r
+  if (mAhciRegisters.AhciCommandTable != NULL) {\r
+    gBS->FreePages ((EFI_PHYSICAL_ADDRESS)(UINTN)mAhciRegisters.AhciCommandTable, EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_TABLE)));\r
+  }\r
+}\r
+\r
+/**\r
+  Initialize ATA host controller at AHCI mode.\r
+\r
+  The function is designed to initialize ATA host controller.\r
+\r
+  @param[in]  Port          The port number to do initialization.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AhciModeInitialize (\r
+  UINT8              Port\r
+  )\r
+{\r
+  EFI_STATUS         Status;\r
+  UINT32             Capability;\r
+  UINT32             Offset;\r
+  UINT32             Data;\r
+  UINT32             PhyDetectDelay;\r
+\r
+  Status = AhciReset (ATA_TIMEOUT);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Collect AHCI controller information\r
+  //\r
+  Capability = AhciReadReg (EFI_AHCI_CAPABILITY_OFFSET);\r
+\r
+  //\r
+  // Enable AE before accessing any AHCI registers if Supports AHCI Mode Only is not set\r
+  //\r
+  if ((Capability & EFI_AHCI_CAP_SAM) == 0) {\r
+    AhciOrReg (EFI_AHCI_GHC_OFFSET, EFI_AHCI_GHC_ENABLE);\r
+  }\r
+\r
+  Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FB;\r
+  AhciWriteReg (Offset, (UINT32)(UINTN)mAhciRegisters.AhciRFis);\r
+\r
+  //\r
+  // Single task envrionment, we only use one command table for all port\r
+  //\r
+  Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLB;\r
+  AhciWriteReg (Offset, (UINT32)(UINTN)mAhciRegisters.AhciCmdList);\r
+\r
+  Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;\r
+  Data = AhciReadReg (Offset);\r
+  if ((Data & EFI_AHCI_PORT_CMD_CPD) != 0) {\r
+    AhciOrReg (Offset, EFI_AHCI_PORT_CMD_POD);\r
+  }\r
+\r
+  if ((Capability & BIT27) != 0) {\r
+    AhciOrReg (Offset, EFI_AHCI_PORT_CMD_SUD);\r
+  }\r
+\r
+  //\r
+  // Disable aggressive power management.\r
+  //\r
+  Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SCTL;\r
+  AhciOrReg (Offset, EFI_AHCI_PORT_SCTL_IPM_INIT);\r
+  //\r
+  // Disable the reporting of the corresponding interrupt to system software.\r
+  //\r
+  Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_IE;\r
+  AhciAndReg (Offset, 0);\r
+\r
+  Status = AhciEnableFisReceive (\r
+             Port,\r
+             EFI_TIMER_PERIOD_MILLISECONDS(500)\r
+             );\r
+  ASSERT_EFI_ERROR (Status);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // According to SATA1.0a spec section 5.2, we need to wait for PxTFD.BSY and PxTFD.DRQ\r
+  // and PxTFD.ERR to be zero. The maximum wait time is 16s which is defined at ATA spec.\r
+  //\r
+  PhyDetectDelay = 16 * 1000;\r
+  do {\r
+    Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SERR;\r
+    if (AhciReadReg(Offset) != 0) {\r
+      AhciWriteReg (Offset, AhciReadReg(Offset));\r
+    }\r
+    Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;\r
+\r
+    Data = AhciReadReg (Offset) & EFI_AHCI_PORT_TFD_MASK;\r
+    if (Data == 0) {\r
+      break;\r
+    }\r
+\r
+    MicroSecondDelay (1000);\r
+    PhyDetectDelay--;\r
+  } while (PhyDetectDelay > 0);\r
+\r
+  if (PhyDetectDelay == 0) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SIG;\r
+  Status = AhciWaitMmioSet (\r
+             Offset,\r
+             0x0000FFFF,\r
+             0x00000101,\r
+             EFI_TIMER_PERIOD_SECONDS(16)\r
+             );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
diff --git a/SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalAhciMode.h b/SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalAhciMode.h
new file mode 100644 (file)
index 0000000..3a7f633
--- /dev/null
@@ -0,0 +1,408 @@
+/** @file\r
+  Header file for AHCI mode of ATA host controller.\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
+\r
+#ifndef __OPAL_PASSWORD_AHCI_MODE_H__\r
+#define __OPAL_PASSWORD_AHCI_MODE_H__\r
+\r
+//\r
+// OPAL LIBRARY CALLBACKS\r
+//\r
+#define ATA_COMMAND_TRUSTED_RECEIVE            0x5C\r
+#define ATA_COMMAND_TRUSTED_SEND               0x5E\r
+\r
+//\r
+// ATA TRUSTED commands express transfer Length in 512 byte multiple\r
+//\r
+#define ATA_TRUSTED_TRANSFER_LENGTH_MULTIPLE   512\r
+#define ATA_DEVICE_LBA                         0x40    ///< Set for commands with LBA (rather than CHS) addresses\r
+\r
+\r
+#define EFI_AHCI_BAR_INDEX                     0x05\r
+\r
+#define EFI_AHCI_CAPABILITY_OFFSET             0x0000\r
+#define   EFI_AHCI_CAP_SAM                     BIT18\r
+#define EFI_AHCI_GHC_OFFSET                    0x0004\r
+#define   EFI_AHCI_GHC_RESET                   BIT0\r
+#define   EFI_AHCI_GHC_IE                      BIT1\r
+#define   EFI_AHCI_GHC_ENABLE                  BIT31\r
+#define EFI_AHCI_IS_OFFSET                     0x0008\r
+#define EFI_AHCI_PI_OFFSET                     0x000C\r
+\r
+typedef struct {\r
+  UINT32  Lower32;\r
+  UINT32  Upper32;\r
+} DATA_32;\r
+\r
+typedef union {\r
+  DATA_32   Uint32;\r
+  UINT64    Uint64;\r
+} DATA_64;\r
+\r
+//\r
+// Each PRDT entry can point to a memory block up to 4M byte\r
+//\r
+#define EFI_AHCI_MAX_DATA_PER_PRDT             0x400000\r
+\r
+#define EFI_AHCI_FIS_REGISTER_H2D              0x27      //Register FIS - Host to Device\r
+#define   EFI_AHCI_FIS_REGISTER_H2D_LENGTH     20\r
+#define EFI_AHCI_FIS_REGISTER_D2H              0x34      //Register FIS - Device to Host\r
+#define   EFI_AHCI_FIS_REGISTER_D2H_LENGTH     20\r
+#define EFI_AHCI_FIS_DMA_ACTIVATE              0x39      //DMA Activate FIS - Device to Host\r
+#define   EFI_AHCI_FIS_DMA_ACTIVATE_LENGTH     4\r
+#define EFI_AHCI_FIS_DMA_SETUP                 0x41      //DMA Setup FIS - Bi-directional\r
+#define   EFI_AHCI_FIS_DMA_SETUP_LENGTH        28\r
+#define EFI_AHCI_FIS_DATA                      0x46      //Data FIS - Bi-directional\r
+#define EFI_AHCI_FIS_BIST                      0x58      //BIST Activate FIS - Bi-directional\r
+#define   EFI_AHCI_FIS_BIST_LENGTH             12\r
+#define EFI_AHCI_FIS_PIO_SETUP                 0x5F      //PIO Setup FIS - Device to Host\r
+#define   EFI_AHCI_FIS_PIO_SETUP_LENGTH        20\r
+#define EFI_AHCI_FIS_SET_DEVICE                0xA1      //Set Device Bits FIS - Device to Host\r
+#define   EFI_AHCI_FIS_SET_DEVICE_LENGTH       8\r
+\r
+#define EFI_AHCI_D2H_FIS_OFFSET                0x40\r
+#define EFI_AHCI_DMA_FIS_OFFSET                0x00\r
+#define EFI_AHCI_PIO_FIS_OFFSET                0x20\r
+#define EFI_AHCI_SDB_FIS_OFFSET                0x58\r
+#define EFI_AHCI_FIS_TYPE_MASK                 0xFF\r
+#define EFI_AHCI_U_FIS_OFFSET                  0x60\r
+\r
+//\r
+// Port register\r
+//\r
+#define EFI_AHCI_PORT_START                    0x0100\r
+#define EFI_AHCI_PORT_REG_WIDTH                0x0080\r
+#define EFI_AHCI_PORT_CLB                      0x0000\r
+#define EFI_AHCI_PORT_CLBU                     0x0004\r
+#define EFI_AHCI_PORT_FB                       0x0008\r
+#define EFI_AHCI_PORT_FBU                      0x000C\r
+#define EFI_AHCI_PORT_IS                       0x0010\r
+#define   EFI_AHCI_PORT_IS_DHRS                BIT0\r
+#define   EFI_AHCI_PORT_IS_PSS                 BIT1\r
+#define   EFI_AHCI_PORT_IS_SSS                 BIT2\r
+#define   EFI_AHCI_PORT_IS_SDBS                BIT3\r
+#define   EFI_AHCI_PORT_IS_UFS                 BIT4\r
+#define   EFI_AHCI_PORT_IS_DPS                 BIT5\r
+#define   EFI_AHCI_PORT_IS_PCS                 BIT6\r
+#define   EFI_AHCI_PORT_IS_DIS                 BIT7\r
+#define   EFI_AHCI_PORT_IS_PRCS                BIT22\r
+#define   EFI_AHCI_PORT_IS_IPMS                BIT23\r
+#define   EFI_AHCI_PORT_IS_OFS                 BIT24\r
+#define   EFI_AHCI_PORT_IS_INFS                BIT26\r
+#define   EFI_AHCI_PORT_IS_IFS                 BIT27\r
+#define   EFI_AHCI_PORT_IS_HBDS                BIT28\r
+#define   EFI_AHCI_PORT_IS_HBFS                BIT29\r
+#define   EFI_AHCI_PORT_IS_TFES                BIT30\r
+#define   EFI_AHCI_PORT_IS_CPDS                BIT31\r
+#define   EFI_AHCI_PORT_IS_CLEAR               0xFFFFFFFF\r
+#define   EFI_AHCI_PORT_IS_FIS_CLEAR           0x0000000F\r
+\r
+#define EFI_AHCI_PORT_IE                       0x0014\r
+#define EFI_AHCI_PORT_CMD                      0x0018\r
+#define   EFI_AHCI_PORT_CMD_ST_MASK            0xFFFFFFFE\r
+#define   EFI_AHCI_PORT_CMD_ST                 BIT0\r
+#define   EFI_AHCI_PORT_CMD_SUD                BIT1\r
+#define   EFI_AHCI_PORT_CMD_POD                BIT2\r
+#define   EFI_AHCI_PORT_CMD_COL                BIT3\r
+#define   EFI_AHCI_PORT_CMD_CR                 BIT15\r
+#define   EFI_AHCI_PORT_CMD_FRE                BIT4\r
+#define   EFI_AHCI_PORT_CMD_FR                 BIT14\r
+#define   EFI_AHCI_PORT_CMD_MASK               ~(EFI_AHCI_PORT_CMD_ST | EFI_AHCI_PORT_CMD_FRE | EFI_AHCI_PORT_CMD_COL)\r
+#define   EFI_AHCI_PORT_CMD_PMA                BIT17\r
+#define   EFI_AHCI_PORT_CMD_HPCP               BIT18\r
+#define   EFI_AHCI_PORT_CMD_MPSP               BIT19\r
+#define   EFI_AHCI_PORT_CMD_CPD                BIT20\r
+#define   EFI_AHCI_PORT_CMD_ESP                BIT21\r
+#define   EFI_AHCI_PORT_CMD_ATAPI              BIT24\r
+#define   EFI_AHCI_PORT_CMD_DLAE               BIT25\r
+#define   EFI_AHCI_PORT_CMD_ALPE               BIT26\r
+#define   EFI_AHCI_PORT_CMD_ASP                BIT27\r
+#define   EFI_AHCI_PORT_CMD_ICC_MASK           (BIT28 | BIT29 | BIT30 | BIT31)\r
+#define   EFI_AHCI_PORT_CMD_ACTIVE             (1 << 28 )\r
+#define EFI_AHCI_PORT_TFD                      0x0020\r
+#define   EFI_AHCI_PORT_TFD_MASK               (BIT7 | BIT3 | BIT0)\r
+#define   EFI_AHCI_PORT_TFD_BSY                BIT7\r
+#define   EFI_AHCI_PORT_TFD_DRQ                BIT3\r
+#define   EFI_AHCI_PORT_TFD_ERR                BIT0\r
+#define   EFI_AHCI_PORT_TFD_ERR_MASK           0x00FF00\r
+#define EFI_AHCI_PORT_SIG                      0x0024\r
+#define EFI_AHCI_PORT_SSTS                     0x0028\r
+#define   EFI_AHCI_PORT_SSTS_DET_MASK          0x000F\r
+#define   EFI_AHCI_PORT_SSTS_DET               0x0001\r
+#define   EFI_AHCI_PORT_SSTS_DET_PCE           0x0003\r
+#define   EFI_AHCI_PORT_SSTS_SPD_MASK          0x00F0\r
+#define EFI_AHCI_PORT_SCTL                     0x002C\r
+#define   EFI_AHCI_PORT_SCTL_DET_MASK          0x000F\r
+#define   EFI_AHCI_PORT_SCTL_MASK              (~EFI_AHCI_PORT_SCTL_DET_MASK)\r
+#define   EFI_AHCI_PORT_SCTL_DET_INIT          0x0001\r
+#define   EFI_AHCI_PORT_SCTL_DET_PHYCOMM       0x0003\r
+#define   EFI_AHCI_PORT_SCTL_SPD_MASK          0x00F0\r
+#define   EFI_AHCI_PORT_SCTL_IPM_MASK          0x0F00\r
+#define   EFI_AHCI_PORT_SCTL_IPM_INIT          0x0300\r
+#define   EFI_AHCI_PORT_SCTL_IPM_PSD           0x0100\r
+#define   EFI_AHCI_PORT_SCTL_IPM_SSD           0x0200\r
+#define EFI_AHCI_PORT_SERR                     0x0030\r
+#define   EFI_AHCI_PORT_SERR_RDIE              BIT0\r
+#define   EFI_AHCI_PORT_SERR_RCE               BIT1\r
+#define   EFI_AHCI_PORT_SERR_TDIE              BIT8\r
+#define   EFI_AHCI_PORT_SERR_PCDIE             BIT9\r
+#define   EFI_AHCI_PORT_SERR_PE                BIT10\r
+#define   EFI_AHCI_PORT_SERR_IE                BIT11\r
+#define   EFI_AHCI_PORT_SERR_PRC               BIT16\r
+#define   EFI_AHCI_PORT_SERR_PIE               BIT17\r
+#define   EFI_AHCI_PORT_SERR_CW                BIT18\r
+#define   EFI_AHCI_PORT_SERR_BDE               BIT19\r
+#define   EFI_AHCI_PORT_SERR_DE                BIT20\r
+#define   EFI_AHCI_PORT_SERR_CRCE              BIT21\r
+#define   EFI_AHCI_PORT_SERR_HE                BIT22\r
+#define   EFI_AHCI_PORT_SERR_LSE               BIT23\r
+#define   EFI_AHCI_PORT_SERR_TSTE              BIT24\r
+#define   EFI_AHCI_PORT_SERR_UFT               BIT25\r
+#define   EFI_AHCI_PORT_SERR_EX                BIT26\r
+#define   EFI_AHCI_PORT_ERR_CLEAR              0xFFFFFFFF\r
+#define EFI_AHCI_PORT_SACT                     0x0034\r
+#define EFI_AHCI_PORT_CI                       0x0038\r
+#define EFI_AHCI_PORT_SNTF                     0x003C\r
+\r
+\r
+#pragma pack(1)\r
+//\r
+// Command List structure includes total 32 entries.\r
+// The entry Data structure is listed at the following.\r
+//\r
+typedef struct {\r
+  UINT32   AhciCmdCfl:5;      //Command FIS Length\r
+  UINT32   AhciCmdA:1;        //ATAPI\r
+  UINT32   AhciCmdW:1;        //Write\r
+  UINT32   AhciCmdP:1;        //Prefetchable\r
+  UINT32   AhciCmdR:1;        //Reset\r
+  UINT32   AhciCmdB:1;        //BIST\r
+  UINT32   AhciCmdC:1;        //Clear Busy upon R_OK\r
+  UINT32   AhciCmdRsvd:1;\r
+  UINT32   AhciCmdPmp:4;      //Port Multiplier Port\r
+  UINT32   AhciCmdPrdtl:16;   //Physical Region Descriptor Table Length\r
+  UINT32   AhciCmdPrdbc;      //Physical Region Descriptor Byte Count\r
+  UINT32   AhciCmdCtba;       //Command Table Descriptor Base Address\r
+  UINT32   AhciCmdCtbau;      //Command Table Descriptor Base Address Upper 32-BITs\r
+  UINT32   AhciCmdRsvd1[4];\r
+} EFI_AHCI_COMMAND_LIST;\r
+\r
+//\r
+// This is a software constructed FIS.\r
+// For Data transfer operations, this is the H2D Register FIS format as\r
+// specified in the Serial ATA Revision 2.6 specification.\r
+//\r
+typedef struct {\r
+  UINT8    AhciCFisType;\r
+  UINT8    AhciCFisPmNum:4;\r
+  UINT8    AhciCFisRsvd:1;\r
+  UINT8    AhciCFisRsvd1:1;\r
+  UINT8    AhciCFisRsvd2:1;\r
+  UINT8    AhciCFisCmdInd:1;\r
+  UINT8    AhciCFisCmd;\r
+  UINT8    AhciCFisFeature;\r
+  UINT8    AhciCFisSecNum;\r
+  UINT8    AhciCFisClyLow;\r
+  UINT8    AhciCFisClyHigh;\r
+  UINT8    AhciCFisDevHead;\r
+  UINT8    AhciCFisSecNumExp;\r
+  UINT8    AhciCFisClyLowExp;\r
+  UINT8    AhciCFisClyHighExp;\r
+  UINT8    AhciCFisFeatureExp;\r
+  UINT8    AhciCFisSecCount;\r
+  UINT8    AhciCFisSecCountExp;\r
+  UINT8    AhciCFisRsvd3;\r
+  UINT8    AhciCFisControl;\r
+  UINT8    AhciCFisRsvd4[4];\r
+  UINT8    AhciCFisRsvd5[44];\r
+} EFI_AHCI_COMMAND_FIS;\r
+\r
+//\r
+// ACMD: ATAPI command (12 or 16 bytes)\r
+//\r
+typedef struct {\r
+  UINT8    AtapiCmd[0x10];\r
+} EFI_AHCI_ATAPI_COMMAND;\r
+\r
+//\r
+// Physical Region Descriptor Table includes up to 65535 entries\r
+// The entry Data structure is listed at the following.\r
+// the actual entry number comes from the PRDTL field in the command\r
+// list entry for this command slot.\r
+//\r
+typedef struct {\r
+  UINT32   AhciPrdtDba;       //Data Base Address\r
+  UINT32   AhciPrdtDbau;      //Data Base Address Upper 32-BITs\r
+  UINT32   AhciPrdtRsvd;\r
+  UINT32   AhciPrdtDbc:22;    //Data Byte Count\r
+  UINT32   AhciPrdtRsvd1:9;\r
+  UINT32   AhciPrdtIoc:1;     //Interrupt on Completion\r
+} EFI_AHCI_COMMAND_PRDT;\r
+\r
+//\r
+// Command table Data strucute which is pointed to by the entry in the command list\r
+//\r
+typedef struct {\r
+  EFI_AHCI_COMMAND_FIS      CommandFis;       // A software constructed FIS.\r
+  EFI_AHCI_ATAPI_COMMAND    AtapiCmd;         // 12 or 16 bytes ATAPI cmd.\r
+  UINT8                     Reserved[0x30];\r
+  EFI_AHCI_COMMAND_PRDT     PrdtTable;    // The scatter/gather list for Data transfer\r
+} EFI_AHCI_COMMAND_TABLE;\r
+\r
+//\r
+// Received FIS structure\r
+//\r
+typedef struct {\r
+  UINT8    AhciDmaSetupFis[0x1C];         // Dma Setup Fis: offset 0x00\r
+  UINT8    AhciDmaSetupFisRsvd[0x04];\r
+  UINT8    AhciPioSetupFis[0x14];         // Pio Setup Fis: offset 0x20\r
+  UINT8    AhciPioSetupFisRsvd[0x0C];\r
+  UINT8    AhciD2HRegisterFis[0x14];      // D2H Register Fis: offset 0x40\r
+  UINT8    AhciD2HRegisterFisRsvd[0x04];\r
+  UINT64   AhciSetDeviceBitsFis;          // Set Device Bits Fix: offset 0x58\r
+  UINT8    AhciUnknownFis[0x40];          // Unkonwn Fis: offset 0x60\r
+  UINT8    AhciUnknownFisRsvd[0x60];\r
+} EFI_AHCI_RECEIVED_FIS;\r
+\r
+#pragma pack()\r
+\r
+typedef struct {\r
+  EFI_AHCI_RECEIVED_FIS     *AhciRFis;\r
+  EFI_AHCI_COMMAND_LIST     *AhciCmdList;\r
+  EFI_AHCI_COMMAND_TABLE    *AhciCommandTable;\r
+} EFI_AHCI_REGISTERS;\r
+\r
+extern EFI_AHCI_REGISTERS   mAhciRegisters;\r
+extern UINT32               mAhciBar;\r
+\r
+/**\r
+  Send Buffer cmd to specific device.\r
+\r
+  @param  AhciRegisters       The pointer to the EFI_AHCI_REGISTERS.\r
+  @param  Port                The number of port.\r
+  @param  PortMultiplier      The timeout Value of stop.\r
+  @param  Buffer              The Data Buffer to store IDENTIFY PACKET Data.\r
+\r
+  @retval EFI_DEVICE_ERROR    The cmd abort with error occurs.\r
+  @retval EFI_TIMEOUT         The operation is time out.\r
+  @retval EFI_UNSUPPORTED     The device is not ready for executing.\r
+  @retval EFI_SUCCESS         The cmd executes successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AhciIdentify (\r
+  IN EFI_AHCI_REGISTERS       *AhciRegisters,\r
+  IN UINT8                    Port,\r
+  IN UINT8                    PortMultiplier,\r
+  IN OUT ATA_IDENTIFY_DATA    *Buffer\r
+  );\r
+\r
+/**\r
+  Get AHCI mode base address registers' Value.\r
+\r
+  @param[in] Bus         The bus number of ata host controller.\r
+  @param[in] Device      The device number of ata host controller.\r
+  @param[in] Function    The function number of ata host controller.\r
+\r
+  @retval EFI_UNSUPPORTED        Return this Value when the BARs is not IO type\r
+  @retval EFI_SUCCESS            Get the Base address successfully\r
+  @retval Other                  Read the pci configureation Data error\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+GetAhciBaseAddress (\r
+  IN     UINTN                       Bus,\r
+  IN     UINTN                       Device,\r
+  IN     UINTN                       Function\r
+  );\r
+\r
+/**\r
+  Allocate transfer-related Data struct which is used at AHCI mode.\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
+AhciAllocateResource (\r
+  VOID\r
+  );\r
+\r
+/**\r
+  Free allocated transfer-related Data struct which is used at AHCI mode.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+AhciFreeResource (\r
+  VOID\r
+  );\r
+\r
+/**\r
+  Initialize ATA host controller at AHCI mode.\r
+\r
+  The function is designed to initialize ATA host controller.\r
+\r
+  @param[in]  Port          The port number to do initialization.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AhciModeInitialize (\r
+  UINT8      Port\r
+  );\r
+\r
+/**\r
+  Start a PIO Data transfer on specific port.\r
+\r
+  @param  AhciRegisters       The pointer to the EFI_AHCI_REGISTERS.\r
+  @param  Port                The number of port.\r
+  @param  PortMultiplier      The timeout Value of stop.\r
+  @param  AtapiCommand        The atapi command will be used for the transfer.\r
+  @param  AtapiCommandLength  The Length of the atapi command.\r
+  @param  Read                The transfer direction.\r
+  @param  AtaCommandBlock     The EFI_ATA_COMMAND_BLOCK Data.\r
+  @param  AtaStatusBlock      The EFI_ATA_STATUS_BLOCK Data.\r
+  @param  MemoryAddr          The pointer to the Data Buffer.\r
+  @param  DataCount           The Data count to be transferred.\r
+  @param  Timeout             The timeout Value of non Data transfer.\r
+\r
+  @retval EFI_DEVICE_ERROR    The PIO Data transfer abort with error occurs.\r
+  @retval EFI_TIMEOUT         The operation is time out.\r
+  @retval EFI_UNSUPPORTED     The device is not ready for transfer.\r
+  @retval EFI_SUCCESS         The PIO Data transfer executes successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AhciPioTransfer (\r
+  IN     EFI_AHCI_REGISTERS         *AhciRegisters,\r
+  IN     UINT8                      Port,\r
+  IN     UINT8                      PortMultiplier,\r
+  IN     EFI_AHCI_ATAPI_COMMAND     *AtapiCommand OPTIONAL,\r
+  IN     UINT8                      AtapiCommandLength,\r
+  IN     BOOLEAN                    Read,\r
+  IN     EFI_ATA_COMMAND_BLOCK      *AtaCommandBlock,\r
+  IN OUT EFI_ATA_STATUS_BLOCK       *AtaStatusBlock,\r
+  IN OUT VOID                       *MemoryAddr,\r
+  IN     UINT32                     DataCount,\r
+  IN     UINT64                     Timeout\r
+  );\r
+\r
+\r
+#endif\r
+\r
diff --git a/SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalIdeMode.c b/SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalIdeMode.c
new file mode 100644 (file)
index 0000000..7620462
--- /dev/null
@@ -0,0 +1,767 @@
+/** @file\r
+  This driver is used for Opal Password Feature support at IDE mode.\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
+  Write multiple words of Data to the IDE Data port.\r
+  Call the IO abstraction once to do the complete read,\r
+  not one word at a time\r
+\r
+  @param  Port       IO port to read\r
+  @param  Count      No. of UINT16's to read\r
+  @param  Buffer     Pointer to the Data Buffer for read\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+IdeWritePortWMultiple (\r
+  IN  UINT16                Port,\r
+  IN  UINTN                 Count,\r
+  IN  UINT16                *Buffer\r
+  )\r
+{\r
+  UINTN Index;\r
+\r
+  for (Index = 0; Index < Count; Index++) {\r
+    IoWrite16 (Port, Buffer[Index]);\r
+  }\r
+}\r
+\r
+/**\r
+  Reads multiple words of Data from the IDE Data port.\r
+  Call the IO abstraction once to do the complete read,\r
+  not one word at a time\r
+\r
+  @param  Port     IO port to read\r
+  @param  Count    Number of UINT16's to read\r
+  @param  Buffer   Pointer to the Data Buffer for read\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+IdeReadPortWMultiple (\r
+  IN  UINT16                Port,\r
+  IN  UINTN                 Count,\r
+  IN  UINT16                *Buffer\r
+  )\r
+{\r
+  UINTN Index;\r
+\r
+  for (Index = 0; Index < Count; Index++) {\r
+    Buffer[Count] = IoRead16 (Port);\r
+  }\r
+}\r
+\r
+/**\r
+  This function is used to analyze the Status Register and print out\r
+  some debug information and if there is ERR bit set in the Status\r
+  Register, the Error Register's Value is also be parsed and print out.\r
+\r
+  @param IdeRegisters     A pointer to EFI_IDE_REGISTERS Data structure.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+DumpAllIdeRegisters (\r
+  IN     EFI_IDE_REGISTERS        *IdeRegisters\r
+  )\r
+{\r
+  ASSERT (IdeRegisters != NULL);\r
+\r
+  DEBUG_CODE_BEGIN ();\r
+  if ((IoRead8 (IdeRegisters->CmdOrStatus) & ATA_STSREG_DWF) != 0) {\r
+    DEBUG ((EFI_D_ERROR, "CheckRegisterStatus()-- %02x : Error : Write Fault\n", IoRead8 (IdeRegisters->CmdOrStatus)));\r
+  }\r
+\r
+  if ((IoRead8 (IdeRegisters->CmdOrStatus) & ATA_STSREG_CORR) != 0) {\r
+    DEBUG ((EFI_D_ERROR, "CheckRegisterStatus()-- %02x : Error : Corrected Data\n", IoRead8 (IdeRegisters->CmdOrStatus)));\r
+  }\r
+\r
+  if ((IoRead8 (IdeRegisters->CmdOrStatus) & ATA_STSREG_ERR) != 0) {\r
+    if ((IoRead8 (IdeRegisters->ErrOrFeature) & ATA_ERRREG_BBK) != 0) {\r
+      DEBUG ((EFI_D_ERROR, "CheckRegisterStatus()-- %02x : Error : Bad Block Detected\n", IoRead8 (IdeRegisters->ErrOrFeature)));\r
+    }\r
+\r
+    if ((IoRead8 (IdeRegisters->ErrOrFeature) & ATA_ERRREG_UNC) != 0) {\r
+      DEBUG ((EFI_D_ERROR, "CheckRegisterStatus()-- %02x : Error : Uncorrectable Data\n", IoRead8 (IdeRegisters->ErrOrFeature)));\r
+    }\r
+\r
+    if ((IoRead8 (IdeRegisters->ErrOrFeature) & ATA_ERRREG_MC) != 0) {\r
+      DEBUG ((EFI_D_ERROR, "CheckRegisterStatus()-- %02x : Error : Media Change\n", IoRead8 (IdeRegisters->ErrOrFeature)));\r
+    }\r
+\r
+    if ((IoRead8 (IdeRegisters->ErrOrFeature) & ATA_ERRREG_ABRT) != 0) {\r
+      DEBUG ((EFI_D_ERROR, "CheckRegisterStatus()-- %02x : Error : Abort\n", IoRead8 (IdeRegisters->ErrOrFeature)));\r
+    }\r
+\r
+    if ((IoRead8 (IdeRegisters->ErrOrFeature) & ATA_ERRREG_TK0NF) != 0) {\r
+      DEBUG ((EFI_D_ERROR, "CheckRegisterStatus()-- %02x : Error : Track 0 Not Found\n", IoRead8 (IdeRegisters->ErrOrFeature)));\r
+    }\r
+\r
+    if ((IoRead8 (IdeRegisters->ErrOrFeature) & ATA_ERRREG_AMNF) != 0) {\r
+      DEBUG ((EFI_D_ERROR, "CheckRegisterStatus()-- %02x : Error : Address Mark Not Found\n", IoRead8 (IdeRegisters->ErrOrFeature)));\r
+    }\r
+  }\r
+  DEBUG_CODE_END ();\r
+}\r
+\r
+/**\r
+  This function is used to analyze the Status Register and print out\r
+  some debug information and if there is ERR bit set in the Status\r
+  Register, the Error Register's Value is also be parsed and print out.\r
+\r
+  @param IdeRegisters     A pointer to EFI_IDE_REGISTERS Data structure.\r
+\r
+  @retval EFI_SUCCESS       No err information in the Status Register.\r
+  @retval EFI_DEVICE_ERROR  Any err information in the Status Register.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+CheckStatusRegister (\r
+  IN  EFI_IDE_REGISTERS        *IdeRegisters\r
+  )\r
+{\r
+  EFI_STATUS      Status;\r
+  UINT8           StatusRegister;\r
+\r
+  ASSERT (IdeRegisters != NULL);\r
+\r
+  StatusRegister = IoRead8 (IdeRegisters->CmdOrStatus);\r
+\r
+  if ((StatusRegister & (ATA_STSREG_ERR | ATA_STSREG_DWF | ATA_STSREG_CORR)) == 0) {\r
+    Status = EFI_SUCCESS;\r
+  } else {\r
+    Status = EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  This function is used to poll for the DRQ bit clear in the Status\r
+  Register. DRQ is cleared when the device is finished transferring Data.\r
+  So this function is called after Data transfer is finished.\r
+\r
+  @param IdeRegisters     A pointer to EFI_IDE_REGISTERS Data structure.\r
+  @param Timeout          The time to complete the command.\r
+\r
+  @retval EFI_SUCCESS     DRQ bit clear within the time out.\r
+  @retval EFI_TIMEOUT     DRQ bit not clear within the time out.\r
+\r
+  @note\r
+  Read Status Register will clear interrupt status.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+DRQClear (\r
+  IN  EFI_IDE_REGISTERS         *IdeRegisters,\r
+  IN  UINT64                    Timeout\r
+  )\r
+{\r
+  UINT32  Delay;\r
+  UINT8   StatusRegister;\r
+\r
+  ASSERT (IdeRegisters != NULL);\r
+\r
+  Delay = (UINT32) (DivU64x32(Timeout, 1000) + 1);\r
+  do {\r
+    StatusRegister = IoRead8 (IdeRegisters->CmdOrStatus);\r
+\r
+    //\r
+    // wait for BSY == 0 and DRQ == 0\r
+    //\r
+    if ((StatusRegister & ATA_STSREG_BSY) == 0) {\r
+\r
+      if ((StatusRegister & ATA_STSREG_DRQ) == ATA_STSREG_DRQ) {\r
+        return EFI_DEVICE_ERROR;\r
+      } else {\r
+        return EFI_SUCCESS;\r
+      }\r
+    }\r
+\r
+    //\r
+    //  Stall for 100 microseconds.\r
+    //\r
+    MicroSecondDelay (100);\r
+\r
+    Delay--;\r
+\r
+  } while (Delay > 0);\r
+\r
+    return EFI_TIMEOUT;\r
+}\r
+/**\r
+  This function is used to poll for the DRQ bit clear in the Alternate\r
+  Status Register. DRQ is cleared when the device is finished\r
+  transferring Data. So this function is called after Data transfer\r
+  is finished.\r
+\r
+  @param IdeRegisters     A pointer to EFI_IDE_REGISTERS Data structure.\r
+  @param Timeout          The time to complete the command.\r
+\r
+  @retval EFI_SUCCESS     DRQ bit clear within the time out.\r
+\r
+  @retval EFI_TIMEOUT     DRQ bit not clear within the time out.\r
+  @note   Read Alternate Status Register will not clear interrupt status.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+DRQClear2 (\r
+  IN  EFI_IDE_REGISTERS    *IdeRegisters,\r
+  IN  UINT64               Timeout\r
+  )\r
+{\r
+  UINT32  Delay;\r
+  UINT8   AltRegister;\r
+\r
+  ASSERT (IdeRegisters != NULL);\r
+\r
+  Delay = (UINT32) (DivU64x32(Timeout, 1000) + 1);\r
+  do {\r
+    AltRegister = IoRead8 (IdeRegisters->AltOrDev);\r
+\r
+    //\r
+    //  wait for BSY == 0 and DRQ == 0\r
+    //\r
+    if ((AltRegister & ATA_STSREG_BSY) == 0) {\r
+      if ((AltRegister & ATA_STSREG_DRQ) == ATA_STSREG_DRQ) {\r
+        return EFI_DEVICE_ERROR;\r
+      } else {\r
+        return EFI_SUCCESS;\r
+      }\r
+    }\r
+\r
+    //\r
+    // Stall for 100 microseconds.\r
+    //\r
+    MicroSecondDelay (100);\r
+\r
+    Delay--;\r
+\r
+  } while (Delay > 0);\r
+\r
+  return EFI_TIMEOUT;\r
+}\r
+\r
+\r
+/**\r
+  This function is used to poll for the DRQ bit set in the Alternate Status Register.\r
+  DRQ is set when the device is ready to transfer Data. So this function is called after\r
+  the command is sent to the device and before required Data is transferred.\r
+\r
+  @param IdeRegisters     A pointer to EFI_IDE_REGISTERS Data structure.\r
+  @param Timeout          The time to complete the command.\r
+\r
+  @retval EFI_SUCCESS     DRQ bit set within the time out.\r
+  @retval EFI_TIMEOUT     DRQ bit not set within the time out.\r
+  @retval EFI_ABORTED     DRQ bit not set caused by the command abort.\r
+  @note  Read Alternate Status Register will not clear interrupt status.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+DRQReady2 (\r
+  IN  EFI_IDE_REGISTERS    *IdeRegisters,\r
+  IN  UINT64               Timeout\r
+  )\r
+{\r
+  UINT32  Delay;\r
+  UINT8   AltRegister;\r
+  UINT8   ErrorRegister;\r
+\r
+  ASSERT (IdeRegisters != NULL);\r
+\r
+  Delay = (UINT32) (DivU64x32(Timeout, 1000) + 1);\r
+\r
+  do {\r
+    //\r
+    //  Read Alternate Status Register will not clear interrupt status\r
+    //\r
+    AltRegister = IoRead8 (IdeRegisters->AltOrDev);\r
+    //\r
+    // BSY == 0 , DRQ == 1\r
+    //\r
+    if ((AltRegister & ATA_STSREG_BSY) == 0) {\r
+      if ((AltRegister & ATA_STSREG_ERR) == ATA_STSREG_ERR) {\r
+        ErrorRegister = IoRead8 (IdeRegisters->ErrOrFeature);\r
+\r
+        if ((ErrorRegister & ATA_ERRREG_ABRT) == ATA_ERRREG_ABRT) {\r
+          return EFI_ABORTED;\r
+        }\r
+        return EFI_DEVICE_ERROR;\r
+      }\r
+\r
+      if ((AltRegister & ATA_STSREG_DRQ) == ATA_STSREG_DRQ) {\r
+        return EFI_SUCCESS;\r
+      } else {\r
+        return EFI_NOT_READY;\r
+      }\r
+    }\r
+\r
+    //\r
+    // Stall for 100 microseconds.\r
+    //\r
+    MicroSecondDelay (100);\r
+\r
+    Delay--;\r
+  } while (Delay > 0);\r
+\r
+  return EFI_TIMEOUT;\r
+}\r
+\r
+/**\r
+  This function is used to poll for the BSY bit clear in the Status Register. BSY\r
+  is clear when the device is not busy. Every command must be sent after device is not busy.\r
+\r
+  @param IdeRegisters     A pointer to EFI_IDE_REGISTERS Data structure.\r
+  @param Timeout          The time to complete the command.\r
+\r
+  @retval EFI_SUCCESS     BSY bit clear within the time out.\r
+  @retval EFI_TIMEOUT     BSY bit not clear within the time out.\r
+\r
+  @note Read Status Register will clear interrupt status.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+WaitForBSYClear (\r
+  IN  EFI_IDE_REGISTERS    *IdeRegisters,\r
+  IN  UINT64               Timeout\r
+  )\r
+{\r
+  UINT32  Delay;\r
+  UINT8   StatusRegister;\r
+\r
+  ASSERT (IdeRegisters != NULL);\r
+\r
+  Delay = (UINT32) (DivU64x32(Timeout, 1000) + 1);\r
+  do {\r
+    StatusRegister = IoRead8 (IdeRegisters->CmdOrStatus);\r
+\r
+    if ((StatusRegister & ATA_STSREG_BSY) == 0x00) {\r
+      return EFI_SUCCESS;\r
+    }\r
+\r
+    //\r
+    // Stall for 100 microseconds.\r
+    //\r
+    MicroSecondDelay (100);\r
+\r
+    Delay--;\r
+\r
+  } while (Delay > 0);\r
+\r
+  return EFI_TIMEOUT;\r
+}\r
+\r
+/**\r
+  Get IDE i/o port registers' base addresses by mode.\r
+\r
+  In 'Compatibility' mode, use fixed addresses.\r
+  In Native-PCI mode, get base addresses from BARs in the PCI IDE controller's\r
+  Configuration Space.\r
+\r
+  The steps to get IDE i/o port registers' base addresses for each channel\r
+  as follows:\r
+\r
+  1. Examine the Programming Interface byte of the Class Code fields in PCI IDE\r
+  controller's Configuration Space to determine the operating mode.\r
+\r
+  2. a) In 'Compatibility' mode, use fixed addresses shown in the Table 1 below.\r
+   ___________________________________________\r
+  |           | Command Block | Control Block |\r
+  |  Channel  |   Registers   |   Registers   |\r
+  |___________|_______________|_______________|\r
+  |  Primary  |  1F0h - 1F7h  |  3F6h - 3F7h  |\r
+  |___________|_______________|_______________|\r
+  | Secondary |  170h - 177h  |  376h - 377h  |\r
+  |___________|_______________|_______________|\r
+\r
+  Table 1. Compatibility resource mappings\r
+\r
+  b) In Native-PCI mode, IDE registers are mapped into IO space using the BARs\r
+  in IDE controller's PCI Configuration Space, shown in the Table 2 below.\r
+   ___________________________________________________\r
+  |           |   Command Block   |   Control Block   |\r
+  |  Channel  |     Registers     |     Registers     |\r
+  |___________|___________________|___________________|\r
+  |  Primary  | BAR at offset 0x10| BAR at offset 0x14|\r
+  |___________|___________________|___________________|\r
+  | Secondary | BAR at offset 0x18| BAR at offset 0x1C|\r
+  |___________|___________________|___________________|\r
+\r
+  Table 2. BARs for Register Mapping\r
+\r
+  @param[in] Bus                 The bus number of ata host controller.\r
+  @param[in] Device              The device number of ata host controller.\r
+  @param[in] Function            The function number of ata host controller.\r
+  @param[in, out] IdeRegisters   Pointer to EFI_IDE_REGISTERS which is used to\r
+                                 store the IDE i/o port registers' base addresses\r
+\r
+  @retval EFI_UNSUPPORTED        Return this Value when the BARs is not IO type\r
+  @retval EFI_SUCCESS            Get the Base address successfully\r
+  @retval Other                  Read the pci configureation Data error\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+GetIdeRegisterIoAddr (\r
+  IN     UINTN                       Bus,\r
+  IN     UINTN                       Device,\r
+  IN     UINTN                       Function,\r
+  IN OUT EFI_IDE_REGISTERS           *IdeRegisters\r
+  )\r
+{\r
+  UINT16            CommandBlockBaseAddr;\r
+  UINT16            ControlBlockBaseAddr;\r
+  UINT8             ClassCode;\r
+  UINT32            BaseAddress[4];\r
+\r
+  if (IdeRegisters == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  ClassCode = PciRead8 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x9));\r
+  BaseAddress[0] = PciRead32 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x10));\r
+  BaseAddress[1] = PciRead32 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x14));\r
+  BaseAddress[2] = PciRead32 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x18));\r
+  BaseAddress[3] = PciRead32 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x1C));\r
+\r
+  if ((ClassCode & IDE_PRIMARY_OPERATING_MODE) == 0) {\r
+    CommandBlockBaseAddr = 0x1f0;\r
+    ControlBlockBaseAddr = 0x3f6;\r
+  } else {\r
+    //\r
+    // The BARs should be of IO type\r
+    //\r
+    if ((BaseAddress[0] & BIT0) == 0 ||\r
+        (BaseAddress[1] & BIT0) == 0) {\r
+      return EFI_UNSUPPORTED;\r
+    }\r
+\r
+    CommandBlockBaseAddr = (UINT16) (BaseAddress[0] & 0x0000fff8);\r
+    ControlBlockBaseAddr = (UINT16) ((BaseAddress[1] & 0x0000fffc) + 2);\r
+  }\r
+\r
+  //\r
+  // Calculate IDE primary channel I/O register base address.\r
+  //\r
+  IdeRegisters[EfiIdePrimary].Data              = CommandBlockBaseAddr;\r
+  IdeRegisters[EfiIdePrimary].ErrOrFeature      = (UINT16) (CommandBlockBaseAddr + 0x01);\r
+  IdeRegisters[EfiIdePrimary].SectorCount       = (UINT16) (CommandBlockBaseAddr + 0x02);\r
+  IdeRegisters[EfiIdePrimary].SectorNumber      = (UINT16) (CommandBlockBaseAddr + 0x03);\r
+  IdeRegisters[EfiIdePrimary].CylinderLsb       = (UINT16) (CommandBlockBaseAddr + 0x04);\r
+  IdeRegisters[EfiIdePrimary].CylinderMsb       = (UINT16) (CommandBlockBaseAddr + 0x05);\r
+  IdeRegisters[EfiIdePrimary].Head              = (UINT16) (CommandBlockBaseAddr + 0x06);\r
+  IdeRegisters[EfiIdePrimary].CmdOrStatus       = (UINT16) (CommandBlockBaseAddr + 0x07);\r
+  IdeRegisters[EfiIdePrimary].AltOrDev          = ControlBlockBaseAddr;\r
+\r
+  if ((ClassCode & IDE_SECONDARY_OPERATING_MODE) == 0) {\r
+    CommandBlockBaseAddr = 0x170;\r
+    ControlBlockBaseAddr = 0x376;\r
+  } else {\r
+    //\r
+    // The BARs should be of IO type\r
+    //\r
+    if ((BaseAddress[2] & BIT0) == 0 ||\r
+        (BaseAddress[3] & BIT0) == 0) {\r
+      return EFI_UNSUPPORTED;\r
+    }\r
+\r
+    CommandBlockBaseAddr = (UINT16) (BaseAddress[2] & 0x0000fff8);\r
+    ControlBlockBaseAddr = (UINT16) ((BaseAddress[3] & 0x0000fffc) + 2);\r
+  }\r
+\r
+  //\r
+  // Calculate IDE secondary channel I/O register base address.\r
+  //\r
+  IdeRegisters[EfiIdeSecondary].Data              = CommandBlockBaseAddr;\r
+  IdeRegisters[EfiIdeSecondary].ErrOrFeature      = (UINT16) (CommandBlockBaseAddr + 0x01);\r
+  IdeRegisters[EfiIdeSecondary].SectorCount       = (UINT16) (CommandBlockBaseAddr + 0x02);\r
+  IdeRegisters[EfiIdeSecondary].SectorNumber      = (UINT16) (CommandBlockBaseAddr + 0x03);\r
+  IdeRegisters[EfiIdeSecondary].CylinderLsb       = (UINT16) (CommandBlockBaseAddr + 0x04);\r
+  IdeRegisters[EfiIdeSecondary].CylinderMsb       = (UINT16) (CommandBlockBaseAddr + 0x05);\r
+  IdeRegisters[EfiIdeSecondary].Head              = (UINT16) (CommandBlockBaseAddr + 0x06);\r
+  IdeRegisters[EfiIdeSecondary].CmdOrStatus       = (UINT16) (CommandBlockBaseAddr + 0x07);\r
+  IdeRegisters[EfiIdeSecondary].AltOrDev          = ControlBlockBaseAddr;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Send ATA Ext command into device with NON_DATA protocol.\r
+\r
+  @param IdeRegisters     A pointer to EFI_IDE_REGISTERS Data structure.\r
+  @param AtaCommandBlock  A pointer to EFI_ATA_COMMAND_BLOCK Data structure.\r
+  @param Timeout          The time to complete the command.\r
+\r
+  @retval  EFI_SUCCESS Reading succeed\r
+  @retval  EFI_DEVICE_ERROR Error executing commands on this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AtaIssueCommand (\r
+  IN  EFI_IDE_REGISTERS         *IdeRegisters,\r
+  IN  EFI_ATA_COMMAND_BLOCK     *AtaCommandBlock,\r
+  IN  UINT64                    Timeout\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+  UINT8       DeviceHead;\r
+  UINT8       AtaCommand;\r
+\r
+  ASSERT (IdeRegisters != NULL);\r
+  ASSERT (AtaCommandBlock != NULL);\r
+\r
+  DeviceHead = AtaCommandBlock->AtaDeviceHead;\r
+  AtaCommand = AtaCommandBlock->AtaCommand;\r
+\r
+  Status = WaitForBSYClear (IdeRegisters, Timeout);\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  //\r
+  // Select device (bit4), set LBA mode(bit6) (use 0xe0 for compatibility)\r
+  //\r
+  IoWrite8 (IdeRegisters->Head, (UINT8) (0xe0 | DeviceHead));\r
+\r
+  //\r
+  // set all the command parameters\r
+  // Before write to all the following registers, BSY and DRQ must be 0.\r
+  //\r
+  Status = DRQClear2 (IdeRegisters, Timeout);\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  //\r
+  // Fill the feature register, which is a two-byte FIFO. Need write twice.\r
+  //\r
+  IoWrite8 (IdeRegisters->ErrOrFeature, AtaCommandBlock->AtaFeaturesExp);\r
+  IoWrite8 (IdeRegisters->ErrOrFeature, AtaCommandBlock->AtaFeatures);\r
+\r
+  //\r
+  // Fill the sector count register, which is a two-byte FIFO. Need write twice.\r
+  //\r
+  IoWrite8 (IdeRegisters->SectorCount, AtaCommandBlock->AtaSectorCountExp);\r
+  IoWrite8 (IdeRegisters->SectorCount, AtaCommandBlock->AtaSectorCount);\r
+\r
+  //\r
+  // Fill the start LBA registers, which are also two-byte FIFO\r
+  //\r
+  IoWrite8 (IdeRegisters->SectorNumber, AtaCommandBlock->AtaSectorNumberExp);\r
+  IoWrite8 (IdeRegisters->SectorNumber, AtaCommandBlock->AtaSectorNumber);\r
+\r
+  IoWrite8 (IdeRegisters->CylinderLsb, AtaCommandBlock->AtaCylinderLowExp);\r
+  IoWrite8 (IdeRegisters->CylinderLsb, AtaCommandBlock->AtaCylinderLow);\r
+\r
+  IoWrite8 (IdeRegisters->CylinderMsb, AtaCommandBlock->AtaCylinderHighExp);\r
+  IoWrite8 (IdeRegisters->CylinderMsb, AtaCommandBlock->AtaCylinderHigh);\r
+\r
+  //\r
+  // Send command via Command Register\r
+  //\r
+  IoWrite8 (IdeRegisters->CmdOrStatus, AtaCommand);\r
+\r
+  //\r
+  // Stall at least 400 microseconds.\r
+  //\r
+  MicroSecondDelay (400);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  This function is used to send out ATA commands conforms to the PIO Data In Protocol.\r
+\r
+  @param IdeRegisters     A pointer to EFI_IDE_REGISTERS Data structure.\r
+  @param Buffer           A pointer to the source Buffer for the Data.\r
+  @param ByteCount        The Length of  the Data.\r
+  @param Read             Flag used to determine the Data transfer direction.\r
+                          Read equals 1, means Data transferred from device to host;\r
+                          Read equals 0, means Data transferred from host to device.\r
+  @param AtaCommandBlock  A pointer to EFI_ATA_COMMAND_BLOCK Data structure.\r
+  @param AtaStatusBlock   A pointer to EFI_ATA_STATUS_BLOCK Data structure.\r
+  @param Timeout          The time to complete the command.\r
+\r
+  @retval EFI_SUCCESS      send out the ATA command and device send required Data successfully.\r
+  @retval EFI_DEVICE_ERROR command sent failed.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AtaPioDataInOut (\r
+  IN     EFI_IDE_REGISTERS         *IdeRegisters,\r
+  IN OUT VOID                      *Buffer,\r
+  IN     UINT64                    ByteCount,\r
+  IN     BOOLEAN                   Read,\r
+  IN     EFI_ATA_COMMAND_BLOCK     *AtaCommandBlock,\r
+  IN OUT EFI_ATA_STATUS_BLOCK      *AtaStatusBlock,\r
+  IN     UINT64                    Timeout\r
+  )\r
+{\r
+  UINTN       WordCount;\r
+  UINTN       Increment;\r
+  UINT16      *Buffer16;\r
+  EFI_STATUS  Status;\r
+\r
+  if ((IdeRegisters == NULL) || (Buffer == NULL) || (AtaCommandBlock == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // Issue ATA command\r
+  //\r
+  Status = AtaIssueCommand (IdeRegisters, AtaCommandBlock, Timeout);\r
+  if (EFI_ERROR (Status)) {\r
+    Status = EFI_DEVICE_ERROR;\r
+    goto Exit;\r
+  }\r
+\r
+  Buffer16 = (UINT16 *) Buffer;\r
+\r
+  //\r
+  // According to PIO Data in protocol, host can perform a series of reads to\r
+  // the Data register after each time device set DRQ ready;\r
+  // The Data Size of "a series of read" is command specific.\r
+  // For most ATA command, Data Size received from device will not exceed\r
+  // 1 sector, hence the Data Size for "a series of read" can be the whole Data\r
+  // Size of one command request.\r
+  // For ATA command such as Read Sector command, the Data Size of one ATA\r
+  // command request is often larger than 1 sector, according to the\r
+  // Read Sector command, the Data Size of "a series of read" is exactly 1\r
+  // sector.\r
+  // Here for simplification reason, we specify the Data Size for\r
+  // "a series of read" to 1 sector (256 words) if Data Size of one ATA command\r
+  // request is larger than 256 words.\r
+  //\r
+  Increment = 256;\r
+\r
+  //\r
+  // used to record bytes of currently transfered Data\r
+  //\r
+  WordCount = 0;\r
+\r
+  while (WordCount < RShiftU64(ByteCount, 1)) {\r
+    //\r
+    // Poll DRQ bit set, Data transfer can be performed only when DRQ is ready\r
+    //\r
+    Status = DRQReady2 (IdeRegisters, Timeout);\r
+    if (EFI_ERROR (Status)) {\r
+      Status = EFI_DEVICE_ERROR;\r
+      goto Exit;\r
+    }\r
+\r
+    //\r
+    // Get the byte count for one series of read\r
+    //\r
+    if ((WordCount + Increment) > RShiftU64(ByteCount, 1)) {\r
+      Increment = (UINTN)(RShiftU64(ByteCount, 1) - WordCount);\r
+    }\r
+\r
+    if (Read) {\r
+      IdeReadPortWMultiple (\r
+        IdeRegisters->Data,\r
+        Increment,\r
+        Buffer16\r
+        );\r
+    } else {\r
+      IdeWritePortWMultiple (\r
+        IdeRegisters->Data,\r
+        Increment,\r
+        Buffer16\r
+        );\r
+    }\r
+\r
+    Status = CheckStatusRegister (IdeRegisters);\r
+    if (EFI_ERROR (Status)) {\r
+      Status = EFI_DEVICE_ERROR;\r
+      goto Exit;\r
+    }\r
+\r
+    WordCount += Increment;\r
+    Buffer16  += Increment;\r
+  }\r
+\r
+  Status = DRQClear (IdeRegisters, Timeout);\r
+  if (EFI_ERROR (Status)) {\r
+    Status = EFI_DEVICE_ERROR;\r
+    goto Exit;\r
+  }\r
+\r
+Exit:\r
+  //\r
+  // Dump All Ide registers to ATA_STATUS_BLOCK\r
+  //\r
+  DumpAllIdeRegisters (IdeRegisters);\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Sends out an ATA Identify Command to the specified device.\r
+\r
+  This function sends out the ATA Identify Command to the\r
+  specified device. Only ATA device responses to this command. If\r
+  the command succeeds, it returns the Identify Data structure which\r
+  contains information about the device. This function extracts the\r
+  information it needs to fill the IDE_BLK_IO_DEV Data structure,\r
+  including device type, media block Size, media capacity, and etc.\r
+\r
+  @param IdeRegisters     A pointer to EFI_IDE_REGISTERS Data structure.\r
+  @param Channel          The channel number of device.\r
+  @param Device           The device number of device.\r
+  @param Buffer           A pointer to Data Buffer which is used to contain IDENTIFY Data.\r
+\r
+  @retval EFI_SUCCESS          Identify ATA device successfully.\r
+  @retval EFI_DEVICE_ERROR     ATA Identify Device Command failed or device is not ATA device.\r
+  @retval EFI_OUT_OF_RESOURCES Allocate memory failed.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AtaIdentify (\r
+  IN     EFI_IDE_REGISTERS             *IdeRegisters,\r
+  IN     UINT8                         Channel,\r
+  IN     UINT8                         Device,\r
+  IN OUT ATA_IDENTIFY_DATA             *Buffer\r
+  )\r
+{\r
+  EFI_STATUS             Status;\r
+  EFI_ATA_COMMAND_BLOCK  AtaCommandBlock;\r
+\r
+  ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK));\r
+\r
+  AtaCommandBlock.AtaCommand    = ATA_CMD_IDENTIFY_DRIVE;\r
+  AtaCommandBlock.AtaDeviceHead = (UINT8)(Device << 0x4);\r
+\r
+  Status = AtaPioDataInOut (\r
+             IdeRegisters,\r
+             Buffer,\r
+             sizeof (ATA_IDENTIFY_DATA),\r
+             TRUE,\r
+             &AtaCommandBlock,\r
+             NULL,\r
+             ATA_TIMEOUT\r
+             );\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+\r
diff --git a/SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalIdeMode.h b/SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalIdeMode.h
new file mode 100644 (file)
index 0000000..ba94a97
--- /dev/null
@@ -0,0 +1,173 @@
+/** @file\r
+  Header file for IDE mode of ATA host controller.\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
+\r
+\r
+#ifndef __OPAL_PASSWORD_IDE_MODE_H__\r
+#define __OPAL_PASSWORD_IDE_MODE_H__\r
+\r
+typedef enum {\r
+  EfiIdePrimary    = 0,\r
+  EfiIdeSecondary  = 1,\r
+  EfiIdeMaxChannel = 2\r
+} EFI_IDE_CHANNEL;\r
+\r
+typedef enum {\r
+  EfiIdeMaster     = 0,\r
+  EfiIdeSlave      = 1,\r
+  EfiIdeMaxDevice  = 2\r
+} EFI_IDE_DEVICE;\r
+\r
+//\r
+// IDE registers set\r
+//\r
+typedef struct {\r
+  UINT16                          Data;\r
+  UINT16                          ErrOrFeature;\r
+  UINT16                          SectorCount;\r
+  UINT16                          SectorNumber;\r
+  UINT16                          CylinderLsb;\r
+  UINT16                          CylinderMsb;\r
+  UINT16                          Head;\r
+  UINT16                          CmdOrStatus;\r
+  UINT16                          AltOrDev;\r
+} EFI_IDE_REGISTERS;\r
+\r
+//\r
+// Bit definitions in Programming Interface byte of the Class Code field\r
+// in PCI IDE controller's Configuration Space\r
+//\r
+#define IDE_PRIMARY_OPERATING_MODE            BIT0\r
+#define IDE_PRIMARY_PROGRAMMABLE_INDICATOR    BIT1\r
+#define IDE_SECONDARY_OPERATING_MODE          BIT2\r
+#define IDE_SECONDARY_PROGRAMMABLE_INDICATOR  BIT3\r
+\r
+/**\r
+  Get IDE i/o port registers' base addresses by mode.\r
+\r
+  In 'Compatibility' mode, use fixed addresses.\r
+  In Native-PCI mode, get base addresses from BARs in the PCI IDE controller's\r
+  Configuration Space.\r
+\r
+  The steps to get IDE i/o port registers' base addresses for each channel\r
+  as follows:\r
+\r
+  1. Examine the Programming Interface byte of the Class Code fields in PCI IDE\r
+  controller's Configuration Space to determine the operating mode.\r
+\r
+  2. a) In 'Compatibility' mode, use fixed addresses shown in the Table 1 below.\r
+   ___________________________________________\r
+  |           | Command Block | Control Block |\r
+  |  Channel  |   Registers   |   Registers   |\r
+  |___________|_______________|_______________|\r
+  |  Primary  |  1F0h - 1F7h  |  3F6h - 3F7h  |\r
+  |___________|_______________|_______________|\r
+  | Secondary |  170h - 177h  |  376h - 377h  |\r
+  |___________|_______________|_______________|\r
+\r
+  Table 1. Compatibility resource mappings\r
+\r
+  b) In Native-PCI mode, IDE registers are mapped into IO space using the BARs\r
+  in IDE controller's PCI Configuration Space, shown in the Table 2 below.\r
+   ___________________________________________________\r
+  |           |   Command Block   |   Control Block   |\r
+  |  Channel  |     Registers     |     Registers     |\r
+  |___________|___________________|___________________|\r
+  |  Primary  | BAR at offset 0x10| BAR at offset 0x14|\r
+  |___________|___________________|___________________|\r
+  | Secondary | BAR at offset 0x18| BAR at offset 0x1C|\r
+  |___________|___________________|___________________|\r
+\r
+  Table 2. BARs for Register Mapping\r
+\r
+  @param[in] Bus                 The bus number of ata host controller.\r
+  @param[in] Device              The device number of ata host controller.\r
+  @param[in] Function            The function number of ata host controller.\r
+  @param[in, out] IdeRegisters   Pointer to EFI_IDE_REGISTERS which is used to\r
+                                 store the IDE i/o port registers' base addresses\r
+\r
+  @retval EFI_UNSUPPORTED        Return this Value when the BARs is not IO type\r
+  @retval EFI_SUCCESS            Get the Base address successfully\r
+  @retval Other                  Read the pci configureation Data error\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+GetIdeRegisterIoAddr (\r
+  IN     UINTN                       Bus,\r
+  IN     UINTN                       Device,\r
+  IN     UINTN                       Function,\r
+  IN OUT EFI_IDE_REGISTERS           *IdeRegisters\r
+  );\r
+\r
+/**\r
+  Sends out an ATA Identify Command to the specified device.\r
+\r
+  This function sends out the ATA Identify Command to the\r
+  specified device. Only ATA device responses to this command. If\r
+  the command succeeds, it returns the Identify Data structure which\r
+  contains information about the device. This function extracts the\r
+  information it needs to fill the IDE_BLK_IO_DEV Data structure,\r
+  including device type, media block Size, media capacity, and etc.\r
+\r
+  @param IdeRegisters     A pointer to EFI_IDE_REGISTERS Data structure.\r
+  @param Channel          The channel number of device.\r
+  @param Device           The device number of device.\r
+  @param Buffer           A pointer to Data Buffer which is used to contain IDENTIFY Data.\r
+\r
+  @retval EFI_SUCCESS          Identify ATA device successfully.\r
+  @retval EFI_DEVICE_ERROR     ATA Identify Device Command failed or device is not ATA device.\r
+  @retval EFI_OUT_OF_RESOURCES Allocate memory failed.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AtaIdentify (\r
+  IN     EFI_IDE_REGISTERS             *IdeRegisters,\r
+  IN     UINT8                         Channel,\r
+  IN     UINT8                         Device,\r
+  IN OUT ATA_IDENTIFY_DATA             *Buffer\r
+  );\r
+\r
+/**\r
+  This function is used to send out ATA commands conforms to the PIO Data In Protocol.\r
+\r
+  @param IdeRegisters     A pointer to EFI_IDE_REGISTERS Data structure.\r
+  @param Buffer           A pointer to the source Buffer for the Data.\r
+  @param ByteCount        The Length of  the Data.\r
+  @param Read             Flag used to determine the Data transfer direction.\r
+                          Read equals 1, means Data transferred from device to host;\r
+                          Read equals 0, means Data transferred from host to device.\r
+  @param AtaCommandBlock  A pointer to EFI_ATA_COMMAND_BLOCK Data structure.\r
+  @param AtaStatusBlock   A pointer to EFI_ATA_STATUS_BLOCK Data structure.\r
+  @param Timeout          The time to complete the command.\r
+\r
+  @retval EFI_SUCCESS      send out the ATA command and device send required Data successfully.\r
+  @retval EFI_DEVICE_ERROR command sent failed.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AtaPioDataInOut (\r
+  IN     EFI_IDE_REGISTERS         *IdeRegisters,\r
+  IN OUT VOID                      *Buffer,\r
+  IN     UINT64                    ByteCount,\r
+  IN     BOOLEAN                   Read,\r
+  IN     EFI_ATA_COMMAND_BLOCK     *AtaCommandBlock,\r
+  IN OUT EFI_ATA_STATUS_BLOCK      *AtaStatusBlock,\r
+  IN     UINT64                    Timeout\r
+  );\r
+\r
+\r
+#endif\r
+\r
diff --git a/SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalNvmeMode.c b/SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalNvmeMode.c
new file mode 100644 (file)
index 0000000..ae9bfd9
--- /dev/null
@@ -0,0 +1,2166 @@
+/** @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
diff --git a/SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalNvmeMode.h b/SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalNvmeMode.h
new file mode 100644 (file)
index 0000000..bfa4f10
--- /dev/null
@@ -0,0 +1,456 @@
+/** @file\r
+  Header file for NVMe function definitions\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
+#ifndef __OPAL_PASSWORD_NVME_MODE_H__\r
+#define __OPAL_PASSWORD_NVME_MODE_H__\r
+\r
+\r
+#include "OpalNvmeReg.h"\r
+\r
+#define NVME_MAX_SECTORS            0x10000\r
+//\r
+// QueueId\r
+//\r
+#define NVME_ADMIN_QUEUE            0x00\r
+#define NVME_IO_QUEUE               0x01\r
+\r
+typedef struct {\r
+  UINT8                             Opcode;\r
+  UINT8                             FusedOperation;\r
+    #define NORMAL_CMD              0x00\r
+    #define FUSED_FIRST_CMD         0x01\r
+    #define FUSED_SECOND_CMD        0x02\r
+  UINT16                            Cid;\r
+} NVME_CDW0;\r
+\r
+typedef struct {\r
+  NVME_CDW0                         Cdw0;\r
+  UINT8                             Flags;\r
+    #define CDW10_VALID             0x01\r
+    #define CDW11_VALID             0x02\r
+    #define CDW12_VALID             0x04\r
+    #define CDW13_VALID             0x08\r
+    #define CDW14_VALID             0x10\r
+    #define CDW15_VALID             0x20\r
+  UINT32                            Nsid;\r
+  UINT32                            Cdw10;\r
+  UINT32                            Cdw11;\r
+  UINT32                            Cdw12;\r
+  UINT32                            Cdw13;\r
+  UINT32                            Cdw14;\r
+  UINT32                            Cdw15;\r
+} NVM_EXPRESS_COMMAND;\r
+\r
+typedef struct {\r
+  UINT32                            Cdw0;\r
+  UINT32                            Cdw1;\r
+  UINT32                            Cdw2;\r
+  UINT32                            Cdw3;\r
+} NVM_EXPRESS_RESPONSE;\r
+\r
+typedef struct {\r
+  UINT64                            CommandTimeout;\r
+  UINT64                            TransferBuffer;\r
+  UINT32                            TransferLength;\r
+  UINT64                            MetadataBuffer;\r
+  UINT32                            MetadataLength;\r
+  UINT8                             QueueId;\r
+  NVM_EXPRESS_COMMAND               *NvmeCmd;\r
+  NVM_EXPRESS_RESPONSE              *NvmeResponse;\r
+} NVM_EXPRESS_PASS_THRU_COMMAND_PACKET;\r
+\r
+\r
+#pragma pack(1)\r
+\r
+// Internal fields\r
+typedef enum {\r
+  NvmeStatusUnknown,\r
+  NvmeStatusInit,\r
+  NvmeStatusInuse,\r
+  NvmeStatusMax,\r
+} NVME_STATUS;\r
+\r
+typedef struct {\r
+  UINT32                            Nbar;\r
+  UINT32                            BaseMem;\r
+  BOOLEAN                           PollCancellation;\r
+  UINT16                            NvmeInitWaitTime;\r
+\r
+  NVME_STATUS                       State;\r
+  UINT8                             BusID;\r
+  UINT8                             DeviceID;\r
+  UINT8                             FuncID;\r
+  UINTN                             PciBase;\r
+\r
+  UINT32                            Nsid;\r
+  UINT64                            Nsuuid;\r
+  UINT32                            BlockSize;\r
+  EFI_LBA                           LastBlock;\r
+\r
+  //\r
+  // Pointers to 4kB aligned submission & completion queues.\r
+  //\r
+  NVME_SQ                           *SqBuffer[NVME_MAX_IO_QUEUES];\r
+  NVME_CQ                           *CqBuffer[NVME_MAX_IO_QUEUES];\r
+  UINT16                            Cid[NVME_MAX_IO_QUEUES];\r
+\r
+  //\r
+  // Submission and completion queue indices.\r
+  //\r
+  NVME_SQTDBL                       SqTdbl[NVME_MAX_IO_QUEUES];\r
+  NVME_CQHDBL                       CqHdbl[NVME_MAX_IO_QUEUES];\r
+  UINT8                             Pt[NVME_MAX_IO_QUEUES];\r
+\r
+  UINTN                             SqeCount[NVME_MAX_IO_QUEUES];\r
+\r
+  //\r
+  // Nvme controller capabilities\r
+  //\r
+  NVME_CAP                          Cap;\r
+\r
+  //\r
+  // pointer to identify controller Data\r
+  //\r
+  NVME_ADMIN_CONTROLLER_DATA        *ControllerData;\r
+  NVME_ADMIN_NAMESPACE_DATA         *NamespaceData;\r
+} NVME_CONTEXT;\r
+\r
+#pragma pack()\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
+/**\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
+/**\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
+/**\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
+/**\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
+/**\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
+/**\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
+/**\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
+/**\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
+/**\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
+/**\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
+/**\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
+/**\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
+/**\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
+/**\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
+/**\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
+/**\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
+/**\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
+#endif\r
diff --git a/SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalNvmeReg.h b/SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalNvmeReg.h
new file mode 100644 (file)
index 0000000..b5460cd
--- /dev/null
@@ -0,0 +1,814 @@
+/** @file\r
+  Header file for Registers and Structure definitions\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
+#ifndef __OPAL_PASSWORD_NVME_REG_H__\r
+#define __OPAL_PASSWORD_NVME_REG_H__\r
+\r
+//\r
+// PCI Header for PCIe root port configuration\r
+//\r
+#define NVME_PCIE_PCICMD                         0x04\r
+#define NVME_PCIE_BNUM                           0x18\r
+#define NVME_PCIE_SEC_BNUM                       0x19\r
+#define NVME_PCIE_IOBL                           0x1C\r
+#define NVME_PCIE_MBL                            0x20\r
+#define NVME_PCIE_PMBL                           0x24\r
+#define NVME_PCIE_PMBU32                         0x28\r
+#define NVME_PCIE_PMLU32                         0x2C\r
+#define NVME_PCIE_INTR                           0x3C\r
+\r
+//\r
+// NVMe related definitions\r
+//\r
+#define PCI_CLASS_MASS_STORAGE_NVM                0x08  // mass storage sub-class non-volatile memory.\r
+#define PCI_IF_NVMHCI                             0x02  // mass storage programming interface NVMHCI.\r
+\r
+#define NVME_ASQ_SIZE                                    1     // Number of admin submission queue entries, which is 0-based\r
+#define NVME_ACQ_SIZE                                    1     // Number of admin completion queue entries, which is 0-based\r
+\r
+#define NVME_CSQ_SIZE                                    63     // Number of I/O submission queue entries, which is 0-based\r
+#define NVME_CCQ_SIZE                                    63     // Number of I/O completion queue entries, which is 0-based\r
+\r
+#define NVME_MAX_IO_QUEUES                               2     // Number of I/O queues supported by the driver, 1 for AQ, 1 for CQ\r
+\r
+#define NVME_CSQ_DEPTH                                   (NVME_CSQ_SIZE+1)\r
+#define NVME_CCQ_DEPTH                                   (NVME_CCQ_SIZE+1)\r
+#define NVME_PRP_SIZE                                    (4)    // Pages of PRP list\r
+\r
+#define NVME_CONTROLLER_ID                               0\r
+\r
+//\r
+// Time out Value for Nvme transaction execution\r
+//\r
+#define NVME_GENERIC_TIMEOUT                             5000000   ///< us\r
+#define NVME_CMD_WAIT                                    100       ///< us\r
+#define NVME_CMD_TIMEOUT                                 20000000  ///< us\r
+\r
+\r
+\r
+#define NVME_MEM_MAX_SIZE \\r
+  (( \\r
+  1                                         /* Controller Data */ +  \\r
+  1                                         /* Identify Data */   +  \\r
+  1                                         /* ASQ */             +  \\r
+  1                                         /* ACQ */             +  \\r
+  1                                         /* SQs */             +  \\r
+  1                                         /* CQs */             +  \\r
+  NVME_PRP_SIZE * NVME_CSQ_DEPTH            /* PRPs */               \\r
+  ) * EFI_PAGE_SIZE)\r
+\r
+\r
+//\r
+// controller register offsets\r
+//\r
+#define NVME_CAP_OFFSET          0x0000  // Controller Capabilities\r
+#define NVME_VER_OFFSET          0x0008  // Version\r
+#define NVME_INTMS_OFFSET        0x000c  // Interrupt Mask Set\r
+#define NVME_INTMC_OFFSET        0x0010  // Interrupt Mask Clear\r
+#define NVME_CC_OFFSET           0x0014  // Controller Configuration\r
+#define NVME_CSTS_OFFSET         0x001c  // Controller Status\r
+#define NVME_AQA_OFFSET          0x0024  // Admin Queue Attributes\r
+#define NVME_ASQ_OFFSET          0x0028  // Admin Submission Queue Base Address\r
+#define NVME_ACQ_OFFSET          0x0030  // Admin Completion Queue Base Address\r
+#define NVME_SQ0_OFFSET          0x1000  // Submission Queue 0 (admin) Tail Doorbell\r
+#define NVME_CQ0_OFFSET          0x1004  // Completion Queue 0 (admin) Head Doorbell\r
+\r
+//\r
+// These register offsets are defined as 0x1000 + (N * (4 << CAP.DSTRD))\r
+// Get the doorbell stride bit shift Value from the controller capabilities.\r
+//\r
+#define NVME_SQTDBL_OFFSET(QID, DSTRD)    0x1000 + ((2 * (QID)) * (4 << (DSTRD)))       // Submission Queue y (NVM) Tail Doorbell\r
+#define NVME_CQHDBL_OFFSET(QID, DSTRD)    0x1000 + (((2 * (QID)) + 1) * (4 << (DSTRD))) // Completion Queue y (NVM) Head Doorbell\r
+\r
+\r
+#pragma pack(1)\r
+\r
+//\r
+// 3.1.1 Offset 00h: CAP - Controller Capabilities\r
+//\r
+typedef struct {\r
+  UINT16 Mqes;      // Maximum Queue Entries Supported\r
+  UINT8  Cqr:1;     // Contiguous Queues Required\r
+  UINT8  Ams:2;     // Arbitration Mechanism Supported\r
+  UINT8  Rsvd1:5;\r
+  UINT8  To;        // Timeout\r
+  UINT16 Dstrd:4;\r
+  UINT16 Rsvd2:1;\r
+  UINT16 Css:4;     // Command Sets Supported\r
+  UINT16 Rsvd3:7;\r
+  UINT8  Mpsmin:4;\r
+  UINT8  Mpsmax:4;\r
+  UINT8  Rsvd4;\r
+} NVME_CAP;\r
+\r
+//\r
+// 3.1.2 Offset 08h: VS - Version\r
+//\r
+typedef struct {\r
+  UINT16 Mnr;       // Minor version number\r
+  UINT16 Mjr;       // Major version number\r
+} NVME_VER;\r
+\r
+//\r
+// 3.1.5 Offset 14h: CC - Controller Configuration\r
+//\r
+typedef struct {\r
+  UINT16 En:1;       // Enable\r
+  UINT16 Rsvd1:3;\r
+  UINT16 Css:3;      // Command Set Selected\r
+  UINT16 Mps:4;      // Memory Page Size\r
+  UINT16 Ams:3;      // Arbitration Mechanism Selected\r
+  UINT16 Shn:2;      // Shutdown Notification\r
+  UINT8  Iosqes:4;   // I/O Submission Queue Entry Size\r
+  UINT8  Iocqes:4;   // I/O Completion Queue Entry Size\r
+  UINT8  Rsvd2;\r
+} NVME_CC;\r
+\r
+//\r
+// 3.1.6 Offset 1Ch: CSTS - Controller Status\r
+//\r
+typedef struct {\r
+  UINT32 Rdy:1;      // Ready\r
+  UINT32 Cfs:1;      // Controller Fatal Status\r
+  UINT32 Shst:2;     // Shutdown Status\r
+  UINT32 Nssro:1;    // NVM Subsystem Reset Occurred\r
+  UINT32 Rsvd1:27;\r
+} NVME_CSTS;\r
+\r
+//\r
+// 3.1.8 Offset 24h: AQA - Admin Queue Attributes\r
+//\r
+typedef struct {\r
+  UINT16 Asqs:12;    // Submission Queue Size\r
+  UINT16 Rsvd1:4;\r
+  UINT16 Acqs:12;    // Completion Queue Size\r
+  UINT16 Rsvd2:4;\r
+} NVME_AQA;\r
+\r
+//\r
+// 3.1.9 Offset 28h: ASQ - Admin Submission Queue Base Address\r
+//\r
+#define NVME_ASQ      UINT64\r
+\r
+//\r
+// 3.1.10 Offset 30h: ACQ - Admin Completion Queue Base Address\r
+//\r
+#define NVME_ACQ      UINT64\r
+\r
+//\r
+// 3.1.11 Offset (1000h + ((2y) * (4 << CAP.DSTRD))): SQyTDBL - Submission Queue y Tail Doorbell\r
+//\r
+typedef struct {\r
+  UINT16 Sqt;\r
+  UINT16 Rsvd1;\r
+} NVME_SQTDBL;\r
+\r
+//\r
+// 3.1.12 Offset (1000h + ((2y + 1) * (4 << CAP.DSTRD))): CQyHDBL - Completion Queue y Head Doorbell\r
+//\r
+typedef struct {\r
+  UINT16 Cqh;\r
+  UINT16 Rsvd1;\r
+} NVME_CQHDBL;\r
+\r
+//\r
+// NVM command set structures\r
+//\r
+// Read Command\r
+//\r
+typedef struct {\r
+  //\r
+  // CDW 10, 11\r
+  //\r
+  UINT64 Slba;                /* Starting Sector Address */\r
+  //\r
+  // CDW 12\r
+  //\r
+  UINT16 Nlb;                 /* Number of Sectors */\r
+  UINT16 Rsvd1:10;\r
+  UINT16 Prinfo:4;            /* Protection Info Check */\r
+  UINT16 Fua:1;               /* Force Unit Access */\r
+  UINT16 Lr:1;                /* Limited Retry */\r
+  //\r
+  // CDW 13\r
+  //\r
+  UINT32 Af:4;                /* Access Frequency */\r
+  UINT32 Al:2;                /* Access Latency */\r
+  UINT32 Sr:1;                /* Sequential Request */\r
+  UINT32 In:1;                /* Incompressible */\r
+  UINT32 Rsvd2:24;\r
+  //\r
+  // CDW 14\r
+  //\r
+  UINT32 Eilbrt;              /* Expected Initial Logical Block Reference Tag */\r
+  //\r
+  // CDW 15\r
+  //\r
+  UINT16 Elbat;               /* Expected Logical Block Application Tag */\r
+  UINT16 Elbatm;              /* Expected Logical Block Application Tag Mask */\r
+} NVME_READ;\r
+\r
+//\r
+// Write Command\r
+//\r
+typedef struct {\r
+  //\r
+  // CDW 10, 11\r
+  //\r
+  UINT64 Slba;                /* Starting Sector Address */\r
+  //\r
+  // CDW 12\r
+  //\r
+  UINT16 Nlb;                 /* Number of Sectors */\r
+  UINT16 Rsvd1:10;\r
+  UINT16 Prinfo:4;            /* Protection Info Check */\r
+  UINT16 Fua:1;               /* Force Unit Access */\r
+  UINT16 Lr:1;                /* Limited Retry */\r
+  //\r
+  // CDW 13\r
+  //\r
+  UINT32 Af:4;                /* Access Frequency */\r
+  UINT32 Al:2;                /* Access Latency */\r
+  UINT32 Sr:1;                /* Sequential Request */\r
+  UINT32 In:1;                /* Incompressible */\r
+  UINT32 Rsvd2:24;\r
+  //\r
+  // CDW 14\r
+  //\r
+  UINT32 Ilbrt;               /* Initial Logical Block Reference Tag */\r
+  //\r
+  // CDW 15\r
+  //\r
+  UINT16 Lbat;                /* Logical Block Application Tag */\r
+  UINT16 Lbatm;               /* Logical Block Application Tag Mask */\r
+} NVME_WRITE;\r
+\r
+//\r
+// Flush\r
+//\r
+typedef struct {\r
+  //\r
+  // CDW 10\r
+  //\r
+  UINT32 Flush;               /* Flush */\r
+} NVME_FLUSH;\r
+\r
+//\r
+// Write Uncorrectable command\r
+//\r
+typedef struct {\r
+  //\r
+  // CDW 10, 11\r
+  //\r
+  UINT64 Slba;                /* Starting LBA */\r
+  //\r
+  // CDW 12\r
+  //\r
+  UINT32 Nlb:16;              /* Number of  Logical Blocks */\r
+  UINT32 Rsvd1:16;\r
+} NVME_WRITE_UNCORRECTABLE;\r
+\r
+//\r
+// Write Zeroes command\r
+//\r
+typedef struct {\r
+  //\r
+  // CDW 10, 11\r
+  //\r
+  UINT64 Slba;                /* Starting LBA */\r
+  //\r
+  // CDW 12\r
+  //\r
+  UINT16 Nlb;                 /* Number of Logical Blocks */\r
+  UINT16 Rsvd1:10;\r
+  UINT16 Prinfo:4;            /* Protection Info Check */\r
+  UINT16 Fua:1;               /* Force Unit Access */\r
+  UINT16 Lr:1;                /* Limited Retry */\r
+  //\r
+  // CDW 13\r
+  //\r
+  UINT32 Rsvd2;\r
+  //\r
+  // CDW 14\r
+  //\r
+  UINT32 Ilbrt;               /* Initial Logical Block Reference Tag */\r
+  //\r
+  // CDW 15\r
+  //\r
+  UINT16 Lbat;                /* Logical Block Application Tag */\r
+  UINT16 Lbatm;               /* Logical Block Application Tag Mask */\r
+} NVME_WRITE_ZEROES;\r
+\r
+//\r
+// Compare command\r
+//\r
+typedef struct {\r
+  //\r
+  // CDW 10, 11\r
+  //\r
+  UINT64 Slba;                /* Starting LBA */\r
+  //\r
+  // CDW 12\r
+  //\r
+  UINT16 Nlb;                 /* Number of Logical Blocks */\r
+  UINT16 Rsvd1:10;\r
+  UINT16 Prinfo:4;            /* Protection Info Check */\r
+  UINT16 Fua:1;               /* Force Unit Access */\r
+  UINT16 Lr:1;                /* Limited Retry */\r
+  //\r
+  // CDW 13\r
+  //\r
+  UINT32 Rsvd2;\r
+  //\r
+  // CDW 14\r
+  //\r
+  UINT32 Eilbrt;              /* Expected Initial Logical Block Reference Tag */\r
+  //\r
+  // CDW 15\r
+  //\r
+  UINT16 Elbat;               /* Expected Logical Block Application Tag */\r
+  UINT16 Elbatm;              /* Expected Logical Block Application Tag Mask */\r
+} NVME_COMPARE;\r
+\r
+typedef union {\r
+  NVME_READ                   Read;\r
+  NVME_WRITE                  Write;\r
+  NVME_FLUSH                  Flush;\r
+  NVME_WRITE_UNCORRECTABLE    WriteUncorrectable;\r
+  NVME_WRITE_ZEROES           WriteZeros;\r
+  NVME_COMPARE                Compare;\r
+} NVME_CMD;\r
+\r
+typedef struct {\r
+  UINT16 Mp;                /* Maximum Power */\r
+  UINT8  Rsvd1;             /* Reserved as of Nvm Express 1.1 Spec */\r
+  UINT8  Mps:1;             /* Max Power Scale */\r
+  UINT8  Nops:1;            /* Non-Operational State */\r
+  UINT8  Rsvd2:6;           /* Reserved as of Nvm Express 1.1 Spec */\r
+  UINT32 Enlat;             /* Entry Latency */\r
+  UINT32 Exlat;             /* Exit Latency */\r
+  UINT8  Rrt:5;             /* Relative Read Throughput */\r
+  UINT8  Rsvd3:3;           /* Reserved as of Nvm Express 1.1 Spec */\r
+  UINT8  Rrl:5;             /* Relative Read Leatency */\r
+  UINT8  Rsvd4:3;           /* Reserved as of Nvm Express 1.1 Spec */\r
+  UINT8  Rwt:5;             /* Relative Write Throughput */\r
+  UINT8  Rsvd5:3;           /* Reserved as of Nvm Express 1.1 Spec */\r
+  UINT8  Rwl:5;             /* Relative Write Leatency */\r
+  UINT8  Rsvd6:3;           /* Reserved as of Nvm Express 1.1 Spec */\r
+  UINT8  Rsvd7[16];         /* Reserved as of Nvm Express 1.1 Spec */\r
+} NVME_PSDESCRIPTOR;\r
+\r
+//\r
+//  Identify Controller Data\r
+//\r
+typedef struct {\r
+  //\r
+  // Controller Capabilities and Features 0-255\r
+  //\r
+  UINT16 Vid;                 /* PCI Vendor ID */\r
+  UINT16 Ssvid;               /* PCI sub-system vendor ID */\r
+  UINT8  Sn[20];              /* Produce serial number */\r
+\r
+  UINT8  Mn[40];              /* Proeduct model number */\r
+  UINT8  Fr[8];               /* Firmware Revision */\r
+  UINT8  Rab;                 /* Recommended Arbitration Burst */\r
+  UINT8  Ieee_oiu[3];         /* Organization Unique Identifier */\r
+  UINT8  Cmic;                /* Multi-interface Capabilities */\r
+  UINT8  Mdts;                /* Maximum Data Transfer Size */\r
+  UINT8  Cntlid[2];           /* Controller ID */\r
+  UINT8  Rsvd1[176];          /* Reserved as of Nvm Express 1.1 Spec */\r
+  //\r
+  // Admin Command Set Attributes\r
+  //\r
+  UINT16 Oacs;                /* Optional Admin Command Support */\r
+  UINT8  Acl;                 /* Abort Command Limit */\r
+  UINT8  Aerl;                /* Async Event Request Limit */\r
+  UINT8  Frmw;                /* Firmware updates */\r
+  UINT8  Lpa;                 /* Log Page Attributes */\r
+  UINT8  Elpe;                /* Error Log Page Entries */\r
+  UINT8  Npss;                /* Number of Power States Support */\r
+  UINT8  Avscc;               /* Admin Vendor Specific Command Configuration */\r
+  UINT8  Apsta;               /* Autonomous Power State Transition Attributes */\r
+  UINT8  Rsvd2[246];          /* Reserved as of Nvm Express 1.1 Spec */\r
+  //\r
+  // NVM Command Set Attributes\r
+  //\r
+  UINT8  Sqes;                /* Submission Queue Entry Size */\r
+  UINT8  Cqes;                /* Completion Queue Entry Size */\r
+  UINT16 Rsvd3;               /* Reserved as of Nvm Express 1.1 Spec */\r
+  UINT32 Nn;                  /* Number of Namespaces */\r
+  UINT16 Oncs;                /* Optional NVM Command Support */\r
+  UINT16 Fuses;               /* Fused Operation Support */\r
+  UINT8  Fna;                 /* Format NVM Attributes */\r
+  UINT8  Vwc;                 /* Volatile Write Cache */\r
+  UINT16 Awun;                /* Atomic Write Unit Normal */\r
+  UINT16 Awupf;               /* Atomic Write Unit Power Fail */\r
+  UINT8  Nvscc;               /* NVM Vendor Specific Command Configuration */\r
+  UINT8  Rsvd4;               /* Reserved as of Nvm Express 1.1 Spec */\r
+  UINT16 Acwu;                /* Atomic Compare & Write Unit */\r
+  UINT16 Rsvd5;               /* Reserved as of Nvm Express 1.1 Spec */\r
+  UINT32 Sgls;                /* SGL Support  */\r
+  UINT8  Rsvd6[164];          /* Reserved as of Nvm Express 1.1 Spec */\r
+  //\r
+  // I/O Command set Attributes\r
+  //\r
+  UINT8 Rsvd7[1344];          /* Reserved as of Nvm Express 1.1 Spec */\r
+  //\r
+  // Power State Descriptors\r
+  //\r
+  NVME_PSDESCRIPTOR PsDescriptor[32];\r
+\r
+  UINT8  VendorData[1024];    /* Vendor specific Data */\r
+} NVME_ADMIN_CONTROLLER_DATA;\r
+\r
+typedef struct {\r
+  UINT16        Security  : 1;    /* supports security send/receive commands */\r
+  UINT16        Format    : 1;    /* supports format nvm command */\r
+  UINT16        Firmware  : 1;    /* supports firmware activate/download commands */\r
+  UINT16        Oacs_rsvd : 13;\r
+ } OACS; // optional admin command support: NVME_ADMIN_CONTROLLER_DATA.Oacs\r
+\r
+typedef struct {\r
+  UINT16 Ms;                /* Metadata Size */\r
+  UINT8  Lbads;             /* LBA Data Size */\r
+  UINT8  Rp:2;              /* Relative Performance */\r
+    #define LBAF_RP_BEST      00b\r
+    #define LBAF_RP_BETTER    01b\r
+    #define LBAF_RP_GOOD      10b\r
+    #define LBAF_RP_DEGRADED  11b\r
+  UINT8  Rsvd1:6;           /* Reserved as of Nvm Express 1.1 Spec */\r
+} NVME_LBAFORMAT;\r
+\r
+//\r
+// Identify Namespace Data\r
+//\r
+typedef struct {\r
+  //\r
+  // NVM Command Set Specific\r
+  //\r
+  UINT64 Nsze;                /* Namespace Size (total number of blocks in formatted namespace) */\r
+  UINT64 Ncap;                /* Namespace Capacity (max number of logical blocks) */\r
+  UINT64 Nuse;                /* Namespace Utilization */\r
+  UINT8  Nsfeat;              /* Namespace Features */\r
+  UINT8  Nlbaf;               /* Number of LBA Formats */\r
+  UINT8  Flbas;               /* Formatted LBA Size */\r
+  UINT8  Mc;                  /* Metadata Capabilities */\r
+  UINT8  Dpc;                 /* End-to-end Data Protection capabilities */\r
+  UINT8  Dps;                 /* End-to-end Data Protection Type Settings */\r
+  UINT8  Nmic;                /* Namespace Multi-path I/O and Namespace Sharing Capabilities */\r
+  UINT8  Rescap;              /* Reservation Capabilities */\r
+  UINT8  Rsvd1[88];           /* Reserved as of Nvm Express 1.1 Spec */\r
+  UINT64 Eui64;               /* IEEE Extended Unique Identifier */\r
+  //\r
+  // LBA Format\r
+  //\r
+  NVME_LBAFORMAT LbaFormat[16];\r
+\r
+  UINT8 Rsvd2[192];           /* Reserved as of Nvm Express 1.1 Spec */\r
+  UINT8 VendorData[3712];     /* Vendor specific Data */\r
+} NVME_ADMIN_NAMESPACE_DATA;\r
+\r
+//\r
+// NvmExpress Admin Identify Cmd\r
+//\r
+typedef struct {\r
+  //\r
+  // CDW 10\r
+  //\r
+  UINT32 Cns:2;\r
+  UINT32 Rsvd1:30;\r
+} NVME_ADMIN_IDENTIFY;\r
+\r
+//\r
+// NvmExpress Admin Create I/O Completion Queue\r
+//\r
+typedef struct {\r
+  //\r
+  // CDW 10\r
+  //\r
+  UINT32 Qid:16;              /* Queue Identifier */\r
+  UINT32 Qsize:16;            /* Queue Size */\r
+\r
+  //\r
+  // CDW 11\r
+  //\r
+  UINT32 Pc:1;                /* Physically Contiguous */\r
+  UINT32 Ien:1;               /* Interrupts Enabled */\r
+  UINT32 Rsvd1:14;            /* reserved as of Nvm Express 1.1 Spec */\r
+  UINT32 Iv:16;               /* Interrupt Vector */\r
+} NVME_ADMIN_CRIOCQ;\r
+\r
+//\r
+// NvmExpress Admin Create I/O Submission Queue\r
+//\r
+typedef struct {\r
+  //\r
+  // CDW 10\r
+  //\r
+  UINT32 Qid:16;              /* Queue Identifier */\r
+  UINT32 Qsize:16;            /* Queue Size */\r
+\r
+  //\r
+  // CDW 11\r
+  //\r
+  UINT32 Pc:1;                /* Physically Contiguous */\r
+  UINT32 Qprio:2;             /* Queue Priority */\r
+  UINT32 Rsvd1:13;            /* Reserved as of Nvm Express 1.1 Spec */\r
+  UINT32 Cqid:16;             /* Completion Queue ID */\r
+} NVME_ADMIN_CRIOSQ;\r
+\r
+//\r
+// NvmExpress Admin Delete I/O Completion Queue\r
+//\r
+typedef struct {\r
+  //\r
+  // CDW 10\r
+  //\r
+  UINT16 Qid;\r
+  UINT16 Rsvd1;\r
+} NVME_ADMIN_DEIOCQ;\r
+\r
+//\r
+// NvmExpress Admin Delete I/O Submission Queue\r
+//\r
+typedef struct {\r
+  //\r
+  // CDW 10\r
+  //\r
+  UINT16 Qid;\r
+  UINT16 Rsvd1;\r
+} NVME_ADMIN_DEIOSQ;\r
+\r
+//\r
+// NvmExpress Admin Security Send\r
+//\r
+typedef struct {\r
+  //\r
+  // CDW 10\r
+  //\r
+  UINT32 Resv:8;              /* Reserve */\r
+  UINT32 Spsp:16;             /* SP Specific */\r
+  UINT32 Secp:8;              /* Security Protocol */\r
+\r
+  //\r
+  // CDW 11\r
+  //\r
+  UINT32 Tl;                  /* Transfer Length */\r
+} NVME_ADMIN_SECSEND;\r
+\r
+//\r
+// NvmExpress Admin Abort Command\r
+//\r
+typedef struct {\r
+  //\r
+  // CDW 10\r
+  //\r
+  UINT32 Sqid:16;             /* Submission Queue identifier */\r
+  UINT32 Cid:16;              /* Command Identifier */\r
+} NVME_ADMIN_ABORT;\r
+\r
+//\r
+// NvmExpress Admin Firmware Activate Command\r
+//\r
+typedef struct {\r
+  //\r
+  // CDW 10\r
+  //\r
+  UINT32 Fs:3;                /* Submission Queue identifier */\r
+  UINT32 Aa:2;                /* Command Identifier */\r
+  UINT32 Rsvd1:27;\r
+} NVME_ADMIN_FIRMWARE_ACTIVATE;\r
+\r
+//\r
+// NvmExpress Admin Firmware Image Download Command\r
+//\r
+typedef struct {\r
+  //\r
+  // CDW 10\r
+  //\r
+  UINT32 Numd;                /* Number of Dwords */\r
+  //\r
+  // CDW 11\r
+  //\r
+  UINT32 Ofst;                /* Offset */\r
+} NVME_ADMIN_FIRMWARE_IMAGE_DOWNLOAD;\r
+\r
+//\r
+// NvmExpress Admin Get Features Command\r
+//\r
+typedef struct {\r
+  //\r
+  // CDW 10\r
+  //\r
+  UINT32 Fid:8;                /* Feature Identifier */\r
+  UINT32 Sel:3;                /* Select */\r
+  UINT32 Rsvd1:21;\r
+} NVME_ADMIN_GET_FEATURES;\r
+\r
+//\r
+// NvmExpress Admin Get Log Page Command\r
+//\r
+typedef struct {\r
+  //\r
+  // CDW 10\r
+  //\r
+  UINT32 Lid:8;               /* Log Page Identifier */\r
+    #define LID_ERROR_INFO\r
+    #define LID_SMART_INFO\r
+    #define LID_FW_SLOT_INFO\r
+  UINT32 Rsvd1:8;\r
+  UINT32 Numd:12;             /* Number of Dwords */\r
+  UINT32 Rsvd2:4;             /* Reserved as of Nvm Express 1.1 Spec */\r
+} NVME_ADMIN_GET_LOG_PAGE;\r
+\r
+//\r
+// NvmExpress Admin Set Features Command\r
+//\r
+typedef struct {\r
+  //\r
+  // CDW 10\r
+  //\r
+  UINT32 Fid:8;               /* Feature Identifier */\r
+  UINT32 Rsvd1:23;\r
+  UINT32 Sv:1;                /* Save */\r
+} NVME_ADMIN_SET_FEATURES;\r
+\r
+//\r
+// NvmExpress Admin Format NVM Command\r
+//\r
+typedef struct {\r
+  //\r
+  // CDW 10\r
+  //\r
+  UINT32 Lbaf:4;              /* LBA Format */\r
+  UINT32 Ms:1;                /* Metadata Settings */\r
+  UINT32 Pi:3;                /* Protection Information */\r
+  UINT32 Pil:1;               /* Protection Information Location */\r
+  UINT32 Ses:3;               /* Secure Erase Settings */\r
+  UINT32 Rsvd1:20;\r
+} NVME_ADMIN_FORMAT_NVM;\r
+\r
+//\r
+// NvmExpress Admin Security Receive Command\r
+//\r
+typedef struct {\r
+  //\r
+  // CDW 10\r
+  //\r
+  UINT32 Rsvd1:8;\r
+  UINT32 Spsp:16;             /* SP Specific */\r
+  UINT32 Secp:8;              /* Security Protocol */\r
+  //\r
+  // CDW 11\r
+  //\r
+  UINT32 Al;                  /* Allocation Length */\r
+} NVME_ADMIN_SECURITY_RECEIVE;\r
+\r
+//\r
+// NvmExpress Admin Security Send Command\r
+//\r
+typedef struct {\r
+  //\r
+  // CDW 10\r
+  //\r
+  UINT32 Rsvd1:8;\r
+  UINT32 Spsp:16;             /* SP Specific */\r
+  UINT32 Secp:8;              /* Security Protocol */\r
+  //\r
+  // CDW 11\r
+  //\r
+  UINT32 Tl;                  /* Transfer Length */\r
+} NVME_ADMIN_SECURITY_SEND;\r
+\r
+typedef union {\r
+  NVME_ADMIN_IDENTIFY                   Identify;\r
+  NVME_ADMIN_CRIOCQ                     CrIoCq;\r
+  NVME_ADMIN_CRIOSQ                     CrIoSq;\r
+  NVME_ADMIN_DEIOCQ                     DeIoCq;\r
+  NVME_ADMIN_DEIOSQ                     DeIoSq;\r
+  NVME_ADMIN_ABORT                      Abort;\r
+  NVME_ADMIN_FIRMWARE_ACTIVATE          Activate;\r
+  NVME_ADMIN_FIRMWARE_IMAGE_DOWNLOAD    FirmwareImageDownload;\r
+  NVME_ADMIN_GET_FEATURES               GetFeatures;\r
+  NVME_ADMIN_GET_LOG_PAGE               GetLogPage;\r
+  NVME_ADMIN_SET_FEATURES               SetFeatures;\r
+  NVME_ADMIN_FORMAT_NVM                 FormatNvm;\r
+  NVME_ADMIN_SECURITY_RECEIVE           SecurityReceive;\r
+  NVME_ADMIN_SECURITY_SEND              SecuritySend;\r
+} NVME_ADMIN_CMD;\r
+\r
+typedef struct {\r
+  UINT32 Cdw10;\r
+  UINT32 Cdw11;\r
+  UINT32 Cdw12;\r
+  UINT32 Cdw13;\r
+  UINT32 Cdw14;\r
+  UINT32 Cdw15;\r
+} NVME_RAW;\r
+\r
+typedef union {\r
+  NVME_ADMIN_CMD Admin;   // Union of Admin commands\r
+  NVME_CMD       Nvm;     // Union of Nvm commands\r
+  NVME_RAW       Raw;\r
+} NVME_PAYLOAD;\r
+\r
+//\r
+// Submission Queue\r
+//\r
+typedef struct {\r
+  //\r
+  // CDW 0, Common to all comnmands\r
+  //\r
+  UINT8  Opc;               // Opcode\r
+  UINT8  Fuse:2;            // Fused Operation\r
+  UINT8  Rsvd1:5;\r
+  UINT8  Psdt:1;            // PRP or SGL for Data Transfer\r
+  UINT16 Cid;               // Command Identifier\r
+\r
+  //\r
+  // CDW 1\r
+  //\r
+  UINT32 Nsid;              // Namespace Identifier\r
+\r
+  //\r
+  // CDW 2,3\r
+  //\r
+  UINT64 Rsvd2;\r
+\r
+  //\r
+  // CDW 4,5\r
+  //\r
+  UINT64 Mptr;              // Metadata Pointer\r
+\r
+  //\r
+  // CDW 6-9\r
+  //\r
+  UINT64 Prp[2];            // First and second PRP entries\r
+\r
+  NVME_PAYLOAD Payload;\r
+\r
+} NVME_SQ;\r
+\r
+//\r
+// Completion Queue\r
+//\r
+typedef struct {\r
+  //\r
+  // CDW 0\r
+  //\r
+  UINT32 Dword0;\r
+  //\r
+  // CDW 1\r
+  //\r
+  UINT32 Rsvd1;\r
+  //\r
+  // CDW 2\r
+  //\r
+  UINT16 Sqhd;              // Submission Queue Head Pointer\r
+  UINT16 Sqid;              // Submission Queue Identifier\r
+  //\r
+  // CDW 3\r
+  //\r
+  UINT16 Cid;               // Command Identifier\r
+  UINT16 Pt:1;              // Phase Tag\r
+  UINT16 Sc:8;              // Status Code\r
+  UINT16 Sct:3;             // Status Code Type\r
+  UINT16 Rsvd2:2;\r
+  UINT16 Mo:1;              // More\r
+  UINT16 Dnr:1;             // Retry\r
+} NVME_CQ;\r
+\r
+//\r
+// Nvm Express Admin cmd opcodes\r
+//\r
+#define NVME_ADMIN_DELIOSQ_OPC               0\r
+#define NVME_ADMIN_CRIOSQ_OPC                1\r
+#define NVME_ADMIN_DELIOCQ_OPC               4\r
+#define NVME_ADMIN_CRIOCQ_OPC                5\r
+#define NVME_ADMIN_IDENTIFY_OPC              6\r
+#define NVME_ADMIN_SECURITY_SEND_OPC         0x81\r
+#define NVME_ADMIN_SECURITY_RECV_OPC         0x82\r
+\r
+#define NVME_IO_FLUSH_OPC                    0\r
+#define NVME_IO_WRITE_OPC                    1\r
+#define NVME_IO_READ_OPC                     2\r
+\r
+//\r
+// Offset from the beginning of private Data queue Buffer\r
+//\r
+#define NVME_ASQ_BUF_OFFSET                  EFI_PAGE_SIZE\r
+\r
+#pragma pack()\r
+\r
+#endif\r
+\r
diff --git a/SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalPasswordSmm.c b/SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalPasswordSmm.c
new file mode 100644 (file)
index 0000000..57d10a1
--- /dev/null
@@ -0,0 +1,1036 @@
+/** @file\r
+  Opal password smm driver which is used to support Opal security feature at s3 path.\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
+#define SMM_SIZE_ALLOC_BYTES                      (512)\r
+#define RESPONSE_SIZE                             (200)\r
+\r
+#define PCI_CLASS_MASS_STORAGE_AHCI               (0x06)\r
+\r
+#define OPAL_PCIE_ROOTPORT_SAVESIZE               (0x40)\r
+#define STORE_INVALID_ROOTPORT_INDEX              ((UINT8) -1)\r
+#define OPAL_DEVICE_TYPE_SATA                     0x1\r
+#define OPAL_DEVICE_TYPE_NVME                     0x2\r
+#define OPAL_DEVICE_TYPE_UNKNOWN                  0xFF\r
+\r
+//\r
+// To unlock the Intel SATA controller at S3 Resume, restored the following registers.\r
+//\r
+const OPAL_HC_PCI_REGISTER_SAVE mSataHcRegisterSaveTemplate[] = {\r
+  {0x9,  S3BootScriptWidthUint8},\r
+  {0x10, S3BootScriptWidthUint32},\r
+  {0x14, S3BootScriptWidthUint32},\r
+  {0x18, S3BootScriptWidthUint32},\r
+  {0x1C, S3BootScriptWidthUint32},\r
+  {0x20, S3BootScriptWidthUint32},\r
+  {0x24, S3BootScriptWidthUint32},\r
+  {0x3c, S3BootScriptWidthUint8},\r
+  {0x3d, S3BootScriptWidthUint8},\r
+  {0x40, S3BootScriptWidthUint16},\r
+  {0x42, S3BootScriptWidthUint16},\r
+  {0x92, S3BootScriptWidthUint16},\r
+  {0x94, S3BootScriptWidthUint32},\r
+  {0x9C, S3BootScriptWidthUint32},\r
+  {0x4,  S3BootScriptWidthUint16},\r
+};\r
+\r
+\r
+UINT8                mSwSmiValue;\r
+LIST_ENTRY           *mOpalDeviceList;\r
+LIST_ENTRY           mSmmDeviceList  = INITIALIZE_LIST_HEAD_VARIABLE (mSmmDeviceList);\r
+\r
+BOOLEAN              mSendBlockSID   = FALSE;\r
+\r
+// AHCI\r
+UINT32               mAhciBar = 0;\r
+EFI_AHCI_REGISTERS   mAhciRegisters;\r
+VOID                 *mBuffer = NULL; // DMA can not read/write Data to smram, so we pre-allocates Buffer from AcpiNVS.\r
+//\r
+// NVME\r
+NVME_CONTEXT         mNvmeContext;\r
+\r
+/**\r
+  Add new bridge node or nvme device info to the device list.\r
+\r
+  @param[in]       BusNum        The bus number.\r
+  @param[in]       DevNum        The device number.\r
+  @param[in]       FuncNum       The function number.\r
+  @param[in]       Dev           The device which need to add device node info.\r
+\r
+**/\r
+VOID\r
+AddPciDeviceNode (\r
+  UINT32                  BusNum,\r
+  UINT32                  DevNum,\r
+  UINT32                  FuncNum,\r
+  OPAL_SMM_DEVICE         *Dev\r
+  )\r
+{\r
+  UINT8       *DevList;\r
+  PCI_DEVICE  *DeviceNode;\r
+\r
+  DevList = AllocateZeroPool (sizeof (PCI_DEVICE) + Dev->Length);\r
+  ASSERT (DevList != NULL);\r
+\r
+  if (Dev->Length != 0) {\r
+    CopyMem (DevList, Dev->PciBridgeNode, Dev->Length);\r
+    FreePool (Dev->PciBridgeNode);\r
+  }\r
+\r
+  DeviceNode = (PCI_DEVICE *) (DevList + Dev->Length);\r
+\r
+  DeviceNode->BusNum  = BusNum;\r
+  DeviceNode->DevNum  = DevNum;\r
+  DeviceNode->FuncNum = FuncNum;\r
+\r
+  Dev->Length += sizeof (PCI_DEVICE);\r
+  Dev->PciBridgeNode = (PCI_DEVICE *)DevList;\r
+}\r
+\r
+/**\r
+  Extract device info from the input device path.\r
+\r
+  @param[in]       DevicePath        Device path info for the device.\r
+  @param[in,out]   Dev               The device which new inputed.\r
+\r
+**/\r
+VOID\r
+ExtractDeviceInfoFromDevicePath (\r
+  IN     EFI_DEVICE_PATH_PROTOCOL    *DevicePath,\r
+  IN OUT OPAL_SMM_DEVICE             *Dev\r
+  )\r
+{\r
+  EFI_DEVICE_PATH_PROTOCOL      *TmpDevPath;\r
+  EFI_DEVICE_PATH_PROTOCOL      *TmpDevPath2;\r
+  PCI_DEVICE_PATH               *PciDevPath;\r
+  SATA_DEVICE_PATH              *SataDevPath;\r
+  NVME_NAMESPACE_DEVICE_PATH    *NvmeDevPath;\r
+  UINTN                         BusNum;\r
+\r
+  TmpDevPath = DevicePath;\r
+  Dev->DeviceType = OPAL_DEVICE_TYPE_UNKNOWN;\r
+\r
+  while (!IsDevicePathEnd(TmpDevPath)) {\r
+    if (TmpDevPath->Type == MESSAGING_DEVICE_PATH && TmpDevPath->SubType == MSG_SATA_DP) {\r
+      //\r
+      // SATA\r
+      //\r
+      SataDevPath = ( SATA_DEVICE_PATH* )TmpDevPath;\r
+      Dev->SataPort = SataDevPath->HBAPortNumber;\r
+      Dev->SataPortMultiplierPort = SataDevPath->PortMultiplierPortNumber;\r
+      Dev->DeviceType = OPAL_DEVICE_TYPE_SATA;\r
+      break;\r
+    } else if (TmpDevPath->Type == MESSAGING_DEVICE_PATH && TmpDevPath->SubType == MSG_NVME_NAMESPACE_DP) {\r
+      //\r
+      // NVMe\r
+      //\r
+      NvmeDevPath = ( NVME_NAMESPACE_DEVICE_PATH* )TmpDevPath;\r
+      Dev->NvmeNamespaceId = NvmeDevPath->NamespaceId;\r
+      Dev->DeviceType = OPAL_DEVICE_TYPE_NVME;\r
+      break;\r
+    }\r
+    TmpDevPath = NextDevicePathNode (TmpDevPath);\r
+  }\r
+\r
+  //\r
+  // Get bridge node info for the nvme device.\r
+  //\r
+  BusNum = 0;\r
+  TmpDevPath = DevicePath;\r
+  TmpDevPath2 = NextDevicePathNode (DevicePath);\r
+  while (!IsDevicePathEnd(TmpDevPath2)) {\r
+    if (TmpDevPath->Type == HARDWARE_DEVICE_PATH && TmpDevPath->SubType == HW_PCI_DP) {\r
+      PciDevPath = (PCI_DEVICE_PATH *) TmpDevPath;\r
+      if ((TmpDevPath2->Type == MESSAGING_DEVICE_PATH && TmpDevPath2->SubType == MSG_NVME_NAMESPACE_DP)||\r
+          (TmpDevPath2->Type == MESSAGING_DEVICE_PATH && TmpDevPath2->SubType == MSG_SATA_DP)) {\r
+        Dev->BusNum = (UINT32)BusNum;\r
+        Dev->DevNum = PciDevPath->Device;\r
+        Dev->FuncNum = PciDevPath->Function;\r
+      } else {\r
+        AddPciDeviceNode((UINT32)BusNum, PciDevPath->Device, PciDevPath->Function, Dev);\r
+        if (TmpDevPath2->Type == HARDWARE_DEVICE_PATH && TmpDevPath2->SubType == HW_PCI_DP) {\r
+          BusNum = PciRead8 (PCI_LIB_ADDRESS (BusNum, PciDevPath->Device, PciDevPath->Function, NVME_PCIE_SEC_BNUM));\r
+        }\r
+      }\r
+    }\r
+\r
+    TmpDevPath  = NextDevicePathNode (TmpDevPath);\r
+    TmpDevPath2 = NextDevicePathNode (TmpDevPath2);\r
+  }\r
+}\r
+\r
+/**\r
+\r
+  The function returns whether or not the device is Opal Locked.\r
+  TRUE means that the device is partially or fully locked.\r
+  This will perform a Level 0 Discovery and parse the locking feature descriptor\r
+\r
+  @param[in]      OpalDev        Opal object to determine if locked\r
+\r
+**/\r
+BOOLEAN\r
+IsOpalDeviceLocked(\r
+  OPAL_SMM_DEVICE    *OpalDev\r
+  )\r
+{\r
+  OPAL_SESSION                   Session;\r
+  OPAL_DISK_SUPPORT_ATTRIBUTE    SupportedAttributes;\r
+  TCG_LOCKING_FEATURE_DESCRIPTOR LockingFeature;\r
+  UINT16                         OpalBaseComId;\r
+  TCG_RESULT                     Ret;\r
+\r
+  Session.Sscp = &OpalDev->Sscp;\r
+  Session.MediaId = 0;\r
+\r
+  Ret = OpalGetSupportedAttributesInfo (&Session, &SupportedAttributes, &OpalBaseComId);\r
+  if (Ret != TcgResultSuccess) {\r
+    return FALSE;\r
+  }\r
+\r
+  OpalDev->OpalBaseComId = OpalBaseComId;\r
+  Session.OpalBaseComId = OpalBaseComId;\r
+\r
+  Ret = OpalGetLockingInfo(&Session, &LockingFeature);\r
+  if (Ret != TcgResultSuccess) {\r
+    return FALSE;\r
+  }\r
+\r
+  return OpalDeviceLocked (&SupportedAttributes, &LockingFeature);\r
+}\r
+\r
+/**\r
+  Save/Restore RootPort configuration space.\r
+\r
+  @param[in] DeviceNode             - The device node.\r
+  @param[in] SaveAction             - TRUE: Save, FALSE: Restore\r
+  @param[in,out] PcieConfBufferList     - Configuration space data buffer for save/restore\r
+\r
+  @retval - PCIE base address of this RootPort\r
+**/\r
+UINTN\r
+SaveRestoreRootportConfSpace (\r
+  IN     OPAL_SMM_DEVICE                *DeviceNode,\r
+  IN     BOOLEAN                        SaveAction,\r
+  IN OUT UINT8                          **PcieConfBufferList\r
+  )\r
+{\r
+  UINTN      RpBase;\r
+  UINTN      Length;\r
+  PCI_DEVICE *DevNode;\r
+  UINT8      *StorePcieConfData;\r
+  UINTN      Index;\r
+\r
+  Length = 0;\r
+  Index  = 0;\r
+  RpBase = 0;\r
+\r
+  while (Length < DeviceNode->Length) {\r
+    DevNode = (PCI_DEVICE *)((UINT8*)DeviceNode->PciBridgeNode + Length);\r
+    RpBase = PCI_LIB_ADDRESS (DevNode->BusNum, DevNode->DevNum, DevNode->FuncNum, 0x0);\r
+\r
+    if (PcieConfBufferList != NULL) {\r
+      if (SaveAction) {\r
+        StorePcieConfData = (UINT8 *) AllocateZeroPool (OPAL_PCIE_ROOTPORT_SAVESIZE);\r
+        ASSERT (StorePcieConfData != NULL);\r
+        OpalPciRead (StorePcieConfData, RpBase, OPAL_PCIE_ROOTPORT_SAVESIZE);\r
+        PcieConfBufferList[Index] = StorePcieConfData;\r
+      } else {\r
+        // Skip PCIe Command & Status registers\r
+        StorePcieConfData = PcieConfBufferList[Index];\r
+        OpalPciWrite (RpBase, StorePcieConfData, 4);\r
+        OpalPciWrite (RpBase + 8, StorePcieConfData + 8, OPAL_PCIE_ROOTPORT_SAVESIZE - 8);\r
+\r
+        FreePool (StorePcieConfData);\r
+      }\r
+    }\r
+\r
+    Length += sizeof (PCI_DEVICE);\r
+    Index ++;\r
+  }\r
+\r
+  return RpBase;\r
+}\r
+\r
+/**\r
+  Configure RootPort for downstream PCIe NAND devices.\r
+\r
+  @param[in] RpBase             - PCIe configuration space address of this RootPort\r
+  @param[in] BusNumber          - Bus number\r
+  @param[in] MemoryBase         - Memory base address\r
+  @param[in] MemoryLength       - Memory size\r
+\r
+**/\r
+VOID\r
+ConfigureRootPortForPcieNand (\r
+  IN UINTN   RpBase,\r
+  IN UINTN   BusNumber,\r
+  IN UINT32  MemoryBase,\r
+  IN UINT32  MemoryLength\r
+  )\r
+{\r
+  UINT32  MemoryLimit;\r
+\r
+  DEBUG ((DEBUG_INFO, "ConfigureRootPortForPcieNand, BusNumber: %x, MemoryBase: %x, MemoryLength: %x\n",\r
+    BusNumber, MemoryBase, MemoryLength));\r
+\r
+  if (MemoryLength == 0) {\r
+    MemoryLimit = MemoryBase;\r
+  } else {\r
+    MemoryLimit = MemoryBase + MemoryLength + 0xFFFFF; // 1M\r
+  }\r
+\r
+  ///\r
+  /// Configue PCIE configuration space for RootPort\r
+  ///\r
+  PciWrite8  (RpBase + NVME_PCIE_BNUM + 1,  (UINT8) BusNumber);           // Secondary Bus Number registers\r
+  PciWrite8  (RpBase + NVME_PCIE_BNUM + 2,  (UINT8) BusNumber);           // Subordinate Bus Number registers\r
+  PciWrite8  (RpBase + NVME_PCIE_IOBL,      0xFF);                        // I/O Base registers\r
+  PciWrite8  (RpBase + NVME_PCIE_IOBL + 1,  0x00);                        // I/O Limit registers\r
+  PciWrite16 (RpBase + NVME_PCIE_MBL,       (UINT16) RShiftU64 ((UINTN)MemoryBase, 16));  // Memory Base register\r
+  PciWrite16 (RpBase + NVME_PCIE_MBL + 2,   (UINT16) RShiftU64 ((UINTN)MemoryLimit, 16)); // Memory Limit register\r
+  PciWrite16 (RpBase + NVME_PCIE_PMBL,      0xFFFF);                      // Prefetchable Memory Base registers\r
+  PciWrite16 (RpBase + NVME_PCIE_PMBL + 2,  0x0000);                      // Prefetchable Memory Limit registers\r
+  PciWrite32 (RpBase + NVME_PCIE_PMBU32,    0xFFFFFFFF);                  // Prefetchable Memory Upper Base registers\r
+  PciWrite32 (RpBase + NVME_PCIE_PMLU32,    0x00000000);                  // Prefetchable Memory Upper Limit registers\r
+}\r
+\r
+\r
+/**\r
+  Dispatch function for a Software SMI handler.\r
+\r
+  @param[in]     DispatchHandle  The unique handle assigned to this handler by SmiHandlerRegister().\r
+  @param[in]     RegisterContext Points to an optional handler context which was specified when the\r
+                                 handler was registered.\r
+  @param[in, out] CommBuffer     A pointer to a collection of Data in memory that will\r
+                                 be conveyed from a non-SMM environment into an SMM environment.\r
+  @param[in, out] CommBufferSize The Size of the CommBuffer.\r
+\r
+  @retval EFI_SUCCESS            The interrupt was handled and quiesced. No other handlers\r
+                                 should still be called.\r
+  @retval Others                 Other execution results.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+SmmUnlockOpalPassword (\r
+  IN     EFI_HANDLE              DispatchHandle,\r
+  IN     CONST VOID              *RegisterContext,\r
+  IN OUT VOID                    *CommBuffer,\r
+  IN OUT UINTN                   *CommBufferSize\r
+  )\r
+{\r
+  EFI_STATUS                     Status;\r
+  OPAL_SMM_DEVICE                *OpalDev;\r
+  LIST_ENTRY                     *Entry;\r
+  UINT8                          BaseClassCode;\r
+  UINT8                          SubClassCode;\r
+  UINT8                          ProgInt;\r
+  TCG_RESULT                     Result;\r
+  UINT8                          SataCmdSt;\r
+  UINT8                          *StorePcieConfDataList[16];\r
+  UINTN                          RpBase;\r
+  UINTN                          MemoryBase;\r
+  UINTN                          MemoryLength;\r
+  OPAL_SESSION                   Session;\r
+\r
+  ZeroMem (StorePcieConfDataList, sizeof (StorePcieConfDataList));\r
+  Status = EFI_DEVICE_ERROR;\r
+\r
+  //\r
+  // try to unlock all locked hdd disks.\r
+  //\r
+  for (Entry = mSmmDeviceList.ForwardLink; Entry != &mSmmDeviceList; Entry = Entry->ForwardLink) {\r
+    OpalDev = BASE_CR(Entry, OPAL_SMM_DEVICE, Link);\r
+\r
+    RpBase    = 0;\r
+    SataCmdSt = 0;\r
+\r
+    ///\r
+    /// Configure RootPort for PCIe AHCI/NVME devices.\r
+    ///\r
+    if (OpalDev->DeviceType == OPAL_DEVICE_TYPE_NVME) {\r
+      ///\r
+      /// Save original RootPort configuration space to heap\r
+      ///\r
+      RpBase = SaveRestoreRootportConfSpace (\r
+                  OpalDev,\r
+                  TRUE,\r
+                  StorePcieConfDataList\r
+                  );\r
+      MemoryBase = mNvmeContext.Nbar;\r
+      MemoryLength = 0;\r
+      ConfigureRootPortForPcieNand (RpBase, OpalDev->BusNum, (UINT32) MemoryBase, (UINT32) MemoryLength);\r
+\r
+      ///\r
+      /// Enable PCIE decode for RootPort\r
+      ///\r
+      SataCmdSt = PciRead8 (RpBase + NVME_PCIE_PCICMD);\r
+      PciWrite8  (RpBase + NVME_PCIE_PCICMD,  0x6);\r
+    } else {\r
+      SataCmdSt = PciRead8 (PCI_LIB_ADDRESS (OpalDev->BusNum, OpalDev->DevNum, OpalDev->FuncNum, NVME_PCIE_PCICMD));\r
+      PciWrite8 (PCI_LIB_ADDRESS (OpalDev->BusNum, OpalDev->DevNum, OpalDev->FuncNum, NVME_PCIE_PCICMD), 0x6);\r
+    }\r
+\r
+    BaseClassCode = PciRead8 (PCI_LIB_ADDRESS (OpalDev->BusNum, OpalDev->DevNum, OpalDev->FuncNum, 0x0B));\r
+    SubClassCode  = PciRead8 (PCI_LIB_ADDRESS (OpalDev->BusNum, OpalDev->DevNum, OpalDev->FuncNum, 0x0A));\r
+    ProgInt       = PciRead8 (PCI_LIB_ADDRESS (OpalDev->BusNum, OpalDev->DevNum, OpalDev->FuncNum, 0x09));\r
+    if (BaseClassCode != PCI_CLASS_MASS_STORAGE) {\r
+      Status = EFI_INVALID_PARAMETER;\r
+      break;\r
+    }\r
+\r
+    Status = EFI_DEVICE_ERROR;\r
+    if (OpalDev->DeviceType == OPAL_DEVICE_TYPE_SATA) {\r
+      if ((SubClassCode == PCI_CLASS_MASS_STORAGE_AHCI) || (SubClassCode == PCI_CLASS_MASS_STORAGE_RAID)) {\r
+        Status = GetAhciBaseAddress (OpalDev->BusNum, OpalDev->DevNum, OpalDev->FuncNum);\r
+        if (EFI_ERROR (Status)) {\r
+          DEBUG ((DEBUG_ERROR, "GetAhciBaseAddress error, Status: %r\n", Status));\r
+          goto done;\r
+        }\r
+        Status = AhciModeInitialize ((UINT8)OpalDev->SataPort);\r
+        ASSERT_EFI_ERROR (Status);\r
+        if (EFI_ERROR (Status)) {\r
+          DEBUG ((DEBUG_ERROR, "AhciModeInitialize error, Status: %r\n", Status));\r
+          goto done;\r
+        }\r
+      } else {\r
+        DEBUG ((DEBUG_ERROR, "SubClassCode not support for SATA device\n"));\r
+      }\r
+    } else if (OpalDev->DeviceType == OPAL_DEVICE_TYPE_NVME) {\r
+      if (SubClassCode == PCI_CLASS_MASS_STORAGE_NVM) {\r
+        if (ProgInt != PCI_IF_NVMHCI) {\r
+          DEBUG ((DEBUG_ERROR, "PI not support, skipped\n"));\r
+          Status = EFI_NOT_FOUND;\r
+          goto done;\r
+        }\r
+\r
+        mNvmeContext.PciBase = PCI_LIB_ADDRESS (OpalDev->BusNum, OpalDev->DevNum, OpalDev->FuncNum, 0x0);\r
+        mNvmeContext.NvmeInitWaitTime = 0;\r
+        mNvmeContext.Nsid = OpalDev->NvmeNamespaceId;\r
+        Status = NvmeControllerInit (&mNvmeContext);\r
+      } else {\r
+        DEBUG ((DEBUG_ERROR, "SubClassCode not support for NVME device\n"));\r
+      }\r
+    } else {\r
+      DEBUG ((DEBUG_ERROR, "Invalid Devicetype\n"));\r
+      goto done;\r
+    }\r
+\r
+    Status = EFI_DEVICE_ERROR;\r
+    if (IsOpalDeviceLocked(OpalDev)) {\r
+      ZeroMem(&Session, sizeof(Session));\r
+      Session.Sscp = &OpalDev->Sscp;\r
+      Session.MediaId = 0;\r
+      Session.OpalBaseComId = OpalDev->OpalBaseComId;\r
+\r
+      if (mSendBlockSID) {\r
+        Result = OpalBlockSid (&Session, TRUE);\r
+        if (Result != TcgResultSuccess) {\r
+          break;\r
+        }\r
+      }\r
+\r
+      Result = OpalSupportUnlock (&Session, OpalDev->Password, OpalDev->PasswordLength, NULL);\r
+      if (Result == TcgResultSuccess) {\r
+        Status = EFI_SUCCESS;\r
+      }\r
+    }\r
+\r
+    if (OpalDev->DeviceType == OPAL_DEVICE_TYPE_NVME) {\r
+      if (SubClassCode == PCI_CLASS_MASS_STORAGE_NVM) {\r
+        Status = NvmeControllerExit (&mNvmeContext);\r
+      }\r
+    }\r
+\r
+done:\r
+    if (OpalDev->DeviceType == OPAL_DEVICE_TYPE_NVME) {\r
+      ASSERT (RpBase != 0);\r
+      PciWrite8  (RpBase + NVME_PCIE_PCICMD, 0);\r
+      RpBase = SaveRestoreRootportConfSpace (\r
+                  OpalDev,\r
+                  FALSE,  // restore\r
+                  StorePcieConfDataList\r
+                  );\r
+      PciWrite8  (RpBase + NVME_PCIE_PCICMD, SataCmdSt);\r
+    } else {\r
+      PciWrite8 (PCI_LIB_ADDRESS (OpalDev->BusNum, OpalDev->DevNum, OpalDev->FuncNum, NVME_PCIE_PCICMD), SataCmdSt);\r
+    }\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      break;\r
+    }\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Main entry point for an SMM handler dispatch or communicate-based callback.\r
+\r
+  @param[in]     DispatchHandle  The unique handle assigned to this handler by SmiHandlerRegister().\r
+  @param[in]     Context         Points to an optional handler context which was specified when the\r
+                                 handler was registered.\r
+  @param[in,out] CommBuffer      A pointer to a collection of Data in memory that will\r
+                                 be conveyed from a non-SMM environment into an SMM environment.\r
+  @param[in,out] CommBufferSize  The Size of the CommBuffer.\r
+\r
+  @retval EFI_SUCCESS                         The interrupt was handled and quiesced. No other handlers\r
+                                              should still be called.\r
+  @retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED  The interrupt has been quiesced but other handlers should\r
+                                              still be called.\r
+  @retval EFI_WARN_INTERRUPT_SOURCE_PENDING   The interrupt is still pending and other handlers should still\r
+                                              be called.\r
+  @retval EFI_INTERRUPT_PENDING               The interrupt could not be quiesced.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+S3SleepEntryCallBack (\r
+  IN           EFI_HANDLE           DispatchHandle,\r
+  IN     CONST VOID                 *Context         OPTIONAL,\r
+  IN OUT       VOID                 *CommBuffer      OPTIONAL,\r
+  IN OUT       UINTN                *CommBufferSize  OPTIONAL\r
+  )\r
+{\r
+  UINTN                             Bus;\r
+  UINTN                             Device;\r
+  UINTN                             Function;\r
+  UINTN                             Index;\r
+  EFI_STATUS                        Status;\r
+  LIST_ENTRY                        *Entry;\r
+  UINTN                             Offset;\r
+  UINT64                            Address;\r
+  S3_BOOT_SCRIPT_LIB_WIDTH          Width;\r
+  UINT32                            Data;\r
+  OPAL_DISK_AND_PASSWORD_INFO       *PciDev;\r
+  OPAL_HC_PCI_REGISTER_SAVE         *HcRegisterSaveListPtr;\r
+  UINTN                             Count;\r
+  OPAL_SMM_DEVICE                   *SmmDev;\r
+\r
+  Data     = 0;\r
+  Status   = EFI_SUCCESS;\r
+\r
+  mOpalDeviceList = OpalSupportGetOpalDeviceList();\r
+\r
+  for (Entry = mOpalDeviceList->ForwardLink; Entry != mOpalDeviceList; Entry = Entry->ForwardLink) {\r
+    PciDev   = BASE_CR (Entry, OPAL_DISK_AND_PASSWORD_INFO, Link);\r
+\r
+    SmmDev = AllocateZeroPool (sizeof (OPAL_SMM_DEVICE));\r
+    if (SmmDev == NULL) {\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+    SmmDev->Signature = OPAL_SMM_DEVICE_SIGNATURE;\r
+\r
+    ExtractDeviceInfoFromDevicePath(&PciDev->OpalDevicePath, SmmDev);\r
+\r
+    SmmDev->PasswordLength = PciDev->PasswordLength;\r
+    CopyMem(&(SmmDev->Password), PciDev->Password, OPAL_PASSWORD_MAX_LENGTH);\r
+\r
+    SmmDev->Sscp.ReceiveData = SecurityReceiveData;\r
+    SmmDev->Sscp.SendData = SecuritySendData;\r
+\r
+    InsertHeadList (&mSmmDeviceList, &SmmDev->Link);\r
+\r
+    if (SmmDev->DeviceType == OPAL_DEVICE_TYPE_NVME) {\r
+      continue;\r
+    }\r
+\r
+    //\r
+    // Save register Data for S3. Sata controller only.\r
+    //\r
+    Bus        = SmmDev->BusNum;\r
+    Device     = SmmDev->DevNum;\r
+    Function   = SmmDev->FuncNum;\r
+\r
+    ASSERT (SmmDev->DeviceType == OPAL_DEVICE_TYPE_SATA);\r
+    HcRegisterSaveListPtr = (OPAL_HC_PCI_REGISTER_SAVE *) mSataHcRegisterSaveTemplate;\r
+    Count = sizeof (mSataHcRegisterSaveTemplate) / sizeof (OPAL_HC_PCI_REGISTER_SAVE);\r
+\r
+    for (Index = 0; Index < Count; Index += 1) {\r
+      Offset  = HcRegisterSaveListPtr[Index].Address;\r
+      Width   = HcRegisterSaveListPtr[Index].Width;\r
+\r
+      switch (Width) {\r
+        case S3BootScriptWidthUint8:\r
+          Data = (UINT32)PciRead8 (PCI_LIB_ADDRESS(Bus,Device,Function,Offset));\r
+          break;\r
+        case S3BootScriptWidthUint16:\r
+          Data = (UINT32)PciRead16 (PCI_LIB_ADDRESS(Bus,Device,Function,Offset));\r
+          break;\r
+        case S3BootScriptWidthUint32:\r
+          Data = PciRead32 (PCI_LIB_ADDRESS(Bus,Device,Function,Offset));\r
+          break;\r
+        default:\r
+          ASSERT (FALSE);\r
+          break;\r
+      }\r
+\r
+      Address = S3_BOOT_SCRIPT_LIB_PCI_ADDRESS (Bus, Device, Function, Offset);\r
+      Status  = S3BootScriptSavePciCfgWrite (Width, Address, 1, &Data);\r
+      if (EFI_ERROR (Status)) {\r
+        return Status;\r
+      }\r
+    }\r
+  }\r
+\r
+  if (!IsListEmpty (mOpalDeviceList)) {\r
+    Status = S3BootScriptSaveIoWrite (S3BootScriptWidthUint8, 0xB2, 1, &mSwSmiValue);\r
+    ASSERT_EFI_ERROR (Status);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Main entry for this driver.\r
+\r
+  @param ImageHandle     Image handle this driver.\r
+  @param SystemTable     Pointer to SystemTable.\r
+\r
+  @retval EFI_SUCESS     This function always complete successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+OpalPasswordSmmInit (\r
+  IN EFI_HANDLE                         ImageHandle,\r
+  IN EFI_SYSTEM_TABLE                   *SystemTable\r
+  )\r
+{\r
+  EFI_STATUS                            Status;\r
+  EFI_SMM_SW_DISPATCH2_PROTOCOL         *SwDispatch;\r
+  EFI_SMM_SX_DISPATCH2_PROTOCOL         *SxDispatch;\r
+  EFI_HANDLE                            SwHandle;\r
+  EFI_SMM_SW_REGISTER_CONTEXT           Context;\r
+  EFI_HANDLE                            S3SleepEntryHandle;\r
+  EFI_SMM_SX_REGISTER_CONTEXT           EntryRegisterContext;\r
+  EFI_SMM_VARIABLE_PROTOCOL             *SmmVariable;\r
+  OPAL_EXTRA_INFO_VAR                   OpalExtraInfo;\r
+  UINTN                                 DataSize;\r
+  EFI_PHYSICAL_ADDRESS                  Address;\r
+\r
+  mBuffer            = NULL;\r
+  SwHandle           = NULL;\r
+  S3SleepEntryHandle = NULL;\r
+  ZeroMem (&mNvmeContext, sizeof (NVME_CONTEXT));\r
+\r
+  Status = gSmst->SmmLocateProtocol (\r
+                    &gEfiSmmSwDispatch2ProtocolGuid,\r
+                    NULL,\r
+                    (VOID **)&SwDispatch\r
+                    );\r
+  ASSERT_EFI_ERROR (Status);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG((DEBUG_ERROR, " SmmLocateProtocol gEfiSmmSwDispatch2ProtocolGuid fail, Status: %r\n", Status));\r
+    return Status;\r
+  }\r
+\r
+  Status = gSmst->SmmLocateProtocol (\r
+                    &gEfiSmmSxDispatch2ProtocolGuid,\r
+                    NULL,\r
+                    (VOID **)&SxDispatch\r
+                    );\r
+  ASSERT_EFI_ERROR (Status);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG((DEBUG_ERROR, " SmmLocateProtocol gEfiSmmSxDispatch2ProtocolGuid fail, Status: %r\n", Status));\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Preallocate a 512 bytes Buffer to perform trusted I/O.\r
+  // Assume this is big enough for unlock commands\r
+  // It's because DMA can not access smmram stack at the cmd execution.\r
+  //\r
+  Address = 0xFFFFFFFF;\r
+  Status = gBS->AllocatePages (\r
+                  AllocateMaxAddress,\r
+                  EfiACPIMemoryNVS,\r
+                  EFI_SIZE_TO_PAGES (SMM_SIZE_ALLOC_BYTES),\r
+                  &Address\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG((DEBUG_ERROR, " AllocatePages for SATA DAM fail, Status: %r\n", Status));\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  mBuffer = (VOID *)(UINTN)Address;\r
+  ZeroMem ((VOID *)(UINTN)mBuffer, SMM_SIZE_ALLOC_BYTES);\r
+\r
+  //\r
+  // Preallocate resource for AHCI transfer descriptor.\r
+  //\r
+  Status = AhciAllocateResource ();\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG((DEBUG_ERROR, " AhciAllocateResource fail, Status: %r\n", Status));\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto EXIT;\r
+  }\r
+\r
+  //\r
+  // Preallocate resource for NVMe configuration space.\r
+  //\r
+  Status = NvmeAllocateResource (ImageHandle, &mNvmeContext);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG((DEBUG_ERROR, " NvmeAllocateResource fail, Status: %r\n", Status));\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto EXIT;\r
+  }\r
+\r
+  //\r
+  // Register a S3 entry callback function to store ATA host controller context to boot script.\r
+  // These boot scripts would be invoked at S3 path to recovery ATA host controller h/w context\r
+  // for executing HDD unlock cmd.\r
+  //\r
+  EntryRegisterContext.Type  = SxS3;\r
+  EntryRegisterContext.Phase = SxEntry;\r
+  Status = SxDispatch->Register (\r
+                         SxDispatch,\r
+                         S3SleepEntryCallBack,\r
+                         &EntryRegisterContext,\r
+                         &S3SleepEntryHandle\r
+                         );\r
+  ASSERT_EFI_ERROR (Status);\r
+  if (EFI_ERROR (Status)) {\r
+    goto EXIT;\r
+  }\r
+\r
+  //\r
+  // Register Opal password smm unlock handler\r
+  //\r
+  Context.SwSmiInputValue = (UINTN) -1;\r
+  Status = SwDispatch->Register (\r
+               SwDispatch,\r
+               SmmUnlockOpalPassword,\r
+               &Context,\r
+               &SwHandle\r
+               );\r
+  ASSERT_EFI_ERROR (Status);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG((DEBUG_ERROR, " SwDispatch->Register fail, Status: %r\n", Status));\r
+    goto EXIT;\r
+  }\r
+\r
+  //\r
+  // trigger smi to unlock hdd if it's locked.\r
+  //\r
+  mSwSmiValue = (UINT8) Context.SwSmiInputValue;\r
+\r
+  Status = gSmst->SmmLocateProtocol (&gEfiSmmVariableProtocolGuid, NULL, (VOID**)&SmmVariable);\r
+  if (!EFI_ERROR (Status)) {\r
+    DataSize = sizeof (OPAL_EXTRA_INFO_VAR);\r
+    Status = SmmVariable->SmmGetVariable (\r
+                    OPAL_EXTRA_INFO_VAR_NAME,\r
+                    &gOpalExtraInfoVariableGuid,\r
+                    NULL,\r
+                    &DataSize,\r
+                    &OpalExtraInfo\r
+                    );\r
+    if (!EFI_ERROR (Status)) {\r
+      mSendBlockSID = OpalExtraInfo.EnableBlockSid;\r
+    }\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+\r
+EXIT:\r
+  if (S3SleepEntryHandle != NULL) {\r
+    SxDispatch->UnRegister (SxDispatch, S3SleepEntryHandle);\r
+  }\r
+\r
+  AhciFreeResource ();\r
+\r
+  NvmeFreeResource (&mNvmeContext);\r
+\r
+  if (mBuffer != NULL) {\r
+    gBS->FreePages ((EFI_PHYSICAL_ADDRESS)(UINTN) mBuffer, EFI_SIZE_TO_PAGES (SMM_SIZE_ALLOC_BYTES));\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Provide Io action support.\r
+\r
+  @param[in]     SmmDev             the opal device need to perform trust io.\r
+  @param[in]     IoType             OPAL_IO_TYPE indicating whether to perform a Trusted Send or Trusted Receive.\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]     Buffer             Address of Data to transfer\r
+\r
+  @retval        TcgResultSuccess   Perform the io action success.\r
+  @retval        TcgResultFailure   Perform the io action failed.\r
+\r
+**/\r
+EFI_STATUS\r
+PerformTrustedIo (\r
+  OPAL_SMM_DEVICE  *SmmDev,\r
+  OPAL_IO_TYPE     IoType,\r
+  UINT8            SecurityProtocol,\r
+  UINT16           SpSpecific,\r
+  UINTN            TransferLength,\r
+  VOID             *Buffer\r
+  )\r
+{\r
+  EFI_STATUS                   Status;\r
+  UINTN                        BufferSizeBlocks;\r
+  EFI_ATA_COMMAND_BLOCK        AtaCommandBlock;\r
+\r
+  Status = EFI_DEVICE_ERROR;\r
+  if (SmmDev->DeviceType == OPAL_DEVICE_TYPE_SATA) {\r
+    BufferSizeBlocks = TransferLength / 512;\r
+\r
+    ZeroMem( &AtaCommandBlock, sizeof( EFI_ATA_COMMAND_BLOCK ) );\r
+    AtaCommandBlock.AtaCommand = ( IoType == OpalSend ) ? ATA_COMMAND_TRUSTED_SEND : ATA_COMMAND_TRUSTED_RECEIVE;\r
+    AtaCommandBlock.AtaSectorCount = ( UINT8 )BufferSizeBlocks;\r
+    AtaCommandBlock.AtaSectorNumber = ( UINT8 )( BufferSizeBlocks >> 8 );\r
+    AtaCommandBlock.AtaFeatures = SecurityProtocol;\r
+    AtaCommandBlock.AtaCylinderLow = ( UINT8 )( SpSpecific >> 8 );\r
+    AtaCommandBlock.AtaCylinderHigh = ( UINT8 )( SpSpecific );\r
+    AtaCommandBlock.AtaDeviceHead = ATA_DEVICE_LBA;\r
+\r
+\r
+    ZeroMem( mBuffer, HDD_PAYLOAD );\r
+    ASSERT( TransferLength <= HDD_PAYLOAD );\r
+\r
+    if (IoType == OpalSend) {\r
+      CopyMem( mBuffer, Buffer, TransferLength );\r
+    }\r
+\r
+    Status = AhciPioTransfer(\r
+                &mAhciRegisters,\r
+                (UINT8) SmmDev->SataPort,\r
+                (UINT8) SmmDev->SataPortMultiplierPort,\r
+                NULL,\r
+                0,\r
+                ( IoType == OpalSend ) ? FALSE : TRUE,   // i/o direction\r
+                &AtaCommandBlock,\r
+                NULL,\r
+                mBuffer,\r
+                (UINT32)TransferLength,\r
+                ATA_TIMEOUT\r
+                );\r
+\r
+    if (IoType == OpalRecv) {\r
+      CopyMem( Buffer, mBuffer, TransferLength );\r
+    }\r
+  } else if (SmmDev->DeviceType == OPAL_DEVICE_TYPE_NVME) {\r
+    Status = NvmeSecuritySendReceive (\r
+                &mNvmeContext,\r
+                IoType == OpalSend,\r
+                SecurityProtocol,\r
+                SwapBytes16(SpSpecific),\r
+                TransferLength,\r
+                Buffer\r
+              );\r
+  } else {\r
+    DEBUG((DEBUG_ERROR, "DeviceType(%x) not support.\n", SmmDev->DeviceType));\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Send a security protocol command to a device that receives data and/or the result\r
+  of one or more commands sent by SendData.\r
+\r
+  The ReceiveData function sends a security protocol command to the given MediaId.\r
+  The security protocol command sent is defined by SecurityProtocolId and contains\r
+  the security protocol specific data SecurityProtocolSpecificData. The function\r
+  returns the data from the security protocol command in PayloadBuffer.\r
+\r
+  For devices supporting the SCSI command set, the security protocol command is sent\r
+  using the SECURITY PROTOCOL IN command defined in SPC-4.\r
+\r
+  For devices supporting the ATA command set, the security protocol command is sent\r
+  using one of the TRUSTED RECEIVE commands defined in ATA8-ACS if PayloadBufferSize\r
+  is non-zero.\r
+\r
+  If the PayloadBufferSize is zero, the security protocol command is sent using the\r
+  Trusted Non-Data command defined in ATA8-ACS.\r
+\r
+  If PayloadBufferSize is too small to store the available data from the security\r
+  protocol command, the function shall copy PayloadBufferSize bytes into the\r
+  PayloadBuffer and return EFI_WARN_BUFFER_TOO_SMALL.\r
+\r
+  If PayloadBuffer or PayloadTransferSize is NULL and PayloadBufferSize is non-zero,\r
+  the function shall return EFI_INVALID_PARAMETER.\r
+\r
+  If the given MediaId does not support security protocol commands, the function shall\r
+  return EFI_UNSUPPORTED. If there is no media in the device, the function returns\r
+  EFI_NO_MEDIA. If the MediaId is not the ID for the current media in the device,\r
+  the function returns EFI_MEDIA_CHANGED.\r
+\r
+  If the security protocol fails to complete within the Timeout period, the function\r
+  shall return EFI_TIMEOUT.\r
+\r
+  If the security protocol command completes without an error, the function shall\r
+  return EFI_SUCCESS. If the security protocol command completes with an error, the\r
+  function shall return EFI_DEVICE_ERROR.\r
+\r
+  @param  This                         Indicates a pointer to the calling context.\r
+  @param  MediaId                      ID of the medium to receive data from.\r
+  @param  Timeout                      The timeout, in 100ns units, to use for the execution\r
+                                       of the security protocol command. A Timeout value of 0\r
+                                       means that this function will wait indefinitely for the\r
+                                       security protocol command to execute. If Timeout is greater\r
+                                       than zero, then this function will return EFI_TIMEOUT\r
+                                       if the time required to execute the receive data command\r
+                                       is greater than Timeout.\r
+  @param  SecurityProtocolId           The value of the "Security Protocol" parameter of\r
+                                       the security protocol command to be sent.\r
+  @param  SecurityProtocolSpecificData The value of the "Security Protocol Specific" parameter\r
+                                       of the security protocol command to be sent.\r
+  @param  PayloadBufferSize            Size in bytes of the payload data buffer.\r
+  @param  PayloadBuffer                A pointer to a destination buffer to store the security\r
+                                       protocol command specific payload data for the security\r
+                                       protocol command. The caller is responsible for having\r
+                                       either implicit or explicit ownership of the buffer.\r
+  @param  PayloadTransferSize          A pointer to a buffer to store the size in bytes of the\r
+                                       data written to the payload data buffer.\r
+\r
+  @retval EFI_SUCCESS                  The security protocol command completed successfully.\r
+  @retval EFI_WARN_BUFFER_TOO_SMALL    The PayloadBufferSize was too small to store the available\r
+                                       data from the device. The PayloadBuffer contains the truncated data.\r
+  @retval EFI_UNSUPPORTED              The given MediaId does not support security protocol commands.\r
+  @retval EFI_DEVICE_ERROR             The security protocol command completed with an error.\r
+  @retval EFI_NO_MEDIA                 There is no media in the device.\r
+  @retval EFI_MEDIA_CHANGED            The MediaId is not for the current media.\r
+  @retval EFI_INVALID_PARAMETER        The PayloadBuffer or PayloadTransferSize is NULL and\r
+                                       PayloadBufferSize is non-zero.\r
+  @retval EFI_TIMEOUT                  A timeout occurred while waiting for the security\r
+                                       protocol command to execute.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+SecurityReceiveData (\r
+  IN  EFI_STORAGE_SECURITY_COMMAND_PROTOCOL    *This,\r
+  IN  UINT32                                   MediaId,\r
+  IN  UINT64                                   Timeout,\r
+  IN  UINT8                                    SecurityProtocolId,\r
+  IN  UINT16                                   SecurityProtocolSpecificData,\r
+  IN  UINTN                                    PayloadBufferSize,\r
+  OUT VOID                                     *PayloadBuffer,\r
+  OUT UINTN                                    *PayloadTransferSize\r
+  )\r
+{\r
+  OPAL_SMM_DEVICE              *SmmDev;\r
+\r
+  SmmDev = OPAL_SMM_DEVICE_FROM_THIS (This);\r
+  if (SmmDev == NULL) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  return PerformTrustedIo (\r
+                        SmmDev,\r
+                        OpalRecv,\r
+                        SecurityProtocolId,\r
+                        SecurityProtocolSpecificData,\r
+                        PayloadBufferSize,\r
+                        PayloadBuffer\r
+                        );\r
+}\r
+\r
+/**\r
+  Send a security protocol command to a device.\r
+\r
+  The SendData function sends a security protocol command containing the payload\r
+  PayloadBuffer to the given MediaId. The security protocol command sent is\r
+  defined by SecurityProtocolId and contains the security protocol specific data\r
+  SecurityProtocolSpecificData. If the underlying protocol command requires a\r
+  specific padding for the command payload, the SendData function shall add padding\r
+  bytes to the command payload to satisfy the padding requirements.\r
+\r
+  For devices supporting the SCSI command set, the security protocol command is sent\r
+  using the SECURITY PROTOCOL OUT command defined in SPC-4.\r
+\r
+  For devices supporting the ATA command set, the security protocol command is sent\r
+  using one of the TRUSTED SEND commands defined in ATA8-ACS if PayloadBufferSize\r
+  is non-zero. If the PayloadBufferSize is zero, the security protocol command is\r
+  sent using the Trusted Non-Data command defined in ATA8-ACS.\r
+\r
+  If PayloadBuffer is NULL and PayloadBufferSize is non-zero, the function shall\r
+  return EFI_INVALID_PARAMETER.\r
+\r
+  If the given MediaId does not support security protocol commands, the function\r
+  shall return EFI_UNSUPPORTED. If there is no media in the device, the function\r
+  returns EFI_NO_MEDIA. If the MediaId is not the ID for the current media in the\r
+  device, the function returns EFI_MEDIA_CHANGED.\r
+\r
+  If the security protocol fails to complete within the Timeout period, the function\r
+  shall return EFI_TIMEOUT.\r
+\r
+  If the security protocol command completes without an error, the function shall return\r
+  EFI_SUCCESS. If the security protocol command completes with an error, the function\r
+  shall return EFI_DEVICE_ERROR.\r
+\r
+  @param  This                         Indicates a pointer to the calling context.\r
+  @param  MediaId                      ID of the medium to receive data from.\r
+  @param  Timeout                      The timeout, in 100ns units, to use for the execution\r
+                                       of the security protocol command. A Timeout value of 0\r
+                                       means that this function will wait indefinitely for the\r
+                                       security protocol command to execute. If Timeout is greater\r
+                                       than zero, then this function will return EFI_TIMEOUT\r
+                                       if the time required to execute the send data command\r
+                                       is greater than Timeout.\r
+  @param  SecurityProtocolId           The value of the "Security Protocol" parameter of\r
+                                       the security protocol command to be sent.\r
+  @param  SecurityProtocolSpecificData The value of the "Security Protocol Specific" parameter\r
+                                       of the security protocol command to be sent.\r
+  @param  PayloadBufferSize            Size in bytes of the payload data buffer.\r
+  @param  PayloadBuffer                A pointer to a destination buffer to store the security\r
+                                       protocol command specific payload data for the security\r
+                                       protocol command.\r
+\r
+  @retval EFI_SUCCESS                  The security protocol command completed successfully.\r
+  @retval EFI_UNSUPPORTED              The given MediaId does not support security protocol commands.\r
+  @retval EFI_DEVICE_ERROR             The security protocol command completed with an error.\r
+  @retval EFI_NO_MEDIA                 There is no media in the device.\r
+  @retval EFI_MEDIA_CHANGED            The MediaId is not for the current media.\r
+  @retval EFI_INVALID_PARAMETER        The PayloadBuffer is NULL and PayloadBufferSize is non-zero.\r
+  @retval EFI_TIMEOUT                  A timeout occurred while waiting for the security\r
+                                       protocol command to execute.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+SecuritySendData (\r
+  IN EFI_STORAGE_SECURITY_COMMAND_PROTOCOL    *This,\r
+  IN UINT32                                   MediaId,\r
+  IN UINT64                                   Timeout,\r
+  IN UINT8                                    SecurityProtocolId,\r
+  IN UINT16                                   SecurityProtocolSpecificData,\r
+  IN UINTN                                    PayloadBufferSize,\r
+  IN VOID                                     *PayloadBuffer\r
+  )\r
+{\r
+  OPAL_SMM_DEVICE              *SmmDev;\r
+\r
+  SmmDev = OPAL_SMM_DEVICE_FROM_THIS (This);\r
+  if (SmmDev == NULL) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  return PerformTrustedIo (\r
+                          SmmDev,\r
+                          OpalSend,\r
+                          SecurityProtocolId,\r
+                          SecurityProtocolSpecificData,\r
+                          PayloadBufferSize,\r
+                          PayloadBuffer\r
+                          );\r
+\r
+}\r
+\r
diff --git a/SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalPasswordSmm.h b/SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalPasswordSmm.h
new file mode 100644 (file)
index 0000000..f365136
--- /dev/null
@@ -0,0 +1,299 @@
+/** @file\r
+  Opal password smm driver which is used to support Opal security feature at s3 path.\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
+#ifndef _OPAL_PASSWORD_SMM_H_\r
+#define _OPAL_PASSWORD_SMM_H_\r
+\r
+#include <PiSmm.h>\r
+#include <IndustryStandard/Atapi.h>\r
+\r
+#include <Protocol/SmmSwDispatch2.h>\r
+#include <Protocol/SmmSxDispatch2.h>\r
+#include <Protocol/AtaPassThru.h>\r
+#include <Protocol/PciIo.h>\r
+#include <Protocol/SmmReadyToLock.h>\r
+#include <Protocol/SmmVariable.h>\r
+#include <Protocol/VariableLock.h>\r
+#include <Protocol/StorageSecurityCommand.h>\r
+\r
+#include <Library/OpalPasswordSupportLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/PcdLib.h>\r
+#include <Library/IoLib.h>\r
+#include <Library/TimerLib.h>\r
+#include <Library/PciLib.h>\r
+#include <Library/BaseLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/SmmServicesTableLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/UefiDriverEntryPoint.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/UefiRuntimeServicesTableLib.h>\r
+#include <Library/UefiLib.h>\r
+#include <Library/S3BootScriptLib.h>\r
+#include <Library/DevicePathLib.h>\r
+#include <Library/SmmMemLib.h>\r
+\r
+#include <Library/DxeServicesTableLib.h>\r
+\r
+#include <IndustryStandard/Pci22.h>\r
+\r
+#include <Guid/OpalPasswordExtraInfoVariable.h>\r
+\r
+#include "OpalAhciMode.h"\r
+#include "OpalIdeMode.h"\r
+#include "OpalNvmeMode.h"\r
+\r
+//\r
+// Time out Value for ATA pass through protocol\r
+//\r
+#define ATA_TIMEOUT                      EFI_TIMER_PERIOD_SECONDS (3)\r
+\r
+//\r
+// The payload Length of HDD related ATA commands\r
+//\r
+#define HDD_PAYLOAD                      512\r
+//\r
+// According to ATA spec, the max Length of hdd password is 32 bytes\r
+//\r
+#define OPAL_PASSWORD_MAX_LENGTH         32\r
+\r
+extern VOID                              *mBuffer;\r
+\r
+#pragma pack(1)\r
+\r
+typedef struct {\r
+  UINT32                   Address;\r
+  S3_BOOT_SCRIPT_LIB_WIDTH Width;\r
+} OPAL_HC_PCI_REGISTER_SAVE;\r
+\r
+\r
+typedef struct {\r
+  UINT32                SegNum;\r
+  UINT32                BusNum;\r
+  UINT32                DevNum;\r
+  UINT32                FuncNum;\r
+} PCI_DEVICE;\r
+\r
+/**\r
+* Opal I/O Type utilized by the Trusted IO callback\r
+*\r
+* The type indicates if the I/O is a send or receive\r
+*/\r
+typedef enum {\r
+    //\r
+    // I/O is a TCG Trusted Send command\r
+    //\r
+    OpalSend,\r
+\r
+    //\r
+    // I/O is a TCG Trusted Receive command\r
+    //\r
+    OpalRecv\r
+} OPAL_IO_TYPE;\r
+\r
+\r
+#define OPAL_SMM_DEVICE_SIGNATURE SIGNATURE_32 ('o', 's', 'd', 's')\r
+\r
+typedef struct {\r
+  UINTN                                    Signature;\r
+  LIST_ENTRY                               Link;\r
+\r
+  EFI_STORAGE_SECURITY_COMMAND_PROTOCOL    Sscp;\r
+\r
+  UINT32                                   SegNum;\r
+  UINT32                                   BusNum;\r
+  UINT32                                   DevNum;\r
+  UINT32                                   FuncNum;\r
+\r
+  UINT8                                    DeviceType;\r
+\r
+  UINT32                                   SataPort;\r
+  UINT32                                   SataPortMultiplierPort;\r
+\r
+  UINT32                                   NvmeNamespaceId;\r
+\r
+  UINT8                                    Password[32];\r
+  UINT8                                    PasswordLength;\r
+\r
+  UINT32                                   Length;\r
+  PCI_DEVICE                               *PciBridgeNode;\r
+\r
+  UINT16                                   OpalBaseComId;\r
+} OPAL_SMM_DEVICE;\r
+\r
+#define OPAL_SMM_DEVICE_FROM_THIS(a)  CR (a, OPAL_SMM_DEVICE, Sscp, OPAL_SMM_DEVICE_SIGNATURE)\r
+\r
+#pragma pack()\r
+\r
+/**\r
+  Send a security protocol command to a device that receives data and/or the result\r
+  of one or more commands sent by SendData.\r
+\r
+  The ReceiveData function sends a security protocol command to the given MediaId.\r
+  The security protocol command sent is defined by SecurityProtocolId and contains\r
+  the security protocol specific data SecurityProtocolSpecificData. The function\r
+  returns the data from the security protocol command in PayloadBuffer.\r
+\r
+  For devices supporting the SCSI command set, the security protocol command is sent\r
+  using the SECURITY PROTOCOL IN command defined in SPC-4.\r
+\r
+  For devices supporting the ATA command set, the security protocol command is sent\r
+  using one of the TRUSTED RECEIVE commands defined in ATA8-ACS if PayloadBufferSize\r
+  is non-zero.\r
+\r
+  If the PayloadBufferSize is zero, the security protocol command is sent using the\r
+  Trusted Non-Data command defined in ATA8-ACS.\r
+\r
+  If PayloadBufferSize is too small to store the available data from the security\r
+  protocol command, the function shall copy PayloadBufferSize bytes into the\r
+  PayloadBuffer and return EFI_WARN_BUFFER_TOO_SMALL.\r
+\r
+  If PayloadBuffer or PayloadTransferSize is NULL and PayloadBufferSize is non-zero,\r
+  the function shall return EFI_INVALID_PARAMETER.\r
+\r
+  If the given MediaId does not support security protocol commands, the function shall\r
+  return EFI_UNSUPPORTED. If there is no media in the device, the function returns\r
+  EFI_NO_MEDIA. If the MediaId is not the ID for the current media in the device,\r
+  the function returns EFI_MEDIA_CHANGED.\r
+\r
+  If the security protocol fails to complete within the Timeout period, the function\r
+  shall return EFI_TIMEOUT.\r
+\r
+  If the security protocol command completes without an error, the function shall\r
+  return EFI_SUCCESS. If the security protocol command completes with an error, the\r
+  function shall return EFI_DEVICE_ERROR.\r
+\r
+  @param  This                         Indicates a pointer to the calling context.\r
+  @param  MediaId                      ID of the medium to receive data from.\r
+  @param  Timeout                      The timeout, in 100ns units, to use for the execution\r
+                                       of the security protocol command. A Timeout value of 0\r
+                                       means that this function will wait indefinitely for the\r
+                                       security protocol command to execute. If Timeout is greater\r
+                                       than zero, then this function will return EFI_TIMEOUT\r
+                                       if the time required to execute the receive data command\r
+                                       is greater than Timeout.\r
+  @param  SecurityProtocolId           The value of the "Security Protocol" parameter of\r
+                                       the security protocol command to be sent.\r
+  @param  SecurityProtocolSpecificData The value of the "Security Protocol Specific" parameter\r
+                                       of the security protocol command to be sent.\r
+  @param  PayloadBufferSize            Size in bytes of the payload data buffer.\r
+  @param  PayloadBuffer                A pointer to a destination buffer to store the security\r
+                                       protocol command specific payload data for the security\r
+                                       protocol command. The caller is responsible for having\r
+                                       either implicit or explicit ownership of the buffer.\r
+  @param  PayloadTransferSize          A pointer to a buffer to store the size in bytes of the\r
+                                       data written to the payload data buffer.\r
+\r
+  @retval EFI_SUCCESS                  The security protocol command completed successfully.\r
+  @retval EFI_WARN_BUFFER_TOO_SMALL    The PayloadBufferSize was too small to store the available\r
+                                       data from the device. The PayloadBuffer contains the truncated data.\r
+  @retval EFI_UNSUPPORTED              The given MediaId does not support security protocol commands.\r
+  @retval EFI_DEVICE_ERROR             The security protocol command completed with an error.\r
+  @retval EFI_NO_MEDIA                 There is no media in the device.\r
+  @retval EFI_MEDIA_CHANGED            The MediaId is not for the current media.\r
+  @retval EFI_INVALID_PARAMETER        The PayloadBuffer or PayloadTransferSize is NULL and\r
+                                       PayloadBufferSize is non-zero.\r
+  @retval EFI_TIMEOUT                  A timeout occurred while waiting for the security\r
+                                       protocol command to execute.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+SecurityReceiveData (\r
+  IN  EFI_STORAGE_SECURITY_COMMAND_PROTOCOL   *This,\r
+  IN  UINT32                                  MediaId,\r
+  IN  UINT64                                  Timeout,\r
+  IN  UINT8                                   SecurityProtocolId,\r
+  IN  UINT16                                  SecurityProtocolSpecificData,\r
+  IN  UINTN                                   PayloadBufferSize,\r
+  OUT VOID                                    *PayloadBuffer,\r
+  OUT UINTN                                   *PayloadTransferSize\r
+  );\r
+\r
+/**\r
+  Send a security protocol command to a device.\r
+\r
+  The SendData function sends a security protocol command containing the payload\r
+  PayloadBuffer to the given MediaId. The security protocol command sent is\r
+  defined by SecurityProtocolId and contains the security protocol specific data\r
+  SecurityProtocolSpecificData. If the underlying protocol command requires a\r
+  specific padding for the command payload, the SendData function shall add padding\r
+  bytes to the command payload to satisfy the padding requirements.\r
+\r
+  For devices supporting the SCSI command set, the security protocol command is sent\r
+  using the SECURITY PROTOCOL OUT command defined in SPC-4.\r
+\r
+  For devices supporting the ATA command set, the security protocol command is sent\r
+  using one of the TRUSTED SEND commands defined in ATA8-ACS if PayloadBufferSize\r
+  is non-zero. If the PayloadBufferSize is zero, the security protocol command is\r
+  sent using the Trusted Non-Data command defined in ATA8-ACS.\r
+\r
+  If PayloadBuffer is NULL and PayloadBufferSize is non-zero, the function shall\r
+  return EFI_INVALID_PARAMETER.\r
+\r
+  If the given MediaId does not support security protocol commands, the function\r
+  shall return EFI_UNSUPPORTED. If there is no media in the device, the function\r
+  returns EFI_NO_MEDIA. If the MediaId is not the ID for the current media in the\r
+  device, the function returns EFI_MEDIA_CHANGED.\r
+\r
+  If the security protocol fails to complete within the Timeout period, the function\r
+  shall return EFI_TIMEOUT.\r
+\r
+  If the security protocol command completes without an error, the function shall return\r
+  EFI_SUCCESS. If the security protocol command completes with an error, the function\r
+  shall return EFI_DEVICE_ERROR.\r
+\r
+  @param  This                         Indicates a pointer to the calling context.\r
+  @param  MediaId                      ID of the medium to receive data from.\r
+  @param  Timeout                      The timeout, in 100ns units, to use for the execution\r
+                                       of the security protocol command. A Timeout value of 0\r
+                                       means that this function will wait indefinitely for the\r
+                                       security protocol command to execute. If Timeout is greater\r
+                                       than zero, then this function will return EFI_TIMEOUT\r
+                                       if the time required to execute the receive data command\r
+                                       is greater than Timeout.\r
+  @param  SecurityProtocolId           The value of the "Security Protocol" parameter of\r
+                                       the security protocol command to be sent.\r
+  @param  SecurityProtocolSpecificData The value of the "Security Protocol Specific" parameter\r
+                                       of the security protocol command to be sent.\r
+  @param  PayloadBufferSize            Size in bytes of the payload data buffer.\r
+  @param  PayloadBuffer                A pointer to a destination buffer to store the security\r
+                                       protocol command specific payload data for the security\r
+                                       protocol command.\r
+\r
+  @retval EFI_SUCCESS                  The security protocol command completed successfully.\r
+  @retval EFI_UNSUPPORTED              The given MediaId does not support security protocol commands.\r
+  @retval EFI_DEVICE_ERROR             The security protocol command completed with an error.\r
+  @retval EFI_NO_MEDIA                 There is no media in the device.\r
+  @retval EFI_MEDIA_CHANGED            The MediaId is not for the current media.\r
+  @retval EFI_INVALID_PARAMETER        The PayloadBuffer is NULL and PayloadBufferSize is non-zero.\r
+  @retval EFI_TIMEOUT                  A timeout occurred while waiting for the security\r
+                                       protocol command to execute.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+SecuritySendData (\r
+  IN EFI_STORAGE_SECURITY_COMMAND_PROTOCOL    *This,\r
+  IN UINT32                                   MediaId,\r
+  IN UINT64                                   Timeout,\r
+  IN UINT8                                    SecurityProtocolId,\r
+  IN UINT16                                   SecurityProtocolSpecificData,\r
+  IN UINTN                                    PayloadBufferSize,\r
+  IN VOID                                     *PayloadBuffer\r
+  );\r
+\r
+#endif // _OPAL_PASSWORD_SMM_H_\r
+\r
diff --git a/SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalPasswordSmm.inf b/SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalPasswordSmm.inf
new file mode 100644 (file)
index 0000000..32db88d
--- /dev/null
@@ -0,0 +1,80 @@
+## @file\r
+#  This is a Opal Password Smm driver.\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
+# 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
+[Defines]\r
+  INF_VERSION                    = 0x00010005\r
+  BASE_NAME                      = OpalPasswordSmm\r
+  FILE_GUID                      = 7D24A234-A8C2-4718-BF60-A2EF070F414E\r
+  MODULE_TYPE                    = DXE_SMM_DRIVER\r
+  VERSION_STRING                 = 1.0\r
+  PI_SPECIFICATION_VERSION       = 0x0001000A\r
+  ENTRY_POINT                    = OpalPasswordSmmInit\r
+\r
+#\r
+# The following information is for reference only and not required by the build tools.\r
+#\r
+#  VALID_ARCHITECTURES           = IA32 X64 IPF EBC\r
+#\r
+\r
+[Sources]\r
+  OpalPasswordSmm.c\r
+  OpalPasswordSmm.h\r
+  OpalAhciMode.c\r
+  OpalAhciMode.h\r
+  OpalIdeMode.c\r
+  OpalIdeMode.h\r
+  OpalNvmeMode.c\r
+  OpalNvmeMode.h\r
+  OpalNvmeReg.h\r
+\r
+[Packages]\r
+  MdePkg/MdePkg.dec\r
+  SecurityPkg/SecurityPkg.dec\r
+  MdeModulePkg/MdeModulePkg.dec\r
+\r
+[LibraryClasses]\r
+  UefiBootServicesTableLib\r
+  UefiDriverEntryPoint\r
+  UefiRuntimeServicesTableLib\r
+  DebugLib\r
+  IoLib\r
+  PciLib\r
+  BaseLib\r
+  BaseMemoryLib\r
+  SmmServicesTableLib\r
+  MemoryAllocationLib\r
+  UefiLib\r
+  TimerLib\r
+  S3BootScriptLib\r
+  DxeServicesTableLib\r
+  DevicePathLib\r
+  OpalPasswordSupportLib\r
+  SmmMemLib\r
+\r
+[Guids]\r
+  gOpalExtraInfoVariableGuid                    ## CONSUMES ## GUID\r
+\r
+[Protocols]\r
+  gEfiSmmSwDispatch2ProtocolGuid                ## CONSUMES\r
+  gEfiAtaPassThruProtocolGuid                   ## CONSUMES\r
+  gEfiPciIoProtocolGuid                         ## CONSUMES\r
+  gEfiSmmSxDispatch2ProtocolGuid                ## CONSUMES\r
+  gEfiSmmVariableProtocolGuid                   ## CONSUMES\r
+  gEfiStorageSecurityCommandProtocolGuid        ## CONSUMES\r
+\r
+[BuildOptions]\r
+  MSFT:*_*_*_CC_FLAGS = /Od /GL-\r
+\r
+[Depex]\r
+  gEfiSmmSwDispatch2ProtocolGuid AND\r
+  gEfiSmmSxDispatch2ProtocolGuid AND\r
+  gEfiSmmVariableProtocolGuid\r