]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPkg/Library/UncachedMemoryAllocationLib/UncachedMemoryAllocationLib.c
ArmPkg: Invalidate cache after allocating UC memory
[mirror_edk2.git] / ArmPkg / Library / UncachedMemoryAllocationLib / UncachedMemoryAllocationLib.c
1 /** @file
2 UncachedMemoryAllocation lib that uses DXE Service to change cachability for
3 a buffer.
4
5 Copyright (c) 2008 - 2010, Apple Inc. All rights reserved.<BR>
6 Copyright (c) 2014, AMR Ltd. All rights reserved.<BR>
7
8 This program and the accompanying materials
9 are licensed and made available under the terms and conditions of the BSD License
10 which accompanies this distribution. The full text of the license may be found at
11 http://opensource.org/licenses/bsd-license.php
12
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 IMPLIED.
15
16 **/
17
18 #include <Base.h>
19 #include <Library/BaseLib.h>
20 #include <Library/BaseMemoryLib.h>
21 #include <Library/MemoryAllocationLib.h>
22 #include <Library/DebugLib.h>
23 #include <Library/UefiBootServicesTableLib.h>
24 #include <Library/UncachedMemoryAllocationLib.h>
25 #include <Library/PcdLib.h>
26 #include <Library/ArmLib.h>
27 #include <Library/DxeServicesTableLib.h>
28 #include <Library/CacheMaintenanceLib.h>
29
30 VOID *
31 UncachedInternalAllocatePages (
32 IN EFI_MEMORY_TYPE MemoryType,
33 IN UINTN Pages
34 );
35
36 VOID *
37 UncachedInternalAllocateAlignedPages (
38 IN EFI_MEMORY_TYPE MemoryType,
39 IN UINTN Pages,
40 IN UINTN Alignment
41 );
42
43
44
45 //
46 // Assume all of memory has the same cache attributes, unless we do our magic
47 //
48 UINT64 gAttributes;
49
50 typedef struct {
51 EFI_PHYSICAL_ADDRESS Base;
52 VOID *Allocation;
53 UINTN Pages;
54 EFI_MEMORY_TYPE MemoryType;
55 BOOLEAN Allocated;
56 LIST_ENTRY Link;
57 } FREE_PAGE_NODE;
58
59 STATIC LIST_ENTRY mPageList = INITIALIZE_LIST_HEAD_VARIABLE (mPageList);
60 // Track the size of the non-allocated buffer in the linked-list
61 STATIC UINTN mFreedBufferSize = 0;
62
63 /**
64 * This function firstly checks if the requested allocation can fit into one
65 * of the previously allocated buffer.
66 * If the requested allocation does not fit in the existing pool then
67 * the function makes a new allocation.
68 *
69 * @param MemoryType Type of memory requested for the new allocation
70 * @param Pages Number of requested page
71 * @param Alignment Required alignment
72 * @param Allocation Address of the newly allocated buffer
73 *
74 * @return EFI_SUCCESS If the function manage to allocate a buffer
75 * @return !EFI_SUCCESS If the function did not manage to allocate a buffer
76 */
77 STATIC
78 EFI_STATUS
79 AllocatePagesFromList (
80 IN EFI_MEMORY_TYPE MemoryType,
81 IN UINTN Pages,
82 IN UINTN Alignment,
83 OUT VOID **Allocation
84 )
85 {
86 EFI_STATUS Status;
87 LIST_ENTRY *Link;
88 FREE_PAGE_NODE *Node;
89 FREE_PAGE_NODE *NewNode;
90 UINTN AlignmentMask;
91 EFI_PHYSICAL_ADDRESS Memory;
92 EFI_GCD_MEMORY_SPACE_DESCRIPTOR Descriptor;
93
94 // Alignment must be a power of two or zero.
95 ASSERT ((Alignment & (Alignment - 1)) == 0);
96
97 //
98 // Look in our list for the smallest page that could satisfy the new allocation
99 //
100 NewNode = NULL;
101 for (Link = mPageList.ForwardLink; Link != &mPageList; Link = Link->ForwardLink) {
102 Node = BASE_CR (Link, FREE_PAGE_NODE, Link);
103 if ((Node->Allocated == FALSE) && (Node->MemoryType == MemoryType)) {
104 // We have a node that fits our requirements
105 if (((UINTN)Node->Base & (Alignment - 1)) == 0) {
106 // We found a page that matches the page size
107 if (Node->Pages == Pages) {
108 Node->Allocated = TRUE;
109 Node->Allocation = (VOID*)(UINTN)Node->Base;
110 *Allocation = Node->Allocation;
111
112 // Update the size of the freed buffer
113 mFreedBufferSize -= Pages * EFI_PAGE_SIZE;
114 return EFI_SUCCESS;
115 } else if (Node->Pages > Pages) {
116 if (NewNode == NULL) {
117 // It is the first node that could contain our new allocation
118 NewNode = Node;
119 } else if (NewNode->Pages > Node->Pages) {
120 // This node offers a smaller number of page.
121 NewNode = Node;
122 }
123 }
124 }
125 }
126 }
127 // Check if we have found a node that could contain our new allocation
128 if (NewNode != NULL) {
129 NewNode->Allocated = TRUE;
130 NewNode->Allocation = (VOID*)(UINTN)NewNode->Base;
131 *Allocation = NewNode->Allocation;
132 mFreedBufferSize -= NewNode->Pages * EFI_PAGE_SIZE;
133 return EFI_SUCCESS;
134 }
135
136 //
137 // Otherwise, we need to allocate a new buffer
138 //
139
140 // We do not want to over-allocate in case the alignment requirement does not
141 // require extra pages
142 if (Alignment > EFI_PAGE_SIZE) {
143 AlignmentMask = Alignment - 1;
144 Pages += EFI_SIZE_TO_PAGES (Alignment);
145 } else {
146 AlignmentMask = 0;
147 }
148
149 Status = gBS->AllocatePages (AllocateAnyPages, MemoryType, Pages, &Memory);
150 if (EFI_ERROR (Status)) {
151 return Status;
152 }
153
154 Status = gDS->GetMemorySpaceDescriptor (Memory, &Descriptor);
155 if (!EFI_ERROR (Status)) {
156 // We are making an assumption that all of memory has the same default attributes
157 gAttributes = Descriptor.Attributes;
158 } else {
159 gBS->FreePages (Memory, Pages);
160 return Status;
161 }
162
163 Status = gDS->SetMemorySpaceAttributes (Memory, EFI_PAGES_TO_SIZE (Pages), EFI_MEMORY_WC);
164 if (EFI_ERROR (Status)) {
165 gBS->FreePages (Memory, Pages);
166 return Status;
167 }
168
169 InvalidateDataCacheRange ((VOID *)(UINTN)Memory, EFI_PAGES_TO_SIZE (Pages));
170
171 NewNode = AllocatePool (sizeof (FREE_PAGE_NODE));
172 if (NewNode == NULL) {
173 ASSERT (FALSE);
174 gBS->FreePages (Memory, Pages);
175 return EFI_OUT_OF_RESOURCES;
176 }
177
178 NewNode->Base = Memory;
179 NewNode->Allocation = (VOID*)(((UINTN)Memory + AlignmentMask) & ~AlignmentMask);
180 NewNode->Pages = Pages;
181 NewNode->Allocated = TRUE;
182 NewNode->MemoryType = MemoryType;
183
184 InsertTailList (&mPageList, &NewNode->Link);
185
186 *Allocation = NewNode->Allocation;
187 return EFI_SUCCESS;
188 }
189
190 /**
191 * Free the memory allocation
192 *
193 * This function will actually try to find the allocation in the linked list.
194 * And it will then mark the entry as freed.
195 *
196 * @param Allocation Base address of the buffer to free
197 *
198 * @return EFI_SUCCESS The allocation has been freed
199 * @return EFI_NOT_FOUND The allocation was not found in the pool.
200 * @return EFI_INVALID_PARAMETER If Allocation is NULL
201 *
202 */
203 STATIC
204 EFI_STATUS
205 FreePagesFromList (
206 IN VOID *Allocation
207 )
208 {
209 LIST_ENTRY *Link;
210 FREE_PAGE_NODE *Node;
211
212 if (Allocation == NULL) {
213 return EFI_INVALID_PARAMETER;
214 }
215
216 for (Link = mPageList.ForwardLink; Link != &mPageList; Link = Link->ForwardLink) {
217 Node = BASE_CR (Link, FREE_PAGE_NODE, Link);
218 if ((UINTN)Node->Allocation == (UINTN)Allocation) {
219 Node->Allocated = FALSE;
220
221 // Update the size of the freed buffer
222 mFreedBufferSize += Node->Pages * EFI_PAGE_SIZE;
223
224 // If the size of the non-allocated reaches the threshold we raise a warning.
225 // It might be an expected behaviour in some cases.
226 // We might device to free some of these buffers later on.
227 if (mFreedBufferSize > PcdGet64 (PcdArmFreeUncachedMemorySizeThreshold)) {
228 DEBUG ((EFI_D_WARN, "Warning: The list of non-allocated buffer has reach the threshold.\n"));
229 }
230 return EFI_SUCCESS;
231 }
232 }
233
234 return EFI_NOT_FOUND;
235 }
236
237 /**
238 * This function is automatically invoked when the driver exits
239 * It frees all the non-allocated memory buffer.
240 * This function is not responsible to free allocated buffer (eg: case of memory leak,
241 * runtime allocation).
242 */
243 EFI_STATUS
244 EFIAPI
245 UncachedMemoryAllocationLibDestructor (
246 IN EFI_HANDLE ImageHandle,
247 IN EFI_SYSTEM_TABLE *SystemTable
248 )
249 {
250 LIST_ENTRY *Link;
251 FREE_PAGE_NODE *OldNode;
252
253 // Test if the list is empty
254 Link = mPageList.ForwardLink;
255 if (Link == &mPageList) {
256 return EFI_SUCCESS;
257 }
258
259 // Free all the pages and nodes
260 do {
261 OldNode = BASE_CR (Link, FREE_PAGE_NODE, Link);
262 // Point to the next entry
263 Link = Link->ForwardLink;
264
265 // We only free the non-allocated buffer
266 if (OldNode->Allocated == FALSE) {
267 gBS->FreePages ((EFI_PHYSICAL_ADDRESS)(UINTN)OldNode->Base, OldNode->Pages);
268 RemoveEntryList (&OldNode->Link);
269 FreePool (OldNode);
270 }
271 } while (Link != &mPageList);
272
273 return EFI_SUCCESS;
274 }
275
276 /**
277 Converts a cached or uncached address to a physical address suitable for use in SoC registers.
278
279 @param VirtualAddress The pointer to convert.
280
281 @return The physical address of the supplied virtual pointer.
282
283 **/
284 EFI_PHYSICAL_ADDRESS
285 ConvertToPhysicalAddress (
286 IN VOID *VirtualAddress
287 )
288 {
289 return (EFI_PHYSICAL_ADDRESS)(UINTN)VirtualAddress;
290 }
291
292
293 VOID *
294 UncachedInternalAllocatePages (
295 IN EFI_MEMORY_TYPE MemoryType,
296 IN UINTN Pages
297 )
298 {
299 return UncachedInternalAllocateAlignedPages (MemoryType, Pages, EFI_PAGE_SIZE);
300 }
301
302
303 VOID *
304 EFIAPI
305 UncachedAllocatePages (
306 IN UINTN Pages
307 )
308 {
309 return UncachedInternalAllocatePages (EfiBootServicesData, Pages);
310 }
311
312 VOID *
313 EFIAPI
314 UncachedAllocateRuntimePages (
315 IN UINTN Pages
316 )
317 {
318 return UncachedInternalAllocatePages (EfiRuntimeServicesData, Pages);
319 }
320
321 VOID *
322 EFIAPI
323 UncachedAllocateReservedPages (
324 IN UINTN Pages
325 )
326 {
327 return UncachedInternalAllocatePages (EfiReservedMemoryType, Pages);
328 }
329
330
331
332 VOID
333 EFIAPI
334 UncachedFreePages (
335 IN VOID *Buffer,
336 IN UINTN Pages
337 )
338 {
339 UncachedFreeAlignedPages (Buffer, Pages);
340 return;
341 }
342
343
344 VOID *
345 UncachedInternalAllocateAlignedPages (
346 IN EFI_MEMORY_TYPE MemoryType,
347 IN UINTN Pages,
348 IN UINTN Alignment
349 )
350 {
351 EFI_STATUS Status;
352 VOID *Allocation;
353
354 if (Pages == 0) {
355 return NULL;
356 }
357
358 Allocation = NULL;
359 Status = AllocatePagesFromList (MemoryType, Pages, Alignment, &Allocation);
360 if (EFI_ERROR (Status)) {
361 ASSERT_EFI_ERROR (Status);
362 return NULL;
363 } else {
364 return Allocation;
365 }
366 }
367
368
369 VOID
370 EFIAPI
371 UncachedFreeAlignedPages (
372 IN VOID *Buffer,
373 IN UINTN Pages
374 )
375 {
376 FreePagesFromList (Buffer);
377 }
378
379
380 VOID *
381 UncachedInternalAllocateAlignedPool (
382 IN EFI_MEMORY_TYPE PoolType,
383 IN UINTN AllocationSize,
384 IN UINTN Alignment
385 )
386 {
387 VOID *AlignedAddress;
388
389 //
390 // Alignment must be a power of two or zero.
391 //
392 ASSERT ((Alignment & (Alignment - 1)) == 0);
393
394 if (Alignment < EFI_PAGE_SIZE) {
395 Alignment = EFI_PAGE_SIZE;
396 }
397
398 AlignedAddress = UncachedInternalAllocateAlignedPages (PoolType, EFI_SIZE_TO_PAGES (AllocationSize), Alignment);
399 if (AlignedAddress == NULL) {
400 return NULL;
401 }
402
403 return (VOID *) AlignedAddress;
404 }
405
406 VOID *
407 EFIAPI
408 UncachedAllocateAlignedPool (
409 IN UINTN AllocationSize,
410 IN UINTN Alignment
411 )
412 {
413 return UncachedInternalAllocateAlignedPool (EfiBootServicesData, AllocationSize, Alignment);
414 }
415
416 VOID *
417 EFIAPI
418 UncachedAllocateAlignedRuntimePool (
419 IN UINTN AllocationSize,
420 IN UINTN Alignment
421 )
422 {
423 return UncachedInternalAllocateAlignedPool (EfiRuntimeServicesData, AllocationSize, Alignment);
424 }
425
426 VOID *
427 EFIAPI
428 UncachedAllocateAlignedReservedPool (
429 IN UINTN AllocationSize,
430 IN UINTN Alignment
431 )
432 {
433 return UncachedInternalAllocateAlignedPool (EfiReservedMemoryType, AllocationSize, Alignment);
434 }
435
436 VOID *
437 UncachedInternalAllocateAlignedZeroPool (
438 IN EFI_MEMORY_TYPE PoolType,
439 IN UINTN AllocationSize,
440 IN UINTN Alignment
441 )
442 {
443 VOID *Memory;
444 Memory = UncachedInternalAllocateAlignedPool (PoolType, AllocationSize, Alignment);
445 if (Memory != NULL) {
446 Memory = ZeroMem (Memory, AllocationSize);
447 }
448 return Memory;
449 }
450
451 VOID *
452 EFIAPI
453 UncachedAllocateAlignedZeroPool (
454 IN UINTN AllocationSize,
455 IN UINTN Alignment
456 )
457 {
458 return UncachedInternalAllocateAlignedZeroPool (EfiBootServicesData, AllocationSize, Alignment);
459 }
460
461 VOID *
462 EFIAPI
463 UncachedAllocateAlignedRuntimeZeroPool (
464 IN UINTN AllocationSize,
465 IN UINTN Alignment
466 )
467 {
468 return UncachedInternalAllocateAlignedZeroPool (EfiRuntimeServicesData, AllocationSize, Alignment);
469 }
470
471 VOID *
472 EFIAPI
473 UncachedAllocateAlignedReservedZeroPool (
474 IN UINTN AllocationSize,
475 IN UINTN Alignment
476 )
477 {
478 return UncachedInternalAllocateAlignedZeroPool (EfiReservedMemoryType, AllocationSize, Alignment);
479 }
480
481 VOID *
482 UncachedInternalAllocateAlignedCopyPool (
483 IN EFI_MEMORY_TYPE PoolType,
484 IN UINTN AllocationSize,
485 IN CONST VOID *Buffer,
486 IN UINTN Alignment
487 )
488 {
489 VOID *Memory;
490
491 ASSERT (Buffer != NULL);
492 ASSERT (AllocationSize <= (MAX_ADDRESS - (UINTN) Buffer + 1));
493
494 Memory = UncachedInternalAllocateAlignedPool (PoolType, AllocationSize, Alignment);
495 if (Memory != NULL) {
496 Memory = CopyMem (Memory, Buffer, AllocationSize);
497 }
498 return Memory;
499 }
500
501 VOID *
502 EFIAPI
503 UncachedAllocateAlignedCopyPool (
504 IN UINTN AllocationSize,
505 IN CONST VOID *Buffer,
506 IN UINTN Alignment
507 )
508 {
509 return UncachedInternalAllocateAlignedCopyPool (EfiBootServicesData, AllocationSize, Buffer, Alignment);
510 }
511
512 VOID *
513 EFIAPI
514 UncachedAllocateAlignedRuntimeCopyPool (
515 IN UINTN AllocationSize,
516 IN CONST VOID *Buffer,
517 IN UINTN Alignment
518 )
519 {
520 return UncachedInternalAllocateAlignedCopyPool (EfiRuntimeServicesData, AllocationSize, Buffer, Alignment);
521 }
522
523 VOID *
524 EFIAPI
525 UncachedAllocateAlignedReservedCopyPool (
526 IN UINTN AllocationSize,
527 IN CONST VOID *Buffer,
528 IN UINTN Alignment
529 )
530 {
531 return UncachedInternalAllocateAlignedCopyPool (EfiReservedMemoryType, AllocationSize, Buffer, Alignment);
532 }
533
534 VOID
535 EFIAPI
536 UncachedFreeAlignedPool (
537 IN VOID *Allocation
538 )
539 {
540 UncachedFreePages (Allocation, 0);
541 }
542
543 VOID *
544 UncachedInternalAllocatePool (
545 IN EFI_MEMORY_TYPE MemoryType,
546 IN UINTN AllocationSize
547 )
548 {
549 UINTN CacheLineLength = ArmDataCacheLineLength ();
550 return UncachedInternalAllocateAlignedPool (MemoryType, AllocationSize, CacheLineLength);
551 }
552
553 VOID *
554 EFIAPI
555 UncachedAllocatePool (
556 IN UINTN AllocationSize
557 )
558 {
559 return UncachedInternalAllocatePool (EfiBootServicesData, AllocationSize);
560 }
561
562 VOID *
563 EFIAPI
564 UncachedAllocateRuntimePool (
565 IN UINTN AllocationSize
566 )
567 {
568 return UncachedInternalAllocatePool (EfiRuntimeServicesData, AllocationSize);
569 }
570
571 VOID *
572 EFIAPI
573 UncachedAllocateReservedPool (
574 IN UINTN AllocationSize
575 )
576 {
577 return UncachedInternalAllocatePool (EfiReservedMemoryType, AllocationSize);
578 }
579
580 VOID *
581 UncachedInternalAllocateZeroPool (
582 IN EFI_MEMORY_TYPE PoolType,
583 IN UINTN AllocationSize
584 )
585 {
586 VOID *Memory;
587
588 Memory = UncachedInternalAllocatePool (PoolType, AllocationSize);
589 if (Memory != NULL) {
590 Memory = ZeroMem (Memory, AllocationSize);
591 }
592 return Memory;
593 }
594
595 VOID *
596 EFIAPI
597 UncachedAllocateZeroPool (
598 IN UINTN AllocationSize
599 )
600 {
601 return UncachedInternalAllocateZeroPool (EfiBootServicesData, AllocationSize);
602 }
603
604 VOID *
605 EFIAPI
606 UncachedAllocateRuntimeZeroPool (
607 IN UINTN AllocationSize
608 )
609 {
610 return UncachedInternalAllocateZeroPool (EfiRuntimeServicesData, AllocationSize);
611 }
612
613 VOID *
614 EFIAPI
615 UncachedAllocateReservedZeroPool (
616 IN UINTN AllocationSize
617 )
618 {
619 return UncachedInternalAllocateZeroPool (EfiReservedMemoryType, AllocationSize);
620 }
621
622 VOID *
623 UncachedInternalAllocateCopyPool (
624 IN EFI_MEMORY_TYPE PoolType,
625 IN UINTN AllocationSize,
626 IN CONST VOID *Buffer
627 )
628 {
629 VOID *Memory;
630
631 ASSERT (Buffer != NULL);
632 ASSERT (AllocationSize <= (MAX_ADDRESS - (UINTN) Buffer + 1));
633
634 Memory = UncachedInternalAllocatePool (PoolType, AllocationSize);
635 if (Memory != NULL) {
636 Memory = CopyMem (Memory, Buffer, AllocationSize);
637 }
638 return Memory;
639 }
640
641 VOID *
642 EFIAPI
643 UncachedAllocateCopyPool (
644 IN UINTN AllocationSize,
645 IN CONST VOID *Buffer
646 )
647 {
648 return UncachedInternalAllocateCopyPool (EfiBootServicesData, AllocationSize, Buffer);
649 }
650
651 VOID *
652 EFIAPI
653 UncachedAllocateRuntimeCopyPool (
654 IN UINTN AllocationSize,
655 IN CONST VOID *Buffer
656 )
657 {
658 return UncachedInternalAllocateCopyPool (EfiRuntimeServicesData, AllocationSize, Buffer);
659 }
660
661 VOID *
662 EFIAPI
663 UncachedAllocateReservedCopyPool (
664 IN UINTN AllocationSize,
665 IN CONST VOID *Buffer
666 )
667 {
668 return UncachedInternalAllocateCopyPool (EfiReservedMemoryType, AllocationSize, Buffer);
669 }
670
671 VOID
672 EFIAPI
673 UncachedFreePool (
674 IN VOID *Buffer
675 )
676 {
677 UncachedFreeAlignedPool (Buffer);
678 }
679
680 VOID
681 EFIAPI
682 UncachedSafeFreePool (
683 IN VOID *Buffer
684 )
685 {
686 if (Buffer != NULL) {
687 UncachedFreePool (Buffer);
688 Buffer = NULL;
689 }
690 }
691