text: /** @file
  
    Copyright (c) 2020, Intel Corporation. All rights reserved.<BR>
    SPDX-License-Identifier: BSD-2-Clause-Patent
  
  **/
  
  #include <Uefi.h>
  #include <PiPei.h>
  #include <Library/BaseLib.h>
  #include <Library/BaseMemoryLib.h>
  #include <Library/MemoryAllocationLib.h>
  #include <Library/DebugLib.h>
  #include <Library/HobLib.h>
  #include <Library/PciSegmentLib.h>
  #include <IndustryStandard/Vtd.h>
  #include <IndustryStandard/Pci.h>
  #include <Protocol/IoMmu.h>
  #include <Ppi/VtdInfo.h>
  
  #include "IntelVTdDmarPei.h"
  
  /**
    Dump DMAR DeviceScopeEntry.
  
    @param[in]  DmarDeviceScopeEntry      DMAR DeviceScopeEntry
  **/
  VOID
  DumpDmarDeviceScopeEntry (
    IN EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER  *DmarDeviceScopeEntry
    )
  {
    UINTN                                 PciPathNumber;
    UINTN                                 PciPathIndex;
    EFI_ACPI_DMAR_PCI_PATH                *PciPath;
  
    if (DmarDeviceScopeEntry == NULL) {
      return;
    }
  
    DEBUG ((DEBUG_INFO,
      "    *************************************************************************\n"
      ));
    DEBUG ((DEBUG_INFO,
      "    *       DMA-Remapping Device Scope Entry Structure                      *\n"
      ));
    DEBUG ((DEBUG_INFO,
      "    *************************************************************************\n"
      ));
    DEBUG ((DEBUG_INFO,
      (sizeof (UINTN) == sizeof (UINT64)) ?
      "    DMAR Device Scope Entry address ...................... 0x%016lx\n" :
      "    DMAR Device Scope Entry address ...................... 0x%08x\n",
      DmarDeviceScopeEntry
      ));
    DEBUG ((DEBUG_INFO,
      "      Device Scope Entry Type ............................ 0x%02x\n",
      DmarDeviceScopeEntry->Type
      ));
    switch (DmarDeviceScopeEntry->Type) {
    case EFI_ACPI_DEVICE_SCOPE_ENTRY_TYPE_PCI_ENDPOINT:
      DEBUG ((DEBUG_INFO,
        "        PCI Endpoint Device\n"
        ));
      break;
    case EFI_ACPI_DEVICE_SCOPE_ENTRY_TYPE_PCI_BRIDGE:
      DEBUG ((DEBUG_INFO,
        "        PCI Sub-hierachy\n"
        ));
      break;
    default:
      break;
    }
    DEBUG ((DEBUG_INFO,
      "      Length ............................................. 0x%02x\n",
      DmarDeviceScopeEntry->Length
      ));
    DEBUG ((DEBUG_INFO,
      "      Enumeration ID ..................................... 0x%02x\n",
      DmarDeviceScopeEntry->EnumerationId
      ));
    DEBUG ((DEBUG_INFO,
      "      Starting Bus Number ................................ 0x%02x\n",
      DmarDeviceScopeEntry->StartBusNumber
      ));
  
    PciPathNumber = (DmarDeviceScopeEntry->Length - sizeof (EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER)) / sizeof (EFI_ACPI_DMAR_PCI_PATH);
    PciPath = (EFI_ACPI_DMAR_PCI_PATH *) (DmarDeviceScopeEntry + 1);
    for (PciPathIndex = 0; PciPathIndex < PciPathNumber; PciPathIndex++) {
      DEBUG ((DEBUG_INFO,
        "      Device ............................................. 0x%02x\n",
        PciPath[PciPathIndex].Device
        ));
      DEBUG ((DEBUG_INFO,
        "      Function ........................................... 0x%02x\n",
        PciPath[PciPathIndex].Function
        ));
    }
  
    DEBUG ((DEBUG_INFO,
      "    *************************************************************************\n\n"
      ));
  
    return;
  }
  
  /**
    Dump DMAR RMRR table.
  
    @param[in]  Rmrr              DMAR RMRR table
  **/
  VOID
  DumpDmarRmrr (
    IN EFI_ACPI_DMAR_RMRR_HEADER  *Rmrr
    )
  {
    EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER     *DmarDeviceScopeEntry;
    INTN                                            RmrrLen;
  
    if (Rmrr == NULL) {
      return;
    }
  
    DEBUG ((DEBUG_INFO,
      "  ***************************************************************************\n"
      ));
    DEBUG ((DEBUG_INFO,
      "  *       Reserved Memory Region Reporting Structure                        *\n"
      ));
    DEBUG ((DEBUG_INFO,
      "  ***************************************************************************\n"
      ));
    DEBUG ((DEBUG_INFO,
      (sizeof (UINTN) == sizeof (UINT64)) ?
      "  RMRR address ........................................... 0x%016lx\n" :
      "  RMRR address ........................................... 0x%08x\n",
      Rmrr
      ));
    DEBUG ((DEBUG_INFO,
      "    Type ................................................. 0x%04x\n",
      Rmrr->Header.Type
      ));
    DEBUG ((DEBUG_INFO,
      "    Length ............................................... 0x%04x\n",
      Rmrr->Header.Length
      ));
    DEBUG ((DEBUG_INFO,
      "    Segment Number ....................................... 0x%04x\n",
      Rmrr->SegmentNumber
      ));
    DEBUG ((DEBUG_INFO,
      "    Reserved Memory Region Base Address .................. 0x%016lx\n",
      Rmrr->ReservedMemoryRegionBaseAddress
      ));
    DEBUG ((DEBUG_INFO,
      "    Reserved Memory Region Limit Address ................. 0x%016lx\n",
      Rmrr->ReservedMemoryRegionLimitAddress
      ));
  
    RmrrLen  = Rmrr->Header.Length - sizeof(EFI_ACPI_DMAR_RMRR_HEADER);
    DmarDeviceScopeEntry = (EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER *) (Rmrr + 1);
    while (RmrrLen > 0) {
      DumpDmarDeviceScopeEntry (DmarDeviceScopeEntry);
      RmrrLen -= DmarDeviceScopeEntry->Length;
      DmarDeviceScopeEntry = (EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER *) ((UINTN) DmarDeviceScopeEntry + DmarDeviceScopeEntry->Length);
    }
  
    DEBUG ((DEBUG_INFO,
      "  ***************************************************************************\n\n"
      ));
  
    return;
  }
  
  /**
    Dump DMAR DRHD table.
  
    @param[in]  Drhd              DMAR DRHD table
  **/
  VOID
  DumpDmarDrhd (
    IN EFI_ACPI_DMAR_DRHD_HEADER  *Drhd
    )
  {
    EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER     *DmarDeviceScopeEntry;
    INTN                                            DrhdLen;
  
    if (Drhd == NULL) {
      return;
    }
  
    DEBUG ((DEBUG_INFO,
      "  ***************************************************************************\n"
      ));
    DEBUG ((DEBUG_INFO,
      "  *       DMA-Remapping Hardware Definition Structure                       *\n"
      ));
    DEBUG ((DEBUG_INFO,
      "  ***************************************************************************\n"
      ));
    DEBUG ((DEBUG_INFO,
      (sizeof (UINTN) == sizeof (UINT64)) ?
      "  DRHD address ........................................... 0x%016lx\n" :
      "  DRHD address ........................................... 0x%08x\n",
      Drhd
      ));
    DEBUG ((DEBUG_INFO,
      "    Type ................................................. 0x%04x\n",
      Drhd->Header.Type
      ));
    DEBUG ((DEBUG_INFO,
      "    Length ............................................... 0x%04x\n",
      Drhd->Header.Length
      ));
    DEBUG ((DEBUG_INFO,
      "    Flags ................................................ 0x%02x\n",
      Drhd->Flags
      ));
    DEBUG ((DEBUG_INFO,
      "      INCLUDE_PCI_ALL .................................... 0x%02x\n",
      Drhd->Flags & EFI_ACPI_DMAR_DRHD_FLAGS_INCLUDE_PCI_ALL
      ));
    DEBUG ((DEBUG_INFO,
      "    Segment Number ....................................... 0x%04x\n",
      Drhd->SegmentNumber
      ));
    DEBUG ((DEBUG_INFO,
      "    Register Base Address ................................ 0x%016lx\n",
      Drhd->RegisterBaseAddress
      ));
  
    DrhdLen  = Drhd->Header.Length - sizeof (EFI_ACPI_DMAR_DRHD_HEADER);
    DmarDeviceScopeEntry = (EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER *) (Drhd + 1);
    while (DrhdLen > 0) {
      DumpDmarDeviceScopeEntry (DmarDeviceScopeEntry);
      DrhdLen -= DmarDeviceScopeEntry->Length;
      DmarDeviceScopeEntry = (EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER *) ((UINTN) DmarDeviceScopeEntry + DmarDeviceScopeEntry->Length);
    }
  
    DEBUG ((DEBUG_INFO,
      "  ***************************************************************************\n\n"
      ));
  
    return;
  }
  
  /**
    Dump DMAR ACPI table.
  
    @param[in]  Dmar              DMAR ACPI table
  **/
  VOID
  DumpAcpiDMAR (
    IN EFI_ACPI_DMAR_HEADER       *Dmar
    )
  {
    EFI_ACPI_DMAR_STRUCTURE_HEADER        *DmarHeader;
    INTN                                  DmarLen;
  
    if (Dmar == NULL) {
      return;
    }
  
    //
    // Dump Dmar table
    //
    DEBUG ((DEBUG_INFO,
      "*****************************************************************************\n"
      ));
    DEBUG ((DEBUG_INFO,
      "*         DMAR Table                                                        *\n"
      ));
    DEBUG ((DEBUG_INFO,
      "*****************************************************************************\n"
      ));
  
    DEBUG ((DEBUG_INFO,
      (sizeof (UINTN) == sizeof (UINT64)) ?
      "DMAR address ............................................. 0x%016lx\n" :
      "DMAR address ............................................. 0x%08x\n",
      Dmar
      ));
  
    DEBUG ((DEBUG_INFO,
      "  Table Contents:\n"
      ));
    DEBUG ((DEBUG_INFO,
      "    Host Address Width ................................... 0x%02x\n",
      Dmar->HostAddressWidth
      ));
    DEBUG ((DEBUG_INFO,
      "    Flags ................................................ 0x%02x\n",
      Dmar->Flags
      ));
    DEBUG ((DEBUG_INFO,
      "      INTR_REMAP ......................................... 0x%02x\n",
      Dmar->Flags & EFI_ACPI_DMAR_FLAGS_INTR_REMAP
      ));
    DEBUG ((DEBUG_INFO,
      "      X2APIC_OPT_OUT_SET ................................. 0x%02x\n",
      Dmar->Flags & EFI_ACPI_DMAR_FLAGS_X2APIC_OPT_OUT
      ));
    DEBUG ((DEBUG_INFO,
      "      DMA_CTRL_PLATFORM_OPT_IN_FLAG ...................... 0x%02x\n",
      Dmar->Flags & EFI_ACPI_DMAR_FLAGS_DMA_CTRL_PLATFORM_OPT_IN_FLAG
      ));
  
    DmarLen  = Dmar->Header.Length - sizeof (EFI_ACPI_DMAR_HEADER);
    DmarHeader = (EFI_ACPI_DMAR_STRUCTURE_HEADER *) (Dmar + 1);
    while (DmarLen > 0) {
      switch (DmarHeader->Type) {
      case EFI_ACPI_DMAR_TYPE_DRHD:
        DumpDmarDrhd ((EFI_ACPI_DMAR_DRHD_HEADER *) DmarHeader);
        break;
      case EFI_ACPI_DMAR_TYPE_RMRR:
        DumpDmarRmrr ((EFI_ACPI_DMAR_RMRR_HEADER *) DmarHeader);
        break;
      default:
        break;
      }
      DmarLen -= DmarHeader->Length;
      DmarHeader = (EFI_ACPI_DMAR_STRUCTURE_HEADER *) ((UINTN) DmarHeader + DmarHeader->Length);
    }
  
    DEBUG ((DEBUG_INFO,
      "*****************************************************************************\n\n"
      ));
  
    return;
  }
  
  /**
    Get VTd engine number.
  
    @param[in]  AcpiDmarTable     DMAR ACPI table
  
    @return the VTd engine number.
  **/
  UINTN
  GetVtdEngineNumber (
    IN EFI_ACPI_DMAR_HEADER       *AcpiDmarTable
    )
  {
    EFI_ACPI_DMAR_STRUCTURE_HEADER        *DmarHeader;
    UINTN                                 VtdIndex;
  
    VtdIndex = 0;
    DmarHeader = (EFI_ACPI_DMAR_STRUCTURE_HEADER *) ((UINTN) (AcpiDmarTable + 1));
    while ((UINTN) DmarHeader < (UINTN) AcpiDmarTable + AcpiDmarTable->Header.Length) {
      switch (DmarHeader->Type) {
      case EFI_ACPI_DMAR_TYPE_DRHD:
        VtdIndex++;
        break;
      default:
        break;
      }
      DmarHeader = (EFI_ACPI_DMAR_STRUCTURE_HEADER *) ((UINTN) DmarHeader + DmarHeader->Length);
    }
    return VtdIndex ;
  }
  
  /**
    Get PCI device information from DMAR DevScopeEntry.
  
    @param[in]  Segment           The segment number.
    @param[in]  DmarDevScopeEntry DMAR DevScopeEntry
    @param[out] Bus               The bus number.
    @param[out] Device            The device number.
    @param[out] Function          The function number.
  
    @retval EFI_SUCCESS  The PCI device information is returned.
  **/
  EFI_STATUS
  GetPciBusDeviceFunction (
    IN  UINT16                                      Segment,
    IN  EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER *DmarDevScopeEntry,
    OUT UINT8                                       *Bus,
    OUT UINT8                                       *Device,
    OUT UINT8                                       *Function
    )
  {
    EFI_ACPI_DMAR_PCI_PATH                          *DmarPciPath;
    UINT8                                           MyBus;
    UINT8                                           MyDevice;
    UINT8                                           MyFunction;
  
    DmarPciPath = (EFI_ACPI_DMAR_PCI_PATH *) ((UINTN) (DmarDevScopeEntry + 1));
    MyBus = DmarDevScopeEntry->StartBusNumber;
    MyDevice = DmarPciPath->Device;
    MyFunction = DmarPciPath->Function;
  
    switch (DmarDevScopeEntry->Type) {
    case EFI_ACPI_DEVICE_SCOPE_ENTRY_TYPE_PCI_ENDPOINT:
    case EFI_ACPI_DEVICE_SCOPE_ENTRY_TYPE_PCI_BRIDGE:
      while ((UINTN) DmarPciPath + sizeof (EFI_ACPI_DMAR_PCI_PATH) < (UINTN) DmarDevScopeEntry + DmarDevScopeEntry->Length) {
        MyBus = PciSegmentRead8 (PCI_SEGMENT_LIB_ADDRESS (Segment, MyBus, MyDevice, MyFunction, PCI_BRIDGE_SECONDARY_BUS_REGISTER_OFFSET));
        DmarPciPath ++;
        MyDevice = DmarPciPath->Device;
        MyFunction = DmarPciPath->Function;
      }
      break;
    case EFI_ACPI_DEVICE_SCOPE_ENTRY_TYPE_IOAPIC:
    case EFI_ACPI_DEVICE_SCOPE_ENTRY_TYPE_MSI_CAPABLE_HPET:
    case EFI_ACPI_DEVICE_SCOPE_ENTRY_TYPE_ACPI_NAMESPACE_DEVICE:
      break;
    }
  
    *Bus = MyBus;
    *Device = MyDevice;
    *Function = MyFunction;
  
    return EFI_SUCCESS;
  }
  
  /**
    Return the index of PCI data.
  
    @param[in]  VTdUnitInfo       The VTd engine unit information.
    @param[in]  Segment           The Segment used to identify a VTd engine.
    @param[in]  SourceId          The SourceId used to identify a VTd engine and table entry.
  
    @return The index of the PCI data.
    @retval (UINTN)-1  The PCI data is not found.
  **/
  UINTN
  GetPciDataIndex (
    IN VTD_UNIT_INFO              *VTdUnitInfo,
    IN UINT16                     Segment,
    IN VTD_SOURCE_ID              SourceId
    )
  {
    UINTN                         Index;
    VTD_SOURCE_ID                 *PciSourceId;
    PEI_PCI_DEVICE_DATA           *PciDeviceDataBase;
  
    if (Segment != VTdUnitInfo->Segment) {
      return (UINTN)-1;
    }
  
    for (Index = 0; Index < VTdUnitInfo->PciDeviceInfo.PciDeviceDataNumber; Index++) {
      PciDeviceDataBase = (PEI_PCI_DEVICE_DATA*) (UINTN) VTdUnitInfo->PciDeviceInfo.PciDeviceData;
      PciSourceId = &PciDeviceDataBase[Index].PciSourceId;
      if ((PciSourceId->Bits.Bus == SourceId.Bits.Bus) &&
          (PciSourceId->Bits.Device == SourceId.Bits.Device) &&
          (PciSourceId->Bits.Function == SourceId.Bits.Function) ) {
        return Index;
      }
    }
  
    return (UINTN)-1;
  }
  
  
  /**
    Register PCI device to VTd engine.
  
    @param[in]  VTdUnitInfo       The VTd engine unit information.
    @param[in]  Segment           The segment of the source.
    @param[in]  SourceId          The SourceId of the source.
    @param[in]  DeviceType        The DMAR device scope type.
    @param[in]  CheckExist        TRUE: ERROR will be returned if the PCI device is already registered.
                                  FALSE: SUCCESS will be returned if the PCI device is registered.
  
    @retval EFI_SUCCESS           The PCI device is registered.
    @retval EFI_OUT_OF_RESOURCES  No enough resource to register a new PCI device.
    @retval EFI_ALREADY_STARTED   The device is already registered.
  
  **/
  EFI_STATUS
  RegisterPciDevice (
    IN VTD_UNIT_INFO              *VTdUnitInfo,
    IN UINT16                     Segment,
    IN VTD_SOURCE_ID              SourceId,
    IN UINT8                      DeviceType,
    IN BOOLEAN                    CheckExist
    )
  {
    PEI_PCI_DEVICE_INFORMATION    *PciDeviceInfo;
    VTD_SOURCE_ID                 *PciSourceId;
    UINTN                         PciDataIndex;
    UINTN                         PciDeviceDataSize;
    PEI_PCI_DEVICE_DATA           *NewPciDeviceData;
    PEI_PCI_DEVICE_DATA           *PciDeviceDataBase;
  
    PciDeviceInfo = &VTdUnitInfo->PciDeviceInfo;
  
    PciDataIndex = GetPciDataIndex (VTdUnitInfo, Segment, SourceId);
    if (PciDataIndex == (UINTN)-1) {
      //
      // Register new
      //
  
      if (PciDeviceInfo->PciDeviceDataNumber >= PciDeviceInfo->PciDeviceDataMaxNumber) {
        //
        // Reallocate
        //
        PciDeviceDataSize = sizeof(*NewPciDeviceData) * (PciDeviceInfo->PciDeviceDataMaxNumber + MAX_VTD_PCI_DATA_NUMBER);
        DEBUG ((DEBUG_INFO, "New PciDeviceDataSize:%d Page:%d\n", PciDeviceDataSize, EFI_SIZE_TO_PAGES (PciDeviceDataSize)));
        NewPciDeviceData = AllocateZeroPages (EFI_SIZE_TO_PAGES(PciDeviceDataSize));
        if (NewPciDeviceData == NULL) {
          return EFI_OUT_OF_RESOURCES;
        }
        PciDeviceInfo->PciDeviceDataMaxNumber += MAX_VTD_PCI_DATA_NUMBER;
        if (PciDeviceInfo->PciDeviceData != 0) {
          CopyMem (NewPciDeviceData, (VOID *) (UINTN) PciDeviceInfo->PciDeviceData, sizeof (*NewPciDeviceData) * PciDeviceInfo->PciDeviceDataNumber);
          FreePages((VOID *) (UINTN) PciDeviceInfo->PciDeviceData, PciDeviceInfo->PciDeviceDataPageSize);
        }
        PciDeviceInfo->PciDeviceData = (UINT32) (UINTN) NewPciDeviceData;
        PciDeviceInfo->PciDeviceDataPageSize = (UINT32) EFI_SIZE_TO_PAGES (PciDeviceDataSize);
      }
  
      ASSERT (PciDeviceInfo->PciDeviceDataNumber < PciDeviceInfo->PciDeviceDataMaxNumber);
  
      PciDeviceDataBase = (PEI_PCI_DEVICE_DATA *) (UINTN) PciDeviceInfo->PciDeviceData;
      PciSourceId = &PciDeviceDataBase[PciDeviceInfo->PciDeviceDataNumber].PciSourceId;
      PciSourceId->Bits.Bus = SourceId.Bits.Bus;
      PciSourceId->Bits.Device = SourceId.Bits.Device;
      PciSourceId->Bits.Function = SourceId.Bits.Function;
  
      DEBUG ((DEBUG_INFO, "  RegisterPciDevice: PCI S%04x B%02x D%02x F%02x", Segment, SourceId.Bits.Bus, SourceId.Bits.Device, SourceId.Bits.Function));
  
      PciDeviceDataBase[PciDeviceInfo->PciDeviceDataNumber].DeviceType = DeviceType;
  
      if ((DeviceType != EFI_ACPI_DEVICE_SCOPE_ENTRY_TYPE_PCI_ENDPOINT) &&
          (DeviceType != EFI_ACPI_DEVICE_SCOPE_ENTRY_TYPE_PCI_BRIDGE)) {
        DEBUG ((DEBUG_INFO, " (*)"));
      }
      DEBUG ((DEBUG_INFO, "\n"));
  
      PciDeviceInfo->PciDeviceDataNumber++;
    } else {
      if (CheckExist) {
        DEBUG ((DEBUG_INFO, "  RegisterPciDevice: PCI S%04x B%02x D%02x F%02x already registered\n", Segment, SourceId.Bits.Bus, SourceId.Bits.Device, SourceId.Bits.Function));
        return EFI_ALREADY_STARTED;
      }
    }
  
    return EFI_SUCCESS;
  }
  
  /**
    Process DMAR DHRD table.
  
    @param[in]  VTdUnitInfo       The VTd engine unit information.
    @param[in]  DmarDrhd          The DRHD table.
  
  **/
  VOID
  ProcessDhrd (
    IN VTD_UNIT_INFO              *VTdUnitInfo,
    IN EFI_ACPI_DMAR_DRHD_HEADER  *DmarDrhd
    )
  {
    EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER     *DmarDevScopeEntry;
    UINT8                                           Bus;
    UINT8                                           Device;
    UINT8                                           Function;
    EFI_STATUS                                      Status;
    VTD_SOURCE_ID                                   SourceId;
  
    DEBUG ((DEBUG_INFO,"  VTD BaseAddress -  0x%016lx\n", DmarDrhd->RegisterBaseAddress));
    VTdUnitInfo->VtdUnitBaseAddress = (UINT32) DmarDrhd->RegisterBaseAddress;
  
    DEBUG ((DEBUG_INFO,"  VTD Segment - %d\n", DmarDrhd->SegmentNumber));
    VTdUnitInfo->Segment = DmarDrhd->SegmentNumber;
  
    VTdUnitInfo->FixedSecondLevelPagingEntry = 0;
    VTdUnitInfo->RmrrSecondLevelPagingEntry = 0;
    VTdUnitInfo->RootEntryTable = 0;
    VTdUnitInfo->ExtRootEntryTable = 0;
    VTdUnitInfo->RootEntryTablePageSize = 0;
    VTdUnitInfo->ExtRootEntryTablePageSize = 0;
  
    VTdUnitInfo->PciDeviceInfo.IncludeAllFlag = 0;
    VTdUnitInfo->PciDeviceInfo.PciDeviceDataMaxNumber = 0;
    VTdUnitInfo->PciDeviceInfo.PciDeviceDataNumber = 0;
    VTdUnitInfo->PciDeviceInfo.PciDeviceDataPageSize = 0;
    VTdUnitInfo->PciDeviceInfo.PciDeviceData = 0;
  
    if ((DmarDrhd->Flags & EFI_ACPI_DMAR_DRHD_FLAGS_INCLUDE_PCI_ALL) != 0) {
      VTdUnitInfo->PciDeviceInfo.IncludeAllFlag = TRUE;
      DEBUG ((DEBUG_INFO,"  ProcessDhrd: with INCLUDE ALL\n"));
    } else {
      VTdUnitInfo->PciDeviceInfo.IncludeAllFlag = FALSE;
      DEBUG ((DEBUG_INFO,"  ProcessDhrd: without INCLUDE ALL\n"));
    }
  
    VTdUnitInfo->PciDeviceInfo.PciDeviceDataNumber = 0;
    VTdUnitInfo->PciDeviceInfo.PciDeviceDataMaxNumber = 0;
    VTdUnitInfo->PciDeviceInfo.PciDeviceDataPageSize = 0;
    VTdUnitInfo->PciDeviceInfo.PciDeviceData = 0;
  
    DmarDevScopeEntry = (EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER *) ((UINTN) (DmarDrhd + 1));
    while ((UINTN)DmarDevScopeEntry < (UINTN) DmarDrhd + DmarDrhd->Header.Length) {
  
      Status = GetPciBusDeviceFunction (DmarDrhd->SegmentNumber, DmarDevScopeEntry, &Bus, &Device, &Function);
      if (EFI_ERROR (Status)) {
        return;
      }
  
      DEBUG ((DEBUG_INFO,"  ProcessDhrd: "));
      switch (DmarDevScopeEntry->Type) {
        case EFI_ACPI_DEVICE_SCOPE_ENTRY_TYPE_PCI_ENDPOINT:
        DEBUG ((DEBUG_INFO,"PCI Endpoint"));
        break;
      case EFI_ACPI_DEVICE_SCOPE_ENTRY_TYPE_PCI_BRIDGE:
        DEBUG ((DEBUG_INFO,"PCI-PCI bridge"));
        break;
      case EFI_ACPI_DEVICE_SCOPE_ENTRY_TYPE_IOAPIC:
        DEBUG ((DEBUG_INFO,"IOAPIC"));
        break;
      case EFI_ACPI_DEVICE_SCOPE_ENTRY_TYPE_MSI_CAPABLE_HPET:
        DEBUG ((DEBUG_INFO,"MSI Capable HPET"));
        break;
      case EFI_ACPI_DEVICE_SCOPE_ENTRY_TYPE_ACPI_NAMESPACE_DEVICE:
        DEBUG ((DEBUG_INFO,"ACPI Namespace Device"));
        break;
      }
      DEBUG ((DEBUG_INFO," S%04x B%02x D%02x F%02x\n", DmarDrhd->SegmentNumber, Bus, Device, Function));
  
      SourceId.Bits.Bus = Bus;
      SourceId.Bits.Device = Device;
      SourceId.Bits.Function = Function;
  
      Status = RegisterPciDevice (VTdUnitInfo, DmarDrhd->SegmentNumber, SourceId, DmarDevScopeEntry->Type, TRUE);
      if (EFI_ERROR (Status)) {
        DEBUG ((DEBUG_ERROR,"RegisterPciDevice Failed !\n"));
      }
  
      DmarDevScopeEntry = (EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER *) ((UINTN) DmarDevScopeEntry + DmarDevScopeEntry->Length);
    }
  }
  
  /**
    Dump the PCI device information managed by this VTd engine.
  
    @param[in]  VTdInfo           The VTd engine context information.
    @param[in]  VtdIndex          The index of VTd engine.
  
  **/
  VOID
  DumpPciDeviceInfo (
    IN VTD_INFO                   *VTdInfo,
    IN UINTN                      VtdIndex
    )
  {
    UINTN                         Index;
    PEI_PCI_DEVICE_DATA           *PciDeviceDataBase;
  
    DEBUG ((DEBUG_INFO,"PCI Device Information (Number 0x%x, IncludeAll - %d):\n",
      VTdInfo->VtdUnitInfo[VtdIndex].PciDeviceInfo.PciDeviceDataNumber,
      VTdInfo->VtdUnitInfo[VtdIndex].PciDeviceInfo.IncludeAllFlag
      ));
  
    PciDeviceDataBase = (PEI_PCI_DEVICE_DATA *) (UINTN) VTdInfo->VtdUnitInfo[VtdIndex].PciDeviceInfo.PciDeviceData;
  
    for (Index = 0; Index < VTdInfo->VtdUnitInfo[VtdIndex].PciDeviceInfo.PciDeviceDataNumber; Index++) {
      DEBUG ((DEBUG_INFO,"  S%04x B%02x D%02x F%02x\n",
        VTdInfo->VtdUnitInfo[VtdIndex].Segment,
        PciDeviceDataBase[Index].PciSourceId.Bits.Bus,
        PciDeviceDataBase[Index].PciSourceId.Bits.Device,
        PciDeviceDataBase[Index].PciSourceId.Bits.Function
        ));
    }
  }
  
  /**
    Parse DMAR DRHD table.
  
    @param[in]  AcpiDmarTable     DMAR ACPI table
  
    @return EFI_SUCCESS  The DMAR DRHD table is parsed.
  
  **/
  EFI_STATUS
  ParseDmarAcpiTableDrhd (
    IN EFI_ACPI_DMAR_HEADER               *AcpiDmarTable
    )
  {
    EFI_ACPI_DMAR_STRUCTURE_HEADER        *DmarHeader;
    UINTN                                 VtdUnitNumber;
    UINTN                                 VtdIndex;
    VTD_INFO                              *VTdInfo;
  
    VtdUnitNumber = GetVtdEngineNumber (AcpiDmarTable);
    if (VtdUnitNumber == 0) {
      return EFI_UNSUPPORTED;
    }
  
    VTdInfo = BuildGuidHob (&mVTdInfoGuid, sizeof (VTD_INFO) + (VtdUnitNumber - 1) * sizeof (VTD_UNIT_INFO));
    ASSERT(VTdInfo != NULL);
    if (VTdInfo == NULL) {
      return EFI_OUT_OF_RESOURCES;
    }
  
    //
    // Initialize the engine mask to all.
    //
    VTdInfo->AcpiDmarTable    = (UINT32) (UINTN) AcpiDmarTable;
    VTdInfo->HostAddressWidth = AcpiDmarTable->HostAddressWidth;
    VTdInfo->VTdEngineCount   = (UINT32) VtdUnitNumber;
  
    VtdIndex = 0;
    DmarHeader = (EFI_ACPI_DMAR_STRUCTURE_HEADER *) ((UINTN) (AcpiDmarTable + 1));
    while ((UINTN) DmarHeader < (UINTN) AcpiDmarTable + AcpiDmarTable->Header.Length) {
      switch (DmarHeader->Type) {
      case EFI_ACPI_DMAR_TYPE_DRHD:
        ASSERT (VtdIndex < VtdUnitNumber);
        ProcessDhrd (&VTdInfo->VtdUnitInfo[VtdIndex], (EFI_ACPI_DMAR_DRHD_HEADER *) DmarHeader);
        VtdIndex++;
  
        break;
  
      default:
        break;
      }
      DmarHeader = (EFI_ACPI_DMAR_STRUCTURE_HEADER *) ((UINTN) DmarHeader + DmarHeader->Length);
    }
    ASSERT (VtdIndex == VtdUnitNumber);
  
    for (VtdIndex = 0; VtdIndex < VtdUnitNumber; VtdIndex++) {
      DumpPciDeviceInfo (VTdInfo, VtdIndex);
    }
  
    return EFI_SUCCESS;
  }
  
  
  /**
    Process DMAR RMRR table.
  
    @param[in]  VTdInfo           The VTd engine context information.
    @param[in]  DmarRmrr          The RMRR table.
  
  **/
  VOID
  ProcessRmrr (
    IN VTD_INFO                                     *VTdInfo,
    IN EFI_ACPI_DMAR_RMRR_HEADER                    *DmarRmrr
    )
  {
    EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER     *DmarDevScopeEntry;
    UINT8                                           Bus;
    UINT8                                           Device;
    UINT8                                           Function;
    EFI_STATUS                                      Status;
    VTD_SOURCE_ID                                   SourceId;
  
    DEBUG ((DEBUG_INFO,"  PEI RMRR (Base 0x%016lx, Limit 0x%016lx)\n", DmarRmrr->ReservedMemoryRegionBaseAddress, DmarRmrr->ReservedMemoryRegionLimitAddress));
  
    if ((DmarRmrr->ReservedMemoryRegionBaseAddress == 0) ||
        (DmarRmrr->ReservedMemoryRegionLimitAddress == 0)) {
      return ;
    }
  
    DmarDevScopeEntry = (EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER *) ((UINTN) (DmarRmrr + 1));
    while ((UINTN) DmarDevScopeEntry < (UINTN) DmarRmrr + DmarRmrr->Header.Length) {
      if (DmarDevScopeEntry->Type != EFI_ACPI_DEVICE_SCOPE_ENTRY_TYPE_PCI_ENDPOINT) {
        DEBUG ((DEBUG_INFO,"RMRR DevScopeEntryType is not endpoint, type[0x%x] \n", DmarDevScopeEntry->Type));
        return;
      }
  
      Status = GetPciBusDeviceFunction (DmarRmrr->SegmentNumber, DmarDevScopeEntry, &Bus, &Device, &Function);
      if (EFI_ERROR (Status)) {
        continue;
      }
  
      DEBUG ((DEBUG_INFO,"RMRR S%04x B%02x D%02x F%02x\n", DmarRmrr->SegmentNumber, Bus, Device, Function));
  
      SourceId.Bits.Bus = Bus;
      SourceId.Bits.Device = Device;
      SourceId.Bits.Function = Function;
  
      Status = EnableRmrrPageAttribute (
                 VTdInfo,
                 DmarRmrr->SegmentNumber,
                 SourceId,
                 DmarRmrr->ReservedMemoryRegionBaseAddress,
                 DmarRmrr->ReservedMemoryRegionLimitAddress,
                 EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE
                 );
      if (EFI_ERROR (Status)) {
        DEBUG ((DEBUG_INFO, "EnableRmrrPageAttribute : %r\n", Status));
      }
  
      DmarDevScopeEntry = (EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER *) ((UINTN) DmarDevScopeEntry + DmarDevScopeEntry->Length);
    }
  }
  
  /**
    Parse DMAR DRHD table.
  
    @param[in]  VTdInfo           The VTd engine context information.
  
  **/
  VOID
  ParseDmarAcpiTableRmrr (
    IN VTD_INFO                           *VTdInfo
    )
  {
    EFI_ACPI_DMAR_HEADER                  *AcpiDmarTable;
    EFI_ACPI_DMAR_STRUCTURE_HEADER        *DmarHeader;
  
    AcpiDmarTable = (EFI_ACPI_DMAR_HEADER *) (UINTN) VTdInfo->AcpiDmarTable;
  
    DmarHeader = (EFI_ACPI_DMAR_STRUCTURE_HEADER *) ((UINTN) (AcpiDmarTable + 1));
    while ((UINTN) DmarHeader < (UINTN) AcpiDmarTable + AcpiDmarTable->Header.Length) {
      switch (DmarHeader->Type) {
      case EFI_ACPI_DMAR_TYPE_RMRR:
        ProcessRmrr (VTdInfo, (EFI_ACPI_DMAR_RMRR_HEADER *) DmarHeader);
        break;
      default:
        break;
      }
      DmarHeader = (EFI_ACPI_DMAR_STRUCTURE_HEADER *) ((UINTN) DmarHeader + DmarHeader->Length);
    }
  }
  
  