]> git.proxmox.com Git - mirror_edk2.git/blobdiff - EmbeddedPkg/Library/NonCoherentDmaLib/NonCoherentDmaLib.c
EmbeddedPkg/NonCoherentDmaLib: implement support for DMA range limits
[mirror_edk2.git] / EmbeddedPkg / Library / NonCoherentDmaLib / NonCoherentDmaLib.c
index 78220f6358aa747dfd7672ba0b4529fa34894b05..1153457654350c4fdf33650aa81887a23ee0f4ee 100644 (file)
@@ -40,6 +40,8 @@ typedef struct {
 STATIC EFI_CPU_ARCH_PROTOCOL      *mCpu;\r
 STATIC LIST_ENTRY                 UncachedAllocationList;\r
 \r
+STATIC PHYSICAL_ADDRESS           mDmaHostAddressLimit;\r
+\r
 STATIC\r
 PHYSICAL_ADDRESS\r
 HostToDeviceAddress (\r
@@ -49,6 +51,102 @@ HostToDeviceAddress (
   return (PHYSICAL_ADDRESS)(UINTN)Address + PcdGet64 (PcdDmaDeviceOffset);\r
 }\r
 \r
+/**\r
+  Allocates one or more 4KB pages of a certain memory type at a specified\r
+  alignment.\r
+\r
+  Allocates the number of 4KB pages specified by Pages of a certain memory type\r
+  with an alignment specified by Alignment. The allocated buffer is returned.\r
+  If Pages is 0, then NULL is returned. If there is not enough memory at the\r
+  specified alignment remaining to satisfy the request, then NULL is returned.\r
+  If Alignment is not a power of two and Alignment is not zero, then ASSERT().\r
+  If Pages plus EFI_SIZE_TO_PAGES (Alignment) overflows, then ASSERT().\r
+\r
+  @param  MemoryType            The type of memory to allocate.\r
+  @param  Pages                 The number of 4 KB pages to allocate.\r
+  @param  Alignment             The requested alignment of the allocation.\r
+                                Must be a power of two.\r
+                                If Alignment is zero, then byte alignment is\r
+                                used.\r
+\r
+  @return A pointer to the allocated buffer or NULL if allocation fails.\r
+\r
+**/\r
+STATIC\r
+VOID *\r
+InternalAllocateAlignedPages (\r
+  IN EFI_MEMORY_TYPE  MemoryType,\r
+  IN UINTN            Pages,\r
+  IN UINTN            Alignment\r
+  )\r
+{\r
+  EFI_STATUS            Status;\r
+  EFI_PHYSICAL_ADDRESS  Memory;\r
+  UINTN                 AlignedMemory;\r
+  UINTN                 AlignmentMask;\r
+  UINTN                 UnalignedPages;\r
+  UINTN                 RealPages;\r
+\r
+  //\r
+  // Alignment must be a power of two or zero.\r
+  //\r
+  ASSERT ((Alignment & (Alignment - 1)) == 0);\r
+\r
+  if (Pages == 0) {\r
+    return NULL;\r
+  }\r
+  if (Alignment > EFI_PAGE_SIZE) {\r
+    //\r
+    // Calculate the total number of pages since alignment is larger than page\r
+    // size.\r
+    //\r
+    AlignmentMask  = Alignment - 1;\r
+    RealPages      = Pages + EFI_SIZE_TO_PAGES (Alignment);\r
+    //\r
+    // Make sure that Pages plus EFI_SIZE_TO_PAGES (Alignment) does not\r
+    // overflow.\r
+    //\r
+    ASSERT (RealPages > Pages);\r
+\r
+    Memory = mDmaHostAddressLimit;\r
+    Status = gBS->AllocatePages (AllocateMaxAddress, MemoryType, RealPages,\r
+                    &Memory);\r
+    if (EFI_ERROR (Status)) {\r
+      return NULL;\r
+    }\r
+    AlignedMemory  = ((UINTN)Memory + AlignmentMask) & ~AlignmentMask;\r
+    UnalignedPages = EFI_SIZE_TO_PAGES (AlignedMemory - (UINTN)Memory);\r
+    if (UnalignedPages > 0) {\r
+      //\r
+      // Free first unaligned page(s).\r
+      //\r
+      Status = gBS->FreePages (Memory, UnalignedPages);\r
+      ASSERT_EFI_ERROR (Status);\r
+    }\r
+    Memory         = AlignedMemory + EFI_PAGES_TO_SIZE (Pages);\r
+    UnalignedPages = RealPages - Pages - UnalignedPages;\r
+    if (UnalignedPages > 0) {\r
+      //\r
+      // Free last unaligned page(s).\r
+      //\r
+      Status = gBS->FreePages (Memory, UnalignedPages);\r
+      ASSERT_EFI_ERROR (Status);\r
+    }\r
+  } else {\r
+    //\r
+    // Do not over-allocate pages in this case.\r
+    //\r
+    Memory = mDmaHostAddressLimit;\r
+    Status = gBS->AllocatePages (AllocateMaxAddress, MemoryType, Pages,\r
+                    &Memory);\r
+    if (EFI_ERROR (Status)) {\r
+      return NULL;\r
+    }\r
+    AlignedMemory = (UINTN)Memory;\r
+  }\r
+  return (VOID *)AlignedMemory;\r
+}\r
+\r
 /**\r
   Provides the DMA controller-specific addresses needed to access system memory.\r
 \r
@@ -111,7 +209,30 @@ DmaMap (
     return  EFI_OUT_OF_RESOURCES;\r
   }\r
 \r
-  if (Operation != MapOperationBusMasterRead &&\r
+  if (((UINTN)HostAddress + *NumberOfBytes) > mDmaHostAddressLimit) {\r
+\r
+    if (Operation == MapOperationBusMasterCommonBuffer) {\r
+      goto CommonBufferError;\r
+    }\r
+\r
+    AllocSize = ALIGN_VALUE (*NumberOfBytes, mCpu->DmaBufferAlignment);\r
+    Map->BufferAddress = InternalAllocateAlignedPages (EfiBootServicesData,\r
+                           EFI_SIZE_TO_PAGES (AllocSize),\r
+                           mCpu->DmaBufferAlignment);\r
+    if (Map->BufferAddress == NULL) {\r
+      Status = EFI_OUT_OF_RESOURCES;\r
+      goto FreeMapInfo;\r
+    }\r
+\r
+    if (Map->Operation == MapOperationBusMasterRead) {\r
+      CopyMem (Map->BufferAddress, (VOID *)(UINTN)Map->HostAddress,\r
+        *NumberOfBytes);\r
+    }\r
+    mCpu->FlushDataCache (mCpu, (UINTN)Map->BufferAddress, AllocSize,\r
+            EfiCpuFlushTypeWriteBack);\r
+\r
+    *DeviceAddress = HostToDeviceAddress (Map->BufferAddress);\r
+  } else if (Operation != MapOperationBusMasterRead &&\r
       ((((UINTN)HostAddress & (mCpu->DmaBufferAlignment - 1)) != 0) ||\r
        ((*NumberOfBytes & (mCpu->DmaBufferAlignment - 1)) != 0))) {\r
 \r
@@ -128,12 +249,7 @@ DmaMap (
       // on uncached buffers.\r
       //\r
       if (Operation == MapOperationBusMasterCommonBuffer) {\r
-        DEBUG ((DEBUG_ERROR,\r
-          "%a: Operation type 'MapOperationBusMasterCommonBuffer' is only "\r
-          "supported\non memory regions that were allocated using "\r
-          "DmaAllocateBuffer ()\n", __FUNCTION__));\r
-        Status = EFI_UNSUPPORTED;\r
-        goto FreeMapInfo;\r
+        goto CommonBufferError;\r
       }\r
 \r
       //\r
@@ -199,6 +315,12 @@ DmaMap (
 \r
   return EFI_SUCCESS;\r
 \r
+CommonBufferError:\r
+  DEBUG ((DEBUG_ERROR,\r
+    "%a: Operation type 'MapOperationBusMasterCommonBuffer' is only "\r
+    "supported\non memory regions that were allocated using "\r
+    "DmaAllocateBuffer ()\n", __FUNCTION__));\r
+  Status = EFI_UNSUPPORTED;\r
 FreeMapInfo:\r
   FreePool (Map);\r
 \r
@@ -229,6 +351,7 @@ DmaUnmap (
   MAP_INFO_INSTANCE *Map;\r
   EFI_STATUS        Status;\r
   VOID              *Buffer;\r
+  UINTN             AllocSize;\r
 \r
   if (Mapping == NULL) {\r
     ASSERT (FALSE);\r
@@ -238,7 +361,17 @@ DmaUnmap (
   Map = (MAP_INFO_INSTANCE *)Mapping;\r
 \r
   Status = EFI_SUCCESS;\r
-  if (Map->DoubleBuffer) {\r
+  if (((UINTN)Map->HostAddress + Map->NumberOfBytes) > mDmaHostAddressLimit) {\r
+    AllocSize = ALIGN_VALUE (Map->NumberOfBytes, mCpu->DmaBufferAlignment);\r
+    if (Map->Operation == MapOperationBusMasterWrite) {\r
+      mCpu->FlushDataCache (mCpu, (UINTN)Map->BufferAddress, AllocSize,\r
+              EfiCpuFlushTypeInvalidate);\r
+      CopyMem ((VOID *)(UINTN)Map->HostAddress, Map->BufferAddress,\r
+        Map->NumberOfBytes);\r
+    }\r
+    FreePages (Map->BufferAddress, EFI_SIZE_TO_PAGES (AllocSize));\r
+  } else if (Map->DoubleBuffer) {\r
+\r
     ASSERT (Map->Operation == MapOperationBusMasterWrite);\r
 \r
     if (Map->Operation != MapOperationBusMasterWrite) {\r
@@ -335,10 +468,9 @@ DmaAllocateAlignedBuffer (
     return EFI_INVALID_PARAMETER;\r
   }\r
 \r
-  if (MemoryType == EfiBootServicesData) {\r
-    Allocation = AllocateAlignedPages (Pages, Alignment);\r
-  } else if (MemoryType == EfiRuntimeServicesData) {\r
-    Allocation = AllocateAlignedRuntimePages (Pages, Alignment);\r
+  if (MemoryType == EfiBootServicesData ||\r
+      MemoryType == EfiRuntimeServicesData) {\r
+    Allocation = InternalAllocateAlignedPages (MemoryType, Pages, Alignment);\r
   } else {\r
     return EFI_INVALID_PARAMETER;\r
   }\r
@@ -479,6 +611,15 @@ NonCoherentDmaLibConstructor (
 {\r
   InitializeListHead (&UncachedAllocationList);\r
 \r
+  //\r
+  // Ensure that the combination of DMA addressing offset and limit produces\r
+  // a sane value.\r
+  //\r
+  ASSERT (PcdGet64 (PcdDmaDeviceLimit) > PcdGet64 (PcdDmaDeviceOffset));\r
+\r
+  mDmaHostAddressLimit = PcdGet64 (PcdDmaDeviceLimit) -\r
+                         PcdGet64 (PcdDmaDeviceOffset);\r
+\r
   // Get the Cpu protocol for later use\r
   return gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **)&mCpu);\r
 }\r