[edk2] [RFC PATCH] ArmPkg: implement generic SMMU driver to remap DRAM for 32-bit PCI DMA

Ard Biesheuvel posted 1 patch 6 years, 11 months ago
Failed in applying to current master (apply log)
ArmPkg/ArmPkg.dec                                                        |   7 +
ArmPkg/ArmPkg.dsc                                                        |   1 +
ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/BmDma.c                        | 467 ++++++++++++++++++++
ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.c   | 323 ++++++++++++++
ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.inf |  62 +++
5 files changed, 860 insertions(+)
[edk2] [RFC PATCH] ArmPkg: implement generic SMMU driver to remap DRAM for 32-bit PCI DMA
Posted by Ard Biesheuvel 6 years, 11 months ago
This implements a driver that uses any SMMU compatible with the generic
ARM SMMU architecture to remap the lowest 4 GB of DRAM in a way that
makes it accessible to PCI masters that are only 32-bit DMA capable.

Note that this driver goes a bit beyond what is strictly necessary to
support 32-bit DMA, given that it also creates an identity map of the
lowest 4 GB of DRAM. This is intended for interoperability with external
drivers that may use the PCI I/O protocol incorrectly (or not at all)
and program host addresses into the DMA registers and/or rings without
any regard for translation or address size. If a platform's base of DRAM
is a power of 2, and if the platform runs UEFI entirely in the lowest
4 GB of DRAM, any host address access by a PCI master will hit the ID
mapped window, and any truncation that may occur will convert the host
address into the device address.

Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
---

This is based on Jiewen Yao's IOMMU protocol support series, v4.

https://lists.01.org/pipermail/edk2-devel/2017-April/010330.html

Tested with AMD Seattle, which has no DRAM below 4 GB.

 ArmPkg/ArmPkg.dec                                                        |   7 +
 ArmPkg/ArmPkg.dsc                                                        |   1 +
 ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/BmDma.c                        | 467 ++++++++++++++++++++
 ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.c   | 323 ++++++++++++++
 ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.inf |  62 +++
 5 files changed, 860 insertions(+)

diff --git a/ArmPkg/ArmPkg.dec b/ArmPkg/ArmPkg.dec
index c4b4da2f95bb..96913e3c0713 100644
--- a/ArmPkg/ArmPkg.dec
+++ b/ArmPkg/ArmPkg.dec
@@ -322,3 +322,10 @@ [PcdsFixedAtBuild.common, PcdsDynamic.common]
   #
   gArmTokenSpaceGuid.PcdPciBusMin|0x0|UINT32|0x00000059
   gArmTokenSpaceGuid.PcdPciBusMax|0x0|UINT32|0x0000005A
+
+  #
+  # Base address and context interrupt of the generic SMMU that
+  # translates memory accesses made by PCI masters
+  #
+  gArmTokenSpaceGuid.PcdPciGenericSmmuBase|0x0|UINT64|0x0000005B
+  gArmTokenSpaceGuid.PcdPciGenericSmmuContextInterrupt|0x0|UINT16|0x0000005C
diff --git a/ArmPkg/ArmPkg.dsc b/ArmPkg/ArmPkg.dsc
index 9144334cb821..9bbc71fa2479 100644
--- a/ArmPkg/ArmPkg.dsc
+++ b/ArmPkg/ArmPkg.dsc
@@ -127,6 +127,7 @@ [Components.common]
   ArmPkg/Drivers/ArmGic/ArmGicDxe.inf
   ArmPkg/Drivers/ArmGic/ArmGicLib.inf
   ArmPkg/Drivers/ArmGic/ArmGicSecLib.inf
+  ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.inf
   ArmPkg/Drivers/GenericWatchdogDxe/GenericWatchdogDxe.inf
   ArmPkg/Drivers/TimerDxe/TimerDxe.inf
 
