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
;
38 } UNCACHED_ALLOCATION
;
40 STATIC EFI_CPU_ARCH_PROTOCOL
*mCpu
;
41 STATIC LIST_ENTRY UncachedAllocationList
;
49 return (PHYSICAL_ADDRESS
)(UINTN
)Address
+ PcdGet64 (PcdDmaDeviceOffset
);
53 Provides the DMA controller-specific addresses needed to access system memory.
55 Operation is relative to the DMA bus master.
57 @param Operation Indicates if the bus master is going to read or
58 write to system memory.
59 @param HostAddress The system memory address to map to the DMA
61 @param NumberOfBytes On input the number of bytes to map. On output
62 the number of bytes that were mapped.
63 @param DeviceAddress The resulting map address for the bus master
64 controller to use to access the host's
66 @param Mapping A resulting value to pass to Unmap().
68 @retval EFI_SUCCESS The range was mapped for the returned
70 @retval EFI_UNSUPPORTED The HostAddress cannot be mapped as a common
72 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
73 @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack
75 @retval EFI_DEVICE_ERROR The system hardware could not map the requested
82 IN DMA_MAP_OPERATION Operation
,
84 IN OUT UINTN
*NumberOfBytes
,
85 OUT PHYSICAL_ADDRESS
*DeviceAddress
,
90 MAP_INFO_INSTANCE
*Map
;
92 EFI_GCD_MEMORY_SPACE_DESCRIPTOR GcdDescriptor
;
95 if (HostAddress
== NULL
||
96 NumberOfBytes
== NULL
||
97 DeviceAddress
== NULL
||
99 return EFI_INVALID_PARAMETER
;
102 if (Operation
>= MapOperationMaximum
) {
103 return EFI_INVALID_PARAMETER
;
106 *DeviceAddress
= HostToDeviceAddress (HostAddress
);
108 // Remember range so we can flush on the other side
109 Map
= AllocatePool (sizeof (MAP_INFO_INSTANCE
));
111 return EFI_OUT_OF_RESOURCES
;
114 if (Operation
!= MapOperationBusMasterRead
&&
115 ((((UINTN
)HostAddress
& (mCpu
->DmaBufferAlignment
- 1)) != 0) ||
116 ((*NumberOfBytes
& (mCpu
->DmaBufferAlignment
- 1)) != 0))) {
118 // Get the cacheability of the region
119 Status
= gDS
->GetMemorySpaceDescriptor ((UINTN
)HostAddress
, &GcdDescriptor
);
120 if (EFI_ERROR(Status
)) {
124 // If the mapped buffer is not an uncached buffer
125 if ((GcdDescriptor
.Attributes
& (EFI_MEMORY_WB
| EFI_MEMORY_WT
)) != 0) {
127 // Operations of type MapOperationBusMasterCommonBuffer are only allowed
128 // on uncached buffers.
130 if (Operation
== MapOperationBusMasterCommonBuffer
) {
132 "%a: Operation type 'MapOperationBusMasterCommonBuffer' is only "
133 "supported\non memory regions that were allocated using "
134 "DmaAllocateBuffer ()\n", __FUNCTION__
));
135 Status
= EFI_UNSUPPORTED
;
140 // If the buffer does not fill entire cache lines we must double buffer
141 // into a suitably aligned allocation that allows us to invalidate the
142 // cache without running the risk of corrupting adjacent unrelated data.
143 // Note that pool allocations are guaranteed to be 8 byte aligned, so
144 // we only have to add (alignment - 8) worth of padding.
146 Map
->DoubleBuffer
= TRUE
;
147 AllocSize
= ALIGN_VALUE (*NumberOfBytes
, mCpu
->DmaBufferAlignment
) +
148 (mCpu
->DmaBufferAlignment
- 8);
149 Map
->BufferAddress
= AllocatePool (AllocSize
);
150 if (Map
->BufferAddress
== NULL
) {
151 Status
= EFI_OUT_OF_RESOURCES
;
155 Buffer
= ALIGN_POINTER (Map
->BufferAddress
, mCpu
->DmaBufferAlignment
);
156 *DeviceAddress
= HostToDeviceAddress (Buffer
);
159 // Get rid of any dirty cachelines covering the double buffer. This
160 // prevents them from being written back unexpectedly, potentially
161 // overwriting the data we receive from the device.
163 mCpu
->FlushDataCache (mCpu
, (UINTN
)Buffer
, *NumberOfBytes
,
164 EfiCpuFlushTypeWriteBack
);
166 Map
->DoubleBuffer
= FALSE
;
169 Map
->DoubleBuffer
= FALSE
;
174 // The operation type check above only executes if the buffer happens to be
175 // misaligned with respect to CWG, but even if it is aligned, we should not
176 // allow arbitrary buffers to be used for creating consistent mappings.
177 // So duplicate the check here when running in DEBUG mode, just to assert
178 // that we are not trying to create a consistent mapping for cached memory.
180 Status
= gDS
->GetMemorySpaceDescriptor ((UINTN
)HostAddress
, &GcdDescriptor
);
181 ASSERT_EFI_ERROR(Status
);
183 ASSERT (Operation
!= MapOperationBusMasterCommonBuffer
||
184 (GcdDescriptor
.Attributes
& (EFI_MEMORY_WB
| EFI_MEMORY_WT
)) == 0);
188 // Flush the Data Cache (should not have any effect if the memory region is
190 mCpu
->FlushDataCache (mCpu
, (UINTN
)HostAddress
, *NumberOfBytes
,
191 EfiCpuFlushTypeWriteBackInvalidate
);
194 Map
->HostAddress
= (UINTN
)HostAddress
;
195 Map
->NumberOfBytes
= *NumberOfBytes
;
196 Map
->Operation
= Operation
;
210 Completes the DmaMapBusMasterRead(), DmaMapBusMasterWrite(), or
211 DmaMapBusMasterCommonBuffer() operation and releases any corresponding
214 @param Mapping The mapping value returned from DmaMap*().
216 @retval EFI_SUCCESS The range was unmapped.
217 @retval EFI_DEVICE_ERROR The data was not committed to the target system
219 @retval EFI_INVALID_PARAMETER An inconsistency was detected between the
220 mapping type and the DoubleBuffer field
229 MAP_INFO_INSTANCE
*Map
;
233 if (Mapping
== NULL
) {
235 return EFI_INVALID_PARAMETER
;
238 Map
= (MAP_INFO_INSTANCE
*)Mapping
;
240 Status
= EFI_SUCCESS
;
241 if (Map
->DoubleBuffer
) {
242 ASSERT (Map
->Operation
== MapOperationBusMasterWrite
);
244 if (Map
->Operation
!= MapOperationBusMasterWrite
) {
245 Status
= EFI_INVALID_PARAMETER
;
247 Buffer
= ALIGN_POINTER (Map
->BufferAddress
, mCpu
->DmaBufferAlignment
);
249 mCpu
->FlushDataCache (mCpu
, (UINTN
)Buffer
, Map
->NumberOfBytes
,
250 EfiCpuFlushTypeInvalidate
);
252 CopyMem ((VOID
*)(UINTN
)Map
->HostAddress
, Buffer
, Map
->NumberOfBytes
);
254 FreePool (Map
->BufferAddress
);
257 if (Map
->Operation
== MapOperationBusMasterWrite
) {
259 // Make sure we read buffer from uncached memory and not the cache
261 mCpu
->FlushDataCache (mCpu
, Map
->HostAddress
, Map
->NumberOfBytes
,
262 EfiCpuFlushTypeInvalidate
);
272 Allocates pages that are suitable for an DmaMap() of type
273 MapOperationBusMasterCommonBuffer mapping.
275 @param MemoryType The type of memory to allocate,
276 EfiBootServicesData or EfiRuntimeServicesData.
277 @param Pages The number of pages to allocate.
278 @param HostAddress A pointer to store the base system memory
279 address of the allocated range.
281 @retval EFI_SUCCESS The requested memory pages were allocated.
282 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
283 @retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated.
289 IN EFI_MEMORY_TYPE MemoryType
,
291 OUT VOID
**HostAddress
294 return DmaAllocateAlignedBuffer (MemoryType
, Pages
, 0, HostAddress
);
298 Allocates pages that are suitable for an DmaMap() of type
299 MapOperationBusMasterCommonBuffer mapping, at the requested alignment.
301 @param MemoryType The type of memory to allocate,
302 EfiBootServicesData or EfiRuntimeServicesData.
303 @param Pages The number of pages to allocate.
304 @param Alignment Alignment in bytes of the base of the returned
305 buffer (must be a power of 2)
306 @param HostAddress A pointer to store the base system memory
307 address of the allocated range.
309 @retval EFI_SUCCESS The requested memory pages were allocated.
310 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
311 @retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated.
316 DmaAllocateAlignedBuffer (
317 IN EFI_MEMORY_TYPE MemoryType
,
320 OUT VOID
**HostAddress
323 EFI_GCD_MEMORY_SPACE_DESCRIPTOR GcdDescriptor
;
326 UNCACHED_ALLOCATION
*Alloc
;
329 if (Alignment
== 0) {
330 Alignment
= EFI_PAGE_SIZE
;
333 if (HostAddress
== NULL
||
334 (Alignment
& (Alignment
- 1)) != 0) {
335 return EFI_INVALID_PARAMETER
;
338 if (MemoryType
== EfiBootServicesData
) {
339 Allocation
= AllocateAlignedPages (Pages
, Alignment
);
340 } else if (MemoryType
== EfiRuntimeServicesData
) {
341 Allocation
= AllocateAlignedRuntimePages (Pages
, Alignment
);
343 return EFI_INVALID_PARAMETER
;
346 if (Allocation
== NULL
) {
347 return EFI_OUT_OF_RESOURCES
;
350 // Get the cacheability of the region
351 Status
= gDS
->GetMemorySpaceDescriptor ((UINTN
)Allocation
, &GcdDescriptor
);
352 if (EFI_ERROR(Status
)) {
356 // Choose a suitable uncached memory type that is supported by the region
357 if (GcdDescriptor
.Capabilities
& EFI_MEMORY_WC
) {
358 MemType
= EFI_MEMORY_WC
;
359 } else if (GcdDescriptor
.Capabilities
& EFI_MEMORY_UC
) {
360 MemType
= EFI_MEMORY_UC
;
362 Status
= EFI_UNSUPPORTED
;
366 Alloc
= AllocatePool (sizeof *Alloc
);
371 Alloc
->HostAddress
= Allocation
;
372 Alloc
->NumPages
= Pages
;
373 Alloc
->Attributes
= GcdDescriptor
.Attributes
;
375 InsertHeadList (&UncachedAllocationList
, &Alloc
->Link
);
377 // Remap the region with the new attributes
378 Status
= gDS
->SetMemorySpaceAttributes ((PHYSICAL_ADDRESS
)(UINTN
)Allocation
,
379 EFI_PAGES_TO_SIZE (Pages
),
381 if (EFI_ERROR (Status
)) {
385 Status
= mCpu
->FlushDataCache (mCpu
,
386 (PHYSICAL_ADDRESS
)(UINTN
)Allocation
,
387 EFI_PAGES_TO_SIZE (Pages
),
388 EfiCpuFlushTypeInvalidate
);
389 if (EFI_ERROR (Status
)) {
393 *HostAddress
= Allocation
;
398 RemoveEntryList (&Alloc
->Link
);
402 FreePages (Allocation
, Pages
);
408 Frees memory that was allocated with DmaAllocateBuffer().
410 @param Pages The number of pages to free.
411 @param HostAddress The base system memory address of the allocated
414 @retval EFI_SUCCESS The requested memory pages were freed.
415 @retval EFI_INVALID_PARAMETER The memory range specified by HostAddress and
416 Pages was not allocated with
428 UNCACHED_ALLOCATION
*Alloc
;
432 if (HostAddress
== NULL
) {
433 return EFI_INVALID_PARAMETER
;
436 for (Link
= GetFirstNode (&UncachedAllocationList
), Found
= FALSE
;
437 !IsNull (&UncachedAllocationList
, Link
);
438 Link
= GetNextNode (&UncachedAllocationList
, Link
)) {
440 Alloc
= BASE_CR (Link
, UNCACHED_ALLOCATION
, Link
);
441 if (Alloc
->HostAddress
== HostAddress
&& Alloc
->NumPages
== Pages
) {
449 return EFI_INVALID_PARAMETER
;
452 RemoveEntryList (&Alloc
->Link
);
454 Status
= gDS
->SetMemorySpaceAttributes ((PHYSICAL_ADDRESS
)(UINTN
)HostAddress
,
455 EFI_PAGES_TO_SIZE (Pages
),
457 if (EFI_ERROR (Status
)) {
462 // If we fail to restore the original attributes, it is better to leak the
463 // memory than to return it to the heap
465 FreePages (HostAddress
, Pages
);
475 NonCoherentDmaLibConstructor (
476 IN EFI_HANDLE ImageHandle
,
477 IN EFI_SYSTEM_TABLE
*SystemTable
480 InitializeListHead (&UncachedAllocationList
);
482 // Get the Cpu protocol for later use
483 return gBS
->LocateProtocol (&gEfiCpuArchProtocolGuid
, NULL
, (VOID
**)&mCpu
);