mirror of
https://github.com/reactos/reactos.git
synced 2024-11-09 08:08:38 +00:00
c501d8112c
svn path=/branches/aicom-network-fixes/; revision=34994
1798 lines
45 KiB
C
1798 lines
45 KiB
C
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS kernel
|
|
* FILE: ntoskrnl/mm/npool.c
|
|
* PURPOSE: Implements the kernel memory pool
|
|
*
|
|
* PROGRAMMERS: David Welch (welch@cwcom.net)
|
|
* Iwan Fatahi (i_fatahi@hotmail.com)
|
|
* Robert Bergkvist (fragdance@hotmail.com)
|
|
*/
|
|
|
|
/* INCLUDES ****************************************************************/
|
|
|
|
#include <ntoskrnl.h>
|
|
#define NDEBUG
|
|
#include <internal/debug.h>
|
|
|
|
#if defined (ALLOC_PRAGMA)
|
|
#pragma alloc_text(INIT, MiInitializeNonPagedPool)
|
|
#endif
|
|
|
|
#ifdef ENABLE_VALIDATE_POOL
|
|
#define VALIDATE_POOL validate_kernel_pool()
|
|
#else
|
|
#define VALIDATE_POOL
|
|
#endif
|
|
|
|
#if 0
|
|
#define POOL_TRACE(args...) do { DbgPrint(args); } while(0);
|
|
#else
|
|
#if defined(__GNUC__)
|
|
#define POOL_TRACE(args...)
|
|
#else
|
|
#define POOL_TRACE
|
|
#endif /* __GNUC__ */
|
|
#endif
|
|
|
|
VOID MmPrintMemoryStatistic(VOID);
|
|
|
|
#define NPOOL_REDZONE_CHECK /* check the block at deallocation */
|
|
// #define NPOOL_REDZONE_CHECK_FULL /* check all blocks at each allocation/deallocation */
|
|
#define NPOOL_REDZONE_SIZE 8 /* number of red zone bytes */
|
|
#define NPOOL_REDZONE_LOVALUE 0x87
|
|
#define NPOOL_REDZONE_HIVALUE 0xA5
|
|
|
|
|
|
/* avl types ****************************************************************/
|
|
|
|
/* FIXME:
|
|
* This declarations should be moved into a separate header file.
|
|
*/
|
|
|
|
typedef struct _NODE
|
|
{
|
|
struct _NODE* link[2];
|
|
struct _NODE* parent;
|
|
signed char balance;
|
|
}
|
|
NODE, *PNODE;
|
|
|
|
/* TYPES *******************************************************************/
|
|
|
|
#define BLOCK_HDR_USED_MAGIC (0xdeadbeef)
|
|
#define BLOCK_HDR_FREE_MAGIC (0xceadbeef)
|
|
|
|
/*
|
|
* fields present at the start of a block (this is for internal use only)
|
|
*/
|
|
typedef struct _HDR
|
|
{
|
|
ULONG Magic;
|
|
ULONG Size;
|
|
struct _HDR* previous;
|
|
} HDR, *PHDR;
|
|
|
|
typedef struct _HDR_USED
|
|
{
|
|
HDR hdr;
|
|
LIST_ENTRY ListEntry;
|
|
ULONG Tag;
|
|
PVOID Caller;
|
|
LIST_ENTRY TagListEntry;
|
|
#if defined(NPOOL_REDZONE_CHECK) || defined(NPOOL_REDZONE_CHECK_FULL)
|
|
ULONG UserSize;
|
|
#endif
|
|
BOOLEAN Dumped;
|
|
} HDR_USED, *PHDR_USED;
|
|
|
|
typedef struct _HDR_FREE
|
|
{
|
|
HDR hdr;
|
|
NODE Node;
|
|
} HDR_FREE, *PHDR_FREE;
|
|
|
|
#define HDR_FREE_SIZE ROUND_UP(sizeof(HDR_FREE), MM_POOL_ALIGNMENT)
|
|
|
|
#if defined(NPOOL_REDZONE_CHECK) || defined(NPOOL_REDZONE_CHECK_FULL)
|
|
#define HDR_USED_SIZE ROUND_UP(sizeof(HDR_USED) + NPOOL_REDZONE_SIZE, MM_POOL_ALIGNMENT)
|
|
#else
|
|
#define HDR_USED_SIZE ROUND_UP(sizeof(HDR_USED), MM_POOL_ALIGNMENT)
|
|
#endif
|
|
|
|
/* GLOBALS *****************************************************************/
|
|
|
|
extern PVOID MiNonPagedPoolStart;
|
|
extern ULONG MiNonPagedPoolLength;
|
|
|
|
/*
|
|
* Head of the list of free blocks
|
|
*/
|
|
static PNODE FreeBlockListRoot = NULL;
|
|
|
|
/*
|
|
* Head of the list of in use block
|
|
*/
|
|
static LIST_ENTRY UsedBlockListHead;
|
|
|
|
static LIST_ENTRY AddressListHead;
|
|
|
|
/*
|
|
* Count of free blocks
|
|
*/
|
|
static ULONG EiNrFreeBlocks = 0;
|
|
|
|
/*
|
|
* Count of used blocks
|
|
*/
|
|
static ULONG EiNrUsedBlocks = 0;
|
|
|
|
/*
|
|
* Lock that protects the non-paged pool data structures
|
|
*/
|
|
static KSPIN_LOCK MmNpoolLock;
|
|
|
|
/*
|
|
* Total memory used for free nonpaged pool blocks
|
|
*/
|
|
ULONG EiFreeNonPagedPool = 0;
|
|
|
|
/*
|
|
* Total memory used for nonpaged pool blocks
|
|
*/
|
|
ULONG EiUsedNonPagedPool = 0;
|
|
|
|
/* Total quota for Non Paged Pool */
|
|
ULONG MmTotalNonPagedPoolQuota = 0;
|
|
|
|
#ifdef TAG_STATISTICS_TRACKING
|
|
#define TAG_HASH_TABLE_SIZE (1024)
|
|
static LIST_ENTRY tag_hash_table[TAG_HASH_TABLE_SIZE];
|
|
#endif /* TAG_STATISTICS_TRACKING */
|
|
|
|
static PULONG MiNonPagedPoolAllocMap;
|
|
static ULONG MiNonPagedPoolNrOfPages;
|
|
|
|
/* avl helper functions ****************************************************/
|
|
|
|
void DumpFreeBlockNode(PNODE p)
|
|
{
|
|
static int count = 0;
|
|
HDR_FREE* blk;
|
|
|
|
count++;
|
|
|
|
if (p)
|
|
{
|
|
DumpFreeBlockNode(p->link[0]);
|
|
blk = CONTAINING_RECORD(p, HDR_FREE, Node);
|
|
DbgPrint("%08x %8d (%d)\n", blk, blk->hdr.Size, count);
|
|
DumpFreeBlockNode(p->link[1]);
|
|
}
|
|
|
|
count--;
|
|
}
|
|
void DumpFreeBlockTree(void)
|
|
{
|
|
DbgPrint("--- Begin tree ------------------\n");
|
|
DbgPrint("%08x\n", CONTAINING_RECORD(FreeBlockListRoot, HDR_FREE, Node));
|
|
DumpFreeBlockNode(FreeBlockListRoot);
|
|
DbgPrint("--- End tree --------------------\n");
|
|
}
|
|
|
|
int compare_node(PNODE p1, PNODE p2)
|
|
{
|
|
HDR_FREE* blk1 = CONTAINING_RECORD(p1, HDR_FREE, Node);
|
|
HDR_FREE* blk2 = CONTAINING_RECORD(p2, HDR_FREE, Node);
|
|
|
|
if (blk1->hdr.Size == blk2->hdr.Size)
|
|
{
|
|
if (blk1 < blk2)
|
|
{
|
|
return -1;
|
|
}
|
|
if (blk1 > blk2)
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (blk1->hdr.Size < blk2->hdr.Size)
|
|
{
|
|
return -1;
|
|
}
|
|
if (blk1->hdr.Size > blk2->hdr.Size)
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
|
|
}
|
|
|
|
int compare_value(PVOID value, PNODE p)
|
|
{
|
|
HDR_FREE* blk = CONTAINING_RECORD(p, HDR_FREE, Node);
|
|
ULONG v = *(PULONG)value;
|
|
|
|
if (v < blk->hdr.Size)
|
|
{
|
|
return -1;
|
|
}
|
|
if (v > blk->hdr.Size)
|
|
{
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* avl functions **********************************************************/
|
|
|
|
/* FIXME:
|
|
* The avl functions should be moved into a separate file.
|
|
*/
|
|
|
|
/* The avl_insert and avl_remove are based on libavl (library for manipulation of binary trees). */
|
|
|
|
void avl_insert (PNODE * root, PNODE n, int (*compare)(PNODE, PNODE))
|
|
{
|
|
PNODE y; /* Top node to update balance factor, and parent. */
|
|
PNODE p, q; /* Iterator, and parent. */
|
|
PNODE w; /* New root of rebalanced subtree. */
|
|
int dir = 0; /* Direction to descend. */
|
|
|
|
n->link[0] = n->link[1] = n->parent = NULL;
|
|
n->balance = 0;
|
|
|
|
y = *root;
|
|
for (q = NULL, p = *root; p != NULL; q = p, p = p->link[dir])
|
|
{
|
|
dir = compare(n, p) > 0;
|
|
if (p->balance != 0)
|
|
{
|
|
y = p;
|
|
}
|
|
}
|
|
|
|
n->parent = q;
|
|
if (q != NULL)
|
|
{
|
|
q->link[dir] = n;
|
|
}
|
|
else
|
|
{
|
|
*root = n;
|
|
}
|
|
|
|
if (*root == n)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (p = n; p != y; p = q)
|
|
{
|
|
q = p->parent;
|
|
dir = q->link[0] != p;
|
|
if (dir == 0)
|
|
{
|
|
q->balance--;
|
|
}
|
|
else
|
|
{
|
|
q->balance++;
|
|
}
|
|
}
|
|
|
|
if (y->balance == -2)
|
|
{
|
|
PNODE x = y->link[0];
|
|
if (x->balance == -1)
|
|
{
|
|
w = x;
|
|
y->link[0] = x->link[1];
|
|
x->link[1] = y;
|
|
x->balance = y->balance = 0;
|
|
x->parent = y->parent;
|
|
y->parent = x;
|
|
if (y->link[0] != NULL)
|
|
{
|
|
y->link[0]->parent = y;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ASSERT(x->balance == +1);
|
|
w = x->link[1];
|
|
x->link[1] = w->link[0];
|
|
w->link[0] = x;
|
|
y->link[0] = w->link[1];
|
|
w->link[1] = y;
|
|
if (w->balance == -1)
|
|
{
|
|
x->balance = 0;
|
|
y->balance = +1;
|
|
}
|
|
else if (w->balance == 0)
|
|
{
|
|
x->balance = y->balance = 0;
|
|
}
|
|
else /* |w->pavl_balance == +1| */
|
|
{
|
|
x->balance = -1;
|
|
y->balance = 0;
|
|
}
|
|
w->balance = 0;
|
|
w->parent = y->parent;
|
|
x->parent = y->parent = w;
|
|
if (x->link[1] != NULL)
|
|
{
|
|
x->link[1]->parent = x;
|
|
}
|
|
if (y->link[0] != NULL)
|
|
{
|
|
y->link[0]->parent = y;
|
|
}
|
|
}
|
|
}
|
|
else if (y->balance == +2)
|
|
{
|
|
PNODE x = y->link[1];
|
|
if (x->balance == +1)
|
|
{
|
|
w = x;
|
|
y->link[1] = x->link[0];
|
|
x->link[0] = y;
|
|
x->balance = y->balance = 0;
|
|
x->parent = y->parent;
|
|
y->parent = x;
|
|
if (y->link[1] != NULL)
|
|
{
|
|
y->link[1]->parent = y;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ASSERT(x->balance == -1);
|
|
w = x->link[0];
|
|
x->link[0] = w->link[1];
|
|
w->link[1] = x;
|
|
y->link[1] = w->link[0];
|
|
w->link[0] = y;
|
|
if (w->balance == 1)
|
|
{
|
|
x->balance = 0;
|
|
y->balance = -1;
|
|
}
|
|
else if (w->balance == 0)
|
|
{
|
|
x->balance = y->balance = 0;
|
|
}
|
|
else /* |w->pavl_balance == -1| */
|
|
{
|
|
x->balance = +1;
|
|
y->balance = 0;
|
|
}
|
|
w->balance = 0;
|
|
w->parent = y->parent;
|
|
x->parent = y->parent = w;
|
|
if (x->link[0] != NULL)
|
|
{
|
|
x->link[0]->parent = x;
|
|
}
|
|
if (y->link[1] != NULL)
|
|
{
|
|
y->link[1]->parent = y;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
if (w->parent != NULL)
|
|
{
|
|
w->parent->link[y != w->parent->link[0]] = w;
|
|
}
|
|
else
|
|
{
|
|
*root = w;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void avl_remove (PNODE *root, PNODE item, int (*compare)(PNODE, PNODE))
|
|
{
|
|
PNODE p; /* Traverses tree to find node to delete. */
|
|
PNODE q; /* Parent of |p|. */
|
|
int dir; /* Side of |q| on which |p| is linked. */
|
|
|
|
if (root == NULL || *root == NULL)
|
|
{
|
|
return ;
|
|
}
|
|
|
|
p = item;
|
|
q = p->parent;
|
|
if (q == NULL)
|
|
{
|
|
q = (PNODE) root;
|
|
dir = 0;
|
|
}
|
|
else
|
|
{
|
|
dir = compare(p, q) > 0;
|
|
}
|
|
|
|
if (p->link[1] == NULL)
|
|
{
|
|
q->link[dir] = p->link[0];
|
|
if (q->link[dir] != NULL)
|
|
{
|
|
q->link[dir]->parent = p->parent;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PNODE r = p->link[1];
|
|
if (r->link[0] == NULL)
|
|
{
|
|
r->link[0] = p->link[0];
|
|
q->link[dir] = r;
|
|
r->parent = p->parent;
|
|
if (r->link[0] != NULL)
|
|
{
|
|
r->link[0]->parent = r;
|
|
}
|
|
r->balance = p->balance;
|
|
q = r;
|
|
dir = 1;
|
|
}
|
|
else
|
|
{
|
|
PNODE s = r->link[0];
|
|
while (s->link[0] != NULL)
|
|
{
|
|
s = s->link[0];
|
|
}
|
|
r = s->parent;
|
|
r->link[0] = s->link[1];
|
|
s->link[0] = p->link[0];
|
|
s->link[1] = p->link[1];
|
|
q->link[dir] = s;
|
|
if (s->link[0] != NULL)
|
|
{
|
|
s->link[0]->parent = s;
|
|
}
|
|
s->link[1]->parent = s;
|
|
s->parent = p->parent;
|
|
if (r->link[0] != NULL)
|
|
{
|
|
r->link[0]->parent = r;
|
|
}
|
|
s->balance = p->balance;
|
|
q = r;
|
|
dir = 0;
|
|
}
|
|
}
|
|
|
|
item->link[0] = item->link[1] = item->parent = NULL;
|
|
item->balance = 0;
|
|
|
|
while (q != (PNODE) root)
|
|
{
|
|
PNODE y = q;
|
|
|
|
if (y->parent != NULL)
|
|
{
|
|
q = y->parent;
|
|
}
|
|
else
|
|
{
|
|
q = (PNODE) root;
|
|
}
|
|
|
|
if (dir == 0)
|
|
{
|
|
dir = q->link[0] != y;
|
|
y->balance++;
|
|
if (y->balance == +1)
|
|
{
|
|
break;
|
|
}
|
|
else if (y->balance == +2)
|
|
{
|
|
PNODE x = y->link[1];
|
|
if (x->balance == -1)
|
|
{
|
|
PNODE w;
|
|
|
|
ASSERT(x->balance == -1);
|
|
w = x->link[0];
|
|
x->link[0] = w->link[1];
|
|
w->link[1] = x;
|
|
y->link[1] = w->link[0];
|
|
w->link[0] = y;
|
|
if (w->balance == +1)
|
|
{
|
|
x->balance = 0;
|
|
y->balance = -1;
|
|
}
|
|
else if (w->balance == 0)
|
|
{
|
|
x->balance = y->balance = 0;
|
|
}
|
|
else /* |w->pavl_balance == -1| */
|
|
{
|
|
x->balance = +1;
|
|
y->balance = 0;
|
|
}
|
|
w->balance = 0;
|
|
w->parent = y->parent;
|
|
x->parent = y->parent = w;
|
|
if (x->link[0] != NULL)
|
|
{
|
|
x->link[0]->parent = x;
|
|
}
|
|
if (y->link[1] != NULL)
|
|
{
|
|
y->link[1]->parent = y;
|
|
}
|
|
q->link[dir] = w;
|
|
}
|
|
else
|
|
{
|
|
y->link[1] = x->link[0];
|
|
x->link[0] = y;
|
|
x->parent = y->parent;
|
|
y->parent = x;
|
|
if (y->link[1] != NULL)
|
|
{
|
|
y->link[1]->parent = y;
|
|
}
|
|
q->link[dir] = x;
|
|
if (x->balance == 0)
|
|
{
|
|
x->balance = -1;
|
|
y->balance = +1;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
x->balance = y->balance = 0;
|
|
y = x;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dir = q->link[0] != y;
|
|
y->balance--;
|
|
if (y->balance == -1)
|
|
{
|
|
break;
|
|
}
|
|
else if (y->balance == -2)
|
|
{
|
|
PNODE x = y->link[0];
|
|
if (x->balance == +1)
|
|
{
|
|
PNODE w;
|
|
ASSERT(x->balance == +1);
|
|
w = x->link[1];
|
|
x->link[1] = w->link[0];
|
|
w->link[0] = x;
|
|
y->link[0] = w->link[1];
|
|
w->link[1] = y;
|
|
if (w->balance == -1)
|
|
{
|
|
x->balance = 0;
|
|
y->balance = +1;
|
|
}
|
|
else if (w->balance == 0)
|
|
{
|
|
x->balance = y->balance = 0;
|
|
}
|
|
else /* |w->pavl_balance == +1| */
|
|
{
|
|
x->balance = -1;
|
|
y->balance = 0;
|
|
}
|
|
w->balance = 0;
|
|
w->parent = y->parent;
|
|
x->parent = y->parent = w;
|
|
if (x->link[1] != NULL)
|
|
{
|
|
x->link[1]->parent = x;
|
|
}
|
|
if (y->link[0] != NULL)
|
|
{
|
|
y->link[0]->parent = y;
|
|
}
|
|
q->link[dir] = w;
|
|
}
|
|
else
|
|
{
|
|
y->link[0] = x->link[1];
|
|
x->link[1] = y;
|
|
x->parent = y->parent;
|
|
y->parent = x;
|
|
if (y->link[0] != NULL)
|
|
{
|
|
y->link[0]->parent = y;
|
|
}
|
|
q->link[dir] = x;
|
|
if (x->balance == 0)
|
|
{
|
|
x->balance = +1;
|
|
y->balance = -1;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
x->balance = y->balance = 0;
|
|
y = x;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
PNODE _cdecl avl_get_first(PNODE root)
|
|
{
|
|
PNODE p;
|
|
if (root == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
p = root;
|
|
while (p->link[0])
|
|
{
|
|
p = p->link[0];
|
|
}
|
|
return p;
|
|
}
|
|
|
|
PNODE avl_get_next(PNODE root, PNODE p)
|
|
{
|
|
PNODE q;
|
|
if (p->link[1])
|
|
{
|
|
p = p->link[1];
|
|
while(p->link[0])
|
|
{
|
|
p = p->link[0];
|
|
}
|
|
return p;
|
|
}
|
|
else
|
|
{
|
|
q = p->parent;
|
|
while (q && q->link[1] == p)
|
|
{
|
|
p = q;
|
|
q = q->parent;
|
|
}
|
|
if (q == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
else
|
|
{
|
|
return q;
|
|
}
|
|
}
|
|
}
|
|
|
|
PNODE avl_find_equal_or_greater(PNODE root, ULONG size, int (compare)(PVOID, PNODE))
|
|
{
|
|
PNODE p;
|
|
PNODE prev = NULL;
|
|
int cmp;
|
|
|
|
for (p = root; p != NULL;)
|
|
{
|
|
cmp = compare((PVOID)&size, p);
|
|
if (cmp < 0)
|
|
{
|
|
prev = p;
|
|
p = p->link[0];
|
|
}
|
|
else if (cmp > 0)
|
|
{
|
|
p = p->link[1];
|
|
}
|
|
else
|
|
{
|
|
while (p->link[0])
|
|
{
|
|
cmp = compare((PVOID)&size, p->link[0]);
|
|
if (cmp != 0)
|
|
{
|
|
break;
|
|
}
|
|
p = p->link[0];
|
|
}
|
|
return p;
|
|
}
|
|
}
|
|
return prev;
|
|
}
|
|
|
|
/* non paged pool functions ************************************************/
|
|
|
|
#ifdef TAG_STATISTICS_TRACKING
|
|
VOID
|
|
MiRemoveFromTagHashTable(HDR_USED* block)
|
|
/*
|
|
* Remove a block from the tag hash table
|
|
*/
|
|
{
|
|
if (block->Tag == 0)
|
|
{
|
|
return;
|
|
}
|
|
RemoveEntryList(&block->TagListEntry);
|
|
}
|
|
|
|
VOID
|
|
MiAddToTagHashTable(HDR_USED* block)
|
|
/*
|
|
* Add a block to the tag hash table
|
|
*/
|
|
{
|
|
ULONG hash;
|
|
|
|
if (block->Tag == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
hash = block->Tag % TAG_HASH_TABLE_SIZE;
|
|
|
|
InsertHeadList(&tag_hash_table[hash], &block->TagListEntry);
|
|
}
|
|
#endif /* TAG_STATISTICS_TRACKING */
|
|
|
|
#if defined(TAG_STATISTICS_TRACKING)
|
|
VOID static
|
|
MiDumpTagStats(ULONG CurrentTag, ULONG CurrentNrBlocks, ULONG CurrentSize)
|
|
{
|
|
CHAR c1, c2, c3, c4;
|
|
|
|
c1 = (CHAR)((CurrentTag >> 24) & 0xFF);
|
|
c2 = (CHAR)((CurrentTag >> 16) & 0xFF);
|
|
c3 = (CHAR)((CurrentTag >> 8) & 0xFF);
|
|
c4 = (CHAR)(CurrentTag & 0xFF);
|
|
|
|
if (isprint(c1) && isprint(c2) && isprint(c3) && isprint(c4))
|
|
{
|
|
DbgPrint("Tag %x (%c%c%c%c) Blocks %d Total Size %d Average Size %d\n",
|
|
CurrentTag, c4, c3, c2, c1, CurrentNrBlocks,
|
|
CurrentSize, CurrentSize / CurrentNrBlocks);
|
|
}
|
|
else
|
|
{
|
|
DbgPrint("Tag %x Blocks %d Total Size %d Average Size %d ",
|
|
CurrentTag, CurrentNrBlocks, CurrentSize,
|
|
CurrentSize / CurrentNrBlocks);
|
|
KeRosPrintAddress((PVOID)CurrentTag);
|
|
DbgPrint("\n");
|
|
}
|
|
}
|
|
#endif /* defined(TAG_STATISTICS_TRACKING) */
|
|
|
|
VOID
|
|
NTAPI
|
|
MiDebugDumpNonPagedPoolStats(BOOLEAN NewOnly)
|
|
{
|
|
#if defined(TAG_STATISTICS_TRACKING)
|
|
ULONG i;
|
|
HDR_USED* current;
|
|
ULONG CurrentTag;
|
|
ULONG CurrentNrBlocks = 0;
|
|
ULONG CurrentSize = 0;
|
|
ULONG TotalBlocks;
|
|
ULONG TotalSize;
|
|
ULONG Size;
|
|
LIST_ENTRY tmpListHead;
|
|
PLIST_ENTRY current_entry;
|
|
|
|
DbgPrint("******* Dumping non paging pool stats ******\n");
|
|
TotalBlocks = 0;
|
|
TotalSize = 0;
|
|
for (i = 0; i < TAG_HASH_TABLE_SIZE; i++)
|
|
{
|
|
InitializeListHead(&tmpListHead);
|
|
|
|
while (!IsListEmpty(&tag_hash_table[i]))
|
|
{
|
|
CurrentTag = 0;
|
|
|
|
current_entry = tag_hash_table[i].Flink;
|
|
while (current_entry != &tag_hash_table[i])
|
|
{
|
|
current = CONTAINING_RECORD(current_entry, HDR_USED, TagListEntry);
|
|
current_entry = current_entry->Flink;
|
|
if (CurrentTag == 0)
|
|
{
|
|
CurrentTag = current->Tag;
|
|
CurrentNrBlocks = 0;
|
|
CurrentSize = 0;
|
|
}
|
|
if (current->Tag == CurrentTag)
|
|
{
|
|
RemoveEntryList(¤t->TagListEntry);
|
|
InsertHeadList(&tmpListHead, ¤t->TagListEntry);
|
|
if (!NewOnly || !current->Dumped)
|
|
{
|
|
CurrentNrBlocks++;
|
|
TotalBlocks++;
|
|
CurrentSize += current->hdr.Size;
|
|
TotalSize += current->hdr.Size;
|
|
current->Dumped = TRUE;
|
|
}
|
|
}
|
|
}
|
|
if (CurrentTag != 0 && CurrentNrBlocks != 0)
|
|
{
|
|
MiDumpTagStats(CurrentTag, CurrentNrBlocks, CurrentSize);
|
|
}
|
|
}
|
|
if (!IsListEmpty(&tmpListHead))
|
|
{
|
|
tag_hash_table[i].Flink = tmpListHead.Flink;
|
|
tag_hash_table[i].Flink->Blink = &tag_hash_table[i];
|
|
tag_hash_table[i].Blink = tmpListHead.Blink;
|
|
tag_hash_table[i].Blink->Flink = &tag_hash_table[i];
|
|
}
|
|
}
|
|
if (TotalBlocks != 0)
|
|
{
|
|
DbgPrint("TotalBlocks %d TotalSize %d AverageSize %d\n",
|
|
TotalBlocks, TotalSize, TotalSize / TotalBlocks);
|
|
}
|
|
else
|
|
{
|
|
DbgPrint("TotalBlocks %d TotalSize %d\n",
|
|
TotalBlocks, TotalSize);
|
|
}
|
|
Size = EiFreeNonPagedPool - (MiNonPagedPoolLength - MiNonPagedPoolNrOfPages * PAGE_SIZE);
|
|
DbgPrint("Freeblocks %d TotalFreeSize %d AverageFreeSize %d\n",
|
|
EiNrFreeBlocks, Size, EiNrFreeBlocks ? Size / EiNrFreeBlocks : 0);
|
|
DbgPrint("***************** Dump Complete ***************\n");
|
|
#endif /* defined(TAG_STATISTICS_TRACKING) */
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
MiDebugDumpNonPagedPool(BOOLEAN NewOnly)
|
|
{
|
|
#if defined(POOL_DEBUG_APIS)
|
|
HDR_USED* current;
|
|
PLIST_ENTRY current_entry;
|
|
KIRQL oldIrql;
|
|
|
|
KeAcquireSpinLock(&MmNpoolLock, &oldIrql);
|
|
|
|
DbgPrint("******* Dumping non paging pool contents ******\n");
|
|
current_entry = UsedBlockListHead.Flink;
|
|
while (current_entry != &UsedBlockListHead)
|
|
{
|
|
current = CONTAINING_RECORD(current_entry, HDR_USED, ListEntry);
|
|
if (!NewOnly || !current->Dumped)
|
|
{
|
|
CHAR c1, c2, c3, c4;
|
|
|
|
c1 = (CHAR)((current->Tag >> 24) & 0xFF);
|
|
c2 = (CHAR)((current->Tag >> 16) & 0xFF);
|
|
c3 = (CHAR)((current->Tag >> 8) & 0xFF);
|
|
c4 = (CHAR)(current->Tag & 0xFF);
|
|
|
|
if (isprint(c1) && isprint(c2) && isprint(c3) && isprint(c4))
|
|
{
|
|
DbgPrint("Size 0x%x Tag 0x%x (%c%c%c%c) Allocator 0x%x\n",
|
|
current->hdr.Size, current->Tag, c4, c3, c2, c1,
|
|
current->Caller);
|
|
}
|
|
else
|
|
{
|
|
DbgPrint("Size 0x%x Tag 0x%x Allocator 0x%x\n",
|
|
current->hdr.Size, current->Tag, current->Caller);
|
|
}
|
|
current->Dumped = TRUE;
|
|
}
|
|
current_entry = current_entry->Flink;
|
|
}
|
|
DbgPrint("***************** Dump Complete ***************\n");
|
|
KeReleaseSpinLock(&MmNpoolLock, oldIrql);
|
|
#endif
|
|
}
|
|
|
|
#ifdef ENABLE_VALIDATE_POOL
|
|
static void validate_free_list(void)
|
|
/*
|
|
* FUNCTION: Validate the integrity of the list of free blocks
|
|
*/
|
|
{
|
|
HDR_FREE* current;
|
|
unsigned int blocks_seen=0;
|
|
PNODE p;
|
|
|
|
p = avl_get_first(FreeBlockListRoot);
|
|
|
|
while(p)
|
|
{
|
|
PVOID base_addr;
|
|
|
|
current = CONTAINING_RECORD(p, HDR_FREE, Node);
|
|
base_addr = (PVOID)current;
|
|
|
|
if (current->hdr.Magic != BLOCK_HDR_FREE_MAGIC)
|
|
{
|
|
DbgPrint("Bad block magic (probable pool corruption) at %x\n",
|
|
current);
|
|
KEBUGCHECK(/*KBUG_POOL_FREE_LIST_CORRUPT*/0);
|
|
}
|
|
|
|
if (base_addr < MiNonPagedPoolStart ||
|
|
(ULONG_PTR)base_addr + current->hdr.Size > (ULONG_PTR)MiNonPagedPoolStart + MiNonPagedPoolLength)
|
|
{
|
|
DbgPrint("Block %x found outside pool area\n",current);
|
|
DbgPrint("Size %d\n",current->hdr.Size);
|
|
DbgPrint("Limits are %x %x\n",MiNonPagedPoolStart,
|
|
(ULONG_PTR)MiNonPagedPoolStart+MiNonPagedPoolLength);
|
|
KEBUGCHECK(/*KBUG_POOL_FREE_LIST_CORRUPT*/0);
|
|
}
|
|
blocks_seen++;
|
|
if (blocks_seen > EiNrFreeBlocks)
|
|
{
|
|
DbgPrint("Too many blocks on free list\n");
|
|
KEBUGCHECK(/*KBUG_POOL_FREE_LIST_CORRUPT*/0);
|
|
}
|
|
p = avl_get_next(FreeBlockListRoot, p);
|
|
}
|
|
}
|
|
|
|
static void validate_used_list(void)
|
|
/*
|
|
* FUNCTION: Validate the integrity of the list of used blocks
|
|
*/
|
|
{
|
|
HDR_USED* current;
|
|
PLIST_ENTRY current_entry;
|
|
unsigned int blocks_seen=0;
|
|
|
|
current_entry = UsedBlockListHead.Flink;
|
|
while (current_entry != &UsedBlockListHead)
|
|
{
|
|
PVOID base_addr;
|
|
|
|
current = CONTAINING_RECORD(current_entry, HDR_USED, ListEntry);
|
|
base_addr = (PVOID)current;
|
|
|
|
if (current->hdr.Magic != BLOCK_HDR_USED_MAGIC)
|
|
{
|
|
DbgPrint("Bad block magic (probable pool corruption) at %x\n",
|
|
current);
|
|
KEBUGCHECK(/*KBUG_POOL_FREE_LIST_CORRUPT*/0);
|
|
}
|
|
if (base_addr < MiNonPagedPoolStart ||
|
|
((ULONG_PTR)base_addr+current->hdr.Size) >
|
|
(ULONG_PTR)MiNonPagedPoolStart+MiNonPagedPoolLength)
|
|
{
|
|
DbgPrint("Block %x found outside pool area\n",current);
|
|
DbgPrint("Size %d\n",current->hdr.Size);
|
|
DbgPrint("Limits are %x %x\n",MiNonPagedPoolStart,
|
|
(ULONG_PTR)MiNonPagedPoolStart+MiNonPagedPoolLength);
|
|
KEBUGCHECK(/*KBUG_POOL_FREE_LIST_CORRUPT*/0);
|
|
}
|
|
blocks_seen++;
|
|
if (blocks_seen > EiNrUsedBlocks)
|
|
{
|
|
DbgPrint("Too many blocks on used list\n");
|
|
KEBUGCHECK(/*KBUG_POOL_FREE_LIST_CORRUPT*/0);
|
|
}
|
|
if (current->ListEntry.Flink != &UsedBlockListHead &&
|
|
current->ListEntry.Flink->Blink != ¤t->ListEntry)
|
|
{
|
|
DbgPrint("%s:%d:Break in list (current %x next %x "
|
|
"current->next->previous %x)\n",
|
|
__FILE__,__LINE__,current, current->ListEntry.Flink,
|
|
current->ListEntry.Flink->Blink);
|
|
KEBUGCHECK(/*KBUG_POOL_FREE_LIST_CORRUPT*/0);
|
|
}
|
|
|
|
current_entry = current_entry->Flink;
|
|
}
|
|
}
|
|
|
|
static void check_duplicates(HDR* blk)
|
|
/*
|
|
* FUNCTION: Check a block has no duplicates
|
|
* ARGUMENTS:
|
|
* blk = block to check
|
|
* NOTE: Bug checks if duplicates are found
|
|
*/
|
|
{
|
|
ULONG_PTR base = (ULONG_PTR)blk;
|
|
ULONG_PTR last = (ULONG_PTR)blk + blk->Size;
|
|
PLIST_ENTRY current_entry;
|
|
PNODE p;
|
|
HDR_FREE* free;
|
|
HDR_USED* used;
|
|
|
|
p = avl_get_first(FreeBlockListRoot);
|
|
|
|
while (p)
|
|
{
|
|
free = CONTAINING_RECORD(p, HDR_FREE, Node);
|
|
if (free->hdr.Magic != BLOCK_HDR_FREE_MAGIC)
|
|
{
|
|
DbgPrint("Bad block magic (probable pool corruption) at %x\n",
|
|
free);
|
|
KEBUGCHECK(/*KBUG_POOL_FREE_LIST_CORRUPT*/0);
|
|
}
|
|
|
|
if ( (ULONG_PTR)free > base && (ULONG_PTR)free < last )
|
|
{
|
|
DbgPrint("intersecting blocks on list\n");
|
|
KEBUGCHECK(/*KBUG_POOL_FREE_LIST_CORRUPT*/0);
|
|
}
|
|
if ( (ULONG_PTR)free < base &&
|
|
((ULONG_PTR)free + free->hdr.Size) > base )
|
|
{
|
|
DbgPrint("intersecting blocks on list\n");
|
|
KEBUGCHECK(/*KBUG_POOL_FREE_LIST_CORRUPT*/0);
|
|
}
|
|
p = avl_get_next(FreeBlockListRoot, p);
|
|
}
|
|
|
|
current_entry = UsedBlockListHead.Flink;
|
|
while (current_entry != &UsedBlockListHead)
|
|
{
|
|
used = CONTAINING_RECORD(current_entry, HDR_USED, ListEntry);
|
|
|
|
if ( (ULONG_PTR)used > base && (ULONG_PTR)used < last )
|
|
{
|
|
DbgPrint("intersecting blocks on list\n");
|
|
KEBUGCHECK(/*KBUG_POOL_FREE_LIST_CORRUPT*/0);
|
|
}
|
|
if ( (ULONG_PTR)used < base &&
|
|
((ULONG_PTR)used + used->hdr.Size) > base )
|
|
{
|
|
DbgPrint("intersecting blocks on list\n");
|
|
KEBUGCHECK(/*KBUG_POOL_FREE_LIST_CORRUPT*/0);
|
|
}
|
|
|
|
current_entry = current_entry->Flink;
|
|
}
|
|
|
|
}
|
|
|
|
static void validate_kernel_pool(void)
|
|
/*
|
|
* FUNCTION: Checks the integrity of the kernel memory heap
|
|
*/
|
|
{
|
|
HDR_FREE* free;
|
|
HDR_USED* used;
|
|
PLIST_ENTRY current_entry;
|
|
PNODE p;
|
|
|
|
validate_free_list();
|
|
validate_used_list();
|
|
|
|
p = avl_get_first(FreeBlockListRoot);
|
|
while (p)
|
|
{
|
|
free = CONTAINING_RECORD(p, HDR_FREE, Node);
|
|
check_duplicates(&free->hdr);
|
|
p = avl_get_next(FreeBlockListRoot, p);
|
|
}
|
|
current_entry = UsedBlockListHead.Flink;
|
|
while (current_entry != &UsedBlockListHead)
|
|
{
|
|
used = CONTAINING_RECORD(current_entry, HDR_USED, ListEntry);
|
|
check_duplicates(&used->hdr);
|
|
current_entry = current_entry->Flink;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if 0
|
|
static VOID
|
|
free_pages(HDR_FREE* blk)
|
|
{
|
|
ULONG start;
|
|
ULONG end;
|
|
|
|
start = (ULONG_PTR)blk;
|
|
end = (ULONG_PTR)blk + blk->hdr.Size;
|
|
|
|
/*
|
|
* If the block doesn't contain a whole page then there is nothing to do
|
|
*/
|
|
if (PAGE_ROUND_UP(start) >= PAGE_ROUND_DOWN(end))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static void remove_from_used_list(HDR_USED* current)
|
|
{
|
|
RemoveEntryList(¤t->ListEntry);
|
|
EiUsedNonPagedPool -= current->hdr.Size;
|
|
EiNrUsedBlocks--;
|
|
}
|
|
|
|
static void remove_from_free_list(HDR_FREE* current)
|
|
{
|
|
DPRINT("remove_from_free_list %d\n", current->hdr.Size);
|
|
|
|
avl_remove(&FreeBlockListRoot, ¤t->Node, compare_node);
|
|
|
|
EiFreeNonPagedPool -= current->hdr.Size;
|
|
EiNrFreeBlocks--;
|
|
DPRINT("remove_from_free_list done\n");
|
|
#ifdef DUMP_AVL
|
|
|
|
DumpFreeBlockTree();
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
add_to_free_list(HDR_FREE* blk)
|
|
/*
|
|
* FUNCTION: add the block to the free list (internal)
|
|
*/
|
|
{
|
|
HDR_FREE* current;
|
|
BOOLEAN UpdatePrevPtr = FALSE;
|
|
|
|
DPRINT("add_to_free_list (%d)\n", blk->hdr.Size);
|
|
|
|
EiNrFreeBlocks++;
|
|
|
|
current = (HDR_FREE*)blk->hdr.previous;
|
|
if (current && current->hdr.Magic == BLOCK_HDR_FREE_MAGIC)
|
|
{
|
|
remove_from_free_list(current);
|
|
current->hdr.Size = current->hdr.Size + blk->hdr.Size;
|
|
current->hdr.Magic = BLOCK_HDR_USED_MAGIC;
|
|
memset(blk, 0xcc, HDR_USED_SIZE);
|
|
blk = current;
|
|
UpdatePrevPtr = TRUE;
|
|
}
|
|
|
|
current = (HDR_FREE*)((ULONG_PTR)blk + blk->hdr.Size);
|
|
if ((char*)current < (char*)MiNonPagedPoolStart + MiNonPagedPoolLength &&
|
|
current->hdr.Magic == BLOCK_HDR_FREE_MAGIC)
|
|
{
|
|
remove_from_free_list(current);
|
|
blk->hdr.Size += current->hdr.Size;
|
|
memset(current, 0xcc, HDR_FREE_SIZE);
|
|
UpdatePrevPtr = TRUE;
|
|
current = (HDR_FREE*)((ULONG_PTR)blk + blk->hdr.Size);
|
|
}
|
|
if (UpdatePrevPtr &&
|
|
(char*)current < (char*)MiNonPagedPoolStart + MiNonPagedPoolLength)
|
|
{
|
|
current->hdr.previous = &blk->hdr;
|
|
}
|
|
DPRINT("%d\n", blk->hdr.Size);
|
|
blk->hdr.Magic = BLOCK_HDR_FREE_MAGIC;
|
|
EiFreeNonPagedPool += blk->hdr.Size;
|
|
avl_insert(&FreeBlockListRoot, &blk->Node, compare_node);
|
|
DPRINT("add_to_free_list done\n");
|
|
#ifdef DUMP_AVL
|
|
|
|
DumpFreeBlockTree();
|
|
#endif
|
|
}
|
|
|
|
static void add_to_used_list(HDR_USED* blk)
|
|
/*
|
|
* FUNCTION: add the block to the used list (internal)
|
|
*/
|
|
{
|
|
InsertHeadList(&UsedBlockListHead, &blk->ListEntry);
|
|
EiUsedNonPagedPool += blk->hdr.Size;
|
|
EiNrUsedBlocks++;
|
|
}
|
|
|
|
|
|
static BOOLEAN
|
|
grow_block(HDR_FREE* blk, PVOID end)
|
|
{
|
|
NTSTATUS Status;
|
|
PFN_TYPE Page[32];
|
|
ULONG_PTR StartIndex, EndIndex;
|
|
ULONG i, j, k;
|
|
|
|
StartIndex = (ULONG_PTR)(PAGE_ROUND_UP((ULONG_PTR)blk + HDR_FREE_SIZE - (ULONG_PTR)MiNonPagedPoolStart)) / PAGE_SIZE;
|
|
EndIndex = ((ULONG_PTR)PAGE_ROUND_UP(end) - (ULONG_PTR)MiNonPagedPoolStart) / PAGE_SIZE;
|
|
|
|
|
|
for (i = StartIndex; i < EndIndex; i++)
|
|
{
|
|
if (!(MiNonPagedPoolAllocMap[i / 32] & (1 << (i % 32))))
|
|
{
|
|
for (j = i + 1; j < EndIndex && j - i < 32; j++)
|
|
{
|
|
if (MiNonPagedPoolAllocMap[j / 32] & (1 << (j % 32)))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
for (k = 0; k < j - i; k++)
|
|
{
|
|
Status = MmRequestPageMemoryConsumer(MC_NPPOOL, FALSE, &Page[k]);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
for (i = 0; i < k; i++)
|
|
{
|
|
MmReleasePageMemoryConsumer(MC_NPPOOL, Page[i]);
|
|
}
|
|
return FALSE;
|
|
}
|
|
}
|
|
Status = MmCreateVirtualMapping(NULL,
|
|
(PVOID)((ULONG_PTR)MiNonPagedPoolStart + i * PAGE_SIZE),
|
|
PAGE_READWRITE|PAGE_SYSTEM,
|
|
Page,
|
|
k);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
for (i = 0; i < k; i++)
|
|
{
|
|
MmReleasePageMemoryConsumer(MC_NPPOOL, Page[i]);
|
|
}
|
|
return FALSE;
|
|
}
|
|
for (j = i; j < k + i; j++)
|
|
{
|
|
MiNonPagedPoolAllocMap[j / 32] |= (1 << (j % 32));
|
|
}
|
|
MiNonPagedPoolNrOfPages += k;
|
|
i += k - 1;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static HDR_USED* get_block(unsigned int size, unsigned long alignment)
|
|
{
|
|
HDR_FREE *blk, *current, *previous = NULL, *next = NULL, *best = NULL;
|
|
ULONG previous_size = 0, current_size, next_size = 0, new_size;
|
|
PVOID end;
|
|
PVOID addr, aligned_addr, best_aligned_addr=NULL;
|
|
PNODE p;
|
|
|
|
DPRINT("get_block %d\n", size);
|
|
|
|
p = avl_find_equal_or_greater(FreeBlockListRoot, size + HDR_USED_SIZE, compare_value);
|
|
while (p)
|
|
{
|
|
current = CONTAINING_RECORD(p, HDR_FREE, Node);
|
|
addr = (PVOID)((ULONG_PTR)current + HDR_USED_SIZE);
|
|
/* calculate first aligned address available within this block */
|
|
aligned_addr = alignment > 0 ? MM_ROUND_UP(addr, alignment) : addr;
|
|
if (size < PAGE_SIZE)
|
|
{
|
|
/* check that the block is in one page */
|
|
if (PAGE_ROUND_DOWN(aligned_addr) != PAGE_ROUND_DOWN((ULONG_PTR)aligned_addr + size - 1))
|
|
{
|
|
aligned_addr = (PVOID)PAGE_ROUND_UP(aligned_addr);
|
|
}
|
|
}
|
|
DPRINT("%x %x\n", addr, aligned_addr);
|
|
if (aligned_addr != addr)
|
|
{
|
|
while((ULONG_PTR)aligned_addr - (ULONG_PTR)addr < HDR_FREE_SIZE)
|
|
{
|
|
if (alignment == 0)
|
|
{
|
|
aligned_addr = (PVOID)((ULONG_PTR)current + HDR_USED_SIZE + HDR_FREE_SIZE);
|
|
}
|
|
else
|
|
{
|
|
aligned_addr = MM_ROUND_UP((PVOID)((ULONG_PTR)current + HDR_USED_SIZE + HDR_FREE_SIZE), alignment);
|
|
}
|
|
if (size < PAGE_SIZE)
|
|
{
|
|
/* check that the block is in one page */
|
|
if (PAGE_ROUND_DOWN(aligned_addr) != PAGE_ROUND_DOWN((ULONG_PTR)aligned_addr + size - 1))
|
|
{
|
|
aligned_addr = (PVOID)PAGE_ROUND_UP(aligned_addr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
DPRINT("%x %x\n", addr, aligned_addr);
|
|
new_size = (ULONG_PTR)aligned_addr - (ULONG_PTR)addr + size;
|
|
if (current->hdr.Size >= new_size + HDR_USED_SIZE &&
|
|
(best == NULL || current->hdr.Size < best->hdr.Size))
|
|
{
|
|
best = current;
|
|
best_aligned_addr = aligned_addr;
|
|
if (new_size <= size + 2 * HDR_FREE_SIZE)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (best)
|
|
{
|
|
if (size < PAGE_SIZE)
|
|
{
|
|
if (current->hdr.Size >= 2 * PAGE_SIZE + HDR_FREE_SIZE)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (current->hdr.Size >= size + alignment + HDR_FREE_SIZE)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
p = avl_get_next(FreeBlockListRoot, p);
|
|
}
|
|
/*
|
|
* We didn't find anything suitable at all.
|
|
*/
|
|
if (best == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
DPRINT(":: blk %x blk->hdr.Size %d (%d) Size %d\n", best, best->hdr.Size, best->hdr.Size - HDR_USED_SIZE, size);
|
|
|
|
current = best;
|
|
current_size = current->hdr.Size - HDR_USED_SIZE;
|
|
addr = (PVOID)((ULONG_PTR)current + HDR_USED_SIZE);
|
|
if (addr != best_aligned_addr)
|
|
{
|
|
blk = (HDR_FREE*)((ULONG_PTR)best_aligned_addr - HDR_USED_SIZE);
|
|
/*
|
|
* if size-aligned, break off the preceding bytes into their own block...
|
|
*/
|
|
previous = current;
|
|
previous_size = (ULONG_PTR)blk - (ULONG_PTR)previous - HDR_FREE_SIZE;
|
|
current = blk;
|
|
current_size -= ((ULONG_PTR)current - (ULONG_PTR)previous);
|
|
}
|
|
end = (PVOID)((ULONG_PTR)current + HDR_USED_SIZE + size);
|
|
|
|
if (current_size >= size + HDR_FREE_SIZE + MM_POOL_ALIGNMENT)
|
|
{
|
|
/* create a new free block after our block, if the memory size is >= 4 byte for this block */
|
|
next = (HDR_FREE*)((ULONG_PTR)current + size + HDR_USED_SIZE);
|
|
next_size = current_size - size - HDR_FREE_SIZE;
|
|
current_size = size;
|
|
end = (PVOID)((ULONG_PTR)next + HDR_FREE_SIZE);
|
|
}
|
|
|
|
if (previous)
|
|
{
|
|
remove_from_free_list(previous);
|
|
if (!grow_block(previous, end))
|
|
{
|
|
add_to_free_list(previous);
|
|
return NULL;
|
|
}
|
|
memset(current, 0, HDR_USED_SIZE);
|
|
current->hdr.Size = current_size + HDR_USED_SIZE;
|
|
current->hdr.Magic = BLOCK_HDR_USED_MAGIC;
|
|
current->hdr.previous = &previous->hdr;
|
|
previous->hdr.Size = previous_size + HDR_FREE_SIZE;
|
|
if (next == NULL)
|
|
{
|
|
blk = (HDR_FREE*)((ULONG_PTR)current + current->hdr.Size);
|
|
if ((ULONG_PTR)blk < (ULONG_PTR)MiNonPagedPoolStart + MiNonPagedPoolLength)
|
|
{
|
|
blk->hdr.previous = ¤t->hdr;
|
|
}
|
|
}
|
|
|
|
add_to_free_list(previous);
|
|
}
|
|
else
|
|
{
|
|
remove_from_free_list(current);
|
|
|
|
if (!grow_block(current, end))
|
|
{
|
|
add_to_free_list(current);
|
|
return NULL;
|
|
}
|
|
current->hdr.Magic = BLOCK_HDR_USED_MAGIC;
|
|
if (next)
|
|
{
|
|
current->hdr.Size = current_size + HDR_USED_SIZE;
|
|
}
|
|
}
|
|
|
|
if (next)
|
|
{
|
|
memset(next, 0, HDR_FREE_SIZE);
|
|
next->hdr.Size = next_size + HDR_FREE_SIZE;
|
|
next->hdr.Magic = BLOCK_HDR_FREE_MAGIC;
|
|
next->hdr.previous = ¤t->hdr;
|
|
blk = (HDR_FREE*)((ULONG_PTR)next + next->hdr.Size);
|
|
if ((ULONG_PTR)blk < (ULONG_PTR)MiNonPagedPoolStart + MiNonPagedPoolLength)
|
|
{
|
|
blk->hdr.previous = &next->hdr;
|
|
}
|
|
add_to_free_list(next);
|
|
}
|
|
|
|
add_to_used_list((HDR_USED*)current);
|
|
VALIDATE_POOL;
|
|
|
|
if (size < PAGE_SIZE)
|
|
{
|
|
addr = (PVOID)((ULONG_PTR)current + HDR_USED_SIZE);
|
|
if (PAGE_ROUND_DOWN(addr) != PAGE_ROUND_DOWN((PVOID)((ULONG_PTR)addr + size - 1)))
|
|
{
|
|
DPRINT1("%x %x\n", addr, (ULONG_PTR)addr + size);
|
|
}
|
|
ASSERT (PAGE_ROUND_DOWN(addr) == PAGE_ROUND_DOWN((PVOID)((ULONG_PTR)addr + size - 1)));
|
|
}
|
|
if (alignment)
|
|
{
|
|
addr = (PVOID)((ULONG_PTR)current + HDR_USED_SIZE);
|
|
ASSERT(MM_ROUND_UP(addr, alignment) == addr);
|
|
}
|
|
return (HDR_USED*)current;
|
|
}
|
|
|
|
ULONG STDCALL
|
|
ExRosQueryNonPagedPoolTag ( PVOID Addr )
|
|
{
|
|
HDR_USED* blk=(HDR_USED*)((ULONG_PTR)Addr - HDR_USED_SIZE);
|
|
if (blk->hdr.Magic != BLOCK_HDR_USED_MAGIC)
|
|
KEBUGCHECK(0);
|
|
|
|
return blk->Tag;
|
|
}
|
|
|
|
#if defined(NPOOL_REDZONE_CHECK) || defined(NPOOL_REDZONE_CHECK_FULL)
|
|
void check_redzone_header(HDR_USED* hdr)
|
|
{
|
|
PUCHAR LoZone = (PUCHAR)((ULONG_PTR)hdr + HDR_USED_SIZE - NPOOL_REDZONE_SIZE);
|
|
PUCHAR HiZone = (PUCHAR)((ULONG_PTR)hdr + HDR_USED_SIZE + hdr->UserSize);
|
|
BOOLEAN LoOK = TRUE;
|
|
BOOLEAN HiOK = TRUE;
|
|
ULONG i;
|
|
CHAR c[5];
|
|
|
|
for (i = 0; i < NPOOL_REDZONE_SIZE; i++)
|
|
{
|
|
if (LoZone[i] != NPOOL_REDZONE_LOVALUE)
|
|
{
|
|
LoOK = FALSE;
|
|
}
|
|
if (HiZone[i] != NPOOL_REDZONE_HIVALUE)
|
|
{
|
|
HiOK = FALSE;
|
|
}
|
|
}
|
|
|
|
if (!HiOK || !LoOK)
|
|
{
|
|
c[0] = (CHAR)((hdr->Tag >> 24) & 0xFF);
|
|
c[1] = (CHAR)((hdr->Tag >> 16) & 0xFF);
|
|
c[2] = (CHAR)((hdr->Tag >> 8) & 0xFF);
|
|
c[3] = (CHAR)(hdr->Tag & 0xFF);
|
|
c[4] = 0;
|
|
|
|
if (!isprint(c[0]) || !isprint(c[1]) || !isprint(c[2]) || !isprint(c[3]))
|
|
{
|
|
c[0] = 0;
|
|
}
|
|
|
|
if (!LoOK)
|
|
{
|
|
DbgPrint("NPOOL: Low-side redzone overwritten, Block %x, Size %d, Tag %x(%s), Caller %x\n",
|
|
(ULONG_PTR)hdr + HDR_USED_SIZE, hdr->UserSize, hdr->Tag, c, hdr->Caller);
|
|
}
|
|
if (!HiOK)
|
|
{
|
|
DbgPrint("NPPOL: High-side redzone overwritten, Block %x, Size %d, Tag %x(%s), Caller %x\n",
|
|
(ULONG_PTR)hdr + HDR_USED_SIZE, hdr->UserSize, hdr->Tag, c, hdr->Caller);
|
|
}
|
|
KEBUGCHECK(0);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef NPOOL_REDZONE_CHECK_FULL
|
|
void check_redzone_list(void)
|
|
{
|
|
PLIST_ENTRY current_entry;
|
|
|
|
current_entry = UsedBlockListHead.Flink;
|
|
while (current_entry != &UsedBlockListHead)
|
|
{
|
|
check_redzone_header(CONTAINING_RECORD(current_entry, HDR_USED, ListEntry));
|
|
current_entry = current_entry->Flink;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
VOID STDCALL ExFreeNonPagedPool (PVOID block)
|
|
/*
|
|
* FUNCTION: Releases previously allocated memory
|
|
* ARGUMENTS:
|
|
* block = block to free
|
|
*/
|
|
{
|
|
HDR_USED* blk=(HDR_USED*)((ULONG_PTR)block - HDR_USED_SIZE);
|
|
KIRQL oldIrql;
|
|
|
|
if (block == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
DPRINT("freeing block %x\n",blk);
|
|
|
|
POOL_TRACE("ExFreePool(block %x), size %d, caller %x\n",block,blk->hdr.Size,
|
|
blk->Caller);
|
|
KeAcquireSpinLock(&MmNpoolLock, &oldIrql);
|
|
|
|
VALIDATE_POOL;
|
|
if (blk->hdr.Magic != BLOCK_HDR_USED_MAGIC)
|
|
{
|
|
if (blk->hdr.Magic == BLOCK_HDR_FREE_MAGIC)
|
|
{
|
|
DbgPrint("ExFreePool of already freed address %x\n", block);
|
|
}
|
|
else
|
|
{
|
|
DbgPrint("ExFreePool of non-allocated address %x (magic %x)\n",
|
|
block, blk->hdr.Magic);
|
|
}
|
|
KEBUGCHECK(0);
|
|
return;
|
|
}
|
|
|
|
#if defined(NPOOL_REDZONE_CHECK) || defined(NPOOL_REDZONE_CHECK_FULL)
|
|
check_redzone_header(blk);
|
|
#endif
|
|
|
|
#ifdef NPOOL_REDZONE_CHECK_FULL
|
|
check_redzone_list();
|
|
#endif
|
|
|
|
memset(block, 0xcc, blk->hdr.Size - HDR_USED_SIZE);
|
|
|
|
#ifdef TAG_STATISTICS_TRACKING
|
|
MiRemoveFromTagHashTable(blk);
|
|
#endif
|
|
|
|
remove_from_used_list(blk);
|
|
blk->hdr.Magic = BLOCK_HDR_FREE_MAGIC;
|
|
add_to_free_list((HDR_FREE*)blk);
|
|
VALIDATE_POOL;
|
|
KeReleaseSpinLock(&MmNpoolLock, oldIrql);
|
|
}
|
|
|
|
PVOID STDCALL
|
|
ExAllocateNonPagedPoolWithTag(POOL_TYPE Type, ULONG Size, ULONG Tag, PVOID Caller)
|
|
{
|
|
#if defined(NPOOL_REDZONE_CHECK) || defined(NPOOL_REDZONE_CHECK_FULL)
|
|
ULONG UserSize;
|
|
#endif
|
|
PVOID block;
|
|
HDR_USED* best = NULL;
|
|
KIRQL oldIrql;
|
|
ULONG alignment;
|
|
|
|
POOL_TRACE("ExAllocatePool(NumberOfBytes %d) caller %x ",
|
|
Size,Caller);
|
|
|
|
KeAcquireSpinLock(&MmNpoolLock, &oldIrql);
|
|
|
|
#ifdef NPOOL_REDZONE_CHECK_FULL
|
|
check_redzone_list();
|
|
#endif
|
|
|
|
VALIDATE_POOL;
|
|
|
|
#if 1
|
|
/* after some allocations print the npaged pool stats */
|
|
#ifdef TAG_STATISTICS_TRACKING
|
|
|
|
{
|
|
static ULONG counter = 0;
|
|
if (counter++ % 100000 == 0)
|
|
{
|
|
MiDebugDumpNonPagedPoolStats(FALSE);
|
|
MmPrintMemoryStatistic();
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
/*
|
|
* accomodate this useful idiom
|
|
*/
|
|
if (Size == 0)
|
|
{
|
|
POOL_TRACE("= NULL\n");
|
|
KeReleaseSpinLock(&MmNpoolLock, oldIrql);
|
|
return(NULL);
|
|
}
|
|
/* Make the size dword alligned, this makes the block dword alligned */
|
|
#if defined(NPOOL_REDZONE_CHECK) || defined(NPOOL_REDZONE_CHECK_FULL)
|
|
UserSize = Size;
|
|
Size = ROUND_UP(Size + NPOOL_REDZONE_SIZE, MM_POOL_ALIGNMENT);
|
|
#else
|
|
Size = ROUND_UP(Size, MM_POOL_ALIGNMENT);
|
|
#endif
|
|
|
|
if (Size >= PAGE_SIZE)
|
|
{
|
|
alignment = PAGE_SIZE;
|
|
}
|
|
else if (Type & CACHE_ALIGNED_POOL_MASK)
|
|
{
|
|
alignment = MM_CACHE_LINE_SIZE;
|
|
}
|
|
else
|
|
{
|
|
alignment = 0;
|
|
}
|
|
|
|
best = get_block(Size, alignment);
|
|
if (best == NULL)
|
|
{
|
|
KeReleaseSpinLock(&MmNpoolLock, oldIrql);
|
|
DPRINT1("Trying to allocate %lu bytes from nonpaged pool - nothing suitable found, returning NULL\n",
|
|
Size );
|
|
KeRosDumpStackFrames(NULL, 10);
|
|
return NULL;
|
|
}
|
|
best->Tag = Tag;
|
|
best->Caller = Caller;
|
|
best->Dumped = FALSE;
|
|
best->TagListEntry.Flink = best->TagListEntry.Blink = NULL;
|
|
#if defined(NPOOL_REDZONE_CHECK) || defined(NPOOL_REDZONE_CHECK_FULL)
|
|
best->UserSize = UserSize;
|
|
memset((PVOID)((ULONG_PTR)best + HDR_USED_SIZE - NPOOL_REDZONE_SIZE), NPOOL_REDZONE_LOVALUE, NPOOL_REDZONE_SIZE);
|
|
memset((PVOID)((ULONG_PTR)best + HDR_USED_SIZE + UserSize), NPOOL_REDZONE_HIVALUE, NPOOL_REDZONE_SIZE);
|
|
#endif
|
|
|
|
#ifdef TAG_STATISTICS_TRACKING
|
|
|
|
MiAddToTagHashTable(best);
|
|
#endif
|
|
|
|
KeReleaseSpinLock(&MmNpoolLock, oldIrql);
|
|
block = (PVOID)((ULONG_PTR)best + HDR_USED_SIZE);
|
|
/* RtlZeroMemory(block, Size);*/
|
|
return(block);
|
|
}
|
|
|
|
VOID
|
|
INIT_FUNCTION
|
|
NTAPI
|
|
MiInitializeNonPagedPool(VOID)
|
|
{
|
|
NTSTATUS Status;
|
|
PFN_TYPE Page;
|
|
ULONG i;
|
|
PVOID Address;
|
|
HDR_USED* used;
|
|
HDR_FREE* free;
|
|
#ifdef TAG_STATISTICS_TRACKING
|
|
|
|
for (i = 0; i < TAG_HASH_TABLE_SIZE; i++)
|
|
{
|
|
InitializeListHead(&tag_hash_table[i]);
|
|
}
|
|
#endif
|
|
KeInitializeSpinLock(&MmNpoolLock);
|
|
InitializeListHead(&UsedBlockListHead);
|
|
InitializeListHead(&AddressListHead);
|
|
FreeBlockListRoot = NULL;
|
|
|
|
MiNonPagedPoolAllocMap = (PVOID)((ULONG_PTR)MiNonPagedPoolStart + PAGE_SIZE);
|
|
#if defined(NPOOL_REDZONE_CHECK) || defined(NPOOL_REDZONE_CHECK_FULL)
|
|
MiNonPagedPoolNrOfPages = ROUND_UP(MiNonPagedPoolLength / PAGE_SIZE, 32) / 8;
|
|
MiNonPagedPoolNrOfPages = ROUND_UP(MiNonPagedPoolNrOfPages + NPOOL_REDZONE_SIZE, MM_POOL_ALIGNMENT);
|
|
MiNonPagedPoolNrOfPages = PAGE_ROUND_UP(MiNonPagedPoolNrOfPages + HDR_FREE_SIZE) + PAGE_SIZE;
|
|
#else
|
|
MiNonPagedPoolNrOfPages = PAGE_ROUND_UP(ROUND_UP(MiNonPagedPoolLength / PAGE_SIZE, 32) / 8 + HDR_FREE_SIZE) + PAGE_SIZE;
|
|
#endif
|
|
MiNonPagedPoolNrOfPages /= PAGE_SIZE;
|
|
Address = MiNonPagedPoolStart;
|
|
|
|
for (i = 0; i < MiNonPagedPoolNrOfPages; i++)
|
|
{
|
|
Status = MmRequestPageMemoryConsumer(MC_NPPOOL, FALSE, &Page);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DbgPrint("Unable to allocate a page\n");
|
|
KEBUGCHECK(0);
|
|
}
|
|
|
|
Status = MmCreateVirtualMapping(NULL,
|
|
Address,
|
|
PAGE_READWRITE|PAGE_SYSTEM,
|
|
&Page,
|
|
1);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DbgPrint("Unable to create virtual mapping\n");
|
|
KEBUGCHECK(0);
|
|
}
|
|
Address = (PVOID)((ULONG_PTR)Address + PAGE_SIZE);
|
|
}
|
|
|
|
for (i = 0; i < MiNonPagedPoolNrOfPages; i++)
|
|
{
|
|
MiNonPagedPoolAllocMap[i / 32] |= (1 << (i % 32));
|
|
}
|
|
|
|
/* the first block is free */
|
|
free = (HDR_FREE*)MiNonPagedPoolStart;
|
|
free->hdr.Magic = BLOCK_HDR_FREE_MAGIC;
|
|
free->hdr.Size = PAGE_SIZE - HDR_USED_SIZE;
|
|
free->hdr.previous = NULL;
|
|
memset((PVOID)((ULONG_PTR)free + HDR_FREE_SIZE), 0xcc, free->hdr.Size - HDR_FREE_SIZE);
|
|
add_to_free_list(free);
|
|
|
|
/* the second block contains the non paged pool bitmap */
|
|
used = (HDR_USED*)((ULONG_PTR)free + free->hdr.Size);
|
|
used->hdr.Magic = BLOCK_HDR_USED_MAGIC;
|
|
#if defined(NPOOL_REDZONE_CHECK) || defined(NPOOL_REDZONE_CHECK_FULL)
|
|
used->UserSize = ROUND_UP(MiNonPagedPoolLength / PAGE_SIZE, 32) / 8;
|
|
used->hdr.Size = ROUND_UP(used->UserSize + NPOOL_REDZONE_SIZE, MM_POOL_ALIGNMENT) + HDR_USED_SIZE;
|
|
memset((PVOID)((ULONG_PTR)used + HDR_USED_SIZE - NPOOL_REDZONE_SIZE), NPOOL_REDZONE_LOVALUE, NPOOL_REDZONE_SIZE);
|
|
memset((PVOID)((ULONG_PTR)used + HDR_USED_SIZE + used->UserSize), NPOOL_REDZONE_HIVALUE, NPOOL_REDZONE_SIZE);
|
|
#else
|
|
used->hdr.Size = ROUND_UP(MiNonPagedPoolLength / PAGE_SIZE, 32) / 8 + HDR_USED_SIZE;
|
|
#endif
|
|
used->hdr.previous = &free->hdr;
|
|
used->Tag = 0xffffffff;
|
|
used->Caller = (PVOID)MiInitializeNonPagedPool;
|
|
used->Dumped = FALSE;
|
|
add_to_used_list(used);
|
|
#ifdef TAG_STATISTICS_TRACKING
|
|
MiAddToTagHashTable(used);
|
|
#endif
|
|
|
|
/* the third block is the free block after the bitmap */
|
|
free = (HDR_FREE*)((ULONG_PTR)used + used->hdr.Size);
|
|
free->hdr.Magic = BLOCK_HDR_FREE_MAGIC;
|
|
free->hdr.Size = MiNonPagedPoolLength - ((ULONG_PTR)free - (ULONG_PTR)MiNonPagedPoolStart);
|
|
free->hdr.previous = &used->hdr;
|
|
memset((PVOID)((ULONG_PTR)free + HDR_FREE_SIZE), 0xcc, (ULONG_PTR)Address - (ULONG_PTR)free - HDR_FREE_SIZE);
|
|
add_to_free_list(free);
|
|
}
|
|
|
|
PVOID
|
|
STDCALL
|
|
MiAllocateSpecialPool (IN POOL_TYPE PoolType,
|
|
IN SIZE_T NumberOfBytes,
|
|
IN ULONG Tag,
|
|
IN ULONG Underrun
|
|
)
|
|
{
|
|
/* FIXME: Special Pools not Supported */
|
|
DbgPrint("Special Pools not supported\n");
|
|
return NULL;
|
|
}
|
|
|
|
/* EOF */
|