diff --git a/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/BmDma.c b/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/BmDma.c
new file mode 100644
index 000000000000..629209e335e5
--- /dev/null
+++ b/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/BmDma.c
@@ -0,0 +1,467 @@
+/** @file
+  BmDma related function
+
+  Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
+  Copyright (c) 2017, Linaro Ltd. All rights reserved.<BR>
+
+  This program and the accompanying materials
+  are licensed and made available under the terms and conditions of the BSD License
+  which accompanies this distribution.  The full text of the license may be found at
+  http://opensource.org/licenses/bsd-license.php
+
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include <PiDxe.h>
+
+#include <Protocol/IoMmu.h>
+
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+
+STATIC CONST UINT64 mTranslationBase = FixedPcdGet64 (PcdSystemMemoryBase);
+
+#define MAP_INFO_SIGNATURE  SIGNATURE_32 ('D', 'M', 'A', 'P')
+typedef struct {
+  UINT32                                    Signature;
+  LIST_ENTRY                                Link;
+  EDKII_IOMMU_OPERATION                     Operation;
+  UINTN                                     NumberOfBytes;
+  UINTN                                     NumberOfPages;
+  EFI_PHYSICAL_ADDRESS                      HostAddress;
+  EFI_PHYSICAL_ADDRESS                      MappedHostAddress;
+} MAP_INFO;
+#define MAP_INFO_FROM_LINK(a) CR (a, MAP_INFO, Link, MAP_INFO_SIGNATURE)
+
+STATIC LIST_ENTRY                 mMaps = INITIALIZE_LIST_HEAD_VARIABLE(mMaps);
+
+/**
+  Provides the controller-specific addresses required to access system memory from a
+  DMA bus master.
+
+  @param  This                  The protocol instance pointer.
+  @param  Operation             Indicates if the bus master is going to read or write to system memory.
+  @param  HostAddress           The system memory address to map to the PCI controller.
+  @param  NumberOfBytes         On input the number of bytes to map. On output the number of bytes
+                                that were mapped.
+  @param  DeviceAddress         The resulting map address for the bus master PCI controller to use to
+                                access the hosts HostAddress.
+  @param  Mapping               A resulting value to pass to Unmap().
+
+  @retval EFI_SUCCESS           The range was mapped for the returned NumberOfBytes.
+  @retval EFI_UNSUPPORTED       The HostAddress cannot be mapped as a common buffer.
+  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+  @retval EFI_OUT_OF_RESOURCES  The request could not be completed due to a lack of resources.
+  @retval EFI_DEVICE_ERROR      The system hardware could not map the requested address.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+IoMmuMap (
+  IN     EDKII_IOMMU_PROTOCOL                       *This,
+  IN     EDKII_IOMMU_OPERATION                      Operation,
+  IN     VOID                                       *HostAddress,
+  IN OUT UINTN                                      *NumberOfBytes,
+  OUT    EFI_PHYSICAL_ADDRESS                       *DeviceAddress,
+  OUT    VOID                                       **Mapping
+  )
+{
+  EFI_STATUS                                        Status;
+  EFI_PHYSICAL_ADDRESS                              PhysicalAddress;
+  MAP_INFO                                          *MapInfo;
+  BOOLEAN                                           NeedRemap;
+
+  if (HostAddress == NULL || NumberOfBytes == NULL || DeviceAddress == NULL ||
+      Mapping == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // Make sure that Operation is valid
+  //
+  if ((UINT32) Operation >= EdkiiIoMmuOperationMaximum) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  NeedRemap = FALSE;
+  PhysicalAddress = (UINTN)HostAddress;
+
+  if ((PhysicalAddress + *NumberOfBytes) > mTranslationBase + SIZE_4GB) {
+    //
+    // If the root bridge or the device cannot handle performing DMA above
+    // 4GB but any part of the DMA transfer being mapped is above 4GB, then
+    // map the DMA transfer to a buffer below 4GB.
+    //
+    NeedRemap = TRUE;
+  }
+
+  if ((Operation == EdkiiIoMmuOperationBusMasterCommonBuffer ||
+       Operation == EdkiiIoMmuOperationBusMasterCommonBuffer64) &&
+      NeedRemap) {
+    //
+    // Common Buffer operations can not be remapped.  If the common buffer
+    // if above 4GB, then it is not possible to generate a mapping, so return
+    // an error.
+    //
+    return EFI_UNSUPPORTED;
+  }
+
+  //
+  // Allocate a MAP_INFO structure to remember the mapping when Unmap() is
+  // called later.
+  //
+  MapInfo = AllocatePool (sizeof (MAP_INFO));
+  if (MapInfo == NULL) {
+    *NumberOfBytes = 0;
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  //
+  // Initialize the MAP_INFO structure
+  //
+  MapInfo->Signature         = MAP_INFO_SIGNATURE;
+  MapInfo->Operation         = Operation;
+  MapInfo->NumberOfBytes     = *NumberOfBytes;
+  MapInfo->NumberOfPages     = EFI_SIZE_TO_PAGES (MapInfo->NumberOfBytes);
+  MapInfo->HostAddress       = PhysicalAddress;
+  MapInfo->MappedHostAddress = (EFI_PHYSICAL_ADDRESS)-1;
+
+  //
+  // Allocate a buffer below 4GB to map the transfer to.
+  //
+  if (NeedRemap) {
+    MapInfo->MappedHostAddress = mTranslationBase + SIZE_4GB - 1;
+    Status = gBS->AllocatePages (
+                    AllocateMaxAddress,
+                    EfiBootServicesData,
+                    MapInfo->NumberOfPages,
+                    &MapInfo->MappedHostAddress
+                    );
+    if (EFI_ERROR (Status)) {
+      FreePool (MapInfo);
+      *NumberOfBytes = 0;
+      return Status;
+    }
+
+    //
+    // If this is a read operation from the Bus Master's point of view,
+    // then copy the contents of the real buffer into the mapped buffer
+    // so the Bus Master can read the contents of the real buffer.
+    //
+    if (Operation == EdkiiIoMmuOperationBusMasterRead ||
+        Operation == EdkiiIoMmuOperationBusMasterRead64) {
+      CopyMem (
+        (VOID *)(UINTN)MapInfo->MappedHostAddress,
+        (VOID *)(UINTN)MapInfo->HostAddress,
+        MapInfo->NumberOfBytes
+        );
+    }
+  } else {
+    MapInfo->MappedHostAddress = MapInfo->HostAddress;
+  }
+
+  InsertTailList (&mMaps, &MapInfo->Link);
+
+  //
+  // The DeviceAddress is the address of the maped buffer below 4GB
+  //
+  *DeviceAddress = MapInfo->MappedHostAddress - mTranslationBase;
+
+  //
+  // Return a pointer to the MAP_INFO structure in Mapping
+  //
+  *Mapping       = MapInfo;
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Completes the Map() operation and releases any corresponding resources.
+
+  @param  This                  The protocol instance pointer.
+  @param  Mapping               The mapping value returned from Map().
+
+  @retval EFI_SUCCESS           The range was unmapped.
+  @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by Map().
+  @retval EFI_DEVICE_ERROR      The data was not committed to the target system memory.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+IoMmuUnmap (
+  IN  EDKII_IOMMU_PROTOCOL                     *This,
+  IN  VOID                                     *Mapping
+  )
+{
+  MAP_INFO                 *MapInfo;
+  LIST_ENTRY               *Link;
+
+  if (Mapping == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  MapInfo = NULL;
+  for (Link = GetFirstNode (&mMaps)
+       ; !IsNull (&mMaps, Link)
+       ; Link = GetNextNode (&mMaps, Link)
+       ) {
+    MapInfo = MAP_INFO_FROM_LINK (Link);
+    if (MapInfo == Mapping) {
+      break;
+    }
+  }
+  //
+  // Mapping is not a valid value returned by Map()
+  //
+  if (MapInfo != Mapping) {
+    return EFI_INVALID_PARAMETER;
+  }
+  RemoveEntryList (&MapInfo->Link);
+
+  if (MapInfo->MappedHostAddress != MapInfo->HostAddress) {
+    //
+    // If this is a write operation from the Bus Master's point of view,
+    // then copy the contents of the mapped buffer into the real buffer
+    // so the processor can read the contents of the real buffer.
+    //
+    if (MapInfo->Operation == EdkiiIoMmuOperationBusMasterWrite ||
+        MapInfo->Operation == EdkiiIoMmuOperationBusMasterWrite64) {
+      CopyMem (
+        (VOID *)(UINTN)MapInfo->HostAddress,
+        (VOID *)(UINTN)MapInfo->MappedHostAddress,
+        MapInfo->NumberOfBytes
+        );
+    }
+
+    //
+    // Free the mapped buffer and the MAP_INFO structure.
+    //
+    gBS->FreePages (MapInfo->MappedHostAddress, MapInfo->NumberOfPages);
+  }
+
+  FreePool (Mapping);
+  return EFI_SUCCESS;
+}
+
+/**
+  Allocates pages that are suitable for an OperationBusMasterCommonBuffer or
+  OperationBusMasterCommonBuffer64 mapping.
+
+  @param  This                  The protocol instance pointer.
+  @param  Type                  This parameter is not used and must be ignored.
+  @param  MemoryType            The type of memory to allocate, EfiBootServicesData or
+                                EfiRuntimeServicesData.
+  @param  Pages                 The number of pages to allocate.
+  @param  HostAddress           A pointer to store the base system memory address of the
+                                allocated range.
+  @param  Attributes            The requested bit mask of attributes for the allocated range.
+
+  @retval EFI_SUCCESS           The requested memory pages were allocated.
+  @retval EFI_UNSUPPORTED       Attributes is unsupported. The only legal attribute bits are
+                                MEMORY_WRITE_COMBINE and MEMORY_CACHED.
+  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+  @retval EFI_OUT_OF_RESOURCES  The memory pages could not be allocated.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+IoMmuAllocateBuffer (
+  IN     EDKII_IOMMU_PROTOCOL                     *This,
+  IN     EFI_ALLOCATE_TYPE                        Type,
+  IN     EFI_MEMORY_TYPE                          MemoryType,
+  IN     UINTN                                    Pages,
+  IN OUT VOID                                     **HostAddress,
+  IN     UINT64                                   Attributes
+  )
+{
+  EFI_STATUS                Status;
+  EFI_PHYSICAL_ADDRESS      PhysicalAddress;
+
+  //
+  // Validate Attributes
+  //
+  if ((Attributes & EDKII_IOMMU_ATTRIBUTE_INVALID_FOR_ALLOCATE_BUFFER) != 0) {
+    return EFI_UNSUPPORTED;
+  }
+
+  //
+  // Check for invalid inputs
+  //
+  if (HostAddress == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // The only valid memory types are EfiBootServicesData and
+  // EfiRuntimeServicesData
+  //
+  if (MemoryType != EfiBootServicesData &&
+      MemoryType != EfiRuntimeServicesData) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // Limit allocations to memory covered by the remapped window.
+  //
+  PhysicalAddress = mTranslationBase + SIZE_4GB - 1;
+  Status = gBS->AllocatePages (
+                  AllocateMaxAddress,
+                  MemoryType,
+                  Pages,
+                  &PhysicalAddress
+                  );
+  if (!EFI_ERROR (Status)) {
+    *HostAddress = (VOID *)(UINTN)PhysicalAddress;
+  }
+
+  return Status;
+}
+
+/**
+  Frees memory that was allocated with AllocateBuffer().
+
+  @param  This                  The protocol instance pointer.
+  @param  Pages                 The number of pages to free.
+  @param  HostAddress           The base system memory address of the allocated range.
+
+  @retval EFI_SUCCESS           The requested memory pages were freed.
+  @retval EFI_INVALID_PARAMETER The memory range specified by HostAddress and Pages
+                                was not allocated with AllocateBuffer().
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+IoMmuFreeBuffer (
+  IN  EDKII_IOMMU_PROTOCOL                     *This,
+  IN  UINTN                                    Pages,
+  IN  VOID                                     *HostAddress
+  )
+{
+  return gBS->FreePages ((EFI_PHYSICAL_ADDRESS) (UINTN) HostAddress, Pages);
+}
+
+/**
+  Set IOMMU attribute for a system memory.
+
+  If the IOMMU protocol exists, the system memory cannot be used
+  for DMA by default.
+
+  When a device requests a DMA access for a system memory,
+  the device driver need use SetAttribute() to update the IOMMU
+  attribute to request DMA access (read and/or write).
+
+  The DeviceHandle is used to identify which device submits the request.
+  The IOMMU implementation need translate the device path to an IOMMU device ID,
+  and set IOMMU hardware register accordingly.
+  1) DeviceHandle can be a standard PCI device.
+     The memory for BusMasterRead need set EDKII_IOMMU_ACCESS_READ.
+     The memory for BusMasterWrite need set EDKII_IOMMU_ACCESS_WRITE.
+     The memory for BusMasterCommonBuffer need set EDKII_IOMMU_ACCESS_READ|EDKII_IOMMU_ACCESS_WRITE.
+     After the memory is used, the memory need set 0 to keep it being protected.
+  2) DeviceHandle can be an ACPI device (ISA, I2C, SPI, etc).
+     The memory for DMA access need set EDKII_IOMMU_ACCESS_READ and/or EDKII_IOMMU_ACCESS_WRITE.
+
+  @param[in]  This              The protocol instance pointer.
+  @param[in]  DeviceHandle      The device who initiates the DMA access request.
+  @param[in]  DeviceAddress     The base of device memory address to be used as the DMA memory.
+  @param[in]  Length            The length of device memory address to be used as the DMA memory.
+  @param[in]  IoMmuAccess       The IOMMU access.
+
+  @retval EFI_SUCCESS            The IoMmuAccess is set for the memory range specified by DeviceAddress and Length.
+  @retval EFI_INVALID_PARAMETER  DeviceHandle is an invalid handle.
+  @retval EFI_INVALID_PARAMETER  DeviceAddress is not IoMmu Page size aligned.
+  @retval EFI_INVALID_PARAMETER  Length is not IoMmu Page size aligned.
+  @retval EFI_INVALID_PARAMETER  Length is 0.
+  @retval EFI_INVALID_PARAMETER  IoMmuAccess specified an illegal combination of access.
+  @retval EFI_UNSUPPORTED        DeviceHandle is unknown by the IOMMU.
+  @retval EFI_UNSUPPORTED        The bit mask of IoMmuAccess is not supported by the IOMMU.
+  @retval EFI_UNSUPPORTED        The IOMMU does not support the memory range specified by DeviceAddress and Length.
+  @retval EFI_OUT_OF_RESOURCES   There are not enough resources available to modify the IOMMU access.
+  @retval EFI_DEVICE_ERROR       The IOMMU device reported an error while attempting the operation.
+
+**/
+EFI_STATUS
+EFIAPI
+IoMmuSetAttribute (
+  IN EDKII_IOMMU_PROTOCOL  *This,
+  IN EFI_HANDLE            DeviceHandle,
+  IN EFI_PHYSICAL_ADDRESS  DeviceAddress,
+  IN UINT64                Length,
+  IN UINT64                IoMmuAccess
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+/**
+  Set IOMMU attribute for a system memory.
+
+  If the IOMMU protocol exists, the system memory cannot be used
+  for DMA by default.
+
+  When a device requests a DMA access for a system memory,
+  the device driver need use SetAttribute() to update the IOMMU
+  attribute to request DMA access (read and/or write).
+
+  The DeviceHandle is used to identify which device submits the request.
+  The IOMMU implementation need translate the device path to an IOMMU device ID,
+  and set IOMMU hardware register accordingly.
+  1) DeviceHandle can be a standard PCI device.
+     The memory for BusMasterRead need set EDKII_IOMMU_ACCESS_READ.
+     The memory for BusMasterWrite need set EDKII_IOMMU_ACCESS_WRITE.
+     The memory for BusMasterCommonBuffer need set EDKII_IOMMU_ACCESS_READ|EDKII_IOMMU_ACCESS_WRITE.
+     After the memory is used, the memory need set 0 to keep it being protected.
+  2) DeviceHandle can be an ACPI device (ISA, I2C, SPI, etc).
+     The memory for DMA access need set EDKII_IOMMU_ACCESS_READ and/or EDKII_IOMMU_ACCESS_WRITE.
+
+  @param[in]  This              The protocol instance pointer.
+  @param[in]  DeviceHandle      The device who initiates the DMA access request.
+  @param[in]  Mapping           The mapping value returned from Map().
+  @param[in]  IoMmuAccess       The IOMMU access.
+
+  @retval EFI_SUCCESS            The IoMmuAccess is set for the memory range specified by DeviceAddress and Length.
+  @retval EFI_INVALID_PARAMETER  DeviceHandle is an invalid handle.
+  @retval EFI_INVALID_PARAMETER  Mapping is not a value that was returned by Map().
+  @retval EFI_INVALID_PARAMETER  IoMmuAccess specified an illegal combination of access.
+  @retval EFI_UNSUPPORTED        DeviceHandle is unknown by the IOMMU.
+  @retval EFI_UNSUPPORTED        The bit mask of IoMmuAccess is not supported by the IOMMU.
+  @retval EFI_UNSUPPORTED        The IOMMU does not support the memory range specified by Mapping.
+  @retval EFI_OUT_OF_RESOURCES   There are not enough resources available to modify the IOMMU access.
+  @retval EFI_DEVICE_ERROR       The IOMMU device reported an error while attempting the operation.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+IoMmuSetMappingAttribute (
+  IN EDKII_IOMMU_PROTOCOL  *This,
+  IN EFI_HANDLE            DeviceHandle,
+  IN VOID                  *Mapping,
+  IN UINT64                IoMmuAccess
+  )
+{
+  //
+  // We only support a static remapping of DRAM into the PCI address space
+  // so there is nothing we need to do to handle invocations of this protocol
+  // method.
+  //
+  return EFI_SUCCESS;
+}
+
+EDKII_IOMMU_PROTOCOL  mGenericSmmuIommuProtocol = {
+  EDKII_IOMMU_PROTOCOL_REVISION,
+  IoMmuSetAttribute,
+  IoMmuMap,
+  IoMmuUnmap,
+  IoMmuAllocateBuffer,
+  IoMmuFreeBuffer,
+  IoMmuSetMappingAttribute,
+};
diff --git a/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.c b/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.c
new file mode 100644
index 000000000000..8f5093af14ea
--- /dev/null
+++ b/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.c
@@ -0,0 +1,323 @@
+/** @file
+
+  Copyright (c) 2017, Linaro Ltd. All rights reserved.<BR>
+
+  This program and the accompanying materials
+  are licensed and made available under the terms and conditions of the BSD License
+  which accompanies this distribution.  The full text of the license may be found at
+  http://opensource.org/licenses/bsd-license.php
+
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include <PiDxe.h>
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/IoLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+
+#include <Protocol/Cpu.h>
+#include <Protocol/HardwareInterrupt.h>
+#include <Protocol/IoMmu.h>
+
+#include <Chipset/AArch64Mmu.h>
+
+#define GL_CR0                          0x0
+#define GL_CR0_CLIENTPD                 BIT0
+
+#define GL_IDR0                         0x20
+#define GL_IDR1                         0x24
+#define GL_STLBIALL                     0x60
+#define GL_TLBIALLNSNH                  0x68
+#define GL_SMR0                         0x800
+#define GL_S2CR0                        0xc00
+#define GL_CBA2R0                       0x1800
+
+#define GL_IDR0_NUMSMRG_MASK            0xff
+
+#define GL_IDR1_NUMPAGENDXB_MASK        0x7
+#define GL_IDR1_NUMPAGENDXB_SHIFT       28
+#define GL_IDR1_PAGE_SIZE_64KB          BIT31
+
+#define CB_BASE(i)                      (mContextBankOffset + ((i) * SIZE_4KB))
+
+#define CB_SCTLR_OFFSET                 0x0
+#define CB_TTBR0_OFFSET                 0x20
+#define CB_TTBCR_OFFSET                 0x30
+
+#define CB_FAR                          0x60
+#define CB_FSR                          0x58
+#define CB_FSYNR0                       0x68
+#define CB_FSYNR1                       0x6c
+
+#define TT_S2_MEMATTR_CACHED            (0xF << 2)
+#define TT_S2_AP_READ_WRITE             (0x3 << 6)
+
+#define TT_ENTRY_ATTRIBUTES             (TT_TYPE_BLOCK_ENTRY | \
+                                         TT_SH_INNER_SHAREABLE | \
+                                         TT_S2_AP_READ_WRITE | \
+                                         TT_S2_MEMATTR_CACHED | \
+                                         TT_AF)
+
+#define TCR_T0SZ(bits)                  ((UINT32)(32 - (bits)) & 0x3f)
+#define TCR_SL0_LEVEL1                  BIT6
+#define TCR_SL0_LEVEL2                  0
+
+#define SCTLR_M_ENABLE                  BIT0
+#define SCTLR_TR_ENABLE                 BIT1
+#define SCTLR_AF_ENABLE                 BIT2
+#define SCTLR_CTX_FAULT_ENABLE          BIT5
+#define SCTLR_CTX_INT_ENABLE            BIT6
+
+#define DRAM_BASE                       FixedPcdGet64 (PcdSystemMemoryBase)
+
+extern EDKII_IOMMU_PROTOCOL             mGenericSmmuIommuProtocol;
+
+//
+// Create a static stage 2 mapping of the first 4 GB of DRAM in the start
+// of the IOVA space, and as an ID mapping at the original offset.
+//
+STATIC CONST UINT64 mPciTranslation[1024] __attribute__((aligned(SIZE_8KB))) = {
+  [0] = TT_ENTRY_ATTRIBUTES | (DRAM_BASE),
+  [1] = TT_ENTRY_ATTRIBUTES | (DRAM_BASE + SIZE_1GB),
+  [2] = TT_ENTRY_ATTRIBUTES | (DRAM_BASE + SIZE_1GB * 2UL),
+  [3] = TT_ENTRY_ATTRIBUTES | (DRAM_BASE + SIZE_1GB * 3UL),
+
+  //
+  // The ID mapping of the first 4 GB of DRAM is a workaround for buggy
+  // drivers that violate the UEFI spec by ignoring the device address
+  // returned by the PCI I/O map/unmap routines, and program host
+  // addresses into the DMA h/w registers or rings instead.
+  //
+  [(DRAM_BASE >> 30)]     = TT_ENTRY_ATTRIBUTES | (DRAM_BASE),
+  [(DRAM_BASE >> 30) + 1] = TT_ENTRY_ATTRIBUTES | (DRAM_BASE + SIZE_1GB),
+  [(DRAM_BASE >> 30) + 2] = TT_ENTRY_ATTRIBUTES | (DRAM_BASE + SIZE_1GB * 2UL),
+  [(DRAM_BASE >> 30) + 3] = TT_ENTRY_ATTRIBUTES | (DRAM_BASE + SIZE_1GB * 3UL),
+};
+
+STATIC EFI_HARDWARE_INTERRUPT_PROTOCOL    *mInterrupt;
+STATIC EFI_EVENT                          mEfiExitBootServicesEvent;
+STATIC UINTN                              mContextBankOffset;
+
+STATIC
+UINT32
+ReadGlobalReg32 (
+  IN  UINT64    Offset
+  )
+{
+  return MmioRead32 (FixedPcdGet64 (PcdPciGenericSmmuBase) + Offset);
+}
+
+STATIC
+VOID
+WriteGlobalReg32 (
+  IN  UINT64    Offset,
+  IN  UINT64    Value
+  )
+{
+  MmioWrite32 (FixedPcdGet64 (PcdPciGenericSmmuBase) + Offset, Value);
+}
+
+STATIC
+UINT32
+ReadCbReg32 (
+  IN  UINTN     Bank,
+  IN  UINT64    Offset
+  )
+{
+  return MmioRead32 (FixedPcdGet64 (PcdPciGenericSmmuBase) + CB_BASE(Bank) +
+                     Offset);
+}
+
+STATIC
+VOID
+WriteCbReg32 (
+  IN  UINTN     Bank,
+  IN  UINT64    Offset,
+  IN  UINT32    Value
+  )
+{
+  MmioWrite32 (FixedPcdGet64 (PcdPciGenericSmmuBase) + CB_BASE(Bank) + Offset,
+               Value);
+}
+
+STATIC
+UINT64
+ReadCbReg64 (
+  IN  UINTN     Bank,
+  IN  UINT64    Offset
+  )
+{
+  return MmioRead64 (FixedPcdGet64 (PcdPciGenericSmmuBase) + CB_BASE(Bank) +
+                     Offset);
+}
+
+STATIC
+VOID
+WriteCbReg64 (
+  IN  UINTN     Bank,
+  IN  UINT64    Offset,
+  IN  UINT64    Value
+  )
+{
+  MmioWrite64 (FixedPcdGet64 (PcdPciGenericSmmuBase) + CB_BASE(Bank) + Offset,
+               Value);
+}
+
+STATIC
+VOID
+EFIAPI
+ContextInterruptHandler (
+  IN  HARDWARE_INTERRUPT_SOURCE   Source,
+  IN  EFI_SYSTEM_CONTEXT          SystemContext
+  )
+{
+  //
+  // Dump the SMMU context fault registers when taking a context interrupt
+  //
+  DEBUG ((DEBUG_WARN,
+    "Context interrupt asserted by SMMU:\n\n"
+    "SMMU_CB0_FAR        0x%016llx \n"
+    "SMMU_CB0_FSR        0x%08llx \n"
+    "SMMU_CB0_FSYNR0     0x%08llx \n"
+    "SMMU_CB0_FSYNR1     0x%08llx \n",
+    ReadCbReg64 (0, CB_FAR),
+    ReadCbReg32 (0, CB_FSR),
+    ReadCbReg32 (0, CB_FSYNR0),
+    ReadCbReg32 (0, CB_FSYNR1)));
+
+  mInterrupt->EndOfInterrupt (mInterrupt, Source);
+}
+
+STATIC
+VOID
+EFIAPI
+ExitBootServicesEvent (
+  IN EFI_EVENT  Event,
+  IN VOID       *Context
+  )
+{
+  //
+  // Put the SMMU back into bypass mode
+  //
+  MmioOr32 (FixedPcdGet64 (PcdPciGenericSmmuBase) + GL_CR0, GL_CR0_CLIENTPD);
+}
+
+EFI_STATUS
+GenericSmmuStaticPciDmaDxeInitialize (
+  IN EFI_HANDLE        ImageHandle,
+  IN EFI_SYSTEM_TABLE  *SystemTable
+  )
+{
+  EFI_STATUS        Status;
+  UINTN             Idx;
+  UINT32            IdVal;
+  UINTN             NumStreamMappingRegisters;
+
+  //
+  // The static mapping uses 1 GB block mappings, whose VAs and PAs should
+  // be equal modulo the block size.
+  //
+  ASSERT ((DRAM_BASE % SIZE_1GB) == 0);
+
+  if ((DRAM_BASE & (DRAM_BASE - 1)) != 0) {
+    //
+    // Buggy drivers that use truncated host addresses instead of device
+    // addresses for DMA may still work correctly if such truncation is
+    // guaranteed to produce the remapped alias. This is the case if
+    // DRAM_BASE is a power of 2.
+    //
+    DEBUG ((DEBUG_WARN,
+      "%a: this driver will work better if DRAM_BASE is a power of 2!\n",
+      __FUNCTION__));
+  }
+
+  Status = gBS->LocateProtocol (&gHardwareInterruptProtocolGuid, NULL,
+                  (VOID **)&mInterrupt);
+  ASSERT_EFI_ERROR (Status);
+
+  Status = mInterrupt->RegisterInterruptSource (mInterrupt,
+                         PcdGet16 (PcdPciGenericSmmuContextInterrupt),
+                         ContextInterruptHandler);
+  ASSERT_EFI_ERROR (Status);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = gBS->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES,
+                  TPL_NOTIFY, ExitBootServicesEvent, NULL,
+                  &mEfiExitBootServicesEvent);
+  ASSERT_EFI_ERROR (Status);
+
+  Status = gBS->InstallMultipleProtocolInterfaces (
+                  &ImageHandle,
+                  &gEdkiiIoMmuProtocolGuid, &mGenericSmmuIommuProtocol,
+                  NULL
+                  );
+  ASSERT_EFI_ERROR (Status);
+
+  IdVal = ReadGlobalReg32 (GL_IDR1);
+  mContextBankOffset = (IdVal & GL_IDR1_PAGE_SIZE_64KB) ? SIZE_64KB : SIZE_4KB;
+  mContextBankOffset <<= (1 + ((IdVal >> GL_IDR1_NUMPAGENDXB_SHIFT) &
+                               GL_IDR1_NUMPAGENDXB_MASK));
+
+  //
+  // Clear all stream mappings
+  //
+  NumStreamMappingRegisters = ReadGlobalReg32 (GL_IDR0) & GL_IDR0_NUMSMRG_MASK;
+  for (Idx = 0; Idx < NumStreamMappingRegisters; Idx++) {
+    WriteGlobalReg32 (GL_SMR0 + Idx * sizeof(UINT32), 0x0);
+    WriteGlobalReg32 (GL_S2CR0 + Idx * sizeof(UINT32), 0x0);
+  }
+
+  //
+  // Set stream match register 0 to match all streams, and map onto
+  // context bank 0
+  //
+  WriteGlobalReg32 (GL_SMR0, 0xffff0000);
+  WriteGlobalReg32 (GL_S2CR0, 0x0);
+
+  //
+  // Disable the context bank
+  //
+  WriteCbReg32 (0, CB_SCTLR_OFFSET, 0);
+
+  //
+  // Assign the translation base register for context bank 0
+  //
+  WriteCbReg64 (0, CB_TTBR0_OFFSET, (UINTN)mPciTranslation);
+
+  //
+  // Flush TLBS.
+  //
+  WriteGlobalReg32 (GL_STLBIALL, 0);
+  WriteGlobalReg32 (GL_TLBIALLNSNH, 0);
+
+  //
+  // Configure the size of the translation space, the number of levels,
+  // and the cacheability attributes of the PTW memory accesses.
+  //
+  WriteCbReg32 (0, CB_TTBCR_OFFSET, TCR_T0SZ(40) |
+                                    TCR_SH_INNER_SHAREABLE |
+                                    TCR_RGN_INNER_WRITE_BACK_NO_ALLOC |
+                                    TCR_RGN_OUTER_WRITE_BACK_NO_ALLOC |
+                                    TCR_SL0_LEVEL1);
+
+  //
+  // Enable the context bank
+  //
+  WriteCbReg32 (0, CB_SCTLR_OFFSET, SCTLR_TR_ENABLE |
+                                    SCTLR_AF_ENABLE |
+                                    SCTLR_CTX_INT_ENABLE |
+                                    SCTLR_CTX_FAULT_ENABLE |
+                                    SCTLR_M_ENABLE);
+
+  //
+  // Get the SMMU out of bypass mode
+  //
+  MmioAnd32 (FixedPcdGet64 (PcdPciGenericSmmuBase) + GL_CR0, ~GL_CR0_CLIENTPD);
+
+  return EFI_SUCCESS;
+}
diff --git a/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.inf b/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.inf
new file mode 100644
index 000000000000..02c17e755c4a
--- /dev/null
+++ b/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.inf
@@ -0,0 +1,62 @@
+## @file
+#
+# Copyright (c) 2017, Linaro Ltd. All rights reserved.<BR>
+#
+# This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution.  The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010019
+  BASE_NAME                      = GenericSmmuStaticPciDmaDxe
+  FILE_GUID                      = 59b5e69f-88b7-4632-a2ab-6abe6bdedda2
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = GenericSmmuStaticPciDmaDxeInitialize
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+#  VALID_ARCHITECTURES           = ARM AARCH64
+#
+#
+
+[Sources]
+  GenericSmmuStaticPciDmaDxe.c
+  BmDma.c
+
+[Packages]
+  ArmPkg/ArmPkg.dec
+  EmbeddedPkg/EmbeddedPkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  MdePkg/MdePkg.dec
+
+[LibraryClasses]
+  DebugLib
+  BaseLib
+  BaseMemoryLib
+  IoLib
+  MemoryAllocationLib
+  UefiBootServicesTableLib
+  UefiDriverEntryPoint
+
+[Protocols]
+  gEdkiiIoMmuProtocolGuid                     ## PRODUCES
+  gEfiPciIoProtocolGuid                       ## CONSUMES
+  gHardwareInterruptProtocolGuid              ## CONSUMES
+
+[Pcd]
+  gArmTokenSpaceGuid.PcdPciGenericSmmuBase
+  gArmTokenSpaceGuid.PcdPciGenericSmmuContextInterrupt
+
+[FixedPcd]
+  gArmTokenSpaceGuid.PcdSystemMemoryBase
+
+[Depex]
+  gHardwareInterruptProtocolGuid
-- 
2.9.3

_______________________________________________
edk2-devel mailing list
edk2-devel@lists.01.org
https://lists.01.org/mailman/listinfo/edk2-devel
Re: [edk2] [RFC PATCH] ArmPkg: implement generic SMMU driver to remap DRAM for 32-bit PCI DMA
Posted by Ard Biesheuvel 6 years, 11 months ago
On 2 May 2017 at 11:32, Ard Biesheuvel <ard.biesheuvel@linaro.org> wrote:
> This implements a driver that uses any SMMU compatible with the generic
> ARM SMMU architecture to remap the lowest 4 GB of DRAM in a way that
> makes it accessible to PCI masters that are only 32-bit DMA capable.
>
> Note that this driver goes a bit beyond what is strictly necessary to
> support 32-bit DMA, given that it also creates an identity map of the
> lowest 4 GB of DRAM. This is intended for interoperability with external
> drivers that may use the PCI I/O protocol incorrectly (or not at all)
> and program host addresses into the DMA registers and/or rings without
> any regard for translation or address size. If a platform's base of DRAM
> is a power of 2, and if the platform runs UEFI entirely in the lowest
> 4 GB of DRAM, any host address access by a PCI master will hit the ID
> mapped window, and any truncation that may occur will convert the host
> address into the device address.
>
> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
> ---
>

The prerequisites are now in place. Any comments?


>  ArmPkg/ArmPkg.dec                                                        |   7 +
>  ArmPkg/ArmPkg.dsc                                                        |   1 +
>  ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/BmDma.c                        | 467 ++++++++++++++++++++
>  ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.c   | 323 ++++++++++++++
>  ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.inf |  62 +++
>  5 files changed, 860 insertions(+)
>
> diff --git a/ArmPkg/ArmPkg.dec b/ArmPkg/ArmPkg.dec
> index c4b4da2f95bb..96913e3c0713 100644
> --- a/ArmPkg/ArmPkg.dec
> +++ b/ArmPkg/ArmPkg.dec
> @@ -322,3 +322,10 @@ [PcdsFixedAtBuild.common, PcdsDynamic.common]
>    #
>    gArmTokenSpaceGuid.PcdPciBusMin|0x0|UINT32|0x00000059
>    gArmTokenSpaceGuid.PcdPciBusMax|0x0|UINT32|0x0000005A
> +
> +  #
> +  # Base address and context interrupt of the generic SMMU that
> +  # translates memory accesses made by PCI masters
> +  #
> +  gArmTokenSpaceGuid.PcdPciGenericSmmuBase|0x0|UINT64|0x0000005B
> +  gArmTokenSpaceGuid.PcdPciGenericSmmuContextInterrupt|0x0|UINT16|0x0000005C
> diff --git a/ArmPkg/ArmPkg.dsc b/ArmPkg/ArmPkg.dsc
> index 9144334cb821..9bbc71fa2479 100644
> --- a/ArmPkg/ArmPkg.dsc
> +++ b/ArmPkg/ArmPkg.dsc
> @@ -127,6 +127,7 @@ [Components.common]
>    ArmPkg/Drivers/ArmGic/ArmGicDxe.inf
>    ArmPkg/Drivers/ArmGic/ArmGicLib.inf
>    ArmPkg/Drivers/ArmGic/ArmGicSecLib.inf
> +  ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.inf
>    ArmPkg/Drivers/GenericWatchdogDxe/GenericWatchdogDxe.inf
>    ArmPkg/Drivers/TimerDxe/TimerDxe.inf
>
> diff --git a/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/BmDma.c b/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/BmDma.c
> new file mode 100644
> index 000000000000..629209e335e5
> --- /dev/null
> +++ b/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/BmDma.c
> @@ -0,0 +1,467 @@
> +/** @file
> +  BmDma related function
> +
> +  Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
> +  Copyright (c) 2017, Linaro Ltd. All rights reserved.<BR>
> +
> +  This program and the accompanying materials
> +  are licensed and made available under the terms and conditions of the BSD License
> +  which accompanies this distribution.  The full text of the license may be found at
> +  http://opensource.org/licenses/bsd-license.php
> +
> +  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
> +  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
> +
> +**/
> +
> +#include <PiDxe.h>
> +
> +#include <Protocol/IoMmu.h>
> +
> +#include <Library/BaseLib.h>
> +#include <Library/DebugLib.h>
> +#include <Library/BaseMemoryLib.h>
> +#include <Library/MemoryAllocationLib.h>
> +#include <Library/UefiBootServicesTableLib.h>
> +
> +STATIC CONST UINT64 mTranslationBase = FixedPcdGet64 (PcdSystemMemoryBase);
> +
> +#define MAP_INFO_SIGNATURE  SIGNATURE_32 ('D', 'M', 'A', 'P')
> +typedef struct {
> +  UINT32                                    Signature;
> +  LIST_ENTRY                                Link;
> +  EDKII_IOMMU_OPERATION                     Operation;
> +  UINTN                                     NumberOfBytes;
> +  UINTN                                     NumberOfPages;
> +  EFI_PHYSICAL_ADDRESS                      HostAddress;
> +  EFI_PHYSICAL_ADDRESS                      MappedHostAddress;
> +} MAP_INFO;
> +#define MAP_INFO_FROM_LINK(a) CR (a, MAP_INFO, Link, MAP_INFO_SIGNATURE)
> +
> +STATIC LIST_ENTRY                 mMaps = INITIALIZE_LIST_HEAD_VARIABLE(mMaps);
> +
> +/**
> +  Provides the controller-specific addresses required to access system memory from a
> +  DMA bus master.
> +
> +  @param  This                  The protocol instance pointer.
> +  @param  Operation             Indicates if the bus master is going to read or write to system memory.
> +  @param  HostAddress           The system memory address to map to the PCI controller.
> +  @param  NumberOfBytes         On input the number of bytes to map. On output the number of bytes
> +                                that were mapped.
> +  @param  DeviceAddress         The resulting map address for the bus master PCI controller to use to
> +                                access the hosts HostAddress.
> +  @param  Mapping               A resulting value to pass to Unmap().
> +
> +  @retval EFI_SUCCESS           The range was mapped for the returned NumberOfBytes.
> +  @retval EFI_UNSUPPORTED       The HostAddress cannot be mapped as a common buffer.
> +  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
> +  @retval EFI_OUT_OF_RESOURCES  The request could not be completed due to a lack of resources.
> +  @retval EFI_DEVICE_ERROR      The system hardware could not map the requested address.
> +
> +**/
> +STATIC
> +EFI_STATUS
> +EFIAPI
> +IoMmuMap (
> +  IN     EDKII_IOMMU_PROTOCOL                       *This,
> +  IN     EDKII_IOMMU_OPERATION                      Operation,
> +  IN     VOID                                       *HostAddress,
> +  IN OUT UINTN                                      *NumberOfBytes,
> +  OUT    EFI_PHYSICAL_ADDRESS                       *DeviceAddress,
> +  OUT    VOID                                       **Mapping
> +  )
> +{
> +  EFI_STATUS                                        Status;
> +  EFI_PHYSICAL_ADDRESS                              PhysicalAddress;
> +  MAP_INFO                                          *MapInfo;
> +  BOOLEAN                                           NeedRemap;
> +
> +  if (HostAddress == NULL || NumberOfBytes == NULL || DeviceAddress == NULL ||
> +      Mapping == NULL) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  //
> +  // Make sure that Operation is valid
> +  //
> +  if ((UINT32) Operation >= EdkiiIoMmuOperationMaximum) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  NeedRemap = FALSE;
> +  PhysicalAddress = (UINTN)HostAddress;
> +
> +  if ((PhysicalAddress + *NumberOfBytes) > mTranslationBase + SIZE_4GB) {
> +    //
> +    // If the root bridge or the device cannot handle performing DMA above
> +    // 4GB but any part of the DMA transfer being mapped is above 4GB, then
> +    // map the DMA transfer to a buffer below 4GB.
> +    //
> +    NeedRemap = TRUE;
> +  }
> +
> +  if ((Operation == EdkiiIoMmuOperationBusMasterCommonBuffer ||
> +       Operation == EdkiiIoMmuOperationBusMasterCommonBuffer64) &&
> +      NeedRemap) {
> +    //
> +    // Common Buffer operations can not be remapped.  If the common buffer
> +    // if above 4GB, then it is not possible to generate a mapping, so return
> +    // an error.
> +    //
> +    return EFI_UNSUPPORTED;
> +  }
> +
> +  //
> +  // Allocate a MAP_INFO structure to remember the mapping when Unmap() is
> +  // called later.
> +  //
> +  MapInfo = AllocatePool (sizeof (MAP_INFO));
> +  if (MapInfo == NULL) {
> +    *NumberOfBytes = 0;
> +    return EFI_OUT_OF_RESOURCES;
> +  }
> +
> +  //
> +  // Initialize the MAP_INFO structure
> +  //
> +  MapInfo->Signature         = MAP_INFO_SIGNATURE;
> +  MapInfo->Operation         = Operation;
> +  MapInfo->NumberOfBytes     = *NumberOfBytes;
> +  MapInfo->NumberOfPages     = EFI_SIZE_TO_PAGES (MapInfo->NumberOfBytes);
> +  MapInfo->HostAddress       = PhysicalAddress;
> +  MapInfo->MappedHostAddress = (EFI_PHYSICAL_ADDRESS)-1;
> +
> +  //
> +  // Allocate a buffer below 4GB to map the transfer to.
> +  //
> +  if (NeedRemap) {
> +    MapInfo->MappedHostAddress = mTranslationBase + SIZE_4GB - 1;
> +    Status = gBS->AllocatePages (
> +                    AllocateMaxAddress,
> +                    EfiBootServicesData,
> +                    MapInfo->NumberOfPages,
> +                    &MapInfo->MappedHostAddress
> +                    );
> +    if (EFI_ERROR (Status)) {
> +      FreePool (MapInfo);
> +      *NumberOfBytes = 0;
> +      return Status;
> +    }
> +
> +    //
> +    // If this is a read operation from the Bus Master's point of view,
> +    // then copy the contents of the real buffer into the mapped buffer
> +    // so the Bus Master can read the contents of the real buffer.
> +    //
> +    if (Operation == EdkiiIoMmuOperationBusMasterRead ||
> +        Operation == EdkiiIoMmuOperationBusMasterRead64) {
> +      CopyMem (
> +        (VOID *)(UINTN)MapInfo->MappedHostAddress,
> +        (VOID *)(UINTN)MapInfo->HostAddress,
> +        MapInfo->NumberOfBytes
> +        );
> +    }
> +  } else {
> +    MapInfo->MappedHostAddress = MapInfo->HostAddress;
> +  }
> +
> +  InsertTailList (&mMaps, &MapInfo->Link);
> +
> +  //
> +  // The DeviceAddress is the address of the maped buffer below 4GB
> +  //
> +  *DeviceAddress = MapInfo->MappedHostAddress - mTranslationBase;
> +
> +  //
> +  // Return a pointer to the MAP_INFO structure in Mapping
> +  //
> +  *Mapping       = MapInfo;
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +  Completes the Map() operation and releases any corresponding resources.
> +
> +  @param  This                  The protocol instance pointer.
> +  @param  Mapping               The mapping value returned from Map().
> +
> +  @retval EFI_SUCCESS           The range was unmapped.
> +  @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by Map().
> +  @retval EFI_DEVICE_ERROR      The data was not committed to the target system memory.
> +**/
> +STATIC
> +EFI_STATUS
> +EFIAPI
> +IoMmuUnmap (
> +  IN  EDKII_IOMMU_PROTOCOL                     *This,
> +  IN  VOID                                     *Mapping
> +  )
> +{
> +  MAP_INFO                 *MapInfo;
> +  LIST_ENTRY               *Link;
> +
> +  if (Mapping == NULL) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  MapInfo = NULL;
> +  for (Link = GetFirstNode (&mMaps)
> +       ; !IsNull (&mMaps, Link)
> +       ; Link = GetNextNode (&mMaps, Link)
> +       ) {
> +    MapInfo = MAP_INFO_FROM_LINK (Link);
> +    if (MapInfo == Mapping) {
> +      break;
> +    }
> +  }
> +  //
> +  // Mapping is not a valid value returned by Map()
> +  //
> +  if (MapInfo != Mapping) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +  RemoveEntryList (&MapInfo->Link);
> +
> +  if (MapInfo->MappedHostAddress != MapInfo->HostAddress) {
> +    //
> +    // If this is a write operation from the Bus Master's point of view,
> +    // then copy the contents of the mapped buffer into the real buffer
> +    // so the processor can read the contents of the real buffer.
> +    //
> +    if (MapInfo->Operation == EdkiiIoMmuOperationBusMasterWrite ||
> +        MapInfo->Operation == EdkiiIoMmuOperationBusMasterWrite64) {
> +      CopyMem (
> +        (VOID *)(UINTN)MapInfo->HostAddress,
> +        (VOID *)(UINTN)MapInfo->MappedHostAddress,
> +        MapInfo->NumberOfBytes
> +        );
> +    }
> +
> +    //
> +    // Free the mapped buffer and the MAP_INFO structure.
> +    //
> +    gBS->FreePages (MapInfo->MappedHostAddress, MapInfo->NumberOfPages);
> +  }
> +
> +  FreePool (Mapping);
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +  Allocates pages that are suitable for an OperationBusMasterCommonBuffer or
> +  OperationBusMasterCommonBuffer64 mapping.
> +
> +  @param  This                  The protocol instance pointer.
> +  @param  Type                  This parameter is not used and must be ignored.
> +  @param  MemoryType            The type of memory to allocate, EfiBootServicesData or
> +                                EfiRuntimeServicesData.
> +  @param  Pages                 The number of pages to allocate.
> +  @param  HostAddress           A pointer to store the base system memory address of the
> +                                allocated range.
> +  @param  Attributes            The requested bit mask of attributes for the allocated range.
> +
> +  @retval EFI_SUCCESS           The requested memory pages were allocated.
> +  @retval EFI_UNSUPPORTED       Attributes is unsupported. The only legal attribute bits are
> +                                MEMORY_WRITE_COMBINE and MEMORY_CACHED.
> +  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
> +  @retval EFI_OUT_OF_RESOURCES  The memory pages could not be allocated.
> +
> +**/
> +STATIC
> +EFI_STATUS
> +EFIAPI
> +IoMmuAllocateBuffer (
> +  IN     EDKII_IOMMU_PROTOCOL                     *This,
> +  IN     EFI_ALLOCATE_TYPE                        Type,
> +  IN     EFI_MEMORY_TYPE                          MemoryType,
> +  IN     UINTN                                    Pages,
> +  IN OUT VOID                                     **HostAddress,
> +  IN     UINT64                                   Attributes
> +  )
> +{
> +  EFI_STATUS                Status;
> +  EFI_PHYSICAL_ADDRESS      PhysicalAddress;
> +
> +  //
> +  // Validate Attributes
> +  //
> +  if ((Attributes & EDKII_IOMMU_ATTRIBUTE_INVALID_FOR_ALLOCATE_BUFFER) != 0) {
> +    return EFI_UNSUPPORTED;
> +  }
> +
> +  //
> +  // Check for invalid inputs
> +  //
> +  if (HostAddress == NULL) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  //
> +  // The only valid memory types are EfiBootServicesData and
> +  // EfiRuntimeServicesData
> +  //
> +  if (MemoryType != EfiBootServicesData &&
> +      MemoryType != EfiRuntimeServicesData) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  //
> +  // Limit allocations to memory covered by the remapped window.
> +  //
> +  PhysicalAddress = mTranslationBase + SIZE_4GB - 1;
> +  Status = gBS->AllocatePages (
> +                  AllocateMaxAddress,
> +                  MemoryType,
> +                  Pages,
> +                  &PhysicalAddress
> +                  );
> +  if (!EFI_ERROR (Status)) {
> +    *HostAddress = (VOID *)(UINTN)PhysicalAddress;
> +  }
> +
> +  return Status;
> +}
> +
> +/**
> +  Frees memory that was allocated with AllocateBuffer().
> +
> +  @param  This                  The protocol instance pointer.
> +  @param  Pages                 The number of pages to free.
> +  @param  HostAddress           The base system memory address of the allocated range.
> +
> +  @retval EFI_SUCCESS           The requested memory pages were freed.
> +  @retval EFI_INVALID_PARAMETER The memory range specified by HostAddress and Pages
> +                                was not allocated with AllocateBuffer().
> +
> +**/
> +STATIC
> +EFI_STATUS
> +EFIAPI
> +IoMmuFreeBuffer (
> +  IN  EDKII_IOMMU_PROTOCOL                     *This,
> +  IN  UINTN                                    Pages,
> +  IN  VOID                                     *HostAddress
> +  )
> +{
> +  return gBS->FreePages ((EFI_PHYSICAL_ADDRESS) (UINTN) HostAddress, Pages);
> +}
> +
> +/**
> +  Set IOMMU attribute for a system memory.
> +
> +  If the IOMMU protocol exists, the system memory cannot be used
> +  for DMA by default.
> +
> +  When a device requests a DMA access for a system memory,
> +  the device driver need use SetAttribute() to update the IOMMU
> +  attribute to request DMA access (read and/or write).
> +
> +  The DeviceHandle is used to identify which device submits the request.
> +  The IOMMU implementation need translate the device path to an IOMMU device ID,
> +  and set IOMMU hardware register accordingly.
> +  1) DeviceHandle can be a standard PCI device.
> +     The memory for BusMasterRead need set EDKII_IOMMU_ACCESS_READ.
> +     The memory for BusMasterWrite need set EDKII_IOMMU_ACCESS_WRITE.
> +     The memory for BusMasterCommonBuffer need set EDKII_IOMMU_ACCESS_READ|EDKII_IOMMU_ACCESS_WRITE.
> +     After the memory is used, the memory need set 0 to keep it being protected.
> +  2) DeviceHandle can be an ACPI device (ISA, I2C, SPI, etc).
> +     The memory for DMA access need set EDKII_IOMMU_ACCESS_READ and/or EDKII_IOMMU_ACCESS_WRITE.
> +
> +  @param[in]  This              The protocol instance pointer.
> +  @param[in]  DeviceHandle      The device who initiates the DMA access request.
> +  @param[in]  DeviceAddress     The base of device memory address to be used as the DMA memory.
> +  @param[in]  Length            The length of device memory address to be used as the DMA memory.
> +  @param[in]  IoMmuAccess       The IOMMU access.
> +
> +  @retval EFI_SUCCESS            The IoMmuAccess is set for the memory range specified by DeviceAddress and Length.
> +  @retval EFI_INVALID_PARAMETER  DeviceHandle is an invalid handle.
> +  @retval EFI_INVALID_PARAMETER  DeviceAddress is not IoMmu Page size aligned.
> +  @retval EFI_INVALID_PARAMETER  Length is not IoMmu Page size aligned.
> +  @retval EFI_INVALID_PARAMETER  Length is 0.
> +  @retval EFI_INVALID_PARAMETER  IoMmuAccess specified an illegal combination of access.
> +  @retval EFI_UNSUPPORTED        DeviceHandle is unknown by the IOMMU.
> +  @retval EFI_UNSUPPORTED        The bit mask of IoMmuAccess is not supported by the IOMMU.
> +  @retval EFI_UNSUPPORTED        The IOMMU does not support the memory range specified by DeviceAddress and Length.
> +  @retval EFI_OUT_OF_RESOURCES   There are not enough resources available to modify the IOMMU access.
> +  @retval EFI_DEVICE_ERROR       The IOMMU device reported an error while attempting the operation.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +IoMmuSetAttribute (
> +  IN EDKII_IOMMU_PROTOCOL  *This,
> +  IN EFI_HANDLE            DeviceHandle,
> +  IN EFI_PHYSICAL_ADDRESS  DeviceAddress,
> +  IN UINT64                Length,
> +  IN UINT64                IoMmuAccess
> +  )
> +{
> +  return EFI_UNSUPPORTED;
> +}
> +
> +/**
> +  Set IOMMU attribute for a system memory.
> +
> +  If the IOMMU protocol exists, the system memory cannot be used
> +  for DMA by default.
> +
> +  When a device requests a DMA access for a system memory,
> +  the device driver need use SetAttribute() to update the IOMMU
> +  attribute to request DMA access (read and/or write).
> +
> +  The DeviceHandle is used to identify which device submits the request.
> +  The IOMMU implementation need translate the device path to an IOMMU device ID,
> +  and set IOMMU hardware register accordingly.
> +  1) DeviceHandle can be a standard PCI device.
> +     The memory for BusMasterRead need set EDKII_IOMMU_ACCESS_READ.
> +     The memory for BusMasterWrite need set EDKII_IOMMU_ACCESS_WRITE.
> +     The memory for BusMasterCommonBuffer need set EDKII_IOMMU_ACCESS_READ|EDKII_IOMMU_ACCESS_WRITE.
> +     After the memory is used, the memory need set 0 to keep it being protected.
> +  2) DeviceHandle can be an ACPI device (ISA, I2C, SPI, etc).
> +     The memory for DMA access need set EDKII_IOMMU_ACCESS_READ and/or EDKII_IOMMU_ACCESS_WRITE.
> +
> +  @param[in]  This              The protocol instance pointer.
> +  @param[in]  DeviceHandle      The device who initiates the DMA access request.
> +  @param[in]  Mapping           The mapping value returned from Map().
> +  @param[in]  IoMmuAccess       The IOMMU access.
> +
> +  @retval EFI_SUCCESS            The IoMmuAccess is set for the memory range specified by DeviceAddress and Length.
> +  @retval EFI_INVALID_PARAMETER  DeviceHandle is an invalid handle.
> +  @retval EFI_INVALID_PARAMETER  Mapping is not a value that was returned by Map().
> +  @retval EFI_INVALID_PARAMETER  IoMmuAccess specified an illegal combination of access.
> +  @retval EFI_UNSUPPORTED        DeviceHandle is unknown by the IOMMU.
> +  @retval EFI_UNSUPPORTED        The bit mask of IoMmuAccess is not supported by the IOMMU.
> +  @retval EFI_UNSUPPORTED        The IOMMU does not support the memory range specified by Mapping.
> +  @retval EFI_OUT_OF_RESOURCES   There are not enough resources available to modify the IOMMU access.
> +  @retval EFI_DEVICE_ERROR       The IOMMU device reported an error while attempting the operation.
> +
> +**/
> +STATIC
> +EFI_STATUS
> +EFIAPI
> +IoMmuSetMappingAttribute (
> +  IN EDKII_IOMMU_PROTOCOL  *This,
> +  IN EFI_HANDLE            DeviceHandle,
> +  IN VOID                  *Mapping,
> +  IN UINT64                IoMmuAccess
> +  )
> +{
> +  //
> +  // We only support a static remapping of DRAM into the PCI address space
> +  // so there is nothing we need to do to handle invocations of this protocol
> +  // method.
> +  //
> +  return EFI_SUCCESS;
> +}
> +
> +EDKII_IOMMU_PROTOCOL  mGenericSmmuIommuProtocol = {
> +  EDKII_IOMMU_PROTOCOL_REVISION,
> +  IoMmuSetAttribute,
> +  IoMmuMap,
> +  IoMmuUnmap,
> +  IoMmuAllocateBuffer,
> +  IoMmuFreeBuffer,
> +  IoMmuSetMappingAttribute,
> +};
> diff --git a/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.c b/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.c
> new file mode 100644
> index 000000000000..8f5093af14ea
> --- /dev/null
> +++ b/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.c
> @@ -0,0 +1,323 @@
> +/** @file
> +
> +  Copyright (c) 2017, Linaro Ltd. All rights reserved.<BR>
> +
> +  This program and the accompanying materials
> +  are licensed and made available under the terms and conditions of the BSD License
> +  which accompanies this distribution.  The full text of the license may be found at
> +  http://opensource.org/licenses/bsd-license.php
> +
> +  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
> +  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
> +
> +**/
> +
> +#include <PiDxe.h>
> +#include <Library/BaseLib.h>
> +#include <Library/DebugLib.h>
> +#include <Library/IoLib.h>
> +#include <Library/UefiBootServicesTableLib.h>
> +
> +#include <Protocol/Cpu.h>
> +#include <Protocol/HardwareInterrupt.h>
> +#include <Protocol/IoMmu.h>
> +
> +#include <Chipset/AArch64Mmu.h>
> +
> +#define GL_CR0                          0x0
> +#define GL_CR0_CLIENTPD                 BIT0
> +
> +#define GL_IDR0                         0x20
> +#define GL_IDR1                         0x24
> +#define GL_STLBIALL                     0x60
> +#define GL_TLBIALLNSNH                  0x68
> +#define GL_SMR0                         0x800
> +#define GL_S2CR0                        0xc00
> +#define GL_CBA2R0                       0x1800
> +
> +#define GL_IDR0_NUMSMRG_MASK            0xff
> +
> +#define GL_IDR1_NUMPAGENDXB_MASK        0x7
> +#define GL_IDR1_NUMPAGENDXB_SHIFT       28
> +#define GL_IDR1_PAGE_SIZE_64KB          BIT31
> +
> +#define CB_BASE(i)                      (mContextBankOffset + ((i) * SIZE_4KB))
> +
> +#define CB_SCTLR_OFFSET                 0x0
> +#define CB_TTBR0_OFFSET                 0x20
> +#define CB_TTBCR_OFFSET                 0x30
> +
> +#define CB_FAR                          0x60
> +#define CB_FSR                          0x58
> +#define CB_FSYNR0                       0x68
> +#define CB_FSYNR1                       0x6c
> +
> +#define TT_S2_MEMATTR_CACHED            (0xF << 2)
> +#define TT_S2_AP_READ_WRITE             (0x3 << 6)
> +
> +#define TT_ENTRY_ATTRIBUTES             (TT_TYPE_BLOCK_ENTRY | \
> +                                         TT_SH_INNER_SHAREABLE | \
> +                                         TT_S2_AP_READ_WRITE | \
> +                                         TT_S2_MEMATTR_CACHED | \
> +                                         TT_AF)
> +
> +#define TCR_T0SZ(bits)                  ((UINT32)(32 - (bits)) & 0x3f)
> +#define TCR_SL0_LEVEL1                  BIT6
> +#define TCR_SL0_LEVEL2                  0
> +
> +#define SCTLR_M_ENABLE                  BIT0
> +#define SCTLR_TR_ENABLE                 BIT1
> +#define SCTLR_AF_ENABLE                 BIT2
> +#define SCTLR_CTX_FAULT_ENABLE          BIT5
> +#define SCTLR_CTX_INT_ENABLE            BIT6
> +
> +#define DRAM_BASE                       FixedPcdGet64 (PcdSystemMemoryBase)
> +
> +extern EDKII_IOMMU_PROTOCOL             mGenericSmmuIommuProtocol;
> +
> +//
> +// Create a static stage 2 mapping of the first 4 GB of DRAM in the start
> +// of the IOVA space, and as an ID mapping at the original offset.
> +//
> +STATIC CONST UINT64 mPciTranslation[1024] __attribute__((aligned(SIZE_8KB))) = {
> +  [0] = TT_ENTRY_ATTRIBUTES | (DRAM_BASE),
> +  [1] = TT_ENTRY_ATTRIBUTES | (DRAM_BASE + SIZE_1GB),
> +  [2] = TT_ENTRY_ATTRIBUTES | (DRAM_BASE + SIZE_1GB * 2UL),
> +  [3] = TT_ENTRY_ATTRIBUTES | (DRAM_BASE + SIZE_1GB * 3UL),
> +
> +  //
> +  // The ID mapping of the first 4 GB of DRAM is a workaround for buggy
> +  // drivers that violate the UEFI spec by ignoring the device address
> +  // returned by the PCI I/O map/unmap routines, and program host
> +  // addresses into the DMA h/w registers or rings instead.
> +  //
> +  [(DRAM_BASE >> 30)]     = TT_ENTRY_ATTRIBUTES | (DRAM_BASE),
> +  [(DRAM_BASE >> 30) + 1] = TT_ENTRY_ATTRIBUTES | (DRAM_BASE + SIZE_1GB),
> +  [(DRAM_BASE >> 30) + 2] = TT_ENTRY_ATTRIBUTES | (DRAM_BASE + SIZE_1GB * 2UL),
> +  [(DRAM_BASE >> 30) + 3] = TT_ENTRY_ATTRIBUTES | (DRAM_BASE + SIZE_1GB * 3UL),
> +};
> +
> +STATIC EFI_HARDWARE_INTERRUPT_PROTOCOL    *mInterrupt;
> +STATIC EFI_EVENT                          mEfiExitBootServicesEvent;
> +STATIC UINTN                              mContextBankOffset;
> +
> +STATIC
> +UINT32
> +ReadGlobalReg32 (
> +  IN  UINT64    Offset
> +  )
> +{
> +  return MmioRead32 (FixedPcdGet64 (PcdPciGenericSmmuBase) + Offset);
> +}
> +
> +STATIC
> +VOID
> +WriteGlobalReg32 (
> +  IN  UINT64    Offset,
> +  IN  UINT64    Value
> +  )
> +{
> +  MmioWrite32 (FixedPcdGet64 (PcdPciGenericSmmuBase) + Offset, Value);
> +}
> +
> +STATIC
> +UINT32
> +ReadCbReg32 (
> +  IN  UINTN     Bank,
> +  IN  UINT64    Offset
> +  )
> +{
> +  return MmioRead32 (FixedPcdGet64 (PcdPciGenericSmmuBase) + CB_BASE(Bank) +
> +                     Offset);
> +}
> +
> +STATIC
> +VOID
> +WriteCbReg32 (
> +  IN  UINTN     Bank,
> +  IN  UINT64    Offset,
> +  IN  UINT32    Value
> +  )
> +{
> +  MmioWrite32 (FixedPcdGet64 (PcdPciGenericSmmuBase) + CB_BASE(Bank) + Offset,
> +               Value);
> +}
> +
> +STATIC
> +UINT64
> +ReadCbReg64 (
> +  IN  UINTN     Bank,
> +  IN  UINT64    Offset
> +  )
> +{
> +  return MmioRead64 (FixedPcdGet64 (PcdPciGenericSmmuBase) + CB_BASE(Bank) +
> +                     Offset);
> +}
> +
> +STATIC
> +VOID
> +WriteCbReg64 (
> +  IN  UINTN     Bank,
> +  IN  UINT64    Offset,
> +  IN  UINT64    Value
> +  )
> +{
> +  MmioWrite64 (FixedPcdGet64 (PcdPciGenericSmmuBase) + CB_BASE(Bank) + Offset,
> +               Value);
> +}
> +
> +STATIC
> +VOID
> +EFIAPI
> +ContextInterruptHandler (
> +  IN  HARDWARE_INTERRUPT_SOURCE   Source,
> +  IN  EFI_SYSTEM_CONTEXT          SystemContext
> +  )
> +{
> +  //
> +  // Dump the SMMU context fault registers when taking a context interrupt
> +  //
> +  DEBUG ((DEBUG_WARN,
> +    "Context interrupt asserted by SMMU:\n\n"
> +    "SMMU_CB0_FAR        0x%016llx \n"
> +    "SMMU_CB0_FSR        0x%08llx \n"
> +    "SMMU_CB0_FSYNR0     0x%08llx \n"
> +    "SMMU_CB0_FSYNR1     0x%08llx \n",
> +    ReadCbReg64 (0, CB_FAR),
> +    ReadCbReg32 (0, CB_FSR),
> +    ReadCbReg32 (0, CB_FSYNR0),
> +    ReadCbReg32 (0, CB_FSYNR1)));
> +
> +  mInterrupt->EndOfInterrupt (mInterrupt, Source);
> +}
> +
> +STATIC
> +VOID
> +EFIAPI
> +ExitBootServicesEvent (
> +  IN EFI_EVENT  Event,
> +  IN VOID       *Context
> +  )
> +{
> +  //
> +  // Put the SMMU back into bypass mode
> +  //
> +  MmioOr32 (FixedPcdGet64 (PcdPciGenericSmmuBase) + GL_CR0, GL_CR0_CLIENTPD);
> +}
> +
> +EFI_STATUS
> +GenericSmmuStaticPciDmaDxeInitialize (
> +  IN EFI_HANDLE        ImageHandle,
> +  IN EFI_SYSTEM_TABLE  *SystemTable
> +  )
> +{
> +  EFI_STATUS        Status;
> +  UINTN             Idx;
> +  UINT32            IdVal;
> +  UINTN             NumStreamMappingRegisters;
> +
> +  //
> +  // The static mapping uses 1 GB block mappings, whose VAs and PAs should
> +  // be equal modulo the block size.
> +  //
> +  ASSERT ((DRAM_BASE % SIZE_1GB) == 0);
> +
> +  if ((DRAM_BASE & (DRAM_BASE - 1)) != 0) {
> +    //
> +    // Buggy drivers that use truncated host addresses instead of device
> +    // addresses for DMA may still work correctly if such truncation is
> +    // guaranteed to produce the remapped alias. This is the case if
> +    // DRAM_BASE is a power of 2.
> +    //
> +    DEBUG ((DEBUG_WARN,
> +      "%a: this driver will work better if DRAM_BASE is a power of 2!\n",
> +      __FUNCTION__));
> +  }
> +
> +  Status = gBS->LocateProtocol (&gHardwareInterruptProtocolGuid, NULL,
> +                  (VOID **)&mInterrupt);
> +  ASSERT_EFI_ERROR (Status);
> +
> +  Status = mInterrupt->RegisterInterruptSource (mInterrupt,
> +                         PcdGet16 (PcdPciGenericSmmuContextInterrupt),
> +                         ContextInterruptHandler);
> +  ASSERT_EFI_ERROR (Status);
> +  if (EFI_ERROR (Status)) {
> +    return Status;
> +  }
> +
> +  Status = gBS->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES,
> +                  TPL_NOTIFY, ExitBootServicesEvent, NULL,
> +                  &mEfiExitBootServicesEvent);
> +  ASSERT_EFI_ERROR (Status);
> +
> +  Status = gBS->InstallMultipleProtocolInterfaces (
> +                  &ImageHandle,
> +                  &gEdkiiIoMmuProtocolGuid, &mGenericSmmuIommuProtocol,
> +                  NULL
> +                  );
> +  ASSERT_EFI_ERROR (Status);
> +
> +  IdVal = ReadGlobalReg32 (GL_IDR1);
> +  mContextBankOffset = (IdVal & GL_IDR1_PAGE_SIZE_64KB) ? SIZE_64KB : SIZE_4KB;
> +  mContextBankOffset <<= (1 + ((IdVal >> GL_IDR1_NUMPAGENDXB_SHIFT) &
> +                               GL_IDR1_NUMPAGENDXB_MASK));
> +
> +  //
> +  // Clear all stream mappings
> +  //
> +  NumStreamMappingRegisters = ReadGlobalReg32 (GL_IDR0) & GL_IDR0_NUMSMRG_MASK;
> +  for (Idx = 0; Idx < NumStreamMappingRegisters; Idx++) {
> +    WriteGlobalReg32 (GL_SMR0 + Idx * sizeof(UINT32), 0x0);
> +    WriteGlobalReg32 (GL_S2CR0 + Idx * sizeof(UINT32), 0x0);
> +  }
> +
> +  //
> +  // Set stream match register 0 to match all streams, and map onto
> +  // context bank 0
> +  //
> +  WriteGlobalReg32 (GL_SMR0, 0xffff0000);
> +  WriteGlobalReg32 (GL_S2CR0, 0x0);
> +
> +  //
> +  // Disable the context bank
> +  //
> +  WriteCbReg32 (0, CB_SCTLR_OFFSET, 0);
> +
> +  //
> +  // Assign the translation base register for context bank 0
> +  //
> +  WriteCbReg64 (0, CB_TTBR0_OFFSET, (UINTN)mPciTranslation);
> +
> +  //
> +  // Flush TLBS.
> +  //
> +  WriteGlobalReg32 (GL_STLBIALL, 0);
> +  WriteGlobalReg32 (GL_TLBIALLNSNH, 0);
> +
> +  //
> +  // Configure the size of the translation space, the number of levels,
> +  // and the cacheability attributes of the PTW memory accesses.
> +  //
> +  WriteCbReg32 (0, CB_TTBCR_OFFSET, TCR_T0SZ(40) |
> +                                    TCR_SH_INNER_SHAREABLE |
> +                                    TCR_RGN_INNER_WRITE_BACK_NO_ALLOC |
> +                                    TCR_RGN_OUTER_WRITE_BACK_NO_ALLOC |
> +                                    TCR_SL0_LEVEL1);
> +
> +  //
> +  // Enable the context bank
> +  //
> +  WriteCbReg32 (0, CB_SCTLR_OFFSET, SCTLR_TR_ENABLE |
> +                                    SCTLR_AF_ENABLE |
> +                                    SCTLR_CTX_INT_ENABLE |
> +                                    SCTLR_CTX_FAULT_ENABLE |
> +                                    SCTLR_M_ENABLE);
> +
> +  //
> +  // Get the SMMU out of bypass mode
> +  //
> +  MmioAnd32 (FixedPcdGet64 (PcdPciGenericSmmuBase) + GL_CR0, ~GL_CR0_CLIENTPD);
> +
> +  return EFI_SUCCESS;
> +}
> diff --git a/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.inf b/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.inf
> new file mode 100644
> index 000000000000..02c17e755c4a
> --- /dev/null
> +++ b/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.inf
> @@ -0,0 +1,62 @@
> +## @file
> +#
> +# Copyright (c) 2017, Linaro Ltd. All rights reserved.<BR>
> +#
> +# This program and the accompanying materials
> +# are licensed and made available under the terms and conditions of the BSD License
> +# which accompanies this distribution.  The full text of the license may be found at
> +# http://opensource.org/licenses/bsd-license.php
> +#
> +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
> +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
> +#
> +##
> +
> +[Defines]
> +  INF_VERSION                    = 0x00010019
> +  BASE_NAME                      = GenericSmmuStaticPciDmaDxe
> +  FILE_GUID                      = 59b5e69f-88b7-4632-a2ab-6abe6bdedda2
> +  MODULE_TYPE                    = DXE_DRIVER
> +  VERSION_STRING                 = 1.0
> +  ENTRY_POINT                    = GenericSmmuStaticPciDmaDxeInitialize
> +
> +#
> +# The following information is for reference only and not required by the build tools.
> +#
> +#  VALID_ARCHITECTURES           = ARM AARCH64
> +#
> +#
> +
> +[Sources]
> +  GenericSmmuStaticPciDmaDxe.c
> +  BmDma.c
> +
> +[Packages]
> +  ArmPkg/ArmPkg.dec
> +  EmbeddedPkg/EmbeddedPkg.dec
> +  MdeModulePkg/MdeModulePkg.dec
> +  MdePkg/MdePkg.dec
> +
> +[LibraryClasses]
> +  DebugLib
> +  BaseLib
> +  BaseMemoryLib
> +  IoLib
> +  MemoryAllocationLib
> +  UefiBootServicesTableLib
> +  UefiDriverEntryPoint
> +
> +[Protocols]
> +  gEdkiiIoMmuProtocolGuid                     ## PRODUCES
> +  gEfiPciIoProtocolGuid                       ## CONSUMES
> +  gHardwareInterruptProtocolGuid              ## CONSUMES
> +
> +[Pcd]
> +  gArmTokenSpaceGuid.PcdPciGenericSmmuBase
> +  gArmTokenSpaceGuid.PcdPciGenericSmmuContextInterrupt
> +
> +[FixedPcd]
> +  gArmTokenSpaceGuid.PcdSystemMemoryBase
> +
> +[Depex]
> +  gHardwareInterruptProtocolGuid
> --
> 2.9.3
>
_______________________________________________
edk2-devel mailing list
edk2-devel@lists.01.org
https://lists.01.org/mailman/listinfo/edk2-devel
Re: [edk2] [RFC PATCH] ArmPkg: implement generic SMMU driver to remap DRAM for 32-bit PCI DMA
Posted by Leif Lindholm 6 years, 11 months ago
On Tue, May 02, 2017 at 11:32:02AM +0100, Ard Biesheuvel wrote:
> This implements a driver that uses any SMMU compatible with the generic
> ARM SMMU architecture to remap the lowest 4 GB of DRAM in a way that
> makes it accessible to PCI masters that are only 32-bit DMA capable.
> 
> Note that this driver goes a bit beyond what is strictly necessary to
> support 32-bit DMA, given that it also creates an identity map of the
> lowest 4 GB of DRAM. This is intended for interoperability with external
> drivers that may use the PCI I/O protocol incorrectly (or not at all)
> and program host addresses into the DMA registers and/or rings without
> any regard for translation or address size. If a platform's base of DRAM
> is a power of 2, and if the platform runs UEFI entirely in the lowest
> 4 GB of DRAM, any host address access by a PCI master will hit the ID
> mapped window, and any truncation that may occur will convert the host
> address into the device address.
> 
> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
> ---
> 
> This is based on Jiewen Yao's IOMMU protocol support series, v4.
> 
> https://lists.01.org/pipermail/edk2-devel/2017-April/010330.html
> 
> Tested with AMD Seattle, which has no DRAM below 4 GB.
> 
>  ArmPkg/ArmPkg.dec                                                        |   7 +
>  ArmPkg/ArmPkg.dsc                                                        |   1 +
>  ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/BmDma.c                        | 467 ++++++++++++++++++++
>  ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.c   | 323 ++++++++++++++
>  ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.inf |  62 +++
>  5 files changed, 860 insertions(+)
> 
> diff --git a/ArmPkg/ArmPkg.dec b/ArmPkg/ArmPkg.dec
> index c4b4da2f95bb..96913e3c0713 100644
> --- a/ArmPkg/ArmPkg.dec
> +++ b/ArmPkg/ArmPkg.dec
> @@ -322,3 +322,10 @@ [PcdsFixedAtBuild.common, PcdsDynamic.common]
>    #
>    gArmTokenSpaceGuid.PcdPciBusMin|0x0|UINT32|0x00000059
>    gArmTokenSpaceGuid.PcdPciBusMax|0x0|UINT32|0x0000005A
> +
> +  #
> +  # Base address and context interrupt of the generic SMMU that
> +  # translates memory accesses made by PCI masters
> +  #
> +  gArmTokenSpaceGuid.PcdPciGenericSmmuBase|0x0|UINT64|0x0000005B
> +  gArmTokenSpaceGuid.PcdPciGenericSmmuContextInterrupt|0x0|UINT16|0x0000005C
> diff --git a/ArmPkg/ArmPkg.dsc b/ArmPkg/ArmPkg.dsc
> index 9144334cb821..9bbc71fa2479 100644
> --- a/ArmPkg/ArmPkg.dsc
> +++ b/ArmPkg/ArmPkg.dsc
> @@ -127,6 +127,7 @@ [Components.common]
>    ArmPkg/Drivers/ArmGic/ArmGicDxe.inf
>    ArmPkg/Drivers/ArmGic/ArmGicLib.inf
>    ArmPkg/Drivers/ArmGic/ArmGicSecLib.inf
> +  ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.inf
>    ArmPkg/Drivers/GenericWatchdogDxe/GenericWatchdogDxe.inf
>    ArmPkg/Drivers/TimerDxe/TimerDxe.inf
>  
> diff --git a/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/BmDma.c b/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/BmDma.c
> new file mode 100644
> index 000000000000..629209e335e5
> --- /dev/null
> +++ b/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/BmDma.c
> @@ -0,0 +1,467 @@
> +/** @file
> +  BmDma related function
> +
> +  Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
> +  Copyright (c) 2017, Linaro Ltd. All rights reserved.<BR>
> +
> +  This program and the accompanying materials
> +  are licensed and made available under the terms and conditions of the BSD License
> +  which accompanies this distribution.  The full text of the license may be found at
> +  http://opensource.org/licenses/bsd-license.php
> +
> +  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
> +  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
> +
> +**/
> +
> +#include <PiDxe.h>
> +
> +#include <Protocol/IoMmu.h>
> +
> +#include <Library/BaseLib.h>
> +#include <Library/DebugLib.h>
> +#include <Library/BaseMemoryLib.h>
> +#include <Library/MemoryAllocationLib.h>
> +#include <Library/UefiBootServicesTableLib.h>
> +
> +STATIC CONST UINT64 mTranslationBase = FixedPcdGet64 (PcdSystemMemoryBase);

So, here is the only objection I have to the patch (apart from a
question below). I am _really_ not happy about the ARM-specific
PcdSystemMemoryBase (in general), although it is less evil than
PcdSystemMemorySize.

Maybe I'm missing something, but I don't see anything ARM-specific
about this driver. If PcdSystemMemoryBase is the reason for placing it
under ArmPkg, can we migrate that option across somewhere else?

Seems an added shame to put two new generic config options
(PcdPciGenericSmmuBase/PcdPciGenericSmmuContextInterrupt) under an
architecture-specific directory.

> +
> +#define MAP_INFO_SIGNATURE  SIGNATURE_32 ('D', 'M', 'A', 'P')
> +typedef struct {
> +  UINT32                                    Signature;
> +  LIST_ENTRY                                Link;
> +  EDKII_IOMMU_OPERATION                     Operation;
> +  UINTN                                     NumberOfBytes;
> +  UINTN                                     NumberOfPages;
> +  EFI_PHYSICAL_ADDRESS                      HostAddress;
> +  EFI_PHYSICAL_ADDRESS                      MappedHostAddress;
> +} MAP_INFO;
> +#define MAP_INFO_FROM_LINK(a) CR (a, MAP_INFO, Link, MAP_INFO_SIGNATURE)
> +
> +STATIC LIST_ENTRY                 mMaps = INITIALIZE_LIST_HEAD_VARIABLE(mMaps);
> +
> +/**
> +  Provides the controller-specific addresses required to access system memory from a
> +  DMA bus master.
> +
> +  @param  This                  The protocol instance pointer.
> +  @param  Operation             Indicates if the bus master is going to read or write to system memory.
> +  @param  HostAddress           The system memory address to map to the PCI controller.
> +  @param  NumberOfBytes         On input the number of bytes to map. On output the number of bytes
> +                                that were mapped.
> +  @param  DeviceAddress         The resulting map address for the bus master PCI controller to use to
> +                                access the hosts HostAddress.
> +  @param  Mapping               A resulting value to pass to Unmap().
> +
> +  @retval EFI_SUCCESS           The range was mapped for the returned NumberOfBytes.
> +  @retval EFI_UNSUPPORTED       The HostAddress cannot be mapped as a common buffer.
> +  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
> +  @retval EFI_OUT_OF_RESOURCES  The request could not be completed due to a lack of resources.
> +  @retval EFI_DEVICE_ERROR      The system hardware could not map the requested address.
> +
> +**/
> +STATIC
> +EFI_STATUS
> +EFIAPI
> +IoMmuMap (
> +  IN     EDKII_IOMMU_PROTOCOL                       *This,
> +  IN     EDKII_IOMMU_OPERATION                      Operation,
> +  IN     VOID                                       *HostAddress,
> +  IN OUT UINTN                                      *NumberOfBytes,
> +  OUT    EFI_PHYSICAL_ADDRESS                       *DeviceAddress,
> +  OUT    VOID                                       **Mapping
> +  )
> +{
> +  EFI_STATUS                                        Status;
> +  EFI_PHYSICAL_ADDRESS                              PhysicalAddress;
> +  MAP_INFO                                          *MapInfo;
> +  BOOLEAN                                           NeedRemap;
> +
> +  if (HostAddress == NULL || NumberOfBytes == NULL || DeviceAddress == NULL ||
> +      Mapping == NULL) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  //
> +  // Make sure that Operation is valid
> +  //
> +  if ((UINT32) Operation >= EdkiiIoMmuOperationMaximum) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  NeedRemap = FALSE;
> +  PhysicalAddress = (UINTN)HostAddress;
> +
> +  if ((PhysicalAddress + *NumberOfBytes) > mTranslationBase + SIZE_4GB) {
> +    //
> +    // If the root bridge or the device cannot handle performing DMA above
> +    // 4GB but any part of the DMA transfer being mapped is above 4GB, then
> +    // map the DMA transfer to a buffer below 4GB.
> +    //

Request for clarification: is this because this driver is only invoked
where the root bridge is incapable? (The test looks unconditional.)

/
    Leif

> +    NeedRemap = TRUE;
> +  }
> +
> +  if ((Operation == EdkiiIoMmuOperationBusMasterCommonBuffer ||
> +       Operation == EdkiiIoMmuOperationBusMasterCommonBuffer64) &&
> +      NeedRemap) {
> +    //
> +    // Common Buffer operations can not be remapped.  If the common buffer
> +    // if above 4GB, then it is not possible to generate a mapping, so return
> +    // an error.
> +    //
> +    return EFI_UNSUPPORTED;
> +  }
> +
> +  //
> +  // Allocate a MAP_INFO structure to remember the mapping when Unmap() is
> +  // called later.
> +  //
> +  MapInfo = AllocatePool (sizeof (MAP_INFO));
> +  if (MapInfo == NULL) {
> +    *NumberOfBytes = 0;
> +    return EFI_OUT_OF_RESOURCES;
> +  }
> +
> +  //
> +  // Initialize the MAP_INFO structure
> +  //
> +  MapInfo->Signature         = MAP_INFO_SIGNATURE;
> +  MapInfo->Operation         = Operation;
> +  MapInfo->NumberOfBytes     = *NumberOfBytes;
> +  MapInfo->NumberOfPages     = EFI_SIZE_TO_PAGES (MapInfo->NumberOfBytes);
> +  MapInfo->HostAddress       = PhysicalAddress;
> +  MapInfo->MappedHostAddress = (EFI_PHYSICAL_ADDRESS)-1;
> +
> +  //
> +  // Allocate a buffer below 4GB to map the transfer to.
> +  //
> +  if (NeedRemap) {
> +    MapInfo->MappedHostAddress = mTranslationBase + SIZE_4GB - 1;
> +    Status = gBS->AllocatePages (
> +                    AllocateMaxAddress,
> +                    EfiBootServicesData,
> +                    MapInfo->NumberOfPages,
> +                    &MapInfo->MappedHostAddress
> +                    );
> +    if (EFI_ERROR (Status)) {
> +      FreePool (MapInfo);
> +      *NumberOfBytes = 0;
> +      return Status;
> +    }
> +
> +    //
> +    // If this is a read operation from the Bus Master's point of view,
> +    // then copy the contents of the real buffer into the mapped buffer
> +    // so the Bus Master can read the contents of the real buffer.
> +    //
> +    if (Operation == EdkiiIoMmuOperationBusMasterRead ||
> +        Operation == EdkiiIoMmuOperationBusMasterRead64) {
> +      CopyMem (
> +        (VOID *)(UINTN)MapInfo->MappedHostAddress,
> +        (VOID *)(UINTN)MapInfo->HostAddress,
> +        MapInfo->NumberOfBytes
> +        );
> +    }
> +  } else {
> +    MapInfo->MappedHostAddress = MapInfo->HostAddress;
> +  }
> +
> +  InsertTailList (&mMaps, &MapInfo->Link);
> +
> +  //
> +  // The DeviceAddress is the address of the maped buffer below 4GB
> +  //
> +  *DeviceAddress = MapInfo->MappedHostAddress - mTranslationBase;
> +
> +  //
> +  // Return a pointer to the MAP_INFO structure in Mapping
> +  //
> +  *Mapping       = MapInfo;
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +  Completes the Map() operation and releases any corresponding resources.
> +
> +  @param  This                  The protocol instance pointer.
> +  @param  Mapping               The mapping value returned from Map().
> +
> +  @retval EFI_SUCCESS           The range was unmapped.
> +  @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by Map().
> +  @retval EFI_DEVICE_ERROR      The data was not committed to the target system memory.
> +**/
> +STATIC
> +EFI_STATUS
> +EFIAPI
> +IoMmuUnmap (
> +  IN  EDKII_IOMMU_PROTOCOL                     *This,
> +  IN  VOID                                     *Mapping
> +  )
> +{
> +  MAP_INFO                 *MapInfo;
> +  LIST_ENTRY               *Link;
> +
> +  if (Mapping == NULL) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  MapInfo = NULL;
> +  for (Link = GetFirstNode (&mMaps)
> +       ; !IsNull (&mMaps, Link)
> +       ; Link = GetNextNode (&mMaps, Link)
> +       ) {
> +    MapInfo = MAP_INFO_FROM_LINK (Link);
> +    if (MapInfo == Mapping) {
> +      break;
> +    }
> +  }
> +  //
> +  // Mapping is not a valid value returned by Map()
> +  //
> +  if (MapInfo != Mapping) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +  RemoveEntryList (&MapInfo->Link);
> +
> +  if (MapInfo->MappedHostAddress != MapInfo->HostAddress) {
> +    //
> +    // If this is a write operation from the Bus Master's point of view,
> +    // then copy the contents of the mapped buffer into the real buffer
> +    // so the processor can read the contents of the real buffer.
> +    //
> +    if (MapInfo->Operation == EdkiiIoMmuOperationBusMasterWrite ||
> +        MapInfo->Operation == EdkiiIoMmuOperationBusMasterWrite64) {
> +      CopyMem (
> +        (VOID *)(UINTN)MapInfo->HostAddress,
> +        (VOID *)(UINTN)MapInfo->MappedHostAddress,
> +        MapInfo->NumberOfBytes
> +        );
> +    }
> +
> +    //
> +    // Free the mapped buffer and the MAP_INFO structure.
> +    //
> +    gBS->FreePages (MapInfo->MappedHostAddress, MapInfo->NumberOfPages);
> +  }
> +
> +  FreePool (Mapping);
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +  Allocates pages that are suitable for an OperationBusMasterCommonBuffer or
> +  OperationBusMasterCommonBuffer64 mapping.
> +
> +  @param  This                  The protocol instance pointer.
> +  @param  Type                  This parameter is not used and must be ignored.
> +  @param  MemoryType            The type of memory to allocate, EfiBootServicesData or
> +                                EfiRuntimeServicesData.
> +  @param  Pages                 The number of pages to allocate.
> +  @param  HostAddress           A pointer to store the base system memory address of the
> +                                allocated range.
> +  @param  Attributes            The requested bit mask of attributes for the allocated range.
> +
> +  @retval EFI_SUCCESS           The requested memory pages were allocated.
> +  @retval EFI_UNSUPPORTED       Attributes is unsupported. The only legal attribute bits are
> +                                MEMORY_WRITE_COMBINE and MEMORY_CACHED.
> +  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
> +  @retval EFI_OUT_OF_RESOURCES  The memory pages could not be allocated.
> +
> +**/
> +STATIC
> +EFI_STATUS
> +EFIAPI
> +IoMmuAllocateBuffer (
> +  IN     EDKII_IOMMU_PROTOCOL                     *This,
> +  IN     EFI_ALLOCATE_TYPE                        Type,
> +  IN     EFI_MEMORY_TYPE                          MemoryType,
> +  IN     UINTN                                    Pages,
> +  IN OUT VOID                                     **HostAddress,
> +  IN     UINT64                                   Attributes
> +  )
> +{
> +  EFI_STATUS                Status;
> +  EFI_PHYSICAL_ADDRESS      PhysicalAddress;
> +
> +  //
> +  // Validate Attributes
> +  //
> +  if ((Attributes & EDKII_IOMMU_ATTRIBUTE_INVALID_FOR_ALLOCATE_BUFFER) != 0) {
> +    return EFI_UNSUPPORTED;
> +  }
> +
> +  //
> +  // Check for invalid inputs
> +  //
> +  if (HostAddress == NULL) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  //
> +  // The only valid memory types are EfiBootServicesData and
> +  // EfiRuntimeServicesData
> +  //
> +  if (MemoryType != EfiBootServicesData &&
> +      MemoryType != EfiRuntimeServicesData) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  //
> +  // Limit allocations to memory covered by the remapped window.
> +  //
> +  PhysicalAddress = mTranslationBase + SIZE_4GB - 1;
> +  Status = gBS->AllocatePages (
> +                  AllocateMaxAddress,
> +                  MemoryType,
> +                  Pages,
> +                  &PhysicalAddress
> +                  );
> +  if (!EFI_ERROR (Status)) {
> +    *HostAddress = (VOID *)(UINTN)PhysicalAddress;
> +  }
> +
> +  return Status;
> +}
> +
> +/**
> +  Frees memory that was allocated with AllocateBuffer().
> +
> +  @param  This                  The protocol instance pointer.
> +  @param  Pages                 The number of pages to free.
> +  @param  HostAddress           The base system memory address of the allocated range.
> +
> +  @retval EFI_SUCCESS           The requested memory pages were freed.
> +  @retval EFI_INVALID_PARAMETER The memory range specified by HostAddress and Pages
> +                                was not allocated with AllocateBuffer().
> +
> +**/
> +STATIC
> +EFI_STATUS
> +EFIAPI
> +IoMmuFreeBuffer (
> +  IN  EDKII_IOMMU_PROTOCOL                     *This,
> +  IN  UINTN                                    Pages,
> +  IN  VOID                                     *HostAddress
> +  )
> +{
> +  return gBS->FreePages ((EFI_PHYSICAL_ADDRESS) (UINTN) HostAddress, Pages);
> +}
> +
> +/**
> +  Set IOMMU attribute for a system memory.
> +
> +  If the IOMMU protocol exists, the system memory cannot be used
> +  for DMA by default.
> +
> +  When a device requests a DMA access for a system memory,
> +  the device driver need use SetAttribute() to update the IOMMU
> +  attribute to request DMA access (read and/or write).
> +
> +  The DeviceHandle is used to identify which device submits the request.
> +  The IOMMU implementation need translate the device path to an IOMMU device ID,
> +  and set IOMMU hardware register accordingly.
> +  1) DeviceHandle can be a standard PCI device.
> +     The memory for BusMasterRead need set EDKII_IOMMU_ACCESS_READ.
> +     The memory for BusMasterWrite need set EDKII_IOMMU_ACCESS_WRITE.
> +     The memory for BusMasterCommonBuffer need set EDKII_IOMMU_ACCESS_READ|EDKII_IOMMU_ACCESS_WRITE.
> +     After the memory is used, the memory need set 0 to keep it being protected.
> +  2) DeviceHandle can be an ACPI device (ISA, I2C, SPI, etc).
> +     The memory for DMA access need set EDKII_IOMMU_ACCESS_READ and/or EDKII_IOMMU_ACCESS_WRITE.
> +
> +  @param[in]  This              The protocol instance pointer.
> +  @param[in]  DeviceHandle      The device who initiates the DMA access request.
> +  @param[in]  DeviceAddress     The base of device memory address to be used as the DMA memory.
> +  @param[in]  Length            The length of device memory address to be used as the DMA memory.
> +  @param[in]  IoMmuAccess       The IOMMU access.
> +
> +  @retval EFI_SUCCESS            The IoMmuAccess is set for the memory range specified by DeviceAddress and Length.
> +  @retval EFI_INVALID_PARAMETER  DeviceHandle is an invalid handle.
> +  @retval EFI_INVALID_PARAMETER  DeviceAddress is not IoMmu Page size aligned.
> +  @retval EFI_INVALID_PARAMETER  Length is not IoMmu Page size aligned.
> +  @retval EFI_INVALID_PARAMETER  Length is 0.
> +  @retval EFI_INVALID_PARAMETER  IoMmuAccess specified an illegal combination of access.
> +  @retval EFI_UNSUPPORTED        DeviceHandle is unknown by the IOMMU.
> +  @retval EFI_UNSUPPORTED        The bit mask of IoMmuAccess is not supported by the IOMMU.
> +  @retval EFI_UNSUPPORTED        The IOMMU does not support the memory range specified by DeviceAddress and Length.
> +  @retval EFI_OUT_OF_RESOURCES   There are not enough resources available to modify the IOMMU access.
> +  @retval EFI_DEVICE_ERROR       The IOMMU device reported an error while attempting the operation.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +IoMmuSetAttribute (
> +  IN EDKII_IOMMU_PROTOCOL  *This,
> +  IN EFI_HANDLE            DeviceHandle,
> +  IN EFI_PHYSICAL_ADDRESS  DeviceAddress,
> +  IN UINT64                Length,
> +  IN UINT64                IoMmuAccess
> +  )
> +{
> +  return EFI_UNSUPPORTED;
> +}
> +
> +/**
> +  Set IOMMU attribute for a system memory.
> +
> +  If the IOMMU protocol exists, the system memory cannot be used
> +  for DMA by default.
> +
> +  When a device requests a DMA access for a system memory,
> +  the device driver need use SetAttribute() to update the IOMMU
> +  attribute to request DMA access (read and/or write).
> +
> +  The DeviceHandle is used to identify which device submits the request.
> +  The IOMMU implementation need translate the device path to an IOMMU device ID,
> +  and set IOMMU hardware register accordingly.
> +  1) DeviceHandle can be a standard PCI device.
> +     The memory for BusMasterRead need set EDKII_IOMMU_ACCESS_READ.
> +     The memory for BusMasterWrite need set EDKII_IOMMU_ACCESS_WRITE.
> +     The memory for BusMasterCommonBuffer need set EDKII_IOMMU_ACCESS_READ|EDKII_IOMMU_ACCESS_WRITE.
> +     After the memory is used, the memory need set 0 to keep it being protected.
> +  2) DeviceHandle can be an ACPI device (ISA, I2C, SPI, etc).
> +     The memory for DMA access need set EDKII_IOMMU_ACCESS_READ and/or EDKII_IOMMU_ACCESS_WRITE.
> +
> +  @param[in]  This              The protocol instance pointer.
> +  @param[in]  DeviceHandle      The device who initiates the DMA access request.
> +  @param[in]  Mapping           The mapping value returned from Map().
> +  @param[in]  IoMmuAccess       The IOMMU access.
> +
> +  @retval EFI_SUCCESS            The IoMmuAccess is set for the memory range specified by DeviceAddress and Length.
> +  @retval EFI_INVALID_PARAMETER  DeviceHandle is an invalid handle.
> +  @retval EFI_INVALID_PARAMETER  Mapping is not a value that was returned by Map().
> +  @retval EFI_INVALID_PARAMETER  IoMmuAccess specified an illegal combination of access.
> +  @retval EFI_UNSUPPORTED        DeviceHandle is unknown by the IOMMU.
> +  @retval EFI_UNSUPPORTED        The bit mask of IoMmuAccess is not supported by the IOMMU.
> +  @retval EFI_UNSUPPORTED        The IOMMU does not support the memory range specified by Mapping.
> +  @retval EFI_OUT_OF_RESOURCES   There are not enough resources available to modify the IOMMU access.
> +  @retval EFI_DEVICE_ERROR       The IOMMU device reported an error while attempting the operation.
> +
> +**/
> +STATIC
> +EFI_STATUS
> +EFIAPI
> +IoMmuSetMappingAttribute (
> +  IN EDKII_IOMMU_PROTOCOL  *This,
> +  IN EFI_HANDLE            DeviceHandle,
> +  IN VOID                  *Mapping,
> +  IN UINT64                IoMmuAccess
> +  )
> +{
> +  //
> +  // We only support a static remapping of DRAM into the PCI address space
> +  // so there is nothing we need to do to handle invocations of this protocol
> +  // method.
> +  //
> +  return EFI_SUCCESS;
> +}
> +
> +EDKII_IOMMU_PROTOCOL  mGenericSmmuIommuProtocol = {
> +  EDKII_IOMMU_PROTOCOL_REVISION,
> +  IoMmuSetAttribute,
> +  IoMmuMap,
> +  IoMmuUnmap,
> +  IoMmuAllocateBuffer,
> +  IoMmuFreeBuffer,
> +  IoMmuSetMappingAttribute,
> +};
> diff --git a/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.c b/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.c
> new file mode 100644
> index 000000000000..8f5093af14ea
> --- /dev/null
> +++ b/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.c
> @@ -0,0 +1,323 @@
> +/** @file
> +
> +  Copyright (c) 2017, Linaro Ltd. All rights reserved.<BR>
> +
> +  This program and the accompanying materials
> +  are licensed and made available under the terms and conditions of the BSD License
> +  which accompanies this distribution.  The full text of the license may be found at
> +  http://opensource.org/licenses/bsd-license.php
> +
> +  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
> +  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
> +
> +**/
> +
> +#include <PiDxe.h>
> +#include <Library/BaseLib.h>
> +#include <Library/DebugLib.h>
> +#include <Library/IoLib.h>
> +#include <Library/UefiBootServicesTableLib.h>
> +
> +#include <Protocol/Cpu.h>
> +#include <Protocol/HardwareInterrupt.h>
> +#include <Protocol/IoMmu.h>
> +
> +#include <Chipset/AArch64Mmu.h>
> +
> +#define GL_CR0                          0x0
> +#define GL_CR0_CLIENTPD                 BIT0
> +
> +#define GL_IDR0                         0x20
> +#define GL_IDR1                         0x24
> +#define GL_STLBIALL                     0x60
> +#define GL_TLBIALLNSNH                  0x68
> +#define GL_SMR0                         0x800
> +#define GL_S2CR0                        0xc00
> +#define GL_CBA2R0                       0x1800
> +
> +#define GL_IDR0_NUMSMRG_MASK            0xff
> +
> +#define GL_IDR1_NUMPAGENDXB_MASK        0x7
> +#define GL_IDR1_NUMPAGENDXB_SHIFT       28
> +#define GL_IDR1_PAGE_SIZE_64KB          BIT31
> +
> +#define CB_BASE(i)                      (mContextBankOffset + ((i) * SIZE_4KB))
> +
> +#define CB_SCTLR_OFFSET                 0x0
> +#define CB_TTBR0_OFFSET                 0x20
> +#define CB_TTBCR_OFFSET                 0x30
> +
> +#define CB_FAR                          0x60
> +#define CB_FSR                          0x58
> +#define CB_FSYNR0                       0x68
> +#define CB_FSYNR1                       0x6c
> +
> +#define TT_S2_MEMATTR_CACHED            (0xF << 2)
> +#define TT_S2_AP_READ_WRITE             (0x3 << 6)
> +
> +#define TT_ENTRY_ATTRIBUTES             (TT_TYPE_BLOCK_ENTRY | \
> +                                         TT_SH_INNER_SHAREABLE | \
> +                                         TT_S2_AP_READ_WRITE | \
> +                                         TT_S2_MEMATTR_CACHED | \
> +                                         TT_AF)
> +
> +#define TCR_T0SZ(bits)                  ((UINT32)(32 - (bits)) & 0x3f)
> +#define TCR_SL0_LEVEL1                  BIT6
> +#define TCR_SL0_LEVEL2                  0
> +
> +#define SCTLR_M_ENABLE                  BIT0
> +#define SCTLR_TR_ENABLE                 BIT1
> +#define SCTLR_AF_ENABLE                 BIT2
> +#define SCTLR_CTX_FAULT_ENABLE          BIT5
> +#define SCTLR_CTX_INT_ENABLE            BIT6
> +
> +#define DRAM_BASE                       FixedPcdGet64 (PcdSystemMemoryBase)
> +
> +extern EDKII_IOMMU_PROTOCOL             mGenericSmmuIommuProtocol;
> +
> +//
> +// Create a static stage 2 mapping of the first 4 GB of DRAM in the start
> +// of the IOVA space, and as an ID mapping at the original offset.
> +//
> +STATIC CONST UINT64 mPciTranslation[1024] __attribute__((aligned(SIZE_8KB))) = {
> +  [0] = TT_ENTRY_ATTRIBUTES | (DRAM_BASE),
> +  [1] = TT_ENTRY_ATTRIBUTES | (DRAM_BASE + SIZE_1GB),
> +  [2] = TT_ENTRY_ATTRIBUTES | (DRAM_BASE + SIZE_1GB * 2UL),
> +  [3] = TT_ENTRY_ATTRIBUTES | (DRAM_BASE + SIZE_1GB * 3UL),
> +
> +  //
> +  // The ID mapping of the first 4 GB of DRAM is a workaround for buggy
> +  // drivers that violate the UEFI spec by ignoring the device address
> +  // returned by the PCI I/O map/unmap routines, and program host
> +  // addresses into the DMA h/w registers or rings instead.
> +  //
> +  [(DRAM_BASE >> 30)]     = TT_ENTRY_ATTRIBUTES | (DRAM_BASE),
> +  [(DRAM_BASE >> 30) + 1] = TT_ENTRY_ATTRIBUTES | (DRAM_BASE + SIZE_1GB),
> +  [(DRAM_BASE >> 30) + 2] = TT_ENTRY_ATTRIBUTES | (DRAM_BASE + SIZE_1GB * 2UL),
> +  [(DRAM_BASE >> 30) + 3] = TT_ENTRY_ATTRIBUTES | (DRAM_BASE + SIZE_1GB * 3UL),
> +};
> +
> +STATIC EFI_HARDWARE_INTERRUPT_PROTOCOL    *mInterrupt;
> +STATIC EFI_EVENT                          mEfiExitBootServicesEvent;
> +STATIC UINTN                              mContextBankOffset;
> +
> +STATIC
> +UINT32
> +ReadGlobalReg32 (
> +  IN  UINT64    Offset
> +  )
> +{
> +  return MmioRead32 (FixedPcdGet64 (PcdPciGenericSmmuBase) + Offset);
> +}
> +
> +STATIC
> +VOID
> +WriteGlobalReg32 (
> +  IN  UINT64    Offset,
> +  IN  UINT64    Value
> +  )
> +{
> +  MmioWrite32 (FixedPcdGet64 (PcdPciGenericSmmuBase) + Offset, Value);
> +}
> +
> +STATIC
> +UINT32
> +ReadCbReg32 (
> +  IN  UINTN     Bank,
> +  IN  UINT64    Offset
> +  )
> +{
> +  return MmioRead32 (FixedPcdGet64 (PcdPciGenericSmmuBase) + CB_BASE(Bank) +
> +                     Offset);
> +}
> +
> +STATIC
> +VOID
> +WriteCbReg32 (
> +  IN  UINTN     Bank,
> +  IN  UINT64    Offset,
> +  IN  UINT32    Value
> +  )
> +{
> +  MmioWrite32 (FixedPcdGet64 (PcdPciGenericSmmuBase) + CB_BASE(Bank) + Offset,
> +               Value);
> +}
> +
> +STATIC
> +UINT64
> +ReadCbReg64 (
> +  IN  UINTN     Bank,
> +  IN  UINT64    Offset
> +  )
> +{
> +  return MmioRead64 (FixedPcdGet64 (PcdPciGenericSmmuBase) + CB_BASE(Bank) +
> +                     Offset);
> +}
> +
> +STATIC
> +VOID
> +WriteCbReg64 (
> +  IN  UINTN     Bank,
> +  IN  UINT64    Offset,
> +  IN  UINT64    Value
> +  )
> +{
> +  MmioWrite64 (FixedPcdGet64 (PcdPciGenericSmmuBase) + CB_BASE(Bank) + Offset,
> +               Value);
> +}
> +
> +STATIC
> +VOID
> +EFIAPI
> +ContextInterruptHandler (
> +  IN  HARDWARE_INTERRUPT_SOURCE   Source,
> +  IN  EFI_SYSTEM_CONTEXT          SystemContext
> +  )
> +{
> +  //
> +  // Dump the SMMU context fault registers when taking a context interrupt
> +  //
> +  DEBUG ((DEBUG_WARN,
> +    "Context interrupt asserted by SMMU:\n\n"
> +    "SMMU_CB0_FAR        0x%016llx \n"
> +    "SMMU_CB0_FSR        0x%08llx \n"
> +    "SMMU_CB0_FSYNR0     0x%08llx \n"
> +    "SMMU_CB0_FSYNR1     0x%08llx \n",
> +    ReadCbReg64 (0, CB_FAR),
> +    ReadCbReg32 (0, CB_FSR),
> +    ReadCbReg32 (0, CB_FSYNR0),
> +    ReadCbReg32 (0, CB_FSYNR1)));
> +
> +  mInterrupt->EndOfInterrupt (mInterrupt, Source);
> +}
> +
> +STATIC
> +VOID
> +EFIAPI
> +ExitBootServicesEvent (
> +  IN EFI_EVENT  Event,
> +  IN VOID       *Context
> +  )
> +{
> +  //
> +  // Put the SMMU back into bypass mode
> +  //
> +  MmioOr32 (FixedPcdGet64 (PcdPciGenericSmmuBase) + GL_CR0, GL_CR0_CLIENTPD);
> +}
> +
> +EFI_STATUS
> +GenericSmmuStaticPciDmaDxeInitialize (
> +  IN EFI_HANDLE        ImageHandle,
> +  IN EFI_SYSTEM_TABLE  *SystemTable
> +  )
> +{
> +  EFI_STATUS        Status;
> +  UINTN             Idx;
> +  UINT32            IdVal;
> +  UINTN             NumStreamMappingRegisters;
> +
> +  //
> +  // The static mapping uses 1 GB block mappings, whose VAs and PAs should
> +  // be equal modulo the block size.
> +  //
> +  ASSERT ((DRAM_BASE % SIZE_1GB) == 0);
> +
> +  if ((DRAM_BASE & (DRAM_BASE - 1)) != 0) {
> +    //
> +    // Buggy drivers that use truncated host addresses instead of device
> +    // addresses for DMA may still work correctly if such truncation is
> +    // guaranteed to produce the remapped alias. This is the case if
> +    // DRAM_BASE is a power of 2.
> +    //
> +    DEBUG ((DEBUG_WARN,
> +      "%a: this driver will work better if DRAM_BASE is a power of 2!\n",
> +      __FUNCTION__));
> +  }
> +
> +  Status = gBS->LocateProtocol (&gHardwareInterruptProtocolGuid, NULL,
> +                  (VOID **)&mInterrupt);
> +  ASSERT_EFI_ERROR (Status);
> +
> +  Status = mInterrupt->RegisterInterruptSource (mInterrupt,
> +                         PcdGet16 (PcdPciGenericSmmuContextInterrupt),
> +                         ContextInterruptHandler);
> +  ASSERT_EFI_ERROR (Status);
> +  if (EFI_ERROR (Status)) {
> +    return Status;
> +  }
> +
> +  Status = gBS->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES,
> +                  TPL_NOTIFY, ExitBootServicesEvent, NULL,
> +                  &mEfiExitBootServicesEvent);
> +  ASSERT_EFI_ERROR (Status);
> +
> +  Status = gBS->InstallMultipleProtocolInterfaces (
> +                  &ImageHandle,
> +                  &gEdkiiIoMmuProtocolGuid, &mGenericSmmuIommuProtocol,
> +                  NULL
> +                  );
> +  ASSERT_EFI_ERROR (Status);
> +
> +  IdVal = ReadGlobalReg32 (GL_IDR1);
> +  mContextBankOffset = (IdVal & GL_IDR1_PAGE_SIZE_64KB) ? SIZE_64KB : SIZE_4KB;
> +  mContextBankOffset <<= (1 + ((IdVal >> GL_IDR1_NUMPAGENDXB_SHIFT) &
> +                               GL_IDR1_NUMPAGENDXB_MASK));
> +
> +  //
> +  // Clear all stream mappings
> +  //
> +  NumStreamMappingRegisters = ReadGlobalReg32 (GL_IDR0) & GL_IDR0_NUMSMRG_MASK;
> +  for (Idx = 0; Idx < NumStreamMappingRegisters; Idx++) {
> +    WriteGlobalReg32 (GL_SMR0 + Idx * sizeof(UINT32), 0x0);
> +    WriteGlobalReg32 (GL_S2CR0 + Idx * sizeof(UINT32), 0x0);
> +  }
> +
> +  //
> +  // Set stream match register 0 to match all streams, and map onto
> +  // context bank 0
> +  //
> +  WriteGlobalReg32 (GL_SMR0, 0xffff0000);
> +  WriteGlobalReg32 (GL_S2CR0, 0x0);
> +
> +  //
> +  // Disable the context bank
> +  //
> +  WriteCbReg32 (0, CB_SCTLR_OFFSET, 0);
> +
> +  //
> +  // Assign the translation base register for context bank 0
> +  //
> +  WriteCbReg64 (0, CB_TTBR0_OFFSET, (UINTN)mPciTranslation);
> +
> +  //
> +  // Flush TLBS.
> +  //
> +  WriteGlobalReg32 (GL_STLBIALL, 0);
> +  WriteGlobalReg32 (GL_TLBIALLNSNH, 0);
> +
> +  //
> +  // Configure the size of the translation space, the number of levels,
> +  // and the cacheability attributes of the PTW memory accesses.
> +  //
> +  WriteCbReg32 (0, CB_TTBCR_OFFSET, TCR_T0SZ(40) |
> +                                    TCR_SH_INNER_SHAREABLE |
> +                                    TCR_RGN_INNER_WRITE_BACK_NO_ALLOC |
> +                                    TCR_RGN_OUTER_WRITE_BACK_NO_ALLOC |
> +                                    TCR_SL0_LEVEL1);
> +
> +  //
> +  // Enable the context bank
> +  //
> +  WriteCbReg32 (0, CB_SCTLR_OFFSET, SCTLR_TR_ENABLE |
> +                                    SCTLR_AF_ENABLE |
> +                                    SCTLR_CTX_INT_ENABLE |
> +                                    SCTLR_CTX_FAULT_ENABLE |
> +                                    SCTLR_M_ENABLE);
> +
> +  //
> +  // Get the SMMU out of bypass mode
> +  //
> +  MmioAnd32 (FixedPcdGet64 (PcdPciGenericSmmuBase) + GL_CR0, ~GL_CR0_CLIENTPD);
> +
> +  return EFI_SUCCESS;
> +}
> diff --git a/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.inf b/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.inf
> new file mode 100644
> index 000000000000..02c17e755c4a
> --- /dev/null
> +++ b/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.inf
> @@ -0,0 +1,62 @@
> +## @file
> +#
> +# Copyright (c) 2017, Linaro Ltd. All rights reserved.<BR>
> +#
> +# This program and the accompanying materials
> +# are licensed and made available under the terms and conditions of the BSD License
> +# which accompanies this distribution.  The full text of the license may be found at
> +# http://opensource.org/licenses/bsd-license.php
> +#
> +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
> +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
> +#
> +##
> +
> +[Defines]
> +  INF_VERSION                    = 0x00010019
> +  BASE_NAME                      = GenericSmmuStaticPciDmaDxe
> +  FILE_GUID                      = 59b5e69f-88b7-4632-a2ab-6abe6bdedda2
> +  MODULE_TYPE                    = DXE_DRIVER
> +  VERSION_STRING                 = 1.0
> +  ENTRY_POINT                    = GenericSmmuStaticPciDmaDxeInitialize
> +
> +#
> +# The following information is for reference only and not required by the build tools.
> +#
> +#  VALID_ARCHITECTURES           = ARM AARCH64
> +#
> +#
> +
> +[Sources]
> +  GenericSmmuStaticPciDmaDxe.c
> +  BmDma.c
> +
> +[Packages]
> +  ArmPkg/ArmPkg.dec
> +  EmbeddedPkg/EmbeddedPkg.dec
> +  MdeModulePkg/MdeModulePkg.dec
> +  MdePkg/MdePkg.dec
> +
> +[LibraryClasses]
> +  DebugLib
> +  BaseLib
> +  BaseMemoryLib
> +  IoLib
> +  MemoryAllocationLib
> +  UefiBootServicesTableLib
> +  UefiDriverEntryPoint
> +
> +[Protocols]
> +  gEdkiiIoMmuProtocolGuid                     ## PRODUCES
> +  gEfiPciIoProtocolGuid                       ## CONSUMES
> +  gHardwareInterruptProtocolGuid              ## CONSUMES
> +
> +[Pcd]
> +  gArmTokenSpaceGuid.PcdPciGenericSmmuBase
> +  gArmTokenSpaceGuid.PcdPciGenericSmmuContextInterrupt
> +
> +[FixedPcd]
> +  gArmTokenSpaceGuid.PcdSystemMemoryBase
> +
> +[Depex]
> +  gHardwareInterruptProtocolGuid
> -- 
> 2.9.3
> 
_______________________________________________
edk2-devel mailing list
edk2-devel@lists.01.org
https://lists.01.org/mailman/listinfo/edk2-devel
Re: [edk2] [RFC PATCH] ArmPkg: implement generic SMMU driver to remap DRAM for 32-bit PCI DMA
Posted by Ard Biesheuvel 6 years, 11 months ago
On 22 May 2017 at 06:49, Leif Lindholm <leif.lindholm@linaro.org> wrote:
> On Tue, May 02, 2017 at 11:32:02AM +0100, Ard Biesheuvel wrote:
>> This implements a driver that uses any SMMU compatible with the generic
>> ARM SMMU architecture to remap the lowest 4 GB of DRAM in a way that
>> makes it accessible to PCI masters that are only 32-bit DMA capable.
>>
>> Note that this driver goes a bit beyond what is strictly necessary to
>> support 32-bit DMA, given that it also creates an identity map of the
>> lowest 4 GB of DRAM. This is intended for interoperability with external
>> drivers that may use the PCI I/O protocol incorrectly (or not at all)
>> and program host addresses into the DMA registers and/or rings without
>> any regard for translation or address size. If a platform's base of DRAM
>> is a power of 2, and if the platform runs UEFI entirely in the lowest
>> 4 GB of DRAM, any host address access by a PCI master will hit the ID
>> mapped window, and any truncation that may occur will convert the host
>> address into the device address.
>>
>> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
>> ---
>>
>> This is based on Jiewen Yao's IOMMU protocol support series, v4.
>>
>> https://lists.01.org/pipermail/edk2-devel/2017-April/010330.html
>>
>> Tested with AMD Seattle, which has no DRAM below 4 GB.
>>
>>  ArmPkg/ArmPkg.dec                                                        |   7 +
>>  ArmPkg/ArmPkg.dsc                                                        |   1 +
>>  ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/BmDma.c                        | 467 ++++++++++++++++++++
>>  ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.c   | 323 ++++++++++++++
>>  ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.inf |  62 +++
>>  5 files changed, 860 insertions(+)
>>
>> diff --git a/ArmPkg/ArmPkg.dec b/ArmPkg/ArmPkg.dec
>> index c4b4da2f95bb..96913e3c0713 100644
>> --- a/ArmPkg/ArmPkg.dec
>> +++ b/ArmPkg/ArmPkg.dec
>> @@ -322,3 +322,10 @@ [PcdsFixedAtBuild.common, PcdsDynamic.common]
>>    #
>>    gArmTokenSpaceGuid.PcdPciBusMin|0x0|UINT32|0x00000059
>>    gArmTokenSpaceGuid.PcdPciBusMax|0x0|UINT32|0x0000005A
>> +
>> +  #
>> +  # Base address and context interrupt of the generic SMMU that
>> +  # translates memory accesses made by PCI masters
>> +  #
>> +  gArmTokenSpaceGuid.PcdPciGenericSmmuBase|0x0|UINT64|0x0000005B
>> +  gArmTokenSpaceGuid.PcdPciGenericSmmuContextInterrupt|0x0|UINT16|0x0000005C
>> diff --git a/ArmPkg/ArmPkg.dsc b/ArmPkg/ArmPkg.dsc
>> index 9144334cb821..9bbc71fa2479 100644
>> --- a/ArmPkg/ArmPkg.dsc
>> +++ b/ArmPkg/ArmPkg.dsc
>> @@ -127,6 +127,7 @@ [Components.common]
>>    ArmPkg/Drivers/ArmGic/ArmGicDxe.inf
>>    ArmPkg/Drivers/ArmGic/ArmGicLib.inf
>>    ArmPkg/Drivers/ArmGic/ArmGicSecLib.inf
>> +  ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.inf
>>    ArmPkg/Drivers/GenericWatchdogDxe/GenericWatchdogDxe.inf
>>    ArmPkg/Drivers/TimerDxe/TimerDxe.inf
>>
>> diff --git a/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/BmDma.c b/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/BmDma.c
>> new file mode 100644
>> index 000000000000..629209e335e5
>> --- /dev/null
>> +++ b/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/BmDma.c
>> @@ -0,0 +1,467 @@
>> +/** @file
>> +  BmDma related function
>> +
>> +  Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
>> +  Copyright (c) 2017, Linaro Ltd. All rights reserved.<BR>
>> +
>> +  This program and the accompanying materials
>> +  are licensed and made available under the terms and conditions of the BSD License
>> +  which accompanies this distribution.  The full text of the license may be found at
>> +  http://opensource.org/licenses/bsd-license.php
>> +
>> +  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
>> +  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
>> +
>> +**/
>> +
>> +#include <PiDxe.h>
>> +
>> +#include <Protocol/IoMmu.h>
>> +
>> +#include <Library/BaseLib.h>
>> +#include <Library/DebugLib.h>
>> +#include <Library/BaseMemoryLib.h>
>> +#include <Library/MemoryAllocationLib.h>
>> +#include <Library/UefiBootServicesTableLib.h>
>> +
>> +STATIC CONST UINT64 mTranslationBase = FixedPcdGet64 (PcdSystemMemoryBase);
>
> So, here is the only objection I have to the patch (apart from a
> question below). I am _really_ not happy about the ARM-specific
> PcdSystemMemoryBase (in general), although it is less evil than
> PcdSystemMemorySize.
>
> Maybe I'm missing something, but I don't see anything ARM-specific
> about this driver. If PcdSystemMemoryBase is the reason for placing it
> under ArmPkg, can we migrate that option across somewhere else?
>

Of course it is ARM specific. SMMUs only exist on ARM systems, and the
driver reuses ArmMmuLib defines to populate the page table entries.
In general, I agree with the tendency to keep code out of ArmPkg, but
in this case, I think it really belongs there.

> Seems an added shame to put two new generic config options
> (PcdPciGenericSmmuBase/PcdPciGenericSmmuContextInterrupt) under an
> architecture-specific directory.
>
>> +
>> +#define MAP_INFO_SIGNATURE  SIGNATURE_32 ('D', 'M', 'A', 'P')
>> +typedef struct {
>> +  UINT32                                    Signature;
>> +  LIST_ENTRY                                Link;
>> +  EDKII_IOMMU_OPERATION                     Operation;
>> +  UINTN                                     NumberOfBytes;
>> +  UINTN                                     NumberOfPages;
>> +  EFI_PHYSICAL_ADDRESS                      HostAddress;
>> +  EFI_PHYSICAL_ADDRESS                      MappedHostAddress;
>> +} MAP_INFO;
>> +#define MAP_INFO_FROM_LINK(a) CR (a, MAP_INFO, Link, MAP_INFO_SIGNATURE)
>> +
>> +STATIC LIST_ENTRY                 mMaps = INITIALIZE_LIST_HEAD_VARIABLE(mMaps);
>> +
>> +/**
>> +  Provides the controller-specific addresses required to access system memory from a
>> +  DMA bus master.
>> +
>> +  @param  This                  The protocol instance pointer.
>> +  @param  Operation             Indicates if the bus master is going to read or write to system memory.
>> +  @param  HostAddress           The system memory address to map to the PCI controller.
>> +  @param  NumberOfBytes         On input the number of bytes to map. On output the number of bytes
>> +                                that were mapped.
>> +  @param  DeviceAddress         The resulting map address for the bus master PCI controller to use to
>> +                                access the hosts HostAddress.
>> +  @param  Mapping               A resulting value to pass to Unmap().
>> +
>> +  @retval EFI_SUCCESS           The range was mapped for the returned NumberOfBytes.
>> +  @retval EFI_UNSUPPORTED       The HostAddress cannot be mapped as a common buffer.
>> +  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
>> +  @retval EFI_OUT_OF_RESOURCES  The request could not be completed due to a lack of resources.
>> +  @retval EFI_DEVICE_ERROR      The system hardware could not map the requested address.
>> +
>> +**/
>> +STATIC
>> +EFI_STATUS
>> +EFIAPI
>> +IoMmuMap (
>> +  IN     EDKII_IOMMU_PROTOCOL                       *This,
>> +  IN     EDKII_IOMMU_OPERATION                      Operation,
>> +  IN     VOID                                       *HostAddress,
>> +  IN OUT UINTN                                      *NumberOfBytes,
>> +  OUT    EFI_PHYSICAL_ADDRESS                       *DeviceAddress,
>> +  OUT    VOID                                       **Mapping
>> +  )
>> +{
>> +  EFI_STATUS                                        Status;
>> +  EFI_PHYSICAL_ADDRESS                              PhysicalAddress;
>> +  MAP_INFO                                          *MapInfo;
>> +  BOOLEAN                                           NeedRemap;
>> +
>> +  if (HostAddress == NULL || NumberOfBytes == NULL || DeviceAddress == NULL ||
>> +      Mapping == NULL) {
>> +    return EFI_INVALID_PARAMETER;
>> +  }
>> +
>> +  //
>> +  // Make sure that Operation is valid
>> +  //
>> +  if ((UINT32) Operation >= EdkiiIoMmuOperationMaximum) {
>> +    return EFI_INVALID_PARAMETER;
>> +  }
>> +
>> +  NeedRemap = FALSE;
>> +  PhysicalAddress = (UINTN)HostAddress;
>> +
>> +  if ((PhysicalAddress + *NumberOfBytes) > mTranslationBase + SIZE_4GB) {
>> +    //
>> +    // If the root bridge or the device cannot handle performing DMA above
>> +    // 4GB but any part of the DMA transfer being mapped is above 4GB, then
>> +    // map the DMA transfer to a buffer below 4GB.
>> +    //
>
> Request for clarification: is this because this driver is only invoked
> where the root bridge is incapable? (The test looks unconditional.)
>

No, the root bridge driver always invokes the IOMMU protocol if there is one.

Here we test whether the mapped buffer already lives in the first 4 GB
of DRAM, otherwise we will use a bounce buffer. This driver never maps
anything dynamically in the SMMU, it just allocates a bounce buffer
from the 32-bit addressable range if required.

>> +    NeedRemap = TRUE;
>> +  }
>> +
>> +  if ((Operation == EdkiiIoMmuOperationBusMasterCommonBuffer ||
>> +       Operation == EdkiiIoMmuOperationBusMasterCommonBuffer64) &&
>> +      NeedRemap) {
>> +    //
>> +    // Common Buffer operations can not be remapped.  If the common buffer
>> +    // if above 4GB, then it is not possible to generate a mapping, so return
>> +    // an error.
>> +    //
>> +    return EFI_UNSUPPORTED;
>> +  }
>> +
>> +  //
>> +  // Allocate a MAP_INFO structure to remember the mapping when Unmap() is
>> +  // called later.
>> +  //
>> +  MapInfo = AllocatePool (sizeof (MAP_INFO));
>> +  if (MapInfo == NULL) {
>> +    *NumberOfBytes = 0;
>> +    return EFI_OUT_OF_RESOURCES;
>> +  }
>> +
>> +  //
>> +  // Initialize the MAP_INFO structure
>> +  //
>> +  MapInfo->Signature         = MAP_INFO_SIGNATURE;
>> +  MapInfo->Operation         = Operation;
>> +  MapInfo->NumberOfBytes     = *NumberOfBytes;
>> +  MapInfo->NumberOfPages     = EFI_SIZE_TO_PAGES (MapInfo->NumberOfBytes);
>> +  MapInfo->HostAddress       = PhysicalAddress;
>> +  MapInfo->MappedHostAddress = (EFI_PHYSICAL_ADDRESS)-1;
>> +
>> +  //
>> +  // Allocate a buffer below 4GB to map the transfer to.
>> +  //
>> +  if (NeedRemap) {
>> +    MapInfo->MappedHostAddress = mTranslationBase + SIZE_4GB - 1;
>> +    Status = gBS->AllocatePages (
>> +                    AllocateMaxAddress,
>> +                    EfiBootServicesData,
>> +                    MapInfo->NumberOfPages,
>> +                    &MapInfo->MappedHostAddress
>> +                    );
>> +    if (EFI_ERROR (Status)) {
>> +      FreePool (MapInfo);
>> +      *NumberOfBytes = 0;
>> +      return Status;
>> +    }
>> +
>> +    //
>> +    // If this is a read operation from the Bus Master's point of view,
>> +    // then copy the contents of the real buffer into the mapped buffer
>> +    // so the Bus Master can read the contents of the real buffer.
>> +    //
>> +    if (Operation == EdkiiIoMmuOperationBusMasterRead ||
>> +        Operation == EdkiiIoMmuOperationBusMasterRead64) {
>> +      CopyMem (
>> +        (VOID *)(UINTN)MapInfo->MappedHostAddress,
>> +        (VOID *)(UINTN)MapInfo->HostAddress,
>> +        MapInfo->NumberOfBytes
>> +        );
>> +    }
>> +  } else {
>> +    MapInfo->MappedHostAddress = MapInfo->HostAddress;
>> +  }
>> +
>> +  InsertTailList (&mMaps, &MapInfo->Link);
>> +
>> +  //
>> +  // The DeviceAddress is the address of the maped buffer below 4GB
>> +  //
>> +  *DeviceAddress = MapInfo->MappedHostAddress - mTranslationBase;
>> +
>> +  //
>> +  // Return a pointer to the MAP_INFO structure in Mapping
>> +  //
>> +  *Mapping       = MapInfo;
>> +
>> +  return EFI_SUCCESS;
>> +}
>> +
>> +/**
>> +  Completes the Map() operation and releases any corresponding resources.
>> +
>> +  @param  This                  The protocol instance pointer.
>> +  @param  Mapping               The mapping value returned from Map().
>> +
>> +  @retval EFI_SUCCESS           The range was unmapped.
>> +  @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by Map().
>> +  @retval EFI_DEVICE_ERROR      The data was not committed to the target system memory.
>> +**/
>> +STATIC
>> +EFI_STATUS
>> +EFIAPI
>> +IoMmuUnmap (
>> +  IN  EDKII_IOMMU_PROTOCOL                     *This,
>> +  IN  VOID                                     *Mapping
>> +  )
>> +{
>> +  MAP_INFO                 *MapInfo;
>> +  LIST_ENTRY               *Link;
>> +
>> +  if (Mapping == NULL) {
>> +    return EFI_INVALID_PARAMETER;
>> +  }
>> +
>> +  MapInfo = NULL;
>> +  for (Link = GetFirstNode (&mMaps)
>> +       ; !IsNull (&mMaps, Link)
>> +       ; Link = GetNextNode (&mMaps, Link)
>> +       ) {
>> +    MapInfo = MAP_INFO_FROM_LINK (Link);
>> +    if (MapInfo == Mapping) {
>> +      break;
>> +    }
>> +  }
>> +  //
>> +  // Mapping is not a valid value returned by Map()
>> +  //
>> +  if (MapInfo != Mapping) {
>> +    return EFI_INVALID_PARAMETER;
>> +  }
>> +  RemoveEntryList (&MapInfo->Link);
>> +
>> +  if (MapInfo->MappedHostAddress != MapInfo->HostAddress) {
>> +    //
>> +    // If this is a write operation from the Bus Master's point of view,
>> +    // then copy the contents of the mapped buffer into the real buffer
>> +    // so the processor can read the contents of the real buffer.
>> +    //
>> +    if (MapInfo->Operation == EdkiiIoMmuOperationBusMasterWrite ||
>> +        MapInfo->Operation == EdkiiIoMmuOperationBusMasterWrite64) {
>> +      CopyMem (
>> +        (VOID *)(UINTN)MapInfo->HostAddress,
>> +        (VOID *)(UINTN)MapInfo->MappedHostAddress,
>> +        MapInfo->NumberOfBytes
>> +        );
>> +    }
>> +
>> +    //
>> +    // Free the mapped buffer and the MAP_INFO structure.
>> +    //
>> +    gBS->FreePages (MapInfo->MappedHostAddress, MapInfo->NumberOfPages);
>> +  }
>> +
>> +  FreePool (Mapping);
>> +  return EFI_SUCCESS;
>> +}
>> +
>> +/**
>> +  Allocates pages that are suitable for an OperationBusMasterCommonBuffer or
>> +  OperationBusMasterCommonBuffer64 mapping.
>> +
>> +  @param  This                  The protocol instance pointer.
>> +  @param  Type                  This parameter is not used and must be ignored.
>> +  @param  MemoryType            The type of memory to allocate, EfiBootServicesData or
>> +                                EfiRuntimeServicesData.
>> +  @param  Pages                 The number of pages to allocate.
>> +  @param  HostAddress           A pointer to store the base system memory address of the
>> +                                allocated range.
>> +  @param  Attributes            The requested bit mask of attributes for the allocated range.
>> +
>> +  @retval EFI_SUCCESS           The requested memory pages were allocated.
>> +  @retval EFI_UNSUPPORTED       Attributes is unsupported. The only legal attribute bits are
>> +                                MEMORY_WRITE_COMBINE and MEMORY_CACHED.
>> +  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
>> +  @retval EFI_OUT_OF_RESOURCES  The memory pages could not be allocated.
>> +
>> +**/
>> +STATIC
>> +EFI_STATUS
>> +EFIAPI
>> +IoMmuAllocateBuffer (
>> +  IN     EDKII_IOMMU_PROTOCOL                     *This,
>> +  IN     EFI_ALLOCATE_TYPE                        Type,
>> +  IN     EFI_MEMORY_TYPE                          MemoryType,
>> +  IN     UINTN                                    Pages,
>> +  IN OUT VOID                                     **HostAddress,
>> +  IN     UINT64                                   Attributes
>> +  )
>> +{
>> +  EFI_STATUS                Status;
>> +  EFI_PHYSICAL_ADDRESS      PhysicalAddress;
>> +
>> +  //
>> +  // Validate Attributes
>> +  //
>> +  if ((Attributes & EDKII_IOMMU_ATTRIBUTE_INVALID_FOR_ALLOCATE_BUFFER) != 0) {
>> +    return EFI_UNSUPPORTED;
>> +  }
>> +
>> +  //
>> +  // Check for invalid inputs
>> +  //
>> +  if (HostAddress == NULL) {
>> +    return EFI_INVALID_PARAMETER;
>> +  }
>> +
>> +  //
>> +  // The only valid memory types are EfiBootServicesData and
>> +  // EfiRuntimeServicesData
>> +  //
>> +  if (MemoryType != EfiBootServicesData &&
>> +      MemoryType != EfiRuntimeServicesData) {
>> +    return EFI_INVALID_PARAMETER;
>> +  }
>> +
>> +  //
>> +  // Limit allocations to memory covered by the remapped window.
>> +  //
>> +  PhysicalAddress = mTranslationBase + SIZE_4GB - 1;
>> +  Status = gBS->AllocatePages (
>> +                  AllocateMaxAddress,
>> +                  MemoryType,
>> +                  Pages,
>> +                  &PhysicalAddress
>> +                  );
>> +  if (!EFI_ERROR (Status)) {
>> +    *HostAddress = (VOID *)(UINTN)PhysicalAddress;
>> +  }
>> +
>> +  return Status;
>> +}
>> +
>> +/**
>> +  Frees memory that was allocated with AllocateBuffer().
>> +
>> +  @param  This                  The protocol instance pointer.
>> +  @param  Pages                 The number of pages to free.
>> +  @param  HostAddress           The base system memory address of the allocated range.
>> +
>> +  @retval EFI_SUCCESS           The requested memory pages were freed.
>> +  @retval EFI_INVALID_PARAMETER The memory range specified by HostAddress and Pages
>> +                                was not allocated with AllocateBuffer().
>> +
>> +**/
>> +STATIC
>> +EFI_STATUS
>> +EFIAPI
>> +IoMmuFreeBuffer (
>> +  IN  EDKII_IOMMU_PROTOCOL                     *This,
>> +  IN  UINTN                                    Pages,
>> +  IN  VOID                                     *HostAddress
>> +  )
>> +{
>> +  return gBS->FreePages ((EFI_PHYSICAL_ADDRESS) (UINTN) HostAddress, Pages);
>> +}
>> +
>> +/**
>> +  Set IOMMU attribute for a system memory.
>> +
>> +  If the IOMMU protocol exists, the system memory cannot be used
>> +  for DMA by default.
>> +
>> +  When a device requests a DMA access for a system memory,
>> +  the device driver need use SetAttribute() to update the IOMMU
>> +  attribute to request DMA access (read and/or write).
>> +
>> +  The DeviceHandle is used to identify which device submits the request.
>> +  The IOMMU implementation need translate the device path to an IOMMU device ID,
>> +  and set IOMMU hardware register accordingly.
>> +  1) DeviceHandle can be a standard PCI device.
>> +     The memory for BusMasterRead need set EDKII_IOMMU_ACCESS_READ.
>> +     The memory for BusMasterWrite need set EDKII_IOMMU_ACCESS_WRITE.
>> +     The memory for BusMasterCommonBuffer need set EDKII_IOMMU_ACCESS_READ|EDKII_IOMMU_ACCESS_WRITE.
>> +     After the memory is used, the memory need set 0 to keep it being protected.
>> +  2) DeviceHandle can be an ACPI device (ISA, I2C, SPI, etc).
>> +     The memory for DMA access need set EDKII_IOMMU_ACCESS_READ and/or EDKII_IOMMU_ACCESS_WRITE.
>> +
>> +  @param[in]  This              The protocol instance pointer.
>> +  @param[in]  DeviceHandle      The device who initiates the DMA access request.
>> +  @param[in]  DeviceAddress     The base of device memory address to be used as the DMA memory.
>> +  @param[in]  Length            The length of device memory address to be used as the DMA memory.
>> +  @param[in]  IoMmuAccess       The IOMMU access.
>> +
>> +  @retval EFI_SUCCESS            The IoMmuAccess is set for the memory range specified by DeviceAddress and Length.
>> +  @retval EFI_INVALID_PARAMETER  DeviceHandle is an invalid handle.
>> +  @retval EFI_INVALID_PARAMETER  DeviceAddress is not IoMmu Page size aligned.
>> +  @retval EFI_INVALID_PARAMETER  Length is not IoMmu Page size aligned.
>> +  @retval EFI_INVALID_PARAMETER  Length is 0.
>> +  @retval EFI_INVALID_PARAMETER  IoMmuAccess specified an illegal combination of access.
>> +  @retval EFI_UNSUPPORTED        DeviceHandle is unknown by the IOMMU.
>> +  @retval EFI_UNSUPPORTED        The bit mask of IoMmuAccess is not supported by the IOMMU.
>> +  @retval EFI_UNSUPPORTED        The IOMMU does not support the memory range specified by DeviceAddress and Length.
>> +  @retval EFI_OUT_OF_RESOURCES   There are not enough resources available to modify the IOMMU access.
>> +  @retval EFI_DEVICE_ERROR       The IOMMU device reported an error while attempting the operation.
>> +
>> +**/
>> +EFI_STATUS
>> +EFIAPI
>> +IoMmuSetAttribute (
>> +  IN EDKII_IOMMU_PROTOCOL  *This,
>> +  IN EFI_HANDLE            DeviceHandle,
>> +  IN EFI_PHYSICAL_ADDRESS  DeviceAddress,
>> +  IN UINT64                Length,
>> +  IN UINT64                IoMmuAccess
>> +  )
>> +{
>> +  return EFI_UNSUPPORTED;
>> +}
>> +
>> +/**
>> +  Set IOMMU attribute for a system memory.
>> +
>> +  If the IOMMU protocol exists, the system memory cannot be used
>> +  for DMA by default.
>> +
>> +  When a device requests a DMA access for a system memory,
>> +  the device driver need use SetAttribute() to update the IOMMU
>> +  attribute to request DMA access (read and/or write).
>> +
>> +  The DeviceHandle is used to identify which device submits the request.
>> +  The IOMMU implementation need translate the device path to an IOMMU device ID,
>> +  and set IOMMU hardware register accordingly.
>> +  1) DeviceHandle can be a standard PCI device.
>> +     The memory for BusMasterRead need set EDKII_IOMMU_ACCESS_READ.
>> +     The memory for BusMasterWrite need set EDKII_IOMMU_ACCESS_WRITE.
>> +     The memory for BusMasterCommonBuffer need set EDKII_IOMMU_ACCESS_READ|EDKII_IOMMU_ACCESS_WRITE.
>> +     After the memory is used, the memory need set 0 to keep it being protected.
>> +  2) DeviceHandle can be an ACPI device (ISA, I2C, SPI, etc).
>> +     The memory for DMA access need set EDKII_IOMMU_ACCESS_READ and/or EDKII_IOMMU_ACCESS_WRITE.
>> +
>> +  @param[in]  This              The protocol instance pointer.
>> +  @param[in]  DeviceHandle      The device who initiates the DMA access request.
>> +  @param[in]  Mapping           The mapping value returned from Map().
>> +  @param[in]  IoMmuAccess       The IOMMU access.
>> +
>> +  @retval EFI_SUCCESS            The IoMmuAccess is set for the memory range specified by DeviceAddress and Length.
>> +  @retval EFI_INVALID_PARAMETER  DeviceHandle is an invalid handle.
>> +  @retval EFI_INVALID_PARAMETER  Mapping is not a value that was returned by Map().
>> +  @retval EFI_INVALID_PARAMETER  IoMmuAccess specified an illegal combination of access.
>> +  @retval EFI_UNSUPPORTED        DeviceHandle is unknown by the IOMMU.
>> +  @retval EFI_UNSUPPORTED        The bit mask of IoMmuAccess is not supported by the IOMMU.
>> +  @retval EFI_UNSUPPORTED        The IOMMU does not support the memory range specified by Mapping.
>> +  @retval EFI_OUT_OF_RESOURCES   There are not enough resources available to modify the IOMMU access.
>> +  @retval EFI_DEVICE_ERROR       The IOMMU device reported an error while attempting the operation.
>> +
>> +**/
>> +STATIC
>> +EFI_STATUS
>> +EFIAPI
>> +IoMmuSetMappingAttribute (
>> +  IN EDKII_IOMMU_PROTOCOL  *This,
>> +  IN EFI_HANDLE            DeviceHandle,
>> +  IN VOID                  *Mapping,
>> +  IN UINT64                IoMmuAccess
>> +  )
>> +{
>> +  //
>> +  // We only support a static remapping of DRAM into the PCI address space
>> +  // so there is nothing we need to do to handle invocations of this protocol
>> +  // method.
>> +  //
>> +  return EFI_SUCCESS;
>> +}
>> +
>> +EDKII_IOMMU_PROTOCOL  mGenericSmmuIommuProtocol = {
>> +  EDKII_IOMMU_PROTOCOL_REVISION,
>> +  IoMmuSetAttribute,
>> +  IoMmuMap,
>> +  IoMmuUnmap,
>> +  IoMmuAllocateBuffer,
>> +  IoMmuFreeBuffer,
>> +  IoMmuSetMappingAttribute,
>> +};
>> diff --git a/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.c b/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.c
>> new file mode 100644
>> index 000000000000..8f5093af14ea
>> --- /dev/null
>> +++ b/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.c
>> @@ -0,0 +1,323 @@
>> +/** @file
>> +
>> +  Copyright (c) 2017, Linaro Ltd. All rights reserved.<BR>
>> +
>> +  This program and the accompanying materials
>> +  are licensed and made available under the terms and conditions of the BSD License
>> +  which accompanies this distribution.  The full text of the license may be found at
>> +  http://opensource.org/licenses/bsd-license.php
>> +
>> +  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
>> +  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
>> +
>> +**/
>> +
>> +#include <PiDxe.h>
>> +#include <Library/BaseLib.h>
>> +#include <Library/DebugLib.h>
>> +#include <Library/IoLib.h>
>> +#include <Library/UefiBootServicesTableLib.h>
>> +
>> +#include <Protocol/Cpu.h>
>> +#include <Protocol/HardwareInterrupt.h>
>> +#include <Protocol/IoMmu.h>
>> +
>> +#include <Chipset/AArch64Mmu.h>
>> +
>> +#define GL_CR0                          0x0
>> +#define GL_CR0_CLIENTPD                 BIT0
>> +
>> +#define GL_IDR0                         0x20
>> +#define GL_IDR1                         0x24
>> +#define GL_STLBIALL                     0x60
>> +#define GL_TLBIALLNSNH                  0x68
>> +#define GL_SMR0                         0x800
>> +#define GL_S2CR0                        0xc00
>> +#define GL_CBA2R0                       0x1800
>> +
>> +#define GL_IDR0_NUMSMRG_MASK            0xff
>> +
>> +#define GL_IDR1_NUMPAGENDXB_MASK        0x7
>> +#define GL_IDR1_NUMPAGENDXB_SHIFT       28
>> +#define GL_IDR1_PAGE_SIZE_64KB          BIT31
>> +
>> +#define CB_BASE(i)                      (mContextBankOffset + ((i) * SIZE_4KB))
>> +
>> +#define CB_SCTLR_OFFSET                 0x0
>> +#define CB_TTBR0_OFFSET                 0x20
>> +#define CB_TTBCR_OFFSET                 0x30
>> +
>> +#define CB_FAR                          0x60
>> +#define CB_FSR                          0x58
>> +#define CB_FSYNR0                       0x68
>> +#define CB_FSYNR1                       0x6c
>> +
>> +#define TT_S2_MEMATTR_CACHED            (0xF << 2)
>> +#define TT_S2_AP_READ_WRITE             (0x3 << 6)
>> +
>> +#define TT_ENTRY_ATTRIBUTES             (TT_TYPE_BLOCK_ENTRY | \
>> +                                         TT_SH_INNER_SHAREABLE | \
>> +                                         TT_S2_AP_READ_WRITE | \
>> +                                         TT_S2_MEMATTR_CACHED | \
>> +                                         TT_AF)
>> +
>> +#define TCR_T0SZ(bits)                  ((UINT32)(32 - (bits)) & 0x3f)
>> +#define TCR_SL0_LEVEL1                  BIT6
>> +#define TCR_SL0_LEVEL2                  0
>> +
>> +#define SCTLR_M_ENABLE                  BIT0
>> +#define SCTLR_TR_ENABLE                 BIT1
>> +#define SCTLR_AF_ENABLE                 BIT2
>> +#define SCTLR_CTX_FAULT_ENABLE          BIT5
>> +#define SCTLR_CTX_INT_ENABLE            BIT6
>> +
>> +#define DRAM_BASE                       FixedPcdGet64 (PcdSystemMemoryBase)
>> +
>> +extern EDKII_IOMMU_PROTOCOL             mGenericSmmuIommuProtocol;
>> +
>> +//
>> +// Create a static stage 2 mapping of the first 4 GB of DRAM in the start
>> +// of the IOVA space, and as an ID mapping at the original offset.
>> +//
>> +STATIC CONST UINT64 mPciTranslation[1024] __attribute__((aligned(SIZE_8KB))) = {
>> +  [0] = TT_ENTRY_ATTRIBUTES | (DRAM_BASE),
>> +  [1] = TT_ENTRY_ATTRIBUTES | (DRAM_BASE + SIZE_1GB),
>> +  [2] = TT_ENTRY_ATTRIBUTES | (DRAM_BASE + SIZE_1GB * 2UL),
>> +  [3] = TT_ENTRY_ATTRIBUTES | (DRAM_BASE + SIZE_1GB * 3UL),
>> +
>> +  //
>> +  // The ID mapping of the first 4 GB of DRAM is a workaround for buggy
>> +  // drivers that violate the UEFI spec by ignoring the device address
>> +  // returned by the PCI I/O map/unmap routines, and program host
>> +  // addresses into the DMA h/w registers or rings instead.
>> +  //
>> +  [(DRAM_BASE >> 30)]     = TT_ENTRY_ATTRIBUTES | (DRAM_BASE),
>> +  [(DRAM_BASE >> 30) + 1] = TT_ENTRY_ATTRIBUTES | (DRAM_BASE + SIZE_1GB),
>> +  [(DRAM_BASE >> 30) + 2] = TT_ENTRY_ATTRIBUTES | (DRAM_BASE + SIZE_1GB * 2UL),
>> +  [(DRAM_BASE >> 30) + 3] = TT_ENTRY_ATTRIBUTES | (DRAM_BASE + SIZE_1GB * 3UL),
>> +};
>> +
>> +STATIC EFI_HARDWARE_INTERRUPT_PROTOCOL    *mInterrupt;
>> +STATIC EFI_EVENT                          mEfiExitBootServicesEvent;
>> +STATIC UINTN                              mContextBankOffset;
>> +
>> +STATIC
>> +UINT32
>> +ReadGlobalReg32 (
>> +  IN  UINT64    Offset
>> +  )
>> +{
>> +  return MmioRead32 (FixedPcdGet64 (PcdPciGenericSmmuBase) + Offset);
>> +}
>> +
>> +STATIC
>> +VOID
>> +WriteGlobalReg32 (
>> +  IN  UINT64    Offset,
>> +  IN  UINT64    Value
>> +  )
>> +{
>> +  MmioWrite32 (FixedPcdGet64 (PcdPciGenericSmmuBase) + Offset, Value);
>> +}
>> +
>> +STATIC
>> +UINT32
>> +ReadCbReg32 (
>> +  IN  UINTN     Bank,
>> +  IN  UINT64    Offset
>> +  )
>> +{
>> +  return MmioRead32 (FixedPcdGet64 (PcdPciGenericSmmuBase) + CB_BASE(Bank) +
>> +                     Offset);
>> +}
>> +
>> +STATIC
>> +VOID
>> +WriteCbReg32 (
>> +  IN  UINTN     Bank,
>> +  IN  UINT64    Offset,
>> +  IN  UINT32    Value
>> +  )
>> +{
>> +  MmioWrite32 (FixedPcdGet64 (PcdPciGenericSmmuBase) + CB_BASE(Bank) + Offset,
>> +               Value);
>> +}
>> +
>> +STATIC
>> +UINT64
>> +ReadCbReg64 (
>> +  IN  UINTN     Bank,
>> +  IN  UINT64    Offset
>> +  )
>> +{
>> +  return MmioRead64 (FixedPcdGet64 (PcdPciGenericSmmuBase) + CB_BASE(Bank) +
>> +                     Offset);
>> +}
>> +
>> +STATIC
>> +VOID
>> +WriteCbReg64 (
>> +  IN  UINTN     Bank,
>> +  IN  UINT64    Offset,
>> +  IN  UINT64    Value
>> +  )
>> +{
>> +  MmioWrite64 (FixedPcdGet64 (PcdPciGenericSmmuBase) + CB_BASE(Bank) + Offset,
>> +               Value);
>> +}
>> +
>> +STATIC
>> +VOID
>> +EFIAPI
>> +ContextInterruptHandler (
>> +  IN  HARDWARE_INTERRUPT_SOURCE   Source,
>> +  IN  EFI_SYSTEM_CONTEXT          SystemContext
>> +  )
>> +{
>> +  //
>> +  // Dump the SMMU context fault registers when taking a context interrupt
>> +  //
>> +  DEBUG ((DEBUG_WARN,
>> +    "Context interrupt asserted by SMMU:\n\n"
>> +    "SMMU_CB0_FAR        0x%016llx \n"
>> +    "SMMU_CB0_FSR        0x%08llx \n"
>> +    "SMMU_CB0_FSYNR0     0x%08llx \n"
>> +    "SMMU_CB0_FSYNR1     0x%08llx \n",
>> +    ReadCbReg64 (0, CB_FAR),
>> +    ReadCbReg32 (0, CB_FSR),
>> +    ReadCbReg32 (0, CB_FSYNR0),
>> +    ReadCbReg32 (0, CB_FSYNR1)));
>> +
>> +  mInterrupt->EndOfInterrupt (mInterrupt, Source);
>> +}
>> +
>> +STATIC
>> +VOID
>> +EFIAPI
>> +ExitBootServicesEvent (
>> +  IN EFI_EVENT  Event,
>> +  IN VOID       *Context
>> +  )
>> +{
>> +  //
>> +  // Put the SMMU back into bypass mode
>> +  //
>> +  MmioOr32 (FixedPcdGet64 (PcdPciGenericSmmuBase) + GL_CR0, GL_CR0_CLIENTPD);
>> +}
>> +
>> +EFI_STATUS
>> +GenericSmmuStaticPciDmaDxeInitialize (
>> +  IN EFI_HANDLE        ImageHandle,
>> +  IN EFI_SYSTEM_TABLE  *SystemTable
>> +  )
>> +{
>> +  EFI_STATUS        Status;
>> +  UINTN             Idx;
>> +  UINT32            IdVal;
>> +  UINTN             NumStreamMappingRegisters;
>> +
>> +  //
>> +  // The static mapping uses 1 GB block mappings, whose VAs and PAs should
>> +  // be equal modulo the block size.
>> +  //
>> +  ASSERT ((DRAM_BASE % SIZE_1GB) == 0);
>> +
>> +  if ((DRAM_BASE & (DRAM_BASE - 1)) != 0) {
>> +    //
>> +    // Buggy drivers that use truncated host addresses instead of device
>> +    // addresses for DMA may still work correctly if such truncation is
>> +    // guaranteed to produce the remapped alias. This is the case if
>> +    // DRAM_BASE is a power of 2.
>> +    //
>> +    DEBUG ((DEBUG_WARN,
>> +      "%a: this driver will work better if DRAM_BASE is a power of 2!\n",
>> +      __FUNCTION__));
>> +  }
>> +
>> +  Status = gBS->LocateProtocol (&gHardwareInterruptProtocolGuid, NULL,
>> +                  (VOID **)&mInterrupt);
>> +  ASSERT_EFI_ERROR (Status);
>> +
>> +  Status = mInterrupt->RegisterInterruptSource (mInterrupt,
>> +                         PcdGet16 (PcdPciGenericSmmuContextInterrupt),
>> +                         ContextInterruptHandler);
>> +  ASSERT_EFI_ERROR (Status);
>> +  if (EFI_ERROR (Status)) {
>> +    return Status;
>> +  }
>> +
>> +  Status = gBS->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES,
>> +                  TPL_NOTIFY, ExitBootServicesEvent, NULL,
>> +                  &mEfiExitBootServicesEvent);
>> +  ASSERT_EFI_ERROR (Status);
>> +
>> +  Status = gBS->InstallMultipleProtocolInterfaces (
>> +                  &ImageHandle,
>> +                  &gEdkiiIoMmuProtocolGuid, &mGenericSmmuIommuProtocol,
>> +                  NULL
>> +                  );
>> +  ASSERT_EFI_ERROR (Status);
>> +
>> +  IdVal = ReadGlobalReg32 (GL_IDR1);
>> +  mContextBankOffset = (IdVal & GL_IDR1_PAGE_SIZE_64KB) ? SIZE_64KB : SIZE_4KB;
>> +  mContextBankOffset <<= (1 + ((IdVal >> GL_IDR1_NUMPAGENDXB_SHIFT) &
>> +                               GL_IDR1_NUMPAGENDXB_MASK));
>> +
>> +  //
>> +  // Clear all stream mappings
>> +  //
>> +  NumStreamMappingRegisters = ReadGlobalReg32 (GL_IDR0) & GL_IDR0_NUMSMRG_MASK;
>> +  for (Idx = 0; Idx < NumStreamMappingRegisters; Idx++) {
>> +    WriteGlobalReg32 (GL_SMR0 + Idx * sizeof(UINT32), 0x0);
>> +    WriteGlobalReg32 (GL_S2CR0 + Idx * sizeof(UINT32), 0x0);
>> +  }
>> +
>> +  //
>> +  // Set stream match register 0 to match all streams, and map onto
>> +  // context bank 0
>> +  //
>> +  WriteGlobalReg32 (GL_SMR0, 0xffff0000);
>> +  WriteGlobalReg32 (GL_S2CR0, 0x0);
>> +
>> +  //
>> +  // Disable the context bank
>> +  //
>> +  WriteCbReg32 (0, CB_SCTLR_OFFSET, 0);
>> +
>> +  //
>> +  // Assign the translation base register for context bank 0
>> +  //
>> +  WriteCbReg64 (0, CB_TTBR0_OFFSET, (UINTN)mPciTranslation);
>> +
>> +  //
>> +  // Flush TLBS.
>> +  //
>> +  WriteGlobalReg32 (GL_STLBIALL, 0);
>> +  WriteGlobalReg32 (GL_TLBIALLNSNH, 0);
>> +
>> +  //
>> +  // Configure the size of the translation space, the number of levels,
>> +  // and the cacheability attributes of the PTW memory accesses.
>> +  //
>> +  WriteCbReg32 (0, CB_TTBCR_OFFSET, TCR_T0SZ(40) |
>> +                                    TCR_SH_INNER_SHAREABLE |
>> +                                    TCR_RGN_INNER_WRITE_BACK_NO_ALLOC |
>> +                                    TCR_RGN_OUTER_WRITE_BACK_NO_ALLOC |
>> +                                    TCR_SL0_LEVEL1);
>> +
>> +  //
>> +  // Enable the context bank
>> +  //
>> +  WriteCbReg32 (0, CB_SCTLR_OFFSET, SCTLR_TR_ENABLE |
>> +                                    SCTLR_AF_ENABLE |
>> +                                    SCTLR_CTX_INT_ENABLE |
>> +                                    SCTLR_CTX_FAULT_ENABLE |
>> +                                    SCTLR_M_ENABLE);
>> +
>> +  //
>> +  // Get the SMMU out of bypass mode
>> +  //
>> +  MmioAnd32 (FixedPcdGet64 (PcdPciGenericSmmuBase) + GL_CR0, ~GL_CR0_CLIENTPD);
>> +
>> +  return EFI_SUCCESS;
>> +}
>> diff --git a/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.inf b/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.inf
>> new file mode 100644
>> index 000000000000..02c17e755c4a
>> --- /dev/null
>> +++ b/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.inf
>> @@ -0,0 +1,62 @@
>> +## @file
>> +#
>> +# Copyright (c) 2017, Linaro Ltd. All rights reserved.<BR>
>> +#
>> +# This program and the accompanying materials
>> +# are licensed and made available under the terms and conditions of the BSD License
>> +# which accompanies this distribution.  The full text of the license may be found at
>> +# http://opensource.org/licenses/bsd-license.php
>> +#
>> +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
>> +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
>> +#
>> +##
>> +
>> +[Defines]
>> +  INF_VERSION                    = 0x00010019
>> +  BASE_NAME                      = GenericSmmuStaticPciDmaDxe
>> +  FILE_GUID                      = 59b5e69f-88b7-4632-a2ab-6abe6bdedda2
>> +  MODULE_TYPE                    = DXE_DRIVER
>> +  VERSION_STRING                 = 1.0
>> +  ENTRY_POINT                    = GenericSmmuStaticPciDmaDxeInitialize
>> +
>> +#
>> +# The following information is for reference only and not required by the build tools.
>> +#
>> +#  VALID_ARCHITECTURES           = ARM AARCH64
>> +#
>> +#
>> +
>> +[Sources]
>> +  GenericSmmuStaticPciDmaDxe.c
>> +  BmDma.c
>> +
>> +[Packages]
>> +  ArmPkg/ArmPkg.dec
>> +  EmbeddedPkg/EmbeddedPkg.dec
>> +  MdeModulePkg/MdeModulePkg.dec
>> +  MdePkg/MdePkg.dec
>> +
>> +[LibraryClasses]
>> +  DebugLib
>> +  BaseLib
>> +  BaseMemoryLib
>> +  IoLib
>> +  MemoryAllocationLib
>> +  UefiBootServicesTableLib
>> +  UefiDriverEntryPoint
>> +
>> +[Protocols]
>> +  gEdkiiIoMmuProtocolGuid                     ## PRODUCES
>> +  gEfiPciIoProtocolGuid                       ## CONSUMES
>> +  gHardwareInterruptProtocolGuid              ## CONSUMES
>> +
>> +[Pcd]
>> +  gArmTokenSpaceGuid.PcdPciGenericSmmuBase
>> +  gArmTokenSpaceGuid.PcdPciGenericSmmuContextInterrupt
>> +
>> +[FixedPcd]
>> +  gArmTokenSpaceGuid.PcdSystemMemoryBase
>> +
>> +[Depex]
>> +  gHardwareInterruptProtocolGuid
>> --
>> 2.9.3
>>
_______________________________________________
edk2-devel mailing list
edk2-devel@lists.01.org
https://lists.01.org/mailman/listinfo/edk2-devel
Re: [edk2] [RFC PATCH] ArmPkg: implement generic SMMU driver to remap DRAM for 32-bit PCI DMA
Posted by Leif Lindholm 6 years, 11 months ago
On Tue, May 23, 2017 at 03:36:24AM -0700, Ard Biesheuvel wrote:
> On 22 May 2017 at 06:49, Leif Lindholm <leif.lindholm@linaro.org> wrote:
> > On Tue, May 02, 2017 at 11:32:02AM +0100, Ard Biesheuvel wrote:
> >> This implements a driver that uses any SMMU compatible with the generic
> >> ARM SMMU architecture to remap the lowest 4 GB of DRAM in a way that
> >> makes it accessible to PCI masters that are only 32-bit DMA capable.
> >>
> >> Note that this driver goes a bit beyond what is strictly necessary to
> >> support 32-bit DMA, given that it also creates an identity map of the
> >> lowest 4 GB of DRAM. This is intended for interoperability with external
> >> drivers that may use the PCI I/O protocol incorrectly (or not at all)
> >> and program host addresses into the DMA registers and/or rings without
> >> any regard for translation or address size. If a platform's base of DRAM
> >> is a power of 2, and if the platform runs UEFI entirely in the lowest
> >> 4 GB of DRAM, any host address access by a PCI master will hit the ID
> >> mapped window, and any truncation that may occur will convert the host
> >> address into the device address.
> >>
> >> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
> >> ---
> >>
> >> This is based on Jiewen Yao's IOMMU protocol support series, v4.
> >>
> >> https://lists.01.org/pipermail/edk2-devel/2017-April/010330.html
> >>
> >> Tested with AMD Seattle, which has no DRAM below 4 GB.
> >>
> >>  ArmPkg/ArmPkg.dec                                                        |   7 +
> >>  ArmPkg/ArmPkg.dsc                                                        |   1 +
> >>  ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/BmDma.c                        | 467 ++++++++++++++++++++
> >>  ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.c   | 323 ++++++++++++++
> >>  ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.inf |  62 +++
> >>  5 files changed, 860 insertions(+)
> >>
> >> diff --git a/ArmPkg/ArmPkg.dec b/ArmPkg/ArmPkg.dec
> >> index c4b4da2f95bb..96913e3c0713 100644
> >> --- a/ArmPkg/ArmPkg.dec
> >> +++ b/ArmPkg/ArmPkg.dec
> >> @@ -322,3 +322,10 @@ [PcdsFixedAtBuild.common, PcdsDynamic.common]
> >>    #
> >>    gArmTokenSpaceGuid.PcdPciBusMin|0x0|UINT32|0x00000059
> >>    gArmTokenSpaceGuid.PcdPciBusMax|0x0|UINT32|0x0000005A
> >> +
> >> +  #
> >> +  # Base address and context interrupt of the generic SMMU that
> >> +  # translates memory accesses made by PCI masters
> >> +  #
> >> +  gArmTokenSpaceGuid.PcdPciGenericSmmuBase|0x0|UINT64|0x0000005B
> >> +  gArmTokenSpaceGuid.PcdPciGenericSmmuContextInterrupt|0x0|UINT16|0x0000005C
> >> diff --git a/ArmPkg/ArmPkg.dsc b/ArmPkg/ArmPkg.dsc
> >> index 9144334cb821..9bbc71fa2479 100644
> >> --- a/ArmPkg/ArmPkg.dsc
> >> +++ b/ArmPkg/ArmPkg.dsc
> >> @@ -127,6 +127,7 @@ [Components.common]
> >>    ArmPkg/Drivers/ArmGic/ArmGicDxe.inf
> >>    ArmPkg/Drivers/ArmGic/ArmGicLib.inf
> >>    ArmPkg/Drivers/ArmGic/ArmGicSecLib.inf
> >> +  ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.inf
> >>    ArmPkg/Drivers/GenericWatchdogDxe/GenericWatchdogDxe.inf
> >>    ArmPkg/Drivers/TimerDxe/TimerDxe.inf
> >>
> >> diff --git a/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/BmDma.c b/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/BmDma.c
> >> new file mode 100644
> >> index 000000000000..629209e335e5
> >> --- /dev/null
> >> +++ b/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/BmDma.c
> >> @@ -0,0 +1,467 @@
> >> +/** @file
> >> +  BmDma related function
> >> +
> >> +  Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
> >> +  Copyright (c) 2017, Linaro Ltd. All rights reserved.<BR>
> >> +
> >> +  This program and the accompanying materials
> >> +  are licensed and made available under the terms and conditions of the BSD License
> >> +  which accompanies this distribution.  The full text of the license may be found at
> >> +  http://opensource.org/licenses/bsd-license.php
> >> +
> >> +  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
> >> +  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
> >> +
> >> +**/
> >> +
> >> +#include <PiDxe.h>
> >> +
> >> +#include <Protocol/IoMmu.h>
> >> +
> >> +#include <Library/BaseLib.h>
> >> +#include <Library/DebugLib.h>
> >> +#include <Library/BaseMemoryLib.h>
> >> +#include <Library/MemoryAllocationLib.h>
> >> +#include <Library/UefiBootServicesTableLib.h>
> >> +
> >> +STATIC CONST UINT64 mTranslationBase = FixedPcdGet64 (PcdSystemMemoryBase);
> >
> > So, here is the only objection I have to the patch (apart from a
> > question below). I am _really_ not happy about the ARM-specific
> > PcdSystemMemoryBase (in general), although it is less evil than
> > PcdSystemMemorySize.
> >
> > Maybe I'm missing something, but I don't see anything ARM-specific
> > about this driver. If PcdSystemMemoryBase is the reason for placing it
> > under ArmPkg, can we migrate that option across somewhere else?
> >
> 
> Of course it is ARM specific. SMMUs only exist on ARM systems,

