From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-wm0-x22a.google.com (mail-wm0-x22a.google.com [IPv6:2a00:1450:400c:c09::22a]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id 6CF3B21969F93 for ; Wed, 24 May 2017 03:31:03 -0700 (PDT) Received: by mail-wm0-x22a.google.com with SMTP id e127so68481981wmg.1 for ; Wed, 24 May 2017 03:31:03 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=date:from:to:cc:subject:message-id:references:mime-version :content-disposition:in-reply-to:user-agent; bh=nY3548tjeoxVGg/hCxQt3QtHiS6o/cwkH6VfvB/wC+k=; b=SMGtmi3Kx5HnLuIYvOLeiC8UVHsKIZexWJzUXwBO8apxQO4Oohv97MJQGsuCFLRI4+ 41PM+jmYI+WLI/f3IJ2zDKKURWgVXlOqHNiPo7NBshqfD73o8T3Ptd+EJZJl0IGvx1vK FAXk/RaJwOfXWVY6W+URW2vUx5rHQvoIsjz1U= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:from:to:cc:subject:message-id:references :mime-version:content-disposition:in-reply-to:user-agent; bh=nY3548tjeoxVGg/hCxQt3QtHiS6o/cwkH6VfvB/wC+k=; b=homXUlYxbUL5yOWjMjbBbZvSXWj4x1P9xPJLOAHJDAA5HDEccl4IzpmXiMNw0SGXE2 Xec1hoWvn1QM7AIhsLRGU9D3vPsQ4js+DqsgKe1ibya5zRMIFZnQvmznVgyWqpDlvLDP 8UrSmNlswxAYTOq1X20pqzjNXkdHZDg27yqjd47nmD3N3KindTsObNw+/i2lpdyDTvhp bCNSW9reJyfdIt7O+70PPFAhLVE22zVQJbCtnYL6N4SG9CJIn2nagVrV0TazP2R4F4DK 8Z2kIcvV3997je0L0IZcsbFFvHw3n3HzGd9sveb6PLC8y3ILSRP3BmQ0c8u+7lCMY75f bzXw== X-Gm-Message-State: AODbwcBX9zGAImH1u096Rz55GU9Miimc+Tpj+U6aM9EoffWMmzBgJn6y yWANZjYgUkncbhq4 X-Received: by 10.223.138.137 with SMTP id y9mr18913986wry.26.1495621861094; Wed, 24 May 2017 03:31:01 -0700 (PDT) Received: from bivouac.eciton.net (bivouac.eciton.net. [2a00:1098:0:86:1000:23:0:2]) by smtp.gmail.com with ESMTPSA id 4sm6017378wrv.33.2017.05.24.03.31.00 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 24 May 2017 03:31:00 -0700 (PDT) Date: Wed, 24 May 2017 11:30:58 +0100 From: Leif Lindholm To: Ard Biesheuvel Cc: "edk2-devel@lists.01.org" , "Duran, Leo" , "Yao, Jiewen" , Alexander Graf Message-ID: <20170524103058.GC32240@bivouac.eciton.net> References: <20170502103202.4943-1-ard.biesheuvel@linaro.org> <20170522134956.GG1657@bivouac.eciton.net> MIME-Version: 1.0 In-Reply-To: User-Agent: Mutt/1.5.23 (2014-03-12) Subject: Re: [RFC PATCH] ArmPkg: implement generic SMMU driver to remap DRAM for 32-bit PCI DMA X-BeenThere: edk2-devel@lists.01.org X-Mailman-Version: 2.1.22 Precedence: list List-Id: EDK II Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 24 May 2017 10:31:04 -0000 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline On Tue, May 23, 2017 at 03:36:24AM -0700, Ard Biesheuvel wrote: > On 22 May 2017 at 06:49, Leif Lindholm 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 > >> --- > >> > >> 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.
> >> + Copyright (c) 2017, Linaro Ltd. All rights reserved.
> >> + > >> + 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 > >> + > >> +#include > >> + > >> +#include > >> +#include > >> +#include > >> +#include > >> +#include > >> + > >> +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 > >> + 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.
> >> + > >> + 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 > >> +#include > >> +#include > >> +#include > >> +#include > >> + > >> +#include > >> +#include > >> +#include > >> + > >> +#include > >> + > >> +#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.
> >> +# > >> +# 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 > >>