|
|
/*++
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; }
|