2 UncachedMemoryAllocation lib that uses DXE Service to change cachability for
5 Copyright (c) 2008 - 2010, Apple Inc. All rights reserved.<BR>
6 Copyright (c) 2014, AMR Ltd. All rights reserved.<BR>
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
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.
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>
30 #include <Protocol/Cpu.h>
32 STATIC EFI_CPU_ARCH_PROTOCOL
*mCpu
;
35 UncachedInternalAllocatePages (
36 IN EFI_MEMORY_TYPE MemoryType
,
41 UncachedInternalAllocateAlignedPages (
42 IN EFI_MEMORY_TYPE MemoryType
,
50 EFI_PHYSICAL_ADDRESS Base
;
53 EFI_MEMORY_TYPE MemoryType
;
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;
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.
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
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
79 AllocatePagesFromList (
80 IN EFI_MEMORY_TYPE MemoryType
,
89 FREE_PAGE_NODE
*NewNode
;
91 EFI_PHYSICAL_ADDRESS Memory
;
92 EFI_GCD_MEMORY_SPACE_DESCRIPTOR Descriptor
;
94 // Alignment must be a power of two or zero.
95 ASSERT ((Alignment
& (Alignment
- 1)) == 0);
98 // Look in our list for the smallest page that could satisfy the new allocation
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
;
113 // Update the size of the freed buffer
114 mFreedBufferSize
-= Pages
* EFI_PAGE_SIZE
;
116 } else if (Node
->Pages
> Pages
) {
117 if (NewNode
== NULL
) {
118 // It is the first node that could contain our new allocation
120 } else if (NewNode
->Pages
> Node
->Pages
) {
121 // This node offers a smaller number of page.
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
;
138 // Otherwise, we need to allocate a new buffer
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
);
150 Status
= gBS
->AllocatePages (AllocateAnyPages
, MemoryType
, Pages
, &Memory
);
151 if (EFI_ERROR (Status
)) {
155 Status
= gDS
->GetMemorySpaceDescriptor (Memory
, &Descriptor
);
156 if (EFI_ERROR (Status
)) {
160 Status
= gDS
->SetMemorySpaceAttributes (Memory
, EFI_PAGES_TO_SIZE (Pages
),
162 if (EFI_ERROR (Status
)) {
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)
172 Status
= mCpu
->SetMemoryAttributes (mCpu
, Memory
, EFI_PAGES_TO_SIZE (Pages
),
174 if (EFI_ERROR (Status
)) {
178 InvalidateDataCacheRange ((VOID
*)(UINTN
)Memory
, EFI_PAGES_TO_SIZE (Pages
));
180 NewNode
= AllocatePool (sizeof (FREE_PAGE_NODE
));
181 if (NewNode
== NULL
) {
183 Status
= EFI_OUT_OF_RESOURCES
;
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
;
194 InsertTailList (&mPageList
, &NewNode
->Link
);
196 *Allocation
= NewNode
->Allocation
;
200 gBS
->FreePages (Memory
, Pages
);
205 * Free the memory allocation
207 * This function will actually try to find the allocation in the linked list.
208 * And it will then mark the entry as freed.
210 * @param Allocation Base address of the buffer to free
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
224 FREE_PAGE_NODE
*Node
;
226 if (Allocation
== NULL
) {
227 return EFI_INVALID_PARAMETER
;
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
;
235 // Update the size of the freed buffer
236 mFreedBufferSize
+= Node
->Pages
* EFI_PAGE_SIZE
;
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"));
248 return EFI_NOT_FOUND
;
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).
259 UncachedMemoryAllocationLibConstructor (
260 IN EFI_HANDLE ImageHandle
,
261 IN EFI_SYSTEM_TABLE
*SystemTable
264 return gBS
->LocateProtocol (&gEfiCpuArchProtocolGuid
, NULL
, (VOID
**)&mCpu
);
269 UncachedMemoryAllocationLibDestructor (
270 IN EFI_HANDLE ImageHandle
,
271 IN EFI_SYSTEM_TABLE
*SystemTable
275 FREE_PAGE_NODE
*OldNode
;
277 // Test if the list is empty
278 Link
= mPageList
.ForwardLink
;
279 if (Link
== &mPageList
) {
283 // Free all the pages and nodes
285 OldNode
= BASE_CR (Link
, FREE_PAGE_NODE
, Link
);
286 // Point to the next entry
287 Link
= Link
->ForwardLink
;
289 // We only free the non-allocated buffer
290 if (OldNode
->Allocated
== FALSE
) {
291 gBS
->FreePages ((EFI_PHYSICAL_ADDRESS
)(UINTN
)OldNode
->Base
, OldNode
->Pages
);
293 gDS
->SetMemorySpaceAttributes ((EFI_PHYSICAL_ADDRESS
)(UINTN
)OldNode
->Base
,
294 EFI_PAGES_TO_SIZE (OldNode
->Pages
), OldNode
->Attributes
);
296 RemoveEntryList (&OldNode
->Link
);
299 } while (Link
!= &mPageList
);
305 Converts a cached or uncached address to a physical address suitable for use in SoC registers.
307 @param VirtualAddress The pointer to convert.
309 @return The physical address of the supplied virtual pointer.
313 ConvertToPhysicalAddress (
314 IN VOID
*VirtualAddress
317 return (EFI_PHYSICAL_ADDRESS
)(UINTN
)VirtualAddress
;
322 UncachedInternalAllocatePages (
323 IN EFI_MEMORY_TYPE MemoryType
,
327 return UncachedInternalAllocateAlignedPages (MemoryType
, Pages
, EFI_PAGE_SIZE
);
333 UncachedAllocatePages (
337 return UncachedInternalAllocatePages (EfiBootServicesData
, Pages
);
342 UncachedAllocateRuntimePages (
346 return UncachedInternalAllocatePages (EfiRuntimeServicesData
, Pages
);
351 UncachedAllocateReservedPages (
355 return UncachedInternalAllocatePages (EfiReservedMemoryType
, Pages
);
367 UncachedFreeAlignedPages (Buffer
, Pages
);
373 UncachedInternalAllocateAlignedPages (
374 IN EFI_MEMORY_TYPE MemoryType
,
387 Status
= AllocatePagesFromList (MemoryType
, Pages
, Alignment
, &Allocation
);
388 if (EFI_ERROR (Status
)) {
389 ASSERT_EFI_ERROR (Status
);
399 UncachedFreeAlignedPages (
404 FreePagesFromList (Buffer
);
409 UncachedInternalAllocateAlignedPool (
410 IN EFI_MEMORY_TYPE PoolType
,
411 IN UINTN AllocationSize
,
415 VOID
*AlignedAddress
;
418 // Alignment must be a power of two or zero.
420 ASSERT ((Alignment
& (Alignment
- 1)) == 0);
422 if (Alignment
< EFI_PAGE_SIZE
) {
423 Alignment
= EFI_PAGE_SIZE
;
426 AlignedAddress
= UncachedInternalAllocateAlignedPages (PoolType
, EFI_SIZE_TO_PAGES (AllocationSize
), Alignment
);
427 if (AlignedAddress
== NULL
) {
431 return (VOID
*) AlignedAddress
;
436 UncachedAllocateAlignedPool (
437 IN UINTN AllocationSize
,
441 return UncachedInternalAllocateAlignedPool (EfiBootServicesData
, AllocationSize
, Alignment
);
446 UncachedAllocateAlignedRuntimePool (
447 IN UINTN AllocationSize
,
451 return UncachedInternalAllocateAlignedPool (EfiRuntimeServicesData
, AllocationSize
, Alignment
);
456 UncachedAllocateAlignedReservedPool (
457 IN UINTN AllocationSize
,
461 return UncachedInternalAllocateAlignedPool (EfiReservedMemoryType
, AllocationSize
, Alignment
);
465 UncachedInternalAllocateAlignedZeroPool (
466 IN EFI_MEMORY_TYPE PoolType
,
467 IN UINTN AllocationSize
,
472 Memory
= UncachedInternalAllocateAlignedPool (PoolType
, AllocationSize
, Alignment
);
473 if (Memory
!= NULL
) {
474 Memory
= ZeroMem (Memory
, AllocationSize
);
481 UncachedAllocateAlignedZeroPool (
482 IN UINTN AllocationSize
,
486 return UncachedInternalAllocateAlignedZeroPool (EfiBootServicesData
, AllocationSize
, Alignment
);
491 UncachedAllocateAlignedRuntimeZeroPool (
492 IN UINTN AllocationSize
,
496 return UncachedInternalAllocateAlignedZeroPool (EfiRuntimeServicesData
, AllocationSize
, Alignment
);
501 UncachedAllocateAlignedReservedZeroPool (
502 IN UINTN AllocationSize
,
506 return UncachedInternalAllocateAlignedZeroPool (EfiReservedMemoryType
, AllocationSize
, Alignment
);
510 UncachedInternalAllocateAlignedCopyPool (
511 IN EFI_MEMORY_TYPE PoolType
,
512 IN UINTN AllocationSize
,
513 IN CONST VOID
*Buffer
,
519 ASSERT (Buffer
!= NULL
);
520 ASSERT (AllocationSize
<= (MAX_ADDRESS
- (UINTN
) Buffer
+ 1));
522 Memory
= UncachedInternalAllocateAlignedPool (PoolType
, AllocationSize
, Alignment
);
523 if (Memory
!= NULL
) {
524 Memory
= CopyMem (Memory
, Buffer
, AllocationSize
);
531 UncachedAllocateAlignedCopyPool (
532 IN UINTN AllocationSize
,
533 IN CONST VOID
*Buffer
,
537 return UncachedInternalAllocateAlignedCopyPool (EfiBootServicesData
, AllocationSize
, Buffer
, Alignment
);
542 UncachedAllocateAlignedRuntimeCopyPool (
543 IN UINTN AllocationSize
,
544 IN CONST VOID
*Buffer
,
548 return UncachedInternalAllocateAlignedCopyPool (EfiRuntimeServicesData
, AllocationSize
, Buffer
, Alignment
);
553 UncachedAllocateAlignedReservedCopyPool (
554 IN UINTN AllocationSize
,
555 IN CONST VOID
*Buffer
,
559 return UncachedInternalAllocateAlignedCopyPool (EfiReservedMemoryType
, AllocationSize
, Buffer
, Alignment
);
564 UncachedFreeAlignedPool (
568 UncachedFreePages (Allocation
, 0);
572 UncachedInternalAllocatePool (
573 IN EFI_MEMORY_TYPE MemoryType
,
574 IN UINTN AllocationSize
577 UINTN CacheLineLength
= ArmCacheWritebackGranule ();
578 return UncachedInternalAllocateAlignedPool (MemoryType
, AllocationSize
, CacheLineLength
);
583 UncachedAllocatePool (
584 IN UINTN AllocationSize
587 return UncachedInternalAllocatePool (EfiBootServicesData
, AllocationSize
);
592 UncachedAllocateRuntimePool (
593 IN UINTN AllocationSize
596 return UncachedInternalAllocatePool (EfiRuntimeServicesData
, AllocationSize
);
601 UncachedAllocateReservedPool (
602 IN UINTN AllocationSize
605 return UncachedInternalAllocatePool (EfiReservedMemoryType
, AllocationSize
);
609 UncachedInternalAllocateZeroPool (
610 IN EFI_MEMORY_TYPE PoolType
,
611 IN UINTN AllocationSize
616 Memory
= UncachedInternalAllocatePool (PoolType
, AllocationSize
);
617 if (Memory
!= NULL
) {
618 Memory
= ZeroMem (Memory
, AllocationSize
);
625 UncachedAllocateZeroPool (
626 IN UINTN AllocationSize
629 return UncachedInternalAllocateZeroPool (EfiBootServicesData
, AllocationSize
);
634 UncachedAllocateRuntimeZeroPool (
635 IN UINTN AllocationSize
638 return UncachedInternalAllocateZeroPool (EfiRuntimeServicesData
, AllocationSize
);
643 UncachedAllocateReservedZeroPool (
644 IN UINTN AllocationSize
647 return UncachedInternalAllocateZeroPool (EfiReservedMemoryType
, AllocationSize
);
651 UncachedInternalAllocateCopyPool (
652 IN EFI_MEMORY_TYPE PoolType
,
653 IN UINTN AllocationSize
,
654 IN CONST VOID
*Buffer
659 ASSERT (Buffer
!= NULL
);
660 ASSERT (AllocationSize
<= (MAX_ADDRESS
- (UINTN
) Buffer
+ 1));
662 Memory
= UncachedInternalAllocatePool (PoolType
, AllocationSize
);
663 if (Memory
!= NULL
) {
664 Memory
= CopyMem (Memory
, Buffer
, AllocationSize
);
671 UncachedAllocateCopyPool (
672 IN UINTN AllocationSize
,
673 IN CONST VOID
*Buffer
676 return UncachedInternalAllocateCopyPool (EfiBootServicesData
, AllocationSize
, Buffer
);
681 UncachedAllocateRuntimeCopyPool (
682 IN UINTN AllocationSize
,
683 IN CONST VOID
*Buffer
686 return UncachedInternalAllocateCopyPool (EfiRuntimeServicesData
, AllocationSize
, Buffer
);
691 UncachedAllocateReservedCopyPool (
692 IN UINTN AllocationSize
,
693 IN CONST VOID
*Buffer
696 return UncachedInternalAllocateCopyPool (EfiReservedMemoryType
, AllocationSize
, Buffer
);
705 UncachedFreeAlignedPool (Buffer
);
710 UncachedSafeFreePool (
714 if (Buffer
!= NULL
) {
715 UncachedFreePool (Buffer
);