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