Update the copyright notice format
[mirror_edk2.git] / ArmPkg / Library / DebugUncachedMemoryAllocationLib / DebugUncachedMemoryAllocationLib.c
1 /** @file
2 Debug version of the UncachedMemoryAllocation lib that uses the VirtualUncachedPages
3 protocol, produced by the DXE CPU driver, to produce debuggable uncached memory buffers.
4
5 The DMA rules for EFI contain the concept of a PCI (DMA master) address for memory and
6 a CPU (C code) address for the memory buffer that don't have to be the same. There seem to
7 be common errors out there with folks mixing up the two addresses. This library causes
8 the PCI (DMA master) address to not be mapped into system memory so if the CPU (C code)
9 uses the wrong pointer it will generate a page fault. The CPU (C code) version of the buffer
10 has a virtual address that does not match the physical address. The virtual address has
11 PcdArmUncachedMemoryMask ored into the physical address.
12
13 Copyright (c) 2008 - 2010, Apple Inc. All rights reserved.<BR>
14
15 This program and the accompanying materials
16 are licensed and made available under the terms and conditions of the BSD License
17 which accompanies this distribution. The full text of the license may be found at
18 http://opensource.org/licenses/bsd-license.php
19
20 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
21 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
22
23 **/
24
25 #include <Library/BaseLib.h>
26 #include <Library/BaseMemoryLib.h>
27 #include <Library/MemoryAllocationLib.h>
28 #include <Library/DebugLib.h>
29 #include <Library/UefiBootServicesTableLib.h>
30 #include <Library/UncachedMemoryAllocationLib.h>
31 #include <Library/PcdLib.h>
32 #include <Library/ArmLib.h>
33
34 #include <Protocol/Cpu.h>
35 #include <Protocol/VirtualUncachedPages.h>
36
37 VOID *
38 UncachedInternalAllocatePages (
39 IN EFI_MEMORY_TYPE MemoryType,
40 IN UINTN Pages
41 );
42
43 VOID *
44 UncachedInternalAllocateAlignedPages (
45 IN EFI_MEMORY_TYPE MemoryType,
46 IN UINTN Pages,
47 IN UINTN Alignment
48 );
49
50
51
52 EFI_CPU_ARCH_PROTOCOL *gDebugUncachedCpu;
53 VIRTUAL_UNCACHED_PAGES_PROTOCOL *gVirtualUncachedPages;
54
55 //
56 // Assume all of memory has the same cache attributes, unless we do our magic
57 //
58 UINT64 gAttributes;
59
60 typedef struct {
61 VOID *Buffer;
62 VOID *Allocation;
63 UINTN Pages;
64 LIST_ENTRY Link;
65 } FREE_PAGE_NODE;
66
67 LIST_ENTRY mPageList = INITIALIZE_LIST_HEAD_VARIABLE (mPageList);
68
69 VOID
70 AddPagesToList (
71 IN VOID *Buffer,
72 IN VOID *Allocation,
73 UINTN Pages
74 )
75 {
76 FREE_PAGE_NODE *NewNode;
77
78 NewNode = AllocatePool (sizeof (LIST_ENTRY));
79 if (NewNode == NULL) {
80 ASSERT (FALSE);
81 return;
82 }
83
84 NewNode->Buffer = Buffer;
85 NewNode->Allocation = Allocation;
86 NewNode->Pages = Pages;
87
88 InsertTailList (&mPageList, &NewNode->Link);
89 }
90
91
92 VOID
93 RemovePagesFromList (
94 IN VOID *Buffer,
95 OUT VOID **Allocation,
96 OUT UINTN *Pages
97 )
98 {
99 LIST_ENTRY *Link;
100 FREE_PAGE_NODE *OldNode;
101
102 *Allocation = NULL;
103 *Pages = 0;
104
105 for (Link = mPageList.ForwardLink; Link != &mPageList; Link = Link->ForwardLink) {
106 OldNode = BASE_CR (Link, FREE_PAGE_NODE, Link);
107 if (OldNode->Buffer == Buffer) {
108 *Allocation = OldNode->Allocation;
109 *Pages = OldNode->Pages;
110
111 RemoveEntryList (&OldNode->Link);
112 FreePool (OldNode);
113 return;
114 }
115 }
116
117 return;
118 }
119
120
121
122 EFI_PHYSICAL_ADDRESS
123 ConvertToPhysicalAddress (
124 IN VOID *VirtualAddress
125 )
126 {
127 UINTN UncachedMemoryMask = (UINTN)PcdGet64 (PcdArmUncachedMemoryMask);
128 UINTN PhysicalAddress;
129
130 PhysicalAddress = (UINTN)VirtualAddress & ~UncachedMemoryMask;
131
132 return (EFI_PHYSICAL_ADDRESS)PhysicalAddress;
133 }
134
135
136 VOID *
137 ConvertToUncachedAddress (
138 IN VOID *Address
139 )
140 {
141 UINTN UncachedMemoryMask = (UINTN)PcdGet64 (PcdArmUncachedMemoryMask);
142 UINTN UncachedAddress;
143
144 UncachedAddress = (UINTN)Address | UncachedMemoryMask;
145
146 return (VOID *)UncachedAddress;
147 }
148
149
150
151 VOID *
152 UncachedInternalAllocatePages (
153 IN EFI_MEMORY_TYPE MemoryType,
154 IN UINTN Pages
155 )
156 {
157 return UncachedInternalAllocateAlignedPages (MemoryType, Pages, EFI_PAGE_SIZE);
158 }
159
160
161 VOID *
162 EFIAPI
163 UncachedAllocatePages (
164 IN UINTN Pages
165 )
166 {
167 return UncachedInternalAllocatePages (EfiBootServicesData, Pages);
168 }
169
170 VOID *
171 EFIAPI
172 UncachedAllocateRuntimePages (
173 IN UINTN Pages
174 )
175 {
176 return UncachedInternalAllocatePages (EfiRuntimeServicesData, Pages);
177 }
178
179 VOID *
180 EFIAPI
181 UncachedAllocateReservedPages (
182 IN UINTN Pages
183 )
184 {
185 return UncachedInternalAllocatePages (EfiReservedMemoryType, Pages);
186 }
187
188
189
190 VOID
191 EFIAPI
192 UncachedFreePages (
193 IN VOID *Buffer,
194 IN UINTN Pages
195 )
196 {
197 UncachedFreeAlignedPages (Buffer, Pages);
198 return;
199 }
200
201
202 VOID *
203 UncachedInternalAllocateAlignedPages (
204 IN EFI_MEMORY_TYPE MemoryType,
205 IN UINTN Pages,
206 IN UINTN Alignment
207 )
208 {
209 EFI_STATUS Status;
210 EFI_PHYSICAL_ADDRESS Memory;
211 EFI_PHYSICAL_ADDRESS AlignedMemory;
212 UINTN AlignmentMask;
213 UINTN UnalignedPages;
214 UINTN RealPages;
215
216 //
217 // Alignment must be a power of two or zero.
218 //
219 ASSERT ((Alignment & (Alignment - 1)) == 0);
220
221 if (Pages == 0) {
222 return NULL;
223 }
224 if (Alignment > EFI_PAGE_SIZE) {
225 //
226 // Caculate the total number of pages since alignment is larger than page size.
227 //
228 AlignmentMask = Alignment - 1;
229 RealPages = Pages + EFI_SIZE_TO_PAGES (Alignment);
230 //
231 // Make sure that Pages plus EFI_SIZE_TO_PAGES (Alignment) does not overflow.
232 //
233 ASSERT (RealPages > Pages);
234
235 Status = gBS->AllocatePages (AllocateAnyPages, MemoryType, RealPages, &Memory);
236 if (EFI_ERROR (Status)) {
237 return NULL;
238 }
239 AlignedMemory = ((UINTN) Memory + AlignmentMask) & ~AlignmentMask;
240 UnalignedPages = EFI_SIZE_TO_PAGES (AlignedMemory - (UINTN) Memory);
241 if (UnalignedPages > 0) {
242 //
243 // Free first unaligned page(s).
244 //
245 Status = gBS->FreePages (Memory, UnalignedPages);
246 ASSERT_EFI_ERROR (Status);
247 }
248 Memory = (EFI_PHYSICAL_ADDRESS) (AlignedMemory + EFI_PAGES_TO_SIZE (Pages));
249 UnalignedPages = RealPages - Pages - UnalignedPages;
250 if (UnalignedPages > 0) {
251 //
252 // Free last unaligned page(s).
253 //
254 Status = gBS->FreePages (Memory, UnalignedPages);
255 ASSERT_EFI_ERROR (Status);
256 }
257 } else {
258 //
259 // Do not over-allocate pages in this case.
260 //
261 Status = gBS->AllocatePages (AllocateAnyPages, MemoryType, Pages, &Memory);
262 if (EFI_ERROR (Status)) {
263 return NULL;
264 }
265 AlignedMemory = (UINTN) Memory;
266 }
267
268 Status = gVirtualUncachedPages->ConvertPages (gVirtualUncachedPages, AlignedMemory, Pages * EFI_PAGE_SIZE, PcdGet64 (PcdArmUncachedMemoryMask), &gAttributes);
269 if (EFI_ERROR (Status)) {
270 return NULL;
271 }
272
273 AlignedMemory = (EFI_PHYSICAL_ADDRESS)(UINTN)ConvertToUncachedAddress ((VOID *)(UINTN)AlignedMemory);
274
275 return (VOID *)(UINTN)AlignedMemory;
276 }
277
278
279 VOID
280 EFIAPI
281 UncachedFreeAlignedPages (
282 IN VOID *Buffer,
283 IN UINTN Pages
284 )
285 {
286 EFI_STATUS Status;
287 EFI_PHYSICAL_ADDRESS Memory;
288
289 ASSERT (Pages != 0);
290
291 Memory = ConvertToPhysicalAddress (Buffer);
292
293 Status = gVirtualUncachedPages->RevertPages (gVirtualUncachedPages, Memory, Pages * EFI_PAGE_SIZE, PcdGet64 (PcdArmUncachedMemoryMask), gAttributes);
294
295
296 Status = gBS->FreePages ((EFI_PHYSICAL_ADDRESS) (UINTN) Memory, Pages);
297 ASSERT_EFI_ERROR (Status);
298 }
299
300
301
302
303 VOID *
304 UncachedInternalAllocateAlignedPool (
305 IN EFI_MEMORY_TYPE PoolType,
306 IN UINTN AllocationSize,
307 IN UINTN Alignment
308 )
309 {
310 VOID *AlignedAddress;
311
312 //
313 // Alignment must be a power of two or zero.
314 //
315 ASSERT ((Alignment & (Alignment - 1)) == 0);
316
317 if (Alignment < EFI_PAGE_SIZE) {
318 Alignment = EFI_PAGE_SIZE;
319 }
320
321 AlignedAddress = UncachedInternalAllocateAlignedPages (PoolType, EFI_SIZE_TO_PAGES (AllocationSize), Alignment);
322 if (AlignedAddress == NULL) {
323 return NULL;
324 }
325
326 AddPagesToList ((VOID *)(UINTN)ConvertToPhysicalAddress (AlignedAddress), (VOID *)(UINTN)AlignedAddress, EFI_SIZE_TO_PAGES (AllocationSize));
327
328 return (VOID *) AlignedAddress;
329 }
330
331 VOID *
332 EFIAPI
333 UncachedAllocateAlignedPool (
334 IN UINTN AllocationSize,
335 IN UINTN Alignment
336 )
337 {
338 return UncachedInternalAllocateAlignedPool (EfiBootServicesData, AllocationSize, Alignment);
339 }
340
341 VOID *
342 EFIAPI
343 UncachedAllocateAlignedRuntimePool (
344 IN UINTN AllocationSize,
345 IN UINTN Alignment
346 )
347 {
348 return UncachedInternalAllocateAlignedPool (EfiRuntimeServicesData, AllocationSize, Alignment);
349 }
350
351 VOID *
352 EFIAPI
353 UncachedAllocateAlignedReservedPool (
354 IN UINTN AllocationSize,
355 IN UINTN Alignment
356 )
357 {
358 return UncachedInternalAllocateAlignedPool (EfiReservedMemoryType, AllocationSize, Alignment);
359 }
360
361 VOID *
362 UncachedInternalAllocateAlignedZeroPool (
363 IN EFI_MEMORY_TYPE PoolType,
364 IN UINTN AllocationSize,
365 IN UINTN Alignment
366 )
367 {
368 VOID *Memory;
369 Memory = UncachedInternalAllocateAlignedPool (PoolType, AllocationSize, Alignment);
370 if (Memory != NULL) {
371 Memory = ZeroMem (Memory, AllocationSize);
372 }
373 return Memory;
374 }
375
376 VOID *
377 EFIAPI
378 UncachedAllocateAlignedZeroPool (
379 IN UINTN AllocationSize,
380 IN UINTN Alignment
381 )
382 {
383 return UncachedInternalAllocateAlignedZeroPool (EfiBootServicesData, AllocationSize, Alignment);
384 }
385
386 VOID *
387 EFIAPI
388 UncachedAllocateAlignedRuntimeZeroPool (
389 IN UINTN AllocationSize,
390 IN UINTN Alignment
391 )
392 {
393 return UncachedInternalAllocateAlignedZeroPool (EfiRuntimeServicesData, AllocationSize, Alignment);
394 }
395
396 VOID *
397 EFIAPI
398 UncachedAllocateAlignedReservedZeroPool (
399 IN UINTN AllocationSize,
400 IN UINTN Alignment
401 )
402 {
403 return UncachedInternalAllocateAlignedZeroPool (EfiReservedMemoryType, AllocationSize, Alignment);
404 }
405
406 VOID *
407 UncachedInternalAllocateAlignedCopyPool (
408 IN EFI_MEMORY_TYPE PoolType,
409 IN UINTN AllocationSize,
410 IN CONST VOID *Buffer,
411 IN UINTN Alignment
412 )
413 {
414 VOID *Memory;
415
416 ASSERT (Buffer != NULL);
417 ASSERT (AllocationSize <= (MAX_ADDRESS - (UINTN) Buffer + 1));
418
419 Memory = UncachedInternalAllocateAlignedPool (PoolType, AllocationSize, Alignment);
420 if (Memory != NULL) {
421 Memory = CopyMem (Memory, Buffer, AllocationSize);
422 }
423 return Memory;
424 }
425
426 VOID *
427 EFIAPI
428 UncachedAllocateAlignedCopyPool (
429 IN UINTN AllocationSize,
430 IN CONST VOID *Buffer,
431 IN UINTN Alignment
432 )
433 {
434 return UncachedInternalAllocateAlignedCopyPool (EfiBootServicesData, AllocationSize, Buffer, Alignment);
435 }
436
437 VOID *
438 EFIAPI
439 UncachedAllocateAlignedRuntimeCopyPool (
440 IN UINTN AllocationSize,
441 IN CONST VOID *Buffer,
442 IN UINTN Alignment
443 )
444 {
445 return UncachedInternalAllocateAlignedCopyPool (EfiRuntimeServicesData, AllocationSize, Buffer, Alignment);
446 }
447
448 VOID *
449 EFIAPI
450 UncachedAllocateAlignedReservedCopyPool (
451 IN UINTN AllocationSize,
452 IN CONST VOID *Buffer,
453 IN UINTN Alignment
454 )
455 {
456 return UncachedInternalAllocateAlignedCopyPool (EfiReservedMemoryType, AllocationSize, Buffer, Alignment);
457 }
458
459 VOID
460 EFIAPI
461 UncachedFreeAlignedPool (
462 IN VOID *Buffer
463 )
464 {
465 VOID *Allocation;
466 UINTN Pages;
467
468 RemovePagesFromList (Buffer, &Allocation, &Pages);
469
470 UncachedFreePages (Allocation, Pages);
471 }
472
473 VOID *
474 UncachedInternalAllocatePool (
475 IN EFI_MEMORY_TYPE MemoryType,
476 IN UINTN AllocationSize
477 )
478 {
479 UINTN CacheLineLength = ArmDataCacheLineLength ();
480 return UncachedInternalAllocateAlignedPool (MemoryType, AllocationSize, CacheLineLength);
481 }
482
483 VOID *
484 EFIAPI
485 UncachedAllocatePool (
486 IN UINTN AllocationSize
487 )
488 {
489 return UncachedInternalAllocatePool (EfiBootServicesData, AllocationSize);
490 }
491
492 VOID *
493 EFIAPI
494 UncachedAllocateRuntimePool (
495 IN UINTN AllocationSize
496 )
497 {
498 return UncachedInternalAllocatePool (EfiRuntimeServicesData, AllocationSize);
499 }
500
501 VOID *
502 EFIAPI
503 UncachedAllocateReservedPool (
504 IN UINTN AllocationSize
505 )
506 {
507 return UncachedInternalAllocatePool (EfiReservedMemoryType, AllocationSize);
508 }
509
510 VOID *
511 UncachedInternalAllocateZeroPool (
512 IN EFI_MEMORY_TYPE PoolType,
513 IN UINTN AllocationSize
514 )
515 {
516 VOID *Memory;
517
518 Memory = UncachedInternalAllocatePool (PoolType, AllocationSize);
519 if (Memory != NULL) {
520 Memory = ZeroMem (Memory, AllocationSize);
521 }
522 return Memory;
523 }
524
525 VOID *
526 EFIAPI
527 UncachedAllocateZeroPool (
528 IN UINTN AllocationSize
529 )
530 {
531 return UncachedInternalAllocateZeroPool (EfiBootServicesData, AllocationSize);
532 }
533
534 VOID *
535 EFIAPI
536 UncachedAllocateRuntimeZeroPool (
537 IN UINTN AllocationSize
538 )
539 {
540 return UncachedInternalAllocateZeroPool (EfiRuntimeServicesData, AllocationSize);
541 }
542
543 VOID *
544 EFIAPI
545 UncachedAllocateReservedZeroPool (
546 IN UINTN AllocationSize
547 )
548 {
549 return UncachedInternalAllocateZeroPool (EfiReservedMemoryType, AllocationSize);
550 }
551
552 VOID *
553 UncachedInternalAllocateCopyPool (
554 IN EFI_MEMORY_TYPE PoolType,
555 IN UINTN AllocationSize,
556 IN CONST VOID *Buffer
557 )
558 {
559 VOID *Memory;
560
561 ASSERT (Buffer != NULL);
562 ASSERT (AllocationSize <= (MAX_ADDRESS - (UINTN) Buffer + 1));
563
564 Memory = UncachedInternalAllocatePool (PoolType, AllocationSize);
565 if (Memory != NULL) {
566 Memory = CopyMem (Memory, Buffer, AllocationSize);
567 }
568 return Memory;
569 }
570
571 VOID *
572 EFIAPI
573 UncachedAllocateCopyPool (
574 IN UINTN AllocationSize,
575 IN CONST VOID *Buffer
576 )
577 {
578 return UncachedInternalAllocateCopyPool (EfiBootServicesData, AllocationSize, Buffer);
579 }
580
581 VOID *
582 EFIAPI
583 UncachedAllocateRuntimeCopyPool (
584 IN UINTN AllocationSize,
585 IN CONST VOID *Buffer
586 )
587 {
588 return UncachedInternalAllocateCopyPool (EfiRuntimeServicesData, AllocationSize, Buffer);
589 }
590
591 VOID *
592 EFIAPI
593 UncachedAllocateReservedCopyPool (
594 IN UINTN AllocationSize,
595 IN CONST VOID *Buffer
596 )
597 {
598 return UncachedInternalAllocateCopyPool (EfiReservedMemoryType, AllocationSize, Buffer);
599 }
600
601 VOID
602 EFIAPI
603 UncachedFreePool (
604 IN VOID *Buffer
605 )
606 {
607 UncachedFreeAlignedPool (Buffer);
608 }
609
610 VOID
611 EFIAPI
612 UncachedSafeFreePool (
613 IN VOID *Buffer
614 )
615 {
616 if (Buffer != NULL) {
617 UncachedFreePool (Buffer);
618 Buffer = NULL;
619 }
620 }
621
622 /**
623 The constructor function caches the pointer of DXE Services Table.
624
625 The constructor function caches the pointer of DXE Services Table.
626 It will ASSERT() if that operation fails.
627 It will ASSERT() if the pointer of DXE Services Table is NULL.
628 It will always return EFI_SUCCESS.
629
630 @param ImageHandle The firmware allocated handle for the EFI image.
631 @param SystemTable A pointer to the EFI System Table.
632
633 @retval EFI_SUCCESS The constructor always returns EFI_SUCCESS.
634
635 **/
636 EFI_STATUS
637 EFIAPI
638 DebugUncachedMemoryAllocationLibConstructor (
639 IN EFI_HANDLE ImageHandle,
640 IN EFI_SYSTEM_TABLE *SystemTable
641 )
642 {
643 EFI_STATUS Status;
644
645 Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **)&gDebugUncachedCpu);
646 ASSERT_EFI_ERROR(Status);
647
648 Status = gBS->LocateProtocol (&gVirtualUncachedPagesProtocolGuid, NULL, (VOID **)&gVirtualUncachedPages);
649 ASSERT_EFI_ERROR(Status);
650
651 return Status;
652 }
653
654
655