+++ /dev/null
-/** @file\r
- BmDma related function\r
-\r
- Copyright (c) 2017 - 2018, Intel Corporation. All rights reserved.<BR>\r
- SPDX-License-Identifier: BSD-2-Clause-Patent\r
-\r
-**/\r
-\r
-#include "DmaProtection.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_HANDLE_INFO_SIGNATURE SIGNATURE_32 ('H', 'M', 'A', 'P')\r
-typedef struct {\r
- UINT32 Signature;\r
- LIST_ENTRY Link;\r
- EFI_HANDLE DeviceHandle;\r
- UINT64 IoMmuAccess;\r
-} MAP_HANDLE_INFO;\r
-#define MAP_HANDLE_INFO_FROM_LINK(a) CR (a, MAP_HANDLE_INFO, Link, MAP_HANDLE_INFO_SIGNATURE)\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
- LIST_ENTRY HandleList;\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
- This function fills DeviceHandle/IoMmuAccess to the MAP_HANDLE_INFO,\r
- based upon the DeviceAddress.\r
-\r
- @param[in] DeviceHandle The device who initiates the DMA access request.\r
- @param[in] DeviceAddress The base of device memory address to be used as the DMA memory.\r
- @param[in] Length The length of device memory address to be used as the DMA memory.\r
- @param[in] IoMmuAccess The IOMMU access.\r
-\r
-**/\r
-VOID\r
-SyncDeviceHandleToMapInfo (\r
- IN EFI_HANDLE DeviceHandle,\r
- IN EFI_PHYSICAL_ADDRESS DeviceAddress,\r
- IN UINT64 Length,\r
- IN UINT64 IoMmuAccess\r
- )\r
-{\r
- MAP_INFO *MapInfo;\r
- MAP_HANDLE_INFO *MapHandleInfo;\r
- LIST_ENTRY *Link;\r
- EFI_TPL OriginalTpl;\r
-\r
- //\r
- // Find MapInfo according to DeviceAddress\r
- //\r
- OriginalTpl = gBS->RaiseTPL (VTD_TPL_LEVEL);\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->DeviceAddress == DeviceAddress) {\r
- break;\r
- }\r
- }\r
- if ((MapInfo == NULL) || (MapInfo->DeviceAddress != DeviceAddress)) {\r
- DEBUG ((DEBUG_ERROR, "SyncDeviceHandleToMapInfo: DeviceAddress(0x%lx) - not found\n", DeviceAddress));\r
- gBS->RestoreTPL (OriginalTpl);\r
- return ;\r
- }\r
-\r
- //\r
- // Find MapHandleInfo according to DeviceHandle\r
- //\r
- MapHandleInfo = NULL;\r
- for (Link = GetFirstNode (&MapInfo->HandleList)\r
- ; !IsNull (&MapInfo->HandleList, Link)\r
- ; Link = GetNextNode (&MapInfo->HandleList, Link)\r
- ) {\r
- MapHandleInfo = MAP_HANDLE_INFO_FROM_LINK (Link);\r
- if (MapHandleInfo->DeviceHandle == DeviceHandle) {\r
- break;\r
- }\r
- }\r
- if ((MapHandleInfo != NULL) && (MapHandleInfo->DeviceHandle == DeviceHandle)) {\r
- MapHandleInfo->IoMmuAccess = IoMmuAccess;\r
- gBS->RestoreTPL (OriginalTpl);\r
- return ;\r
- }\r
-\r
- //\r
- // No DeviceHandle\r
- // Initialize and insert the MAP_HANDLE_INFO structure\r
- //\r
- MapHandleInfo = AllocatePool (sizeof (MAP_HANDLE_INFO));\r
- if (MapHandleInfo == NULL) {\r
- DEBUG ((DEBUG_ERROR, "SyncDeviceHandleToMapInfo: %r\n", EFI_OUT_OF_RESOURCES));\r
- gBS->RestoreTPL (OriginalTpl);\r
- return ;\r
- }\r
-\r
- MapHandleInfo->Signature = MAP_HANDLE_INFO_SIGNATURE;\r
- MapHandleInfo->DeviceHandle = DeviceHandle;\r
- MapHandleInfo->IoMmuAccess = IoMmuAccess;\r
-\r
- InsertTailList (&MapInfo->HandleList, &MapHandleInfo->Link);\r
- gBS->RestoreTPL (OriginalTpl);\r
-\r
- return ;\r
-}\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
- EFI_TPL OriginalTpl;\r
-\r
- if (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
- DEBUG ((DEBUG_VERBOSE, "IoMmuMap: ==> 0x%08x - 0x%08x (%x)\n", HostAddress, *NumberOfBytes, Operation));\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
- // is 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
- InitializeListHead(&MapInfo->HandleList);\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
- OriginalTpl = gBS->RaiseTPL (VTD_TPL_LEVEL);\r
- InsertTailList (&gMaps, &MapInfo->Link);\r
- gBS->RestoreTPL (OriginalTpl);\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
- MAP_HANDLE_INFO *MapHandleInfo;\r
- LIST_ENTRY *Link;\r
- EFI_TPL OriginalTpl;\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
- OriginalTpl = gBS->RaiseTPL (VTD_TPL_LEVEL);\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
- gBS->RestoreTPL (OriginalTpl);\r
- DEBUG ((DEBUG_ERROR, "IoMmuUnmap: %r\n", EFI_INVALID_PARAMETER));\r
- return EFI_INVALID_PARAMETER;\r
- }\r
- RemoveEntryList (&MapInfo->Link);\r
- gBS->RestoreTPL (OriginalTpl);\r
-\r
- //\r
- // remove all nodes in MapInfo->HandleList\r
- //\r
- while (!IsListEmpty (&MapInfo->HandleList)) {\r
- MapHandleInfo = MAP_HANDLE_INFO_FROM_LINK (MapInfo->HandleList.ForwardLink);\r
- RemoveEntryList (&MapHandleInfo->Link);\r
- FreePool (MapHandleInfo);\r
- }\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, MEMORY_CACHED and DUAL_ADDRESS_CYCLE.\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