/** @file AcpiVtd.c

  @copyright
  Copyright 1996 - 2021 Intel Corporation. <BR>

  SPDX-License-Identifier: BSD-2-Clause-Patent
**/

//
// Statements that include other files
//
#include <AcpiVtd.h>
#include <Library/SetupLib.h>
#include <IioRegs.h>
#include <IioSetupDefinitions.h>
#include <Protocol/Tcg2Protocol.h>
#include <Library/TpmMeasurementLib.h>

VTD_SUPPORT_INSTANCE mPrivateData;

#define MAX_BUS_ADDR_WIDTH 45

/**

  Add DMAR entry

  @param This        - DMA Remap protocol pointer
  @param RemapType   - Type of DMA remapping structure to add
  @param RemapEntry  - Entry to add

  @retval EFI_INVALID_PARAMETER - DMA remapping support not initialized or entry is malformed
  @retval EFI_UNSUPPORTED       - Adding entries is not supported
  @retval EFI_SUCCESS           - The entry was inserted successfully.

**/
EFI_STATUS
EFIAPI
InsertDmaRemap (
  IN  DMA_REMAP_PROTOCOL                *This,
  IN  REMAP_TYPE                        RemapType,
  IN  VOID                              *RemapEntry
  )
{
  UINTN                                                       DevIndex;
  EFI_ACPI_DMAR_HEADER                                        *Dmar;
  EFI_ACPI_DMAR_DRHD_HEADER                                   *Drhd;
  EFI_ACPI_DMAR_RMRR_HEADER                                   *Rmrr;
  EFI_ACPI_DMAR_SATC_HEADER                                   *Atsr;
  EFI_ACPI_DMAR_RHSA_HEADER                                   *Rhsa;
  EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER                 *DevScope;
  DMAR_DRHD                                                   *DmaRemap;
  DMAR_RMRR                                                   *RevMemRegion;
  DMAR_ATSR                                                   *AtsrRegion;
  DMAR_RHSA                                                   *RhsaRegion;
  EFI_ACPI_DMAR_PCI_PATH                                      *PciPath;
  EFI_ACPI_DMAR_PCI_PATH                                      *PciInputPath;

  if (mPrivateData.Dmar == NULL) {
      ASSERT (FALSE);
      return EFI_INVALID_PARAMETER;
  }

  Dmar = mPrivateData.Dmar;
  if (((UINT8 *) Dmar + Dmar->Header.Length) == NULL) {
      ASSERT (FALSE);
      return EFI_INVALID_PARAMETER;
  }

  if (RemapType == DrhdType) {
    DmaRemap = (DMAR_DRHD *) RemapEntry;
    ASSERT (DmaRemap->Signature == DRHD_SIGNATURE);
    Drhd = (EFI_ACPI_DMAR_DRHD_HEADER *) ((UINT8 *) Dmar + Dmar->Header.Length);
    if (Drhd == NULL) {
      ASSERT (FALSE);
      return EFI_INVALID_PARAMETER;
    }

    if (DmaRemap->RegisterBase == 0) {
      return EFI_UNSUPPORTED;
    }

    Drhd->Header.Type         = EFI_ACPI_DMAR_TYPE_DRHD;
    Drhd->Header.Length       = sizeof (EFI_ACPI_DMAR_DRHD_HEADER);
    Drhd->Flags               = DmaRemap->Flags;
    Drhd->SegmentNumber       = DmaRemap->SegmentNumber;
    Drhd->RegisterBaseAddress = DmaRemap->RegisterBase;
    DevScope                  = NULL;

    for (DevIndex = 0; DevIndex < DmaRemap->DeviceScopeNumber; DevIndex++) {
      if (((UINT8 *) Drhd + Drhd->Header.Length) == NULL) {
          ASSERT (FALSE);
          return EFI_INVALID_PARAMETER;
      }

      DevScope = (EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER *) ((UINT8 *) Drhd + Drhd->Header.Length);
      if (DevScope != NULL) {
        DevScope->Type              = DmaRemap->DeviceScope[DevIndex].DeviceType;
        DevScope->Length            = sizeof (EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER);
        DevScope->EnumerationId     = DmaRemap->DeviceScope[DevIndex].EnumerationID;
        DevScope->StartBusNumber    = DmaRemap->DeviceScope[DevIndex].StartBusNumber;
        if (((UINT8 *) DevScope + DevScope->Length) == NULL) {
          ASSERT (FALSE);
          return EFI_INVALID_PARAMETER;
        }

        PciPath = (EFI_ACPI_DMAR_PCI_PATH *) ((UINT8 *) DevScope + DevScope->Length);
        PciInputPath = (EFI_ACPI_DMAR_PCI_PATH *) DmaRemap->DeviceScope[DevIndex].PciNode;
        while (*(UINT8 *) PciInputPath != (UINT8) -1) {
          CopyMem(PciPath, PciInputPath, sizeof (EFI_ACPI_DMAR_PCI_PATH));
          DevScope->Length += sizeof (EFI_ACPI_DMAR_PCI_PATH);
          PciInputPath++;
          PciPath++;
        }
        Drhd->Header.Length = Drhd->Header.Length + (UINT16) DevScope->Length;
      } else {
        DEBUG ((DEBUG_ERROR, "DevScope Error. Invalid pointer.\n"));
      }
    }

    Dmar->Header.Length += Drhd->Header.Length;

  } else if (RemapType == RmrrType) {
    RevMemRegion = (DMAR_RMRR *) RemapEntry;
    ASSERT (RevMemRegion->Signature == RMRR_SIGNATURE);
    Rmrr = (EFI_ACPI_DMAR_RMRR_HEADER *) ((UINT8 *) Dmar + Dmar->Header.Length);
    if (Rmrr == NULL) {
      ASSERT (FALSE);
      return EFI_INVALID_PARAMETER;
    }

    Rmrr->Header.Type                       = EFI_ACPI_DMAR_TYPE_RMRR;
    Rmrr->Header.Length                     = sizeof (EFI_ACPI_DMAR_RMRR_HEADER);
    Rmrr->SegmentNumber                     = RevMemRegion->SegmentNumber;
    Rmrr->ReservedMemoryRegionBaseAddress   = RevMemRegion->RsvdMemBase;
    Rmrr->ReservedMemoryRegionLimitAddress  = RevMemRegion->RsvdMemLimit;

    DevScope = NULL;
    for (DevIndex = 0; DevIndex < RevMemRegion->DeviceScopeNumber; DevIndex++) {
      if (((UINT8 *) Rmrr + Rmrr->Header.Length) == NULL) {
        ASSERT (FALSE);
        return EFI_INVALID_PARAMETER;
      }

      DevScope                        = (EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER *) ((UINT8 *) Rmrr + Rmrr->Header.Length);
      if (DevScope != NULL) {
        DevScope->Type                  = RevMemRegion->DeviceScope[DevIndex].DeviceType;
        DevScope->StartBusNumber        = RevMemRegion->DeviceScope[DevIndex].StartBusNumber;
        DevScope->Length                = sizeof (EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER);
        if (((UINT8 *) DevScope + DevScope->Length) == NULL) {
          ASSERT (FALSE);
          return EFI_INVALID_PARAMETER;
        }

        PciPath                         = (EFI_ACPI_DMAR_PCI_PATH *) ((UINT8 *) DevScope + DevScope->Length);
        PciInputPath                    = (EFI_ACPI_DMAR_PCI_PATH *) RevMemRegion->DeviceScope[DevIndex].PciNode;
        while (*(UINT8 *) PciInputPath != (UINT8) -1) {
          CopyMem (PciPath, PciInputPath, sizeof (EFI_ACPI_DMAR_PCI_PATH));
          DevScope->Length += sizeof (EFI_ACPI_DMAR_PCI_PATH);
          PciInputPath++;
          PciPath++;
        }
        Rmrr->Header.Length = Rmrr->Header.Length + (UINT16) DevScope->Length;
      } else {
        DEBUG ((DEBUG_ERROR, "DevScope Error. Invalid pointer.\n"));
      }
    }

    Dmar->Header.Length += Rmrr->Header.Length;

  } else if (RemapType == AtsrType) {
    AtsrRegion = (DMAR_ATSR *) RemapEntry;
    ASSERT (AtsrRegion->Signature == ATSR_SIGNATURE);
    Atsr = (EFI_ACPI_DMAR_SATC_HEADER *) ((UINT8 *) Dmar + Dmar->Header.Length);
    if (Atsr == NULL) {
      ASSERT (FALSE);
      return EFI_INVALID_PARAMETER;
    }

    Atsr->Header.Type   = EFI_ACPI_DMAR_TYPE_ATSR;
    Atsr->Flags         = AtsrRegion->Flags;
    Atsr->SegmentNumber = AtsrRegion->SegmentNumber;
    Atsr->Header.Length = sizeof (EFI_ACPI_DMAR_SATC_HEADER);

    DevScope = NULL;
    for (DevIndex = 0; DevIndex < AtsrRegion->DeviceScopeNumber; DevIndex++) {
      if ((AtsrRegion->ATSRPresentBit & (01 << DevIndex)) == 00) {
        continue;
      }
      if (((UINT8 *) Atsr + Atsr->Header.Length) == NULL) {
        ASSERT (FALSE);
        return EFI_INVALID_PARAMETER;
      }

      DevScope = (EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER *) ((UINT8 *) Atsr + Atsr->Header.Length);
      if (DevScope != NULL) {
        DevScope->Type              = AtsrRegion->DeviceScope[DevIndex].DeviceType;
        DevScope->StartBusNumber    = AtsrRegion->DeviceScope[DevIndex].StartBusNumber;
        DevScope->Length            = sizeof (EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER);
        if (((UINT8 *) DevScope + DevScope->Length) == NULL) {
          ASSERT (FALSE);
          return EFI_INVALID_PARAMETER;
        }

        PciPath = (EFI_ACPI_DMAR_PCI_PATH *) ((UINT8 *) DevScope + DevScope->Length);
        PciInputPath = (EFI_ACPI_DMAR_PCI_PATH *) AtsrRegion->DeviceScope[DevIndex].PciNode;
        while (*(UINT8 *) PciInputPath != (UINT8) -1) {
          CopyMem(PciPath, PciInputPath, sizeof (EFI_ACPI_DMAR_PCI_PATH));
          DevScope->Length += sizeof (EFI_ACPI_DMAR_PCI_PATH);
          PciInputPath++;
          PciPath++;
        }

        Atsr->Header.Length = Atsr->Header.Length + (UINT16) DevScope->Length;

      } else {
        DEBUG ((DEBUG_ERROR, "DevScope Error. Invalid pointer.\n"));
      }
    }

    Dmar->Header.Length += Atsr->Header.Length;

  } else if (RemapType == RhsaType) {
    RhsaRegion = (DMAR_RHSA *) RemapEntry;
    ASSERT (RhsaRegion->Signature == RHSA_SIGNATURE);

    Rhsa                        = (EFI_ACPI_DMAR_RHSA_HEADER *) ((UINT8 *) Dmar + Dmar->Header.Length);
    Rhsa->Header.Type           = EFI_ACPI_DMAR_TYPE_RHSA;
    Rhsa->ProximityDomain       = RhsaRegion->Domian;
    Rhsa->RegisterBaseAddress   = RhsaRegion->RegisterBase;
    Rhsa->Header.Length         = sizeof (EFI_ACPI_DMAR_RHSA_HEADER);
    Dmar->Header.Length         += Rhsa->Header.Length;
  } else {
    return EFI_INVALID_PARAMETER;
  }

  ASSERT (Dmar->Header.Length < TABLE_SIZE);
  return EFI_SUCCESS;
}

