3 Generic non-coherent implementation of DmaLib.h
5 Copyright (c) 2008 - 2010, Apple Inc. All rights reserved.<BR>
6 Copyright (c) 2015 - 2017, Linaro, Ltd. All rights reserved.<BR>
8 SPDX-License-Identifier: BSD-2-Clause-Patent
13 #include <Library/BaseLib.h>
14 #include <Library/DebugLib.h>
15 #include <Library/DmaLib.h>
16 #include <Library/DxeServicesTableLib.h>
17 #include <Library/MemoryAllocationLib.h>
18 #include <Library/UefiBootServicesTableLib.h>
19 #include <Library/IoLib.h>
20 #include <Library/BaseMemoryLib.h>
22 #include <Protocol/Cpu.h>
25 EFI_PHYSICAL_ADDRESS HostAddress
;
28 DMA_MAP_OPERATION Operation
;
37 } UNCACHED_ALLOCATION
;
39 STATIC EFI_CPU_ARCH_PROTOCOL
*mCpu
;
40 STATIC LIST_ENTRY UncachedAllocationList
;
42 STATIC PHYSICAL_ADDRESS mDmaHostAddressLimit
;
50 return (PHYSICAL_ADDRESS
)(UINTN
)Address
+ PcdGet64 (PcdDmaDeviceOffset
);
54 Allocates one or more 4KB pages of a certain memory type at a specified
57 Allocates the number of 4KB pages specified by Pages of a certain memory type
58 with an alignment specified by Alignment. The allocated buffer is returned.
59 If Pages is 0, then NULL is returned. If there is not enough memory at the
60 specified alignment remaining to satisfy the request, then NULL is returned.
61 If Alignment is not a power of two and Alignment is not zero, then ASSERT().
62 If Pages plus EFI_SIZE_TO_PAGES (Alignment) overflows, then ASSERT().
64 @param MemoryType The type of memory to allocate.
65 @param Pages The number of 4 KB pages to allocate.
66 @param Alignment The requested alignment of the allocation.
67 Must be a power of two.
68 If Alignment is zero, then byte alignment is
71 @return A pointer to the allocated buffer or NULL if allocation fails.
76 InternalAllocateAlignedPages (
77 IN EFI_MEMORY_TYPE MemoryType
,
83 EFI_PHYSICAL_ADDRESS Memory
;
90 // Alignment must be a power of two or zero.
92 ASSERT ((Alignment
& (Alignment
- 1)) == 0);
98 if (Alignment
> EFI_PAGE_SIZE
) {
100 // Calculate the total number of pages since alignment is larger than page
103 AlignmentMask
= Alignment
- 1;
104 RealPages
= Pages
+ EFI_SIZE_TO_PAGES (Alignment
);
106 // Make sure that Pages plus EFI_SIZE_TO_PAGES (Alignment) does not
109 ASSERT (RealPages
> Pages
);
111 Memory
= mDmaHostAddressLimit
;
112 Status
= gBS
->AllocatePages (
118 if (EFI_ERROR (Status
)) {
122 AlignedMemory
= ((UINTN
)Memory
+ AlignmentMask
) & ~AlignmentMask
;
123 UnalignedPages
= EFI_SIZE_TO_PAGES (AlignedMemory
- (UINTN
)Memory
);
124 if (UnalignedPages
> 0) {
126 // Free first unaligned page(s).
128 Status
= gBS
->FreePages (Memory
, UnalignedPages
);
129 ASSERT_EFI_ERROR (Status
);
132 Memory
= AlignedMemory
+ EFI_PAGES_TO_SIZE (Pages
);
133 UnalignedPages
= RealPages
- Pages
- UnalignedPages
;
134 if (UnalignedPages
> 0) {
136 // Free last unaligned page(s).
138 Status
= gBS
->FreePages (Memory
, UnalignedPages
);
139 ASSERT_EFI_ERROR (Status
);
143 // Do not over-allocate pages in this case.
145 Memory
= mDmaHostAddressLimit
;
146 Status
= gBS
->AllocatePages (
152 if (EFI_ERROR (Status
)) {
156 AlignedMemory
= (UINTN
)Memory
;
159 return (VOID
*)AlignedMemory
;
163 Provides the DMA controller-specific addresses needed to access system memory.
165 Operation is relative to the DMA bus master.
167 @param Operation Indicates if the bus master is going to read or
168 write to system memory.
169 @param HostAddress The system memory address to map to the DMA
171 @param NumberOfBytes On input the number of bytes to map. On output
172 the number of bytes that were mapped.
173 @param DeviceAddress The resulting map address for the bus master
174 controller to use to access the host's
176 @param Mapping A resulting value to pass to Unmap().
178 @retval EFI_SUCCESS The range was mapped for the returned
180 @retval EFI_UNSUPPORTED The HostAddress cannot be mapped as a common
182 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
183 @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack
185 @retval EFI_DEVICE_ERROR The system hardware could not map the requested
192 IN DMA_MAP_OPERATION Operation
,
193 IN VOID
*HostAddress
,
194 IN OUT UINTN
*NumberOfBytes
,
195 OUT PHYSICAL_ADDRESS
*DeviceAddress
,
200 MAP_INFO_INSTANCE
*Map
;
202 EFI_GCD_MEMORY_SPACE_DESCRIPTOR GcdDescriptor
;
205 if ((HostAddress
== NULL
) ||
206 (NumberOfBytes
== NULL
) ||
207 (DeviceAddress
== NULL
) ||
210 return EFI_INVALID_PARAMETER
;
213 if (Operation
>= MapOperationMaximum
) {
214 return EFI_INVALID_PARAMETER
;
217 *DeviceAddress
= HostToDeviceAddress (HostAddress
);
219 // Remember range so we can flush on the other side
220 Map
= AllocatePool (sizeof (MAP_INFO_INSTANCE
));
222 return EFI_OUT_OF_RESOURCES
;
225 if (((UINTN
)HostAddress
+ *NumberOfBytes
) > mDmaHostAddressLimit
) {
226 if (Operation
== MapOperationBusMasterCommonBuffer
) {
227 goto CommonBufferError
;
230 AllocSize
= ALIGN_VALUE (*NumberOfBytes
, mCpu
->DmaBufferAlignment
);
231 Map
->BufferAddress
= InternalAllocateAlignedPages (
233 EFI_SIZE_TO_PAGES (AllocSize
),
234 mCpu
->DmaBufferAlignment
236 if (Map
->BufferAddress
== NULL
) {
237 Status
= EFI_OUT_OF_RESOURCES
;
241 if (Operation
== MapOperationBusMasterRead
) {
242 CopyMem (Map
->BufferAddress
, (VOID
*)(UINTN
)HostAddress
, *NumberOfBytes
);
245 mCpu
->FlushDataCache (
247 (UINTN
)Map
->BufferAddress
,
249 EfiCpuFlushTypeWriteBack
252 *DeviceAddress
= HostToDeviceAddress (Map
->BufferAddress
);
253 } else if ((Operation
!= MapOperationBusMasterRead
) &&
254 ((((UINTN
)HostAddress
& (mCpu
->DmaBufferAlignment
- 1)) != 0) ||
255 ((*NumberOfBytes
& (mCpu
->DmaBufferAlignment
- 1)) != 0)))
257 // Get the cacheability of the region
258 Status
= gDS
->GetMemorySpaceDescriptor ((UINTN
)HostAddress
, &GcdDescriptor
);
259 if (EFI_ERROR (Status
)) {
263 // If the mapped buffer is not an uncached buffer
264 if ((GcdDescriptor
.Attributes
& (EFI_MEMORY_WB
| EFI_MEMORY_WT
)) != 0) {
266 // Operations of type MapOperationBusMasterCommonBuffer are only allowed
267 // on uncached buffers.
269 if (Operation
== MapOperationBusMasterCommonBuffer
) {
270 goto CommonBufferError
;
274 // If the buffer does not fill entire cache lines we must double buffer
275 // into a suitably aligned allocation that allows us to invalidate the
276 // cache without running the risk of corrupting adjacent unrelated data.
277 // Note that pool allocations are guaranteed to be 8 byte aligned, so
278 // we only have to add (alignment - 8) worth of padding.
280 Map
->DoubleBuffer
= TRUE
;
281 AllocSize
= ALIGN_VALUE (*NumberOfBytes
, mCpu
->DmaBufferAlignment
) +
282 (mCpu
->DmaBufferAlignment
- 8);
283 Map
->BufferAddress
= AllocatePool (AllocSize
);
284 if (Map
->BufferAddress
== NULL
) {
285 Status
= EFI_OUT_OF_RESOURCES
;
289 Buffer
= ALIGN_POINTER (Map
->BufferAddress
, mCpu
->DmaBufferAlignment
);
290 *DeviceAddress
= HostToDeviceAddress (Buffer
);
293 // Get rid of any dirty cachelines covering the double buffer. This
294 // prevents them from being written back unexpectedly, potentially
295 // overwriting the data we receive from the device.
297 mCpu
->FlushDataCache (
301 EfiCpuFlushTypeWriteBack
304 Map
->DoubleBuffer
= FALSE
;
307 Map
->DoubleBuffer
= FALSE
;
312 // The operation type check above only executes if the buffer happens to be
313 // misaligned with respect to CWG, but even if it is aligned, we should not
314 // allow arbitrary buffers to be used for creating consistent mappings.
315 // So duplicate the check here when running in DEBUG mode, just to assert
316 // that we are not trying to create a consistent mapping for cached memory.
318 Status
= gDS
->GetMemorySpaceDescriptor ((UINTN
)HostAddress
, &GcdDescriptor
);
319 ASSERT_EFI_ERROR (Status
);
322 Operation
!= MapOperationBusMasterCommonBuffer
||
323 (GcdDescriptor
.Attributes
& (EFI_MEMORY_WB
| EFI_MEMORY_WT
)) == 0
328 // Flush the Data Cache (should not have any effect if the memory region is
330 mCpu
->FlushDataCache (
334 EfiCpuFlushTypeWriteBackInvalidate
338 Map
->HostAddress
= (UINTN
)HostAddress
;
339 Map
->NumberOfBytes
= *NumberOfBytes
;
340 Map
->Operation
= Operation
;
349 "%a: Operation type 'MapOperationBusMasterCommonBuffer' is only "
350 "supported\non memory regions that were allocated using "
351 "DmaAllocateBuffer ()\n",
354 Status
= EFI_UNSUPPORTED
;
362 Completes the DmaMapBusMasterRead(), DmaMapBusMasterWrite(), or
363 DmaMapBusMasterCommonBuffer() operation and releases any corresponding
366 @param Mapping The mapping value returned from DmaMap*().
368 @retval EFI_SUCCESS The range was unmapped.
369 @retval EFI_DEVICE_ERROR The data was not committed to the target system
371 @retval EFI_INVALID_PARAMETER An inconsistency was detected between the
372 mapping type and the DoubleBuffer field
381 MAP_INFO_INSTANCE
*Map
;
386 if (Mapping
== NULL
) {
388 return EFI_INVALID_PARAMETER
;
391 Map
= (MAP_INFO_INSTANCE
*)Mapping
;
393 Status
= EFI_SUCCESS
;
394 if (((UINTN
)Map
->HostAddress
+ Map
->NumberOfBytes
) > mDmaHostAddressLimit
) {
395 AllocSize
= ALIGN_VALUE (Map
->NumberOfBytes
, mCpu
->DmaBufferAlignment
);
396 if (Map
->Operation
== MapOperationBusMasterWrite
) {
397 mCpu
->FlushDataCache (
399 (UINTN
)Map
->BufferAddress
,
401 EfiCpuFlushTypeInvalidate
404 (VOID
*)(UINTN
)Map
->HostAddress
,
410 FreePages (Map
->BufferAddress
, EFI_SIZE_TO_PAGES (AllocSize
));
411 } else if (Map
->DoubleBuffer
) {
412 ASSERT (Map
->Operation
== MapOperationBusMasterWrite
);
414 if (Map
->Operation
!= MapOperationBusMasterWrite
) {
415 Status
= EFI_INVALID_PARAMETER
;
417 Buffer
= ALIGN_POINTER (Map
->BufferAddress
, mCpu
->DmaBufferAlignment
);
419 mCpu
->FlushDataCache (
423 EfiCpuFlushTypeInvalidate
426 CopyMem ((VOID
*)(UINTN
)Map
->HostAddress
, Buffer
, Map
->NumberOfBytes
);
428 FreePool (Map
->BufferAddress
);
431 if (Map
->Operation
== MapOperationBusMasterWrite
) {
433 // Make sure we read buffer from uncached memory and not the cache
435 mCpu
->FlushDataCache (
439 EfiCpuFlushTypeInvalidate
450 Allocates pages that are suitable for an DmaMap() of type
451 MapOperationBusMasterCommonBuffer mapping.
453 @param MemoryType The type of memory to allocate,
454 EfiBootServicesData or EfiRuntimeServicesData.
455 @param Pages The number of pages to allocate.
456 @param HostAddress A pointer to store the base system memory
457 address of the allocated range.
459 @retval EFI_SUCCESS The requested memory pages were allocated.
460 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
461 @retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated.
467 IN EFI_MEMORY_TYPE MemoryType
,
469 OUT VOID
**HostAddress
472 return DmaAllocateAlignedBuffer (MemoryType
, Pages
, 0, HostAddress
);
476 Allocates pages that are suitable for an DmaMap() of type
477 MapOperationBusMasterCommonBuffer mapping, at the requested alignment.
479 @param MemoryType The type of memory to allocate,
480 EfiBootServicesData or EfiRuntimeServicesData.
481 @param Pages The number of pages to allocate.
482 @param Alignment Alignment in bytes of the base of the returned
483 buffer (must be a power of 2)
484 @param HostAddress A pointer to store the base system memory
485 address of the allocated range.
487 @retval EFI_SUCCESS The requested memory pages were allocated.
488 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
489 @retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated.
494 DmaAllocateAlignedBuffer (
495 IN EFI_MEMORY_TYPE MemoryType
,
498 OUT VOID
**HostAddress
501 EFI_GCD_MEMORY_SPACE_DESCRIPTOR GcdDescriptor
;
504 UNCACHED_ALLOCATION
*Alloc
;
507 if (Alignment
== 0) {
508 Alignment
= EFI_PAGE_SIZE
;
511 if ((HostAddress
== NULL
) ||
512 ((Alignment
& (Alignment
- 1)) != 0))
514 return EFI_INVALID_PARAMETER
;
517 if ((MemoryType
== EfiBootServicesData
) ||
518 (MemoryType
== EfiRuntimeServicesData
))
520 Allocation
= InternalAllocateAlignedPages (MemoryType
, Pages
, Alignment
);
522 return EFI_INVALID_PARAMETER
;
525 if (Allocation
== NULL
) {
526 return EFI_OUT_OF_RESOURCES
;
529 // Get the cacheability of the region
530 Status
= gDS
->GetMemorySpaceDescriptor ((UINTN
)Allocation
, &GcdDescriptor
);
531 if (EFI_ERROR (Status
)) {
535 // Choose a suitable uncached memory type that is supported by the region
536 if (GcdDescriptor
.Capabilities
& EFI_MEMORY_WC
) {
537 MemType
= EFI_MEMORY_WC
;
538 } else if (GcdDescriptor
.Capabilities
& EFI_MEMORY_UC
) {
539 MemType
= EFI_MEMORY_UC
;
541 Status
= EFI_UNSUPPORTED
;
545 Alloc
= AllocatePool (sizeof *Alloc
);
550 Alloc
->HostAddress
= Allocation
;
551 Alloc
->NumPages
= Pages
;
552 Alloc
->Attributes
= GcdDescriptor
.Attributes
;
554 InsertHeadList (&UncachedAllocationList
, &Alloc
->Link
);
556 // Remap the region with the new attributes
557 Status
= gDS
->SetMemorySpaceAttributes (
558 (PHYSICAL_ADDRESS
)(UINTN
)Allocation
,
559 EFI_PAGES_TO_SIZE (Pages
),
562 if (EFI_ERROR (Status
)) {
566 Status
= mCpu
->FlushDataCache (
568 (PHYSICAL_ADDRESS
)(UINTN
)Allocation
,
569 EFI_PAGES_TO_SIZE (Pages
),
570 EfiCpuFlushTypeInvalidate
572 if (EFI_ERROR (Status
)) {
576 *HostAddress
= Allocation
;
581 RemoveEntryList (&Alloc
->Link
);
585 FreePages (Allocation
, Pages
);
590 Frees memory that was allocated with DmaAllocateBuffer().
592 @param Pages The number of pages to free.
593 @param HostAddress The base system memory address of the allocated
596 @retval EFI_SUCCESS The requested memory pages were freed.
597 @retval EFI_INVALID_PARAMETER The memory range specified by HostAddress and
598 Pages was not allocated with
610 UNCACHED_ALLOCATION
*Alloc
;
614 if (HostAddress
== NULL
) {
615 return EFI_INVALID_PARAMETER
;
618 for (Link
= GetFirstNode (&UncachedAllocationList
), Found
= FALSE
;
619 !IsNull (&UncachedAllocationList
, Link
);
620 Link
= GetNextNode (&UncachedAllocationList
, Link
))
622 Alloc
= BASE_CR (Link
, UNCACHED_ALLOCATION
, Link
);
623 if ((Alloc
->HostAddress
== HostAddress
) && (Alloc
->NumPages
== Pages
)) {
631 return EFI_INVALID_PARAMETER
;
634 RemoveEntryList (&Alloc
->Link
);
636 Status
= gDS
->SetMemorySpaceAttributes (
637 (PHYSICAL_ADDRESS
)(UINTN
)HostAddress
,
638 EFI_PAGES_TO_SIZE (Pages
),
641 if (EFI_ERROR (Status
)) {
646 // If we fail to restore the original attributes, it is better to leak the
647 // memory than to return it to the heap
649 FreePages (HostAddress
, Pages
);
658 NonCoherentDmaLibConstructor (
659 IN EFI_HANDLE ImageHandle
,
660 IN EFI_SYSTEM_TABLE
*SystemTable
663 InitializeListHead (&UncachedAllocationList
);
666 // Ensure that the combination of DMA addressing offset and limit produces
669 ASSERT (PcdGet64 (PcdDmaDeviceLimit
) > PcdGet64 (PcdDmaDeviceOffset
));
671 mDmaHostAddressLimit
= PcdGet64 (PcdDmaDeviceLimit
) -
672 PcdGet64 (PcdDmaDeviceOffset
);
674 // Get the Cpu protocol for later use
675 return gBS
->LocateProtocol (&gEfiCpuArchProtocolGuid
, NULL
, (VOID
**)&mCpu
);