SMMUs, yes. IOMMUs, no.

> and the
> driver reuses ArmMmuLib defines to populate the page table entries.

Ah, managed to miss that bit.
The remainder of the code looks completely generic.

> In general, I agree with the tendency to keep code out of ArmPkg, but
> in this case, I think it really belongs there.

I'll concede it makes sense to keep it in ArmPkg for now, and
re-engineering it for complete portability when there are no other
potential users is a bit premature.

> > Seems an added shame to put two new generic config options
> > (PcdPciGenericSmmuBase/PcdPciGenericSmmuContextInterrupt) under an
> > architecture-specific directory.
> >
> >> +
> >> +#define MAP_INFO_SIGNATURE  SIGNATURE_32 ('D', 'M', 'A', 'P')
> >> +typedef struct {
> >> +  UINT32                                    Signature;
> >> +  LIST_ENTRY                                Link;
> >> +  EDKII_IOMMU_OPERATION                     Operation;
> >> +  UINTN                                     NumberOfBytes;
> >> +  UINTN                                     NumberOfPages;
> >> +  EFI_PHYSICAL_ADDRESS                      HostAddress;
> >> +  EFI_PHYSICAL_ADDRESS                      MappedHostAddress;
> >> +} MAP_INFO;
> >> +#define MAP_INFO_FROM_LINK(a) CR (a, MAP_INFO, Link, MAP_INFO_SIGNATURE)
> >> +
> >> +STATIC LIST_ENTRY                 mMaps = INITIALIZE_LIST_HEAD_VARIABLE(mMaps);
> >> +
> >> +/**
> >> +  Provides the controller-specific addresses required to access system memory from a
> >> +  DMA bus master.
> >> +
> >> +  @param  This                  The protocol instance pointer.
> >> +  @param  Operation             Indicates if the bus master is going to read or write to system memory.
> >> +  @param  HostAddress           The system memory address to map to the PCI controller.
> >> +  @param  NumberOfBytes         On input the number of bytes to map. On output the number of bytes
> >> +                                that were mapped.
> >> +  @param  DeviceAddress         The resulting map address for the bus master PCI controller to use to
> >> +                                access the hosts HostAddress.
> >> +  @param  Mapping               A resulting value to pass to Unmap().
> >> +
> >> +  @retval EFI_SUCCESS           The range was mapped for the returned NumberOfBytes.
> >> +  @retval EFI_UNSUPPORTED       The HostAddress cannot be mapped as a common buffer.
> >> +  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
> >> +  @retval EFI_OUT_OF_RESOURCES  The request could not be completed due to a lack of resources.
> >> +  @retval EFI_DEVICE_ERROR      The system hardware could not map the requested address.
> >> +
> >> +**/
> >> +STATIC
> >> +EFI_STATUS
> >> +EFIAPI
> >> +IoMmuMap (
> >> +  IN     EDKII_IOMMU_PROTOCOL                       *This,
> >> +  IN     EDKII_IOMMU_OPERATION                      Operation,
> >> +  IN     VOID                                       *HostAddress,
> >> +  IN OUT UINTN                                      *NumberOfBytes,
> >> +  OUT    EFI_PHYSICAL_ADDRESS                       *DeviceAddress,
> >> +  OUT    VOID                                       **Mapping
> >> +  )
> >> +{
> >> +  EFI_STATUS                                        Status;
> >> +  EFI_PHYSICAL_ADDRESS                              PhysicalAddress;
> >> +  MAP_INFO                                          *MapInfo;
> >> +  BOOLEAN                                           NeedRemap;
> >> +
> >> +  if (HostAddress == NULL || NumberOfBytes == NULL || DeviceAddress == NULL ||
> >> +      Mapping == NULL) {
> >> +    return EFI_INVALID_PARAMETER;
> >> +  }
> >> +
> >> +  //
> >> +  // Make sure that Operation is valid
> >> +  //
> >> +  if ((UINT32) Operation >= EdkiiIoMmuOperationMaximum) {
> >> +    return EFI_INVALID_PARAMETER;
> >> +  }
> >> +
> >> +  NeedRemap = FALSE;
> >> +  PhysicalAddress = (UINTN)HostAddress;
> >> +
> >> +  if ((PhysicalAddress + *NumberOfBytes) > mTranslationBase + SIZE_4GB) {
> >> +    //
> >> +    // If the root bridge or the device cannot handle performing DMA above
> >> +    // 4GB but any part of the DMA transfer being mapped is above 4GB, then
> >> +    // map the DMA transfer to a buffer below 4GB.
> >> +    //
> >
> > Request for clarification: is this because this driver is only invoked
> > where the root bridge is incapable? (The test looks unconditional.)
> >
> 
> No, the root bridge driver always invokes the IOMMU protocol if there is one.
> 
> Here we test whether the mapped buffer already lives in the first 4 GB
> of DRAM, otherwise we will use a bounce buffer. This driver never maps
> anything dynamically in the SMMU, it just allocates a bounce buffer
> from the 32-bit addressable range if required.

Right, so could the comment be updated to say something more like

    //
    // If any part of the DMA transfer being mapped is above 4GB, then
    // map the DMA transfer to a buffer below 4GB.
    //

?

The remainder of the comment is also useful, but would it not make
more sense in the function header?

With that:
Reviewed-by: Leif Lindholm <leif.lindholm@linaro.org>

/
    Leif

> >> +    NeedRemap = TRUE;
> >> +  }
> >> +
> >> +  if ((Operation == EdkiiIoMmuOperationBusMasterCommonBuffer ||
> >> +       Operation == EdkiiIoMmuOperationBusMasterCommonBuffer64) &&
> >> +      NeedRemap) {
> >> +    //
> >> +    // Common Buffer operations can not be remapped.  If the common buffer
> >> +    // if above 4GB, then it is not possible to generate a mapping, so return
> >> +    // an error.
> >> +    //
> >> +    return EFI_UNSUPPORTED;
> >> +  }
> >> +
> >> +  //
> >> +  // Allocate a MAP_INFO structure to remember the mapping when Unmap() is
> >> +  // called later.
> >> +  //
> >> +  MapInfo = AllocatePool (sizeof (MAP_INFO));
> >> +  if (MapInfo == NULL) {
> >> +    *NumberOfBytes = 0;
> >> +    return EFI_OUT_OF_RESOURCES;
> >> +  }
> >> +
> >> +  //
> >> +  // Initialize the MAP_INFO structure
> >> +  //
> >> +  MapInfo->Signature         = MAP_INFO_SIGNATURE;
> >> +  MapInfo->Operation         = Operation;
> >> +  MapInfo->NumberOfBytes     = *NumberOfBytes;
> >> +  MapInfo->NumberOfPages     = EFI_SIZE_TO_PAGES (MapInfo->NumberOfBytes);
> >> +  MapInfo->HostAddress       = PhysicalAddress;
> >> +  MapInfo->MappedHostAddress = (EFI_PHYSICAL_ADDRESS)-1;
> >> +
> >> +  //
> >> +  // Allocate a buffer below 4GB to map the transfer to.
> >> +  //
> >> +  if (NeedRemap) {
> >> +    MapInfo->MappedHostAddress = mTranslationBase + SIZE_4GB - 1;
> >> +    Status = gBS->AllocatePages (
> >> +                    AllocateMaxAddress,
> >> +                    EfiBootServicesData,
> >> +                    MapInfo->NumberOfPages,
> >> +                    &MapInfo->MappedHostAddress
> >> +                    );
> >> +    if (EFI_ERROR (Status)) {
> >> +      FreePool (MapInfo);
> >> +      *NumberOfBytes = 0;
> >> +      return Status;
> >> +    }
> >> +
> >> +    //
> >> +    // If this is a read operation from the Bus Master's point of view,
> >> +    // then copy the contents of the real buffer into the mapped buffer
> >> +    // so the Bus Master can read the contents of the real buffer.
> >> +    //
> >> +    if (Operation == EdkiiIoMmuOperationBusMasterRead ||
> >> +        Operation == EdkiiIoMmuOperationBusMasterRead64) {
> >> +      CopyMem (
> >> +        (VOID *)(UINTN)MapInfo->MappedHostAddress,
> >> +        (VOID *)(UINTN)MapInfo->HostAddress,
> >> +        MapInfo->NumberOfBytes
> >> +        );
> >> +    }
> >> +  } else {
> >> +    MapInfo->MappedHostAddress = MapInfo->HostAddress;
> >> +  }
> >> +
> >> +  InsertTailList (&mMaps, &MapInfo->Link);
> >> +
> >> +  //
> >> +  // The DeviceAddress is the address of the maped buffer below 4GB
> >> +  //
> >> +  *DeviceAddress = MapInfo->MappedHostAddress - mTranslationBase;
> >> +
> >> +  //
> >> +  // Return a pointer to the MAP_INFO structure in Mapping
> >> +  //
> >> +  *Mapping       = MapInfo;
> >> +
> >> +  return EFI_SUCCESS;
> >> +}
> >> +
> >> +/**
> >> +  Completes the Map() operation and releases any corresponding resources.
> >> +
> >> +  @param  This                  The protocol instance pointer.
> >> +  @param  Mapping               The mapping value returned from Map().
> >> +
> >> +  @retval EFI_SUCCESS           The range was unmapped.
> >> +  @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by Map().
> >> +  @retval EFI_DEVICE_ERROR      The data was not committed to the target system memory.
> >> +**/
> >> +STATIC
> >> +EFI_STATUS
> >> +EFIAPI
> >> +IoMmuUnmap (
> >> +  IN  EDKII_IOMMU_PROTOCOL                     *This,
> >> +  IN  VOID                                     *Mapping
> >> +  )
> >> +{
> >> +  MAP_INFO                 *MapInfo;
> >> +  LIST_ENTRY               *Link;
> >> +
> >> +  if (Mapping == NULL) {
> >> +    return EFI_INVALID_PARAMETER;
> >> +  }
> >> +
> >> +  MapInfo = NULL;
> >> +  for (Link = GetFirstNode (&mMaps)
> >> +       ; !IsNull (&mMaps, Link)
> >> +       ; Link = GetNextNode (&mMaps, Link)
> >> +       ) {
> >> +    MapInfo = MAP_INFO_FROM_LINK (Link);
> >> +    if (MapInfo == Mapping) {
> >> +      break;
> >> +    }
> >> +  }
> >> +  //
> >> +  // Mapping is not a valid value returned by Map()
> >> +  //
> >> +  if (MapInfo != Mapping) {
> >> +    return EFI_INVALID_PARAMETER;
> >> +  }
> >> +  RemoveEntryList (&MapInfo->Link);
> >> +
> >> +  if (MapInfo->MappedHostAddress != MapInfo->HostAddress) {
> >> +    //
> >> +    // If this is a write operation from the Bus Master's point of view,
> >> +    // then copy the contents of the mapped buffer into the real buffer
> >> +    // so the processor can read the contents of the real buffer.
> >> +    //
> >> +    if (MapInfo->Operation == EdkiiIoMmuOperationBusMasterWrite ||
> >> +        MapInfo->Operation == EdkiiIoMmuOperationBusMasterWrite64) {
> >> +      CopyMem (
> >> +        (VOID *)(UINTN)MapInfo->HostAddress,
> >> +        (VOID *)(UINTN)MapInfo->MappedHostAddress,
> >> +        MapInfo->NumberOfBytes
> >> +        );
> >> +    }
> >> +
> >> +    //
> >> +    // Free the mapped buffer and the MAP_INFO structure.
> >> +    //
> >> +    gBS->FreePages (MapInfo->MappedHostAddress, MapInfo->NumberOfPages);
> >> +  }
> >> +
> >> +  FreePool (Mapping);
> >> +  return EFI_SUCCESS;
> >> +}
> >> +
> >> +/**
> >> +  Allocates pages that are suitable for an OperationBusMasterCommonBuffer or
> >> +  OperationBusMasterCommonBuffer64 mapping.
> >> +
> >> +  @param  This                  The protocol instance pointer.
> >> +  @param  Type                  This parameter is not used and must be ignored.
> >> +  @param  MemoryType            The type of memory to allocate, EfiBootServicesData or
> >> +                                EfiRuntimeServicesData.
> >> +  @param  Pages                 The number of pages to allocate.
> >> +  @param  HostAddress           A pointer to store the base system memory address of the
> >> +                                allocated range.
> >> +  @param  Attributes            The requested bit mask of attributes for the allocated range.
> >> +
> >> +  @retval EFI_SUCCESS           The requested memory pages were allocated.
> >> +  @retval EFI_UNSUPPORTED       Attributes is unsupported. The only legal attribute bits are
> >> +                                MEMORY_WRITE_COMBINE and MEMORY_CACHED.
> >> +  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
> >> +  @retval EFI_OUT_OF_RESOURCES  The memory pages could not be allocated.
> >> +
> >> +**/
> >> +STATIC
> >> +EFI_STATUS
> >> +EFIAPI
> >> +IoMmuAllocateBuffer (
> >> +  IN     EDKII_IOMMU_PROTOCOL                     *This,
> >> +  IN     EFI_ALLOCATE_TYPE                        Type,
> >> +  IN     EFI_MEMORY_TYPE                          MemoryType,
> >> +  IN     UINTN                                    Pages,
> >> +  IN OUT VOID                                     **HostAddress,
> >> +  IN     UINT64                                   Attributes
> >> +  )
> >> +{
> >> +  EFI_STATUS                Status;
> >> +  EFI_PHYSICAL_ADDRESS      PhysicalAddress;
> >> +
> >> +  //
> >> +  // Validate Attributes
> >> +  //
> >> +  if ((Attributes & EDKII_IOMMU_ATTRIBUTE_INVALID_FOR_ALLOCATE_BUFFER) != 0) {
> >> +    return EFI_UNSUPPORTED;
> >> +  }
> >> +
> >> +  //
> >> +  // Check for invalid inputs
> >> +  //
> >> +  if (HostAddress == NULL) {
> >> +    return EFI_INVALID_PARAMETER;
> >> +  }
> >> +
> >> +  //
> >> +  // The only valid memory types are EfiBootServicesData and
> >> +  // EfiRuntimeServicesData
> >> +  //
> >> +  if (MemoryType != EfiBootServicesData &&
> >> +      MemoryType != EfiRuntimeServicesData) {
> >> +    return EFI_INVALID_PARAMETER;
> >> +  }
> >> +
> >> +  //
> >> +  // Limit allocations to memory covered by the remapped window.
> >> +  //
> >> +  PhysicalAddress = mTranslationBase + SIZE_4GB - 1;
> >> +  Status = gBS->AllocatePages (
> >> +                  AllocateMaxAddress,
> >> +                  MemoryType,
> >> +                  Pages,
> >> +                  &PhysicalAddress
> >> +                  );
> >> +  if (!EFI_ERROR (Status)) {
> >> +    *HostAddress = (VOID *)(UINTN)PhysicalAddress;
> >> +  }
> >> +
> >> +  return Status;
> >> +}
> >> +
> >> +/**
> >> +  Frees memory that was allocated with AllocateBuffer().
> >> +
> >> +  @param  This                  The protocol instance pointer.
> >> +  @param  Pages                 The number of pages to free.
> >> +  @param  HostAddress           The base system memory address of the allocated range.
> >> +
> >> +  @retval EFI_SUCCESS           The requested memory pages were freed.
> >> +  @retval EFI_INVALID_PARAMETER The memory range specified by HostAddress and Pages
> >> +                                was not allocated with AllocateBuffer().
> >> +
> >> +**/
> >> +STATIC
> >> +EFI_STATUS
> >> +EFIAPI
> >> +IoMmuFreeBuffer (
> >> +  IN  EDKII_IOMMU_PROTOCOL                     *This,
> >> +  IN  UINTN                                    Pages,
> >> +  IN  VOID                                     *HostAddress
> >> +  )
> >> +{
> >> +  return gBS->FreePages ((EFI_PHYSICAL_ADDRESS) (UINTN) HostAddress, Pages);
> >> +}
> >> +
> >> +/**
> >> +  Set IOMMU attribute for a system memory.
> >> +
> >> +  If the IOMMU protocol exists, the system memory cannot be used
> >> +  for DMA by default.
> >> +
> >> +  When a device requests a DMA access for a system memory,
> >> +  the device driver need use SetAttribute() to update the IOMMU
> >> +  attribute to request DMA access (read and/or write).
> >> +
> >> +  The DeviceHandle is used to identify which device submits the request.
> >> +  The IOMMU implementation need translate the device path to an IOMMU device ID,
> >> +  and set IOMMU hardware register accordingly.
> >> +  1) DeviceHandle can be a standard PCI device.
> >> +     The memory for BusMasterRead need set EDKII_IOMMU_ACCESS_READ.
> >> +     The memory for BusMasterWrite need set EDKII_IOMMU_ACCESS_WRITE.
> >> +     The memory for BusMasterCommonBuffer need set EDKII_IOMMU_ACCESS_READ|EDKII_IOMMU_ACCESS_WRITE.
> >> +     After the memory is used, the memory need set 0 to keep it being protected.
> >> +  2) DeviceHandle can be an ACPI device (ISA, I2C, SPI, etc).
> >> +     The memory for DMA access need set EDKII_IOMMU_ACCESS_READ and/or EDKII_IOMMU_ACCESS_WRITE.
> >> +
> >> +  @param[in]  This              The protocol instance pointer.
> >> +  @param[in]  DeviceHandle      The device who initiates the DMA access request.
> >> +  @param[in]  DeviceAddress     The base of device memory address to be used as the DMA memory.
> >> +  @param[in]  Length            The length of device memory address to be used as the DMA memory.
> >> +  @param[in]  IoMmuAccess       The IOMMU access.
> >> +
> >> +  @retval EFI_SUCCESS            The IoMmuAccess is set for the memory range specified by DeviceAddress and Length.
> >> +  @retval EFI_INVALID_PARAMETER  DeviceHandle is an invalid handle.
> >> +  @retval EFI_INVALID_PARAMETER  DeviceAddress is not IoMmu Page size aligned.
> >> +  @retval EFI_INVALID_PARAMETER  Length is not IoMmu Page size aligned.
> >> +  @retval EFI_INVALID_PARAMETER  Length is 0.
> >> +  @retval EFI_INVALID_PARAMETER  IoMmuAccess specified an illegal combination of access.
> >> +  @retval EFI_UNSUPPORTED        DeviceHandle is unknown by the IOMMU.
> >> +  @retval EFI_UNSUPPORTED        The bit mask of IoMmuAccess is not supported by the IOMMU.
> >> +  @retval EFI_UNSUPPORTED        The IOMMU does not support the memory range specified by DeviceAddress and Length.
> >> +  @retval EFI_OUT_OF_RESOURCES   There are not enough resources available to modify the IOMMU access.
> >> +  @retval EFI_DEVICE_ERROR       The IOMMU device reported an error while attempting the operation.
> >> +
> >> +**/
> >> +EFI_STATUS
> >> +EFIAPI
> >> +IoMmuSetAttribute (
> >> +  IN EDKII_IOMMU_PROTOCOL  *This,
> >> +  IN EFI_HANDLE            DeviceHandle,
> >> +  IN EFI_PHYSICAL_ADDRESS  DeviceAddress,
> >> +  IN UINT64                Length,
> >> +  IN UINT64                IoMmuAccess
> >> +  )
> >> +{
> >> +  return EFI_UNSUPPORTED;
> >> +}
> >> +
> >> +/**
> >> +  Set IOMMU attribute for a system memory.
> >> +
> >> +  If the IOMMU protocol exists, the system memory cannot be used
> >> +  for DMA by default.
> >> +
> >> +  When a device requests a DMA access for a system memory,
> >> +  the device driver need use SetAttribute() to update the IOMMU
> >> +  attribute to request DMA access (read and/or write).
> >> +
> >> +  The DeviceHandle is used to identify which device submits the request.
> >> +  The IOMMU implementation need translate the device path to an IOMMU device ID,
> >> +  and set IOMMU hardware register accordingly.
> >> +  1) DeviceHandle can be a standard PCI device.
> >> +     The memory for BusMasterRead need set EDKII_IOMMU_ACCESS_READ.
> >> +     The memory for BusMasterWrite need set EDKII_IOMMU_ACCESS_WRITE.
> >> +     The memory for BusMasterCommonBuffer need set EDKII_IOMMU_ACCESS_READ|EDKII_IOMMU_ACCESS_WRITE.
> >> +     After the memory is used, the memory need set 0 to keep it being protected.
> >> +  2) DeviceHandle can be an ACPI device (ISA, I2C, SPI, etc).
> >> +     The memory for DMA access need set EDKII_IOMMU_ACCESS_READ and/or EDKII_IOMMU_ACCESS_WRITE.
> >> +
> >> +  @param[in]  This              The protocol instance pointer.
> >> +  @param[in]  DeviceHandle      The device who initiates the DMA access request.
> >> +  @param[in]  Mapping           The mapping value returned from Map().
> >> +  @param[in]  IoMmuAccess       The IOMMU access.
> >> +
> >> +  @retval EFI_SUCCESS            The IoMmuAccess is set for the memory range specified by DeviceAddress and Length.
> >> +  @retval EFI_INVALID_PARAMETER  DeviceHandle is an invalid handle.
> >> +  @retval EFI_INVALID_PARAMETER  Mapping is not a value that was returned by Map().
> >> +  @retval EFI_INVALID_PARAMETER  IoMmuAccess specified an illegal combination of access.
> >> +  @retval EFI_UNSUPPORTED        DeviceHandle is unknown by the IOMMU.
> >> +  @retval EFI_UNSUPPORTED        The bit mask of IoMmuAccess is not supported by the IOMMU.
> >> +  @retval EFI_UNSUPPORTED        The IOMMU does not support the memory range specified by Mapping.
> >> +  @retval EFI_OUT_OF_RESOURCES   There are not enough resources available to modify the IOMMU access.
> >> +  @retval EFI_DEVICE_ERROR       The IOMMU device reported an error while attempting the operation.
> >> +
> >> +**/
> >> +STATIC
> >> +EFI_STATUS
> >> +EFIAPI
> >> +IoMmuSetMappingAttribute (
> >> +  IN EDKII_IOMMU_PROTOCOL  *This,
> >> +  IN EFI_HANDLE            DeviceHandle,
> >> +  IN VOID                  *Mapping,
> >> +  IN UINT64                IoMmuAccess
> >> +  )
> >> +{
> >> +  //
> >> +  // We only support a static remapping of DRAM into the PCI address space
> >> +  // so there is nothing we need to do to handle invocations of this protocol
> >> +  // method.
> >> +  //
> >> +  return EFI_SUCCESS;
> >> +}
> >> +
> >> +EDKII_IOMMU_PROTOCOL  mGenericSmmuIommuProtocol = {
> >> +  EDKII_IOMMU_PROTOCOL_REVISION,
> >> +  IoMmuSetAttribute,
> >> +  IoMmuMap,
> >> +  IoMmuUnmap,
> >> +  IoMmuAllocateBuffer,
> >> +  IoMmuFreeBuffer,
> >> +  IoMmuSetMappingAttribute,
> >> +};
> >> diff --git a/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.c b/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.c
> >> new file mode 100644
> >> index 000000000000..8f5093af14ea
> >> --- /dev/null
> >> +++ b/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.c
> >> @@ -0,0 +1,323 @@
> >> +/** @file
> >> +
> >> +  Copyright (c) 2017, Linaro Ltd. All rights reserved.<BR>
> >> +
> >> +  This program and the accompanying materials
> >> +  are licensed and made available under the terms and conditions of the BSD License
> >> +  which accompanies this distribution.  The full text of the license may be found at
> >> +  http://opensource.org/licenses/bsd-license.php
> >> +
> >> +  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
> >> +  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
> >> +
> >> +**/
> >> +
> >> +#include <PiDxe.h>
> >> +#include <Library/BaseLib.h>
> >> +#include <Library/DebugLib.h>
> >> +#include <Library/IoLib.h>
> >> +#include <Library/UefiBootServicesTableLib.h>
> >> +
> >> +#include <Protocol/Cpu.h>
> >> +#include <Protocol/HardwareInterrupt.h>
> >> +#include <Protocol/IoMmu.h>
> >> +
> >> +#include <Chipset/AArch64Mmu.h>
> >> +
> >> +#define GL_CR0                          0x0
> >> +#define GL_CR0_CLIENTPD                 BIT0
> >> +
> >> +#define GL_IDR0                         0x20
> >> +#define GL_IDR1                         0x24
> >> +#define GL_STLBIALL                     0x60
> >> +#define GL_TLBIALLNSNH                  0x68
> >> +#define GL_SMR0                         0x800
> >> +#define GL_S2CR0                        0xc00
> >> +#define GL_CBA2R0                       0x1800
> >> +
> >> +#define GL_IDR0_NUMSMRG_MASK            0xff
> >> +
> >> +#define GL_IDR1_NUMPAGENDXB_MASK        0x7
> >> +#define GL_IDR1_NUMPAGENDXB_SHIFT       28
> >> +#define GL_IDR1_PAGE_SIZE_64KB          BIT31
> >> +
> >> +#define CB_BASE(i)                      (mContextBankOffset + ((i) * SIZE_4KB))
> >> +
> >> +#define CB_SCTLR_OFFSET                 0x0
> >> +#define CB_TTBR0_OFFSET                 0x20
> >> +#define CB_TTBCR_OFFSET                 0x30
> >> +
> >> +#define CB_FAR                          0x60
> >> +#define CB_FSR                          0x58
> >> +#define CB_FSYNR0                       0x68
> >> +#define CB_FSYNR1                       0x6c
> >> +
> >> +#define TT_S2_MEMATTR_CACHED            (0xF << 2)
> >> +#define TT_S2_AP_READ_WRITE             (0x3 << 6)
> >> +
> >> +#define TT_ENTRY_ATTRIBUTES             (TT_TYPE_BLOCK_ENTRY | \
> >> +                                         TT_SH_INNER_SHAREABLE | \
> >> +                                         TT_S2_AP_READ_WRITE | \
> >> +                                         TT_S2_MEMATTR_CACHED | \
> >> +                                         TT_AF)
> >> +
> >> +#define TCR_T0SZ(bits)                  ((UINT32)(32 - (bits)) & 0x3f)
> >> +#define TCR_SL0_LEVEL1                  BIT6
> >> +#define TCR_SL0_LEVEL2                  0
> >> +
> >> +#define SCTLR_M_ENABLE                  BIT0
> >> +#define SCTLR_TR_ENABLE                 BIT1
> >> +#define SCTLR_AF_ENABLE                 BIT2
> >> +#define SCTLR_CTX_FAULT_ENABLE          BIT5
> >> +#define SCTLR_CTX_INT_ENABLE            BIT6
> >> +
> >> +#define DRAM_BASE                       FixedPcdGet64 (PcdSystemMemoryBase)
> >> +
> >> +extern EDKII_IOMMU_PROTOCOL             mGenericSmmuIommuProtocol;
> >> +
> >> +//
> >> +// Create a static stage 2 mapping of the first 4 GB of DRAM in the start
> >> +// of the IOVA space, and as an ID mapping at the original offset.
> >> +//
> >> +STATIC CONST UINT64 mPciTranslation[1024] __attribute__((aligned(SIZE_8KB))) = {
> >> +  [0] = TT_ENTRY_ATTRIBUTES | (DRAM_BASE),
> >> +  [1] = TT_ENTRY_ATTRIBUTES | (DRAM_BASE + SIZE_1GB),
> >> +  [2] = TT_ENTRY_ATTRIBUTES | (DRAM_BASE + SIZE_1GB * 2UL),
> >> +  [3] = TT_ENTRY_ATTRIBUTES | (DRAM_BASE + SIZE_1GB * 3UL),
> >> +
> >> +  //
> >> +  // The ID mapping of the first 4 GB of DRAM is a workaround for buggy
> >> +  // drivers that violate the UEFI spec by ignoring the device address
> >> +  // returned by the PCI I/O map/unmap routines, and program host
> >> +  // addresses into the DMA h/w registers or rings instead.
> >> +  //
> >> +  [(DRAM_BASE >> 30)]     = TT_ENTRY_ATTRIBUTES | (DRAM_BASE),
> >> +  [(DRAM_BASE >> 30) + 1] = TT_ENTRY_ATTRIBUTES | (DRAM_BASE + SIZE_1GB),
> >> +  [(DRAM_BASE >> 30) + 2] = TT_ENTRY_ATTRIBUTES | (DRAM_BASE + SIZE_1GB * 2UL),
> >> +  [(DRAM_BASE >> 30) + 3] = TT_ENTRY_ATTRIBUTES | (DRAM_BASE + SIZE_1GB * 3UL),
> >> +};
> >> +
> >> +STATIC EFI_HARDWARE_INTERRUPT_PROTOCOL    *mInterrupt;
> >> +STATIC EFI_EVENT                          mEfiExitBootServicesEvent;
> >> +STATIC UINTN                              mContextBankOffset;
> >> +
> >> +STATIC
> >> +UINT32
> >> +ReadGlobalReg32 (
> >> +  IN  UINT64    Offset
> >> +  )
> >> +{
> >> +  return MmioRead32 (FixedPcdGet64 (PcdPciGenericSmmuBase) + Offset);
> >> +}
> >> +
> >> +STATIC
> >> +VOID
> >> +WriteGlobalReg32 (
> >> +  IN  UINT64    Offset,
> >> +  IN  UINT64    Value
> >> +  )
> >> +{
> >> +  MmioWrite32 (FixedPcdGet64 (PcdPciGenericSmmuBase) + Offset, Value);
> >> +}
> >> +
> >> +STATIC
> >> +UINT32
> >> +ReadCbReg32 (
> >> +  IN  UINTN     Bank,
> >> +  IN  UINT64    Offset
> >> +  )
> >> +{
> >> +  return MmioRead32 (FixedPcdGet64 (PcdPciGenericSmmuBase) + CB_BASE(Bank) +
> >> +                     Offset);
> >> +}
> >> +
> >> +STATIC
> >> +VOID
> >> +WriteCbReg32 (
> >> +  IN  UINTN     Bank,
> >> +  IN  UINT64    Offset,
> >> +  IN  UINT32    Value
> >> +  )
> >> +{
> >> +  MmioWrite32 (FixedPcdGet64 (PcdPciGenericSmmuBase) + CB_BASE(Bank) + Offset,
> >> +               Value);
> >> +}
> >> +
> >> +STATIC
> >> +UINT64
> >> +ReadCbReg64 (
> >> +  IN  UINTN     Bank,
> >> +  IN  UINT64    Offset
> >> +  )
> >> +{
> >> +  return MmioRead64 (FixedPcdGet64 (PcdPciGenericSmmuBase) + CB_BASE(Bank) +
> >> +                     Offset);
> >> +}
> >> +
> >> +STATIC
> >> +VOID
> >> +WriteCbReg64 (
> >> +  IN  UINTN     Bank,
> >> +  IN  UINT64    Offset,
> >> +  IN  UINT64    Value
> >> +  )
> >> +{
> >> +  MmioWrite64 (FixedPcdGet64 (PcdPciGenericSmmuBase) + CB_BASE(Bank) + Offset,
> >> +               Value);
> >> +}
> >> +
> >> +STATIC
> >> +VOID
> >> +EFIAPI
> >> +ContextInterruptHandler (
> >> +  IN  HARDWARE_INTERRUPT_SOURCE   Source,
> >> +  IN  EFI_SYSTEM_CONTEXT          SystemContext
> >> +  )
> >> +{
> >> +  //
> >> +  // Dump the SMMU context fault registers when taking a context interrupt
> >> +  //
> >> +  DEBUG ((DEBUG_WARN,
> >> +    "Context interrupt asserted by SMMU:\n\n"
> >> +    "SMMU_CB0_FAR        0x%016llx \n"
> >> +    "SMMU_CB0_FSR        0x%08llx \n"
> >> +    "SMMU_CB0_FSYNR0     0x%08llx \n"
> >> +    "SMMU_CB0_FSYNR1     0x%08llx \n",
> >> +    ReadCbReg64 (0, CB_FAR),
> >> +    ReadCbReg32 (0, CB_FSR),
> >> +    ReadCbReg32 (0, CB_FSYNR0),
> >> +    ReadCbReg32 (0, CB_FSYNR1)));
> >> +
> >> +  mInterrupt->EndOfInterrupt (mInterrupt, Source);
> >> +}
> >> +
> >> +STATIC
> >> +VOID
> >> +EFIAPI
> >> +ExitBootServicesEvent (
> >> +  IN EFI_EVENT  Event,
> >> +  IN VOID       *Context
> >> +  )
> >> +{
> >> +  //
> >> +  // Put the SMMU back into bypass mode
> >> +  //
> >> +  MmioOr32 (FixedPcdGet64 (PcdPciGenericSmmuBase) + GL_CR0, GL_CR0_CLIENTPD);
> >> +}
> >> +
> >> +EFI_STATUS
> >> +GenericSmmuStaticPciDmaDxeInitialize (
> >> +  IN EFI_HANDLE        ImageHandle,
> >> +  IN EFI_SYSTEM_TABLE  *SystemTable
> >> +  )
> >> +{
> >> +  EFI_STATUS        Status;
> >> +  UINTN             Idx;
> >> +  UINT32            IdVal;
> >> +  UINTN             NumStreamMappingRegisters;
> >> +
> >> +  //
> >> +  // The static mapping uses 1 GB block mappings, whose VAs and PAs should
> >> +  // be equal modulo the block size.
> >> +  //
> >> +  ASSERT ((DRAM_BASE % SIZE_1GB) == 0);
> >> +
> >> +  if ((DRAM_BASE & (DRAM_BASE - 1)) != 0) {
> >> +    //
> >> +    // Buggy drivers that use truncated host addresses instead of device
> >> +    // addresses for DMA may still work correctly if such truncation is
> >> +    // guaranteed to produce the remapped alias. This is the case if
> >> +    // DRAM_BASE is a power of 2.
> >> +    //
> >> +    DEBUG ((DEBUG_WARN,
> >> +      "%a: this driver will work better if DRAM_BASE is a power of 2!\n",
> >> +      __FUNCTION__));
> >> +  }
> >> +
> >> +  Status = gBS->LocateProtocol (&gHardwareInterruptProtocolGuid, NULL,
> >> +                  (VOID **)&mInterrupt);
> >> +  ASSERT_EFI_ERROR (Status);
> >> +
> >> +  Status = mInterrupt->RegisterInterruptSource (mInterrupt,
> >> +                         PcdGet16 (PcdPciGenericSmmuContextInterrupt),
> >> +                         ContextInterruptHandler);
> >> +  ASSERT_EFI_ERROR (Status);
> >> +  if (EFI_ERROR (Status)) {
> >> +    return Status;
> >> +  }
> >> +
> >> +  Status = gBS->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES,
> >> +                  TPL_NOTIFY, ExitBootServicesEvent, NULL,
> >> +                  &mEfiExitBootServicesEvent);
> >> +  ASSERT_EFI_ERROR (Status);
> >> +
> >> +  Status = gBS->InstallMultipleProtocolInterfaces (
> >> +                  &ImageHandle,
> >> +                  &gEdkiiIoMmuProtocolGuid, &mGenericSmmuIommuProtocol,
> >> +                  NULL
> >> +                  );
> >> +  ASSERT_EFI_ERROR (Status);
> >> +
> >> +  IdVal = ReadGlobalReg32 (GL_IDR1);
> >> +  mContextBankOffset = (IdVal & GL_IDR1_PAGE_SIZE_64KB) ? SIZE_64KB : SIZE_4KB;
> >> +  mContextBankOffset <<= (1 + ((IdVal >> GL_IDR1_NUMPAGENDXB_SHIFT) &
> >> +                               GL_IDR1_NUMPAGENDXB_MASK));
> >> +
> >> +  //
> >> +  // Clear all stream mappings
> >> +  //
> >> +  NumStreamMappingRegisters = ReadGlobalReg32 (GL_IDR0) & GL_IDR0_NUMSMRG_MASK;
> >> +  for (Idx = 0; Idx < NumStreamMappingRegisters; Idx++) {
> >> +    WriteGlobalReg32 (GL_SMR0 + Idx * sizeof(UINT32), 0x0);
> >> +    WriteGlobalReg32 (GL_S2CR0 + Idx * sizeof(UINT32), 0x0);
> >> +  }
> >> +
> >> +  //
> >> +  // Set stream match register 0 to match all streams, and map onto
> >> +  // context bank 0
> >> +  //
> >> +  WriteGlobalReg32 (GL_SMR0, 0xffff0000);
> >> +  WriteGlobalReg32 (GL_S2CR0, 0x0);
> >> +
> >> +  //
> >> +  // Disable the context bank
> >> +  //
> >> +  WriteCbReg32 (0, CB_SCTLR_OFFSET, 0);
> >> +
> >> +  //
> >> +  // Assign the translation base register for context bank 0
> >> +  //
> >> +  WriteCbReg64 (0, CB_TTBR0_OFFSET, (UINTN)mPciTranslation);
> >> +
> >> +  //
> >> +  // Flush TLBS.
> >> +  //
> >> +  WriteGlobalReg32 (GL_STLBIALL, 0);
> >> +  WriteGlobalReg32 (GL_TLBIALLNSNH, 0);
> >> +
> >> +  //
> >> +  // Configure the size of the translation space, the number of levels,
> >> +  // and the cacheability attributes of the PTW memory accesses.
> >> +  //
> >> +  WriteCbReg32 (0, CB_TTBCR_OFFSET, TCR_T0SZ(40) |
> >> +                                    TCR_SH_INNER_SHAREABLE |
> >> +                                    TCR_RGN_INNER_WRITE_BACK_NO_ALLOC |
> >> +                                    TCR_RGN_OUTER_WRITE_BACK_NO_ALLOC |
> >> +                                    TCR_SL0_LEVEL1);
> >> +
> >> +  //
> >> +  // Enable the context bank
> >> +  //
> >> +  WriteCbReg32 (0, CB_SCTLR_OFFSET, SCTLR_TR_ENABLE |
> >> +                                    SCTLR_AF_ENABLE |
> >> +                                    SCTLR_CTX_INT_ENABLE |
> >> +                                    SCTLR_CTX_FAULT_ENABLE |
> >> +                                    SCTLR_M_ENABLE);
> >> +
> >> +  //
> >> +  // Get the SMMU out of bypass mode
> >> +  //
> >> +  MmioAnd32 (FixedPcdGet64 (PcdPciGenericSmmuBase) + GL_CR0, ~GL_CR0_CLIENTPD);
> >> +
> >> +  return EFI_SUCCESS;
> >> +}
> >> diff --git a/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.inf b/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.inf
> >> new file mode 100644
> >> index 000000000000..02c17e755c4a
> >> --- /dev/null
> >> +++ b/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.inf
> >> @@ -0,0 +1,62 @@
> >> +## @file
> >> +#
> >> +# Copyright (c) 2017, Linaro Ltd. All rights reserved.<BR>
> >> +#
> >> +# This program and the accompanying materials
> >> +# are licensed and made available under the terms and conditions of the BSD License
> >> +# which accompanies this distribution.  The full text of the license may be found at
> >> +# http://opensource.org/licenses/bsd-license.php
> >> +#
> >> +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
> >> +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
> >> +#
> >> +##
> >> +
> >> +[Defines]
> >> +  INF_VERSION                    = 0x00010019
> >> +  BASE_NAME                      = GenericSmmuStaticPciDmaDxe
> >> +  FILE_GUID                      = 59b5e69f-88b7-4632-a2ab-6abe6bdedda2
> >> +  MODULE_TYPE                    = DXE_DRIVER
> >> +  VERSION_STRING                 = 1.0
> >> +  ENTRY_POINT                    = GenericSmmuStaticPciDmaDxeInitialize
> >> +
> >> +#
> >> +# The following information is for reference only and not required by the build tools.
> >> +#
> >> +#  VALID_ARCHITECTURES           = ARM AARCH64
> >> +#
> >> +#
> >> +
> >> +[Sources]
> >> +  GenericSmmuStaticPciDmaDxe.c
> >> +  BmDma.c
> >> +
> >> +[Packages]
> >> +  ArmPkg/ArmPkg.dec
> >> +  EmbeddedPkg/EmbeddedPkg.dec
> >> +  MdeModulePkg/MdeModulePkg.dec
> >> +  MdePkg/MdePkg.dec
> >> +
> >> +[LibraryClasses]
> >> +  DebugLib
> >> +  BaseLib
> >> +  BaseMemoryLib
> >> +  IoLib
> >> +  MemoryAllocationLib
> >> +  UefiBootServicesTableLib
> >> +  UefiDriverEntryPoint
> >> +
> >> +[Protocols]
> >> +  gEdkiiIoMmuProtocolGuid                     ## PRODUCES
> >> +  gEfiPciIoProtocolGuid                       ## CONSUMES
> >> +  gHardwareInterruptProtocolGuid              ## CONSUMES
> >> +
> >> +[Pcd]
> >> +  gArmTokenSpaceGuid.PcdPciGenericSmmuBase
> >> +  gArmTokenSpaceGuid.PcdPciGenericSmmuContextInterrupt
> >> +
> >> +[FixedPcd]
> >> +  gArmTokenSpaceGuid.PcdSystemMemoryBase
> >> +
> >> +[Depex]
> >> +  gHardwareInterruptProtocolGuid
> >> --
> >> 2.9.3
> >>
_______________________________________________
edk2-devel mailing list
edk2-devel@lists.01.org
https://lists.01.org/mailman/listinfo/edk2-devel
Re: [edk2] [RFC PATCH] ArmPkg: implement generic SMMU driver to remap DRAM for 32-bit PCI DMA
Posted by Ard Biesheuvel 6 years, 11 months ago
On 24 May 2017 at 03:30, Leif Lindholm <leif.lindholm@linaro.org> wrote:
> On Tue, May 23, 2017 at 03:36:24AM -0700, Ard Biesheuvel wrote:
>> On 22 May 2017 at 06:49, Leif Lindholm <leif.lindholm@linaro.org> wrote:
>> > On Tue, May 02, 2017 at 11:32:02AM +0100, Ard Biesheuvel wrote:
>> >> This implements a driver that uses any SMMU compatible with the generic
>> >> ARM SMMU architecture to remap the lowest 4 GB of DRAM in a way that
>> >> makes it accessible to PCI masters that are only 32-bit DMA capable.
>> >>
>> >> Note that this driver goes a bit beyond what is strictly necessary to
>> >> support 32-bit DMA, given that it also creates an identity map of the
>> >> lowest 4 GB of DRAM. This is intended for interoperability with external
>> >> drivers that may use the PCI I/O protocol incorrectly (or not at all)
>> >> and program host addresses into the DMA registers and/or rings without
>> >> any regard for translation or address size. If a platform's base of DRAM
>> >> is a power of 2, and if the platform runs UEFI entirely in the lowest
>> >> 4 GB of DRAM, any host address access by a PCI master will hit the ID
>> >> mapped window, and any truncation that may occur will convert the host
>> >> address into the device address.
>> >>
>> >> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
>> >> ---
>> >>
>> >> This is based on Jiewen Yao's IOMMU protocol support series, v4.
>> >>
>> >> https://lists.01.org/pipermail/edk2-devel/2017-April/010330.html
>> >>
>> >> Tested with AMD Seattle, which has no DRAM below 4 GB.
>> >>
>> >>  ArmPkg/ArmPkg.dec                                                        |   7 +
>> >>  ArmPkg/ArmPkg.dsc                                                        |   1 +
>> >>  ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/BmDma.c                        | 467 ++++++++++++++++++++
>> >>  ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.c   | 323 ++++++++++++++
>> >>  ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.inf |  62 +++
>> >>  5 files changed, 860 insertions(+)
>> >>
>> >> diff --git a/ArmPkg/ArmPkg.dec b/ArmPkg/ArmPkg.dec
>> >> index c4b4da2f95bb..96913e3c0713 100644
>> >> --- a/ArmPkg/ArmPkg.dec
>> >> +++ b/ArmPkg/ArmPkg.dec
>> >> @@ -322,3 +322,10 @@ [PcdsFixedAtBuild.common, PcdsDynamic.common]
>> >>    #
>> >>    gArmTokenSpaceGuid.PcdPciBusMin|0x0|UINT32|0x00000059
>> >>    gArmTokenSpaceGuid.PcdPciBusMax|0x0|UINT32|0x0000005A
>> >> +
>> >> +  #
>> >> +  # Base address and context interrupt of the generic SMMU that
>> >> +  # translates memory accesses made by PCI masters
>> >> +  #
>> >> +  gArmTokenSpaceGuid.PcdPciGenericSmmuBase|0x0|UINT64|0x0000005B
>> >> +  gArmTokenSpaceGuid.PcdPciGenericSmmuContextInterrupt|0x0|UINT16|0x0000005C
>> >> diff --git a/ArmPkg/ArmPkg.dsc b/ArmPkg/ArmPkg.dsc
>> >> index 9144334cb821..9bbc71fa2479 100644
>> >> --- a/ArmPkg/ArmPkg.dsc
>> >> +++ b/ArmPkg/ArmPkg.dsc
>> >> @@ -127,6 +127,7 @@ [Components.common]
>> >>    ArmPkg/Drivers/ArmGic/ArmGicDxe.inf
>> >>    ArmPkg/Drivers/ArmGic/ArmGicLib.inf
>> >>    ArmPkg/Drivers/ArmGic/ArmGicSecLib.inf
>> >> +  ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.inf
>> >>    ArmPkg/Drivers/GenericWatchdogDxe/GenericWatchdogDxe.inf
>> >>    ArmPkg/Drivers/TimerDxe/TimerDxe.inf
>> >>
>> >> diff --git a/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/BmDma.c b/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/BmDma.c
>> >> new file mode 100644
>> >> index 000000000000..629209e335e5
>> >> --- /dev/null
>> >> +++ b/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/BmDma.c
>> >> @@ -0,0 +1,467 @@
>> >> +/** @file
>> >> +  BmDma related function
>> >> +
>> >> +  Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
>> >> +  Copyright (c) 2017, Linaro Ltd. All rights reserved.<BR>
>> >> +
>> >> +  This program and the accompanying materials
>> >> +  are licensed and made available under the terms and conditions of the BSD License
>> >> +  which accompanies this distribution.  The full text of the license may be found at
>> >> +  http://opensource.org/licenses/bsd-license.php
>> >> +
>> >> +  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
>> >> +  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
>> >> +
>> >> +**/
>> >> +
>> >> +#include <PiDxe.h>
>> >> +
>> >> +#include <Protocol/IoMmu.h>
>> >> +
>> >> +#include <Library/BaseLib.h>
>> >> +#include <Library/DebugLib.h>
>> >> +#include <Library/BaseMemoryLib.h>
>> >> +#include <Library/MemoryAllocationLib.h>
>> >> +#include <Library/UefiBootServicesTableLib.h>
>> >> +
>> >> +STATIC CONST UINT64 mTranslationBase = FixedPcdGet64 (PcdSystemMemoryBase);
>> >
>> > So, here is the only objection I have to the patch (apart from a
>> > question below). I am _really_ not happy about the ARM-specific
>> > PcdSystemMemoryBase (in general), although it is less evil than
>> > PcdSystemMemorySize.
>> >
>> > Maybe I'm missing something, but I don't see anything ARM-specific
>> > about this driver. If PcdSystemMemoryBase is the reason for placing it
>> > under ArmPkg, can we migrate that option across somewhere else?
>> >
>>
>> Of course it is ARM specific. SMMUs only exist on ARM systems,
>
> SMMUs, yes. IOMMUs, no.
>
>> and the
>> driver reuses ArmMmuLib defines to populate the page table entries.
>
> Ah, managed to miss that bit.
> The remainder of the code looks completely generic.
>
>> In general, I agree with the tendency to keep code out of ArmPkg, but
>> in this case, I think it really belongs there.
>
> I'll concede it makes sense to keep it in ArmPkg for now, and
> re-engineering it for complete portability when there are no other
> potential users is a bit premature.
>