/**

    Returns info about the provided entry

    @param Entry      - DMA remapping entry
    @param Type       - DMA remapping type
    @param IncludeAll - Include all or all root port ASTR
    @param Length     - GC_TODO: add arg description

    @retval EFI_INVALID_PARAMETER - Null input pointer
    @retval EFI_SUCCESS           - Table info updated

**/
EFI_STATUS
GetTablesInfo (
  IN     UINT8       *Entry,
  IN OUT REMAP_TYPE  *Type,
  IN OUT BOOLEAN     *IncludeAll,
  IN OUT UINTN       *Length
  )
{
  EFI_ACPI_DMAR_DRHD_HEADER  *Comm;

  if (!Entry || !Type || !IncludeAll || !Length) {
    ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER);
    return EFI_INVALID_PARAMETER;
  }

  Comm    = (EFI_ACPI_DMAR_DRHD_HEADER *) Entry;
  *Length = Comm->Header.Length;

  if (Comm->Header.Type == EFI_ACPI_DMAR_TYPE_RMRR) {
    *Type = RmrrType;
  } else if (Comm->Header.Type == EFI_ACPI_DMAR_TYPE_DRHD) {
    *Type = DrhdType;
  } else if (Comm->Header.Type == EFI_ACPI_DMAR_TYPE_ATSR) {
    *Type = AtsrType;
  } else if (Comm->Header.Type == EFI_ACPI_DMAR_TYPE_RHSA) {
    *Type = RhsaType;
  } else {
    *Type = 0xFF;
  }

  if (Comm->Flags & EFI_ACPI_DMAR_DRHD_FLAGS_INCLUDE_PCI_ALL) {
    *IncludeAll = TRUE;
  } else {
    *IncludeAll = FALSE;
  }

  return EFI_SUCCESS;
}

