2 * COPYRIGHT: See COPYING.ARM in the top level directory
3 * PROJECT: ReactOS UEFI Boot Library
4 * FILE: boot/environ/lib/mm/heapalloc.c
5 * PURPOSE: Boot Library Memory Manager Heap Allocator
6 * PROGRAMMER: Alex Ionescu (alex.ionescu@reactos.org)
9 /* INCLUDES ******************************************************************/
13 /* DATA VARIABLES ************************************************************/
15 #define BL_HEAP_POINTER_FLAG_BITS 3
17 typedef struct _BL_HEAP_POINTER
23 ULONG_PTR BufferFree
: 1;
24 ULONG_PTR BufferOnHeap
: 1;
25 ULONG_PTR NotUsed
: 1;
26 ULONG_PTR BufferPointer
: ((8 * sizeof(ULONG_PTR
)) - BL_HEAP_POINTER_FLAG_BITS
);
30 } BL_HEAP_POINTER
, *PBL_HEAP_POINTER
;
32 typedef struct _BL_FREE_HEAP_ENTRY
34 BL_HEAP_POINTER BufferNext
;
35 BL_HEAP_POINTER BufferPrevious
;
36 BL_HEAP_POINTER FreeNext
;
37 BL_HEAP_POINTER FreePrevious
;
38 } BL_FREE_HEAP_ENTRY
, *PBL_FREE_HEAP_ENTRY
;
40 typedef struct _BL_BUSY_HEAP_ENTRY
42 BL_HEAP_POINTER BufferNext
;
43 BL_HEAP_POINTER BufferPrevious
;
44 UCHAR Buffer
[ANYSIZE_ARRAY
];
45 } BL_BUSY_HEAP_ENTRY
, *PBL_BUSY_HEAP_ENTRY
;
47 typedef struct _BL_HEAP_BOUNDARIES
53 PBL_BUSY_HEAP_ENTRY HeapStart
;
54 } BL_HEAP_BOUNDARIES
, *PBL_HEAP_BOUNDARIES
;
56 ULONG HapInitializationStatus
;
57 LIST_ENTRY MmHeapBoundaries
;
58 ULONG HapMinimumHeapSize
;
59 ULONG HapAllocationAttributes
;
60 PBL_FREE_HEAP_ENTRY
* MmFreeList
;
62 /* INLINES *******************************************************************/
67 _In_ BL_HEAP_POINTER Link
70 /* Decode the buffer pointer by ignoring the flags */
71 return (PBL_FREE_HEAP_ENTRY
)(Link
.BufferPointer
<< BL_HEAP_POINTER_FLAG_BITS
);
80 PBL_FREE_HEAP_ENTRY Entry
= FreeEntry
;
82 /* The space between the next buffer header and this one is the size */
83 return (ULONG_PTR
)MmHapDecodeLink(Entry
->BufferNext
) - (ULONG_PTR
)Entry
;
92 PBL_FREE_HEAP_ENTRY Entry
= FreeEntry
;
94 /* Get the size of the buffer as the user sees it */
95 return MmHapBufferSize(Entry
) - FIELD_OFFSET(BL_BUSY_HEAP_ENTRY
, Buffer
);
99 /* FUNCTIONS *****************************************************************/
102 MmHapHeapAllocatorExtend (
103 _In_ ULONG ExtendSize
106 ULONG HeapSize
, AlignedSize
, HeapLimit
;
107 PBL_HEAP_BOUNDARIES Heap
, NewHeap
;
109 PBL_BUSY_HEAP_ENTRY HeapBase
= NULL
;
111 /* Compute a new heap, and add 2 more pages for the free list */
112 HeapSize
= ExtendSize
+ (2 * PAGE_SIZE
);
113 if (HeapSize
< ExtendSize
)
115 return STATUS_INTEGER_OVERFLOW
;
118 /* Make sure the new heap is at least the minimum configured size */
119 if (HapMinimumHeapSize
> HeapSize
)
121 HeapSize
= HapMinimumHeapSize
;
124 /* Align it on a page boundary */
125 AlignedSize
= ALIGN_UP_BY(HeapSize
, PAGE_SIZE
);
128 return STATUS_INTEGER_OVERFLOW
;
131 /* Check if we already have a heap */
132 if (!IsListEmpty(&MmHeapBoundaries
))
134 /* Find the first heap*/
135 Heap
= CONTAINING_RECORD(MmHeapBoundaries
.Flink
,
139 /* Check if we have a page free above the heap */
140 HeapLimit
= Heap
->HeapLimit
+ PAGE_SIZE
;
141 if (HeapLimit
<= Heap
->HeapEnd
)
143 EarlyPrint(L
"Heap extension TODO\n");
144 return STATUS_INSUFFICIENT_RESOURCES
;
148 /* We do not -- allocate one */
149 Status
= MmPapAllocatePagesInRange((PULONG
)&HeapBase
,
151 AlignedSize
>> PAGE_SHIFT
,
152 HapAllocationAttributes
,
156 if (!NT_SUCCESS(Status
))
161 /* Set the heap bottom, limit, and top */
162 NewHeap
= (PBL_HEAP_BOUNDARIES
)HeapBase
->Buffer
;
163 NewHeap
->HeapBase
= (ULONG_PTR
)HeapBase
;
164 NewHeap
->HeapLimit
= (ULONG_PTR
)HeapBase
+ AlignedSize
;
165 NewHeap
->HeapStart
= (PBL_BUSY_HEAP_ENTRY
)(NewHeap
+ 1);
167 /* Set the buffer links */
168 HeapBase
->BufferPrevious
.P
= NULL
;
169 HeapBase
->BufferNext
.P
= NewHeap
->HeapStart
;
171 /* Set the buffer at the top of the heap and mark it as being free */
172 NewHeap
->HeapStart
->BufferPrevious
.P
= HeapBase
;
173 NewHeap
->HeapStart
->BufferNext
.P
= NewHeap
->HeapStart
;
174 NewHeap
->HeapStart
->BufferNext
.BufferFree
= 1;
175 NewHeap
->HeapStart
->BufferNext
.BufferOnHeap
= 1;
177 /* Is this the first heap ever? */
178 if (IsListEmpty(&MmHeapBoundaries
))
180 /* We will host the free list at the top of the heap */
181 MmFreeList
= (PBL_FREE_HEAP_ENTRY
*)((ULONG_PTR
)NewHeap
->HeapLimit
- 8 * sizeof(PBL_FREE_HEAP_ENTRY
));
182 NewHeap
->HeapLimit
= (ULONG_PTR
)MmFreeList
;
183 RtlZeroMemory(MmFreeList
, 8 * sizeof(PBL_FREE_HEAP_ENTRY
));
186 /* Remove a page on top */
187 HeapLimit
= NewHeap
->HeapLimit
;
188 NewHeap
->HeapEnd
= NewHeap
->HeapLimit
;
189 NewHeap
->HeapLimit
-= PAGE_SIZE
;
191 /* Add us into the heap list */
192 InsertTailList(&MmHeapBoundaries
, &NewHeap
->ListEntry
);
193 return STATUS_SUCCESS
;
201 ULONG BucketIndex
= 0;
203 /* Use the last bucket if this is a large allocation */
204 if (Size
>= PAGE_SIZE
)
209 /* Otherwise, use a higher index for each new power of two */
210 while (Size
>> BucketIndex
)
215 /* Allocations are at least 16 bytes (2^4 = 5th index) */
216 return BucketIndex
- 5;
220 MmHapReportHeapCorruption (
221 _In_ PBL_FREE_HEAP_ENTRY BufferEntry
225 BOOLEAN DebuggerEnabled
;
227 BlStatusPrint(L
"Heap corruption in the links surrounding %p!\n", BufferEntry
);
229 DebuggerEnabled
= BlBdDebuggerEnabled();
232 BlStatusPrint(L
"\n*** Fatal Error 0x%08x :\n (0x%p, 0x%p, 0x%p, 0x%p)\n\n", 2, BufferEntry
, NULL
, NULL
, NULL
);
236 EarlyPrint(L
"Heap corruption in the links surrounding %p!\n", BufferEntry
);
241 MmHapCheckFreeLinks (
242 _In_ PVOID BufferEntry
245 PBL_FREE_HEAP_ENTRY Prev
, Next
;
246 PBL_FREE_HEAP_ENTRY Entry
= BufferEntry
;
248 /* Get the previous and next free pointers */
249 Prev
= MmHapDecodeLink(Entry
->FreePrevious
);
250 Next
= MmHapDecodeLink(Entry
->FreeNext
);
252 /* Make sure that both the previous and next entries point to this one */
253 if (((Next
) && (MmHapDecodeLink(Next
->FreePrevious
)) != Entry
) ||
254 ((Prev
) && (MmHapDecodeLink(Prev
->FreeNext
)) != Entry
))
256 /* They don't, so the free headers are corrupted */
257 MmHapReportHeapCorruption(Entry
);
261 /* They do, return the free entry as valid */
266 MmHapCheckBufferLinks (
267 _In_ PVOID BufferEntry
270 PBL_FREE_HEAP_ENTRY Prev
, Next
;
271 PBL_FREE_HEAP_ENTRY Entry
= BufferEntry
;
273 /* Get the previous and next buffer pointers */
274 Prev
= MmHapDecodeLink(Entry
->BufferPrevious
);
275 Next
= MmHapDecodeLink(Entry
->BufferNext
);
277 /* Make sure that both the previous and next entries point to this one */
278 if (((Next
) && (MmHapDecodeLink(Next
->BufferPrevious
)) != Entry
) ||
279 ((Prev
) && (MmHapDecodeLink(Prev
->BufferNext
)) != Entry
))
281 /* They don't, so the heap headers are corrupted */
282 MmHapReportHeapCorruption(Entry
);
286 /* They, do the entry is valid */
291 MmHapRemoveBufferFromFreeList (
292 _In_ PBL_FREE_HEAP_ENTRY FreeEntry
295 PBL_FREE_HEAP_ENTRY Prev
, Next
;
297 /* Firest, make sure the free entry is valid */
298 FreeEntry
= MmHapCheckFreeLinks(FreeEntry
);
304 /* Get the previous and next entry */
305 Prev
= MmHapDecodeLink(FreeEntry
->FreePrevious
);
306 Next
= MmHapDecodeLink(FreeEntry
->FreeNext
);
308 /* Update the next entry to point to our previous entry */
311 Next
->FreePrevious
.P
= Prev
;
314 /* Are we at the head? */
317 /* Nope, so update our previous entry to point to our next entry */
318 Prev
->FreeNext
.P
= Next
;
322 /* Yep, so update the appropriate bucket listhead */
323 MmFreeList
[MmHapGetBucketId(MmHapBufferSize(FreeEntry
))] = Prev
;
326 /* Return the (now removed) entry */
331 MmHapCoalesceFreeBuffer (
332 _In_ PBL_FREE_HEAP_ENTRY FreeEntry
335 PBL_FREE_HEAP_ENTRY Prev
, Next
;
337 /* First make sure that this is a valid buffer entry */
338 if (!MmHapCheckBufferLinks(FreeEntry
))
343 /* Get the next entry and check if it's free */
344 Next
= MmHapDecodeLink(FreeEntry
->BufferNext
);
345 if (!(Next
->BufferNext
.BufferOnHeap
) && (Next
->BufferNext
.BufferFree
))
347 /* Remove the next buffer from the free list since we're coalescing */
348 Next
= MmHapRemoveBufferFromFreeList(Next
);
354 /* The forward link of the *new* free buffer should now point to us */
355 MmHapDecodeLink(Next
->BufferNext
)->BufferPrevious
.P
= FreeEntry
;
357 /* Our forward link should point to the *new* free buffer as well */
358 FreeEntry
->BufferNext
.P
= MmHapDecodeLink(Next
->BufferNext
);
360 /* Mark our buffer as free */
361 FreeEntry
->BufferNext
.BufferFree
= 1;
364 /* Get the previous entry and check if it's free */
365 Prev
= MmHapDecodeLink(FreeEntry
->BufferPrevious
);
366 if (!(Prev
) || !(Prev
->BufferNext
.BufferFree
))
371 /* It's free, so remove it */
372 Prev
= MmHapRemoveBufferFromFreeList(Prev
);
378 /* The previous link of our next buffer should now point to our *previous* */
379 MmHapDecodeLink(FreeEntry
->BufferNext
)->BufferPrevious
.P
= Prev
;
381 /* Our previous link should point the next free buffer now */
382 Prev
->BufferNext
.P
= MmHapDecodeLink(FreeEntry
->BufferNext
);
384 /* Set the new freed buffer as the previous buffer, and mark it free */
386 FreeEntry
->BufferNext
.BufferFree
= 1;
392 _In_ PBL_BUSY_HEAP_ENTRY Entry
,
396 PBL_FREE_HEAP_ENTRY FreeEntry
, Head
;
398 BL_LIBRARY_PARAMETERS LocalParameters
;
400 /* First, check if the entry is valid */
401 Entry
= MmHapCheckBufferLinks(Entry
);
407 /* Check if we should zero the entry */
408 LocalParameters
= BlpLibraryParameters
;
409 if ((LocalParameters
.LibraryFlags
& BL_LIBRARY_FLAG_ZERO_HEAP_ALLOCATIONS_ON_FREE
) &&
412 /* Yep, zero it out */
413 RtlZeroMemory(Entry
->Buffer
, MmHapUserBufferSize(Entry
));
416 /* Now mark the entry as free */
417 Entry
->BufferNext
.BufferFree
= 1;
419 /* Now that this buffer is free, try to coalesce it */
420 FreeEntry
= MmHapCoalesceFreeBuffer((PBL_FREE_HEAP_ENTRY
)Entry
);
426 /* Compute the bucket ID for the free list */
427 BucketId
= MmHapGetBucketId(MmHapBufferSize(Entry
));
429 /* Get the current head for this bucket, if one exists */
430 Head
= MmFreeList
? MmFreeList
[BucketId
] : NULL
;
432 /* Update the head's backlink to point to this newly freed entry */
435 Head
->FreePrevious
.P
= FreeEntry
;
438 /* Nobody behind us, the old head in front of us */
439 FreeEntry
->FreePrevious
.P
= NULL
;
440 FreeEntry
->FreeNext
.P
= Head
;
442 /* Put us at the head of list now, and return the entry */
443 MmFreeList
[BucketId
] = FreeEntry
;
448 MmHapFindBufferInFreeList (
452 PBL_FREE_HEAP_ENTRY FreeEntry
= NULL
;
453 PBL_BUSY_HEAP_ENTRY NextEntry
;
456 /* Get the appropriate bucket for our size */
457 BucketId
= MmHapGetBucketId(Size
);
463 /* Keep going as long as we don't have a free entry */
466 /* Fet the first free entry in this list */
467 FreeEntry
= MmFreeList
? MmFreeList
[BucketId
] : NULL
;
469 /* Loop as long as there's entries in the list */
472 /* Can this free entry satisfy our needs? */
473 if (MmHapBufferSize(FreeEntry
) >= Size
)
479 /* It cannot, keep going to the next one */
480 FreeEntry
= MmHapDecodeLink(FreeEntry
->FreeNext
);
483 /* Try the next list -- have we exhausted all the lists? */
486 /* Have we not found an entry yet? Fail if so... */
494 /* We should have an entry if we're here. Remove it from the free list */
495 NT_ASSERT(FreeEntry
!= NULL
);
496 FreeEntry
= MmHapRemoveBufferFromFreeList(FreeEntry
);
502 /* Make sure it's not corrupted */
503 FreeEntry
= MmHapCheckBufferLinks(FreeEntry
);
509 /* Do we have space for at least another buffer? */
510 if ((MmHapBufferSize(FreeEntry
) - Size
) >= sizeof(BL_FREE_HEAP_ENTRY
))
512 /* Go to where the new next buffer will start */
513 NextEntry
= (PBL_BUSY_HEAP_ENTRY
)((ULONG_PTR
)FreeEntry
+ Size
);
515 /* Make the new next buffer point to the next buffer */
516 NextEntry
->BufferNext
.P
= MmHapDecodeLink(FreeEntry
->BufferNext
);
518 /* Make the old next buffer point back to the new one */
519 MmHapDecodeLink(FreeEntry
->BufferNext
)->BufferPrevious
.P
= NextEntry
;
521 /* Point the new next buffer point back to us */
522 NextEntry
->BufferPrevious
.P
= FreeEntry
;
524 /* Point us to the new next buffer */
525 FreeEntry
->BufferNext
.P
= NextEntry
;
527 /* And insert the new next buffer into the free list */
528 MmHapAddToFreeList(NextEntry
, 1);
531 /* Return the entry, which is now allocated */
532 return (PBL_BUSY_HEAP_ENTRY
)FreeEntry
;
538 _In_ ULONG HeapAttributes
543 /* No free list to begin with */
546 /* Configure the minimum heap size and allocation attributes */
547 HapMinimumHeapSize
= ALIGN_UP_BY(HeapSize
, PAGE_SIZE
);
548 HapAllocationAttributes
= HeapAttributes
& 0x20000;
550 /* Initialize the heap boundary list */
551 InitializeListHead(&MmHeapBoundaries
);
553 /* Initialize a heap big enough to handle a one pointer long allocation */
554 Status
= MmHapHeapAllocatorExtend(sizeof(PVOID
));
555 if (NT_SUCCESS(Status
))
557 /* The heap is ready! */
558 HapInitializationStatus
= 1;
559 Status
= STATUS_SUCCESS
;
562 /* Return initialization status */
572 PBL_HEAP_BOUNDARIES Heap
;
573 PBL_BUSY_HEAP_ENTRY BusyEntry
, FreeEntry
, NextEntry
;
575 /* Ignore heap allocation if the heap allocator isn't ready yet */
576 if (HapInitializationStatus
!= 1)
581 /* Align the buffer size to the minimum size required */
582 BufferSize
= ALIGN_UP(Size
+ FIELD_OFFSET(BL_BUSY_HEAP_ENTRY
, Buffer
),
583 FIELD_OFFSET(BL_BUSY_HEAP_ENTRY
, Buffer
));
585 /* Watch out for overflow */
586 if (BufferSize
<= Size
)
591 /* Make sure it's at least big enough to hold a free entry later on */
592 if (BufferSize
< sizeof(BL_FREE_HEAP_ENTRY
))
594 BufferSize
= sizeof(BL_FREE_HEAP_ENTRY
);
597 /* Loop while we try to allocate memory */
600 /* Find a free buffer for this allocation */
601 BusyEntry
= MmHapFindBufferInFreeList(BufferSize
);
607 /* We couldn't find a free buffer. Do we have any heaps? */
608 if (!IsListEmpty(&MmHeapBoundaries
))
610 /* Get the current heap */
611 Heap
= CONTAINING_RECORD(MmHeapBoundaries
.Flink
,
615 /* Check if we have space in the heap page for this allocation? */
616 FreeEntry
= Heap
->HeapStart
;
617 NextEntry
= (PBL_BUSY_HEAP_ENTRY
)((ULONG_PTR
)FreeEntry
+ BufferSize
);
619 if ((NextEntry
>= FreeEntry
) &&
620 ((ULONG_PTR
)NextEntry
<=
621 Heap
->HeapLimit
- FIELD_OFFSET(BL_BUSY_HEAP_ENTRY
, Buffer
)))
623 /* Update the heap top pointer past this allocation */
624 Heap
->HeapStart
= NextEntry
;
626 /* Make this allocation point to the slot */
627 FreeEntry
->BufferNext
.P
= Heap
->HeapStart
;
629 /* And make the free heap entry point back to us */
630 Heap
->HeapStart
->BufferPrevious
.P
= FreeEntry
;
632 /* Mark the heap entry as being free and on the heap */
633 Heap
->HeapStart
->BufferNext
.BufferFree
= 1;
634 Heap
->HeapStart
->BufferNext
.BufferOnHeap
= 1;
636 /* The previously freed entry on the heap page is now ours */
637 BusyEntry
= FreeEntry
;
642 /* We have no heaps or space on any heap -- extend the heap and retry */
643 if (!NT_SUCCESS(MmHapHeapAllocatorExtend(BufferSize
)))
645 EarlyPrint(L
"Heap extension failed!\n");
649 EarlyPrint(L
"Heap extended -- trying again\n");
652 /* Clear all the bits, marking this entry as allocated */
653 BusyEntry
->BufferNext
.P
= MmHapDecodeLink(BusyEntry
->BufferNext
);
655 /* Return the entry's data buffer */
656 EarlyPrint(L
"Returning buffer at 0x%p\n", &BusyEntry
->Buffer
);
657 return &BusyEntry
->Buffer
;
665 PBL_BUSY_HEAP_ENTRY BusyEntry
;
666 PBL_HEAP_BOUNDARIES Heap
;
667 PLIST_ENTRY NextEntry
;
669 /* If the heap is not initialized, fail */
670 if (HapInitializationStatus
!= 1)
672 return STATUS_UNSUCCESSFUL
;
675 /* Get the heap header */
676 BusyEntry
= CONTAINING_RECORD(Buffer
, BL_BUSY_HEAP_ENTRY
, Buffer
);
678 /* Loop all the heaps */
679 NextEntry
= MmHeapBoundaries
.Flink
;
680 while (NextEntry
!= &MmHeapBoundaries
)
682 /* Get the current heap in the list */
683 Heap
= CONTAINING_RECORD(NextEntry
, BL_HEAP_BOUNDARIES
, ListEntry
);
685 /* Is this entry part of this heap? */
686 if (((ULONG_PTR
)Heap
->HeapBase
<= (ULONG_PTR
)BusyEntry
) &&
687 ((ULONG_PTR
)BusyEntry
< (ULONG_PTR
)Heap
->HeapStart
))
689 /* Ignore double-free */
690 if (BusyEntry
->BufferNext
.BufferFree
)
692 return STATUS_INVALID_PARAMETER
;
695 /* It is -- add it to the free list */
696 MmHapAddToFreeList(BusyEntry
, 0);
697 return STATUS_SUCCESS
;
700 /* It isn't, move to the next heap */
701 NextEntry
= NextEntry
->Flink
;
704 /* The entry is not on any valid heap */
705 return STATUS_INVALID_PARAMETER
;