1 /* $Id: ppool.c,v 1.20 2003/10/12 17:05:48 hbirr Exp $
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
);
76 MmInitializePagedPool(VOID
)
78 MmPagedPoolFirstFreeBlock
= (PMM_PPOOL_FREE_BLOCK_HEADER
)MmPagedPoolBase
;
80 * We are still at a high IRQL level at this point so explicitly commit
81 * the first page of the paged pool before writing the first block header.
83 MmCommitPagedPoolAddress((PVOID
)MmPagedPoolFirstFreeBlock
);
84 MmPagedPoolFirstFreeBlock
->Size
= MmPagedPoolSize
;
85 MmPagedPoolFirstFreeBlock
->NextFree
= NULL
;
87 #if MM_PPOOL_REDZONE_BYTES
88 MmPagedPoolFirstUsedBlock
= NULL
;
89 #endif//MM_PPOOL_REDZONE_BYTES
91 ExInitializeFastMutex(&MmPagedPoolLock
);
94 #ifdef ENABLE_VALIDATE_POOL
95 static void VerifyPagedPool ( int line
)
97 PMM_PPOOL_FREE_BLOCK_HEADER p
= MmPagedPoolFirstFreeBlock
;
99 DPRINT ( "VerifyPagedPool(%i):\n", line
);
102 DPRINT ( " 0x%x: %lu bytes (next 0x%x)\n", p
, p
->Size
, p
->NextFree
);
104 ASSERT_SIZE(p
->Size
);
108 DPRINT ( "VerifyPagedPool(%i): (%lu blocks)\n", line
, count
);
110 #define VerifyPagedPool() VerifyPagedPool(__LINE__)
112 #define VerifyPagedPool()
116 MmDbgPagedPoolRedZoneCheck ( const char* file
, int line
)
118 #if MM_PPOOL_REDZONE_BYTES
119 PMM_PPOOL_USED_BLOCK_HEADER pUsed
= MmPagedPoolFirstUsedBlock
;
126 PUCHAR Addr
= (PUCHAR
)block_to_address(pUsed
);
127 for ( i
= 0; i
< MM_PPOOL_REDZONE_BYTES
; i
++ )
129 bLow
= bLow
&& ( *(Addr
-i
-1) == MM_PPOOL_REDZONE_VALUE
);
130 bHigh
= bHigh
&& ( *(Addr
+pUsed
->UserSize
+i
) == MM_PPOOL_REDZONE_VALUE
);
132 if ( !bLow
|| !bHigh
)
134 const char* violation
= "High and Low-side";
135 if ( bHigh
) // high is okay, so it was just low failed
136 violation
= "Low-side";
137 else if ( bLow
) // low side is okay, so it was just high failed
138 violation
= "High-side";
139 DbgPrint("%s(%i): %s redzone violation detected for paged pool address 0x%x\n",
140 file
, line
, violation
, Addr
);
143 pUsed
= pUsed
->NextUsed
;
145 #endif//MM_PPOOL_REDZONE_BYTES
148 /**********************************************************************
150 * ExAllocatePagedPoolWithTag@12
159 ExAllocatePagedPoolWithTag (IN POOL_TYPE PoolType
,
160 IN ULONG NumberOfBytes
,
163 PMM_PPOOL_FREE_BLOCK_HEADER BestBlock
;
164 PMM_PPOOL_FREE_BLOCK_HEADER CurrentBlock
;
166 PMM_PPOOL_USED_BLOCK_HEADER NewBlock
;
167 PMM_PPOOL_FREE_BLOCK_HEADER NextBlock
;
168 PMM_PPOOL_FREE_BLOCK_HEADER PreviousBlock
;
169 PMM_PPOOL_FREE_BLOCK_HEADER BestPreviousBlock
;
173 ExAcquireFastMutex(&MmPagedPoolLock
);
176 * Don't bother allocating anything for a zero-byte block.
178 if (NumberOfBytes
== 0)
180 MmDbgPagedPoolRedZoneCheck(__FILE__
,__LINE__
);
181 ExReleaseFastMutex(&MmPagedPoolLock
);
185 DPRINT ( "ExAllocatePagedPoolWithTag(%i,%lu,%lu)\n", PoolType
, NumberOfBytes
, Tag
);
188 if (NumberOfBytes
>= PAGE_SIZE
)
190 Alignment
= PAGE_SIZE
;
192 else if (PoolType
== PagedPoolCacheAligned
)
194 Alignment
= MM_CACHE_LINE_SIZE
;
202 * Calculate the total number of bytes we will need.
204 BlockSize
= NumberOfBytes
+ sizeof(MM_PPOOL_USED_BLOCK_HEADER
) + 2*MM_PPOOL_REDZONE_BYTES
;
205 if (BlockSize
< sizeof(MM_PPOOL_FREE_BLOCK_HEADER
))
207 /* At least we need the size of the free block header. */
208 BlockSize
= sizeof(MM_PPOOL_FREE_BLOCK_HEADER
);
213 * Find the best-fitting block.
215 PreviousBlock
= NULL
;
216 BestPreviousBlock
= BestBlock
= NULL
;
217 CurrentBlock
= MmPagedPoolFirstFreeBlock
;
220 PVOID BestAlignedAddr
= NULL
;
221 while ( CurrentBlock
!= NULL
)
223 PVOID Addr
= block_to_address(CurrentBlock
);
224 PVOID CurrentBlockEnd
= (PVOID
)CurrentBlock
+ CurrentBlock
->Size
;
225 /* calculate last size-aligned address available within this block */
226 PVOID AlignedAddr
= MM_ROUND_DOWN(CurrentBlockEnd
-NumberOfBytes
-MM_PPOOL_REDZONE_BYTES
, Alignment
);
227 assert ( AlignedAddr
+NumberOfBytes
+MM_PPOOL_REDZONE_BYTES
<= CurrentBlockEnd
);
229 /* special case, this address is already size-aligned, and the right size */
230 if ( Addr
== AlignedAddr
)
232 BestAlignedAddr
= AlignedAddr
;
233 BestPreviousBlock
= PreviousBlock
;
234 BestBlock
= CurrentBlock
;
237 else if ( Addr
< (PVOID
)address_to_block(AlignedAddr
) )
240 * there's enough room to allocate our size-aligned memory out
241 * of this block, see if it's a better choice than any previous
244 if ( BestBlock
== NULL
|| BestBlock
->Size
> CurrentBlock
->Size
)
246 BestAlignedAddr
= AlignedAddr
;
247 BestPreviousBlock
= PreviousBlock
;
248 BestBlock
= CurrentBlock
;
252 PreviousBlock
= CurrentBlock
;
253 CurrentBlock
= CurrentBlock
->NextFree
;
257 * we found a best block can/should we chop a few bytes off the beginning
258 * into a separate memory block?
260 if ( BestBlock
!= NULL
)
262 PVOID Addr
= block_to_address(BestBlock
);
263 if ( BestAlignedAddr
!= Addr
)
265 PMM_PPOOL_FREE_BLOCK_HEADER NewFreeBlock
=
266 (PMM_PPOOL_FREE_BLOCK_HEADER
)address_to_block(BestAlignedAddr
);
267 assert ( BestAlignedAddr
> Addr
);
268 NewFreeBlock
->Size
= Addr
+ BestBlock
->Size
- BestAlignedAddr
;
269 ASSERT_SIZE(NewFreeBlock
->Size
);
270 BestBlock
->Size
= (size_t)NewFreeBlock
- (size_t)Addr
;
271 ASSERT_SIZE(BestBlock
->Size
);
273 DPRINT ( "breaking off preceding bytes into their own block...\n" );
274 DPRINT ( "NewFreeBlock 0x%x Size %lu (Old Block's new size %lu) NextFree 0x%x\n",
275 NewFreeBlock
, NewFreeBlock
->Size
, BestBlock
->Size
, BestBlock
->NextFree
);
277 /* insert the new block into the chain */
278 NewFreeBlock
->NextFree
= BestBlock
->NextFree
;
279 BestBlock
->NextFree
= NewFreeBlock
;
281 /* we want the following code to use our size-aligned block */
282 BestPreviousBlock
= BestBlock
;
283 BestBlock
= NewFreeBlock
;
290 * non-size-aligned block search
292 else while ( CurrentBlock
!= NULL
)
294 if ( CurrentBlock
->Size
>= BlockSize
295 && ( BestBlock
== NULL
|| BestBlock
->Size
> CurrentBlock
->Size
)
298 BestPreviousBlock
= PreviousBlock
;
299 BestBlock
= CurrentBlock
;
302 PreviousBlock
= CurrentBlock
;
303 CurrentBlock
= CurrentBlock
->NextFree
;
307 * We didn't find anything suitable at all.
309 if (BestBlock
== NULL
)
311 DPRINT("ExAllocatePagedPoolWithTag() - nothing suitable found, returning NULL\n" );
312 ExReleaseFastMutex(&MmPagedPoolLock
);
316 DPRINT("BestBlock 0x%x NextFree 0x%x\n", BestBlock
, BestBlock
->NextFree
);
321 * Is there enough space to create a second block from the unused portion.
323 if ( BestBlock
->Size
> BlockSize
324 && (BestBlock
->Size
- BlockSize
) > sizeof(MM_PPOOL_FREE_BLOCK_HEADER
)
327 ULONG NewSize
= BestBlock
->Size
- BlockSize
;
328 ASSERT_SIZE ( NewSize
);
330 //DPRINT("creating 2nd block from unused portion\n");
331 DPRINT("BestBlock 0x%x Size 0x%x BlockSize 0x%x NewSize 0x%x\n",
332 BestBlock
, BestBlock
->Size
, BlockSize
, NewSize
);
335 * Create the new free block.
337 //DPRINT("creating the new free block");
338 NextBlock
= (PMM_PPOOL_FREE_BLOCK_HEADER
)((char*)BestBlock
+ BlockSize
);
340 NextBlock
->Size
= NewSize
;
341 ASSERT_SIZE ( NextBlock
->Size
);
343 NextBlock
->NextFree
= BestBlock
->NextFree
;
347 * Replace the old free block with it.
349 //DPRINT("replacing old free block with it");
350 if (BestPreviousBlock
== NULL
)
352 //DPRINT("(from beginning)");
353 MmPagedPoolFirstFreeBlock
= NextBlock
;
357 //DPRINT("(from previous)");
358 BestPreviousBlock
->NextFree
= NextBlock
;
363 * Create the new used block header.
365 //DPRINT("create new used block header");
366 NewBlock
= (PMM_PPOOL_USED_BLOCK_HEADER
)BestBlock
;
368 NewBlock
->Size
= BlockSize
;
369 ASSERT_SIZE ( NewBlock
->Size
);
374 ULONG NewSize
= BestBlock
->Size
;
377 * Remove the selected block from the list of free blocks.
379 //DPRINT ( "Removing selected block from free block list\n" );
380 if (BestPreviousBlock
== NULL
)
382 MmPagedPoolFirstFreeBlock
= BestBlock
->NextFree
;
386 BestPreviousBlock
->NextFree
= BestBlock
->NextFree
;
390 * Set up the header of the new block
392 NewBlock
= (PMM_PPOOL_USED_BLOCK_HEADER
)BestBlock
;
393 NewBlock
->Size
= NewSize
;
394 ASSERT_SIZE ( NewBlock
->Size
);
397 #if MM_PPOOL_REDZONE_BYTES
398 // now add the block to the used block list
399 NewBlock
->NextUsed
= MmPagedPoolFirstUsedBlock
;
400 MmPagedPoolFirstUsedBlock
= NewBlock
;
401 #endif//MM_PPOOL_REDZONE_BYTES
405 ExReleaseFastMutex(&MmPagedPoolLock
);
407 BlockAddress
= block_to_address ( NewBlock
);
409 memset(BlockAddress
, 0, NumberOfBytes
);
411 #if MM_PPOOL_REDZONE_BYTES
412 NewBlock
->UserSize
= NumberOfBytes
;
413 // write out buffer-overrun detection bytes
415 PUCHAR Addr
= (PUCHAR
)BlockAddress
;
416 //DbgPrint ( "writing buffer-overrun detection bytes" );
417 memset ( Addr
- MM_PPOOL_REDZONE_BYTES
,
418 MM_PPOOL_REDZONE_VALUE
, MM_PPOOL_REDZONE_BYTES
);
419 memset ( Addr
+ NewBlock
->UserSize
, MM_PPOOL_REDZONE_VALUE
,
420 MM_PPOOL_REDZONE_BYTES
);
421 /*for ( i = 0; i < MM_PPOOL_REDZONE_BYTES; i++ )
426 *(Addr+NewBlock->UserSize+i) = 0xCD;
428 //DbgPrint ( "done!\n" );
431 #endif//MM_PPOOL_REDZONE_BYTES
433 return(BlockAddress
);
437 ExFreePagedPool(IN PVOID Block
)
439 PMM_PPOOL_FREE_BLOCK_HEADER PreviousBlock
;
440 PMM_PPOOL_USED_BLOCK_HEADER UsedBlock
= address_to_block(Block
);
441 ULONG UsedSize
= UsedBlock
->Size
;
442 PMM_PPOOL_FREE_BLOCK_HEADER FreeBlock
=
443 (PMM_PPOOL_FREE_BLOCK_HEADER
)UsedBlock
;
444 PMM_PPOOL_FREE_BLOCK_HEADER NextBlock
;
445 PMM_PPOOL_FREE_BLOCK_HEADER NextNextBlock
;
447 #if MM_PPOOL_REDZONE_BYTES
448 // write out buffer-overrun detection bytes
451 PUCHAR Addr
= (PUCHAR
)Block
;
452 //DbgPrint ( "checking buffer-overrun detection bytes..." );
453 for ( i
= 0; i
< MM_PPOOL_REDZONE_BYTES
; i
++ )
455 assert ( *(Addr
-i
-1) == MM_PPOOL_REDZONE_VALUE
);
456 assert ( *(Addr
+UsedBlock
->UserSize
+i
) == MM_PPOOL_REDZONE_VALUE
);
458 //DbgPrint ( "done!\n" );
460 #endif//MM_PPOOL_REDZONE_BYTES
462 ExAcquireFastMutex(&MmPagedPoolLock
);
464 #if MM_PPOOL_REDZONE_BYTES
465 // remove from used list...
467 PMM_PPOOL_USED_BLOCK_HEADER pPrev
= MmPagedPoolFirstUsedBlock
;
468 if ( pPrev
== UsedBlock
)
470 // special-case, our freeing block is first in list...
471 MmPagedPoolFirstUsedBlock
= pPrev
->NextUsed
;
475 while ( pPrev
&& pPrev
->NextUsed
!= UsedBlock
)
476 pPrev
= pPrev
->NextUsed
;
477 // if this assert fails - memory has been corrupted
478 // ( or I have a logic error...! )
479 assert ( pPrev
->NextUsed
== UsedBlock
);
480 pPrev
->NextUsed
= UsedBlock
->NextUsed
;
483 #endif//MM_PPOOL_REDZONE_BYTES
486 * Begin setting up the newly freed block's header.
488 FreeBlock
->Size
= UsedSize
;
489 ASSERT_SIZE ( FreeBlock
->Size
);
492 * Find the blocks immediately before and after the newly freed block on the free list.
494 PreviousBlock
= NULL
;
495 NextBlock
= MmPagedPoolFirstFreeBlock
;
496 while (NextBlock
!= NULL
&& NextBlock
< FreeBlock
)
498 PreviousBlock
= NextBlock
;
499 NextBlock
= NextBlock
->NextFree
;
503 * Insert the freed block on the free list.
505 if (PreviousBlock
== NULL
)
507 FreeBlock
->NextFree
= MmPagedPoolFirstFreeBlock
;
508 MmPagedPoolFirstFreeBlock
= FreeBlock
;
512 PreviousBlock
->NextFree
= FreeBlock
;
513 FreeBlock
->NextFree
= NextBlock
;
517 * If the next block is immediately adjacent to the newly freed one then
520 if (NextBlock
!= NULL
&&
521 ((char*)FreeBlock
+ FreeBlock
->Size
) == (char*)NextBlock
)
523 FreeBlock
->Size
= FreeBlock
->Size
+ NextBlock
->Size
;
524 ASSERT_SIZE ( FreeBlock
->Size
);
525 FreeBlock
->NextFree
= NextBlock
->NextFree
;
526 NextNextBlock
= NextBlock
->NextFree
;
530 NextNextBlock
= NextBlock
;
534 * If the previous block is adjacent to the newly freed one then
537 if (PreviousBlock
!= NULL
&&
538 ((char*)PreviousBlock
+ PreviousBlock
->Size
) == (char*)FreeBlock
)
540 PreviousBlock
->Size
= PreviousBlock
->Size
+ FreeBlock
->Size
;
541 ASSERT_SIZE ( PreviousBlock
->Size
);
542 PreviousBlock
->NextFree
= NextNextBlock
;
547 ExReleaseFastMutex(&MmPagedPoolLock
);