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