--- /dev/null
+/** @file\r
+ BmDma related function\r
+\r
+ Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include <PiDxe.h>\r
+\r
+#include <Protocol/IoMmu.h>\r
+\r
+#include <Library/BaseLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+\r
+// TBD: May make it a policy\r
+#define DMA_MEMORY_TOP MAX_UINTN\r
+//#define DMA_MEMORY_TOP 0x0000000001FFFFFFULL\r
+\r
+#define MAP_INFO_SIGNATURE SIGNATURE_32 ('D', 'M', 'A', 'P')\r
+typedef struct {\r
+ UINT32 Signature;\r
+ LIST_ENTRY Link;\r
+ EDKII_IOMMU_OPERATION Operation;\r
+ UINTN NumberOfBytes;\r
+ UINTN NumberOfPages;\r
+ EFI_PHYSICAL_ADDRESS HostAddress;\r
+ EFI_PHYSICAL_ADDRESS DeviceAddress;\r
+} MAP_INFO;\r
+#define MAP_INFO_FROM_LINK(a) CR (a, MAP_INFO, Link, MAP_INFO_SIGNATURE)\r
+\r
+LIST_ENTRY gMaps = INITIALIZE_LIST_HEAD_VARIABLE(gMaps);\r
+\r
+/**\r
+ Provides the controller-specific addresses required to access system memory from a\r
+ DMA bus master.\r
+\r
+ @param This The protocol instance pointer.\r
+ @param Operation Indicates if the bus master is going to read or write to system memory.\r
+ @param HostAddress The system memory address to map to the PCI controller.\r
+ @param NumberOfBytes On input the number of bytes to map. On output the number of bytes\r
+ that were mapped.\r
+ @param DeviceAddress The resulting map address for the bus master PCI controller to use to\r
+ access the hosts HostAddress.\r
+ @param Mapping A resulting value to pass to Unmap().\r
+\r
+ @retval EFI_SUCCESS The range was mapped for the returned NumberOfBytes.\r
+ @retval EFI_UNSUPPORTED The HostAddress cannot be mapped as a common buffer.\r
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.\r
+ @retval EFI_DEVICE_ERROR The system hardware could not map the requested address.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IoMmuMap (\r
+ IN EDKII_IOMMU_PROTOCOL *This,\r
+ IN EDKII_IOMMU_OPERATION Operation,\r
+ IN VOID *HostAddress,\r
+ IN OUT UINTN *NumberOfBytes,\r
+ OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,\r
+ OUT VOID **Mapping\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_PHYSICAL_ADDRESS PhysicalAddress;\r
+ MAP_INFO *MapInfo;\r
+ EFI_PHYSICAL_ADDRESS DmaMemoryTop;\r
+ BOOLEAN NeedRemap;\r
+\r
+ DEBUG ((DEBUG_VERBOSE, "IoMmuMap: ==> 0x%08x - 0x%08x (%x)\n", HostAddress, NumberOfBytes, Operation));\r
+\r
+ if (HostAddress == NULL || NumberOfBytes == NULL || DeviceAddress == NULL ||\r
+ Mapping == NULL) {\r
+ DEBUG ((DEBUG_ERROR, "IoMmuMap: %r\n", EFI_INVALID_PARAMETER));\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Make sure that Operation is valid\r
+ //\r
+ if ((UINT32) Operation >= EdkiiIoMmuOperationMaximum) {\r
+ DEBUG ((DEBUG_ERROR, "IoMmuMap: %r\n", EFI_INVALID_PARAMETER));\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ NeedRemap = FALSE;\r
+ PhysicalAddress = (EFI_PHYSICAL_ADDRESS) (UINTN) HostAddress;\r
+\r
+ DmaMemoryTop = DMA_MEMORY_TOP;\r
+\r
+ //\r
+ // Alignment check\r
+ //\r
+ if ((*NumberOfBytes != ALIGN_VALUE(*NumberOfBytes, SIZE_4KB)) ||\r
+ (PhysicalAddress != ALIGN_VALUE(PhysicalAddress, SIZE_4KB))) {\r
+ if ((Operation == EdkiiIoMmuOperationBusMasterCommonBuffer) ||\r
+ (Operation == EdkiiIoMmuOperationBusMasterCommonBuffer64)) {\r
+ //\r
+ // The input buffer might be a subset from IoMmuAllocateBuffer.\r
+ // Skip the check.\r
+ //\r
+ } else {\r
+ NeedRemap = TRUE;\r
+ }\r
+ }\r
+\r
+ if ((PhysicalAddress + *NumberOfBytes) >= DMA_MEMORY_TOP) {\r
+ NeedRemap = TRUE;\r
+ }\r
+\r
+ if (((Operation != EdkiiIoMmuOperationBusMasterRead64 &&\r
+ Operation != EdkiiIoMmuOperationBusMasterWrite64 &&\r
+ Operation != EdkiiIoMmuOperationBusMasterCommonBuffer64)) &&\r
+ ((PhysicalAddress + *NumberOfBytes) > SIZE_4GB)) {\r
+ //\r
+ // If the root bridge or the device cannot handle performing DMA above\r
+ // 4GB but any part of the DMA transfer being mapped is above 4GB, then\r
+ // map the DMA transfer to a buffer below 4GB.\r
+ //\r
+ NeedRemap = TRUE;\r
+ DmaMemoryTop = MIN (DmaMemoryTop, SIZE_4GB - 1);\r
+ }\r
+\r
+ if (Operation == EdkiiIoMmuOperationBusMasterCommonBuffer ||\r
+ Operation == EdkiiIoMmuOperationBusMasterCommonBuffer64) {\r
+ if (NeedRemap) {\r
+ //\r
+ // Common Buffer operations can not be remapped. If the common buffer\r
+ // if above 4GB, then it is not possible to generate a mapping, so return\r
+ // an error.\r
+ //\r
+ DEBUG ((DEBUG_ERROR, "IoMmuMap: %r\n", EFI_UNSUPPORTED));\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Allocate a MAP_INFO structure to remember the mapping when Unmap() is\r
+ // called later.\r
+ //\r
+ MapInfo = AllocatePool (sizeof (MAP_INFO));\r
+ if (MapInfo == NULL) {\r
+ *NumberOfBytes = 0;\r
+ DEBUG ((DEBUG_ERROR, "IoMmuMap: %r\n", EFI_OUT_OF_RESOURCES));\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ //\r
+ // Initialize the MAP_INFO structure\r
+ //\r
+ MapInfo->Signature = MAP_INFO_SIGNATURE;\r
+ MapInfo->Operation = Operation;\r
+ MapInfo->NumberOfBytes = *NumberOfBytes;\r
+ MapInfo->NumberOfPages = EFI_SIZE_TO_PAGES (MapInfo->NumberOfBytes);\r
+ MapInfo->HostAddress = PhysicalAddress;\r
+ MapInfo->DeviceAddress = DmaMemoryTop;\r
+\r
+ //\r
+ // Allocate a buffer below 4GB to map the transfer to.\r
+ //\r
+ if (NeedRemap) {\r
+ Status = gBS->AllocatePages (\r
+ AllocateMaxAddress,\r
+ EfiBootServicesData,\r
+ MapInfo->NumberOfPages,\r
+ &MapInfo->DeviceAddress\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ FreePool (MapInfo);\r
+ *NumberOfBytes = 0;\r
+ DEBUG ((DEBUG_ERROR, "IoMmuMap: %r\n", Status));\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // If this is a read operation from the Bus Master's point of view,\r
+ // then copy the contents of the real buffer into the mapped buffer\r
+ // so the Bus Master can read the contents of the real buffer.\r
+ //\r
+ if (Operation == EdkiiIoMmuOperationBusMasterRead ||\r
+ Operation == EdkiiIoMmuOperationBusMasterRead64) {\r
+ CopyMem (\r
+ (VOID *) (UINTN) MapInfo->DeviceAddress,\r
+ (VOID *) (UINTN) MapInfo->HostAddress,\r
+ MapInfo->NumberOfBytes\r
+ );\r
+ }\r
+ } else {\r
+ MapInfo->DeviceAddress = MapInfo->HostAddress;\r
+ }\r
+\r
+ InsertTailList (&gMaps, &MapInfo->Link);\r
+\r
+ //\r
+ // The DeviceAddress is the address of the maped buffer below 4GB\r
+ //\r
+ *DeviceAddress = MapInfo->DeviceAddress;\r
+ //\r
+ // Return a pointer to the MAP_INFO structure in Mapping\r
+ //\r
+ *Mapping = MapInfo;\r
+\r
+ DEBUG ((DEBUG_VERBOSE, "IoMmuMap: 0x%08x - 0x%08x <==\n", *DeviceAddress, *Mapping));\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Completes the Map() operation and releases any corresponding resources.\r
+\r
+ @param This The protocol instance pointer.\r
+ @param Mapping The mapping value returned from Map().\r
+\r
+ @retval EFI_SUCCESS The range was unmapped.\r
+ @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by Map().\r
+ @retval EFI_DEVICE_ERROR The data was not committed to the target system memory.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IoMmuUnmap (\r
+ IN EDKII_IOMMU_PROTOCOL *This,\r
+ IN VOID *Mapping\r
+ )\r
+{\r
+ MAP_INFO *MapInfo;\r
+ LIST_ENTRY *Link;\r
+\r
+ DEBUG ((DEBUG_VERBOSE, "IoMmuUnmap: 0x%08x\n", Mapping));\r
+\r
+ if (Mapping == NULL) {\r
+ DEBUG ((DEBUG_ERROR, "IoMmuUnmap: %r\n", EFI_INVALID_PARAMETER));\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ MapInfo = NULL;\r
+ for (Link = GetFirstNode (&gMaps)\r
+ ; !IsNull (&gMaps, Link)\r
+ ; Link = GetNextNode (&gMaps, Link)\r
+ ) {\r
+ MapInfo = MAP_INFO_FROM_LINK (Link);\r
+ if (MapInfo == Mapping) {\r
+ break;\r
+ }\r
+ }\r
+ //\r
+ // Mapping is not a valid value returned by Map()\r
+ //\r
+ if (MapInfo != Mapping) {\r
+ DEBUG ((DEBUG_ERROR, "IoMmuUnmap: %r\n", EFI_INVALID_PARAMETER));\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ RemoveEntryList (&MapInfo->Link);\r
+\r
+ if (MapInfo->DeviceAddress != MapInfo->HostAddress) {\r
+ //\r
+ // If this is a write operation from the Bus Master's point of view,\r
+ // then copy the contents of the mapped buffer into the real buffer\r
+ // so the processor can read the contents of the real buffer.\r
+ //\r
+ if (MapInfo->Operation == EdkiiIoMmuOperationBusMasterWrite ||\r
+ MapInfo->Operation == EdkiiIoMmuOperationBusMasterWrite64) {\r
+ CopyMem (\r
+ (VOID *) (UINTN) MapInfo->HostAddress,\r
+ (VOID *) (UINTN) MapInfo->DeviceAddress,\r
+ MapInfo->NumberOfBytes\r
+ );\r
+ }\r
+\r
+ //\r
+ // Free the mapped buffer and the MAP_INFO structure.\r
+ //\r
+ gBS->FreePages (MapInfo->DeviceAddress, MapInfo->NumberOfPages);\r
+ }\r
+\r
+ FreePool (Mapping);\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Allocates pages that are suitable for an OperationBusMasterCommonBuffer or\r
+ OperationBusMasterCommonBuffer64 mapping.\r
+\r
+ @param This The protocol instance pointer.\r
+ @param Type This parameter is not used and must be ignored.\r
+ @param MemoryType The type of memory to allocate, EfiBootServicesData or\r
+ EfiRuntimeServicesData.\r
+ @param Pages The number of pages to allocate.\r
+ @param HostAddress A pointer to store the base system memory address of the\r
+ allocated range.\r
+ @param Attributes The requested bit mask of attributes for the allocated range.\r
+\r
+ @retval EFI_SUCCESS The requested memory pages were allocated.\r
+ @retval EFI_UNSUPPORTED Attributes is unsupported. The only legal attribute bits are\r
+ MEMORY_WRITE_COMBINE and MEMORY_CACHED.\r
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
+ @retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IoMmuAllocateBuffer (\r
+ IN EDKII_IOMMU_PROTOCOL *This,\r
+ IN EFI_ALLOCATE_TYPE Type,\r
+ IN EFI_MEMORY_TYPE MemoryType,\r
+ IN UINTN Pages,\r
+ IN OUT VOID **HostAddress,\r
+ IN UINT64 Attributes\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_PHYSICAL_ADDRESS PhysicalAddress;\r
+\r
+ DEBUG ((DEBUG_VERBOSE, "IoMmuAllocateBuffer: ==> 0x%08x\n", Pages));\r
+\r
+ //\r
+ // Validate Attributes\r
+ //\r
+ if ((Attributes & EDKII_IOMMU_ATTRIBUTE_INVALID_FOR_ALLOCATE_BUFFER) != 0) {\r
+ DEBUG ((DEBUG_ERROR, "IoMmuAllocateBuffer: %r\n", EFI_UNSUPPORTED));\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ //\r
+ // Check for invalid inputs\r
+ //\r
+ if (HostAddress == NULL) {\r
+ DEBUG ((DEBUG_ERROR, "IoMmuAllocateBuffer: %r\n", EFI_INVALID_PARAMETER));\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // The only valid memory types are EfiBootServicesData and\r
+ // EfiRuntimeServicesData\r
+ //\r
+ if (MemoryType != EfiBootServicesData &&\r
+ MemoryType != EfiRuntimeServicesData) {\r
+ DEBUG ((DEBUG_ERROR, "IoMmuAllocateBuffer: %r\n", EFI_INVALID_PARAMETER));\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ PhysicalAddress = DMA_MEMORY_TOP;\r
+ if ((Attributes & EDKII_IOMMU_ATTRIBUTE_DUAL_ADDRESS_CYCLE) == 0) {\r
+ //\r
+ // Limit allocations to memory below 4GB\r
+ //\r
+ PhysicalAddress = MIN (PhysicalAddress, SIZE_4GB - 1);\r
+ }\r
+ Status = gBS->AllocatePages (\r
+ AllocateMaxAddress,\r
+ MemoryType,\r
+ Pages,\r
+ &PhysicalAddress\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ *HostAddress = (VOID *) (UINTN) PhysicalAddress;\r
+ }\r
+\r
+ DEBUG ((DEBUG_VERBOSE, "IoMmuAllocateBuffer: 0x%08x <==\n", *HostAddress));\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Frees memory that was allocated with AllocateBuffer().\r
+\r
+ @param This The protocol instance pointer.\r
+ @param Pages The number of pages to free.\r
+ @param HostAddress The base system memory address of the allocated range.\r
+\r
+ @retval EFI_SUCCESS The requested memory pages were freed.\r
+ @retval EFI_INVALID_PARAMETER The memory range specified by HostAddress and Pages\r
+ was not allocated with AllocateBuffer().\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IoMmuFreeBuffer (\r
+ IN EDKII_IOMMU_PROTOCOL *This,\r
+ IN UINTN Pages,\r
+ IN VOID *HostAddress\r
+ )\r
+{\r
+ DEBUG ((DEBUG_VERBOSE, "IoMmuFreeBuffer: 0x%\n", Pages));\r
+ return gBS->FreePages ((EFI_PHYSICAL_ADDRESS) (UINTN) HostAddress, Pages);\r
+}\r
+\r
+/**\r
+ Get device information from mapping.\r
+\r
+ @param[in] Mapping The mapping.\r
+ @param[out] DeviceAddress The device address of the mapping.\r
+ @param[out] NumberOfPages The number of pages of the mapping.\r
+\r
+ @retval EFI_SUCCESS The device information is returned.\r
+ @retval EFI_INVALID_PARAMETER The mapping is invalid.\r
+**/\r
+EFI_STATUS\r
+GetDeviceInfoFromMapping (\r
+ IN VOID *Mapping,\r
+ OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,\r
+ OUT UINTN *NumberOfPages\r
+ )\r
+{\r
+ MAP_INFO *MapInfo;\r
+ LIST_ENTRY *Link;\r
+\r
+ if (Mapping == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ MapInfo = NULL;\r
+ for (Link = GetFirstNode (&gMaps)\r
+ ; !IsNull (&gMaps, Link)\r
+ ; Link = GetNextNode (&gMaps, Link)\r
+ ) {\r
+ MapInfo = MAP_INFO_FROM_LINK (Link);\r
+ if (MapInfo == Mapping) {\r
+ break;\r
+ }\r
+ }\r
+ //\r
+ // Mapping is not a valid value returned by Map()\r
+ //\r
+ if (MapInfo != Mapping) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ *DeviceAddress = MapInfo->DeviceAddress;\r
+ *NumberOfPages = MapInfo->NumberOfPages;\r
+ return EFI_SUCCESS;\r
+}\r
+\r