Indeed. Especially because the issue it addresses (i.e., all DRAM
above 4 GB) is not equally likely to occur on other architectures.

>> > Seems an added shame to put two new generic config options
>> > (PcdPciGenericSmmuBase/PcdPciGenericSmmuContextInterrupt) under an
>> > architecture-specific directory.
>> >
>> >> +
>> >> +#define MAP_INFO_SIGNATURE  SIGNATURE_32 ('D', 'M', 'A', 'P')
>> >> +typedef struct {
>> >> +  UINT32                                    Signature;
>> >> +  LIST_ENTRY                                Link;
>> >> +  EDKII_IOMMU_OPERATION                     Operation;
>> >> +  UINTN                                     NumberOfBytes;
>> >> +  UINTN                                     NumberOfPages;
>> >> +  EFI_PHYSICAL_ADDRESS                      HostAddress;
>> >> +  EFI_PHYSICAL_ADDRESS                      MappedHostAddress;
>> >> +} MAP_INFO;
>> >> +#define MAP_INFO_FROM_LINK(a) CR (a, MAP_INFO, Link, MAP_INFO_SIGNATURE)
>> >> +
>> >> +STATIC LIST_ENTRY                 mMaps = INITIALIZE_LIST_HEAD_VARIABLE(mMaps);
>> >> +
>> >> +/**
>> >> +  Provides the controller-specific addresses required to access system memory from a
>> >> +  DMA bus master.
>> >> +
>> >> +  @param  This                  The protocol instance pointer.
>> >> +  @param  Operation             Indicates if the bus master is going to read or write to system memory.
>> >> +  @param  HostAddress           The system memory address to map to the PCI controller.
>> >> +  @param  NumberOfBytes         On input the number of bytes to map. On output the number of bytes
>> >> +                                that were mapped.
>> >> +  @param  DeviceAddress         The resulting map address for the bus master PCI controller to use to
>> >> +                                access the hosts HostAddress.
>> >> +  @param  Mapping               A resulting value to pass to Unmap().
>> >> +
>> >> +  @retval EFI_SUCCESS           The range was mapped for the returned NumberOfBytes.
>> >> +  @retval EFI_UNSUPPORTED       The HostAddress cannot be mapped as a common buffer.
>> >> +  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
>> >> +  @retval EFI_OUT_OF_RESOURCES  The request could not be completed due to a lack of resources.
>> >> +  @retval EFI_DEVICE_ERROR      The system hardware could not map the requested address.
>> >> +
>> >> +**/
>> >> +STATIC
>> >> +EFI_STATUS
>> >> +EFIAPI
>> >> +IoMmuMap (
>> >> +  IN     EDKII_IOMMU_PROTOCOL                       *This,
>> >> +  IN     EDKII_IOMMU_OPERATION                      Operation,
>> >> +  IN     VOID                                       *HostAddress,
>> >> +  IN OUT UINTN                                      *NumberOfBytes,
>> >> +  OUT    EFI_PHYSICAL_ADDRESS                       *DeviceAddress,
>> >> +  OUT    VOID                                       **Mapping
>> >> +  )
>> >> +{
>> >> +  EFI_STATUS                                        Status;
>> >> +  EFI_PHYSICAL_ADDRESS                              PhysicalAddress;
>> >> +  MAP_INFO                                          *MapInfo;
>> >> +  BOOLEAN                                           NeedRemap;
>> >> +
>> >> +  if (HostAddress == NULL || NumberOfBytes == NULL || DeviceAddress == NULL ||
>> >> +      Mapping == NULL) {
>> >> +    return EFI_INVALID_PARAMETER;
>> >> +  }
>> >> +
>> >> +  //
>> >> +  // Make sure that Operation is valid
>> >> +  //
>> >> +  if ((UINT32) Operation >= EdkiiIoMmuOperationMaximum) {
>> >> +    return EFI_INVALID_PARAMETER;
>> >> +  }
>> >> +
>> >> +  NeedRemap = FALSE;
>> >> +  PhysicalAddress = (UINTN)HostAddress;
>> >> +
>> >> +  if ((PhysicalAddress + *NumberOfBytes) > mTranslationBase + SIZE_4GB) {
>> >> +    //
>> >> +    // If the root bridge or the device cannot handle performing DMA above
>> >> +    // 4GB but any part of the DMA transfer being mapped is above 4GB, then
>> >> +    // map the DMA transfer to a buffer below 4GB.
>> >> +    //
>> >
>> > Request for clarification: is this because this driver is only invoked
>> > where the root bridge is incapable? (The test looks unconditional.)
>> >
>>
>> No, the root bridge driver always invokes the IOMMU protocol if there is one.
>>
>> Here we test whether the mapped buffer already lives in the first 4 GB
>> of DRAM, otherwise we will use a bounce buffer. This driver never maps
>> anything dynamically in the SMMU, it just allocates a bounce buffer
>> from the 32-bit addressable range if required.
>
> Right, so could the comment be updated to say something more like
>
>     //
>     // If any part of the DMA transfer being mapped is above 4GB, then
>     // map the DMA transfer to a buffer below 4GB.
>     //
>
> ?
>

To be pedantic, it should say 'to a buffer that is 32-bit addressable
from the PCI side'. Will add that

> The remainder of the comment is also useful, but would it not make
> more sense in the function header?
>

Tbh I took Jiewen's code and added the SMMU/ARM bits. But I will
change it nonetheless.

> With that:
> Reviewed-by: Leif Lindholm <leif.lindholm@linaro.org>
>

Thanks


>> >> +    NeedRemap = TRUE;
>> >> +  }
>> >> +
>> >> +  if ((Operation == EdkiiIoMmuOperationBusMasterCommonBuffer ||
>> >> +       Operation == EdkiiIoMmuOperationBusMasterCommonBuffer64) &&
>> >> +      NeedRemap) {
>> >> +    //
>> >> +    // Common Buffer operations can not be remapped.  If the common buffer
>> >> +    // if above 4GB, then it is not possible to generate a mapping, so return
>> >> +    // an error.
>> >> +    //
>> >> +    return EFI_UNSUPPORTED;
>> >> +  }
>> >> +
>> >> +  //
>> >> +  // Allocate a MAP_INFO structure to remember the mapping when Unmap() is
>> >> +  // called later.
>> >> +  //
>> >> +  MapInfo = AllocatePool (sizeof (MAP_INFO));
>> >> +  if (MapInfo == NULL) {
>> >> +    *NumberOfBytes = 0;
>> >> +    return EFI_OUT_OF_RESOURCES;
>> >> +  }
>> >> +
>> >> +  //
>> >> +  // Initialize the MAP_INFO structure
>> >> +  //
>> >> +  MapInfo->Signature         = MAP_INFO_SIGNATURE;
>> >> +  MapInfo->Operation         = Operation;
>> >> +  MapInfo->NumberOfBytes     = *NumberOfBytes;
>> >> +  MapInfo->NumberOfPages     = EFI_SIZE_TO_PAGES (MapInfo->NumberOfBytes);
>> >> +  MapInfo->HostAddress       = PhysicalAddress;
>> >> +  MapInfo->MappedHostAddress = (EFI_PHYSICAL_ADDRESS)-1;
>> >> +
>> >> +  //
>> >> +  // Allocate a buffer below 4GB to map the transfer to.
>> >> +  //
>> >> +  if (NeedRemap) {
>> >> +    MapInfo->MappedHostAddress = mTranslationBase + SIZE_4GB - 1;
>> >> +    Status = gBS->AllocatePages (
>> >> +                    AllocateMaxAddress,
>> >> +                    EfiBootServicesData,
>> >> +                    MapInfo->NumberOfPages,
>> >> +                    &MapInfo->MappedHostAddress
>> >> +                    );
>> >> +    if (EFI_ERROR (Status)) {
>> >> +      FreePool (MapInfo);
>> >> +      *NumberOfBytes = 0;
>> >> +      return Status;
>> >> +    }
>> >> +
>> >> +    //
>> >> +    // If this is a read operation from the Bus Master's point of view,
>> >> +    // then copy the contents of the real buffer into the mapped buffer
>> >> +    // so the Bus Master can read the contents of the real buffer.
>> >> +    //
>> >> +    if (Operation == EdkiiIoMmuOperationBusMasterRead ||
>> >> +        Operation == EdkiiIoMmuOperationBusMasterRead64) {
>> >> +      CopyMem (
>> >> +        (VOID *)(UINTN)MapInfo->MappedHostAddress,
>> >> +        (VOID *)(UINTN)MapInfo->HostAddress,
>> >> +        MapInfo->NumberOfBytes
>> >> +        );
>> >> +    }
>> >> +  } else {
>> >> +    MapInfo->MappedHostAddress = MapInfo->HostAddress;
>> >> +  }
>> >> +
>> >> +  InsertTailList (&mMaps, &MapInfo->Link);
>> >> +
>> >> +  //
>> >> +  // The DeviceAddress is the address of the maped buffer below 4GB
>> >> +  //
>> >> +  *DeviceAddress = MapInfo->MappedHostAddress - mTranslationBase;
>> >> +
>> >> +  //
>> >> +  // Return a pointer to the MAP_INFO structure in Mapping
>> >> +  //
>> >> +  *Mapping       = MapInfo;
>> >> +
>> >> +  return EFI_SUCCESS;
>> >> +}
>> >> +
>> >> +/**
>> >> +  Completes the Map() operation and releases any corresponding resources.
>> >> +
>> >> +  @param  This                  The protocol instance pointer.
>> >> +  @param  Mapping               The mapping value returned from Map().
>> >> +
>> >> +  @retval EFI_SUCCESS           The range was unmapped.
>> >> +  @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by Map().
>> >> +  @retval EFI_DEVICE_ERROR      The data was not committed to the target system memory.
>> >> +**/
>> >> +STATIC
>> >> +EFI_STATUS
>> >> +EFIAPI
>> >> +IoMmuUnmap (
>> >> +  IN  EDKII_IOMMU_PROTOCOL                     *This,
>> >> +  IN  VOID                                     *Mapping
>> >> +  )
>> >> +{
>> >> +  MAP_INFO                 *MapInfo;
>> >> +  LIST_ENTRY               *Link;
>> >> +
>> >> +  if (Mapping == NULL) {
>> >> +    return EFI_INVALID_PARAMETER;
>> >> +  }
>> >> +
>> >> +  MapInfo = NULL;
>> >> +  for (Link = GetFirstNode (&mMaps)
>> >> +       ; !IsNull (&mMaps, Link)
>> >> +       ; Link = GetNextNode (&mMaps, Link)
>> >> +       ) {
>> >> +    MapInfo = MAP_INFO_FROM_LINK (Link);
>> >> +    if (MapInfo == Mapping) {
>> >> +      break;
>> >> +    }
>> >> +  }
>> >> +  //
>> >> +  // Mapping is not a valid value returned by Map()
>> >> +  //
>> >> +  if (MapInfo != Mapping) {
>> >> +    return EFI_INVALID_PARAMETER;
>> >> +  }
>> >> +  RemoveEntryList (&MapInfo->Link);
>> >> +
>> >> +  if (MapInfo->MappedHostAddress != MapInfo->HostAddress) {
>> >> +    //
>> >> +    // If this is a write operation from the Bus Master's point of view,
>> >> +    // then copy the contents of the mapped buffer into the real buffer
>> >> +    // so the processor can read the contents of the real buffer.
>> >> +    //
>> >> +    if (MapInfo->Operation == EdkiiIoMmuOperationBusMasterWrite ||
>> >> +        MapInfo->Operation == EdkiiIoMmuOperationBusMasterWrite64) {
>> >> +      CopyMem (
>> >> +        (VOID *)(UINTN)MapInfo->HostAddress,
>> >> +        (VOID *)(UINTN)MapInfo->MappedHostAddress,
>> >> +        MapInfo->NumberOfBytes
>> >> +        );
>> >> +    }
>> >> +
>> >> +    //
>> >> +    // Free the mapped buffer and the MAP_INFO structure.
>> >> +    //
>> >> +    gBS->FreePages (MapInfo->MappedHostAddress, MapInfo->NumberOfPages);
>> >> +  }
>> >> +
>> >> +  FreePool (Mapping);
>> >> +  return EFI_SUCCESS;
>> >> +}
>> >> +
>> >> +/**
>> >> +  Allocates pages that are suitable for an OperationBusMasterCommonBuffer or
>> >> +  OperationBusMasterCommonBuffer64 mapping.
>> >> +
>> >> +  @param  This                  The protocol instance pointer.
>> >> +  @param  Type                  This parameter is not used and must be ignored.
>> >> +  @param  MemoryType            The type of memory to allocate, EfiBootServicesData or
>> >> +                                EfiRuntimeServicesData.
>> >> +  @param  Pages                 The number of pages to allocate.
>> >> +  @param  HostAddress           A pointer to store the base system memory address of the
>> >> +                                allocated range.
>> >> +  @param  Attributes            The requested bit mask of attributes for the allocated range.
>> >> +
>> >> +  @retval EFI_SUCCESS           The requested memory pages were allocated.
>> >> +  @retval EFI_UNSUPPORTED       Attributes is unsupported. The only legal attribute bits are
>> >> +                                MEMORY_WRITE_COMBINE and MEMORY_CACHED.
>> >> +  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
>> >> +  @retval EFI_OUT_OF_RESOURCES  The memory pages could not be allocated.
>> >> +
>> >> +**/
>> >> +STATIC
>> >> +EFI_STATUS
>> >> +EFIAPI
>> >> +IoMmuAllocateBuffer (
>> >> +  IN     EDKII_IOMMU_PROTOCOL                     *This,
>> >> +  IN     EFI_ALLOCATE_TYPE                        Type,
>> >> +  IN     EFI_MEMORY_TYPE                          MemoryType,
>> >> +  IN     UINTN                                    Pages,
>> >> +  IN OUT VOID                                     **HostAddress,
>> >> +  IN     UINT64                                   Attributes
>> >> +  )
>> >> +{
>> >> +  EFI_STATUS                Status;
>> >> +  EFI_PHYSICAL_ADDRESS      PhysicalAddress;
>> >> +
>> >> +  //
>> >> +  // Validate Attributes
>> >> +  //
>> >> +  if ((Attributes & EDKII_IOMMU_ATTRIBUTE_INVALID_FOR_ALLOCATE_BUFFER) != 0) {
>> >> +    return EFI_UNSUPPORTED;
>> >> +  }
>> >> +
>> >> +  //
>> >> +  // Check for invalid inputs
>> >> +  //
>> >> +  if (HostAddress == NULL) {
>> >> +    return EFI_INVALID_PARAMETER;
>> >> +  }
>> >> +
>> >> +  //
>> >> +  // The only valid memory types are EfiBootServicesData and
>> >> +  // EfiRuntimeServicesData
>> >> +  //
>> >> +  if (MemoryType != EfiBootServicesData &&
>> >> +      MemoryType != EfiRuntimeServicesData) {
>> >> +    return EFI_INVALID_PARAMETER;
>> >> +  }
>> >> +
>> >> +  //
>> >> +  // Limit allocations to memory covered by the remapped window.
>> >> +  //
>> >> +  PhysicalAddress = mTranslationBase + SIZE_4GB - 1;
>> >> +  Status = gBS->AllocatePages (
>> >> +                  AllocateMaxAddress,
>> >> +                  MemoryType,
>> >> +                  Pages,
>> >> +                  &PhysicalAddress
>> >> +                  );
>> >> +  if (!EFI_ERROR (Status)) {
>> >> +    *HostAddress = (VOID *)(UINTN)PhysicalAddress;
>> >> +  }
>> >> +
>> >> +  return Status;
>> >> +}
>> >> +
>> >> +/**
>> >> +  Frees memory that was allocated with AllocateBuffer().
>> >> +
>> >> +  @param  This                  The protocol instance pointer.
>> >> +  @param  Pages                 The number of pages to free.
>> >> +  @param  HostAddress           The base system memory address of the allocated range.
>> >> +
>> >> +  @retval EFI_SUCCESS           The requested memory pages were freed.
>> >> +  @retval EFI_INVALID_PARAMETER The memory range specified by HostAddress and Pages
>> >> +                                was not allocated with AllocateBuffer().
>> >> +
>> >> +**/
>> >> +STATIC
>> >> +EFI_STATUS
>> >> +EFIAPI
>> >> +IoMmuFreeBuffer (
>> >> +  IN  EDKII_IOMMU_PROTOCOL                     *This,
>> >> +  IN  UINTN                                    Pages,
>> >> +  IN  VOID                                     *HostAddress
>> >> +  )
>> >> +{
>> >> +  return gBS->FreePages ((EFI_PHYSICAL_ADDRESS) (UINTN) HostAddress, Pages);
>> >> +}
>> >> +
>> >> +/**
>> >> +  Set IOMMU attribute for a system memory.
>> >> +
>> >> +  If the IOMMU protocol exists, the system memory cannot be used
>> >> +  for DMA by default.
>> >> +
>> >> +  When a device requests a DMA access for a system memory,
>> >> +  the device driver need use SetAttribute() to update the IOMMU
>> >> +  attribute to request DMA access (read and/or write).
>> >> +
>> >> +  The DeviceHandle is used to identify which device submits the request.
>> >> +  The IOMMU implementation need translate the device path to an IOMMU device ID,
>> >> +  and set IOMMU hardware register accordingly.
>> >> +  1) DeviceHandle can be a standard PCI device.
>> >> +     The memory for BusMasterRead need set EDKII_IOMMU_ACCESS_READ.
>> >> +     The memory for BusMasterWrite need set EDKII_IOMMU_ACCESS_WRITE.
>> >> +     The memory for BusMasterCommonBuffer need set EDKII_IOMMU_ACCESS_READ|EDKII_IOMMU_ACCESS_WRITE.
>> >> +     After the memory is used, the memory need set 0 to keep it being protected.
>> >> +  2) DeviceHandle can be an ACPI device (ISA, I2C, SPI, etc).
>> >> +     The memory for DMA access need set EDKII_IOMMU_ACCESS_READ and/or EDKII_IOMMU_ACCESS_WRITE.
>> >> +
>> >> +  @param[in]  This              The protocol instance pointer.
>> >> +  @param[in]  DeviceHandle      The device who initiates the DMA access request.
>> >> +  @param[in]  DeviceAddress     The base of device memory address to be used as the DMA memory.
>> >> +  @param[in]  Length            The length of device memory address to be used as the DMA memory.
>> >> +  @param[in]  IoMmuAccess       The IOMMU access.
>> >> +
>> >> +  @retval EFI_SUCCESS            The IoMmuAccess is set for the memory range specified by DeviceAddress and Length.
>> >> +  @retval EFI_INVALID_PARAMETER  DeviceHandle is an invalid handle.
>> >> +  @retval EFI_INVALID_PARAMETER  DeviceAddress is not IoMmu Page size aligned.
>> >> +  @retval EFI_INVALID_PARAMETER  Length is not IoMmu Page size aligned.
>> >> +  @retval EFI_INVALID_PARAMETER  Length is 0.
>> >> +  @retval EFI_INVALID_PARAMETER  IoMmuAccess specified an illegal combination of access.
>> >> +  @retval EFI_UNSUPPORTED        DeviceHandle is unknown by the IOMMU.
>> >> +  @retval EFI_UNSUPPORTED        The bit mask of IoMmuAccess is not supported by the IOMMU.
>> >> +  @retval EFI_UNSUPPORTED        The IOMMU does not support the memory range specified by DeviceAddress and Length.
>> >> +  @retval EFI_OUT_OF_RESOURCES   There are not enough resources available to modify the IOMMU access.
>> >> +  @retval EFI_DEVICE_ERROR       The IOMMU device reported an error while attempting the operation.
>> >> +
>> >> +**/
>> >> +EFI_STATUS
>> >> +EFIAPI
>> >> +IoMmuSetAttribute (
>> >> +  IN EDKII_IOMMU_PROTOCOL  *This,
>> >> +  IN EFI_HANDLE            DeviceHandle,
>> >> +  IN EFI_PHYSICAL_ADDRESS  DeviceAddress,
>> >> +  IN UINT64                Length,
>> >> +  IN UINT64                IoMmuAccess
>> >> +  )
>> >> +{
>> >> +  return EFI_UNSUPPORTED;
>> >> +}
>> >> +
>> >> +/**
>> >> +  Set IOMMU attribute for a system memory.
>> >> +
>> >> +  If the IOMMU protocol exists, the system memory cannot be used
>> >> +  for DMA by default.
>> >> +
>> >> +  When a device requests a DMA access for a system memory,
>> >> +  the device driver need use SetAttribute() to update the IOMMU
>> >> +  attribute to request DMA access (read and/or write).
>> >> +
>> >> +  The DeviceHandle is used to identify which device submits the request.
>> >> +  The IOMMU implementation need translate the device path to an IOMMU device ID,
>> >> +  and set IOMMU hardware register accordingly.
>> >> +  1) DeviceHandle can be a standard PCI device.
>> >> +     The memory for BusMasterRead need set EDKII_IOMMU_ACCESS_READ.
>> >> +     The memory for BusMasterWrite need set EDKII_IOMMU_ACCESS_WRITE.
>> >> +     The memory for BusMasterCommonBuffer need set EDKII_IOMMU_ACCESS_READ|EDKII_IOMMU_ACCESS_WRITE.
>> >> +     After the memory is used, the memory need set 0 to keep it being protected.
>> >> +  2) DeviceHandle can be an ACPI device (ISA, I2C, SPI, etc).
>> >> +     The memory for DMA access need set EDKII_IOMMU_ACCESS_READ and/or EDKII_IOMMU_ACCESS_WRITE.
>> >> +
>> >> +  @param[in]  This              The protocol instance pointer.
>> >> +  @param[in]  DeviceHandle      The device who initiates the DMA access request.
>> >> +  @param[in]  Mapping           The mapping value returned from Map().
>> >> +  @param[in]  IoMmuAccess       The IOMMU access.
>> >> +
>> >> +  @retval EFI_SUCCESS            The IoMmuAccess is set for the memory range specified by DeviceAddress and Length.
>> >> +  @retval EFI_INVALID_PARAMETER  DeviceHandle is an invalid handle.
>> >> +  @retval EFI_INVALID_PARAMETER  Mapping is not a value that was returned by Map().
>> >> +  @retval EFI_INVALID_PARAMETER  IoMmuAccess specified an illegal combination of access.
>> >> +  @retval EFI_UNSUPPORTED        DeviceHandle is unknown by the IOMMU.
>> >> +  @retval EFI_UNSUPPORTED        The bit mask of IoMmuAccess is not supported by the IOMMU.
>> >> +  @retval EFI_UNSUPPORTED        The IOMMU does not support the memory range specified by Mapping.
>> >> +  @retval EFI_OUT_OF_RESOURCES   There are not enough resources available to modify the IOMMU access.
>> >> +  @retval EFI_DEVICE_ERROR       The IOMMU device reported an error while attempting the operation.
>> >> +
>> >> +**/
>> >> +STATIC
>> >> +EFI_STATUS
>> >> +EFIAPI
>> >> +IoMmuSetMappingAttribute (
>> >> +  IN EDKII_IOMMU_PROTOCOL  *This,
>> >> +  IN EFI_HANDLE            DeviceHandle,
>> >> +  IN VOID                  *Mapping,
>> >> +  IN UINT64                IoMmuAccess
>> >> +  )
>> >> +{
>> >> +  //
>> >> +  // We only support a static remapping of DRAM into the PCI address space
>> >> +  // so there is nothing we need to do to handle invocations of this protocol
>> >> +  // method.
>> >> +  //
>> >> +  return EFI_SUCCESS;
>> >> +}
>> >> +
>> >> +EDKII_IOMMU_PROTOCOL  mGenericSmmuIommuProtocol = {
>> >> +  EDKII_IOMMU_PROTOCOL_REVISION,
>> >> +  IoMmuSetAttribute,
>> >> +  IoMmuMap,
>> >> +  IoMmuUnmap,
>> >> +  IoMmuAllocateBuffer,
>> >> +  IoMmuFreeBuffer,
>> >> +  IoMmuSetMappingAttribute,
>> >> +};
>> >> diff --git a/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.c b/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.c
>> >> new file mode 100644
>> >> index 000000000000..8f5093af14ea
>> >> --- /dev/null
>> >> +++ b/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.c
>> >> @@ -0,0 +1,323 @@
>> >> +/** @file
>> >> +
>> >> +  Copyright (c) 2017, Linaro Ltd. All rights reserved.<BR>
>> >> +
>> >> +  This program and the accompanying materials
>> >> +  are licensed and made available under the terms and conditions of the BSD License
>> >> +  which accompanies this distribution.  The full text of the license may be found at
>> >> +  http://opensource.org/licenses/bsd-license.php
>> >> +
>> >> +  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
>> >> +  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
>> >> +
>> >> +**/
>> >> +
>> >> +#include <PiDxe.h>
>> >> +#include <Library/BaseLib.h>
>> >> +#include <Library/DebugLib.h>
>> >> +#include <Library/IoLib.h>
>> >> +#include <Library/UefiBootServicesTableLib.h>
>> >> +
>> >> +#include <Protocol/Cpu.h>
>> >> +#include <Protocol/HardwareInterrupt.h>
>> >> +#include <Protocol/IoMmu.h>
>> >> +
>> >> +#include <Chipset/AArch64Mmu.h>
>> >> +
>> >> +#define GL_CR0                          0x0
>> >> +#define GL_CR0_CLIENTPD                 BIT0
>> >> +
>> >> +#define GL_IDR0                         0x20
>> >> +#define GL_IDR1                         0x24
>> >> +#define GL_STLBIALL                     0x60
>> >> +#define GL_TLBIALLNSNH                  0x68
>> >> +#define GL_SMR0                         0x800
>> >> +#define GL_S2CR0                        0xc00
>> >> +#define GL_CBA2R0                       0x1800
>> >> +
>> >> +#define GL_IDR0_NUMSMRG_MASK            0xff
>> >> +
>> >> +#define GL_IDR1_NUMPAGENDXB_MASK        0x7
>> >> +#define GL_IDR1_NUMPAGENDXB_SHIFT       28
>> >> +#define GL_IDR1_PAGE_SIZE_64KB          BIT31
>> >> +
>> >> +#define CB_BASE(i)                      (mContextBankOffset + ((i) * SIZE_4KB))
>> >> +
>> >> +#define CB_SCTLR_OFFSET                 0x0
>> >> +#define CB_TTBR0_OFFSET                 0x20
>> >> +#define CB_TTBCR_OFFSET                 0x30
>> >> +
>> >> +#define CB_FAR                          0x60
>> >> +#define CB_FSR                          0x58
>> >> +#define CB_FSYNR0                       0x68
>> >> +#define CB_FSYNR1                       0x6c
>> >> +
>> >> +#define TT_S2_MEMATTR_CACHED            (0xF << 2)
>> >> +#define TT_S2_AP_READ_WRITE             (0x3 << 6)
>> >> +
>> >> +#define TT_ENTRY_ATTRIBUTES             (TT_TYPE_BLOCK_ENTRY | \
>> >> +                                         TT_SH_INNER_SHAREABLE | \
>> >> +                                         TT_S2_AP_READ_WRITE | \
>> >> +                                         TT_S2_MEMATTR_CACHED | \
>> >> +                                         TT_AF)
>> >> +
>> >> +#define TCR_T0SZ(bits)                  ((UINT32)(32 - (bits)) & 0x3f)
>> >> +#define TCR_SL0_LEVEL1                  BIT6
>> >> +#define TCR_SL0_LEVEL2                  0
>> >> +
>> >> +#define SCTLR_M_ENABLE                  BIT0
>> >> +#define SCTLR_TR_ENABLE                 BIT1
>> >> +#define SCTLR_AF_ENABLE                 BIT2
>> >> +#define SCTLR_CTX_FAULT_ENABLE          BIT5
>> >> +#define SCTLR_CTX_INT_ENABLE            BIT6
>> >> +
>> >> +#define DRAM_BASE                       FixedPcdGet64 (PcdSystemMemoryBase)
>> >> +
>> >> +extern EDKII_IOMMU_PROTOCOL             mGenericSmmuIommuProtocol;
>> >> +
>> >> +//
>> >> +// Create a static stage 2 mapping of the first 4 GB of DRAM in the start
>> >> +// of the IOVA space, and as an ID mapping at the original offset.
>> >> +//
>> >> +STATIC CONST UINT64 mPciTranslation[1024] __attribute__((aligned(SIZE_8KB))) = {
>> >> +  [0] = TT_ENTRY_ATTRIBUTES | (DRAM_BASE),
>> >> +  [1] = TT_ENTRY_ATTRIBUTES | (DRAM_BASE + SIZE_1GB),
>> >> +  [2] = TT_ENTRY_ATTRIBUTES | (DRAM_BASE + SIZE_1GB * 2UL),
>> >> +  [3] = TT_ENTRY_ATTRIBUTES | (DRAM_BASE + SIZE_1GB * 3UL),
>> >> +
>> >> +  //
>> >> +  // The ID mapping of the first 4 GB of DRAM is a workaround for buggy
>> >> +  // drivers that violate the UEFI spec by ignoring the device address
>> >> +  // returned by the PCI I/O map/unmap routines, and program host
>> >> +  // addresses into the DMA h/w registers or rings instead.
>> >> +  //
>> >> +  [(DRAM_BASE >> 30)]     = TT_ENTRY_ATTRIBUTES | (DRAM_BASE),
>> >> +  [(DRAM_BASE >> 30) + 1] = TT_ENTRY_ATTRIBUTES | (DRAM_BASE + SIZE_1GB),
>> >> +  [(DRAM_BASE >> 30) + 2] = TT_ENTRY_ATTRIBUTES | (DRAM_BASE + SIZE_1GB * 2UL),
>> >> +  [(DRAM_BASE >> 30) + 3] = TT_ENTRY_ATTRIBUTES | (DRAM_BASE + SIZE_1GB * 3UL),
>> >> +};
>> >> +
>> >> +STATIC EFI_HARDWARE_INTERRUPT_PROTOCOL    *mInterrupt;
>> >> +STATIC EFI_EVENT                          mEfiExitBootServicesEvent;
>> >> +STATIC UINTN                              mContextBankOffset;
>> >> +
>> >> +STATIC
>> >> +UINT32
>> >> +ReadGlobalReg32 (
>> >> +  IN  UINT64    Offset
>> >> +  )
>> >> +{
>> >> +  return MmioRead32 (FixedPcdGet64 (PcdPciGenericSmmuBase) + Offset);
>> >> +}
>> >> +
>> >> +STATIC
>> >> +VOID
>> >> +WriteGlobalReg32 (
>> >> +  IN  UINT64    Offset,
>> >> +  IN  UINT64    Value
>> >> +  )
>> >> +{
>> >> +  MmioWrite32 (FixedPcdGet64 (PcdPciGenericSmmuBase) + Offset, Value);
>> >> +}
>> >> +
>> >> +STATIC
>> >> +UINT32
>> >> +ReadCbReg32 (
>> >> +  IN  UINTN     Bank,
>> >> +  IN  UINT64    Offset
>> >> +  )
>> >> +{
>> >> +  return MmioRead32 (FixedPcdGet64 (PcdPciGenericSmmuBase) + CB_BASE(Bank) +
>> >> +                     Offset);
>> >> +}
>> >> +
>> >> +STATIC
>> >> +VOID
>> >> +WriteCbReg32 (
>> >> +  IN  UINTN     Bank,
>> >> +  IN  UINT64    Offset,
>> >> +  IN  UINT32    Value
>> >> +  )
>> >> +{
>> >> +  MmioWrite32 (FixedPcdGet64 (PcdPciGenericSmmuBase) + CB_BASE(Bank) + Offset,
>> >> +               Value);
>> >> +}
>> >> +
>> >> +STATIC
>> >> +UINT64
>> >> +ReadCbReg64 (
>> >> +  IN  UINTN     Bank,
>> >> +  IN  UINT64    Offset
>> >> +  )
>> >> +{
>> >> +  return MmioRead64 (FixedPcdGet64 (PcdPciGenericSmmuBase) + CB_BASE(Bank) +
>> >> +                     Offset);
>> >> +}
>> >> +
>> >> +STATIC
>> >> +VOID
>> >> +WriteCbReg64 (
>> >> +  IN  UINTN     Bank,
>> >> +  IN  UINT64    Offset,
>> >> +  IN  UINT64    Value
>> >> +  )
>> >> +{
>> >> +  MmioWrite64 (FixedPcdGet64 (PcdPciGenericSmmuBase) + CB_BASE(Bank) + Offset,
>> >> +               Value);
>> >> +}
>> >> +
>> >> +STATIC
>> >> +VOID
>> >> +EFIAPI
>> >> +ContextInterruptHandler (
>> >> +  IN  HARDWARE_INTERRUPT_SOURCE   Source,
>> >> +  IN  EFI_SYSTEM_CONTEXT          SystemContext
>> >> +  )
>> >> +{
>> >> +  //
>> >> +  // Dump the SMMU context fault registers when taking a context interrupt
>> >> +  //
>> >> +  DEBUG ((DEBUG_WARN,
>> >> +    "Context interrupt asserted by SMMU:\n\n"
>> >> +    "SMMU_CB0_FAR        0x%016llx \n"
>> >> +    "SMMU_CB0_FSR        0x%08llx \n"
>> >> +    "SMMU_CB0_FSYNR0     0x%08llx \n"
>> >> +    "SMMU_CB0_FSYNR1     0x%08llx \n",
>> >> +    ReadCbReg64 (0, CB_FAR),
>> >> +    ReadCbReg32 (0, CB_FSR),
>> >> +    ReadCbReg32 (0, CB_FSYNR0),
>> >> +    ReadCbReg32 (0, CB_FSYNR1)));
>> >> +
>> >> +  mInterrupt->EndOfInterrupt (mInterrupt, Source);
>> >> +}
>> >> +
>> >> +STATIC
>> >> +VOID
>> >> +EFIAPI
>> >> +ExitBootServicesEvent (
>> >> +  IN EFI_EVENT  Event,
>> >> +  IN VOID       *Context
>> >> +  )
>> >> +{
>> >> +  //
>> >> +  // Put the SMMU back into bypass mode
>> >> +  //
>> >> +  MmioOr32 (FixedPcdGet64 (PcdPciGenericSmmuBase) + GL_CR0, GL_CR0_CLIENTPD);
>> >> +}
>> >> +
>> >> +EFI_STATUS
>> >> +GenericSmmuStaticPciDmaDxeInitialize (
>> >> +  IN EFI_HANDLE        ImageHandle,
>> >> +  IN EFI_SYSTEM_TABLE  *SystemTable
>> >> +  )
>> >> +{
>> >> +  EFI_STATUS        Status;
>> >> +  UINTN             Idx;
>> >> +  UINT32            IdVal;
>> >> +  UINTN             NumStreamMappingRegisters;
>> >> +
>> >> +  //
>> >> +  // The static mapping uses 1 GB block mappings, whose VAs and PAs should
>> >> +  // be equal modulo the block size.
>> >> +  //
>> >> +  ASSERT ((DRAM_BASE % SIZE_1GB) == 0);
>> >> +
>> >> +  if ((DRAM_BASE & (DRAM_BASE - 1)) != 0) {
>> >> +    //
>> >> +    // Buggy drivers that use truncated host addresses instead of device
>> >> +    // addresses for DMA may still work correctly if such truncation is
>> >> +    // guaranteed to produce the remapped alias. This is the case if
>> >> +    // DRAM_BASE is a power of 2.
>> >> +    //
>> >> +    DEBUG ((DEBUG_WARN,
>> >> +      "%a: this driver will work better if DRAM_BASE is a power of 2!\n",
>> >> +      __FUNCTION__));
>> >> +  }
>> >> +
>> >> +  Status = gBS->LocateProtocol (&gHardwareInterruptProtocolGuid, NULL,
>> >> +                  (VOID **)&mInterrupt);
>> >> +  ASSERT_EFI_ERROR (Status);
>> >> +
>> >> +  Status = mInterrupt->RegisterInterruptSource (mInterrupt,
>> >> +                         PcdGet16 (PcdPciGenericSmmuContextInterrupt),
>> >> +                         ContextInterruptHandler);
>> >> +  ASSERT_EFI_ERROR (Status);
>> >> +  if (EFI_ERROR (Status)) {
>> >> +    return Status;
>> >> +  }
>> >> +
>> >> +  Status = gBS->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES,
>> >> +                  TPL_NOTIFY, ExitBootServicesEvent, NULL,
>> >> +                  &mEfiExitBootServicesEvent);
>> >> +  ASSERT_EFI_ERROR (Status);
>> >> +
>> >> +  Status = gBS->InstallMultipleProtocolInterfaces (
>> >> +                  &ImageHandle,
>> >> +                  &gEdkiiIoMmuProtocolGuid, &mGenericSmmuIommuProtocol,
>> >> +                  NULL
>> >> +                  );
>> >> +  ASSERT_EFI_ERROR (Status);
>> >> +
>> >> +  IdVal = ReadGlobalReg32 (GL_IDR1);
>> >> +  mContextBankOffset = (IdVal & GL_IDR1_PAGE_SIZE_64KB) ? SIZE_64KB : SIZE_4KB;
>> >> +  mContextBankOffset <<= (1 + ((IdVal >> GL_IDR1_NUMPAGENDXB_SHIFT) &
>> >> +                               GL_IDR1_NUMPAGENDXB_MASK));
>> >> +
>> >> +  //
>> >> +  // Clear all stream mappings
>> >> +  //
>> >> +  NumStreamMappingRegisters = ReadGlobalReg32 (GL_IDR0) & GL_IDR0_NUMSMRG_MASK;
>> >> +  for (Idx = 0; Idx < NumStreamMappingRegisters; Idx++) {
>> >> +    WriteGlobalReg32 (GL_SMR0 + Idx * sizeof(UINT32), 0x0);
>> >> +    WriteGlobalReg32 (GL_S2CR0 + Idx * sizeof(UINT32), 0x0);
>> >> +  }
>> >> +
>> >> +  //
>> >> +  // Set stream match register 0 to match all streams, and map onto
>> >> +  // context bank 0
>> >> +  //
>> >> +  WriteGlobalReg32 (GL_SMR0, 0xffff0000);
>> >> +  WriteGlobalReg32 (GL_S2CR0, 0x0);
>> >> +
>> >> +  //
>> >> +  // Disable the context bank
>> >> +  //
>> >> +  WriteCbReg32 (0, CB_SCTLR_OFFSET, 0);
>> >> +
>> >> +  //
>> >> +  // Assign the translation base register for context bank 0
>> >> +  //
>> >> +  WriteCbReg64 (0, CB_TTBR0_OFFSET, (UINTN)mPciTranslation);
>> >> +
>> >> +  //
>> >> +  // Flush TLBS.
>> >> +  //
>> >> +  WriteGlobalReg32 (GL_STLBIALL, 0);
>> >> +  WriteGlobalReg32 (GL_TLBIALLNSNH, 0);
>> >> +
>> >> +  //
>> >> +  // Configure the size of the translation space, the number of levels,
>> >> +  // and the cacheability attributes of the PTW memory accesses.
>> >> +  //
>> >> +  WriteCbReg32 (0, CB_TTBCR_OFFSET, TCR_T0SZ(40) |
>> >> +                                    TCR_SH_INNER_SHAREABLE |
>> >> +                                    TCR_RGN_INNER_WRITE_BACK_NO_ALLOC |
>> >> +                                    TCR_RGN_OUTER_WRITE_BACK_NO_ALLOC |
>> >> +                                    TCR_SL0_LEVEL1);
>> >> +
>> >> +  //
>> >> +  // Enable the context bank
>> >> +  //
>> >> +  WriteCbReg32 (0, CB_SCTLR_OFFSET, SCTLR_TR_ENABLE |
>> >> +                                    SCTLR_AF_ENABLE |
>> >> +                                    SCTLR_CTX_INT_ENABLE |
>> >> +                                    SCTLR_CTX_FAULT_ENABLE |
>> >> +                                    SCTLR_M_ENABLE);
>> >> +
>> >> +  //
>> >> +  // Get the SMMU out of bypass mode
>> >> +  //
>> >> +  MmioAnd32 (FixedPcdGet64 (PcdPciGenericSmmuBase) + GL_CR0, ~GL_CR0_CLIENTPD);
>> >> +
>> >> +  return EFI_SUCCESS;
>> >> +}
>> >> diff --git a/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.inf b/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.inf
>> >> new file mode 100644
>> >> index 000000000000..02c17e755c4a
>> >> --- /dev/null
>> >> +++ b/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.inf
>> >> @@ -0,0 +1,62 @@
>> >> +## @file
>> >> +#
>> >> +# Copyright (c) 2017, Linaro Ltd. All rights reserved.<BR>
>> >> +#
>> >> +# This program and the accompanying materials
>> >> +# are licensed and made available under the terms and conditions of the BSD License
>> >> +# which accompanies this distribution.  The full text of the license may be found at
>> >> +# http://opensource.org/licenses/bsd-license.php
>> >> +#
>> >> +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
>> >> +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
>> >> +#
>> >> +##
>> >> +
>> >> +[Defines]
>> >> +  INF_VERSION                    = 0x00010019
>> >> +  BASE_NAME                      = GenericSmmuStaticPciDmaDxe
>> >> +  FILE_GUID                      = 59b5e69f-88b7-4632-a2ab-6abe6bdedda2
>> >> +  MODULE_TYPE                    = DXE_DRIVER
>> >> +  VERSION_STRING                 = 1.0
>> >> +  ENTRY_POINT                    = GenericSmmuStaticPciDmaDxeInitialize
>> >> +
>> >> +#
>> >> +# The following information is for reference only and not required by the build tools.
>> >> +#
>> >> +#  VALID_ARCHITECTURES           = ARM AARCH64
>> >> +#
>> >> +#
>> >> +
>> >> +[Sources]
>> >> +  GenericSmmuStaticPciDmaDxe.c
>> >> +  BmDma.c
>> >> +
>> >> +[Packages]
>> >> +  ArmPkg/ArmPkg.dec
>> >> +  EmbeddedPkg/EmbeddedPkg.dec
>> >> +  MdeModulePkg/MdeModulePkg.dec
>> >> +  MdePkg/MdePkg.dec
>> >> +
>> >> +[LibraryClasses]
>> >> +  DebugLib
>> >> +  BaseLib
>> >> +  BaseMemoryLib
>> >> +  IoLib
>> >> +  MemoryAllocationLib
>> >> +  UefiBootServicesTableLib
>> >> +  UefiDriverEntryPoint
>> >> +
>> >> +[Protocols]
>> >> +  gEdkiiIoMmuProtocolGuid                     ## PRODUCES
>> >> +  gEfiPciIoProtocolGuid                       ## CONSUMES
>> >> +  gHardwareInterruptProtocolGuid              ## CONSUMES
>> >> +
>> >> +[Pcd]
>> >> +  gArmTokenSpaceGuid.PcdPciGenericSmmuBase
>> >> +  gArmTokenSpaceGuid.PcdPciGenericSmmuContextInterrupt
>> >> +
>> >> +[FixedPcd]
>> >> +  gArmTokenSpaceGuid.PcdSystemMemoryBase
>> >> +
>> >> +[Depex]
>> >> +  gHardwareInterruptProtocolGuid
>> >> --
>> >> 2.9.3
>> >>
_______________________________________________
edk2-devel mailing list
edk2-devel@lists.01.org
https://lists.01.org/mailman/listinfo/edk2-devel