From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-io0-x230.google.com (mail-io0-x230.google.com [IPv6:2607:f8b0:4001:c06::230]) (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 562DD21A16E5F for ; Tue, 23 May 2017 03:36:26 -0700 (PDT) Received: by mail-io0-x230.google.com with SMTP id p24so94389692ioi.0 for ; Tue, 23 May 2017 03:36:26 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=mime-version:in-reply-to:references:from:date:message-id:subject:to :cc; bh=M7ZXqHldm/w4QrR/OYYzlfs/xogRMnvl6cRLYZpjYq4=; b=DhrVFWY+mU7lH4IaYKjjZcJ4TebH2iBridxlrHVpCprQ6qnqiFcnM1qk+ohw8FYfDC BFv1AnvIiheLRbyFK7P7otpcRTbk5ipJCSsW7TjXjaksv4S1exETcdZGqte/Dz6RmC4z yrvZjvb1/7lx3XeZmLqvMurpbqKZTdFTYFQG0= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:in-reply-to:references:from:date :message-id:subject:to:cc; bh=M7ZXqHldm/w4QrR/OYYzlfs/xogRMnvl6cRLYZpjYq4=; b=HxmxvWRT9nVWwkOnLw0hfUsM1OCmxnAwvw5/9nm4SsdDgs9SKgGRCNu1wqIWhvxaTo UySD/2YoL5U6d/5eZeMW0PqMOCvBhCfi30R9/hO697Iu8z0UMGVcnSmwJXU2z7n3Quf/ zY+58cx/ZX9MxZQ2IQ96UyhqHnptUj8Ay94UIzqSbkK3AMETpIzwKbrn6SARla70fDVo TdtLU4R1ywLiFzoioK6zLFiVJB9ES2uTAHBqZD4WEyI0MPrwgPL/iVvTjHRgHrazHtVO vuXbjQ5WhM5NRF8QzGJyYgon9tMmnTWprFP/qTt+boCzKnvkltL6sYvnOrV5dYRxAa5n QM4w== X-Gm-Message-State: AODbwcCIfF0Pp0L6RZ3A+ifpzjSB15zN6QY2kjxy01p7ZRoPFsiYcIUP IJwb7Zw19es/JyTOJSG05vzlLKA6XuvS X-Received: by 10.107.130.166 with SMTP id m38mr28414657ioi.87.1495535784866; Tue, 23 May 2017 03:36:24 -0700 (PDT) MIME-Version: 1.0 Received: by 10.107.164.24 with HTTP; Tue, 23 May 2017 03:36:24 -0700 (PDT) In-Reply-To: <20170522134956.GG1657@bivouac.eciton.net> References: <20170502103202.4943-1-ard.biesheuvel@linaro.org> <20170522134956.GG1657@bivouac.eciton.net> From: Ard Biesheuvel Date: Tue, 23 May 2017 03:36:24 -0700 Message-ID: To: Leif Lindholm Cc: "edk2-devel@lists.01.org" , "Duran, Leo" , "Yao, Jiewen" , Alexander Graf 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: Tue, 23 May 2017 10:36:26 -0000 Content-Type: text/plain; charset="UTF-8" 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, and the driver reuses ArmMmuLib defines to populate the page table entries. In general, I agree with the tendency to keep code out of ArmPkg, but in this case, I think it really belongs there. > Seems an added shame to put two new generic config options > (PcdPciGenericSmmuBase/PcdPciGenericSmmuContextInterrupt) under an > architecture-specific directory. > >> + >> +#define MAP_INFO_SIGNATURE SIGNATURE_32 ('D', 'M', 'A', 'P') >> +typedef struct { >> + UINT32 Signature; >> + LIST_ENTRY Link; >> + EDKII_IOMMU_OPERATION Operation; >> + UINTN NumberOfBytes; >> + UINTN NumberOfPages; >> + EFI_PHYSICAL_ADDRESS HostAddress; >> + EFI_PHYSICAL_ADDRESS MappedHostAddress; >> +} MAP_INFO; >> +#define MAP_INFO_FROM_LINK(a) CR (a, MAP_INFO, Link, MAP_INFO_SIGNATURE) >> + >> +STATIC LIST_ENTRY mMaps = INITIALIZE_LIST_HEAD_VARIABLE(mMaps); >> + >> +/** >> + Provides the controller-specific addresses required to access system memory from a >> + DMA bus master. >> + >> + @param This The protocol instance pointer. >> + @param Operation Indicates if the bus master is going to read or write to system memory. >> + @param HostAddress The system memory address to map to the PCI controller. >> + @param NumberOfBytes On input the number of bytes to map. On output the number of bytes >> + that were mapped. >> + @param DeviceAddress The resulting map address for the bus master PCI controller to use to >> + access the hosts HostAddress. >> + @param Mapping A resulting value to pass to Unmap(). >> + >> + @retval EFI_SUCCESS The range was mapped for the returned NumberOfBytes. >> + @retval EFI_UNSUPPORTED The HostAddress cannot be mapped as a common buffer. >> + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. >> + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. >> + @retval EFI_DEVICE_ERROR The system hardware could not map the requested address. >> + >> +**/ >> +STATIC >> +EFI_STATUS >> +EFIAPI >> +IoMmuMap ( >> + IN EDKII_IOMMU_PROTOCOL *This, >> + IN EDKII_IOMMU_OPERATION Operation, >> + IN VOID *HostAddress, >> + IN OUT UINTN *NumberOfBytes, >> + OUT EFI_PHYSICAL_ADDRESS *DeviceAddress, >> + OUT VOID **Mapping >> + ) >> +{ >> + EFI_STATUS Status; >> + EFI_PHYSICAL_ADDRESS PhysicalAddress; >> + MAP_INFO *MapInfo; >> + BOOLEAN NeedRemap; >> + >> + if (HostAddress == NULL || NumberOfBytes == NULL || DeviceAddress == NULL || >> + Mapping == NULL) { >> + return EFI_INVALID_PARAMETER; >> + } >> + >> + // >> + // Make sure that Operation is valid >> + // >> + if ((UINT32) Operation >= EdkiiIoMmuOperationMaximum) { >> + return EFI_INVALID_PARAMETER; >> + } >> + >> + NeedRemap = FALSE; >> + PhysicalAddress = (UINTN)HostAddress; >> + >> + if ((PhysicalAddress + *NumberOfBytes) > mTranslationBase + SIZE_4GB) { >> + // >> + // If the root bridge or the device cannot handle performing DMA above >> + // 4GB but any part of the DMA transfer being mapped is above 4GB, then >> + // map the DMA transfer to a buffer below 4GB. >> + // > > Request for clarification: is this because this driver is only invoked > where the root bridge is incapable? (The test looks unconditional.) > No, the root bridge driver always invokes the IOMMU protocol if there is one. Here we test whether the mapped buffer already lives in the first 4 GB of DRAM, otherwise we will use a bounce buffer. This driver never maps anything dynamically in the SMMU, it just allocates a bounce buffer from the 32-bit addressable range if required. >> + NeedRemap = TRUE; >> + } >> + >> + if ((Operation == EdkiiIoMmuOperationBusMasterCommonBuffer || >> + Operation == EdkiiIoMmuOperationBusMasterCommonBuffer64) && >> + NeedRemap) { >> + // >> + // Common Buffer operations can not be remapped. If the common buffer >> + // if above 4GB, then it is not possible to generate a mapping, so return >> + // an error. >> + // >> + return EFI_UNSUPPORTED; >> + } >> + >> + // >> + // Allocate a MAP_INFO structure to remember the mapping when Unmap() is >> + // called later. >> + // >> + MapInfo = AllocatePool (sizeof (MAP_INFO)); >> + if (MapInfo == NULL) { >> + *NumberOfBytes = 0; >> + return EFI_OUT_OF_RESOURCES; >> + } >> + >> + // >> + // Initialize the MAP_INFO structure >> + // >> + MapInfo->Signature = MAP_INFO_SIGNATURE; >> + MapInfo->Operation = Operation; >> + MapInfo->NumberOfBytes = *NumberOfBytes; >> + MapInfo->NumberOfPages = EFI_SIZE_TO_PAGES (MapInfo->NumberOfBytes); >> + MapInfo->HostAddress = PhysicalAddress; >> + MapInfo->MappedHostAddress = (EFI_PHYSICAL_ADDRESS)-1; >> + >> + // >> + // Allocate a buffer below 4GB to map the transfer to. >> + // >> + if (NeedRemap) { >> + MapInfo->MappedHostAddress = mTranslationBase + SIZE_4GB - 1; >> + Status = gBS->AllocatePages ( >> + AllocateMaxAddress, >> + EfiBootServicesData, >> + MapInfo->NumberOfPages, >> + &MapInfo->MappedHostAddress >> + ); >> + if (EFI_ERROR (Status)) { >> + FreePool (MapInfo); >> + *NumberOfBytes = 0; >> + return Status; >> + } >> + >> + // >> + // If this is a read operation from the Bus Master's point of view, >> + // then copy the contents of the real buffer into the mapped buffer >> + // so the Bus Master can read the contents of the real buffer. >> + // >> + if (Operation == EdkiiIoMmuOperationBusMasterRead || >> + Operation == EdkiiIoMmuOperationBusMasterRead64) { >> + CopyMem ( >> + (VOID *)(UINTN)MapInfo->MappedHostAddress, >> + (VOID *)(UINTN)MapInfo->HostAddress, >> + MapInfo->NumberOfBytes >> + ); >> + } >> + } else { >> + MapInfo->MappedHostAddress = MapInfo->HostAddress; >> + } >> + >> + InsertTailList (&mMaps, &MapInfo->Link); >> + >> + // >> + // The DeviceAddress is the address of the maped buffer below 4GB >> + // >> + *DeviceAddress = MapInfo->MappedHostAddress - mTranslationBase; >> + >> + // >> + // Return a pointer to the MAP_INFO structure in Mapping >> + // >> + *Mapping = MapInfo; >> + >> + return EFI_SUCCESS; >> +} >> + >> +/** >> + Completes the Map() operation and releases any corresponding resources. >> + >> + @param This The protocol instance pointer. >> + @param Mapping The mapping value returned from Map(). >> + >> + @retval EFI_SUCCESS The range was unmapped. >> + @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by Map(). >> + @retval EFI_DEVICE_ERROR The data was not committed to the target system memory. >> +**/ >> +STATIC >> +EFI_STATUS >> +EFIAPI >> +IoMmuUnmap ( >> + IN EDKII_IOMMU_PROTOCOL *This, >> + IN VOID *Mapping >> + ) >> +{ >> + MAP_INFO *MapInfo; >> + LIST_ENTRY *Link; >> + >> + if (Mapping == NULL) { >> + return EFI_INVALID_PARAMETER; >> + } >> + >> + MapInfo = NULL; >> + for (Link = GetFirstNode (&mMaps) >> + ; !IsNull (&mMaps, Link) >> + ; Link = GetNextNode (&mMaps, Link) >> + ) { >> + MapInfo = MAP_INFO_FROM_LINK (Link); >> + if (MapInfo == Mapping) { >> + break; >> + } >> + } >> + // >> + // Mapping is not a valid value returned by Map() >> + // >> + if (MapInfo != Mapping) { >> + return EFI_INVALID_PARAMETER; >> + } >> + RemoveEntryList (&MapInfo->Link); >> + >> + if (MapInfo->MappedHostAddress != MapInfo->HostAddress) { >> + // >> + // If this is a write operation from the Bus Master's point of view, >> + // then copy the contents of the mapped buffer into the real buffer >> + // so the processor can read the contents of the real buffer. >> + // >> + if (MapInfo->Operation == EdkiiIoMmuOperationBusMasterWrite || >> + MapInfo->Operation == EdkiiIoMmuOperationBusMasterWrite64) { >> + CopyMem ( >> + (VOID *)(UINTN)MapInfo->HostAddress, >> + (VOID *)(UINTN)MapInfo->MappedHostAddress, >> + MapInfo->NumberOfBytes >> + ); >> + } >> + >> + // >> + // Free the mapped buffer and the MAP_INFO structure. >> + // >> + gBS->FreePages (MapInfo->MappedHostAddress, MapInfo->NumberOfPages); >> + } >> + >> + FreePool (Mapping); >> + return EFI_SUCCESS; >> +} >> + >> +/** >> + Allocates pages that are suitable for an OperationBusMasterCommonBuffer or >> + OperationBusMasterCommonBuffer64 mapping. >> + >> + @param This The protocol instance pointer. >> + @param Type This parameter is not used and must be ignored. >> + @param MemoryType The type of memory to allocate, EfiBootServicesData or >> + EfiRuntimeServicesData. >> + @param Pages The number of pages to allocate. >> + @param HostAddress A pointer to store the base system memory address of the >> + allocated range. >> + @param Attributes The requested bit mask of attributes for the allocated range. >> + >> + @retval EFI_SUCCESS The requested memory pages were allocated. >> + @retval EFI_UNSUPPORTED Attributes is unsupported. The only legal attribute bits are >> + MEMORY_WRITE_COMBINE and MEMORY_CACHED. >> + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. >> + @retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated. >> + >> +**/ >> +STATIC >> +EFI_STATUS >> +EFIAPI >> +IoMmuAllocateBuffer ( >> + IN EDKII_IOMMU_PROTOCOL *This, >> + IN EFI_ALLOCATE_TYPE Type, >> + IN EFI_MEMORY_TYPE MemoryType, >> + IN UINTN Pages, >> + IN OUT VOID **HostAddress, >> + IN UINT64 Attributes >> + ) >> +{ >> + EFI_STATUS Status; >> + EFI_PHYSICAL_ADDRESS PhysicalAddress; >> + >> + // >> + // Validate Attributes >> + // >> + if ((Attributes & EDKII_IOMMU_ATTRIBUTE_INVALID_FOR_ALLOCATE_BUFFER) != 0) { >> + return EFI_UNSUPPORTED; >> + } >> + >> + // >> + // Check for invalid inputs >> + // >> + if (HostAddress == NULL) { >> + return EFI_INVALID_PARAMETER; >> + } >> + >> + // >> + // The only valid memory types are EfiBootServicesData and >> + // EfiRuntimeServicesData >> + // >> + if (MemoryType != EfiBootServicesData && >> + MemoryType != EfiRuntimeServicesData) { >> + return EFI_INVALID_PARAMETER; >> + } >> + >> + // >> + // Limit allocations to memory covered by the remapped window. >> + // >> + PhysicalAddress = mTranslationBase + SIZE_4GB - 1; >> + Status = gBS->AllocatePages ( >> + AllocateMaxAddress, >> + MemoryType, >> + Pages, >> + &PhysicalAddress >> + ); >> + if (!EFI_ERROR (Status)) { >> + *HostAddress = (VOID *)(UINTN)PhysicalAddress; >> + } >> + >> + return Status; >> +} >> + >> +/** >> + Frees memory that was allocated with AllocateBuffer(). >> + >> + @param This The protocol instance pointer. >> + @param Pages The number of pages to free. >> + @param HostAddress The base system memory address of the allocated range. >> + >> + @retval EFI_SUCCESS The requested memory pages were freed. >> + @retval EFI_INVALID_PARAMETER The memory range specified by HostAddress and Pages >> + was not allocated with AllocateBuffer(). >> + >> +**/ >> +STATIC >> +EFI_STATUS >> +EFIAPI >> +IoMmuFreeBuffer ( >> + IN EDKII_IOMMU_PROTOCOL *This, >> + IN UINTN Pages, >> + IN VOID *HostAddress >> + ) >> +{ >> + return gBS->FreePages ((EFI_PHYSICAL_ADDRESS) (UINTN) HostAddress, Pages); >> +} >> + >> +/** >> + Set IOMMU attribute for a system memory. >> + >> + If the IOMMU protocol exists, the system memory cannot be used >> + for DMA by default. >> + >> + When a device requests a DMA access for a system memory, >> + the device driver need use SetAttribute() to update the IOMMU >> + attribute to request DMA access (read and/or write). >> + >> + The DeviceHandle is used to identify which device submits the request. >> + The IOMMU implementation need translate the device path to an IOMMU device ID, >> + and set IOMMU hardware register accordingly. >> + 1) DeviceHandle can be a standard PCI device. >> + The memory for BusMasterRead need set EDKII_IOMMU_ACCESS_READ. >> + The memory for BusMasterWrite need set EDKII_IOMMU_ACCESS_WRITE. >> + The memory for BusMasterCommonBuffer need set EDKII_IOMMU_ACCESS_READ|EDKII_IOMMU_ACCESS_WRITE. >> + After the memory is used, the memory need set 0 to keep it being protected. >> + 2) DeviceHandle can be an ACPI device (ISA, I2C, SPI, etc). >> + The memory for DMA access need set EDKII_IOMMU_ACCESS_READ and/or EDKII_IOMMU_ACCESS_WRITE. >> + >> + @param[in] This The protocol instance pointer. >> + @param[in] DeviceHandle The device who initiates the DMA access request. >> + @param[in] DeviceAddress The base of device memory address to be used as the DMA memory. >> + @param[in] Length The length of device memory address to be used as the DMA memory. >> + @param[in] IoMmuAccess The IOMMU access. >> + >> + @retval EFI_SUCCESS The IoMmuAccess is set for the memory range specified by DeviceAddress and Length. >> + @retval EFI_INVALID_PARAMETER DeviceHandle is an invalid handle. >> + @retval EFI_INVALID_PARAMETER DeviceAddress is not IoMmu Page size aligned. >> + @retval EFI_INVALID_PARAMETER Length is not IoMmu Page size aligned. >> + @retval EFI_INVALID_PARAMETER Length is 0. >> + @retval EFI_INVALID_PARAMETER IoMmuAccess specified an illegal combination of access. >> + @retval EFI_UNSUPPORTED DeviceHandle is unknown by the IOMMU. >> + @retval EFI_UNSUPPORTED The bit mask of IoMmuAccess is not supported by the IOMMU. >> + @retval EFI_UNSUPPORTED The IOMMU does not support the memory range specified by DeviceAddress and Length. >> + @retval EFI_OUT_OF_RESOURCES There are not enough resources available to modify the IOMMU access. >> + @retval EFI_DEVICE_ERROR The IOMMU device reported an error while attempting the operation. >> + >> +**/ >> +EFI_STATUS >> +EFIAPI >> +IoMmuSetAttribute ( >> + IN EDKII_IOMMU_PROTOCOL *This, >> + IN EFI_HANDLE DeviceHandle, >> + IN EFI_PHYSICAL_ADDRESS DeviceAddress, >> + IN UINT64 Length, >> + IN UINT64 IoMmuAccess >> + ) >> +{ >> + return EFI_UNSUPPORTED; >> +} >> + >> +/** >> + Set IOMMU attribute for a system memory. >> + >> + If the IOMMU protocol exists, the system memory cannot be used >> + for DMA by default. >> + >> + When a device requests a DMA access for a system memory, >> + the device driver need use SetAttribute() to update the IOMMU >> + attribute to request DMA access (read and/or write). >> + >> + The DeviceHandle is used to identify which device submits the request. >> + The IOMMU implementation need translate the device path to an IOMMU device ID, >> + and set IOMMU hardware register accordingly. >> + 1) DeviceHandle can be a standard PCI device. >> + The memory for BusMasterRead need set EDKII_IOMMU_ACCESS_READ. >> + The memory for BusMasterWrite need set EDKII_IOMMU_ACCESS_WRITE. >> + The memory for BusMasterCommonBuffer need set EDKII_IOMMU_ACCESS_READ|EDKII_IOMMU_ACCESS_WRITE. >> + After the memory is used, the memory need set 0 to keep it being protected. >> + 2) DeviceHandle can be an ACPI device (ISA, I2C, SPI, etc). >> + The memory for DMA access need set EDKII_IOMMU_ACCESS_READ and/or EDKII_IOMMU_ACCESS_WRITE. >> + >> + @param[in] This The protocol instance pointer. >> + @param[in] DeviceHandle The device who initiates the DMA access request. >> + @param[in] Mapping The mapping value returned from Map(). >> + @param[in] IoMmuAccess The IOMMU access. >> + >> + @retval EFI_SUCCESS The IoMmuAccess is set for the memory range specified by DeviceAddress and Length. >> + @retval EFI_INVALID_PARAMETER DeviceHandle is an invalid handle. >> + @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by Map(). >> + @retval EFI_INVALID_PARAMETER IoMmuAccess specified an illegal combination of access. >> + @retval EFI_UNSUPPORTED DeviceHandle is unknown by the IOMMU. >> + @retval EFI_UNSUPPORTED The bit mask of IoMmuAccess is not supported by the IOMMU. >> + @retval EFI_UNSUPPORTED The IOMMU does not support the memory range specified by Mapping. >> + @retval EFI_OUT_OF_RESOURCES There are not enough resources available to modify the IOMMU access. >> + @retval EFI_DEVICE_ERROR The IOMMU device reported an error while attempting the operation. >> + >> +**/ >> +STATIC >> +EFI_STATUS >> +EFIAPI >> +IoMmuSetMappingAttribute ( >> + IN EDKII_IOMMU_PROTOCOL *This, >> + IN EFI_HANDLE DeviceHandle, >> + IN VOID *Mapping, >> + IN UINT64 IoMmuAccess >> + ) >> +{ >> + // >> + // We only support a static remapping of DRAM into the PCI address space >> + // so there is nothing we need to do to handle invocations of this protocol >> + // method. >> + // >> + return EFI_SUCCESS; >> +} >> + >> +EDKII_IOMMU_PROTOCOL mGenericSmmuIommuProtocol = { >> + EDKII_IOMMU_PROTOCOL_REVISION, >> + IoMmuSetAttribute, >> + IoMmuMap, >> + IoMmuUnmap, >> + IoMmuAllocateBuffer, >> + IoMmuFreeBuffer, >> + IoMmuSetMappingAttribute, >> +}; >> diff --git a/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.c b/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.c >> new file mode 100644 >> index 000000000000..8f5093af14ea >> --- /dev/null >> +++ b/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.c >> @@ -0,0 +1,323 @@ >> +/** @file >> + >> + Copyright (c) 2017, Linaro Ltd. All rights reserved.
>> + >> + 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 >>