/**

    Reorder the table entries

    @param None

    @retval EFI_SUCCESS - The table entries are ordered

**/
EFI_STATUS
ReorderTables (
  VOID
  )
{
  REMAP_TYPE  Type;
  BOOLEAN     IncludeAll;
  UINTN       Length;
  UINTN       CurrLength;
  UINTN       TableLength;
  UINT8       *Ptr;
  UINT8       *PtrOrder;

  Ptr       = (UINT8 *) mPrivateData.Dmar;
  PtrOrder  = (UINT8 *) mPrivateData.DmarOrder;

  CopyMem (PtrOrder, Ptr, sizeof (EFI_ACPI_DMAR_HEADER));
  PtrOrder += sizeof (EFI_ACPI_DMAR_HEADER);

  TableLength = mPrivateData.Dmar->Header.Length;

  CurrLength  = sizeof (EFI_ACPI_DMAR_HEADER);
  Ptr         = (UINT8 *) mPrivateData.Dmar + CurrLength;
  while (CurrLength < TableLength) {
    GetTablesInfo (Ptr, &Type, &IncludeAll, &Length);
    if (Type == DrhdType && !IncludeAll) {
      CopyMem (PtrOrder, Ptr, Length);
      PtrOrder += Length;
    }

    Ptr += Length;
    CurrLength += Length;
  }

  CurrLength  = sizeof (EFI_ACPI_DMAR_HEADER);
  Ptr         = (UINT8 *) mPrivateData.Dmar + CurrLength;
  while (CurrLength < TableLength) {
    GetTablesInfo (Ptr, &Type, &IncludeAll, &Length);
    if (Type == DrhdType && IncludeAll) {
      CopyMem (PtrOrder, Ptr, Length);
      PtrOrder += Length;
    }

    Ptr += Length;
    CurrLength += Length;
  }

  CurrLength  = sizeof (EFI_ACPI_DMAR_HEADER);
  Ptr         = (UINT8 *) mPrivateData.Dmar + CurrLength;
  while (CurrLength < TableLength) {
    GetTablesInfo (Ptr, &Type, &IncludeAll, &Length);
    if (Type == RmrrType && !IncludeAll) {
      CopyMem (PtrOrder, Ptr, Length);
      PtrOrder += Length;
    }

    Ptr += Length;
    CurrLength += Length;
  }

  CurrLength  = sizeof (EFI_ACPI_DMAR_HEADER);
  Ptr         = (UINT8 *) mPrivateData.Dmar + CurrLength;
  while (CurrLength < TableLength) {
    GetTablesInfo (Ptr, &Type, &IncludeAll, &Length);
    if (Type == AtsrType && !IncludeAll) {
      CopyMem (PtrOrder, Ptr, Length);
      PtrOrder += Length;
    }

    Ptr += Length;
    CurrLength += Length;
  }

  CurrLength  = sizeof (EFI_ACPI_DMAR_HEADER);
  Ptr         = (UINT8 *) mPrivateData.Dmar + CurrLength;
  while (CurrLength < TableLength) {
    GetTablesInfo (Ptr, &Type, &IncludeAll, &Length);
    if (Type == RhsaType) {
      CopyMem (PtrOrder, Ptr, Length);
      PtrOrder += Length;
    }

    Ptr += Length;
    CurrLength += Length;
  }

  return EFI_SUCCESS;
}

