]> git.proxmox.com Git - mirror_edk2.git/blob - EmbeddedPkg/Library/NonCoherentDmaLib/NonCoherentDmaLib.c
EmbeddedPkg: Apply uncrustify changes
[mirror_edk2.git] / EmbeddedPkg / Library / NonCoherentDmaLib / NonCoherentDmaLib.c
1 /** @file
2
3 Generic non-coherent implementation of DmaLib.h
4
5 Copyright (c) 2008 - 2010, Apple Inc. All rights reserved.<BR>
6 Copyright (c) 2015 - 2017, Linaro, Ltd. All rights reserved.<BR>
7
8 SPDX-License-Identifier: BSD-2-Clause-Patent
9
10 **/
11
12 #include <PiDxe.h>
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>
21
22 #include <Protocol/Cpu.h>
23
24 typedef struct {
25 EFI_PHYSICAL_ADDRESS HostAddress;
26 VOID *BufferAddress;
27 UINTN NumberOfBytes;
28 DMA_MAP_OPERATION Operation;
29 BOOLEAN DoubleBuffer;
30 } MAP_INFO_INSTANCE;
31
32 typedef struct {
33 LIST_ENTRY Link;
34 VOID *HostAddress;
35 UINTN NumPages;
36 UINT64 Attributes;
37 } UNCACHED_ALLOCATION;
38
39 STATIC EFI_CPU_ARCH_PROTOCOL *mCpu;
40 STATIC LIST_ENTRY UncachedAllocationList;
41
42 STATIC PHYSICAL_ADDRESS mDmaHostAddressLimit;
43
44 STATIC
45 PHYSICAL_ADDRESS
46 HostToDeviceAddress (
47 IN VOID *Address
48 )
49 {
50 return (PHYSICAL_ADDRESS)(UINTN)Address + PcdGet64 (PcdDmaDeviceOffset);
51 }
52
53 /**
54 Allocates one or more 4KB pages of a certain memory type at a specified
55 alignment.
56
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().
63
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
69 used.
70
71 @return A pointer to the allocated buffer or NULL if allocation fails.
72
73 **/
74 STATIC
75 VOID *
76 InternalAllocateAlignedPages (
77 IN EFI_MEMORY_TYPE MemoryType,
78 IN UINTN Pages,
79 IN UINTN Alignment
80 )
81 {
82 EFI_STATUS Status;
83 EFI_PHYSICAL_ADDRESS Memory;
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 (
113 AllocateMaxAddress,
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 (
147 AllocateMaxAddress,
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
162 /**
163 Provides the DMA controller-specific addresses needed to access system memory.
164
165 Operation is relative to the DMA bus master.
166
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
170 controller.
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
175 HostAddress.
176 @param Mapping A resulting value to pass to Unmap().
177
178 @retval EFI_SUCCESS The range was mapped for the returned
179 NumberOfBytes.
180 @retval EFI_UNSUPPORTED The HostAddress cannot be mapped as a common
181 buffer.
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
184 of resources.
185 @retval EFI_DEVICE_ERROR The system hardware could not map the requested
186 address.
187
188 **/
189 EFI_STATUS
190 EFIAPI
191 DmaMap (
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;
200 MAP_INFO_INSTANCE *Map;
201 VOID *Buffer;
202 EFI_GCD_MEMORY_SPACE_DESCRIPTOR GcdDescriptor;
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 (
232 EfiBootServicesData,
233 EFI_SIZE_TO_PAGES (AllocSize),
234 mCpu->DmaBufferAlignment
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
309 DEBUG_CODE_BEGIN ();
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 (
322 Operation != MapOperationBusMasterCommonBuffer ||
323 (GcdDescriptor.Attributes & (EFI_MEMORY_WB | EFI_MEMORY_WT)) == 0
324 );
325
326 DEBUG_CODE_END ();
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
346 CommonBufferError:
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 __FUNCTION__
353 ));
354 Status = EFI_UNSUPPORTED;
355 FreeMapInfo:
356 FreePool (Map);
357
358 return Status;
359 }
360
361 /**
362 Completes the DmaMapBusMasterRead(), DmaMapBusMasterWrite(), or
363 DmaMapBusMasterCommonBuffer() operation and releases any corresponding
364 resources.
365
366 @param Mapping The mapping value returned from DmaMap*().
367
368 @retval EFI_SUCCESS The range was unmapped.
369 @retval EFI_DEVICE_ERROR The data was not committed to the target system
370 memory.
371 @retval EFI_INVALID_PARAMETER An inconsistency was detected between the
372 mapping type and the DoubleBuffer field
373
374 **/
375 EFI_STATUS
376 EFIAPI
377 DmaUnmap (
378 IN VOID *Mapping
379 )
380 {
381 MAP_INFO_INSTANCE *Map;
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
449 /**
450 Allocates pages that are suitable for an DmaMap() of type
451 MapOperationBusMasterCommonBuffer mapping.
452
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.
458
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.
462
463 **/
464 EFI_STATUS
465 EFIAPI
466 DmaAllocateBuffer (
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
475 /**
476 Allocates pages that are suitable for an DmaMap() of type
477 MapOperationBusMasterCommonBuffer mapping, at the requested alignment.
478
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.
486
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.
490
491 **/
492 EFI_STATUS
493 EFIAPI
494 DmaAllocateAlignedBuffer (
495 IN EFI_MEMORY_TYPE MemoryType,
496 IN UINTN Pages,
497 IN UINTN Alignment,
498 OUT VOID **HostAddress
499 )
500 {
501 EFI_GCD_MEMORY_SPACE_DESCRIPTOR GcdDescriptor;
502 VOID *Allocation;
503 UINT64 MemType;
504 UNCACHED_ALLOCATION *Alloc;
505 EFI_STATUS Status;
506
507 if (Alignment == 0) {
508 Alignment = EFI_PAGE_SIZE;
509 }
510
511 if ((HostAddress == NULL) ||
512 ((Alignment & (Alignment - 1)) != 0))
513 {
514 return EFI_INVALID_PARAMETER;
515 }
516
517 if ((MemoryType == EfiBootServicesData) ||
518 (MemoryType == EfiRuntimeServicesData))
519 {
520 Allocation = InternalAllocateAlignedPages (MemoryType, Pages, Alignment);
521 } else {
522 return EFI_INVALID_PARAMETER;
523 }
524
525 if (Allocation == NULL) {
526 return EFI_OUT_OF_RESOURCES;
527 }
528
529 // Get the cacheability of the region
530 Status = gDS->GetMemorySpaceDescriptor ((UINTN)Allocation, &GcdDescriptor);
531 if (EFI_ERROR (Status)) {
532 goto FreeBuffer;
533 }
534
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;
540 } else {
541 Status = EFI_UNSUPPORTED;
542 goto FreeBuffer;
543 }
544
545 Alloc = AllocatePool (sizeof *Alloc);
546 if (Alloc == NULL) {
547 goto FreeBuffer;
548 }
549
550 Alloc->HostAddress = Allocation;
551 Alloc->NumPages = Pages;
552 Alloc->Attributes = GcdDescriptor.Attributes;
553
554 InsertHeadList (&UncachedAllocationList, &Alloc->Link);
555
556 // Remap the region with the new attributes
557 Status = gDS->SetMemorySpaceAttributes (
558 (PHYSICAL_ADDRESS)(UINTN)Allocation,
559 EFI_PAGES_TO_SIZE (Pages),
560 MemType
561 );
562 if (EFI_ERROR (Status)) {
563 goto FreeAlloc;
564 }
565
566 Status = mCpu->FlushDataCache (
567 mCpu,
568 (PHYSICAL_ADDRESS)(UINTN)Allocation,
569 EFI_PAGES_TO_SIZE (Pages),
570 EfiCpuFlushTypeInvalidate
571 );
572 if (EFI_ERROR (Status)) {
573 goto FreeAlloc;
574 }
575
576 *HostAddress = Allocation;
577
578 return EFI_SUCCESS;
579
580 FreeAlloc:
581 RemoveEntryList (&Alloc->Link);
582 FreePool (Alloc);
583
584 FreeBuffer:
585 FreePages (Allocation, Pages);
586 return Status;
587 }
588
589 /**
590 Frees memory that was allocated with DmaAllocateBuffer().
591
592 @param Pages The number of pages to free.
593 @param HostAddress The base system memory address of the allocated
594 range.
595
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
599 DmaAllocateBuffer().
600
601 **/
602 EFI_STATUS
603 EFIAPI
604 DmaFreeBuffer (
605 IN UINTN Pages,
606 IN VOID *HostAddress
607 )
608 {
609 LIST_ENTRY *Link;
610 UNCACHED_ALLOCATION *Alloc;
611 BOOLEAN Found;
612 EFI_STATUS Status;
613
614 if (HostAddress == NULL) {
615 return EFI_INVALID_PARAMETER;
616 }
617
618 for (Link = GetFirstNode (&UncachedAllocationList), Found = FALSE;
619 !IsNull (&UncachedAllocationList, Link);
620 Link = GetNextNode (&UncachedAllocationList, Link))
621 {
622 Alloc = BASE_CR (Link, UNCACHED_ALLOCATION, Link);
623 if ((Alloc->HostAddress == HostAddress) && (Alloc->NumPages == Pages)) {
624 Found = TRUE;
625 break;
626 }
627 }
628
629 if (!Found) {
630 ASSERT (FALSE);
631 return EFI_INVALID_PARAMETER;
632 }
633
634 RemoveEntryList (&Alloc->Link);
635
636 Status = gDS->SetMemorySpaceAttributes (
637 (PHYSICAL_ADDRESS)(UINTN)HostAddress,
638 EFI_PAGES_TO_SIZE (Pages),
639 Alloc->Attributes
640 );
641 if (EFI_ERROR (Status)) {
642 goto FreeAlloc;
643 }
644
645 //
646 // If we fail to restore the original attributes, it is better to leak the
647 // memory than to return it to the heap
648 //
649 FreePages (HostAddress, Pages);
650
651 FreeAlloc:
652 FreePool (Alloc);
653 return Status;
654 }
655
656 EFI_STATUS
657 EFIAPI
658 NonCoherentDmaLibConstructor (
659 IN EFI_HANDLE ImageHandle,
660 IN EFI_SYSTEM_TABLE *SystemTable
661 )
662 {
663 InitializeListHead (&UncachedAllocationList);
664
665 //
666 // Ensure that the combination of DMA addressing offset and limit produces
667 // a sane value.
668 //
669 ASSERT (PcdGet64 (PcdDmaDeviceLimit) > PcdGet64 (PcdDmaDeviceOffset));
670
671 mDmaHostAddressLimit = PcdGet64 (PcdDmaDeviceLimit) -
672 PcdGet64 (PcdDmaDeviceOffset);
673
674 // Get the Cpu protocol for later use
675 return gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **)&mCpu);
676 }