]> git.proxmox.com Git - mirror_edk2.git/blobdiff - IntelSiliconPkg/IntelVTdDxe/BmDma.c
IntelSiliconPkg: Add VTd driver.
[mirror_edk2.git] / IntelSiliconPkg / IntelVTdDxe / BmDma.c
diff --git a/IntelSiliconPkg/IntelVTdDxe/BmDma.c b/IntelSiliconPkg/IntelVTdDxe/BmDma.c
new file mode 100644 (file)
index 0000000..5dcee00
--- /dev/null
@@ -0,0 +1,441 @@
+/** @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