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