--- /dev/null
+/** @file\r
+ Debug version of the UncachedMemoryAllocation lib that uses the VirtualUncachedPages\r
+ protocol, produced by the DXE CPU driver, to produce debuggable uncached memory buffers.\r
+ \r
+ The DMA rules for EFI contain the concept of a PCI (DMA master) address for memory and\r
+ a CPU (C code) address for the memory buffer that don't have to be the same. There seem to\r
+ be common errors out there with folks mixing up the two addresses. This library causes\r
+ the PCI (DMA master) address to not be mapped into system memory so if the CPU (C code)\r
+ uses the wrong pointer it will generate a page fault. The CPU (C code) version of the buffer\r
+ has a virtual address that does not match the physical address. The virtual address has\r
+ PcdArmUncachedMemoryMask ored into the physical address.\r
+\r
+ Copyright (c) 2008-2010, Apple Inc. All rights reserved.\r
+ \r
+ All rights reserved. This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include <Library/BaseLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/UncachedMemoryAllocationLib.h>\r
+#include <Library/PcdLib.h>\r
+#include <Library/ArmLib.h>\r
+\r
+#include <Protocol/Cpu.h>\r
+#include <Protocol/VirtualUncachedPages.h>\r
+\r
+VOID *\r
+UncachedInternalAllocatePages (\r
+ IN EFI_MEMORY_TYPE MemoryType, \r
+ IN UINTN Pages\r
+ );\r
+\r
+VOID *\r
+UncachedInternalAllocateAlignedPages (\r
+ IN EFI_MEMORY_TYPE MemoryType, \r
+ IN UINTN Pages,\r
+ IN UINTN Alignment\r
+ );\r
+ \r
+ \r
+\r
+EFI_CPU_ARCH_PROTOCOL *gCpu;\r
+VIRTUAL_UNCACHED_PAGES_PROTOCOL *gVirtualUncachedPages;\r
+\r
+//\r
+// Assume all of memory has the same cache attributes, unless we do our magic\r
+//\r
+UINT64 gAttributes;\r
+\r
+typedef struct {\r
+ VOID *Buffer;\r
+ VOID *Allocation;\r
+ UINTN Pages;\r
+ LIST_ENTRY Link;\r
+} FREE_PAGE_NODE;\r
+\r
+LIST_ENTRY mPageList = INITIALIZE_LIST_HEAD_VARIABLE (mPageList);\r
+\r
+VOID\r
+AddPagesToList (\r
+ IN VOID *Buffer,\r
+ IN VOID *Allocation,\r
+ UINTN Pages\r
+ )\r
+{\r
+ FREE_PAGE_NODE *NewNode;\r
+ \r
+ NewNode = AllocatePool (sizeof (LIST_ENTRY));\r
+ if (NewNode == NULL) {\r
+ ASSERT (FALSE);\r
+ return;\r
+ }\r
+ \r
+ NewNode->Buffer = Buffer;\r
+ NewNode->Allocation = Allocation;\r
+ \r
+ InsertTailList (&mPageList, &NewNode->Link);\r
+}\r
+\r
+\r
+VOID\r
+RemovePagesFromList (\r
+ IN VOID *Buffer,\r
+ OUT VOID **Allocation,\r
+ OUT UINTN *Pages\r
+ )\r
+{\r
+ LIST_ENTRY *Link;\r
+ FREE_PAGE_NODE *OldNode;\r
+\r
+ *Allocation = NULL;\r
+ *Pages = 0;\r
+ \r
+ for (Link = mPageList.ForwardLink; Link != &mPageList; Link = Link->ForwardLink) {\r
+ OldNode = BASE_CR (Link, FREE_PAGE_NODE, Link);\r
+ if (OldNode->Buffer == Buffer) {\r
+ *Allocation = OldNode->Allocation;\r
+ *Pages = OldNode->Pages;\r
+ \r
+ RemoveEntryList (&OldNode->Link);\r
+ FreePool (OldNode);\r
+ return;\r
+ }\r
+ }\r
+\r
+ return;\r
+}\r
+\r
+\r
+\r
+EFI_PHYSICAL_ADDRESS\r
+ConvertToPhysicalAddress (\r
+ IN VOID *VirtualAddress\r
+ )\r
+{\r
+ UINTN UncachedMemoryMask = (UINTN)PcdGet64 (PcdArmUncachedMemoryMask);\r
+ UINTN PhysicalAddress;\r
+ \r
+ PhysicalAddress = (UINTN)VirtualAddress & ~UncachedMemoryMask;\r
+ \r
+ return (EFI_PHYSICAL_ADDRESS)PhysicalAddress;\r
+}\r
+\r
+\r
+VOID *\r
+ConvertToUncachedAddress (\r
+ IN VOID *Address\r
+ )\r
+{\r
+ UINTN UncachedMemoryMask = (UINTN)PcdGet64 (PcdArmUncachedMemoryMask);\r
+ UINTN UncachedAddress;\r
+ \r
+ UncachedAddress = (UINTN)Address | UncachedMemoryMask;\r
+ \r
+ return (VOID *)UncachedAddress;\r
+}\r
+\r
+\r
+\r
+VOID *\r
+UncachedInternalAllocatePages (\r
+ IN EFI_MEMORY_TYPE MemoryType, \r
+ IN UINTN Pages\r
+ )\r
+{\r
+ return UncachedInternalAllocateAlignedPages (MemoryType, Pages, EFI_PAGE_SIZE);\r
+}\r
+\r
+\r
+VOID *\r
+EFIAPI\r
+UncachedAllocatePages (\r
+ IN UINTN Pages\r
+ )\r
+{\r
+ return UncachedInternalAllocatePages (EfiBootServicesData, Pages);\r
+}\r
+\r
+VOID *\r
+EFIAPI\r
+UncachedAllocateRuntimePages (\r
+ IN UINTN Pages\r
+ )\r
+{\r
+ return UncachedInternalAllocatePages (EfiRuntimeServicesData, Pages);\r
+}\r
+\r
+VOID *\r
+EFIAPI\r
+UncachedAllocateReservedPages (\r
+ IN UINTN Pages\r
+ )\r
+{\r
+ return UncachedInternalAllocatePages (EfiReservedMemoryType, Pages);\r
+}\r
+\r
+\r
+\r
+VOID\r
+EFIAPI\r
+UncachedFreePages (\r
+ IN VOID *Buffer,\r
+ IN UINTN Pages\r
+ )\r
+{\r
+ UncachedFreeAlignedPages (Buffer, Pages);\r
+ return;\r
+}\r
+\r
+\r
+VOID *\r
+UncachedInternalAllocateAlignedPages (\r
+ IN EFI_MEMORY_TYPE MemoryType, \r
+ IN UINTN Pages,\r
+ IN UINTN Alignment\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_PHYSICAL_ADDRESS Memory;\r
+ EFI_PHYSICAL_ADDRESS AlignedMemory;\r
+ UINTN AlignmentMask;\r
+ UINTN UnalignedPages;\r
+ UINTN RealPages;\r
+\r
+ //\r
+ // Alignment must be a power of two or zero.\r
+ //\r
+ ASSERT ((Alignment & (Alignment - 1)) == 0);\r
+ \r
+ if (Pages == 0) {\r
+ return NULL;\r
+ }\r
+ if (Alignment > EFI_PAGE_SIZE) {\r
+ //\r
+ // Caculate the total number of pages since alignment is larger than page size.\r
+ //\r
+ AlignmentMask = Alignment - 1;\r
+ RealPages = Pages + EFI_SIZE_TO_PAGES (Alignment);\r
+ //\r
+ // Make sure that Pages plus EFI_SIZE_TO_PAGES (Alignment) does not overflow.\r
+ //\r
+ ASSERT (RealPages > Pages);\r
+ \r
+ Status = gBS->AllocatePages (AllocateAnyPages, MemoryType, RealPages, &Memory);\r
+ if (EFI_ERROR (Status)) {\r
+ return NULL;\r
+ }\r
+ AlignedMemory = ((UINTN) Memory + AlignmentMask) & ~AlignmentMask;\r
+ UnalignedPages = EFI_SIZE_TO_PAGES (AlignedMemory - (UINTN) Memory);\r
+ if (UnalignedPages > 0) {\r
+ //\r
+ // Free first unaligned page(s).\r
+ //\r
+ Status = gBS->FreePages (Memory, UnalignedPages);\r
+ ASSERT_EFI_ERROR (Status);\r
+ }\r
+ Memory = (EFI_PHYSICAL_ADDRESS) (AlignedMemory + EFI_PAGES_TO_SIZE (Pages));\r
+ UnalignedPages = RealPages - Pages - UnalignedPages;\r
+ if (UnalignedPages > 0) {\r
+ //\r
+ // Free last unaligned page(s).\r
+ //\r
+ Status = gBS->FreePages (Memory, UnalignedPages);\r
+ ASSERT_EFI_ERROR (Status);\r
+ }\r
+ } else {\r
+ //\r
+ // Do not over-allocate pages in this case.\r
+ //\r
+ Status = gBS->AllocatePages (AllocateAnyPages, MemoryType, Pages, &Memory);\r
+ if (EFI_ERROR (Status)) {\r
+ return NULL;\r
+ }\r
+ AlignedMemory = (UINTN) Memory;\r
+ }\r
+ \r
+ Status = gVirtualUncachedPages->ConvertPages (gVirtualUncachedPages, AlignedMemory, Pages * EFI_PAGE_SIZE, PcdGet64 (PcdArmUncachedMemoryMask), &gAttributes);\r
+ if (EFI_ERROR (Status)) {\r
+ return NULL;\r
+ }\r
+ \r
+ AlignedMemory = (EFI_PHYSICAL_ADDRESS)(UINTN)ConvertToUncachedAddress ((VOID *)(UINTN)AlignedMemory);\r
+ \r
+ return (VOID *)(UINTN)AlignedMemory;\r
+}\r
+\r
+\r
+VOID\r
+EFIAPI\r
+UncachedFreeAlignedPages (\r
+ IN VOID *Buffer,\r
+ IN UINTN Pages\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_PHYSICAL_ADDRESS Memory; \r
+\r
+ ASSERT (Pages != 0);\r
+ \r
+ Memory = ConvertToPhysicalAddress (Buffer);\r
+ \r
+ Status = gVirtualUncachedPages->RevertPages (gVirtualUncachedPages, Memory, Pages * EFI_PAGE_SIZE, PcdGet64 (PcdArmUncachedMemoryMask), gAttributes);\r
+\r
+ \r
+ Status = gBS->FreePages ((EFI_PHYSICAL_ADDRESS) (UINTN) Buffer, Pages);\r
+ ASSERT_EFI_ERROR (Status);\r
+}\r
+\r
+\r
+\r
+\r
+VOID *\r
+UncachedInternalAllocateAlignedPool (\r
+ IN EFI_MEMORY_TYPE PoolType,\r
+ IN UINTN AllocationSize,\r
+ IN UINTN Alignment\r
+ )\r
+{\r
+ VOID *AlignedAddress;\r
+ \r
+ //\r
+ // Alignment must be a power of two or zero.\r
+ //\r
+ ASSERT ((Alignment & (Alignment - 1)) == 0);\r
+\r
+ if (Alignment < EFI_PAGE_SIZE) {\r
+ Alignment = EFI_PAGE_SIZE;\r
+ }\r
+ \r
+ AlignedAddress = UncachedInternalAllocateAlignedPages (PoolType, EFI_SIZE_TO_PAGES (AllocationSize), Alignment);\r
+ if (AlignedAddress == NULL) {\r
+ return NULL;\r
+ }\r
+\r
+ AddPagesToList ((VOID *)(UINTN)ConvertToPhysicalAddress (AlignedAddress), (VOID *)(UINTN)AlignedAddress, EFI_SIZE_TO_PAGES (AllocationSize));\r
+\r
+ return (VOID *) AlignedAddress;\r
+}\r
+\r
+VOID *\r
+EFIAPI\r
+UncachedAllocateAlignedPool (\r
+ IN UINTN AllocationSize,\r
+ IN UINTN Alignment\r
+ )\r
+{\r
+ return UncachedInternalAllocateAlignedPool (EfiBootServicesData, AllocationSize, Alignment);\r
+}\r
+\r
+VOID *\r
+EFIAPI\r
+UncachedAllocateAlignedRuntimePool (\r
+ IN UINTN AllocationSize,\r
+ IN UINTN Alignment\r
+ )\r
+{\r
+ return UncachedInternalAllocateAlignedPool (EfiRuntimeServicesData, AllocationSize, Alignment);\r
+}\r
+\r
+VOID *\r
+EFIAPI\r
+UncachedAllocateAlignedReservedPool (\r
+ IN UINTN AllocationSize,\r
+ IN UINTN Alignment\r
+ )\r
+{\r
+ return UncachedInternalAllocateAlignedPool (EfiReservedMemoryType, AllocationSize, Alignment);\r
+}\r
+\r
+VOID *\r
+UncachedInternalAllocateAlignedZeroPool (\r
+ IN EFI_MEMORY_TYPE PoolType,\r
+ IN UINTN AllocationSize,\r
+ IN UINTN Alignment\r
+ )\r
+{\r
+ VOID *Memory;\r
+ Memory = UncachedInternalAllocateAlignedPool (PoolType, AllocationSize, Alignment);\r
+ if (Memory != NULL) {\r
+ Memory = ZeroMem (Memory, AllocationSize);\r
+ }\r
+ return Memory;\r
+}\r
+\r
+VOID *\r
+EFIAPI\r
+UncachedAllocateAlignedZeroPool (\r
+ IN UINTN AllocationSize,\r
+ IN UINTN Alignment\r
+ )\r
+{\r
+ return UncachedInternalAllocateAlignedZeroPool (EfiBootServicesData, AllocationSize, Alignment);\r
+}\r
+\r
+VOID *\r
+EFIAPI\r
+UncachedAllocateAlignedRuntimeZeroPool (\r
+ IN UINTN AllocationSize,\r
+ IN UINTN Alignment\r
+ )\r
+{\r
+ return UncachedInternalAllocateAlignedZeroPool (EfiRuntimeServicesData, AllocationSize, Alignment);\r
+}\r
+\r
+VOID *\r
+EFIAPI\r
+UncachedAllocateAlignedReservedZeroPool (\r
+ IN UINTN AllocationSize,\r
+ IN UINTN Alignment\r
+ )\r
+{\r
+ return UncachedInternalAllocateAlignedZeroPool (EfiReservedMemoryType, AllocationSize, Alignment);\r
+}\r
+\r
+VOID *\r
+UncachedInternalAllocateAlignedCopyPool (\r
+ IN EFI_MEMORY_TYPE PoolType,\r
+ IN UINTN AllocationSize,\r
+ IN CONST VOID *Buffer,\r
+ IN UINTN Alignment\r
+ )\r
+{\r
+ VOID *Memory;\r
+ \r
+ ASSERT (Buffer != NULL);\r
+ ASSERT (AllocationSize <= (MAX_ADDRESS - (UINTN) Buffer + 1));\r
+\r
+ Memory = UncachedInternalAllocateAlignedPool (PoolType, AllocationSize, Alignment);\r
+ if (Memory != NULL) {\r
+ Memory = CopyMem (Memory, Buffer, AllocationSize);\r
+ }\r
+ return Memory;\r
+}\r
+\r
+VOID *\r
+EFIAPI\r
+UncachedAllocateAlignedCopyPool (\r
+ IN UINTN AllocationSize,\r
+ IN CONST VOID *Buffer,\r
+ IN UINTN Alignment\r
+ )\r
+{\r
+ return UncachedInternalAllocateAlignedCopyPool (EfiBootServicesData, AllocationSize, Buffer, Alignment);\r
+}\r
+\r
+VOID *\r
+EFIAPI\r
+UncachedAllocateAlignedRuntimeCopyPool (\r
+ IN UINTN AllocationSize,\r
+ IN CONST VOID *Buffer,\r
+ IN UINTN Alignment\r
+ )\r
+{\r
+ return UncachedInternalAllocateAlignedCopyPool (EfiRuntimeServicesData, AllocationSize, Buffer, Alignment);\r
+}\r
+\r
+VOID *\r
+EFIAPI\r
+UncachedAllocateAlignedReservedCopyPool (\r
+ IN UINTN AllocationSize,\r
+ IN CONST VOID *Buffer,\r
+ IN UINTN Alignment\r
+ )\r
+{\r
+ return UncachedInternalAllocateAlignedCopyPool (EfiReservedMemoryType, AllocationSize, Buffer, Alignment);\r
+}\r
+\r
+VOID\r
+EFIAPI\r
+UncachedFreeAlignedPool (\r
+ IN VOID *Buffer\r
+ )\r
+{\r
+ VOID *Allocation;\r
+ UINTN Pages;\r
+ \r
+ RemovePagesFromList (Buffer, &Allocation, &Pages);\r
+\r
+ UncachedFreePages (Allocation, Pages);\r
+}\r
+\r
+VOID *\r
+UncachedInternalAllocatePool (\r
+ IN EFI_MEMORY_TYPE MemoryType, \r
+ IN UINTN AllocationSize\r
+ )\r
+{\r
+ UINTN CacheLineLength = ArmDataCacheLineLength ();\r
+ return UncachedInternalAllocateAlignedPool (MemoryType, AllocationSize, CacheLineLength);\r
+}\r
+\r
+VOID *\r
+EFIAPI\r
+UncachedAllocatePool (\r
+ IN UINTN AllocationSize\r
+ )\r
+{\r
+ return UncachedInternalAllocatePool (EfiBootServicesData, AllocationSize);\r
+}\r
+\r
+VOID *\r
+EFIAPI\r
+UncachedAllocateRuntimePool (\r
+ IN UINTN AllocationSize\r
+ )\r
+{\r
+ return UncachedInternalAllocatePool (EfiRuntimeServicesData, AllocationSize);\r
+}\r
+\r
+VOID *\r
+EFIAPI\r
+UncachedAllocateReservedPool (\r
+ IN UINTN AllocationSize\r
+ )\r
+{\r
+ return UncachedInternalAllocatePool (EfiReservedMemoryType, AllocationSize);\r
+}\r
+\r
+VOID *\r
+UncachedInternalAllocateZeroPool (\r
+ IN EFI_MEMORY_TYPE PoolType, \r
+ IN UINTN AllocationSize\r
+ ) \r
+{\r
+ VOID *Memory;\r
+\r
+ Memory = UncachedInternalAllocatePool (PoolType, AllocationSize);\r
+ if (Memory != NULL) {\r
+ Memory = ZeroMem (Memory, AllocationSize);\r
+ }\r
+ return Memory;\r
+}\r
+\r
+VOID *\r
+EFIAPI\r
+UncachedAllocateZeroPool (\r
+ IN UINTN AllocationSize\r
+ )\r
+{\r
+ return UncachedInternalAllocateZeroPool (EfiBootServicesData, AllocationSize);\r
+}\r
+\r
+VOID *\r
+EFIAPI\r
+UncachedAllocateRuntimeZeroPool (\r
+ IN UINTN AllocationSize\r
+ )\r
+{\r
+ return UncachedInternalAllocateZeroPool (EfiRuntimeServicesData, AllocationSize);\r
+}\r
+\r
+VOID *\r
+EFIAPI\r
+UncachedAllocateReservedZeroPool (\r
+ IN UINTN AllocationSize\r
+ )\r
+{\r
+ return UncachedInternalAllocateZeroPool (EfiReservedMemoryType, AllocationSize);\r
+}\r
+\r
+VOID *\r
+UncachedInternalAllocateCopyPool (\r
+ IN EFI_MEMORY_TYPE PoolType, \r
+ IN UINTN AllocationSize,\r
+ IN CONST VOID *Buffer\r
+ ) \r
+{\r
+ VOID *Memory;\r
+\r
+ ASSERT (Buffer != NULL);\r
+ ASSERT (AllocationSize <= (MAX_ADDRESS - (UINTN) Buffer + 1));\r
+\r
+ Memory = UncachedInternalAllocatePool (PoolType, AllocationSize);\r
+ if (Memory != NULL) {\r
+ Memory = CopyMem (Memory, Buffer, AllocationSize);\r
+ }\r
+ return Memory;\r
+} \r
+\r
+VOID *\r
+EFIAPI\r
+UncachedAllocateCopyPool (\r
+ IN UINTN AllocationSize,\r
+ IN CONST VOID *Buffer\r
+ )\r
+{\r
+ return UncachedInternalAllocateCopyPool (EfiBootServicesData, AllocationSize, Buffer);\r
+}\r
+\r
+VOID *\r
+EFIAPI\r
+UncachedAllocateRuntimeCopyPool (\r
+ IN UINTN AllocationSize,\r
+ IN CONST VOID *Buffer\r
+ )\r
+{\r
+ return UncachedInternalAllocateCopyPool (EfiRuntimeServicesData, AllocationSize, Buffer);\r
+}\r
+\r
+VOID *\r
+EFIAPI\r
+UncachedAllocateReservedCopyPool (\r
+ IN UINTN AllocationSize,\r
+ IN CONST VOID *Buffer\r
+ )\r
+{\r
+ return UncachedInternalAllocateCopyPool (EfiReservedMemoryType, AllocationSize, Buffer);\r
+}\r
+\r
+VOID\r
+EFIAPI\r
+UncachedFreePool (\r
+ IN VOID *Buffer\r
+ )\r
+{\r
+ UncachedFreeAlignedPool (Buffer);\r
+}\r
+\r
+VOID\r
+EFIAPI\r
+UncachedSafeFreePool (\r
+ IN VOID *Buffer\r
+ )\r
+{\r
+ if (Buffer != NULL) {\r
+ UncachedFreePool (Buffer);\r
+ Buffer = NULL;\r
+ }\r
+}\r
+\r
+/**\r
+ The constructor function caches the pointer of DXE Services Table.\r
+\r
+ The constructor function caches the pointer of DXE Services Table.\r
+ It will ASSERT() if that operation fails.\r
+ It will ASSERT() if the pointer of DXE Services Table is NULL.\r
+ It will always return EFI_SUCCESS.\r
+\r
+ @param ImageHandle The firmware allocated handle for the EFI image.\r
+ @param SystemTable A pointer to the EFI System Table.\r
+\r
+ @retval EFI_SUCCESS The constructor always returns EFI_SUCCESS.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+DebugUncachedMemoryAllocationLibConstructor (\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_SYSTEM_TABLE *SystemTable\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ \r
+ Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **)&gCpu);\r
+ ASSERT_EFI_ERROR(Status);\r
+\r
+ Status = gBS->LocateProtocol (&gVirtualUncachedPagesProtocolGuid, NULL, (VOID **)&gVirtualUncachedPages);\r
+ ASSERT_EFI_ERROR(Status);\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+\r