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