From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-it0-x233.google.com (mail-it0-x233.google.com [IPv6:2607:f8b0:4001:c0b::233]) (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 AE07521A16EEF for ; Fri, 19 May 2017 06:36:48 -0700 (PDT) Received: by mail-it0-x233.google.com with SMTP id e65so46517958ita.1 for ; Fri, 19 May 2017 06:36:48 -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=U5qlPvUh1jElTfL1u4ceUDtgIk5MrlMvb6YynYvJwpw=; b=e/NBBM4UBEm07HN5jqnK4F4IYcZs5pmsxPhIsi3xyhiy6TaauuHFWPO/FdgbkctsTL +gFFjjXwi0USqsVf4k9NUVjij64ucaMsI8vPMaFDNAVjjT+blsHIUvSVwSaabItwQa3z EhCgnMP2iPvm9ALzP24EyWzmqu0NBfDgv7YBs= 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=U5qlPvUh1jElTfL1u4ceUDtgIk5MrlMvb6YynYvJwpw=; b=CxIMUpOoSgZYtYhLKD60tu3N3lbkQd7rZHlq/3b1x9BGcLau08pMncoHi298PcDemw uuyhKvXm6M7T6W+oKG6XT5gjzz0SiePaXWIrA/i6AoVgRnJHxdXj3kM8y+KOiryTT/j1 rBrd+ZsDo1UdGnkCN8IQ21nV/HWGN3hl59OfWd6yXxXfU4+5WqkYCgKLAkBJ4mpgfHiW cJTioxz7HJOA/zRCGd9ZAZK3OtKfYQ887NGQcQOBsNL84tk4cNdTtL3rMdw2GKjqdhoj 0aCbg9yQ3E69/RG32WVK0ry6ystG2K13idxMHV4m0RGaUohQw71RgQkGFAVML+izeOsb 4k5w== X-Gm-Message-State: AODbwcBxDFy6G4uR2ABDY3IOMgr+S4RY+zy9mp6JF98nWOns2jum7l+T BdDhvWePN2znh/HM6tXGenTKffi96aFj X-Received: by 10.36.224.133 with SMTP id c127mr28886614ith.73.1495201007258; Fri, 19 May 2017 06:36:47 -0700 (PDT) MIME-Version: 1.0 Received: by 10.107.164.24 with HTTP; Fri, 19 May 2017 06:36:46 -0700 (PDT) In-Reply-To: <20170502103202.4943-1-ard.biesheuvel@linaro.org> References: <20170502103202.4943-1-ard.biesheuvel@linaro.org> From: Ard Biesheuvel Date: Fri, 19 May 2017 14:36:46 +0100 Message-ID: To: "edk2-devel@lists.01.org" , Leif Lindholm , "Duran, Leo" , "Yao, Jiewen" Cc: Alexander Graf , Ard Biesheuvel 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: Fri, 19 May 2017 13:36:49 -0000 Content-Type: text/plain; charset="UTF-8" On 2 May 2017 at 11:32, 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 > --- > The prerequisites are now in place. Any comments? > ArmPkg/ArmPkg.dec | 7 + > ArmPkg/ArmPkg.dsc | 1 + > ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/BmDma.c | 467 ++++++++++++++++++++ > ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.c | 323 ++++++++++++++ > ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.inf | 62 +++ > 5 files changed, 860 insertions(+) > > diff --git a/ArmPkg/ArmPkg.dec b/ArmPkg/ArmPkg.dec > index c4b4da2f95bb..96913e3c0713 100644 > --- a/ArmPkg/ArmPkg.dec > +++ b/ArmPkg/ArmPkg.dec > @@ -322,3 +322,10 @@ [PcdsFixedAtBuild.common, PcdsDynamic.common] > # > gArmTokenSpaceGuid.PcdPciBusMin|0x0|UINT32|0x00000059 > gArmTokenSpaceGuid.PcdPciBusMax|0x0|UINT32|0x0000005A > + > + # > + # Base address and context interrupt of the generic SMMU that > + # translates memory accesses made by PCI masters > + # > + gArmTokenSpaceGuid.PcdPciGenericSmmuBase|0x0|UINT64|0x0000005B > + gArmTokenSpaceGuid.PcdPciGenericSmmuContextInterrupt|0x0|UINT16|0x0000005C > diff --git a/ArmPkg/ArmPkg.dsc b/ArmPkg/ArmPkg.dsc > index 9144334cb821..9bbc71fa2479 100644 > --- a/ArmPkg/ArmPkg.dsc > +++ b/ArmPkg/ArmPkg.dsc > @@ -127,6 +127,7 @@ [Components.common] > ArmPkg/Drivers/ArmGic/ArmGicDxe.inf > ArmPkg/Drivers/ArmGic/ArmGicLib.inf > ArmPkg/Drivers/ArmGic/ArmGicSecLib.inf > + ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.inf > ArmPkg/Drivers/GenericWatchdogDxe/GenericWatchdogDxe.inf > ArmPkg/Drivers/TimerDxe/TimerDxe.inf > > diff --git a/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/BmDma.c b/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/BmDma.c > new file mode 100644 > index 000000000000..629209e335e5 > --- /dev/null > +++ b/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/BmDma.c > @@ -0,0 +1,467 @@ > +/** @file > + BmDma related function > + > + Copyright (c) 2017, Intel Corporation. All rights reserved.
> + 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); > + > +#define MAP_INFO_SIGNATURE SIGNATURE_32 ('D', 'M', 'A', 'P') > +typedef struct { > + UINT32 Signature; > + LIST_ENTRY Link; > + EDKII_IOMMU_OPERATION Operation; > + UINTN NumberOfBytes; > + UINTN NumberOfPages; > + EFI_PHYSICAL_ADDRESS HostAddress; > + EFI_PHYSICAL_ADDRESS MappedHostAddress; > +} MAP_INFO; > +#define MAP_INFO_FROM_LINK(a) CR (a, MAP_INFO, Link, MAP_INFO_SIGNATURE) > + > +STATIC LIST_ENTRY mMaps = INITIALIZE_LIST_HEAD_VARIABLE(mMaps); > + > +/** > + Provides the controller-specific addresses required to access system memory from a > + DMA bus master. > + > + @param This The protocol instance pointer. > + @param Operation Indicates if the bus master is going to read or write to system memory. > + @param HostAddress The system memory address to map to the PCI controller. > + @param NumberOfBytes On input the number of bytes to map. On output the number of bytes > + that were mapped. > + @param DeviceAddress The resulting map address for the bus master PCI controller to use to > + access the hosts HostAddress. > + @param Mapping A resulting value to pass to Unmap(). > + > + @retval EFI_SUCCESS The range was mapped for the returned NumberOfBytes. > + @retval EFI_UNSUPPORTED The HostAddress cannot be mapped as a common buffer. > + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. > + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. > + @retval EFI_DEVICE_ERROR The system hardware could not map the requested address. > + > +**/ > +STATIC > +EFI_STATUS > +EFIAPI > +IoMmuMap ( > + IN EDKII_IOMMU_PROTOCOL *This, > + IN EDKII_IOMMU_OPERATION Operation, > + IN VOID *HostAddress, > + IN OUT UINTN *NumberOfBytes, > + OUT EFI_PHYSICAL_ADDRESS *DeviceAddress, > + OUT VOID **Mapping > + ) > +{ > + EFI_STATUS Status; > + EFI_PHYSICAL_ADDRESS PhysicalAddress; > + MAP_INFO *MapInfo; > + BOOLEAN NeedRemap; > + > + if (HostAddress == NULL || NumberOfBytes == NULL || DeviceAddress == NULL || > + Mapping == NULL) { > + return EFI_INVALID_PARAMETER; > + } > + > + // > + // Make sure that Operation is valid > + // > + if ((UINT32) Operation >= EdkiiIoMmuOperationMaximum) { > + return EFI_INVALID_PARAMETER; > + } > + > + NeedRemap = FALSE; > + PhysicalAddress = (UINTN)HostAddress; > + > + if ((PhysicalAddress + *NumberOfBytes) > mTranslationBase + SIZE_4GB) { > + // > + // If the root bridge or the device cannot handle performing DMA above > + // 4GB but any part of the DMA transfer being mapped is above 4GB, then > + // map the DMA transfer to a buffer below 4GB. > + // > + NeedRemap = TRUE; > + } > + > + if ((Operation == EdkiiIoMmuOperationBusMasterCommonBuffer || > + Operation == EdkiiIoMmuOperationBusMasterCommonBuffer64) && > + NeedRemap) { > + // > + // Common Buffer operations can not be remapped. If the common buffer > + // if above 4GB, then it is not possible to generate a mapping, so return > + // an error. > + // > + return EFI_UNSUPPORTED; > + } > + > + // > + // Allocate a MAP_INFO structure to remember the mapping when Unmap() is > + // called later. > + // > + MapInfo = AllocatePool (sizeof (MAP_INFO)); > + if (MapInfo == NULL) { > + *NumberOfBytes = 0; > + return EFI_OUT_OF_RESOURCES; > + } > + > + // > + // Initialize the MAP_INFO structure > + // > + MapInfo->Signature = MAP_INFO_SIGNATURE; > + MapInfo->Operation = Operation; > + MapInfo->NumberOfBytes = *NumberOfBytes; > + MapInfo->NumberOfPages = EFI_SIZE_TO_PAGES (MapInfo->NumberOfBytes); > + MapInfo->HostAddress = PhysicalAddress; > + MapInfo->MappedHostAddress = (EFI_PHYSICAL_ADDRESS)-1; > + > + // > + // Allocate a buffer below 4GB to map the transfer to. > + // > + if (NeedRemap) { > + MapInfo->MappedHostAddress = mTranslationBase + SIZE_4GB - 1; > + Status = gBS->AllocatePages ( > + AllocateMaxAddress, > + EfiBootServicesData, > + MapInfo->NumberOfPages, > + &MapInfo->MappedHostAddress > + ); > + if (EFI_ERROR (Status)) { > + FreePool (MapInfo); > + *NumberOfBytes = 0; > + return Status; > + } > + > + // > + // If this is a read operation from the Bus Master's point of view, > + // then copy the contents of the real buffer into the mapped buffer > + // so the Bus Master can read the contents of the real buffer. > + // > + if (Operation == EdkiiIoMmuOperationBusMasterRead || > + Operation == EdkiiIoMmuOperationBusMasterRead64) { > + CopyMem ( > + (VOID *)(UINTN)MapInfo->MappedHostAddress, > + (VOID *)(UINTN)MapInfo->HostAddress, > + MapInfo->NumberOfBytes > + ); > + } > + } else { > + MapInfo->MappedHostAddress = MapInfo->HostAddress; > + } > + > + InsertTailList (&mMaps, &MapInfo->Link); > + > + // > + // The DeviceAddress is the address of the maped buffer below 4GB > + // > + *DeviceAddress = MapInfo->MappedHostAddress - mTranslationBase; > + > + // > + // Return a pointer to the MAP_INFO structure in Mapping > + // > + *Mapping = MapInfo; > + > + return EFI_SUCCESS; > +} > + > +/** > + Completes the Map() operation and releases any corresponding resources. > + > + @param This The protocol instance pointer. > + @param Mapping The mapping value returned from Map(). > + > + @retval EFI_SUCCESS The range was unmapped. > + @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by Map(). > + @retval EFI_DEVICE_ERROR The data was not committed to the target system memory. > +**/ > +STATIC > +EFI_STATUS > +EFIAPI > +IoMmuUnmap ( > + IN EDKII_IOMMU_PROTOCOL *This, > + IN VOID *Mapping > + ) > +{ > + MAP_INFO *MapInfo; > + LIST_ENTRY *Link; > + > + if (Mapping == NULL) { > + return EFI_INVALID_PARAMETER; > + } > + > + MapInfo = NULL; > + for (Link = GetFirstNode (&mMaps) > + ; !IsNull (&mMaps, Link) > + ; Link = GetNextNode (&mMaps, Link) > + ) { > + MapInfo = MAP_INFO_FROM_LINK (Link); > + if (MapInfo == Mapping) { > + break; > + } > + } > + // > + // Mapping is not a valid value returned by Map() > + // > + if (MapInfo != Mapping) { > + return EFI_INVALID_PARAMETER; > + } > + RemoveEntryList (&MapInfo->Link); > + > + if (MapInfo->MappedHostAddress != MapInfo->HostAddress) { > + // > + // If this is a write operation from the Bus Master's point of view, > + // then copy the contents of the mapped buffer into the real buffer > + // so the processor can read the contents of the real buffer. > + // > + if (MapInfo->Operation == EdkiiIoMmuOperationBusMasterWrite || > + MapInfo->Operation == EdkiiIoMmuOperationBusMasterWrite64) { > + CopyMem ( > + (VOID *)(UINTN)MapInfo->HostAddress, > + (VOID *)(UINTN)MapInfo->MappedHostAddress, > + MapInfo->NumberOfBytes > + ); > + } > + > + // > + // Free the mapped buffer and the MAP_INFO structure. > + // > + gBS->FreePages (MapInfo->MappedHostAddress, MapInfo->NumberOfPages); > + } > + > + FreePool (Mapping); > + return EFI_SUCCESS; > +} > + > +/** > + Allocates pages that are suitable for an OperationBusMasterCommonBuffer or > + OperationBusMasterCommonBuffer64 mapping. > + > + @param This The protocol instance pointer. > + @param Type This parameter is not used and must be ignored. > + @param MemoryType The type of memory to allocate, EfiBootServicesData or > + EfiRuntimeServicesData. > + @param Pages The number of pages to allocate. > + @param HostAddress A pointer to store the base system memory address of the > + allocated range. > + @param Attributes The requested bit mask of attributes for the allocated range. > + > + @retval EFI_SUCCESS The requested memory pages were allocated. > + @retval EFI_UNSUPPORTED Attributes is unsupported. The only legal attribute bits are > + MEMORY_WRITE_COMBINE and MEMORY_CACHED. > + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. > + @retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated. > + > +**/ > +STATIC > +EFI_STATUS > +EFIAPI > +IoMmuAllocateBuffer ( > + IN EDKII_IOMMU_PROTOCOL *This, > + IN EFI_ALLOCATE_TYPE Type, > + IN EFI_MEMORY_TYPE MemoryType, > + IN UINTN Pages, > + IN OUT VOID **HostAddress, > + IN UINT64 Attributes > + ) > +{ > + EFI_STATUS Status; > + EFI_PHYSICAL_ADDRESS PhysicalAddress; > + > + // > + // Validate Attributes > + // > + if ((Attributes & EDKII_IOMMU_ATTRIBUTE_INVALID_FOR_ALLOCATE_BUFFER) != 0) { > + return EFI_UNSUPPORTED; > + } > + > + // > + // Check for invalid inputs > + // > + if (HostAddress == NULL) { > + return EFI_INVALID_PARAMETER; > + } > + > + // > + // The only valid memory types are EfiBootServicesData and > + // EfiRuntimeServicesData > + // > + if (MemoryType != EfiBootServicesData && > + MemoryType != EfiRuntimeServicesData) { > + return EFI_INVALID_PARAMETER; > + } > + > + // > + // Limit allocations to memory covered by the remapped window. > + // > + PhysicalAddress = mTranslationBase + SIZE_4GB - 1; > + Status = gBS->AllocatePages ( > + AllocateMaxAddress, > + MemoryType, > + Pages, > + &PhysicalAddress > + ); > + if (!EFI_ERROR (Status)) { > + *HostAddress = (VOID *)(UINTN)PhysicalAddress; > + } > + > + return Status; > +} > + > +/** > + Frees memory that was allocated with AllocateBuffer(). > + > + @param This The protocol instance pointer. > + @param Pages The number of pages to free. > + @param HostAddress The base system memory address of the allocated range. > + > + @retval EFI_SUCCESS The requested memory pages were freed. > + @retval EFI_INVALID_PARAMETER The memory range specified by HostAddress and Pages > + was not allocated with AllocateBuffer(). > + > +**/ > +STATIC > +EFI_STATUS > +EFIAPI > +IoMmuFreeBuffer ( > + IN EDKII_IOMMU_PROTOCOL *This, > + IN UINTN Pages, > + IN VOID *HostAddress > + ) > +{ > + return gBS->FreePages ((EFI_PHYSICAL_ADDRESS) (UINTN) HostAddress, Pages); > +} > + > +/** > + Set IOMMU attribute for a system memory. > + > + If the IOMMU protocol exists, the system memory cannot be used > + for DMA by default. > + > + When a device requests a DMA access for a system memory, > + the device driver need use SetAttribute() to update the IOMMU > + attribute to request DMA access (read and/or write). > + > + The DeviceHandle is used to identify which device submits the request. > + The IOMMU implementation need translate the device path to an IOMMU device ID, > + and set IOMMU hardware register accordingly. > + 1) DeviceHandle can be a standard PCI device. > + The memory for BusMasterRead need set EDKII_IOMMU_ACCESS_READ. > + The memory for BusMasterWrite need set EDKII_IOMMU_ACCESS_WRITE. > + The memory for BusMasterCommonBuffer need set EDKII_IOMMU_ACCESS_READ|EDKII_IOMMU_ACCESS_WRITE. > + After the memory is used, the memory need set 0 to keep it being protected. > + 2) DeviceHandle can be an ACPI device (ISA, I2C, SPI, etc). > + The memory for DMA access need set EDKII_IOMMU_ACCESS_READ and/or EDKII_IOMMU_ACCESS_WRITE. > + > + @param[in] This The protocol instance pointer. > + @param[in] DeviceHandle The device who initiates the DMA access request. > + @param[in] DeviceAddress The base of device memory address to be used as the DMA memory. > + @param[in] Length The length of device memory address to be used as the DMA memory. > + @param[in] IoMmuAccess The IOMMU access. > + > + @retval EFI_SUCCESS The IoMmuAccess is set for the memory range specified by DeviceAddress and Length. > + @retval EFI_INVALID_PARAMETER DeviceHandle is an invalid handle. > + @retval EFI_INVALID_PARAMETER DeviceAddress is not IoMmu Page size aligned. > + @retval EFI_INVALID_PARAMETER Length is not IoMmu Page size aligned. > + @retval EFI_INVALID_PARAMETER Length is 0. > + @retval EFI_INVALID_PARAMETER IoMmuAccess specified an illegal combination of access. > + @retval EFI_UNSUPPORTED DeviceHandle is unknown by the IOMMU. > + @retval EFI_UNSUPPORTED The bit mask of IoMmuAccess is not supported by the IOMMU. > + @retval EFI_UNSUPPORTED The IOMMU does not support the memory range specified by DeviceAddress and Length. > + @retval EFI_OUT_OF_RESOURCES There are not enough resources available to modify the IOMMU access. > + @retval EFI_DEVICE_ERROR The IOMMU device reported an error while attempting the operation. > + > +**/ > +EFI_STATUS > +EFIAPI > +IoMmuSetAttribute ( > + IN EDKII_IOMMU_PROTOCOL *This, > + IN EFI_HANDLE DeviceHandle, > + IN EFI_PHYSICAL_ADDRESS DeviceAddress, > + IN UINT64 Length, > + IN UINT64 IoMmuAccess > + ) > +{ > + return EFI_UNSUPPORTED; > +} > + > +/** > + Set IOMMU attribute for a system memory. > + > + If the IOMMU protocol exists, the system memory cannot be used > + for DMA by default. > + > + When a device requests a DMA access for a system memory, > + the device driver need use SetAttribute() to update the IOMMU > + attribute to request DMA access (read and/or write). > + > + The DeviceHandle is used to identify which device submits the request. > + The IOMMU implementation need translate the device path to an IOMMU device ID, > + and set IOMMU hardware register accordingly. > + 1) DeviceHandle can be a standard PCI device. > + The memory for BusMasterRead need set EDKII_IOMMU_ACCESS_READ. > + The memory for BusMasterWrite need set EDKII_IOMMU_ACCESS_WRITE. > + The memory for BusMasterCommonBuffer need set EDKII_IOMMU_ACCESS_READ|EDKII_IOMMU_ACCESS_WRITE. > + After the memory is used, the memory need set 0 to keep it being protected. > + 2) DeviceHandle can be an ACPI device (ISA, I2C, SPI, etc). > + The memory for DMA access need set EDKII_IOMMU_ACCESS_READ and/or EDKII_IOMMU_ACCESS_WRITE. > + > + @param[in] This The protocol instance pointer. > + @param[in] DeviceHandle The device who initiates the DMA access request. > + @param[in] Mapping The mapping value returned from Map(). > + @param[in] IoMmuAccess The IOMMU access. > + > + @retval EFI_SUCCESS The IoMmuAccess is set for the memory range specified by DeviceAddress and Length. > + @retval EFI_INVALID_PARAMETER DeviceHandle is an invalid handle. > + @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by Map(). > + @retval EFI_INVALID_PARAMETER IoMmuAccess specified an illegal combination of access. > + @retval EFI_UNSUPPORTED DeviceHandle is unknown by the IOMMU. > + @retval EFI_UNSUPPORTED The bit mask of IoMmuAccess is not supported by the IOMMU. > + @retval EFI_UNSUPPORTED The IOMMU does not support the memory range specified by Mapping. > + @retval EFI_OUT_OF_RESOURCES There are not enough resources available to modify the IOMMU access. > + @retval EFI_DEVICE_ERROR The IOMMU device reported an error while attempting the operation. > + > +**/ > +STATIC > +EFI_STATUS > +EFIAPI > +IoMmuSetMappingAttribute ( > + IN EDKII_IOMMU_PROTOCOL *This, > + IN EFI_HANDLE DeviceHandle, > + IN VOID *Mapping, > + IN UINT64 IoMmuAccess > + ) > +{ > + // > + // We only support a static remapping of DRAM into the PCI address space > + // so there is nothing we need to do to handle invocations of this protocol > + // method. > + // > + return EFI_SUCCESS; > +} > + > +EDKII_IOMMU_PROTOCOL mGenericSmmuIommuProtocol = { > + EDKII_IOMMU_PROTOCOL_REVISION, > + IoMmuSetAttribute, > + IoMmuMap, > + IoMmuUnmap, > + IoMmuAllocateBuffer, > + IoMmuFreeBuffer, > + IoMmuSetMappingAttribute, > +}; > diff --git a/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.c b/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.c > new file mode 100644 > index 000000000000..8f5093af14ea > --- /dev/null > +++ b/ArmPkg/Drivers/GenericSmmuStaticPciDmaDxe/GenericSmmuStaticPciDmaDxe.c > @@ -0,0 +1,323 @@ > +/** @file > + > + Copyright (c) 2017, Linaro Ltd. All rights reserved.
> + > + 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 >