/**

  Return a reordered version of the DMAR table provided on input

  @param[in]      This      - DMA remap protocol
  @param[in][out] DmarTable - DMAR table

  @retval EFI_INVALID_PARAMETER - DmarTable NULL
  @retval EFI_UNSUPPORTED       - DMAR table length doesn't meet expected value
  @retval EFI_SUCCESS           - Updated DMAR table returned

**/
EFI_STATUS
EFIAPI
GetDmarTable (
  IN     DMA_REMAP_PROTOCOL                *This,
  IN OUT VOID                              **DmarTable
  )
{
  if (DmarTable == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  if (mPrivateData.Dmar->Header.Length <= sizeof (EFI_ACPI_DMAR_HEADER)) {
    return EFI_UNSUPPORTED;
  }

  ReorderTables ();
  *DmarTable = mPrivateData.DmarOrder;
  return EFI_SUCCESS;
}

#define TBT_SECURITY_EVENT_STRING      "DMA Protection Disabled"
#define TBT_SECURITY_EVENT_STRING_LEN  (sizeof (TBT_SECURITY_EVENT_STRING) - 1)

GLOBAL_REMOVE_IF_UNREFERENCED EFI_EVENT                         EndOfDxeEvent;

/**
  Security EndOfDxe CallBack Function
  If the firmware/BIOS has an option to enable and disable DMA protections via a VT-d switch in BIOS options, then the shipping configuration must be with VT-d protection enabled.
  On every boot where VT-d/DMA protection is disabled, or will be disabled, or configured to a lower security state, and a platform has a TPM enabled, then the platform SHALL
  extend an EV_EFI_ACTION event into PCR[7] before enabling external DMA
  The event string SHALL be "DMA Protection Disabled". The platform firmware MUST log this measurement in the event log using the string "DMA Protection Disabled" for the Event Data.
  Measure and log launch of TBT Security, and extend the measurement result into a specific PCR.
  Extend an EV_EFI_ACTION event into PCR[7] before enabling external DMA. The event string SHALL be "DMA Protection Disabled". The platform firmware MUST log this measurement
  in the event log using the string "DMA Protection Disabled" for the Event Data.

  @param[in] Event     - A pointer to the Event that triggered the callback.
  @param[in] Context   - A pointer to private data registered with the callback function.
**/
VOID
EFIAPI
ExtendPCR7CallBack (
  IN EFI_EVENT          Event,
  IN VOID               *Context
  )
{
  UINTN                 Status;
  UINT64                HashDataLen;

  DEBUG ((DEBUG_INFO, "ExtendPCR7CallBack START\n"));

  //
  // When VT-d/DMA protection is disabled and a platform has a TPM enabled,
  // the platform SHALL extend an EV_EFI_ACTION event into PCR[7].
  //
  HashDataLen = TBT_SECURITY_EVENT_STRING_LEN;

  Status = TpmMeasureAndLogData (
             7,
             EV_EFI_ACTION,
             TBT_SECURITY_EVENT_STRING,
             (UINT32) HashDataLen,
             TBT_SECURITY_EVENT_STRING,
             HashDataLen
             );
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR, "TpmMeasureAndLogData Status: %r\n", Status));
  } else {
    DEBUG ((DEBUG_INFO, "TpmMeasureAndLogData Successfully\n"));
  }

  DEBUG ((DEBUG_INFO, "ExtendPCR7CallBack END\n"));
}

