]> git.proxmox.com Git - mirror_edk2.git/commitdiff
IntelSiliconPkg: Add VTd driver.
authorJiewen Yao <jiewen.yao@intel.com>
Sat, 25 Mar 2017 08:59:36 +0000 (16:59 +0800)
committerJiewen Yao <jiewen.yao@intel.com>
Thu, 27 Jul 2017 01:29:16 +0000 (09:29 +0800)
It provides AllocateBuffer/FreeBuffer/Map/Unmap function.
It also provides VTd capability yet.

Cc: Star Zeng <star.zeng@intel.com>
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Jiewen Yao <jiewen.yao@intel.com>
Reviewed-by: Star Zeng <star.zeng@intel.com>
12 files changed:
IntelSiliconPkg/IntelVTdDxe/BmDma.c [new file with mode: 0644]
IntelSiliconPkg/IntelVTdDxe/DmaProtection.c [new file with mode: 0644]
IntelSiliconPkg/IntelVTdDxe/DmaProtection.h [new file with mode: 0644]
IntelSiliconPkg/IntelVTdDxe/DmarAcpiTable.c [new file with mode: 0644]
IntelSiliconPkg/IntelVTdDxe/IntelVTdDxe.c [new file with mode: 0644]
IntelSiliconPkg/IntelVTdDxe/IntelVTdDxe.inf [new file with mode: 0644]
IntelSiliconPkg/IntelVTdDxe/IntelVTdDxe.uni [new file with mode: 0644]
IntelSiliconPkg/IntelVTdDxe/IntelVTdDxeExtra.uni [new file with mode: 0644]
IntelSiliconPkg/IntelVTdDxe/PciInfo.c [new file with mode: 0644]
IntelSiliconPkg/IntelVTdDxe/TranslationTable.c [new file with mode: 0644]
IntelSiliconPkg/IntelVTdDxe/TranslationTableEx.c [new file with mode: 0644]
IntelSiliconPkg/IntelVTdDxe/VtdReg.c [new file with mode: 0644]

