public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
From: Hao Wu <hao.a.wu@intel.com>
To: edk2-devel@lists.01.org
Cc: Hao Wu <hao.a.wu@intel.com>, Jian J Wang <jian.j.wang@intel.com>,
	Ray Ni <ray.ni@intel.com>, Eric Dong <eric.dong@intel.com>
Subject: [PATCH v3 08/12] MdeModulePkg/AhciPei: Add AHCI mode ATA device support in PEI
Date: Fri,  1 Feb 2019 13:47:24 +0800	[thread overview]
Message-ID: <20190201054728.8612-9-hao.a.wu@intel.com> (raw)
In-Reply-To: <20190201054728.8612-1-hao.a.wu@intel.com>

REF:https://bugzilla.tianocore.org/show_bug.cgi?id=1409

This commit will add the AHCI mode ATA device support in the PEI phase.

More specifically, the newly add AhciPei driver will consume the ATA AHCI
host controller PPI for ATA controllers working under AHCI code within the
system. And then produces the below PPIs for each controller:

EDKII PEI ATA PassThru PPI
Storage Security Command PPI

Also, the driver will consume the S3StorageDeviceInitList LockBox in S3
phase. The purpose is to perform an on-demand (partial) ATA device
enumeration/initialization on each controller to benefit the S3 resume
performance.

The implementation of this driver is currently based on the below
specifications:
Serial ATA Revision 2.6
Serial ATA Advanced Host Controller Interface (AHCI) 1.3.1
AT Attachment with Packet Interface - 6 (ATA/ATAPI-6)

Cc: Jian J Wang <jian.j.wang@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Eric Dong <eric.dong@intel.com>
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Hao Wu <hao.a.wu@intel.com>
---
 MdeModulePkg/MdeModulePkg.dsc                         |    1 +
 MdeModulePkg/Bus/Ata/AhciPei/AhciPei.inf              |   74 +
 MdeModulePkg/Bus/Ata/AhciPei/AhciPei.h                |  708 +++++++
 MdeModulePkg/Bus/Ata/AhciPei/AhciPeiPassThru.h        |  184 ++
 MdeModulePkg/Bus/Ata/AhciPei/AhciPeiStorageSecurity.h |  247 +++
 MdeModulePkg/Bus/Ata/AhciPei/AhciMode.c               | 2015 ++++++++++++++++++++
 MdeModulePkg/Bus/Ata/AhciPei/AhciPei.c                |  304 +++
 MdeModulePkg/Bus/Ata/AhciPei/AhciPeiPassThru.c        |  521 +++++
 MdeModulePkg/Bus/Ata/AhciPei/AhciPeiS3.c              |  139 ++
 MdeModulePkg/Bus/Ata/AhciPei/AhciPeiStorageSecurity.c |  391 ++++
 MdeModulePkg/Bus/Ata/AhciPei/DevicePath.c             |  284 +++
 MdeModulePkg/Bus/Ata/AhciPei/DmaMem.c                 |  270 +++
 MdeModulePkg/Bus/Ata/AhciPei/AhciPei.uni              |   21 +
 MdeModulePkg/Bus/Ata/AhciPei/AhciPeiExtra.uni         |   19 +
 14 files changed, 5178 insertions(+)

diff --git a/MdeModulePkg/MdeModulePkg.dsc b/MdeModulePkg/MdeModulePkg.dsc
index 4f2ac8ae89..b4ebd845d3 100644
--- a/MdeModulePkg/MdeModulePkg.dsc
+++ b/MdeModulePkg/MdeModulePkg.dsc
@@ -241,6 +241,7 @@
   MdeModulePkg/Bus/Pci/SataControllerDxe/SataControllerDxe.inf
   MdeModulePkg/Bus/Ata/AtaBusDxe/AtaBusDxe.inf
   MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AtaAtapiPassThru.inf
+  MdeModulePkg/Bus/Ata/AhciPei/AhciPei.inf
   MdeModulePkg/Bus/Scsi/ScsiBusDxe/ScsiBusDxe.inf
   MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDiskDxe.inf
   MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBusDxe.inf