/**
  Register an End of DXE event for extended a TPM log to PCR[7] when vtd is diable
  This feature is introduced by TBT Security requirment
**/
VOID
RegisterExtendPCR7CallBack (
  VOID
  )
{
  EFI_STATUS Status = EFI_SUCCESS;

  //
  // Register an End of DXE event for extended a TPM log to PCR[7].
  //
  DEBUG ((DEBUG_INFO, "Register an End of DXE event for extended a TPM log to PCR[7] when VTd/DMA protection is disabled.\n"));
  Status = gBS->CreateEventEx (
                  EVT_NOTIFY_SIGNAL,
                  TPL_NOTIFY,
                  ExtendPCR7CallBack,
                  NULL,
                  &gEfiEndOfDxeEventGroupGuid,
                  &EndOfDxeEvent
                  );
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR, "Failed to Register an End of DXE event for extended a TPM log to PCR[7], Status: %r\n", Status));
  }
}

/**

  VT-D Driver entry point

  @param ImageHandle     The image handle.
  @param SystemTable     The system table.

  @retval Status - If not EFI_SUCCESS then an error occurred during initialization.

**/
EFI_STATUS
EFIAPI
VtdTableEntryPoint (
  IN EFI_HANDLE                         ImageHandle,
  IN EFI_SYSTEM_TABLE                   *SystemTable
  )
{
  EFI_STATUS                            Status = EFI_SUCCESS;
  EFI_ACPI_DMAR_HEADER                  *Dmar;
  UINT64                                TempOemTableId;
  UINT8                                 VTdSupport;
  UINT8                                 DmaCtrlOptIn;
  UINT8                                 InterruptRemap;
  UINT8                                 X2ApicOptOut;
  UINT8                                 ATS;
  UINTN                                 Dmarlength;
  UINT8                                 ControlIommu;

  //
  // Initialize our protocol
  //
  ZeroMem (&mPrivateData, sizeof (VTD_SUPPORT_INSTANCE));

  Status = GetOptionData (&gEfiSocketIioVariableGuid, OFFSET_OF (SOCKET_IIO_CONFIGURATION, VTdSupport), &VTdSupport, sizeof (VTdSupport));
  Status |= GetOptionData (&gEfiSocketIioVariableGuid, OFFSET_OF (SOCKET_IIO_CONFIGURATION, DmaCtrlOptIn), &DmaCtrlOptIn, sizeof (DmaCtrlOptIn));
  Status |= GetOptionData (&gEfiSocketIioVariableGuid, OFFSET_OF (SOCKET_IIO_CONFIGURATION, InterruptRemap), &InterruptRemap, sizeof (InterruptRemap));
  Status |= GetOptionData (&gEfiSocketIioVariableGuid, OFFSET_OF (SOCKET_IIO_CONFIGURATION, ATS), &ATS, sizeof (ATS));
  Status |= GetOptionData (&gEfiSocketIioVariableGuid, OFFSET_OF (SOCKET_IIO_CONFIGURATION, X2ApicOptOut), &X2ApicOptOut, sizeof (X2ApicOptOut));

  if (!EFI_ERROR (Status)) {
    mPrivateData.DmaRemapProt.VTdSupport      = VTdSupport;
    mPrivateData.DmaRemapProt.DmaCtrlOptIn    = DmaCtrlOptIn;
    mPrivateData.DmaRemapProt.InterruptRemap  = VTdSupport && (InterruptRemap != IIO_OPTION_DISABLE);
    mPrivateData.DmaRemapProt.ATS             = ATS;
    mPrivateData.DmaRemapProt.X2ApicOptOut    = X2ApicOptOut;
  }

  Status = GetOptionData (&gEfiSocketIioVariableGuid, OFFSET_OF (SOCKET_IIO_CONFIGURATION, ControlIommu), &ControlIommu, sizeof (ControlIommu));
  if (EFI_ERROR (Status)) {
    ControlIommu = 0;
  }

  mPrivateData.Signature = EFI_ACPI_6_4_DMA_REMAPPING_TABLE_SIGNATURE;

  Dmarlength = MAX_SOCKET * NUMBER_PORTS_PER_SOCKET * ( sizeof (EFI_ACPI_DMAR_HEADER) + sizeof (EFI_ACPI_DMAR_DRHD_HEADER) +
                    sizeof (EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER) + sizeof (EFI_ACPI_DMAR_PCI_PATH));

  mPrivateData.Dmar = (EFI_ACPI_DMAR_HEADER *) AllocateZeroPool (Dmarlength);

  mPrivateData.DmarOrder = (EFI_ACPI_DMAR_HEADER *) AllocateZeroPool (Dmarlength);

  mPrivateData.DmaRemapProt.InsertDmaRemap = InsertDmaRemap;
  mPrivateData.DmaRemapProt.GetDmarTable = GetDmarTable;

  if (mPrivateData.Dmar != NULL) {
    Dmar                          = mPrivateData.Dmar;
    Dmar->Header.Length           = sizeof (EFI_ACPI_DMAR_HEADER);
    Dmar->Header.Signature        = EFI_ACPI_6_4_DMA_REMAPPING_TABLE_SIGNATURE;
    Dmar->Header.Revision         = EFI_ACPI_DMAR_REVISION;
    Dmar->Header.OemRevision      = ACPI_DMAR_OEM_REVISION;
    Dmar->Header.CreatorId        = ACPI_DMAR_OEM_CREATOR_ID;
    Dmar->Header.CreatorRevision  = ACPI_DMAR_OEM_CREATOR_REVISION;
    Dmar->HostAddressWidth        = MAX_BUS_ADDR_WIDTH;

    TempOemTableId = PcdGet64 (PcdAcpiDefaultOemTableId);

    CopyMem (Dmar->Header.OemId, PcdGetPtr (PcdAcpiDefaultOemId), sizeof (Dmar->Header.OemId));
    CopyMem (&Dmar->Header.OemTableId, &TempOemTableId, sizeof (Dmar->Header.OemTableId));

    Status = gBS->InstallProtocolInterface (
                    &mPrivateData.Handle,
                    &gDmaRemapProtocolGuid,
                    EFI_NATIVE_INTERFACE,
                    &mPrivateData.DmaRemapProt
                    );
  } else {

    ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER);
    Status = EFI_OUT_OF_RESOURCES;
  }

  if (FixedPcdGetBool (PcdConditionallyExtendPcr7)) {
    if (!VTdSupport || !DmaCtrlOptIn || (ControlIommu == 0)) {
      //
      // Inform OS by TPM PCR7 when VTd/DMA protection is disabled.
      //
      RegisterExtendPCR7CallBack ();
    }
  }

  return Status;
}
