summaryrefslogtreecommitdiff
path: root/IntelSiliconPkg/Feature/VTd/IntelVTdDxe/BmDma.c
diff options
context:
space:
mode:
Diffstat (limited to 'IntelSiliconPkg/Feature/VTd/IntelVTdDxe/BmDma.c')
-rw-r--r--IntelSiliconPkg/Feature/VTd/IntelVTdDxe/BmDma.c441
1 files changed, 441 insertions, 0 deletions
diff --git a/IntelSiliconPkg/Feature/VTd/IntelVTdDxe/BmDma.c b/IntelSiliconPkg/Feature/VTd/IntelVTdDxe/BmDma.c
new file mode 100644
index 0000000..7a5f361
--- /dev/null
+++ b/IntelSiliconPkg/Feature/VTd/IntelVTdDxe/BmDma.c
@@ -0,0 +1,441 @@
+/** @file
+ BmDma related function
+
+ Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
+ 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 <PiDxe.h>
+
+#include <Protocol/IoMmu.h>
+
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+
+// TBD: May make it a policy
+#define DMA_MEMORY_TOP MAX_UINTN
+//#define DMA_MEMORY_TOP 0x0000000001FFFFFFULL
+
+#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 DeviceAddress;
+} MAP_INFO;
+#define MAP_INFO_FROM_LINK(a) CR (a, MAP_INFO, Link, MAP_INFO_SIGNATURE)
+
+LIST_ENTRY gMaps = INITIALIZE_LIST_HEAD_VARIABLE(gMaps);
+
+/**
+ 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.
+
+**/
+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;
+ EFI_PHYSICAL_ADDRESS DmaMemoryTop;
+ BOOLEAN NeedRemap;
+
+ if (NumberOfBytes == NULL || DeviceAddress == NULL ||
+ Mapping == NULL) {
+ DEBUG ((DEBUG_ERROR, "IoMmuMap: %r\n", EFI_INVALID_PARAMETER));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ DEBUG ((DEBUG_VERBOSE, "IoMmuMap: ==> 0x%08x - 0x%08x (%x)\n", HostAddress, *NumberOfBytes, Operation));
+
+ //
+ // Make sure that Operation is valid
+ //
+ if ((UINT32) Operation >= EdkiiIoMmuOperationMaximum) {
+ DEBUG ((DEBUG_ERROR, "IoMmuMap: %r\n", EFI_INVALID_PARAMETER));
+ return EFI_INVALID_PARAMETER;
+ }
+ NeedRemap = FALSE;
+ PhysicalAddress = (EFI_PHYSICAL_ADDRESS) (UINTN) HostAddress;
+
+ DmaMemoryTop = DMA_MEMORY_TOP;
+
+ //
+ // Alignment check
+ //
+ if ((*NumberOfBytes != ALIGN_VALUE(*NumberOfBytes, SIZE_4KB)) ||
+ (PhysicalAddress != ALIGN_VALUE(PhysicalAddress, SIZE_4KB))) {
+ if ((Operation == EdkiiIoMmuOperationBusMasterCommonBuffer) ||
+ (Operation == EdkiiIoMmuOperationBusMasterCommonBuffer64)) {
+ //
+ // The input buffer might be a subset from IoMmuAllocateBuffer.
+ // Skip the check.
+ //
+ } else {
+ NeedRemap = TRUE;
+ }
+ }
+
+ if ((PhysicalAddress + *NumberOfBytes) >= DMA_MEMORY_TOP) {
+ NeedRemap = TRUE;
+ }
+
+ if (((Operation != EdkiiIoMmuOperationBusMasterRead64 &&
+ Operation != EdkiiIoMmuOperationBusMasterWrite64 &&
+ Operation != EdkiiIoMmuOperationBusMasterCommonBuffer64)) &&
+ ((PhysicalAddress + *NumberOfBytes) > 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;
+ DmaMemoryTop = MIN (DmaMemoryTop, SIZE_4GB - 1);
+ }
+
+ if (Operation == EdkiiIoMmuOperationBusMasterCommonBuffer ||
+ Operation == EdkiiIoMmuOperationBusMasterCommonBuffer64) {
+ if (NeedRemap) {
+ //
+ // Common Buffer operations can not be remapped. If the common buffer
+ // is above 4GB, then it is not possible to generate a mapping, so return
+ // an error.
+ //
+ DEBUG ((DEBUG_ERROR, "IoMmuMap: %r\n", EFI_UNSUPPORTED));
+ 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;
+ DEBUG ((DEBUG_ERROR, "IoMmuMap: %r\n", EFI_OUT_OF_RESOURCES));
+ 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->DeviceAddress = DmaMemoryTop;
+
+ //
+ // Allocate a buffer below 4GB to map the transfer to.
+ //
+ if (NeedRemap) {
+ Status = gBS->AllocatePages (
+ AllocateMaxAddress,
+ EfiBootServicesData,
+ MapInfo->NumberOfPages,
+ &MapInfo->DeviceAddress
+ );
+ if (EFI_ERROR (Status)) {
+ FreePool (MapInfo);
+ *NumberOfBytes = 0;
+ DEBUG ((DEBUG_ERROR, "IoMmuMap: %r\n", Status));
+ 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->DeviceAddress,
+ (VOID *) (UINTN) MapInfo->HostAddress,
+ MapInfo->NumberOfBytes
+ );
+ }
+ } else {
+ MapInfo->DeviceAddress = MapInfo->HostAddress;
+ }
+
+ InsertTailList (&gMaps, &MapInfo->Link);
+
+ //
+ // The DeviceAddress is the address of the maped buffer below 4GB
+ //
+ *DeviceAddress = MapInfo->DeviceAddress;
+ //
+ // Return a pointer to the MAP_INFO structure in Mapping
+ //
+ *Mapping = MapInfo;
+
+ DEBUG ((DEBUG_VERBOSE, "IoMmuMap: 0x%08x - 0x%08x <==\n", *DeviceAddress, *Mapping));
+
+ 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.
+**/
+EFI_STATUS
+EFIAPI
+IoMmuUnmap (
+ IN EDKII_IOMMU_PROTOCOL *This,
+ IN VOID *Mapping
+ )
+{
+ MAP_INFO *MapInfo;
+ LIST_ENTRY *Link;
+
+ DEBUG ((DEBUG_VERBOSE, "IoMmuUnmap: 0x%08x\n", Mapping));
+
+ if (Mapping == NULL) {
+ DEBUG ((DEBUG_ERROR, "IoMmuUnmap: %r\n", EFI_INVALID_PARAMETER));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ MapInfo = NULL;
+ for (Link = GetFirstNode (&gMaps)
+ ; !IsNull (&gMaps, Link)
+ ; Link = GetNextNode (&gMaps, Link)
+ ) {
+ MapInfo = MAP_INFO_FROM_LINK (Link);
+ if (MapInfo == Mapping) {
+ break;
+ }
+ }
+ //
+ // Mapping is not a valid value returned by Map()
+ //
+ if (MapInfo != Mapping) {
+ DEBUG ((DEBUG_ERROR, "IoMmuUnmap: %r\n", EFI_INVALID_PARAMETER));
+ return EFI_INVALID_PARAMETER;
+ }
+ RemoveEntryList (&MapInfo->Link);
+
+ if (MapInfo->DeviceAddress != 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->DeviceAddress,
+ MapInfo->NumberOfBytes
+ );
+ }
+
+ //
+ // Free the mapped buffer and the MAP_INFO structure.
+ //
+ gBS->FreePages (MapInfo->DeviceAddress, 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.
+
+**/
+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;
+
+ DEBUG ((DEBUG_VERBOSE, "IoMmuAllocateBuffer: ==> 0x%08x\n", Pages));
+
+ //
+ // Validate Attributes
+ //
+ if ((Attributes & EDKII_IOMMU_ATTRIBUTE_INVALID_FOR_ALLOCATE_BUFFER) != 0) {
+ DEBUG ((DEBUG_ERROR, "IoMmuAllocateBuffer: %r\n", EFI_UNSUPPORTED));
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // Check for invalid inputs
+ //
+ if (HostAddress == NULL) {
+ DEBUG ((DEBUG_ERROR, "IoMmuAllocateBuffer: %r\n", EFI_INVALID_PARAMETER));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // The only valid memory types are EfiBootServicesData and
+ // EfiRuntimeServicesData
+ //
+ if (MemoryType != EfiBootServicesData &&
+ MemoryType != EfiRuntimeServicesData) {
+ DEBUG ((DEBUG_ERROR, "IoMmuAllocateBuffer: %r\n", EFI_INVALID_PARAMETER));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ PhysicalAddress = DMA_MEMORY_TOP;
+ if ((Attributes & EDKII_IOMMU_ATTRIBUTE_DUAL_ADDRESS_CYCLE) == 0) {
+ //
+ // Limit allocations to memory below 4GB
+ //
+ PhysicalAddress = MIN (PhysicalAddress, SIZE_4GB - 1);
+ }
+ Status = gBS->AllocatePages (
+ AllocateMaxAddress,
+ MemoryType,
+ Pages,
+ &PhysicalAddress
+ );
+ if (!EFI_ERROR (Status)) {
+ *HostAddress = (VOID *) (UINTN) PhysicalAddress;
+ }
+
+ DEBUG ((DEBUG_VERBOSE, "IoMmuAllocateBuffer: 0x%08x <==\n", *HostAddress));
+
+ 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().
+
+**/
+EFI_STATUS
+EFIAPI
+IoMmuFreeBuffer (
+ IN EDKII_IOMMU_PROTOCOL *This,
+ IN UINTN Pages,
+ IN VOID *HostAddress
+ )
+{
+ DEBUG ((DEBUG_VERBOSE, "IoMmuFreeBuffer: 0x%\n", Pages));
+ return gBS->FreePages ((EFI_PHYSICAL_ADDRESS) (UINTN) HostAddress, Pages);
+}
+
+/**
+ Get device information from mapping.
+
+ @param[in] Mapping The mapping.
+ @param[out] DeviceAddress The device address of the mapping.
+ @param[out] NumberOfPages The number of pages of the mapping.
+
+ @retval EFI_SUCCESS The device information is returned.
+ @retval EFI_INVALID_PARAMETER The mapping is invalid.
+**/
+EFI_STATUS
+GetDeviceInfoFromMapping (
+ IN VOID *Mapping,
+ OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,
+ OUT UINTN *NumberOfPages
+ )
+{
+ MAP_INFO *MapInfo;
+ LIST_ENTRY *Link;
+
+ if (Mapping == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ MapInfo = NULL;
+ for (Link = GetFirstNode (&gMaps)
+ ; !IsNull (&gMaps, Link)
+ ; Link = GetNextNode (&gMaps, 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;
+ }
+
+ *DeviceAddress = MapInfo->DeviceAddress;
+ *NumberOfPages = MapInfo->NumberOfPages;
+ return EFI_SUCCESS;
+}
+