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