3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/mm/ppool.c
6 * PURPOSE: Implements the paged pool
7 * PROGRAMMER: David Welch (welch@mcmail.com)
12 /* INCLUDES *****************************************************************/
14 #include <ddk/ntddk.h>
15 #include <internal/pool.h>
16 #include <internal/mm.h>
19 #include <internal/debug.h>
21 /* GLOBALS *******************************************************************/
23 /* Enable strict checking of the paged pool on every allocation */
24 #define ENABLE_VALIDATE_POOL
27 #define assert(x) if (!(x)) {DbgPrint("Assertion "#x" failed at %s:%d\n", __FILE__,__LINE__); KeBugCheck(0); }
28 #define ASSERT_SIZE(n) assert ( (n) <= MmPagedPoolSize && (n) >= 0 )
29 #define ASSERT_PTR(p) assert ( ((size_t)(p)) >= ((size_t)MmPagedPoolBase) && ((size_t)(p)) < ((size_t)(MmPagedPoolBase+MmPagedPoolSize)) )
31 // to disable buffer over/under-run detection, set the following macro to 0
32 #define MM_PPOOL_REDZONE_BYTES 4
33 #define MM_PPOOL_REDZONE_VALUE 0xCD
35 typedef struct _MM_PPOOL_FREE_BLOCK_HEADER
38 struct _MM_PPOOL_FREE_BLOCK_HEADER* NextFree;
39 } MM_PPOOL_FREE_BLOCK_HEADER, *PMM_PPOOL_FREE_BLOCK_HEADER;
41 typedef struct _MM_PPOOL_USED_BLOCK_HEADER
44 #if MM_PPOOL_REDZONE_BYTES
45 ULONG UserSize; // how many bytes the user actually asked for...
46 struct _MM_PPOOL_USED_BLOCK_HEADER* NextUsed;
47 #endif//MM_PPOOL_REDZONE_BYTES
48 } MM_PPOOL_USED_BLOCK_HEADER, *PMM_PPOOL_USED_BLOCK_HEADER;
50 PVOID MmPagedPoolBase;
51 ULONG MmPagedPoolSize;
52 static FAST_MUTEX MmPagedPoolLock;
53 static PMM_PPOOL_FREE_BLOCK_HEADER MmPagedPoolFirstFreeBlock;
54 #if MM_PPOOL_REDZONE_BYTES
55 static PMM_PPOOL_USED_BLOCK_HEADER MmPagedPoolFirstUsedBlock;
56 #endif//MM_PPOOL_REDZONE_BYTES
58 /* FUNCTIONS *****************************************************************/
60 inline static void* block_to_address ( PVOID blk )
62 * FUNCTION: Translate a block header address to the corresponding block
66 return ( (void *) ((char*)blk + sizeof(MM_PPOOL_USED_BLOCK_HEADER) + MM_PPOOL_REDZONE_BYTES) );
69 inline static PMM_PPOOL_USED_BLOCK_HEADER address_to_block(PVOID addr)
71 return (PMM_PPOOL_USED_BLOCK_HEADER)
72 ( ((char*)addr) - sizeof(MM_PPOOL_USED_BLOCK_HEADER) - MM_PPOOL_REDZONE_BYTES );
75 VOID MmInitializePagedPool(VOID)
77 MmPagedPoolFirstFreeBlock = (PMM_PPOOL_FREE_BLOCK_HEADER)MmPagedPoolBase;
79 * We are still at a high IRQL level at this point so explicitly commit
80 * the first page of the paged pool before writing the first block header.
82 MmCommitPagedPoolAddress((PVOID)MmPagedPoolFirstFreeBlock);
83 MmPagedPoolFirstFreeBlock->Size = MmPagedPoolSize;
84 MmPagedPoolFirstFreeBlock->NextFree = NULL;
86 #if MM_PPOOL_REDZONE_BYTES
87 MmPagedPoolFirstUsedBlock = NULL;
88 #endif//MM_PPOOL_REDZONE_BYTES
90 ExInitializeFastMutex(&MmPagedPoolLock);
93 #ifdef ENABLE_VALIDATE_POOL
94 static void VerifyPagedPool ( int line )
96 PMM_PPOOL_FREE_BLOCK_HEADER p = MmPagedPoolFirstFreeBlock;
98 DPRINT ( "VerifyPagedPool(%i):\n", line );
101 DPRINT ( " 0x%x: %lu bytes (next 0x%x)\n", p, p->Size, p->NextFree );
103 ASSERT_SIZE(p->Size);
107 DPRINT ( "VerifyPagedPool(%i): (%lu blocks)\n", line, count );
109 #define VerifyPagedPool() VerifyPagedPool(__LINE__)
111 #define VerifyPagedPool()
115 MmDbgPagedPoolRedZoneCheck ( const char* file, int line )
117 #if MM_PPOOL_REDZONE_BYTES
118 PMM_PPOOL_USED_BLOCK_HEADER pUsed = MmPagedPoolFirstUsedBlock;
125 PUCHAR Addr = (PUCHAR)block_to_address(pUsed);
126 for ( i = 0; i < MM_PPOOL_REDZONE_BYTES; i++ )
128 bLow = bLow && ( *(Addr-i-1) == MM_PPOOL_REDZONE_VALUE );
129 bHigh = bHigh && ( *(Addr+pUsed->UserSize+i) == MM_PPOOL_REDZONE_VALUE );
131 if ( !bLow || !bHigh )
133 const char* violation = "High and Low-side";
134 if ( bHigh ) // high is okay, so it was just low failed
135 violation = "Low-side";
136 else if ( bLow ) // low side is okay, so it was just high failed
137 violation = "High-side";
138 DbgPrint("%s(%i): %s redzone violation detected for paged pool address 0x%x\n",
139 file, line, violation, Addr );
142 pUsed = pUsed->NextUsed;
144 #endif//MM_PPOOL_REDZONE_BYTES
147 /**********************************************************************
149 * ExAllocatePagedPoolWithTag@12
158 ExAllocatePagedPoolWithTag (IN POOL_TYPE PoolType,
159 IN ULONG NumberOfBytes,
162 PMM_PPOOL_FREE_BLOCK_HEADER BestBlock;
163 PMM_PPOOL_FREE_BLOCK_HEADER CurrentBlock;
165 PMM_PPOOL_USED_BLOCK_HEADER NewBlock;
166 PMM_PPOOL_FREE_BLOCK_HEADER NextBlock;
167 PMM_PPOOL_FREE_BLOCK_HEADER PreviousBlock;
168 PMM_PPOOL_FREE_BLOCK_HEADER BestPreviousBlock;
172 ExAcquireFastMutex(&MmPagedPoolLock);
175 * Don't bother allocating anything for a zero-byte block.
177 if (NumberOfBytes == 0)
179 MmDbgPagedPoolRedZoneCheck(__FILE__,__LINE__);
180 ExReleaseFastMutex(&MmPagedPoolLock);
184 DPRINT ( "ExAllocatePagedPoolWithTag(%i,%lu,%lu)\n", PoolType, NumberOfBytes, Tag );
187 if (NumberOfBytes >= PAGE_SIZE)
189 Alignment = PAGE_SIZE;
191 else if (PoolType == PagedPoolCacheAligned)
193 Alignment = MM_CACHE_LINE_SIZE;
201 * Calculate the total number of bytes we will need.
203 BlockSize = NumberOfBytes + sizeof(MM_PPOOL_USED_BLOCK_HEADER) + 2*MM_PPOOL_REDZONE_BYTES;
204 if (BlockSize < sizeof(MM_PPOOL_FREE_BLOCK_HEADER))
206 /* At least we need the size of the free block header. */
207 BlockSize = sizeof(MM_PPOOL_FREE_BLOCK_HEADER);
212 * Find the best-fitting block.
214 PreviousBlock = NULL;
215 BestPreviousBlock = BestBlock = NULL;
216 CurrentBlock = MmPagedPoolFirstFreeBlock;
219 PVOID BestAlignedAddr = NULL;
220 while ( CurrentBlock != NULL )
222 PVOID Addr = block_to_address(CurrentBlock);
223 PVOID CurrentBlockEnd = (PVOID)CurrentBlock + CurrentBlock->Size;
224 /* calculate last size-aligned address available within this block */
225 PVOID AlignedAddr = MM_ROUND_DOWN(CurrentBlockEnd-NumberOfBytes-MM_PPOOL_REDZONE_BYTES, Alignment);
226 assert ( AlignedAddr+NumberOfBytes+MM_PPOOL_REDZONE_BYTES <= CurrentBlockEnd );
228 /* special case, this address is already size-aligned, and the right size */
229 if ( Addr == AlignedAddr )
231 BestAlignedAddr = AlignedAddr;
232 BestPreviousBlock = PreviousBlock;
233 BestBlock = CurrentBlock;
236 else if ( Addr < (PVOID)address_to_block(AlignedAddr) )
239 * there's enough room to allocate our size-aligned memory out
240 * of this block, see if it's a better choice than any previous
243 if ( BestBlock == NULL || BestBlock->Size > CurrentBlock->Size )
245 BestAlignedAddr = AlignedAddr;
246 BestPreviousBlock = PreviousBlock;
247 BestBlock = CurrentBlock;
251 PreviousBlock = CurrentBlock;
252 CurrentBlock = CurrentBlock->NextFree;
256 * we found a best block can/should we chop a few bytes off the beginning
257 * into a separate memory block?
259 if ( BestBlock != NULL )
261 PVOID Addr = block_to_address(BestBlock);
262 if ( BestAlignedAddr != Addr )
264 PMM_PPOOL_FREE_BLOCK_HEADER NewFreeBlock =
265 (PMM_PPOOL_FREE_BLOCK_HEADER)address_to_block(BestAlignedAddr);
266 assert ( BestAlignedAddr > Addr );
267 NewFreeBlock->Size = Addr + BestBlock->Size - BestAlignedAddr;
268 ASSERT_SIZE(NewFreeBlock->Size);
269 BestBlock->Size = (size_t)NewFreeBlock - (size_t)Addr;
270 ASSERT_SIZE(BestBlock->Size);
272 DPRINT ( "breaking off preceding bytes into their own block...\n" );
273 DPRINT ( "NewFreeBlock 0x%x Size %lu (Old Block's new size %lu) NextFree 0x%x\n",
274 NewFreeBlock, NewFreeBlock->Size, BestBlock->Size, BestBlock->NextFree );
276 /* insert the new block into the chain */
277 NewFreeBlock->NextFree = BestBlock->NextFree;
278 BestBlock->NextFree = NewFreeBlock;
280 /* we want the following code to use our size-aligned block */
281 BestPreviousBlock = BestBlock;
282 BestBlock = NewFreeBlock;
289 * non-size-aligned block search
291 else while ( CurrentBlock != NULL )
293 if ( CurrentBlock->Size >= BlockSize
294 && ( BestBlock == NULL || BestBlock->Size > CurrentBlock->Size )
297 BestPreviousBlock = PreviousBlock;
298 BestBlock = CurrentBlock;
301 PreviousBlock = CurrentBlock;
302 CurrentBlock = CurrentBlock->NextFree;
306 * We didn't find anything suitable at all.
308 if (BestBlock == NULL)
310 DPRINT("ExAllocatePagedPoolWithTag() - nothing suitable found, returning NULL\n" );
311 ExReleaseFastMutex(&MmPagedPoolLock);
315 DPRINT("BestBlock 0x%x NextFree 0x%x\n", BestBlock, BestBlock->NextFree );
320 * Is there enough space to create a second block from the unused portion.
322 if ( BestBlock->Size > BlockSize
323 && (BestBlock->Size - BlockSize) > sizeof(MM_PPOOL_FREE_BLOCK_HEADER)
326 ULONG NewSize = BestBlock->Size - BlockSize;
327 ASSERT_SIZE ( NewSize );
329 //DPRINT("creating 2nd block from unused portion\n");
330 DPRINT("BestBlock 0x%x Size 0x%x BlockSize 0x%x NewSize 0x%x\n",
331 BestBlock, BestBlock->Size, BlockSize, NewSize );
334 * Create the new free block.
336 //DPRINT("creating the new free block");
337 NextBlock = (PMM_PPOOL_FREE_BLOCK_HEADER)((char*)BestBlock + BlockSize);
339 NextBlock->Size = NewSize;
340 ASSERT_SIZE ( NextBlock->Size );
342 NextBlock->NextFree = BestBlock->NextFree;
346 * Replace the old free block with it.
348 //DPRINT("replacing old free block with it");
349 if (BestPreviousBlock == NULL)
351 //DPRINT("(from beginning)");
352 MmPagedPoolFirstFreeBlock = NextBlock;
356 //DPRINT("(from previous)");
357 BestPreviousBlock->NextFree = NextBlock;
362 * Create the new used block header.
364 //DPRINT("create new used block header");
365 NewBlock = (PMM_PPOOL_USED_BLOCK_HEADER)BestBlock;
367 NewBlock->Size = BlockSize;
368 ASSERT_SIZE ( NewBlock->Size );
373 ULONG NewSize = BestBlock->Size;
376 * Remove the selected block from the list of free blocks.
378 //DPRINT ( "Removing selected block from free block list\n" );
379 if (BestPreviousBlock == NULL)
381 MmPagedPoolFirstFreeBlock = BestBlock->NextFree;
385 BestPreviousBlock->NextFree = BestBlock->NextFree;
389 * Set up the header of the new block
391 NewBlock = (PMM_PPOOL_USED_BLOCK_HEADER)BestBlock;
392 NewBlock->Size = NewSize;
393 ASSERT_SIZE ( NewBlock->Size );
396 #if MM_PPOOL_REDZONE_BYTES
397 // now add the block to the used block list
398 NewBlock->NextUsed = MmPagedPoolFirstUsedBlock;
399 MmPagedPoolFirstUsedBlock = NewBlock;
400 #endif//MM_PPOOL_REDZONE_BYTES
404 ExReleaseFastMutex(&MmPagedPoolLock);
406 BlockAddress = block_to_address ( NewBlock );
408 memset(BlockAddress, 0, NumberOfBytes);
410 #if MM_PPOOL_REDZONE_BYTES
411 NewBlock->UserSize = NumberOfBytes;
412 // write out buffer-overrun detection bytes
414 PUCHAR Addr = (PUCHAR)BlockAddress;
415 //DbgPrint ( "writing buffer-overrun detection bytes" );
416 memset ( Addr - MM_PPOOL_REDZONE_BYTES,
417 MM_PPOOL_REDZONE_VALUE, MM_PPOOL_REDZONE_BYTES );
418 memset ( Addr + NewBlock->UserSize, MM_PPOOL_REDZONE_VALUE,
419 MM_PPOOL_REDZONE_BYTES );
420 /*for ( i = 0; i < MM_PPOOL_REDZONE_BYTES; i++ )
425 *(Addr+NewBlock->UserSize+i) = 0xCD;
427 //DbgPrint ( "done!\n" );
430 #endif//MM_PPOOL_REDZONE_BYTES
432 return(BlockAddress);
436 ExFreePagedPool(IN PVOID Block)
438 PMM_PPOOL_FREE_BLOCK_HEADER PreviousBlock;
439 PMM_PPOOL_USED_BLOCK_HEADER UsedBlock = address_to_block(Block);
440 ULONG UsedSize = UsedBlock->Size;
441 PMM_PPOOL_FREE_BLOCK_HEADER FreeBlock =
442 (PMM_PPOOL_FREE_BLOCK_HEADER)UsedBlock;
443 PMM_PPOOL_FREE_BLOCK_HEADER NextBlock;
444 PMM_PPOOL_FREE_BLOCK_HEADER NextNextBlock;
446 #if MM_PPOOL_REDZONE_BYTES
447 // write out buffer-overrun detection bytes
450 PUCHAR Addr = (PUCHAR)Block;
451 //DbgPrint ( "checking buffer-overrun detection bytes..." );
452 for ( i = 0; i < MM_PPOOL_REDZONE_BYTES; i++ )
454 assert ( *(Addr-i-1) == MM_PPOOL_REDZONE_VALUE );
455 assert ( *(Addr+UsedBlock->UserSize+i) == MM_PPOOL_REDZONE_VALUE );
457 //DbgPrint ( "done!\n" );
459 #endif//MM_PPOOL_REDZONE_BYTES
461 ExAcquireFastMutex(&MmPagedPoolLock);
463 #if MM_PPOOL_REDZONE_BYTES
464 // remove from used list...
466 PMM_PPOOL_USED_BLOCK_HEADER pPrev = MmPagedPoolFirstUsedBlock;
467 if ( pPrev == UsedBlock )
469 // special-case, our freeing block is first in list...
470 MmPagedPoolFirstUsedBlock = pPrev->NextUsed;
474 while ( pPrev && pPrev->NextUsed != UsedBlock )
475 pPrev = pPrev->NextUsed;
476 // if this assert fails - memory has been corrupted
477 // ( or I have a logic error...! )
478 assert ( pPrev->NextUsed == UsedBlock );
479 pPrev->NextUsed = UsedBlock->NextUsed;
482 #endif//MM_PPOOL_REDZONE_BYTES
485 * Begin setting up the newly freed block's header.
487 FreeBlock->Size = UsedSize;
488 ASSERT_SIZE ( FreeBlock->Size );
491 * Find the blocks immediately before and after the newly freed block on the free list.
493 PreviousBlock = NULL;
494 NextBlock = MmPagedPoolFirstFreeBlock;
495 while (NextBlock != NULL && NextBlock < FreeBlock)
497 PreviousBlock = NextBlock;
498 NextBlock = NextBlock->NextFree;
502 * Insert the freed block on the free list.
504 if (PreviousBlock == NULL)
506 FreeBlock->NextFree = MmPagedPoolFirstFreeBlock;
507 MmPagedPoolFirstFreeBlock = FreeBlock;
511 PreviousBlock->NextFree = FreeBlock;
512 FreeBlock->NextFree = NextBlock;
516 * If the next block is immediately adjacent to the newly freed one then
519 if (NextBlock != NULL &&
520 ((char*)FreeBlock + FreeBlock->Size) == (char*)NextBlock)
522 FreeBlock->Size = FreeBlock->Size + NextBlock->Size;
523 ASSERT_SIZE ( FreeBlock->Size );
524 FreeBlock->NextFree = NextBlock->NextFree;
525 NextNextBlock = NextBlock->NextFree;
529 NextNextBlock = NextBlock;
533 * If the previous block is adjacent to the newly freed one then
536 if (PreviousBlock != NULL &&
537 ((char*)PreviousBlock + PreviousBlock->Size) == (char*)FreeBlock)
539 PreviousBlock->Size = PreviousBlock->Size + FreeBlock->Size;
540 ASSERT_SIZE ( PreviousBlock->Size );
541 PreviousBlock->NextFree = NextNextBlock;
546 ExReleaseFastMutex(&MmPagedPoolLock);