TianoCore EDK2 master
Loading...
Searching...
No Matches
NonCoherentDmaLib.c
Go to the documentation of this file.
1
12#include <PiDxe.h>
13#include <Library/BaseLib.h>
14#include <Library/DebugLib.h>
15#include <Library/DmaLib.h>
19#include <Library/IoLib.h>
21
22#include <Protocol/Cpu.h>
23
24typedef struct {
25 EFI_PHYSICAL_ADDRESS HostAddress;
26 VOID *BufferAddress;
27 UINTN NumberOfBytes;
28 DMA_MAP_OPERATION Operation;
29 BOOLEAN DoubleBuffer;
31
32typedef struct {
33 LIST_ENTRY Link;
34 VOID *HostAddress;
35 UINTN NumPages;
36 UINT64 Attributes;
38
40STATIC LIST_ENTRY UncachedAllocationList;
41
42STATIC PHYSICAL_ADDRESS mDmaHostAddressLimit;
43
45PHYSICAL_ADDRESS
46HostToDeviceAddress (
47 IN VOID *Address
48 )
49{
50 return (PHYSICAL_ADDRESS)(UINTN)Address + PcdGet64 (PcdDmaDeviceOffset);
51}
52
75VOID *
77 IN EFI_MEMORY_TYPE MemoryType,
78 IN UINTN Pages,
79 IN UINTN Alignment
80 )
81{
82 EFI_STATUS Status;
84 UINTN AlignedMemory;
85 UINTN AlignmentMask;
86 UINTN UnalignedPages;
87 UINTN RealPages;
88
89 //
90 // Alignment must be a power of two or zero.
91 //
92 ASSERT ((Alignment & (Alignment - 1)) == 0);
93
94 if (Pages == 0) {
95 return NULL;
96 }
97
98 if (Alignment > EFI_PAGE_SIZE) {
99 //
100 // Calculate the total number of pages since alignment is larger than page
101 // size.
102 //
103 AlignmentMask = Alignment - 1;
104 RealPages = Pages + EFI_SIZE_TO_PAGES (Alignment);
105 //
106 // Make sure that Pages plus EFI_SIZE_TO_PAGES (Alignment) does not
107 // overflow.
108 //
109 ASSERT (RealPages > Pages);
110
111 Memory = mDmaHostAddressLimit;
112 Status = gBS->AllocatePages (
114 MemoryType,
115 RealPages,
116 &Memory
117 );
118 if (EFI_ERROR (Status)) {
119 return NULL;
120 }
121
122 AlignedMemory = ((UINTN)Memory + AlignmentMask) & ~AlignmentMask;
123 UnalignedPages = EFI_SIZE_TO_PAGES (AlignedMemory - (UINTN)Memory);
124 if (UnalignedPages > 0) {
125 //
126 // Free first unaligned page(s).
127 //
128 Status = gBS->FreePages (Memory, UnalignedPages);
129 ASSERT_EFI_ERROR (Status);
130 }
131
132 Memory = AlignedMemory + EFI_PAGES_TO_SIZE (Pages);
133 UnalignedPages = RealPages - Pages - UnalignedPages;
134 if (UnalignedPages > 0) {
135 //
136 // Free last unaligned page(s).
137 //
138 Status = gBS->FreePages (Memory, UnalignedPages);
139 ASSERT_EFI_ERROR (Status);
140 }
141 } else {
142 //
143 // Do not over-allocate pages in this case.
144 //
145 Memory = mDmaHostAddressLimit;
146 Status = gBS->AllocatePages (
148 MemoryType,
149 Pages,
150 &Memory
151 );
152 if (EFI_ERROR (Status)) {
153 return NULL;
154 }
155
156 AlignedMemory = (UINTN)Memory;
157 }
158
159 return (VOID *)AlignedMemory;
160}
161
190EFIAPI
192 IN DMA_MAP_OPERATION Operation,
193 IN VOID *HostAddress,
194 IN OUT UINTN *NumberOfBytes,
195 OUT PHYSICAL_ADDRESS *DeviceAddress,
196 OUT VOID **Mapping
197 )
198{
199 EFI_STATUS Status;
201 VOID *Buffer;
203 UINTN AllocSize;
204
205 if ((HostAddress == NULL) ||
206 (NumberOfBytes == NULL) ||
207 (DeviceAddress == NULL) ||
208 (Mapping == NULL))
209 {
210 return EFI_INVALID_PARAMETER;
211 }
212
213 if (Operation >= MapOperationMaximum) {
214 return EFI_INVALID_PARAMETER;
215 }
216
217 *DeviceAddress = HostToDeviceAddress (HostAddress);
218
219 // Remember range so we can flush on the other side
220 Map = AllocatePool (sizeof (MAP_INFO_INSTANCE));
221 if (Map == NULL) {
222 return EFI_OUT_OF_RESOURCES;
223 }
224
225 if (((UINTN)HostAddress + *NumberOfBytes) > mDmaHostAddressLimit) {
226 if (Operation == MapOperationBusMasterCommonBuffer) {
227 goto CommonBufferError;
228 }
229
230 AllocSize = ALIGN_VALUE (*NumberOfBytes, mCpu->DmaBufferAlignment);
231 Map->BufferAddress = InternalAllocateAlignedPages (
233 EFI_SIZE_TO_PAGES (AllocSize),
235 );
236 if (Map->BufferAddress == NULL) {
237 Status = EFI_OUT_OF_RESOURCES;
238 goto FreeMapInfo;
239 }
240
241 if (Operation == MapOperationBusMasterRead) {
242 CopyMem (Map->BufferAddress, (VOID *)(UINTN)HostAddress, *NumberOfBytes);
243 }
244
245 mCpu->FlushDataCache (
246 mCpu,
247 (UINTN)Map->BufferAddress,
248 AllocSize,
249 EfiCpuFlushTypeWriteBack
250 );
251
252 *DeviceAddress = HostToDeviceAddress (Map->BufferAddress);
253 } else if ((Operation != MapOperationBusMasterRead) &&
254 ((((UINTN)HostAddress & (mCpu->DmaBufferAlignment - 1)) != 0) ||
255 ((*NumberOfBytes & (mCpu->DmaBufferAlignment - 1)) != 0)))
256 {
257 // Get the cacheability of the region
258 Status = gDS->GetMemorySpaceDescriptor ((UINTN)HostAddress, &GcdDescriptor);
259 if (EFI_ERROR (Status)) {
260 goto FreeMapInfo;
261 }
262
263 // If the mapped buffer is not an uncached buffer
264 if ((GcdDescriptor.Attributes & (EFI_MEMORY_WB | EFI_MEMORY_WT)) != 0) {
265 //
266 // Operations of type MapOperationBusMasterCommonBuffer are only allowed
267 // on uncached buffers.
268 //
269 if (Operation == MapOperationBusMasterCommonBuffer) {
270 goto CommonBufferError;
271 }
272
273 //
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.
279 //
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;
286 goto FreeMapInfo;
287 }
288
289 Buffer = ALIGN_POINTER (Map->BufferAddress, mCpu->DmaBufferAlignment);
290 *DeviceAddress = HostToDeviceAddress (Buffer);
291
292 //
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.
296 //
297 mCpu->FlushDataCache (
298 mCpu,
299 (UINTN)Buffer,
300 *NumberOfBytes,
301 EfiCpuFlushTypeWriteBack
302 );
303 } else {
304 Map->DoubleBuffer = FALSE;
305 }
306 } else {
307 Map->DoubleBuffer = FALSE;
308
310
311 //
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.
317 //
318 Status = gDS->GetMemorySpaceDescriptor ((UINTN)HostAddress, &GcdDescriptor);
319 ASSERT_EFI_ERROR (Status);
320
321 ASSERT (
323 (GcdDescriptor.Attributes & (EFI_MEMORY_WB | EFI_MEMORY_WT)) == 0
324 );
325
327
328 // Flush the Data Cache (should not have any effect if the memory region is
329 // uncached)
330 mCpu->FlushDataCache (
331 mCpu,
332 (UINTN)HostAddress,
333 *NumberOfBytes,
334 EfiCpuFlushTypeWriteBackInvalidate
335 );
336 }
337
338 Map->HostAddress = (UINTN)HostAddress;
339 Map->NumberOfBytes = *NumberOfBytes;
340 Map->Operation = Operation;
341
342 *Mapping = Map;
343
344 return EFI_SUCCESS;
345
346CommonBufferError:
347 DEBUG ((
348 DEBUG_ERROR,
349 "%a: Operation type 'MapOperationBusMasterCommonBuffer' is only "
350 "supported\non memory regions that were allocated using "
351 "DmaAllocateBuffer ()\n",
352 __func__
353 ));
354 Status = EFI_UNSUPPORTED;
355FreeMapInfo:
356 FreePool (Map);
357
358 return Status;
359}
360
376EFIAPI
378 IN VOID *Mapping
379 )
380{
382 EFI_STATUS Status;
383 VOID *Buffer;
384 UINTN AllocSize;
385
386 if (Mapping == NULL) {
387 ASSERT (FALSE);
388 return EFI_INVALID_PARAMETER;
389 }
390
391 Map = (MAP_INFO_INSTANCE *)Mapping;
392
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 (
398 mCpu,
399 (UINTN)Map->BufferAddress,
400 AllocSize,
401 EfiCpuFlushTypeInvalidate
402 );
403 CopyMem (
404 (VOID *)(UINTN)Map->HostAddress,
405 Map->BufferAddress,
406 Map->NumberOfBytes
407 );
408 }
409
410 FreePages (Map->BufferAddress, EFI_SIZE_TO_PAGES (AllocSize));
411 } else if (Map->DoubleBuffer) {
412 ASSERT (Map->Operation == MapOperationBusMasterWrite);
413
414 if (Map->Operation != MapOperationBusMasterWrite) {
415 Status = EFI_INVALID_PARAMETER;
416 } else {
417 Buffer = ALIGN_POINTER (Map->BufferAddress, mCpu->DmaBufferAlignment);
418
419 mCpu->FlushDataCache (
420 mCpu,
421 (UINTN)Buffer,
422 Map->NumberOfBytes,
423 EfiCpuFlushTypeInvalidate
424 );
425
426 CopyMem ((VOID *)(UINTN)Map->HostAddress, Buffer, Map->NumberOfBytes);
427
428 FreePool (Map->BufferAddress);
429 }
430 } else {
431 if (Map->Operation == MapOperationBusMasterWrite) {
432 //
433 // Make sure we read buffer from uncached memory and not the cache
434 //
435 mCpu->FlushDataCache (
436 mCpu,
437 Map->HostAddress,
438 Map->NumberOfBytes,
439 EfiCpuFlushTypeInvalidate
440 );
441 }
442 }
443
444 FreePool (Map);
445
446 return Status;
447}
448
465EFIAPI
467 IN EFI_MEMORY_TYPE MemoryType,
468 IN UINTN Pages,
469 OUT VOID **HostAddress
470 )
471{
472 return DmaAllocateAlignedBuffer (MemoryType, Pages, 0, HostAddress);
473}
474
493EFIAPI
495 IN EFI_MEMORY_TYPE MemoryType,
496 IN UINTN Pages,
497 IN UINTN Alignment,
498 OUT VOID **HostAddress
499 )
500{
502 VOID *Allocation;
503 UINT64 Attributes;
504 UNCACHED_ALLOCATION *Alloc;
505 EFI_STATUS Status;
506
507 Attributes = EFI_MEMORY_XP;
508
509 if (Alignment == 0) {
510 Alignment = EFI_PAGE_SIZE;
511 }
512
513 if ((HostAddress == NULL) ||
514 ((Alignment & (Alignment - 1)) != 0))
515 {
516 return EFI_INVALID_PARAMETER;
517 }
518
519 if ((MemoryType == EfiBootServicesData) ||
520 (MemoryType == EfiRuntimeServicesData))
521 {
522 Allocation = InternalAllocateAlignedPages (MemoryType, Pages, Alignment);
523 } else {
524 return EFI_INVALID_PARAMETER;
525 }
526
527 if (Allocation == NULL) {
528 return EFI_OUT_OF_RESOURCES;
529 }
530
531 // Get the cacheability of the region
532 Status = gDS->GetMemorySpaceDescriptor ((UINTN)Allocation, &GcdDescriptor);
533 if (EFI_ERROR (Status)) {
534 goto FreeBuffer;
535 }
536
537 // Choose a suitable uncached memory type that is supported by the region
538 if (GcdDescriptor.Capabilities & EFI_MEMORY_WC) {
539 Attributes |= EFI_MEMORY_WC;
540 } else if (GcdDescriptor.Capabilities & EFI_MEMORY_UC) {
541 Attributes |= EFI_MEMORY_UC;
542 } else {
543 Status = EFI_UNSUPPORTED;
544 goto FreeBuffer;
545 }
546
547 Alloc = AllocatePool (sizeof *Alloc);
548 if (Alloc == NULL) {
549 goto FreeBuffer;
550 }
551
552 Alloc->HostAddress = Allocation;
553 Alloc->NumPages = Pages;
554 Alloc->Attributes = GcdDescriptor.Attributes;
555
556 InsertHeadList (&UncachedAllocationList, &Alloc->Link);
557
558 // Ensure that EFI_MEMORY_XP is in the capability set
559 if ((GcdDescriptor.Capabilities & EFI_MEMORY_XP) != EFI_MEMORY_XP) {
560 Status = gDS->SetMemorySpaceCapabilities (
561 (PHYSICAL_ADDRESS)(UINTN)Allocation,
562 EFI_PAGES_TO_SIZE (Pages),
563 GcdDescriptor.Capabilities | EFI_MEMORY_XP
564 );
565
566 // if we were to fail setting the capability, this would indicate an internal failure of the GCD code. We should
567 // assert here to let a platform know something went crazy, but for a release build we can let the allocation occur
568 // without the EFI_MEMORY_XP bit set, as that was the existing behavior
569 if (EFI_ERROR (Status)) {
570 DEBUG ((
571 DEBUG_ERROR,
572 "%a failed to set EFI_MEMORY_XP capability on 0x%llx for length 0x%llx. Attempting to allocate without XP set.\n",
573 __func__,
574 Allocation,
575 EFI_PAGES_TO_SIZE (Pages)
576 ));
577
578 ASSERT_EFI_ERROR (Status);
579
580 Attributes &= ~EFI_MEMORY_XP;
581 }
582 }
583
584 // Remap the region with the new attributes and mark it non-executable
585 Status = gDS->SetMemorySpaceAttributes (
586 (PHYSICAL_ADDRESS)(UINTN)Allocation,
587 EFI_PAGES_TO_SIZE (Pages),
588 Attributes
589 );
590 if (EFI_ERROR (Status)) {
591 goto FreeAlloc;
592 }
593
594 Status = mCpu->FlushDataCache (
595 mCpu,
596 (PHYSICAL_ADDRESS)(UINTN)Allocation,
597 EFI_PAGES_TO_SIZE (Pages),
598 EfiCpuFlushTypeInvalidate
599 );
600 if (EFI_ERROR (Status)) {
601 goto FreeAlloc;
602 }
603
604 *HostAddress = Allocation;
605
606 return EFI_SUCCESS;
607
608FreeAlloc:
609 RemoveEntryList (&Alloc->Link);
610 FreePool (Alloc);
611
612FreeBuffer:
613 FreePages (Allocation, Pages);
614 return Status;
615}
616
631EFIAPI
633 IN UINTN Pages,
634 IN VOID *HostAddress
635 )
636{
637 LIST_ENTRY *Link;
638 UNCACHED_ALLOCATION *Alloc;
639 BOOLEAN Found;
640 EFI_STATUS Status;
641
642 if (HostAddress == NULL) {
643 return EFI_INVALID_PARAMETER;
644 }
645
646 for (Link = GetFirstNode (&UncachedAllocationList), Found = FALSE;
647 !IsNull (&UncachedAllocationList, Link);
648 Link = GetNextNode (&UncachedAllocationList, Link))
649 {
650 Alloc = BASE_CR (Link, UNCACHED_ALLOCATION, Link);
651 if ((Alloc->HostAddress == HostAddress) && (Alloc->NumPages == Pages)) {
652 Found = TRUE;
653 break;
654 }
655 }
656
657 if (!Found) {
658 ASSERT (FALSE);
659 return EFI_INVALID_PARAMETER;
660 }
661
662 RemoveEntryList (&Alloc->Link);
663
664 Status = gDS->SetMemorySpaceAttributes (
665 (PHYSICAL_ADDRESS)(UINTN)HostAddress,
666 EFI_PAGES_TO_SIZE (Pages),
667 Alloc->Attributes
668 );
669 if (EFI_ERROR (Status)) {
670 goto FreeAlloc;
671 }
672
673 //
674 // If we fail to restore the original attributes, it is better to leak the
675 // memory than to return it to the heap
676 //
677 FreePages (HostAddress, Pages);
678
679FreeAlloc:
680 FreePool (Alloc);
681 return Status;
682}
683
685EFIAPI
686NonCoherentDmaLibConstructor (
687 IN EFI_HANDLE ImageHandle,
688 IN EFI_SYSTEM_TABLE *SystemTable
689 )
690{
691 InitializeListHead (&UncachedAllocationList);
692
693 //
694 // Ensure that the combination of DMA addressing offset and limit produces
695 // a sane value.
696 //
697 ASSERT (PcdGet64 (PcdDmaDeviceLimit) > PcdGet64 (PcdDmaDeviceOffset));
698
699 mDmaHostAddressLimit = PcdGet64 (PcdDmaDeviceLimit) -
700 PcdGet64 (PcdDmaDeviceOffset);
701
702 // Get the Cpu protocol for later use
703 return gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **)&mCpu);
704}
UINT64 UINTN
BOOLEAN EFIAPI IsNull(IN CONST LIST_ENTRY *List, IN CONST LIST_ENTRY *Node)
Definition: LinkedList.c:443
LIST_ENTRY *EFIAPI GetNextNode(IN CONST LIST_ENTRY *List, IN CONST LIST_ENTRY *Node)
Definition: LinkedList.c:333
LIST_ENTRY *EFIAPI InsertHeadList(IN OUT LIST_ENTRY *ListHead, IN OUT LIST_ENTRY *Entry)
Definition: LinkedList.c:218
LIST_ENTRY *EFIAPI GetFirstNode(IN CONST LIST_ENTRY *List)
Definition: LinkedList.c:298
LIST_ENTRY *EFIAPI RemoveEntryList(IN CONST LIST_ENTRY *Entry)
Definition: LinkedList.c:590
LIST_ENTRY *EFIAPI InitializeListHead(IN OUT LIST_ENTRY *ListHead)
Definition: LinkedList.c:182
VOID *EFIAPI CopyMem(OUT VOID *DestinationBuffer, IN CONST VOID *SourceBuffer, IN UINTN Length)
DMA_MAP_OPERATION
Definition: DmaLib.h:37
@ MapOperationBusMasterWrite
Definition: DmaLib.h:45
@ MapOperationBusMasterRead
Definition: DmaLib.h:41
@ MapOperationBusMasterCommonBuffer
Definition: DmaLib.h:50
EFI_DXE_SERVICES * gDS
VOID EFIAPI FreePages(IN VOID *Buffer, IN UINTN Pages)
VOID EFIAPI FreePool(IN VOID *Buffer)
#define NULL
Definition: Base.h:319
#define STATIC
Definition: Base.h:264
#define ALIGN_POINTER(Pointer, Alignment)
Definition: Base.h:963
#define ALIGN_VALUE(Value, Alignment)
Definition: Base.h:948
#define TRUE
Definition: Base.h:301
#define FALSE
Definition: Base.h:307
#define IN
Definition: Base.h:279
#define OUT
Definition: Base.h:284
#define BASE_CR(Record, TYPE, Field)
Definition: Base.h:891
#define ASSERT_EFI_ERROR(StatusParameter)
Definition: DebugLib.h:462
#define DEBUG_CODE_BEGIN()
Definition: DebugLib.h:564
#define DEBUG(Expression)
Definition: DebugLib.h:434
#define DEBUG_CODE_END()
Definition: DebugLib.h:578
EFI_STATUS EFIAPI DmaAllocateAlignedBuffer(IN EFI_MEMORY_TYPE MemoryType, IN UINTN Pages, IN UINTN Alignment, OUT VOID **HostAddress)
EFI_STATUS EFIAPI DmaUnmap(IN VOID *Mapping)
EFI_STATUS EFIAPI DmaMap(IN DMA_MAP_OPERATION Operation, IN VOID *HostAddress, IN OUT UINTN *NumberOfBytes, OUT PHYSICAL_ADDRESS *DeviceAddress, OUT VOID **Mapping)
EFI_STATUS EFIAPI DmaAllocateBuffer(IN EFI_MEMORY_TYPE MemoryType, IN UINTN Pages, OUT VOID **HostAddress)
STATIC VOID * InternalAllocateAlignedPages(IN EFI_MEMORY_TYPE MemoryType, IN UINTN Pages, IN UINTN Alignment)
EFI_STATUS EFIAPI DmaFreeBuffer(IN UINTN Pages, IN VOID *HostAddress)
#define PcdGet64(TokenName)
Definition: PcdLib.h:375
VOID *EFIAPI AllocatePool(IN UINTN AllocationSize)
UINT64 EFI_PHYSICAL_ADDRESS
Definition: UefiBaseType.h:50
#define EFI_PAGES_TO_SIZE(Pages)
Definition: UefiBaseType.h:213
RETURN_STATUS EFI_STATUS
Definition: UefiBaseType.h:29
#define EFI_SIZE_TO_PAGES(Size)
Definition: UefiBaseType.h:200
VOID * EFI_HANDLE
Definition: UefiBaseType.h:33
#define EFI_SUCCESS
Definition: UefiBaseType.h:112
EFI_BOOT_SERVICES * gBS
EFI_MEMORY_TYPE
@ EfiBootServicesData
@ EfiRuntimeServicesData
@ AllocateMaxAddress
Definition: UefiSpec.h:38
UINT32 DmaBufferAlignment
Definition: Cpu.h:281