]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Bus/Ata/AhciPei/AhciMode.c
MdeModulePkg/AhciPei: Add AHCI mode ATA device support in PEI
[mirror_edk2.git] / MdeModulePkg / Bus / Ata / AhciPei / AhciMode.c
diff --git a/MdeModulePkg/Bus/Ata/AhciPei/AhciMode.c b/MdeModulePkg/Bus/Ata/AhciPei/AhciMode.c
new file mode 100644 (file)
index 0000000..54df319
--- /dev/null
@@ -0,0 +1,2015 @@
+/** @file\r
+  The AhciPei driver is used to manage ATA hard disk device working under AHCI\r
+  mode at PEI phase.\r
+\r
+  Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>\r
+\r
+  This program and the accompanying materials\r
+  are licensed and made available under the terms and conditions\r
+  of the BSD License which accompanies this distribution.  The\r
+  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 "AhciPei.h"\r
+\r
+#define ATA_CMD_TRUST_NON_DATA           0x5B\r
+#define ATA_CMD_TRUST_RECEIVE            0x5C\r
+#define ATA_CMD_TRUST_SEND               0x5E\r
+\r
+//\r
+// Look up table (IsWrite) for EFI_ATA_PASS_THRU_CMD_PROTOCOL\r
+//\r
+EFI_ATA_PASS_THRU_CMD_PROTOCOL  mAtaPassThruCmdProtocols[2] = {\r
+  EFI_ATA_PASS_THRU_PROTOCOL_PIO_DATA_IN,\r
+  EFI_ATA_PASS_THRU_PROTOCOL_PIO_DATA_OUT\r
+};\r
+\r
+//\r
+// Look up table (Lba48Bit, IsIsWrite) for ATA_CMD\r
+//\r
+UINT8 mAtaCommands[2][2] = {\r
+  {\r
+    ATA_CMD_READ_SECTORS,            // 28-bit LBA; PIO read\r
+    ATA_CMD_WRITE_SECTORS            // 28-bit LBA; PIO write\r
+  },\r
+  {\r
+    ATA_CMD_READ_SECTORS_EXT,        // 48-bit LBA; PIO read\r
+    ATA_CMD_WRITE_SECTORS_EXT        // 48-bit LBA; PIO write\r
+  }\r
+};\r
+\r
+//\r
+// Look up table (IsTrustSend) for ATA_CMD\r
+//\r
+UINT8  mAtaTrustCommands[2] = {\r
+  ATA_CMD_TRUST_RECEIVE,    // PIO read\r
+  ATA_CMD_TRUST_SEND        // PIO write\r
+};\r
+\r
+//\r
+// Look up table (Lba48Bit) for maximum transfer block number\r
+//\r
+#define MAX_28BIT_TRANSFER_BLOCK_NUM     0x100\r
+#define MAX_48BIT_TRANSFER_BLOCK_NUM     0xFFFF\r
+\r
+UINT32 mMaxTransferBlockNumber[2] = {\r
+  MAX_28BIT_TRANSFER_BLOCK_NUM,\r
+  MAX_48BIT_TRANSFER_BLOCK_NUM\r
+};\r
+\r
+//\r
+// The maximum total sectors count in 28 bit addressing mode\r
+//\r
+#define MAX_28BIT_ADDRESSING_CAPACITY    0xfffffff\r
+\r
+\r
+/**\r
+  Read AHCI Operation register.\r
+\r
+  @param[in] AhciBar    AHCI bar address.\r
+  @param[in] Offset     The operation register offset.\r
+\r
+  @return The register content read.\r
+\r
+**/\r
+UINT32\r
+AhciReadReg (\r
+  IN UINTN     AhciBar,\r
+  IN UINT32    Offset\r
+  )\r
+{\r
+  UINT32   Data;\r
+\r
+  Data = 0;\r
+  Data = MmioRead32 (AhciBar + Offset);\r
+\r
+  return Data;\r
+}\r
+\r
+/**\r
+  Write AHCI Operation register.\r
+\r
+  @param[in] AhciBar    AHCI bar address.\r
+  @param[in] Offset     The operation register offset.\r
+  @param[in] Data       The Data used to write down.\r
+\r
+**/\r
+VOID\r
+AhciWriteReg (\r
+  IN UINTN     AhciBar,\r
+  IN UINT32    Offset,\r
+  IN UINT32    Data\r
+  )\r
+{\r
+  MmioWrite32 (AhciBar + Offset, Data);\r
+}\r
+\r
+/**\r
+  Do AND operation with the value of AHCI Operation register.\r
+\r
+  @param[in] AhciBar    AHCI bar address.\r
+  @param[in] Offset     The operation register offset.\r
+  @param[in] AndData    The data used to do AND operation.\r
+\r
+**/\r
+VOID\r
+AhciAndReg (\r
+  IN UINTN     AhciBar,\r
+  IN UINT32    Offset,\r
+  IN UINT32    AndData\r
+  )\r
+{\r
+  UINT32 Data;\r
+\r
+  Data  = AhciReadReg (AhciBar, Offset);\r
+  Data &= AndData;\r
+\r
+  AhciWriteReg (AhciBar, Offset, Data);\r
+}\r
+\r
+/**\r
+  Do OR operation with the Value of AHCI Operation register.\r
+\r
+  @param[in] AhciBar    AHCI bar address.\r
+  @param[in] Offset     The operation register offset.\r
+  @param[in] OrData     The Data used to do OR operation.\r
+\r
+**/\r
+VOID\r
+AhciOrReg (\r
+  IN UINTN     AhciBar,\r
+  IN UINT32    Offset,\r
+  IN UINT32    OrData\r
+  )\r
+{\r
+  UINT32 Data;\r
+\r
+  Data  = AhciReadReg (AhciBar, Offset);\r
+  Data |= OrData;\r
+\r
+  AhciWriteReg (AhciBar, Offset, Data);\r
+}\r
+\r
+/**\r
+  Wait for memory set to the test Value.\r
+\r
+  @param[in] AhciBar      AHCI bar address.\r
+  @param[in] Offset       The memory offset to test.\r
+  @param[in] MaskValue    The mask Value of memory.\r
+  @param[in] TestValue    The test Value of memory.\r
+  @param[in] Timeout      The timeout, in 100ns units, 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 UINTN     AhciBar,\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 (AhciBar, 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
+/**\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
+\r
+  @retval EFI_NOT_READY    The memory is not set.\r
+  @retval EFI_SUCCESS      The memory is correct set.\r
+\r
+**/\r
+EFI_STATUS\r
+AhciCheckMemSet (\r
+  IN UINTN     Address,\r
+  IN UINT32    MaskValue,\r
+  IN UINT32    TestValue\r
+  )\r
+{\r
+  UINT32     Value;\r
+\r
+  Value  = *(volatile UINT32 *) Address;\r
+  Value &= MaskValue;\r
+\r
+  if (Value == TestValue) {\r
+    return EFI_SUCCESS;\r
+  } else {\r
+    return EFI_NOT_READY;\r
+  }\r
+}\r
+\r
+/**\r
+  Wait for the value of the specified system memory set to the test value.\r
+\r
+  @param[in] Address      The system 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] Timeout      The timeout, in 100ns units, for wait memory set.\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
+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
+  UINT64     Delay;\r
+  BOOLEAN    InfiniteWait;\r
+\r
+  if (Timeout == 0) {\r
+    InfiniteWait = TRUE;\r
+  } else {\r
+    InfiniteWait = FALSE;\r
+  }\r
+\r
+  Delay =  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 (InfiniteWait || (Delay > 0));\r
+\r
+  return EFI_TIMEOUT;\r
+}\r
+\r
+/**\r
+\r
+  Clear the port interrupt and error status. It will also clear HBA interrupt\r
+  status.\r
+\r
+  @param[in] AhciBar    AHCI bar address.\r
+  @param[in] Port       The number of port.\r
+\r
+**/\r
+VOID\r
+AhciClearPortStatus (\r
+  IN UINTN    AhciBar,\r
+  IN UINT8    Port\r
+  )\r
+{\r
+  UINT32    Offset;\r
+\r
+  //\r
+  // Clear any error status\r
+  //\r
+  Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_SERR;\r
+  AhciWriteReg (AhciBar, Offset, AhciReadReg (AhciBar, Offset));\r
+\r
+  //\r
+  // Clear any port interrupt status\r
+  //\r
+  Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_IS;\r
+  AhciWriteReg (AhciBar, Offset, AhciReadReg (AhciBar, Offset));\r
+\r
+  //\r
+  // Clear any HBA interrupt status\r
+  //\r
+  AhciWriteReg (AhciBar, AHCI_IS_OFFSET, AhciReadReg (AhciBar, AHCI_IS_OFFSET));\r
+}\r
+\r
+/**\r
+  Enable the FIS running for giving port.\r
+\r
+  @param[in] AhciBar    AHCI bar address.\r
+  @param[in] Port       The number of port.\r
+  @param[in] Timeout    The timeout, in 100ns units, to 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
+AhciEnableFisReceive (\r
+  IN UINTN     AhciBar,\r
+  IN UINT8     Port,\r
+  IN UINT64    Timeout\r
+  )\r
+{\r
+  UINT32 Offset;\r
+\r
+  Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CMD;\r
+  AhciOrReg (AhciBar, Offset, AHCI_PORT_CMD_FRE);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Disable the FIS running for giving port.\r
+\r
+  @param[in] AhciBar    AHCI bar address.\r
+  @param[in] Port       The number of port.\r
+  @param[in] Timeout    The timeout value of disabling FIS, uses 100ns as a unit.\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
+AhciDisableFisReceive (\r
+  IN UINTN     AhciBar,\r
+  IN UINT8     Port,\r
+  IN UINT64    Timeout\r
+  )\r
+{\r
+  UINT32    Offset;\r
+  UINT32    Data;\r
+\r
+  Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CMD;\r
+  Data   = AhciReadReg (AhciBar, Offset);\r
+\r
+  //\r
+  // Before disabling Fis receive, the DMA engine of the port should NOT be in\r
+  // running status.\r
+  //\r
+  if ((Data & (AHCI_PORT_CMD_ST | 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 & AHCI_PORT_CMD_FR) != AHCI_PORT_CMD_FR) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  AhciAndReg (AhciBar, Offset, (UINT32)~(AHCI_PORT_CMD_FRE));\r
+\r
+  return AhciWaitMmioSet (\r
+           AhciBar,\r
+           Offset,\r
+           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[in]     Private              The pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA.\r
+  @param[in]     Port                 The number of port.\r
+  @param[in]     PortMultiplier       The number of port multiplier.\r
+  @param[in]     FisIndex             The offset index of the FIS base address.\r
+  @param[in]     CommandFis           The control fis will be used for the transfer.\r
+  @param[in]     CommandList          The command list will be used for the transfer.\r
+  @param[in]     CommandSlotNumber    The command slot will be used for the transfer.\r
+  @param[in,out] DataPhysicalAddr     The pointer to the data buffer pci bus master\r
+                                      address.\r
+  @param[in]     DataLength           The data count to be transferred.\r
+\r
+**/\r
+VOID\r
+AhciBuildCommand (\r
+  IN     PEI_AHCI_CONTROLLER_PRIVATE_DATA    *Private,\r
+  IN     UINT8                               Port,\r
+  IN     UINT8                               PortMultiplier,\r
+  IN     UINT8                               FisIndex,\r
+  IN     EFI_AHCI_COMMAND_FIS                *CommandFis,\r
+  IN     EFI_AHCI_COMMAND_LIST               *CommandList,\r
+  IN     UINT8                               CommandSlotNumber,\r
+  IN OUT VOID                                *DataPhysicalAddr,\r
+  IN     UINT32                              DataLength\r
+  )\r
+{\r
+  EFI_AHCI_REGISTERS    *AhciRegisters;\r
+  UINTN                 AhciBar;\r
+  UINT64                BaseAddr;\r
+  UINT32                PrdtNumber;\r
+  UINT32                PrdtIndex;\r
+  UINTN                 RemainedData;\r
+  UINTN                 MemAddr;\r
+  DATA_64               Data64;\r
+  UINT32                Offset;\r
+\r
+  AhciRegisters = &Private->AhciRegisters;\r
+  AhciBar       = Private->MmioBase;\r
+\r
+  //\r
+  // Filling the PRDT\r
+  //\r
+  PrdtNumber = (UINT32)DivU64x32 (\r
+                         (UINT64)DataLength + AHCI_MAX_DATA_PER_PRDT - 1,\r
+                         AHCI_MAX_DATA_PER_PRDT\r
+                         );\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
+  // Current driver implementation supports up to a maximum of AHCI_MAX_PRDT_NUMBER\r
+  // PRDT entries.\r
+  //\r
+  ASSERT (PrdtNumber <= AHCI_MAX_PRDT_NUMBER);\r
+  if (PrdtNumber > AHCI_MAX_PRDT_NUMBER) {\r
+    return;\r
+  }\r
+\r
+  Data64.Uint64 = (UINTN) (AhciRegisters->AhciRFis) + sizeof (EFI_AHCI_RECEIVED_FIS) * FisIndex;\r
+\r
+  BaseAddr = Data64.Uint64;\r
+\r
+  ZeroMem ((VOID *)((UINTN) BaseAddr), sizeof (EFI_AHCI_RECEIVED_FIS));\r
+\r
+  ZeroMem (AhciRegisters->AhciCmdTable, sizeof (EFI_AHCI_COMMAND_TABLE));\r
+\r
+  CommandFis->AhciCFisPmNum = PortMultiplier;\r
+\r
+  CopyMem (&AhciRegisters->AhciCmdTable->CommandFis, CommandFis, sizeof (EFI_AHCI_COMMAND_FIS));\r
+\r
+  Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CMD;\r
+  AhciAndReg (AhciBar, Offset, (UINT32)~(AHCI_PORT_CMD_DLAE | AHCI_PORT_CMD_ATAPI));\r
+\r
+  RemainedData = (UINTN) DataLength;\r
+  MemAddr      = (UINTN) DataPhysicalAddr;\r
+  CommandList->AhciCmdPrdtl = PrdtNumber;\r
+\r
+  for (PrdtIndex = 0; PrdtIndex < PrdtNumber; PrdtIndex++) {\r
+    if (RemainedData < AHCI_MAX_DATA_PER_PRDT) {\r
+      AhciRegisters->AhciCmdTable->PrdtTable[PrdtIndex].AhciPrdtDbc = (UINT32)RemainedData - 1;\r
+    } else {\r
+      AhciRegisters->AhciCmdTable->PrdtTable[PrdtIndex].AhciPrdtDbc = AHCI_MAX_DATA_PER_PRDT - 1;\r
+    }\r
+\r
+    Data64.Uint64 = (UINT64)MemAddr;\r
+    AhciRegisters->AhciCmdTable->PrdtTable[PrdtIndex].AhciPrdtDba  = Data64.Uint32.Lower32;\r
+    AhciRegisters->AhciCmdTable->PrdtTable[PrdtIndex].AhciPrdtDbau = Data64.Uint32.Upper32;\r
+    RemainedData -= AHCI_MAX_DATA_PER_PRDT;\r
+    MemAddr      += AHCI_MAX_DATA_PER_PRDT;\r
+  }\r
+\r
+  //\r
+  // Set the last PRDT to Interrupt On Complete\r
+  //\r
+  if (PrdtNumber > 0) {\r
+    AhciRegisters->AhciCmdTable->PrdtTable[PrdtNumber - 1].AhciPrdtIoc = 1;\r
+  }\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->AhciCmdTable;\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
+  Buid a command FIS.\r
+\r
+  @param[in,out] CmdFis             A pointer to the EFI_AHCI_COMMAND_FIS data\r
+                                    structure.\r
+  @param[in]     AtaCommandBlock    A pointer to the EFI_ATA_COMMAND_BLOCK data\r
+                                    structure.\r
+\r
+**/\r
+VOID\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 = 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
+  Stop command running for giving port\r
+\r
+  @param[in] AhciBar    AHCI bar address.\r
+  @param[in] Port       The number of port.\r
+  @param[in] Timeout    The timeout value, in 100ns units, to 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
+AhciStopCommand (\r
+  IN  UINTN     AhciBar,\r
+  IN  UINT8     Port,\r
+  IN  UINT64    Timeout\r
+  )\r
+{\r
+  UINT32    Offset;\r
+  UINT32    Data;\r
+\r
+  Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CMD;\r
+  Data   = AhciReadReg (AhciBar, Offset);\r
+\r
+  if ((Data & (AHCI_PORT_CMD_ST | AHCI_PORT_CMD_CR)) == 0) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  if ((Data & AHCI_PORT_CMD_ST) != 0) {\r
+    AhciAndReg (AhciBar, Offset, (UINT32)~(AHCI_PORT_CMD_ST));\r
+  }\r
+\r
+  return AhciWaitMmioSet (\r
+           AhciBar,\r
+           Offset,\r
+           AHCI_PORT_CMD_CR,\r
+           0,\r
+           Timeout\r
+           );\r
+}\r
+\r
+/**\r
+  Start command for give slot on specific port.\r
+\r
+  @param[in] AhciBar       AHCI bar address.\r
+  @param[in] Port          The number of port.\r
+  @param[in] CommandSlot   The number of Command Slot.\r
+  @param[in] Timeout       The timeout value, in 100ns units, to 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
+AhciStartCommand (\r
+  IN  UINTN     AhciBar,\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 (AhciBar, AHCI_CAPABILITY_OFFSET);\r
+\r
+  CmdSlotBit = (UINT32) (1 << CommandSlot);\r
+\r
+  AhciClearPortStatus (\r
+    AhciBar,\r
+    Port\r
+    );\r
+\r
+  Status = AhciEnableFisReceive (\r
+             AhciBar,\r
+             Port,\r
+             Timeout\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CMD;\r
+  PortStatus = AhciReadReg (AhciBar, Offset);\r
+\r
+  StartCmd = 0;\r
+  if ((PortStatus & AHCI_PORT_CMD_ALPE) != 0) {\r
+    StartCmd = AhciReadReg (AhciBar, Offset);\r
+    StartCmd &= ~AHCI_PORT_CMD_ICC_MASK;\r
+    StartCmd |= AHCI_PORT_CMD_ACTIVE;\r
+  }\r
+\r
+  Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_TFD;\r
+  PortTfd = AhciReadReg (AhciBar, Offset);\r
+\r
+  if ((PortTfd & (AHCI_PORT_TFD_BSY | AHCI_PORT_TFD_DRQ)) != 0) {\r
+    if ((Capability & BIT24) != 0) {\r
+      Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CMD;\r
+      AhciOrReg (AhciBar, Offset, AHCI_PORT_CMD_CLO);\r
+\r
+      AhciWaitMmioSet (\r
+        AhciBar,\r
+        Offset,\r
+        AHCI_PORT_CMD_CLO,\r
+        0,\r
+        Timeout\r
+        );\r
+    }\r
+  }\r
+\r
+  Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CMD;\r
+  AhciOrReg (AhciBar, Offset, AHCI_PORT_CMD_ST | StartCmd);\r
+\r
+  //\r
+  // Setting the command\r
+  //\r
+  Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CI;\r
+  AhciAndReg (AhciBar, Offset, 0);\r
+  AhciOrReg  (AhciBar, Offset, CmdSlotBit);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Start a PIO Data transfer on specific port.\r
+\r
+  @param[in]     Private            The pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA.\r
+  @param[in]     Port               The number of port.\r
+  @param[in]     PortMultiplier     The number of port multiplier.\r
+  @param[in]     FisIndex           The offset index of the FIS base address.\r
+  @param[in]     Read               The transfer direction.\r
+  @param[in]     AtaCommandBlock    The EFI_ATA_COMMAND_BLOCK data.\r
+  @param[in,out] AtaStatusBlock     The EFI_ATA_STATUS_BLOCK data.\r
+  @param[in,out] MemoryAddr         The pointer to the data buffer.\r
+  @param[in]     DataCount          The data count to be transferred.\r
+  @param[in]     Timeout            The timeout value of PIO data transfer, uses\r
+                                    100ns as a unit.\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_OUT_OF_RESOURCES    The operation fails due to lack of resources.\r
+  @retval EFI_SUCCESS             The PIO data transfer executes successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+AhciPioTransfer (\r
+  IN     PEI_AHCI_CONTROLLER_PRIVATE_DATA    *Private,\r
+  IN     UINT8                               Port,\r
+  IN     UINT8                               PortMultiplier,\r
+  IN     UINT8                               FisIndex,\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
+  EDKII_IOMMU_OPERATION         MapOp;\r
+  UINTN                         MapLength;\r
+  EFI_PHYSICAL_ADDRESS          PhyAddr;\r
+  VOID                          *MapData;\r
+  EFI_AHCI_REGISTERS            *AhciRegisters;\r
+  UINTN                         AhciBar;\r
+  BOOLEAN                       InfiniteWait;\r
+  UINT32                        Offset;\r
+  UINT32                        OldRfisLo;\r
+  UINT32                        OldRfisHi;\r
+  UINT32                        OldCmdListLo;\r
+  UINT32                        OldCmdListHi;\r
+  DATA_64                       Data64;\r
+  UINT32                        FisBaseAddr;\r
+  UINT32                        Delay;\r
+  EFI_AHCI_COMMAND_FIS          CFis;\r
+  EFI_AHCI_COMMAND_LIST         CmdList;\r
+  UINT32                        PortTfd;\r
+  UINT32                        PrdCount;\r
+  BOOLEAN                       PioFisReceived;\r
+  BOOLEAN                       D2hFisReceived;\r
+\r
+  //\r
+  // Current driver implementation supports up to a maximum of AHCI_MAX_PRDT_NUMBER\r
+  // PRDT entries.\r
+  //\r
+  if (DataCount / (UINT32)AHCI_MAX_PRDT_NUMBER > AHCI_MAX_DATA_PER_PRDT) {\r
+    DEBUG ((\r
+      DEBUG_ERROR,\r
+      "%a: Driver only support a maximum of 0x%x PRDT entries, "\r
+      "current number of data byte 0x%x is too large, maximum allowed is 0x%x.\n",\r
+      __FUNCTION__, AHCI_MAX_PRDT_NUMBER, DataCount,\r
+      AHCI_MAX_PRDT_NUMBER * AHCI_MAX_DATA_PER_PRDT\r
+      ));\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  MapOp     = Read ? EdkiiIoMmuOperationBusMasterWrite :\r
+                     EdkiiIoMmuOperationBusMasterRead;\r
+  MapLength = DataCount;\r
+  Status    = IoMmuMap (\r
+                MapOp,\r
+                MemoryAddr,\r
+                &MapLength,\r
+                &PhyAddr,\r
+                &MapData\r
+                );\r
+  if (EFI_ERROR (Status) || (MapLength != DataCount)) {\r
+    DEBUG ((DEBUG_ERROR, "%a: Fail to map data buffer.\n", __FUNCTION__));\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  AhciRegisters  = &Private->AhciRegisters;\r
+  AhciBar        = Private->MmioBase;\r
+  InfiniteWait   = (Timeout == 0) ? TRUE : FALSE;\r
+\r
+  //\r
+  // Fill FIS base address register\r
+  //\r
+  Offset        = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_FB;\r
+  OldRfisLo     = AhciReadReg (AhciBar, Offset);\r
+  Offset        = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_FBU;\r
+  OldRfisHi     = AhciReadReg (AhciBar, Offset);\r
+  Data64.Uint64 = (UINTN) (AhciRegisters->AhciRFis) + sizeof (EFI_AHCI_RECEIVED_FIS) * FisIndex;\r
+  Offset        = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_FB;\r
+  AhciWriteReg (AhciBar, Offset, Data64.Uint32.Lower32);\r
+  Offset        = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_FBU;\r
+  AhciWriteReg (AhciBar, Offset, Data64.Uint32.Upper32);\r
+\r
+  //\r
+  // Single task envrionment, we only use one command table for all port\r
+  //\r
+  Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CLB;\r
+  OldCmdListLo  = AhciReadReg (AhciBar, Offset);\r
+  Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CLBU;\r
+  OldCmdListHi  = AhciReadReg (AhciBar, Offset);\r
+  Data64.Uint64 = (UINTN) (AhciRegisters->AhciCmdList);\r
+  Offset        = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CLB;\r
+  AhciWriteReg (AhciBar, Offset, Data64.Uint32.Lower32);\r
+  Offset        = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CLBU;\r
+  AhciWriteReg (AhciBar, Offset, Data64.Uint32.Upper32);\r
+\r
+  //\r
+  // Package read needed\r
+  //\r
+  AhciBuildCommandFis (&CFis, AtaCommandBlock);\r
+\r
+  ZeroMem (&CmdList, sizeof (EFI_AHCI_COMMAND_LIST));\r
+\r
+  CmdList.AhciCmdCfl = AHCI_FIS_REGISTER_H2D_LENGTH / 4;\r
+  CmdList.AhciCmdW   = Read ? 0 : 1;\r
+\r
+  AhciBuildCommand (\r
+    Private,\r
+    Port,\r
+    PortMultiplier,\r
+    FisIndex,\r
+    &CFis,\r
+    &CmdList,\r
+    0,\r
+    (VOID *)(UINTN)PhyAddr,\r
+    DataCount\r
+    );\r
+\r
+  Status = AhciStartCommand (\r
+             AhciBar,\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 + sizeof (EFI_AHCI_RECEIVED_FIS) * FisIndex;\r
+  if (Read) {\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
+      PioFisReceived = FALSE;\r
+      D2hFisReceived = FALSE;\r
+      Offset = FisBaseAddr + AHCI_PIO_FIS_OFFSET;\r
+      Status = AhciCheckMemSet (Offset, AHCI_FIS_TYPE_MASK, AHCI_FIS_PIO_SETUP);\r
+      if (!EFI_ERROR (Status)) {\r
+        DEBUG ((DEBUG_INFO, "%a: PioFisReceived.\n", __FUNCTION__));\r
+        PioFisReceived = TRUE;\r
+      }\r
+      //\r
+      // According to SATA 2.6 spec section 11.7, D2h FIS means an error encountered.\r
+      // But Qemu and Marvel 9230 sata controller may just receive a D2h FIS from\r
+      // device after the transaction is finished successfully.\r
+      // To get better device compatibilities, we further check if the PxTFD's\r
+      // ERR bit is set. By this way, we can know if there is a real error happened.\r
+      //\r
+      Offset = FisBaseAddr + AHCI_D2H_FIS_OFFSET;\r
+      Status = AhciCheckMemSet (Offset, AHCI_FIS_TYPE_MASK, AHCI_FIS_REGISTER_D2H);\r
+      if (!EFI_ERROR (Status)) {\r
+        DEBUG ((DEBUG_INFO, "%a: D2hFisReceived.\n", __FUNCTION__));\r
+        D2hFisReceived = TRUE;\r
+      }\r
+\r
+      if (PioFisReceived || D2hFisReceived) {\r
+        Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_TFD;\r
+        PortTfd = AhciReadReg (AhciBar, (UINT32) Offset);\r
+        //\r
+        // PxTFD will be updated if there is a D2H or SetupFIS received.\r
+        //\r
+        if ((PortTfd & 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
+          Status = EFI_SUCCESS;\r
+          break;\r
+        }\r
+      }\r
+\r
+      //\r
+      // Stall for 100 microseconds.\r
+      //\r
+      MicroSecondDelay(100);\r
+\r
+      Delay--;\r
+      if (Delay == 0) {\r
+        Status = EFI_TIMEOUT;\r
+      }\r
+    } while (InfiniteWait || (Delay > 0));\r
+  } else {\r
+    //\r
+    // Wait for D2H Fis is received\r
+    //\r
+    Offset = FisBaseAddr + AHCI_D2H_FIS_OFFSET;\r
+    Status = AhciWaitMemSet (\r
+               Offset,\r
+               AHCI_FIS_TYPE_MASK,\r
+               AHCI_FIS_REGISTER_D2H,\r
+               Timeout\r
+               );\r
+    if (EFI_ERROR (Status)) {\r
+      DEBUG ((DEBUG_ERROR, "%a: AhciWaitMemSet (%r)\n", __FUNCTION__, Status));\r
+      goto Exit;\r
+    }\r
+\r
+    Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_TFD;\r
+    PortTfd = AhciReadReg (AhciBar, (UINT32) Offset);\r
+    if ((PortTfd & AHCI_PORT_TFD_ERR) != 0) {\r
+      Status = EFI_DEVICE_ERROR;\r
+    }\r
+  }\r
+\r
+Exit:\r
+  AhciStopCommand (\r
+    AhciBar,\r
+    Port,\r
+    Timeout\r
+    );\r
+\r
+  AhciDisableFisReceive (\r
+    AhciBar,\r
+    Port,\r
+    Timeout\r
+    );\r
+\r
+  if (MapData != NULL) {\r
+    IoMmuUnmap (MapData);\r
+  }\r
+\r
+  Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_FB;\r
+  AhciWriteReg (AhciBar, Offset, OldRfisLo);\r
+  Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_FBU;\r
+  AhciWriteReg (AhciBar, Offset, OldRfisHi);\r
+\r
+  Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CLB;\r
+  AhciWriteReg (AhciBar, Offset, OldCmdListLo);\r
+  Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CLBU;\r
+  AhciWriteReg (AhciBar, Offset, OldCmdListHi);\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Start a non data transfer on specific port.\r
+\r
+  @param[in]     Private            The pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA.\r
+  @param[in]     Port               The number of port.\r
+  @param[in]     PortMultiplier     The number of port multiplier.\r
+  @param[in]     FisIndex           The offset index of the FIS base address.\r
+  @param[in]     AtaCommandBlock    The EFI_ATA_COMMAND_BLOCK data.\r
+  @param[in,out] AtaStatusBlock     The EFI_ATA_STATUS_BLOCK data.\r
+  @param[in]     Timeout            The timeout value of non data transfer, uses\r
+                                    100ns as a unit.\r
+\r
+  @retval EFI_DEVICE_ERROR        The non 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 non data transfer executes successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+AhciNonDataTransfer (\r
+  IN     PEI_AHCI_CONTROLLER_PRIVATE_DATA    *Private,\r
+  IN     UINT8                               Port,\r
+  IN     UINT8                               PortMultiplier,\r
+  IN     UINT8                               FisIndex,\r
+  IN     EFI_ATA_COMMAND_BLOCK               *AtaCommandBlock,\r
+  IN OUT EFI_ATA_STATUS_BLOCK                *AtaStatusBlock,\r
+  IN     UINT64                              Timeout\r
+  )\r
+{\r
+  EFI_STATUS                  Status;\r
+  UINTN                       AhciBar;\r
+  EFI_AHCI_REGISTERS          *AhciRegisters;\r
+  UINTN                       FisBaseAddr;\r
+  UINTN                       Offset;\r
+  UINT32                      PortTfd;\r
+  EFI_AHCI_COMMAND_FIS        CFis;\r
+  EFI_AHCI_COMMAND_LIST       CmdList;\r
+\r
+  AhciBar        = Private->MmioBase;\r
+  AhciRegisters = &Private->AhciRegisters;\r
+\r
+  //\r
+  // Package read needed\r
+  //\r
+  AhciBuildCommandFis (&CFis, AtaCommandBlock);\r
+\r
+  ZeroMem (&CmdList, sizeof (EFI_AHCI_COMMAND_LIST));\r
+\r
+  CmdList.AhciCmdCfl = AHCI_FIS_REGISTER_H2D_LENGTH / 4;\r
+\r
+  AhciBuildCommand (\r
+    Private,\r
+    Port,\r
+    PortMultiplier,\r
+    FisIndex,\r
+    &CFis,\r
+    &CmdList,\r
+    0,\r
+    NULL,\r
+    0\r
+    );\r
+\r
+  Status = AhciStartCommand (\r
+             AhciBar,\r
+             Port,\r
+             0,\r
+             Timeout\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // Wait device sends the Response Fis\r
+  //\r
+  FisBaseAddr = (UINTN)AhciRegisters->AhciRFis + sizeof (EFI_AHCI_RECEIVED_FIS) * FisIndex;\r
+  Offset      = FisBaseAddr + AHCI_D2H_FIS_OFFSET;\r
+  Status      = AhciWaitMemSet (\r
+                  Offset,\r
+                  AHCI_FIS_TYPE_MASK,\r
+                  AHCI_FIS_REGISTER_D2H,\r
+                  Timeout\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto Exit;\r
+  }\r
+\r
+  Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_TFD;\r
+  PortTfd = AhciReadReg (AhciBar, (UINT32) Offset);\r
+  if ((PortTfd & AHCI_PORT_TFD_ERR) != 0) {\r
+    Status = EFI_DEVICE_ERROR;\r
+  }\r
+\r
+Exit:\r
+  AhciStopCommand (\r
+    AhciBar,\r
+    Port,\r
+    Timeout\r
+    );\r
+\r
+  AhciDisableFisReceive (\r
+    AhciBar,\r
+    Port,\r
+    Timeout\r
+    );\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Do AHCI HBA reset.\r
+\r
+  @param[in] AhciBar         AHCI bar address.\r
+  @param[in] Timeout         The timeout, in 100ns units, to 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
+AhciReset (\r
+  IN UINTN     AhciBar,\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 (AhciBar, 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 & AHCI_CAP_SAM) == 0) {\r
+    AhciOrReg (AhciBar, AHCI_GHC_OFFSET, AHCI_GHC_ENABLE);\r
+  }\r
+\r
+  AhciOrReg (AhciBar, AHCI_GHC_OFFSET, AHCI_GHC_RESET);\r
+\r
+  Delay = (UINT32) (DivU64x32(Timeout, 1000) + 1);\r
+\r
+  do {\r
+    Value = AhciReadReg(AhciBar, AHCI_GHC_OFFSET);\r
+    if ((Value & 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
+  Send Identify Drive command to a specific device.\r
+\r
+  @param[in] Private           The pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA.\r
+  @param[in] Port              The number of port.\r
+  @param[in] PortMultiplier    The port multiplier port number.\r
+  @param[in] FisIndex          The offset index of the FIS base address.\r
+  @param[in] Buffer            The data buffer to store IDENTIFY PACKET data.\r
+\r
+  @retval EFI_SUCCESS              The cmd executes successfully.\r
+  @retval EFI_INVALID_PARAMETER    Buffer is NULL.\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
+\r
+**/\r
+EFI_STATUS\r
+AhciIdentify (\r
+  IN PEI_AHCI_CONTROLLER_PRIVATE_DATA    *Private,\r
+  IN UINT8                               Port,\r
+  IN UINT8                               PortMultiplier,\r
+  IN UINT8                               FisIndex,\r
+  IN ATA_IDENTIFY_DATA                   *Buffer\r
+  )\r
+{\r
+  EFI_STATUS                     Status;\r
+  EFI_ATA_COMMAND_BLOCK    Acb;\r
+  EFI_ATA_STATUS_BLOCK     Asb;\r
+\r
+  if (Buffer == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  ZeroMem (&Acb, sizeof (EFI_ATA_COMMAND_BLOCK));\r
+  ZeroMem (&Asb, sizeof (EFI_ATA_STATUS_BLOCK));\r
+\r
+  Acb.AtaCommand     = ATA_CMD_IDENTIFY_DRIVE;\r
+  Acb.AtaSectorCount = 1;\r
+\r
+  Status = AhciPioTransfer (\r
+             Private,\r
+             Port,\r
+             PortMultiplier,\r
+             FisIndex,\r
+             TRUE,\r
+             &Acb,\r
+             &Asb,\r
+             Buffer,\r
+             sizeof (ATA_IDENTIFY_DATA),\r
+             ATA_TIMEOUT\r
+             );\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Collect the number of bits set within a port bitmap.\r
+\r
+  @param[in] PortBitMap    A 32-bit wide bit map of ATA AHCI ports.\r
+\r
+  @retval The number of bits set in the bitmap.\r
+\r
+**/\r
+UINT8\r
+AhciGetNumberOfPortsFromMap (\r
+  IN UINT32    PortBitMap\r
+  )\r
+{\r
+  UINT8    NumberOfPorts;\r
+\r
+  NumberOfPorts = 0;\r
+\r
+  while (PortBitMap != 0) {\r
+    if ((PortBitMap & ((UINT32)BIT0)) != 0) {\r
+      NumberOfPorts++;\r
+    }\r
+    PortBitMap = PortBitMap >> 1;\r
+  }\r
+\r
+  return NumberOfPorts;\r
+}\r
+\r
+/**\r
+  Get the specified port number from a port bitmap.\r
+\r
+  @param[in]  PortBitMap    A 32-bit wide bit map of ATA AHCI ports.\r
+  @param[in]  PortIndex     The specified port index.\r
+  @param[out] Port          The port number of the port specified by PortIndex.\r
+\r
+  @retval EFI_SUCCESS       The specified port is found and its port number is\r
+                            in Port.\r
+  @retval EFI_NOT_FOUND     Cannot find the specified port within the port bitmap.\r
+\r
+**/\r
+EFI_STATUS\r
+AhciGetPortFromMap (\r
+  IN  UINT32    PortBitMap,\r
+  IN  UINT8     PortIndex,\r
+  OUT UINT8     *Port\r
+  )\r
+{\r
+  if (PortIndex == 0) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  *Port = 0;\r
+\r
+  while (PortBitMap != 0) {\r
+    if ((PortBitMap & ((UINT32)BIT0)) != 0) {\r
+      PortIndex--;\r
+\r
+      //\r
+      // Found the port specified by PortIndex.\r
+      //\r
+      if (PortIndex == 0) {\r
+        return EFI_SUCCESS;\r
+      }\r
+    }\r
+    PortBitMap = PortBitMap >> 1;\r
+    *Port      = *Port + 1;\r
+  }\r
+\r
+  return EFI_NOT_FOUND;\r
+}\r
+\r
+/**\r
+  Allocate transfer-related data struct which is used at AHCI mode.\r
+\r
+  @param[in,out] Private    A pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA instance.\r
+\r
+  @retval EFI_SUCCESS    Data structures are allocated successfully.\r
+  @retval Others         Data structures are not allocated successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+AhciCreateTransferDescriptor (\r
+  IN OUT PEI_AHCI_CONTROLLER_PRIVATE_DATA    *Private\r
+  )\r
+{\r
+  EFI_STATUS              Status;\r
+  UINTN                   AhciBar;\r
+  EFI_AHCI_REGISTERS      *AhciRegisters;\r
+  EFI_PHYSICAL_ADDRESS    DeviceAddress;\r
+  VOID                    *Base;\r
+  VOID                    *Mapping;\r
+  UINT32                  Capability;\r
+  UINT32                  PortImplementBitMap;\r
+  UINT8                   MaxPortNumber;\r
+  UINT8                   MaxCommandSlotNumber;\r
+  UINTN                   MaxRFisSize;\r
+  UINTN                   MaxCmdListSize;\r
+  UINTN                   MaxCmdTableSize;\r
+\r
+  AhciBar       = Private->MmioBase;\r
+  AhciRegisters = &Private->AhciRegisters;\r
+\r
+  //\r
+  // Collect AHCI controller information\r
+  //\r
+  Capability = AhciReadReg (AhciBar, AHCI_CAPABILITY_OFFSET);\r
+\r
+  //\r
+  // Get the number of command slots per port supported by this HBA.\r
+  //\r
+  MaxCommandSlotNumber = (UINT8) (((Capability & 0x1F00) >> 8) + 1);\r
+  ASSERT (MaxCommandSlotNumber > 0);\r
+  if (MaxCommandSlotNumber == 0) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  //\r
+  // Get the highest bit of implemented ports which decides how many bytes are\r
+  // allocated for recived FIS.\r
+  //\r
+  PortImplementBitMap = AhciReadReg (AhciBar, AHCI_PI_OFFSET);\r
+  MaxPortNumber       = (UINT8)(UINTN)(HighBitSet32(PortImplementBitMap) + 1);\r
+  if (MaxPortNumber == 0) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+  //\r
+  // Get the number of ports that actually needed to be initialized.\r
+  //\r
+  MaxPortNumber = MIN (MaxPortNumber, AhciGetNumberOfPortsFromMap (Private->PortBitMap));\r
+\r
+  //\r
+  // Allocate memory for received FIS.\r
+  //\r
+  MaxRFisSize = MaxPortNumber * sizeof (EFI_AHCI_RECEIVED_FIS);\r
+  Status = IoMmuAllocateBuffer (\r
+             EFI_SIZE_TO_PAGES (MaxRFisSize),\r
+             &Base,\r
+             &DeviceAddress,\r
+             &Mapping\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+  ASSERT (DeviceAddress == ((EFI_PHYSICAL_ADDRESS) (UINTN) Base));\r
+  AhciRegisters->AhciRFis    = Base;\r
+  AhciRegisters->AhciRFisMap = Mapping;\r
+  AhciRegisters->MaxRFisSize = MaxRFisSize;\r
+  ZeroMem (AhciRegisters->AhciRFis, EFI_PAGE_SIZE * EFI_SIZE_TO_PAGES (MaxRFisSize));\r
+\r
+  //\r
+  // Allocate memory for command list.\r
+  // Note that the implemenation is a single task model which only use a command\r
+  // list for each port.\r
+  //\r
+  MaxCmdListSize = 1 * sizeof (EFI_AHCI_COMMAND_LIST);\r
+  Status = IoMmuAllocateBuffer (\r
+             EFI_SIZE_TO_PAGES (MaxCmdListSize),\r
+             &Base,\r
+             &DeviceAddress,\r
+             &Mapping\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ErrorExit;\r
+  }\r
+  ASSERT (DeviceAddress == ((EFI_PHYSICAL_ADDRESS) (UINTN) Base));\r
+  AhciRegisters->AhciCmdList    = Base;\r
+  AhciRegisters->AhciCmdListMap = Mapping;\r
+  AhciRegisters->MaxCmdListSize = MaxCmdListSize;\r
+  ZeroMem (AhciRegisters->AhciCmdList, EFI_PAGE_SIZE * EFI_SIZE_TO_PAGES (MaxCmdListSize));\r
+\r
+  //\r
+  // Allocate memory for command table\r
+  // According to AHCI 1.3 spec, a PRD table can contain maximum 65535 entries.\r
+  //\r
+  MaxCmdTableSize = sizeof (EFI_AHCI_COMMAND_TABLE);\r
+  Status = IoMmuAllocateBuffer (\r
+             EFI_SIZE_TO_PAGES (MaxCmdTableSize),\r
+             &Base,\r
+             &DeviceAddress,\r
+             &Mapping\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ErrorExit;\r
+  }\r
+  ASSERT (DeviceAddress == ((EFI_PHYSICAL_ADDRESS) (UINTN) Base));\r
+  AhciRegisters->AhciCmdTable    = Base;\r
+  AhciRegisters->AhciCmdTableMap = Mapping;\r
+  AhciRegisters->MaxCmdTableSize = MaxCmdTableSize;\r
+  ZeroMem (AhciRegisters->AhciCmdTable, EFI_PAGE_SIZE * EFI_SIZE_TO_PAGES (MaxCmdTableSize));\r
+\r
+  return EFI_SUCCESS;\r
+\r
+ErrorExit:\r
+  if (AhciRegisters->AhciRFisMap != NULL) {\r
+    IoMmuFreeBuffer (\r
+       EFI_SIZE_TO_PAGES (AhciRegisters->MaxRFisSize),\r
+       AhciRegisters->AhciRFis,\r
+       AhciRegisters->AhciRFisMap\r
+       );\r
+    AhciRegisters->AhciRFis = NULL;\r
+  }\r
+\r
+  if (AhciRegisters->AhciCmdListMap != NULL) {\r
+    IoMmuFreeBuffer (\r
+       EFI_SIZE_TO_PAGES (AhciRegisters->MaxCmdListSize),\r
+       AhciRegisters->AhciCmdList,\r
+       AhciRegisters->AhciCmdListMap\r
+       );\r
+    AhciRegisters->AhciCmdList = NULL;\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Gets ATA device Capacity according to ATA 6.\r
+\r
+  This function returns the capacity of the ATA device if it follows\r
+  ATA 6 to support 48 bit addressing.\r
+\r
+  @param[in] IdentifyData    A pointer to ATA_IDENTIFY_DATA structure.\r
+\r
+  @return The capacity of the ATA device or 0 if the device does not support\r
+          48-bit addressing defined in ATA 6.\r
+\r
+**/\r
+EFI_LBA\r
+GetAtapi6Capacity (\r
+  IN ATA_IDENTIFY_DATA    *IdentifyData\r
+  )\r
+{\r
+  EFI_LBA                       Capacity;\r
+  EFI_LBA                       TmpLba;\r
+  UINTN                         Index;\r
+\r
+  if ((IdentifyData->command_set_supported_83 & BIT10) == 0) {\r
+    //\r
+    // The device doesn't support 48 bit addressing\r
+    //\r
+    return 0;\r
+  }\r
+\r
+  //\r
+  // 48 bit address feature set is supported, get maximum capacity\r
+  //\r
+  Capacity = 0;\r
+  for (Index = 0; Index < 4; Index++) {\r
+    //\r
+    // Lower byte goes first: word[100] is the lowest word, word[103] is highest\r
+    //\r
+    TmpLba = IdentifyData->maximum_lba_for_48bit_addressing[Index];\r
+    Capacity |= LShiftU64 (TmpLba, 16 * Index);\r
+  }\r
+\r
+  return Capacity;\r
+}\r
+\r
+/**\r
+  Identifies ATA device via the Identify data.\r
+\r
+  This function identifies the ATA device and initializes the media information.\r
+\r
+  @attention This is boundary function that may receive untrusted input.\r
+  @attention The input is from peripheral hardware device.\r
+\r
+  The Identify Drive command response data from an ATA device is the peripheral\r
+  hardware input, so this routine will do basic validation for the Identify Drive\r
+  command response data.\r
+\r
+  @param[in,out] DeviceData    A pointer to PEI_AHCI_ATA_DEVICE_DATA structure.\r
+\r
+  @retval EFI_SUCCESS        The device is successfully identified and media\r
+                             information is correctly initialized.\r
+  @retval EFI_UNSUPPORTED    The device is not a valid ATA device (hard disk).\r
+\r
+**/\r
+EFI_STATUS\r
+IdentifyAtaDevice (\r
+  IN OUT PEI_AHCI_ATA_DEVICE_DATA    *DeviceData\r
+  )\r
+{\r
+  ATA_IDENTIFY_DATA          *IdentifyData;\r
+  EFI_PEI_BLOCK_IO2_MEDIA    *Media;\r
+  EFI_LBA                    Capacity;\r
+  UINT32                     MaxSectorCount;\r
+  UINT16                     PhyLogicSectorSupport;\r
+\r
+  IdentifyData = DeviceData->IdentifyData;\r
+  Media        = &DeviceData->Media;\r
+\r
+  if ((IdentifyData->config & BIT15) != 0) {\r
+    DEBUG ((\r
+      DEBUG_ERROR, "%a: Not a hard disk device on Port 0x%x PortMultiplierPort 0x%x\n",\r
+      __FUNCTION__, DeviceData->Port, DeviceData->PortMultiplier\r
+      ));\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  DEBUG ((\r
+    DEBUG_INFO, "%a: Identify Device: Port 0x%x PortMultiplierPort 0x%x\n",\r
+    __FUNCTION__, DeviceData->Port, DeviceData->PortMultiplier\r
+    ));\r
+\r
+  //\r
+  // Skip checking whether the WORD 88 (supported UltraDMA by drive), since the\r
+  // driver only support PIO data transfer for now.\r
+  //\r
+\r
+  //\r
+  // Get the capacity information of the device.\r
+  //\r
+  Capacity = GetAtapi6Capacity (IdentifyData);\r
+  if (Capacity > MAX_28BIT_ADDRESSING_CAPACITY) {\r
+    //\r
+    // Capacity exceeds 120GB. 48-bit addressing is really needed\r
+    //\r
+    DeviceData->Lba48Bit = TRUE;\r
+  } else {\r
+    //\r
+    // This is a hard disk <= 120GB capacity, treat it as normal hard disk\r
+    //\r
+    Capacity = ((UINT32)IdentifyData->user_addressable_sectors_hi << 16) |\r
+                        IdentifyData->user_addressable_sectors_lo;\r
+    DeviceData->Lba48Bit = FALSE;\r
+  }\r
+\r
+  if (Capacity == 0) {\r
+    DEBUG ((DEBUG_ERROR, "%a: Invalid Capacity (0) for ATA device.\n", __FUNCTION__));\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+  Media->LastBlock = (EFI_PEI_LBA) (Capacity - 1);\r
+\r
+  Media->BlockSize = 0x200;\r
+  //\r
+  // Check whether Long Physical Sector Feature is supported\r
+  //\r
+  PhyLogicSectorSupport = IdentifyData->phy_logic_sector_support;\r
+  DEBUG ((\r
+    DEBUG_INFO, "%a: PhyLogicSectorSupport = 0x%x\n",\r
+    __FUNCTION__, PhyLogicSectorSupport\r
+    ));\r
+  if ((PhyLogicSectorSupport & (BIT14 | BIT15)) == BIT14) {\r
+    //\r
+    // Check logical block size\r
+    //\r
+    if ((PhyLogicSectorSupport & BIT12) != 0) {\r
+      Media->BlockSize = (UINT32) (((IdentifyData->logic_sector_size_hi << 16) |\r
+                                     IdentifyData->logic_sector_size_lo) * sizeof (UINT16));\r
+    }\r
+  }\r
+\r
+  //\r
+  // Check BlockSize validity\r
+  //\r
+  MaxSectorCount = mMaxTransferBlockNumber[DeviceData->Lba48Bit];\r
+  if ((Media->BlockSize == 0) || (Media->BlockSize > MAX_UINT32 / MaxSectorCount)) {\r
+    DEBUG ((DEBUG_ERROR, "%a: Invalid BlockSize (0x%x).\n", __FUNCTION__, Media->BlockSize));\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  DEBUG ((\r
+    DEBUG_INFO, "%a: BlockSize = 0x%x, LastBlock = 0x%lx\n",\r
+    __FUNCTION__, Media->BlockSize, Media->LastBlock\r
+    ));\r
+\r
+  if ((IdentifyData->trusted_computing_support & BIT0) != 0) {\r
+    DEBUG ((DEBUG_INFO, "%a: Found Trust Computing feature support.\n", __FUNCTION__));\r
+    DeviceData->TrustComputing = TRUE;\r
+  }\r
+\r
+  Media->InterfaceType  = MSG_SATA_DP;\r
+  Media->RemovableMedia = FALSE;\r
+  Media->MediaPresent   = TRUE;\r
+  Media->ReadOnly       = FALSE;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Allocate device information data structure to contain device information.\r
+  And insert the data structure to the tail of device list for tracing.\r
+\r
+  @param[in,out] Private               A pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA\r
+                                       instance.\r
+  @param[in]     DeviceIndex           The device index.\r
+  @param[in]     Port                  The port number of the ATA device to send\r
+                                       the command.\r
+  @param[in]     PortMultiplierPort    The port multiplier port number of the ATA\r
+                                       device to send the command.\r
+                                       If there is no port multiplier, then specify\r
+                                       0xFFFF.\r
+  @param[in]     FisIndex              The index of the FIS of the ATA device to\r
+                                       send the command.\r
+  @param[in]     IdentifyData          The data buffer to store the output of the\r
+                                       IDENTIFY command.\r
+\r
+  @retval EFI_SUCCESS                  Successfully insert the ATA device to the\r
+                                       tail of device list.\r
+  @retval EFI_OUT_OF_RESOURCES         Not enough resource.\r
+\r
+**/\r
+EFI_STATUS\r
+CreateNewDevice (\r
+  IN OUT PEI_AHCI_CONTROLLER_PRIVATE_DATA    *Private,\r
+  IN     UINTN                               DeviceIndex,\r
+  IN     UINT16                              Port,\r
+  IN     UINT16                              PortMultiplier,\r
+  IN     UINT8                               FisIndex,\r
+  IN     ATA_IDENTIFY_DATA                   *IdentifyData\r
+  )\r
+{\r
+  PEI_AHCI_ATA_DEVICE_DATA    *DeviceData;\r
+  EFI_STATUS                  Status;\r
+\r
+  DeviceData = AllocateZeroPool (sizeof (PEI_AHCI_ATA_DEVICE_DATA));\r
+  if (DeviceData == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  if (IdentifyData != NULL) {\r
+    DeviceData->IdentifyData = AllocateCopyPool (sizeof (ATA_IDENTIFY_DATA), IdentifyData);\r
+    if (DeviceData->IdentifyData == NULL) {\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+  }\r
+\r
+  DeviceData->Signature      = AHCI_PEI_ATA_DEVICE_DATA_SIGNATURE;\r
+  DeviceData->Port           = Port;\r
+  DeviceData->PortMultiplier = PortMultiplier;\r
+  DeviceData->FisIndex       = FisIndex;\r
+  DeviceData->DeviceIndex    = DeviceIndex;\r
+  DeviceData->Private        = Private;\r
+\r
+  Status = IdentifyAtaDevice (DeviceData);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  if (DeviceData->TrustComputing) {\r
+    Private->TrustComputingDevices++;\r
+    DeviceData->TrustComputingDeviceIndex = Private->TrustComputingDevices;\r
+  }\r
+  Private->ActiveDevices++;\r
+  InsertTailList (&Private->DeviceList, &DeviceData->Link);\r
+\r
+  return EFI_SUCCESS;\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,out] Private    A pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA instance.\r
+\r
+  @retval EFI_SUCCESS             The ATA AHCI controller is initialized successfully.\r
+  @retval EFI_OUT_OF_RESOURCES    Not enough resource to complete while initializing\r
+                                  the controller.\r
+  @retval Others                  A device error occurred while initializing the\r
+                                  controller.\r
+\r
+**/\r
+EFI_STATUS\r
+AhciModeInitialization (\r
+  IN OUT PEI_AHCI_CONTROLLER_PRIVATE_DATA    *Private\r
+  )\r
+{\r
+  EFI_STATUS            Status;\r
+  UINTN                 AhciBar;\r
+  UINT32                Capability;\r
+  UINT32                Value;\r
+  UINT8                 MaxPortNumber;\r
+  UINT32                PortImplementBitMap;\r
+  UINT32                PortInitializeBitMap;\r
+  EFI_AHCI_REGISTERS    *AhciRegisters;\r
+  UINT8                 PortIndex;\r
+  UINT8                 Port;\r
+  DATA_64               Data64;\r
+  UINT32                Data;\r
+  UINT32                Offset;\r
+  UINT32                PhyDetectDelay;\r
+  UINTN                 DeviceIndex;\r
+  ATA_IDENTIFY_DATA     IdentifyData;\r
+\r
+  AhciBar = Private->MmioBase;\r
+\r
+  Status = AhciReset (AhciBar, AHCI_PEI_RESET_TIMEOUT);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((DEBUG_ERROR, "%a: AHCI HBA reset failed with %r.\n", __FUNCTION__, Status));\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  //\r
+  // Collect AHCI controller information\r
+  //\r
+  Capability = AhciReadReg (AhciBar, AHCI_CAPABILITY_OFFSET);\r
+\r
+  //\r
+  // Make sure that GHC.AE bit is set before accessing any AHCI registers.\r
+  //\r
+  Value = AhciReadReg (AhciBar, AHCI_GHC_OFFSET);\r
+  if ((Value & AHCI_GHC_ENABLE) == 0) {\r
+    AhciOrReg (AhciBar, AHCI_GHC_OFFSET, AHCI_GHC_ENABLE);\r
+  }\r
+\r
+  Status = AhciCreateTransferDescriptor (Private);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((\r
+      DEBUG_ERROR,\r
+      "%a: Transfer-related data allocation failed with %r.\n",\r
+      __FUNCTION__, Status\r
+      ));\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  //\r
+  // Get the number of command slots per port supported by this HBA.\r
+  //\r
+  MaxPortNumber = (UINT8) ((Capability & 0x1F) + 1);\r
+\r
+  //\r
+  // Get the bit map of those ports exposed by this HBA.\r
+  // It indicates which ports that the HBA supports are available for software\r
+  // to use.\r
+  //\r
+  PortImplementBitMap = AhciReadReg (AhciBar, AHCI_PI_OFFSET);\r
+\r
+  //\r
+  // Get the number of ports that actually needed to be initialized.\r
+  //\r
+  MaxPortNumber = MIN (MaxPortNumber, (UINT8)(UINTN)(HighBitSet32(PortImplementBitMap) + 1));\r
+  MaxPortNumber = MIN (MaxPortNumber, AhciGetNumberOfPortsFromMap (Private->PortBitMap));\r
+\r
+  PortInitializeBitMap = Private->PortBitMap;\r
+  AhciRegisters        = &Private->AhciRegisters;\r
+  DeviceIndex          = 0;\r
+  //\r
+  // Enumerate ATA ports\r
+  //\r
+  for (PortIndex = 1; PortIndex <= MaxPortNumber; PortIndex ++) {\r
+    Status = AhciGetPortFromMap (PortInitializeBitMap, PortIndex, &Port);\r
+    if ((PortImplementBitMap & (BIT0 << Port)) != 0) {\r
+      //\r
+      // Initialize FIS Base Address Register and Command List Base Address\r
+      // Register for use.\r
+      //\r
+      Data64.Uint64 = (UINTN) (AhciRegisters->AhciRFis) +\r
+                      sizeof (EFI_AHCI_RECEIVED_FIS) * (PortIndex - 1);\r
+      Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_FB;\r
+      AhciWriteReg (AhciBar, Offset, Data64.Uint32.Lower32);\r
+      Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_FBU;\r
+      AhciWriteReg (AhciBar, Offset, Data64.Uint32.Upper32);\r
+\r
+      Data64.Uint64 = (UINTN) (AhciRegisters->AhciCmdList);\r
+      Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CLB;\r
+      AhciWriteReg (AhciBar, Offset, Data64.Uint32.Lower32);\r
+      Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CLBU;\r
+      AhciWriteReg (AhciBar, Offset, Data64.Uint32.Upper32);\r
+\r
+      Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CMD;\r
+      Data = AhciReadReg (AhciBar, Offset);\r
+      if ((Data & AHCI_PORT_CMD_CPD) != 0) {\r
+        AhciOrReg (AhciBar, Offset, AHCI_PORT_CMD_POD);\r
+      }\r
+\r
+      if ((Capability & AHCI_CAP_SSS) != 0) {\r
+        AhciOrReg (AhciBar, Offset, AHCI_PORT_CMD_SUD);\r
+      }\r
+\r
+      //\r
+      // Disable aggressive power management.\r
+      //\r
+      Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_SCTL;\r
+      AhciOrReg (AhciBar, Offset, AHCI_PORT_SCTL_IPM_INIT);\r
+      //\r
+      // Disable the reporting of the corresponding interrupt to system software.\r
+      //\r
+      Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_IE;\r
+      AhciAndReg (AhciBar, Offset, 0);\r
+\r
+      //\r
+      // Enable FIS Receive DMA engine for the first D2H FIS.\r
+      //\r
+      Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CMD;\r
+      AhciOrReg (AhciBar, Offset, AHCI_PORT_CMD_FRE);\r
+\r
+      //\r
+      // Wait no longer than 15 ms to wait the Phy to detect the presence of a device.\r
+      //\r
+      PhyDetectDelay = AHCI_BUS_PHY_DETECT_TIMEOUT;\r
+      Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_SSTS;\r
+      do {\r
+        Data = AhciReadReg (AhciBar, Offset) & AHCI_PORT_SSTS_DET_MASK;\r
+        if ((Data == AHCI_PORT_SSTS_DET_PCE) || (Data == AHCI_PORT_SSTS_DET)) {\r
+          break;\r
+        }\r
+\r
+        MicroSecondDelay (1000);\r
+        PhyDetectDelay--;\r
+      } while (PhyDetectDelay > 0);\r
+\r
+      if (PhyDetectDelay == 0) {\r
+        //\r
+        // No device detected at this port.\r
+        // Clear PxCMD.SUD for those ports at which there are no device present.\r
+        //\r
+        Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CMD;\r
+        AhciAndReg (AhciBar, Offset, (UINT32) ~(AHCI_PORT_CMD_SUD));\r
+        DEBUG ((DEBUG_ERROR, "%a: No device detected at Port %d.\n", __FUNCTION__, Port));\r
+        continue;\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 = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_SERR;\r
+        if (AhciReadReg(AhciBar, Offset) != 0) {\r
+          AhciWriteReg (AhciBar, Offset, AhciReadReg (AhciBar, Offset));\r
+        }\r
+        Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_TFD;\r
+\r
+        Data = AhciReadReg (AhciBar, Offset) & 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
+        DEBUG ((\r
+          DEBUG_ERROR,\r
+          "%a: Port %d device presence detected but phy not ready (TFD=0x%x).\n",\r
+          __FUNCTION__, Port, Data\r
+          ));\r
+        continue;\r
+      }\r
+\r
+      //\r
+      // When the first D2H register FIS is received, the content of PxSIG register is updated.\r
+      //\r
+      Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_SIG;\r
+      Status = AhciWaitMmioSet (\r
+                 AhciBar,\r
+                 Offset,\r
+                 0x0000FFFF,\r
+                 0x00000101,\r
+                 160000000\r
+                 );\r
+      if (EFI_ERROR (Status)) {\r
+        DEBUG ((\r
+          DEBUG_ERROR,\r
+          "%a: Error occured when waiting for the first D2H register FIS - %r\n",\r
+          __FUNCTION__, Status\r
+          ));\r
+        continue;\r
+      }\r
+\r
+      Data = AhciReadReg (AhciBar, Offset);\r
+      if ((Data & AHCI_ATAPI_SIG_MASK) == AHCI_ATA_DEVICE_SIG) {\r
+        Status = AhciIdentify (Private, Port, 0, PortIndex - 1, &IdentifyData);\r
+        if (EFI_ERROR (Status)) {\r
+          DEBUG ((DEBUG_ERROR, "%a: AhciIdentify() failed with %r\n", __FUNCTION__, Status));\r
+          continue;\r
+        }\r
+        DEBUG ((DEBUG_INFO, "%a: ATA hard disk found on Port %d.\n", __FUNCTION__, Port));\r
+      } else {\r
+        continue;\r
+      }\r
+\r
+      //\r
+      // Found an ATA hard disk device, add it into the device list.\r
+      //\r
+      DeviceIndex++;\r
+      CreateNewDevice (\r
+        Private,\r
+        DeviceIndex,\r
+        Port,\r
+        0xFFFF,\r
+        PortIndex - 1,\r
+        &IdentifyData\r
+        );\r
+    }\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Trust transfer data from/to ATA device.\r
+\r
+  This function performs one ATA pass through transaction to do a trust transfer\r
+  from/to ATA device. It chooses the appropriate ATA command and protocol to invoke\r
+  PassThru interface of ATA pass through.\r
+\r
+  @param[in]     DeviceData     Pointer to PEI_AHCI_ATA_DEVICE_DATA structure.\r
+  @param[in,out] Buffer         The pointer to the current transaction buffer.\r
+  @param[in]     SecurityProtocolId\r
+                                The value of the "Security Protocol" parameter\r
+                                of the security protocol command to be sent.\r
+  @param[in]     SecurityProtocolSpecificData\r
+                                The value of the "Security Protocol Specific"\r
+                                parameter of the security protocol command to\r
+                                be sent.\r
+  @param[in]     TransferLength The block number or sector count of the transfer.\r
+  @param[in]     IsTrustSend    Indicates whether it is a trust send operation\r
+                                or not.\r
+  @param[in]     Timeout        The timeout, in 100ns units, to use for the execution\r
+                                of the security protocol command. A Timeout value\r
+                                of 0 means that this function will wait indefinitely\r
+                                for the security protocol command to execute. If\r
+                                Timeout is greater than zero, then this function\r
+                                will return EFI_TIMEOUT if the time required to\r
+                                execute the receive data command is greater than\r
+                                Timeout.\r
+  @param[out]    TransferLengthOut\r
+                                A pointer to a buffer to store the size in bytes\r
+                                of the data written to the buffer. Ignore it when\r
+                                IsTrustSend is TRUE.\r
+\r
+  @retval EFI_SUCCESS    The data transfer is complete successfully.\r
+  @return others         Some error occurs when transferring data.\r
+\r
+**/\r
+EFI_STATUS\r
+TrustTransferAtaDevice (\r
+  IN     PEI_AHCI_ATA_DEVICE_DATA    *DeviceData,\r
+  IN OUT VOID                        *Buffer,\r
+  IN     UINT8                       SecurityProtocolId,\r
+  IN     UINT16                      SecurityProtocolSpecificData,\r
+  IN     UINTN                       TransferLength,\r
+  IN     BOOLEAN                     IsTrustSend,\r
+  IN     UINT64                      Timeout,\r
+  OUT    UINTN                       *TransferLengthOut\r
+  )\r
+{\r
+  PEI_AHCI_CONTROLLER_PRIVATE_DATA    *Private;\r
+  EDKII_PEI_ATA_PASS_THRU_PPI         *AtaPassThru;\r
+  EFI_ATA_COMMAND_BLOCK               Acb;\r
+  EFI_ATA_PASS_THRU_COMMAND_PACKET    Packet;\r
+  EFI_STATUS                          Status;\r
+  VOID                                *NewBuffer;\r
+\r
+  Private     = DeviceData->Private;\r
+  AtaPassThru = &Private->AtaPassThruPpi;\r
+\r
+  //\r
+  // Ensure IsTrustSend are valid boolean values\r
+  //\r
+  ASSERT ((UINTN) IsTrustSend < 2);\r
+  if ((UINTN) IsTrustSend >= 2) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // Prepare for ATA command block.\r
+  //\r
+  ZeroMem (&Acb, sizeof (EFI_ATA_COMMAND_BLOCK));\r
+  if (TransferLength == 0) {\r
+    Acb.AtaCommand    = ATA_CMD_TRUST_NON_DATA;\r
+  } else {\r
+    Acb.AtaCommand    = mAtaTrustCommands[IsTrustSend];\r
+  }\r
+  Acb.AtaFeatures      = SecurityProtocolId;\r
+  Acb.AtaSectorCount   = (UINT8) (TransferLength / 512);\r
+  Acb.AtaSectorNumber  = (UINT8) ((TransferLength / 512) >> 8);\r
+  //\r
+  // NOTE: ATA Spec has no explicitly definition for Security Protocol Specific layout.\r
+  // Here use big endian for Cylinder register.\r
+  //\r
+  Acb.AtaCylinderHigh  = (UINT8) SecurityProtocolSpecificData;\r
+  Acb.AtaCylinderLow   = (UINT8) (SecurityProtocolSpecificData >> 8);\r
+  Acb.AtaDeviceHead    = (UINT8) (BIT7 | BIT6 | BIT5 |\r
+                                  (DeviceData->PortMultiplier == 0xFFFF ?\r
+                                  0 : (DeviceData->PortMultiplier << 4)));\r
+\r
+  //\r
+  // Prepare for ATA pass through packet.\r
+  //\r
+  ZeroMem (&Packet, sizeof (EFI_ATA_PASS_THRU_COMMAND_PACKET));\r
+  if (TransferLength == 0) {\r
+    Packet.InTransferLength  = 0;\r
+    Packet.OutTransferLength = 0;\r
+    Packet.Protocol = EFI_ATA_PASS_THRU_PROTOCOL_ATA_NON_DATA;\r
+  } else if (IsTrustSend) {\r
+    //\r
+    // Check the alignment of the incoming buffer prior to invoking underlying\r
+    // ATA PassThru PPI.\r
+    //\r
+    if ((AtaPassThru->Mode->IoAlign > 1) &&\r
+        !IS_ALIGNED (Buffer, AtaPassThru->Mode->IoAlign)) {\r
+      NewBuffer = AllocateAlignedPages (\r
+                    EFI_SIZE_TO_PAGES (TransferLength),\r
+                    AtaPassThru->Mode->IoAlign\r
+                    );\r
+      if (NewBuffer == NULL) {\r
+        return EFI_OUT_OF_RESOURCES;\r
+      }\r
+\r
+      CopyMem (NewBuffer, Buffer, TransferLength);\r
+      Buffer = NewBuffer;\r
+    }\r
+    Packet.OutDataBuffer = Buffer;\r
+    Packet.OutTransferLength = (UINT32) TransferLength;\r
+    Packet.Protocol = mAtaPassThruCmdProtocols[IsTrustSend];\r
+  } else {\r
+    Packet.InDataBuffer = Buffer;\r
+    Packet.InTransferLength = (UINT32) TransferLength;\r
+    Packet.Protocol = mAtaPassThruCmdProtocols[IsTrustSend];\r
+  }\r
+  Packet.Asb      = NULL;\r
+  Packet.Acb      = &Acb;\r
+  Packet.Timeout  = Timeout;\r
+  Packet.Length   = EFI_ATA_PASS_THRU_LENGTH_BYTES;\r
+\r
+  Status = AtaPassThru->PassThru (\r
+                          AtaPassThru,\r
+                          DeviceData->Port,\r
+                          DeviceData->PortMultiplier,\r
+                          &Packet\r
+                          );\r
+  if (TransferLengthOut != NULL) {\r
+    if (!IsTrustSend) {\r
+      *TransferLengthOut = Packet.InTransferLength;\r
+    }\r
+  }\r
+  return Status;\r
+}\r