diff --git a/MdeModulePkg/Bus/Ata/AhciPei/AhciPei.inf b/MdeModulePkg/Bus/Ata/AhciPei/AhciPei.inf
new file mode 100644
index 0000000000..e02d2272fb
--- /dev/null
+++ b/MdeModulePkg/Bus/Ata/AhciPei/AhciPei.inf
@@ -0,0 +1,74 @@
+## @file
+#  The AhciPei driver is used to manage ATA hard disk device working under AHCI
+#  mode at PEI phase.
+#
+#  Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
+#
+#  This program and the accompanying materials
+#  are licensed and made available under the terms and conditions of the BSD License
+#  which accompanies this distribution.  The full text of the license may be found at
+#  http://opensource.org/licenses/bsd-license.php
+#
+#  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = AhciPei
+  MODULE_UNI_FILE                = AhciPei.uni
+  FILE_GUID                      = 79E5CA15-7A2D-4F37-A63B-D1C7BBCA47AD
+  MODULE_TYPE                    = PEIM
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = AtaAhciPeimEntry
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+#  VALID_ARCHITECTURES           = IA32 X64 EBC
+#
+
+[Sources]
+  AhciPei.c
+  AhciPei.h
+  AhciPeiPassThru.c
+  AhciPeiPassThru.h
+  AhciPeiS3.c
+  AhciPeiStorageSecurity.c
+  AhciPeiStorageSecurity.h
+  AhciMode.c
+  DevicePath.c
+  DmaMem.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+
+[LibraryClasses]
+  DebugLib
+  PeiServicesLib
+  MemoryAllocationLib
+  BaseMemoryLib
+  IoLib
+  TimerLib
+  LockBoxLib
+  PeimEntryPoint
+
+[Ppis]
+  gEdkiiPeiAtaAhciHostControllerPpiGuid          ## CONSUMES
+  gEdkiiIoMmuPpiGuid                             ## CONSUMES
+  gEfiEndOfPeiSignalPpiGuid                      ## CONSUMES
+  gEdkiiPeiAtaPassThruPpiGuid                    ## SOMETIMES_PRODUCES
+  gEdkiiPeiStorageSecurityCommandPpiGuid         ## SOMETIMES_PRODUCES
+
+[Guids]
+  gS3StorageDeviceInitListGuid                   ## SOMETIMES_CONSUMES ## UNDEFINED
+
+[Depex]
+  gEfiPeiMemoryDiscoveredPpiGuid AND
+  gEfiPeiMasterBootModePpiGuid AND
+  gEdkiiPeiAtaAhciHostControllerPpiGuid
+
+[UserExtensions.TianoCore."ExtraFiles"]
+  AhciPeiExtra.uni
diff --git a/MdeModulePkg/Bus/Ata/AhciPei/AhciPei.h b/MdeModulePkg/Bus/Ata/AhciPei/AhciPei.h
new file mode 100644
index 0000000000..e53330b2e1
--- /dev/null
+++ b/MdeModulePkg/Bus/Ata/AhciPei/AhciPei.h
@@ -0,0 +1,708 @@
+/** @file
+  The AhciPei driver is used to manage ATA hard disk device working under AHCI
+  mode at PEI phase.
+
+  Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
+
+  This program and the accompanying materials
+  are licensed and made available under the terms and conditions
+  of the BSD License which accompanies this distribution.  The
+  full text of the license may be found at
+  http://opensource.org/licenses/bsd-license.php
+
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _AHCI_PEI_H_
+#define _AHCI_PEI_H_
+
+#include <PiPei.h>
+
+#include <IndustryStandard/Atapi.h>
+
+#include <Ppi/AtaAhciController.h>
+#include <Ppi/IoMmu.h>
+#include <Ppi/EndOfPeiPhase.h>
+#include <Ppi/AtaPassThru.h>
+#include <Ppi/BlockIo2.h>
+#include <Ppi/StorageSecurityCommand.h>
+
+#include <Library/DebugLib.h>
+#include <Library/PeiServicesLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/IoLib.h>
+#include <Library/TimerLib.h>
+
+//
+// Structure forward declarations
+//
+typedef struct _PEI_AHCI_CONTROLLER_PRIVATE_DATA  PEI_AHCI_CONTROLLER_PRIVATE_DATA;
+
+#include "AhciPeiPassThru.h"
+#include "AhciPeiStorageSecurity.h"
+
+//
+// ATA AHCI driver implementation related definitions
+//
+//
+// Refer SATA1.0a spec section 5.2, the Phy detection time should be less than 10ms.
+// The value is in millisecond units. Add a bit of margin for robustness.
+//
+#define AHCI_BUS_PHY_DETECT_TIMEOUT            15
+//
+// Refer SATA1.0a spec, the bus reset time should be less than 1s.
+// The value is in 100ns units.
+//
+#define AHCI_PEI_RESET_TIMEOUT                 10000000
+//
+// Time out Value for ATA pass through protocol, in 100ns units.
+//
+#define ATA_TIMEOUT                            30000000
+//
+// Maximal number of Physical Region Descriptor Table entries supported.
+//
+#define AHCI_MAX_PRDT_NUMBER                   8
+
+#define AHCI_CAPABILITY_OFFSET                 0x0000
+#define   AHCI_CAP_SAM                         BIT18
+#define   AHCI_CAP_SSS                         BIT27
+
+#define AHCI_GHC_OFFSET                        0x0004
+#define   AHCI_GHC_RESET                       BIT0
+#define   AHCI_GHC_ENABLE                      BIT31
+
+#define AHCI_IS_OFFSET                         0x0008
+#define AHCI_PI_OFFSET                         0x000C
+
+#define AHCI_MAX_PORTS                         32
+
+typedef struct {
+  UINT32  Lower32;
+  UINT32  Upper32;
+} DATA_32;
+
+typedef union {
+  DATA_32   Uint32;
+  UINT64    Uint64;
+} DATA_64;
+
+#define AHCI_ATAPI_SIG_MASK                    0xFFFF0000
+#define AHCI_ATA_DEVICE_SIG                    0x00000000
+
+//
+// Each PRDT entry can point to a memory block up to 4M byte
+//
+#define AHCI_MAX_DATA_PER_PRDT                 0x400000
+
+#define AHCI_FIS_REGISTER_H2D                  0x27      //Register FIS - Host to Device
+#define   AHCI_FIS_REGISTER_H2D_LENGTH         20
+#define AHCI_FIS_REGISTER_D2H                  0x34      //Register FIS - Device to Host
+#define AHCI_FIS_PIO_SETUP                     0x5F      //PIO Setup FIS - Device to Host
+
+#define AHCI_D2H_FIS_OFFSET                    0x40
+#define AHCI_PIO_FIS_OFFSET                    0x20
+#define AHCI_FIS_TYPE_MASK                     0xFF
+
+//
+// Port register
+//
+#define AHCI_PORT_START                        0x0100
+#define AHCI_PORT_REG_WIDTH                    0x0080
+#define AHCI_PORT_CLB                          0x0000
+#define AHCI_PORT_CLBU                         0x0004
+#define AHCI_PORT_FB                           0x0008
+#define AHCI_PORT_FBU                          0x000C
+#define AHCI_PORT_IS                           0x0010
+#define AHCI_PORT_IE                           0x0014
+#define AHCI_PORT_CMD                          0x0018
+#define   AHCI_PORT_CMD_ST                     BIT0
+#define   AHCI_PORT_CMD_SUD                    BIT1
+#define   AHCI_PORT_CMD_POD                    BIT2
+#define   AHCI_PORT_CMD_CLO                    BIT3
+#define   AHCI_PORT_CMD_FRE                    BIT4
+#define   AHCI_PORT_CMD_FR                     BIT14
+#define   AHCI_PORT_CMD_CR                     BIT15
+#define   AHCI_PORT_CMD_CPD                    BIT20
+#define   AHCI_PORT_CMD_ATAPI                  BIT24
+#define   AHCI_PORT_CMD_DLAE                   BIT25
+#define   AHCI_PORT_CMD_ALPE                   BIT26
+#define   AHCI_PORT_CMD_ACTIVE                 (1 << 28)
+#define   AHCI_PORT_CMD_ICC_MASK               (BIT28 | BIT29 | BIT30 | BIT31)
+
+#define AHCI_PORT_TFD                          0x0020
+#define   AHCI_PORT_TFD_ERR                    BIT0
+#define   AHCI_PORT_TFD_DRQ                    BIT3
+#define   AHCI_PORT_TFD_BSY                    BIT7
+#define   AHCI_PORT_TFD_MASK                   (BIT7 | BIT3 | BIT0)
+
+#define AHCI_PORT_SIG                          0x0024
+#define AHCI_PORT_SSTS                         0x0028
+#define   AHCI_PORT_SSTS_DET_MASK              0x000F
+#define   AHCI_PORT_SSTS_DET                   0x0001
+#define   AHCI_PORT_SSTS_DET_PCE               0x0003
+
+#define AHCI_PORT_SCTL                         0x002C
+#define   AHCI_PORT_SCTL_IPM_INIT              0x0300
+
+#define AHCI_PORT_SERR                         0x0030
+#define AHCI_PORT_CI                           0x0038
+
+#define IS_ALIGNED(addr, size)                 (((UINTN) (addr) & (size - 1)) == 0)
+#define TIMER_PERIOD_SECONDS(Seconds)          MultU64x32((UINT64)(Seconds), 10000000)
+
+#pragma pack(1)
+
+//
+// Received FIS structure
+//
+typedef struct {
+  UINT8     AhciDmaSetupFis[0x1C];         // Dma Setup Fis: offset 0x00
+  UINT8     AhciDmaSetupFisRsvd[0x04];
+  UINT8     AhciPioSetupFis[0x14];         // Pio Setup Fis: offset 0x20
+  UINT8     AhciPioSetupFisRsvd[0x0C];
+  UINT8     AhciD2HRegisterFis[0x14];      // D2H Register Fis: offset 0x40
+  UINT8     AhciD2HRegisterFisRsvd[0x04];
+  UINT64    AhciSetDeviceBitsFis;          // Set Device Bits Fix: offset 0x58
+  UINT8     AhciUnknownFis[0x40];          // Unkonwn Fis: offset 0x60
+  UINT8     AhciUnknownFisRsvd[0x60];
+} EFI_AHCI_RECEIVED_FIS;
+
+//
+// Command List structure includes total 32 entries.
+// The entry Data structure is listed at the following.
+//
+typedef struct {
+  UINT32    AhciCmdCfl:5;      //Command FIS Length
+  UINT32    AhciCmdA:1;        //ATAPI
+  UINT32    AhciCmdW:1;        //Write
+  UINT32    AhciCmdP:1;        //Prefetchable
+  UINT32    AhciCmdR:1;        //Reset
+  UINT32    AhciCmdB:1;        //BIST
+  UINT32    AhciCmdC:1;        //Clear Busy upon R_OK
+  UINT32    AhciCmdRsvd:1;
+  UINT32    AhciCmdPmp:4;      //Port Multiplier Port
+  UINT32    AhciCmdPrdtl:16;   //Physical Region Descriptor Table Length
+  UINT32    AhciCmdPrdbc;      //Physical Region Descriptor Byte Count
+  UINT32    AhciCmdCtba;       //Command Table Descriptor Base Address
+  UINT32    AhciCmdCtbau;      //Command Table Descriptor Base Address Upper 32-BITs
+  UINT32    AhciCmdRsvd1[4];
+} EFI_AHCI_COMMAND_LIST;
+
+//
+// This is a software constructed FIS.
+// For Data transfer operations, this is the H2D Register FIS format as
+// specified in the Serial ATA Revision 2.6 specification.
+//
+typedef struct {
+  UINT8     AhciCFisType;
+  UINT8     AhciCFisPmNum:4;
+  UINT8     AhciCFisRsvd:1;
+  UINT8     AhciCFisRsvd1:1;
+  UINT8     AhciCFisRsvd2:1;
+  UINT8     AhciCFisCmdInd:1;
+  UINT8     AhciCFisCmd;
+  UINT8     AhciCFisFeature;
+  UINT8     AhciCFisSecNum;
+  UINT8     AhciCFisClyLow;
+  UINT8     AhciCFisClyHigh;
+  UINT8     AhciCFisDevHead;
+  UINT8     AhciCFisSecNumExp;
+  UINT8     AhciCFisClyLowExp;
+  UINT8     AhciCFisClyHighExp;
+  UINT8     AhciCFisFeatureExp;
+  UINT8     AhciCFisSecCount;
+  UINT8     AhciCFisSecCountExp;
+  UINT8     AhciCFisRsvd3;
+  UINT8     AhciCFisControl;
+  UINT8     AhciCFisRsvd4[4];
+  UINT8     AhciCFisRsvd5[44];
+} EFI_AHCI_COMMAND_FIS;
+
+//
+// ACMD: ATAPI command (12 or 16 bytes)
+//
+typedef struct {
+  UINT8    AtapiCmd[0x10];
+} EFI_AHCI_ATAPI_COMMAND;
+
+//
+// Physical Region Descriptor Table includes up to 65535 entries
+// The entry data structure is listed at the following.
+// the actual entry number comes from the PRDTL field in the command
+// list entry for this command slot.
+//
+typedef struct {
+  UINT32    AhciPrdtDba;       //Data Base Address
+  UINT32    AhciPrdtDbau;      //Data Base Address Upper 32-BITs
+  UINT32    AhciPrdtRsvd;
+  UINT32    AhciPrdtDbc:22;    //Data Byte Count
+  UINT32    AhciPrdtRsvd1:9;
+  UINT32    AhciPrdtIoc:1;     //Interrupt on Completion
+} EFI_AHCI_COMMAND_PRDT;
+
+//
+// Command table Data strucute which is pointed to by the entry in the command list
+//
+typedef struct {
+  EFI_AHCI_COMMAND_FIS      CommandFis;       // A software constructed FIS.
+  EFI_AHCI_ATAPI_COMMAND    AtapiCmd;         // 12 or 16 bytes ATAPI cmd.
+  UINT8                     Reserved[0x30];
+  //
+  // The scatter/gather list for Data transfer.
+  //
+  EFI_AHCI_COMMAND_PRDT     PrdtTable[AHCI_MAX_PRDT_NUMBER];
+} EFI_AHCI_COMMAND_TABLE;
+
+#pragma pack()
+
+typedef struct {
+  EFI_AHCI_RECEIVED_FIS     *AhciRFis;
+  EFI_AHCI_COMMAND_LIST     *AhciCmdList;
+  EFI_AHCI_COMMAND_TABLE    *AhciCmdTable;
+  UINTN                     MaxRFisSize;
+  UINTN                     MaxCmdListSize;
+  UINTN                     MaxCmdTableSize;
+  VOID                      *AhciRFisMap;
+  VOID                      *AhciCmdListMap;
+  VOID                      *AhciCmdTableMap;
+} EFI_AHCI_REGISTERS;
+
+//
+// Unique signature for AHCI ATA device information structure.
+//
+#define AHCI_PEI_ATA_DEVICE_DATA_SIGNATURE    SIGNATURE_32 ('A', 'P', 'A', 'D')
+
+//
+// AHCI mode device information structure.
+//
+typedef struct {
+  UINT32                              Signature;
+  LIST_ENTRY                          Link;
+
+  UINT16                              Port;
+  UINT16                              PortMultiplier;
+  UINT8                               FisIndex;
+  UINTN                               DeviceIndex;
+  ATA_IDENTIFY_DATA                   *IdentifyData;
+
+  BOOLEAN                             Lba48Bit;
+  BOOLEAN                             TrustComputing;
+  UINTN                               TrustComputingDeviceIndex;
+  EFI_PEI_BLOCK_IO2_MEDIA             Media;
+
+  PEI_AHCI_CONTROLLER_PRIVATE_DATA    *Private;
+} PEI_AHCI_ATA_DEVICE_DATA;
+
+#define AHCI_PEI_ATA_DEVICE_INFO_FROM_THIS(a)  \
+  CR (a,                                       \
+      PEI_AHCI_ATA_DEVICE_DATA,                \
+      Link,                                    \
+      AHCI_PEI_ATA_DEVICE_DATA_SIGNATURE       \
+      );
+
+//
+// Unique signature for private data structure.
+//
+#define AHCI_PEI_CONTROLLER_PRIVATE_DATA_SIGNATURE    SIGNATURE_32 ('A','P','C','P')
+
+//
+// ATA AHCI controller private data structure.
+//
+struct _PEI_AHCI_CONTROLLER_PRIVATE_DATA {
+  UINT32                                Signature;
+  UINTN                                 MmioBase;
+  UINTN                                 DevicePathLength;
+  EFI_DEVICE_PATH_PROTOCOL              *DevicePath;
+
+  EFI_ATA_PASS_THRU_MODE                AtaPassThruMode;
+  EDKII_PEI_ATA_PASS_THRU_PPI           AtaPassThruPpi;
+  EDKII_PEI_STORAGE_SECURITY_CMD_PPI    StorageSecurityPpi;
+  EFI_PEI_PPI_DESCRIPTOR                AtaPassThruPpiList;
+  EFI_PEI_PPI_DESCRIPTOR                BlkIoPpiList;
+  EFI_PEI_PPI_DESCRIPTOR                BlkIo2PpiList;
+  EFI_PEI_PPI_DESCRIPTOR                StorageSecurityPpiList;
+  EFI_PEI_NOTIFY_DESCRIPTOR             EndOfPeiNotifyList;
+
+  EFI_AHCI_REGISTERS                    AhciRegisters;
+
+  UINT32                                PortBitMap;
+  UINT32                                ActiveDevices;
+  UINT32                                TrustComputingDevices;
+  LIST_ENTRY                            DeviceList;
+
+  UINT16                                PreviousPort;
+  UINT16                                PreviousPortMultiplier;
+};
+
+#define GET_AHCI_PEIM_HC_PRIVATE_DATA_FROM_THIS_PASS_THRU(a)           \
+  CR (a, PEI_AHCI_CONTROLLER_PRIVATE_DATA, AtaPassThruPpi, AHCI_PEI_CONTROLLER_PRIVATE_DATA_SIGNATURE)
+#define GET_AHCI_PEIM_HC_PRIVATE_DATA_FROM_THIS_BLKIO(a)               \
+  CR (a, PEI_AHCI_CONTROLLER_PRIVATE_DATA, BlkIoPpi, AHCI_PEI_CONTROLLER_PRIVATE_DATA_SIGNATURE)
+#define GET_AHCI_PEIM_HC_PRIVATE_DATA_FROM_THIS_BLKIO2(a)              \
+  CR (a, PEI_AHCI_CONTROLLER_PRIVATE_DATA, BlkIo2Ppi, AHCI_PEI_CONTROLLER_PRIVATE_DATA_SIGNATURE)
+#define GET_AHCI_PEIM_HC_PRIVATE_DATA_FROM_THIS_STROAGE_SECURITY(a)    \
+  CR (a, PEI_AHCI_CONTROLLER_PRIVATE_DATA, StorageSecurityPpi, AHCI_PEI_CONTROLLER_PRIVATE_DATA_SIGNATURE)
+#define GET_AHCI_PEIM_HC_PRIVATE_DATA_FROM_THIS_NOTIFY(a)              \
+  CR (a, PEI_AHCI_CONTROLLER_PRIVATE_DATA, EndOfPeiNotifyList, AHCI_PEI_CONTROLLER_PRIVATE_DATA_SIGNATURE)
+
+//
+// Global variables
+//
+extern UINT32    mMaxTransferBlockNumber[2];
+
+//
+// Internal functions
+//
+
+/**
+  Allocates pages that are suitable for an OperationBusMasterCommonBuffer or
+  OperationBusMasterCommonBuffer64 mapping.
+
+  @param  Pages                 The number of pages to allocate.
+  @param  HostAddress           A pointer to store the base system memory address of the
+                                allocated range.
+  @param  DeviceAddress         The resulting map address for the bus master PCI controller to use to
+                                access the hosts HostAddress.
+  @param  Mapping               A resulting value to pass to Unmap().
+
+  @retval EFI_SUCCESS           The requested memory pages were allocated.
+  @retval EFI_UNSUPPORTED       Attributes is unsupported. The only legal attribute bits are
+                                MEMORY_WRITE_COMBINE and MEMORY_CACHED.
+  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+  @retval EFI_OUT_OF_RESOURCES  The memory pages could not be allocated.
+
+**/
+EFI_STATUS
+IoMmuAllocateBuffer (
+  IN UINTN                  Pages,
+  OUT VOID                  **HostAddress,
+  OUT EFI_PHYSICAL_ADDRESS  *DeviceAddress,
+  OUT VOID                  **Mapping
+  );
+
+/**
+  Frees memory that was allocated with AllocateBuffer().
+
+  @param  Pages                 The number of pages to free.
+  @param  HostAddress           The base system memory address of the allocated range.
+  @param  Mapping               The mapping value returned from Map().
+
+  @retval EFI_SUCCESS           The requested memory pages were freed.
+  @retval EFI_INVALID_PARAMETER The memory range specified by HostAddress and Pages
+                                was not allocated with AllocateBuffer().
+
+**/
+EFI_STATUS
+IoMmuFreeBuffer (
+  IN UINTN                  Pages,
+  IN VOID                   *HostAddress,
+  IN VOID                   *Mapping
+  );
+
+/**
+  Provides the controller-specific addresses required to access system memory from a
+  DMA bus master.
+
+  @param  Operation             Indicates if the bus master is going to read or write to system memory.
+  @param  HostAddress           The system memory address to map to the PCI controller.
+  @param  NumberOfBytes         On input the number of bytes to map. On output the number of bytes
+                                that were mapped.
+  @param  DeviceAddress         The resulting map address for the bus master PCI controller to use to
+                                access the hosts HostAddress.
+  @param  Mapping               A resulting value to pass to Unmap().
+
+  @retval EFI_SUCCESS           The range was mapped for the returned NumberOfBytes.
+  @retval EFI_UNSUPPORTED       The HostAddress cannot be mapped as a common buffer.
+  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+  @retval EFI_OUT_OF_RESOURCES  The request could not be completed due to a lack of resources.
+  @retval EFI_DEVICE_ERROR      The system hardware could not map the requested address.
+
+**/
+EFI_STATUS
+IoMmuMap (
+  IN  EDKII_IOMMU_OPERATION Operation,
+  IN VOID                   *HostAddress,
+  IN  OUT UINTN             *NumberOfBytes,
+  OUT EFI_PHYSICAL_ADDRESS  *DeviceAddress,
+  OUT VOID                  **Mapping
+  );
+
+/**
+  Completes the Map() operation and releases any corresponding resources.
+
+  @param  Mapping               The mapping value returned from Map().
+
+  @retval EFI_SUCCESS           The range was unmapped.
+  @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by Map().
+  @retval EFI_DEVICE_ERROR      The data was not committed to the target system memory.
+**/
+EFI_STATUS
+IoMmuUnmap (
+  IN VOID                  *Mapping
+  );
+
+/**
+  One notified function to cleanup the allocated DMA buffers at EndOfPei.
+
+  @param[in] PeiServices         Pointer to PEI Services Table.
+  @param[in] NotifyDescriptor    Pointer to the descriptor for the Notification
+                                 event that caused this function to execute.
+  @param[in] Ppi                 Pointer to the PPI data associated with this function.
+
+  @retval EFI_SUCCESS    The function completes successfully
+
+**/
+EFI_STATUS
+EFIAPI
+AhciPeimEndOfPei (
+  IN EFI_PEI_SERVICES           **PeiServices,
+  IN EFI_PEI_NOTIFY_DESCRIPTOR  *NotifyDescriptor,
+  IN VOID                       *Ppi
+  );
+
+/**
+  Collect the number of bits set within a port bitmap.
+
+  @param[in]    PortBitMap    A 32-bit wide bit map of ATA AHCI ports.
+
+  @retval The number of bits set in the bitmap.
+
+**/
+UINT8
+AhciGetNumberOfPortsFromMap (
+  IN UINT32    PortBitMap
+  );
+
+/**
+  Start a PIO Data transfer on specific port.
+
+  @param[in]     Private            The pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA.
+  @param[in]     Port               The number of port.
+  @param[in]     PortMultiplier     The number of port multiplier.
+  @param[in]     FisIndex           The offset index of the FIS base address.
+  @param[in]     Read               The transfer direction.
+  @param[in]     AtaCommandBlock    The EFI_ATA_COMMAND_BLOCK data.
+  @param[in,out] AtaStatusBlock     The EFI_ATA_STATUS_BLOCK data.
+  @param[in,out] MemoryAddr         The pointer to the data buffer.
+  @param[in]     DataCount          The data count to be transferred.
+  @param[in]     Timeout            The timeout value of PIO data transfer, uses
+                                    100ns as a unit.
+
+  @retval EFI_DEVICE_ERROR        The PIO data transfer abort with error occurs.
+  @retval EFI_TIMEOUT             The operation is time out.
+  @retval EFI_UNSUPPORTED         The device is not ready for transfer.
+  @retval EFI_OUT_OF_RESOURCES    The operation fails due to lack of resources.
+  @retval EFI_SUCCESS             The PIO data transfer executes successfully.
+
+**/
+EFI_STATUS
+AhciPioTransfer (
+  IN     PEI_AHCI_CONTROLLER_PRIVATE_DATA    *Private,
+  IN     UINT8                               Port,
+  IN     UINT8                               PortMultiplier,
+  IN     UINT8                               FisIndex,
+  IN     BOOLEAN                             Read,
+  IN     EFI_ATA_COMMAND_BLOCK               *AtaCommandBlock,
+  IN OUT EFI_ATA_STATUS_BLOCK                *AtaStatusBlock,
+  IN OUT VOID                                *MemoryAddr,
+  IN     UINT32                              DataCount,
+  IN     UINT64                              Timeout
+  );
+
+/**
+  Start a non data transfer on specific port.
+
+  @param[in]     Private            The pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA.
+  @param[in]     Port               The number of port.
+  @param[in]     PortMultiplier     The number of port multiplier.
+  @param[in]     FisIndex           The offset index of the FIS base address.
+  @param[in]     AtaCommandBlock    The EFI_ATA_COMMAND_BLOCK data.
+  @param[in,out] AtaStatusBlock     The EFI_ATA_STATUS_BLOCK data.
+  @param[in]     Timeout            The timeout value of non data transfer, uses
+                                    100ns as a unit.
+
+  @retval EFI_DEVICE_ERROR        The non data transfer abort with error occurs.
+  @retval EFI_TIMEOUT             The operation is time out.
+  @retval EFI_UNSUPPORTED         The device is not ready for transfer.
+  @retval EFI_SUCCESS             The non data transfer executes successfully.
+
+**/
+EFI_STATUS
+AhciNonDataTransfer (
+  IN     PEI_AHCI_CONTROLLER_PRIVATE_DATA    *Private,
+  IN     UINT8                               Port,
+  IN     UINT8                               PortMultiplier,
+  IN     UINT8                               FisIndex,
+  IN     EFI_ATA_COMMAND_BLOCK               *AtaCommandBlock,
+  IN OUT EFI_ATA_STATUS_BLOCK                *AtaStatusBlock,
+  IN     UINT64                              Timeout
+  );
+
+/**
+  Initialize ATA host controller at AHCI mode.
+
+  The function is designed to initialize ATA host controller.
+
+  @param[in,out] Private    A pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA instance.
+
+  @retval EFI_SUCCESS             The ATA AHCI controller is initialized successfully.
+  @retval EFI_OUT_OF_RESOURCES    Not enough resource to complete while initializing
+                                  the controller.
+  @retval Others                  A device error occurred while initializing the
+                                  controller.
+
+**/
+EFI_STATUS
+AhciModeInitialization (
+  IN OUT PEI_AHCI_CONTROLLER_PRIVATE_DATA    *Private
+  );
+
+/**
+  Trust transfer data from/to ATA device.
+
+  This function performs one ATA pass through transaction to do a trust transfer
+  from/to ATA device. It chooses the appropriate ATA command and protocol to invoke
+  PassThru interface of ATA pass through.
+
+  @param[in]     DeviceData     Pointer to PEI_AHCI_ATA_DEVICE_DATA structure.
+  @param[in,out] Buffer         The pointer to the current transaction buffer.
+  @param[in]     SecurityProtocolId
+                                The value of the "Security Protocol" parameter
+                                of the security protocol command to be sent.
+  @param[in]     SecurityProtocolSpecificData
+                                The value of the "Security Protocol Specific"
+                                parameter of the security protocol command to
+                                be sent.
+  @param[in]     TransferLength The block number or sector count of the transfer.
+  @param[in]     IsTrustSend    Indicates whether it is a trust send operation
+                                or not.
+  @param[in]     Timeout        The timeout, in 100ns units, to use for the execution
+                                of the security protocol command. A Timeout value
+                                of 0 means that this function will wait indefinitely
+                                for the security protocol command to execute. If
+                                Timeout is greater than zero, then this function
+                                will return EFI_TIMEOUT if the time required to
+                                execute the receive data command is greater than
+                                Timeout.
+  @param[out]    TransferLengthOut
+                                A pointer to a buffer to store the size in bytes
+                                of the data written to the buffer. Ignore it when
+                                IsTrustSend is TRUE.
+
+  @retval EFI_SUCCESS    The data transfer is complete successfully.
+  @return others         Some error occurs when transferring data.
+
+**/
+EFI_STATUS
+TrustTransferAtaDevice (
+  IN     PEI_AHCI_ATA_DEVICE_DATA    *DeviceData,
+  IN OUT VOID                        *Buffer,
+  IN     UINT8                       SecurityProtocolId,
+  IN     UINT16                      SecurityProtocolSpecificData,
+  IN     UINTN                       TransferLength,
+  IN     BOOLEAN                     IsTrustSend,
+  IN     UINT64                      Timeout,
+  OUT    UINTN                       *TransferLengthOut
+  );
+
+/**
+  Returns a pointer to the next node in a device path.
+
+  If Node is NULL, then ASSERT().
+
+  @param  Node    A pointer to a device path node data structure.
+
+  @return a pointer to the device path node that follows the device path node
+  specified by Node.
+
+**/
+EFI_DEVICE_PATH_PROTOCOL *
+NextDevicePathNode (
+  IN CONST VOID  *Node
+  );
+
+/**
+  Get the size of the current device path instance.
+
+  @param[in]  DevicePath             A pointer to the EFI_DEVICE_PATH_PROTOCOL
+                                     structure.
+  @param[out] InstanceSize           The size of the current device path instance.
+  @param[out] EntireDevicePathEnd    Indicate whether the instance is the last
+                                     one in the device path strucure.
+
+  @retval EFI_SUCCESS    The size of the current device path instance is fetched.
+  @retval Others         Fails to get the size of the current device path instance.
+
+**/
+EFI_STATUS
+GetDevicePathInstanceSize (
+  IN  EFI_DEVICE_PATH_PROTOCOL    *DevicePath,
+  OUT UINTN                       *InstanceSize,
+  OUT BOOLEAN                     *EntireDevicePathEnd
+  );
+
+/**
+  Check the validity of the device path of a ATA AHCI host controller.
+
+  @param[in] DevicePath          A pointer to the EFI_DEVICE_PATH_PROTOCOL
+                                 structure.
+  @param[in] DevicePathLength    The length of the device path.
+
+  @retval EFI_SUCCESS              The device path is valid.
+  @retval EFI_INVALID_PARAMETER    The device path is invalid.
+
+**/
+EFI_STATUS
+AhciCheckHcDevicePath (
+  IN EFI_DEVICE_PATH_PROTOCOL    *DevicePath,
+  IN UINTN                       DevicePathLength
+  );
+
+/**
+  Build the device path for an ATA device with given port and port multiplier number.
+
+  @param[in]  Private               A pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA
+                                    data structure.
+  @param[in]  Port                  The given port number.
+  @param[in]  PortMultiplierPort    The given port multiplier number.
+  @param[out] DevicePathLength      The length of the device path in bytes specified
+                                    by DevicePath.
+  @param[out] DevicePath            The device path of ATA device.
+
+  @retval EFI_SUCCESS               The operation succeeds.
+  @retval EFI_INVALID_PARAMETER     The parameters are invalid.
+  @retval EFI_OUT_OF_RESOURCES      The operation fails due to lack of resources.
+
+**/
+EFI_STATUS
+AhciBuildDevicePath (
+  IN  PEI_AHCI_CONTROLLER_PRIVATE_DATA    *Private,
+  IN  UINT16                              Port,
+  IN  UINT16                              PortMultiplierPort,
+  OUT UINTN                               *DevicePathLength,
+  OUT EFI_DEVICE_PATH_PROTOCOL            **DevicePath
+  );
+
+/**
+  Collect the ports that need to be enumerated on a controller for S3 phase.
+
+  @param[in]  HcDevicePath          Device path of the controller.
+  @param[in]  HcDevicePathLength    Length of the device path specified by
+                                    HcDevicePath.
+  @param[out] PortBitMap            Bitmap that indicates the ports that need
+                                    to be enumerated on the controller.
+
+  @retval    The number of ports that need to be enumerated.
+
+**/
+UINT8
+AhciS3GetEumeratePorts (
+  IN  EFI_DEVICE_PATH_PROTOCOL    *HcDevicePath,
+  IN  UINTN                       HcDevicePathLength,
+  OUT UINT32                      *PortBitMap
+  );
+
+#endif
diff --git a/MdeModulePkg/Bus/Ata/AhciPei/AhciPeiPassThru.h b/MdeModulePkg/Bus/Ata/AhciPei/AhciPeiPassThru.h
new file mode 100644
index 0000000000..925ee27f93
--- /dev/null
+++ b/MdeModulePkg/Bus/Ata/AhciPei/AhciPeiPassThru.h
@@ -0,0 +1,184 @@
+/** @file
+  The AhciPei driver is used to manage ATA hard disk device working under AHCI
+  mode at PEI phase.
+
+  Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
+
+  This program and the accompanying materials
+  are licensed and made available under the terms and conditions
+  of the BSD License which accompanies this distribution.  The
+  full text of the license may be found at
+  http://opensource.org/licenses/bsd-license.php
+
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _AHCI_PEI_PASSTHRU_H_
+#define _AHCI_PEI_PASSTHRU_H_
+
+/**
+  Sends an ATA command to an ATA device that is attached to the ATA controller.
+
+  @param[in]     This                  The PPI instance pointer.
+  @param[in]     Port                  The port number of the ATA device to send
+                                       the command.
+  @param[in]     PortMultiplierPort    The port multiplier port number of the ATA
+                                       device to send the command.
+                                       If there is no port multiplier, then specify
+                                       0xFFFF.
+  @param[in,out] Packet                A pointer to the ATA command to send to
+                                       the ATA device specified by Port and
+                                       PortMultiplierPort.
+
+  @retval EFI_SUCCESS              The ATA command was sent by the host. For
+                                   bi-directional commands, InTransferLength bytes
+                                   were transferred from InDataBuffer. For write
+                                   and bi-directional commands, OutTransferLength
+                                   bytes were transferred by OutDataBuffer.
+  @retval EFI_NOT_FOUND            The specified ATA device is not found.
+  @retval EFI_INVALID_PARAMETER    The contents of Acb are invalid. The ATA command
+                                   was not sent, so no additional status information
+                                   is available.
+  @retval EFI_BAD_BUFFER_SIZE      The ATA command was not executed. The number
+                                   of bytes that could be transferred is returned
+                                   in InTransferLength. For write and bi-directional
+                                   commands, OutTransferLength bytes were transferred
+                                   by OutDataBuffer.
+  @retval EFI_NOT_READY            The ATA command could not be sent because there
+                                   are too many ATA commands already queued. The
+                                   caller may retry again later.
+  @retval EFI_DEVICE_ERROR         A device error occurred while attempting to
+                                   send the ATA command.
+
+**/
+EFI_STATUS
+EFIAPI
+AhciAtaPassThruPassThru (
+  IN     EDKII_PEI_ATA_PASS_THRU_PPI         *This,
+  IN     UINT16                              Port,
+  IN     UINT16                              PortMultiplierPort,
+  IN OUT EFI_ATA_PASS_THRU_COMMAND_PACKET    *Packet
+  );
+
+/**
+  Used to retrieve the list of legal port numbers for ATA devices on an ATA controller.
+  These can either be the list of ports where ATA devices are actually present or the
+  list of legal port numbers for the ATA controller. Regardless, the caller of this
+  function must probe the port number returned to see if an ATA device is actually
+  present at that location on the ATA controller.
+
+  The GetNextPort() function retrieves the port number on an ATA controller. If on
+  input Port is 0xFFFF, then the port number of the first port on the ATA controller
+  is returned in Port and EFI_SUCCESS is returned.
+
+  If Port is a port number that was returned on a previous call to GetNextPort(),
+  then the port number of the next port on the ATA controller is returned in Port,
+  and EFI_SUCCESS is returned. If Port is not 0xFFFF and Port was not returned on
+  a previous call to GetNextPort(), then EFI_INVALID_PARAMETER is returned.
+
+  If Port is the port number of the last port on the ATA controller, then EFI_NOT_FOUND
+  is returned.
+
+  @param[in]     This    The PPI instance pointer.
+  @param[in,out] Port    On input, a pointer to the port number on the ATA controller.
+                         On output, a pointer to the next port number on the ATA
+                         controller. An input value of 0xFFFF retrieves the first
+                         port number on the ATA controller.
+
+  @retval EFI_SUCCESS              The next port number on the ATA controller was
+                                   returned in Port.
+  @retval EFI_NOT_FOUND            There are no more ports on this ATA controller.
+  @retval EFI_INVALID_PARAMETER    Port is not 0xFFFF and Port was not returned
+                                   on a previous call to GetNextPort().
+
+**/
+EFI_STATUS
+EFIAPI
+AhciAtaPassThruGetNextPort (
+  IN     EDKII_PEI_ATA_PASS_THRU_PPI    *This,
+  IN OUT UINT16                         *Port
+  );
+
+/**
+  Used to retrieve the list of legal port multiplier port numbers for ATA devices
+  on a port of an ATA controller. These can either be the list of port multiplier
+  ports where ATA devices are actually present on port or the list of legal port
+  multiplier ports on that port. Regardless, the caller of this function must probe
+  the port number and port multiplier port number returned to see if an ATA device
+  is actually present.
+
+  The GetNextDevice() function retrieves the port multiplier port number of an ATA
+  device present on a port of an ATA controller.
+
+  If PortMultiplierPort points to a port multiplier port number value that was
+  returned on a previous call to GetNextDevice(), then the port multiplier port
+  number of the next ATA device on the port of the ATA controller is returned in
+  PortMultiplierPort, and EFI_SUCCESS is returned.
+
+  If PortMultiplierPort points to 0xFFFF, then the port multiplier port number
+  of the first ATA device on port of the ATA controller is returned in PortMultiplierPort
+  and EFI_SUCCESS is returned.
+
+  If PortMultiplierPort is not 0xFFFF and the value pointed to by PortMultiplierPort
+  was not returned on a previous call to GetNextDevice(), then EFI_INVALID_PARAMETER
+  is returned.
+
+  If PortMultiplierPort is the port multiplier port number of the last ATA device
+  on the port of the ATA controller, then EFI_NOT_FOUND is returned.
+
+  @param[in]     This                  The PPI instance pointer.
+  @param[in]     Port                  The port number present on the ATA controller.
+  @param[in,out] PortMultiplierPort    On input, a pointer to the port multiplier
+                                       port number of an ATA device present on the
+                                       ATA controller. If on input a PortMultiplierPort
+                                       of 0xFFFF is specified, then the port multiplier
+                                       port number of the first ATA device is returned.
+                                       On output, a pointer to the port multiplier port
+                                       number of the next ATA device present on an ATA
+                                       controller.
+
+  @retval EFI_SUCCESS              The port multiplier port number of the next ATA
+                                   device on the port of the ATA controller was
+                                   returned in PortMultiplierPort.
+  @retval EFI_NOT_FOUND            There are no more ATA devices on this port of
+                                   the ATA controller.
+  @retval EFI_INVALID_PARAMETER    PortMultiplierPort is not 0xFFFF, and PortMultiplierPort
+                                   was not returned on a previous call to GetNextDevice().
+
+**/
+EFI_STATUS
+EFIAPI
+AhciAtaPassThruGetNextDevice (
+  IN     EDKII_PEI_ATA_PASS_THRU_PPI    *This,
+  IN     UINT16                         Port,
+  IN OUT UINT16                         *PortMultiplierPort
+  );
+
+/**
+  Gets the device path information of the underlying ATA host controller.
+
+  @param[in]  This                The PPI instance pointer.
+  @param[out] DevicePathLength    The length of the device path in bytes specified
+                                  by DevicePath.
+  @param[out] DevicePath          The device path of the underlying ATA host controller.
+                                  This field re-uses EFI Device Path Protocol as
+                                  defined by Section 10.2 EFI Device Path Protocol
+                                  of UEFI 2.7 Specification.
+
+  @retval EFI_SUCCESS              The device path of the ATA host controller has
+                                   been successfully returned.
+  @retval EFI_INVALID_PARAMETER    DevicePathLength or DevicePath is NULL.
+  @retval EFI_OUT_OF_RESOURCES     Not enough resource to return the device path.
+
+**/
+EFI_STATUS
+EFIAPI
+AhciAtaPassThruGetDevicePath (
+  IN  EDKII_PEI_ATA_PASS_THRU_PPI    *This,
+  OUT UINTN                          *DevicePathLength,
+  OUT EFI_DEVICE_PATH_PROTOCOL       **DevicePath
+  );
+
+#endif
diff --git a/MdeModulePkg/Bus/Ata/AhciPei/AhciPeiStorageSecurity.h b/MdeModulePkg/Bus/Ata/AhciPei/AhciPeiStorageSecurity.h
new file mode 100644
index 0000000000..0bdb964ec7
--- /dev/null
+++ b/MdeModulePkg/Bus/Ata/AhciPei/AhciPeiStorageSecurity.h
@@ -0,0 +1,247 @@
+/** @file
+  The AhciPei driver is used to manage ATA hard disk device working under AHCI
+  mode at PEI phase.
+
+  Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
+
+  This program and the accompanying materials
+  are licensed and made available under the terms and conditions
+  of the BSD License which accompanies this distribution.  The
+  full text of the license may be found at
+  http://opensource.org/licenses/bsd-license.php
+
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _AHCI_PEI_STORAGE_SECURITY_H_
+#define _AHCI_PEI_STORAGE_SECURITY_H_
+
+/**
+  Gets the count of storage security devices that one specific driver detects.
+
+  @param[in]  This               The PPI instance pointer.
+  @param[out] NumberofDevices    The number of storage security devices discovered.
+
+  @retval EFI_SUCCESS              The operation performed successfully.
+  @retval EFI_INVALID_PARAMETER    The parameters are invalid.
+
+**/
+EFI_STATUS
+EFIAPI
+AhciStorageSecurityGetDeviceNo (
+  IN  EDKII_PEI_STORAGE_SECURITY_CMD_PPI    *This,
+  OUT UINTN                                 *NumberofDevices
+  );
+
+/**
+  Gets the device path of a specific storage security device.
+
+  @param[in]  This                 The PPI instance pointer.
+  @param[in]  DeviceIndex          Specifies the storage security device to which
+                                   the function wants to talk. Because the driver
+                                   that implements Storage Security Command PPIs
+                                   will manage multiple storage devices, the PPIs
+                                   that want to talk to a single device must specify
+                                   the device index that was assigned during the
+                                   enumeration process. This index is a number from
+                                   one to NumberofDevices.
+  @param[out] DevicePathLength     The length of the device path in bytes specified
+                                   by DevicePath.
+  @param[out] DevicePath           The device path of storage security device.
+                                   This field re-uses EFI Device Path Protocol as
+                                   defined by Section 10.2 EFI Device Path Protocol
+                                   of UEFI 2.7 Specification.
+
+  @retval EFI_SUCCESS              The operation succeeds.
+  @retval EFI_INVALID_PARAMETER    DevicePathLength or DevicePath is NULL.
+  @retval EFI_NOT_FOUND            The specified storage security device not found.
+  @retval EFI_OUT_OF_RESOURCES     The operation fails due to lack of resources.
+
+**/
+EFI_STATUS
+EFIAPI
+AhciStorageSecurityGetDevicePath (
+  IN  EDKII_PEI_STORAGE_SECURITY_CMD_PPI    *This,
+  IN  UINTN                                 DeviceIndex,
+  OUT UINTN                                 *DevicePathLength,
+  OUT EFI_DEVICE_PATH_PROTOCOL              **DevicePath
+  );
+
+/**
+  Send a security protocol command to a device that receives data and/or the result
+  of one or more commands sent by SendData.
+
+  The ReceiveData function sends a security protocol command to the given DeviceIndex.
+  The security protocol command sent is defined by SecurityProtocolId and contains
+  the security protocol specific data SecurityProtocolSpecificData. The function
+  returns the data from the security protocol command in PayloadBuffer.
+
+  For devices supporting the SCSI command set, the security protocol command is sent
+  using the SECURITY PROTOCOL IN command defined in SPC-4.
+
+  For devices supporting the ATA command set, the security protocol command is sent
+  using one of the TRUSTED RECEIVE commands defined in ATA8-ACS if PayloadBufferSize
+  is non-zero.
+
+  If the PayloadBufferSize is zero, the security protocol command is sent using the
+  Trusted Non-Data command defined in ATA8-ACS.
+
+  If PayloadBufferSize is too small to store the available data from the security
+  protocol command, the function shall copy PayloadBufferSize bytes into the
+  PayloadBuffer and return EFI_WARN_BUFFER_TOO_SMALL.
+
+  If PayloadBuffer or PayloadTransferSize is NULL and PayloadBufferSize is non-zero,
+  the function shall return EFI_INVALID_PARAMETER.
+
+  If the given DeviceIndex does not support security protocol commands, the function
+  shall return EFI_UNSUPPORTED.
+
+  If the security protocol fails to complete within the Timeout period, the function
+  shall return EFI_TIMEOUT.
+
+  If the security protocol command completes without an error, the function shall
+  return EFI_SUCCESS. If the security protocol command completes with an error, the
+  function shall return EFI_DEVICE_ERROR.
+
+  @param[in]  This             The PPI instance pointer.
+  @param[in]  DeviceIndex      Specifies the storage security device to which the
+                               function wants to talk. Because the driver that
+                               implements Storage Security Command PPIs will manage
+                               multiple storage devices, the PPIs that want to talk
+                               to a single device must specify the device index
+                               that was assigned during the enumeration process.
+                               This index is a number from one to NumberofDevices.
+  @param[in]  Timeout          The timeout, in 100ns units, to use for the execution
+                               of the security protocol command. A Timeout value
+                               of 0 means that this function will wait indefinitely
+                               for the security protocol command to execute. If
+                               Timeout is greater than zero, then this function
+                               will return EFI_TIMEOUT if the time required to
+                               execute the receive data command is greater than
+                               Timeout.
+  @param[in]  SecurityProtocolId
+                               The value of the "Security Protocol" parameter of
+                               the security protocol command to be sent.
+  @param[in]  SecurityProtocolSpecificData
+                               The value of the "Security Protocol Specific"
+                               parameter of the security protocol command to be
+                               sent.
+  @param[in]  PayloadBufferSize
+                               Size in bytes of the payload data buffer.
+  @param[out] PayloadBuffer    A pointer to a destination buffer to store the
+                               security protocol command specific payload data
+                               for the security protocol command. The caller is
+                               responsible for having either implicit or explicit
+                               ownership of the buffer.
+  @param[out] PayloadTransferSize
+                               A pointer to a buffer to store the size in bytes
+                               of the data written to the payload data buffer.
+
+  @retval EFI_SUCCESS                  The security protocol command completed
+                                       successfully.
+  @retval EFI_WARN_BUFFER_TOO_SMALL    The PayloadBufferSize was too small to
+                                       store the available data from the device.
+                                       The PayloadBuffer contains the truncated
+                                       data.
+  @retval EFI_UNSUPPORTED              The given DeviceIndex does not support
+                                       security protocol commands.
+  @retval EFI_DEVICE_ERROR             The security protocol command completed
+                                       with an error.
+  @retval EFI_INVALID_PARAMETER        The PayloadBuffer or PayloadTransferSize
+                                       is NULL and PayloadBufferSize is non-zero.
+  @retval EFI_TIMEOUT                  A timeout occurred while waiting for the
+                                       security protocol command to execute.
+
+**/
+EFI_STATUS
+EFIAPI
+AhciStorageSecurityReceiveData (
+  IN  EDKII_PEI_STORAGE_SECURITY_CMD_PPI    *This,
+  IN  UINTN                                 DeviceIndex,
+  IN  UINT64                                Timeout,
+  IN  UINT8                                 SecurityProtocolId,
+  IN  UINT16                                SecurityProtocolSpecificData,
+  IN  UINTN                                 PayloadBufferSize,
+  OUT VOID                                  *PayloadBuffer,
+  OUT UINTN                                 *PayloadTransferSize
+  );
+
+/**
+  Send a security protocol command to a device.
+
+  The SendData function sends a security protocol command containing the payload
+  PayloadBuffer to the given DeviceIndex. The security protocol command sent is
+  defined by SecurityProtocolId and contains the security protocol specific data
+  SecurityProtocolSpecificData. If the underlying protocol command requires a
+  specific padding for the command payload, the SendData function shall add padding
+  bytes to the command payload to satisfy the padding requirements.
+
+  For devices supporting the SCSI command set, the security protocol command is
+  sent using the SECURITY PROTOCOL OUT command defined in SPC-4.
+
+  For devices supporting the ATA command set, the security protocol command is
+  sent using one of the TRUSTED SEND commands defined in ATA8-ACS if PayloadBufferSize
+  is non-zero. If the PayloadBufferSize is zero, the security protocol command
+  is sent using the Trusted Non-Data command defined in ATA8-ACS.
+
+  If PayloadBuffer is NULL and PayloadBufferSize is non-zero, the function shall
+  return EFI_INVALID_PARAMETER.
+
+  If the given DeviceIndex does not support security protocol commands, the function
+  shall return EFI_UNSUPPORTED.
+
+  If the security protocol fails to complete within the Timeout period, the function
+  shall return EFI_TIMEOUT.
+
+  If the security protocol command completes without an error, the function shall
+  return EFI_SUCCESS. If the security protocol command completes with an error,
+  the functio shall return EFI_DEVICE_ERROR.
+
+  @param[in] This              The PPI instance pointer.
+  @param[in] DeviceIndex       The ID of the device.
+  @param[in] Timeout           The timeout, in 100ns units, to use for the execution
+                               of the security protocol command. A Timeout value
+                               of 0 means that this function will wait indefinitely
+                               for the security protocol command to execute. If
+                               Timeout is greater than zero, then this function
+                               will return EFI_TIMEOUT if the time required to
+                               execute the receive data command is greater than
+                               Timeout.
+  @param[in] SecurityProtocolId
+                               The value of the "Security Protocol" parameter of
+                               the security protocol command to be sent.
+  @param[in] SecurityProtocolSpecificData
+                               The value of the "Security Protocol Specific"
+                               parameter of the security protocol command to be
+                               sent.
+  @param[in] PayloadBufferSize Size in bytes of the payload data buffer.
+  @param[in] PayloadBuffer     A pointer to a destination buffer to store the
+                               security protocol command specific payload data
+                               for the security protocol command.
+
+  @retval EFI_SUCCESS              The security protocol command completed successfully.
+  @retval EFI_UNSUPPORTED          The given DeviceIndex does not support security
+                                   protocol commands.
+  @retval EFI_DEVICE_ERROR         The security protocol command completed with
+                                   an error.
+  @retval EFI_INVALID_PARAMETER    The PayloadBuffer is NULL and PayloadBufferSize
+                                   is non-zero.
+  @retval EFI_TIMEOUT              A timeout occurred while waiting for the security
+                                   protocol command to execute.
+
+**/
+EFI_STATUS
+EFIAPI
+AhciStorageSecuritySendData (
+  IN EDKII_PEI_STORAGE_SECURITY_CMD_PPI  *This,
+  IN UINTN                               DeviceIndex,
+  IN UINT64                              Timeout,
+  IN UINT8                               SecurityProtocolId,
+  IN UINT16                              SecurityProtocolSpecificData,
+  IN UINTN                               PayloadBufferSize,
+  IN VOID                                *PayloadBuffer
+  );
+
+#endif
diff --git a/MdeModulePkg/Bus/Ata/AhciPei/AhciMode.c b/MdeModulePkg/Bus/Ata/AhciPei/AhciMode.c
new file mode 100644
index 0000000000..54df31906f
--- /dev/null
+++ b/MdeModulePkg/Bus/Ata/AhciPei/AhciMode.c
@@ -0,0 +1,2015 @@
+/** @file
+  The AhciPei driver is used to manage ATA hard disk device working under AHCI
+  mode at PEI phase.
+
+  Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
+
+  This program and the accompanying materials
+  are licensed and made available under the terms and conditions
+  of the BSD License which accompanies this distribution.  The
+  full text of the license may be found at
+  http://opensource.org/licenses/bsd-license.php
+
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "AhciPei.h"
+
+#define ATA_CMD_TRUST_NON_DATA           0x5B
+#define ATA_CMD_TRUST_RECEIVE            0x5C
+#define ATA_CMD_TRUST_SEND               0x5E
+
+//
+// Look up table (IsWrite) for EFI_ATA_PASS_THRU_CMD_PROTOCOL
+//
+EFI_ATA_PASS_THRU_CMD_PROTOCOL  mAtaPassThruCmdProtocols[2] = {
+  EFI_ATA_PASS_THRU_PROTOCOL_PIO_DATA_IN,
+  EFI_ATA_PASS_THRU_PROTOCOL_PIO_DATA_OUT
+};
+
+//
+// Look up table (Lba48Bit, IsIsWrite) for ATA_CMD
+//
+UINT8 mAtaCommands[2][2] = {
+  {
+    ATA_CMD_READ_SECTORS,            // 28-bit LBA; PIO read
+    ATA_CMD_WRITE_SECTORS            // 28-bit LBA; PIO write
+  },
+  {
+    ATA_CMD_READ_SECTORS_EXT,        // 48-bit LBA; PIO read
+    ATA_CMD_WRITE_SECTORS_EXT        // 48-bit LBA; PIO write
+  }
+};
+
+//
+// Look up table (IsTrustSend) for ATA_CMD
+//
+UINT8  mAtaTrustCommands[2] = {
+  ATA_CMD_TRUST_RECEIVE,    // PIO read
+  ATA_CMD_TRUST_SEND        // PIO write
+};
+
+//
+// Look up table (Lba48Bit) for maximum transfer block number
+//
+#define MAX_28BIT_TRANSFER_BLOCK_NUM     0x100
+#define MAX_48BIT_TRANSFER_BLOCK_NUM     0xFFFF
+
+UINT32 mMaxTransferBlockNumber[2] = {
+  MAX_28BIT_TRANSFER_BLOCK_NUM,
+  MAX_48BIT_TRANSFER_BLOCK_NUM
+};
+
+//
+// The maximum total sectors count in 28 bit addressing mode
+//
+#define MAX_28BIT_ADDRESSING_CAPACITY    0xfffffff
+
+
+/**
+  Read AHCI Operation register.
+
+  @param[in] AhciBar    AHCI bar address.
+  @param[in] Offset     The operation register offset.
+
+  @return The register content read.
+
+**/
+UINT32
+AhciReadReg (
+  IN UINTN     AhciBar,
+  IN UINT32    Offset
+  )
+{
+  UINT32   Data;
+
+  Data = 0;
+  Data = MmioRead32 (AhciBar + Offset);
+
+  return Data;
+}
+
+/**
+  Write AHCI Operation register.
+
+  @param[in] AhciBar    AHCI bar address.
+  @param[in] Offset     The operation register offset.
+  @param[in] Data       The Data used to write down.
+
+**/
+VOID
+AhciWriteReg (
+  IN UINTN     AhciBar,
+  IN UINT32    Offset,
+  IN UINT32    Data
+  )
+{
+  MmioWrite32 (AhciBar + Offset, Data);
+}
+
+/**
+  Do AND operation with the value of AHCI Operation register.
+
+  @param[in] AhciBar    AHCI bar address.
+  @param[in] Offset     The operation register offset.
+  @param[in] AndData    The data used to do AND operation.
+
+**/
+VOID
+AhciAndReg (
+  IN UINTN     AhciBar,
+  IN UINT32    Offset,
+  IN UINT32    AndData
+  )
+{
+  UINT32 Data;
+
+  Data  = AhciReadReg (AhciBar, Offset);
+  Data &= AndData;
+
+  AhciWriteReg (AhciBar, Offset, Data);
+}
+
+/**
+  Do OR operation with the Value of AHCI Operation register.
+
+  @param[in] AhciBar    AHCI bar address.
+  @param[in] Offset     The operation register offset.
+  @param[in] OrData     The Data used to do OR operation.
+
+**/
+VOID
+AhciOrReg (
+  IN UINTN     AhciBar,
+  IN UINT32    Offset,
+  IN UINT32    OrData
+  )
+{
+  UINT32 Data;
+
+  Data  = AhciReadReg (AhciBar, Offset);
+  Data |= OrData;
+
+  AhciWriteReg (AhciBar, Offset, Data);
+}
+
+/**
+  Wait for memory set to the test Value.
+
+  @param[in] AhciBar      AHCI bar address.
+  @param[in] Offset       The memory offset to test.
+  @param[in] MaskValue    The mask Value of memory.
+  @param[in] TestValue    The test Value of memory.
+  @param[in] Timeout      The timeout, in 100ns units, for wait memory set.
+
+  @retval EFI_DEVICE_ERROR    The memory is not set.
+  @retval EFI_TIMEOUT         The memory setting is time out.
+  @retval EFI_SUCCESS         The memory is correct set.
+
+**/
+EFI_STATUS
+EFIAPI
+AhciWaitMmioSet (
+  IN UINTN     AhciBar,
+  IN UINT32    Offset,
+  IN UINT32    MaskValue,
+  IN UINT32    TestValue,
+  IN UINT64    Timeout
+  )
+{
+  UINT32    Value;
+  UINT32    Delay;
+
+  Delay = (UINT32) (DivU64x32(Timeout, 1000) + 1);
+
+  do {
+    Value = AhciReadReg (AhciBar, Offset) & MaskValue;
+
+    if (Value == TestValue) {
+      return EFI_SUCCESS;
+    }
+
+    //
+    // Stall for 100 microseconds.
+    //
+    MicroSecondDelay (100);
+
+    Delay--;
+
+  } while (Delay > 0);
+
+  return EFI_TIMEOUT;
+}
+
+/**
+  Check the memory status to the test value.
+
+  @param[in] Address       The memory address to test.
+  @param[in] MaskValue     The mask value of memory.
+  @param[in] TestValue     The test value of memory.
+
+  @retval EFI_NOT_READY    The memory is not set.
+  @retval EFI_SUCCESS      The memory is correct set.
+
+**/
+EFI_STATUS
+AhciCheckMemSet (
+  IN UINTN     Address,
+  IN UINT32    MaskValue,
+  IN UINT32    TestValue
+  )
+{
+  UINT32     Value;
+
+  Value  = *(volatile UINT32 *) Address;
+  Value &= MaskValue;
+
+  if (Value == TestValue) {
+    return EFI_SUCCESS;
+  } else {
+    return EFI_NOT_READY;
+  }
+}
+
+/**
+  Wait for the value of the specified system memory set to the test value.
+
+  @param[in] Address      The system memory address to test.
+  @param[in] MaskValue    The mask value of memory.
+  @param[in] TestValue    The test value of memory.
+  @param[in] Timeout      The timeout, in 100ns units, for wait memory set.
+
+  @retval EFI_TIMEOUT    The system memory setting is time out.
+  @retval EFI_SUCCESS    The system memory is correct set.
+
+**/
+EFI_STATUS
+AhciWaitMemSet (
+  IN  EFI_PHYSICAL_ADDRESS    Address,
+  IN  UINT32                  MaskValue,
+  IN  UINT32                  TestValue,
+  IN  UINT64                  Timeout
+  )
+{
+  UINT32     Value;
+  UINT64     Delay;
+  BOOLEAN    InfiniteWait;
+
+  if (Timeout == 0) {
+    InfiniteWait = TRUE;
+  } else {
+    InfiniteWait = FALSE;
+  }
+
+  Delay =  DivU64x32 (Timeout, 1000) + 1;
+
+  do {
+    //
+    // Access sytem memory to see if the value is the tested one.
+    //
+    // The system memory pointed by Address will be updated by the
+    // SATA Host Controller, "volatile" is introduced to prevent
+    // compiler from optimizing the access to the memory address
+    // to only read once.
+    //
+    Value  = *(volatile UINT32 *) (UINTN) Address;
+    Value &= MaskValue;
+
+    if (Value == TestValue) {
+      return EFI_SUCCESS;
+    }
+
+    //
+    // Stall for 100 microseconds.
+    //
+    MicroSecondDelay (100);
+
+    Delay--;
+
+  } while (InfiniteWait || (Delay > 0));
+
+  return EFI_TIMEOUT;
+}
+
+/**
+
+  Clear the port interrupt and error status. It will also clear HBA interrupt
+  status.
+
+  @param[in] AhciBar    AHCI bar address.
+  @param[in] Port       The number of port.
+
+**/
+VOID
+AhciClearPortStatus (
+  IN UINTN    AhciBar,
+  IN UINT8    Port
+  )
+{
+  UINT32    Offset;
+
+  //
+  // Clear any error status
+  //
+  Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_SERR;
+  AhciWriteReg (AhciBar, Offset, AhciReadReg (AhciBar, Offset));
+
+  //
+  // Clear any port interrupt status
+  //
+  Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_IS;
+  AhciWriteReg (AhciBar, Offset, AhciReadReg (AhciBar, Offset));
+
+  //
+  // Clear any HBA interrupt status
+  //
+  AhciWriteReg (AhciBar, AHCI_IS_OFFSET, AhciReadReg (AhciBar, AHCI_IS_OFFSET));
+}
+
+/**
+  Enable the FIS running for giving port.
+
+  @param[in] AhciBar    AHCI bar address.
+  @param[in] Port       The number of port.
+  @param[in] Timeout    The timeout, in 100ns units, to enabling FIS.
+
+  @retval EFI_DEVICE_ERROR    The FIS enable setting fails.
+  @retval EFI_TIMEOUT         The FIS enable setting is time out.
+  @retval EFI_SUCCESS         The FIS enable successfully.
+
+**/
+EFI_STATUS
+AhciEnableFisReceive (
+  IN UINTN     AhciBar,
+  IN UINT8     Port,
+  IN UINT64    Timeout
+  )
+{
+  UINT32 Offset;
+
+  Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CMD;
+  AhciOrReg (AhciBar, Offset, AHCI_PORT_CMD_FRE);
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Disable the FIS running for giving port.
+
+  @param[in] AhciBar    AHCI bar address.
+  @param[in] Port       The number of port.
+  @param[in] Timeout    The timeout value of disabling FIS, uses 100ns as a unit.
+
+  @retval EFI_DEVICE_ERROR    The FIS disable setting fails.
+  @retval EFI_TIMEOUT         The FIS disable setting is time out.
+  @retval EFI_UNSUPPORTED     The port is in running state.
+  @retval EFI_SUCCESS         The FIS disable successfully.
+
+**/
+EFI_STATUS
+AhciDisableFisReceive (
+  IN UINTN     AhciBar,
+  IN UINT8     Port,
+  IN UINT64    Timeout
+  )
+{
+  UINT32    Offset;
+  UINT32    Data;
+
+  Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CMD;
+  Data   = AhciReadReg (AhciBar, Offset);
+
+  //
+  // Before disabling Fis receive, the DMA engine of the port should NOT be in
+  // running status.
+  //
+  if ((Data & (AHCI_PORT_CMD_ST | AHCI_PORT_CMD_CR)) != 0) {
+    return EFI_UNSUPPORTED;
+  }
+
+  //
+  // Check if the Fis receive DMA engine for the port is running.
+  //
+  if ((Data & AHCI_PORT_CMD_FR) != AHCI_PORT_CMD_FR) {
+    return EFI_SUCCESS;
+  }
+
+  AhciAndReg (AhciBar, Offset, (UINT32)~(AHCI_PORT_CMD_FRE));
+
+  return AhciWaitMmioSet (
+           AhciBar,
+           Offset,
+           AHCI_PORT_CMD_FR,
+           0,
+           Timeout
+           );
+}
+
+/**
+  Build the command list, command table and prepare the fis receiver.
+
+  @param[in]     Private              The pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA.
+  @param[in]     Port                 The number of port.
+  @param[in]     PortMultiplier       The number of port multiplier.
+  @param[in]     FisIndex             The offset index of the FIS base address.
+  @param[in]     CommandFis           The control fis will be used for the transfer.
+  @param[in]     CommandList          The command list will be used for the transfer.
+  @param[in]     CommandSlotNumber    The command slot will be used for the transfer.
+  @param[in,out] DataPhysicalAddr     The pointer to the data buffer pci bus master
+                                      address.
+  @param[in]     DataLength           The data count to be transferred.
+
+**/
+VOID
+AhciBuildCommand (
+  IN     PEI_AHCI_CONTROLLER_PRIVATE_DATA    *Private,
+  IN     UINT8                               Port,
+  IN     UINT8                               PortMultiplier,
+  IN     UINT8                               FisIndex,
+  IN     EFI_AHCI_COMMAND_FIS                *CommandFis,
+  IN     EFI_AHCI_COMMAND_LIST               *CommandList,
+  IN     UINT8                               CommandSlotNumber,
+  IN OUT VOID                                *DataPhysicalAddr,
+  IN     UINT32                              DataLength
+  )
+{
+  EFI_AHCI_REGISTERS    *AhciRegisters;
+  UINTN                 AhciBar;
+  UINT64                BaseAddr;
+  UINT32                PrdtNumber;
+  UINT32                PrdtIndex;
+  UINTN                 RemainedData;
+  UINTN                 MemAddr;
+  DATA_64               Data64;
+  UINT32                Offset;
+
+  AhciRegisters = &Private->AhciRegisters;
+  AhciBar       = Private->MmioBase;
+
+  //
+  // Filling the PRDT
+  //
+  PrdtNumber = (UINT32)DivU64x32 (
+                         (UINT64)DataLength + AHCI_MAX_DATA_PER_PRDT - 1,
+                         AHCI_MAX_DATA_PER_PRDT
+                         );
+
+  //
+  // According to AHCI 1.3 spec, a PRDT entry can point to a maximum 4MB data block.
+  // It also limits that the maximum amount of the PRDT entry in the command table
+  // is 65535.
+  // Current driver implementation supports up to a maximum of AHCI_MAX_PRDT_NUMBER
+  // PRDT entries.
+  //
+  ASSERT (PrdtNumber <= AHCI_MAX_PRDT_NUMBER);
+  if (PrdtNumber > AHCI_MAX_PRDT_NUMBER) {
+    return;
+  }
+
+  Data64.Uint64 = (UINTN) (AhciRegisters->AhciRFis) + sizeof (EFI_AHCI_RECEIVED_FIS) * FisIndex;
+
+  BaseAddr = Data64.Uint64;
+
+  ZeroMem ((VOID *)((UINTN) BaseAddr), sizeof (EFI_AHCI_RECEIVED_FIS));
+
+  ZeroMem (AhciRegisters->AhciCmdTable, sizeof (EFI_AHCI_COMMAND_TABLE));
+
+  CommandFis->AhciCFisPmNum = PortMultiplier;
+
+  CopyMem (&AhciRegisters->AhciCmdTable->CommandFis, CommandFis, sizeof (EFI_AHCI_COMMAND_FIS));
+
+  Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CMD;
+  AhciAndReg (AhciBar, Offset, (UINT32)~(AHCI_PORT_CMD_DLAE | AHCI_PORT_CMD_ATAPI));
+
+  RemainedData = (UINTN) DataLength;
+  MemAddr      = (UINTN) DataPhysicalAddr;
+  CommandList->AhciCmdPrdtl = PrdtNumber;
+
+  for (PrdtIndex = 0; PrdtIndex < PrdtNumber; PrdtIndex++) {
+    if (RemainedData < AHCI_MAX_DATA_PER_PRDT) {
+      AhciRegisters->AhciCmdTable->PrdtTable[PrdtIndex].AhciPrdtDbc = (UINT32)RemainedData - 1;
+    } else {
+      AhciRegisters->AhciCmdTable->PrdtTable[PrdtIndex].AhciPrdtDbc = AHCI_MAX_DATA_PER_PRDT - 1;
+    }
+
+    Data64.Uint64 = (UINT64)MemAddr;
+    AhciRegisters->AhciCmdTable->PrdtTable[PrdtIndex].AhciPrdtDba  = Data64.Uint32.Lower32;
+    AhciRegisters->AhciCmdTable->PrdtTable[PrdtIndex].AhciPrdtDbau = Data64.Uint32.Upper32;
+    RemainedData -= AHCI_MAX_DATA_PER_PRDT;
+    MemAddr      += AHCI_MAX_DATA_PER_PRDT;
+  }
+
+  //
+  // Set the last PRDT to Interrupt On Complete
+  //
+  if (PrdtNumber > 0) {
+    AhciRegisters->AhciCmdTable->PrdtTable[PrdtNumber - 1].AhciPrdtIoc = 1;
+  }
+
+  CopyMem (
+    (VOID *) ((UINTN) AhciRegisters->AhciCmdList + (UINTN) CommandSlotNumber * sizeof (EFI_AHCI_COMMAND_LIST)),
+    CommandList,
+    sizeof (EFI_AHCI_COMMAND_LIST)
+    );
+
+  Data64.Uint64 = (UINT64)(UINTN) AhciRegisters->AhciCmdTable;
+  AhciRegisters->AhciCmdList[CommandSlotNumber].AhciCmdCtba  = Data64.Uint32.Lower32;
+  AhciRegisters->AhciCmdList[CommandSlotNumber].AhciCmdCtbau = Data64.Uint32.Upper32;
+  AhciRegisters->AhciCmdList[CommandSlotNumber].AhciCmdPmp   = PortMultiplier;
+}
+
+/**
+  Buid a command FIS.
+
+  @param[in,out] CmdFis             A pointer to the EFI_AHCI_COMMAND_FIS data
+                                    structure.
+  @param[in]     AtaCommandBlock    A pointer to the EFI_ATA_COMMAND_BLOCK data
+                                    structure.
+
+**/
+VOID
+AhciBuildCommandFis (
+  IN OUT EFI_AHCI_COMMAND_FIS     *CmdFis,
+  IN     EFI_ATA_COMMAND_BLOCK    *AtaCommandBlock
+  )
+{
+  ZeroMem (CmdFis, sizeof (EFI_AHCI_COMMAND_FIS));
+
+  CmdFis->AhciCFisType = AHCI_FIS_REGISTER_H2D;
+  //
+  // Indicator it's a command
+  //
+  CmdFis->AhciCFisCmdInd      = 0x1;
+  CmdFis->AhciCFisCmd         = AtaCommandBlock->AtaCommand;
+
+  CmdFis->AhciCFisFeature     = AtaCommandBlock->AtaFeatures;
+  CmdFis->AhciCFisFeatureExp  = AtaCommandBlock->AtaFeaturesExp;
+
+  CmdFis->AhciCFisSecNum      = AtaCommandBlock->AtaSectorNumber;
+  CmdFis->AhciCFisSecNumExp   = AtaCommandBlock->AtaSectorNumberExp;
+
+  CmdFis->AhciCFisClyLow      = AtaCommandBlock->AtaCylinderLow;
+  CmdFis->AhciCFisClyLowExp   = AtaCommandBlock->AtaCylinderLowExp;
+
+  CmdFis->AhciCFisClyHigh     = AtaCommandBlock->AtaCylinderHigh;
+  CmdFis->AhciCFisClyHighExp  = AtaCommandBlock->AtaCylinderHighExp;
+
+  CmdFis->AhciCFisSecCount    = AtaCommandBlock->AtaSectorCount;
+  CmdFis->AhciCFisSecCountExp = AtaCommandBlock->AtaSectorCountExp;
+
+  CmdFis->AhciCFisDevHead     = (UINT8) (AtaCommandBlock->AtaDeviceHead | 0xE0);
+}
+
+/**
+  Stop command running for giving port
+
+  @param[in] AhciBar    AHCI bar address.
+  @param[in] Port       The number of port.
+  @param[in] Timeout    The timeout value, in 100ns units, to stop.
+
+  @retval EFI_DEVICE_ERROR    The command stop unsuccessfully.
+  @retval EFI_TIMEOUT         The operation is time out.
+  @retval EFI_SUCCESS         The command stop successfully.
+
+**/
+EFI_STATUS
+AhciStopCommand (
+  IN  UINTN     AhciBar,
+  IN  UINT8     Port,
+  IN  UINT64    Timeout
+  )
+{
+  UINT32    Offset;
+  UINT32    Data;
+
+  Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CMD;
+  Data   = AhciReadReg (AhciBar, Offset);
+
+  if ((Data & (AHCI_PORT_CMD_ST | AHCI_PORT_CMD_CR)) == 0) {
+    return EFI_SUCCESS;
+  }
+
+  if ((Data & AHCI_PORT_CMD_ST) != 0) {
+    AhciAndReg (AhciBar, Offset, (UINT32)~(AHCI_PORT_CMD_ST));
+  }
+
+  return AhciWaitMmioSet (
+           AhciBar,
+           Offset,
+           AHCI_PORT_CMD_CR,
+           0,
+           Timeout
+           );
+}
+
+/**
+  Start command for give slot on specific port.
+
+  @param[in] AhciBar       AHCI bar address.
+  @param[in] Port          The number of port.
+  @param[in] CommandSlot   The number of Command Slot.
+  @param[in] Timeout       The timeout value, in 100ns units, to start.
+
+  @retval EFI_DEVICE_ERROR    The command start unsuccessfully.
+  @retval EFI_TIMEOUT         The operation is time out.
+  @retval EFI_SUCCESS         The command start successfully.
+
+**/
+EFI_STATUS
+AhciStartCommand (
+  IN  UINTN     AhciBar,
+  IN  UINT8     Port,
+  IN  UINT8     CommandSlot,
+  IN  UINT64    Timeout
+  )
+{
+  UINT32        CmdSlotBit;
+  EFI_STATUS    Status;
+  UINT32        PortStatus;
+  UINT32        StartCmd;
+  UINT32        PortTfd;
+  UINT32        Offset;
+  UINT32        Capability;
+
+  //
+  // Collect AHCI controller information
+  //
+  Capability = AhciReadReg (AhciBar, AHCI_CAPABILITY_OFFSET);
+
+  CmdSlotBit = (UINT32) (1 << CommandSlot);
+
+  AhciClearPortStatus (
+    AhciBar,
+    Port
+    );
+
+  Status = AhciEnableFisReceive (
+             AhciBar,
+             Port,
+             Timeout
+             );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CMD;
+  PortStatus = AhciReadReg (AhciBar, Offset);
+
+  StartCmd = 0;
+  if ((PortStatus & AHCI_PORT_CMD_ALPE) != 0) {
+    StartCmd = AhciReadReg (AhciBar, Offset);
+    StartCmd &= ~AHCI_PORT_CMD_ICC_MASK;
+    StartCmd |= AHCI_PORT_CMD_ACTIVE;
+  }
+
+  Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_TFD;
+  PortTfd = AhciReadReg (AhciBar, Offset);
+
+  if ((PortTfd & (AHCI_PORT_TFD_BSY | AHCI_PORT_TFD_DRQ)) != 0) {
+    if ((Capability & BIT24) != 0) {
+      Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CMD;
+      AhciOrReg (AhciBar, Offset, AHCI_PORT_CMD_CLO);
+
+      AhciWaitMmioSet (
+        AhciBar,
+        Offset,
+        AHCI_PORT_CMD_CLO,
+        0,
+        Timeout
+        );
+    }
+  }
+
+  Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CMD;
+  AhciOrReg (AhciBar, Offset, AHCI_PORT_CMD_ST | StartCmd);
+
+  //
+  // Setting the command
+  //
+  Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CI;
+  AhciAndReg (AhciBar, Offset, 0);
+  AhciOrReg  (AhciBar, Offset, CmdSlotBit);
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Start a PIO Data transfer on specific port.
+
+  @param[in]     Private            The pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA.
+  @param[in]     Port               The number of port.
+  @param[in]     PortMultiplier     The number of port multiplier.
+  @param[in]     FisIndex           The offset index of the FIS base address.
+  @param[in]     Read               The transfer direction.
+  @param[in]     AtaCommandBlock    The EFI_ATA_COMMAND_BLOCK data.
+  @param[in,out] AtaStatusBlock     The EFI_ATA_STATUS_BLOCK data.
+  @param[in,out] MemoryAddr         The pointer to the data buffer.
+  @param[in]     DataCount          The data count to be transferred.
+  @param[in]     Timeout            The timeout value of PIO data transfer, uses
+                                    100ns as a unit.
+
+  @retval EFI_DEVICE_ERROR        The PIO data transfer abort with error occurs.
+  @retval EFI_TIMEOUT             The operation is time out.
+  @retval EFI_UNSUPPORTED         The device is not ready for transfer.
+  @retval EFI_OUT_OF_RESOURCES    The operation fails due to lack of resources.
+  @retval EFI_SUCCESS             The PIO data transfer executes successfully.
+
+**/
+EFI_STATUS
+AhciPioTransfer (
+  IN     PEI_AHCI_CONTROLLER_PRIVATE_DATA    *Private,
+  IN     UINT8                               Port,
+  IN     UINT8                               PortMultiplier,
+  IN     UINT8                               FisIndex,
+  IN     BOOLEAN                             Read,
+  IN     EFI_ATA_COMMAND_BLOCK               *AtaCommandBlock,
+  IN OUT EFI_ATA_STATUS_BLOCK                *AtaStatusBlock,
+  IN OUT VOID                                *MemoryAddr,
+  IN     UINT32                              DataCount,
+  IN     UINT64                              Timeout
+  )
+{
+  EFI_STATUS                    Status;
+  EDKII_IOMMU_OPERATION         MapOp;
+  UINTN                         MapLength;
+  EFI_PHYSICAL_ADDRESS          PhyAddr;
+  VOID                          *MapData;
+  EFI_AHCI_REGISTERS            *AhciRegisters;
+  UINTN                         AhciBar;
+  BOOLEAN                       InfiniteWait;
+  UINT32                        Offset;
+  UINT32                        OldRfisLo;
+  UINT32                        OldRfisHi;
+  UINT32                        OldCmdListLo;
+  UINT32                        OldCmdListHi;
+  DATA_64                       Data64;
+  UINT32                        FisBaseAddr;
+  UINT32                        Delay;
+  EFI_AHCI_COMMAND_FIS          CFis;
+  EFI_AHCI_COMMAND_LIST         CmdList;
+  UINT32                        PortTfd;
+  UINT32                        PrdCount;
+  BOOLEAN                       PioFisReceived;
+  BOOLEAN                       D2hFisReceived;
+
+  //
+  // Current driver implementation supports up to a maximum of AHCI_MAX_PRDT_NUMBER
+  // PRDT entries.
+  //
+  if (DataCount / (UINT32)AHCI_MAX_PRDT_NUMBER > AHCI_MAX_DATA_PER_PRDT) {
+    DEBUG ((
+      DEBUG_ERROR,
+      "%a: Driver only support a maximum of 0x%x PRDT entries, "
+      "current number of data byte 0x%x is too large, maximum allowed is 0x%x.\n",
+      __FUNCTION__, AHCI_MAX_PRDT_NUMBER, DataCount,
+      AHCI_MAX_PRDT_NUMBER * AHCI_MAX_DATA_PER_PRDT
+      ));
+    return EFI_UNSUPPORTED;
+  }
+
+  MapOp     = Read ? EdkiiIoMmuOperationBusMasterWrite :
+                     EdkiiIoMmuOperationBusMasterRead;
+  MapLength = DataCount;
+  Status    = IoMmuMap (
+                MapOp,
+                MemoryAddr,
+                &MapLength,
+                &PhyAddr,
+                &MapData
+                );
+  if (EFI_ERROR (Status) || (MapLength != DataCount)) {
+    DEBUG ((DEBUG_ERROR, "%a: Fail to map data buffer.\n", __FUNCTION__));
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  AhciRegisters  = &Private->AhciRegisters;
+  AhciBar        = Private->MmioBase;
+  InfiniteWait   = (Timeout == 0) ? TRUE : FALSE;
+
+  //
+  // Fill FIS base address register
+  //
+  Offset        = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_FB;
+  OldRfisLo     = AhciReadReg (AhciBar, Offset);
+  Offset        = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_FBU;
+  OldRfisHi     = AhciReadReg (AhciBar, Offset);
+  Data64.Uint64 = (UINTN) (AhciRegisters->AhciRFis) + sizeof (EFI_AHCI_RECEIVED_FIS) * FisIndex;
+  Offset        = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_FB;
+  AhciWriteReg (AhciBar, Offset, Data64.Uint32.Lower32);
+  Offset        = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_FBU;
+  AhciWriteReg (AhciBar, Offset, Data64.Uint32.Upper32);
+
+  //
+  // Single task envrionment, we only use one command table for all port
+  //
+  Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CLB;
+  OldCmdListLo  = AhciReadReg (AhciBar, Offset);
+  Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CLBU;
+  OldCmdListHi  = AhciReadReg (AhciBar, Offset);
+  Data64.Uint64 = (UINTN) (AhciRegisters->AhciCmdList);
+  Offset        = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CLB;
+  AhciWriteReg (AhciBar, Offset, Data64.Uint32.Lower32);
+  Offset        = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CLBU;
+  AhciWriteReg (AhciBar, Offset, Data64.Uint32.Upper32);
+
+  //
+  // Package read needed
+  //
+  AhciBuildCommandFis (&CFis, AtaCommandBlock);
+
+  ZeroMem (&CmdList, sizeof (EFI_AHCI_COMMAND_LIST));
+
+  CmdList.AhciCmdCfl = AHCI_FIS_REGISTER_H2D_LENGTH / 4;
+  CmdList.AhciCmdW   = Read ? 0 : 1;
+
+  AhciBuildCommand (
+    Private,
+    Port,
+    PortMultiplier,
+    FisIndex,
+    &CFis,
+    &CmdList,
+    0,
+    (VOID *)(UINTN)PhyAddr,
+    DataCount
+    );
+
+  Status = AhciStartCommand (
+             AhciBar,
+             Port,
+             0,
+             Timeout
+             );
+  if (EFI_ERROR (Status)) {
+    goto Exit;
+  }
+
+  //
+  // Checking the status and wait the driver sending Data
+  //
+  FisBaseAddr = (UINT32)(UINTN)AhciRegisters->AhciRFis + sizeof (EFI_AHCI_RECEIVED_FIS) * FisIndex;
+  if (Read) {
+    //
+    // Wait device sends the PIO setup fis before data transfer
+    //
+    Status = EFI_TIMEOUT;
+    Delay  = (UINT32) DivU64x32 (Timeout, 1000) + 1;
+    do {
+      PioFisReceived = FALSE;
+      D2hFisReceived = FALSE;
+      Offset = FisBaseAddr + AHCI_PIO_FIS_OFFSET;
+      Status = AhciCheckMemSet (Offset, AHCI_FIS_TYPE_MASK, AHCI_FIS_PIO_SETUP);
+      if (!EFI_ERROR (Status)) {
+        DEBUG ((DEBUG_INFO, "%a: PioFisReceived.\n", __FUNCTION__));
+        PioFisReceived = TRUE;
+      }
+      //
+      // According to SATA 2.6 spec section 11.7, D2h FIS means an error encountered.
+      // But Qemu and Marvel 9230 sata controller may just receive a D2h FIS from
+      // device after the transaction is finished successfully.
+      // To get better device compatibilities, we further check if the PxTFD's
+      // ERR bit is set. By this way, we can know if there is a real error happened.
+      //
+      Offset = FisBaseAddr + AHCI_D2H_FIS_OFFSET;
+      Status = AhciCheckMemSet (Offset, AHCI_FIS_TYPE_MASK, AHCI_FIS_REGISTER_D2H);
+      if (!EFI_ERROR (Status)) {
+        DEBUG ((DEBUG_INFO, "%a: D2hFisReceived.\n", __FUNCTION__));
+        D2hFisReceived = TRUE;
+      }
+
+      if (PioFisReceived || D2hFisReceived) {
+        Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_TFD;
+        PortTfd = AhciReadReg (AhciBar, (UINT32) Offset);
+        //
+        // PxTFD will be updated if there is a D2H or SetupFIS received.
+        //
+        if ((PortTfd & AHCI_PORT_TFD_ERR) != 0) {
+          Status = EFI_DEVICE_ERROR;
+          break;
+        }
+
+        PrdCount = *(volatile UINT32 *) (&(AhciRegisters->AhciCmdList[0].AhciCmdPrdbc));
+        if (PrdCount == DataCount) {
+          Status = EFI_SUCCESS;
+          break;
+        }
+      }
+
+      //
+      // Stall for 100 microseconds.
+      //
+      MicroSecondDelay(100);
+
+      Delay--;
+      if (Delay == 0) {
+        Status = EFI_TIMEOUT;
+      }
+    } while (InfiniteWait || (Delay > 0));
+  } else {
+    //
+    // Wait for D2H Fis is received
+    //
+    Offset = FisBaseAddr + AHCI_D2H_FIS_OFFSET;
+    Status = AhciWaitMemSet (
+               Offset,
+               AHCI_FIS_TYPE_MASK,
+               AHCI_FIS_REGISTER_D2H,
+               Timeout
+               );
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_ERROR, "%a: AhciWaitMemSet (%r)\n", __FUNCTION__, Status));
+      goto Exit;
+    }
+
+    Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_TFD;
+    PortTfd = AhciReadReg (AhciBar, (UINT32) Offset);
+    if ((PortTfd & AHCI_PORT_TFD_ERR) != 0) {
+      Status = EFI_DEVICE_ERROR;
+    }
+  }
+
+Exit:
+  AhciStopCommand (
+    AhciBar,
+    Port,
+    Timeout
+    );
+
+  AhciDisableFisReceive (
+    AhciBar,
+    Port,
+    Timeout
+    );
+
+  if (MapData != NULL) {
+    IoMmuUnmap (MapData);
+  }
+
+  Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_FB;
+  AhciWriteReg (AhciBar, Offset, OldRfisLo);
+  Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_FBU;
+  AhciWriteReg (AhciBar, Offset, OldRfisHi);
+
+  Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CLB;
+  AhciWriteReg (AhciBar, Offset, OldCmdListLo);
+  Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CLBU;
+  AhciWriteReg (AhciBar, Offset, OldCmdListHi);
+
+  return Status;
+}
+
+/**
+  Start a non data transfer on specific port.
+
+  @param[in]     Private            The pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA.
+  @param[in]     Port               The number of port.
+  @param[in]     PortMultiplier     The number of port multiplier.
+  @param[in]     FisIndex           The offset index of the FIS base address.
+  @param[in]     AtaCommandBlock    The EFI_ATA_COMMAND_BLOCK data.
+  @param[in,out] AtaStatusBlock     The EFI_ATA_STATUS_BLOCK data.
+  @param[in]     Timeout            The timeout value of non data transfer, uses
+                                    100ns as a unit.
+
+  @retval EFI_DEVICE_ERROR        The non data transfer abort with error occurs.
+  @retval EFI_TIMEOUT             The operation is time out.
+  @retval EFI_UNSUPPORTED         The device is not ready for transfer.
+  @retval EFI_SUCCESS             The non data transfer executes successfully.
+
+**/
+EFI_STATUS
+AhciNonDataTransfer (
+  IN     PEI_AHCI_CONTROLLER_PRIVATE_DATA    *Private,
+  IN     UINT8                               Port,
+  IN     UINT8                               PortMultiplier,
+  IN     UINT8                               FisIndex,
+  IN     EFI_ATA_COMMAND_BLOCK               *AtaCommandBlock,
+  IN OUT EFI_ATA_STATUS_BLOCK                *AtaStatusBlock,
+  IN     UINT64                              Timeout
+  )
+{
+  EFI_STATUS                  Status;
+  UINTN                       AhciBar;
+  EFI_AHCI_REGISTERS          *AhciRegisters;
+  UINTN                       FisBaseAddr;
+  UINTN                       Offset;
+  UINT32                      PortTfd;
+  EFI_AHCI_COMMAND_FIS        CFis;
+  EFI_AHCI_COMMAND_LIST       CmdList;
+
+  AhciBar        = Private->MmioBase;
+  AhciRegisters = &Private->AhciRegisters;
+
+  //
+  // Package read needed
+  //
+  AhciBuildCommandFis (&CFis, AtaCommandBlock);
+
+  ZeroMem (&CmdList, sizeof (EFI_AHCI_COMMAND_LIST));
+
+  CmdList.AhciCmdCfl = AHCI_FIS_REGISTER_H2D_LENGTH / 4;
+
+  AhciBuildCommand (
+    Private,
+    Port,
+    PortMultiplier,
+    FisIndex,
+    &CFis,
+    &CmdList,
+    0,
+    NULL,
+    0
+    );
+
+  Status = AhciStartCommand (
+             AhciBar,
+             Port,
+             0,
+             Timeout
+             );
+  if (EFI_ERROR (Status)) {
+    goto Exit;
+  }
+
+  //
+  // Wait device sends the Response Fis
+  //
+  FisBaseAddr = (UINTN)AhciRegisters->AhciRFis + sizeof (EFI_AHCI_RECEIVED_FIS) * FisIndex;
+  Offset      = FisBaseAddr + AHCI_D2H_FIS_OFFSET;
+  Status      = AhciWaitMemSet (
+                  Offset,
+                  AHCI_FIS_TYPE_MASK,
+                  AHCI_FIS_REGISTER_D2H,
+                  Timeout
+                  );
+
+  if (EFI_ERROR (Status)) {
+    goto Exit;
+  }
+
+  Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_TFD;
+  PortTfd = AhciReadReg (AhciBar, (UINT32) Offset);
+  if ((PortTfd & AHCI_PORT_TFD_ERR) != 0) {
+    Status = EFI_DEVICE_ERROR;
+  }
+
+Exit:
+  AhciStopCommand (
+    AhciBar,
+    Port,
+    Timeout
+    );
+
+  AhciDisableFisReceive (
+    AhciBar,
+    Port,
+    Timeout
+    );
+
+  return Status;
+}
+
+/**
+  Do AHCI HBA reset.
+
+  @param[in] AhciBar         AHCI bar address.
+  @param[in] Timeout         The timeout, in 100ns units, to reset.
+
+  @retval EFI_DEVICE_ERROR   AHCI controller is failed to complete hardware reset.
+  @retval EFI_TIMEOUT        The reset operation is time out.
+  @retval EFI_SUCCESS        AHCI controller is reset successfully.
+
+**/
+EFI_STATUS
+AhciReset (
+  IN UINTN     AhciBar,
+  IN UINT64    Timeout
+  )
+{
+  UINT32    Delay;
+  UINT32    Value;
+  UINT32    Capability;
+
+  //
+  // Collect AHCI controller information
+  //
+  Capability = AhciReadReg (AhciBar, AHCI_CAPABILITY_OFFSET);
+
+  //
+  // Enable AE before accessing any AHCI registers if Supports AHCI Mode Only is not set
+  //
+  if ((Capability & AHCI_CAP_SAM) == 0) {
+    AhciOrReg (AhciBar, AHCI_GHC_OFFSET, AHCI_GHC_ENABLE);
+  }
+
+  AhciOrReg (AhciBar, AHCI_GHC_OFFSET, AHCI_GHC_RESET);
+
+  Delay = (UINT32) (DivU64x32(Timeout, 1000) + 1);
+
+  do {
+    Value = AhciReadReg(AhciBar, AHCI_GHC_OFFSET);
+    if ((Value & AHCI_GHC_RESET) == 0) {
+      return EFI_SUCCESS;
+    }
+
+    //
+    // Stall for 100 microseconds.
+    //
+    MicroSecondDelay(100);
+
+    Delay--;
+  } while (Delay > 0);
+
+  return EFI_TIMEOUT;
+}
+
+/**
+  Send Identify Drive command to a specific device.
+
+  @param[in] Private           The pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA.
+  @param[in] Port              The number of port.
+  @param[in] PortMultiplier    The port multiplier port number.
+  @param[in] FisIndex          The offset index of the FIS base address.
+  @param[in] Buffer            The data buffer to store IDENTIFY PACKET data.
+
+  @retval EFI_SUCCESS              The cmd executes successfully.
+  @retval EFI_INVALID_PARAMETER    Buffer is NULL.
+  @retval EFI_DEVICE_ERROR         The cmd abort with error occurs.
+  @retval EFI_TIMEOUT              The operation is time out.
+  @retval EFI_UNSUPPORTED          The device is not ready for executing.
+
+**/
+EFI_STATUS
+AhciIdentify (
+  IN PEI_AHCI_CONTROLLER_PRIVATE_DATA    *Private,
+  IN UINT8                               Port,
+  IN UINT8                               PortMultiplier,
+  IN UINT8                               FisIndex,
+  IN ATA_IDENTIFY_DATA                   *Buffer
+  )
+{
+  EFI_STATUS                     Status;
+  EFI_ATA_COMMAND_BLOCK    Acb;
+  EFI_ATA_STATUS_BLOCK     Asb;
+
+  if (Buffer == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  ZeroMem (&Acb, sizeof (EFI_ATA_COMMAND_BLOCK));
+  ZeroMem (&Asb, sizeof (EFI_ATA_STATUS_BLOCK));
+
+  Acb.AtaCommand     = ATA_CMD_IDENTIFY_DRIVE;
+  Acb.AtaSectorCount = 1;
+
+  Status = AhciPioTransfer (
+             Private,
+             Port,
+             PortMultiplier,
+             FisIndex,
+             TRUE,
+             &Acb,
+             &Asb,
+             Buffer,
+             sizeof (ATA_IDENTIFY_DATA),
+             ATA_TIMEOUT
+             );
+
+  return Status;
+}
+
+
+/**
+  Collect the number of bits set within a port bitmap.
+
+  @param[in] PortBitMap    A 32-bit wide bit map of ATA AHCI ports.
+
+  @retval The number of bits set in the bitmap.
+
+**/
+UINT8
+AhciGetNumberOfPortsFromMap (
+  IN UINT32    PortBitMap
+  )
+{
+  UINT8    NumberOfPorts;
+
+  NumberOfPorts = 0;
+
+  while (PortBitMap != 0) {
+    if ((PortBitMap & ((UINT32)BIT0)) != 0) {
+      NumberOfPorts++;
+    }
+    PortBitMap = PortBitMap >> 1;
+  }
+
+  return NumberOfPorts;
+}
+
+/**
+  Get the specified port number from a port bitmap.
+
+  @param[in]  PortBitMap    A 32-bit wide bit map of ATA AHCI ports.
+  @param[in]  PortIndex     The specified port index.
+  @param[out] Port          The port number of the port specified by PortIndex.
+
+  @retval EFI_SUCCESS       The specified port is found and its port number is
+                            in Port.
+  @retval EFI_NOT_FOUND     Cannot find the specified port within the port bitmap.
+
+**/
+EFI_STATUS
+AhciGetPortFromMap (
+  IN  UINT32    PortBitMap,
+  IN  UINT8     PortIndex,
+  OUT UINT8     *Port
+  )
+{
+  if (PortIndex == 0) {
+    return EFI_NOT_FOUND;
+  }
+
+  *Port = 0;
+
+  while (PortBitMap != 0) {
+    if ((PortBitMap & ((UINT32)BIT0)) != 0) {
+      PortIndex--;
+
+      //
+      // Found the port specified by PortIndex.
+      //
+      if (PortIndex == 0) {
+        return EFI_SUCCESS;
+      }
+    }
+    PortBitMap = PortBitMap >> 1;
+    *Port      = *Port + 1;
+  }
+
+  return EFI_NOT_FOUND;
+}
+
+/**
+  Allocate transfer-related data struct which is used at AHCI mode.
+
+  @param[in,out] Private    A pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA instance.
+
+  @retval EFI_SUCCESS    Data structures are allocated successfully.
+  @retval Others         Data structures are not allocated successfully.
+
+**/
+EFI_STATUS
+AhciCreateTransferDescriptor (
+  IN OUT PEI_AHCI_CONTROLLER_PRIVATE_DATA    *Private
+  )
+{
+  EFI_STATUS              Status;
+  UINTN                   AhciBar;
+  EFI_AHCI_REGISTERS      *AhciRegisters;
+  EFI_PHYSICAL_ADDRESS    DeviceAddress;
+  VOID                    *Base;
+  VOID                    *Mapping;
+  UINT32                  Capability;
+  UINT32                  PortImplementBitMap;
+  UINT8                   MaxPortNumber;
+  UINT8                   MaxCommandSlotNumber;
+  UINTN                   MaxRFisSize;
+  UINTN                   MaxCmdListSize;
+  UINTN                   MaxCmdTableSize;
+
+  AhciBar       = Private->MmioBase;
+  AhciRegisters = &Private->AhciRegisters;
+
+  //
+  // Collect AHCI controller information
+  //
+  Capability = AhciReadReg (AhciBar, AHCI_CAPABILITY_OFFSET);
+
+  //
+  // Get the number of command slots per port supported by this HBA.
+  //
+  MaxCommandSlotNumber = (UINT8) (((Capability & 0x1F00) >> 8) + 1);
+  ASSERT (MaxCommandSlotNumber > 0);
+  if (MaxCommandSlotNumber == 0) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  //
+  // Get the highest bit of implemented ports which decides how many bytes are
+  // allocated for recived FIS.
+  //
+  PortImplementBitMap = AhciReadReg (AhciBar, AHCI_PI_OFFSET);
+  MaxPortNumber       = (UINT8)(UINTN)(HighBitSet32(PortImplementBitMap) + 1);
+  if (MaxPortNumber == 0) {
+    return EFI_DEVICE_ERROR;
+  }
+  //
+  // Get the number of ports that actually needed to be initialized.
+  //
+  MaxPortNumber = MIN (MaxPortNumber, AhciGetNumberOfPortsFromMap (Private->PortBitMap));
+
+  //
+  // Allocate memory for received FIS.
+  //
+  MaxRFisSize = MaxPortNumber * sizeof (EFI_AHCI_RECEIVED_FIS);
+  Status = IoMmuAllocateBuffer (
+             EFI_SIZE_TO_PAGES (MaxRFisSize),
+             &Base,
+             &DeviceAddress,
+             &Mapping
+             );
+  if (EFI_ERROR (Status)) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+  ASSERT (DeviceAddress == ((EFI_PHYSICAL_ADDRESS) (UINTN) Base));
+  AhciRegisters->AhciRFis    = Base;
+  AhciRegisters->AhciRFisMap = Mapping;
+  AhciRegisters->MaxRFisSize = MaxRFisSize;
+  ZeroMem (AhciRegisters->AhciRFis, EFI_PAGE_SIZE * EFI_SIZE_TO_PAGES (MaxRFisSize));
+
+  //
+  // Allocate memory for command list.
+  // Note that the implemenation is a single task model which only use a command
+  // list for each port.
+  //
+  MaxCmdListSize = 1 * sizeof (EFI_AHCI_COMMAND_LIST);
+  Status = IoMmuAllocateBuffer (
+             EFI_SIZE_TO_PAGES (MaxCmdListSize),
+             &Base,
+             &DeviceAddress,
+             &Mapping
+             );
+  if (EFI_ERROR (Status)) {
+    Status = EFI_OUT_OF_RESOURCES;
+    goto ErrorExit;
+  }
+  ASSERT (DeviceAddress == ((EFI_PHYSICAL_ADDRESS) (UINTN) Base));
+  AhciRegisters->AhciCmdList    = Base;
+  AhciRegisters->AhciCmdListMap = Mapping;
+  AhciRegisters->MaxCmdListSize = MaxCmdListSize;
+  ZeroMem (AhciRegisters->AhciCmdList, EFI_PAGE_SIZE * EFI_SIZE_TO_PAGES (MaxCmdListSize));
+
+  //
+  // Allocate memory for command table
+  // According to AHCI 1.3 spec, a PRD table can contain maximum 65535 entries.
+  //
+  MaxCmdTableSize = sizeof (EFI_AHCI_COMMAND_TABLE);
+  Status = IoMmuAllocateBuffer (
+             EFI_SIZE_TO_PAGES (MaxCmdTableSize),
+             &Base,
+             &DeviceAddress,
+             &Mapping
+             );
+  if (EFI_ERROR (Status)) {
+    Status = EFI_OUT_OF_RESOURCES;
+    goto ErrorExit;
+  }
+  ASSERT (DeviceAddress == ((EFI_PHYSICAL_ADDRESS) (UINTN) Base));
+  AhciRegisters->AhciCmdTable    = Base;
+  AhciRegisters->AhciCmdTableMap = Mapping;
+  AhciRegisters->MaxCmdTableSize = MaxCmdTableSize;
+  ZeroMem (AhciRegisters->AhciCmdTable, EFI_PAGE_SIZE * EFI_SIZE_TO_PAGES (MaxCmdTableSize));
+
+  return EFI_SUCCESS;
+
+ErrorExit:
+  if (AhciRegisters->AhciRFisMap != NULL) {
+    IoMmuFreeBuffer (
+       EFI_SIZE_TO_PAGES (AhciRegisters->MaxRFisSize),
+       AhciRegisters->AhciRFis,
+       AhciRegisters->AhciRFisMap
+       );
+    AhciRegisters->AhciRFis = NULL;
+  }
+
+  if (AhciRegisters->AhciCmdListMap != NULL) {
+    IoMmuFreeBuffer (
+       EFI_SIZE_TO_PAGES (AhciRegisters->MaxCmdListSize),
+       AhciRegisters->AhciCmdList,
+       AhciRegisters->AhciCmdListMap
+       );
+    AhciRegisters->AhciCmdList = NULL;
+  }
+
+  return Status;
+}
+
+/**
+  Gets ATA device Capacity according to ATA 6.
+
+  This function returns the capacity of the ATA device if it follows
+  ATA 6 to support 48 bit addressing.
+
+  @param[in] IdentifyData    A pointer to ATA_IDENTIFY_DATA structure.
+
+  @return The capacity of the ATA device or 0 if the device does not support
+          48-bit addressing defined in ATA 6.
+
+**/
+EFI_LBA
+GetAtapi6Capacity (
+  IN ATA_IDENTIFY_DATA    *IdentifyData
+  )
+{
+  EFI_LBA                       Capacity;
+  EFI_LBA                       TmpLba;
+  UINTN                         Index;
+
+  if ((IdentifyData->command_set_supported_83 & BIT10) == 0) {
+    //
+    // The device doesn't support 48 bit addressing
+    //
+    return 0;
+  }
+
+  //
+  // 48 bit address feature set is supported, get maximum capacity
+  //
+  Capacity = 0;
+  for (Index = 0; Index < 4; Index++) {
+    //
+    // Lower byte goes first: word[100] is the lowest word, word[103] is highest
+    //
+    TmpLba = IdentifyData->maximum_lba_for_48bit_addressing[Index];
+    Capacity |= LShiftU64 (TmpLba, 16 * Index);
+  }
+
+  return Capacity;
+}
+
+/**
+  Identifies ATA device via the Identify data.
+
+  This function identifies the ATA device and initializes the media information.
+
+  @attention This is boundary function that may receive untrusted input.
+  @attention The input is from peripheral hardware device.
+
+  The Identify Drive command response data from an ATA device is the peripheral
+  hardware input, so this routine will do basic validation for the Identify Drive
+  command response data.
+
+  @param[in,out] DeviceData    A pointer to PEI_AHCI_ATA_DEVICE_DATA structure.
+
+  @retval EFI_SUCCESS        The device is successfully identified and media
+                             information is correctly initialized.
+  @retval EFI_UNSUPPORTED    The device is not a valid ATA device (hard disk).
+
+**/
+EFI_STATUS
+IdentifyAtaDevice (
+  IN OUT PEI_AHCI_ATA_DEVICE_DATA    *DeviceData
+  )
+{
+  ATA_IDENTIFY_DATA          *IdentifyData;
+  EFI_PEI_BLOCK_IO2_MEDIA    *Media;
+  EFI_LBA                    Capacity;
+  UINT32                     MaxSectorCount;
+  UINT16                     PhyLogicSectorSupport;
+
+  IdentifyData = DeviceData->IdentifyData;
+  Media        = &DeviceData->Media;
+
+  if ((IdentifyData->config & BIT15) != 0) {
+    DEBUG ((
+      DEBUG_ERROR, "%a: Not a hard disk device on Port 0x%x PortMultiplierPort 0x%x\n",
+      __FUNCTION__, DeviceData->Port, DeviceData->PortMultiplier
+      ));
+    return EFI_UNSUPPORTED;
+  }
+
+  DEBUG ((
+    DEBUG_INFO, "%a: Identify Device: Port 0x%x PortMultiplierPort 0x%x\n",
+    __FUNCTION__, DeviceData->Port, DeviceData->PortMultiplier
+    ));
+
+  //
+  // Skip checking whether the WORD 88 (supported UltraDMA by drive), since the
+  // driver only support PIO data transfer for now.
+  //
+
+  //
+  // Get the capacity information of the device.
+  //
+  Capacity = GetAtapi6Capacity (IdentifyData);
+  if (Capacity > MAX_28BIT_ADDRESSING_CAPACITY) {
+    //
+    // Capacity exceeds 120GB. 48-bit addressing is really needed
+    //
+    DeviceData->Lba48Bit = TRUE;
+  } else {
+    //
+    // This is a hard disk <= 120GB capacity, treat it as normal hard disk
+    //
+    Capacity = ((UINT32)IdentifyData->user_addressable_sectors_hi << 16) |
+                        IdentifyData->user_addressable_sectors_lo;
+    DeviceData->Lba48Bit = FALSE;
+  }
+
+  if (Capacity == 0) {
+    DEBUG ((DEBUG_ERROR, "%a: Invalid Capacity (0) for ATA device.\n", __FUNCTION__));
+    return EFI_UNSUPPORTED;
+  }
+  Media->LastBlock = (EFI_PEI_LBA) (Capacity - 1);
+
+  Media->BlockSize = 0x200;
+  //
+  // Check whether Long Physical Sector Feature is supported
+  //
+  PhyLogicSectorSupport = IdentifyData->phy_logic_sector_support;
+  DEBUG ((
+    DEBUG_INFO, "%a: PhyLogicSectorSupport = 0x%x\n",
+    __FUNCTION__, PhyLogicSectorSupport
+    ));
+  if ((PhyLogicSectorSupport & (BIT14 | BIT15)) == BIT14) {
+    //
+    // Check logical block size
+    //
+    if ((PhyLogicSectorSupport & BIT12) != 0) {
+      Media->BlockSize = (UINT32) (((IdentifyData->logic_sector_size_hi << 16) |
+                                     IdentifyData->logic_sector_size_lo) * sizeof (UINT16));
+    }
+  }
+
+  //
+  // Check BlockSize validity
+  //
+  MaxSectorCount = mMaxTransferBlockNumber[DeviceData->Lba48Bit];
+  if ((Media->BlockSize == 0) || (Media->BlockSize > MAX_UINT32 / MaxSectorCount)) {
+    DEBUG ((DEBUG_ERROR, "%a: Invalid BlockSize (0x%x).\n", __FUNCTION__, Media->BlockSize));
+    return EFI_UNSUPPORTED;
+  }
+
+  DEBUG ((
+    DEBUG_INFO, "%a: BlockSize = 0x%x, LastBlock = 0x%lx\n",
+    __FUNCTION__, Media->BlockSize, Media->LastBlock
+    ));
+
+  if ((IdentifyData->trusted_computing_support & BIT0) != 0) {
+    DEBUG ((DEBUG_INFO, "%a: Found Trust Computing feature support.\n", __FUNCTION__));
+    DeviceData->TrustComputing = TRUE;
+  }
+
+  Media->InterfaceType  = MSG_SATA_DP;
+  Media->RemovableMedia = FALSE;
+  Media->MediaPresent   = TRUE;
+  Media->ReadOnly       = FALSE;
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Allocate device information data structure to contain device information.
+  And insert the data structure to the tail of device list for tracing.
+
+  @param[in,out] Private               A pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA
+                                       instance.
+  @param[in]     DeviceIndex           The device index.
+  @param[in]     Port                  The port number of the ATA device to send
+                                       the command.
+  @param[in]     PortMultiplierPort    The port multiplier port number of the ATA
+                                       device to send the command.
+                                       If there is no port multiplier, then specify
+                                       0xFFFF.
+  @param[in]     FisIndex              The index of the FIS of the ATA device to
+                                       send the command.
+  @param[in]     IdentifyData          The data buffer to store the output of the
+                                       IDENTIFY command.
+
+  @retval EFI_SUCCESS                  Successfully insert the ATA device to the
+                                       tail of device list.
+  @retval EFI_OUT_OF_RESOURCES         Not enough resource.
+
+**/
+EFI_STATUS
+CreateNewDevice (
+  IN OUT PEI_AHCI_CONTROLLER_PRIVATE_DATA    *Private,
+  IN     UINTN                               DeviceIndex,
+  IN     UINT16                              Port,
+  IN     UINT16                              PortMultiplier,
+  IN     UINT8                               FisIndex,
+  IN     ATA_IDENTIFY_DATA                   *IdentifyData
+  )
+{
+  PEI_AHCI_ATA_DEVICE_DATA    *DeviceData;
+  EFI_STATUS                  Status;
+
+  DeviceData = AllocateZeroPool (sizeof (PEI_AHCI_ATA_DEVICE_DATA));
+  if (DeviceData == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  if (IdentifyData != NULL) {
+    DeviceData->IdentifyData = AllocateCopyPool (sizeof (ATA_IDENTIFY_DATA), IdentifyData);
+    if (DeviceData->IdentifyData == NULL) {
+      return EFI_OUT_OF_RESOURCES;
+    }
+  }
+
+  DeviceData->Signature      = AHCI_PEI_ATA_DEVICE_DATA_SIGNATURE;
+  DeviceData->Port           = Port;
+  DeviceData->PortMultiplier = PortMultiplier;
+  DeviceData->FisIndex       = FisIndex;
+  DeviceData->DeviceIndex    = DeviceIndex;
+  DeviceData->Private        = Private;
+
+  Status = IdentifyAtaDevice (DeviceData);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  if (DeviceData->TrustComputing) {
+    Private->TrustComputingDevices++;
+    DeviceData->TrustComputingDeviceIndex = Private->TrustComputingDevices;
+  }
+  Private->ActiveDevices++;
+  InsertTailList (&Private->DeviceList, &DeviceData->Link);
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Initialize ATA host controller at AHCI mode.
+
+  The function is designed to initialize ATA host controller.
+
+  @param[in,out] Private    A pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA instance.
+
+  @retval EFI_SUCCESS             The ATA AHCI controller is initialized successfully.
+  @retval EFI_OUT_OF_RESOURCES    Not enough resource to complete while initializing
+                                  the controller.
+  @retval Others                  A device error occurred while initializing the
+                                  controller.
+
+**/
+EFI_STATUS
+AhciModeInitialization (
+  IN OUT PEI_AHCI_CONTROLLER_PRIVATE_DATA    *Private
+  )
+{
+  EFI_STATUS            Status;
+  UINTN                 AhciBar;
+  UINT32                Capability;
+  UINT32                Value;
+  UINT8                 MaxPortNumber;
+  UINT32                PortImplementBitMap;
+  UINT32                PortInitializeBitMap;
+  EFI_AHCI_REGISTERS    *AhciRegisters;
+  UINT8                 PortIndex;
+  UINT8                 Port;
+  DATA_64               Data64;
+  UINT32                Data;
+  UINT32                Offset;
+  UINT32                PhyDetectDelay;
+  UINTN                 DeviceIndex;
+  ATA_IDENTIFY_DATA     IdentifyData;
+
+  AhciBar = Private->MmioBase;
+
+  Status = AhciReset (AhciBar, AHCI_PEI_RESET_TIMEOUT);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a: AHCI HBA reset failed with %r.\n", __FUNCTION__, Status));
+    return EFI_DEVICE_ERROR;
+  }
+
+  //
+  // Collect AHCI controller information
+  //
+  Capability = AhciReadReg (AhciBar, AHCI_CAPABILITY_OFFSET);
+
+  //
+  // Make sure that GHC.AE bit is set before accessing any AHCI registers.
+  //
+  Value = AhciReadReg (AhciBar, AHCI_GHC_OFFSET);
+  if ((Value & AHCI_GHC_ENABLE) == 0) {
+    AhciOrReg (AhciBar, AHCI_GHC_OFFSET, AHCI_GHC_ENABLE);
+  }
+
+  Status = AhciCreateTransferDescriptor (Private);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((
+      DEBUG_ERROR,
+      "%a: Transfer-related data allocation failed with %r.\n",
+      __FUNCTION__, Status
+      ));
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  //
+  // Get the number of command slots per port supported by this HBA.
+  //
+  MaxPortNumber = (UINT8) ((Capability & 0x1F) + 1);
+
+  //
+  // Get the bit map of those ports exposed by this HBA.
+  // It indicates which ports that the HBA supports are available for software
+  // to use.
+  //
+  PortImplementBitMap = AhciReadReg (AhciBar, AHCI_PI_OFFSET);
+
+  //
+  // Get the number of ports that actually needed to be initialized.
+  //
+  MaxPortNumber = MIN (MaxPortNumber, (UINT8)(UINTN)(HighBitSet32(PortImplementBitMap) + 1));
+  MaxPortNumber = MIN (MaxPortNumber, AhciGetNumberOfPortsFromMap (Private->PortBitMap));
+
+  PortInitializeBitMap = Private->PortBitMap;
+  AhciRegisters        = &Private->AhciRegisters;
+  DeviceIndex          = 0;
+  //
+  // Enumerate ATA ports
+  //
+  for (PortIndex = 1; PortIndex <= MaxPortNumber; PortIndex ++) {
+    Status = AhciGetPortFromMap (PortInitializeBitMap, PortIndex, &Port);
+    if ((PortImplementBitMap & (BIT0 << Port)) != 0) {
+      //
+      // Initialize FIS Base Address Register and Command List Base Address
+      // Register for use.
+      //
+      Data64.Uint64 = (UINTN) (AhciRegisters->AhciRFis) +
+                      sizeof (EFI_AHCI_RECEIVED_FIS) * (PortIndex - 1);
+      Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_FB;
+      AhciWriteReg (AhciBar, Offset, Data64.Uint32.Lower32);
+      Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_FBU;
+      AhciWriteReg (AhciBar, Offset, Data64.Uint32.Upper32);
+
+      Data64.Uint64 = (UINTN) (AhciRegisters->AhciCmdList);
+      Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CLB;
+      AhciWriteReg (AhciBar, Offset, Data64.Uint32.Lower32);
+      Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CLBU;
+      AhciWriteReg (AhciBar, Offset, Data64.Uint32.Upper32);
+
+      Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CMD;
+      Data = AhciReadReg (AhciBar, Offset);
+      if ((Data & AHCI_PORT_CMD_CPD) != 0) {
+        AhciOrReg (AhciBar, Offset, AHCI_PORT_CMD_POD);
+      }
+
+      if ((Capability & AHCI_CAP_SSS) != 0) {
+        AhciOrReg (AhciBar, Offset, AHCI_PORT_CMD_SUD);
+      }
+
+      //
+      // Disable aggressive power management.
+      //
+      Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_SCTL;
+      AhciOrReg (AhciBar, Offset, AHCI_PORT_SCTL_IPM_INIT);
+      //
+      // Disable the reporting of the corresponding interrupt to system software.
+      //
+      Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_IE;
+      AhciAndReg (AhciBar, Offset, 0);
+
+      //
+      // Enable FIS Receive DMA engine for the first D2H FIS.
+      //
+      Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CMD;
+      AhciOrReg (AhciBar, Offset, AHCI_PORT_CMD_FRE);
+
+      //
+      // Wait no longer than 15 ms to wait the Phy to detect the presence of a device.
+      //
+      PhyDetectDelay = AHCI_BUS_PHY_DETECT_TIMEOUT;
+      Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_SSTS;
+      do {
+        Data = AhciReadReg (AhciBar, Offset) & AHCI_PORT_SSTS_DET_MASK;
+        if ((Data == AHCI_PORT_SSTS_DET_PCE) || (Data == AHCI_PORT_SSTS_DET)) {
+          break;
+        }
+
+        MicroSecondDelay (1000);
+        PhyDetectDelay--;
+      } while (PhyDetectDelay > 0);
+
+      if (PhyDetectDelay == 0) {
+        //
+        // No device detected at this port.
+        // Clear PxCMD.SUD for those ports at which there are no device present.
+        //
+        Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CMD;
+        AhciAndReg (AhciBar, Offset, (UINT32) ~(AHCI_PORT_CMD_SUD));
+        DEBUG ((DEBUG_ERROR, "%a: No device detected at Port %d.\n", __FUNCTION__, Port));
+        continue;
+      }
+
+      //
+      // According to SATA1.0a spec section 5.2, we need to wait for PxTFD.BSY and PxTFD.DRQ
+      // and PxTFD.ERR to be zero. The maximum wait time is 16s which is defined at ATA spec.
+      //
+      PhyDetectDelay = 16 * 1000;
+      do {
+        Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_SERR;
+        if (AhciReadReg(AhciBar, Offset) != 0) {
+          AhciWriteReg (AhciBar, Offset, AhciReadReg (AhciBar, Offset));
+        }
+        Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_TFD;
+
+        Data = AhciReadReg (AhciBar, Offset) & AHCI_PORT_TFD_MASK;
+        if (Data == 0) {
+          break;
+        }
+
+        MicroSecondDelay (1000);
+        PhyDetectDelay--;
+      } while (PhyDetectDelay > 0);
+
+      if (PhyDetectDelay == 0) {
+        DEBUG ((
+          DEBUG_ERROR,
+          "%a: Port %d device presence detected but phy not ready (TFD=0x%x).\n",
+          __FUNCTION__, Port, Data
+          ));
+        continue;
+      }
+
+      //
+      // When the first D2H register FIS is received, the content of PxSIG register is updated.
+      //
+      Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_SIG;
+      Status = AhciWaitMmioSet (
+                 AhciBar,
+                 Offset,
+                 0x0000FFFF,
+                 0x00000101,
+                 160000000
+                 );
+      if (EFI_ERROR (Status)) {
+        DEBUG ((
+          DEBUG_ERROR,
+          "%a: Error occured when waiting for the first D2H register FIS - %r\n",
+          __FUNCTION__, Status
+          ));
+        continue;
+      }
+
+      Data = AhciReadReg (AhciBar, Offset);
+      if ((Data & AHCI_ATAPI_SIG_MASK) == AHCI_ATA_DEVICE_SIG) {
+        Status = AhciIdentify (Private, Port, 0, PortIndex - 1, &IdentifyData);
+        if (EFI_ERROR (Status)) {
+          DEBUG ((DEBUG_ERROR, "%a: AhciIdentify() failed with %r\n", __FUNCTION__, Status));
+          continue;
+        }
+        DEBUG ((DEBUG_INFO, "%a: ATA hard disk found on Port %d.\n", __FUNCTION__, Port));
+      } else {
+        continue;
+      }
+
+      //
+      // Found an ATA hard disk device, add it into the device list.
+      //
+      DeviceIndex++;
+      CreateNewDevice (
+        Private,
+        DeviceIndex,
+        Port,
+        0xFFFF,
+        PortIndex - 1,
+        &IdentifyData
+        );
+    }
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Trust transfer data from/to ATA device.
+
+  This function performs one ATA pass through transaction to do a trust transfer
+  from/to ATA device. It chooses the appropriate ATA command and protocol to invoke
+  PassThru interface of ATA pass through.
+
+  @param[in]     DeviceData     Pointer to PEI_AHCI_ATA_DEVICE_DATA structure.
+  @param[in,out] Buffer         The pointer to the current transaction buffer.
+  @param[in]     SecurityProtocolId
+                                The value of the "Security Protocol" parameter
+                                of the security protocol command to be sent.
+  @param[in]     SecurityProtocolSpecificData
+                                The value of the "Security Protocol Specific"
+                                parameter of the security protocol command to
+                                be sent.
+  @param[in]     TransferLength The block number or sector count of the transfer.
+  @param[in]     IsTrustSend    Indicates whether it is a trust send operation
+                                or not.
+  @param[in]     Timeout        The timeout, in 100ns units, to use for the execution
+                                of the security protocol command. A Timeout value
+                                of 0 means that this function will wait indefinitely
+                                for the security protocol command to execute. If
+                                Timeout is greater than zero, then this function
+                                will return EFI_TIMEOUT if the time required to
+                                execute the receive data command is greater than
+                                Timeout.
+  @param[out]    TransferLengthOut
+                                A pointer to a buffer to store the size in bytes
+                                of the data written to the buffer. Ignore it when
+                                IsTrustSend is TRUE.
+
+  @retval EFI_SUCCESS    The data transfer is complete successfully.
+  @return others         Some error occurs when transferring data.
+
+**/
+EFI_STATUS
+TrustTransferAtaDevice (
+  IN     PEI_AHCI_ATA_DEVICE_DATA    *DeviceData,
+  IN OUT VOID                        *Buffer,
+  IN     UINT8                       SecurityProtocolId,
+  IN     UINT16                      SecurityProtocolSpecificData,
+  IN     UINTN                       TransferLength,
+  IN     BOOLEAN                     IsTrustSend,
+  IN     UINT64                      Timeout,
+  OUT    UINTN                       *TransferLengthOut
+  )
+{
+  PEI_AHCI_CONTROLLER_PRIVATE_DATA    *Private;
+  EDKII_PEI_ATA_PASS_THRU_PPI         *AtaPassThru;
+  EFI_ATA_COMMAND_BLOCK               Acb;
+  EFI_ATA_PASS_THRU_COMMAND_PACKET    Packet;
+  EFI_STATUS                          Status;
+  VOID                                *NewBuffer;
+
+  Private     = DeviceData->Private;
+  AtaPassThru = &Private->AtaPassThruPpi;
+
+  //
+  // Ensure IsTrustSend are valid boolean values
+  //
+  ASSERT ((UINTN) IsTrustSend < 2);
+  if ((UINTN) IsTrustSend >= 2) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // Prepare for ATA command block.
+  //
+  ZeroMem (&Acb, sizeof (EFI_ATA_COMMAND_BLOCK));
+  if (TransferLength == 0) {
+    Acb.AtaCommand    = ATA_CMD_TRUST_NON_DATA;
+  } else {
+    Acb.AtaCommand    = mAtaTrustCommands[IsTrustSend];
+  }
+  Acb.AtaFeatures      = SecurityProtocolId;
+  Acb.AtaSectorCount   = (UINT8) (TransferLength / 512);
+  Acb.AtaSectorNumber  = (UINT8) ((TransferLength / 512) >> 8);
+  //
+  // NOTE: ATA Spec has no explicitly definition for Security Protocol Specific layout.
+  // Here use big endian for Cylinder register.
+  //
+  Acb.AtaCylinderHigh  = (UINT8) SecurityProtocolSpecificData;
+  Acb.AtaCylinderLow   = (UINT8) (SecurityProtocolSpecificData >> 8);
+  Acb.AtaDeviceHead    = (UINT8) (BIT7 | BIT6 | BIT5 |
+                                  (DeviceData->PortMultiplier == 0xFFFF ?
+                                  0 : (DeviceData->PortMultiplier << 4)));
+
+  //
+  // Prepare for ATA pass through packet.
+  //
+  ZeroMem (&Packet, sizeof (EFI_ATA_PASS_THRU_COMMAND_PACKET));
+  if (TransferLength == 0) {
+    Packet.InTransferLength  = 0;
+    Packet.OutTransferLength = 0;
+    Packet.Protocol = EFI_ATA_PASS_THRU_PROTOCOL_ATA_NON_DATA;
+  } else if (IsTrustSend) {
+    //
+    // Check the alignment of the incoming buffer prior to invoking underlying
+    // ATA PassThru PPI.
+    //
+    if ((AtaPassThru->Mode->IoAlign > 1) &&
+        !IS_ALIGNED (Buffer, AtaPassThru->Mode->IoAlign)) {
+      NewBuffer = AllocateAlignedPages (
+                    EFI_SIZE_TO_PAGES (TransferLength),
+                    AtaPassThru->Mode->IoAlign
+                    );
+      if (NewBuffer == NULL) {
+        return EFI_OUT_OF_RESOURCES;
+      }
+
+      CopyMem (NewBuffer, Buffer, TransferLength);
+      Buffer = NewBuffer;
+    }
+    Packet.OutDataBuffer = Buffer;
+    Packet.OutTransferLength = (UINT32) TransferLength;
+    Packet.Protocol = mAtaPassThruCmdProtocols[IsTrustSend];
+  } else {
+    Packet.InDataBuffer = Buffer;
+    Packet.InTransferLength = (UINT32) TransferLength;
+    Packet.Protocol = mAtaPassThruCmdProtocols[IsTrustSend];
+  }
+  Packet.Asb      = NULL;
+  Packet.Acb      = &Acb;
+  Packet.Timeout  = Timeout;
+  Packet.Length   = EFI_ATA_PASS_THRU_LENGTH_BYTES;
+
+  Status = AtaPassThru->PassThru (
+                          AtaPassThru,
+                          DeviceData->Port,
+                          DeviceData->PortMultiplier,
+                          &Packet
+                          );
+  if (TransferLengthOut != NULL) {
+    if (!IsTrustSend) {
+      *TransferLengthOut = Packet.InTransferLength;
+    }
+  }
+  return Status;
+}
diff --git a/MdeModulePkg/Bus/Ata/AhciPei/AhciPei.c b/MdeModulePkg/Bus/Ata/AhciPei/AhciPei.c
new file mode 100644
index 0000000000..82daa2ef29
--- /dev/null
+++ b/MdeModulePkg/Bus/Ata/AhciPei/AhciPei.c
@@ -0,0 +1,304 @@
+/** @file
+  The AhciPei driver is used to manage ATA hard disk device working under AHCI
+  mode at PEI phase.
+
+  Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
+
+  This program and the accompanying materials
+  are licensed and made available under the terms and conditions
+  of the BSD License which accompanies this distribution.  The
+  full text of the license may be found at
+  http://opensource.org/licenses/bsd-license.php
+
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "AhciPei.h"
+
+EFI_PEI_PPI_DESCRIPTOR  mAhciAtaPassThruPpiListTemplate = {
+  (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
+  &gEdkiiPeiAtaPassThruPpiGuid,
+  NULL
+};
+
+EFI_PEI_PPI_DESCRIPTOR  mAhciStorageSecurityPpiListTemplate = {
+  (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
+  &gEdkiiPeiStorageSecurityCommandPpiGuid,
+  NULL
+};
+
+EFI_PEI_NOTIFY_DESCRIPTOR  mAhciEndOfPeiNotifyListTemplate = {
+  (EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
+  &gEfiEndOfPeiSignalPpiGuid,
+  AhciPeimEndOfPei
+};
+
+
+/**
+  Free the DMA resources allocated by an ATA AHCI controller.
+
+  @param[in] Private    A pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA data
+                        structure.
+
+**/
+VOID
+AhciFreeDmaResource (
+  IN PEI_AHCI_CONTROLLER_PRIVATE_DATA    *Private
+  )
+{
+  EFI_AHCI_REGISTERS    *AhciRegisters;
+
+  ASSERT (Private != NULL);
+
+  AhciRegisters = &Private->AhciRegisters;
+
+  if (AhciRegisters->AhciRFisMap != NULL) {
+    IoMmuFreeBuffer (
+       EFI_SIZE_TO_PAGES (AhciRegisters->MaxRFisSize),
+       AhciRegisters->AhciRFis,
+       AhciRegisters->AhciRFisMap
+       );
+  }
+
+  if (AhciRegisters->AhciCmdListMap != NULL) {
+    IoMmuFreeBuffer (
+       EFI_SIZE_TO_PAGES (AhciRegisters->MaxCmdListSize),
+       AhciRegisters->AhciCmdList,
+       AhciRegisters->AhciCmdListMap
+       );
+  }
+
+  if (AhciRegisters->AhciCmdTableMap != NULL) {
+    IoMmuFreeBuffer (
+      EFI_SIZE_TO_PAGES (AhciRegisters->MaxCmdTableSize),
+      AhciRegisters->AhciCmdTable,
+      AhciRegisters->AhciCmdTableMap
+      );
+  }
+
+}
+
+/**
+  One notified function to cleanup the allocated DMA buffers at EndOfPei.
+
+  @param[in] PeiServices         Pointer to PEI Services Table.
+  @param[in] NotifyDescriptor    Pointer to the descriptor for the Notification
+                                 event that caused this function to execute.
+  @param[in] Ppi                 Pointer to the PPI data associated with this function.
+
+  @retval EFI_SUCCESS    The function completes successfully
+
+**/
+EFI_STATUS
+EFIAPI
+AhciPeimEndOfPei (
+  IN EFI_PEI_SERVICES           **PeiServices,
+  IN EFI_PEI_NOTIFY_DESCRIPTOR  *NotifyDescriptor,
+  IN VOID                       *Ppi
+  )
+{
+  PEI_AHCI_CONTROLLER_PRIVATE_DATA    *Private;
+
+  Private = GET_AHCI_PEIM_HC_PRIVATE_DATA_FROM_THIS_NOTIFY (NotifyDescriptor);
+  AhciFreeDmaResource (Private);
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Entry point of the PEIM.
+
+  @param[in] FileHandle     Handle of the file being invoked.
+  @param[in] PeiServices    Describes the list of possible PEI Services.
+
+  @retval EFI_SUCCESS    PPI successfully installed.
+
+**/
+EFI_STATUS
+EFIAPI
+AtaAhciPeimEntry (
+  IN EFI_PEI_FILE_HANDLE       FileHandle,
+  IN CONST EFI_PEI_SERVICES    **PeiServices
+  )
+{
+  EFI_STATUS                            Status;
+  EFI_BOOT_MODE                         BootMode;
+  EDKII_ATA_AHCI_HOST_CONTROLLER_PPI    *AhciHcPpi;
+  UINT8                                 Controller;
+  UINTN                                 MmioBase;
+  UINTN                                 DevicePathLength;
+  EFI_DEVICE_PATH_PROTOCOL              *DevicePath;
+  UINT32                                PortBitMap;
+  PEI_AHCI_CONTROLLER_PRIVATE_DATA      *Private;
+  UINT8                                 NumberOfPorts;
+
+  DEBUG ((DEBUG_INFO, "%a: Enters.\n", __FUNCTION__));
+
+  //
+  // Get the current boot mode.
+  //
+  Status = PeiServicesGetBootMode (&BootMode);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a: Fail to get the current boot mode.\n", __FUNCTION__));
+    return Status;
+  }
+
+  //
+  // Locate the ATA AHCI host controller PPI.
+  //
+  Status = PeiServicesLocatePpi (
+             &gEdkiiPeiAtaAhciHostControllerPpiGuid,
+             0,
+             NULL,
+             (VOID **) &AhciHcPpi
+             );
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a: Failed to locate AtaAhciHostControllerPpi.\n", __FUNCTION__));
+    return EFI_UNSUPPORTED;
+  }
+
+  Controller = 0;
+  MmioBase   = 0;
+  while (TRUE) {
+    Status = AhciHcPpi->GetAhciHcMmioBar (
+                          AhciHcPpi,
+                          Controller,
+                          &MmioBase
+                          );
+    //
+    // When status is error, meant no controller is found.
+    //
+    if (EFI_ERROR (Status)) {
+      break;
+    }
+
+    Status = AhciHcPpi->GetAhciHcDevicePath (
+                          AhciHcPpi,
+                          Controller,
+                          &DevicePathLength,
+                          &DevicePath
+                          );
+    if (EFI_ERROR (Status)) {
+      DEBUG ((
+        DEBUG_ERROR, "%a: Fail to allocate get the device path for Controller %d.\n",
+        __FUNCTION__, Controller
+        ));
+      return Status;
+    }
+
+    //
+    // Check validity of the device path of the ATA AHCI controller.
+    //
+    Status = AhciCheckHcDevicePath (DevicePath, DevicePathLength);
+    if (EFI_ERROR (Status)) {
+      DEBUG ((
+        DEBUG_ERROR, "%a: The device path is invalid for Controller %d.\n",
+        __FUNCTION__, Controller
+        ));
+      Controller++;
+      continue;
+    }
+
+    if (BootMode == BOOT_ON_S3_RESUME) {
+      NumberOfPorts = AhciS3GetEumeratePorts (DevicePath, DevicePathLength, &PortBitMap);
+      if (NumberOfPorts == 0) {
+        //
+        // No ports need to be enumerated for this controller.
+        //
+        Controller++;
+        continue;
+      }
+    } else {
+      PortBitMap = MAX_UINT32;
+    }
+
+    //
+    // Memory allocation for controller private data.
+    //
+    Private = AllocateZeroPool (sizeof (PEI_AHCI_CONTROLLER_PRIVATE_DATA));
+    if (Private == NULL) {
+      DEBUG ((
+        DEBUG_ERROR, "%a: Fail to allocate private data for Controller %d.\n",
+        __FUNCTION__, Controller
+        ));
+      return EFI_OUT_OF_RESOURCES;
+    }
+
+    //
+    // Initialize controller private data.
+    //
+    Private->Signature        = AHCI_PEI_CONTROLLER_PRIVATE_DATA_SIGNATURE;
+    Private->MmioBase         = MmioBase;
+    Private->DevicePathLength = DevicePathLength;
+    Private->DevicePath       = DevicePath;
+    Private->PortBitMap       = PortBitMap;
+    InitializeListHead (&Private->DeviceList);
+
+    Status = AhciModeInitialization (Private);
+    if (EFI_ERROR (Status)) {
+      DEBUG ((
+        DEBUG_ERROR,
+        "%a: Controller initialization fail for Controller %d with Status - %r.\n",
+        __FUNCTION__,
+        Controller,
+        Status
+        ));
+      Controller++;
+      continue;
+    }
+
+    Private->AtaPassThruMode.Attributes   = EFI_ATA_PASS_THRU_ATTRIBUTES_PHYSICAL |
+                                            EFI_ATA_PASS_THRU_ATTRIBUTES_LOGICAL;
+    Private->AtaPassThruMode.IoAlign      = sizeof (UINTN);
+    Private->AtaPassThruPpi.Revision      = EDKII_PEI_ATA_PASS_THRU_PPI_REVISION;
+    Private->AtaPassThruPpi.Mode          = &Private->AtaPassThruMode;
+    Private->AtaPassThruPpi.PassThru      = AhciAtaPassThruPassThru;
+    Private->AtaPassThruPpi.GetNextPort   = AhciAtaPassThruGetNextPort;
+    Private->AtaPassThruPpi.GetNextDevice = AhciAtaPassThruGetNextDevice;
+    Private->AtaPassThruPpi.GetDevicePath = AhciAtaPassThruGetDevicePath;
+    CopyMem (
+      &Private->AtaPassThruPpiList,
+      &mAhciAtaPassThruPpiListTemplate,
+      sizeof (EFI_PEI_PPI_DESCRIPTOR)
+      );
+    Private->AtaPassThruPpiList.Ppi       = &Private->AtaPassThruPpi;
+    PeiServicesInstallPpi (&Private->AtaPassThruPpiList);
+
+    if (Private->TrustComputingDevices != 0) {
+      DEBUG ((
+        DEBUG_INFO,
+        "%a: Security Security Command PPI will be produced for Controller %d.\n",
+        __FUNCTION__, Controller
+        ));
+      Private->StorageSecurityPpi.Revision           = EDKII_STORAGE_SECURITY_PPI_REVISION;
+      Private->StorageSecurityPpi.GetNumberofDevices = AhciStorageSecurityGetDeviceNo;
+      Private->StorageSecurityPpi.GetDevicePath      = AhciStorageSecurityGetDevicePath;
+      Private->StorageSecurityPpi.ReceiveData        = AhciStorageSecurityReceiveData;
+      Private->StorageSecurityPpi.SendData           = AhciStorageSecuritySendData;
+      CopyMem (
+        &Private->StorageSecurityPpiList,
+        &mAhciStorageSecurityPpiListTemplate,
+        sizeof (EFI_PEI_PPI_DESCRIPTOR)
+        );
+      Private->StorageSecurityPpiList.Ppi            = &Private->StorageSecurityPpi;
+      PeiServicesInstallPpi (&Private->StorageSecurityPpiList);
+    }
+
+    CopyMem (
+      &Private->EndOfPeiNotifyList,
+      &mAhciEndOfPeiNotifyListTemplate,
+      sizeof (EFI_PEI_NOTIFY_DESCRIPTOR)
+      );
+    PeiServicesNotifyPpi  (&Private->EndOfPeiNotifyList);
+
+    DEBUG ((
+      DEBUG_INFO, "%a: Controller %d has been successfully initialized.\n",
+      __FUNCTION__, Controller
+      ));
+    Controller++;
+  }
+
+  return EFI_SUCCESS;
+}
diff --git a/MdeModulePkg/Bus/Ata/AhciPei/AhciPeiPassThru.c b/MdeModulePkg/Bus/Ata/AhciPei/AhciPeiPassThru.c
new file mode 100644
index 0000000000..394d6d98ad
--- /dev/null
+++ b/MdeModulePkg/Bus/Ata/AhciPei/AhciPeiPassThru.c
@@ -0,0 +1,521 @@
+/** @file
+  The AhciPei driver is used to manage ATA hard disk device working under AHCI
+  mode at PEI phase.
+
+  Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
+
+  This program and the accompanying materials
+  are licensed and made available under the terms and conditions
+  of the BSD License which accompanies this distribution.  The
+  full text of the license may be found at
+  http://opensource.org/licenses/bsd-license.php
+
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "AhciPei.h"
+
+/**
+  Traverse the attached ATA devices list to find out the device with given Port
+  and PortMultiplierPort.
+
+  @param[in] Private               A pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA
+                                   instance.
+  @param[in] Port                  The port number of the ATA device.
+  @param[in] PortMultiplierPort    The port multiplier port number of the ATA device.
+
+  @retval    The pointer to the PEI_AHCI_ATA_DEVICE_DATA structure of the device
+             info to access.
+
+**/
+PEI_AHCI_ATA_DEVICE_DATA *
+SearchDeviceByPort (
+  IN PEI_AHCI_CONTROLLER_PRIVATE_DATA    *Private,
+  IN UINT16                              Port,
+  IN UINT16                              PortMultiplierPort
+  )
+{
+  PEI_AHCI_ATA_DEVICE_DATA    *DeviceData;
+  LIST_ENTRY                  *Node;
+
+  Node = GetFirstNode (&Private->DeviceList);
+  while (!IsNull (&Private->DeviceList, Node)) {
+    DeviceData = AHCI_PEI_ATA_DEVICE_INFO_FROM_THIS (Node);
+
+    if ((DeviceData->Port == Port) &&
+        (DeviceData->PortMultiplier == PortMultiplierPort)) {
+      return DeviceData;
+    }
+
+    Node = GetNextNode (&Private->DeviceList, Node);
+  }
+
+  return NULL;
+}
+
+/**
+  Sends an ATA command to an ATA device that is attached to the ATA controller.
+
+  @param[in]     Private               Pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA.
+  @param[in]     Port                  The port number of the ATA device.
+  @param[in]     PortMultiplierPort    The port multiplier port number of the ATA
+                                       device.
+  @param[in]     FisIndex              The index of the FIS.
+  @param[in,out] Packet                A pointer to the ATA command to send to
+                                       the ATA device specified by Port and
+                                       PortMultiplierPort.
+
+  @retval EFI_SUCCESS              The ATA command was sent by the host. For
+                                   bi-directional commands, InTransferLength bytes
+                                   were transferred from InDataBuffer. For write
+                                   and bi-directional commands, OutTransferLength
+                                   bytes were transferred by OutDataBuffer.
+  @retval EFI_BAD_BUFFER_SIZE      The ATA command was not executed. The number
+                                   of bytes that could be transferred is returned
+                                   in InTransferLength. For write and bi-directional
+                                   commands, OutTransferLength bytes were transferred
+                                   by OutDataBuffer.
+  @retval EFI_NOT_READY            The ATA command could not be sent because there
+                                   are too many ATA commands already queued. The
+                                   caller may retry again later.
+  @retval EFI_DEVICE_ERROR         A device error occurred while attempting to
+                                   send the ATA command.
+  @retval EFI_INVALID_PARAMETER    Port, PortMultiplierPort, or the contents of
+                                   Acb are invalid. The ATA command was not sent,
+                                   so no additional status information is available.
+
+**/
+EFI_STATUS
+AhciPassThruExecute (
+  IN     PEI_AHCI_CONTROLLER_PRIVATE_DATA    *Private,
+  IN     UINT16                              Port,
+  IN     UINT16                              PortMultiplierPort,
+  IN     UINT8                               FisIndex,
+  IN OUT EFI_ATA_PASS_THRU_COMMAND_PACKET    *Packet
+  )
+{
+  EFI_STATUS    Status;
+
+  switch (Packet->Protocol) {
+    case EFI_ATA_PASS_THRU_PROTOCOL_ATA_NON_DATA:
+      Status = AhciNonDataTransfer (
+                 Private,
+                 (UINT8) Port,
+                 (UINT8) PortMultiplierPort,
+                 FisIndex,
+                 Packet->Acb,
+                 Packet->Asb,
+                 Packet->Timeout
+                 );
+      break;
+    case EFI_ATA_PASS_THRU_PROTOCOL_PIO_DATA_IN:
+      Status = AhciPioTransfer (
+                 Private,
+                 (UINT8) Port,
+                 (UINT8) PortMultiplierPort,
+                 FisIndex,
+                 TRUE,
+                 Packet->Acb,
+                 Packet->Asb,
+                 Packet->InDataBuffer,
+                 Packet->InTransferLength,
+                 Packet->Timeout
+                 );
+      break;
+    case EFI_ATA_PASS_THRU_PROTOCOL_PIO_DATA_OUT:
+      Status = AhciPioTransfer (
+                 Private,
+                 (UINT8) Port,
+                 (UINT8) PortMultiplierPort,
+                 FisIndex,
+                 FALSE,
+                 Packet->Acb,
+                 Packet->Asb,
+                 Packet->OutDataBuffer,
+                 Packet->OutTransferLength,
+                 Packet->Timeout
+                 );
+      break;
+    default:
+      return EFI_UNSUPPORTED;
+  }
+
+  return Status;
+}
+
+/**
+  Sends an ATA command to an ATA device that is attached to the ATA controller.
+
+  @param[in]     This                  The PPI instance pointer.
+  @param[in]     Port                  The port number of the ATA device to send
+                                       the command.
+  @param[in]     PortMultiplierPort    The port multiplier port number of the ATA
+                                       device to send the command.
+                                       If there is no port multiplier, then specify
+                                       0xFFFF.
+  @param[in,out] Packet                A pointer to the ATA command to send to
+                                       the ATA device specified by Port and
+                                       PortMultiplierPort.
+
+  @retval EFI_SUCCESS              The ATA command was sent by the host. For
+                                   bi-directional commands, InTransferLength bytes
+                                   were transferred from InDataBuffer. For write
+                                   and bi-directional commands, OutTransferLength
+                                   bytes were transferred by OutDataBuffer.
+  @retval EFI_NOT_FOUND            The specified ATA device is not found.
+  @retval EFI_INVALID_PARAMETER    The contents of Acb are invalid. The ATA command
+                                   was not sent, so no additional status information
+                                   is available.
+  @retval EFI_BAD_BUFFER_SIZE      The ATA command was not executed. The number
+                                   of bytes that could be transferred is returned
+                                   in InTransferLength. For write and bi-directional
+                                   commands, OutTransferLength bytes were transferred
+                                   by OutDataBuffer.
+  @retval EFI_NOT_READY            The ATA command could not be sent because there
+                                   are too many ATA commands already queued. The
+                                   caller may retry again later.
+  @retval EFI_DEVICE_ERROR         A device error occurred while attempting to
+                                   send the ATA command.
+
+**/
+EFI_STATUS
+EFIAPI
+AhciAtaPassThruPassThru (
+  IN     EDKII_PEI_ATA_PASS_THRU_PPI         *This,
+  IN     UINT16                              Port,
+  IN     UINT16                              PortMultiplierPort,
+  IN OUT EFI_ATA_PASS_THRU_COMMAND_PACKET    *Packet
+  )
+{
+  UINT32                              IoAlign;
+  PEI_AHCI_CONTROLLER_PRIVATE_DATA    *Private;
+  PEI_AHCI_ATA_DEVICE_DATA            *DeviceData;
+  UINT32                              MaxSectorCount;
+  UINT32                              BlockSize;
+
+  if (This == NULL || Packet == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  IoAlign = This->Mode->IoAlign;
+  if ((IoAlign > 1) && !IS_ALIGNED (Packet->InDataBuffer, IoAlign)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if ((IoAlign > 1) && !IS_ALIGNED (Packet->OutDataBuffer, IoAlign)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if ((IoAlign > 1) && !IS_ALIGNED (Packet->Asb, IoAlign)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Private = GET_AHCI_PEIM_HC_PRIVATE_DATA_FROM_THIS_PASS_THRU (This);
+  DeviceData = SearchDeviceByPort (Private, Port, PortMultiplierPort);
+  if (DeviceData == NULL) {
+    return EFI_NOT_FOUND;
+  }
+
+  MaxSectorCount = mMaxTransferBlockNumber[DeviceData->Lba48Bit];
+  BlockSize      = DeviceData->Media.BlockSize;
+
+  //
+  // Convert the transfer length from sector count to byte.
+  //
+  if (((Packet->Length & EFI_ATA_PASS_THRU_LENGTH_BYTES) == 0) &&
+       (Packet->InTransferLength != 0)) {
+    Packet->InTransferLength = Packet->InTransferLength * BlockSize;
+  }
+
+  //
+  // Convert the transfer length from sector count to byte.
+  //
+  if (((Packet->Length & EFI_ATA_PASS_THRU_LENGTH_BYTES) == 0) &&
+       (Packet->OutTransferLength != 0)) {
+    Packet->OutTransferLength = Packet->OutTransferLength * BlockSize;
+  }
+
+  //
+  // If the data buffer described by InDataBuffer/OutDataBuffer and
+  // InTransferLength/OutTransferLength is too big to be transferred in a single
+  // command, then no data is transferred and EFI_BAD_BUFFER_SIZE is returned.
+  //
+  if (((Packet->InTransferLength != 0) && (Packet->InTransferLength > MaxSectorCount * BlockSize)) ||
+      ((Packet->OutTransferLength != 0) && (Packet->OutTransferLength > MaxSectorCount * BlockSize))) {
+    return EFI_BAD_BUFFER_SIZE;
+  }
+
+  return AhciPassThruExecute (
+           Private,
+           DeviceData->Port,
+           DeviceData->PortMultiplier,
+           DeviceData->FisIndex,
+           Packet
+           );
+}
+
+/**
+  Used to retrieve the list of legal port numbers for ATA devices on an ATA controller.
+  These can either be the list of ports where ATA devices are actually present or the
+  list of legal port numbers for the ATA controller. Regardless, the caller of this
+  function must probe the port number returned to see if an ATA device is actually
+  present at that location on the ATA controller.
+
+  The GetNextPort() function retrieves the port number on an ATA controller. If on
+  input Port is 0xFFFF, then the port number of the first port on the ATA controller
+  is returned in Port and EFI_SUCCESS is returned.
+
+  If Port is a port number that was returned on a previous call to GetNextPort(),
+  then the port number of the next port on the ATA controller is returned in Port,
+  and EFI_SUCCESS is returned. If Port is not 0xFFFF and Port was not returned on
+  a previous call to GetNextPort(), then EFI_INVALID_PARAMETER is returned.
+
+  If Port is the port number of the last port on the ATA controller, then EFI_NOT_FOUND
+  is returned.
+
+  @param[in]     This    The PPI instance pointer.
+  @param[in,out] Port    On input, a pointer to the port number on the ATA controller.
+                         On output, a pointer to the next port number on the ATA
+                         controller. An input value of 0xFFFF retrieves the first
+                         port number on the ATA controller.
+
+  @retval EFI_SUCCESS              The next port number on the ATA controller was
+                                   returned in Port.
+  @retval EFI_NOT_FOUND            There are no more ports on this ATA controller.
+  @retval EFI_INVALID_PARAMETER    Port is not 0xFFFF and Port was not returned
+                                   on a previous call to GetNextPort().
+
+**/
+EFI_STATUS
+EFIAPI
+AhciAtaPassThruGetNextPort (
+  IN     EDKII_PEI_ATA_PASS_THRU_PPI    *This,
+  IN OUT UINT16                         *Port
+  )
+{
+  PEI_AHCI_CONTROLLER_PRIVATE_DATA    *Private;
+  PEI_AHCI_ATA_DEVICE_DATA            *DeviceData;
+  LIST_ENTRY                          *Node;
+
+  if (This == NULL || Port == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Private = GET_AHCI_PEIM_HC_PRIVATE_DATA_FROM_THIS_PASS_THRU (This);
+
+  if (*Port == 0xFFFF) {
+    //
+    // If the Port is all 0xFF's, start to traverse the device list from the
+    // beginning.
+    //
+    Node = GetFirstNode (&Private->DeviceList);
+    if (!IsNull (&Private->DeviceList, Node)) {
+      DeviceData = AHCI_PEI_ATA_DEVICE_INFO_FROM_THIS (Node);
+
+      *Port = DeviceData->Port;
+      goto Exit;
+    }
+
+    return EFI_NOT_FOUND;
+  } else if (*Port == Private->PreviousPort) {
+    Node = GetFirstNode (&Private->DeviceList);
+
+    while (!IsNull (&Private->DeviceList, Node)) {
+      DeviceData = AHCI_PEI_ATA_DEVICE_INFO_FROM_THIS (Node);
+
+      if (DeviceData->Port > *Port){
+        *Port = DeviceData->Port;
+        goto Exit;
+      }
+
+      Node = GetNextNode (&Private->DeviceList, Node);
+    }
+
+    return EFI_NOT_FOUND;
+  } else {
+    //
+    // Port is not equal to all 0xFF's and not equal to previous return value.
+    //
+    return EFI_INVALID_PARAMETER;
+  }
+
+Exit:
+  //
+  // Update the PreviousPort.
+  //
+  Private->PreviousPort = *Port;
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Used to retrieve the list of legal port multiplier port numbers for ATA devices
+  on a port of an ATA controller. These can either be the list of port multiplier
+  ports where ATA devices are actually present on port or the list of legal port
+  multiplier ports on that port. Regardless, the caller of this function must probe
+  the port number and port multiplier port number returned to see if an ATA device
+  is actually present.
+
+  The GetNextDevice() function retrieves the port multiplier port number of an ATA
+  device present on a port of an ATA controller.
+
+  If PortMultiplierPort points to a port multiplier port number value that was
+  returned on a previous call to GetNextDevice(), then the port multiplier port
+  number of the next ATA device on the port of the ATA controller is returned in
+  PortMultiplierPort, and EFI_SUCCESS is returned.
+
+  If PortMultiplierPort points to 0xFFFF, then the port multiplier port number
+  of the first ATA device on port of the ATA controller is returned in PortMultiplierPort
+  and EFI_SUCCESS is returned.
+
+  If PortMultiplierPort is not 0xFFFF and the value pointed to by PortMultiplierPort
+  was not returned on a previous call to GetNextDevice(), then EFI_INVALID_PARAMETER
+  is returned.
+
+  If PortMultiplierPort is the port multiplier port number of the last ATA device
+  on the port of the ATA controller, then EFI_NOT_FOUND is returned.
+
+  @param[in]     This                  The PPI instance pointer.
+  @param[in]     Port                  The port number present on the ATA controller.
+  @param[in,out] PortMultiplierPort    On input, a pointer to the port multiplier
+                                       port number of an ATA device present on the
+                                       ATA controller. If on input a PortMultiplierPort
+                                       of 0xFFFF is specified, then the port multiplier
+                                       port number of the first ATA device is returned.
+                                       On output, a pointer to the port multiplier port
+                                       number of the next ATA device present on an ATA
+                                       controller.
+
+  @retval EFI_SUCCESS              The port multiplier port number of the next ATA
+                                   device on the port of the ATA controller was
+                                   returned in PortMultiplierPort.
+  @retval EFI_NOT_FOUND            There are no more ATA devices on this port of
+                                   the ATA controller.
+  @retval EFI_INVALID_PARAMETER    PortMultiplierPort is not 0xFFFF, and PortMultiplierPort
+                                   was not returned on a previous call to GetNextDevice().
+
+**/
+EFI_STATUS
+EFIAPI
+AhciAtaPassThruGetNextDevice (
+  IN     EDKII_PEI_ATA_PASS_THRU_PPI    *This,
+  IN     UINT16                         Port,
+  IN OUT UINT16                         *PortMultiplierPort
+  )
+{
+  PEI_AHCI_CONTROLLER_PRIVATE_DATA    *Private;
+  PEI_AHCI_ATA_DEVICE_DATA            *DeviceData;
+  LIST_ENTRY                          *Node;
+
+  if (This == NULL || PortMultiplierPort == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Private = GET_AHCI_PEIM_HC_PRIVATE_DATA_FROM_THIS_PASS_THRU (This);
+
+  if (Private->PreviousPortMultiplier == 0xFFFF) {
+    //
+    // If a device is directly attached on a port, previous call to this
+    // function will return the value 0xFFFF for PortMultiplierPort. In
+    // this case, there should be no more device on the port multiplier.
+    //
+    Private->PreviousPortMultiplier = 0;
+    return EFI_NOT_FOUND;
+  }
+
+  if (*PortMultiplierPort == Private->PreviousPortMultiplier) {
+    Node = GetFirstNode (&Private->DeviceList);
+
+    while (!IsNull (&Private->DeviceList, Node)) {
+      DeviceData = AHCI_PEI_ATA_DEVICE_INFO_FROM_THIS (Node);
+
+      if ((DeviceData->Port == Port) &&
+          (DeviceData->PortMultiplier > *PortMultiplierPort)){
+        *PortMultiplierPort = DeviceData->PortMultiplier;
+        goto Exit;
+      }
+
+      Node = GetNextNode (&Private->DeviceList, Node);
+    }
+
+    return EFI_NOT_FOUND;
+  } else if (*PortMultiplierPort == 0xFFFF) {
+    //
+    // If the PortMultiplierPort is all 0xFF's, start to traverse the device list
+    // from the beginning.
+    //
+    Node = GetFirstNode (&Private->DeviceList);
+
+    while (!IsNull (&Private->DeviceList, Node)) {
+      DeviceData = AHCI_PEI_ATA_DEVICE_INFO_FROM_THIS (Node);
+
+      if (DeviceData->Port == Port){
+        *PortMultiplierPort = DeviceData->PortMultiplier;
+        goto Exit;
+      }
+
+      Node = GetNextNode (&Private->DeviceList, Node);
+    }
+
+    return EFI_NOT_FOUND;
+  } else {
+    //
+    // PortMultiplierPort is not equal to all 0xFF's and not equal to previous
+    // return value.
+    //
+    return EFI_INVALID_PARAMETER;
+  }
+
+Exit:
+  //
+  // Update the PreviousPortMultiplier.
+  //
+  Private->PreviousPortMultiplier = *PortMultiplierPort;
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Gets the device path information of the underlying ATA host controller.
+
+  @param[in]  This                The PPI instance pointer.
+  @param[out] DevicePathLength    The length of the device path in bytes specified
+                                  by DevicePath.
+  @param[out] DevicePath          The device path of the underlying ATA host controller.
+                                  This field re-uses EFI Device Path Protocol as
+                                  defined by Section 10.2 EFI Device Path Protocol
+                                  of UEFI 2.7 Specification.
+
+  @retval EFI_SUCCESS              The device path of the ATA host controller has
+                                   been successfully returned.
+  @retval EFI_INVALID_PARAMETER    DevicePathLength or DevicePath is NULL.
+  @retval EFI_OUT_OF_RESOURCES     Not enough resource to return the device path.
+
+**/
+EFI_STATUS
+EFIAPI
+AhciAtaPassThruGetDevicePath (
+  IN  EDKII_PEI_ATA_PASS_THRU_PPI    *This,
+  OUT UINTN                          *DevicePathLength,
+  OUT EFI_DEVICE_PATH_PROTOCOL       **DevicePath
+  )
+{
+  PEI_AHCI_CONTROLLER_PRIVATE_DATA    *Private;
+
+  if (This == NULL || DevicePathLength == NULL || DevicePath == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Private = GET_AHCI_PEIM_HC_PRIVATE_DATA_FROM_THIS_PASS_THRU (This);
+
+  *DevicePathLength = Private->DevicePathLength;
+  *DevicePath       = AllocateCopyPool (Private->DevicePathLength, Private->DevicePath);
+  if (*DevicePath == NULL) {
+    *DevicePathLength = 0;
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  return EFI_SUCCESS;
+}
diff --git a/MdeModulePkg/Bus/Ata/AhciPei/AhciPeiS3.c b/MdeModulePkg/Bus/Ata/AhciPei/AhciPeiS3.c
new file mode 100644
index 0000000000..3f77b979c1
--- /dev/null
+++ b/MdeModulePkg/Bus/Ata/AhciPei/AhciPeiS3.c
@@ -0,0 +1,139 @@
+/** @file
+  The AhciPei driver is used to manage ATA hard disk device working under AHCI
+  mode at PEI phase.
+
+  Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
+
+  This program and the accompanying materials
+  are licensed and made available under the terms and conditions
+  of the BSD License which accompanies this distribution.  The
+  full text of the license may be found at
+  http://opensource.org/licenses/bsd-license.php
+
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "AhciPei.h"
+
+#include <Guid/S3StorageDeviceInitList.h>
+
+#include <Library/LockBoxLib.h>
+
+/**
+  Collect the ports that need to be enumerated on a controller for S3 phase.
+
+  @param[in]  HcDevicePath          Device path of the controller.
+  @param[in]  HcDevicePathLength    Length of the device path specified by
+                                    HcDevicePath.
+  @param[out] PortBitMap            Bitmap that indicates the ports that need
+                                    to be enumerated on the controller.
+
+  @retval    The number of ports that need to be enumerated.
+
+**/
+UINT8
+AhciS3GetEumeratePorts (
+  IN  EFI_DEVICE_PATH_PROTOCOL    *HcDevicePath,
+  IN  UINTN                       HcDevicePathLength,
+  OUT UINT32                      *PortBitMap
+  )
+{
+  EFI_STATUS                  Status;
+  UINT8                       DummyData;
+  UINTN                       S3InitDevicesLength;
+  EFI_DEVICE_PATH_PROTOCOL    *S3InitDevices;
+  EFI_DEVICE_PATH_PROTOCOL    *DevicePathInst;
+  UINTN                       DevicePathInstLength;
+  BOOLEAN                     EntireEnd;
+  SATA_DEVICE_PATH            *SataDeviceNode;
+
+  *PortBitMap = 0;
+
+  //
+  // From the LockBox, get the list of device paths for devices need to be
+  // initialized in S3.
+  //
+  S3InitDevices       = NULL;
+  S3InitDevicesLength = sizeof (DummyData);
+  EntireEnd           = FALSE;
+  Status = RestoreLockBox (&gS3StorageDeviceInitListGuid, &DummyData, &S3InitDevicesLength);
+  if (Status != EFI_BUFFER_TOO_SMALL) {
+    return 0;
+  } else {
+    S3InitDevices = AllocatePool (S3InitDevicesLength);
+    if (S3InitDevices == NULL) {
+      return 0;
+    }
+
+    Status = RestoreLockBox (&gS3StorageDeviceInitListGuid, S3InitDevices, &S3InitDevicesLength);
+    if (EFI_ERROR (Status)) {
+      return 0;
+    }
+  }
+
+  if (S3InitDevices == NULL) {
+    return 0;
+  }
+
+  //
+  // Only enumerate the ports that exist in the device list.
+  //
+  do {
+    //
+    // Fetch the size of current device path instance.
+    //
+    Status = GetDevicePathInstanceSize (
+               S3InitDevices,
+               &DevicePathInstLength,
+               &EntireEnd
+               );
+    if (EFI_ERROR (Status)) {
+      break;
+    }
+
+    DevicePathInst = S3InitDevices;
+    S3InitDevices  = (EFI_DEVICE_PATH_PROTOCOL *)((UINTN) S3InitDevices + DevicePathInstLength);
+
+    if (HcDevicePathLength >= DevicePathInstLength) {
+      continue;
+    }
+
+    //
+    // Compare the device paths to determine if the device is managed by this
+    // controller.
+    //
+    if (CompareMem (
+          DevicePathInst,
+          HcDevicePath,
+          HcDevicePathLength - sizeof (EFI_DEVICE_PATH_PROTOCOL)
+          ) == 0) {
+      //
+      // Get the port number.
+      //
+      while (DevicePathInst->Type != END_DEVICE_PATH_TYPE) {
+        if ((DevicePathInst->Type == MESSAGING_DEVICE_PATH) &&
+            (DevicePathInst->SubType == MSG_SATA_DP)) {
+          SataDeviceNode = (SATA_DEVICE_PATH *) DevicePathInst;
+          //
+          // For now, the driver only support upto AHCI_MAX_PORTS ports and
+          // devices directly connected to a HBA.
+          //
+          if ((SataDeviceNode->HBAPortNumber >= AHCI_MAX_PORTS) ||
+              (SataDeviceNode->PortMultiplierPortNumber != 0xFFFF)) {
+            break;
+          }
+          *PortBitMap |= (UINT32)BIT0 << SataDeviceNode->HBAPortNumber;
+          break;
+        }
+        DevicePathInst = NextDevicePathNode (DevicePathInst);
+      }
+    }
+  } while (!EntireEnd);
+
+  //
+  // Return the number of ports need to be enumerated on this controller.
+  //
+  return AhciGetNumberOfPortsFromMap (*PortBitMap);
+}
diff --git a/MdeModulePkg/Bus/Ata/AhciPei/AhciPeiStorageSecurity.c b/MdeModulePkg/Bus/Ata/AhciPei/AhciPeiStorageSecurity.c
new file mode 100644
index 0000000000..49c384cadd
--- /dev/null
+++ b/MdeModulePkg/Bus/Ata/AhciPei/AhciPeiStorageSecurity.c
@@ -0,0 +1,391 @@
+/** @file
+  The AhciPei driver is used to manage ATA hard disk device working under AHCI
+  mode at PEI phase.
+
+  Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
+
+  This program and the accompanying materials
+  are licensed and made available under the terms and conditions
+  of the BSD License which accompanies this distribution.  The
+  full text of the license may be found at
+  http://opensource.org/licenses/bsd-license.php
+
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "AhciPei.h"
+
+/**
+  Traverse the attached ATA devices list to find out the device with given trust
+  computing device index.
+
+  @param[in] Private                      A pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA
+                                          instance.
+  @param[in] TrustComputingDeviceIndex    The trust computing device index.
+
+  @retval    The pointer to the PEI_AHCI_ATA_DEVICE_DATA structure of the device
+             info to access.
+
+**/
+PEI_AHCI_ATA_DEVICE_DATA *
+SearchTrustComputingDeviceByIndex (
+  IN PEI_AHCI_CONTROLLER_PRIVATE_DATA    *Private,
+  IN UINTN                               TrustComputingDeviceIndex
+  )
+{
+  PEI_AHCI_ATA_DEVICE_DATA    *DeviceData;
+  LIST_ENTRY                  *Node;
+
+  Node = GetFirstNode (&Private->DeviceList);
+  while (!IsNull (&Private->DeviceList, Node)) {
+    DeviceData = AHCI_PEI_ATA_DEVICE_INFO_FROM_THIS (Node);
+
+    if (DeviceData->TrustComputingDeviceIndex == TrustComputingDeviceIndex) {
+      return DeviceData;
+    }
+
+    Node = GetNextNode (&Private->DeviceList, Node);
+  }
+
+  return NULL;
+}
+
+/**
+  Gets the count of storage security devices that one specific driver detects.
+
+  @param[in]  This               The PPI instance pointer.
+  @param[out] NumberofDevices    The number of storage security devices discovered.
+
+  @retval EFI_SUCCESS              The operation performed successfully.
+  @retval EFI_INVALID_PARAMETER    The parameters are invalid.
+
+**/
+EFI_STATUS
+EFIAPI
+AhciStorageSecurityGetDeviceNo (
+  IN  EDKII_PEI_STORAGE_SECURITY_CMD_PPI    *This,
+  OUT UINTN                                 *NumberofDevices
+  )
+{
+  PEI_AHCI_CONTROLLER_PRIVATE_DATA    *Private;
+
+  if (This == NULL || NumberofDevices == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Private = GET_AHCI_PEIM_HC_PRIVATE_DATA_FROM_THIS_STROAGE_SECURITY (This);
+  *NumberofDevices = Private->TrustComputingDevices;
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Gets the device path of a specific storage security device.
+
+  @param[in]  This                 The PPI instance pointer.
+  @param[in]  DeviceIndex          Specifies the storage security device to which
+                                   the function wants to talk. Because the driver
+                                   that implements Storage Security Command PPIs
+                                   will manage multiple storage devices, the PPIs
+                                   that want to talk to a single device must specify
+                                   the device index that was assigned during the
+                                   enumeration process. This index is a number from
+                                   one to NumberofDevices.
+  @param[out] DevicePathLength     The length of the device path in bytes specified
+                                   by DevicePath.
+  @param[out] DevicePath           The device path of storage security device.
+                                   This field re-uses EFI Device Path Protocol as
+                                   defined by Section 10.2 EFI Device Path Protocol
+                                   of UEFI 2.7 Specification.
+
+  @retval EFI_SUCCESS              The operation succeeds.
+  @retval EFI_INVALID_PARAMETER    DevicePathLength or DevicePath is NULL.
+  @retval EFI_NOT_FOUND            The specified storage security device not found.
+  @retval EFI_OUT_OF_RESOURCES     The operation fails due to lack of resources.
+
+**/
+EFI_STATUS
+EFIAPI
+AhciStorageSecurityGetDevicePath (
+  IN  EDKII_PEI_STORAGE_SECURITY_CMD_PPI    *This,
+  IN  UINTN                                 DeviceIndex,
+  OUT UINTN                                 *DevicePathLength,
+  OUT EFI_DEVICE_PATH_PROTOCOL              **DevicePath
+  )
+{
+  PEI_AHCI_CONTROLLER_PRIVATE_DATA    *Private;
+  PEI_AHCI_ATA_DEVICE_DATA            *DeviceData;
+  EFI_STATUS                          Status;
+
+  if (This == NULL || DevicePathLength == NULL || DevicePath == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Private = GET_AHCI_PEIM_HC_PRIVATE_DATA_FROM_THIS_STROAGE_SECURITY (This);
+  if ((DeviceIndex == 0) || (DeviceIndex > Private->TrustComputingDevices)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  DeviceData = SearchTrustComputingDeviceByIndex (Private, DeviceIndex);
+  if (DeviceData == NULL) {
+    return EFI_NOT_FOUND;
+  }
+
+  Status = AhciBuildDevicePath (
+             Private,
+             DeviceData->Port,
+             DeviceData->PortMultiplier,
+             DevicePathLength,
+             DevicePath
+             );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Send a security protocol command to a device that receives data and/or the result
+  of one or more commands sent by SendData.
+
+  The ReceiveData function sends a security protocol command to the given DeviceIndex.
+  The security protocol command sent is defined by SecurityProtocolId and contains
+  the security protocol specific data SecurityProtocolSpecificData. The function
+  returns the data from the security protocol command in PayloadBuffer.
+
+  For devices supporting the SCSI command set, the security protocol command is sent
+  using the SECURITY PROTOCOL IN command defined in SPC-4.
+
+  For devices supporting the ATA command set, the security protocol command is sent
+  using one of the TRUSTED RECEIVE commands defined in ATA8-ACS if PayloadBufferSize
+  is non-zero.
+
+  If the PayloadBufferSize is zero, the security protocol command is sent using the
+  Trusted Non-Data command defined in ATA8-ACS.
+
+  If PayloadBufferSize is too small to store the available data from the security
+  protocol command, the function shall copy PayloadBufferSize bytes into the
+  PayloadBuffer and return EFI_WARN_BUFFER_TOO_SMALL.
+
+  If PayloadBuffer or PayloadTransferSize is NULL and PayloadBufferSize is non-zero,
+  the function shall return EFI_INVALID_PARAMETER.
+
+  If the given DeviceIndex does not support security protocol commands, the function
+  shall return EFI_UNSUPPORTED.
+
+  If the security protocol fails to complete within the Timeout period, the function
+  shall return EFI_TIMEOUT.
+
+  If the security protocol command completes without an error, the function shall
+  return EFI_SUCCESS. If the security protocol command completes with an error, the
+  function shall return EFI_DEVICE_ERROR.
+
+  @param[in]  This             The PPI instance pointer.
+  @param[in]  DeviceIndex      Specifies the storage security device to which the
+                               function wants to talk. Because the driver that
+                               implements Storage Security Command PPIs will manage
+                               multiple storage devices, the PPIs that want to talk
+                               to a single device must specify the device index
+                               that was assigned during the enumeration process.
+                               This index is a number from one to NumberofDevices.
+  @param[in]  Timeout          The timeout, in 100ns units, to use for the execution
+                               of the security protocol command. A Timeout value
+                               of 0 means that this function will wait indefinitely
+                               for the security protocol command to execute. If
+                               Timeout is greater than zero, then this function
+                               will return EFI_TIMEOUT if the time required to
+                               execute the receive data command is greater than
+                               Timeout.
+  @param[in]  SecurityProtocolId
+                               The value of the "Security Protocol" parameter of
+                               the security protocol command to be sent.
+  @param[in]  SecurityProtocolSpecificData
+                               The value of the "Security Protocol Specific"
+                               parameter of the security protocol command to be
+                               sent.
+  @param[in]  PayloadBufferSize
+                               Size in bytes of the payload data buffer.
+  @param[out] PayloadBuffer    A pointer to a destination buffer to store the
+                               security protocol command specific payload data
+                               for the security protocol command. The caller is
+                               responsible for having either implicit or explicit
+                               ownership of the buffer.
+  @param[out] PayloadTransferSize
+                               A pointer to a buffer to store the size in bytes
+                               of the data written to the payload data buffer.
+
+  @retval EFI_SUCCESS                  The security protocol command completed
+                                       successfully.
+  @retval EFI_WARN_BUFFER_TOO_SMALL    The PayloadBufferSize was too small to
+                                       store the available data from the device.
+                                       The PayloadBuffer contains the truncated
+                                       data.
+  @retval EFI_UNSUPPORTED              The given DeviceIndex does not support
+                                       security protocol commands.
+  @retval EFI_DEVICE_ERROR             The security protocol command completed
+                                       with an error.
+  @retval EFI_INVALID_PARAMETER        The PayloadBuffer or PayloadTransferSize
+                                       is NULL and PayloadBufferSize is non-zero.
+  @retval EFI_TIMEOUT                  A timeout occurred while waiting for the
+                                       security protocol command to execute.
+
+**/
+EFI_STATUS
+EFIAPI
+AhciStorageSecurityReceiveData (
+  IN  EDKII_PEI_STORAGE_SECURITY_CMD_PPI    *This,
+  IN  UINTN                                 DeviceIndex,
+  IN  UINT64                                Timeout,
+  IN  UINT8                                 SecurityProtocolId,
+  IN  UINT16                                SecurityProtocolSpecificData,
+  IN  UINTN                                 PayloadBufferSize,
+  OUT VOID                                  *PayloadBuffer,
+  OUT UINTN                                 *PayloadTransferSize
+  )
+{
+  PEI_AHCI_CONTROLLER_PRIVATE_DATA    *Private;
+  PEI_AHCI_ATA_DEVICE_DATA            *DeviceData;
+
+  if ((PayloadBuffer == NULL) || (PayloadTransferSize == NULL) || (PayloadBufferSize == 0)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Private = GET_AHCI_PEIM_HC_PRIVATE_DATA_FROM_THIS_STROAGE_SECURITY (This);
+  if ((DeviceIndex == 0) || (DeviceIndex > Private->TrustComputingDevices)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  DeviceData = SearchTrustComputingDeviceByIndex (Private, DeviceIndex);
+  if (DeviceData == NULL) {
+    return EFI_NOT_FOUND;
+  }
+
+  ASSERT ((DeviceData->IdentifyData->trusted_computing_support & BIT0) != 0);
+  if ((DeviceData->IdentifyData->trusted_computing_support & BIT0) == 0) {
+    return EFI_UNSUPPORTED;
+  }
+
+  return TrustTransferAtaDevice (
+           DeviceData,
+           PayloadBuffer,
+           SecurityProtocolId,
+           SecurityProtocolSpecificData,
+           PayloadBufferSize,
+           FALSE,
+           Timeout,
+           PayloadTransferSize
+           );
+}
+
+/**
+  Send a security protocol command to a device.
+
+  The SendData function sends a security protocol command containing the payload
+  PayloadBuffer to the given DeviceIndex. The security protocol command sent is
+  defined by SecurityProtocolId and contains the security protocol specific data
+  SecurityProtocolSpecificData. If the underlying protocol command requires a
+  specific padding for the command payload, the SendData function shall add padding
+  bytes to the command payload to satisfy the padding requirements.
+
+  For devices supporting the SCSI command set, the security protocol command is
+  sent using the SECURITY PROTOCOL OUT command defined in SPC-4.
+
+  For devices supporting the ATA command set, the security protocol command is
+  sent using one of the TRUSTED SEND commands defined in ATA8-ACS if PayloadBufferSize
+  is non-zero. If the PayloadBufferSize is zero, the security protocol command
+  is sent using the Trusted Non-Data command defined in ATA8-ACS.
+
+  If PayloadBuffer is NULL and PayloadBufferSize is non-zero, the function shall
+  return EFI_INVALID_PARAMETER.
+
+  If the given DeviceIndex does not support security protocol commands, the function
+  shall return EFI_UNSUPPORTED.
+
+  If the security protocol fails to complete within the Timeout period, the function
+  shall return EFI_TIMEOUT.
+
+  If the security protocol command completes without an error, the function shall
+  return EFI_SUCCESS. If the security protocol command completes with an error,
+  the functio shall return EFI_DEVICE_ERROR.
+
+  @param[in] This              The PPI instance pointer.
+  @param[in] DeviceIndex       The ID of the device.
+  @param[in] Timeout           The timeout, in 100ns units, to use for the execution
+                               of the security protocol command. A Timeout value
+                               of 0 means that this function will wait indefinitely
+                               for the security protocol command to execute. If
+                               Timeout is greater than zero, then this function
+                               will return EFI_TIMEOUT if the time required to
+                               execute the receive data command is greater than
+                               Timeout.
+  @param[in] SecurityProtocolId
+                               The value of the "Security Protocol" parameter of
+                               the security protocol command to be sent.
+  @param[in] SecurityProtocolSpecificData
+                               The value of the "Security Protocol Specific"
+                               parameter of the security protocol command to be
+                               sent.
+  @param[in] PayloadBufferSize Size in bytes of the payload data buffer.
+  @param[in] PayloadBuffer     A pointer to a destination buffer to store the
+                               security protocol command specific payload data
+                               for the security protocol command.
+
+  @retval EFI_SUCCESS              The security protocol command completed successfully.
+  @retval EFI_UNSUPPORTED          The given DeviceIndex does not support security
+                                   protocol commands.
+  @retval EFI_DEVICE_ERROR         The security protocol command completed with
+                                   an error.
+  @retval EFI_INVALID_PARAMETER    The PayloadBuffer is NULL and PayloadBufferSize
+                                   is non-zero.
+  @retval EFI_TIMEOUT              A timeout occurred while waiting for the security
+                                   protocol command to execute.
+
+**/
+EFI_STATUS
+EFIAPI
+AhciStorageSecuritySendData (
+  IN EDKII_PEI_STORAGE_SECURITY_CMD_PPI  *This,
+  IN UINTN                               DeviceIndex,
+  IN UINT64                              Timeout,
+  IN UINT8                               SecurityProtocolId,
+  IN UINT16                              SecurityProtocolSpecificData,
+  IN UINTN                               PayloadBufferSize,
+  IN VOID                                *PayloadBuffer
+  )
+{
+  PEI_AHCI_CONTROLLER_PRIVATE_DATA    *Private;
+  PEI_AHCI_ATA_DEVICE_DATA            *DeviceData;
+
+  if ((PayloadBuffer == NULL) && (PayloadBufferSize != 0)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Private = GET_AHCI_PEIM_HC_PRIVATE_DATA_FROM_THIS_STROAGE_SECURITY (This);
+  if ((DeviceIndex == 0) || (DeviceIndex > Private->TrustComputingDevices)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  DeviceData = SearchTrustComputingDeviceByIndex (Private, DeviceIndex);
+  if (DeviceData == NULL) {
+    return EFI_NOT_FOUND;
+  }
+
+  ASSERT ((DeviceData->IdentifyData->trusted_computing_support & BIT0) != 0);
+  if ((DeviceData->IdentifyData->trusted_computing_support & BIT0) == 0) {
+    return EFI_UNSUPPORTED;
+  }
+
+  return TrustTransferAtaDevice (
+           DeviceData,
+           PayloadBuffer,
+           SecurityProtocolId,
+           SecurityProtocolSpecificData,
+           PayloadBufferSize,
+           TRUE,
+           Timeout,
+           NULL
+           );
+}
diff --git a/MdeModulePkg/Bus/Ata/AhciPei/DevicePath.c b/MdeModulePkg/Bus/Ata/AhciPei/DevicePath.c
new file mode 100644
index 0000000000..bdf8cc7780
--- /dev/null
+++ b/MdeModulePkg/Bus/Ata/AhciPei/DevicePath.c
@@ -0,0 +1,284 @@
+/** @file
+  The device path help function.
+
+  Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
+
+  This program and the accompanying materials
+  are licensed and made available under the terms and conditions
+  of the BSD License which accompanies this distribution.  The
+  full text of the license may be found at
+  http://opensource.org/licenses/bsd-license.php
+
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "AhciPei.h"
+
+//
+// Template for a SATA Device Path node
+//
+SATA_DEVICE_PATH  mAhciSataDevicePathNodeTemplate = {
+  {        // Header
+    MESSAGING_DEVICE_PATH,
+    MSG_SATA_DP,
+    {
+      (UINT8) (sizeof (SATA_DEVICE_PATH)),
+      (UINT8) ((sizeof (SATA_DEVICE_PATH)) >> 8)
+    }
+  },
+  0x0,     // HBAPortNumber
+  0xFFFF,  // PortMultiplierPortNumber
+  0x0      // Lun
+};
+
+//
+// Template for an End of entire Device Path node
+//
+EFI_DEVICE_PATH_PROTOCOL  mAhciEndDevicePathNodeTemplate = {
+  END_DEVICE_PATH_TYPE,
+  END_ENTIRE_DEVICE_PATH_SUBTYPE,
+  {
+    (UINT8) (sizeof (EFI_DEVICE_PATH_PROTOCOL)),
+    (UINT8) ((sizeof (EFI_DEVICE_PATH_PROTOCOL)) >> 8)
+  }
+};
+
+/**
+  Returns the 16-bit Length field of a device path node.
+
+  Returns the 16-bit Length field of the device path node specified by Node.
+  Node is not required to be aligned on a 16-bit boundary, so it is recommended
+  that a function such as ReadUnaligned16() be used to extract the contents of
+  the Length field.
+
+  If Node is NULL, then ASSERT().
+
+  @param  Node      A pointer to a device path node data structure.
+
+  @return The 16-bit Length field of the device path node specified by Node.
+
+**/
+UINTN
+DevicePathNodeLength (
+  IN CONST VOID  *Node
+  )
+{
+  ASSERT (Node != NULL);
+  return ReadUnaligned16 ((UINT16 *)&((EFI_DEVICE_PATH_PROTOCOL *)(Node))->Length[0]);
+}
+
+/**
+  Returns a pointer to the next node in a device path.
+
+  If Node is NULL, then ASSERT().
+
+  @param  Node    A pointer to a device path node data structure.
+
+  @return a pointer to the device path node that follows the device path node
+  specified by Node.
+
+**/
+EFI_DEVICE_PATH_PROTOCOL *
+NextDevicePathNode (
+  IN CONST VOID  *Node
+  )
+{
+  ASSERT (Node != NULL);
+  return (EFI_DEVICE_PATH_PROTOCOL *)((UINT8 *)(Node) + DevicePathNodeLength(Node));
+}
+
+/**
+  Get the size of the current device path instance.
+
+  @param[in]  DevicePath             A pointer to the EFI_DEVICE_PATH_PROTOCOL
+                                     structure.
+  @param[out] InstanceSize           The size of the current device path instance.
+  @param[out] EntireDevicePathEnd    Indicate whether the instance is the last
+                                     one in the device path strucure.
+
+  @retval EFI_SUCCESS    The size of the current device path instance is fetched.
+  @retval Others         Fails to get the size of the current device path instance.
+
+**/
+EFI_STATUS
+GetDevicePathInstanceSize (
+  IN  EFI_DEVICE_PATH_PROTOCOL    *DevicePath,
+  OUT UINTN                       *InstanceSize,
+  OUT BOOLEAN                     *EntireDevicePathEnd
+  )
+{
+  EFI_DEVICE_PATH_PROTOCOL    *Walker;
+
+  if (DevicePath == NULL || InstanceSize == NULL || EntireDevicePathEnd == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // Find the end of the device path instance
+  //
+  Walker = DevicePath;
+  while (Walker->Type != END_DEVICE_PATH_TYPE) {
+    Walker = NextDevicePathNode (Walker);
+  }
+
+  //
+  // Check if 'Walker' points to the end of an entire device path
+  //
+  if (Walker->SubType == END_ENTIRE_DEVICE_PATH_SUBTYPE) {
+    *EntireDevicePathEnd = TRUE;
+  } else if (Walker->SubType == END_INSTANCE_DEVICE_PATH_SUBTYPE) {
+    *EntireDevicePathEnd = FALSE;
+  } else {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // Compute the size of the device path instance
+  //
+  *InstanceSize = ((UINTN) Walker - (UINTN) (DevicePath)) + sizeof (EFI_DEVICE_PATH_PROTOCOL);
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Check the validity of the device path of a ATA AHCI host controller.
+
+  @param[in] DevicePath          A pointer to the EFI_DEVICE_PATH_PROTOCOL
+                                 structure.
+  @param[in] DevicePathLength    The length of the device path.
+
+  @retval EFI_SUCCESS              The device path is valid.
+  @retval EFI_INVALID_PARAMETER    The device path is invalid.
+
+**/
+EFI_STATUS
+AhciCheckHcDevicePath (
+  IN EFI_DEVICE_PATH_PROTOCOL    *DevicePath,
+  IN UINTN                       DevicePathLength
+  )
+{
+  EFI_DEVICE_PATH_PROTOCOL    *Start;
+  UINTN                       Size;
+
+  if (DevicePath == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // Validate the DevicePathLength is big enough to touch the first node.
+  //
+  if (DevicePathLength < sizeof (EFI_DEVICE_PATH_PROTOCOL)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Start = DevicePath;
+  while (!(DevicePath->Type == END_DEVICE_PATH_TYPE &&
+           DevicePath->SubType == END_ENTIRE_DEVICE_PATH_SUBTYPE)) {
+    DevicePath = NextDevicePathNode (DevicePath);
+
+    //
+    // Prevent overflow and invalid zero in the 'Length' field of a device path
+    // node.
+    //
+    if ((UINTN) DevicePath <= (UINTN) Start) {
+      return EFI_INVALID_PARAMETER;
+    }
+
+    //
+    // Prevent touching memory beyond given DevicePathLength.
+    //
+    if ((UINTN) DevicePath - (UINTN) Start >
+        DevicePathLength - sizeof (EFI_DEVICE_PATH_PROTOCOL)) {
+      return EFI_INVALID_PARAMETER;
+    }
+  }
+
+  //
+  // Check if the device path and its size match each other.
+  //
+  Size = ((UINTN) DevicePath - (UINTN) Start) + sizeof (EFI_DEVICE_PATH_PROTOCOL);
+  if (Size != DevicePathLength) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Build the device path for an ATA device with given port and port multiplier number.
+
+  @param[in]  Private               A pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA
+                                    data structure.
+  @param[in]  Port                  The given port number.
+  @param[in]  PortMultiplierPort    The given port multiplier number.
+  @param[out] DevicePathLength      The length of the device path in bytes specified
+                                    by DevicePath.
+  @param[out] DevicePath            The device path of ATA device.
+
+  @retval EFI_SUCCESS               The operation succeeds.
+  @retval EFI_INVALID_PARAMETER     The parameters are invalid.
+  @retval EFI_OUT_OF_RESOURCES      The operation fails due to lack of resources.
+
+**/
+EFI_STATUS
+AhciBuildDevicePath (
+  IN  PEI_AHCI_CONTROLLER_PRIVATE_DATA    *Private,
+  IN  UINT16                              Port,
+  IN  UINT16                              PortMultiplierPort,
+  OUT UINTN                               *DevicePathLength,
+  OUT EFI_DEVICE_PATH_PROTOCOL            **DevicePath
+  )
+{
+  EFI_DEVICE_PATH_PROTOCOL    *DevicePathWalker;
+  SATA_DEVICE_PATH            *SataDeviceNode;
+
+  if (DevicePathLength == NULL || DevicePath == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  *DevicePathLength = Private->DevicePathLength + sizeof (SATA_DEVICE_PATH);
+  *DevicePath       = AllocatePool (*DevicePathLength);
+  if (*DevicePath == NULL) {
+    *DevicePathLength = 0;
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  //
+  // Construct the host controller part device nodes
+  //
+  DevicePathWalker = *DevicePath;
+  CopyMem (
+    DevicePathWalker,
+    Private->DevicePath,
+    Private->DevicePathLength - sizeof (EFI_DEVICE_PATH_PROTOCOL)
+    );
+
+  //
+  // Construct the SATA device node
+  //
+  DevicePathWalker = (EFI_DEVICE_PATH_PROTOCOL *) ((UINT8 *)DevicePathWalker +
+                     (Private->DevicePathLength - sizeof (EFI_DEVICE_PATH_PROTOCOL)));
+  CopyMem (
+    DevicePathWalker,
+    &mAhciSataDevicePathNodeTemplate,
+    sizeof (mAhciSataDevicePathNodeTemplate)
+    );
+  SataDeviceNode                           = (SATA_DEVICE_PATH *)DevicePathWalker;
+  SataDeviceNode->HBAPortNumber            = Port;
+  SataDeviceNode->PortMultiplierPortNumber = PortMultiplierPort;
+
+  //
+  // Construct the end device node
+  //
+  DevicePathWalker = (EFI_DEVICE_PATH_PROTOCOL *) ((UINT8 *)DevicePathWalker +
+                     sizeof (SATA_DEVICE_PATH));
+  CopyMem (
+    DevicePathWalker,
+    &mAhciEndDevicePathNodeTemplate,
+    sizeof (mAhciEndDevicePathNodeTemplate)
+    );
+
+  return EFI_SUCCESS;
+}
diff --git a/MdeModulePkg/Bus/Ata/AhciPei/DmaMem.c b/MdeModulePkg/Bus/Ata/AhciPei/DmaMem.c
new file mode 100644
index 0000000000..098b02c1a1
--- /dev/null
+++ b/MdeModulePkg/Bus/Ata/AhciPei/DmaMem.c
@@ -0,0 +1,270 @@
+/** @file
+  The DMA memory help function.
+
+  Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
+
+  This program and the accompanying materials
+  are licensed and made available under the terms and conditions
+  of the BSD License which accompanies this distribution.  The
+  full text of the license may be found at
+  http://opensource.org/licenses/bsd-license.php
+
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "AhciPei.h"
+
+/**
+  Get IOMMU PPI.
+
+  @return Pointer to IOMMU PPI.
+
+**/
+EDKII_IOMMU_PPI *
+GetIoMmu (
+  VOID
+  )
+{
+  EFI_STATUS         Status;
+  EDKII_IOMMU_PPI    *IoMmu;
+
+  IoMmu  = NULL;
+  Status = PeiServicesLocatePpi (
+             &gEdkiiIoMmuPpiGuid,
+             0,
+             NULL,
+             (VOID **) &IoMmu
+             );
+  if (!EFI_ERROR (Status) && (IoMmu != NULL)) {
+    return IoMmu;
+  }
+
+  return NULL;
+}
+
+/**
+  Provides the controller-specific addresses required to access system memory from a
+  DMA bus master.
+
+  @param  Operation             Indicates if the bus master is going to read or write to system memory.
+  @param  HostAddress           The system memory address to map to the PCI controller.
+  @param  NumberOfBytes         On input the number of bytes to map. On output the number of bytes
+                                that were mapped.
+  @param  DeviceAddress         The resulting map address for the bus master PCI controller to use to
+                                access the hosts HostAddress.
+  @param  Mapping               A resulting value to pass to Unmap().
+
+  @retval EFI_SUCCESS           The range was mapped for the returned NumberOfBytes.
+  @retval EFI_UNSUPPORTED       The HostAddress cannot be mapped as a common buffer.
+  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+  @retval EFI_OUT_OF_RESOURCES  The request could not be completed due to a lack of resources.
+  @retval EFI_DEVICE_ERROR      The system hardware could not map the requested address.
+
+**/
+EFI_STATUS
+IoMmuMap (
+  IN  EDKII_IOMMU_OPERATION Operation,
+  IN VOID                   *HostAddress,
+  IN  OUT UINTN             *NumberOfBytes,
+  OUT EFI_PHYSICAL_ADDRESS  *DeviceAddress,
+  OUT VOID                  **Mapping
+  )
+{
+  EFI_STATUS         Status;
+  UINT64             Attribute;
+  EDKII_IOMMU_PPI    *IoMmu;
+
+  IoMmu = GetIoMmu ();
+
+  if (IoMmu != NULL) {
+    Status = IoMmu->Map (
+                     IoMmu,
+                     Operation,
+                     HostAddress,
+                     NumberOfBytes,
+                     DeviceAddress,
+                     Mapping
+                     );
+    if (EFI_ERROR (Status)) {
+      return EFI_OUT_OF_RESOURCES;
+    }
+    switch (Operation) {
+    case EdkiiIoMmuOperationBusMasterRead:
+    case EdkiiIoMmuOperationBusMasterRead64:
+      Attribute = EDKII_IOMMU_ACCESS_READ;
+      break;
+    case EdkiiIoMmuOperationBusMasterWrite:
+    case EdkiiIoMmuOperationBusMasterWrite64:
+      Attribute = EDKII_IOMMU_ACCESS_WRITE;
+      break;
+    case EdkiiIoMmuOperationBusMasterCommonBuffer:
+    case EdkiiIoMmuOperationBusMasterCommonBuffer64:
+      Attribute = EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE;
+      break;
+    default:
+      ASSERT(FALSE);
+      return EFI_INVALID_PARAMETER;
+    }
+    Status = IoMmu->SetAttribute (
+                      IoMmu,
+                      *Mapping,
+                      Attribute
+                      );
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+  } else {
+    *DeviceAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress;
+    *Mapping = NULL;
+    Status = EFI_SUCCESS;
+  }
+  return Status;
+}
+
+/**
+  Completes the Map() operation and releases any corresponding resources.
+
+  @param  Mapping               The mapping value returned from Map().
+
+  @retval EFI_SUCCESS           The range was unmapped.
+  @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by Map().
+  @retval EFI_DEVICE_ERROR      The data was not committed to the target system memory.
+**/
+EFI_STATUS
+IoMmuUnmap (
+  IN VOID                  *Mapping
+  )
+{
+  EFI_STATUS         Status;
+  EDKII_IOMMU_PPI    *IoMmu;
+
+  IoMmu = GetIoMmu ();
+
+  if (IoMmu != NULL) {
+    Status = IoMmu->SetAttribute (IoMmu, Mapping, 0);
+    Status = IoMmu->Unmap (IoMmu, Mapping);
+  } else {
+    Status = EFI_SUCCESS;
+  }
+  return Status;
+}
+
+/**
+  Allocates pages that are suitable for an OperationBusMasterCommonBuffer or
+  OperationBusMasterCommonBuffer64 mapping.
+
+  @param  Pages                 The number of pages to allocate.
+  @param  HostAddress           A pointer to store the base system memory address of the
+                                allocated range.
+  @param  DeviceAddress         The resulting map address for the bus master PCI controller to use to
+                                access the hosts HostAddress.
+  @param  Mapping               A resulting value to pass to Unmap().
+
+  @retval EFI_SUCCESS           The requested memory pages were allocated.
+  @retval EFI_UNSUPPORTED       Attributes is unsupported. The only legal attribute bits are
+                                MEMORY_WRITE_COMBINE and MEMORY_CACHED.
+  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+  @retval EFI_OUT_OF_RESOURCES  The memory pages could not be allocated.
+
+**/
+EFI_STATUS
+IoMmuAllocateBuffer (
+  IN UINTN                  Pages,
+  OUT VOID                  **HostAddress,
+  OUT EFI_PHYSICAL_ADDRESS  *DeviceAddress,
+  OUT VOID                  **Mapping
+  )
+{
+  EFI_STATUS            Status;
+  UINTN                 NumberOfBytes;
+  EFI_PHYSICAL_ADDRESS  HostPhyAddress;
+  EDKII_IOMMU_PPI       *IoMmu;
+
+  *HostAddress = NULL;
+  *DeviceAddress = 0;
+
+  IoMmu = GetIoMmu ();
+
+  if (IoMmu != NULL) {
+    Status = IoMmu->AllocateBuffer (
+                      IoMmu,
+                      EfiBootServicesData,
+                      Pages,
+                      HostAddress,
+                      0
+                      );
+    if (EFI_ERROR (Status)) {
+      return EFI_OUT_OF_RESOURCES;
+    }
+
+    NumberOfBytes = EFI_PAGES_TO_SIZE(Pages);
+    Status = IoMmu->Map (
+                      IoMmu,
+                      EdkiiIoMmuOperationBusMasterCommonBuffer,
+                      *HostAddress,
+                      &NumberOfBytes,
+                      DeviceAddress,
+                      Mapping
+                      );
+    if (EFI_ERROR (Status)) {
+      return EFI_OUT_OF_RESOURCES;
+    }
+    Status = IoMmu->SetAttribute (
+                      IoMmu,
+                      *Mapping,
+                      EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE
+                      );
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+  } else {
+    Status = PeiServicesAllocatePages (
+               EfiBootServicesData,
+               Pages,
+               &HostPhyAddress
+               );
+    if (EFI_ERROR (Status)) {
+      return EFI_OUT_OF_RESOURCES;
+    }
+    *HostAddress = (VOID *)(UINTN)HostPhyAddress;
+    *DeviceAddress = HostPhyAddress;
+    *Mapping = NULL;
+  }
+  return Status;
+}
+
+/**
+  Frees memory that was allocated with AllocateBuffer().
+
+  @param  Pages                 The number of pages to free.
+  @param  HostAddress           The base system memory address of the allocated range.
+  @param  Mapping               The mapping value returned from Map().
+
+  @retval EFI_SUCCESS           The requested memory pages were freed.
+  @retval EFI_INVALID_PARAMETER The memory range specified by HostAddress and Pages
+                                was not allocated with AllocateBuffer().
+
+**/
+EFI_STATUS
+IoMmuFreeBuffer (
+  IN UINTN                  Pages,
+  IN VOID                   *HostAddress,
+  IN VOID                   *Mapping
+  )
+{
+  EFI_STATUS         Status;
+  EDKII_IOMMU_PPI    *IoMmu;
+
+  IoMmu = GetIoMmu ();
+
+  if (IoMmu != NULL) {
+    Status = IoMmu->SetAttribute (IoMmu, Mapping, 0);
+    Status = IoMmu->Unmap (IoMmu, Mapping);
+    Status = IoMmu->FreeBuffer (IoMmu, Pages, HostAddress);
+  } else {
+    Status = EFI_SUCCESS;
+  }
+  return Status;
+}
diff --git a/MdeModulePkg/Bus/Ata/AhciPei/AhciPei.uni b/MdeModulePkg/Bus/Ata/AhciPei/AhciPei.uni
new file mode 100644
index 0000000000..6dd3a74cc5
--- /dev/null
+++ b/MdeModulePkg/Bus/Ata/AhciPei/AhciPei.uni
@@ -0,0 +1,21 @@
+// /** @file
+// The AhciPei driver is used to manage ATA hard disk device working under AHCI
+// mode at PEI phase.
+//
+// Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
+//
+// This program and the accompanying materials
+// are licensed and made available under the terms and conditions
+// of the BSD License which accompanies this distribution.  The
+// full text of the license may be found at
+// http://opensource.org/licenses/bsd-license.php
+//
+// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT             #language en-US "Manage AHCI mode ATA hard disk device at PEI phase"
+
+#string STR_MODULE_DESCRIPTION          #language en-US "The AhciPei driver is used to manage ATA hard disk device working under AHCI mode at PEI phase."
diff --git a/MdeModulePkg/Bus/Ata/AhciPei/AhciPeiExtra.uni b/MdeModulePkg/Bus/Ata/AhciPei/AhciPeiExtra.uni
new file mode 100644
index 0000000000..295f426473
--- /dev/null
+++ b/MdeModulePkg/Bus/Ata/AhciPei/AhciPeiExtra.uni
@@ -0,0 +1,19 @@
+// /** @file
+// AhciPei Localized Strings and Content
+//
+// Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
+//
+// This program and the accompanying materials
+// are licensed and made available under the terms and conditions
+// of the BSD License which accompanies this distribution.  The
+// full text of the license may be found at
+// http://opensource.org/licenses/bsd-license.php
+//
+// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"AHCI Bus Peim"
-- 
2.12.0.windows.1



  parent reply	other threads:[~2019-02-01  5:47 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-02-01  5:47 [PATCH v3 00/12] Split the S3 PEI phase HW init codes from Opal driver Hao Wu
2019-02-01  5:47 ` [PATCH v3 01/12] MdeModulePkg: Add definitions for ATA AHCI host controller PPI Hao Wu
2019-02-01  5:47 ` [PATCH v3 02/12] MdeModulePkg: Add definitions for EDKII PEI ATA PassThru PPI Hao Wu
2019-02-01  5:47 ` [PATCH v3 03/12] MdeModulePkg: Add definitions for Storage Security Command PPI Hao Wu
2019-02-01  5:47 ` [PATCH v3 04/12] MdeModulePkg: Add GUID for LockBox to save storage dev to init in S3 Hao Wu
2019-02-01  5:47 ` [PATCH v3 05/12] MdeModulePkg/NvmExpressPei: Avoid updating the module-level variable Hao Wu
2019-02-01  5:47 ` [PATCH v3 06/12] MdeModulePkg/NvmExpressPei: Add logic to produce SSC PPI Hao Wu
2019-02-01  5:47 ` [PATCH v3 07/12] MdeModulePkg/NvmExpressPei: Consume S3StorageDeviceInitList LockBox Hao Wu
2019-02-01  5:47 ` Hao Wu [this message]
2019-02-01  5:47 ` [PATCH v3 09/12] MdeModulePkg/SmmLockBoxLib: Use 'DEBUG_' prefix instead of 'EFI_D_' Hao Wu
2019-02-01  9:04   ` Laszlo Ersek
2019-02-01  5:47 ` [PATCH v3 10/12] MdeModulePkg/SmmLockBox(PEI): Remove an ASSERT in RestoreLockBox() Hao Wu
2019-02-01  9:17   ` Laszlo Ersek
2019-02-01 10:54     ` Wu, Hao A
2019-02-01  5:47 ` [PATCH v3 11/12] MdeModulePkg/SmmLockBoxLib: Support LockBox enlarge in UpdateLockBox() Hao Wu
2019-02-01  7:52   ` Ni, Ray
2019-02-01  9:39   ` Laszlo Ersek
2019-02-01 11:03     ` Wu, Hao A
2019-02-01  5:47 ` [PATCH v3 12/12] SecurityPkg/OpalPassword: Remove HW init codes and consume SSC PPI Hao Wu

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-list from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20190201054728.8612-9-hao.a.wu@intel.com \
    --to=devel@edk2.groups.io \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox