From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-wr0-x22d.google.com (mail-wr0-x22d.google.com [IPv6:2a00:1450:400c:c0c::22d]) (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 EFA4D21A134BC for ; Mon, 22 May 2017 06:50:05 -0700 (PDT) Received: by mail-wr0-x22d.google.com with SMTP id z52so39031526wrc.2 for ; Mon, 22 May 2017 06:50:05 -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=IgYXJSbXg1t29Jp4MkUuRCQ7nRea0Dr5YkKrsvdclxk=; b=WIzucbzFo3umIPx/zlas861pYdUWQGT8UXopE4NPYEydH5bxDoIcOx/umf/1Tyc1AB qe0U14HKRVk3mwavEAz2i/+ePQT5UuphUXwpbm0142i009/0xbT6HMmNK+srlpwnEb4A A4ypyMkk8qa7xUeX0s53pxedAHnIryYrChwM0= 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=IgYXJSbXg1t29Jp4MkUuRCQ7nRea0Dr5YkKrsvdclxk=; b=OZwa1F6jl//RO02q7Zhzsi48WGttQ7GsItoP0XMVOU2UOP8D4QAR7ZLVWP6nr973HB pqDlyhiFyPF8uTe6IvqOytYEBsJ/NUm2ntsLg0LBVPVK0r/wt/95liEnM1Qiz+/pRgM4 WyFMtqv/gZajNBrwtEjQVAGUSHae07pwoc9E4D3k//XWex/n/bZK8rmqmklzxrBLrP8J jboflc7TGMzgv9QQvcgshQFKcpiHZMBnz45phj9rUDmwJWKbhAbhzpB6uW4efHGB0VkD uWh1bnTU3I+IpqpW7wOI1/6DwIb9f+QiuHSNlIsgJgO3cIwIx5qo0wx/pGhTVZKOs3Ym 5M5g== X-Gm-Message-State: AODbwcCcmCrCentI/MBpfKU7jwRxMkmRHbR9MjJsYqebx2lK8kApsV0W MM1nDjhiJ9Lpv6vy X-Received: by 10.223.151.13 with SMTP id r13mr11603625wrb.6.1495461003455; Mon, 22 May 2017 06:50:03 -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 y131sm14526251wmy.34.2017.05.22.06.50.02 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 22 May 2017 06:50:02 -0700 (PDT) Date: Mon, 22 May 2017 14:49:56 +0100 From: Leif Lindholm To: Ard Biesheuvel Cc: edk2-devel@lists.01.org, leo.duran@amd.com, jiewen.yao@intel.com, agraf@suse.de Message-ID: <20170522134956.GG1657@bivouac.eciton.net> References: <20170502103202.4943-1-ard.biesheuvel@linaro.org> MIME-Version: 1.0 In-Reply-To: <20170502103202.4943-1-ard.biesheuvel@linaro.org> 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: Mon, 22 May 2017 13:50:06 -0000 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline 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? 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.
> + > + 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 >