/** @file UncachedMemoryAllocation lib that uses DXE CPU driver to chnage cachability for a buffer. Copyright (c) 2008 - 2010, Apple Inc. All rights reserved.
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this distribution. The full text of the license may be found at http://opensource.org/licenses/bsd-license.php THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. **/ #include #include #include #include #include #include #include #include #include #include #include VOID * UncachedInternalAllocatePages ( IN EFI_MEMORY_TYPE MemoryType, IN UINTN Pages ); VOID * UncachedInternalAllocateAlignedPages ( IN EFI_MEMORY_TYPE MemoryType, IN UINTN Pages, IN UINTN Alignment ); EFI_CPU_ARCH_PROTOCOL *gDebugUncachedCpu; // // Assume all of memory has the same cache attributes, unless we do our magic // UINT64 gAttributes; typedef struct { VOID *Allocation; UINTN Pages; LIST_ENTRY Link; } FREE_PAGE_NODE; LIST_ENTRY mPageList = INITIALIZE_LIST_HEAD_VARIABLE (mPageList); VOID AddPagesToList ( IN VOID *Allocation, UINTN Pages ) { FREE_PAGE_NODE *NewNode; NewNode = AllocatePool (sizeof (LIST_ENTRY)); if (NewNode == NULL) { ASSERT (FALSE); return; } NewNode->Allocation = Allocation; NewNode->Pages = Pages; InsertTailList (&mPageList, &NewNode->Link); } VOID RemovePagesFromList ( OUT VOID *Allocation, OUT UINTN *Pages ) { LIST_ENTRY *Link; FREE_PAGE_NODE *OldNode; *Pages = 0; for (Link = mPageList.ForwardLink; Link != &mPageList; Link = Link->ForwardLink) { OldNode = BASE_CR (Link, FREE_PAGE_NODE, Link); if (OldNode->Allocation == Allocation) { *Pages = OldNode->Pages; RemoveEntryList (&OldNode->Link); FreePool (OldNode); return; } } return; } /** Converts a cached or uncached address to a physical address suitable for use in SoC registers. @param VirtualAddress The pointer to convert. @return The physical address of the supplied virtual pointer. **/ EFI_PHYSICAL_ADDRESS ConvertToPhysicalAddress ( IN VOID *VirtualAddress ) { return (EFI_PHYSICAL_ADDRESS)(UINTN)VirtualAddress; } VOID * UncachedInternalAllocatePages ( IN EFI_MEMORY_TYPE MemoryType, IN UINTN Pages ) { return UncachedInternalAllocateAlignedPages (MemoryType, Pages, EFI_PAGE_SIZE); } VOID * EFIAPI UncachedAllocatePages ( IN UINTN Pages ) { return UncachedInternalAllocatePages (EfiBootServicesData, Pages); } VOID * EFIAPI UncachedAllocateRuntimePages ( IN UINTN Pages ) { return UncachedInternalAllocatePages (EfiRuntimeServicesData, Pages); } VOID * EFIAPI UncachedAllocateReservedPages ( IN UINTN Pages ) { return UncachedInternalAllocatePages (EfiReservedMemoryType, Pages); } VOID EFIAPI UncachedFreePages ( IN VOID *Buffer, IN UINTN Pages ) { UncachedFreeAlignedPages (Buffer, Pages); return; } VOID * UncachedInternalAllocateAlignedPages ( IN EFI_MEMORY_TYPE MemoryType, IN UINTN Pages, IN UINTN Alignment ) { EFI_STATUS Status; EFI_PHYSICAL_ADDRESS Memory; EFI_PHYSICAL_ADDRESS AlignedMemory; UINTN AlignmentMask; UINTN UnalignedPages; UINTN RealPages; EFI_GCD_MEMORY_SPACE_DESCRIPTOR Descriptor; // // Alignment must be a power of two or zero. // ASSERT ((Alignment & (Alignment - 1)) == 0); if (Pages == 0) { return NULL; } if (Alignment > EFI_PAGE_SIZE) { // // Caculate the total number of pages since alignment is larger than page size. // AlignmentMask = Alignment - 1; RealPages = Pages + EFI_SIZE_TO_PAGES (Alignment); // // Make sure that Pages plus EFI_SIZE_TO_PAGES (Alignment) does not overflow. // ASSERT (RealPages > Pages); Status = gBS->AllocatePages (AllocateAnyPages, MemoryType, RealPages, &Memory); if (EFI_ERROR (Status)) { return NULL; } AlignedMemory = ((UINTN) Memory + AlignmentMask) & ~AlignmentMask; UnalignedPages = EFI_SIZE_TO_PAGES (AlignedMemory - (UINTN) Memory); if (UnalignedPages > 0) { // // Free first unaligned page(s). // Status = gBS->FreePages (Memory, UnalignedPages); ASSERT_EFI_ERROR (Status); } Memory = (EFI_PHYSICAL_ADDRESS) (AlignedMemory + EFI_PAGES_TO_SIZE (Pages)); UnalignedPages = RealPages - Pages - UnalignedPages; if (UnalignedPages > 0) { // // Free last unaligned page(s). // Status = gBS->FreePages (Memory, UnalignedPages); ASSERT_EFI_ERROR (Status); } } else { // // Do not over-allocate pages in this case. // Status = gBS->AllocatePages (AllocateAnyPages, MemoryType, Pages, &Memory); if (EFI_ERROR (Status)) { return NULL; } AlignedMemory = (UINTN) Memory; } Status = gDS->GetMemorySpaceDescriptor (Memory, &Descriptor); if (!EFI_ERROR (Status)) { // We are making an assumption that all of memory has the same default attributes gAttributes = Descriptor.Attributes; } Status = gDS->SetMemorySpaceAttributes (Memory, EFI_PAGES_TO_SIZE (Pages), EFI_MEMORY_UC); ASSERT_EFI_ERROR (Status); return (VOID *)(UINTN)Memory; } VOID EFIAPI UncachedFreeAlignedPages ( IN VOID *Buffer, IN UINTN Pages ) { EFI_STATUS Status; EFI_PHYSICAL_ADDRESS Memory; ASSERT (Pages != 0); Memory = (EFI_PHYSICAL_ADDRESS) (UINTN) Buffer; Status = gDebugUncachedCpu->SetMemoryAttributes (gDebugUncachedCpu, Memory, EFI_PAGES_TO_SIZE (Pages), gAttributes); Status = gBS->FreePages (Memory, Pages); ASSERT_EFI_ERROR (Status); } VOID * UncachedInternalAllocateAlignedPool ( IN EFI_MEMORY_TYPE PoolType, IN UINTN AllocationSize, IN UINTN Alignment ) { VOID *AlignedAddress; // // Alignment must be a power of two or zero. // ASSERT ((Alignment & (Alignment - 1)) == 0); if (Alignment < EFI_PAGE_SIZE) { Alignment = EFI_PAGE_SIZE; } AlignedAddress = UncachedInternalAllocateAlignedPages (PoolType, EFI_SIZE_TO_PAGES (AllocationSize), Alignment); if (AlignedAddress == NULL) { return NULL; } AddPagesToList ((VOID *)(UINTN)AlignedAddress, EFI_SIZE_TO_PAGES (AllocationSize)); return (VOID *) AlignedAddress; } VOID * EFIAPI UncachedAllocateAlignedPool ( IN UINTN AllocationSize, IN UINTN Alignment ) { return UncachedInternalAllocateAlignedPool (EfiBootServicesData, AllocationSize, Alignment); } VOID * EFIAPI UncachedAllocateAlignedRuntimePool ( IN UINTN AllocationSize, IN UINTN Alignment ) { return UncachedInternalAllocateAlignedPool (EfiRuntimeServicesData, AllocationSize, Alignment); } VOID * EFIAPI UncachedAllocateAlignedReservedPool ( IN UINTN AllocationSize, IN UINTN Alignment ) { return UncachedInternalAllocateAlignedPool (EfiReservedMemoryType, AllocationSize, Alignment); } VOID * UncachedInternalAllocateAlignedZeroPool ( IN EFI_MEMORY_TYPE PoolType, IN UINTN AllocationSize, IN UINTN Alignment ) { VOID *Memory; Memory = UncachedInternalAllocateAlignedPool (PoolType, AllocationSize, Alignment); if (Memory != NULL) { Memory = ZeroMem (Memory, AllocationSize); } return Memory; } VOID * EFIAPI UncachedAllocateAlignedZeroPool ( IN UINTN AllocationSize, IN UINTN Alignment ) { return UncachedInternalAllocateAlignedZeroPool (EfiBootServicesData, AllocationSize, Alignment); } VOID * EFIAPI UncachedAllocateAlignedRuntimeZeroPool ( IN UINTN AllocationSize, IN UINTN Alignment ) { return UncachedInternalAllocateAlignedZeroPool (EfiRuntimeServicesData, AllocationSize, Alignment); } VOID * EFIAPI UncachedAllocateAlignedReservedZeroPool ( IN UINTN AllocationSize, IN UINTN Alignment ) { return UncachedInternalAllocateAlignedZeroPool (EfiReservedMemoryType, AllocationSize, Alignment); } VOID * UncachedInternalAllocateAlignedCopyPool ( IN EFI_MEMORY_TYPE PoolType, IN UINTN AllocationSize, IN CONST VOID *Buffer, IN UINTN Alignment ) { VOID *Memory; ASSERT (Buffer != NULL); ASSERT (AllocationSize <= (MAX_ADDRESS - (UINTN) Buffer + 1)); Memory = UncachedInternalAllocateAlignedPool (PoolType, AllocationSize, Alignment); if (Memory != NULL) { Memory = CopyMem (Memory, Buffer, AllocationSize); } return Memory; } VOID * EFIAPI UncachedAllocateAlignedCopyPool ( IN UINTN AllocationSize, IN CONST VOID *Buffer, IN UINTN Alignment ) { return UncachedInternalAllocateAlignedCopyPool (EfiBootServicesData, AllocationSize, Buffer, Alignment); } VOID * EFIAPI UncachedAllocateAlignedRuntimeCopyPool ( IN UINTN AllocationSize, IN CONST VOID *Buffer, IN UINTN Alignment ) { return UncachedInternalAllocateAlignedCopyPool (EfiRuntimeServicesData, AllocationSize, Buffer, Alignment); } VOID * EFIAPI UncachedAllocateAlignedReservedCopyPool ( IN UINTN AllocationSize, IN CONST VOID *Buffer, IN UINTN Alignment ) { return UncachedInternalAllocateAlignedCopyPool (EfiReservedMemoryType, AllocationSize, Buffer, Alignment); } VOID EFIAPI UncachedFreeAlignedPool ( IN VOID *Allocation ) { UINTN Pages; RemovePagesFromList (Allocation, &Pages); UncachedFreePages (Allocation, Pages); } VOID * UncachedInternalAllocatePool ( IN EFI_MEMORY_TYPE MemoryType, IN UINTN AllocationSize ) { UINTN CacheLineLength = ArmDataCacheLineLength (); return UncachedInternalAllocateAlignedPool (MemoryType, AllocationSize, CacheLineLength); } VOID * EFIAPI UncachedAllocatePool ( IN UINTN AllocationSize ) { return UncachedInternalAllocatePool (EfiBootServicesData, AllocationSize); } VOID * EFIAPI UncachedAllocateRuntimePool ( IN UINTN AllocationSize ) { return UncachedInternalAllocatePool (EfiRuntimeServicesData, AllocationSize); } VOID * EFIAPI UncachedAllocateReservedPool ( IN UINTN AllocationSize ) { return UncachedInternalAllocatePool (EfiReservedMemoryType, AllocationSize); } VOID * UncachedInternalAllocateZeroPool ( IN EFI_MEMORY_TYPE PoolType, IN UINTN AllocationSize ) { VOID *Memory; Memory = UncachedInternalAllocatePool (PoolType, AllocationSize); if (Memory != NULL) { Memory = ZeroMem (Memory, AllocationSize); } return Memory; } VOID * EFIAPI UncachedAllocateZeroPool ( IN UINTN AllocationSize ) { return UncachedInternalAllocateZeroPool (EfiBootServicesData, AllocationSize); } VOID * EFIAPI UncachedAllocateRuntimeZeroPool ( IN UINTN AllocationSize ) { return UncachedInternalAllocateZeroPool (EfiRuntimeServicesData, AllocationSize); } VOID * EFIAPI UncachedAllocateReservedZeroPool ( IN UINTN AllocationSize ) { return UncachedInternalAllocateZeroPool (EfiReservedMemoryType, AllocationSize); } VOID * UncachedInternalAllocateCopyPool ( IN EFI_MEMORY_TYPE PoolType, IN UINTN AllocationSize, IN CONST VOID *Buffer ) { VOID *Memory; ASSERT (Buffer != NULL); ASSERT (AllocationSize <= (MAX_ADDRESS - (UINTN) Buffer + 1)); Memory = UncachedInternalAllocatePool (PoolType, AllocationSize); if (Memory != NULL) { Memory = CopyMem (Memory, Buffer, AllocationSize); } return Memory; } VOID * EFIAPI UncachedAllocateCopyPool ( IN UINTN AllocationSize, IN CONST VOID *Buffer ) { return UncachedInternalAllocateCopyPool (EfiBootServicesData, AllocationSize, Buffer); } VOID * EFIAPI UncachedAllocateRuntimeCopyPool ( IN UINTN AllocationSize, IN CONST VOID *Buffer ) { return UncachedInternalAllocateCopyPool (EfiRuntimeServicesData, AllocationSize, Buffer); } VOID * EFIAPI UncachedAllocateReservedCopyPool ( IN UINTN AllocationSize, IN CONST VOID *Buffer ) { return UncachedInternalAllocateCopyPool (EfiReservedMemoryType, AllocationSize, Buffer); } VOID EFIAPI UncachedFreePool ( IN VOID *Buffer ) { UncachedFreeAlignedPool (Buffer); } VOID EFIAPI UncachedSafeFreePool ( IN VOID *Buffer ) { if (Buffer != NULL) { UncachedFreePool (Buffer); Buffer = NULL; } } /** The constructor function caches the pointer of DXE Services Table. The constructor function caches the pointer of DXE Services Table. It will ASSERT() if that operation fails. It will ASSERT() if the pointer of DXE Services Table is NULL. It will always return EFI_SUCCESS. @param ImageHandle The firmware allocated handle for the EFI image. @param SystemTable A pointer to the EFI System Table. @retval EFI_SUCCESS The constructor always returns EFI_SUCCESS. **/ EFI_STATUS EFIAPI UncachedMemoryAllocationLibConstructor ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { EFI_STATUS Status; Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **)&gDebugUncachedCpu); ASSERT_EFI_ERROR(Status); return Status; }