Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenheap.c
Go to the documentation of this file.
00001 /* 00002 * FreeLoader 00003 * Copyright (C) 2011 Timo Kreuzer (timo.kreuzer@reactos.org) 00004 * 00005 * This program is free software; you can redistribute it and/or modify 00006 * it under the terms of the GNU General Public License as published by 00007 * the Free Software Foundation; either version 2 of the License, or 00008 * (at your option) any later version. 00009 * 00010 * This program is distributed in the hope that it will be useful, 00011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00013 * GNU General Public License for more details. 00014 * 00015 * You should have received a copy of the GNU General Public License along 00016 * with this program; if not, write to the Free Software Foundation, Inc., 00017 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 00018 */ 00019 00020 #include <freeldr.h> 00021 #include <debug.h> 00022 00023 #define FREELDR_HEAP_VERIFIER 00024 00025 DBG_DEFAULT_CHANNEL(HEAP); 00026 00027 #define DEFAULT_HEAP_SIZE (1024 * 1024) 00028 #define TEMP_HEAP_SIZE (1024 * 1024) 00029 00030 #define REDZONE_MARK 0xCCCCCCCCCCCCCCCCULL 00031 #define REDZONE_ALLOCATION 24 00032 #define REDZONE_LOW_OFFSET 16 00033 #define REDZONE_SIZE(Block) ((ULONG64*)Block->Data) 00034 #define REDZONE_LOW(Block) ((ULONG64*)Block->Data + 1) 00035 #define REDZONE_HI(Block) ((ULONG64*)((PUCHAR)Block->Data + 16 + *REDZONE_SIZE(Block))) 00036 00037 PVOID FrLdrDefaultHeap; 00038 PVOID FrLdrTempHeap; 00039 00040 typedef struct _BLOCK_DATA 00041 { 00042 ULONG_PTR Flink:32; 00043 ULONG_PTR Blink:32; 00044 } BLOCK_DATA, *PBLOCK_DATA; 00045 00046 typedef struct _HEAP_BLOCK 00047 { 00048 USHORT Size; 00049 USHORT PreviousSize; 00050 ULONG Tag; 00051 BLOCK_DATA Data[]; 00052 } HEAP_BLOCK, *PHEAP_BLOCK; 00053 00054 typedef struct _HEAP 00055 { 00056 SIZE_T MaximumSize; 00057 SIZE_T CurrentAllocBytes; 00058 SIZE_T MaxAllocBytes; 00059 ULONG NumAllocs; 00060 ULONG NumFrees; 00061 SIZE_T LargestAllocation; 00062 ULONGLONG AllocationTime; 00063 ULONGLONG FreeTime; 00064 ULONG_PTR TerminatingBlock; 00065 HEAP_BLOCK Blocks; 00066 } HEAP, *PHEAP; 00067 00068 PVOID 00069 HeapCreate( 00070 SIZE_T MaximumSize, 00071 TYPE_OF_MEMORY MemoryType) 00072 { 00073 PHEAP Heap; 00074 PHEAP_BLOCK Block; 00075 SIZE_T Remaining; 00076 USHORT PreviousSize; 00077 TRACE("HeapCreate(MemoryType=%ld)\n", MemoryType); 00078 00079 /* Allocate some memory for the heap */ 00080 MaximumSize = ALIGN_UP_BY(MaximumSize, MM_PAGE_SIZE); 00081 Heap = MmAllocateMemoryWithType(MaximumSize, MemoryType); 00082 if (!Heap) 00083 { 00084 ERR("HEAP: Failed to allocate heap of size 0x%lx, Type\n", 00085 MaximumSize, MemoryType); 00086 return NULL; 00087 } 00088 00089 /* Initialize the heap header */ 00090 Heap->MaximumSize = MaximumSize; 00091 Heap->CurrentAllocBytes = 0; 00092 Heap->MaxAllocBytes = 0; 00093 Heap->NumAllocs = 0; 00094 Heap->NumFrees = 0; 00095 Heap->LargestAllocation = 0; 00096 00097 /* Calculate what's left to process */ 00098 Remaining = (MaximumSize - sizeof(HEAP)) / sizeof(HEAP_BLOCK); 00099 TRACE("Remaining = %ld\n", Remaining); 00100 00101 /* Substract 2 for the terminating entry (header + free entry) */ 00102 Remaining -= 2; 00103 00104 Block = &Heap->Blocks; 00105 PreviousSize = 0; 00106 00107 /* Create free blocks */ 00108 while (Remaining > 1) 00109 { 00110 /* Initialize this free block */ 00111 Block->Size = (USHORT)min(MAXUSHORT, Remaining - 1); 00112 Block->PreviousSize = PreviousSize; 00113 Block->Tag = 0; 00114 Block->Data[0].Flink = (Block - &Heap->Blocks) + Block->Size + 1; 00115 Block->Data[0].Blink = (Block - &Heap->Blocks) - 1 - PreviousSize; 00116 00117 /* Substract current block size from remainder */ 00118 Remaining -= (Block->Size + 1); 00119 00120 /* Go to next block */ 00121 PreviousSize = Block->Size; 00122 Block = Block + Block->Size + 1; 00123 00124 TRACE("Remaining = %ld\n", Remaining); 00125 } 00126 00127 /* Now finish with a terminating block */ 00128 Heap->TerminatingBlock = Block - &Heap->Blocks; 00129 Block->Size = 0; 00130 Block->PreviousSize = PreviousSize; 00131 Block->Tag = 'dnE#'; 00132 Block->Data[0].Flink = 0; 00133 Block->Data[0].Blink = (Block - &Heap->Blocks) - 1 - PreviousSize; 00134 Heap->Blocks.Data[0].Blink = Heap->TerminatingBlock; 00135 00136 return Heap; 00137 } 00138 00139 VOID 00140 HeapDestroy( 00141 PVOID HeapHandle) 00142 { 00143 PHEAP Heap = HeapHandle; 00144 00145 /* Mark all pages as firmware temporary, so they are free for the kernel */ 00146 MmMarkPagesInLookupTable(PageLookupTableAddress, 00147 (ULONG_PTR)Heap / MM_PAGE_SIZE, 00148 (PFN_COUNT)(Heap->MaximumSize / MM_PAGE_SIZE), 00149 LoaderFirmwareTemporary); 00150 } 00151 00152 VOID 00153 HeapRelease( 00154 PVOID HeapHandle) 00155 { 00156 PHEAP Heap = HeapHandle; 00157 PHEAP_BLOCK Block; 00158 PUCHAR StartAddress, EndAddress; 00159 PFN_COUNT FreePages, AllFreePages = 0; 00160 TRACE("HeapRelease(%p)\n", HeapHandle); 00161 00162 /* Loop all heap chunks */ 00163 for (Block = &Heap->Blocks; 00164 Block->Size != 0; 00165 Block = Block + 1 + Block->Size) 00166 { 00167 /* Continue, if its not free */ 00168 if (Block->Tag != 0) 00169 { 00170 #ifdef FREELDR_HEAP_VERIFIER 00171 /* Verify size and redzones */ 00172 ASSERT(*REDZONE_SIZE(Block) <= Block->Size * sizeof(HEAP_BLOCK)); 00173 ASSERT(*REDZONE_LOW(Block) == REDZONE_MARK); 00174 ASSERT(*REDZONE_HI(Block) == REDZONE_MARK); 00175 #endif 00176 continue; 00177 } 00178 00179 /* Calculate page aligned start address of the free region */ 00180 StartAddress = ALIGN_UP_POINTER_BY(Block->Data, PAGE_SIZE); 00181 00182 /* Walk over adjacent free blocks */ 00183 while (Block->Tag == 0) Block = Block + Block->Size + 1; 00184 00185 /* Check if this was the last block */ 00186 if (Block->Size == 0) 00187 { 00188 /* Align the end address up to cover the end of the heap */ 00189 EndAddress = ALIGN_UP_POINTER_BY(Block->Data, PAGE_SIZE); 00190 } 00191 else 00192 { 00193 /* Align the end address down to not cover any allocations */ 00194 EndAddress = ALIGN_DOWN_POINTER_BY(Block->Data, PAGE_SIZE); 00195 } 00196 00197 /* Check if we have free pages */ 00198 if (EndAddress > StartAddress) 00199 { 00200 /* Calculate the size of the free region in pages */ 00201 FreePages = (PFN_COUNT)((EndAddress - StartAddress) / MM_PAGE_SIZE); 00202 AllFreePages += FreePages; 00203 00204 /* Now mark the pages free */ 00205 MmMarkPagesInLookupTable(PageLookupTableAddress, 00206 (ULONG_PTR)StartAddress / MM_PAGE_SIZE, 00207 FreePages, 00208 LoaderFree); 00209 } 00210 00211 /* bail out, if it was the last block */ 00212 if (Block->Size == 0) break; 00213 } 00214 00215 TRACE("HeapRelease() done, freed %ld pages\n", AllFreePages); 00216 } 00217 00218 VOID 00219 HeapCleanupAll(VOID) 00220 { 00221 PHEAP Heap; 00222 00223 Heap = FrLdrDefaultHeap; 00224 TRACE("Heap statistics for default heap:\n" 00225 "CurrentAlloc=0x%lx, MaxAlloc=0x%lx, LargestAllocation=0x%lx\n" 00226 "NumAllocs=%ld, NumFrees=%ld\n", 00227 Heap->CurrentAllocBytes, Heap->MaxAllocBytes, Heap->LargestAllocation, 00228 Heap->NumAllocs, Heap->NumFrees); 00229 TRACE("AllocTime = %I64d, FreeTime = %I64d, sum = %I64d\n", 00230 Heap->AllocationTime, Heap->FreeTime, Heap->AllocationTime + Heap->FreeTime); 00231 00232 00233 /* Release fre pages */ 00234 HeapRelease(FrLdrDefaultHeap); 00235 00236 Heap = FrLdrTempHeap; 00237 TRACE("Heap statistics for temp heap:\n" 00238 "CurrentAlloc=0x%lx, MaxAlloc=0x%lx, LargestAllocation=0x%lx\n" 00239 "NumAllocs=%ld, NumFrees=%ld\n", 00240 Heap->CurrentAllocBytes, Heap->MaxAllocBytes, Heap->LargestAllocation, 00241 Heap->NumAllocs, Heap->NumFrees); 00242 00243 /* Destroy the heap */ 00244 HeapDestroy(FrLdrTempHeap); 00245 } 00246 00247 static VOID 00248 HeapRemoveFreeList( 00249 PHEAP Heap, 00250 PHEAP_BLOCK Block) 00251 { 00252 PHEAP_BLOCK Previous, Next; 00253 00254 Next = &Heap->Blocks + Block->Data[0].Flink; 00255 Previous = &Heap->Blocks + Block->Data[0].Blink; 00256 ASSERT((Next->Tag == 0) || (Next->Tag == 'dnE#')); 00257 ASSERT(Next->Data[0].Blink == Block - &Heap->Blocks); 00258 ASSERT((Previous->Tag == 0) || (Previous->Tag == 'dnE#')); 00259 ASSERT(Previous->Data[0].Flink == Block - &Heap->Blocks); 00260 00261 Next->Data[0].Blink = Previous - &Heap->Blocks; 00262 Previous->Data[0].Flink = Next - &Heap->Blocks; 00263 } 00264 00265 static VOID 00266 HeapInsertFreeList( 00267 PHEAP Heap, 00268 PHEAP_BLOCK FreeBlock) 00269 { 00270 PHEAP_BLOCK ListHead, NextBlock; 00271 ASSERT(FreeBlock->Tag == 0); 00272 00273 /* Terminating block serves as free list head */ 00274 ListHead = &Heap->Blocks + Heap->TerminatingBlock; 00275 00276 for (NextBlock = &Heap->Blocks + ListHead->Data[0].Flink; 00277 NextBlock < FreeBlock; 00278 NextBlock = &Heap->Blocks + NextBlock->Data[0].Flink); 00279 00280 FreeBlock->Data[0].Flink = NextBlock - &Heap->Blocks; 00281 FreeBlock->Data[0].Blink = NextBlock->Data[0].Blink; 00282 NextBlock->Data[0].Blink = FreeBlock - &Heap->Blocks; 00283 NextBlock = &Heap->Blocks + FreeBlock->Data[0].Blink; 00284 NextBlock->Data[0].Flink = FreeBlock - &Heap->Blocks; 00285 } 00286 00287 PVOID 00288 HeapAllocate( 00289 PVOID HeapHandle, 00290 SIZE_T ByteSize, 00291 ULONG Tag) 00292 { 00293 PHEAP Heap = HeapHandle; 00294 PHEAP_BLOCK Block, NextBlock; 00295 USHORT BlockSize, Remaining; 00296 ULONGLONG Time = __rdtsc(); 00297 00298 #ifdef FREELDR_HEAP_VERIFIER 00299 /* Add space for a size field and 2 redzones */ 00300 ByteSize += REDZONE_ALLOCATION; 00301 #endif 00302 00303 /* Check if the allocation is too large */ 00304 if ((ByteSize + sizeof(HEAP_BLOCK)) > MAXUSHORT * sizeof(HEAP_BLOCK)) 00305 { 00306 ERR("HEAP: Allocation of 0x%lx bytes too large\n", ByteSize); 00307 return NULL; 00308 } 00309 00310 /* We need a proper tag */ 00311 if (Tag == 0) Tag = 'enoN'; 00312 00313 /* Calculate alloc size */ 00314 BlockSize = (USHORT)((ByteSize + sizeof(HEAP_BLOCK) - 1) / sizeof(HEAP_BLOCK)); 00315 00316 /* Walk the free block list */ 00317 Block = &Heap->Blocks + Heap->TerminatingBlock; 00318 for (Block = &Heap->Blocks + Block->Data[0].Flink; 00319 Block->Size != 0; 00320 Block = &Heap->Blocks + Block->Data[0].Flink) 00321 { 00322 ASSERT(Block->Tag == 0); 00323 00324 /* Continue, if its too small */ 00325 if (Block->Size < BlockSize) continue; 00326 00327 /* This block is just fine, use it */ 00328 Block->Tag = Tag; 00329 00330 /* Remove this entry from the free list */ 00331 HeapRemoveFreeList(Heap, Block); 00332 00333 /* Calculate the remaining size */ 00334 Remaining = Block->Size - BlockSize; 00335 00336 /* Check if the remaining space is large enough for a new block */ 00337 if (Remaining > 1) 00338 { 00339 /* Make the allocated block as large as neccessary */ 00340 Block->Size = BlockSize; 00341 00342 /* Get pointer to the new block */ 00343 NextBlock = Block + 1 + BlockSize; 00344 00345 /* Make it a free block */ 00346 NextBlock->Tag = 0; 00347 NextBlock->Size = Remaining - 1; 00348 NextBlock->PreviousSize = BlockSize; 00349 BlockSize = NextBlock->Size; 00350 HeapInsertFreeList(Heap, NextBlock); 00351 00352 /* Advance to the next block */ 00353 NextBlock = NextBlock + 1 + BlockSize; 00354 } 00355 else 00356 { 00357 /* Not enough left, use the full block */ 00358 BlockSize = Block->Size; 00359 00360 /* Get the next block */ 00361 NextBlock = Block + 1 + BlockSize; 00362 } 00363 00364 /* Update the next blocks back link */ 00365 NextBlock->PreviousSize = BlockSize; 00366 00367 /* Update heap usage */ 00368 Heap->NumAllocs++; 00369 Heap->CurrentAllocBytes += Block->Size * sizeof(HEAP_BLOCK); 00370 Heap->MaxAllocBytes = max(Heap->MaxAllocBytes, Heap->CurrentAllocBytes); 00371 Heap->LargestAllocation = max(Heap->LargestAllocation, 00372 Block->Size * sizeof(HEAP_BLOCK)); 00373 Heap->AllocationTime += (__rdtsc() - Time); 00374 00375 TRACE("HeapAllocate(%p, %ld, %.4s) -> return %p\n", 00376 HeapHandle, ByteSize, &Tag, Block->Data); 00377 00378 /* HACK: zero out the allocation */ 00379 RtlZeroMemory(Block->Data, Block->Size * sizeof(HEAP_BLOCK)); 00380 00381 #ifdef FREELDR_HEAP_VERIFIER 00382 /* Write size and redzones */ 00383 *REDZONE_SIZE(Block) = ByteSize - REDZONE_ALLOCATION; 00384 *REDZONE_LOW(Block) = REDZONE_MARK; 00385 *REDZONE_HI(Block) = REDZONE_MARK; 00386 00387 /* Allcoation starts after size field and redzone */ 00388 return (PUCHAR)Block->Data + REDZONE_LOW_OFFSET; 00389 #endif 00390 /* Return pointer to the data */ 00391 return Block->Data; 00392 } 00393 00394 /* We found nothing */ 00395 WARN("HEAP: nothing suitable found for 0x%lx bytes\n", ByteSize); 00396 return NULL; 00397 } 00398 00399 VOID 00400 HeapFree( 00401 PVOID HeapHandle, 00402 PVOID Pointer, 00403 ULONG Tag) 00404 { 00405 PHEAP Heap = HeapHandle; 00406 PHEAP_BLOCK Block, PrevBlock, NextBlock; 00407 ULONGLONG Time = __rdtsc(); 00408 TRACE("HeapFree(%p, %p)\n", HeapHandle, Pointer); 00409 ASSERT(Tag != 'dnE#'); 00410 00411 /* Check if the block is really inside this heap */ 00412 if ((Pointer < (PVOID)(Heap + 1)) || 00413 (Pointer > (PVOID)((PUCHAR)Heap + Heap->MaximumSize))) 00414 { 00415 ERR("HEAP: trying to free %p outside of heap %p\n", Pointer, Heap); 00416 ASSERT(FALSE); 00417 } 00418 00419 Block = ((PHEAP_BLOCK)Pointer) - 1; 00420 #ifdef FREELDR_HEAP_VERIFIER 00421 Block = (PHEAP_BLOCK)((PUCHAR)Block - REDZONE_LOW_OFFSET); 00422 00423 /* Verify size and redzones */ 00424 ASSERT(*REDZONE_SIZE(Block) <= Block->Size * sizeof(HEAP_BLOCK)); 00425 ASSERT(*REDZONE_LOW(Block) == REDZONE_MARK); 00426 ASSERT(*REDZONE_HI(Block) == REDZONE_MARK); 00427 #endif 00428 00429 /* Check if the tag matches */ 00430 if ((Tag && (Block->Tag != Tag)) || (Block->Tag == 0)) 00431 { 00432 ERR("HEAP: Bad tag! Pointer=%p: block tag '%.4s', requested '%.4s', size=0x%lx\n", 00433 Pointer, &Block->Tag, &Tag, Block->Size); 00434 ASSERT(FALSE); 00435 } 00436 00437 /* Mark as free */ 00438 Block->Tag = 0; 00439 00440 /* Update heap usage */ 00441 Heap->NumFrees++; 00442 Heap->CurrentAllocBytes -= Block->Size * sizeof(HEAP_BLOCK); 00443 00444 /* Get pointers to the next and previous block */ 00445 PrevBlock = Block - Block->PreviousSize - 1; 00446 NextBlock = Block + Block->Size + 1; 00447 00448 /* Check if next block is free */ 00449 if ((NextBlock->Tag == 0) && 00450 ((Block->Size + NextBlock->Size + 1) <= MAXUSHORT)) 00451 { 00452 /* Merge next block into current */ 00453 Block->Size += NextBlock->Size + 1; 00454 HeapRemoveFreeList(Heap, NextBlock); 00455 00456 NextBlock = Block + Block->Size + 1; 00457 } 00458 00459 /* Check if there is a block before and it's free */ 00460 if ((Block->PreviousSize != 0) && (PrevBlock->Tag == 0) && 00461 ((PrevBlock->Size + Block->Size + 1) <= MAXUSHORT)) 00462 { 00463 /* Merge current block into previous */ 00464 PrevBlock->Size += Block->Size + 1; 00465 Block = PrevBlock; 00466 } 00467 else 00468 { 00469 /* Insert the entry into the free list */ 00470 HeapInsertFreeList(Heap, Block); 00471 } 00472 00473 /* Update the next block's back link */ 00474 NextBlock->PreviousSize = Block->Size; 00475 00476 Heap->FreeTime += (__rdtsc() - Time); 00477 } 00478 00479 00480 /* Wrapper functions *********************************************************/ 00481 00482 VOID 00483 MmInitializeHeap(PVOID PageLookupTable) 00484 { 00485 TRACE("MmInitializeHeap()\n"); 00486 00487 /* Create the default heap */ 00488 FrLdrDefaultHeap = HeapCreate(DEFAULT_HEAP_SIZE, LoaderOsloaderHeap); 00489 ASSERT(FrLdrDefaultHeap); 00490 00491 /* Create a temporary heap */ 00492 FrLdrTempHeap = HeapCreate(TEMP_HEAP_SIZE, LoaderFirmwareTemporary); 00493 ASSERT(FrLdrTempHeap); 00494 00495 TRACE("MmInitializeHeap() done, default heap %p, temp heap %p\n", 00496 FrLdrDefaultHeap, FrLdrTempHeap); 00497 } 00498 00499 PVOID 00500 MmHeapAlloc(SIZE_T MemorySize) 00501 { 00502 return HeapAllocate(FrLdrDefaultHeap, MemorySize, 'pHmM'); 00503 } 00504 00505 VOID 00506 MmHeapFree(PVOID MemoryPointer) 00507 { 00508 HeapFree(FrLdrDefaultHeap, MemoryPointer, 'pHmM'); 00509 } 00510 00511 PVOID 00512 NTAPI 00513 ExAllocatePoolWithTag( 00514 IN POOL_TYPE PoolType, 00515 IN SIZE_T NumberOfBytes, 00516 IN ULONG Tag) 00517 { 00518 return HeapAllocate(FrLdrDefaultHeap, NumberOfBytes, Tag); 00519 } 00520 00521 PVOID 00522 NTAPI 00523 ExAllocatePool( 00524 IN POOL_TYPE PoolType, 00525 IN SIZE_T NumberOfBytes) 00526 { 00527 return HeapAllocate(FrLdrDefaultHeap, NumberOfBytes, 0); 00528 } 00529 00530 VOID 00531 NTAPI 00532 ExFreePool( 00533 IN PVOID P) 00534 { 00535 HeapFree(FrLdrDefaultHeap, P, 0); 00536 } 00537 00538 VOID 00539 NTAPI 00540 ExFreePoolWithTag( 00541 IN PVOID P, 00542 IN ULONG Tag) 00543 { 00544 HeapFree(FrLdrDefaultHeap, P, Tag); 00545 } 00546 00547 PVOID 00548 NTAPI 00549 RtlAllocateHeap( 00550 IN PVOID HeapHandle, 00551 IN ULONG Flags, 00552 IN SIZE_T Size) 00553 { 00554 PVOID ptr; 00555 00556 ptr = HeapAllocate(FrLdrDefaultHeap, Size, ' ltR'); 00557 if (ptr && (Flags & HEAP_ZERO_MEMORY)) 00558 { 00559 RtlZeroMemory(ptr, Size); 00560 } 00561 00562 return ptr; 00563 } 00564 00565 BOOLEAN 00566 NTAPI 00567 RtlFreeHeap( 00568 IN PVOID HeapHandle, 00569 IN ULONG Flags, 00570 IN PVOID HeapBase) 00571 { 00572 HeapFree(FrLdrDefaultHeap, HeapBase, ' ltR'); 00573 return TRUE; 00574 } 00575 Generated on Sun May 27 2012 04:19:16 for ReactOS by
1.7.6.1
|