diff --git a/IntelSiliconPkg/IntelVTdDxe/BmDma.c b/IntelSiliconPkg/IntelVTdDxe/BmDma.c
new file mode 100644 (file)
index 0000000..5dcee00
--- /dev/null
@@ -0,0 +1,441 @@
+/** @file\r
+  BmDma related function\r
+\r
+  Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>\r
+  This program and the accompanying materials\r
+  are licensed and made available under the terms and conditions of the BSD License\r
+  which accompanies this distribution.  The full text of the license may be found at\r
+  http://opensource.org/licenses/bsd-license.php\r
+\r
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include <PiDxe.h>\r
+\r
+#include <Protocol/IoMmu.h>\r
+\r
+#include <Library/BaseLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+\r
+// TBD: May make it a policy\r
+#define DMA_MEMORY_TOP          MAX_UINTN\r
+//#define DMA_MEMORY_TOP          0x0000000001FFFFFFULL\r
+\r
+#define MAP_INFO_SIGNATURE  SIGNATURE_32 ('D', 'M', 'A', 'P')\r
+typedef struct {\r
+  UINT32                                    Signature;\r
+  LIST_ENTRY                                Link;\r
+  EDKII_IOMMU_OPERATION                     Operation;\r
+  UINTN                                     NumberOfBytes;\r
+  UINTN                                     NumberOfPages;\r
+  EFI_PHYSICAL_ADDRESS                      HostAddress;\r
+  EFI_PHYSICAL_ADDRESS                      DeviceAddress;\r
+} MAP_INFO;\r
+#define MAP_INFO_FROM_LINK(a) CR (a, MAP_INFO, Link, MAP_INFO_SIGNATURE)\r
+\r
+LIST_ENTRY                        gMaps = INITIALIZE_LIST_HEAD_VARIABLE(gMaps);\r
+\r
+/**\r
+  Provides the controller-specific addresses required to access system memory from a\r
+  DMA bus master.\r
+\r
+  @param  This                  The protocol instance pointer.\r
+  @param  Operation             Indicates if the bus master is going to read or write to system memory.\r
+  @param  HostAddress           The system memory address to map to the PCI controller.\r
+  @param  NumberOfBytes         On input the number of bytes to map. On output the number of bytes\r
+                                that were mapped.\r
+  @param  DeviceAddress         The resulting map address for the bus master PCI controller to use to\r
+                                access the hosts HostAddress.\r
+  @param  Mapping               A resulting value to pass to Unmap().\r
+\r
+  @retval EFI_SUCCESS           The range was mapped for the returned NumberOfBytes.\r
+  @retval EFI_UNSUPPORTED       The HostAddress cannot be mapped as a common buffer.\r
+  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
+  @retval EFI_OUT_OF_RESOURCES  The request could not be completed due to a lack of resources.\r
+  @retval EFI_DEVICE_ERROR      The system hardware could not map the requested address.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IoMmuMap (\r
+  IN     EDKII_IOMMU_PROTOCOL                       *This,\r
+  IN     EDKII_IOMMU_OPERATION                      Operation,\r
+  IN     VOID                                       *HostAddress,\r
+  IN OUT UINTN                                      *NumberOfBytes,\r
+  OUT    EFI_PHYSICAL_ADDRESS                       *DeviceAddress,\r
+  OUT    VOID                                       **Mapping\r
+  )\r
+{\r
+  EFI_STATUS                                        Status;\r
+  EFI_PHYSICAL_ADDRESS                              PhysicalAddress;\r
+  MAP_INFO                                          *MapInfo;\r
+  EFI_PHYSICAL_ADDRESS                              DmaMemoryTop;\r
+  BOOLEAN                                           NeedRemap;\r
+\r
+  DEBUG ((DEBUG_VERBOSE, "IoMmuMap: ==> 0x%08x - 0x%08x (%x)\n", HostAddress, NumberOfBytes, Operation));\r
+\r
+  if (HostAddress == NULL || NumberOfBytes == NULL || DeviceAddress == NULL ||\r
+      Mapping == NULL) {\r
+    DEBUG ((DEBUG_ERROR, "IoMmuMap: %r\n", EFI_INVALID_PARAMETER));\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // Make sure that Operation is valid\r
+  //\r
+  if ((UINT32) Operation >= EdkiiIoMmuOperationMaximum) {\r
+    DEBUG ((DEBUG_ERROR, "IoMmuMap: %r\n", EFI_INVALID_PARAMETER));\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  NeedRemap = FALSE;\r
+  PhysicalAddress = (EFI_PHYSICAL_ADDRESS) (UINTN) HostAddress;\r
+\r
+  DmaMemoryTop = DMA_MEMORY_TOP;\r
+\r
+  //\r
+  // Alignment check\r
+  //\r
+  if ((*NumberOfBytes != ALIGN_VALUE(*NumberOfBytes, SIZE_4KB)) ||\r
+      (PhysicalAddress != ALIGN_VALUE(PhysicalAddress, SIZE_4KB))) {\r
+    if ((Operation == EdkiiIoMmuOperationBusMasterCommonBuffer) ||\r
+        (Operation == EdkiiIoMmuOperationBusMasterCommonBuffer64)) {\r
+      //\r
+      // The input buffer might be a subset from IoMmuAllocateBuffer.\r
+      // Skip the check.\r
+      //\r
+    } else {\r
+      NeedRemap = TRUE;\r
+    }\r
+  }\r
+\r
+  if ((PhysicalAddress + *NumberOfBytes) >= DMA_MEMORY_TOP) {\r
+    NeedRemap = TRUE;\r
+  }\r
+\r
+  if (((Operation != EdkiiIoMmuOperationBusMasterRead64 &&\r
+        Operation != EdkiiIoMmuOperationBusMasterWrite64 &&\r
+        Operation != EdkiiIoMmuOperationBusMasterCommonBuffer64)) &&\r
+      ((PhysicalAddress + *NumberOfBytes) > SIZE_4GB)) {\r
+    //\r
+    // If the root bridge or the device cannot handle performing DMA above\r
+    // 4GB but any part of the DMA transfer being mapped is above 4GB, then\r
+    // map the DMA transfer to a buffer below 4GB.\r
+    //\r
+    NeedRemap = TRUE;\r
+    DmaMemoryTop = MIN (DmaMemoryTop, SIZE_4GB - 1);\r
+  }\r
+\r
+  if (Operation == EdkiiIoMmuOperationBusMasterCommonBuffer ||\r
+      Operation == EdkiiIoMmuOperationBusMasterCommonBuffer64) {\r
+    if (NeedRemap) {\r
+      //\r
+      // Common Buffer operations can not be remapped.  If the common buffer\r
+      // if above 4GB, then it is not possible to generate a mapping, so return\r
+      // an error.\r
+      //\r
+      DEBUG ((DEBUG_ERROR, "IoMmuMap: %r\n", EFI_UNSUPPORTED));\r
+      return EFI_UNSUPPORTED;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Allocate a MAP_INFO structure to remember the mapping when Unmap() is\r
+  // called later.\r
+  //\r
+  MapInfo = AllocatePool (sizeof (MAP_INFO));\r
+  if (MapInfo == NULL) {\r
+    *NumberOfBytes = 0;\r
+    DEBUG ((DEBUG_ERROR, "IoMmuMap: %r\n", EFI_OUT_OF_RESOURCES));\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  //\r
+  // Initialize the MAP_INFO structure\r
+  //\r
+  MapInfo->Signature         = MAP_INFO_SIGNATURE;\r
+  MapInfo->Operation         = Operation;\r
+  MapInfo->NumberOfBytes     = *NumberOfBytes;\r
+  MapInfo->NumberOfPages     = EFI_SIZE_TO_PAGES (MapInfo->NumberOfBytes);\r
+  MapInfo->HostAddress       = PhysicalAddress;\r
+  MapInfo->DeviceAddress     = DmaMemoryTop;\r
+\r
+  //\r
+  // Allocate a buffer below 4GB to map the transfer to.\r
+  //\r
+  if (NeedRemap) {\r
+    Status = gBS->AllocatePages (\r
+                    AllocateMaxAddress,\r
+                    EfiBootServicesData,\r
+                    MapInfo->NumberOfPages,\r
+                    &MapInfo->DeviceAddress\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      FreePool (MapInfo);\r
+      *NumberOfBytes = 0;\r
+      DEBUG ((DEBUG_ERROR, "IoMmuMap: %r\n", Status));\r
+      return Status;\r
+    }\r
+\r
+    //\r
+    // If this is a read operation from the Bus Master's point of view,\r
+    // then copy the contents of the real buffer into the mapped buffer\r
+    // so the Bus Master can read the contents of the real buffer.\r
+    //\r
+    if (Operation == EdkiiIoMmuOperationBusMasterRead ||\r
+        Operation == EdkiiIoMmuOperationBusMasterRead64) {\r
+      CopyMem (\r
+        (VOID *) (UINTN) MapInfo->DeviceAddress,\r
+        (VOID *) (UINTN) MapInfo->HostAddress,\r
+        MapInfo->NumberOfBytes\r
+        );\r
+    }\r
+  } else {\r
+    MapInfo->DeviceAddress = MapInfo->HostAddress;\r
+  }\r
+\r
+  InsertTailList (&gMaps, &MapInfo->Link);\r
+\r
+  //\r
+  // The DeviceAddress is the address of the maped buffer below 4GB\r
+  //\r
+  *DeviceAddress = MapInfo->DeviceAddress;\r
+  //\r
+  // Return a pointer to the MAP_INFO structure in Mapping\r
+  //\r
+  *Mapping       = MapInfo;\r
+\r
+  DEBUG ((DEBUG_VERBOSE, "IoMmuMap: 0x%08x - 0x%08x <==\n", *DeviceAddress, *Mapping));\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Completes the Map() operation and releases any corresponding resources.\r
+\r
+  @param  This                  The protocol instance pointer.\r
+  @param  Mapping               The mapping value returned from Map().\r
+\r
+  @retval EFI_SUCCESS           The range was unmapped.\r
+  @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by Map().\r
+  @retval EFI_DEVICE_ERROR      The data was not committed to the target system memory.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IoMmuUnmap (\r
+  IN  EDKII_IOMMU_PROTOCOL                     *This,\r
+  IN  VOID                                     *Mapping\r
+  )\r
+{\r
+  MAP_INFO                 *MapInfo;\r
+  LIST_ENTRY               *Link;\r
+\r
+  DEBUG ((DEBUG_VERBOSE, "IoMmuUnmap: 0x%08x\n", Mapping));\r
+\r
+  if (Mapping == NULL) {\r
+    DEBUG ((DEBUG_ERROR, "IoMmuUnmap: %r\n", EFI_INVALID_PARAMETER));\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  MapInfo = NULL;\r
+  for (Link = GetFirstNode (&gMaps)\r
+       ; !IsNull (&gMaps, Link)\r
+       ; Link = GetNextNode (&gMaps, Link)\r
+       ) {\r
+    MapInfo = MAP_INFO_FROM_LINK (Link);\r
+    if (MapInfo == Mapping) {\r
+      break;\r
+    }\r
+  }\r
+  //\r
+  // Mapping is not a valid value returned by Map()\r
+  //\r
+  if (MapInfo != Mapping) {\r
+    DEBUG ((DEBUG_ERROR, "IoMmuUnmap: %r\n", EFI_INVALID_PARAMETER));\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  RemoveEntryList (&MapInfo->Link);\r
+\r
+  if (MapInfo->DeviceAddress != MapInfo->HostAddress) {\r
+    //\r
+    // If this is a write operation from the Bus Master's point of view,\r
+    // then copy the contents of the mapped buffer into the real buffer\r
+    // so the processor can read the contents of the real buffer.\r
+    //\r
+    if (MapInfo->Operation == EdkiiIoMmuOperationBusMasterWrite ||\r
+        MapInfo->Operation == EdkiiIoMmuOperationBusMasterWrite64) {\r
+      CopyMem (\r
+        (VOID *) (UINTN) MapInfo->HostAddress,\r
+        (VOID *) (UINTN) MapInfo->DeviceAddress,\r
+        MapInfo->NumberOfBytes\r
+        );\r
+    }\r
+\r
+    //\r
+    // Free the mapped buffer and the MAP_INFO structure.\r
+    //\r
+    gBS->FreePages (MapInfo->DeviceAddress, MapInfo->NumberOfPages);\r
+  }\r
+\r
+  FreePool (Mapping);\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Allocates pages that are suitable for an OperationBusMasterCommonBuffer or\r
+  OperationBusMasterCommonBuffer64 mapping.\r
+\r
+  @param  This                  The protocol instance pointer.\r
+  @param  Type                  This parameter is not used and must be ignored.\r
+  @param  MemoryType            The type of memory to allocate, EfiBootServicesData or\r
+                                EfiRuntimeServicesData.\r
+  @param  Pages                 The number of pages to allocate.\r
+  @param  HostAddress           A pointer to store the base system memory address of the\r
+                                allocated range.\r
+  @param  Attributes            The requested bit mask of attributes for the allocated range.\r
+\r
+  @retval EFI_SUCCESS           The requested memory pages were allocated.\r
+  @retval EFI_UNSUPPORTED       Attributes is unsupported. The only legal attribute bits are\r
+                                MEMORY_WRITE_COMBINE and MEMORY_CACHED.\r
+  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
+  @retval EFI_OUT_OF_RESOURCES  The memory pages could not be allocated.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IoMmuAllocateBuffer (\r
+  IN     EDKII_IOMMU_PROTOCOL                     *This,\r
+  IN     EFI_ALLOCATE_TYPE                        Type,\r
+  IN     EFI_MEMORY_TYPE                          MemoryType,\r
+  IN     UINTN                                    Pages,\r
+  IN OUT VOID                                     **HostAddress,\r
+  IN     UINT64                                   Attributes\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+  EFI_PHYSICAL_ADDRESS      PhysicalAddress;\r
+\r
+  DEBUG ((DEBUG_VERBOSE, "IoMmuAllocateBuffer: ==> 0x%08x\n", Pages));\r
+\r
+  //\r
+  // Validate Attributes\r
+  //\r
+  if ((Attributes & EDKII_IOMMU_ATTRIBUTE_INVALID_FOR_ALLOCATE_BUFFER) != 0) {\r
+    DEBUG ((DEBUG_ERROR, "IoMmuAllocateBuffer: %r\n", EFI_UNSUPPORTED));\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  //\r
+  // Check for invalid inputs\r
+  //\r
+  if (HostAddress == NULL) {\r
+    DEBUG ((DEBUG_ERROR, "IoMmuAllocateBuffer: %r\n", EFI_INVALID_PARAMETER));\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // The only valid memory types are EfiBootServicesData and\r
+  // EfiRuntimeServicesData\r
+  //\r
+  if (MemoryType != EfiBootServicesData &&\r
+      MemoryType != EfiRuntimeServicesData) {\r
+    DEBUG ((DEBUG_ERROR, "IoMmuAllocateBuffer: %r\n", EFI_INVALID_PARAMETER));\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  PhysicalAddress = DMA_MEMORY_TOP;\r
+  if ((Attributes & EDKII_IOMMU_ATTRIBUTE_DUAL_ADDRESS_CYCLE) == 0) {\r
+    //\r
+    // Limit allocations to memory below 4GB\r
+    //\r
+    PhysicalAddress = MIN (PhysicalAddress, SIZE_4GB - 1);\r
+  }\r
+  Status = gBS->AllocatePages (\r
+                  AllocateMaxAddress,\r
+                  MemoryType,\r
+                  Pages,\r
+                  &PhysicalAddress\r
+                  );\r
+  if (!EFI_ERROR (Status)) {\r
+    *HostAddress = (VOID *) (UINTN) PhysicalAddress;\r
+  }\r
+\r
+  DEBUG ((DEBUG_VERBOSE, "IoMmuAllocateBuffer: 0x%08x <==\n", *HostAddress));\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Frees memory that was allocated with AllocateBuffer().\r
+\r
+  @param  This                  The protocol instance pointer.\r
+  @param  Pages                 The number of pages to free.\r
+  @param  HostAddress           The base system memory address of the allocated range.\r
+\r
+  @retval EFI_SUCCESS           The requested memory pages were freed.\r
+  @retval EFI_INVALID_PARAMETER The memory range specified by HostAddress and Pages\r
+                                was not allocated with AllocateBuffer().\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IoMmuFreeBuffer (\r
+  IN  EDKII_IOMMU_PROTOCOL                     *This,\r
+  IN  UINTN                                    Pages,\r
+  IN  VOID                                     *HostAddress\r
+  )\r
+{\r
+  DEBUG ((DEBUG_VERBOSE, "IoMmuFreeBuffer: 0x%\n", Pages));\r
+  return gBS->FreePages ((EFI_PHYSICAL_ADDRESS) (UINTN) HostAddress, Pages);\r
+}\r
+\r
+/**\r
+  Get device information from mapping.\r
+\r
+  @param[in]  Mapping        The mapping.\r
+  @param[out] DeviceAddress  The device address of the mapping.\r
+  @param[out] NumberOfPages  The number of pages of the mapping.\r
+\r
+  @retval EFI_SUCCESS            The device information is returned.\r
+  @retval EFI_INVALID_PARAMETER  The mapping is invalid.\r
+**/\r
+EFI_STATUS\r
+GetDeviceInfoFromMapping (\r
+  IN  VOID                                     *Mapping,\r
+  OUT EFI_PHYSICAL_ADDRESS                     *DeviceAddress,\r
+  OUT UINTN                                    *NumberOfPages\r
+  )\r
+{\r
+  MAP_INFO                 *MapInfo;\r
+  LIST_ENTRY               *Link;\r
+\r
+  if (Mapping == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  MapInfo = NULL;\r
+  for (Link = GetFirstNode (&gMaps)\r
+       ; !IsNull (&gMaps, Link)\r
+       ; Link = GetNextNode (&gMaps, Link)\r
+       ) {\r
+    MapInfo = MAP_INFO_FROM_LINK (Link);\r
+    if (MapInfo == Mapping) {\r
+      break;\r
+    }\r
+  }\r
+  //\r
+  // Mapping is not a valid value returned by Map()\r
+  //\r
+  if (MapInfo != Mapping) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  *DeviceAddress = MapInfo->DeviceAddress;\r
+  *NumberOfPages = MapInfo->NumberOfPages;\r
+  return EFI_SUCCESS;\r
+}\r
+\r
diff --git a/IntelSiliconPkg/IntelVTdDxe/DmaProtection.c b/IntelSiliconPkg/IntelVTdDxe/DmaProtection.c
new file mode 100644 (file)
index 0000000..f0628b5
--- /dev/null
@@ -0,0 +1,367 @@
+/** @file\r
+\r
+  Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>\r
+  This program and the accompanying materials\r
+  are licensed and made available under the terms and conditions of the BSD License\r
+  which accompanies this distribution.  The full text of the license may be found at\r
+  http://opensource.org/licenses/bsd-license.php.\r
+\r
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "DmaProtection.h"\r
+\r
+EFI_ACPI_SDT_PROTOCOL                   *mAcpiSdt;\r
+UINT64                                  mBelow4GMemoryLimit;\r
+UINT64                                  mAbove4GMemoryLimit;\r
+\r
+EDKII_PLATFORM_VTD_POLICY_PROTOCOL      *mPlatformVTdPolicy;\r
+\r
+/**\r
+  return the UEFI memory information.\r
+\r
+  @param[out] Below4GMemoryLimit  The below 4GiB memory limit\r
+  @param[out] Above4GMemoryLimit  The above 4GiB memory limit\r
+**/\r
+VOID\r
+ReturnUefiMemoryMap (\r
+  OUT UINT64   *Below4GMemoryLimit,\r
+  OUT UINT64   *Above4GMemoryLimit\r
+  )\r
+{\r
+  EFI_STATUS                  Status;\r
+  EFI_MEMORY_DESCRIPTOR       *EfiMemoryMap;\r
+  EFI_MEMORY_DESCRIPTOR       *EfiMemoryMapEnd;\r
+  EFI_MEMORY_DESCRIPTOR       *EfiEntry;\r
+  EFI_MEMORY_DESCRIPTOR       *NextEfiEntry;\r
+  EFI_MEMORY_DESCRIPTOR       TempEfiEntry;\r
+  UINTN                       EfiMemoryMapSize;\r
+  UINTN                       EfiMapKey;\r
+  UINTN                       EfiDescriptorSize;\r
+  UINT32                      EfiDescriptorVersion;\r
+  UINT64                      MemoryBlockLength;\r
+\r
+  *Below4GMemoryLimit = 0;\r
+  *Above4GMemoryLimit = 0;\r
+\r
+  //\r
+  // Get the EFI memory map.\r
+  //\r
+  EfiMemoryMapSize  = 0;\r
+  EfiMemoryMap      = NULL;\r
+  Status = gBS->GetMemoryMap (\r
+                  &EfiMemoryMapSize,\r
+                  EfiMemoryMap,\r
+                  &EfiMapKey,\r
+                  &EfiDescriptorSize,\r
+                  &EfiDescriptorVersion\r
+                  );\r
+  ASSERT (Status == EFI_BUFFER_TOO_SMALL);\r
+\r
+  do {\r
+    //\r
+    // Use size returned back plus 1 descriptor for the AllocatePool.\r
+    // We don't just multiply by 2 since the "for" loop below terminates on\r
+    // EfiMemoryMapEnd which is dependent upon EfiMemoryMapSize. Otherwize\r
+    // we process bogus entries and create bogus E820 entries.\r
+    //\r
+    EfiMemoryMap = (EFI_MEMORY_DESCRIPTOR *) AllocatePool (EfiMemoryMapSize);\r
+    ASSERT (EfiMemoryMap != NULL);\r
+    Status = gBS->GetMemoryMap (\r
+                    &EfiMemoryMapSize,\r
+                    EfiMemoryMap,\r
+                    &EfiMapKey,\r
+                    &EfiDescriptorSize,\r
+                    &EfiDescriptorVersion\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      FreePool (EfiMemoryMap);\r
+    }\r
+  } while (Status == EFI_BUFFER_TOO_SMALL);\r
+\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  //\r
+  // Sort memory map from low to high\r
+  //\r
+  EfiEntry        = EfiMemoryMap;\r
+  NextEfiEntry    = NEXT_MEMORY_DESCRIPTOR (EfiEntry, EfiDescriptorSize);\r
+  EfiMemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) EfiMemoryMap + EfiMemoryMapSize);\r
+  while (EfiEntry < EfiMemoryMapEnd) {\r
+    while (NextEfiEntry < EfiMemoryMapEnd) {\r
+      if (EfiEntry->PhysicalStart > NextEfiEntry->PhysicalStart) {\r
+        CopyMem (&TempEfiEntry, EfiEntry, sizeof (EFI_MEMORY_DESCRIPTOR));\r
+        CopyMem (EfiEntry, NextEfiEntry, sizeof (EFI_MEMORY_DESCRIPTOR));\r
+        CopyMem (NextEfiEntry, &TempEfiEntry, sizeof (EFI_MEMORY_DESCRIPTOR));\r
+      }\r
+\r
+      NextEfiEntry = NEXT_MEMORY_DESCRIPTOR (NextEfiEntry, EfiDescriptorSize);\r
+    }\r
+\r
+    EfiEntry      = NEXT_MEMORY_DESCRIPTOR (EfiEntry, EfiDescriptorSize);\r
+    NextEfiEntry  = NEXT_MEMORY_DESCRIPTOR (EfiEntry, EfiDescriptorSize);\r
+  }\r
+\r
+  //\r
+  //\r
+  //\r
+  DEBUG ((DEBUG_INFO, "MemoryMap:\n"));\r
+  EfiEntry        = EfiMemoryMap;\r
+  EfiMemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) EfiMemoryMap + EfiMemoryMapSize);\r
+  while (EfiEntry < EfiMemoryMapEnd) {\r
+    MemoryBlockLength = (UINT64) (LShiftU64 (EfiEntry->NumberOfPages, 12));\r
+    DEBUG ((DEBUG_INFO, "Entry(0x%02x) 0x%016lx - 0x%016lx\n", EfiEntry->Type, EfiEntry->PhysicalStart, EfiEntry->PhysicalStart + MemoryBlockLength));\r
+    switch (EfiEntry->Type) {\r
+    case EfiLoaderCode:\r
+    case EfiLoaderData:\r
+    case EfiBootServicesCode:\r
+    case EfiBootServicesData:\r
+    case EfiConventionalMemory:\r
+    case EfiRuntimeServicesCode:\r
+    case EfiRuntimeServicesData:\r
+    case EfiACPIReclaimMemory:\r
+    case EfiACPIMemoryNVS:\r
+    case EfiReservedMemoryType:\r
+      if ((EfiEntry->PhysicalStart + MemoryBlockLength) <= BASE_1MB) {\r
+        //\r
+        // Skip the memory block is under 1MB\r
+        //\r
+      } else if (EfiEntry->PhysicalStart >= BASE_4GB) {\r
+        if (*Above4GMemoryLimit < EfiEntry->PhysicalStart + MemoryBlockLength) {\r
+          *Above4GMemoryLimit = EfiEntry->PhysicalStart + MemoryBlockLength;\r
+        }\r
+      } else {\r
+        if (*Below4GMemoryLimit < EfiEntry->PhysicalStart + MemoryBlockLength) {\r
+          *Below4GMemoryLimit = EfiEntry->PhysicalStart + MemoryBlockLength;\r
+        }\r
+      }\r
+      break;\r
+    }\r
+    EfiEntry = NEXT_MEMORY_DESCRIPTOR (EfiEntry, EfiDescriptorSize);\r
+  }\r
+\r
+  FreePool (EfiMemoryMap);\r
+\r
+  DEBUG ((DEBUG_INFO, "Result:\n"));\r
+  DEBUG ((DEBUG_INFO, "Below4GMemoryLimit:  0x%016lx\n", *Below4GMemoryLimit));\r
+  DEBUG ((DEBUG_INFO, "Above4GMemoryLimit:  0x%016lx\n", *Above4GMemoryLimit));\r
+\r
+  return ;\r
+}\r
+\r
+/**\r
+  Initialize platform VTd policy.\r
+**/\r
+VOID\r
+InitializePlatformVTdPolicy (\r
+  VOID\r
+  )\r
+{\r
+  EFI_STATUS                        Status;\r
+  UINTN                             DeviceInfoCount;\r
+  EDKII_PLATFORM_VTD_DEVICE_INFO    *DeviceInfo;\r
+  UINTN                             Index;\r
+\r
+  //\r
+  // It is optional.\r
+  //\r
+  Status = gBS->LocateProtocol (\r
+                  &gEdkiiPlatformVTdPolicyProtocolGuid,\r
+                  NULL,\r
+                  (VOID **)&mPlatformVTdPolicy\r
+                  );\r
+  if (!EFI_ERROR(Status)) {\r
+    Status = mPlatformVTdPolicy->GetExceptionDeviceList (mPlatformVTdPolicy, &DeviceInfoCount, &DeviceInfo);\r
+    if (!EFI_ERROR(Status)) {\r
+      for (Index = 0; Index < DeviceInfoCount; Index++) {\r
+        AlwaysEnablePageAttribute (DeviceInfo[Index].Segment, DeviceInfo[Index].SourceId);\r
+      }\r
+      FreePool (DeviceInfo);\r
+    }\r
+  }\r
+}\r
+\r
+/**\r
+  Setup VTd engine.\r
+**/\r
+VOID\r
+SetupVtd (\r
+  VOID\r
+  )\r
+{\r
+  EFI_STATUS      Status;\r
+  VOID            *PciEnumerationComplete;\r
+  UINTN           Index;\r
+  UINT64          Below4GMemoryLimit;\r
+  UINT64          Above4GMemoryLimit;\r
+\r
+  //\r
+  // PCI Enumeration must be done\r
+  //\r
+  Status = gBS->LocateProtocol (\r
+                  &gEfiPciEnumerationCompleteProtocolGuid,\r
+                  NULL,\r
+                  &PciEnumerationComplete\r
+                  );\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  ReturnUefiMemoryMap (&Below4GMemoryLimit, &Above4GMemoryLimit);\r
+  Below4GMemoryLimit = ALIGN_VALUE_UP(Below4GMemoryLimit, SIZE_256MB);\r
+  DEBUG ((DEBUG_INFO, " Adjusted Below4GMemoryLimit: 0x%016lx\n", Below4GMemoryLimit));\r
+\r
+  mBelow4GMemoryLimit = Below4GMemoryLimit;\r
+  mAbove4GMemoryLimit = Above4GMemoryLimit;\r
+\r
+  //\r
+  // 1. setup\r
+  //\r
+  DEBUG ((DEBUG_INFO, "GetDmarAcpiTable\n"));\r
+  Status = GetDmarAcpiTable ();\r
+  if (EFI_ERROR (Status)) {\r
+    return;\r
+  }\r
+  DEBUG ((DEBUG_INFO, "ParseDmarAcpiTable\n"));\r
+  Status = ParseDmarAcpiTableDrhd ();\r
+  if (EFI_ERROR (Status)) {\r
+    return;\r
+  }\r
+  DEBUG ((DEBUG_INFO, "PrepareVtdConfig\n"));\r
+  PrepareVtdConfig ();\r
+\r
+  //\r
+  // 2. initialization\r
+  //\r
+  DEBUG ((DEBUG_INFO, "SetupTranslationTable\n"));\r
+  Status = SetupTranslationTable ();\r
+  if (EFI_ERROR (Status)) {\r
+    return;\r
+  }\r
+\r
+  InitializePlatformVTdPolicy ();\r
+\r
+  ParseDmarAcpiTableRmrr ();\r
+\r
+  for (Index = 0; Index < mVtdUnitNumber; Index++) {\r
+    DEBUG ((DEBUG_INFO,"VTD Unit %d (Segment: %04x)\n", Index, mVtdUnitInformation[Index].Segment));\r
+    if (mVtdUnitInformation[Index].ExtRootEntryTable != NULL) {\r
+      DumpDmarExtContextEntryTable (mVtdUnitInformation[Index].ExtRootEntryTable);\r
+    }\r
+    if (mVtdUnitInformation[Index].RootEntryTable != NULL) {\r
+      DumpDmarContextEntryTable (mVtdUnitInformation[Index].RootEntryTable);\r
+    }\r
+  }\r
+\r
+  //\r
+  // 3. enable\r
+  //\r
+  DEBUG ((DEBUG_INFO, "EnableDmar\n"));\r
+  Status = EnableDmar ();\r
+  if (EFI_ERROR (Status)) {\r
+    return;\r
+  }\r
+  DEBUG ((DEBUG_INFO, "DumpVtdRegs\n"));\r
+  DumpVtdRegsAll ();\r
+}\r
+\r
+/**\r
+  ACPI notification function.\r
+\r
+  @param[in] Table    A pointer to the ACPI table header.\r
+  @param[in] Version  The ACPI table's version.\r
+  @param[in] TableKey The table key for this ACPI table.\r
+\r
+  @retval EFI_SUCCESS The notification function is executed.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AcpiNotificationFunc (\r
+  IN EFI_ACPI_SDT_HEADER    *Table,\r
+  IN EFI_ACPI_TABLE_VERSION Version,\r
+  IN UINTN                  TableKey\r
+  )\r
+{\r
+  if (Table->Signature == EFI_ACPI_4_0_DMA_REMAPPING_TABLE_SIGNATURE) {\r
+    DEBUG((DEBUG_INFO, "Vtd AcpiNotificationFunc\n"));\r
+    SetupVtd ();\r
+  }\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Exit boot service callback function.\r
+\r
+  @param[in]  Event    The event handle.\r
+  @param[in]  Context  The event content.\r
+**/\r
+VOID\r
+EFIAPI\r
+OnExitBootServices (\r
+  IN EFI_EVENT                               Event,\r
+  IN VOID                                    *Context\r
+  )\r
+{\r
+  DEBUG ((DEBUG_INFO, "Vtd OnExitBootServices\n"));\r
+  DumpVtdRegsAll ();\r
+  DisableDmar ();\r
+  DumpVtdRegsAll ();\r
+}\r
+\r
+/**\r
+  Legacy boot callback function.\r
+\r
+  @param[in]  Event    The event handle.\r
+  @param[in]  Context  The event content.\r
+**/\r
+VOID\r
+EFIAPI\r
+OnLegacyBoot (\r
+  EFI_EVENT                               Event,\r
+  VOID                                    *Context\r
+  )\r
+{\r
+  DEBUG ((DEBUG_INFO, "Vtd OnLegacyBoot\n"));\r
+  DumpVtdRegsAll ();\r
+  DisableDmar ();\r
+  DumpVtdRegsAll ();\r
+}\r
+\r
+/**\r
+  Initialize DMA protection.\r
+**/\r
+VOID\r
+InitializeDmaProtection (\r
+  VOID\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+  EFI_EVENT   ExitBootServicesEvent;\r
+  EFI_EVENT   LegacyBootEvent;\r
+\r
+  Status = gBS->LocateProtocol (&gEfiAcpiSdtProtocolGuid, NULL, (VOID **) &mAcpiSdt);\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  Status = mAcpiSdt->RegisterNotify (TRUE, AcpiNotificationFunc);\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  Status = gBS->CreateEventEx (\r
+                  EVT_NOTIFY_SIGNAL,\r
+                  TPL_NOTIFY,\r
+                  OnExitBootServices,\r
+                  NULL,\r
+                  &gEfiEventExitBootServicesGuid,\r
+                  &ExitBootServicesEvent\r
+                  );\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  Status = EfiCreateEventLegacyBootEx (\r
+             TPL_NOTIFY,\r
+             OnLegacyBoot,\r
+             NULL,\r
+             &LegacyBootEvent\r
+             );\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  return ;\r
+}\r
diff --git a/IntelSiliconPkg/IntelVTdDxe/DmaProtection.h b/IntelSiliconPkg/IntelVTdDxe/DmaProtection.h
new file mode 100644 (file)
index 0000000..6efed6e
--- /dev/null
@@ -0,0 +1,501 @@
+/** @file\r
+\r
+  Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>\r
+  This program and the accompanying materials\r
+  are licensed and made available under the terms and conditions of the BSD License\r
+  which accompanies this distribution.  The full text of the license may be found at\r
+  http://opensource.org/licenses/bsd-license.php.\r
+\r
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef _DMAR_PROTECTION_H_\r
+#define _DMAR_PROTECTION_H_\r
+\r
+#include <Uefi.h>\r
+#include <PiDxe.h>\r
+\r
+#include <Library/BaseLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/IoLib.h>\r
+#include <Library/PciSegmentLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/UefiLib.h>\r
+\r
+#include <Guid/EventGroup.h>\r
+#include <Guid/Acpi.h>\r
+\r
+#include <Protocol/DxeSmmReadyToLock.h>\r
+#include <Protocol/PciRootBridgeIo.h>\r
+#include <Protocol/PciIo.h>\r
+#include <Protocol/PciEnumerationComplete.h>\r
+#include <Protocol/AcpiSystemDescriptionTable.h>\r
+#include <Protocol/PlatformVtdPolicy.h>\r
+#include <Protocol/IoMmu.h>\r
+\r
+#include <IndustryStandard/Pci.h>\r
+#include <IndustryStandard/DmaRemappingReportingTable.h>\r
+#include <IndustryStandard/Vtd.h>\r
+\r
+#define ALIGN_VALUE_UP(Value, Alignment)  (((Value) + (Alignment) - 1) & (~((Alignment) - 1)))\r
+#define ALIGN_VALUE_LOW(Value, Alignment) ((Value) & (~((Alignment) - 1)))\r
+\r
+//\r
+// This is the initial max PCI descriptor.\r
+// The number may be enlarged later.\r
+//\r
+#define MAX_PCI_DESCRIPTORS             0x100\r
+\r
+typedef struct {\r
+  BOOLEAN                IncludeAllFlag;\r
+  UINTN                  PciDescriptorNumber;\r
+  UINTN                  PciDescriptorMaxNumber;\r
+  BOOLEAN                *IsRealPciDevice;\r
+  VTD_SOURCE_ID          *PciDescriptors;\r
+} PCI_DEVICE_INFORMATION;\r
+\r
+typedef struct {\r
+  UINTN                            VtdUnitBaseAddress;\r
+  UINT16                           Segment;\r
+  VTD_CAP_REG                      CapReg;\r
+  VTD_ECAP_REG                     ECapReg;\r
+  VTD_ROOT_ENTRY                   *RootEntryTable;\r
+  VTD_EXT_ROOT_ENTRY               *ExtRootEntryTable;\r
+  VTD_SECOND_LEVEL_PAGING_ENTRY    *FixedSecondLevelPagingEntry;\r
+  BOOLEAN                          HasDirtyPages;\r
+  PCI_DEVICE_INFORMATION           PciDeviceInfo;\r
+} VTD_UNIT_INFORMATION;\r
+\r
+extern EFI_ACPI_DMAR_HEADER  *mAcpiDmarTable;\r
+\r
+extern UINT64                           mVtdHostAddressWidthMask;\r
+extern UINTN                            mVtdUnitNumber;\r
+extern VTD_UNIT_INFORMATION             *mVtdUnitInformation;\r
+\r
+extern UINT64                           mBelow4GMemoryLimit;\r
+extern UINT64                           mAbove4GMemoryLimit;\r
+\r
+extern EDKII_PLATFORM_VTD_POLICY_PROTOCOL   *mPlatformVTdPolicy;\r
+\r
+/**\r
+  Prepare VTD configuration.\r
+**/\r
+VOID\r
+PrepareVtdConfig (\r
+  VOID\r
+  );\r
+\r
+/**\r
+  Setup VTd translation table.\r
+\r
+  @retval EFI_SUCCESS           Setup translation table successfully.\r
+  @retval EFI_OUT_OF_RESOURCE   Setup translation table fail.\r
+**/\r
+EFI_STATUS\r
+SetupTranslationTable (\r
+  VOID\r
+  );\r
+\r
+/**\r
+  Enable DMAR translation.\r
+\r
+  @retval EFI_SUCCESS           DMAR translation is enabled.\r
+  @retval EFI_DEVICE_ERROR      DMAR translation is not enabled.\r
+**/\r
+EFI_STATUS\r
+EnableDmar (\r
+  VOID\r
+  );\r
+\r
+/**\r
+  Disable DMAR translation.\r
+\r
+  @retval EFI_SUCCESS           DMAR translation is disabled.\r
+  @retval EFI_DEVICE_ERROR      DMAR translation is not disabled.\r
+**/\r
+EFI_STATUS\r
+DisableDmar (\r
+  VOID\r
+  );\r
+\r
+/**\r
+  Invalid VTd IOTLB page.\r
+\r
+  @param[in]  VtdIndex              The index of VTd engine.\r
+  @param[in]  Address               The address of IOTLB page.\r
+  @param[in]  AddressMode           The address mode of IOTLB page.\r
+  @param[in]  DomainIdentifier      The domain ID of the source.\r
+\r
+  @retval EFI_SUCCESS           VTd IOTLB page is invalidated.\r
+  @retval EFI_DEVICE_ERROR      VTd IOTLB page is not invalidated.\r
+**/\r
+EFI_STATUS\r
+InvalidateVtdIOTLBPage (\r
+  IN UINTN  VtdIndex,\r
+  IN UINT64 Address,\r
+  IN UINT8  AddressMode,\r
+  IN UINT16 DomainIdentifier\r
+  );\r
+\r
+/**\r
+  Invalid VTd IOTLB domain.\r
+\r
+  @param[in]  VtdIndex              The index of VTd engine.\r
+  @param[in]  DomainIdentifier      The domain ID of the source.\r
+\r
+  @retval EFI_SUCCESS           VTd IOTLB domain is invalidated.\r
+  @retval EFI_DEVICE_ERROR      VTd IOTLB domain is not invalidated.\r
+**/\r
+EFI_STATUS\r
+InvalidateVtdIOTLBDomain (\r
+  IN UINTN  VtdIndex,\r
+  IN UINT16 DomainIdentifier\r
+  );\r
+\r
+/**\r
+  Invalid VTd global IOTLB.\r
+\r
+  @param[in]  VtdIndex              The index of VTd engine.\r
+\r
+  @retval EFI_SUCCESS           VTd global IOTLB is invalidated.\r
+  @retval EFI_DEVICE_ERROR      VTd global IOTLB is not invalidated.\r
+**/\r
+EFI_STATUS\r
+InvalidateVtdIOTLBGlobal (\r
+  IN UINTN  VtdIndex\r
+  );\r
+\r
+/**\r
+  Dump VTd registers.\r
+\r
+  @param[in]  VtdIndex              The index of VTd engine.\r
+**/\r
+VOID\r
+DumpVtdRegs (\r
+  IN UINTN  VtdIndex\r
+  );\r
+\r
+/**\r
+  Dump VTd registers for all VTd engine.\r
+**/\r
+VOID\r
+DumpVtdRegsAll (\r
+  VOID\r
+  );\r
+\r
+/**\r
+  Dump VTd capability registers.\r
+\r
+  @param[in]  CapReg              The capability register.\r
+**/\r
+VOID\r
+DumpVtdCapRegs (\r
+  IN VTD_CAP_REG *CapReg\r
+  );\r
+\r
+/**\r
+  Dump VTd extended capability registers.\r
+\r
+  @param[in]  ECapReg              The extended capability register.\r
+**/\r
+VOID\r
+DumpVtdECapRegs (\r
+  IN VTD_ECAP_REG *ECapReg\r
+  );\r
+\r
+/**\r
+  Register PCI device to VTd engine as PCI descriptor.\r
+\r
+  @param[in]  VtdIndex              The index of VTd engine.\r
+  @param[in]  Segment               The segment of the source.\r
+  @param[in]  SourceId              The SourceId of the source.\r
+  @param[in]  IsRealPciDevice       TRUE: It is a real PCI device.\r
+                                    FALSE: It is not a real PCI device.\r
+  @param[in]  CheckExist            TRUE: ERROR will be returned if the PCI device is already registered.\r
+                                    FALSE: SUCCESS will be returned if the PCI device is registered.\r
+\r
+  @retval EFI_SUCCESS           The PCI device is registered.\r
+  @retval EFI_OUT_OF_RESOURCES  No enough resource to register a new PCI device.\r
+  @retval EFI_ALREADY_STARTED   The device is already registered.\r
+**/\r
+EFI_STATUS\r
+RegisterPciDevice (\r
+  IN UINTN          VtdIndex,\r
+  IN UINT16         Segment,\r
+  IN VTD_SOURCE_ID  SourceId,\r
+  IN BOOLEAN        IsRealPciDevice,\r
+  IN BOOLEAN        CheckExist\r
+  );\r
+\r
+/**\r
+  Scan PCI bus and register PCI devices under the bus.\r
+\r
+  @param[in]  VtdIndex              The index of VTd engine.\r
+  @param[in]  Segment               The segment of the source.\r
+  @param[in]  Bus                   The bus of the source.\r
+\r
+  @retval EFI_SUCCESS           The PCI devices under the bus are registered.\r
+  @retval EFI_OUT_OF_RESOURCES  No enough resource to register a new PCI device.\r
+**/\r
+EFI_STATUS\r
+ScanPciBus (\r
+  IN UINTN          VtdIndex,\r
+  IN UINT16         Segment,\r
+  IN UINT8          Bus\r
+  );\r
+\r
+/**\r
+  Dump the PCI device information managed by this VTd engine.\r
+\r
+  @param[in]  VtdIndex              The index of VTd engine.\r
+**/\r
+VOID\r
+DumpPciDeviceInfo (\r
+  IN UINTN  VtdIndex\r
+  );\r
+\r
+/**\r
+  Find the VTd index by the Segment and SourceId.\r
+\r
+  @param[in]  Segment               The segment of the source.\r
+  @param[in]  SourceId              The SourceId of the source.\r
+  @param[out] ExtContextEntry       The ExtContextEntry of the source.\r
+  @param[out] ContextEntry          The ContextEntry of the source.\r
+\r
+  @return The index of the PCI descriptor.\r
+  @retval (UINTN)-1  The PCI descriptor is not found.\r
+**/\r
+UINTN\r
+FindVtdIndexByPciDevice (\r
+  IN  UINT16                  Segment,\r
+  IN  VTD_SOURCE_ID           SourceId,\r
+  OUT VTD_EXT_CONTEXT_ENTRY   **ExtContextEntry,\r
+  OUT VTD_CONTEXT_ENTRY       **ContextEntry\r
+  );\r
+\r
+/**\r
+  Get the DMAR ACPI table.\r
+\r
+  @retval EFI_SUCCESS    The DMAR ACPI table is got.\r
+  @retval EFI_NOT_FOUND  The DMAR ACPI table is not found.\r
+**/\r
+EFI_STATUS\r
+GetDmarAcpiTable (\r
+  VOID\r
+  );\r
+\r
+/**\r
+  Parse DMAR DRHD table.\r
+\r
+  @return EFI_SUCCESS  The DMAR DRHD table is parsed.\r
+**/\r
+EFI_STATUS\r
+ParseDmarAcpiTableDrhd (\r
+  VOID\r
+  );\r
+\r
+/**\r
+  Parse DMAR RMRR table.\r
+\r
+  @return EFI_SUCCESS  The DMAR RMRR table is parsed.\r
+**/\r
+EFI_STATUS\r
+ParseDmarAcpiTableRmrr (\r
+  VOID\r
+  );\r
+\r
+/**\r
+  Dump DMAR context entry table.\r
+\r
+  @param[in]  RootEntry DMAR root entry.\r
+**/\r
+VOID\r
+DumpDmarContextEntryTable (\r
+  IN VTD_ROOT_ENTRY *RootEntry\r
+  );\r
+\r
+/**\r
+  Dump DMAR extended context entry table.\r
+\r
+  @param[in]  ExtRootEntry DMAR extended root entry.\r
+**/\r
+VOID\r
+DumpDmarExtContextEntryTable (\r
+  IN VTD_EXT_ROOT_ENTRY *ExtRootEntry\r
+  );\r
+\r
+/**\r
+  Dump DMAR second level paging entry.\r
+\r
+  @param[in]  SecondLevelPagingEntry The second level paging entry.\r
+**/\r
+VOID\r
+DumpSecondLevelPagingEntry (\r
+  IN VOID *SecondLevelPagingEntry\r
+  );\r
+\r
+/**\r
+  Set VTd attribute for a system memory.\r
+\r
+  @param[in]  VtdIndex                The index used to identify a VTd engine.\r
+  @param[in]  SecondLevelPagingEntry  The second level paging entry in VTd table for the device.\r
+  @param[in]  BaseAddress             The base of device memory address to be used as the DMA memory.\r
+  @param[in]  Length                  The length of device memory address to be used as the DMA memory.\r
+  @param[in]  IoMmuAccess             The IOMMU access.\r
+\r
+  @retval EFI_SUCCESS            The IoMmuAccess is set for the memory range specified by BaseAddress and Length.\r
+  @retval EFI_INVALID_PARAMETER  BaseAddress is not IoMmu Page size aligned.\r
+  @retval EFI_INVALID_PARAMETER  Length is not IoMmu Page size aligned.\r
+  @retval EFI_INVALID_PARAMETER  Length is 0.\r
+  @retval EFI_INVALID_PARAMETER  IoMmuAccess specified an illegal combination of access.\r
+  @retval EFI_UNSUPPORTED        The bit mask of IoMmuAccess is not supported by the IOMMU.\r
+  @retval EFI_UNSUPPORTED        The IOMMU does not support the memory range specified by BaseAddress and Length.\r
+  @retval EFI_OUT_OF_RESOURCES   There are not enough resources available to modify the IOMMU access.\r
+  @retval EFI_DEVICE_ERROR       The IOMMU device reported an error while attempting the operation.\r
+**/\r
+EFI_STATUS\r
+SetPageAttribute (\r
+  IN UINTN                         VtdIndex,\r
+  IN VTD_SECOND_LEVEL_PAGING_ENTRY *SecondLevelPagingEntry,\r
+  IN UINT64                        BaseAddress,\r
+  IN UINT64                        Length,\r
+  IN UINT64                        IoMmuAccess\r
+  );\r
+\r
+/**\r
+  Set VTd attribute for a system memory.\r
+\r
+  @param[in]  Segment           The Segment used to identify a VTd engine.\r
+  @param[in]  SourceId          The SourceId used to identify a VTd engine and table entry.\r
+  @param[in]  BaseAddress       The base of device memory address to be used as the DMA memory.\r
+  @param[in]  Length            The length of device memory address to be used as the DMA memory.\r
+  @param[in]  IoMmuAccess       The IOMMU access.\r
+\r
+  @retval EFI_SUCCESS            The IoMmuAccess is set for the memory range specified by BaseAddress and Length.\r
+  @retval EFI_INVALID_PARAMETER  BaseAddress is not IoMmu Page size aligned.\r
+  @retval EFI_INVALID_PARAMETER  Length is not IoMmu Page size aligned.\r
+  @retval EFI_INVALID_PARAMETER  Length is 0.\r
+  @retval EFI_INVALID_PARAMETER  IoMmuAccess specified an illegal combination of access.\r
+  @retval EFI_UNSUPPORTED        The bit mask of IoMmuAccess is not supported by the IOMMU.\r
+  @retval EFI_UNSUPPORTED        The IOMMU does not support the memory range specified by BaseAddress and Length.\r
+  @retval EFI_OUT_OF_RESOURCES   There are not enough resources available to modify the IOMMU access.\r
+  @retval EFI_DEVICE_ERROR       The IOMMU device reported an error while attempting the operation.\r
+**/\r
+EFI_STATUS\r
+SetAccessAttribute (\r
+  IN UINT16                Segment,\r
+  IN VTD_SOURCE_ID         SourceId,\r
+  IN UINT64                BaseAddress,\r
+  IN UINT64                Length,\r
+  IN UINT64                IoMmuAccess\r
+  );\r
+\r
+/**\r
+  Return the index of PCI descriptor.\r
+\r
+  @param[in]  VtdIndex          The index used to identify a VTd engine.\r
+  @param[in]  Segment           The Segment used to identify a VTd engine.\r
+  @param[in]  SourceId          The SourceId used to identify a VTd engine and table entry.\r
+\r
+  @return The index of the PCI descriptor.\r
+  @retval (UINTN)-1  The PCI descriptor is not found.\r
+**/\r
+UINTN\r
+GetPciDescriptor (\r
+  IN UINTN          VtdIndex,\r
+  IN UINT16         Segment,\r
+  IN VTD_SOURCE_ID  SourceId\r
+  );\r
+\r
+/**\r
+  Dump VTd registers if there is error.\r
+**/\r
+VOID\r
+DumpVtdIfError (\r
+  VOID\r
+  );\r
+\r
+/**\r
+  Initialize platform VTd policy.\r
+**/\r
+VOID\r
+InitializePlatformVTdPolicy (\r
+  VOID\r
+  );\r
+\r
+/**\r
+  Always enable the VTd page attribute for the device.\r
+\r
+  @param[in]  Segment           The Segment used to identify a VTd engine.\r
+  @param[in]  SourceId          The SourceId used to identify a VTd engine and table entry.\r
+\r
+  @retval EFI_SUCCESS           The VTd entry is updated to always enable all DMA access for the specific device.\r
+**/\r
+EFI_STATUS\r
+AlwaysEnablePageAttribute (\r
+  IN UINT16                  Segment,\r
+  IN VTD_SOURCE_ID           SourceId\r
+  );\r
+\r
+/**\r
+  Convert the DeviceHandle to SourceId and Segment.\r
+\r
+  @param[in]  DeviceHandle      The device who initiates the DMA access request.\r
+  @param[out] Segment           The Segment used to identify a VTd engine.\r
+  @param[out] SourceId          The SourceId used to identify a VTd engine and table entry.\r
+\r
+  @retval EFI_SUCCESS            The Segment and SourceId are returned.\r
+  @retval EFI_INVALID_PARAMETER  DeviceHandle is an invalid handle.\r
+  @retval EFI_UNSUPPORTED        DeviceHandle is unknown by the IOMMU.\r
+**/\r
+EFI_STATUS\r
+DeviceHandleToSourceId (\r
+  IN EFI_HANDLE            DeviceHandle,\r
+  OUT UINT16               *Segment,\r
+  OUT VTD_SOURCE_ID        *SourceId\r
+  );\r
+\r
+/**\r
+  Get device information from mapping.\r
+\r
+  @param[in]  Mapping        The mapping.\r
+  @param[out] DeviceAddress  The device address of the mapping.\r
+  @param[out] NumberOfPages  The number of pages of the mapping.\r
+\r
+  @retval EFI_SUCCESS            The device information is returned.\r
+  @retval EFI_INVALID_PARAMETER  The mapping is invalid.\r
+**/\r
+EFI_STATUS\r
+GetDeviceInfoFromMapping (\r
+  IN  VOID                                     *Mapping,\r
+  OUT EFI_PHYSICAL_ADDRESS                     *DeviceAddress,\r
+  OUT UINTN                                    *NumberOfPages\r
+  );\r
+\r
+/**\r
+  Initialize DMA protection.\r
+**/\r
+VOID\r
+InitializeDmaProtection (\r
+  VOID\r
+  );\r
+\r
+/**\r
+  Allocate zero pages.\r
+\r
+  @param[in]  Pages the number of pages.\r
+\r
+  @return the page address.\r
+  @retval NULL No resource to allocate pages.\r
+**/\r
+VOID *\r
+EFIAPI\r
+AllocateZeroPages (\r
+  IN UINTN  Pages\r
+  );\r
+\r
+#endif\r
diff --git a/IntelSiliconPkg/IntelVTdDxe/DmarAcpiTable.c b/IntelSiliconPkg/IntelVTdDxe/DmarAcpiTable.c
new file mode 100644 (file)
index 0000000..84b5485
--- /dev/null
@@ -0,0 +1,998 @@
+/** @file\r
+\r
+  Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>\r
+  This program and the accompanying materials\r
+  are licensed and made available under the terms and conditions of the BSD License\r
+  which accompanies this distribution.  The full text of the license may be found at\r
+  http://opensource.org/licenses/bsd-license.php.\r
+\r
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "DmaProtection.h"\r
+\r
+#pragma pack(1)\r
+\r
+typedef struct {\r
+  EFI_ACPI_DESCRIPTION_HEADER  Header;\r
+  UINT32                       Entry;\r
+} RSDT_TABLE;\r
+\r
+typedef struct {\r
+  EFI_ACPI_DESCRIPTION_HEADER  Header;\r
+  UINT64                       Entry;\r
+} XSDT_TABLE;\r
+\r
+#pragma pack()\r
+\r
+EFI_ACPI_DMAR_HEADER  *mAcpiDmarTable;\r
+\r
+/**\r
+  Dump DMAR DeviceScopeEntry.\r
+\r
+  @param[in]  DmarDeviceScopeEntry  DMAR DeviceScopeEntry\r
+**/\r
+VOID\r
+DumpDmarDeviceScopeEntry (\r
+  IN EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER     *DmarDeviceScopeEntry\r
+  )\r
+{\r
+  UINTN   PciPathNumber;\r
+  UINTN   PciPathIndex;\r
+  EFI_ACPI_DMAR_PCI_PATH  *PciPath;\r
+\r
+  if (DmarDeviceScopeEntry == NULL) {\r
+    return;\r
+  }\r
+\r
+  DEBUG ((DEBUG_INFO,\r
+    "    *************************************************************************\n"\r
+    "    *       DMA-Remapping Device Scope Entry Structure                      *\n"\r
+    "    *************************************************************************\n"\r
+    ));\r
+  DEBUG ((DEBUG_INFO,\r
+    (sizeof(UINTN) == sizeof(UINT64)) ?\r
+    "    DMAR Device Scope Entry address ...................... 0x%016lx\n" :\r
+    "    DMAR Device Scope Entry address ...................... 0x%08x\n",\r
+    DmarDeviceScopeEntry\r
+    ));\r
+  DEBUG ((DEBUG_INFO,\r
+    "      Device Scope Entry Type ............................ 0x%02x\n",\r
+    DmarDeviceScopeEntry->Type\r
+    ));\r
+  switch (DmarDeviceScopeEntry->Type) {\r
+  case EFI_ACPI_DEVICE_SCOPE_ENTRY_TYPE_PCI_ENDPOINT:\r
+    DEBUG ((DEBUG_INFO,\r
+      "        PCI Endpoint Device\n"\r
+      ));\r
+    break;\r
+  case EFI_ACPI_DEVICE_SCOPE_ENTRY_TYPE_PCI_BRIDGE:\r
+    DEBUG ((DEBUG_INFO,\r
+      "        PCI Sub-hierachy\n"\r
+      ));\r
+    break;\r
+  case EFI_ACPI_DEVICE_SCOPE_ENTRY_TYPE_IOAPIC:\r
+    DEBUG ((DEBUG_INFO,\r
+      "        IOAPIC\n"\r
+      ));\r
+    break;\r
+  case EFI_ACPI_DEVICE_SCOPE_ENTRY_TYPE_MSI_CAPABLE_HPET:\r
+    DEBUG ((DEBUG_INFO,\r
+      "        MSI Capable HPET\n"\r
+      ));\r
+    break;\r
+  case EFI_ACPI_DEVICE_SCOPE_ENTRY_TYPE_ACPI_NAMESPACE_DEVICE:\r
+    DEBUG ((DEBUG_INFO,\r
+      "        ACPI Namespace Device\n"\r
+      ));\r
+    break;\r
+  default:\r
+    break;\r
+  }\r
+  DEBUG ((DEBUG_INFO,\r
+    "      Length ............................................. 0x%02x\n",\r
+    DmarDeviceScopeEntry->Length\r
+    ));\r
+  DEBUG ((DEBUG_INFO,\r
+    "      Enumeration ID ..................................... 0x%02x\n",\r
+    DmarDeviceScopeEntry->EnumerationId\r
+    ));\r
+  DEBUG ((DEBUG_INFO,\r
+    "      Starting Bus Number ................................ 0x%02x\n",\r
+    DmarDeviceScopeEntry->StartBusNumber\r
+    ));\r
+\r
+  PciPathNumber = (DmarDeviceScopeEntry->Length - sizeof(EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER)) / sizeof(EFI_ACPI_DMAR_PCI_PATH);\r
+  PciPath = (EFI_ACPI_DMAR_PCI_PATH *)(DmarDeviceScopeEntry + 1);\r
+  for (PciPathIndex = 0; PciPathIndex < PciPathNumber; PciPathIndex++) {\r
+    DEBUG ((DEBUG_INFO,\r
+      "      Device ............................................. 0x%02x\n",\r
+      PciPath[PciPathIndex].Device\r
+      ));\r
+    DEBUG ((DEBUG_INFO,\r
+      "      Function ........................................... 0x%02x\n",\r
+      PciPath[PciPathIndex].Function\r
+      ));\r
+  }\r
+\r
+  DEBUG ((DEBUG_INFO,\r
+    "    *************************************************************************\n\n"\r
+    ));\r
+\r
+  return;\r
+}\r
+\r
+/**\r
+  Dump DMAR ANDD table.\r
+\r
+  @param[in]  Andd  DMAR ANDD table\r
+**/\r
+VOID\r
+DumpDmarAndd (\r
+  IN EFI_ACPI_DMAR_ANDD_HEADER *Andd\r
+  )\r
+{\r
+  if (Andd == NULL) {\r
+    return;\r
+  }\r
+\r
+  DEBUG ((DEBUG_INFO,\r
+    "  ***************************************************************************\n"\r
+    "  *       ACPI Name-space Device Declaration Structure                      *\n"\r
+    "  ***************************************************************************\n"\r
+    ));\r
+  DEBUG ((DEBUG_INFO,\r
+    (sizeof(UINTN) == sizeof(UINT64)) ?\r
+    "  ANDD address ........................................... 0x%016lx\n" :\r
+    "  ANDD address ........................................... 0x%08x\n",\r
+    Andd\r
+    ));\r
+  DEBUG ((DEBUG_INFO,\r
+    "    Type ................................................. 0x%04x\n",\r
+    Andd->Header.Type\r
+    ));\r
+  DEBUG ((DEBUG_INFO,\r
+    "    Length ............................................... 0x%04x\n",\r
+    Andd->Header.Length\r
+    ));\r
+  DEBUG ((DEBUG_INFO,\r
+    "    ACPI Device Number ................................... 0x%02x\n",\r
+    Andd->AcpiDeviceNumber\r
+    ));\r
+  DEBUG ((DEBUG_INFO,\r
+    "    ACPI Object Name ..................................... '%a'\n",\r
+    (Andd + 1)\r
+    ));\r
+\r
+  DEBUG ((DEBUG_INFO,\r
+    "  ***************************************************************************\n\n"\r
+    ));\r
+\r
+  return;\r
+}\r
+\r
+/**\r
+  Dump DMAR RHSA table.\r
+\r
+  @param[in]  Rhsa  DMAR RHSA table\r
+**/\r
+VOID\r
+DumpDmarRhsa (\r
+  IN EFI_ACPI_DMAR_RHSA_HEADER *Rhsa\r
+  )\r
+{\r
+  if (Rhsa == NULL) {\r
+    return;\r
+  }\r
+\r
+  DEBUG ((DEBUG_INFO,\r
+    "  ***************************************************************************\n"\r
+    "  *       Remapping Hardware Status Affinity Structure                      *\n"\r
+    "  ***************************************************************************\n"\r
+    ));\r
+  DEBUG ((DEBUG_INFO,\r
+    (sizeof(UINTN) == sizeof(UINT64)) ?\r
+    "  RHSA address ........................................... 0x%016lx\n" :\r
+    "  RHSA address ........................................... 0x%08x\n",\r
+    Rhsa\r
+    ));\r
+  DEBUG ((DEBUG_INFO,\r
+    "    Type ................................................. 0x%04x\n",\r
+    Rhsa->Header.Type\r
+    ));\r
+  DEBUG ((DEBUG_INFO,\r
+    "    Length ............................................... 0x%04x\n",\r
+    Rhsa->Header.Length\r
+    ));\r
+  DEBUG ((DEBUG_INFO,\r
+    "    Register Base Address ................................ 0x%016lx\n",\r
+    Rhsa->RegisterBaseAddress\r
+    ));\r
+  DEBUG ((DEBUG_INFO,\r
+    "    Proximity Domain ..................................... 0x%08x\n",\r
+    Rhsa->ProximityDomain\r
+    ));\r
+\r
+  DEBUG ((DEBUG_INFO,\r
+    "  ***************************************************************************\n\n"\r
+    ));\r
+\r
+  return;\r
+}\r
+\r
+/**\r
+  Dump DMAR ATSR table.\r
+\r
+  @param[in]  Atsr  DMAR ATSR table\r
+**/\r
+VOID\r
+DumpDmarAtsr (\r
+  IN EFI_ACPI_DMAR_ATSR_HEADER *Atsr\r
+  )\r
+{\r
+  EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER       *DmarDeviceScopeEntry;\r
+  INTN                                    AtsrLen;\r
+\r
+  if (Atsr == NULL) {\r
+    return;\r
+  }\r
+\r
+  DEBUG ((DEBUG_INFO,\r
+    "  ***************************************************************************\n"\r
+    "  *       Root Port ATS Capability Reporting Structure                      *\n"\r
+    "  ***************************************************************************\n"\r
+    ));\r
+  DEBUG ((DEBUG_INFO,\r
+    (sizeof(UINTN) == sizeof(UINT64)) ?\r
+    "  ATSR address ........................................... 0x%016lx\n" :\r
+    "  ATSR address ........................................... 0x%08x\n",\r
+    Atsr\r
+    ));\r
+  DEBUG ((DEBUG_INFO,\r
+    "    Type ................................................. 0x%04x\n",\r
+    Atsr->Header.Type\r
+    ));\r
+  DEBUG ((DEBUG_INFO,\r
+    "    Length ............................................... 0x%04x\n",\r
+    Atsr->Header.Length\r
+    ));\r
+  DEBUG ((DEBUG_INFO,\r
+    "    Flags ................................................ 0x%02x\n",\r
+    Atsr->Flags\r
+    ));\r
+  DEBUG ((DEBUG_INFO,\r
+    "      ALL_PORTS .......................................... 0x%02x\n",\r
+    Atsr->Flags & EFI_ACPI_DMAR_ATSR_FLAGS_ALL_PORTS\r
+    ));\r
+  DEBUG ((DEBUG_INFO,\r
+    "    Segment Number ....................................... 0x%04x\n",\r
+    Atsr->SegmentNumber\r
+    ));\r
+\r
+  AtsrLen  = Atsr->Header.Length - sizeof(EFI_ACPI_DMAR_ATSR_HEADER);\r
+  DmarDeviceScopeEntry = (EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER *)(Atsr + 1);\r
+  while (AtsrLen > 0) {\r
+    DumpDmarDeviceScopeEntry (DmarDeviceScopeEntry);\r
+    AtsrLen -= DmarDeviceScopeEntry->Length;\r
+    DmarDeviceScopeEntry = (EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER *)((UINTN)DmarDeviceScopeEntry + DmarDeviceScopeEntry->Length);\r
+  }\r
+\r
+  DEBUG ((DEBUG_INFO,\r
+    "  ***************************************************************************\n\n"\r
+    ));\r
+\r
+  return;\r
+}\r
+\r
+/**\r
+  Dump DMAR RMRR table.\r
+\r
+  @param[in]  Rmrr  DMAR RMRR table\r
+**/\r
+VOID\r
+DumpDmarRmrr (\r
+  IN EFI_ACPI_DMAR_RMRR_HEADER *Rmrr\r
+  )\r
+{\r
+  EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER       *DmarDeviceScopeEntry;\r
+  INTN                                    RmrrLen;\r
+\r
+  if (Rmrr == NULL) {\r
+    return;\r
+  }\r
+\r
+  DEBUG ((DEBUG_INFO,\r
+    "  ***************************************************************************\n"\r
+    "  *       Reserved Memory Region Reporting Structure                        *\n"\r
+    "  ***************************************************************************\n"\r
+    ));\r
+  DEBUG ((DEBUG_INFO,\r
+    (sizeof(UINTN) == sizeof(UINT64)) ?\r
+    "  RMRR address ........................................... 0x%016lx\n" :\r
+    "  RMRR address ........................................... 0x%08x\n",\r
+    Rmrr\r
+    ));\r
+  DEBUG ((DEBUG_INFO,\r
+    "    Type ................................................. 0x%04x\n",\r
+    Rmrr->Header.Type\r
+    ));\r
+  DEBUG ((DEBUG_INFO,\r
+    "    Length ............................................... 0x%04x\n",\r
+    Rmrr->Header.Length\r
+    ));\r
+  DEBUG ((DEBUG_INFO,\r
+    "    Segment Number ....................................... 0x%04x\n",\r
+    Rmrr->SegmentNumber\r
+    ));\r
+  DEBUG ((DEBUG_INFO,\r
+    "    Reserved Memory Region Base Address .................. 0x%016lx\n",\r
+    Rmrr->ReservedMemoryRegionBaseAddress\r
+    ));\r
+  DEBUG ((DEBUG_INFO,\r
+    "    Reserved Memory Region Limit Address ................. 0x%016lx\n",\r
+    Rmrr->ReservedMemoryRegionLimitAddress\r
+    ));\r
+\r
+  RmrrLen  = Rmrr->Header.Length - sizeof(EFI_ACPI_DMAR_RMRR_HEADER);\r
+  DmarDeviceScopeEntry = (EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER *)(Rmrr + 1);\r
+  while (RmrrLen > 0) {\r
+    DumpDmarDeviceScopeEntry (DmarDeviceScopeEntry);\r
+    RmrrLen -= DmarDeviceScopeEntry->Length;\r
+    DmarDeviceScopeEntry = (EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER *)((UINTN)DmarDeviceScopeEntry + DmarDeviceScopeEntry->Length);\r
+  }\r
+\r
+  DEBUG ((DEBUG_INFO,\r
+    "  ***************************************************************************\n\n"\r
+    ));\r
+\r
+  return;\r
+}\r
+\r
+/**\r
+  Dump DMAR DRHD table.\r
+\r
+  @param[in]  Drhd  DMAR DRHD table\r
+**/\r
+VOID\r
+DumpDmarDrhd (\r
+  IN EFI_ACPI_DMAR_DRHD_HEADER *Drhd\r
+  )\r
+{\r
+  EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER       *DmarDeviceScopeEntry;\r
+  INTN                                    DrhdLen;\r
+\r
+  if (Drhd == NULL) {\r
+    return;\r
+  }\r
+\r
+  DEBUG ((DEBUG_INFO,\r
+    "  ***************************************************************************\n"\r
+    "  *       DMA-Remapping Hardware Definition Structure                       *\n"\r
+    "  ***************************************************************************\n"\r
+    ));\r
+  DEBUG ((DEBUG_INFO,\r
+    (sizeof(UINTN) == sizeof(UINT64)) ?\r
+    "  DRHD address ........................................... 0x%016lx\n" :\r
+    "  DRHD address ........................................... 0x%08x\n",\r
+    Drhd\r
+    ));\r
+  DEBUG ((DEBUG_INFO,\r
+    "    Type ................................................. 0x%04x\n",\r
+    Drhd->Header.Type\r
+    ));\r
+  DEBUG ((DEBUG_INFO,\r
+    "    Length ............................................... 0x%04x\n",\r
+    Drhd->Header.Length\r
+    ));\r
+  DEBUG ((DEBUG_INFO,\r
+    "    Flags ................................................ 0x%02x\n",\r
+    Drhd->Flags\r
+    ));\r
+  DEBUG ((DEBUG_INFO,\r
+    "      INCLUDE_PCI_ALL .................................... 0x%02x\n",\r
+    Drhd->Flags & EFI_ACPI_DMAR_DRHD_FLAGS_INCLUDE_PCI_ALL\r
+    ));\r
+  DEBUG ((DEBUG_INFO,\r
+    "    Segment Number ....................................... 0x%04x\n",\r
+    Drhd->SegmentNumber\r
+    ));\r
+  DEBUG ((DEBUG_INFO,\r
+    "    Register Base Address ................................ 0x%016lx\n",\r
+    Drhd->RegisterBaseAddress\r
+    ));\r
+\r
+  DrhdLen  = Drhd->Header.Length - sizeof(EFI_ACPI_DMAR_DRHD_HEADER);\r
+  DmarDeviceScopeEntry = (EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER *)(Drhd + 1);\r
+  while (DrhdLen > 0) {\r
+    DumpDmarDeviceScopeEntry (DmarDeviceScopeEntry);\r
+    DrhdLen -= DmarDeviceScopeEntry->Length;\r
+    DmarDeviceScopeEntry = (EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER *)((UINTN)DmarDeviceScopeEntry + DmarDeviceScopeEntry->Length);\r
+  }\r
+\r
+  DEBUG ((DEBUG_INFO,\r
+    "  ***************************************************************************\n\n"\r
+    ));\r
+\r
+  return;\r
+}\r
+\r
+/**\r
+  Dump DMAR ACPI table.\r
+\r
+  @param[in]  Dmar  DMAR ACPI table\r
+**/\r
+VOID\r
+DumpAcpiDMAR (\r
+  IN EFI_ACPI_DMAR_HEADER  *Dmar\r
+  )\r
+{\r
+  EFI_ACPI_DMAR_STRUCTURE_HEADER *DmarHeader;\r
+  INTN                  DmarLen;\r
+\r
+  if (Dmar == NULL) {\r
+    return;\r
+  }\r
+\r
+  //\r
+  // Dump Dmar table\r
+  //\r
+  DEBUG ((DEBUG_INFO,\r
+    "*****************************************************************************\n"\r
+    "*         DMAR Table                                                        *\n"\r
+    "*****************************************************************************\n"\r
+    ));\r
+\r
+  DEBUG ((DEBUG_INFO,\r
+    (sizeof(UINTN) == sizeof(UINT64)) ?\r
+    "DMAR address ............................................. 0x%016lx\n" :\r
+    "DMAR address ............................................. 0x%08x\n",\r
+    Dmar\r
+    ));\r
+\r
+  DEBUG ((DEBUG_INFO,\r
+    "  Table Contents:\n"\r
+    ));\r
+  DEBUG ((DEBUG_INFO,\r
+    "    Host Address Width ................................... 0x%02x\n",\r
+    Dmar->HostAddressWidth\r
+    ));\r
+  DEBUG ((DEBUG_INFO,\r
+    "    Flags ................................................ 0x%02x\n",\r
+    Dmar->Flags\r
+    ));\r
+  DEBUG ((DEBUG_INFO,\r
+    "      INTR_REMAP ......................................... 0x%02x\n",\r
+    Dmar->Flags & EFI_ACPI_DMAR_FLAGS_INTR_REMAP\r
+    ));\r
+  DEBUG ((DEBUG_INFO,\r
+    "      X2APIC_OPT_OUT_SET ................................. 0x%02x\n",\r
+    Dmar->Flags & EFI_ACPI_DMAR_FLAGS_X2APIC_OPT_OUT\r
+    ));\r
+\r
+  DmarLen  = Dmar->Header.Length - sizeof(EFI_ACPI_DMAR_HEADER);\r
+  DmarHeader = (EFI_ACPI_DMAR_STRUCTURE_HEADER *)(Dmar + 1);\r
+  while (DmarLen > 0) {\r
+    switch (DmarHeader->Type) {\r
+    case EFI_ACPI_DMAR_TYPE_DRHD:\r
+      DumpDmarDrhd ((EFI_ACPI_DMAR_DRHD_HEADER *)DmarHeader);\r
+      break;\r
+    case EFI_ACPI_DMAR_TYPE_RMRR:\r
+      DumpDmarRmrr ((EFI_ACPI_DMAR_RMRR_HEADER *)DmarHeader);\r
+      break;\r
+    case EFI_ACPI_DMAR_TYPE_ATSR:\r
+      DumpDmarAtsr ((EFI_ACPI_DMAR_ATSR_HEADER *)DmarHeader);\r
+      break;\r
+    case EFI_ACPI_DMAR_TYPE_RHSA:\r
+      DumpDmarRhsa ((EFI_ACPI_DMAR_RHSA_HEADER *)DmarHeader);\r
+      break;\r
+    case EFI_ACPI_DMAR_TYPE_ANDD:\r
+      DumpDmarAndd ((EFI_ACPI_DMAR_ANDD_HEADER *)DmarHeader);\r
+      break;\r
+    default:\r
+      break;\r
+    }\r
+    DmarLen -= DmarHeader->Length;\r
+    DmarHeader = (EFI_ACPI_DMAR_STRUCTURE_HEADER *)((UINTN)DmarHeader + DmarHeader->Length);\r
+  }\r
+\r
+  DEBUG ((DEBUG_INFO,\r
+    "*****************************************************************************\n\n"\r
+    ));\r
+\r
+  return;\r
+}\r
+\r
+/**\r
+  Dump DMAR ACPI table.\r
+**/\r
+VOID\r
+VtdDumpDmarTable (\r
+  VOID\r
+  )\r
+{\r
+  DumpAcpiDMAR ((EFI_ACPI_DMAR_HEADER *)(UINTN)mAcpiDmarTable);\r
+}\r
+\r
+/**\r
+  Get PCI device information from DMAR DevScopeEntry.\r
+\r
+  @param[in]  Segment               The segment number.\r
+  @param[in]  DmarDevScopeEntry     DMAR DevScopeEntry\r
+  @param[out] Bus                   The bus number.\r
+  @param[out] Device                The device number.\r
+  @param[out] Function              The function number.\r
+\r
+  @retval EFI_SUCCESS  The PCI device information is returned.\r
+**/\r
+EFI_STATUS\r
+GetPciBusDeviceFunction (\r
+  IN  UINT16                                      Segment,\r
+  IN  EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER *DmarDevScopeEntry,\r
+  OUT UINT8                                       *Bus,\r
+  OUT UINT8                                       *Device,\r
+  OUT UINT8                                       *Function\r
+  )\r
+{\r
+  EFI_ACPI_DMAR_PCI_PATH                     *DmarPciPath;\r
+  UINT8                                      MyBus;\r
+  UINT8                                      MyDevice;\r
+  UINT8                                      MyFunction;\r
+\r
+  DmarPciPath = (EFI_ACPI_DMAR_PCI_PATH *)((UINTN)(DmarDevScopeEntry + 1));\r
+  MyBus = DmarDevScopeEntry->StartBusNumber;\r
+  MyDevice = DmarPciPath->Device;\r
+  MyFunction = DmarPciPath->Function;\r
+\r
+  switch (DmarDevScopeEntry->Type) {\r
+  case EFI_ACPI_DEVICE_SCOPE_ENTRY_TYPE_PCI_ENDPOINT:\r
+  case EFI_ACPI_DEVICE_SCOPE_ENTRY_TYPE_PCI_BRIDGE:\r
+    while ((UINTN)DmarPciPath < (UINTN)DmarDevScopeEntry + DmarDevScopeEntry->Length) {\r
+      MyBus = PciSegmentRead8 (PCI_SEGMENT_LIB_ADDRESS(Segment, MyBus, MyDevice, MyFunction, PCI_BRIDGE_SECONDARY_BUS_REGISTER_OFFSET));\r
+      MyDevice = DmarPciPath->Device;\r
+      MyFunction = DmarPciPath->Function;\r
+      DmarPciPath ++;\r
+    }\r
+    break;\r
+  case EFI_ACPI_DEVICE_SCOPE_ENTRY_TYPE_IOAPIC:\r
+  case EFI_ACPI_DEVICE_SCOPE_ENTRY_TYPE_MSI_CAPABLE_HPET:\r
+  case EFI_ACPI_DEVICE_SCOPE_ENTRY_TYPE_ACPI_NAMESPACE_DEVICE:\r
+    break;\r
+  }\r
+\r
+  *Bus = MyBus;\r
+  *Device = MyDevice;\r
+  *Function = MyFunction;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Process DMAR DHRD table.\r
+\r
+  @param[in]  VtdIndex  The index of VTd engine.\r
+  @param[in]  DmarDrhd  The DRHD table.\r
+\r
+  @retval EFI_SUCCESS The DRHD table is processed.\r
+**/\r
+EFI_STATUS\r
+ProcessDhrd (\r
+  IN UINTN                      VtdIndex,\r
+  IN EFI_ACPI_DMAR_DRHD_HEADER  *DmarDrhd\r
+  )\r
+{\r
+  EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER       *DmarDevScopeEntry;\r
+  UINT8                                             Bus;\r
+  UINT8                                             Device;\r
+  UINT8                                             Function;\r
+  UINT8                                             SecondaryBusNumber;\r
+  EFI_STATUS                                        Status;\r
+  VTD_SOURCE_ID                                     SourceId;\r
+  BOOLEAN                                           IsRealPciDevice;\r
+\r
+  mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress = (UINTN)DmarDrhd->RegisterBaseAddress;\r
+  DEBUG ((DEBUG_INFO,"  VTD (%d) BaseAddress -  0x%016lx\n", VtdIndex, DmarDrhd->RegisterBaseAddress));\r
+\r
+  mVtdUnitInformation[VtdIndex].Segment = DmarDrhd->SegmentNumber;\r
+\r
+  if ((DmarDrhd->Flags & EFI_ACPI_DMAR_DRHD_FLAGS_INCLUDE_PCI_ALL) != 0) {\r
+    mVtdUnitInformation[VtdIndex].PciDeviceInfo.IncludeAllFlag = TRUE;\r
+    DEBUG ((DEBUG_INFO,"  ProcessDhrd: with INCLUDE ALL\n"));\r
+\r
+    Status = ScanPciBus(VtdIndex, DmarDrhd->SegmentNumber, 0);\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+  } else {\r
+    mVtdUnitInformation[VtdIndex].PciDeviceInfo.IncludeAllFlag = FALSE;\r
+    DEBUG ((DEBUG_INFO,"  ProcessDhrd: without INCLUDE ALL\n"));\r
+  }\r
+\r
+  DmarDevScopeEntry = (EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER *)((UINTN)(DmarDrhd + 1));\r
+  while ((UINTN)DmarDevScopeEntry < (UINTN)DmarDrhd + DmarDrhd->Header.Length) {\r
+\r
+    Status = GetPciBusDeviceFunction (DmarDrhd->SegmentNumber, DmarDevScopeEntry, &Bus, &Device, &Function);\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+    switch (DmarDevScopeEntry->Type) {\r
+    case EFI_ACPI_DEVICE_SCOPE_ENTRY_TYPE_PCI_ENDPOINT:\r
+    case EFI_ACPI_DEVICE_SCOPE_ENTRY_TYPE_PCI_BRIDGE:\r
+      IsRealPciDevice = TRUE;\r
+      break;\r
+    default:\r
+      IsRealPciDevice = FALSE;\r
+      break;\r
+    }\r
+\r
+    DEBUG ((DEBUG_INFO,"  ProcessDhrd: "));\r
+    switch (DmarDevScopeEntry->Type) {\r
+    case EFI_ACPI_DEVICE_SCOPE_ENTRY_TYPE_PCI_ENDPOINT:\r
+      DEBUG ((DEBUG_INFO,"PCI Endpoint"));\r
+      break;\r
+    case EFI_ACPI_DEVICE_SCOPE_ENTRY_TYPE_PCI_BRIDGE:\r
+      DEBUG ((DEBUG_INFO,"PCI-PCI bridge"));\r
+      break;\r
+    case EFI_ACPI_DEVICE_SCOPE_ENTRY_TYPE_IOAPIC:\r
+      DEBUG ((DEBUG_INFO,"IOAPIC"));\r
+      break;\r
+    case EFI_ACPI_DEVICE_SCOPE_ENTRY_TYPE_MSI_CAPABLE_HPET:\r
+      DEBUG ((DEBUG_INFO,"MSI Capable HPET"));\r
+      break;\r
+    case EFI_ACPI_DEVICE_SCOPE_ENTRY_TYPE_ACPI_NAMESPACE_DEVICE:\r
+      DEBUG ((DEBUG_INFO,"ACPI Namespace Device"));\r
+      break;\r
+    }\r
+    DEBUG ((DEBUG_INFO," S%04x B%02x D%02x F%02x\n", DmarDrhd->SegmentNumber, Bus, Device, Function));\r
+\r
+    SourceId.Bits.Bus = Bus;\r
+    SourceId.Bits.Device = Device;\r
+    SourceId.Bits.Function = Function;\r
+\r
+    Status = RegisterPciDevice (VtdIndex, DmarDrhd->SegmentNumber, SourceId, IsRealPciDevice, TRUE);\r
+    if (EFI_ERROR (Status)) {\r
+      //\r
+      // There might be duplication for special device other than standard PCI device.\r
+      //\r
+      switch (DmarDevScopeEntry->Type) {\r
+      case EFI_ACPI_DEVICE_SCOPE_ENTRY_TYPE_PCI_ENDPOINT:\r
+      case EFI_ACPI_DEVICE_SCOPE_ENTRY_TYPE_PCI_BRIDGE:\r
+        return Status;\r
+      }\r
+    }\r
+\r
+    switch (DmarDevScopeEntry->Type) {\r
+    case EFI_ACPI_DEVICE_SCOPE_ENTRY_TYPE_PCI_BRIDGE:\r
+      SecondaryBusNumber = PciSegmentRead8 (PCI_SEGMENT_LIB_ADDRESS(DmarDrhd->SegmentNumber, Bus, Device, Function, PCI_BRIDGE_SECONDARY_BUS_REGISTER_OFFSET));\r
+      Status = ScanPciBus (VtdIndex, DmarDrhd->SegmentNumber, SecondaryBusNumber);\r
+      if (EFI_ERROR (Status)) {\r
+        return Status;\r
+      }\r
+      break;\r
+    default:\r
+      break;\r
+    }\r
+\r
+    DmarDevScopeEntry = (EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER *)((UINTN)DmarDevScopeEntry + DmarDevScopeEntry->Length);\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Process DMAR RMRR table.\r
+\r
+  @param[in]  DmarRmrr  The RMRR table.\r
+\r
+  @retval EFI_SUCCESS The RMRR table is processed.\r
+**/\r
+EFI_STATUS\r
+ProcessRmrr (\r
+  IN EFI_ACPI_DMAR_RMRR_HEADER  *DmarRmrr\r
+  )\r
+{\r
+  EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER       *DmarDevScopeEntry;\r
+  UINT8                                             Bus;\r
+  UINT8                                             Device;\r
+  UINT8                                             Function;\r
+  EFI_STATUS                                        Status;\r
+  VTD_SOURCE_ID                                     SourceId;\r
+\r
+  DEBUG ((DEBUG_INFO,"  RMRR (Base 0x%016lx, Limit 0x%016lx)\n", DmarRmrr->ReservedMemoryRegionBaseAddress, DmarRmrr->ReservedMemoryRegionLimitAddress));\r
+\r
+  DmarDevScopeEntry = (EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER *)((UINTN)(DmarRmrr + 1));\r
+  while ((UINTN)DmarDevScopeEntry < (UINTN)DmarRmrr + DmarRmrr->Header.Length) {\r
+    if (DmarDevScopeEntry->Type != EFI_ACPI_DEVICE_SCOPE_ENTRY_TYPE_PCI_ENDPOINT) {\r
+      DEBUG ((DEBUG_INFO,"RMRR DevScopeEntryType is not endpoint, type[0x%x] \n", DmarDevScopeEntry->Type));\r
+      return EFI_DEVICE_ERROR;\r
+    }\r
+\r
+    Status = GetPciBusDeviceFunction (DmarRmrr->SegmentNumber, DmarDevScopeEntry, &Bus, &Device, &Function);\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+\r
+    DEBUG ((DEBUG_INFO,"RMRR S%04x B%02x D%02x F%02x\n", DmarRmrr->SegmentNumber, Bus, Device, Function));\r
+\r
+    SourceId.Bits.Bus = Bus;\r
+    SourceId.Bits.Device = Device;\r
+    SourceId.Bits.Function = Function;\r
+    Status = SetAccessAttribute (\r
+               DmarRmrr->SegmentNumber,\r
+               SourceId,\r
+               DmarRmrr->ReservedMemoryRegionBaseAddress,\r
+               DmarRmrr->ReservedMemoryRegionLimitAddress + 1 - DmarRmrr->ReservedMemoryRegionBaseAddress,\r
+               EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE\r
+               );\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+\r
+    DmarDevScopeEntry = (EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER *)((UINTN)DmarDevScopeEntry + DmarDevScopeEntry->Length);\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Get VTd engine number.\r
+**/\r
+UINTN\r
+GetVtdEngineNumber (\r
+  VOID\r
+  )\r
+{\r
+  EFI_ACPI_DMAR_STRUCTURE_HEADER                    *DmarHeader;\r
+  UINTN                                             VtdIndex;\r
+\r
+  VtdIndex = 0;\r
+  DmarHeader = (EFI_ACPI_DMAR_STRUCTURE_HEADER *)((UINTN)(mAcpiDmarTable + 1));\r
+  while ((UINTN)DmarHeader < (UINTN)mAcpiDmarTable + mAcpiDmarTable->Header.Length) {\r
+    switch (DmarHeader->Type) {\r
+    case EFI_ACPI_DMAR_TYPE_DRHD:\r
+      VtdIndex++;\r
+      break;\r
+    default:\r
+      break;\r
+    }\r
+    DmarHeader = (EFI_ACPI_DMAR_STRUCTURE_HEADER *)((UINTN)DmarHeader + DmarHeader->Length);\r
+  }\r
+  return VtdIndex ;\r
+}\r
+\r
+/**\r
+  Parse DMAR DRHD table.\r
+\r
+  @return EFI_SUCCESS  The DMAR DRHD table is parsed.\r
+**/\r
+EFI_STATUS\r
+ParseDmarAcpiTableDrhd (\r
+  VOID\r
+  )\r
+{\r
+  EFI_ACPI_DMAR_STRUCTURE_HEADER                    *DmarHeader;\r
+  EFI_STATUS                                        Status;\r
+  UINTN                                             VtdIndex;\r
+\r
+  mVtdUnitNumber = GetVtdEngineNumber ();\r
+  DEBUG ((DEBUG_INFO,"  VtdUnitNumber - %d\n", mVtdUnitNumber));\r
+  ASSERT (mVtdUnitNumber > 0);\r
+  if (mVtdUnitNumber == 0) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  mVtdUnitInformation = AllocateZeroPool (sizeof(*mVtdUnitInformation) * mVtdUnitNumber);\r
+  ASSERT (mVtdUnitInformation != NULL);\r
+  if (mVtdUnitInformation == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  mVtdHostAddressWidthMask = LShiftU64 (1ull, mAcpiDmarTable->HostAddressWidth) - 1;\r
+\r
+  VtdIndex = 0;\r
+  DmarHeader = (EFI_ACPI_DMAR_STRUCTURE_HEADER *)((UINTN)(mAcpiDmarTable + 1));\r
+  while ((UINTN)DmarHeader < (UINTN)mAcpiDmarTable + mAcpiDmarTable->Header.Length) {\r
+    switch (DmarHeader->Type) {\r
+    case EFI_ACPI_DMAR_TYPE_DRHD:\r
+      ASSERT (VtdIndex < mVtdUnitNumber);\r
+      Status = ProcessDhrd (VtdIndex, (EFI_ACPI_DMAR_DRHD_HEADER *)DmarHeader);\r
+      if (EFI_ERROR (Status)) {\r
+        return Status;\r
+      }\r
+      VtdIndex++;\r
+\r
+      break;\r
+\r
+    default:\r
+      break;\r
+    }\r
+    DmarHeader = (EFI_ACPI_DMAR_STRUCTURE_HEADER *)((UINTN)DmarHeader + DmarHeader->Length);\r
+  }\r
+  ASSERT (VtdIndex == mVtdUnitNumber);\r
+\r
+  for (VtdIndex = 0; VtdIndex < mVtdUnitNumber; VtdIndex++) {\r
+    DumpPciDeviceInfo (VtdIndex);\r
+  }\r
+  return EFI_SUCCESS ;\r
+}\r
+\r
+/**\r
+  Parse DMAR DRHD table.\r
+\r
+  @return EFI_SUCCESS  The DMAR DRHD table is parsed.\r
+**/\r
+EFI_STATUS\r
+ParseDmarAcpiTableRmrr (\r
+  VOID\r
+  )\r
+{\r
+  EFI_ACPI_DMAR_STRUCTURE_HEADER                    *DmarHeader;\r
+  EFI_STATUS                                        Status;\r
+\r
+  DmarHeader = (EFI_ACPI_DMAR_STRUCTURE_HEADER *)((UINTN)(mAcpiDmarTable + 1));\r
+  while ((UINTN)DmarHeader < (UINTN)mAcpiDmarTable + mAcpiDmarTable->Header.Length) {\r
+    switch (DmarHeader->Type) {\r
+    case EFI_ACPI_DMAR_TYPE_RMRR:\r
+      Status = ProcessRmrr ((EFI_ACPI_DMAR_RMRR_HEADER *)DmarHeader);\r
+      if (EFI_ERROR (Status)) {\r
+        return Status;\r
+      }\r
+      break;\r
+    default:\r
+      break;\r
+    }\r
+    DmarHeader = (EFI_ACPI_DMAR_STRUCTURE_HEADER *)((UINTN)DmarHeader + DmarHeader->Length);\r
+  }\r
+  return EFI_SUCCESS ;\r
+}\r
+\r
+/**\r
+  This function scan ACPI table in RSDT.\r
+\r
+  @param[in]  Rsdt      ACPI RSDT\r
+  @param[in]  Signature ACPI table signature\r
+\r
+  @return ACPI table\r
+**/\r
+VOID *\r
+ScanTableInRSDT (\r
+  IN RSDT_TABLE                   *Rsdt,\r
+  IN UINT32                       Signature\r
+  )\r
+{\r
+  UINTN                         Index;\r
+  UINT32                        EntryCount;\r
+  UINT32                        *EntryPtr;\r
+  EFI_ACPI_DESCRIPTION_HEADER   *Table;\r
+\r
+  EntryCount = (Rsdt->Header.Length - sizeof (EFI_ACPI_DESCRIPTION_HEADER)) / sizeof(UINT32);\r
+\r
+  EntryPtr = &Rsdt->Entry;\r
+  for (Index = 0; Index < EntryCount; Index ++, EntryPtr ++) {\r
+    Table = (EFI_ACPI_DESCRIPTION_HEADER*)((UINTN)(*EntryPtr));\r
+    if (Table->Signature == Signature) {\r
+      return Table;\r
+    }\r
+  }\r
+\r
+  return NULL;\r
+}\r
+\r
+/**\r
+  This function scan ACPI table in XSDT.\r
+\r
+  @param[in]  Xsdt      ACPI XSDT\r
+  @param[in]  Signature ACPI table signature\r
+\r
+  @return ACPI table\r
+**/\r
+VOID *\r
+ScanTableInXSDT (\r
+  IN XSDT_TABLE                   *Xsdt,\r
+  IN UINT32                       Signature\r
+  )\r
+{\r
+  UINTN                        Index;\r
+  UINT32                       EntryCount;\r
+  UINT64                       EntryPtr;\r
+  UINTN                        BasePtr;\r
+  EFI_ACPI_DESCRIPTION_HEADER  *Table;\r
+\r
+  EntryCount = (Xsdt->Header.Length - sizeof (EFI_ACPI_DESCRIPTION_HEADER)) / sizeof(UINT64);\r
+\r
+  BasePtr = (UINTN)(&(Xsdt->Entry));\r
+  for (Index = 0; Index < EntryCount; Index ++) {\r
+    CopyMem (&EntryPtr, (VOID *)(BasePtr + Index * sizeof(UINT64)), sizeof(UINT64));\r
+    Table = (EFI_ACPI_DESCRIPTION_HEADER*)((UINTN)(EntryPtr));\r
+    if (Table->Signature == Signature) {\r
+      return Table;\r
+    }\r
+  }\r
+\r
+  return NULL;\r
+}\r
+\r
+/**\r
+  This function scan ACPI table in RSDP.\r
+\r
+  @param[in]  Rsdp      ACPI RSDP\r
+  @param[in]  Signature ACPI table signature\r
+\r
+  @return ACPI table\r
+**/\r
+VOID *\r
+FindAcpiPtr (\r
+  IN EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER *Rsdp,\r
+  IN UINT32                                       Signature\r
+  )\r
+{\r
+  EFI_ACPI_DESCRIPTION_HEADER                    *AcpiTable;\r
+  RSDT_TABLE                                     *Rsdt;\r
+  XSDT_TABLE                                     *Xsdt;\r
+\r
+  AcpiTable = NULL;\r
+\r
+  //\r
+  // Check ACPI2.0 table\r
+  //\r
+  Rsdt = (RSDT_TABLE *)(UINTN)Rsdp->RsdtAddress;\r
+  Xsdt = NULL;\r
+  if ((Rsdp->Revision >= 2) && (Rsdp->XsdtAddress < (UINT64)(UINTN)-1)) {\r
+    Xsdt = (XSDT_TABLE *)(UINTN)Rsdp->XsdtAddress;\r
+  }\r
+  //\r
+  // Check Xsdt\r
+  //\r
+  if (Xsdt != NULL) {\r
+    AcpiTable = ScanTableInXSDT (Xsdt, Signature);\r
+  }\r
+  //\r
+  // Check Rsdt\r
+  //\r
+  if ((AcpiTable == NULL) && (Rsdt != NULL)) {\r
+    AcpiTable = ScanTableInRSDT (Rsdt, Signature);\r
+  }\r
+\r
+  return AcpiTable;\r
+}\r
+\r
+/**\r
+  Get the DMAR ACPI table.\r
+\r
+  @retval EFI_SUCCESS    The DMAR ACPI table is got.\r
+  @retval EFI_NOT_FOUND  The DMAR ACPI table is not found.\r
+**/\r
+EFI_STATUS\r
+GetDmarAcpiTable (\r
+  VOID\r
+  )\r
+{\r
+  VOID                              *AcpiTable;\r
+  EFI_STATUS                        Status;\r
+\r
+  AcpiTable = NULL;\r
+  Status = EfiGetSystemConfigurationTable (\r
+             &gEfiAcpi20TableGuid,\r
+             &AcpiTable\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    Status = EfiGetSystemConfigurationTable (\r
+               &gEfiAcpiTableGuid,\r
+               &AcpiTable\r
+               );\r
+  }\r
+  ASSERT (AcpiTable != NULL);\r
+\r
+  mAcpiDmarTable = FindAcpiPtr (\r
+                      (EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER *)AcpiTable,\r
+                      EFI_ACPI_4_0_DMA_REMAPPING_TABLE_SIGNATURE\r
+                      );\r
+  DEBUG ((DEBUG_INFO,"DMAR Table - 0x%08x\n", mAcpiDmarTable));\r
+  if (mAcpiDmarTable == NULL) {\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+  VtdDumpDmarTable();\r
+\r
+  return EFI_SUCCESS;\r
+}\r
diff --git a/IntelSiliconPkg/IntelVTdDxe/IntelVTdDxe.c b/IntelSiliconPkg/IntelVTdDxe/IntelVTdDxe.c
new file mode 100644 (file)
index 0000000..d22222d
--- /dev/null
@@ -0,0 +1,353 @@
+/** @file\r
+  Intel VTd driver.\r
+\r
+  Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>\r
+  This program and the accompanying materials\r
+  are licensed and made available under the terms and conditions of the BSD License\r
+  which accompanies this distribution.  The full text of the license may be found at\r
+  http://opensource.org/licenses/bsd-license.php\r
+\r
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include <PiDxe.h>\r
+\r
+#include <Protocol/IoMmu.h>\r
+#include <Protocol/PciIo.h>\r
+\r
+#include <Library/IoLib.h>\r
+#include <Library/BaseLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+\r
+#include "DmaProtection.h"\r
+\r
+/**\r
+  Provides the controller-specific addresses required to access system memory from a\r
+  DMA bus master.\r
+\r
+  @param  This                  The protocol instance pointer.\r
+  @param  Operation             Indicates if the bus master is going to read or write to system memory.\r
+  @param  HostAddress           The system memory address to map to the PCI controller.\r
+  @param  NumberOfBytes         On input the number of bytes to map. On output the number of bytes\r
+                                that were mapped.\r
+  @param  DeviceAddress         The resulting map address for the bus master PCI controller to use to\r
+                                access the hosts HostAddress.\r
+  @param  Mapping               A resulting value to pass to Unmap().\r
+\r
+  @retval EFI_SUCCESS           The range was mapped for the returned NumberOfBytes.\r
+  @retval EFI_UNSUPPORTED       The HostAddress cannot be mapped as a common buffer.\r
+  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
+  @retval EFI_OUT_OF_RESOURCES  The request could not be completed due to a lack of resources.\r
+  @retval EFI_DEVICE_ERROR      The system hardware could not map the requested address.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IoMmuMap (\r
+  IN     EDKII_IOMMU_PROTOCOL                       *This,\r
+  IN     EDKII_IOMMU_OPERATION                      Operation,\r
+  IN     VOID                                       *HostAddress,\r
+  IN OUT UINTN                                      *NumberOfBytes,\r
+  OUT    EFI_PHYSICAL_ADDRESS                       *DeviceAddress,\r
+  OUT    VOID                                       **Mapping\r
+  );\r
+\r
+/**\r
+  Completes the Map() operation and releases any corresponding resources.\r
+\r
+  @param  This                  The protocol instance pointer.\r
+  @param  Mapping               The mapping value returned from Map().\r
+\r
+  @retval EFI_SUCCESS           The range was unmapped.\r
+  @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by Map().\r
+  @retval EFI_DEVICE_ERROR      The data was not committed to the target system memory.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IoMmuUnmap (\r
+  IN  EDKII_IOMMU_PROTOCOL                     *This,\r
+  IN  VOID                                     *Mapping\r
+  );\r
+\r
+/**\r
+  Allocates pages that are suitable for an OperationBusMasterCommonBuffer or\r
+  OperationBusMasterCommonBuffer64 mapping.\r
+\r
+  @param  This                  The protocol instance pointer.\r
+  @param  Type                  This parameter is not used and must be ignored.\r
+  @param  MemoryType            The type of memory to allocate, EfiBootServicesData or\r
+                                EfiRuntimeServicesData.\r
+  @param  Pages                 The number of pages to allocate.\r
+  @param  HostAddress           A pointer to store the base system memory address of the\r
+                                allocated range.\r
+  @param  Attributes            The requested bit mask of attributes for the allocated range.\r
+\r
+  @retval EFI_SUCCESS           The requested memory pages were allocated.\r
+  @retval EFI_UNSUPPORTED       Attributes is unsupported. The only legal attribute bits are\r
+                                MEMORY_WRITE_COMBINE and MEMORY_CACHED.\r
+  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
+  @retval EFI_OUT_OF_RESOURCES  The memory pages could not be allocated.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IoMmuAllocateBuffer (\r
+  IN     EDKII_IOMMU_PROTOCOL                     *This,\r
+  IN     EFI_ALLOCATE_TYPE                        Type,\r
+  IN     EFI_MEMORY_TYPE                          MemoryType,\r
+  IN     UINTN                                    Pages,\r
+  IN OUT VOID                                     **HostAddress,\r
+  IN     UINT64                                   Attributes\r
+  );\r
+\r
+/**\r
+  Frees memory that was allocated with AllocateBuffer().\r
+\r
+  @param  This                  The protocol instance pointer.\r
+  @param  Pages                 The number of pages to free.\r
+  @param  HostAddress           The base system memory address of the allocated range.\r
+\r
+  @retval EFI_SUCCESS           The requested memory pages were freed.\r
+  @retval EFI_INVALID_PARAMETER The memory range specified by HostAddress and Pages\r
+                                was not allocated with AllocateBuffer().\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IoMmuFreeBuffer (\r
+  IN  EDKII_IOMMU_PROTOCOL                     *This,\r
+  IN  UINTN                                    Pages,\r
+  IN  VOID                                     *HostAddress\r
+  );\r
+\r
+/**\r
+  Convert the DeviceHandle to SourceId and Segment.\r
+\r
+  @param[in]  DeviceHandle      The device who initiates the DMA access request.\r
+  @param[out] Segment           The Segment used to identify a VTd engine.\r
+  @param[out] SourceId          The SourceId used to identify a VTd engine and table entry.\r
+\r
+  @retval EFI_SUCCESS            The Segment and SourceId are returned.\r
+  @retval EFI_INVALID_PARAMETER  DeviceHandle is an invalid handle.\r
+  @retval EFI_UNSUPPORTED        DeviceHandle is unknown by the IOMMU.\r
+**/\r
+EFI_STATUS\r
+DeviceHandleToSourceId (\r
+  IN EFI_HANDLE            DeviceHandle,\r
+  OUT UINT16               *Segment,\r
+  OUT VTD_SOURCE_ID        *SourceId\r
+  )\r
+{\r
+  EFI_PCI_IO_PROTOCOL                      *PciIo;\r
+  UINTN                                    Seg;\r
+  UINTN                                    Bus;\r
+  UINTN                                    Dev;\r
+  UINTN                                    Func;\r
+  EFI_STATUS                               Status;\r
+  EDKII_PLATFORM_VTD_DEVICE_INFO           DeviceInfo;\r
+\r
+  Status = EFI_NOT_FOUND;\r
+  if (mPlatformVTdPolicy != NULL) {\r
+    Status = mPlatformVTdPolicy->GetDeviceId (mPlatformVTdPolicy, DeviceHandle, &DeviceInfo);\r
+    if (!EFI_ERROR(Status)) {\r
+      *Segment  = DeviceInfo.Segment;\r
+      *SourceId = DeviceInfo.SourceId;\r
+      return EFI_SUCCESS;\r
+    }\r
+  }\r
+\r
+  Status = gBS->HandleProtocol (DeviceHandle, &gEfiPciIoProtocolGuid, (VOID **)&PciIo);\r
+  if (EFI_ERROR(Status)) {\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+  Status = PciIo->GetLocation (PciIo, &Seg, &Bus, &Dev, &Func);\r
+  if (EFI_ERROR(Status)) {\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+  *Segment = (UINT16)Seg;\r
+  SourceId->Bits.Bus = (UINT8)Bus;\r
+  SourceId->Bits.Device = (UINT8)Dev;\r
+  SourceId->Bits.Function = (UINT8)Func;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Set IOMMU attribute for a system memory.\r
+\r
+  If the IOMMU protocol exists, the system memory cannot be used\r
+  for DMA by default.\r
+\r
+  When a device requests a DMA access for a system memory,\r
+  the device driver need use SetAttribute() to update the IOMMU\r
+  attribute to request DMA access (read and/or write).\r
+\r
+  The DeviceHandle is used to identify which device submits the request.\r
+  The IOMMU implementation need translate the device path to an IOMMU device ID,\r
+  and set IOMMU hardware register accordingly.\r
+  1) DeviceHandle can be a standard PCI device.\r
+     The memory for BusMasterRead need set EDKII_IOMMU_ACCESS_READ.\r
+     The memory for BusMasterWrite need set EDKII_IOMMU_ACCESS_WRITE.\r
+     The memory for BusMasterCommonBuffer need set EDKII_IOMMU_ACCESS_READ|EDKII_IOMMU_ACCESS_WRITE.\r
+     After the memory is used, the memory need set 0 to keep it being protected.\r
+  2) DeviceHandle can be an ACPI device (ISA, I2C, SPI, etc).\r
+     The memory for DMA access need set EDKII_IOMMU_ACCESS_READ and/or EDKII_IOMMU_ACCESS_WRITE.\r
+\r
+  @param[in]  This              The protocol instance pointer.\r
+  @param[in]  DeviceHandle      The device who initiates the DMA access request.\r
+  @param[in]  DeviceAddress     The base of device memory address to be used as the DMA memory.\r
+  @param[in]  Length            The length of device memory address to be used as the DMA memory.\r
+  @param[in]  IoMmuAccess       The IOMMU access.\r
+\r
+  @retval EFI_SUCCESS            The IoMmuAccess is set for the memory range specified by DeviceAddress and Length.\r
+  @retval EFI_INVALID_PARAMETER  DeviceHandle is an invalid handle.\r
+  @retval EFI_INVALID_PARAMETER  DeviceAddress is not IoMmu Page size aligned.\r
+  @retval EFI_INVALID_PARAMETER  Length is not IoMmu Page size aligned.\r
+  @retval EFI_INVALID_PARAMETER  Length is 0.\r
+  @retval EFI_INVALID_PARAMETER  IoMmuAccess specified an illegal combination of access.\r
+  @retval EFI_UNSUPPORTED        DeviceHandle is unknown by the IOMMU.\r
+  @retval EFI_UNSUPPORTED        The bit mask of IoMmuAccess is not supported by the IOMMU.\r
+  @retval EFI_UNSUPPORTED        The IOMMU does not support the memory range specified by DeviceAddress and Length.\r
+  @retval EFI_OUT_OF_RESOURCES   There are not enough resources available to modify the IOMMU access.\r
+  @retval EFI_DEVICE_ERROR       The IOMMU device reported an error while attempting the operation.\r
+\r
+**/\r
+EFI_STATUS\r
+VTdSetAttribute (\r
+  IN EDKII_IOMMU_PROTOCOL  *This,\r
+  IN EFI_HANDLE            DeviceHandle,\r
+  IN EFI_PHYSICAL_ADDRESS  DeviceAddress,\r
+  IN UINT64                Length,\r
+  IN UINT64                IoMmuAccess\r
+  )\r
+{\r
+  EFI_STATUS           Status;\r
+  UINT16               Segment;\r
+  VTD_SOURCE_ID        SourceId;\r
+\r
+  DumpVtdIfError ();\r
+\r
+  Status = DeviceHandleToSourceId (DeviceHandle, &Segment, &SourceId);\r
+  if (EFI_ERROR(Status)) {\r
+    return Status;\r
+  }\r
+\r
+  DEBUG ((DEBUG_VERBOSE, "IoMmuSetAttribute: "));\r
+  DEBUG ((DEBUG_VERBOSE, "PCI(S%x.B%x.D%x.F%x) ", Segment, SourceId.Bits.Bus, SourceId.Bits.Device, SourceId.Bits.Function));\r
+  DEBUG ((DEBUG_VERBOSE, "(0x%lx~0x%lx) - %lx\n", DeviceAddress, Length, IoMmuAccess));\r
+\r
+  Status = SetAccessAttribute (Segment, SourceId, DeviceAddress, Length, IoMmuAccess);\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Set IOMMU attribute for a system memory.\r
+\r
+  If the IOMMU protocol exists, the system memory cannot be used\r
+  for DMA by default.\r
+\r
+  When a device requests a DMA access for a system memory,\r
+  the device driver need use SetAttribute() to update the IOMMU\r
+  attribute to request DMA access (read and/or write).\r
+\r
+  The DeviceHandle is used to identify which device submits the request.\r
+  The IOMMU implementation need translate the device path to an IOMMU device ID,\r
+  and set IOMMU hardware register accordingly.\r
+  1) DeviceHandle can be a standard PCI device.\r
+     The memory for BusMasterRead need set EDKII_IOMMU_ACCESS_READ.\r
+     The memory for BusMasterWrite need set EDKII_IOMMU_ACCESS_WRITE.\r
+     The memory for BusMasterCommonBuffer need set EDKII_IOMMU_ACCESS_READ|EDKII_IOMMU_ACCESS_WRITE.\r
+     After the memory is used, the memory need set 0 to keep it being protected.\r
+  2) DeviceHandle can be an ACPI device (ISA, I2C, SPI, etc).\r
+     The memory for DMA access need set EDKII_IOMMU_ACCESS_READ and/or EDKII_IOMMU_ACCESS_WRITE.\r
+\r
+  @param[in]  This              The protocol instance pointer.\r
+  @param[in]  DeviceHandle      The device who initiates the DMA access request.\r
+  @param[in]  Mapping           The mapping value returned from Map().\r
+  @param[in]  IoMmuAccess       The IOMMU access.\r
+\r
+  @retval EFI_SUCCESS            The IoMmuAccess is set for the memory range specified by DeviceAddress and Length.\r
+  @retval EFI_INVALID_PARAMETER  DeviceHandle is an invalid handle.\r
+  @retval EFI_INVALID_PARAMETER  Mapping is not a value that was returned by Map().\r
+  @retval EFI_INVALID_PARAMETER  IoMmuAccess specified an illegal combination of access.\r
+  @retval EFI_UNSUPPORTED        DeviceHandle is unknown by the IOMMU.\r
+  @retval EFI_UNSUPPORTED        The bit mask of IoMmuAccess is not supported by the IOMMU.\r
+  @retval EFI_UNSUPPORTED        The IOMMU does not support the memory range specified by Mapping.\r
+  @retval EFI_OUT_OF_RESOURCES   There are not enough resources available to modify the IOMMU access.\r
+  @retval EFI_DEVICE_ERROR       The IOMMU device reported an error while attempting the operation.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IoMmuSetAttribute (\r
+  IN EDKII_IOMMU_PROTOCOL  *This,\r
+  IN EFI_HANDLE            DeviceHandle,\r
+  IN VOID                  *Mapping,\r
+  IN UINT64                IoMmuAccess\r
+  )\r
+{\r
+  EFI_STATUS            Status;\r
+  EFI_PHYSICAL_ADDRESS  DeviceAddress;\r
+  UINTN                 NumberOfPages;\r
+\r
+  Status = GetDeviceInfoFromMapping (Mapping, &DeviceAddress, &NumberOfPages);\r
+  if (EFI_ERROR(Status)) {\r
+    return Status;\r
+  }\r
+  Status = VTdSetAttribute (\r
+             This,\r
+             DeviceHandle,\r
+             DeviceAddress,\r
+             EFI_PAGES_TO_SIZE(NumberOfPages),\r
+             IoMmuAccess\r
+             );\r
+\r
+  return Status;\r
+}\r
+\r
+EDKII_IOMMU_PROTOCOL  mIntelVTd = {\r
+  EDKII_IOMMU_PROTOCOL_REVISION,\r
+  IoMmuSetAttribute,\r
+  IoMmuMap,\r
+  IoMmuUnmap,\r
+  IoMmuAllocateBuffer,\r
+  IoMmuFreeBuffer,\r
+};\r
+\r
+/**\r
+  Initialize the VTd driver.\r
+\r
+  @param[in]  ImageHandle  ImageHandle of the loaded driver\r
+  @param[in]  SystemTable  Pointer to the System Table\r
+\r
+  @retval  EFI_SUCCESS           The Protocol is installed.\r
+  @retval  EFI_OUT_OF_RESOURCES  Not enough resources available to initialize driver.\r
+  @retval  EFI_DEVICE_ERROR      A device error occurred attempting to initialize the driver.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IntelVTdInitialize (\r
+  IN EFI_HANDLE        ImageHandle,\r
+  IN EFI_SYSTEM_TABLE  *SystemTable\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+  EFI_HANDLE  Handle;\r
+\r
+  InitializeDmaProtection ();\r
+\r
+  Handle = NULL;\r
+  Status = gBS->InstallMultipleProtocolInterfaces (\r
+                  &Handle,\r
+                  &gEdkiiIoMmuProtocolGuid, &mIntelVTd,\r
+                  NULL\r
+                  );\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  return Status;\r
+}\r
diff --git a/IntelSiliconPkg/IntelVTdDxe/IntelVTdDxe.inf b/IntelSiliconPkg/IntelVTdDxe/IntelVTdDxe.inf
new file mode 100644 (file)
index 0000000..6a61c13
--- /dev/null
@@ -0,0 +1,79 @@
+## @file\r
+# Intel VTd DXE Driver.\r
+#\r
+# This driver initializes VTd engine based upon DMAR ACPI tables\r
+# and provide DMA protection to PCI or ACPI device.\r
+#\r
+# Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>\r
+# This program and the accompanying materials\r
+# are licensed and made available under the terms and conditions of the BSD License\r
+# which accompanies this distribution.  The full text of the license may be found at\r
+# http://opensource.org/licenses/bsd-license.php\r
+#\r
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+#\r
+##\r
+\r
+[Defines]\r
+  INF_VERSION                    = 0x00010005\r
+  BASE_NAME                      = IntelVTdDxe\r
+  MODULE_UNI_FILE                = IntelVTdDxe.uni\r
+  FILE_GUID                      = 987555D6-595D-4CFA-B895-59B89368BD4D\r
+  MODULE_TYPE                    = DXE_DRIVER\r
+  VERSION_STRING                 = 1.0\r
+  ENTRY_POINT                    = IntelVTdInitialize\r
+\r
+#\r
+# The following information is for reference only and not required by the build tools.\r
+#\r
+#  VALID_ARCHITECTURES           = IA32 X64 IPF EBC\r
+#\r
+#\r
+\r
+[Sources]\r
+  IntelVTdDxe.c\r
+  BmDma.c\r
+  DmaProtection.c\r
+  DmaProtection.h\r
+  DmarAcpiTable.c\r
+  PciInfo.c\r
+  TranslationTable.c\r
+  TranslationTableEx.c\r
+  VtdReg.c\r
+\r
+[Packages]\r
+  MdePkg/MdePkg.dec\r
+  MdeModulePkg/MdeModulePkg.dec\r
+  IntelSiliconPkg/IntelSiliconPkg.dec\r
+\r
+[LibraryClasses]\r
+  DebugLib\r
+  UefiDriverEntryPoint\r
+  UefiBootServicesTableLib\r
+  BaseLib\r
+  IoLib\r
+  PciSegmentLib\r
+  BaseMemoryLib\r
+  MemoryAllocationLib\r
+  UefiLib\r
+\r
+[Guids]\r
+  gEfiEventExitBootServicesGuid   ## CONSUMES ## Event\r
+  gEfiAcpi20TableGuid             ## CONSUMES ## SystemTable\r
+  gEfiAcpiTableGuid               ## CONSUMES ## SystemTable\r
+\r
+[Protocols]\r
+  gEdkiiIoMmuProtocolGuid                     ## PRODUCES\r
+  gEfiAcpiSdtProtocolGuid                     ## CONSUMES\r
+  gEfiPciIoProtocolGuid                       ## CONSUMES\r
+  gEfiPciEnumerationCompleteProtocolGuid      ## CONSUMES\r
+  gEdkiiPlatformVTdPolicyProtocolGuid         ## SOMETIMES_CONSUMES\r
+\r
+[Depex]\r
+  gEfiPciRootBridgeIoProtocolGuid AND\r
+  gEfiAcpiSdtProtocolGuid\r
+\r
+[UserExtensions.TianoCore."ExtraFiles"]\r
+  IntelVTdDxeExtra.uni\r
+\r
diff --git a/IntelSiliconPkg/IntelVTdDxe/IntelVTdDxe.uni b/IntelSiliconPkg/IntelVTdDxe/IntelVTdDxe.uni
new file mode 100644 (file)
index 0000000..45ce3c3
--- /dev/null
@@ -0,0 +1,20 @@
+// /** @file\r
+// IntelVTdDxe Module Localized Abstract and Description Content\r
+//\r
+// Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>\r
+//\r
+// This program and the accompanying materials are\r
+// licensed and made available under the terms and conditions of the BSD License\r
+// which accompanies this distribution.  The full text of the license may be found at\r
+// http://opensource.org/licenses/bsd-license.php\r
+//\r
+// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+//\r
+// **/\r
+\r
+\r
+#string STR_MODULE_ABSTRACT             #language en-US "Intel VTd DXE Driver."\r
+\r
+#string STR_MODULE_DESCRIPTION          #language en-US "This driver initializes VTd engine based upon DMAR ACPI tables and provide DMA protection to PCI or ACPI device."\r
+\r
diff --git a/IntelSiliconPkg/IntelVTdDxe/IntelVTdDxeExtra.uni b/IntelSiliconPkg/IntelVTdDxe/IntelVTdDxeExtra.uni
new file mode 100644 (file)
index 0000000..5cb3eb0
--- /dev/null
@@ -0,0 +1,20 @@
+// /** @file\r
+// IntelVTdDxe Localized Strings and Content\r
+//\r
+// Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>\r
+//\r
+// This program and the accompanying materials are\r
+// licensed and made available under the terms and conditions of the BSD License\r
+// which accompanies this distribution.  The full text of the license may be found at\r
+// http://opensource.org/licenses/bsd-license.php\r
+//\r
+// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+//\r
+// **/\r
+\r
+#string STR_PROPERTIES_MODULE_NAME\r
+#language en-US\r
+"Intel VTd DXE Driver"\r
+\r
+\r
diff --git a/IntelSiliconPkg/IntelVTdDxe/PciInfo.c b/IntelSiliconPkg/IntelVTdDxe/PciInfo.c
new file mode 100644 (file)
index 0000000..ea84317
--- /dev/null
@@ -0,0 +1,315 @@
+/** @file\r
+\r
+  Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>\r
+  This program and the accompanying materials\r
+  are licensed and made available under the terms and conditions of the BSD License\r
+  which accompanies this distribution.  The full text of the license may be found at\r
+  http://opensource.org/licenses/bsd-license.php.\r
+\r
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "DmaProtection.h"\r
+\r
+/**\r
+  Return the index of PCI descriptor.\r
+\r
+  @param[in]  VtdIndex          The index used to identify a VTd engine.\r
+  @param[in]  Segment           The Segment used to identify a VTd engine.\r
+  @param[in]  SourceId          The SourceId used to identify a VTd engine and table entry.\r
+\r
+  @return The index of the PCI descriptor.\r
+  @retval (UINTN)-1  The PCI descriptor is not found.\r
+**/\r
+UINTN\r
+GetPciDescriptor (\r
+  IN UINTN          VtdIndex,\r
+  IN UINT16         Segment,\r
+  IN VTD_SOURCE_ID  SourceId\r
+  )\r
+{\r
+  UINTN  Index;\r
+\r
+  if (Segment != mVtdUnitInformation[VtdIndex].Segment) {\r
+    return (UINTN)-1;\r
+  }\r
+\r
+  for (Index = 0; Index < mVtdUnitInformation[VtdIndex].PciDeviceInfo.PciDescriptorNumber; Index++) {\r
+    if ((mVtdUnitInformation[VtdIndex].PciDeviceInfo.PciDescriptors[Index].Bits.Bus == SourceId.Bits.Bus) &&\r
+        (mVtdUnitInformation[VtdIndex].PciDeviceInfo.PciDescriptors[Index].Bits.Device == SourceId.Bits.Device) &&\r
+        (mVtdUnitInformation[VtdIndex].PciDeviceInfo.PciDescriptors[Index].Bits.Function == SourceId.Bits.Function) ) {\r
+      return Index;\r
+    }\r
+  }\r
+\r
+  return (UINTN)-1;\r
+}\r
+\r
+/**\r
+  Register PCI device to VTd engine as PCI descriptor.\r
+\r
+  @param[in]  VtdIndex              The index of VTd engine.\r
+  @param[in]  Segment               The segment of the source.\r
+  @param[in]  SourceId              The SourceId of the source.\r
+  @param[in]  IsRealPciDevice       TRUE: It is a real PCI device.\r
+                                    FALSE: It is not a real PCI device.\r
+  @param[in]  CheckExist            TRUE: ERROR will be returned if the PCI device is already registered.\r
+                                    FALSE: SUCCESS will be returned if the PCI device is registered.\r
+\r
+  @retval EFI_SUCCESS           The PCI device is registered.\r
+  @retval EFI_OUT_OF_RESOURCES  No enough resource to register a new PCI device.\r
+  @retval EFI_ALREADY_STARTED   The device is already registered.\r
+**/\r
+EFI_STATUS\r
+RegisterPciDevice (\r
+  IN UINTN          VtdIndex,\r
+  IN UINT16         Segment,\r
+  IN VTD_SOURCE_ID  SourceId,\r
+  IN BOOLEAN        IsRealPciDevice,\r
+  IN BOOLEAN        CheckExist\r
+  )\r
+{\r
+  PCI_DEVICE_INFORMATION  *PciDeviceInfo;\r
+  VTD_SOURCE_ID           *PciDescriptor;\r
+  UINTN                   PciDescriptorIndex;\r
+  UINTN                   Index;\r
+  BOOLEAN                 *NewIsRealPciDevice;\r
+  VTD_SOURCE_ID           *NewPciDescriptors;\r
+\r
+  PciDeviceInfo = &mVtdUnitInformation[VtdIndex].PciDeviceInfo;\r
+\r
+  if (PciDeviceInfo->IncludeAllFlag) {\r
+    //\r
+    // Do not register device in other VTD Unit\r
+    //\r
+    for (Index = 0; Index < VtdIndex; Index++) {\r
+      PciDescriptorIndex = GetPciDescriptor (Index, Segment, SourceId);\r
+      if (PciDescriptorIndex != (UINTN)-1) {\r
+        DEBUG ((DEBUG_INFO, "  RegisterPciDevice: PCI S%04x B%02x D%02x F%02x already registered by Other Vtd(%d)\n", Segment, SourceId.Bits.Bus, SourceId.Bits.Device, SourceId.Bits.Function, Index));\r
+        return EFI_SUCCESS;\r
+      }\r
+    }\r
+  }\r
+\r
+  PciDescriptorIndex = GetPciDescriptor (VtdIndex, Segment, SourceId);\r
+  if (PciDescriptorIndex == (UINTN)-1) {\r
+    //\r
+    // Register new\r
+    //\r
+\r
+    if (PciDeviceInfo->PciDescriptorNumber >= PciDeviceInfo->PciDescriptorMaxNumber) {\r
+      //\r
+      // Reallocate\r
+      //\r
+      NewIsRealPciDevice = AllocateZeroPool (sizeof(*NewIsRealPciDevice) * (PciDeviceInfo->PciDescriptorMaxNumber + MAX_PCI_DESCRIPTORS));\r
+      if (NewIsRealPciDevice == NULL) {\r
+        return EFI_OUT_OF_RESOURCES;\r
+      }\r
+      NewPciDescriptors = AllocateZeroPool (sizeof(*NewPciDescriptors) * (PciDeviceInfo->PciDescriptorMaxNumber + MAX_PCI_DESCRIPTORS));\r
+      if (NewPciDescriptors == NULL) {\r
+        FreePool (NewIsRealPciDevice);\r
+        return EFI_OUT_OF_RESOURCES;\r
+      }\r
+      PciDeviceInfo->PciDescriptorMaxNumber += MAX_PCI_DESCRIPTORS;\r
+      if (PciDeviceInfo->IsRealPciDevice != NULL) {\r
+        CopyMem (NewIsRealPciDevice, PciDeviceInfo->IsRealPciDevice, sizeof(*NewIsRealPciDevice) * PciDeviceInfo->PciDescriptorNumber);\r
+        FreePool (PciDeviceInfo->IsRealPciDevice);\r
+      }\r
+      PciDeviceInfo->IsRealPciDevice = NewIsRealPciDevice;\r
+      if (PciDeviceInfo->PciDescriptors != NULL) {\r
+        CopyMem (NewPciDescriptors, PciDeviceInfo->PciDescriptors, sizeof(*NewPciDescriptors) * PciDeviceInfo->PciDescriptorNumber);\r
+        FreePool (PciDeviceInfo->PciDescriptors);\r
+      }\r
+      PciDeviceInfo->PciDescriptors = NewPciDescriptors;\r
+    }\r
+\r
+    ASSERT (PciDeviceInfo->PciDescriptorNumber < PciDeviceInfo->PciDescriptorMaxNumber);\r
+\r
+    PciDescriptor = &PciDeviceInfo->PciDescriptors[PciDeviceInfo->PciDescriptorNumber];\r
+    PciDescriptor->Bits.Bus = SourceId.Bits.Bus;\r
+    PciDescriptor->Bits.Device = SourceId.Bits.Device;\r
+    PciDescriptor->Bits.Function = SourceId.Bits.Function;\r
+    PciDeviceInfo->IsRealPciDevice[PciDeviceInfo->PciDescriptorNumber] = IsRealPciDevice;\r
+\r
+    PciDeviceInfo->PciDescriptorNumber++;\r
+\r
+    DEBUG ((DEBUG_INFO, "  RegisterPciDevice: PCI S%04x B%02x D%02x F%02x", Segment, SourceId.Bits.Bus, SourceId.Bits.Device, SourceId.Bits.Function));\r
+    if (!IsRealPciDevice) {\r
+      DEBUG ((DEBUG_INFO, " (*)"));\r
+    }\r
+    DEBUG ((DEBUG_INFO, "\n"));\r
+  } else {\r
+    if (CheckExist) {\r
+      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));\r
+      return EFI_ALREADY_STARTED;\r
+    }\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Scan PCI bus and register PCI devices under the bus.\r
+\r
+  @param[in]  VtdIndex              The index of VTd engine.\r
+  @param[in]  Segment               The segment of the source.\r
+  @param[in]  Bus                   The bus of the source.\r
+\r
+  @retval EFI_SUCCESS           The PCI devices under the bus are registered.\r
+  @retval EFI_OUT_OF_RESOURCES  No enough resource to register a new PCI device.\r
+**/\r
+EFI_STATUS\r
+ScanPciBus (\r
+  IN UINTN          VtdIndex,\r
+  IN UINT16         Segment,\r
+  IN UINT8          Bus\r
+  )\r
+{\r
+  UINT8                   Device;\r
+  UINT8                   Function;\r
+  UINT8                   SecondaryBusNumber;\r
+  UINT8                   HeaderType;\r
+  UINT8                   BaseClass;\r
+  UINT8                   SubClass;\r
+  UINT32                  MaxFunction;\r
+  UINT16                  VendorID;\r
+  UINT16                  DeviceID;\r
+  EFI_STATUS              Status;\r
+  VTD_SOURCE_ID           SourceId;\r
+\r
+  // Scan the PCI bus for devices\r
+  for (Device = 0; Device < PCI_MAX_DEVICE + 1; Device++) {\r
+    HeaderType = PciSegmentRead8 (PCI_SEGMENT_LIB_ADDRESS(Segment, Bus, Device, 0, PCI_HEADER_TYPE_OFFSET));\r
+    MaxFunction = PCI_MAX_FUNC + 1;\r
+    if ((HeaderType & HEADER_TYPE_MULTI_FUNCTION) == 0x00) {\r
+      MaxFunction = 1;\r
+    }\r
+    for (Function = 0; Function < MaxFunction; Function++) {\r
+      VendorID  = PciSegmentRead16 (PCI_SEGMENT_LIB_ADDRESS(Segment, Bus, Device, Function, PCI_VENDOR_ID_OFFSET));\r
+      DeviceID  = PciSegmentRead16 (PCI_SEGMENT_LIB_ADDRESS(Segment, Bus, Device, Function, PCI_DEVICE_ID_OFFSET));\r
+      if (VendorID == 0xFFFF && DeviceID == 0xFFFF) {\r
+        continue;\r
+      }\r
+\r
+      SourceId.Bits.Bus = Bus;\r
+      SourceId.Bits.Device = Device;\r
+      SourceId.Bits.Function = Function;\r
+      Status = RegisterPciDevice (VtdIndex, Segment, SourceId, TRUE, FALSE);\r
+      if (EFI_ERROR (Status)) {\r
+        return Status;\r
+      }\r
+\r
+      BaseClass = PciSegmentRead8 (PCI_SEGMENT_LIB_ADDRESS(Segment, Bus, Device, Function, PCI_CLASSCODE_OFFSET + 2));\r
+      if (BaseClass == PCI_CLASS_BRIDGE) {\r
+        SubClass = PciSegmentRead8 (PCI_SEGMENT_LIB_ADDRESS(Segment, Bus, Device, Function, PCI_CLASSCODE_OFFSET + 1));\r
+        if (SubClass == PCI_CLASS_BRIDGE_P2P) {\r
+          SecondaryBusNumber = PciSegmentRead8 (PCI_SEGMENT_LIB_ADDRESS(Segment, Bus, Device, Function, PCI_BRIDGE_SECONDARY_BUS_REGISTER_OFFSET));\r
+          DEBUG ((DEBUG_INFO,"  ScanPciBus: PCI bridge S%04x B%02x D%02x F%02x (SecondBus:%02x)\n", Segment, Bus, Device, Function, SecondaryBusNumber));\r
+          if (SecondaryBusNumber != 0) {\r
+            Status = ScanPciBus (VtdIndex, Segment, SecondaryBusNumber);\r
+            if (EFI_ERROR (Status)) {\r
+              return Status;\r
+            }\r
+          }\r
+        }\r
+      }\r
+    }\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Dump the PCI device information managed by this VTd engine.\r
+\r
+  @param[in]  VtdIndex              The index of VTd engine.\r
+**/\r
+VOID\r
+DumpPciDeviceInfo (\r
+  IN UINTN  VtdIndex\r
+  )\r
+{\r
+  UINTN  Index;\r
+\r
+  DEBUG ((DEBUG_INFO,"PCI Device Information (Number 0x%x, IncludeAll - %d):\n",\r
+    mVtdUnitInformation[VtdIndex].PciDeviceInfo.PciDescriptorNumber,\r
+    mVtdUnitInformation[VtdIndex].PciDeviceInfo.IncludeAllFlag\r
+    ));\r
+  for (Index = 0; Index < mVtdUnitInformation[VtdIndex].PciDeviceInfo.PciDescriptorNumber; Index++) {\r
+    DEBUG ((DEBUG_INFO,"  S%04x B%02x D%02x F%02x\n",\r
+      mVtdUnitInformation[VtdIndex].Segment,\r
+      mVtdUnitInformation[VtdIndex].PciDeviceInfo.PciDescriptors[Index].Bits.Bus,\r
+      mVtdUnitInformation[VtdIndex].PciDeviceInfo.PciDescriptors[Index].Bits.Device,\r
+      mVtdUnitInformation[VtdIndex].PciDeviceInfo.PciDescriptors[Index].Bits.Function\r
+      ));\r
+  }\r
+}\r
+\r
+/**\r
+  Find the VTd index by the Segment and SourceId.\r
+\r
+  @param[in]  Segment               The segment of the source.\r
+  @param[in]  SourceId              The SourceId of the source.\r
+  @param[out] ExtContextEntry       The ExtContextEntry of the source.\r
+  @param[out] ContextEntry          The ContextEntry of the source.\r
+\r
+  @return The index of the PCI descriptor.\r
+  @retval (UINTN)-1  The PCI descriptor is not found.\r
+**/\r
+UINTN\r
+FindVtdIndexByPciDevice (\r
+  IN  UINT16                  Segment,\r
+  IN  VTD_SOURCE_ID           SourceId,\r
+  OUT VTD_EXT_CONTEXT_ENTRY   **ExtContextEntry,\r
+  OUT VTD_CONTEXT_ENTRY       **ContextEntry\r
+  )\r
+{\r
+  UINTN                   VtdIndex;\r
+  VTD_ROOT_ENTRY          *RootEntry;\r
+  VTD_CONTEXT_ENTRY       *ContextEntryTable;\r
+  VTD_CONTEXT_ENTRY       *ThisContextEntry;\r
+  VTD_EXT_ROOT_ENTRY      *ExtRootEntry;\r
+  VTD_EXT_CONTEXT_ENTRY   *ExtContextEntryTable;\r
+  VTD_EXT_CONTEXT_ENTRY   *ThisExtContextEntry;\r
+  UINTN                   PciDescriptorIndex;\r
+\r
+  for (VtdIndex = 0; VtdIndex < mVtdUnitNumber; VtdIndex++) {\r
+    if (Segment != mVtdUnitInformation[VtdIndex].Segment) {\r
+      continue;\r
+    }\r
+\r
+    PciDescriptorIndex = GetPciDescriptor (VtdIndex, Segment, SourceId);\r
+    if (PciDescriptorIndex == (UINTN)-1) {\r
+      continue;\r
+    }\r
+\r
+//    DEBUG ((DEBUG_INFO,"FindVtdIndex(0x%x) for S%04x B%02x D%02x F%02x\n", VtdIndex, Segment, SourceId.Bits.Bus, SourceId.Bits.Device, SourceId.Bits.Function));\r
+\r
+    if (mVtdUnitInformation[VtdIndex].ExtRootEntryTable != 0) {\r
+      ExtRootEntry = &mVtdUnitInformation[VtdIndex].ExtRootEntryTable[SourceId.Index.RootIndex];\r
+      ExtContextEntryTable = (VTD_EXT_CONTEXT_ENTRY *)(UINTN)LShiftU64 (ExtRootEntry->Bits.LowerContextTablePointer, 12) ;\r
+      ThisExtContextEntry  = &ExtContextEntryTable[SourceId.Index.ContextIndex];\r
+      if (ThisExtContextEntry->Bits.AddressWidth == 0) {\r
+        continue;\r
+      }\r
+      *ExtContextEntry = ThisExtContextEntry;\r
+      *ContextEntry    = NULL;\r
+    } else {\r
+      RootEntry = &mVtdUnitInformation[VtdIndex].RootEntryTable[SourceId.Index.RootIndex];\r
+      ContextEntryTable = (VTD_CONTEXT_ENTRY *)(UINTN)LShiftU64 (RootEntry->Bits.ContextTablePointer, 12) ;\r
+      ThisContextEntry  = &ContextEntryTable[SourceId.Index.ContextIndex];\r
+      if (ThisContextEntry->Bits.AddressWidth == 0) {\r
+        continue;\r
+      }\r
+      *ExtContextEntry = NULL;\r
+      *ContextEntry    = ThisContextEntry;\r
+    }\r
+\r
+    return VtdIndex;\r
+  }\r
+\r
+  return (UINTN)-1;\r
+}\r
+\r
diff --git a/IntelSiliconPkg/IntelVTdDxe/TranslationTable.c b/IntelSiliconPkg/IntelVTdDxe/TranslationTable.c
new file mode 100644 (file)
index 0000000..0cff2cc
--- /dev/null
@@ -0,0 +1,969 @@
+/** @file\r
+\r
+  Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>\r
+  This program and the accompanying materials\r
+  are licensed and made available under the terms and conditions of the BSD License\r
+  which accompanies this distribution.  The full text of the license may be found at\r
+  http://opensource.org/licenses/bsd-license.php.\r
+\r
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "DmaProtection.h"\r
+\r
+/**\r
+  Create extended context entry.\r
+\r
+  @param[in]  VtdIndex  The index of the VTd engine.\r
+\r
+  @retval EFI_SUCCESS          The extended context entry is created.\r
+  @retval EFI_OUT_OF_RESOURCE  No enough resource to create extended context entry.\r
+**/\r
+EFI_STATUS\r
+CreateExtContextEntry (\r
+  IN UINTN  VtdIndex\r
+  );\r
+\r
+/**\r
+  Allocate zero pages.\r
+\r
+  @param[in]  Pages the number of pages.\r
+\r
+  @return the page address.\r
+  @retval NULL No resource to allocate pages.\r
+**/\r
+VOID *\r
+EFIAPI\r
+AllocateZeroPages (\r
+  IN UINTN  Pages\r
+  )\r
+{\r
+  VOID *Addr;\r
+\r
+  Addr = AllocatePages (Pages);\r
+  if (Addr == NULL) {\r
+    return NULL;\r
+  }\r
+  ZeroMem (Addr, EFI_PAGES_TO_SIZE(Pages));\r
+  return Addr;\r
+}\r
+\r
+/**\r
+  Set second level paging entry attribute based upon IoMmuAccess.\r
+\r
+  @param[in]  PtEntry      The paging entry.\r
+  @param[in]  IoMmuAccess  The IOMMU access.\r
+**/\r
+VOID\r
+SetSecondLevelPagingEntryAttribute (\r
+  IN VTD_SECOND_LEVEL_PAGING_ENTRY  *PtEntry,\r
+  IN UINT64                         IoMmuAccess\r
+  )\r
+{\r
+  PtEntry->Bits.Read  = ((IoMmuAccess & EDKII_IOMMU_ACCESS_READ) != 0);\r
+  PtEntry->Bits.Write = ((IoMmuAccess & EDKII_IOMMU_ACCESS_WRITE) != 0);\r
+}\r
+\r
+/**\r
+  Create context entry.\r
+\r
+  @param[in]  VtdIndex  The index of the VTd engine.\r
+\r
+  @retval EFI_SUCCESS          The context entry is created.\r
+  @retval EFI_OUT_OF_RESOURCE  No enough resource to create context entry.\r
+**/\r
+EFI_STATUS\r
+CreateContextEntry (\r
+  IN UINTN  VtdIndex\r
+  )\r
+{\r
+  UINTN                  Index;\r
+  VOID                   *Buffer;\r
+  UINTN                  RootPages;\r
+  UINTN                  ContextPages;\r
+  VTD_ROOT_ENTRY         *RootEntry;\r
+  VTD_CONTEXT_ENTRY      *ContextEntryTable;\r
+  VTD_CONTEXT_ENTRY      *ContextEntry;\r
+  VTD_SOURCE_ID          *PciDescriptor;\r
+  VTD_SOURCE_ID          SourceId;\r
+  UINTN                  MaxBusNumber;\r
+  UINTN                  EntryTablePages;\r
+\r
+  MaxBusNumber = 0;\r
+  for (Index = 0; Index < mVtdUnitInformation[VtdIndex].PciDeviceInfo.PciDescriptorNumber; Index++) {\r
+    PciDescriptor = &mVtdUnitInformation[VtdIndex].PciDeviceInfo.PciDescriptors[Index];\r
+    if (PciDescriptor->Bits.Bus > MaxBusNumber) {\r
+      MaxBusNumber = PciDescriptor->Bits.Bus;\r
+    }\r
+  }\r
+  DEBUG ((DEBUG_INFO,"  MaxBusNumber - 0x%x\n", MaxBusNumber));\r
+\r
+  RootPages = EFI_SIZE_TO_PAGES (sizeof (VTD_ROOT_ENTRY) * VTD_ROOT_ENTRY_NUMBER);\r
+  ContextPages = EFI_SIZE_TO_PAGES (sizeof (VTD_CONTEXT_ENTRY) * VTD_CONTEXT_ENTRY_NUMBER);\r
+  EntryTablePages = RootPages + ContextPages * (MaxBusNumber + 1);\r
+  Buffer = AllocateZeroPages (EntryTablePages);\r
+  if (Buffer == NULL) {\r
+    DEBUG ((DEBUG_INFO,"Could not Alloc Root Entry Table.. \n"));\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+  mVtdUnitInformation[VtdIndex].RootEntryTable = (VTD_ROOT_ENTRY *)Buffer;\r
+  Buffer = (UINT8 *)Buffer + EFI_PAGES_TO_SIZE (RootPages);\r
+\r
+  for (Index = 0; Index < mVtdUnitInformation[VtdIndex].PciDeviceInfo.PciDescriptorNumber; Index++) {\r
+    PciDescriptor = &mVtdUnitInformation[VtdIndex].PciDeviceInfo.PciDescriptors[Index];\r
+\r
+    SourceId.Bits.Bus = PciDescriptor->Bits.Bus;\r
+    SourceId.Bits.Device = PciDescriptor->Bits.Device;\r
+    SourceId.Bits.Function = PciDescriptor->Bits.Function;\r
+\r
+    RootEntry = &mVtdUnitInformation[VtdIndex].RootEntryTable[SourceId.Index.RootIndex];\r
+    if (RootEntry->Bits.Present == 0) {\r
+      RootEntry->Bits.ContextTablePointer  = RShiftU64 ((UINT64)(UINTN)Buffer, 12);\r
+      RootEntry->Bits.Present = 1;\r
+      Buffer = (UINT8 *)Buffer + EFI_PAGES_TO_SIZE (ContextPages);\r
+    }\r
+\r
+    ContextEntryTable = (VTD_CONTEXT_ENTRY *)(UINTN)LShiftU64(RootEntry->Bits.ContextTablePointer, 12) ;\r
+    ContextEntry = &ContextEntryTable[SourceId.Index.ContextIndex];\r
+    ContextEntry->Bits.TranslationType = 0;\r
+    ContextEntry->Bits.FaultProcessingDisable = 0;\r
+    ContextEntry->Bits.Present = 0;\r
+\r
+    DEBUG ((DEBUG_INFO,"Source: S%04x B%02x D%02x F%02x\n", mVtdUnitInformation[VtdIndex].Segment, SourceId.Bits.Bus, SourceId.Bits.Device, SourceId.Bits.Function));\r
+\r
+    switch (mVtdUnitInformation[VtdIndex].CapReg.Bits.SAGAW) {\r
+    case BIT1:\r
+      ContextEntry->Bits.AddressWidth = 0x1;\r
+      break;\r
+    case BIT2:\r
+      ContextEntry->Bits.AddressWidth = 0x2;\r
+      break;\r
+    }\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Create second level paging entry table.\r
+\r
+  @param[in]  VtdIndex                    The index of the VTd engine.\r
+  @param[in]  SecondLevelPagingEntry      The second level paging entry.\r
+  @param[in]  MemoryBase                  The base of the memory.\r
+  @param[in]  MemoryLimit                 The limit of the memory.\r
+  @param[in]  IoMmuAccess                 The IOMMU access.\r
+\r
+  @return The second level paging entry.\r
+**/\r
+VTD_SECOND_LEVEL_PAGING_ENTRY *\r
+CreateSecondLevelPagingEntryTable (\r
+  IN UINTN                         VtdIndex,\r
+  IN VTD_SECOND_LEVEL_PAGING_ENTRY *SecondLevelPagingEntry,\r
+  IN UINT64                        MemoryBase,\r
+  IN UINT64                        MemoryLimit,\r
+  IN UINT64                        IoMmuAccess\r
+  )\r
+{\r
+  UINTN                          Index4;\r
+  UINTN                          Index3;\r
+  UINTN                          Index2;\r
+  UINTN                          Lvl4Start;\r
+  UINTN                          Lvl4End;\r
+  UINTN                          Lvl3Start;\r
+  UINTN                          Lvl3End;\r
+  VTD_SECOND_LEVEL_PAGING_ENTRY  *Lvl4PtEntry;\r
+  VTD_SECOND_LEVEL_PAGING_ENTRY  *Lvl3PtEntry;\r
+  VTD_SECOND_LEVEL_PAGING_ENTRY  *Lvl2PtEntry;\r
+  UINT64                         BaseAddress;\r
+  UINT64                         EndAddress;\r
+\r
+  if (MemoryLimit == 0) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  BaseAddress = ALIGN_VALUE_LOW(MemoryBase, SIZE_2MB);\r
+  EndAddress = ALIGN_VALUE_UP(MemoryLimit, SIZE_2MB);\r
+  DEBUG ((DEBUG_INFO,"CreateSecondLevelPagingEntryTable: BaseAddress - 0x%016lx, EndAddress - 0x%016lx\n", BaseAddress, EndAddress));\r
+\r
+  if (SecondLevelPagingEntry == NULL) {\r
+    SecondLevelPagingEntry = AllocateZeroPages (1);\r
+    if (SecondLevelPagingEntry == NULL) {\r
+      DEBUG ((DEBUG_ERROR,"Could not Alloc LVL4 PT. \n"));\r
+      return NULL;\r
+    }\r
+  }\r
+\r
+  //\r
+  // If no access is needed, just create not present entry.\r
+  //\r
+  if (IoMmuAccess == 0) {\r
+    return SecondLevelPagingEntry;\r
+  }\r
+\r
+  Lvl4Start = RShiftU64 (BaseAddress, 39) & 0x1FF;\r
+  Lvl4End = RShiftU64 (EndAddress - 1, 39) & 0x1FF;\r
+\r
+  DEBUG ((DEBUG_INFO,"  Lvl4Start - 0x%x, Lvl4End - 0x%x\n", Lvl4Start, Lvl4End));\r
+\r
+  Lvl4PtEntry = (VTD_SECOND_LEVEL_PAGING_ENTRY *)SecondLevelPagingEntry;\r
+  for (Index4 = Lvl4Start; Index4 <= Lvl4End; Index4++) {\r
+    if (Lvl4PtEntry[Index4].Uint64 == 0) {\r
+      Lvl4PtEntry[Index4].Uint64 = (UINT64)(UINTN)AllocateZeroPages (1);\r
+      if (Lvl4PtEntry[Index4].Uint64 == 0) {\r
+        DEBUG ((DEBUG_ERROR,"!!!!!! ALLOCATE LVL4 PAGE FAIL (0x%x)!!!!!!\n", Index4));\r
+        ASSERT(FALSE);\r
+        return NULL;\r
+      }\r
+      SetSecondLevelPagingEntryAttribute (&Lvl4PtEntry[Index4], EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE);\r
+    }\r
+\r
+    Lvl3Start = RShiftU64 (BaseAddress, 30) & 0x1FF;\r
+    if (ALIGN_VALUE_LOW(BaseAddress + SIZE_1GB, SIZE_1GB) <= EndAddress) {\r
+      Lvl3End = SIZE_4KB/sizeof(VTD_SECOND_LEVEL_PAGING_ENTRY);\r
+    } else {\r
+      Lvl3End = RShiftU64 (EndAddress - 1, 30) & 0x1FF;\r
+    }\r
+    DEBUG ((DEBUG_INFO,"  Lvl4(0x%x): Lvl3Start - 0x%x, Lvl3End - 0x%x\n", Index4, Lvl3Start, Lvl3End));\r
+\r
+    Lvl3PtEntry = (VTD_SECOND_LEVEL_PAGING_ENTRY *)(UINTN)LShiftU64 (Lvl4PtEntry[Index4].Bits.Address, 12);\r
+    for (Index3 = Lvl3Start; Index3 <= Lvl3End; Index3++) {\r
+      if (Lvl3PtEntry[Index3].Uint64 == 0) {\r
+        Lvl3PtEntry[Index3].Uint64 = (UINT64)(UINTN)AllocateZeroPages (1);\r
+        if (Lvl3PtEntry[Index3].Uint64 == 0) {\r
+          DEBUG ((DEBUG_ERROR,"!!!!!! ALLOCATE LVL3 PAGE FAIL (0x%x, 0x%x)!!!!!!\n", Index4, Index3));\r
+          ASSERT(FALSE);\r
+          return NULL;\r
+        }\r
+        SetSecondLevelPagingEntryAttribute (&Lvl3PtEntry[Index3], EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE);\r
+      }\r
+\r
+      Lvl2PtEntry = (VTD_SECOND_LEVEL_PAGING_ENTRY *)(UINTN)LShiftU64 (Lvl3PtEntry[Index3].Bits.Address, 12);\r
+      for (Index2 = 0; Index2 < SIZE_4KB/sizeof(VTD_SECOND_LEVEL_PAGING_ENTRY); Index2++) {\r
+        Lvl2PtEntry[Index2].Uint64 = BaseAddress;\r
+        SetSecondLevelPagingEntryAttribute (&Lvl2PtEntry[Index2], IoMmuAccess);\r
+        Lvl2PtEntry[Index2].Bits.PageSize = 1;\r
+        BaseAddress += SIZE_2MB;\r
+        if (BaseAddress >= MemoryLimit) {\r
+          goto Done;\r
+        }\r
+      }\r
+    }\r
+  }\r
+\r
+Done:\r
+  return SecondLevelPagingEntry;\r
+}\r
+\r
+/**\r
+  Create second level paging entry.\r
+\r
+  @param[in]  VtdIndex                    The index of the VTd engine.\r
+  @param[in]  IoMmuAccess                 The IOMMU access.\r
+\r
+  @return The second level paging entry.\r
+**/\r
+VTD_SECOND_LEVEL_PAGING_ENTRY *\r
+CreateSecondLevelPagingEntry (\r
+  IN UINTN   VtdIndex,\r
+  IN UINT64  IoMmuAccess\r
+  )\r
+{\r
+  VTD_SECOND_LEVEL_PAGING_ENTRY *SecondLevelPagingEntry;\r
+\r
+  SecondLevelPagingEntry = NULL;\r
+  SecondLevelPagingEntry = CreateSecondLevelPagingEntryTable (VtdIndex, SecondLevelPagingEntry, 0, mBelow4GMemoryLimit, IoMmuAccess);\r
+  if (SecondLevelPagingEntry == NULL) {\r
+    return NULL;\r
+  }\r
+  SecondLevelPagingEntry = CreateSecondLevelPagingEntryTable (VtdIndex, SecondLevelPagingEntry, SIZE_4GB, mAbove4GMemoryLimit, IoMmuAccess);\r
+  if (SecondLevelPagingEntry == NULL) {\r
+    return NULL;\r
+  }\r
+\r
+  return SecondLevelPagingEntry;\r
+}\r
+\r
+/**\r
+  Setup VTd translation table.\r
+\r
+  @retval EFI_SUCCESS           Setup translation table successfully.\r
+  @retval EFI_OUT_OF_RESOURCE   Setup translation table fail.\r
+**/\r
+EFI_STATUS\r
+SetupTranslationTable (\r
+  VOID\r
+  )\r
+{\r
+  EFI_STATUS      Status;\r
+  UINTN           Index;\r
+\r
+  for (Index = 0; Index < mVtdUnitNumber; Index++) {\r
+    DEBUG((DEBUG_INFO, "CreateContextEntry - %d\n", Index));\r
+    if (mVtdUnitInformation[Index].ECapReg.Bits.ECS) {\r
+      Status = CreateExtContextEntry (Index);\r
+    } else {\r
+      Status = CreateContextEntry (Index);\r
+    }\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Dump DMAR context entry table.\r
+\r
+  @param[in]  RootEntry DMAR root entry.\r
+**/\r
+VOID\r
+DumpDmarContextEntryTable (\r
+  IN VTD_ROOT_ENTRY *RootEntry\r
+  )\r
+{\r
+  UINTN                 Index;\r
+  UINTN                 Index2;\r
+  VTD_CONTEXT_ENTRY     *ContextEntry;\r
+\r
+  DEBUG ((DEBUG_INFO,"=========================\n"));\r
+  DEBUG ((DEBUG_INFO,"DMAR Context Entry Table:\n"));\r
+\r
+  DEBUG ((DEBUG_INFO,"RootEntry Address - 0x%x\n", RootEntry));\r
+\r
+  for (Index = 0; Index < VTD_ROOT_ENTRY_NUMBER; Index++) {\r
+    if ((RootEntry[Index].Uint128.Uint64Lo != 0) || (RootEntry[Index].Uint128.Uint64Hi != 0)) {\r
+      DEBUG ((DEBUG_INFO,"  RootEntry(0x%02x) B%02x - 0x%016lx %016lx\n",\r
+        Index, Index, RootEntry[Index].Uint128.Uint64Hi, RootEntry[Index].Uint128.Uint64Lo));\r
+    }\r
+    if (RootEntry[Index].Bits.Present == 0) {\r
+      continue;\r
+    }\r
+    ContextEntry = (VTD_CONTEXT_ENTRY *)(UINTN)LShiftU64 (RootEntry[Index].Bits.ContextTablePointer, 12);\r
+    for (Index2 = 0; Index2 < VTD_CONTEXT_ENTRY_NUMBER; Index2++) {\r
+      if ((ContextEntry[Index2].Uint128.Uint64Lo != 0) || (ContextEntry[Index2].Uint128.Uint64Hi != 0)) {\r
+        DEBUG ((DEBUG_INFO,"    ContextEntry(0x%02x) D%02xF%02x - 0x%016lx %016lx\n",\r
+          Index2, Index2 >> 3, Index2 & 0x7, ContextEntry[Index2].Uint128.Uint64Hi, ContextEntry[Index2].Uint128.Uint64Lo));\r
+      }\r
+      if (ContextEntry[Index2].Bits.Present == 0) {\r
+        continue;\r
+      }\r
+      DumpSecondLevelPagingEntry ((VOID *)(UINTN)LShiftU64 (ContextEntry[Index2].Bits.SecondLevelPageTranslationPointer, 12));\r
+    }\r
+  }\r
+  DEBUG ((DEBUG_INFO,"=========================\n"));\r
+}\r
+\r
+/**\r
+  Dump DMAR second level paging entry.\r
+\r
+  @param[in]  SecondLevelPagingEntry The second level paging entry.\r
+**/\r
+VOID\r
+DumpSecondLevelPagingEntry (\r
+  IN VOID *SecondLevelPagingEntry\r
+  )\r
+{\r
+  UINTN                          Index4;\r
+  UINTN                          Index3;\r
+  UINTN                          Index2;\r
+  UINTN                          Index1;\r
+  VTD_SECOND_LEVEL_PAGING_ENTRY  *Lvl4PtEntry;\r
+  VTD_SECOND_LEVEL_PAGING_ENTRY  *Lvl3PtEntry;\r
+  VTD_SECOND_LEVEL_PAGING_ENTRY  *Lvl2PtEntry;\r
+  VTD_SECOND_LEVEL_PAGING_ENTRY  *Lvl1PtEntry;\r
+\r
+  DEBUG ((DEBUG_VERBOSE,"================\n"));\r
+  DEBUG ((DEBUG_VERBOSE,"DMAR Second Level Page Table:\n"));\r
+\r
+  DEBUG ((DEBUG_VERBOSE,"SecondLevelPagingEntry Base - 0x%x\n", SecondLevelPagingEntry));\r
+  Lvl4PtEntry = (VTD_SECOND_LEVEL_PAGING_ENTRY *)SecondLevelPagingEntry;\r
+  for (Index4 = 0; Index4 < SIZE_4KB/sizeof(VTD_SECOND_LEVEL_PAGING_ENTRY); Index4++) {\r
+    if (Lvl4PtEntry[Index4].Uint64 != 0) {\r
+      DEBUG ((DEBUG_VERBOSE,"  Lvl4Pt Entry(0x%03x) - 0x%016lx\n", Index4, Lvl4PtEntry[Index4].Uint64));\r
+    }\r
+    if (Lvl4PtEntry[Index4].Uint64 == 0) {\r
+      continue;\r
+    }\r
+    Lvl3PtEntry = (VTD_SECOND_LEVEL_PAGING_ENTRY *)(UINTN)LShiftU64 (Lvl4PtEntry[Index4].Bits.Address, 12);\r
+    for (Index3 = 0; Index3 < SIZE_4KB/sizeof(VTD_SECOND_LEVEL_PAGING_ENTRY); Index3++) {\r
+      if (Lvl3PtEntry[Index3].Uint64 != 0) {\r
+        DEBUG ((DEBUG_VERBOSE,"    Lvl3Pt Entry(0x%03x) - 0x%016lx\n", Index3, Lvl3PtEntry[Index3].Uint64));\r
+      }\r
+      if (Lvl3PtEntry[Index3].Uint64 == 0) {\r
+        continue;\r
+      }\r
+\r
+      Lvl2PtEntry = (VTD_SECOND_LEVEL_PAGING_ENTRY *)(UINTN)LShiftU64 (Lvl3PtEntry[Index3].Bits.Address, 12);\r
+      for (Index2 = 0; Index2 < SIZE_4KB/sizeof(VTD_SECOND_LEVEL_PAGING_ENTRY); Index2++) {\r
+        if (Lvl2PtEntry[Index2].Uint64 != 0) {\r
+          DEBUG ((DEBUG_VERBOSE,"      Lvl2Pt Entry(0x%03x) - 0x%016lx\n", Index2, Lvl2PtEntry[Index2].Uint64));\r
+        }\r
+        if (Lvl2PtEntry[Index2].Uint64 == 0) {\r
+          continue;\r
+        }\r
+        if (Lvl2PtEntry[Index2].Bits.PageSize == 0) {\r
+          Lvl1PtEntry = (VTD_SECOND_LEVEL_PAGING_ENTRY *)(UINTN)LShiftU64 (Lvl2PtEntry[Index2].Bits.Address, 12);\r
+          for (Index1 = 0; Index1 < SIZE_4KB/sizeof(VTD_SECOND_LEVEL_PAGING_ENTRY); Index1++) {\r
+            if (Lvl1PtEntry[Index1].Uint64 != 0) {\r
+              DEBUG ((DEBUG_VERBOSE,"        Lvl1Pt Entry(0x%03x) - 0x%016lx\n", Index1, Lvl1PtEntry[Index1].Uint64));\r
+            }\r
+          }\r
+        }\r
+      }\r
+    }\r
+  }\r
+  DEBUG ((DEBUG_VERBOSE,"================\n"));\r
+}\r
+\r
+/**\r
+  Invalid page entry.\r
+\r
+  @param VtdIndex  The VTd engine index.\r
+**/\r
+VOID\r
+InvalidatePageEntry (\r
+  IN UINTN                 VtdIndex\r
+  )\r
+{\r
+  if (mVtdUnitInformation[VtdIndex].HasDirtyPages) {\r
+    InvalidateVtdIOTLBGlobal (VtdIndex);\r
+  }\r
+  mVtdUnitInformation[VtdIndex].HasDirtyPages = FALSE;\r
+}\r
+\r
+#define VTD_PG_R                   BIT0\r
+#define VTD_PG_W                   BIT1\r
+#define VTD_PG_X                   BIT2\r
+#define VTD_PG_EMT                 (BIT3 | BIT4 | BIT5)\r
+#define VTD_PG_TM                  (BIT62)\r
+\r
+#define VTD_PG_PS                  BIT7\r
+\r
+#define PAGE_PROGATE_BITS          (VTD_PG_TM | VTD_PG_EMT | VTD_PG_W | VTD_PG_R)\r
+\r
+#define PAGING_4K_MASK  0xFFF\r
+#define PAGING_2M_MASK  0x1FFFFF\r
+#define PAGING_1G_MASK  0x3FFFFFFF\r
+\r
+#define PAGING_VTD_INDEX_MASK     0x1FF\r
+\r
+#define PAGING_4K_ADDRESS_MASK_64 0x000FFFFFFFFFF000ull\r
+#define PAGING_2M_ADDRESS_MASK_64 0x000FFFFFFFE00000ull\r
+#define PAGING_1G_ADDRESS_MASK_64 0x000FFFFFC0000000ull\r
+\r
+typedef enum {\r
+  PageNone,\r
+  Page4K,\r
+  Page2M,\r
+  Page1G,\r
+} PAGE_ATTRIBUTE;\r
+\r
+typedef struct {\r
+  PAGE_ATTRIBUTE   Attribute;\r
+  UINT64           Length;\r
+  UINT64           AddressMask;\r
+} PAGE_ATTRIBUTE_TABLE;\r
+\r
+PAGE_ATTRIBUTE_TABLE mPageAttributeTable[] = {\r
+  {Page4K,  SIZE_4KB, PAGING_4K_ADDRESS_MASK_64},\r
+  {Page2M,  SIZE_2MB, PAGING_2M_ADDRESS_MASK_64},\r
+  {Page1G,  SIZE_1GB, PAGING_1G_ADDRESS_MASK_64},\r
+};\r
+\r
+/**\r
+  Return length according to page attributes.\r
+\r
+  @param[in]  PageAttributes   The page attribute of the page entry.\r
+\r
+  @return The length of page entry.\r
+**/\r
+UINTN\r
+PageAttributeToLength (\r
+  IN PAGE_ATTRIBUTE  PageAttribute\r
+  )\r
+{\r
+  UINTN  Index;\r
+  for (Index = 0; Index < sizeof(mPageAttributeTable)/sizeof(mPageAttributeTable[0]); Index++) {\r
+    if (PageAttribute == mPageAttributeTable[Index].Attribute) {\r
+      return (UINTN)mPageAttributeTable[Index].Length;\r
+    }\r
+  }\r
+  return 0;\r
+}\r
+\r
+/**\r
+  Return page table entry to match the address.\r
+\r
+  @param[in]   SecondLevelPagingEntry   The second level paging entry in VTd table for the device.\r
+  @param[in]   Address                  The address to be checked.\r
+  @param[out]  PageAttributes           The page attribute of the page entry.\r
+\r
+  @return The page entry.\r
+**/\r
+VOID *\r
+GetSecondLevelPageTableEntry (\r
+  IN  VTD_SECOND_LEVEL_PAGING_ENTRY *SecondLevelPagingEntry,\r
+  IN  PHYSICAL_ADDRESS              Address,\r
+  OUT PAGE_ATTRIBUTE                *PageAttribute\r
+  )\r
+{\r
+  UINTN                 Index1;\r
+  UINTN                 Index2;\r
+  UINTN                 Index3;\r
+  UINTN                 Index4;\r
+  UINT64                *L1PageTable;\r
+  UINT64                *L2PageTable;\r
+  UINT64                *L3PageTable;\r
+  UINT64                *L4PageTable;\r
+\r
+  Index4 = ((UINTN)RShiftU64 (Address, 39)) & PAGING_VTD_INDEX_MASK;\r
+  Index3 = ((UINTN)Address >> 30) & PAGING_VTD_INDEX_MASK;\r
+  Index2 = ((UINTN)Address >> 21) & PAGING_VTD_INDEX_MASK;\r
+  Index1 = ((UINTN)Address >> 12) & PAGING_VTD_INDEX_MASK;\r
+\r
+  L4PageTable = (UINT64 *)SecondLevelPagingEntry;\r
+  if (L4PageTable[Index4] == 0) {\r
+    L4PageTable[Index4] = (UINT64)(UINTN)AllocateZeroPages (1);\r
+    if (L4PageTable[Index4] == 0) {\r
+      DEBUG ((DEBUG_ERROR,"!!!!!! ALLOCATE LVL4 PAGE FAIL (0x%x)!!!!!!\n", Index4));\r
+      ASSERT(FALSE);\r
+      *PageAttribute = PageNone;\r
+      return NULL;\r
+    }\r
+    SetSecondLevelPagingEntryAttribute ((VTD_SECOND_LEVEL_PAGING_ENTRY *)&L4PageTable[Index4], EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE);\r
+  }\r
+\r
+  L3PageTable = (UINT64 *)(UINTN)(L4PageTable[Index4] & PAGING_4K_ADDRESS_MASK_64);\r
+  if (L3PageTable[Index3] == 0) {\r
+    L3PageTable[Index3] = (UINT64)(UINTN)AllocateZeroPages (1);\r
+    if (L3PageTable[Index3] == 0) {\r
+      DEBUG ((DEBUG_ERROR,"!!!!!! ALLOCATE LVL3 PAGE FAIL (0x%x, 0x%x)!!!!!!\n", Index4, Index3));\r
+      ASSERT(FALSE);\r
+      *PageAttribute = PageNone;\r
+      return NULL;\r
+    }\r
+    SetSecondLevelPagingEntryAttribute ((VTD_SECOND_LEVEL_PAGING_ENTRY *)&L3PageTable[Index3], EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE);\r
+  }\r
+  if ((L3PageTable[Index3] & VTD_PG_PS) != 0) {\r
+    // 1G\r
+    *PageAttribute = Page1G;\r
+    return &L3PageTable[Index3];\r
+  }\r
+\r
+  L2PageTable = (UINT64 *)(UINTN)(L3PageTable[Index3] & PAGING_4K_ADDRESS_MASK_64);\r
+  if (L2PageTable[Index2] == 0) {\r
+    L2PageTable[Index2] = Address & PAGING_2M_ADDRESS_MASK_64;\r
+    SetSecondLevelPagingEntryAttribute ((VTD_SECOND_LEVEL_PAGING_ENTRY *)&L2PageTable[Index2], 0);\r
+    L2PageTable[Index2] |= VTD_PG_PS;\r
+  }\r
+  if ((L2PageTable[Index2] & VTD_PG_PS) != 0) {\r
+    // 2M\r
+    *PageAttribute = Page2M;\r
+    return &L2PageTable[Index2];\r
+  }\r
+\r
+  // 4k\r
+  L1PageTable = (UINT64 *)(UINTN)(L2PageTable[Index2] & PAGING_4K_ADDRESS_MASK_64);\r
+  if ((L1PageTable[Index1] == 0) && (Address != 0)) {\r
+    *PageAttribute = PageNone;\r
+    return NULL;\r
+  }\r
+  *PageAttribute = Page4K;\r
+  return &L1PageTable[Index1];\r
+}\r
+\r
+/**\r
+  Modify memory attributes of page entry.\r
+\r
+  @param[in]   PageEntry        The page entry.\r
+  @param[in]   IoMmuAccess      The IOMMU access.\r
+  @param[out]  IsModified       TRUE means page table modified. FALSE means page table not modified.\r
+**/\r
+VOID\r
+ConvertSecondLevelPageEntryAttribute (\r
+  IN  VTD_SECOND_LEVEL_PAGING_ENTRY     *PageEntry,\r
+  IN  UINT64                            IoMmuAccess,\r
+  OUT BOOLEAN                           *IsModified\r
+  )\r
+{\r
+  UINT64  CurrentPageEntry;\r
+  UINT64  NewPageEntry;\r
+\r
+  CurrentPageEntry = PageEntry->Uint64;\r
+  SetSecondLevelPagingEntryAttribute (PageEntry, IoMmuAccess);\r
+  NewPageEntry = PageEntry->Uint64;\r
+  if (CurrentPageEntry != NewPageEntry) {\r
+    *IsModified = TRUE;\r
+    DEBUG ((DEBUG_VERBOSE, "ConvertSecondLevelPageEntryAttribute 0x%lx", CurrentPageEntry));\r
+    DEBUG ((DEBUG_VERBOSE, "->0x%lx\n", NewPageEntry));\r
+  } else {\r
+    *IsModified = FALSE;\r
+  }\r
+}\r
+\r
+/**\r
+  This function returns if there is need to split page entry.\r
+\r
+  @param[in]  BaseAddress      The base address to be checked.\r
+  @param[in]  Length           The length to be checked.\r
+  @param[in]  PageAttribute    The page attribute of the page entry.\r
+\r
+  @retval SplitAttributes on if there is need to split page entry.\r
+**/\r
+PAGE_ATTRIBUTE\r
+NeedSplitPage (\r
+  IN  PHYSICAL_ADDRESS                  BaseAddress,\r
+  IN  UINT64                            Length,\r
+  IN  PAGE_ATTRIBUTE                    PageAttribute\r
+  )\r
+{\r
+  UINT64                PageEntryLength;\r
+\r
+  PageEntryLength = PageAttributeToLength (PageAttribute);\r
+\r
+  if (((BaseAddress & (PageEntryLength - 1)) == 0) && (Length >= PageEntryLength)) {\r
+    return PageNone;\r
+  }\r
+\r
+  if (((BaseAddress & PAGING_2M_MASK) != 0) || (Length < SIZE_2MB)) {\r
+    return Page4K;\r
+  }\r
+\r
+  return Page2M;\r
+}\r
+\r
+/**\r
+  This function splits one page entry to small page entries.\r
+\r
+  @param[in]  PageEntry        The page entry to be splitted.\r
+  @param[in]  PageAttribute    The page attribute of the page entry.\r
+  @param[in]  SplitAttribute   How to split the page entry.\r
+\r
+  @retval RETURN_SUCCESS            The page entry is splitted.\r
+  @retval RETURN_UNSUPPORTED        The page entry does not support to be splitted.\r
+  @retval RETURN_OUT_OF_RESOURCES   No resource to split page entry.\r
+**/\r
+RETURN_STATUS\r
+SplitSecondLevelPage (\r
+  IN  VTD_SECOND_LEVEL_PAGING_ENTRY     *PageEntry,\r
+  IN  PAGE_ATTRIBUTE                    PageAttribute,\r
+  IN  PAGE_ATTRIBUTE                    SplitAttribute\r
+  )\r
+{\r
+  UINT64   BaseAddress;\r
+  UINT64   *NewPageEntry;\r
+  UINTN    Index;\r
+\r
+  ASSERT (PageAttribute == Page2M || PageAttribute == Page1G);\r
+\r
+  if (PageAttribute == Page2M) {\r
+    //\r
+    // Split 2M to 4K\r
+    //\r
+    ASSERT (SplitAttribute == Page4K);\r
+    if (SplitAttribute == Page4K) {\r
+      NewPageEntry = AllocateZeroPages (1);\r
+      DEBUG ((DEBUG_VERBOSE, "Split - 0x%x\n", NewPageEntry));\r
+      if (NewPageEntry == NULL) {\r
+        return RETURN_OUT_OF_RESOURCES;\r
+      }\r
+      BaseAddress = PageEntry->Uint64 & PAGING_2M_ADDRESS_MASK_64;\r
+      for (Index = 0; Index < SIZE_4KB / sizeof(UINT64); Index++) {\r
+        NewPageEntry[Index] = (BaseAddress + SIZE_4KB * Index) | (PageEntry->Uint64 & PAGE_PROGATE_BITS);\r
+      }\r
+      PageEntry->Uint64 = (UINT64)(UINTN)NewPageEntry;\r
+      SetSecondLevelPagingEntryAttribute (PageEntry, EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE);\r
+      return RETURN_SUCCESS;\r
+    } else {\r
+      return RETURN_UNSUPPORTED;\r
+    }\r
+  } else if (PageAttribute == Page1G) {\r
+    //\r
+    // Split 1G to 2M\r
+    // No need support 1G->4K directly, we should use 1G->2M, then 2M->4K to get more compact page table.\r
+    //\r
+    ASSERT (SplitAttribute == Page2M || SplitAttribute == Page4K);\r
+    if ((SplitAttribute == Page2M || SplitAttribute == Page4K)) {\r
+      NewPageEntry = AllocateZeroPages (1);\r
+      DEBUG ((DEBUG_VERBOSE, "Split - 0x%x\n", NewPageEntry));\r
+      if (NewPageEntry == NULL) {\r
+        return RETURN_OUT_OF_RESOURCES;\r
+      }\r
+      BaseAddress = PageEntry->Uint64 & PAGING_1G_ADDRESS_MASK_64;\r
+      for (Index = 0; Index < SIZE_4KB / sizeof(UINT64); Index++) {\r
+        NewPageEntry[Index] = (BaseAddress + SIZE_2MB * Index) | VTD_PG_PS | (PageEntry->Uint64 & PAGE_PROGATE_BITS);\r
+      }\r
+      PageEntry->Uint64 = (UINT64)(UINTN)NewPageEntry;\r
+      SetSecondLevelPagingEntryAttribute (PageEntry, EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE);\r
+      return RETURN_SUCCESS;\r
+    } else {\r
+      return RETURN_UNSUPPORTED;\r
+    }\r
+  } else {\r
+    return RETURN_UNSUPPORTED;\r
+  }\r
+}\r
+\r
+/**\r
+  Set VTd attribute for a system memory on second level page entry\r
+\r
+  @param[in]  VtdIndex                The index used to identify a VTd engine.\r
+  @param[in]  SecondLevelPagingEntry  The second level paging entry in VTd table for the device.\r
+  @param[in]  BaseAddress             The base of device memory address to be used as the DMA memory.\r
+  @param[in]  Length                  The length of device memory address to be used as the DMA memory.\r
+  @param[in]  IoMmuAccess             The IOMMU access.\r
+\r
+  @retval EFI_SUCCESS            The IoMmuAccess is set for the memory range specified by BaseAddress and Length.\r
+  @retval EFI_INVALID_PARAMETER  BaseAddress is not IoMmu Page size aligned.\r
+  @retval EFI_INVALID_PARAMETER  Length is not IoMmu Page size aligned.\r
+  @retval EFI_INVALID_PARAMETER  Length is 0.\r
+  @retval EFI_INVALID_PARAMETER  IoMmuAccess specified an illegal combination of access.\r
+  @retval EFI_UNSUPPORTED        The bit mask of IoMmuAccess is not supported by the IOMMU.\r
+  @retval EFI_UNSUPPORTED        The IOMMU does not support the memory range specified by BaseAddress and Length.\r
+  @retval EFI_OUT_OF_RESOURCES   There are not enough resources available to modify the IOMMU access.\r
+  @retval EFI_DEVICE_ERROR       The IOMMU device reported an error while attempting the operation.\r
+**/\r
+EFI_STATUS\r
+SetSecondLevelPagingAttribute (\r
+  IN UINTN                         VtdIndex,\r
+  IN VTD_SECOND_LEVEL_PAGING_ENTRY *SecondLevelPagingEntry,\r
+  IN UINT64                        BaseAddress,\r
+  IN UINT64                        Length,\r
+  IN UINT64                        IoMmuAccess\r
+  )\r
+{\r
+  VTD_SECOND_LEVEL_PAGING_ENTRY  *PageEntry;\r
+  PAGE_ATTRIBUTE                 PageAttribute;\r
+  UINTN                          PageEntryLength;\r
+  PAGE_ATTRIBUTE                 SplitAttribute;\r
+  EFI_STATUS                     Status;\r
+  BOOLEAN                        IsEntryModified;\r
+\r
+  DEBUG ((DEBUG_VERBOSE,"SetSecondLevelPagingAttribute (%d) (0x%016lx - 0x%016lx : %x) \n", VtdIndex, BaseAddress, Length, IoMmuAccess));\r
+  DEBUG ((DEBUG_VERBOSE,"  SecondLevelPagingEntry Base - 0x%x\n", SecondLevelPagingEntry));\r
+\r
+  if (BaseAddress != ALIGN_VALUE(BaseAddress, SIZE_4KB)) {\r
+    DEBUG ((DEBUG_ERROR, "SetSecondLevelPagingAttribute - Invalid Alignment\n"));\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+  if (Length != ALIGN_VALUE(Length, SIZE_4KB)) {\r
+    DEBUG ((DEBUG_ERROR, "SetSecondLevelPagingAttribute - Invalid Alignment\n"));\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  while (Length != 0) {\r
+    PageEntry = GetSecondLevelPageTableEntry (SecondLevelPagingEntry, BaseAddress, &PageAttribute);\r
+    if (PageEntry == NULL) {\r
+      DEBUG ((DEBUG_ERROR, "PageEntry - NULL\n"));\r
+      return RETURN_UNSUPPORTED;\r
+    }\r
+    PageEntryLength = PageAttributeToLength (PageAttribute);\r
+    SplitAttribute = NeedSplitPage (BaseAddress, Length, PageAttribute);\r
+    if (SplitAttribute == PageNone) {\r
+      ConvertSecondLevelPageEntryAttribute (PageEntry, IoMmuAccess, &IsEntryModified);\r
+      if (IsEntryModified) {\r
+        mVtdUnitInformation[VtdIndex].HasDirtyPages = TRUE;\r
+      }\r
+      //\r
+      // Convert success, move to next\r
+      //\r
+      BaseAddress += PageEntryLength;\r
+      Length -= PageEntryLength;\r
+    } else {\r
+      Status = SplitSecondLevelPage (PageEntry, PageAttribute, SplitAttribute);\r
+      if (RETURN_ERROR (Status)) {\r
+        DEBUG ((DEBUG_ERROR, "SplitSecondLevelPage - %r\n", Status));\r
+        return RETURN_UNSUPPORTED;\r
+      }\r
+      mVtdUnitInformation[VtdIndex].HasDirtyPages = TRUE;\r
+      //\r
+      // Just split current page\r
+      // Convert success in next around\r
+      //\r
+    }\r
+  }\r
+\r
+  InvalidatePageEntry (VtdIndex);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Set VTd attribute for a system memory.\r
+\r
+  @param[in]  VtdIndex                The index used to identify a VTd engine.\r
+  @param[in]  SecondLevelPagingEntry  The second level paging entry in VTd table for the device.\r
+  @param[in]  BaseAddress             The base of device memory address to be used as the DMA memory.\r
+  @param[in]  Length                  The length of device memory address to be used as the DMA memory.\r
+  @param[in]  IoMmuAccess             The IOMMU access.\r
+\r
+  @retval EFI_SUCCESS            The IoMmuAccess is set for the memory range specified by BaseAddress and Length.\r
+  @retval EFI_INVALID_PARAMETER  BaseAddress is not IoMmu Page size aligned.\r
+  @retval EFI_INVALID_PARAMETER  Length is not IoMmu Page size aligned.\r
+  @retval EFI_INVALID_PARAMETER  Length is 0.\r
+  @retval EFI_INVALID_PARAMETER  IoMmuAccess specified an illegal combination of access.\r
+  @retval EFI_UNSUPPORTED        The bit mask of IoMmuAccess is not supported by the IOMMU.\r
+  @retval EFI_UNSUPPORTED        The IOMMU does not support the memory range specified by BaseAddress and Length.\r
+  @retval EFI_OUT_OF_RESOURCES   There are not enough resources available to modify the IOMMU access.\r
+  @retval EFI_DEVICE_ERROR       The IOMMU device reported an error while attempting the operation.\r
+**/\r
+EFI_STATUS\r
+SetPageAttribute (\r
+  IN UINTN                         VtdIndex,\r
+  IN VTD_SECOND_LEVEL_PAGING_ENTRY *SecondLevelPagingEntry,\r
+  IN UINT64                        BaseAddress,\r
+  IN UINT64                        Length,\r
+  IN UINT64                        IoMmuAccess\r
+  )\r
+{\r
+  EFI_STATUS Status;\r
+  Status = EFI_NOT_FOUND;\r
+  if (SecondLevelPagingEntry != NULL) {\r
+    Status = SetSecondLevelPagingAttribute (VtdIndex, SecondLevelPagingEntry, BaseAddress, Length, IoMmuAccess);\r
+  }\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Set VTd attribute for a system memory.\r
+\r
+  @param[in]  Segment           The Segment used to identify a VTd engine.\r
+  @param[in]  SourceId          The SourceId used to identify a VTd engine and table entry.\r
+  @param[in]  BaseAddress       The base of device memory address to be used as the DMA memory.\r
+  @param[in]  Length            The length of device memory address to be used as the DMA memory.\r
+  @param[in]  IoMmuAccess       The IOMMU access.\r
+\r
+  @retval EFI_SUCCESS            The IoMmuAccess is set for the memory range specified by BaseAddress and Length.\r
+  @retval EFI_INVALID_PARAMETER  BaseAddress is not IoMmu Page size aligned.\r
+  @retval EFI_INVALID_PARAMETER  Length is not IoMmu Page size aligned.\r
+  @retval EFI_INVALID_PARAMETER  Length is 0.\r
+  @retval EFI_INVALID_PARAMETER  IoMmuAccess specified an illegal combination of access.\r
+  @retval EFI_UNSUPPORTED        The bit mask of IoMmuAccess is not supported by the IOMMU.\r
+  @retval EFI_UNSUPPORTED        The IOMMU does not support the memory range specified by BaseAddress and Length.\r
+  @retval EFI_OUT_OF_RESOURCES   There are not enough resources available to modify the IOMMU access.\r
+  @retval EFI_DEVICE_ERROR       The IOMMU device reported an error while attempting the operation.\r
+**/\r
+EFI_STATUS\r
+SetAccessAttribute (\r
+  IN UINT16                Segment,\r
+  IN VTD_SOURCE_ID         SourceId,\r
+  IN UINT64                BaseAddress,\r
+  IN UINT64                Length,\r
+  IN UINT64                IoMmuAccess\r
+  )\r
+{\r
+  UINTN                         VtdIndex;\r
+  EFI_STATUS                    Status;\r
+  VTD_EXT_CONTEXT_ENTRY         *ExtContextEntry;\r
+  VTD_CONTEXT_ENTRY             *ContextEntry;\r
+  VTD_SECOND_LEVEL_PAGING_ENTRY *SecondLevelPagingEntry;\r
+  UINT64                        Pt;\r
+\r
+  DEBUG ((DEBUG_INFO,"SetAccessAttribute (S%04x B%02x D%02x F%02x) (0x%016lx - 0x%08x, %x)\n", Segment, SourceId.Bits.Bus, SourceId.Bits.Device, SourceId.Bits.Function, BaseAddress, (UINTN)Length, IoMmuAccess));\r
+\r
+  VtdIndex = FindVtdIndexByPciDevice (Segment, SourceId, &ExtContextEntry, &ContextEntry);\r
+  if (VtdIndex == (UINTN)-1) {\r
+    DEBUG ((DEBUG_ERROR,"SetAccessAttribute - Pci device (S%04x B%02x D%02x F%02x) not found!\n", Segment, SourceId.Bits.Bus, SourceId.Bits.Device, SourceId.Bits.Function));\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  if (ExtContextEntry != NULL) {\r
+    if (ExtContextEntry->Bits.Present == 0) {\r
+      SecondLevelPagingEntry = CreateSecondLevelPagingEntry (VtdIndex, 0);\r
+      DEBUG ((DEBUG_VERBOSE,"SecondLevelPagingEntry - 0x%x (S%04x B%02x D%02x F%02x) New\n", SecondLevelPagingEntry, Segment, SourceId.Bits.Bus, SourceId.Bits.Device, SourceId.Bits.Function));\r
+      Pt = (UINT64)RShiftU64 ((UINT64)(UINTN)SecondLevelPagingEntry, 12);\r
+\r
+      ExtContextEntry->Bits.SecondLevelPageTranslationPointer = Pt;\r
+      ExtContextEntry->Bits.DomainIdentifier = GetPciDescriptor (VtdIndex, Segment, SourceId);\r
+      ExtContextEntry->Bits.Present = 1;\r
+      DumpDmarExtContextEntryTable (mVtdUnitInformation[VtdIndex].ExtRootEntryTable);\r
+    } else {\r
+      SecondLevelPagingEntry = (VOID *)(UINTN)LShiftU64 (ExtContextEntry->Bits.SecondLevelPageTranslationPointer, 12);\r
+      DEBUG ((DEBUG_VERBOSE,"SecondLevelPagingEntry - 0x%x (S%04x B%02x D%02x F%02x)\n", SecondLevelPagingEntry, Segment, SourceId.Bits.Bus, SourceId.Bits.Device, SourceId.Bits.Function));\r
+    }\r
+  } else {\r
+    if (ContextEntry->Bits.Present == 0) {\r
+      SecondLevelPagingEntry = CreateSecondLevelPagingEntry (VtdIndex, 0);\r
+      DEBUG ((DEBUG_VERBOSE,"SecondLevelPagingEntry - 0x%x (S%04x B%02x D%02x F%02x) New\n", SecondLevelPagingEntry, Segment, SourceId.Bits.Bus, SourceId.Bits.Device, SourceId.Bits.Function));\r
+      Pt = (UINT64)RShiftU64 ((UINT64)(UINTN)SecondLevelPagingEntry, 12);\r
+\r
+      ContextEntry->Bits.SecondLevelPageTranslationPointer = Pt;\r
+      ContextEntry->Bits.DomainIdentifier = GetPciDescriptor (VtdIndex, Segment, SourceId);\r
+      ContextEntry->Bits.Present = 1;\r
+      DumpDmarContextEntryTable (mVtdUnitInformation[VtdIndex].RootEntryTable);\r
+    } else {\r
+      SecondLevelPagingEntry = (VOID *)(UINTN)LShiftU64 (ContextEntry->Bits.SecondLevelPageTranslationPointer, 12);\r
+      DEBUG ((DEBUG_VERBOSE,"SecondLevelPagingEntry - 0x%x (S%04x B%02x D%02x F%02x)\n", SecondLevelPagingEntry, Segment, SourceId.Bits.Bus, SourceId.Bits.Device, SourceId.Bits.Function));\r
+    }\r
+  }\r
+\r
+  //\r
+  // Do not update FixedSecondLevelPagingEntry\r
+  //\r
+  if (SecondLevelPagingEntry != mVtdUnitInformation[VtdIndex].FixedSecondLevelPagingEntry) {\r
+    Status = SetPageAttribute (\r
+               VtdIndex,\r
+               SecondLevelPagingEntry,\r
+               BaseAddress,\r
+               Length,\r
+               IoMmuAccess\r
+               );\r
+    if (EFI_ERROR (Status)) {\r
+      DEBUG ((DEBUG_ERROR,"SetPageAttribute - %r\n", Status));\r
+      return Status;\r
+    }\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Always enable the VTd page attribute for the device.\r
+\r
+  @param[in]  Segment           The Segment used to identify a VTd engine.\r
+  @param[in]  SourceId          The SourceId used to identify a VTd engine and table entry.\r
+\r
+  @retval EFI_SUCCESS           The VTd entry is updated to always enable all DMA access for the specific device.\r
+**/\r
+EFI_STATUS\r
+AlwaysEnablePageAttribute (\r
+  IN UINT16                  Segment,\r
+  IN VTD_SOURCE_ID           SourceId\r
+  )\r
+{\r
+  UINTN                         VtdIndex;\r
+  VTD_EXT_CONTEXT_ENTRY         *ExtContextEntry;\r
+  VTD_CONTEXT_ENTRY             *ContextEntry;\r
+  VTD_SECOND_LEVEL_PAGING_ENTRY *SecondLevelPagingEntry;\r
+  UINT64                        Pt;\r
+\r
+  DEBUG ((DEBUG_INFO,"AlwaysEnablePageAttribute (S%04x B%02x D%02x F%02x)\n", Segment, SourceId.Bits.Bus, SourceId.Bits.Device, SourceId.Bits.Function));\r
+\r
+  VtdIndex = FindVtdIndexByPciDevice (Segment, SourceId, &ExtContextEntry, &ContextEntry);\r
+  if (VtdIndex == (UINTN)-1) {\r
+    DEBUG ((DEBUG_ERROR,"AlwaysEnablePageAttribute - Pci device (S%04x B%02x D%02x F%02x) not found!\n", Segment, SourceId.Bits.Bus, SourceId.Bits.Device, SourceId.Bits.Function));\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  if (mVtdUnitInformation[VtdIndex].FixedSecondLevelPagingEntry == 0) {\r
+    DEBUG((DEBUG_INFO, "CreateSecondLevelPagingEntry - %d\n", VtdIndex));\r
+    mVtdUnitInformation[VtdIndex].FixedSecondLevelPagingEntry = CreateSecondLevelPagingEntry (VtdIndex, EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE);\r
+  }\r
+\r
+  SecondLevelPagingEntry = mVtdUnitInformation[VtdIndex].FixedSecondLevelPagingEntry;\r
+  Pt = (UINT64)RShiftU64 ((UINT64)(UINTN)SecondLevelPagingEntry, 12);\r
+  if (ExtContextEntry != NULL) {\r
+    ExtContextEntry->Bits.SecondLevelPageTranslationPointer = Pt;\r
+    ExtContextEntry->Bits.DomainIdentifier = ((1 << (UINT8)((UINTN)mVtdUnitInformation[VtdIndex].CapReg.Bits.ND * 2 + 4)) - 1);\r
+    ExtContextEntry->Bits.Present = 1;\r
+  } else {\r
+    ContextEntry->Bits.SecondLevelPageTranslationPointer = Pt;\r
+    ContextEntry->Bits.DomainIdentifier = ((1 << (UINT8)((UINTN)mVtdUnitInformation[VtdIndex].CapReg.Bits.ND * 2 + 4)) - 1);\r
+    ContextEntry->Bits.Present = 1;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
diff --git a/IntelSiliconPkg/IntelVTdDxe/TranslationTableEx.c b/IntelSiliconPkg/IntelVTdDxe/TranslationTableEx.c
new file mode 100644 (file)
index 0000000..0f54b97
--- /dev/null
@@ -0,0 +1,153 @@
+/** @file\r
+\r
+  Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>\r
+  This program and the accompanying materials\r
+  are licensed and made available under the terms and conditions of the BSD License\r
+  which accompanies this distribution.  The full text of the license may be found at\r
+  http://opensource.org/licenses/bsd-license.php.\r
+\r
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "DmaProtection.h"\r
+\r
+/**\r
+  Create extended context entry.\r
+\r
+  @param[in]  VtdIndex  The index of the VTd engine.\r
+\r
+  @retval EFI_SUCCESS          The extended context entry is created.\r
+  @retval EFI_OUT_OF_RESOURCE  No enough resource to create extended context entry.\r
+**/\r
+EFI_STATUS\r
+CreateExtContextEntry (\r
+  IN UINTN  VtdIndex\r
+  )\r
+{\r
+  UINTN                  Index;\r
+  VOID                   *Buffer;\r
+  UINTN                  RootPages;\r
+  UINTN                  ContextPages;\r
+  VTD_EXT_ROOT_ENTRY     *ExtRootEntry;\r
+  VTD_EXT_CONTEXT_ENTRY  *ExtContextEntryTable;\r
+  VTD_EXT_CONTEXT_ENTRY  *ExtContextEntry;\r
+  VTD_SOURCE_ID          *PciDescriptor;\r
+  VTD_SOURCE_ID          SourceId;\r
+  UINTN                  MaxBusNumber;\r
+  UINTN                  EntryTablePages;\r
+\r
+  MaxBusNumber = 0;\r
+  for (Index = 0; Index < mVtdUnitInformation[VtdIndex].PciDeviceInfo.PciDescriptorNumber; Index++) {\r
+    PciDescriptor = &mVtdUnitInformation[VtdIndex].PciDeviceInfo.PciDescriptors[Index];\r
+    if (PciDescriptor->Bits.Bus > MaxBusNumber) {\r
+      MaxBusNumber = PciDescriptor->Bits.Bus;\r
+    }\r
+  }\r
+  DEBUG ((DEBUG_INFO,"  MaxBusNumber - 0x%x\n", MaxBusNumber));\r
+\r
+  RootPages = EFI_SIZE_TO_PAGES (sizeof (VTD_EXT_ROOT_ENTRY) * VTD_ROOT_ENTRY_NUMBER);\r
+  ContextPages = EFI_SIZE_TO_PAGES (sizeof (VTD_EXT_CONTEXT_ENTRY) * VTD_CONTEXT_ENTRY_NUMBER);\r
+  EntryTablePages = RootPages + ContextPages * (MaxBusNumber + 1);\r
+  Buffer = AllocateZeroPages (EntryTablePages);\r
+  if (Buffer == NULL) {\r
+    DEBUG ((DEBUG_INFO,"Could not Alloc Root Entry Table.. \n"));\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+  mVtdUnitInformation[VtdIndex].ExtRootEntryTable = (VTD_EXT_ROOT_ENTRY *)Buffer;\r
+  Buffer = (UINT8 *)Buffer + EFI_PAGES_TO_SIZE (RootPages);\r
+\r
+  for (Index = 0; Index < mVtdUnitInformation[VtdIndex].PciDeviceInfo.PciDescriptorNumber; Index++) {\r
+    PciDescriptor = &mVtdUnitInformation[VtdIndex].PciDeviceInfo.PciDescriptors[Index];\r
+\r
+    SourceId.Bits.Bus = PciDescriptor->Bits.Bus;\r
+    SourceId.Bits.Device = PciDescriptor->Bits.Device;\r
+    SourceId.Bits.Function = PciDescriptor->Bits.Function;\r
+\r
+    ExtRootEntry = &mVtdUnitInformation[VtdIndex].ExtRootEntryTable[SourceId.Index.RootIndex];\r
+    if (ExtRootEntry->Bits.LowerPresent == 0) {\r
+      ExtRootEntry->Bits.LowerContextTablePointer  = RShiftU64 ((UINT64)(UINTN)Buffer, 12);\r
+      ExtRootEntry->Bits.LowerPresent = 1;\r
+      ExtRootEntry->Bits.UpperContextTablePointer  = RShiftU64 ((UINT64)(UINTN)Buffer, 12) + 1;\r
+      ExtRootEntry->Bits.UpperPresent = 1;\r
+      Buffer = (UINT8 *)Buffer + EFI_PAGES_TO_SIZE (ContextPages);\r
+    }\r
+\r
+    ExtContextEntryTable = (VTD_EXT_CONTEXT_ENTRY *)(UINTN)LShiftU64(ExtRootEntry->Bits.LowerContextTablePointer, 12) ;\r
+    ExtContextEntry = &ExtContextEntryTable[SourceId.Index.ContextIndex];\r
+    ExtContextEntry->Bits.TranslationType = 0;\r
+    ExtContextEntry->Bits.FaultProcessingDisable = 0;\r
+    ExtContextEntry->Bits.Present = 0;\r
+\r
+    DEBUG ((DEBUG_INFO,"DOMAIN: S%04x, B%02x D%02x F%02x\n", mVtdUnitInformation[VtdIndex].Segment, SourceId.Bits.Bus, SourceId.Bits.Device, SourceId.Bits.Function));\r
+\r
+    switch (mVtdUnitInformation[VtdIndex].CapReg.Bits.SAGAW) {\r
+    case BIT1:\r
+      ExtContextEntry->Bits.AddressWidth = 0x1;\r
+      break;\r
+    case BIT2:\r
+      ExtContextEntry->Bits.AddressWidth = 0x2;\r
+      break;\r
+    }\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Dump DMAR extended context entry table.\r
+\r
+  @param[in]  ExtRootEntry DMAR extended root entry.\r
+**/\r
+VOID\r
+DumpDmarExtContextEntryTable (\r
+  IN VTD_EXT_ROOT_ENTRY *ExtRootEntry\r
+  )\r
+{\r
+  UINTN                 Index;\r
+  UINTN                 Index2;\r
+  VTD_EXT_CONTEXT_ENTRY *ExtContextEntry;\r
+\r
+  DEBUG ((DEBUG_INFO,"=========================\n"));\r
+  DEBUG ((DEBUG_INFO,"DMAR ExtContext Entry Table:\n"));\r
+\r
+  DEBUG ((DEBUG_INFO,"ExtRootEntry Address - 0x%x\n", ExtRootEntry));\r
+\r
+  for (Index = 0; Index < VTD_ROOT_ENTRY_NUMBER; Index++) {\r
+    if ((ExtRootEntry[Index].Uint128.Uint64Lo != 0) || (ExtRootEntry[Index].Uint128.Uint64Hi != 0)) {\r
+      DEBUG ((DEBUG_INFO,"  ExtRootEntry(0x%02x) B%02x - 0x%016lx %016lx\n",\r
+        Index, Index, ExtRootEntry[Index].Uint128.Uint64Hi, ExtRootEntry[Index].Uint128.Uint64Lo));\r
+    }\r
+    if (ExtRootEntry[Index].Bits.LowerPresent == 0) {\r
+      continue;\r
+    }\r
+    ExtContextEntry = (VTD_EXT_CONTEXT_ENTRY *)(UINTN)LShiftU64 (ExtRootEntry[Index].Bits.LowerContextTablePointer, 12);\r
+    for (Index2 = 0; Index2 < VTD_CONTEXT_ENTRY_NUMBER/2; Index2++) {\r
+      if ((ExtContextEntry[Index2].Uint256.Uint64_1 != 0) || (ExtContextEntry[Index2].Uint256.Uint64_2 != 0) ||\r
+          (ExtContextEntry[Index2].Uint256.Uint64_3 != 0) || (ExtContextEntry[Index2].Uint256.Uint64_4 != 0)) {\r
+        DEBUG ((DEBUG_INFO,"    ExtContextEntryLower(0x%02x) D%02xF%02x - 0x%016lx %016lx %016lx %016lx\n",\r
+          Index2, Index2 >> 3, Index2 & 0x7, ExtContextEntry[Index2].Uint256.Uint64_4, ExtContextEntry[Index2].Uint256.Uint64_3, ExtContextEntry[Index2].Uint256.Uint64_2, ExtContextEntry[Index2].Uint256.Uint64_1));\r
+      }\r
+      if (ExtContextEntry[Index2].Bits.Present == 0) {\r
+        continue;\r
+      }\r
+    }\r
+\r
+    if (ExtRootEntry[Index].Bits.UpperPresent == 0) {\r
+      continue;\r
+    }\r
+    ExtContextEntry = (VTD_EXT_CONTEXT_ENTRY *)(UINTN)LShiftU64 (ExtRootEntry[Index].Bits.UpperContextTablePointer, 12);\r
+    for (Index2 = 0; Index2 < VTD_CONTEXT_ENTRY_NUMBER/2; Index2++) {\r
+      if ((ExtContextEntry[Index2].Uint256.Uint64_1 != 0) || (ExtContextEntry[Index2].Uint256.Uint64_2 != 0) ||\r
+          (ExtContextEntry[Index2].Uint256.Uint64_3 != 0) || (ExtContextEntry[Index2].Uint256.Uint64_4 != 0)) {\r
+        DEBUG ((DEBUG_INFO,"    ExtContextEntryUpper(0x%02x) D%02xF%02x - 0x%016lx %016lx %016lx %016lx\n",\r
+          Index2, (Index2 + 128) >> 3, (Index2 + 128) & 0x7, ExtContextEntry[Index2].Uint256.Uint64_4, ExtContextEntry[Index2].Uint256.Uint64_3, ExtContextEntry[Index2].Uint256.Uint64_2, ExtContextEntry[Index2].Uint256.Uint64_1));\r
+      }\r
+      if (ExtContextEntry[Index2].Bits.Present == 0) {\r
+        continue;\r
+      }\r
+    }\r
+  }\r
+  DEBUG ((DEBUG_INFO,"=========================\n"));\r
+}\r
diff --git a/IntelSiliconPkg/IntelVTdDxe/VtdReg.c b/IntelSiliconPkg/IntelVTdDxe/VtdReg.c
new file mode 100644 (file)
index 0000000..456a039
--- /dev/null
@@ -0,0 +1,602 @@
+/** @file\r
+\r
+  Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>\r
+  This program and the accompanying materials\r
+  are licensed and made available under the terms and conditions of the BSD License\r
+  which accompanies this distribution.  The full text of the license may be found at\r
+  http://opensource.org/licenses/bsd-license.php.\r
+\r
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "DmaProtection.h"\r
+\r
+UINT64                           mVtdHostAddressWidthMask;\r
+UINTN                            mVtdUnitNumber;\r
+VTD_UNIT_INFORMATION             *mVtdUnitInformation;\r
+\r
+BOOLEAN  mVtdEnabled;\r
+\r
+/**\r
+  Invalid VTd global IOTLB.\r
+\r
+  @param[in]  VtdIndex              The index of VTd engine.\r
+\r
+  @retval EFI_SUCCESS           VTd global IOTLB is invalidated.\r
+  @retval EFI_DEVICE_ERROR      VTd global IOTLB is not invalidated.\r
+**/\r
+EFI_STATUS\r
+InvalidateVtdIOTLBGlobal (\r
+  IN UINTN  VtdIndex\r
+  )\r
+{\r
+  UINT64  Reg64;\r
+  UINT32  Reg32;\r
+\r
+  if (!mVtdEnabled) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  DEBUG((DEBUG_VERBOSE, "InvalidateVtdIOTLBGlobal(%d)\n", VtdIndex));\r
+\r
+  AsmWbinvd();\r
+\r
+  //\r
+  // Write Buffer Flush before invalidation\r
+  //\r
+  Reg32 = MmioRead32 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + R_CAP_REG);\r
+  if ((Reg32 & B_CAP_REG_RWBF) != 0) {\r
+    MmioWrite32 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + R_GCMD_REG, B_GMCD_REG_WBF);\r
+  }\r
+\r
+  //\r
+  // Invalidate the context cache\r
+  //\r
+  Reg64 = MmioRead64 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + R_CCMD_REG);\r
+  if ((Reg64 & B_CCMD_REG_ICC) != 0) {\r
+    DEBUG ((DEBUG_ERROR,"ERROR: InvalidateVtdIOTLBGlobal: B_CCMD_REG_ICC is set for VTD(%d)\n",VtdIndex));\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  Reg64 &= ((~B_CCMD_REG_ICC) & (~B_CCMD_REG_CIRG_MASK));\r
+  Reg64 |= (B_CCMD_REG_ICC | V_CCMD_REG_CIRG_GLOBAL);\r
+  MmioWrite64 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + R_CCMD_REG, Reg64);\r
+\r
+  do {\r
+    Reg64 = MmioRead64 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + R_CCMD_REG);\r
+  } while ((Reg64 & B_CCMD_REG_ICC) != 0);\r
+\r
+  //\r
+  // Invalidate the IOTLB cache\r
+  //\r
+\r
+  Reg64 = MmioRead64 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + (mVtdUnitInformation[VtdIndex].ECapReg.Bits.IRO * 16) + R_IOTLB_REG);\r
+  if ((Reg64 & B_IOTLB_REG_IVT) != 0) {\r
+    DEBUG ((DEBUG_ERROR,"ERROR: InvalidateVtdIOTLBGlobal: B_IOTLB_REG_IVT is set for VTD(%d)\n", VtdIndex));\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  Reg64 &= ((~B_IOTLB_REG_IVT) & (~B_IOTLB_REG_IIRG_MASK));\r
+  Reg64 |= (B_IOTLB_REG_IVT | V_IOTLB_REG_IIRG_GLOBAL);\r
+  MmioWrite64 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + (mVtdUnitInformation[VtdIndex].ECapReg.Bits.IRO * 16) + R_IOTLB_REG, Reg64);\r
+\r
+  do {\r
+    Reg64 = MmioRead64 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + (mVtdUnitInformation[VtdIndex].ECapReg.Bits.IRO * 16) + R_IOTLB_REG);\r
+  } while ((Reg64 & B_IOTLB_REG_IVT) != 0);\r
+\r
+  //\r
+  // Disable VTd\r
+  //\r
+  MmioWrite32 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + R_GCMD_REG, B_GMCD_REG_SRTP);\r
+  do {\r
+    Reg32 = MmioRead32 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + R_GSTS_REG);\r
+  } while((Reg32 & B_GSTS_REG_RTPS) == 0);\r
+\r
+  //\r
+  // Enable VTd\r
+  //\r
+  MmioWrite32 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + R_GCMD_REG, B_GMCD_REG_TE);\r
+  do {\r
+    Reg32 = MmioRead32 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + R_GSTS_REG);\r
+  } while ((Reg32 & B_GSTS_REG_TE) == 0);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Invalid VTd IOTLB domain.\r
+\r
+  @param[in]  VtdIndex              The index of VTd engine.\r
+  @param[in]  DomainIdentifier      The domain ID of the source.\r
+\r
+  @retval EFI_SUCCESS           VTd IOTLB domain is invalidated.\r
+  @retval EFI_DEVICE_ERROR      VTd IOTLB domain is not invalidated.\r
+**/\r
+EFI_STATUS\r
+InvalidateVtdIOTLBDomain (\r
+  IN UINTN  VtdIndex,\r
+  IN UINT16 DomainIdentifier\r
+  )\r
+{\r
+  UINT64  Reg64;\r
+\r
+  if (!mVtdEnabled) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  DEBUG((DEBUG_VERBOSE, "InvalidateVtdIOTLBDomain(%d): 0x%016lx (0x%04x)\n", VtdIndex, DomainIdentifier));\r
+\r
+  //\r
+  // Invalidate the context cache\r
+  //\r
+  Reg64 = MmioRead64 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + R_CCMD_REG);\r
+  if ((Reg64 & B_CCMD_REG_ICC) != 0) {\r
+    DEBUG ((DEBUG_ERROR,"ERROR: InvalidateVtdIOTLBDomain: B_CCMD_REG_ICC is set for VTD(%d)\n",VtdIndex));\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  Reg64 &= ((~B_CCMD_REG_ICC) & (~B_CCMD_REG_CIRG_MASK));\r
+  Reg64 |= (B_CCMD_REG_ICC | V_CCMD_REG_CIRG_DOMAIN);\r
+  Reg64 |= (B_CCMD_REG_ICC | V_CCMD_REG_CIRG_DOMAIN);\r
+  MmioWrite64 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + R_CCMD_REG, Reg64);\r
+\r
+  do {\r
+    Reg64 = MmioRead64 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + R_CCMD_REG);\r
+  } while ((Reg64 & B_CCMD_REG_ICC) != 0);\r
+\r
+  //\r
+  // Invalidate the IOTLB cache\r
+  //\r
+\r
+  Reg64 = MmioRead64 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + (mVtdUnitInformation[VtdIndex].ECapReg.Bits.IRO * 16) + R_IOTLB_REG);\r
+  if ((Reg64 & B_IOTLB_REG_IVT) != 0) {\r
+    DEBUG ((DEBUG_ERROR,"ERROR: InvalidateVtdIOTLBDomain: B_IOTLB_REG_IVT is set for VTD(%d)\n", VtdIndex));\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  Reg64 &= ((~B_IOTLB_REG_IVT) & (~B_IOTLB_REG_IIRG_MASK));\r
+  Reg64 |= (B_IOTLB_REG_IVT | V_IOTLB_REG_IIRG_DOMAIN);\r
+  Reg64 |= DomainIdentifier;\r
+  MmioWrite64 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + (mVtdUnitInformation[VtdIndex].ECapReg.Bits.IRO * 16) + R_IOTLB_REG, Reg64);\r
+\r
+  do {\r
+    Reg64 = MmioRead64 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + (mVtdUnitInformation[VtdIndex].ECapReg.Bits.IRO * 16) + R_IOTLB_REG);\r
+  } while ((Reg64 & B_IOTLB_REG_IVT) != 0);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Invalid VTd IOTLB page.\r
+\r
+  @param[in]  VtdIndex              The index of VTd engine.\r
+  @param[in]  Address               The address of IOTLB page.\r
+  @param[in]  AddressMode           The address mode of IOTLB page.\r
+  @param[in]  DomainIdentifier      The domain ID of the source.\r
+\r
+  @retval EFI_SUCCESS           VTd IOTLB page is invalidated.\r
+  @retval EFI_DEVICE_ERROR      VTd IOTLB page is not invalidated.\r
+**/\r
+EFI_STATUS\r
+InvalidateVtdIOTLBPage (\r
+  IN UINTN  VtdIndex,\r
+  IN UINT64 Address,\r
+  IN UINT8  AddressMode,\r
+  IN UINT16 DomainIdentifier\r
+  )\r
+{\r
+  UINT64  Reg64;\r
+  UINT64  Data64;\r
+\r
+  if (!mVtdEnabled) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  DEBUG((DEBUG_VERBOSE, "InvalidateVtdIOTLBPage(%d): 0x%016lx (0x%02x)\n", VtdIndex, Address, AddressMode));\r
+\r
+  if (mVtdUnitInformation[VtdIndex].CapReg.Bits.PSI != 0) {\r
+    Reg64 = MmioRead64 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + (mVtdUnitInformation[VtdIndex].ECapReg.Bits.IRO * 16) + R_IOTLB_REG);\r
+    if ((Reg64 & B_IOTLB_REG_IVT) != 0) {\r
+      DEBUG ((DEBUG_ERROR,"ERROR: InvalidateVtdIOTLBPage: B_IOTLB_REG_IVT is set for VTD(%d)\n", VtdIndex));\r
+      return EFI_DEVICE_ERROR;\r
+    }\r
+\r
+    Data64 = Address | AddressMode;\r
+    MmioWrite64 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + (mVtdUnitInformation[VtdIndex].ECapReg.Bits.IRO * 16) + R_IVA_REG, Data64);\r
+\r
+    Reg64 &= ((~B_IOTLB_REG_IVT) & (~B_IOTLB_REG_IIRG_MASK));\r
+    Reg64 |= (B_IOTLB_REG_IVT | V_IOTLB_REG_IIRG_PAGE);\r
+    Reg64 |= LShiftU64 (DomainIdentifier, 32);\r
+    MmioWrite64 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + (mVtdUnitInformation[VtdIndex].ECapReg.Bits.IRO * 16) + R_IOTLB_REG, Reg64);\r
+\r
+    do {\r
+      Reg64 = MmioRead64 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + (mVtdUnitInformation[VtdIndex].ECapReg.Bits.IRO * 16) + R_IOTLB_REG);\r
+    } while ((Reg64 & B_IOTLB_REG_IVT) != 0);\r
+  } else {\r
+    InvalidateVtdIOTLBGlobal (VtdIndex);\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Prepare VTD configuration.\r
+**/\r
+VOID\r
+PrepareVtdConfig (\r
+  VOID\r
+  )\r
+{\r
+  UINTN         Index;\r
+  UINTN         DomainNumber;\r
+\r
+  for (Index = 0; Index < mVtdUnitNumber; Index++) {\r
+    DEBUG ((DEBUG_INFO, "Dump VTd Capability (%d)\n", Index));\r
+    mVtdUnitInformation[Index].CapReg.Uint64 = MmioRead64 (mVtdUnitInformation[Index].VtdUnitBaseAddress + R_CAP_REG);\r
+    DumpVtdCapRegs (&mVtdUnitInformation[Index].CapReg);\r
+    mVtdUnitInformation[Index].ECapReg.Uint64 = MmioRead64 (mVtdUnitInformation[Index].VtdUnitBaseAddress + R_ECAP_REG);\r
+    DumpVtdECapRegs (&mVtdUnitInformation[Index].ECapReg);\r
+\r
+    if ((mVtdUnitInformation[Index].CapReg.Bits.SLLPS & BIT0) == 0) {\r
+      DEBUG((DEBUG_WARN, "!!!! 2MB super page is not supported on VTD %d !!!!\n", Index));\r
+    }\r
+    if ((mVtdUnitInformation[Index].CapReg.Bits.SAGAW & BIT2) == 0) {\r
+      DEBUG((DEBUG_ERROR, "!!!! 4-level page-table is not supported on VTD %d !!!!\n", Index));\r
+      return ;\r
+    }\r
+\r
+    DomainNumber = (UINTN)1 << (UINT8)((UINTN)mVtdUnitInformation[Index].CapReg.Bits.ND * 2 + 4);\r
+    if (mVtdUnitInformation[Index].PciDeviceInfo.PciDescriptorNumber >= DomainNumber) {\r
+      DEBUG((DEBUG_ERROR, "!!!! Pci device Number(0x%x) >= DomainNumber(0x%x) !!!!\n", mVtdUnitInformation[Index].PciDeviceInfo.PciDescriptorNumber, DomainNumber));\r
+      return ;\r
+    }\r
+  }\r
+  return ;\r
+}\r
+\r
+/**\r
+  Enable DMAR translation.\r
+\r
+  @retval EFI_SUCCESS           DMAR translation is enabled.\r
+  @retval EFI_DEVICE_ERROR      DMAR translation is not enabled.\r
+**/\r
+EFI_STATUS\r
+EnableDmar (\r
+  VOID\r
+  )\r
+{\r
+  UINTN     Index;\r
+  UINT64    Reg64;\r
+  UINT32    Reg32;\r
+\r
+  AsmWbinvd();\r
+\r
+  for (Index = 0; Index < mVtdUnitNumber; Index++) {\r
+    DEBUG((DEBUG_INFO, ">>>>>>EnableDmar() for engine [%d] \n", Index));\r
+\r
+    if (mVtdUnitInformation[Index].ExtRootEntryTable != NULL) {\r
+      DEBUG((DEBUG_INFO, "ExtRootEntryTable 0x%x \n", mVtdUnitInformation[Index].ExtRootEntryTable));\r
+      MmioWrite64 (mVtdUnitInformation[Index].VtdUnitBaseAddress + R_RTADDR_REG, (UINT64)(UINTN)mVtdUnitInformation[Index].ExtRootEntryTable | BIT11);\r
+    } else {\r
+      DEBUG((DEBUG_INFO, "RootEntryTable 0x%x \n", mVtdUnitInformation[Index].RootEntryTable));\r
+      MmioWrite64 (mVtdUnitInformation[Index].VtdUnitBaseAddress + R_RTADDR_REG, (UINT64)(UINTN)mVtdUnitInformation[Index].RootEntryTable);\r
+    }\r
+\r
+    MmioWrite32 (mVtdUnitInformation[Index].VtdUnitBaseAddress + R_GCMD_REG, B_GMCD_REG_SRTP);\r
+\r
+    DEBUG((DEBUG_INFO, "EnableDmar: waiting for RTPS bit to be set... \n"));\r
+    do {\r
+      Reg32 = MmioRead32 (mVtdUnitInformation[Index].VtdUnitBaseAddress + R_GSTS_REG);\r
+    } while((Reg32 & B_GSTS_REG_RTPS) == 0);\r
+\r
+    //\r
+    // Init DMAr Fault Event and Data registers\r
+    //\r
+    Reg32 = MmioRead32 (mVtdUnitInformation[Index].VtdUnitBaseAddress + R_FEDATA_REG);\r
+\r
+    //\r
+    // Write Buffer Flush before invalidation\r
+    //\r
+    Reg32 = MmioRead32 (mVtdUnitInformation[Index].VtdUnitBaseAddress + R_CAP_REG);\r
+    if ((Reg32 & B_CAP_REG_RWBF) != 0) {\r
+      MmioWrite32 (mVtdUnitInformation[Index].VtdUnitBaseAddress + R_GCMD_REG, B_GMCD_REG_WBF);\r
+    }\r
+\r
+    //\r
+    // Invalidate the context cache\r
+    //\r
+    Reg64 = MmioRead64 (mVtdUnitInformation[Index].VtdUnitBaseAddress + R_CCMD_REG);\r
+    if ((Reg64 & B_CCMD_REG_ICC) != 0) {\r
+      DEBUG ((DEBUG_INFO,"ERROR: EnableDmar: B_CCMD_REG_ICC is set for VTD(%d)\n",Index));\r
+      return EFI_DEVICE_ERROR;\r
+    }\r
+\r
+    Reg64 &= ((~B_CCMD_REG_ICC) & (~B_CCMD_REG_CIRG_MASK));\r
+    Reg64 |= (B_CCMD_REG_ICC | V_CCMD_REG_CIRG_GLOBAL);\r
+    MmioWrite64 (mVtdUnitInformation[Index].VtdUnitBaseAddress + R_CCMD_REG, Reg64);\r
+\r
+    DEBUG((DEBUG_INFO, "EnableDmar: Waiting B_CCMD_REG_ICC ...\n"));\r
+    do {\r
+      Reg64 = MmioRead64 (mVtdUnitInformation[Index].VtdUnitBaseAddress + R_CCMD_REG);\r
+    } while ((Reg64 & B_CCMD_REG_ICC) != 0);\r
+\r
+    //\r
+    // Invalidate the IOTLB cache\r
+    //\r
+    DEBUG((DEBUG_INFO, "EnableDmar: IRO 0x%x\n", mVtdUnitInformation[Index].ECapReg.Bits.IRO));\r
+\r
+    Reg64 = MmioRead64 (mVtdUnitInformation[Index].VtdUnitBaseAddress + (mVtdUnitInformation[Index].ECapReg.Bits.IRO * 16) + R_IOTLB_REG);\r
+    if ((Reg64 & B_IOTLB_REG_IVT) != 0) {\r
+      DEBUG ((DEBUG_INFO,"ERROR: EnableDmar: B_IOTLB_REG_IVT is set for VTD(%d)\n", Index));\r
+      return EFI_DEVICE_ERROR;\r
+    }\r
+\r
+    Reg64 &= ((~B_IOTLB_REG_IVT) & (~B_IOTLB_REG_IIRG_MASK));\r
+    Reg64 |= (B_IOTLB_REG_IVT | V_IOTLB_REG_IIRG_GLOBAL);\r
+    MmioWrite64 (mVtdUnitInformation[Index].VtdUnitBaseAddress + (mVtdUnitInformation[Index].ECapReg.Bits.IRO * 16) + R_IOTLB_REG, Reg64);\r
+\r
+    DEBUG((DEBUG_INFO, "EnableDmar: Waiting B_IOTLB_REG_IVT ...\n"));\r
+    do {\r
+      Reg64 = MmioRead64 (mVtdUnitInformation[Index].VtdUnitBaseAddress + (mVtdUnitInformation[Index].ECapReg.Bits.IRO * 16) + R_IOTLB_REG);\r
+    } while ((Reg64 & B_IOTLB_REG_IVT) != 0);\r
+\r
+    //\r
+    // Enable VTd\r
+    //\r
+    MmioWrite32 (mVtdUnitInformation[Index].VtdUnitBaseAddress + R_GCMD_REG, B_GMCD_REG_TE);\r
+    DEBUG((DEBUG_INFO, "EnableDmar: Waiting B_GSTS_REG_TE ...\n"));\r
+    do {\r
+      Reg32 = MmioRead32 (mVtdUnitInformation[Index].VtdUnitBaseAddress + R_GSTS_REG);\r
+    } while ((Reg32 & B_GSTS_REG_TE) == 0);\r
+\r
+    DEBUG ((DEBUG_INFO,"VTD (%d) enabled!<<<<<<\n",Index));\r
+  }\r
+\r
+  mVtdEnabled = TRUE;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Disable DMAR translation.\r
+\r
+  @retval EFI_SUCCESS           DMAR translation is disabled.\r
+  @retval EFI_DEVICE_ERROR      DMAR translation is not disabled.\r
+**/\r
+EFI_STATUS\r
+DisableDmar (\r
+  VOID\r
+  )\r
+{\r
+  UINTN     Index;\r
+  UINT32    Reg32;\r
+\r
+  AsmWbinvd();\r
+\r
+  for (Index = 0; Index < mVtdUnitNumber; Index++) {\r
+    DEBUG((DEBUG_INFO, ">>>>>>DisableDmar() for engine [%d] \n", Index));\r
+\r
+    //\r
+    // Write Buffer Flush before invalidation\r
+    //\r
+    Reg32 = MmioRead32 (mVtdUnitInformation[Index].VtdUnitBaseAddress + R_CAP_REG);\r
+    if ((Reg32 & B_CAP_REG_RWBF) != 0) {\r
+      MmioWrite32 (mVtdUnitInformation[Index].VtdUnitBaseAddress + R_GCMD_REG, B_GMCD_REG_WBF);\r
+    }\r
+\r
+    //\r
+    // Disable VTd\r
+    //\r
+    MmioWrite32 (mVtdUnitInformation[Index].VtdUnitBaseAddress + R_GCMD_REG, B_GMCD_REG_SRTP);\r
+    do {\r
+      Reg32 = MmioRead32 (mVtdUnitInformation[Index].VtdUnitBaseAddress + R_GSTS_REG);\r
+    } while((Reg32 & B_GSTS_REG_RTPS) == 0);\r
+\r
+    Reg32 = MmioRead32 (mVtdUnitInformation[Index].VtdUnitBaseAddress + R_GSTS_REG);\r
+    DEBUG((DEBUG_INFO, "DisableDmar: GSTS_REG - 0x%08x\n", Reg32));\r
+\r
+    DEBUG ((DEBUG_INFO,"VTD (%d) Disabled!<<<<<<\n",Index));\r
+  }\r
+\r
+  mVtdEnabled = FALSE;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Dump VTd capability registers.\r
+\r
+  @param[in]  CapReg              The capability register.\r
+**/\r
+VOID\r
+DumpVtdCapRegs (\r
+  IN VTD_CAP_REG *CapReg\r
+  )\r
+{\r
+  DEBUG((DEBUG_INFO, "  CapReg:\n", CapReg->Uint64));\r
+  DEBUG((DEBUG_INFO, "    ND     - 0x%x\n", CapReg->Bits.ND));\r
+  DEBUG((DEBUG_INFO, "    AFL    - 0x%x\n", CapReg->Bits.AFL));\r
+  DEBUG((DEBUG_INFO, "    RWBF   - 0x%x\n", CapReg->Bits.RWBF));\r
+  DEBUG((DEBUG_INFO, "    PLMR   - 0x%x\n", CapReg->Bits.PLMR));\r
+  DEBUG((DEBUG_INFO, "    PHMR   - 0x%x\n", CapReg->Bits.PHMR));\r
+  DEBUG((DEBUG_INFO, "    CM     - 0x%x\n", CapReg->Bits.CM));\r
+  DEBUG((DEBUG_INFO, "    SAGAW  - 0x%x\n", CapReg->Bits.SAGAW));\r
+  DEBUG((DEBUG_INFO, "    MGAW   - 0x%x\n", CapReg->Bits.MGAW));\r
+  DEBUG((DEBUG_INFO, "    ZLR    - 0x%x\n", CapReg->Bits.ZLR));\r
+  DEBUG((DEBUG_INFO, "    FRO    - 0x%x\n", CapReg->Bits.FRO));\r
+  DEBUG((DEBUG_INFO, "    SLLPS  - 0x%x\n", CapReg->Bits.SLLPS));\r
+  DEBUG((DEBUG_INFO, "    PSI    - 0x%x\n", CapReg->Bits.PSI));\r
+  DEBUG((DEBUG_INFO, "    NFR    - 0x%x\n", CapReg->Bits.NFR));\r
+  DEBUG((DEBUG_INFO, "    MAMV   - 0x%x\n", CapReg->Bits.MAMV));\r
+  DEBUG((DEBUG_INFO, "    DWD    - 0x%x\n", CapReg->Bits.DWD));\r
+  DEBUG((DEBUG_INFO, "    DRD    - 0x%x\n", CapReg->Bits.DRD));\r
+  DEBUG((DEBUG_INFO, "    FL1GP  - 0x%x\n", CapReg->Bits.FL1GP));\r
+  DEBUG((DEBUG_INFO, "    PI     - 0x%x\n", CapReg->Bits.PI));\r
+}\r
+\r
+/**\r
+  Dump VTd extended capability registers.\r
+\r
+  @param[in]  ECapReg              The extended capability register.\r
+**/\r
+VOID\r
+DumpVtdECapRegs (\r
+  IN VTD_ECAP_REG *ECapReg\r
+  )\r
+{\r
+  DEBUG((DEBUG_INFO, "  ECapReg:\n", ECapReg->Uint64));\r
+  DEBUG((DEBUG_INFO, "    C      - 0x%x\n", ECapReg->Bits.C));\r
+  DEBUG((DEBUG_INFO, "    QI     - 0x%x\n", ECapReg->Bits.QI));\r
+  DEBUG((DEBUG_INFO, "    DT     - 0x%x\n", ECapReg->Bits.DT));\r
+  DEBUG((DEBUG_INFO, "    IR     - 0x%x\n", ECapReg->Bits.IR));\r
+  DEBUG((DEBUG_INFO, "    EIM    - 0x%x\n", ECapReg->Bits.EIM));\r
+  DEBUG((DEBUG_INFO, "    PT     - 0x%x\n", ECapReg->Bits.PT));\r
+  DEBUG((DEBUG_INFO, "    SC     - 0x%x\n", ECapReg->Bits.SC));\r
+  DEBUG((DEBUG_INFO, "    IRO    - 0x%x\n", ECapReg->Bits.IRO));\r
+  DEBUG((DEBUG_INFO, "    MHMV   - 0x%x\n", ECapReg->Bits.MHMV));\r
+  DEBUG((DEBUG_INFO, "    ECS    - 0x%x\n", ECapReg->Bits.ECS));\r
+  DEBUG((DEBUG_INFO, "    MTS    - 0x%x\n", ECapReg->Bits.MTS));\r
+  DEBUG((DEBUG_INFO, "    NEST   - 0x%x\n", ECapReg->Bits.NEST));\r
+  DEBUG((DEBUG_INFO, "    DIS    - 0x%x\n", ECapReg->Bits.DIS));\r
+  DEBUG((DEBUG_INFO, "    PASID  - 0x%x\n", ECapReg->Bits.PASID));\r
+  DEBUG((DEBUG_INFO, "    PRS    - 0x%x\n", ECapReg->Bits.PRS));\r
+  DEBUG((DEBUG_INFO, "    ERS    - 0x%x\n", ECapReg->Bits.ERS));\r
+  DEBUG((DEBUG_INFO, "    SRS    - 0x%x\n", ECapReg->Bits.SRS));\r
+  DEBUG((DEBUG_INFO, "    NWFS   - 0x%x\n", ECapReg->Bits.NWFS));\r
+  DEBUG((DEBUG_INFO, "    EAFS   - 0x%x\n", ECapReg->Bits.EAFS));\r
+  DEBUG((DEBUG_INFO, "    PSS    - 0x%x\n", ECapReg->Bits.PSS));\r
+}\r
+\r
+/**\r
+  Dump VTd registers.\r
+\r
+  @param[in]  VtdIndex              The index of VTd engine.\r
+**/\r
+VOID\r
+DumpVtdRegs (\r
+  IN UINTN  VtdIndex\r
+  )\r
+{\r
+  UINTN         Index;\r
+  UINT64        Reg64;\r
+  VTD_FRCD_REG  FrcdReg;\r
+  VTD_CAP_REG   CapReg;\r
+  UINT32        Reg32;\r
+  VTD_SOURCE_ID SourceId;\r
+\r
+  DEBUG((DEBUG_INFO, "#### DumpVtdRegs(%d) Begin ####\n", VtdIndex));\r
+\r
+  Reg32 = MmioRead32 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + R_VER_REG);\r
+  DEBUG((DEBUG_INFO, "  VER_REG     - 0x%08x\n", Reg32));\r
+\r
+  CapReg.Uint64 = MmioRead64 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + R_CAP_REG);\r
+  DEBUG((DEBUG_INFO, "  CAP_REG     - 0x%016lx\n", CapReg.Uint64));\r
+\r
+  Reg64 = MmioRead64 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + R_ECAP_REG);\r
+  DEBUG((DEBUG_INFO, "  ECAP_REG    - 0x%016lx\n", Reg64));\r
+\r
+  Reg32 = MmioRead32 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + R_GSTS_REG);\r
+  DEBUG((DEBUG_INFO, "  GSTS_REG    - 0x%08x \n", Reg32));\r
+\r
+  Reg64 = MmioRead64 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + R_RTADDR_REG);\r
+  DEBUG((DEBUG_INFO, "  RTADDR_REG  - 0x%016lx\n", Reg64));\r
+\r
+  Reg64 = MmioRead64 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + R_CCMD_REG);\r
+  DEBUG((DEBUG_INFO, "  CCMD_REG    - 0x%016lx\n", Reg64));\r
+\r
+  Reg32 = MmioRead32 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + R_FSTS_REG);\r
+  DEBUG((DEBUG_INFO, "  FSTS_REG    - 0x%08x\n", Reg32));\r
+\r
+  Reg32 = MmioRead32 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + R_FECTL_REG);\r
+  DEBUG((DEBUG_INFO, "  FECTL_REG   - 0x%08x\n", Reg32));\r
+\r
+  Reg32 = MmioRead32 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + R_FEDATA_REG);\r
+  DEBUG((DEBUG_INFO, "  FEDATA_REG  - 0x%08x\n", Reg32));\r
+\r
+  Reg32 = MmioRead32 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + R_FEADDR_REG);\r
+  DEBUG((DEBUG_INFO, "  FEADDR_REG  - 0x%08x\n",Reg32));\r
+\r
+  Reg32 = MmioRead32 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + R_FEUADDR_REG);\r
+  DEBUG((DEBUG_INFO, "  FEUADDR_REG - 0x%08x\n",Reg32));\r
+\r
+  for (Index = 0; Index < (UINTN)CapReg.Bits.NFR + 1; Index++) {\r
+    FrcdReg.Uint64[0] = MmioRead64 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + ((CapReg.Bits.FRO * 16) + (Index * 16) + R_FRCD_REG));\r
+    FrcdReg.Uint64[1] = MmioRead64 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + ((CapReg.Bits.FRO * 16) + (Index * 16) + R_FRCD_REG + sizeof(UINT64)));\r
+    DEBUG((DEBUG_INFO, "  FRCD_REG[%d] - 0x%016lx %016lx\n", Index, FrcdReg.Uint64[1], FrcdReg.Uint64[0]));\r
+    if (FrcdReg.Uint64[1] != 0 || FrcdReg.Uint64[0] != 0) {\r
+      DEBUG((DEBUG_INFO, "    Fault Info - 0x%016lx\n", LShiftU64(FrcdReg.Bits.FI, 12)));\r
+      SourceId.Uint16 = (UINT16)FrcdReg.Bits.SID;\r
+      DEBUG((DEBUG_INFO, "    Source - B%02x D%02x F%02x\n", SourceId.Bits.Bus, SourceId.Bits.Device, SourceId.Bits.Function));\r
+      DEBUG((DEBUG_INFO, "    Type - %x (%a)\n", FrcdReg.Bits.T, FrcdReg.Bits.T ? "read" : "write"));\r
+      DEBUG((DEBUG_INFO, "    Reason - %x\n", FrcdReg.Bits.FR));\r
+    }\r
+  }\r
+\r
+  Reg64 = MmioRead64 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + (mVtdUnitInformation[VtdIndex].ECapReg.Bits.IRO * 16) + R_IVA_REG);\r
+  DEBUG((DEBUG_INFO, "  IVA_REG     - 0x%016lx\n",Reg64));\r
+\r
+  Reg64 = MmioRead64 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + (mVtdUnitInformation[VtdIndex].ECapReg.Bits.IRO * 16) + R_IOTLB_REG);\r
+  DEBUG((DEBUG_INFO, "  IOTLB_REG   - 0x%016lx\n",Reg64));\r
+\r
+  DEBUG((DEBUG_INFO, "#### DumpVtdRegs(%d) End ####\n", VtdIndex));\r
+}\r
+\r
+/**\r
+  Dump VTd registers for all VTd engine.\r
+**/\r
+VOID\r
+DumpVtdRegsAll (\r
+  VOID\r
+  )\r
+{\r
+  UINTN       Num;\r
+\r
+  for (Num = 0; Num < mVtdUnitNumber; Num++) {\r
+    DumpVtdRegs (Num);\r
+  }\r
+}\r
+\r
+/**\r
+  Dump VTd registers if there is error.\r
+**/\r
+VOID\r
+DumpVtdIfError (\r
+  VOID\r
+  )\r
+{\r
+  UINTN         Num;\r
+  UINTN         Index;\r
+  VTD_FRCD_REG  FrcdReg;\r
+  VTD_CAP_REG   CapReg;\r
+  UINT32        Reg32;\r
+  BOOLEAN       HasError;\r
+\r
+  for (Num = 0; Num < mVtdUnitNumber; Num++) {\r
+    HasError = FALSE;\r
+    Reg32 = MmioRead32 (mVtdUnitInformation[Num].VtdUnitBaseAddress + R_FSTS_REG);\r
+    if (Reg32 != 0) {\r
+      HasError = TRUE;\r
+    }\r
+    Reg32 = MmioRead32 (mVtdUnitInformation[Num].VtdUnitBaseAddress + R_FECTL_REG);\r
+    if ((Reg32 & BIT30) != 0) {\r
+      HasError = TRUE;\r
+    }\r
+\r
+    CapReg.Uint64 = MmioRead64 (mVtdUnitInformation[Num].VtdUnitBaseAddress + R_CAP_REG);\r
+    for (Index = 0; Index < (UINTN)CapReg.Bits.NFR + 1; Index++) {\r
+      FrcdReg.Uint64[0] = MmioRead64 (mVtdUnitInformation[Num].VtdUnitBaseAddress + ((CapReg.Bits.FRO * 16) + (Index * 16) + R_FRCD_REG));\r
+      FrcdReg.Uint64[1] = MmioRead64 (mVtdUnitInformation[Num].VtdUnitBaseAddress + ((CapReg.Bits.FRO * 16) + (Index * 16) + R_FRCD_REG + sizeof(UINT64)));\r
+      if ((FrcdReg.Uint64[0] != 0) || (FrcdReg.Uint64[1] != 0)) {\r
+        HasError = TRUE;\r
+      }\r
+    }\r
+\r
+    if (HasError) {\r
+      DEBUG((DEBUG_INFO, "#### ERROR ####\n"));\r
+      DumpVtdRegs (Num);\r
+      DEBUG((DEBUG_INFO, "#### ERROR ####\n"));\r
+    }\r
+  }\r
+}\r