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 This program and the accompanying materials are licensed and made
9 available under the terms and conditions of the BSD License which
10 accompanies this distribution. The full text of the license may be
11 found at http://opensource.org/licenses/bsd-license.php
13 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
14 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR
20 #include <Library/BaseLib.h>
21 #include <Library/DebugLib.h>
22 #include <Library/DmaLib.h>
23 #include <Library/DxeServicesTableLib.h>
24 #include <Library/MemoryAllocationLib.h>
25 #include <Library/UefiBootServicesTableLib.h>
26 #include <Library/IoLib.h>
27 #include <Library/BaseMemoryLib.h>
29 #include <Protocol/Cpu.h>
32 EFI_PHYSICAL_ADDRESS HostAddress
;
35 DMA_MAP_OPERATION Operation
;
45 } UNCACHED_ALLOCATION
;
47 STATIC EFI_CPU_ARCH_PROTOCOL
*mCpu
;
48 STATIC LIST_ENTRY UncachedAllocationList
;
56 return (PHYSICAL_ADDRESS
)(UINTN
)Address
+ PcdGet64 (PcdDmaDeviceOffset
);
60 Provides the DMA controller-specific addresses needed to access system memory.
62 Operation is relative to the DMA bus master.
64 @param Operation Indicates if the bus master is going to read or
65 write to system memory.
66 @param HostAddress The system memory address to map to the DMA
68 @param NumberOfBytes On input the number of bytes to map. On output
69 the number of bytes that were mapped.
70 @param DeviceAddress The resulting map address for the bus master
71 controller to use to access the host's
73 @param Mapping A resulting value to pass to Unmap().
75 @retval EFI_SUCCESS The range was mapped for the returned
77 @retval EFI_UNSUPPORTED The HostAddress cannot be mapped as a common
79 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
80 @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack
82 @retval EFI_DEVICE_ERROR The system hardware could not map the requested
89 IN DMA_MAP_OPERATION Operation
,
91 IN OUT UINTN
*NumberOfBytes
,
92 OUT PHYSICAL_ADDRESS
*DeviceAddress
,
97 MAP_INFO_INSTANCE
*Map
;
99 EFI_GCD_MEMORY_SPACE_DESCRIPTOR GcdDescriptor
;
102 if (HostAddress
== NULL
||
103 NumberOfBytes
== NULL
||
104 DeviceAddress
== NULL
||
106 return EFI_INVALID_PARAMETER
;
109 if (Operation
>= MapOperationMaximum
) {
110 return EFI_INVALID_PARAMETER
;
113 *DeviceAddress
= HostToDeviceAddress (HostAddress
);
115 // Remember range so we can flush on the other side
116 Map
= AllocatePool (sizeof (MAP_INFO_INSTANCE
));
118 return EFI_OUT_OF_RESOURCES
;
121 if (Operation
!= MapOperationBusMasterRead
&&
122 ((((UINTN
)HostAddress
& (mCpu
->DmaBufferAlignment
- 1)) != 0) ||
123 ((*NumberOfBytes
& (mCpu
->DmaBufferAlignment
- 1)) != 0))) {
125 // Get the cacheability of the region
126 Status
= gDS
->GetMemorySpaceDescriptor ((UINTN
)HostAddress
, &GcdDescriptor
);
127 if (EFI_ERROR(Status
)) {
131 // If the mapped buffer is not an uncached buffer
132 if ((GcdDescriptor
.Attributes
& (EFI_MEMORY_WB
| EFI_MEMORY_WT
)) != 0) {
134 // Operations of type MapOperationBusMasterCommonBuffer are only allowed
135 // on uncached buffers.
137 if (Operation
== MapOperationBusMasterCommonBuffer
) {
139 "%a: Operation type 'MapOperationBusMasterCommonBuffer' is only "
140 "supported\non memory regions that were allocated using "
141 "DmaAllocateBuffer ()\n", __FUNCTION__
));
142 Status
= EFI_UNSUPPORTED
;
147 // If the buffer does not fill entire cache lines we must double buffer
148 // into a suitably aligned allocation that allows us to invalidate the
149 // cache without running the risk of corrupting adjacent unrelated data.
150 // Note that pool allocations are guaranteed to be 8 byte aligned, so
151 // we only have to add (alignment - 8) worth of padding.
153 Map
->DoubleBuffer
= TRUE
;
154 AllocSize
= ALIGN_VALUE (*NumberOfBytes
, mCpu
->DmaBufferAlignment
) +
155 (mCpu
->DmaBufferAlignment
- 8);
156 Map
->BufferAddress
= AllocatePool (AllocSize
);
157 if (Map
->BufferAddress
== NULL
) {
158 Status
= EFI_OUT_OF_RESOURCES
;
162 Buffer
= ALIGN_POINTER (Map
->BufferAddress
, mCpu
->DmaBufferAlignment
);
163 *DeviceAddress
= HostToDeviceAddress (Buffer
);
166 // Get rid of any dirty cachelines covering the double buffer. This
167 // prevents them from being written back unexpectedly, potentially
168 // overwriting the data we receive from the device.
170 mCpu
->FlushDataCache (mCpu
, (UINTN
)Buffer
, *NumberOfBytes
,
171 EfiCpuFlushTypeWriteBack
);
173 Map
->DoubleBuffer
= FALSE
;
176 Map
->DoubleBuffer
= FALSE
;
181 // The operation type check above only executes if the buffer happens to be
182 // misaligned with respect to CWG, but even if it is aligned, we should not
183 // allow arbitrary buffers to be used for creating consistent mappings.
184 // So duplicate the check here when running in DEBUG mode, just to assert
185 // that we are not trying to create a consistent mapping for cached memory.
187 Status
= gDS
->GetMemorySpaceDescriptor ((UINTN
)HostAddress
, &GcdDescriptor
);
188 ASSERT_EFI_ERROR(Status
);
190 ASSERT (Operation
!= MapOperationBusMasterCommonBuffer
||
191 (GcdDescriptor
.Attributes
& (EFI_MEMORY_WB
| EFI_MEMORY_WT
)) == 0);
195 // Flush the Data Cache (should not have any effect if the memory region is
197 mCpu
->FlushDataCache (mCpu
, (UINTN
)HostAddress
, *NumberOfBytes
,
198 EfiCpuFlushTypeWriteBackInvalidate
);
201 Map
->HostAddress
= (UINTN
)HostAddress
;
202 Map
->NumberOfBytes
= *NumberOfBytes
;
203 Map
->Operation
= Operation
;
217 Completes the DmaMapBusMasterRead(), DmaMapBusMasterWrite(), or
218 DmaMapBusMasterCommonBuffer() operation and releases any corresponding
221 @param Mapping The mapping value returned from DmaMap*().
223 @retval EFI_SUCCESS The range was unmapped.
224 @retval EFI_DEVICE_ERROR The data was not committed to the target system
226 @retval EFI_INVALID_PARAMETER An inconsistency was detected between the
227 mapping type and the DoubleBuffer field
236 MAP_INFO_INSTANCE
*Map
;
240 if (Mapping
== NULL
) {
242 return EFI_INVALID_PARAMETER
;
245 Map
= (MAP_INFO_INSTANCE
*)Mapping
;
247 Status
= EFI_SUCCESS
;
248 if (Map
->DoubleBuffer
) {
249 ASSERT (Map
->Operation
== MapOperationBusMasterWrite
);
251 if (Map
->Operation
!= MapOperationBusMasterWrite
) {
252 Status
= EFI_INVALID_PARAMETER
;
254 Buffer
= ALIGN_POINTER (Map
->BufferAddress
, mCpu
->DmaBufferAlignment
);
256 mCpu
->FlushDataCache (mCpu
, (UINTN
)Buffer
, Map
->NumberOfBytes
,
257 EfiCpuFlushTypeInvalidate
);
259 CopyMem ((VOID
*)(UINTN
)Map
->HostAddress
, Buffer
, Map
->NumberOfBytes
);
261 FreePool (Map
->BufferAddress
);
264 if (Map
->Operation
== MapOperationBusMasterWrite
) {
266 // Make sure we read buffer from uncached memory and not the cache
268 mCpu
->FlushDataCache (mCpu
, Map
->HostAddress
, Map
->NumberOfBytes
,
269 EfiCpuFlushTypeInvalidate
);
279 Allocates pages that are suitable for an DmaMap() of type
280 MapOperationBusMasterCommonBuffer mapping.
282 @param MemoryType The type of memory to allocate,
283 EfiBootServicesData or EfiRuntimeServicesData.
284 @param Pages The number of pages to allocate.
285 @param HostAddress A pointer to store the base system memory
286 address of the allocated range.
288 @retval EFI_SUCCESS The requested memory pages were allocated.
289 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
290 @retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated.
296 IN EFI_MEMORY_TYPE MemoryType
,
298 OUT VOID
**HostAddress
301 return DmaAllocateAlignedBuffer (MemoryType
, Pages
, 0, HostAddress
);
305 Allocates pages that are suitable for an DmaMap() of type
306 MapOperationBusMasterCommonBuffer mapping, at the requested alignment.
308 @param MemoryType The type of memory to allocate,
309 EfiBootServicesData or EfiRuntimeServicesData.
310 @param Pages The number of pages to allocate.
311 @param Alignment Alignment in bytes of the base of the returned
312 buffer (must be a power of 2)
313 @param HostAddress A pointer to store the base system memory
314 address of the allocated range.
316 @retval EFI_SUCCESS The requested memory pages were allocated.
317 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
318 @retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated.
323 DmaAllocateAlignedBuffer (
324 IN EFI_MEMORY_TYPE MemoryType
,
327 OUT VOID
**HostAddress
330 EFI_GCD_MEMORY_SPACE_DESCRIPTOR GcdDescriptor
;
333 UNCACHED_ALLOCATION
*Alloc
;
336 if (Alignment
== 0) {
337 Alignment
= EFI_PAGE_SIZE
;
340 if (HostAddress
== NULL
||
341 (Alignment
& (Alignment
- 1)) != 0) {
342 return EFI_INVALID_PARAMETER
;
345 if (MemoryType
== EfiBootServicesData
) {
346 Allocation
= AllocateAlignedPages (Pages
, Alignment
);
347 } else if (MemoryType
== EfiRuntimeServicesData
) {
348 Allocation
= AllocateAlignedRuntimePages (Pages
, Alignment
);
350 return EFI_INVALID_PARAMETER
;
353 if (Allocation
== NULL
) {
354 return EFI_OUT_OF_RESOURCES
;
357 // Get the cacheability of the region
358 Status
= gDS
->GetMemorySpaceDescriptor ((UINTN
)Allocation
, &GcdDescriptor
);
359 if (EFI_ERROR(Status
)) {
363 // Choose a suitable uncached memory type that is supported by the region
364 if (GcdDescriptor
.Capabilities
& EFI_MEMORY_WC
) {
365 MemType
= EFI_MEMORY_WC
;
366 } else if (GcdDescriptor
.Capabilities
& EFI_MEMORY_UC
) {
367 MemType
= EFI_MEMORY_UC
;
369 Status
= EFI_UNSUPPORTED
;
373 Alloc
= AllocatePool (sizeof *Alloc
);
378 Alloc
->HostAddress
= Allocation
;
379 Alloc
->NumPages
= Pages
;
380 Alloc
->Attributes
= GcdDescriptor
.Attributes
;
382 InsertHeadList (&UncachedAllocationList
, &Alloc
->Link
);
384 // Remap the region with the new attributes
385 Status
= gDS
->SetMemorySpaceAttributes ((PHYSICAL_ADDRESS
)(UINTN
)Allocation
,
386 EFI_PAGES_TO_SIZE (Pages
),
388 if (EFI_ERROR (Status
)) {
392 Status
= mCpu
->FlushDataCache (mCpu
,
393 (PHYSICAL_ADDRESS
)(UINTN
)Allocation
,
394 EFI_PAGES_TO_SIZE (Pages
),
395 EfiCpuFlushTypeInvalidate
);
396 if (EFI_ERROR (Status
)) {
400 *HostAddress
= Allocation
;
405 RemoveEntryList (&Alloc
->Link
);
409 FreePages (Allocation
, Pages
);
415 Frees memory that was allocated with DmaAllocateBuffer().
417 @param Pages The number of pages to free.
418 @param HostAddress The base system memory address of the allocated
421 @retval EFI_SUCCESS The requested memory pages were freed.
422 @retval EFI_INVALID_PARAMETER The memory range specified by HostAddress and
423 Pages was not allocated with
435 UNCACHED_ALLOCATION
*Alloc
;
439 if (HostAddress
== NULL
) {
440 return EFI_INVALID_PARAMETER
;
443 for (Link
= GetFirstNode (&UncachedAllocationList
), Found
= FALSE
;
444 !IsNull (&UncachedAllocationList
, Link
);
445 Link
= GetNextNode (&UncachedAllocationList
, Link
)) {
447 Alloc
= BASE_CR (Link
, UNCACHED_ALLOCATION
, Link
);
448 if (Alloc
->HostAddress
== HostAddress
&& Alloc
->NumPages
== Pages
) {
456 return EFI_INVALID_PARAMETER
;
459 RemoveEntryList (&Alloc
->Link
);
461 Status
= gDS
->SetMemorySpaceAttributes ((PHYSICAL_ADDRESS
)(UINTN
)HostAddress
,
462 EFI_PAGES_TO_SIZE (Pages
),
464 if (EFI_ERROR (Status
)) {
469 // If we fail to restore the original attributes, it is better to leak the
470 // memory than to return it to the heap
472 FreePages (HostAddress
, Pages
);
482 NonCoherentDmaLibConstructor (
483 IN EFI_HANDLE ImageHandle
,
484 IN EFI_SYSTEM_TABLE
*SystemTable
487 InitializeListHead (&UncachedAllocationList
);
489 // Get the Cpu protocol for later use
490 return gBS
->LocateProtocol (&gEfiCpuArchProtocolGuid
, NULL
, (VOID
**)&mCpu
);