Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

4812 lines
140 KiB

/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
mminit.c
Abstract:
This module contains the initialization for the memory management
system.
Author:
Lou Perazzoli (loup) 20-Mar-1989
Landy Wang (landyw) 02-Jun-1997
Revision History:
--*/
#include "mi.h"
PMMPTE MmSharedUserDataPte;
extern ULONG_PTR MmSystemPtesStart[MaximumPtePoolTypes];
extern PMMPTE MiSpecialPoolFirstPte;
extern ULONG MmPagedPoolCommit;
extern ULONG MmInPageSupportMinimum;
extern PFN_NUMBER MiExpansionPoolPagesInitialCharge;
extern ULONG MmAllocationPreference;
extern PVOID BBTBuffer;
extern PFN_COUNT BBTPagesToReserve;
ULONG_PTR MmSubsectionBase;
ULONG_PTR MmSubsectionTopPage;
ULONG MmDataClusterSize;
ULONG MmCodeClusterSize;
PFN_NUMBER MmResidentAvailableAtInit;
PPHYSICAL_MEMORY_DESCRIPTOR MmPhysicalMemoryBlock;
LIST_ENTRY MmLockConflictList;
LIST_ENTRY MmProtectedPteList;
KSPIN_LOCK MmProtectedPteLock;
LOGICAL MmPagedPoolMaximumDesired = FALSE;
#if defined (_MI_DEBUG_SUB)
ULONG MiTrackSubs = 0x2000; // Set to nonzero to enable subsection tracking code.
LONG MiSubsectionIndex;
PMI_SUB_TRACES MiSubsectionTraces;
#endif
#if defined (_MI_DEBUG_DIRTY)
ULONG MiTrackDirtys = 0x10000; // Set to nonzero to enable subsection tracking code.
LONG MiDirtyIndex;
PMI_DIRTY_TRACES MiDirtyTraces;
#endif
#if defined (_MI_DEBUG_DATA)
ULONG MiTrackData = 0x10000; // Set to nonzero to enable data tracking code.
LONG MiDataIndex;
PMI_DATA_TRACES MiDataTraces;
#endif
VOID
MiMapBBTMemory (
IN PLOADER_PARAMETER_BLOCK LoaderBlock
);
VOID
MiEnablePagingTheExecutive(
VOID
);
VOID
MiEnablePagingOfDriverAtInit (
IN PMMPTE PointerPte,
IN PMMPTE LastPte
);
VOID
MiBuildPagedPool (
);
VOID
MiWriteProtectSystemImage (
IN PVOID DllBase
);
VOID
MiInitializePfnTracing (
VOID
);
PFN_NUMBER
MiPagesInLoaderBlock (
IN PLOADER_PARAMETER_BLOCK LoaderBlock,
IN PBOOLEAN IncludeType
);
#ifndef NO_POOL_CHECKS
VOID
MiInitializeSpecialPoolCriteria (
IN VOID
);
#endif
#ifdef _MI_MESSAGE_SERVER
VOID
MiInitializeMessageQueue (
VOID
);
#endif
static
VOID
MiMemoryLicense (
IN PLOADER_PARAMETER_BLOCK LoaderBlock
);
VOID
MiInitializeCacheOverrides (
VOID
);
//
// The thresholds can be overridden by the registry.
//
PFN_NUMBER MmLowMemoryThreshold;
PFN_NUMBER MmHighMemoryThreshold;
PKEVENT MiLowMemoryEvent;
PKEVENT MiHighMemoryEvent;
NTSTATUS
MiCreateMemoryEvent (
IN PUNICODE_STRING EventName,
OUT PKEVENT *Event
);
LOGICAL
MiInitializeMemoryEvents (
VOID
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT,MmInitSystem)
#pragma alloc_text(INIT,MiMapBBTMemory)
#pragma alloc_text(INIT,MmInitializeMemoryLimits)
#pragma alloc_text(INIT,MmFreeLoaderBlock)
#pragma alloc_text(INIT,MiBuildPagedPool)
#pragma alloc_text(INIT,MiFindInitializationCode)
#pragma alloc_text(INIT,MiEnablePagingTheExecutive)
#pragma alloc_text(INIT,MiEnablePagingOfDriverAtInit)
#pragma alloc_text(INIT,MiPagesInLoaderBlock)
#pragma alloc_text(INIT,MiCreateMemoryEvent)
#pragma alloc_text(INIT,MiInitializeMemoryEvents)
#pragma alloc_text(INIT,MiInitializeCacheOverrides)
#pragma alloc_text(INIT,MiMemoryLicense)
#pragma alloc_text(PAGELK,MiFreeInitializationCode)
#endif
//
// Default is a 300 second life span for modified mapped pages -
// This can be overridden in the registry.
//
#ifdef ALLOC_DATA_PRAGMA
#pragma data_seg("INITDATA")
#endif
ULONG MmModifiedPageLifeInSeconds = 300;
#ifdef ALLOC_DATA_PRAGMA
#pragma data_seg()
#endif
LARGE_INTEGER MiModifiedPageLife;
BOOLEAN MiTimerPending = FALSE;
KEVENT MiMappedPagesTooOldEvent;
KDPC MiModifiedPageWriterTimerDpc;
KTIMER MiModifiedPageWriterTimer;
//
// The following constants are based on the number PAGES not the
// memory size. For convenience the number of pages is calculated
// based on a 4k page size. Hence 12mb with 4k page is 3072.
//
#define MM_SMALL_SYSTEM ((13*1024*1024) / 4096)
#define MM_MEDIUM_SYSTEM ((19*1024*1024) / 4096)
#define MM_MIN_INITIAL_PAGED_POOL ((32*1024*1024) >> PAGE_SHIFT)
#define MM_DEFAULT_IO_LOCK_LIMIT (2 * 1024 * 1024)
extern WSLE_NUMBER MmMaximumWorkingSetSize;
extern ULONG MmEnforceWriteProtection;
extern CHAR MiPteStr[];
extern LONG MiTrimInProgressCount;
#if (_MI_PAGING_LEVELS < 3)
PFN_NUMBER MmSystemPageDirectory[PD_PER_SYSTEM];
PMMPTE MmSystemPagePtes;
#endif
ULONG MmTotalSystemCodePages;
MM_SYSTEMSIZE MmSystemSize;
ULONG MmLargeSystemCache;
ULONG MmProductType;
extern ULONG MiVerifyAllDrivers;
LIST_ENTRY MmLoadedUserImageList;
PPAGE_FAULT_NOTIFY_ROUTINE MmPageFaultNotifyRoutine;
#if defined (_WIN64)
#define MM_ALLOCATION_FRAGMENT (64 * 1024 * 1024)
#else
#define MM_ALLOCATION_FRAGMENT (64 * 1024)
#endif
//
// Registry-settable.
//
SIZE_T MmAllocationFragment;
#if defined(MI_MULTINODE)
HALNUMAPAGETONODE
MiNonNumaPageToNodeColor (
IN PFN_NUMBER PageFrameIndex
)
/*++
Routine Description:
Return the node color of the page.
Arguments:
PageFrameIndex - Supplies the physical page number.
Return Value:
Node color is always zero in non-NUMA configurations.
--*/
{
UNREFERENCED_PARAMETER (PageFrameIndex);
return 0;
}
//
// This node determination function pointer is initialized to return 0.
//
// Architecture-dependent initialization may repoint it to a HAL routine
// for NUMA configurations.
//
PHALNUMAPAGETONODE MmPageToNode = MiNonNumaPageToNodeColor;
VOID
MiDetermineNode (
IN PFN_NUMBER PageFrameIndex,
IN PMMPFN Pfn
)
/*++
Routine Description:
This routine is called during initial freelist population or when
physical memory is being hot-added. It then determines which node
(in a multinode NUMA system) the physical memory resides in, and
marks the PFN entry accordingly.
N.B. The actual page to node determination is machine dependent
and done by a routine in the chipset driver or the HAL, called
via the MmPageToNode function pointer.
Arguments:
PageFrameIndex - Supplies the physical page number.
Pfn - Supplies a pointer to the PFN database element.
Return Value:
None.
Environment:
None although typically this routine is called with the PFN
database locked.
--*/
{
ULONG Temp;
ASSERT (Pfn == MI_PFN_ELEMENT(PageFrameIndex));
Temp = MmPageToNode (PageFrameIndex);
ASSERT (Temp < MAXIMUM_CCNUMA_NODES);
Pfn->u3.e1.PageColor = Temp;
}
#endif
BOOLEAN
MmInitSystem (
IN ULONG Phase,
IN PLOADER_PARAMETER_BLOCK LoaderBlock
)
/*++
Routine Description:
This function is called during Phase 0, phase 1 and at the end
of phase 1 ("phase 2") initialization.
Phase 0 initializes the memory management paging functions,
nonpaged and paged pool, the PFN database, etc.
Phase 1 initializes the section objects, the physical memory
object, and starts the memory management system threads.
Phase 2 frees memory used by the OsLoader.
Arguments:
Phase - System initialization phase.
LoaderBlock - Supplies a pointer to the system loader block.
Return Value:
Returns TRUE if the initialization was successful.
Environment:
Kernel Mode Only. System initialization.
--*/
{
PEPROCESS Process;
PSINGLE_LIST_ENTRY SingleListEntry;
PFN_NUMBER NumberOfPages;
HANDLE ThreadHandle;
OBJECT_ATTRIBUTES ObjectAttributes;
PMMPTE PointerPte;
PMMPTE PointerPde;
PMMPTE StartPde;
PMMPTE EndPde;
PMMPFN Pfn1;
PFN_NUMBER i, j;
PFN_NUMBER DeferredMdlEntries;
PFN_NUMBER PageFrameIndex;
PFN_NUMBER DirectoryFrameIndex;
MMPTE TempPte;
KIRQL OldIrql;
PLIST_ENTRY NextEntry;
PKLDR_DATA_TABLE_ENTRY DataTableEntry;
ULONG MaximumSystemCacheSize;
ULONG MaximumSystemCacheSizeTotal;
PIMAGE_NT_HEADERS NtHeaders;
ULONG_PTR SystemPteMultiplier;
ULONG_PTR DefaultSystemViewSize;
ULONG_PTR SessionEnd;
SIZE_T SystemViewMax;
SIZE_T HydraImageMax;
SIZE_T HydraViewMax;
SIZE_T HydraPoolMax;
SIZE_T HydraSpaceUsedForSystemViews;
BOOLEAN IncludeType[LoaderMaximum];
LOGICAL AutosizingFragment;
ULONG VerifierFlags;
#if DBG
MMPTE Pointer;
#endif
#if (_MI_PAGING_LEVELS >= 3)
LOGICAL FirstPpe;
PMMPTE StartPpe;
#endif
#if (_MI_PAGING_LEVELS >= 4)
LOGICAL FirstPxe;
PMMPTE StartPxe;
#endif
#if defined(_X86_)
PCHAR ReducedUserVaOption;
ULONG UserVaLimit;
ULONG ReductionInBytes;
#endif
j = 0;
PointerPde = NULL;
//
// Make sure structure alignment is okay.
//
if (Phase == 0) {
MmThrottleTop = 450;
MmThrottleBottom = 127;
//
// Set the highest user address, the system range start address, the
// user probe address, and the virtual bias.
//
#if defined(_WIN64)
MmHighestUserAddress = MI_HIGHEST_USER_ADDRESS;
MmUserProbeAddress = MI_USER_PROBE_ADDRESS;
MmSystemRangeStart = MI_SYSTEM_RANGE_START;
#else
MmHighestUserAddress = (PVOID)(KSEG0_BASE - 0x10000 - 1);
MmUserProbeAddress = KSEG0_BASE - 0x10000;
MmSystemRangeStart = (PVOID)KSEG0_BASE;
#endif
MiHighestUserPte = MiGetPteAddress (MmHighestUserAddress);
MiHighestUserPde = MiGetPdeAddress (MmHighestUserAddress);
#if (_MI_PAGING_LEVELS >= 4)
MiHighestUserPpe = MiGetPpeAddress (MmHighestUserAddress);
MiHighestUserPxe = MiGetPxeAddress (MmHighestUserAddress);
#endif
#if defined(_X86_) || defined(_AMD64_)
MmBootImageSize = LoaderBlock->Extension->LoaderPagesSpanned;
MmBootImageSize *= PAGE_SIZE;
MmBootImageSize = MI_ROUND_TO_SIZE (MmBootImageSize,
MM_VA_MAPPED_BY_PDE);
ASSERT ((MmBootImageSize % MM_VA_MAPPED_BY_PDE) == 0);
#endif
#if defined(_X86_)
MmVirtualBias = LoaderBlock->u.I386.VirtualBias;
#endif
//
// Initialize system and Hydra mapped view sizes.
//
DefaultSystemViewSize = MM_SYSTEM_VIEW_SIZE;
MmSessionSize = MI_SESSION_SPACE_DEFAULT_TOTAL_SIZE;
SessionEnd = (ULONG_PTR) MM_SESSION_SPACE_DEFAULT_END;
#define MM_MB_MAPPED_BY_PDE (MM_VA_MAPPED_BY_PDE / (1024*1024))
//
// A PDE of virtual space is the minimum system view size allowed.
//
if (MmSystemViewSize < (MM_VA_MAPPED_BY_PDE / (1024*1024))) {
MmSystemViewSize = DefaultSystemViewSize;
}
else {
//
// The view size has been specified (in megabytes) by the registry.
// Validate it.
//
if (MmVirtualBias == 0) {
//
// Round the system view size (in megabytes) to a PDE multiple.
//
MmSystemViewSize = MI_ROUND_TO_SIZE (MmSystemViewSize,
MM_MB_MAPPED_BY_PDE);
//
// NT64 locates system views just after systemwide paged pool,
// so the size of the system views are not limited by session
// space. Arbitrarily make the maximum a PPE's worth.
//
//
// NT32 shares system view VA space with session VA space due
// to the shortage of virtual addresses. Thus increasing the
// system view size means potentially decreasing the maximum
// session space size.
//
SystemViewMax = (MI_SESSION_SPACE_MAXIMUM_TOTAL_SIZE) / (1024*1024);
#if !defined(_WIN64)
//
// Ensure at least enough space is left for
// the standard default session layout.
//
SystemViewMax -= (MmSessionSize / (1024*1024));
#endif
//
// Note a view size of -1 will be rounded to zero. Treat -1
// as requesting the maximum.
//
if ((MmSystemViewSize > SystemViewMax) ||
(MmSystemViewSize == 0)) {
MmSystemViewSize = SystemViewMax;
}
MmSystemViewSize *= (1024*1024);
}
else {
MmSystemViewSize = DefaultSystemViewSize;
}
}
#if defined(_WIN64)
HydraSpaceUsedForSystemViews = 0;
#else
HydraSpaceUsedForSystemViews = MmSystemViewSize;
#endif
MiSessionImageEnd = SessionEnd;
//
// Select reasonable Hydra image, pool and view virtual sizes.
// A PDE of virtual space is the minimum size allowed for each type.
//
if (MmVirtualBias == 0) {
if (MmSessionImageSize < MM_MB_MAPPED_BY_PDE) {
MmSessionImageSize = MI_SESSION_DEFAULT_IMAGE_SIZE;
}
else {
//
// The Hydra image size has been specified (in megabytes)
// by the registry.
//
// Round it to a PDE multiple and validate it.
//
MmSessionImageSize = MI_ROUND_TO_SIZE (MmSessionImageSize,
MM_MB_MAPPED_BY_PDE);
HydraImageMax = (MI_SESSION_SPACE_MAXIMUM_TOTAL_SIZE - HydraSpaceUsedForSystemViews - (MmSessionSize - MI_SESSION_DEFAULT_IMAGE_SIZE)) / (1024*1024);
//
// Note a view size of -1 will be rounded to zero.
// Treat -1 as requesting the maximum.
//
if ((MmSessionImageSize > HydraImageMax) ||
(MmSessionImageSize == 0)) {
MmSessionImageSize = HydraImageMax;
}
MmSessionImageSize *= (1024*1024);
MmSessionSize -= MI_SESSION_DEFAULT_IMAGE_SIZE;
MmSessionSize += MmSessionImageSize;
}
MiSessionImageStart = SessionEnd - MmSessionImageSize;
//
// The session image start and size has been established.
//
// Now initialize the session pool and view ranges which lie
// virtually below it.
//
if (MmSessionViewSize < MM_MB_MAPPED_BY_PDE) {
MmSessionViewSize = MI_SESSION_DEFAULT_VIEW_SIZE;
}
else {
//
// The Hydra view size has been specified (in megabytes)
// by the registry. Validate it.
//
// Round the Hydra view size to a PDE multiple.
//
MmSessionViewSize = MI_ROUND_TO_SIZE (MmSessionViewSize,
MM_MB_MAPPED_BY_PDE);
HydraViewMax = (MI_SESSION_SPACE_MAXIMUM_TOTAL_SIZE - HydraSpaceUsedForSystemViews - (MmSessionSize - MI_SESSION_DEFAULT_VIEW_SIZE)) / (1024*1024);
//
// Note a view size of -1 will be rounded to zero.
// Treat -1 as requesting the maximum.
//
if ((MmSessionViewSize > HydraViewMax) ||
(MmSessionViewSize == 0)) {
MmSessionViewSize = HydraViewMax;
}
MmSessionViewSize *= (1024*1024);
MmSessionSize -= MI_SESSION_DEFAULT_VIEW_SIZE;
MmSessionSize += MmSessionViewSize;
}
MiSessionViewStart = SessionEnd - MmSessionImageSize - MI_SESSION_SPACE_WS_SIZE - MI_SESSION_SPACE_STRUCT_SIZE - MmSessionViewSize;
//
// The session view start and size has been established.
//
// Now initialize the session pool start and size which lies
// virtually just below it.
//
MiSessionPoolEnd = MiSessionViewStart;
if (MmSessionPoolSize < MM_MB_MAPPED_BY_PDE) {
#if !defined(_WIN64)
//
// Professional and below use systemwide paged pool for session
// allocations (this decision is made in win32k.sys). Server
// and above use real session pool and 16mb isn't enough to
// play high end game applications, etc. Since we're not
// booted /3GB, try for an additional 16mb now.
//
if ((MmSessionPoolSize == 0) && (MmProductType != 0x00690057)) {
HydraPoolMax = MI_SESSION_SPACE_MAXIMUM_TOTAL_SIZE - HydraSpaceUsedForSystemViews - MmSessionSize;
if (HydraPoolMax >= 2 * MI_SESSION_DEFAULT_POOL_SIZE) {
MmSessionPoolSize = 2 * MI_SESSION_DEFAULT_POOL_SIZE;
MmSessionSize -= MI_SESSION_DEFAULT_POOL_SIZE;
MmSessionSize += MmSessionPoolSize;
}
else {
MmSessionPoolSize = MI_SESSION_DEFAULT_POOL_SIZE;
}
}
else
#endif
MmSessionPoolSize = MI_SESSION_DEFAULT_POOL_SIZE;
}
else {
//
// The Hydra pool size has been specified (in megabytes)
// by the registry. Validate it.
//
// Round the Hydra pool size to a PDE multiple.
//
MmSessionPoolSize = MI_ROUND_TO_SIZE (MmSessionPoolSize,
MM_MB_MAPPED_BY_PDE);
HydraPoolMax = (MI_SESSION_SPACE_MAXIMUM_TOTAL_SIZE - HydraSpaceUsedForSystemViews - (MmSessionSize - MI_SESSION_DEFAULT_POOL_SIZE)) / (1024*1024);
//
// Note a view size of -1 will be rounded to zero.
// Treat -1 as requesting the maximum.
//
if ((MmSessionPoolSize > HydraPoolMax) ||
(MmSessionPoolSize == 0)) {
MmSessionPoolSize = HydraPoolMax;
}
MmSessionPoolSize *= (1024*1024);
MmSessionSize -= MI_SESSION_DEFAULT_POOL_SIZE;
MmSessionSize += MmSessionPoolSize;
}
MiSessionPoolStart = MiSessionPoolEnd - MmSessionPoolSize;
MmSessionBase = (ULONG_PTR) MiSessionPoolStart;
#if defined (_WIN64)
//
// Session special pool immediately follows session regular pool
// assuming the user has enabled either the verifier or special
// pool.
//
if ((MmVerifyDriverBufferLength != (ULONG)-1) ||
((MmSpecialPoolTag != 0) && (MmSpecialPoolTag != (ULONG)-1))) {
MmSessionSize = MI_SESSION_SPACE_MAXIMUM_TOTAL_SIZE;
MmSessionSpecialPoolEnd = (PVOID) MiSessionPoolStart;
MmSessionBase = MM_SESSION_SPACE_DEFAULT;
MmSessionSpecialPoolStart = (PVOID) MmSessionBase;
}
#endif
ASSERT (MmSessionBase + MmSessionSize == SessionEnd);
MiSessionSpaceEnd = SessionEnd;
MiSessionSpacePageTables = (ULONG)(MmSessionSize / MM_VA_MAPPED_BY_PDE);
#if !defined (_WIN64)
MiSystemViewStart = MmSessionBase - MmSystemViewSize;
#endif
}
else {
//
// When booted /3GB, no size overrides are allowed due to the
// already severely limited virtual address space.
// Initialize the other Hydra variables after the system cache.
//
MmSessionViewSize = MI_SESSION_DEFAULT_VIEW_SIZE;
MmSessionPoolSize = MI_SESSION_DEFAULT_POOL_SIZE;
MmSessionImageSize = MI_SESSION_DEFAULT_IMAGE_SIZE;
MiSessionImageStart = MiSessionImageEnd - MmSessionImageSize;
}
//
// Set the highest section base address.
//
// N.B. In 32-bit systems this address must be 2gb or less even for
// systems that run with 3gb enabled. Otherwise, it would not
// be possible to map based sections identically in all processes.
//
MmHighSectionBase = ((PCHAR)MmHighestUserAddress - 0x800000);
MaximumSystemCacheSize = (MM_SYSTEM_CACHE_END - MM_SYSTEM_CACHE_START) >> PAGE_SHIFT;
#if defined(_X86_)
//
// If boot.ini specified a sane number of MB that the administrator
// wants to use for user virtual address space then use it.
//
UserVaLimit = 0;
ReducedUserVaOption = strstr(LoaderBlock->LoadOptions, "USERVA");
if (ReducedUserVaOption != NULL) {
ReducedUserVaOption = strstr(ReducedUserVaOption,"=");
if (ReducedUserVaOption != NULL) {
UserVaLimit = atol(ReducedUserVaOption+1);
UserVaLimit = MI_ROUND_TO_SIZE (UserVaLimit, ((MM_VA_MAPPED_BY_PDE) / (1024*1024)));
}
//
// Ignore the USERVA switch if the limit is too small.
//
if (UserVaLimit <= (2048 + 16)) {
UserVaLimit = 0;
}
}
if (MmVirtualBias != 0) {
//
// If the size of the boot image (likely due to a large registry)
// overflows into where paged pool would normally start, then
// move paged pool up now. This costs virtual address space (ie:
// performance) but more importantly, allows the system to boot.
//
if (MmBootImageSize > 16 * 1024 * 1024) {
MmPagedPoolStart = (PVOID)((PCHAR)MmPagedPoolStart + (MmBootImageSize - 16 * 1024 * 1024));
ASSERT (((ULONG_PTR)MmPagedPoolStart % MM_VA_MAPPED_BY_PDE) == 0);
}
//
// The system has been biased to an alternate base address to
// allow 3gb of user address space, set the user probe address
// and the maximum system cache size.
//
// If the system has been biased to an alternate base address to
// allow 3gb of user address space, then set the user probe address
// and the maximum system cache size.
if ((UserVaLimit > 2048) && (UserVaLimit < 3072)) {
//
// Use any space between the maximum user virtual address
// and the system for extra system PTEs.
//
// Convert input MB to bytes.
//
UserVaLimit -= 2048;
UserVaLimit *= (1024*1024);
//
// Don't let the user specify a value which would cause us to
// prematurely overwrite portions of the kernel & loader block.
//
if (UserVaLimit < MmBootImageSize) {
UserVaLimit = MmBootImageSize;
}
}
else {
UserVaLimit = 0x40000000;
}
MmHighestUserAddress = ((PCHAR)MmHighestUserAddress + UserVaLimit);
MmSystemRangeStart = ((PCHAR)MmSystemRangeStart + UserVaLimit);
MmUserProbeAddress += UserVaLimit;
MiMaximumWorkingSet += UserVaLimit >> PAGE_SHIFT;
if (UserVaLimit != 0x40000000) {
MiUseMaximumSystemSpace = (ULONG_PTR)MmSystemRangeStart;
MiUseMaximumSystemSpaceEnd = 0xC0000000;
}
MiHighestUserPte = MiGetPteAddress (MmHighestUserAddress);
MiHighestUserPde = MiGetPdeAddress (MmHighestUserAddress);
//
// Moving to 3GB means moving session space to just above
// the system cache (and lowering the system cache max size
// accordingly). Here's the visual:
//
// +------------------------------------+
// C1000000 | System cache resides here |
// | and grows upward. |
// | | |
// | | |
// | \/ |
// | |
// +------------------------------------+
// | Session space (Hydra). |
// +------------------------------------+
// | Systemwide global mapped views. |
// +------------------------------------+
// | |
// | ^ |
// | | |
// | | |
// | |
// | Kernel, HAL & boot loaded images |
// | grow downward from E1000000. |
// | Total size is specified by |
// | LoaderBlock->u.I386.BootImageSize. |
// | Note only ntldrs after Build 2195 |
// | are capable of loading the boot |
// | images in descending order from |
// | a hardcoded E1000000 on down. |
// E1000000 +------------------------------------+
//
MaximumSystemCacheSize -= MmBootImageSize >> PAGE_SHIFT;
MaximumSystemCacheSize -= MmSessionSize >> PAGE_SHIFT;
MaximumSystemCacheSize -= MmSystemViewSize >> PAGE_SHIFT;
MmSessionBase = (ULONG_PTR)(MM_SYSTEM_CACHE_START +
(MaximumSystemCacheSize << PAGE_SHIFT));
MiSystemViewStart = MmSessionBase + MmSessionSize;
MiSessionPoolStart = MmSessionBase;
MiSessionPoolEnd = MiSessionPoolStart + MmSessionPoolSize;
MiSessionViewStart = MiSessionPoolEnd;
MiSessionSpaceEnd = (ULONG_PTR)MmSessionBase + MmSessionSize;
MiSessionSpacePageTables = MmSessionSize / MM_VA_MAPPED_BY_PDE;
MiSessionImageEnd = MiSessionSpaceEnd;
MiSessionImageStart = MiSessionImageEnd - MmSessionImageSize;
}
else if ((UserVaLimit >= 64) && (UserVaLimit < 2048)) {
//
// Convert input MB to bytes.
//
UserVaLimit *= (1024*1024);
ReductionInBytes = 0x80000000 - UserVaLimit;
MmHighestUserAddress = ((PCHAR)MmHighestUserAddress - ReductionInBytes);
MmSystemRangeStart = ((PCHAR)MmSystemRangeStart - ReductionInBytes);
MmUserProbeAddress -= ReductionInBytes;
MiMaximumWorkingSet -= ReductionInBytes >> PAGE_SHIFT;
MiUseMaximumSystemSpace = (ULONG_PTR)MmSystemRangeStart;
MiUseMaximumSystemSpaceEnd = (ULONG_PTR)MiUseMaximumSystemSpace + ReductionInBytes;
MmHighSectionBase = (PVOID)((PCHAR)MmHighSectionBase - ReductionInBytes);
MiHighestUserPte = MiGetPteAddress (MmHighestUserAddress);
MiHighestUserPde = MiGetPdeAddress (MmHighestUserAddress);
}
#else
#if !defined (_WIN64)
MaximumSystemCacheSize -= (MmSystemViewSize >> PAGE_SHIFT);
#endif
#endif
//
// Initialize some global session variables.
//
MmSessionSpace = (PMM_SESSION_SPACE)((ULONG_PTR)MmSessionBase + MmSessionSize - MmSessionImageSize - MI_SESSION_SPACE_STRUCT_SIZE);
MiSessionImagePteStart = MiGetPteAddress ((PVOID) MiSessionImageStart);
MiSessionImagePteEnd = MiGetPteAddress ((PVOID) MiSessionImageEnd);
MiSessionBasePte = MiGetPteAddress ((PVOID)MmSessionBase);
MiSessionSpaceWs = MiSessionViewStart + MmSessionViewSize;
MiSessionLastPte = MiGetPteAddress ((PVOID)MiSessionSpaceEnd);
#if DBG
//
// A few sanity checks to ensure things are as they should be.
//
if ((sizeof(CONTROL_AREA) % 8) != 0) {
DbgPrint("control area list is not a quadword sized structure\n");
}
if ((sizeof(SUBSECTION) % 8) != 0) {
DbgPrint("subsection list is not a quadword sized structure\n");
}
//
// Some checks to make sure prototype PTEs can be placed in
// either paged or nonpaged (prototype PTEs for paged pool are here)
// can be put into PTE format.
//
PointerPte = (PMMPTE)MmPagedPoolStart;
Pointer.u.Long = MiProtoAddressForPte (PointerPte);
TempPte = Pointer;
PointerPde = MiPteToProto(&TempPte);
if (PointerPte != PointerPde) {
DbgPrint("unable to map start of paged pool as prototype PTE %p %p\n",
PointerPde,
PointerPte);
}
PointerPte =
(PMMPTE)((ULONG_PTR)MM_NONPAGED_POOL_END & ~((1 << PTE_SHIFT) - 1));
Pointer.u.Long = MiProtoAddressForPte (PointerPte);
TempPte = Pointer;
PointerPde = MiPteToProto(&TempPte);
if (PointerPte != PointerPde) {
DbgPrint("unable to map end of nonpaged pool as prototype PTE %p %p\n",
PointerPde,
PointerPte);
}
PointerPte = (PMMPTE)(((ULONG_PTR)NON_PAGED_SYSTEM_END -
0x37000 + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1));
for (j = 0; j < 20; j += 1) {
Pointer.u.Long = MiProtoAddressForPte (PointerPte);
TempPte = Pointer;
PointerPde = MiPteToProto(&TempPte);
if (PointerPte != PointerPde) {
DbgPrint("unable to map end of nonpaged pool as prototype PTE %p %p\n",
PointerPde,
PointerPte);
}
PointerPte += 1;
}
PointerPte = (PMMPTE)(((ULONG_PTR)MM_NONPAGED_POOL_END - 0x133448) & ~(ULONG_PTR)7);
Pointer.u.Long = MiGetSubsectionAddressForPte (PointerPte);
TempPte = Pointer;
PointerPde = (PMMPTE)MiGetSubsectionAddress(&TempPte);
if (PointerPte != PointerPde) {
DbgPrint("unable to map end of nonpaged pool as section PTE %p %p\n",
PointerPde,
PointerPte);
MiFormatPte(&TempPte);
}
//
// End of sanity checks.
//
#endif
if (MmEnforceWriteProtection) {
MiPteStr[0] = (CHAR)1;
}
InitializeListHead (&MmLoadedUserImageList);
InitializeListHead (&MmLockConflictList);
InitializeListHead (&MmProtectedPteList);
KeInitializeSpinLock (&MmProtectedPteLock);
MmCriticalSectionTimeout.QuadPart = Int32x32To64(
MmCritsectTimeoutSeconds,
-10000000);
//
// Initialize System Address Space creation mutex.
//
ExInitializeFastMutex (&MmSectionCommitMutex);
ExInitializeFastMutex (&MmSectionBasedMutex);
ExInitializeFastMutex (&MmDynamicMemoryMutex);
KeInitializeMutant (&MmSystemLoadLock, FALSE);
KeInitializeEvent (&MmAvailablePagesEvent, NotificationEvent, TRUE);
KeInitializeEvent (&MmAvailablePagesEventMedium, NotificationEvent, TRUE);
KeInitializeEvent (&MmAvailablePagesEventHigh, NotificationEvent, TRUE);
KeInitializeEvent (&MmMappedFileIoComplete, NotificationEvent, FALSE);
KeInitializeEvent (&MmZeroingPageEvent, SynchronizationEvent, FALSE);
KeInitializeEvent (&MmCollidedFlushEvent, NotificationEvent, FALSE);
KeInitializeEvent (&MmCollidedLockEvent, NotificationEvent, FALSE);
KeInitializeEvent (&MiMappedPagesTooOldEvent, NotificationEvent, FALSE);
KeInitializeDpc (&MiModifiedPageWriterTimerDpc,
MiModifiedPageWriterTimerDispatch,
NULL);
KeInitializeTimerEx (&MiModifiedPageWriterTimer, SynchronizationTimer);
MiModifiedPageLife.QuadPart = Int32x32To64(
MmModifiedPageLifeInSeconds,
-10000000);
InitializeListHead (&MmWorkingSetExpansionHead.ListHead);
InitializeSListHead (&MmDeadStackSListHead);
InitializeSListHead (&MmEventCountSListHead);
InitializeSListHead (&MmInPageSupportSListHead);
MmZeroingPageThreadActive = FALSE;
MiMemoryLicense (LoaderBlock);
//
// include all memory types ...
//
for (i = 0; i < LoaderMaximum; i += 1) {
IncludeType[i] = TRUE;
}
//
// ... expect these..
//
IncludeType[LoaderBad] = FALSE;
IncludeType[LoaderFirmwarePermanent] = FALSE;
IncludeType[LoaderSpecialMemory] = FALSE;
IncludeType[LoaderBBTMemory] = FALSE;
//
// Compute number of pages in the system.
//
NumberOfPages = MiPagesInLoaderBlock (LoaderBlock, IncludeType);
#if defined (_MI_MORE_THAN_4GB_)
Mm64BitPhysicalAddress = TRUE;
#endif
//
// When safebooting, don't enable special pool, the verifier or any
// other options that track corruption regardless of registry settings.
//
if (strstr(LoaderBlock->LoadOptions, SAFEBOOT_LOAD_OPTION_A)) {
MmVerifyDriverBufferLength = (ULONG)-1;
MiVerifyAllDrivers = 0;
MmVerifyDriverLevel = 0;
MmDontVerifyRandomDrivers = TRUE;
MmSpecialPoolTag = (ULONG)-1;
MmSnapUnloads = FALSE;
MmProtectFreedNonPagedPool = FALSE;
MmEnforceWriteProtection = 0;
MmTrackLockedPages = FALSE;
MmTrackPtes = 0;
#if defined (_WIN64)
MmSessionSpecialPoolEnd = NULL;
MmSessionSpecialPoolStart = NULL;
#endif
SharedUserData->SafeBootMode = TRUE;
}
else {
MiTriageSystem (LoaderBlock);
}
SystemPteMultiplier = 0;
if (MmNumberOfSystemPtes == 0) {
#if defined (_WIN64)
//
// 64-bit NT is not constrained by virtual address space. No
// tradeoffs between nonpaged pool, paged pool and system PTEs
// need to be made. So just allocate PTEs on a linear scale as
// a function of the amount of RAM.
//
// For example on a Hydra NT64, 4gb of RAM gets 128gb of PTEs.
// The page table cost is the inversion of the multiplier based
// on the PTE_PER_PAGE.
//
if (ExpMultiUserTS == TRUE) {
SystemPteMultiplier = 32;
}
else {
SystemPteMultiplier = 16;
}
if (NumberOfPages < 0x8000) {
SystemPteMultiplier >>= 1;
}
#else
if (NumberOfPages < MM_MEDIUM_SYSTEM) {
MmNumberOfSystemPtes = MM_MINIMUM_SYSTEM_PTES;
}
else {
MmNumberOfSystemPtes = MM_DEFAULT_SYSTEM_PTES;
if (NumberOfPages > 8192) {
MmNumberOfSystemPtes += MmNumberOfSystemPtes;
//
// Any reasonable Hydra machine gets the maximum.
//
if (ExpMultiUserTS == TRUE) {
MmNumberOfSystemPtes = MM_MAXIMUM_SYSTEM_PTES;
}
}
}
#endif
}
else if (MmNumberOfSystemPtes == (ULONG)-1) {
//
// This registry setting indicates the maximum number of
// system PTEs possible for this machine must be allocated.
// Snap this for later reference.
//
MiRequestedSystemPtes = MmNumberOfSystemPtes;
#if defined (_WIN64)
SystemPteMultiplier = 256;
#else
MmNumberOfSystemPtes = MM_MAXIMUM_SYSTEM_PTES;
#endif
}
if (SystemPteMultiplier != 0) {
if (NumberOfPages * SystemPteMultiplier > MM_MAXIMUM_SYSTEM_PTES) {
MmNumberOfSystemPtes = MM_MAXIMUM_SYSTEM_PTES;
}
else {
MmNumberOfSystemPtes = (ULONG)(NumberOfPages * SystemPteMultiplier);
}
}
if (MmNumberOfSystemPtes > MM_MAXIMUM_SYSTEM_PTES) {
MmNumberOfSystemPtes = MM_MAXIMUM_SYSTEM_PTES;
}
if (MmNumberOfSystemPtes < MM_MINIMUM_SYSTEM_PTES) {
MmNumberOfSystemPtes = MM_MINIMUM_SYSTEM_PTES;
}
if (MmHeapSegmentReserve == 0) {
MmHeapSegmentReserve = 1024 * 1024;
}
if (MmHeapSegmentCommit == 0) {
MmHeapSegmentCommit = PAGE_SIZE * 2;
}
if (MmHeapDeCommitTotalFreeThreshold == 0) {
MmHeapDeCommitTotalFreeThreshold = 64 * 1024;
}
if (MmHeapDeCommitFreeBlockThreshold == 0) {
MmHeapDeCommitFreeBlockThreshold = PAGE_SIZE;
}
#ifndef NO_POOL_CHECKS
MiInitializeSpecialPoolCriteria ();
#endif
//
// If the registry indicates drivers are in the suspect list,
// extra system PTEs need to be allocated to support special pool
// for their allocations.
//
if ((MmVerifyDriverBufferLength != (ULONG)-1) ||
((MmSpecialPoolTag != 0) && (MmSpecialPoolTag != (ULONG)-1))) {
MmNumberOfSystemPtes += MM_SPECIAL_POOL_PTES;
}
MmNumberOfSystemPtes += BBTPagesToReserve;
#if defined(_X86_)
//
// The allocation preference key must be carefully managed. This is
// because doing every allocation top-down can caused failures if
// an ntdll process startup allocation (like the stack trace database)
// gets a high address which then causes a subsequent system DLL rebase
// collision.
//
// This is circumvented as follows:
//
// 1. For 32-bit machines, the allocation preference key is only
// useful when booted /3GB as only then can this key help track
// down apps with high virtual address bit sign extension problems.
// In 3GB mode, the system DLLs are based just below 2GB so ntdll
// would have to allocate more than 1GB of VA space before this
// becomes a problem. So really the problem can only occur for
// machines in 2GB mode and since the key doesn't help these
// machines anyway, just turn it off in these cases.
//
// 2. For 64-bit machines, there is plenty of VA space above the
// addresses system DLLs are based at so it is a non-issue.
// EXCEPT for wow64 binaries which run in sandboxed 2GB address
// spaces. Explicit checks are made to detect a wow64 process in
// the Mm APIs which check this key and the key is ignored in
// this case as it doesn't provide any sign extension help and
// therefore we don't allow it to burn up any valuable VA space
// which could cause a collision.
//
if (MmVirtualBias == 0) {
MmAllocationPreference = 0;
}
#endif
if (MmAllocationPreference != 0) {
MmAllocationPreference = MEM_TOP_DOWN;
}
ExInitializeResourceLite (&MmSystemWsLock);
MiInitializeDriverVerifierList (LoaderBlock);
//
// Set the initial commit page limit high enough so initial pool
// allocations (which happen in the machine dependent init) can
// succeed.
//
MmTotalCommitLimit = _2gb / PAGE_SIZE;
MmTotalCommitLimitMaximum = MmTotalCommitLimit;
//
// Pick a reasonable size for the default prototype PTE allocation
// chunk size. Make sure it's always a PAGE_SIZE multiple. The
// registry entry is treated as the number of 1K chunks.
//
if (MmAllocationFragment == 0) {
AutosizingFragment = TRUE;
MmAllocationFragment = MM_ALLOCATION_FRAGMENT;
#if !defined (_WIN64)
if (NumberOfPages < 64 * 1024) {
MmAllocationFragment = MM_ALLOCATION_FRAGMENT / 4;
}
else if (NumberOfPages < 256 * 1024) {
MmAllocationFragment = MM_ALLOCATION_FRAGMENT / 2;
}
#endif
}
else {
//
// Convert the registry entry from 1K chunks into bytes.
// Then round it to a PAGE_SIZE multiple. Finally bound it
// reasonably.
//
AutosizingFragment = FALSE;
MmAllocationFragment *= 1024;
MmAllocationFragment = ROUND_TO_PAGES (MmAllocationFragment);
if (MmAllocationFragment > MM_ALLOCATION_FRAGMENT) {
MmAllocationFragment = MM_ALLOCATION_FRAGMENT;
}
else if (MmAllocationFragment < PAGE_SIZE) {
MmAllocationFragment = PAGE_SIZE;
}
}
MiInitializeIoTrackers ();
MiInitializeCacheOverrides ();
//
// Initialize the machine dependent portion of the hardware.
//
MiInitMachineDependent (LoaderBlock);
MmPhysicalMemoryBlock = MmInitializeMemoryLimits (LoaderBlock,
IncludeType,
NULL);
if (MmPhysicalMemoryBlock == NULL) {
KeBugCheckEx (INSTALL_MORE_MEMORY,
MmNumberOfPhysicalPages,
MmLowestPhysicalPage,
MmHighestPhysicalPage,
0x100);
}
#if defined(_X86_)
MiReportPhysicalMemory ();
#endif
#if defined (_MI_MORE_THAN_4GB_)
if (MiNoLowMemory != 0) {
MiRemoveLowPages (0);
}
#endif
//
// Initialize listhead, spinlock and semaphore for
// segment dereferencing thread.
//
KeInitializeSpinLock (&MmDereferenceSegmentHeader.Lock);
InitializeListHead (&MmDereferenceSegmentHeader.ListHead);
KeInitializeSemaphore (&MmDereferenceSegmentHeader.Semaphore, 0, MAXLONG);
InitializeListHead (&MmUnusedSegmentList);
InitializeListHead (&MmUnusedSubsectionList);
KeInitializeEvent (&MmUnusedSegmentCleanup, NotificationEvent, FALSE);
MiInitializeCommitment ();
MiInitializePfnTracing ();
#if defined(_X86_)
//
// Virtual bias indicates the offset that needs to be added to
// 0x80000000 to get to the start of the loaded images. Update it
// now to indicate the offset to MmSessionBase as that is the lowest
// system address that process creation needs to make sure to duplicate.
//
// This is not done until after machine dependent initialization runs
// as that initialization relies on the original meaning of VirtualBias.
//
// Note if the system is booted with both /3GB & /USERVA, then system
// PTEs will be allocated below virtual 3GB and that will end up being
// the lowest system address the process creation needs to duplicate.
//
if (MmVirtualBias != 0) {
MmVirtualBias = (ULONG_PTR)MmSessionBase - CODE_START;
}
#endif
if (MmMirroring & MM_MIRRORING_ENABLED) {
#if defined (_WIN64)
//
// All page frame numbers must fit in 32 bits because the bitmap
// package is currently 32-bit.
//
// The bitmaps are deliberately not initialized as each mirroring
// must reinitialize them anyway.
//
if (MmHighestPossiblePhysicalPage + 1 < _4gb) {
#endif
MiCreateBitMap (&MiMirrorBitMap,
MmHighestPossiblePhysicalPage + 1,
NonPagedPool);
if (MiMirrorBitMap != NULL) {
MiCreateBitMap (&MiMirrorBitMap2,
MmHighestPossiblePhysicalPage + 1,
NonPagedPool);
if (MiMirrorBitMap2 == NULL) {
MiRemoveBitMap (&MiMirrorBitMap);
}
}
#if defined (_WIN64)
}
#endif
}
#if !defined (_WIN64)
if ((AutosizingFragment == TRUE) &&
(NumberOfPages >= 256 * 1024)) {
//
// This is a system with at least 1GB of RAM. Presumably it
// will be used to cache many files. Maybe we should factor in
// pool size here and adjust it accordingly.
//
MmAllocationFragment;
}
#endif
MiReloadBootLoadedDrivers (LoaderBlock);
#if defined (_MI_MORE_THAN_4GB_)
if (MiNoLowMemory != 0) {
MiRemoveLowPages (1);
}
#endif
MiInitializeVerifyingComponents (LoaderBlock);
//
// Setup the system size as small, medium, or large depending
// on memory available.
//
// For internal MM tuning, the following applies
//
// 12Mb is small
// 12-19 is medium
// > 19 is large
//
//
// For all other external tuning,
// < 19 is small
// 19 - 31 is medium for workstation
// 19 - 63 is medium for server
// >= 32 is large for workstation
// >= 64 is large for server
//
if (MmNumberOfPhysicalPages <= MM_SMALL_SYSTEM) {
MmSystemSize = MmSmallSystem;
MmMaximumDeadKernelStacks = 0;
MmModifiedPageMinimum = 40;
MmModifiedPageMaximum = 100;
MmDataClusterSize = 0;
MmCodeClusterSize = 1;
MmReadClusterSize = 2;
MmInPageSupportMinimum = 2;
}
else if (MmNumberOfPhysicalPages <= MM_MEDIUM_SYSTEM) {
MmSystemSize = MmSmallSystem;
MmMaximumDeadKernelStacks = 2;
MmModifiedPageMinimum = 80;
MmModifiedPageMaximum = 150;
MmSystemCacheWsMinimum += 100;
MmSystemCacheWsMaximum += 150;
MmDataClusterSize = 1;
MmCodeClusterSize = 2;
MmReadClusterSize = 4;
MmInPageSupportMinimum = 3;
}
else {
MmSystemSize = MmMediumSystem;
MmMaximumDeadKernelStacks = 5;
MmModifiedPageMinimum = 150;
MmModifiedPageMaximum = 300;
MmSystemCacheWsMinimum += 400;
MmSystemCacheWsMaximum += 800;
MmDataClusterSize = 3;
MmCodeClusterSize = 7;
MmReadClusterSize = 7;
MmInPageSupportMinimum = 4;
}
if (MmNumberOfPhysicalPages < ((24*1024*1024)/PAGE_SIZE)) {
MmSystemCacheWsMinimum = 32;
}
if (MmNumberOfPhysicalPages >= ((32*1024*1024)/PAGE_SIZE)) {
//
// If we are on a workstation, 32Mb and above are considered
// large systems.
//
if (MmProductType == 0x00690057) {
MmSystemSize = MmLargeSystem;
}
else {
//
// For servers, 64Mb and greater is a large system
//
if (MmNumberOfPhysicalPages >= ((64*1024*1024)/PAGE_SIZE)) {
MmSystemSize = MmLargeSystem;
}
}
}
if (MmNumberOfPhysicalPages > ((33*1024*1024)/PAGE_SIZE)) {
MmModifiedPageMinimum = 400;
MmModifiedPageMaximum = 800;
MmSystemCacheWsMinimum += 500;
MmSystemCacheWsMaximum += 900;
MmInPageSupportMinimum += 4;
}
if (MmNumberOfPhysicalPages > ((220*1024*1024)/PAGE_SIZE)) { // bump max cache size a bit more
if ( (LONG)MmSystemCacheWsMinimum < (LONG)((24*1024*1024) >> PAGE_SHIFT) &&
(LONG)MmSystemCacheWsMaximum < (LONG)((24*1024*1024) >> PAGE_SHIFT)){
MmSystemCacheWsMaximum = ((24*1024*1024) >> PAGE_SHIFT);
}
ASSERT ((LONG)MmSystemCacheWsMaximum > (LONG)MmSystemCacheWsMinimum);
}
else if (MmNumberOfPhysicalPages > ((110*1024*1024)/PAGE_SIZE)) { // bump max cache size a bit
if ( (LONG)MmSystemCacheWsMinimum < (LONG)((16*1024*1024) >> PAGE_SHIFT) &&
(LONG)MmSystemCacheWsMaximum < (LONG)((16*1024*1024) >> PAGE_SHIFT)){
MmSystemCacheWsMaximum = ((16*1024*1024) >> PAGE_SHIFT);
}
ASSERT ((LONG)MmSystemCacheWsMaximum > (LONG)MmSystemCacheWsMinimum);
}
if (NT_SUCCESS (MmIsVerifierEnabled (&VerifierFlags))) {
//
// The verifier is enabled so don't defer any MDL unlocks because
// without state, debugging driver bugs in this area is very
// difficult.
//
DeferredMdlEntries = 0;
}
else if (MmNumberOfPhysicalPages > ((255*1024*1024)/PAGE_SIZE)) {
DeferredMdlEntries = 32;
}
else if (MmNumberOfPhysicalPages > ((127*1024*1024)/PAGE_SIZE)) {
DeferredMdlEntries = 8;
}
else {
DeferredMdlEntries = 4;
}
#if defined(MI_MULTINODE)
for (i = 0; i < KeNumberNodes; i += 1) {
InitializeSListHead (&KeNodeBlock[i]->PfnDereferenceSListHead);
KeNodeBlock[i]->PfnDeferredList = NULL;
for (j = 0; j < DeferredMdlEntries; j += 1) {
SingleListEntry = ExAllocatePoolWithTag (NonPagedPool,
sizeof(MI_PFN_DEREFERENCE_CHUNK),
'mDmM');
if (SingleListEntry != NULL) {
InterlockedPushEntrySList (&KeNodeBlock[i]->PfnDereferenceSListHead,
SingleListEntry);
}
}
}
#else
InitializeSListHead (&MmPfnDereferenceSListHead);
for (j = 0; j < DeferredMdlEntries; j += 1) {
SingleListEntry = ExAllocatePoolWithTag (NonPagedPool,
sizeof(MI_PFN_DEREFERENCE_CHUNK),
'mDmM');
if (SingleListEntry != NULL) {
InterlockedPushEntrySList (&MmPfnDereferenceSListHead,
SingleListEntry);
}
}
#endif
ASSERT (SharedUserData->NumberOfPhysicalPages == 0);
SharedUserData->NumberOfPhysicalPages = (ULONG) MmNumberOfPhysicalPages;
//
// Determine if we are on an AS system (Winnt is not AS).
//
if (MmProductType == 0x00690057) {
SharedUserData->NtProductType = NtProductWinNt;
MmProductType = 0;
MmThrottleTop = 250;
MmThrottleBottom = 30;
}
else {
if (MmProductType == 0x0061004c) {
SharedUserData->NtProductType = NtProductLanManNt;
}
else {
SharedUserData->NtProductType = NtProductServer;
}
MmProductType = 1;
MmThrottleTop = 450;
MmThrottleBottom = 80;
MmMinimumFreePages = 81;
MmInPageSupportMinimum += 8;
}
MiAdjustWorkingSetManagerParameters ((LOGICAL)(MmProductType == 0 ? TRUE : FALSE));
//
// Set the ResidentAvailablePages to the number of available
// pages minus the fluid value.
//
MmResidentAvailablePages = MmAvailablePages - MM_FLUID_PHYSICAL_PAGES;
//
// Subtract off the size of future nonpaged pool expansion
// so that nonpaged pool will always be able to expand regardless of
// prior system load activity.
//
MmResidentAvailablePages -= MiExpansionPoolPagesInitialCharge;
//
// Subtract off the size of the system cache working set.
//
MmResidentAvailablePages -= MmSystemCacheWsMinimum;
MmResidentAvailableAtInit = MmResidentAvailablePages;
if (MmResidentAvailablePages < 0) {
#if DBG
DbgPrint("system cache working set too big\n");
#endif
return FALSE;
}
//
// Initialize spin lock for allowing working set expansion.
//
KeInitializeSpinLock (&MmExpansionLock);
ExInitializeFastMutex (&MmPageFileCreationLock);
//
// Initialize resource for extending sections.
//
ExInitializeResourceLite (&MmSectionExtendResource);
ExInitializeResourceLite (&MmSectionExtendSetResource);
//
// Build the system cache structures.
//
StartPde = MiGetPdeAddress (MmSystemCacheWorkingSetList);
PointerPte = MiGetPteAddress (MmSystemCacheWorkingSetList);
#if (_MI_PAGING_LEVELS >= 3)
TempPte = ValidKernelPte;
#if (_MI_PAGING_LEVELS >= 4)
StartPxe = MiGetPdeAddress(StartPde);
if (StartPxe->u.Hard.Valid == 0) {
//
// Map in a page directory parent page for the system cache working
// set. Note that we only populate one page table for this.
//
DirectoryFrameIndex = MiRemoveAnyPage(
MI_GET_PAGE_COLOR_FROM_PTE (StartPxe));
TempPte.u.Hard.PageFrameNumber = DirectoryFrameIndex;
*StartPxe = TempPte;
MiInitializePfn (DirectoryFrameIndex, StartPxe, 1);
MiFillMemoryPte (MiGetVirtualAddressMappedByPte(StartPxe),
PAGE_SIZE,
ZeroKernelPte.u.Long);
}
#endif
StartPpe = MiGetPteAddress(StartPde);
if (StartPpe->u.Hard.Valid == 0) {
//
// Map in a page directory page for the system cache working set.
// Note that we only populate one page table for this.
//
DirectoryFrameIndex = MiRemoveAnyPage(
MI_GET_PAGE_COLOR_FROM_PTE (StartPpe));
TempPte.u.Hard.PageFrameNumber = DirectoryFrameIndex;
*StartPpe = TempPte;
MiInitializePfn (DirectoryFrameIndex, StartPpe, 1);
MiFillMemoryPte (MiGetVirtualAddressMappedByPte(StartPpe),
PAGE_SIZE,
ZeroKernelPte.u.Long);
}
#if (_MI_PAGING_LEVELS >= 4)
//
// The shared user data is already initialized and it shares the
// page table page with the system cache working set list.
//
ASSERT (StartPde->u.Hard.Valid == 1);
#else
//
// Map in a page table page.
//
ASSERT (StartPde->u.Hard.Valid == 0);
PageFrameIndex = MiRemoveAnyPage(
MI_GET_PAGE_COLOR_FROM_PTE (StartPde));
TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
MI_WRITE_VALID_PTE (StartPde, TempPte);
MiInitializePfn (PageFrameIndex, StartPde, 1);
MiFillMemoryPte (MiGetVirtualAddressMappedByPte (StartPde),
PAGE_SIZE,
ZeroKernelPte.u.Long);
#endif
StartPpe = MiGetPpeAddress(MmSystemCacheStart);
StartPde = MiGetPdeAddress(MmSystemCacheStart);
PointerPte = MiGetVirtualAddressMappedByPte (StartPde);
#else
#if !defined(_X86PAE_)
ASSERT ((StartPde + 1) == MiGetPdeAddress (MmSystemCacheStart));
#endif
#endif
MaximumSystemCacheSizeTotal = MaximumSystemCacheSize;
#if defined(_X86_)
MaximumSystemCacheSizeTotal += MiMaximumSystemCacheSizeExtra;
#endif
//
// Size the system cache based on the amount of physical memory.
//
i = (MmNumberOfPhysicalPages + 65) / 1024;
if (i >= 4) {
//
// System has at least 4032 pages. Make the system
// cache 128mb + 64mb for each additional 1024 pages.
//
MmSizeOfSystemCacheInPages = (PFN_COUNT)(
((128*1024*1024) >> PAGE_SHIFT) +
((i - 4) * ((64*1024*1024) >> PAGE_SHIFT)));
if (MmSizeOfSystemCacheInPages > MaximumSystemCacheSizeTotal) {
MmSizeOfSystemCacheInPages = MaximumSystemCacheSizeTotal;
}
}
MmSystemCacheEnd = (PVOID)(((PCHAR)MmSystemCacheStart +
MmSizeOfSystemCacheInPages * PAGE_SIZE) - 1);
#if defined(_X86_)
if (MmSizeOfSystemCacheInPages > MaximumSystemCacheSize) {
ASSERT (MiMaximumSystemCacheSizeExtra != 0);
MmSystemCacheEnd = (PVOID)(((PCHAR)MmSystemCacheStart +
MaximumSystemCacheSize * PAGE_SIZE) - 1);
MiSystemCacheStartExtra = (PVOID)MiExtraResourceStart;
MiSystemCacheEndExtra = (PVOID)(((PCHAR)MiSystemCacheStartExtra +
(MmSizeOfSystemCacheInPages - MaximumSystemCacheSize) * PAGE_SIZE) - 1);
}
else {
MiSystemCacheStartExtra = MmSystemCacheStart;
MiSystemCacheEndExtra = MmSystemCacheEnd;
}
#endif
EndPde = MiGetPdeAddress(MmSystemCacheEnd);
TempPte = ValidKernelPte;
#if (_MI_PAGING_LEVELS >= 4)
StartPxe = MiGetPxeAddress(MmSystemCacheStart);
if (StartPxe->u.Hard.Valid == 0) {
FirstPxe = TRUE;
FirstPpe = TRUE;
}
else {
FirstPxe = FALSE;
FirstPpe = (StartPpe->u.Hard.Valid == 0) ? TRUE : FALSE;
}
#elif (_MI_PAGING_LEVELS >= 3)
FirstPpe = (StartPpe->u.Hard.Valid == 0) ? TRUE : FALSE;
#else
DirectoryFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (MiGetPteAddress(PDE_BASE));
#endif
LOCK_PFN (OldIrql);
while (StartPde <= EndPde) {
#if (_MI_PAGING_LEVELS >= 4)
if (FirstPxe == TRUE || MiIsPteOnPpeBoundary(StartPde)) {
FirstPxe = FALSE;
StartPxe = MiGetPdeAddress(StartPde);
//
// Map in a page directory page.
//
DirectoryFrameIndex = MiRemoveAnyPage(
MI_GET_PAGE_COLOR_FROM_PTE (StartPxe));
TempPte.u.Hard.PageFrameNumber = DirectoryFrameIndex;
MI_WRITE_VALID_PTE (StartPxe, TempPte);
MiInitializePfn (DirectoryFrameIndex,
StartPxe,
1);
MiFillMemoryPte (MiGetVirtualAddressMappedByPte(StartPxe),
PAGE_SIZE,
ZeroKernelPte.u.Long);
}
#endif
#if (_MI_PAGING_LEVELS >= 3)
if (FirstPpe == TRUE || MiIsPteOnPdeBoundary(StartPde)) {
FirstPpe = FALSE;
StartPpe = MiGetPteAddress(StartPde);
//
// Map in a page directory page.
//
DirectoryFrameIndex = MiRemoveAnyPage(
MI_GET_PAGE_COLOR_FROM_PTE (StartPpe));
TempPte.u.Hard.PageFrameNumber = DirectoryFrameIndex;
MI_WRITE_VALID_PTE (StartPpe, TempPte);
MiInitializePfn (DirectoryFrameIndex,
StartPpe,
1);
MiFillMemoryPte (MiGetVirtualAddressMappedByPte(StartPpe),
PAGE_SIZE,
ZeroKernelPte.u.Long);
}
#endif
ASSERT (StartPde->u.Hard.Valid == 0);
//
// Map in a page table page.
//
PageFrameIndex = MiRemoveAnyPage(
MI_GET_PAGE_COLOR_FROM_PTE (StartPde));
TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
MI_WRITE_VALID_PTE (StartPde, TempPte);
MiInitializePfn (PageFrameIndex, StartPde, 1);
MiFillMemoryPte (MiGetVirtualAddressMappedByPte(StartPde),
PAGE_SIZE,
ZeroKernelPte.u.Long);
StartPde += 1;
}
UNLOCK_PFN (OldIrql);
//
// Initialize the system cache. Only set the large system cache if
// we have a large amount of physical memory.
//
if (MmLargeSystemCache != 0 && MmNumberOfPhysicalPages > 0x7FF0) {
if ((MmAvailablePages >
MmSystemCacheWsMaximum + ((64*1024*1024) >> PAGE_SHIFT))) {
MmSystemCacheWsMaximum =
MmAvailablePages - ((32*1024*1024) >> PAGE_SHIFT);
ASSERT ((LONG)MmSystemCacheWsMaximum > (LONG)MmSystemCacheWsMinimum);
}
}
if (MmSystemCacheWsMaximum > (MM_MAXIMUM_WORKING_SET - 5)) {
MmSystemCacheWsMaximum = MM_MAXIMUM_WORKING_SET - 5;
}
if (MmSystemCacheWsMaximum > MmSizeOfSystemCacheInPages) {
MmSystemCacheWsMaximum = MmSizeOfSystemCacheInPages;
if ((MmSystemCacheWsMinimum + 500) > MmSystemCacheWsMaximum) {
MmSystemCacheWsMinimum = MmSystemCacheWsMaximum - 500;
}
}
MiInitializeSystemCache ((ULONG)MmSystemCacheWsMinimum,
(ULONG)MmSystemCacheWsMaximum);
MmAttemptForCantExtend.Segment = NULL;
MmAttemptForCantExtend.RequestedExpansionSize = 1;
MmAttemptForCantExtend.ActualExpansion = 0;
MmAttemptForCantExtend.InProgress = FALSE;
MmAttemptForCantExtend.PageFileNumber = MI_EXTEND_ANY_PAGEFILE;
KeInitializeEvent (&MmAttemptForCantExtend.Event,
NotificationEvent,
FALSE);
//
// Now that we have booted far enough, replace the temporary
// commit limits with real ones: set the initial commit page
// limit to the number of available pages. This value is
// updated as paging files are created.
//
MmTotalCommitLimit = MmAvailablePages;
if (MmTotalCommitLimit > 1024) {
MmTotalCommitLimit -= 1024;
}
MmTotalCommitLimitMaximum = MmTotalCommitLimit;
//
// Set maximum working set size to 512 pages less than the
// total available memory.
//
MmMaximumWorkingSetSize = (WSLE_NUMBER)(MmAvailablePages - 512);
if (MmMaximumWorkingSetSize > (MM_MAXIMUM_WORKING_SET - 5)) {
MmMaximumWorkingSetSize = MM_MAXIMUM_WORKING_SET - 5;
}
//
// Create the modified page writer event.
//
KeInitializeEvent (&MmModifiedPageWriterEvent, NotificationEvent, FALSE);
//
// Build paged pool.
//
MiBuildPagedPool ();
//
// Initialize the loaded module list. This cannot be done until
// paged pool has been built.
//
if (MiInitializeLoadedModuleList (LoaderBlock) == FALSE) {
#if DBG
DbgPrint("Loaded module list initialization failed\n");
#endif
return FALSE;
}
//
// Initialize the unused segment threshold. Attempt to keep pool usage
// below this percentage (by trimming the cache) if pool requests
// can fail.
//
if (MmConsumedPoolPercentage == 0) {
MmConsumedPoolPercentage = 80;
}
else if (MmConsumedPoolPercentage < 5) {
MmConsumedPoolPercentage = 5;
}
else if (MmConsumedPoolPercentage > 100) {
MmConsumedPoolPercentage = 100;
}
//
// Add more system PTEs if this is a large memory system.
// Note that 64 bit systems can determine the right value at the
// beginning since there is no virtual address space crunch.
//
#if !defined (_WIN64)
if (MmNumberOfPhysicalPages > ((127*1024*1024) >> PAGE_SHIFT)) {
PMMPTE StartingPte;
PointerPde = MiGetPdeAddress ((PCHAR)MmPagedPoolEnd + 1);
StartingPte = MiGetPteAddress ((PCHAR)MmPagedPoolEnd + 1);
j = 0;
TempPte = ValidKernelPde;
LOCK_PFN (OldIrql);
while (PointerPde->u.Hard.Valid == 0) {
MiChargeCommitmentCantExpand (1, TRUE);
MM_TRACK_COMMIT (MM_DBG_COMMIT_EXTRA_SYSTEM_PTES, 1);
PageFrameIndex = MiRemoveZeroPage (
MI_GET_PAGE_COLOR_FROM_PTE (PointerPde));
TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
MI_WRITE_VALID_PTE (PointerPde, TempPte);
MiInitializePfn (PageFrameIndex, PointerPde, 1);
PointerPde += 1;
StartingPte += PAGE_SIZE / sizeof(MMPTE);
j += PAGE_SIZE / sizeof(MMPTE);
}
UNLOCK_PFN (OldIrql);
if (j != 0) {
StartingPte = MiGetPteAddress ((PCHAR)MmPagedPoolEnd + 1);
MmNonPagedSystemStart = MiGetVirtualAddressMappedByPte (StartingPte);
MmNumberOfSystemPtes += j;
MiAddSystemPtes (StartingPte, j, SystemPteSpace);
MiIncrementSystemPtes (j);
}
}
#endif
#if defined (_MI_DEBUG_SUB)
if (MiTrackSubs != 0) {
MiSubsectionTraces = ExAllocatePoolWithTag (NonPagedPool,
MiTrackSubs * sizeof (MI_SUB_TRACES),
'tCmM');
}
#endif
#if defined (_MI_DEBUG_DIRTY)
if (MiTrackDirtys != 0) {
MiDirtyTraces = ExAllocatePoolWithTag (NonPagedPool,
MiTrackDirtys * sizeof (MI_DIRTY_TRACES),
'tCmM');
}
#endif
#if defined (_MI_DEBUG_DATA)
if (MiTrackData != 0) {
MiDataTraces = ExAllocatePoolWithTag (NonPagedPool,
MiTrackData * sizeof (MI_DATA_TRACES),
'tCmM');
}
#endif
#if DBG
if (MmDebug & MM_DBG_DUMP_BOOT_PTES) {
MiDumpValidAddresses ();
MiDumpPfn ();
}
#endif
MmPageFaultNotifyRoutine = NULL;
#ifdef _MI_MESSAGE_SERVER
MiInitializeMessageQueue ();
#endif
return TRUE;
}
if (Phase == 1) {
#if DBG
MmDebug |= MM_DBG_CHECK_PFN_LOCK;
#endif
#if defined(_X86_) || defined(_AMD64_)
MiInitMachineDependent (LoaderBlock);
#endif
MiMapBBTMemory(LoaderBlock);
if (!MiSectionInitialization ()) {
return FALSE;
}
Process = PsGetCurrentProcess ();
if (Process->PhysicalVadList.Flink == NULL) {
InitializeListHead (&Process->PhysicalVadList);
}
#if defined(MM_SHARED_USER_DATA_VA)
//
// Create double mapped page between kernel and user mode.
// The PTE is deliberately allocated from paged pool so that
// it will always have a PTE itself instead of being superpaged.
// This way, checks throughout the fault handler can assume that
// the PTE can be checked without having to special case this.
//
MmSharedUserDataPte = ExAllocatePoolWithTag (PagedPool,
sizeof(MMPTE),
' mM');
if (MmSharedUserDataPte == NULL) {
return FALSE;
}
PointerPte = MiGetPteAddress ((PVOID)KI_USER_SHARED_DATA);
ASSERT (PointerPte->u.Hard.Valid == 1);
PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
MI_MAKE_VALID_PTE (TempPte,
PageFrameIndex,
MM_READONLY,
PointerPte);
*MmSharedUserDataPte = TempPte;
Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
LOCK_PFN (OldIrql);
Pfn1->OriginalPte.u.Long = MM_DEMAND_ZERO_WRITE_PTE;
UNLOCK_PFN (OldIrql);
#ifdef _X86_
if (MmHighestUserAddress < (PVOID) MM_SHARED_USER_DATA_VA) {
//
// Install the PTE mapping now as faults will not because the
// shared user data is in the system portion of the address space.
// Note the pagetable page has already been allocated and locked
// down.
//
//
// Make the mapping user accessible.
//
ASSERT (MmSharedUserDataPte->u.Hard.Owner == 0);
MmSharedUserDataPte->u.Hard.Owner = 1;
PointerPde = MiGetPdeAddress (MM_SHARED_USER_DATA_VA);
ASSERT (PointerPde->u.Hard.Owner == 0);
PointerPde->u.Hard.Owner = 1;
ASSERT (MiUseMaximumSystemSpace != 0);
PointerPte = MiGetPteAddress (MM_SHARED_USER_DATA_VA);
ASSERT (PointerPte->u.Hard.Valid == 0);
MI_WRITE_VALID_PTE (PointerPte, *MmSharedUserDataPte);
}
#endif
#endif
MiSessionWideInitializeAddresses ();
MiInitializeSessionWsSupport ();
MiInitializeSessionIds ();
//
// Start the modified page writer.
//
InitializeObjectAttributes (&ObjectAttributes, NULL, 0, NULL, NULL);
if (!NT_SUCCESS(PsCreateSystemThread(
&ThreadHandle,
THREAD_ALL_ACCESS,
&ObjectAttributes,
0L,
NULL,
MiModifiedPageWriter,
NULL
))) {
return FALSE;
}
ZwClose (ThreadHandle);
//
// Initialize the low and high memory events. This must be done
// before starting the working set manager.
//
if (MiInitializeMemoryEvents () == FALSE) {
return FALSE;
}
//
// Start the balance set manager.
//
// The balance set manager performs stack swapping and working
// set management and requires two threads.
//
KeInitializeEvent (&MmWorkingSetManagerEvent,
SynchronizationEvent,
FALSE);
InitializeObjectAttributes (&ObjectAttributes, NULL, 0, NULL, NULL);
if (!NT_SUCCESS(PsCreateSystemThread(
&ThreadHandle,
THREAD_ALL_ACCESS,
&ObjectAttributes,
0L,
NULL,
KeBalanceSetManager,
NULL
))) {
return FALSE;
}
ZwClose (ThreadHandle);
if (!NT_SUCCESS(PsCreateSystemThread(
&ThreadHandle,
THREAD_ALL_ACCESS,
&ObjectAttributes,
0L,
NULL,
KeSwapProcessOrStack,
NULL
))) {
return FALSE;
}
ZwClose (ThreadHandle);
#ifndef NO_POOL_CHECKS
MiInitializeSpecialPoolCriteria ();
#endif
#if defined(_X86_)
MiEnableKernelVerifier ();
#endif
ExAcquireResourceExclusiveLite (&PsLoadedModuleResource, TRUE);
NextEntry = PsLoadedModuleList.Flink;
for ( ; NextEntry != &PsLoadedModuleList; NextEntry = NextEntry->Flink) {
DataTableEntry = CONTAINING_RECORD(NextEntry,
KLDR_DATA_TABLE_ENTRY,
InLoadOrderLinks);
NtHeaders = RtlImageNtHeader(DataTableEntry->DllBase);
if ((NtHeaders->OptionalHeader.MajorOperatingSystemVersion >= 5) &&
(NtHeaders->OptionalHeader.MajorImageVersion >= 5)) {
DataTableEntry->Flags |= LDRP_ENTRY_NATIVE;
}
MiWriteProtectSystemImage (DataTableEntry->DllBase);
}
ExReleaseResourceLite (&PsLoadedModuleResource);
InterlockedDecrement (&MiTrimInProgressCount);
return TRUE;
}
if (Phase == 2) {
MiEnablePagingTheExecutive();
return TRUE;
}
return FALSE;
}
VOID
MiMapBBTMemory (
IN PLOADER_PARAMETER_BLOCK LoaderBlock
)
/*++
Routine Description:
This function walks through the loader block's memory descriptor list
and maps memory reserved for the BBT buffer into the system.
The mapped PTEs are PDE-aligned and made user accessible.
Arguments:
LoaderBlock - Supplies a pointer to the system loader block.
Return Value:
None.
Environment:
Kernel Mode Only. System initialization.
--*/
{
PVOID Va;
PMEMORY_ALLOCATION_DESCRIPTOR MemoryDescriptor;
PLIST_ENTRY NextMd;
PFN_NUMBER NumberOfPagesMapped;
PFN_NUMBER NumberOfPages;
PFN_NUMBER PageFrameIndex;
PMMPTE PointerPte;
PMMPTE PointerPde;
PMMPTE LastPde;
MMPTE TempPte;
if (BBTPagesToReserve <= 0) {
return;
}
//
// Request enough PTEs such that protection can be applied to the PDEs.
//
NumberOfPages = (BBTPagesToReserve + (PTE_PER_PAGE - 1)) & ~(PTE_PER_PAGE - 1);
PointerPte = MiReserveAlignedSystemPtes ((ULONG)NumberOfPages,
SystemPteSpace,
MM_VA_MAPPED_BY_PDE);
if (PointerPte == NULL) {
BBTPagesToReserve = 0;
return;
}
//
// Allow user access to the buffer.
//
PointerPde = MiGetPteAddress (PointerPte);
LastPde = MiGetPteAddress (PointerPte + NumberOfPages);
ASSERT (LastPde != PointerPde);
do {
TempPte = *PointerPde;
TempPte.u.Long |= MM_PTE_OWNER_MASK;
MI_WRITE_VALID_PTE (PointerPde, TempPte);
PointerPde += 1;
} while (PointerPde < LastPde);
KeFlushEntireTb (TRUE, TRUE);
Va = MiGetVirtualAddressMappedByPte (PointerPte);
TempPte = ValidUserPte;
NumberOfPagesMapped = 0;
NextMd = LoaderBlock->MemoryDescriptorListHead.Flink;
while (NextMd != &LoaderBlock->MemoryDescriptorListHead) {
MemoryDescriptor = CONTAINING_RECORD(NextMd,
MEMORY_ALLOCATION_DESCRIPTOR,
ListEntry);
if (MemoryDescriptor->MemoryType == LoaderBBTMemory) {
PageFrameIndex = MemoryDescriptor->BasePage;
NumberOfPages = MemoryDescriptor->PageCount;
if (NumberOfPagesMapped + NumberOfPages > BBTPagesToReserve) {
NumberOfPages = BBTPagesToReserve - NumberOfPagesMapped;
}
NumberOfPagesMapped += NumberOfPages;
do {
TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
MI_WRITE_VALID_PTE (PointerPte, TempPte);
PointerPte += 1;
PageFrameIndex += 1;
NumberOfPages -= 1;
} while (NumberOfPages);
if (NumberOfPagesMapped == BBTPagesToReserve) {
break;
}
}
NextMd = MemoryDescriptor->ListEntry.Flink;
}
RtlZeroMemory(Va, BBTPagesToReserve << PAGE_SHIFT);
//
// Tell BBT_Init how many pages were allocated.
//
if (NumberOfPagesMapped < BBTPagesToReserve) {
BBTPagesToReserve = (ULONG)NumberOfPagesMapped;
}
*(PULONG)Va = BBTPagesToReserve;
//
// At this point instrumentation code will detect the existence of
// buffer and initialize the structures.
//
BBTBuffer = Va;
PERFINFO_MMINIT_START();
}
PPHYSICAL_MEMORY_DESCRIPTOR
MmInitializeMemoryLimits (
IN PLOADER_PARAMETER_BLOCK LoaderBlock,
IN PBOOLEAN IncludeType,
IN OUT PPHYSICAL_MEMORY_DESCRIPTOR InputMemory OPTIONAL
)
/*++
Routine Description:
This function walks through the loader block's memory
descriptor list and builds a list of contiguous physical
memory blocks of the desired types.
Arguments:
LoaderBlock - Supplies a pointer the system loader block.
IncludeType - Array of BOOLEANS of size LoaderMaximum.
TRUE means include this type of memory in return.
Memory - If non-NULL, supplies the physical memory blocks to place the
search results in. If NULL, pool is allocated to hold the
returned search results in - the caller must free this pool.
Return Value:
A pointer to the physical memory blocks for the requested search or NULL
on failure.
Environment:
Kernel Mode Only. System initialization.
--*/
{
PLIST_ENTRY NextMd;
ULONG i;
ULONG InitialAllocation;
PFN_NUMBER NextPage;
PFN_NUMBER TotalPages;
PPHYSICAL_MEMORY_DESCRIPTOR Memory;
PPHYSICAL_MEMORY_DESCRIPTOR Memory2;
PMEMORY_ALLOCATION_DESCRIPTOR MemoryDescriptor;
InitialAllocation = 0;
if (ARGUMENT_PRESENT (InputMemory)) {
Memory = InputMemory;
}
else {
//
// The caller wants us to allocate the return result buffer. Size it
// by allocating the maximum possibly needed as this should not be
// very big (relatively). It is the caller's responsibility to free
// this. Obviously this option can only be requested after pool has
// been initialized.
//
NextMd = LoaderBlock->MemoryDescriptorListHead.Flink;
while (NextMd != &LoaderBlock->MemoryDescriptorListHead) {
InitialAllocation += 1;
MemoryDescriptor = CONTAINING_RECORD(NextMd,
MEMORY_ALLOCATION_DESCRIPTOR,
ListEntry);
NextMd = MemoryDescriptor->ListEntry.Flink;
}
Memory = ExAllocatePoolWithTag (NonPagedPool,
sizeof(PHYSICAL_MEMORY_DESCRIPTOR) + sizeof(PHYSICAL_MEMORY_RUN) * (InitialAllocation - 1),
'lMmM');
if (Memory == NULL) {
return NULL;
}
Memory->NumberOfRuns = InitialAllocation;
}
//
// Walk through the memory descriptors and build the physical memory list.
//
i = 0;
TotalPages = 0;
NextPage = (PFN_NUMBER) -1;
NextMd = LoaderBlock->MemoryDescriptorListHead.Flink;
while (NextMd != &LoaderBlock->MemoryDescriptorListHead) {
MemoryDescriptor = CONTAINING_RECORD(NextMd,
MEMORY_ALLOCATION_DESCRIPTOR,
ListEntry);
if (MemoryDescriptor->MemoryType < LoaderMaximum &&
IncludeType [MemoryDescriptor->MemoryType]) {
TotalPages += MemoryDescriptor->PageCount;
//
// Merge runs whenever possible.
//
if (MemoryDescriptor->BasePage == NextPage) {
ASSERT (MemoryDescriptor->PageCount != 0);
Memory->Run[i - 1].PageCount += MemoryDescriptor->PageCount;
NextPage += MemoryDescriptor->PageCount;
}
else {
Memory->Run[i].BasePage = MemoryDescriptor->BasePage;
Memory->Run[i].PageCount = MemoryDescriptor->PageCount;
NextPage = Memory->Run[i].BasePage + Memory->Run[i].PageCount;
i += 1;
}
}
NextMd = MemoryDescriptor->ListEntry.Flink;
}
ASSERT (i <= Memory->NumberOfRuns);
if (i == 0) {
//
// Don't bother shrinking this as the caller will be freeing it
// shortly as it is just an empty list.
//
Memory->Run[i].BasePage = 0;
Memory->Run[i].PageCount = 0;
}
else if (!ARGUMENT_PRESENT (InputMemory)) {
//
// Shrink the buffer (if possible) now that the final size is known.
//
if (InitialAllocation > i) {
Memory2 = ExAllocatePoolWithTag (NonPagedPool,
sizeof(PHYSICAL_MEMORY_DESCRIPTOR) + sizeof(PHYSICAL_MEMORY_RUN) * (i - 1),
'lMmM');
if (Memory2 != NULL) {
RtlCopyMemory (Memory2->Run,
Memory->Run,
sizeof(PHYSICAL_MEMORY_RUN) * i);
ExFreePool (Memory);
Memory = Memory2;
}
}
}
Memory->NumberOfRuns = i;
Memory->NumberOfPages = TotalPages;
return Memory;
}
PFN_NUMBER
MiPagesInLoaderBlock (
IN PLOADER_PARAMETER_BLOCK LoaderBlock,
IN PBOOLEAN IncludeType
)
/*++
Routine Description:
This function walks through the loader block's memory
descriptor list and returns the number of pages of the desired type.
Arguments:
LoaderBlock - Supplies a pointer the system loader block.
IncludeType - Array of BOOLEANS of size LoaderMaximum.
TRUE means include this type of memory in the returned count.
Return Value:
The number of pages of the requested type in the loader block list.
Environment:
Kernel Mode Only. System initialization.
--*/
{
PMEMORY_ALLOCATION_DESCRIPTOR MemoryDescriptor;
PLIST_ENTRY NextMd;
PFN_NUMBER TotalPages;
//
// Walk through the memory descriptors counting pages.
//
TotalPages = 0;
NextMd = LoaderBlock->MemoryDescriptorListHead.Flink;
while (NextMd != &LoaderBlock->MemoryDescriptorListHead) {
MemoryDescriptor = CONTAINING_RECORD(NextMd,
MEMORY_ALLOCATION_DESCRIPTOR,
ListEntry);
if (MemoryDescriptor->MemoryType < LoaderMaximum &&
IncludeType [MemoryDescriptor->MemoryType]) {
TotalPages += MemoryDescriptor->PageCount;
}
NextMd = MemoryDescriptor->ListEntry.Flink;
}
return TotalPages;
}
static
VOID
MiMemoryLicense (
IN PLOADER_PARAMETER_BLOCK LoaderBlock
)
/*++
Routine Description:
This function walks through the loader block's memory descriptor list
and based on the system's license, ensures only the proper amount of
physical memory is used.
Arguments:
LoaderBlock - Supplies a pointer to the system loader block.
Return Value:
None.
Environment:
Kernel Mode Only. System initialization.
--*/
{
PLIST_ENTRY NextMd;
PFN_NUMBER TotalPagesAllowed;
PFN_NUMBER PageCount;
ULONG VirtualBias;
PMEMORY_ALLOCATION_DESCRIPTOR MemoryDescriptor;
//
// The default configuration gets a maximum of 4gb physical memory.
// On PAE machines the system continues to operate in 8-byte PTE mode.
//
TotalPagesAllowed = MI_DEFAULT_MAX_PAGES;
//
// If properly licensed (ie: DataCenter) and booted without the
// 3gb switch, then use all available physical memory.
//
if (ExVerifySuite(DataCenter) == TRUE) {
//
// Note MmVirtualBias has not yet been initialized at the time of the
// first call to this routine, so use the LoaderBlock directly.
//
#if defined(_X86_)
VirtualBias = LoaderBlock->u.I386.VirtualBias;
#else
VirtualBias = 0;
#endif
if (VirtualBias == 0) {
//
// Limit the maximum physical memory to the amount we have
// actually physically seen in a machine inhouse.
//
TotalPagesAllowed = MI_DTC_MAX_PAGES;
}
else {
//
// The system is booting /3gb, so don't use more than 16gb of
// physical memory. This ensures enough virtual space to map
// the PFN database.
//
TotalPagesAllowed = MI_DTC_BOOTED_3GB_MAX_PAGES;
}
}
else if ((MmProductType != 0x00690057) &&
(ExVerifySuite(Enterprise) == TRUE)) {
//
// Enforce the Advanced Server physical memory limit.
// On PAE machines the system continues to operate in 8-byte PTE mode.
//
TotalPagesAllowed = MI_ADS_MAX_PAGES;
}
else if (ExVerifySuite(Blade) == TRUE) {
//
// Enforce the Blade physical memory limit.
//
TotalPagesAllowed = MI_BLADE_MAX_PAGES;
}
//
// Walk through the memory descriptors and remove or truncate descriptors
// that exceed the maximum physical memory to be used.
//
PageCount = 0;
NextMd = LoaderBlock->MemoryDescriptorListHead.Flink;
while (NextMd != &LoaderBlock->MemoryDescriptorListHead) {
MemoryDescriptor = CONTAINING_RECORD(NextMd,
MEMORY_ALLOCATION_DESCRIPTOR,
ListEntry);
if ((MemoryDescriptor->MemoryType == LoaderFirmwarePermanent) ||
(MemoryDescriptor->MemoryType == LoaderBBTMemory) ||
(MemoryDescriptor->MemoryType == LoaderBad) ||
(MemoryDescriptor->MemoryType == LoaderSpecialMemory)) {
NextMd = MemoryDescriptor->ListEntry.Flink;
continue;
}
PageCount += MemoryDescriptor->PageCount;
if (PageCount <= TotalPagesAllowed) {
NextMd = MemoryDescriptor->ListEntry.Flink;
continue;
}
//
// This descriptor needs to be removed or truncated.
//
if (PageCount - MemoryDescriptor->PageCount >= TotalPagesAllowed) {
//
// Completely remove this descriptor.
//
// Note since this only adjusts the links and since the entry is
// not freed, it can still be safely referenced again below to
// obtain the NextMd. N.B. This keeps the memory descriptors
// sorted in ascending order.
//
RemoveEntryList (NextMd);
}
else {
//
// Truncate this descriptor.
//
ASSERT (PageCount - MemoryDescriptor->PageCount < TotalPagesAllowed);
MemoryDescriptor->PageCount -= (ULONG)(PageCount - TotalPagesAllowed);
PageCount = TotalPagesAllowed;
}
NextMd = MemoryDescriptor->ListEntry.Flink;
}
return;
}
VOID
MmFreeLoaderBlock (
IN PLOADER_PARAMETER_BLOCK LoaderBlock
)
/*++
Routine Description:
This function is called as the last routine in phase 1 initialization.
It frees memory used by the OsLoader.
Arguments:
LoaderBlock - Supplies a pointer to the system loader block.
Return Value:
None.
Environment:
Kernel Mode Only. System initialization.
--*/
{
PLIST_ENTRY NextMd;
PMEMORY_ALLOCATION_DESCRIPTOR MemoryDescriptor;
ULONG i;
PFN_NUMBER NextPhysicalPage;
PFN_NUMBER PagesFreed;
PMMPFN Pfn1;
KIRQL OldIrql;
PPHYSICAL_MEMORY_RUN RunBase;
PPHYSICAL_MEMORY_RUN Runs;
i = 0;
NextMd = LoaderBlock->MemoryDescriptorListHead.Flink;
while (NextMd != &LoaderBlock->MemoryDescriptorListHead) {
i += 1;
MemoryDescriptor = CONTAINING_RECORD(NextMd,
MEMORY_ALLOCATION_DESCRIPTOR,
ListEntry);
NextMd = MemoryDescriptor->ListEntry.Flink;
}
RunBase = ExAllocatePoolWithTag (NonPagedPool,
sizeof(PHYSICAL_MEMORY_RUN) * i,
'lMmM');
if (RunBase == NULL) {
return;
}
Runs = RunBase;
//
//
// Walk through the memory descriptors and add pages to the
// free list in the PFN database.
//
NextMd = LoaderBlock->MemoryDescriptorListHead.Flink;
while (NextMd != &LoaderBlock->MemoryDescriptorListHead) {
MemoryDescriptor = CONTAINING_RECORD(NextMd,
MEMORY_ALLOCATION_DESCRIPTOR,
ListEntry);
switch (MemoryDescriptor->MemoryType) {
case LoaderOsloaderHeap:
case LoaderRegistryData:
case LoaderNlsData:
//case LoaderMemoryData: //this has page table and other stuff.
//
// Capture the data to temporary storage so we won't
// free memory we are referencing.
//
Runs->BasePage = MemoryDescriptor->BasePage;
Runs->PageCount = MemoryDescriptor->PageCount;
Runs += 1;
break;
default:
break;
}
NextMd = MemoryDescriptor->ListEntry.Flink;
}
PagesFreed = 0;
LOCK_PFN (OldIrql);
if (Runs != RunBase) {
Runs -= 1;
do {
i = (ULONG)Runs->PageCount;
NextPhysicalPage = Runs->BasePage;
#if defined (_MI_MORE_THAN_4GB_)
if (MiNoLowMemory != 0) {
if (NextPhysicalPage < MiNoLowMemory) {
//
// Don't free this run as it is below the memory threshold
// configured for this system.
//
Runs -= 1;
continue;
}
}
#endif
Pfn1 = MI_PFN_ELEMENT (NextPhysicalPage);
PagesFreed += i;
while (i != 0) {
if (Pfn1->u3.e2.ReferenceCount == 0) {
if (Pfn1->u1.Flink == 0) {
//
// Set the PTE address to the physical page for
// virtual address alignment checking.
//
Pfn1->PteAddress =
(PMMPTE)(NextPhysicalPage << PTE_SHIFT);
MiDetermineNode (NextPhysicalPage, Pfn1);
MiInsertPageInFreeList (NextPhysicalPage);
}
}
else {
if (NextPhysicalPage != 0) {
//
// Remove PTE and insert into the free list. If it is
// a physical address within the PFN database, the PTE
// element does not exist and therefore cannot be updated.
//
if (!MI_IS_PHYSICAL_ADDRESS (
MiGetVirtualAddressMappedByPte (Pfn1->PteAddress))) {
//
// Not a physical address.
//
*(Pfn1->PteAddress) = ZeroPte;
}
MI_SET_PFN_DELETED (Pfn1);
MiDecrementShareCountOnly (NextPhysicalPage);
}
}
Pfn1 += 1;
i -= 1;
NextPhysicalPage += 1;
}
Runs -= 1;
} while (Runs >= RunBase);
}
//
// Since systemwide commitment was determined early in Phase 0 and
// excluded the ranges just freed, add them back in now.
//
if (PagesFreed != 0) {
InterlockedExchangeAddSizeT (&MmTotalCommitLimitMaximum, PagesFreed);
InterlockedExchangeAddSizeT (&MmTotalCommitLimit, PagesFreed);
}
#if defined(_X86_)
if (MmVirtualBias != 0) {
//
// If the kernel has been biased to allow for 3gb of user address space,
// then the first 16mb of memory is doubly mapped to KSEG0_BASE and to
// ALTERNATE_BASE. Therefore, the KSEG0_BASE entries must be unmapped.
//
PMMPTE Pde;
ULONG NumberOfPdes;
NumberOfPdes = MmBootImageSize / MM_VA_MAPPED_BY_PDE;
Pde = MiGetPdeAddress((PVOID)KSEG0_BASE);
for (i = 0; i < NumberOfPdes; i += 1) {
MI_WRITE_INVALID_PTE (Pde, ZeroKernelPte);
Pde += 1;
}
}
#endif
KeFlushEntireTb (TRUE, TRUE);
UNLOCK_PFN (OldIrql);
ExFreePool (RunBase);
return;
}
VOID
MiBuildPagedPool (
VOID
)
/*++
Routine Description:
This function is called to build the structures required for paged
pool and initialize the pool. Once this routine is called, paged
pool may be allocated.
Arguments:
None.
Return Value:
None.
Environment:
Kernel Mode Only. System initialization.
--*/
{
SIZE_T Size;
PMMPTE PointerPte;
PMMPTE LastPte;
PMMPTE LastPde;
PMMPTE PointerPde;
MMPTE TempPte;
PFN_NUMBER PageFrameIndex;
PFN_NUMBER ContainingFrame;
SIZE_T AdditionalCommittedPages;
KIRQL OldIrql;
ULONG i;
#if (_MI_PAGING_LEVELS >= 4)
PMMPTE PointerPxe;
PMMPTE PointerPxeEnd;
#endif
#if (_MI_PAGING_LEVELS >= 3)
PVOID LastVa;
PMMPTE PointerPpe;
PMMPTE PointerPpeEnd;
#else
PMMPFN Pfn1;
#endif
i = 0;
AdditionalCommittedPages = 0;
#if (_MI_PAGING_LEVELS < 3)
//
// Double map system page directory page.
//
PointerPte = MiGetPteAddress(PDE_BASE);
for (i = 0 ; i < PD_PER_SYSTEM; i += 1) {
MmSystemPageDirectory[i] = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
Pfn1 = MI_PFN_ELEMENT(MmSystemPageDirectory[i]);
Pfn1->OriginalPte.u.Long = MM_DEMAND_ZERO_WRITE_PTE;
PointerPte += 1;
}
//
// Was not mapped physically, map it virtually in system space.
//
PointerPte = MiReserveSystemPtes (PD_PER_SYSTEM, SystemPteSpace);
if (PointerPte == NULL) {
MiIssueNoPtesBugcheck (PD_PER_SYSTEM, SystemPteSpace);
}
MmSystemPagePtes = (PMMPTE)MiGetVirtualAddressMappedByPte (PointerPte);
TempPte = ValidKernelPde;
for (i = 0 ; i < PD_PER_SYSTEM; i += 1) {
TempPte.u.Hard.PageFrameNumber = MmSystemPageDirectory[i];
MI_WRITE_VALID_PTE (PointerPte, TempPte);
PointerPte += 1;
}
#endif
if (MmPagedPoolMaximumDesired == TRUE) {
MmSizeOfPagedPoolInBytes =
((PCHAR)MmNonPagedSystemStart - (PCHAR)MmPagedPoolStart);
}
else if (MmSizeOfPagedPoolInBytes == 0) {
//
// A size of 0 means size the pool based on physical memory.
//
MmSizeOfPagedPoolInBytes = 2 * MmMaximumNonPagedPoolInBytes;
#if (_MI_PAGING_LEVELS >= 3)
MmSizeOfPagedPoolInBytes *= 2;
#endif
}
if (MmIsThisAnNtAsSystem()) {
if ((MmNumberOfPhysicalPages > ((24*1024*1024) >> PAGE_SHIFT)) &&
(MmSizeOfPagedPoolInBytes < MM_MINIMUM_PAGED_POOL_NTAS)) {
MmSizeOfPagedPoolInBytes = MM_MINIMUM_PAGED_POOL_NTAS;
}
}
if (MmSizeOfPagedPoolInBytes >
(ULONG_PTR)((PCHAR)MmNonPagedSystemStart - (PCHAR)MmPagedPoolStart)) {
MmSizeOfPagedPoolInBytes =
((PCHAR)MmNonPagedSystemStart - (PCHAR)MmPagedPoolStart);
}
Size = BYTES_TO_PAGES(MmSizeOfPagedPoolInBytes);
if (Size < MM_MIN_INITIAL_PAGED_POOL) {
Size = MM_MIN_INITIAL_PAGED_POOL;
}
if (Size > (MM_MAX_PAGED_POOL >> PAGE_SHIFT)) {
Size = MM_MAX_PAGED_POOL >> PAGE_SHIFT;
}
#if defined (_WIN64)
//
// NT64 places system mapped views directly after paged pool. Ensure
// enough VA space is available.
//
if (Size + (MmSystemViewSize >> PAGE_SHIFT) > (MM_MAX_PAGED_POOL >> PAGE_SHIFT)) {
ASSERT (MmSizeOfPagedPoolInBytes > 2 * MmSystemViewSize);
MmSizeOfPagedPoolInBytes -= MmSystemViewSize;
Size = BYTES_TO_PAGES(MmSizeOfPagedPoolInBytes);
}
#endif
Size = (Size + (PTE_PER_PAGE - 1)) / PTE_PER_PAGE;
MmSizeOfPagedPoolInBytes = (ULONG_PTR)Size * PAGE_SIZE * PTE_PER_PAGE;
//
// Set size to the number of pages in the pool.
//
Size = Size * PTE_PER_PAGE;
//
// If paged pool is really nonpagable then limit the size based
// on how much physical memory is actually present. Disable this
// feature if not enough physical memory is present to do it.
//
if (MmDisablePagingExecutive & MM_PAGED_POOL_LOCKED_DOWN) {
Size = MmSizeOfPagedPoolInBytes / PAGE_SIZE;
if ((MI_NONPAGABLE_MEMORY_AVAILABLE() < 2048) ||
(MmAvailablePages < 2048)) {
Size = 0;
}
else {
if ((SPFN_NUMBER)(Size) > MI_NONPAGABLE_MEMORY_AVAILABLE() - 2048) {
Size = (MI_NONPAGABLE_MEMORY_AVAILABLE() - 2048);
}
if (Size > MmAvailablePages - 2048) {
Size = MmAvailablePages - 2048;
}
}
Size = ((Size * PAGE_SIZE) / MM_VA_MAPPED_BY_PDE) * MM_VA_MAPPED_BY_PDE;
if ((((Size / 5) * 4) >= MmSizeOfPagedPoolInBytes) &&
(Size >= MM_MIN_INITIAL_PAGED_POOL)) {
MmSizeOfPagedPoolInBytes = Size;
}
else {
MmDisablePagingExecutive &= ~MM_PAGED_POOL_LOCKED_DOWN;
}
Size = MmSizeOfPagedPoolInBytes >> PAGE_SHIFT;
}
ASSERT ((MmSizeOfPagedPoolInBytes + (PCHAR)MmPagedPoolStart) <=
(PCHAR)MmNonPagedSystemStart);
ASSERT64 ((MmSizeOfPagedPoolInBytes + (PCHAR)MmPagedPoolStart + MmSystemViewSize) <=
(PCHAR)MmNonPagedSystemStart);
MmPagedPoolEnd = (PVOID)(((PUCHAR)MmPagedPoolStart +
MmSizeOfPagedPoolInBytes) - 1);
MmPageAlignedPoolBase[PagedPool] = MmPagedPoolStart;
//
// Build page table page for paged pool.
//
PointerPde = MiGetPdeAddress (MmPagedPoolStart);
TempPte = ValidKernelPde;
#if (_MI_PAGING_LEVELS >= 3)
//
// Map in all the page directory pages to span all of paged pool.
// This removes the need for a system lookup directory.
//
LastVa = (PVOID)((PCHAR)MmPagedPoolEnd + MmSystemViewSize);
PointerPpe = MiGetPpeAddress (MmPagedPoolStart);
PointerPpeEnd = MiGetPpeAddress (LastVa);
MiSystemViewStart = (ULONG_PTR)MmPagedPoolEnd + 1;
PointerPde = MiGetPdeAddress (MmPagedPoolEnd) + 1;
LastPde = MiGetPdeAddress (LastVa);
LOCK_PFN (OldIrql);
#if (_MI_PAGING_LEVELS >= 4)
PointerPxe = MiGetPxeAddress (MmPagedPoolStart);
PointerPxeEnd = MiGetPxeAddress (LastVa);
while (PointerPxe <= PointerPxeEnd) {
if (PointerPxe->u.Hard.Valid == 0) {
PageFrameIndex = MiRemoveAnyPage(
MI_GET_PAGE_COLOR_FROM_PTE (PointerPxe));
TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
MI_WRITE_VALID_PTE (PointerPxe, TempPte);
MiInitializePfn (PageFrameIndex, PointerPxe, 1);
//
// Make all entries no access since the PDEs may not fill the page.
//
MiFillMemoryPte (MiGetVirtualAddressMappedByPte (PointerPxe),
PAGE_SIZE,
MM_KERNEL_NOACCESS_PTE);
MmResidentAvailablePages -= 1;
AdditionalCommittedPages += 1;
}
PointerPxe += 1;
}
#endif
while (PointerPpe <= PointerPpeEnd) {
if (PointerPpe->u.Hard.Valid == 0) {
PageFrameIndex = MiRemoveAnyPage(
MI_GET_PAGE_COLOR_FROM_PTE (PointerPpe));
TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
MI_WRITE_VALID_PTE (PointerPpe, TempPte);
MiInitializePfn (PageFrameIndex, PointerPpe, 1);
//
// Make all entries no access since the PDEs may not fill the page.
//
MiFillMemoryPte (MiGetVirtualAddressMappedByPte (PointerPpe),
PAGE_SIZE,
MM_KERNEL_NOACCESS_PTE);
MmResidentAvailablePages -= 1;
AdditionalCommittedPages += 1;
}
PointerPpe += 1;
}
//
// Initialize the system view page table pages.
//
MmResidentAvailablePages -= (LastPde - PointerPde + 1);
AdditionalCommittedPages += (LastPde - PointerPde + 1);
while (PointerPde <= LastPde) {
ASSERT (PointerPde->u.Hard.Valid == 0);
PageFrameIndex = MiRemoveAnyPage(
MI_GET_PAGE_COLOR_FROM_PTE (PointerPde));
TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
MI_WRITE_VALID_PTE (PointerPde, TempPte);
MiInitializePfn (PageFrameIndex, PointerPde, 1);
MiFillMemoryPte (MiGetVirtualAddressMappedByPte (PointerPde),
PAGE_SIZE,
ZeroKernelPte.u.Long);
PointerPde += 1;
}
UNLOCK_PFN (OldIrql);
PointerPde = MiGetPdeAddress (MmPagedPoolStart);
#endif
PointerPte = MiGetPteAddress (MmPagedPoolStart);
MmPagedPoolInfo.FirstPteForPagedPool = PointerPte;
MmPagedPoolInfo.LastPteForPagedPool = MiGetPteAddress (MmPagedPoolEnd);
MiFillMemoryPte (PointerPde,
sizeof(MMPTE) *
(1 + MiGetPdeAddress (MmPagedPoolEnd) - PointerPde),
MM_KERNEL_NOACCESS_PTE);
LOCK_PFN (OldIrql);
//
// Map in a page table page.
//
PageFrameIndex = MiRemoveAnyPage(
MI_GET_PAGE_COLOR_FROM_PTE (PointerPde));
TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
MI_WRITE_VALID_PTE (PointerPde, TempPte);
#if (_MI_PAGING_LEVELS >= 3)
ContainingFrame = MI_GET_PAGE_FRAME_FROM_PTE(MiGetPpeAddress (MmPagedPoolStart));
#else
ContainingFrame = MmSystemPageDirectory[(PointerPde - MiGetPdeAddress(0)) / PDE_PER_PAGE];
#endif
MiInitializePfnForOtherProcess (PageFrameIndex,
PointerPde,
ContainingFrame);
MiFillMemoryPte (PointerPte, PAGE_SIZE, MM_KERNEL_NOACCESS_PTE);
UNLOCK_PFN (OldIrql);
MmPagedPoolInfo.NextPdeForPagedPoolExpansion = PointerPde + 1;
//
// Build bitmaps for paged pool.
//
MiCreateBitMap (&MmPagedPoolInfo.PagedPoolAllocationMap, Size, NonPagedPool);
RtlSetAllBits (MmPagedPoolInfo.PagedPoolAllocationMap);
//
// Indicate first page worth of PTEs are available.
//
RtlClearBits (MmPagedPoolInfo.PagedPoolAllocationMap, 0, PTE_PER_PAGE);
MiCreateBitMap (&MmPagedPoolInfo.EndOfPagedPoolBitmap, Size, NonPagedPool);
RtlClearAllBits (MmPagedPoolInfo.EndOfPagedPoolBitmap);
//
// If verifier is present then build the verifier paged pool bitmap.
//
if (MmVerifyDriverBufferLength != (ULONG)-1) {
MiCreateBitMap (&VerifierLargePagedPoolMap, Size, NonPagedPool);
RtlClearAllBits (VerifierLargePagedPoolMap);
}
//
// Initialize paged pool.
//
InitializePool (PagedPool, 0L);
//
// If paged pool is really nonpagable then allocate the memory now.
//
if (MmDisablePagingExecutive & MM_PAGED_POOL_LOCKED_DOWN) {
PointerPde = MiGetPdeAddress (MmPagedPoolStart);
PointerPde += 1;
LastPde = MiGetPdeAddress (MmPagedPoolEnd);
TempPte = ValidKernelPde;
PointerPte = MiGetPteAddress (MmPagedPoolStart);
LastPte = MiGetPteAddress (MmPagedPoolEnd);
ASSERT (MmPagedPoolCommit == 0);
MmPagedPoolCommit = (ULONG)(LastPte - PointerPte + 1);
ASSERT (MmPagedPoolInfo.PagedPoolCommit == 0);
MmPagedPoolInfo.PagedPoolCommit = MmPagedPoolCommit;
#if DBG
//
// Ensure no paged pool has been allocated yet.
//
for (i = 0; i < PTE_PER_PAGE; i += 1) {
ASSERT (!RtlCheckBit (MmPagedPoolInfo.PagedPoolAllocationMap, i));
}
while (i < MmSizeOfPagedPoolInBytes / PAGE_SIZE) {
ASSERT (RtlCheckBit (MmPagedPoolInfo.PagedPoolAllocationMap, i));
i += 1;
}
#endif
RtlClearAllBits (MmPagedPoolInfo.PagedPoolAllocationMap);
LOCK_PFN (OldIrql);
//
// Map in the page table pages.
//
MmResidentAvailablePages -= (LastPde - PointerPde + 1);
AdditionalCommittedPages += (LastPde - PointerPde + 1);
while (PointerPde <= LastPde) {
ASSERT (PointerPde->u.Hard.Valid == 0);
PageFrameIndex = MiRemoveAnyPage(
MI_GET_PAGE_COLOR_FROM_PTE (PointerPde));
TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
MI_WRITE_VALID_PTE (PointerPde, TempPte);
#if (_MI_PAGING_LEVELS >= 3)
ContainingFrame = MI_GET_PAGE_FRAME_FROM_PTE(MiGetPteAddress (PointerPde));
#else
ContainingFrame = MmSystemPageDirectory[(PointerPde - MiGetPdeAddress(0)) / PDE_PER_PAGE];
#endif
MiInitializePfnForOtherProcess (PageFrameIndex,
MiGetPteAddress (PointerPde),
ContainingFrame);
MiFillMemoryPte (MiGetVirtualAddressMappedByPte (PointerPde),
PAGE_SIZE,
MM_KERNEL_NOACCESS_PTE);
PointerPde += 1;
}
MmPagedPoolInfo.NextPdeForPagedPoolExpansion = PointerPde;
TempPte = ValidKernelPte;
MI_SET_PTE_DIRTY (TempPte);
ASSERT (MmAvailablePages > (PFN_COUNT)(LastPte - PointerPte + 1));
ASSERT (MmResidentAvailablePages > (SPFN_NUMBER)(LastPte - PointerPte + 1));
MmResidentAvailablePages -= (LastPte - PointerPte + 1);
AdditionalCommittedPages += (LastPte - PointerPte + 1);
while (PointerPte <= LastPte) {
ASSERT (PointerPte->u.Hard.Valid == 0);
PageFrameIndex = MiRemoveAnyPage(
MI_GET_PAGE_COLOR_FROM_PTE (PointerPte));
TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
MI_WRITE_VALID_PTE (PointerPte, TempPte);
MiInitializePfn (PageFrameIndex, PointerPte, 1);
PointerPte += 1;
}
UNLOCK_PFN (OldIrql);
}
//
// Since the commitment return path is lock free, the total committed
// page count must be atomically incremented.
//
InterlockedExchangeAddSizeT (&MmTotalCommittedPages, AdditionalCommittedPages);
MiInitializeSpecialPool (NonPagedPool);
//
// Allow mapping of views into system space.
//
MiInitializeSystemSpaceMap (NULL);
return;
}
VOID
MiFindInitializationCode (
OUT PVOID *StartVa,
OUT PVOID *EndVa
)
/*++
Routine Description:
This function locates the start and end of the kernel initialization
code. This code resides in the "init" section of the kernel image.
Arguments:
StartVa - Returns the starting address of the init section.
EndVa - Returns the ending address of the init section.
Return Value:
None.
Environment:
Kernel Mode Only. End of system initialization.
--*/
{
PKLDR_DATA_TABLE_ENTRY LdrDataTableEntry;
PVOID CurrentBase;
PVOID InitStart;
PVOID InitEnd;
PLIST_ENTRY Next;
PIMAGE_NT_HEADERS NtHeader;
PIMAGE_SECTION_HEADER SectionTableEntry;
PIMAGE_SECTION_HEADER LastDiscard;
LONG i;
LOGICAL DiscardSection;
PVOID MiFindInitializationCodeAddress;
PKTHREAD CurrentThread;
MiFindInitializationCodeAddress = MmGetProcedureAddress((PVOID)(ULONG_PTR)&MiFindInitializationCode);
#if defined(_IA64_)
//
// One more indirection is needed due to the PLABEL.
//
MiFindInitializationCodeAddress = (PVOID)(*((PULONGLONG)MiFindInitializationCodeAddress));
#endif
*StartVa = NULL;
//
// Walk through the loader blocks looking for the base which
// contains this routine.
//
CurrentThread = KeGetCurrentThread ();
KeEnterCriticalRegionThread (CurrentThread);
ExAcquireResourceExclusiveLite (&PsLoadedModuleResource, TRUE);
Next = PsLoadedModuleList.Flink;
while (Next != &PsLoadedModuleList) {
LdrDataTableEntry = CONTAINING_RECORD (Next,
KLDR_DATA_TABLE_ENTRY,
InLoadOrderLinks);
if (LdrDataTableEntry->SectionPointer != NULL) {
//
// This entry was loaded by MmLoadSystemImage so it's already
// had its init section removed.
//
Next = Next->Flink;
continue;
}
CurrentBase = (PVOID)LdrDataTableEntry->DllBase;
NtHeader = RtlImageNtHeader(CurrentBase);
SectionTableEntry = (PIMAGE_SECTION_HEADER)((PCHAR)NtHeader +
sizeof(ULONG) +
sizeof(IMAGE_FILE_HEADER) +
NtHeader->FileHeader.SizeOfOptionalHeader);
//
// From the image header, locate the sections named 'INIT',
// PAGEVRF* and PAGESPEC. INIT always goes, the others go depending
// on registry configuration.
//
i = NtHeader->FileHeader.NumberOfSections;
InitStart = NULL;
while (i > 0) {
#if DBG
if ((*(PULONG)SectionTableEntry->Name == 'tini') ||
(*(PULONG)SectionTableEntry->Name == 'egap')) {
DbgPrint("driver %wZ has lower case sections (init or pagexxx)\n",
&LdrDataTableEntry->FullDllName);
}
#endif
DiscardSection = FALSE;
//
// Free any INIT sections (or relocation sections that haven't
// been already). Note a driver may have a relocation section
// but not have any INIT code.
//
if ((*(PULONG)SectionTableEntry->Name == 'TINI') ||
((SectionTableEntry->Characteristics & IMAGE_SCN_MEM_DISCARDABLE) != 0)) {
DiscardSection = TRUE;
}
else if ((*(PULONG)SectionTableEntry->Name == 'EGAP') &&
(SectionTableEntry->Name[4] == 'V') &&
(SectionTableEntry->Name[5] == 'R') &&
(SectionTableEntry->Name[6] == 'F')) {
//
// Discard PAGEVRF* if no drivers are being instrumented.
//
if (MmVerifyDriverBufferLength == (ULONG)-1) {
DiscardSection = TRUE;
}
}
else if ((*(PULONG)SectionTableEntry->Name == 'EGAP') &&
(*(PULONG)&SectionTableEntry->Name[4] == 'CEPS')) {
//
// Discard PAGESPEC special pool code if it's not enabled.
//
if (MiSpecialPoolFirstPte == NULL) {
DiscardSection = TRUE;
}
}
if (DiscardSection == TRUE) {
InitStart = (PVOID)((PCHAR)CurrentBase + SectionTableEntry->VirtualAddress);
InitEnd = (PVOID)((PCHAR)InitStart + SectionTableEntry->SizeOfRawData - 1);
InitEnd = (PVOID)((PCHAR)PAGE_ALIGN ((PCHAR)InitEnd +
(NtHeader->OptionalHeader.SectionAlignment - 1)) - 1);
InitStart = (PVOID)ROUND_TO_PAGES (InitStart);
//
// Check if more sections are discardable after this one so
// even small INIT sections can be discarded.
//
if (i == 1) {
LastDiscard = SectionTableEntry;
}
else {
LastDiscard = NULL;
do {
i -= 1;
SectionTableEntry += 1;
if ((SectionTableEntry->Characteristics &
IMAGE_SCN_MEM_DISCARDABLE) != 0) {
//
// Discard this too.
//
LastDiscard = SectionTableEntry;
}
else {
break;
}
} while (i > 1);
}
if (LastDiscard) {
InitEnd = (PVOID)(((PCHAR)CurrentBase +
LastDiscard->VirtualAddress) +
(LastDiscard->SizeOfRawData - 1));
//
// If this isn't the last section in the driver then the
// the next section is not discardable. So the last
// section is not rounded down, but all others must be.
//
if (i != 1) {
InitEnd = (PVOID)((PCHAR)PAGE_ALIGN ((PCHAR)InitEnd +
(NtHeader->OptionalHeader.SectionAlignment - 1)) - 1);
}
}
if (InitEnd > (PVOID)((PCHAR)CurrentBase +
LdrDataTableEntry->SizeOfImage)) {
InitEnd = (PVOID)(((ULONG_PTR)CurrentBase +
(LdrDataTableEntry->SizeOfImage - 1)) |
(PAGE_SIZE - 1));
}
if (InitStart <= InitEnd) {
if ((MiFindInitializationCodeAddress >= InitStart) &&
(MiFindInitializationCodeAddress <= InitEnd)) {
//
// This init section is in the kernel, don't free it
// now as it would free this code!
//
ASSERT (*StartVa == NULL);
*StartVa = InitStart;
*EndVa = InitEnd;
}
else {
MiFreeInitializationCode (InitStart, InitEnd);
}
}
}
i -= 1;
SectionTableEntry += 1;
}
Next = Next->Flink;
}
ExReleaseResourceLite (&PsLoadedModuleResource);
KeLeaveCriticalRegionThread (CurrentThread);
return;
}
VOID
MiFreeInitializationCode (
IN PVOID StartVa,
IN PVOID EndVa
)
/*++
Routine Description:
This function is called to delete the initialization code.
Arguments:
StartVa - Supplies the starting address of the range to delete.
EndVa - Supplies the ending address of the range to delete.
Return Value:
None.
Environment:
Kernel Mode Only. Runs after system initialization.
--*/
{
PMMPFN Pfn1;
PMMPTE PointerPte;
PFN_NUMBER PageFrameIndex;
PFN_NUMBER PagesFreed;
KIRQL OldIrql;
ASSERT(ExPageLockHandle);
#if defined (_MI_MORE_THAN_4GB_)
if (MiNoLowMemory != 0) {
//
// Don't free this range as the kernel is always below the memory
// threshold configured for this system.
//
return;
}
#endif
PagesFreed = 0;
MmLockPagableSectionByHandle(ExPageLockHandle);
if (MI_IS_PHYSICAL_ADDRESS(StartVa)) {
LOCK_PFN (OldIrql);
while (StartVa < EndVa) {
//
// On certain architectures (e.g., MIPS) virtual addresses
// may be physical and hence have no corresponding PTE.
//
PageFrameIndex = MI_CONVERT_PHYSICAL_TO_PFN (StartVa);
Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
Pfn1->u2.ShareCount = 0;
Pfn1->u3.e2.ReferenceCount = 0;
MI_SET_PFN_DELETED (Pfn1);
MiInsertPageInFreeList (PageFrameIndex);
StartVa = (PVOID)((PUCHAR)StartVa + PAGE_SIZE);
PagesFreed += 1;
}
UNLOCK_PFN (OldIrql);
}
else {
PointerPte = MiGetPteAddress (StartVa);
PagesFreed = MiDeleteSystemPagableVm (PointerPte,
(PFN_NUMBER) (1 + MiGetPteAddress (EndVa) -
PointerPte),
ZeroKernelPte,
FALSE,
NULL);
}
MmUnlockPagableImageSection(ExPageLockHandle);
//
// Since systemwide commitment was determined early in Phase 0 and
// excluded the ranges just freed, add them back in now.
//
if (PagesFreed != 0) {
//
// Since systemwide commitment was determined early in Phase 0
// and excluded the ranges just freed, increase the limits
// accordingly now. Note that there is no commitment to be
// returned (as none was ever charged earlier) for boot
// loaded drivers.
//
InterlockedExchangeAddSizeT (&MmTotalCommitLimitMaximum, PagesFreed);
InterlockedExchangeAddSizeT (&MmTotalCommitLimit, PagesFreed);
}
return;
}
VOID
MiEnablePagingTheExecutive (
VOID
)
/*++
Routine Description:
This function locates the start and end of the kernel initialization
code. This code resides in the "init" section of the kernel image.
Arguments:
StartVa - Returns the starting address of the init section.
EndVa - Returns the ending address of the init section.
Return Value:
None.
Environment:
Kernel Mode Only. End of system initialization.
--*/
{
KIRQL OldIrql;
KIRQL OldIrqlWs;
PVOID StartVa;
PETHREAD CurrentThread;
PLONG SectionLockCountPointer;
PKLDR_DATA_TABLE_ENTRY LdrDataTableEntry;
PVOID CurrentBase;
PLIST_ENTRY Next;
PIMAGE_NT_HEADERS NtHeader;
PIMAGE_SECTION_HEADER StartSectionTableEntry;
PIMAGE_SECTION_HEADER SectionTableEntry;
LONG i;
PMMPTE PointerPte;
PMMPTE LastPte;
PMMPTE SubsectionStartPte;
PMMPTE SubsectionLastPte;
LOGICAL PageSection;
PVOID SectionBaseAddress;
LOGICAL AlreadyLockedOnce;
ULONG Waited;
PEPROCESS CurrentProcess;
//
// Don't page kernel mode code if customer does not want it paged or if
// this is a diskless remote boot client.
//
if (MmDisablePagingExecutive & MM_SYSTEM_CODE_LOCKED_DOWN) {
return;
}
#if defined(REMOTE_BOOT)
if (IoRemoteBootClient && IoCscInitializationFailed) {
return;
}
#endif
//
// Initializing LastPte is not needed for correctness, but
// without it the compiler cannot compile this code W4 to check
// for use of uninitialized variables.
//
LastPte = NULL;
//
// Walk through the loader blocks looking for the base which
// contains this routine.
//
CurrentThread = PsGetCurrentThread ();
CurrentProcess = PsGetCurrentProcessByThread (CurrentThread);
KeEnterCriticalRegionThread (&CurrentThread->Tcb);
ExAcquireResourceExclusiveLite (&PsLoadedModuleResource, TRUE);
Next = PsLoadedModuleList.Flink;
while (Next != &PsLoadedModuleList) {
LdrDataTableEntry = CONTAINING_RECORD (Next,
KLDR_DATA_TABLE_ENTRY,
InLoadOrderLinks);
if (LdrDataTableEntry->SectionPointer != NULL) {
//
// This entry was loaded by MmLoadSystemImage so it's already paged.
//
Next = Next->Flink;
continue;
}
CurrentBase = (PVOID)LdrDataTableEntry->DllBase;
if (MI_IS_PHYSICAL_ADDRESS (CurrentBase)) {
//
// Mapped physically, can't be paged.
//
Next = Next->Flink;
continue;
}
NtHeader = RtlImageNtHeader (CurrentBase);
restart:
StartSectionTableEntry = NULL;
SectionTableEntry = (PIMAGE_SECTION_HEADER)((PCHAR)NtHeader +
sizeof(ULONG) +
sizeof(IMAGE_FILE_HEADER) +
NtHeader->FileHeader.SizeOfOptionalHeader);
//
// From the image header, locate the section named 'PAGE' or '.edata'.
//
i = NtHeader->FileHeader.NumberOfSections;
PointerPte = NULL;
while (i > 0) {
SectionBaseAddress = SECTION_BASE_ADDRESS(SectionTableEntry);
if ((PUCHAR)SectionBaseAddress ==
((PUCHAR)CurrentBase + SectionTableEntry->VirtualAddress)) {
AlreadyLockedOnce = TRUE;
//
// This subsection has already been locked down (and possibly
// unlocked as well) at least once. If it is NOT locked down
// right now and the pages are not in the system working set
// then include it in the chunk to be paged.
//
SectionLockCountPointer = SECTION_LOCK_COUNT_POINTER (SectionTableEntry);
if (*SectionLockCountPointer == 0) {
SubsectionStartPte = MiGetPteAddress ((PVOID)(ROUND_TO_PAGES (
(ULONG_PTR)CurrentBase +
SectionTableEntry->VirtualAddress)));
SubsectionLastPte = MiGetPteAddress ((PVOID)((ULONG_PTR)CurrentBase +
SectionTableEntry->VirtualAddress +
(NtHeader->OptionalHeader.SectionAlignment - 1) +
SectionTableEntry->SizeOfRawData -
PAGE_SIZE));
if (SubsectionLastPte >= SubsectionStartPte) {
AlreadyLockedOnce = FALSE;
}
}
}
else {
AlreadyLockedOnce = FALSE;
}
PageSection = ((*(PULONG)SectionTableEntry->Name == 'EGAP') ||
(*(PULONG)SectionTableEntry->Name == 'ade.')) &&
(AlreadyLockedOnce == FALSE);
if (*(PULONG)SectionTableEntry->Name == 'EGAP' &&
SectionTableEntry->Name[4] == 'K' &&
SectionTableEntry->Name[5] == 'D') {
//
// Only pageout PAGEKD if KdPitchDebugger is TRUE.
//
PageSection = KdPitchDebugger;
}
if ((*(PULONG)SectionTableEntry->Name == 'EGAP') &&
(SectionTableEntry->Name[4] == 'V') &&
(SectionTableEntry->Name[5] == 'R') &&
(SectionTableEntry->Name[6] == 'F')) {
//
// Pageout PAGEVRF* if no drivers are being instrumented.
//
if (MmVerifyDriverBufferLength != (ULONG)-1) {
PageSection = FALSE;
}
}
if ((*(PULONG)SectionTableEntry->Name == 'EGAP') &&
(*(PULONG)&SectionTableEntry->Name[4] == 'CEPS')) {
//
// Pageout PAGESPEC special pool code if it's not enabled.
//
if (MiSpecialPoolFirstPte != NULL) {
PageSection = FALSE;
}
}
if (PageSection) {
//
// This section is pagable, save away the start and end.
//
if (PointerPte == NULL) {
//
// Previous section was NOT pagable, get the start address.
//
ASSERT (StartSectionTableEntry == NULL);
StartSectionTableEntry = SectionTableEntry;
PointerPte = MiGetPteAddress ((PVOID)(ROUND_TO_PAGES (
(ULONG_PTR)CurrentBase +
SectionTableEntry->VirtualAddress)));
}
LastPte = MiGetPteAddress ((PVOID)((ULONG_PTR)CurrentBase +
SectionTableEntry->VirtualAddress +
(NtHeader->OptionalHeader.SectionAlignment - 1) +
SectionTableEntry->SizeOfRawData -
PAGE_SIZE));
}
else {
//
// This section is not pagable, if the previous section was
// pagable, enable it.
//
if (PointerPte != NULL) {
ASSERT (StartSectionTableEntry != NULL);
LOCK_SYSTEM_WS (OldIrqlWs, CurrentThread);
LOCK_PFN (OldIrql);
StartVa = PAGE_ALIGN (StartSectionTableEntry);
while (StartVa < (PVOID) SectionTableEntry) {
Waited = MiMakeSystemAddressValidPfnSystemWs (StartVa);
if (Waited != 0) {
//
// Restart at the top as the locks were released.
//
UNLOCK_PFN (OldIrql);
UNLOCK_SYSTEM_WS (OldIrqlWs);
goto restart;
}
StartVa = (PVOID)((PCHAR)StartVa + PAGE_SIZE);
}
//
// Now that we're holding the proper locks, rewalk all
// the sections to make sure they weren't locked down
// after we checked above.
//
while (StartSectionTableEntry < SectionTableEntry) {
SectionBaseAddress = SECTION_BASE_ADDRESS(StartSectionTableEntry);
SectionLockCountPointer = SECTION_LOCK_COUNT_POINTER (StartSectionTableEntry);
if (((PUCHAR)SectionBaseAddress ==
((PUCHAR)CurrentBase + StartSectionTableEntry->VirtualAddress)) &&
(*SectionLockCountPointer != 0)) {
//
// Restart at the top as the section has been
// explicitly locked by a driver since we first
// checked above.
//
UNLOCK_PFN (OldIrql);
UNLOCK_SYSTEM_WS (OldIrqlWs);
goto restart;
}
StartSectionTableEntry += 1;
}
MiEnablePagingOfDriverAtInit (PointerPte, LastPte);
UNLOCK_PFN (OldIrql);
UNLOCK_SYSTEM_WS (OldIrqlWs);
PointerPte = NULL;
StartSectionTableEntry = NULL;
}
}
i -= 1;
SectionTableEntry += 1;
}
if (PointerPte != NULL) {
ASSERT (StartSectionTableEntry != NULL);
LOCK_SYSTEM_WS (OldIrqlWs, CurrentThread);
LOCK_PFN (OldIrql);
StartVa = PAGE_ALIGN (StartSectionTableEntry);
while (StartVa < (PVOID) SectionTableEntry) {
Waited = MiMakeSystemAddressValidPfnSystemWs (StartVa);
if (Waited != 0) {
//
// Restart at the top as the locks were released.
//
UNLOCK_PFN (OldIrql);
UNLOCK_SYSTEM_WS (OldIrqlWs);
goto restart;
}
StartVa = (PVOID)((PCHAR)StartVa + PAGE_SIZE);
}
//
// Now that we're holding the proper locks, rewalk all
// the sections to make sure they weren't locked down
// after we checked above.
//
while (StartSectionTableEntry < SectionTableEntry) {
SectionBaseAddress = SECTION_BASE_ADDRESS(StartSectionTableEntry);
SectionLockCountPointer = SECTION_LOCK_COUNT_POINTER (StartSectionTableEntry);
if (((PUCHAR)SectionBaseAddress ==
((PUCHAR)CurrentBase + StartSectionTableEntry->VirtualAddress)) &&
(*SectionLockCountPointer != 0)) {
//
// Restart at the top as the section has been
// explicitly locked by a driver since we first
// checked above.
//
UNLOCK_PFN (OldIrql);
UNLOCK_SYSTEM_WS (OldIrqlWs);
goto restart;
}
StartSectionTableEntry += 1;
}
MiEnablePagingOfDriverAtInit (PointerPte, LastPte);
UNLOCK_PFN (OldIrql);
UNLOCK_SYSTEM_WS (OldIrqlWs);
}
Next = Next->Flink;
}
ExReleaseResourceLite (&PsLoadedModuleResource);
KeLeaveCriticalRegionThread (&CurrentThread->Tcb);
return;
}
VOID
MiEnablePagingOfDriverAtInit (
IN PMMPTE PointerPte,
IN PMMPTE LastPte
)
/*++
Routine Description:
This routine marks the specified range of PTEs as pagable.
Arguments:
PointerPte - Supplies the starting PTE.
LastPte - Supplies the ending PTE.
Return Value:
None.
Environment:
Working set mutex AND PFN lock held.
--*/
{
PVOID Base;
PFN_NUMBER PageFrameIndex;
PMMPFN Pfn;
MMPTE TempPte;
LOGICAL SessionAddress;
MM_PFN_LOCK_ASSERT();
Base = MiGetVirtualAddressMappedByPte (PointerPte);
SessionAddress = MI_IS_SESSION_PTE (PointerPte);
while (PointerPte <= LastPte) {
//
// The PTE must be carefully checked as drivers may call MmPageEntire
// during their DriverEntry yet faults may occur prior to this routine
// running which cause pages to already be resident and in the working
// set at this point. So checks for validity and wsindex must be
// applied.
//
if (PointerPte->u.Hard.Valid == 1) {
PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
Pfn = MI_PFN_ELEMENT (PageFrameIndex);
ASSERT (Pfn->u2.ShareCount == 1);
if (Pfn->u1.WsIndex == 0) {
//
// Set the working set index to zero. This allows page table
// pages to be brought back in with the proper WSINDEX.
//
MI_ZERO_WSINDEX (Pfn);
//
// Original PTE may need to be set for drivers loaded via
// ntldr.
//
if (Pfn->OriginalPte.u.Long == 0) {
Pfn->OriginalPte.u.Long = MM_KERNEL_DEMAND_ZERO_PTE;
Pfn->OriginalPte.u.Soft.Protection |= MM_EXECUTE;
}
MI_SET_MODIFIED (Pfn, 1, 0x11);
TempPte = *PointerPte;
MI_MAKE_VALID_PTE_TRANSITION (TempPte,
Pfn->OriginalPte.u.Soft.Protection);
KeFlushSingleTb (Base,
TRUE,
TRUE,
(PHARDWARE_PTE)PointerPte,
TempPte.u.Flush);
//
// Flush the translation buffer and decrement the number of valid
// PTEs within the containing page table page. Note that for a
// private page, the page table page is still needed because the
// page is in transition.
//
MiDecrementShareCount (PageFrameIndex);
MmResidentAvailablePages += 1;
MmTotalSystemCodePages += 1;
}
else {
//
// This would need to be taken out of the WSLEs so skip it for
// now and let the normal paging algorithms remove it if we
// run into memory pressure.
//
}
}
Base = (PVOID)((PCHAR)Base + PAGE_SIZE);
PointerPte += 1;
}
if (SessionAddress == TRUE) {
//
// Session space has no ASN - flush the entire TB.
//
MI_FLUSH_ENTIRE_SESSION_TB (TRUE, TRUE);
}
return;
}
MM_SYSTEMSIZE
MmQuerySystemSize(
VOID
)
{
//
// 12Mb is small
// 12-19 is medium
// > 19 is large
//
return MmSystemSize;
}
NTKERNELAPI
BOOLEAN
MmIsThisAnNtAsSystem(
VOID
)
{
return (BOOLEAN)MmProductType;
}
NTKERNELAPI
VOID
FASTCALL
MmSetPageFaultNotifyRoutine(
PPAGE_FAULT_NOTIFY_ROUTINE NotifyRoutine
)
{
MmPageFaultNotifyRoutine = NotifyRoutine;
}
#ifdef _MI_MESSAGE_SERVER
LIST_ENTRY MiMessageInfoListHead;
KSPIN_LOCK MiMessageLock;
KEVENT MiMessageEvent;
ULONG MiMessageCount;
VOID
MiInitializeMessageQueue (
VOID
)
{
MiMessageCount = 0;
InitializeListHead (&MiMessageInfoListHead);
KeInitializeSpinLock (&MiMessageLock);
//
// Use a synchronization event so the event's signal state is
// auto cleared on a successful wait.
//
KeInitializeEvent (&MiMessageEvent, SynchronizationEvent, FALSE);
}
LOGICAL
MiQueueMessage (
IN PVOID Message
)
{
KIRQL OldIrql;
LOGICAL Enqueued;
Enqueued = TRUE;
ExAcquireSpinLock (&MiMessageLock, &OldIrql);
if (MiMessageCount <= 500) {
InsertTailList (&MiMessageInfoListHead, (PLIST_ENTRY)Message);
MiMessageCount += 1;
}
else {
Enqueued = FALSE;
}
ExReleaseSpinLock (&MiMessageLock, OldIrql);
if (Enqueued == TRUE) {
KeSetEvent (&MiMessageEvent, 0, FALSE);
}
else {
ExFreePool (Message);
}
return Enqueued;
}
//
// sr: free the pool when done.
//
PVOID
MiRemoveMessage (
VOID
)
{
PVOID Message;
KIRQL OldIrql;
NTSTATUS Status;
Message = NULL;
//
// N.B. waiting with a timeout and return so caller can support unload.
//
Status = KeWaitForSingleObject (&MiMessageEvent,
WrVirtualMemory,
KernelMode,
FALSE,
(PLARGE_INTEGER) &MmTwentySeconds);
if (Status != STATUS_TIMEOUT) {
ExAcquireSpinLock (&MiMessageLock, &OldIrql);
if (!IsListEmpty (&MiMessageInfoListHead)) {
Message = (PVOID)RemoveHeadList(&MiMessageInfoListHead);
MiMessageCount -= 1;
if (!IsListEmpty (&MiMessageInfoListHead)) {
//
// The list still contains entries so undo the event autoreset.
//
KeSetEvent (&MiMessageEvent, 0, FALSE);
}
}
ExReleaseSpinLock (&MiMessageLock, OldIrql);
}
return Message;
}
#endif
#define CONSTANT_UNICODE_STRING(s) { sizeof( s ) - sizeof( WCHAR ), sizeof( s ), s }
LOGICAL
MiInitializeMemoryEvents (
VOID
)
{
KIRQL OldIrql;
NTSTATUS Status;
UNICODE_STRING LowMem = CONSTANT_UNICODE_STRING(L"\\KernelObjects\\LowMemoryCondition");
UNICODE_STRING HighMem = CONSTANT_UNICODE_STRING(L"\\KernelObjects\\HighMemoryCondition");
//
// The thresholds may be set in the registry, if so, they are interpreted
// in megabytes so convert them to pages now.
//
// If the user modifies the registry to introduce his own values, don't
// bother error checking them as they can't hurt the system regardless (bad
// values just may result in events not getting signaled or staying
// signaled when they shouldn't, but that's not fatal).
//
if (MmLowMemoryThreshold != 0) {
MmLowMemoryThreshold *= ((1024 * 1024) / PAGE_SIZE);
}
else {
//
// Scale the threshold so on servers the low threshold is
// approximately 32MB per 4GB, capping it at 64MB.
//
MmLowMemoryThreshold = MmPlentyFreePages;
if (MmNumberOfPhysicalPages > 0x40000) {
MmLowMemoryThreshold = (32 * 1024 * 1024) / PAGE_SIZE;
MmLowMemoryThreshold += ((MmNumberOfPhysicalPages - 0x40000) >> 7);
}
else if (MmNumberOfPhysicalPages > 0x8000) {
MmLowMemoryThreshold += ((MmNumberOfPhysicalPages - 0x8000) >> 5);
}
if (MmLowMemoryThreshold > (64 * 1024 * 1024) / PAGE_SIZE) {
MmLowMemoryThreshold = (64 * 1024 * 1024) / PAGE_SIZE;
}
}
if (MmHighMemoryThreshold != 0) {
MmHighMemoryThreshold *= ((1024 * 1024) / PAGE_SIZE);
}
else {
MmHighMemoryThreshold = 3 * MmLowMemoryThreshold;
ASSERT (MmHighMemoryThreshold > MmLowMemoryThreshold);
}
if (MmHighMemoryThreshold < MmLowMemoryThreshold) {
MmHighMemoryThreshold = MmLowMemoryThreshold;
}
Status = MiCreateMemoryEvent (&LowMem, &MiLowMemoryEvent);
if (!NT_SUCCESS (Status)) {
#if DBG
DbgPrint ("MM: Memory event initialization failed %x\n", Status);
#endif
return FALSE;
}
Status = MiCreateMemoryEvent (&HighMem, &MiHighMemoryEvent);
if (!NT_SUCCESS (Status)) {
#if DBG
DbgPrint ("MM: Memory event initialization failed %x\n", Status);
#endif
return FALSE;
}
//
// Initialize the event values.
//
LOCK_PFN (OldIrql);
MiNotifyMemoryEvents ();
UNLOCK_PFN (OldIrql);
return TRUE;
}
extern POBJECT_TYPE ExEventObjectType;
NTSTATUS
MiCreateMemoryEvent (
IN PUNICODE_STRING EventName,
OUT PKEVENT *Event
)
{
PACL Dacl;
HANDLE EventHandle;
ULONG DaclLength;
NTSTATUS Status;
OBJECT_ATTRIBUTES ObjectAttributes;
SECURITY_DESCRIPTOR SecurityDescriptor;
Status = RtlCreateSecurityDescriptor (&SecurityDescriptor,
SECURITY_DESCRIPTOR_REVISION);
if (!NT_SUCCESS (Status)) {
return Status;
}
DaclLength = sizeof (ACL) + sizeof (ACCESS_ALLOWED_ACE) * 3 +
RtlLengthSid (SeLocalSystemSid) +
RtlLengthSid (SeAliasAdminsSid) +
RtlLengthSid (SeWorldSid);
Dacl = ExAllocatePoolWithTag (PagedPool, DaclLength, 'lcaD');
if (Dacl == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
Status = RtlCreateAcl (Dacl, DaclLength, ACL_REVISION);
if (!NT_SUCCESS (Status)) {
ExFreePool (Dacl);
return Status;
}
Status = RtlAddAccessAllowedAce (Dacl,
ACL_REVISION,
EVENT_ALL_ACCESS,
SeAliasAdminsSid);
if (!NT_SUCCESS (Status)) {
ExFreePool (Dacl);
return Status;
}
Status = RtlAddAccessAllowedAce (Dacl,
ACL_REVISION,
EVENT_ALL_ACCESS,
SeLocalSystemSid);
if (!NT_SUCCESS (Status)) {
ExFreePool (Dacl);
return Status;
}
Status = RtlAddAccessAllowedAce (Dacl,
ACL_REVISION,
SYNCHRONIZE|EVENT_QUERY_STATE|READ_CONTROL,
SeWorldSid);
if (!NT_SUCCESS (Status)) {
ExFreePool (Dacl);
return Status;
}
Status = RtlSetDaclSecurityDescriptor (&SecurityDescriptor,
TRUE,
Dacl,
FALSE);
if (!NT_SUCCESS (Status)) {
ExFreePool (Dacl);
return Status;
}
InitializeObjectAttributes (&ObjectAttributes,
EventName,
OBJ_KERNEL_HANDLE | OBJ_PERMANENT,
NULL,
&SecurityDescriptor);
Status = ZwCreateEvent (&EventHandle,
EVENT_ALL_ACCESS,
&ObjectAttributes,
NotificationEvent,
FALSE);
ExFreePool (Dacl);
if (NT_SUCCESS (Status)) {
Status = ObReferenceObjectByHandle (EventHandle,
EVENT_MODIFY_STATE,
ExEventObjectType,
KernelMode,
(PVOID *)Event,
NULL);
}
ZwClose (EventHandle);
return Status;
}
VOID
MiNotifyMemoryEvents (
VOID
)
// PFN lock is held.
{
if (MmAvailablePages <= MmLowMemoryThreshold) {
if (KeReadStateEvent (MiHighMemoryEvent) != 0) {
KeClearEvent (MiHighMemoryEvent);
}
if (KeReadStateEvent (MiLowMemoryEvent) == 0) {
KeSetEvent (MiLowMemoryEvent, 0, FALSE);
}
}
else if (MmAvailablePages < MmHighMemoryThreshold) {
//
// Gray zone, make sure both events are cleared.
//
if (KeReadStateEvent (MiHighMemoryEvent) != 0) {
KeClearEvent (MiHighMemoryEvent);
}
if (KeReadStateEvent (MiLowMemoryEvent) != 0) {
KeClearEvent (MiLowMemoryEvent);
}
}
else {
if (KeReadStateEvent (MiHighMemoryEvent) == 0) {
KeSetEvent (MiHighMemoryEvent, 0, FALSE);
}
if (KeReadStateEvent (MiLowMemoryEvent) != 0) {
KeClearEvent (MiLowMemoryEvent);
}
}
return;
}
VOID
MiInitializeCacheOverrides (
VOID
)
{
#if defined (_WIN64)
ULONG NumberOfBytes;
NTSTATUS Status;
HAL_PLATFORM_INFORMATION Information;
//
// Gather platform information from the HAL.
//
Status = HalQuerySystemInformation (HalPlatformInformation,
sizeof (Information),
&Information,
&NumberOfBytes);
if (!NT_SUCCESS (Status)) {
return;
}
//
// Apply mapping modifications based on platform information flags.
//
// It would be better if the platform returned what the new cachetype
// should be.
//
if (Information.PlatformFlags & HAL_PLATFORM_DISABLE_UC_MAIN_MEMORY) {
MI_SET_CACHETYPE_TRANSLATION (MmNonCached, 0, MiCached);
}
#endif
return;
}