]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPkg/Library/UncachedMemoryAllocationLib/UncachedMemoryAllocationLib.c
ArmPkg/UncachedMemoryAllocationLib: map uncached allocations non-executable
[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),
158 EFI_MEMORY_WC | EFI_MEMORY_XP);
159 if (EFI_ERROR (Status)) {
160 gBS->FreePages (Memory, Pages);
161 return Status;
162 }
163
164 InvalidateDataCacheRange ((VOID *)(UINTN)Memory, EFI_PAGES_TO_SIZE (Pages));
165
166 NewNode = AllocatePool (sizeof (FREE_PAGE_NODE));
167 if (NewNode == NULL) {
168 ASSERT (FALSE);
169 gBS->FreePages (Memory, Pages);
170 return EFI_OUT_OF_RESOURCES;
171 }
172
173 NewNode->Base = Memory;
174 NewNode->Allocation = (VOID*)(((UINTN)Memory + AlignmentMask) & ~AlignmentMask);
175 NewNode->Pages = Pages;
176 NewNode->Allocated = TRUE;
177 NewNode->MemoryType = MemoryType;
178 NewNode->Attributes = Descriptor.Attributes;
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
265 gDS->SetMemorySpaceAttributes ((EFI_PHYSICAL_ADDRESS)(UINTN)OldNode->Base,
266 EFI_PAGES_TO_SIZE (OldNode->Pages), OldNode->Attributes);
267
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 = ArmCacheWritebackGranule ();
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