On 2 May 2017 at 11:32, Ard Biesheuvel <[email protected]> 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 <[email protected]> > --- >
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 [email protected] https://lists.01.org/mailman/listinfo/edk2-devel

