From 628fc940d3fbd094e7006032d4132672105e3521 Mon Sep 17 00:00:00 2001 From: Timo Kreuzer Date: Mon, 3 Oct 2011 14:46:38 +0000 Subject: [PATCH] [FREELDR] Implement new heap code. The code is much less complex than bget and allows to use allocation tags and releasing unused memory. Its not yet active. svn path=/trunk/; revision=53956 --- reactos/boot/freeldr/freeldr/mm/heap_new.c | 420 +++++++++++++++++++++ 1 file changed, 420 insertions(+) create mode 100644 reactos/boot/freeldr/freeldr/mm/heap_new.c diff --git a/reactos/boot/freeldr/freeldr/mm/heap_new.c b/reactos/boot/freeldr/freeldr/mm/heap_new.c new file mode 100644 index 00000000000..9a37815c93a --- /dev/null +++ b/reactos/boot/freeldr/freeldr/mm/heap_new.c @@ -0,0 +1,420 @@ +/* + * FreeLoader + * Copyright (C) 2011 Timo Kreuzer (timo.kreuzer@reactos.orh) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include + +DBG_DEFAULT_CHANNEL(HEAP); + +PVOID FrLdrDefaultHeap; +PVOID FrLdrTempHeap; + +typedef struct _HEAP_BLOCK +{ + USHORT Size; + USHORT PreviousSize; + ULONG Tag; + UCHAR Data[]; +} HEAP_BLOCK, *PHEAP_BLOCK; + +typedef struct _HEAP +{ + ULONG MaximumSize; + ULONG CurrentAllocBytes; + ULONG MaxAllocBytes; + ULONG NumAllocs; + ULONG NumFrees; + ULONG LargestAllocation; + HEAP_BLOCK Blocks; +} HEAP, *PHEAP; + +PVOID +HeapCreate( + ULONG MaximumSize, + TYPE_OF_MEMORY MemoryType) +{ + PHEAP Heap; + PHEAP_BLOCK Block; + ULONG Remaining; + USHORT PreviousSize; + TRACE("HeapCreate(MemoryType=%ld)\n", MemoryType); + + /* Allocate some memory for the heap */ + Heap = MmAllocateMemoryWithType(MaximumSize, MemoryType); + if (!Heap) + { + ERR("HEAP: Failed to allocate heap of size 0x%lx, Type\n", + MaximumSize, MemoryType); + return NULL; + } + + /* Initialize the heap header */ + Heap->MaximumSize = ALIGN_UP_BY(MaximumSize, MM_PAGE_SIZE); + Heap->CurrentAllocBytes = 0; + Heap->MaxAllocBytes = 0; + Heap->NumAllocs = 0; + Heap->NumFrees = 0; + Heap->LargestAllocation = 0; + + /* Calculate what's left to process */ + Remaining = (MaximumSize - sizeof(HEAP)) / sizeof(HEAP_BLOCK); + TRACE("Remaining = %ld\n", Remaining); + + /* Substract one for the terminating entry */ + Remaining--; + + Block = &Heap->Blocks; + PreviousSize = 0; + + /* Create free blocks */ + while (Remaining > 1) + { + /* Initialize this free block */ + Block->Size = (USHORT)min(MAXUSHORT, Remaining - 1); + Block->PreviousSize = PreviousSize; + Block->Tag = 0; + + /* Substract current block size from remainder */ + Remaining -= (Block->Size + 1); + + /* Go to next block */ + PreviousSize = Block->Size; + Block = Block + Block->Size + 1; + + TRACE("Remaining = %ld\n", Remaining); + } + + /* Now finish with a terminating block */ + Block->Size = 0; + Block->PreviousSize = PreviousSize; + Block->Tag = 'dnE#'; + + return Heap; +} + +VOID +HeapDestroy( + PVOID HeapHandle) +{ + PHEAP Heap = HeapHandle; + + /* Mark all pages free */ + MmMarkPagesInLookupTable(PageLookupTableAddress, + (ULONG_PTR)Heap / MM_PAGE_SIZE, + Heap->MaximumSize / MM_PAGE_SIZE, + LoaderFree); +} + +VOID +HeapRelease( + PVOID HeapHandle) +{ + PHEAP Heap = HeapHandle; + PHEAP_BLOCK Block; + PUCHAR StartAddress, EndAddress; + ULONG FreePages, AllFreePages = 0; + TRACE("HeapRelease(%p)\n", HeapHandle); + + /* Loop all heap chunks */ + for (Block = &Heap->Blocks; + Block->Size != 0; + Block = Block + 1 + Block->Size) + { + /* Continue, if its not free */ + if (Block->Tag != 0) continue; + + /* Calculate page aligned start address of the free region */ + StartAddress = ALIGN_UP_POINTER_BY(Block->Data, PAGE_SIZE); + + /* Walk over adjacent free blocks */ + while (Block->Tag == 0) Block = Block + Block->Size + 1; + + /* Check if this was the last block */ + if (Block->Size == 0) + { + /* Align the end address up to cover the end of the heap */ + EndAddress = ALIGN_DOWN_POINTER_BY(Block->Data, PAGE_SIZE); + } + else + { + /* Align the end address down to not cover any allocations */ + EndAddress = ALIGN_DOWN_POINTER_BY(Block->Data, PAGE_SIZE); + } + + FreePages = (EndAddress - StartAddress) / MM_PAGE_SIZE; + AllFreePages += FreePages; + + /* Now mark the pages free */ + MmMarkPagesInLookupTable(PageLookupTableAddress, + (ULONG_PTR)StartAddress / MM_PAGE_SIZE, + FreePages, + LoaderFree); + + /* bail out, if it was the last block */ + if (Block->Size == 0) break; + } + + TRACE("HeapRelease() done, freed %ld pages\n", AllFreePages); +} + +PVOID +HeapAllocate( + PVOID HeapHandle, + SIZE_T ByteSize, + ULONG Tag) +{ + PHEAP Heap = HeapHandle; + PHEAP_BLOCK Block, NextBlock; + USHORT BlockSize, Remaining; + + /* Check if the allocation is too large */ + if ((ByteSize + sizeof(HEAP_BLOCK)) > MAXUSHORT * sizeof(HEAP_BLOCK)) + { + ERR("HEAP: Allocation of 0x%lx bytes too large\n", ByteSize); + return NULL; + } + + /* We need a proper tag */ + if (Tag == 0) Tag = 'enoN'; + + /* Calculate alloc size */ + BlockSize = (USHORT)(ByteSize + sizeof(HEAP_BLOCK) - 1) / sizeof(HEAP_BLOCK); + + /* Loop all heap chunks */ + for (Block = &Heap->Blocks; + Block->Size != 0; + Block = Block + 1 + Block->Size) + { + /* Continue, if its not free */ + if (Block->Tag != 0) continue; + + /* Continue, if its too small */ + if (Block->Size < BlockSize) continue; + + /* This block is just fine, use it */ + Block->Tag = Tag; + + /* Calculate the remaining size */ + Remaining = Block->Size - BlockSize; + + /* Check if the remaining space is large enough for a new block */ + if (Remaining > 1) + { + /* Make the allocated block as large as neccessary */ + Block->Size = BlockSize; + + /* Get pointer to the new block */ + NextBlock = Block + 1 + BlockSize; + + /* Make it a free block */ + NextBlock->Size = Remaining - 1; + NextBlock->PreviousSize = BlockSize; + NextBlock->Tag = 0; + + /* Advance to the next block */ + BlockSize = NextBlock->Size; + NextBlock = NextBlock + 1 + BlockSize; + } + else + { + /* Not enough left, use the full block */ + NextBlock = Block + 1 + BlockSize; + } + + /* Update the next blocks back link */ + NextBlock->PreviousSize = Block->Size; + + /* Update heap usage */ + Heap->NumAllocs++; + Heap->CurrentAllocBytes += Block->Size * sizeof(HEAP_BLOCK); + Heap->MaxAllocBytes = max(Heap->MaxAllocBytes, Heap->CurrentAllocBytes); + Heap->LargestAllocation = max(Heap->LargestAllocation, + Block->Size * sizeof(HEAP_BLOCK)); + + TRACE("HeapAllocate(%p, %ld, %.4s) -> return %p\n", + HeapHandle, ByteSize, &Tag, Block->Data); + + /* Return pointer to the data */ + return Block->Data; + } + + /* We found nothing */ + WARN("HEAP: nothing suitable found for 0x%lx bytes\n", ByteSize); + return NULL; +} + +VOID +HeapFree( + PVOID HeapHandle, + PVOID Pointer, + ULONG Tag) +{ + PHEAP Heap = HeapHandle; + PHEAP_BLOCK Block, PrevBlock, NextBlock; + TRACE("HeapFree(%p, %p)\n", HeapHandle, Pointer); + ASSERT(Tag != 'dnE#'); + + /* Check if the block is really inside this heap */ + if ((Pointer < (PVOID)(Heap + 1)) || + (Pointer > (PVOID)((PUCHAR)Heap + Heap->MaximumSize))) + { + ERR("Bad free of %p from heap %p\n", Pointer, Heap); + ASSERT(FALSE); + } + + Block = ((PHEAP_BLOCK)Pointer) - 1; + + /* Check if the tag matches */ + if (Tag && Block->Tag != Tag) + { + ERR("HEAP: Bad tag! Pointer = %p: block tag '%.4s', requested '%.4s'\n", + Pointer, &Block->Tag, &Tag); + ASSERT(FALSE); + } + + /* Mark as free */ + Block->Tag = 0; + + /* Update heap usage */ + Heap->NumFrees++; + Heap->CurrentAllocBytes -= Block->Size * sizeof(HEAP_BLOCK); + + /* Get pointers to the next and previous block */ + PrevBlock = Block - Block->PreviousSize - 1; + NextBlock = Block + Block->Size + 1; + + /* Check if next block is free */ + if (NextBlock->Tag == 0) + { + /* Check if their combined size if small enough */ + if ((Block->Size + NextBlock->Size + 1) <= MAXUSHORT) + { + /* Merge next block into current */ + Block->Size += NextBlock->Size + 1; + NextBlock = Block + Block->Size + 1; + NextBlock->PreviousSize = Block->Size; + } + } + + /* Check if there is a block before and is free */ + if ((Block->PreviousSize != 0) && (PrevBlock->Tag == 0)) + { + /* Check if their combined size if small enough */ + if ((PrevBlock->Size + Block->Size + 2) < MAXUSHORT) + { + /* Merge current block into previous */ + PrevBlock->Size += Block->Size + 1; + NextBlock->PreviousSize = PrevBlock->Size; + } + } +} + + +/* Wrapper functions *********************************************************/ + +#define HEAP_SIZE_PROCESS_HEAP (1024 * 1024) + +VOID +MmInitializeHeap(PVOID PageLookupTable) +{ + TRACE("MmInitializeHeap()\n"); + + /* Create the process heap */ + FrLdrDefaultHeap = HeapCreate(HEAP_SIZE_PROCESS_HEAP, LoaderOsloaderHeap); + + /* Create the process heap */ + FrLdrTempHeap = HeapCreate(HEAP_SIZE_PROCESS_HEAP, LoaderFirmwareTemporary); + + /* Create the pool heap */ + TRACE("MmInitializeHeap() done\n"); + +} + +PVOID +MmHeapAlloc(ULONG MemorySize) +{ + return HeapAllocate(FrLdrDefaultHeap, MemorySize, 'pHmM'); +} + +VOID +MmHeapFree(PVOID MemoryPointer) +{ + HeapFree(FrLdrDefaultHeap, MemoryPointer, 'pHmM'); +} + + +#undef ExAllocatePoolWithTag +PVOID +NTAPI +ExAllocatePoolWithTag( + IN POOL_TYPE PoolType, + IN SIZE_T NumberOfBytes, + IN ULONG Tag) +{ + return HeapAllocate(FrLdrDefaultHeap, NumberOfBytes, Tag); +} + +#undef ExFreePool +VOID +NTAPI +ExFreePool( + IN PVOID P) +{ + HeapFree(FrLdrDefaultHeap, P, 0); +} + +#undef ExFreePoolWithTag +VOID +NTAPI +ExFreePoolWithTag( + IN PVOID P, + IN ULONG Tag) +{ + HeapFree(FrLdrDefaultHeap, P, Tag); +} + +PVOID +NTAPI +RtlAllocateHeap( + IN PVOID HeapHandle, + IN ULONG Flags, + IN SIZE_T Size) +{ + PVOID ptr; + + ptr = HeapAllocate(FrLdrDefaultHeap, Size, ' ltR'); + if (ptr && (Flags & HEAP_ZERO_MEMORY)) + { + RtlZeroMemory(ptr, Size); + } + + return ptr; +} + +BOOLEAN +NTAPI +RtlFreeHeap( + IN PVOID HeapHandle, + IN ULONG Flags, + IN PVOID HeapBase) +{ + HeapFree(FrLdrDefaultHeap, HeapBase, ' ltR'); + return TRUE; +} +