a buffer.\r
\r
Copyright (c) 2008 - 2010, Apple Inc. All rights reserved.<BR>\r
+ Copyright (c) 2014, AMR Ltd. All rights reserved.<BR>\r
\r
This program and the accompanying materials\r
are licensed and made available under the terms and conditions of the BSD License\r
UINT64 gAttributes;\r
\r
typedef struct {\r
- VOID *Allocation;\r
- UINTN Pages;\r
- LIST_ENTRY Link;\r
+ EFI_PHYSICAL_ADDRESS Base;\r
+ VOID *Allocation;\r
+ UINTN Pages;\r
+ EFI_MEMORY_TYPE MemoryType;\r
+ BOOLEAN Allocated;\r
+ LIST_ENTRY Link;\r
} FREE_PAGE_NODE;\r
\r
-LIST_ENTRY mPageList = INITIALIZE_LIST_HEAD_VARIABLE (mPageList);\r
+STATIC LIST_ENTRY mPageList = INITIALIZE_LIST_HEAD_VARIABLE (mPageList);\r
+// Track the size of the non-allocated buffer in the linked-list\r
+STATIC UINTN mFreedBufferSize = 0;\r
\r
-VOID\r
-AddPagesToList (\r
- IN VOID *Allocation,\r
- UINTN Pages\r
+/**\r
+ * This function firstly checks if the requested allocation can fit into one\r
+ * of the previously allocated buffer.\r
+ * If the requested allocation does not fit in the existing pool then\r
+ * the function makes a new allocation.\r
+ *\r
+ * @param MemoryType Type of memory requested for the new allocation\r
+ * @param Pages Number of requested page\r
+ * @param Alignment Required alignment\r
+ * @param Allocation Address of the newly allocated buffer\r
+ *\r
+ * @return EFI_SUCCESS If the function manage to allocate a buffer\r
+ * @return !EFI_SUCCESS If the function did not manage to allocate a buffer\r
+ */\r
+STATIC\r
+EFI_STATUS\r
+AllocatePagesFromList (\r
+ IN EFI_MEMORY_TYPE MemoryType,\r
+ IN UINTN Pages,\r
+ IN UINTN Alignment,\r
+ OUT VOID **Allocation\r
)\r
{\r
+ EFI_STATUS Status;\r
+ LIST_ENTRY *Link;\r
+ FREE_PAGE_NODE *Node;\r
FREE_PAGE_NODE *NewNode;\r
+ UINTN AlignmentMask;\r
+ EFI_PHYSICAL_ADDRESS Memory;\r
+ EFI_GCD_MEMORY_SPACE_DESCRIPTOR Descriptor;\r
+\r
+ // Alignment must be a power of two or zero.\r
+ ASSERT ((Alignment & (Alignment - 1)) == 0);\r
+\r
+ //\r
+ // Look in our list for the smallest page that could satisfy the new allocation\r
+ //\r
+ NewNode = NULL;\r
+ for (Link = mPageList.ForwardLink; Link != &mPageList; Link = Link->ForwardLink) {\r
+ Node = BASE_CR (Link, FREE_PAGE_NODE, Link);\r
+ if ((Node->Allocated == FALSE) && (Node->MemoryType == MemoryType)) {\r
+ // We have a node that fits our requirements\r
+ if (((UINTN)Node->Base & (Alignment - 1)) == 0) {\r
+ // We found a page that matches the page size\r
+ if (Node->Pages == Pages) {\r
+ Node->Allocated = TRUE;\r
+ Node->Allocation = (VOID*)(UINTN)Node->Base;\r
+ *Allocation = Node->Allocation;\r
+\r
+ // Update the size of the freed buffer\r
+ mFreedBufferSize -= Pages * EFI_PAGE_SIZE;\r
+ return EFI_SUCCESS;\r
+ } else if (Node->Pages > Pages) {\r
+ if (NewNode == NULL) {\r
+ // It is the first node that could contain our new allocation\r
+ NewNode = Node;\r
+ } else if (NewNode->Pages > Node->Pages) {\r
+ // This node offers a smaller number of page.\r
+ NewNode = Node;\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ // Check if we have found a node that could contain our new allocation\r
+ if (NewNode != NULL) {\r
+ NewNode->Allocated = TRUE;\r
+ Node->Allocation = (VOID*)(UINTN)Node->Base;\r
+ *Allocation = Node->Allocation;\r
+ return EFI_SUCCESS;\r
+ }\r
\r
- NewNode = AllocatePool (sizeof (LIST_ENTRY));\r
+ //\r
+ // Otherwise, we need to allocate a new buffer\r
+ //\r
+\r
+ // We do not want to over-allocate in case the alignment requirement does not\r
+ // require extra pages\r
+ if (Alignment > EFI_PAGE_SIZE) {\r
+ AlignmentMask = Alignment - 1;\r
+ Pages += EFI_SIZE_TO_PAGES (Alignment);\r
+ } else {\r
+ AlignmentMask = 0;\r
+ }\r
+\r
+ Status = gBS->AllocatePages (AllocateAnyPages, MemoryType, Pages, &Memory);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Status = gDS->GetMemorySpaceDescriptor (Memory, &Descriptor);\r
+ if (!EFI_ERROR (Status)) {\r
+ // We are making an assumption that all of memory has the same default attributes\r
+ gAttributes = Descriptor.Attributes;\r
+ } else {\r
+ gBS->FreePages (Memory, Pages);\r
+ return Status;\r
+ }\r
+\r
+ Status = gDS->SetMemorySpaceAttributes (Memory, EFI_PAGES_TO_SIZE (Pages), EFI_MEMORY_WC);\r
+ if (EFI_ERROR (Status)) {\r
+ gBS->FreePages (Memory, Pages);\r
+ return Status;\r
+ }\r
+\r
+ NewNode = AllocatePool (sizeof (FREE_PAGE_NODE));\r
if (NewNode == NULL) {\r
ASSERT (FALSE);\r
- return;\r
+ gBS->FreePages (Memory, Pages);\r
+ return EFI_OUT_OF_RESOURCES;\r
}\r
\r
- NewNode->Allocation = Allocation;\r
+ NewNode->Base = Memory;\r
+ NewNode->Allocation = (VOID*)(((UINTN)Memory + AlignmentMask) & ~AlignmentMask);\r
NewNode->Pages = Pages;\r
+ NewNode->Allocated = TRUE;\r
+ NewNode->MemoryType = MemoryType;\r
\r
InsertTailList (&mPageList, &NewNode->Link);\r
+\r
+ *Allocation = NewNode->Allocation;\r
+ return EFI_SUCCESS;\r
}\r
\r
+/**\r
+ * Free the memory allocation\r
+ *\r
+ * This function will actually try to find the allocation in the linked list.\r
+ * And it will then mark the entry as freed.\r
+ *\r
+ * @param Allocation Base address of the buffer to free\r
+ *\r
+ * @return EFI_SUCCESS The allocation has been freed\r
+ * @return EFI_NOT_FOUND The allocation was not found in the pool.\r
+ * @return EFI_INVALID_PARAMETER If Allocation is NULL\r
+ *\r
+ */\r
+STATIC\r
+EFI_STATUS\r
+FreePagesFromList (\r
+ IN VOID *Allocation\r
+ )\r
+{\r
+ LIST_ENTRY *Link;\r
+ FREE_PAGE_NODE *Node;\r
+\r
+ if (Allocation == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
\r
-VOID\r
-RemovePagesFromList (\r
- OUT VOID *Allocation,\r
- OUT UINTN *Pages\r
+ for (Link = mPageList.ForwardLink; Link != &mPageList; Link = Link->ForwardLink) {\r
+ Node = BASE_CR (Link, FREE_PAGE_NODE, Link);\r
+ if ((UINTN)Node->Allocation == (UINTN)Allocation) {\r
+ Node->Allocated = FALSE;\r
+\r
+ // Update the size of the freed buffer\r
+ mFreedBufferSize += Node->Pages * EFI_PAGE_SIZE;\r
+\r
+ // If the size of the non-allocated reaches the threshold we raise a warning.\r
+ // It might be an expected behaviour in some cases.\r
+ // We might device to free some of these buffers later on.\r
+ if (mFreedBufferSize > PcdGet64 (PcdArmFreeUncachedMemorySizeThreshold)) {\r
+ DEBUG ((EFI_D_WARN, "Warning: The list of non-allocated buffer has reach the threshold.\n"));\r
+ }\r
+ return EFI_SUCCESS;\r
+ }\r
+ }\r
+\r
+ return EFI_NOT_FOUND;\r
+}\r
+\r
+/**\r
+ * This function is automatically invoked when the driver exits\r
+ * It frees all the non-allocated memory buffer.\r
+ * This function is not responsible to free allocated buffer (eg: case of memory leak,\r
+ * runtime allocation).\r
+ */\r
+EFI_STATUS\r
+EFIAPI\r
+UncachedMemoryAllocationLibDestructor (\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_SYSTEM_TABLE *SystemTable\r
)\r
{\r
LIST_ENTRY *Link;\r
FREE_PAGE_NODE *OldNode;\r
\r
- *Pages = 0;\r
+ // Test if the list is empty\r
+ Link = mPageList.ForwardLink;\r
+ if (Link == &mPageList) {\r
+ return EFI_SUCCESS;\r
+ }\r
\r
- for (Link = mPageList.ForwardLink; Link != &mPageList; Link = Link->ForwardLink) {\r
+ // Free all the pages and nodes\r
+ do {\r
OldNode = BASE_CR (Link, FREE_PAGE_NODE, Link);\r
- if (OldNode->Allocation == Allocation) {\r
- *Pages = OldNode->Pages;\r
+ // Point to the next entry\r
+ Link = Link->ForwardLink;\r
\r
+ // We only free the non-allocated buffer\r
+ if (OldNode->Allocated == FALSE) {\r
+ gBS->FreePages ((EFI_PHYSICAL_ADDRESS)(UINTN)OldNode->Base, OldNode->Pages);\r
RemoveEntryList (&OldNode->Link);\r
FreePool (OldNode);\r
- return;\r
}\r
- }\r
+ } while (Link != &mPageList);\r
\r
- return;\r
+ return EFI_SUCCESS;\r
}\r
\r
-\r
/**\r
Converts a cached or uncached address to a physical address suitable for use in SoC registers.\r
\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
- EFI_GCD_MEMORY_SPACE_DESCRIPTOR Descriptor;\r
-\r
- //\r
- // Alignment must be a power of two or zero.\r
- //\r
- ASSERT ((Alignment & (Alignment - 1)) == 0);\r
+ EFI_STATUS Status;\r
+ VOID *Allocation;\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 = gDS->GetMemorySpaceDescriptor (Memory, &Descriptor);\r
- if (!EFI_ERROR (Status)) {\r
- // We are making an assumption that all of memory has the same default attributes\r
- gAttributes = Descriptor.Attributes;\r
+ Allocation = NULL;\r
+ Status = AllocatePagesFromList (MemoryType, Pages, Alignment, &Allocation);\r
+ if (EFI_ERROR (Status)) {\r
+ ASSERT_EFI_ERROR (Status);\r
+ return NULL;\r
+ } else {\r
+ return Allocation;\r
}\r
-\r
- Status = gDS->SetMemorySpaceAttributes (Memory, EFI_PAGES_TO_SIZE (Pages), EFI_MEMORY_WC);\r
- ASSERT_EFI_ERROR (Status);\r
-\r
- return (VOID *)(UINTN)Memory;\r
}\r
\r
\r
IN UINTN Pages\r
)\r
{\r
- EFI_STATUS Status;\r
- EFI_PHYSICAL_ADDRESS Memory;\r
-\r
- ASSERT (Pages != 0);\r
-\r
- Memory = (EFI_PHYSICAL_ADDRESS) (UINTN) Buffer;\r
- Status = gDS->SetMemorySpaceAttributes (Memory, EFI_PAGES_TO_SIZE (Pages), gAttributes);\r
-\r
- Status = gBS->FreePages (Memory, Pages);\r
- ASSERT_EFI_ERROR (Status);\r
+ FreePagesFromList (Buffer);\r
}\r
\r
\r
-\r
-\r
VOID *\r
UncachedInternalAllocateAlignedPool (\r
IN EFI_MEMORY_TYPE PoolType,\r
return NULL;\r
}\r
\r
- AddPagesToList ((VOID *)(UINTN)AlignedAddress, EFI_SIZE_TO_PAGES (AllocationSize));\r
-\r
return (VOID *) AlignedAddress;\r
}\r
\r
IN VOID *Allocation\r
)\r
{\r
- UINTN Pages;\r
-\r
- RemovePagesFromList (Allocation, &Pages);\r
-\r
- UncachedFreePages (Allocation, Pages);\r
+ UncachedFreePages (Allocation, 0);\r
}\r
\r
VOID *\r