mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1980 lines
55 KiB
1980 lines
55 KiB
/*++
|
|
|
|
Copyright (c) 1989 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
mminit.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the initialization for the memory management
|
|
system.
|
|
|
|
Author:
|
|
|
|
Lou Perazzoli (loup) 20-Mar-1989
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "mi.h"
|
|
|
|
MMPTE MmSharedUserDataPte;
|
|
|
|
extern ULONG MmPagedPoolCommit;
|
|
|
|
extern ULONG MmHeapSegmentReserve;
|
|
extern ULONG MmHeapSegmentCommit;
|
|
extern ULONG MmHeapDeCommitTotalFreeThreshold;
|
|
extern ULONG MmHeapDeCommitFreeBlockThreshold;
|
|
extern MMINPAGE_SUPPORT_LIST MmInPageSupportList;
|
|
extern MMEVENT_COUNT_LIST MmEventCountList;
|
|
extern KMUTANT MmSystemLoadLock;
|
|
extern ULONG MmSystemPtesStart[MaximumPtePoolTypes];
|
|
|
|
ULONG MmSubsectionBase;
|
|
ULONG MmSubsectionTopPage;
|
|
ULONG MmDataClusterSize;
|
|
ULONG MmCodeClusterSize;
|
|
ULONG MmResidentAvailableAtInit;
|
|
KEVENT MmImageMappingPteEvent;
|
|
PPHYSICAL_MEMORY_DESCRIPTOR MmPhysicalMemoryBlock;
|
|
|
|
#if DBG
|
|
|
|
PRTL_EVENT_ID_INFO MiAllocVmEventId;
|
|
PRTL_EVENT_ID_INFO MiFreeVmEventId;
|
|
|
|
#endif // DBG
|
|
|
|
VOID
|
|
MiEnablePagingTheExecutive(
|
|
VOID
|
|
);
|
|
|
|
VOID
|
|
MiEnablePagingOfDriverAtInit (
|
|
IN PMMPTE PointerPte,
|
|
IN PMMPTE LastPte
|
|
);
|
|
|
|
VOID
|
|
MiBuildPagedPool (
|
|
);
|
|
|
|
VOID
|
|
MiMergeMemoryLimit (
|
|
IN OUT PPHYSICAL_MEMORY_DESCRIPTOR Memory,
|
|
IN ULONG StartPage,
|
|
IN ULONG NoPages
|
|
);
|
|
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(INIT,MmInitSystem)
|
|
#pragma alloc_text(INIT,MmInitializeMemoryLimits)
|
|
#pragma alloc_text(INIT,MiMergeMemoryLimit)
|
|
#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(PAGELK,MiFreeInitializationCode)
|
|
#endif
|
|
|
|
#define MM_MAX_LOADER_BLOCKS 20
|
|
|
|
//
|
|
// The following constants are base on the number PAGES not the
|
|
// memory size. For convience 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 (512 * 1024)
|
|
|
|
extern ULONG MmMaximumWorkingSetSize;
|
|
|
|
ULONG MmSystemPageDirectory;
|
|
|
|
PMMPTE MmSystemPagePtes;
|
|
|
|
ULONG MmTotalSystemCodePages;
|
|
|
|
MM_SYSTEMSIZE MmSystemSize;
|
|
|
|
ULONG MmLargeSystemCache;
|
|
|
|
ULONG MmProductType;
|
|
|
|
LIST_ENTRY MmLoadedUserImageList;
|
|
|
|
BOOLEAN
|
|
MmInitSystem (
|
|
IN ULONG Phase,
|
|
IN PLOADER_PARAMETER_BLOCK LoaderBlock,
|
|
IN PPHYSICAL_MEMORY_DESCRIPTOR PhysicalMemoryBlock
|
|
)
|
|
|
|
/*++
|
|
|
|
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.
|
|
|
|
LoadBlock - Supplies a pointer the ssystem loader block.
|
|
|
|
Return Value:
|
|
|
|
Returns TRUE if the initialization was successful.
|
|
|
|
Environment:
|
|
|
|
Kernel Mode Only. System initialization.
|
|
|
|
--*/
|
|
|
|
{
|
|
HANDLE ThreadHandle;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
PMMPTE PointerPte;
|
|
PMMPTE PointerPde;
|
|
PMMPTE StartPde;
|
|
PMMPTE StartingPte;
|
|
PMMPTE EndPde;
|
|
PMMPFN Pfn1;
|
|
ULONG i, j;
|
|
ULONG PageFrameIndex;
|
|
MMPTE TempPte;
|
|
KIRQL OldIrql;
|
|
|
|
BOOLEAN IncludeType[LoaderMaximum];
|
|
ULONG MemoryAlloc[(sizeof(PHYSICAL_MEMORY_DESCRIPTOR) +
|
|
sizeof(PHYSICAL_MEMORY_RUN)*MAX_PHYSICAL_MEMORY_FRAGMENTS) /
|
|
sizeof(ULONG)];
|
|
PPHYSICAL_MEMORY_DESCRIPTOR Memory;
|
|
|
|
//
|
|
// Make sure structure alignment is okay.
|
|
//
|
|
|
|
if (Phase == 0) {
|
|
MmThrottleTop = 450;
|
|
MmThrottleBottom = 127;
|
|
|
|
#if DBG
|
|
|
|
//
|
|
// A few sanity checks to ensure things are as they should be.
|
|
//
|
|
|
|
if (sizeof(MMPFN) != 24) {
|
|
DbgPrint("pfn element size is not 24\n");
|
|
}
|
|
|
|
if ((sizeof(MMWSL) % 8) != 0) {
|
|
DbgPrint("working set list is not a quadword sized structure\n");
|
|
}
|
|
|
|
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;
|
|
i = MiProtoAddressForPte (PointerPte);
|
|
TempPte.u.Long = i;
|
|
PointerPde = MiPteToProto(&TempPte);
|
|
if (PointerPte != PointerPde) {
|
|
DbgPrint("unable to map start of paged pool as prototype pte %lx %lx\n",
|
|
PointerPde, PointerPte);
|
|
}
|
|
|
|
PointerPte =
|
|
(PMMPTE)((ULONG)MM_NONPAGED_POOL_END & ~((1 << PTE_SHIFT) - 1));
|
|
i = MiProtoAddressForPte (PointerPte);
|
|
TempPte.u.Long = i;
|
|
PointerPde = MiPteToProto(&TempPte);
|
|
if (PointerPte != PointerPde) {
|
|
DbgPrint("unable to map end of nonpaged pool as prototype pte %lx %lx\n",
|
|
PointerPde, PointerPte);
|
|
}
|
|
|
|
PointerPte = (PMMPTE)(((ULONG)NON_PAGED_SYSTEM_END - 0x37000 + PAGE_SIZE-1) & ~(PAGE_SIZE-1));
|
|
|
|
for (j = 0; j < 20; j++) {
|
|
i = MiProtoAddressForPte (PointerPte);
|
|
TempPte.u.Long = i;
|
|
PointerPde = MiPteToProto(&TempPte);
|
|
if (PointerPte != PointerPde) {
|
|
DbgPrint("unable to map end of nonpaged pool as prototype pte %lx %lx\n",
|
|
PointerPde, PointerPte);
|
|
}
|
|
PointerPte++;
|
|
|
|
}
|
|
|
|
PointerPte = (PMMPTE)(((ULONG)MM_NONPAGED_POOL_END - 0x133448) & ~7);
|
|
i = MiGetSubsectionAddressForPte (PointerPte);
|
|
TempPte.u.Long = i;
|
|
PointerPde = (PMMPTE)MiGetSubsectionAddress(&TempPte);
|
|
if (PointerPte != PointerPde) {
|
|
DbgPrint("unable to map end of nonpaged pool as section pte %lx %lx\n",
|
|
PointerPde, PointerPte);
|
|
|
|
MiFormatPte(&TempPte);
|
|
}
|
|
|
|
//
|
|
// End of sanity checks.
|
|
//
|
|
#endif //dbg
|
|
|
|
InitializeListHead( &MmLoadedUserImageList );
|
|
|
|
MmCriticalSectionTimeout.QuadPart = Int32x32To64(
|
|
MmCritsectTimeoutSeconds,
|
|
-10000000);
|
|
|
|
|
|
//
|
|
// Initialize PFN database mutex and System Address Space creation
|
|
// mutex.
|
|
//
|
|
|
|
MmNumberOfColors = MM_MAXIMUM_NUMBER_OF_COLORS;
|
|
|
|
|
|
ExInitializeFastMutex (&MmSectionCommitMutex);
|
|
ExInitializeFastMutex (&MmSectionBasedMutex);
|
|
|
|
KeInitializeMutant (&MmSystemLoadLock, FALSE);
|
|
|
|
KeInitializeEvent (&MmAvailablePagesEvent, NotificationEvent, TRUE);
|
|
KeInitializeEvent (&MmAvailablePagesEventHigh, NotificationEvent, TRUE);
|
|
KeInitializeEvent (&MmMappedFileIoComplete, NotificationEvent, FALSE);
|
|
KeInitializeEvent (&MmImageMappingPteEvent, NotificationEvent, FALSE);
|
|
KeInitializeEvent (&MmZeroingPageEvent, SynchronizationEvent, FALSE);
|
|
KeInitializeEvent (&MmCollidedFlushEvent, NotificationEvent, FALSE);
|
|
KeInitializeEvent (&MmCollidedLockEvent, NotificationEvent, FALSE);
|
|
|
|
InitializeListHead (&MmWorkingSetExpansionHead.ListHead);
|
|
InitializeListHead (&MmInPageSupportList.ListHead);
|
|
InitializeListHead (&MmEventCountList.ListHead);
|
|
|
|
MmZeroingPageThreadActive = FALSE;
|
|
|
|
//
|
|
// Compute pyhiscal memory block a yet again
|
|
//
|
|
|
|
Memory = (PPHYSICAL_MEMORY_DESCRIPTOR)&MemoryAlloc;
|
|
Memory->NumberOfRuns = MAX_PHYSICAL_MEMORY_FRAGMENTS;
|
|
|
|
// include all memory types ...
|
|
for (i=0; i < LoaderMaximum; i++) {
|
|
IncludeType[i] = TRUE;
|
|
}
|
|
|
|
// ... expect these..
|
|
IncludeType[LoaderBad] = FALSE;
|
|
IncludeType[LoaderFirmwarePermanent] = FALSE;
|
|
IncludeType[LoaderSpecialMemory] = FALSE;
|
|
|
|
MmInitializeMemoryLimits(LoaderBlock, IncludeType, Memory);
|
|
|
|
//
|
|
// Add all memory runs in PhysicalMemoryBlock to Memory
|
|
//
|
|
|
|
for (i=0; i < PhysicalMemoryBlock->NumberOfRuns; i++) {
|
|
MiMergeMemoryLimit (
|
|
Memory,
|
|
PhysicalMemoryBlock->Run[i].BasePage,
|
|
PhysicalMemoryBlock->Run[i].PageCount
|
|
);
|
|
}
|
|
#ifdef MIPS
|
|
|
|
//
|
|
// On mips machines these first two pages of physical memory are
|
|
// used for important stuff.
|
|
//
|
|
|
|
Memory->Run[Memory->NumberOfRuns].BasePage = 0;
|
|
Memory->Run[Memory->NumberOfRuns].PageCount = 2;
|
|
Memory->NumberOfRuns += 1;
|
|
Memory->NumberOfPages += 2;
|
|
#endif //MIPS
|
|
//
|
|
// Sort and merge adjacent runs
|
|
//
|
|
|
|
for (i=0; i < Memory->NumberOfRuns; i++) {
|
|
for (j=i+1; j < Memory->NumberOfRuns; j++) {
|
|
if (Memory->Run[j].BasePage < Memory->Run[i].BasePage) {
|
|
// swap runs
|
|
PhysicalMemoryBlock->Run[0] = Memory->Run[j];
|
|
Memory->Run[j] = Memory->Run[i];
|
|
Memory->Run[i] = PhysicalMemoryBlock->Run[0];
|
|
}
|
|
|
|
if (Memory->Run[i].BasePage + Memory->Run[i].PageCount ==
|
|
Memory->Run[j].BasePage) {
|
|
// merge runs
|
|
Memory->NumberOfRuns -= 1;
|
|
Memory->Run[i].PageCount += Memory->Run[j].PageCount;
|
|
Memory->Run[j] = Memory->Run[Memory->NumberOfRuns];
|
|
i -= 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (MmNumberOfSystemPtes == 0) {
|
|
if (Memory->NumberOfPages < MM_MEDIUM_SYSTEM) {
|
|
MmNumberOfSystemPtes = MM_MINIMUM_SYSTEM_PTES;
|
|
} else {
|
|
MmNumberOfSystemPtes = MM_DEFAULT_SYSTEM_PTES;
|
|
if (Memory->NumberOfPages > 8192) {
|
|
MmNumberOfSystemPtes += MmNumberOfSystemPtes;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (MmNumberOfSystemPtes > MM_MAXIMUM_SYSTEM_PTES) {
|
|
MmNumberOfSystemPtes = MM_MAXIMUM_SYSTEM_PTES;
|
|
}
|
|
|
|
if (MmNumberOfSystemPtes < MM_MINIMUM_SYSTEM_PTES) {
|
|
MmNumberOfSystemPtes = MM_MINIMUM_SYSTEM_PTES;
|
|
}
|
|
|
|
if ( !MmHeapSegmentReserve ) {
|
|
MmHeapSegmentReserve = 1024 * 1024;
|
|
}
|
|
|
|
if ( !MmHeapSegmentCommit ) {
|
|
MmHeapSegmentCommit = PAGE_SIZE * 2;
|
|
}
|
|
|
|
if ( !MmHeapDeCommitTotalFreeThreshold ) {
|
|
MmHeapDeCommitTotalFreeThreshold = 64 * 1024;
|
|
}
|
|
|
|
if ( !MmHeapDeCommitFreeBlockThreshold ) {
|
|
MmHeapDeCommitFreeBlockThreshold = PAGE_SIZE;
|
|
}
|
|
|
|
#if DBG
|
|
if (MmSpecialPoolTag != 0) {
|
|
MmNumberOfSystemPtes += 25000;
|
|
}
|
|
#endif //DBG
|
|
|
|
//
|
|
// Initialize the machine dependent portion of the hardware.
|
|
//
|
|
|
|
ExInitializeResource (&MmSystemWsLock);
|
|
|
|
MiInitMachineDependent (LoaderBlock);
|
|
|
|
j = (sizeof(PHYSICAL_MEMORY_DESCRIPTOR) +
|
|
(sizeof(PHYSICAL_MEMORY_RUN) *
|
|
(Memory->NumberOfRuns - 1)));
|
|
|
|
MmPhysicalMemoryBlock = ExAllocatePoolWithTag (NonPagedPoolMustSucceed,
|
|
j,
|
|
' mM');
|
|
|
|
RtlCopyMemory (MmPhysicalMemoryBlock, Memory, j);
|
|
|
|
//
|
|
// 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
|
|
//
|
|
|
|
MmReadClusterSize = 7;
|
|
if (MmNumberOfPhysicalPages <= MM_SMALL_SYSTEM ) {
|
|
MmSystemSize = MmSmallSystem;
|
|
MmMaximumDeadKernelStacks = 0;
|
|
MmModifiedPageMinimum = 40;
|
|
MmModifiedPageMaximum = 100;
|
|
MmDataClusterSize = 0;
|
|
MmCodeClusterSize = 1;
|
|
MmReadClusterSize = 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;
|
|
} else {
|
|
MmSystemSize = MmMediumSystem;
|
|
MmMaximumDeadKernelStacks = 5;
|
|
MmModifiedPageMinimum = 150;
|
|
MmModifiedPageMaximum = 300;
|
|
MmSystemCacheWsMinimum += 400;
|
|
MmSystemCacheWsMaximum += 800;
|
|
MmDataClusterSize = 3;
|
|
MmCodeClusterSize = 7;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
MiAdjustWorkingSetManagerParameters((BOOLEAN)(MmProductType == 0 ? TRUE : FALSE));
|
|
|
|
//
|
|
// Set the ResidentAvailablePages to the number of available
|
|
// pages minum the fluid value.
|
|
//
|
|
|
|
MmResidentAvailablePages = MmAvailablePages - MM_FLUID_PHYSICAL_PAGES;
|
|
|
|
//
|
|
// Subtract off the size of the system cache working set.
|
|
//
|
|
|
|
MmResidentAvailablePages -= MmSystemCacheWsMinimum;
|
|
MmResidentAvailableAtInit = MmResidentAvailablePages;
|
|
|
|
|
|
if ((LONG)MmResidentAvailablePages < 0) {
|
|
#if DBG
|
|
DbgPrint("system cache working set too big\n");
|
|
#endif
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Initialize spin lock for charging and releasing page file
|
|
// commitment.
|
|
//
|
|
|
|
KeInitializeSpinLock (&MmChargeCommitmentLock);
|
|
|
|
//
|
|
// Initialize spin lock for allowing working set expansion.
|
|
//
|
|
|
|
KeInitializeSpinLock (&MmExpansionLock);
|
|
|
|
ExInitializeFastMutex (&MmPageFileCreationLock);
|
|
|
|
//
|
|
// Initialize resource for extending sections.
|
|
//
|
|
|
|
ExInitializeResource (&MmSectionExtendResource);
|
|
ExInitializeResource (&MmSectionExtendSetResource);
|
|
|
|
//
|
|
// Build the system cache structures.
|
|
//
|
|
|
|
StartPde = MiGetPdeAddress (MmSystemCacheWorkingSetList);
|
|
PointerPte = MiGetPteAddress (MmSystemCacheWorkingSetList);
|
|
|
|
ASSERT ((StartPde + 1) == MiGetPdeAddress (MmSystemCacheStart));
|
|
|
|
//
|
|
// 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 = ((128*1024*1024) >> PAGE_SHIFT) +
|
|
((i - 4) * ((64*1024*1024) >> PAGE_SHIFT));
|
|
if (MmSizeOfSystemCacheInPages > MM_MAXIMUM_SYSTEM_CACHE_SIZE) {
|
|
MmSizeOfSystemCacheInPages = MM_MAXIMUM_SYSTEM_CACHE_SIZE;
|
|
}
|
|
}
|
|
|
|
MmSystemCacheEnd = (PVOID)(((ULONG)MmSystemCacheStart +
|
|
MmSizeOfSystemCacheInPages * PAGE_SIZE) - 1);
|
|
|
|
EndPde = MiGetPdeAddress(MmSystemCacheEnd);
|
|
|
|
TempPte = ValidKernelPte;
|
|
|
|
LOCK_PFN (OldIrql);
|
|
while (StartPde <= EndPde) {
|
|
ASSERT (StartPde->u.Hard.Valid == 0);
|
|
|
|
//
|
|
// Map in a page directory page.
|
|
//
|
|
|
|
PageFrameIndex = MiRemoveAnyPage(
|
|
MI_GET_PAGE_COLOR_FROM_PTE (StartPde));
|
|
TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
|
|
*StartPde = TempPte;
|
|
|
|
Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
|
|
Pfn1->PteFrame =
|
|
MiGetPdeAddress(PDE_BASE)->u.Hard.PageFrameNumber;
|
|
Pfn1->PteAddress = StartPde;
|
|
Pfn1->u2.ShareCount += 1;
|
|
Pfn1->u3.e2.ReferenceCount = 1;
|
|
Pfn1->u3.e1.PageLocation = ActiveAndValid;
|
|
Pfn1->OriginalPte.u.Long = 0;
|
|
|
|
RtlFillMemoryUlong (PointerPte,
|
|
PAGE_SIZE,
|
|
ZeroKernelPte.u.Long);
|
|
|
|
StartPde += 1;
|
|
PointerPte += PTE_PER_PAGE;
|
|
}
|
|
|
|
UNLOCK_PFN (OldIrql);
|
|
|
|
//
|
|
// Initialize the system cache.
|
|
//
|
|
|
|
if (MmLargeSystemCache != 0) {
|
|
if ((MmAvailablePages >
|
|
MmSystemCacheWsMaximum + ((6*1024*1024) >> PAGE_SHIFT))) {
|
|
MmSystemCacheWsMaximum =
|
|
MmAvailablePages - ((4*1024*1024) >> PAGE_SHIFT);
|
|
MmMoreThanEnoughFreePages = 256;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
if (!MiInitializeSystemCache (MmSizeOfSystemCacheInPages,
|
|
MmSystemCacheWsMinimum,
|
|
MmSystemCacheWsMaximum
|
|
)) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Set the commit page limit to four times the number of available
|
|
// pages. This value is updated as paging files are created.
|
|
//
|
|
|
|
MmTotalCommitLimit = MmAvailablePages << 2;
|
|
|
|
MmAttemptForCantExtend.Segment = NULL;
|
|
MmAttemptForCantExtend.RequestedExpansionSize = 1;
|
|
MmAttemptForCantExtend.ActualExpansion = 1;
|
|
MmAttemptForCantExtend.InProgress = FALSE;
|
|
|
|
KeInitializeEvent (&MmAttemptForCantExtend.Event,
|
|
NotificationEvent,
|
|
FALSE);
|
|
|
|
if (MmOverCommit == 0) {
|
|
|
|
// If this value was not set via the regisistry, set the
|
|
// over commit value to the number of available pages
|
|
// minus 1024 pages (4mb with 4k pages).
|
|
//
|
|
|
|
if (MmAvailablePages > 1024) {
|
|
MmOverCommit = MmAvailablePages - 1024;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set maximum working set size to 512 pages less total available
|
|
// memory. 2mb on machine with 4k pages.
|
|
//
|
|
|
|
MmMaximumWorkingSetSize = 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 ();
|
|
|
|
//
|
|
// Add more system PTEs if large memory system.
|
|
//
|
|
|
|
if (MmNumberOfPhysicalPages > ((128*1024*1024) >> PAGE_SHIFT)) {
|
|
|
|
|
|
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);
|
|
PageFrameIndex = MiRemoveAnyPage (
|
|
MI_GET_PAGE_COLOR_FROM_PTE (PointerPde));
|
|
TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
|
|
*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);
|
|
MmSystemPtesStart[SystemPteSpace] = (ULONG)StartingPte;
|
|
MmNonPagedSystemStart = MiGetVirtualAddressMappedByPte (StartingPte);
|
|
MmNumberOfSystemPtes += j;
|
|
MiReleaseSystemPtes (StartingPte, j, SystemPteSpace);
|
|
}
|
|
}
|
|
|
|
|
|
#if DBG
|
|
if (MmDebug & MM_DBG_DUMP_BOOT_PTES) {
|
|
MiDumpValidAddresses ();
|
|
MiDumpPfn ();
|
|
}
|
|
#endif
|
|
|
|
#if DBG
|
|
MiAllocVmEventId = RtlCreateEventId( NULL,
|
|
0,
|
|
"AllocVM",
|
|
5,
|
|
RTL_EVENT_ULONG_PARAM, "Addr", 0,
|
|
RTL_EVENT_ULONG_PARAM, "Size", 0,
|
|
RTL_EVENT_FLAGS_PARAM, "", 3,
|
|
MEM_RESERVE, "Reserve",
|
|
MEM_COMMIT, "Commit",
|
|
MEM_TOP_DOWN, "TopDown",
|
|
RTL_EVENT_ENUM_PARAM, "", 8,
|
|
PAGE_NOACCESS, "NoAccess",
|
|
PAGE_READONLY, "ReadOnly",
|
|
PAGE_READWRITE, "ReadWrite",
|
|
PAGE_WRITECOPY, "CopyOnWrite",
|
|
PAGE_EXECUTE, "Execute",
|
|
PAGE_EXECUTE_READ, "ExecuteRead",
|
|
PAGE_EXECUTE_READWRITE, "ExecuteReadWrite",
|
|
PAGE_EXECUTE_WRITECOPY, "ExecuteCopyOnWrite",
|
|
RTL_EVENT_FLAGS_PARAM, "", 2,
|
|
PAGE_GUARD, "Guard",
|
|
PAGE_NOCACHE, "NoCache"
|
|
);
|
|
MiFreeVmEventId = RtlCreateEventId( NULL,
|
|
0,
|
|
"FreeVM",
|
|
3,
|
|
RTL_EVENT_ULONG_PARAM, "Addr", 0,
|
|
RTL_EVENT_ULONG_PARAM, "Size", 0,
|
|
RTL_EVENT_FLAGS_PARAM, "", 2,
|
|
MEM_RELEASE, "Release",
|
|
MEM_DECOMMIT, "DeCommit"
|
|
);
|
|
#endif // DBG
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
if (Phase == 1) {
|
|
|
|
#if DBG
|
|
MmDebug |= MM_DBG_CHECK_PFN_LOCK;
|
|
#endif
|
|
|
|
#ifdef _X86_
|
|
MiInitMachineDependent (LoaderBlock);
|
|
#endif
|
|
|
|
if (!MiSectionInitialization ()) {
|
|
return FALSE;
|
|
}
|
|
|
|
#if defined(MM_SHARED_USER_DATA_VA)
|
|
|
|
//
|
|
// Create double mapped page between kernel and user mode.
|
|
//
|
|
|
|
PointerPte = MiGetPteAddress(KI_USER_SHARED_DATA);
|
|
ASSERT (PointerPte->u.Hard.Valid == 1);
|
|
PageFrameIndex = PointerPte->u.Hard.PageFrameNumber;
|
|
|
|
MI_MAKE_VALID_PTE (MmSharedUserDataPte,
|
|
PageFrameIndex,
|
|
MM_READONLY,
|
|
PointerPte);
|
|
Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
|
|
Pfn1->OriginalPte.u.Long = MM_DEMAND_ZERO_WRITE_PTE;
|
|
#endif
|
|
|
|
//
|
|
// Set up system wide lock pages limit.
|
|
//
|
|
|
|
MmLockPagesLimit = MmLockLimitInBytes >> PAGE_SHIFT;
|
|
if (MmLockPagesLimit < MM_DEFAULT_IO_LOCK_LIMIT) {
|
|
MmLockPagesLimit = MM_DEFAULT_IO_LOCK_LIMIT;
|
|
}
|
|
|
|
if ((MmLockPagesLimit + ((7 * 1024*1024) / PAGE_SIZE)) > MmAvailablePages) {
|
|
MmLockPagesLimit = MmAvailablePages - ((7 * 1024*1024) / PAGE_SIZE);
|
|
if ((LONG)MmLockPagesLimit < (MM_DEFAULT_IO_LOCK_LIMIT / PAGE_SIZE)) {
|
|
MmLockPagesLimit = MM_DEFAULT_IO_LOCK_LIMIT / PAGE_SIZE;
|
|
}
|
|
}
|
|
|
|
MmPagingFileCreated = ExAllocatePoolWithTag (NonPagedPool,
|
|
sizeof(KEVENT),
|
|
'fPmM');
|
|
|
|
if (MmPagingFileCreated == NULL) {
|
|
|
|
//
|
|
// Pool allocation failed, return FALSE.
|
|
//
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
KeInitializeEvent (MmPagingFileCreated, NotificationEvent, FALSE);
|
|
|
|
//
|
|
// 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);
|
|
|
|
//
|
|
// 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);
|
|
|
|
MiEnablePagingTheExecutive();
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
VOID
|
|
MmInitializeMemoryLimits (
|
|
IN PLOADER_PARAMETER_BLOCK LoaderBlock,
|
|
IN PBOOLEAN IncludeType,
|
|
OUT PPHYSICAL_MEMORY_DESCRIPTOR Memory
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function walks through the loader block's memory
|
|
description list and builds a list of contiguous physical
|
|
memory blocks of the desired types.
|
|
|
|
Arguments:
|
|
|
|
LoadBlock - Supplies a pointer the ssystem loader block.
|
|
|
|
IncludeType - Array of BOOLEANS size LoaderMaximum.
|
|
TRUE means include this type of memory in return.
|
|
|
|
Memory - Returns the physical memory blocks.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
Environment:
|
|
|
|
Kernel Mode Only. System initialization.
|
|
|
|
--*/
|
|
{
|
|
|
|
PMEMORY_ALLOCATION_DESCRIPTOR MemoryDescriptor;
|
|
PLIST_ENTRY NextMd;
|
|
ULONG i;
|
|
ULONG LowestFound;
|
|
ULONG Found;
|
|
ULONG Merged;
|
|
ULONG NextPage;
|
|
ULONG TotalPages = 0;
|
|
|
|
//
|
|
// Walk through the memory descriptors and build physical memory list.
|
|
//
|
|
|
|
LowestFound = 0;
|
|
Memory->Run[0].BasePage = 0xffffffff;
|
|
NextPage = 0xffffffff;
|
|
Memory->Run[0].PageCount = 0;
|
|
i = 0;
|
|
|
|
do {
|
|
Merged = FALSE;
|
|
Found = FALSE;
|
|
NextMd = LoaderBlock->MemoryDescriptorListHead.Flink;
|
|
|
|
while (NextMd != &LoaderBlock->MemoryDescriptorListHead) {
|
|
|
|
MemoryDescriptor = CONTAINING_RECORD(NextMd,
|
|
MEMORY_ALLOCATION_DESCRIPTOR,
|
|
ListEntry);
|
|
|
|
if (MemoryDescriptor->MemoryType < LoaderMaximum &&
|
|
IncludeType [MemoryDescriptor->MemoryType] ) {
|
|
|
|
//
|
|
// Try to merge runs.
|
|
//
|
|
|
|
if (MemoryDescriptor->BasePage == NextPage) {
|
|
ASSERT (MemoryDescriptor->PageCount != 0);
|
|
Memory->Run[i - 1].PageCount += MemoryDescriptor->PageCount;
|
|
NextPage += MemoryDescriptor->PageCount;
|
|
TotalPages += MemoryDescriptor->PageCount;
|
|
Merged = TRUE;
|
|
Found = TRUE;
|
|
break;
|
|
}
|
|
|
|
if (MemoryDescriptor->BasePage >= LowestFound) {
|
|
if (Memory->Run[i].BasePage > MemoryDescriptor->BasePage) {
|
|
Memory->Run[i].BasePage = MemoryDescriptor->BasePage;
|
|
Memory->Run[i].PageCount = MemoryDescriptor->PageCount;
|
|
}
|
|
Found = TRUE;
|
|
}
|
|
}
|
|
NextMd = MemoryDescriptor->ListEntry.Flink;
|
|
}
|
|
|
|
if (!Merged && Found) {
|
|
NextPage = Memory->Run[i].BasePage + Memory->Run[i].PageCount;
|
|
TotalPages += Memory->Run[i].PageCount;
|
|
i += 1;
|
|
}
|
|
Memory->Run[i].BasePage = 0xffffffff;
|
|
LowestFound = NextPage;
|
|
|
|
} while (Found);
|
|
ASSERT (i <= Memory->NumberOfRuns);
|
|
Memory->NumberOfRuns = i;
|
|
Memory->NumberOfPages = TotalPages;
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
MiMergeMemoryLimit (
|
|
IN OUT PPHYSICAL_MEMORY_DESCRIPTOR Memory,
|
|
IN ULONG StartPage,
|
|
IN ULONG NoPages
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function ensures the passed range is in the passed in Memory
|
|
block adding any new data as needed.
|
|
|
|
The passed memory block is assumed to be at least
|
|
MAX_PHYSICAL_MEMORY_FRAGMENTS large
|
|
|
|
Arguments:
|
|
|
|
Memory - Memory block to verify run is present in
|
|
|
|
StartPage - First page of run
|
|
|
|
NoPages - Number of pages in run
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
Environment:
|
|
|
|
Kernel Mode Only. System initialization.
|
|
|
|
--*/
|
|
{
|
|
ULONG EndPage, sp, ep, i;
|
|
|
|
|
|
EndPage = StartPage + NoPages;
|
|
|
|
//
|
|
// Clip range to area which is not already described
|
|
//
|
|
|
|
for (i=0; i < Memory->NumberOfRuns; i++) {
|
|
sp = Memory->Run[i].BasePage;
|
|
ep = sp + Memory->Run[i].PageCount;
|
|
|
|
if (sp < StartPage) {
|
|
if (ep > StartPage && ep < EndPage) {
|
|
// bump begining page of the target area
|
|
StartPage = ep;
|
|
}
|
|
|
|
if (ep > EndPage) {
|
|
//
|
|
// Target area is contained totally within this
|
|
// descriptor. This range is fully accounted for.
|
|
//
|
|
|
|
StartPage = EndPage;
|
|
}
|
|
|
|
} else {
|
|
// sp >= StartPage
|
|
|
|
if (sp < EndPage) {
|
|
if (ep < EndPage) {
|
|
//
|
|
// This descriptor is totally within the target area -
|
|
// check the area on either side of this desctipor
|
|
//
|
|
|
|
MiMergeMemoryLimit (Memory, StartPage, sp - StartPage);
|
|
StartPage = ep;
|
|
|
|
} else {
|
|
// clip the ending page of the target area
|
|
EndPage = sp;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Anything left of target area?
|
|
//
|
|
|
|
if (StartPage == EndPage) {
|
|
return ;
|
|
}
|
|
} // next descrtiptor
|
|
|
|
//
|
|
// The range StartPage - EndPage is a missing. Add it.
|
|
//
|
|
|
|
if (Memory->NumberOfRuns == MAX_PHYSICAL_MEMORY_FRAGMENTS) {
|
|
return ;
|
|
}
|
|
|
|
Memory->Run[Memory->NumberOfRuns].BasePage = StartPage;
|
|
Memory->Run[Memory->NumberOfRuns].PageCount = EndPage - StartPage;
|
|
Memory->NumberOfPages += EndPage - StartPage;
|
|
Memory->NumberOfRuns += 1;
|
|
}
|
|
|
|
|
|
|
|
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:
|
|
|
|
LoadBlock - Supplies a pointer the ssystem loader block.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
Environment:
|
|
|
|
Kernel Mode Only. System initialization.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PLIST_ENTRY NextMd;
|
|
PMEMORY_ALLOCATION_DESCRIPTOR MemoryDescriptor;
|
|
MEMORY_ALLOCATION_DESCRIPTOR SavedDescriptor[MM_MAX_LOADER_BLOCKS];
|
|
ULONG i;
|
|
ULONG NextPhysicalPage;
|
|
PMMPFN Pfn1;
|
|
LONG BlockNumber = -1;
|
|
KIRQL OldIrql;
|
|
|
|
//
|
|
//
|
|
// 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.
|
|
//
|
|
|
|
BlockNumber += 1;
|
|
if (BlockNumber >= MM_MAX_LOADER_BLOCKS) {
|
|
KeBugCheck (MEMORY_MANAGEMENT);
|
|
}
|
|
|
|
SavedDescriptor[BlockNumber] = *MemoryDescriptor;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
}
|
|
|
|
NextMd = MemoryDescriptor->ListEntry.Flink;
|
|
}
|
|
|
|
LOCK_PFN (OldIrql);
|
|
|
|
while (BlockNumber >= 0) {
|
|
|
|
i = SavedDescriptor[BlockNumber].PageCount;
|
|
NextPhysicalPage = SavedDescriptor[BlockNumber].BasePage;
|
|
|
|
Pfn1 = MI_PFN_ELEMENT (NextPhysicalPage);
|
|
while (i != 0) {
|
|
|
|
if (Pfn1->u3.e2.ReferenceCount == 0) {
|
|
if (Pfn1->u1.Flink == 0) {
|
|
|
|
//
|
|
// Set the PTE address to the phyiscal page for
|
|
// virtual address alignment checking.
|
|
//
|
|
|
|
Pfn1->PteAddress =
|
|
(PMMPTE)(NextPhysicalPage << PTE_SHIFT);
|
|
MiInsertPageInList (MmPageLocationList[FreePageList],
|
|
NextPhysicalPage);
|
|
}
|
|
} else {
|
|
|
|
if (NextPhysicalPage != 0) {
|
|
//
|
|
// Remove PTE and insert into the free list. If it is
|
|
// a phyical 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++;
|
|
i -= 1;
|
|
NextPhysicalPage += 1;
|
|
}
|
|
BlockNumber -= 1;
|
|
}
|
|
|
|
KeFlushEntireTb (TRUE, TRUE);
|
|
UNLOCK_PFN (OldIrql);
|
|
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.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG Size;
|
|
PMMPTE PointerPte;
|
|
PMMPTE PointerPde;
|
|
MMPTE TempPte;
|
|
PMMPFN Pfn1;
|
|
ULONG PageFrameIndex;
|
|
KIRQL OldIrql;
|
|
|
|
//
|
|
// Double map system page directory page.
|
|
//
|
|
|
|
MmSystemPageDirectory = MiGetPdeAddress(PDE_BASE)->u.Hard.PageFrameNumber;
|
|
|
|
MmSystemPagePtes = (PMMPTE)MiMapPageInHyperSpace (MmSystemPageDirectory,
|
|
&OldIrql);
|
|
MiUnmapPageInHyperSpace (OldIrql);
|
|
|
|
if (!MI_IS_PHYSICAL_ADDRESS(MmSystemPagePtes)) {
|
|
|
|
//
|
|
// Was not mapped physically, map it virtually in system space.
|
|
//
|
|
|
|
PointerPte = MiReserveSystemPtes (
|
|
1,
|
|
SystemPteSpace,
|
|
MM_COLOR_ALIGNMENT,
|
|
((ULONG)PDE_BASE & MM_COLOR_MASK_VIRTUAL),
|
|
TRUE);
|
|
*PointerPte = ValidKernelPte;
|
|
PointerPte->u.Hard.PageFrameNumber = MmSystemPageDirectory;
|
|
MmSystemPagePtes = (PMMPTE)MiGetVirtualAddressMappedByPte (PointerPte);
|
|
}
|
|
|
|
//
|
|
// Allocate the prototype PTEs for paged pool.
|
|
//
|
|
|
|
//
|
|
// A size of 0 means size the pool based on physical memory.
|
|
//
|
|
|
|
if (MmSizeOfPagedPoolInBytes == 0) {
|
|
MmSizeOfPagedPoolInBytes = 2 * MmMaximumNonPagedPoolInBytes;
|
|
}
|
|
|
|
if (MmIsThisAnNtAsSystem()) {
|
|
if (MmSizeOfPagedPoolInBytes < MM_MINIMUM_PAGED_POOL_NTAS) {
|
|
MmSizeOfPagedPoolInBytes = MM_MINIMUM_PAGED_POOL_NTAS;
|
|
}
|
|
}
|
|
|
|
if (MmSizeOfPagedPoolInBytes >
|
|
(ULONG)((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;
|
|
}
|
|
|
|
Size = (Size + (PTE_PER_PAGE - 1)) / PTE_PER_PAGE;
|
|
MmSizeOfPagedPoolInBytes = Size * PAGE_SIZE * PTE_PER_PAGE;
|
|
|
|
ASSERT ((MmSizeOfPagedPoolInBytes + (PCHAR)MmPagedPoolStart) <=
|
|
(PCHAR)MmNonPagedSystemStart);
|
|
|
|
//
|
|
// Set size to the number of pages in the pool.
|
|
//
|
|
|
|
Size = Size * PTE_PER_PAGE;
|
|
|
|
MmPagedPoolEnd = (PVOID)(((PUCHAR)MmPagedPoolStart +
|
|
MmSizeOfPagedPoolInBytes) - 1);
|
|
|
|
MmPageAlignedPoolBase[PagedPool] = MmPagedPoolStart;
|
|
|
|
//
|
|
// Build page table page for paged pool.
|
|
//
|
|
|
|
PointerPde = MiGetPdeAddress (MmPagedPoolStart);
|
|
MmPagedPoolBasePde = PointerPde;
|
|
|
|
PointerPte = MiGetPteAddress (MmPagedPoolStart);
|
|
MmFirstPteForPagedPool = PointerPte;
|
|
MmLastPteForPagedPool = MiGetPteAddress (MmPagedPoolEnd);
|
|
|
|
RtlFillMemoryUlong (PointerPde,
|
|
sizeof(MMPTE) *
|
|
(1 + MiGetPdeAddress (MmPagedPoolEnd) - PointerPde),
|
|
MM_KERNEL_NOACCESS_PTE);
|
|
|
|
TempPte = ValidKernelPde;
|
|
|
|
LOCK_PFN (OldIrql);
|
|
|
|
//
|
|
// Map in a page table page.
|
|
//
|
|
|
|
PageFrameIndex = MiRemoveAnyPage(
|
|
MI_GET_PAGE_COLOR_FROM_PTE (PointerPde));
|
|
TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
|
|
*PointerPde = TempPte;
|
|
|
|
Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
|
|
Pfn1->PteFrame = MmSystemPageDirectory;
|
|
Pfn1->PteAddress = PointerPde;
|
|
Pfn1->u2.ShareCount = 1;
|
|
Pfn1->u3.e2.ReferenceCount = 1;
|
|
Pfn1->u3.e1.PageLocation = ActiveAndValid;
|
|
Pfn1->OriginalPte.u.Long = 0;
|
|
RtlFillMemoryUlong (PointerPte, PAGE_SIZE, MM_KERNEL_DEMAND_ZERO_PTE);
|
|
|
|
UNLOCK_PFN (OldIrql);
|
|
|
|
MmNextPteForPagedPoolExpansion = PointerPde + 1;
|
|
|
|
//
|
|
// Build bitmaps for paged pool.
|
|
//
|
|
|
|
MiCreateBitMap (&MmPagedPoolAllocationMap, Size, NonPagedPool);
|
|
RtlSetAllBits (MmPagedPoolAllocationMap);
|
|
|
|
//
|
|
// Indicate first page worth of PTEs are available.
|
|
//
|
|
|
|
RtlClearBits (MmPagedPoolAllocationMap, 0, PTE_PER_PAGE);
|
|
|
|
MiCreateBitMap (&MmEndOfPagedPoolBitmap, Size, NonPagedPool);
|
|
RtlClearAllBits (MmEndOfPagedPoolBitmap);
|
|
|
|
//
|
|
// Initialize paged pool.
|
|
//
|
|
|
|
InitializePool (PagedPool, 0L);
|
|
|
|
//
|
|
// Allow mapping of views into system space.
|
|
//
|
|
|
|
MiInitializeSystemSpaceMap ();
|
|
|
|
//
|
|
// Set up the modified page writer.
|
|
|
|
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.
|
|
|
|
--*/
|
|
|
|
{
|
|
PLDR_DATA_TABLE_ENTRY LdrDataTableEntry;
|
|
PVOID CurrentBase;
|
|
PVOID InitStart;
|
|
PVOID InitEnd;
|
|
PLIST_ENTRY Next;
|
|
PIMAGE_NT_HEADERS NtHeader;
|
|
PIMAGE_SECTION_HEADER SectionTableEntry;
|
|
LONG i;
|
|
ULONG ValidPages;
|
|
PMMPTE PointerPte;
|
|
KIRQL OldIrql;
|
|
PVOID MiFindInitializationCodeAddress = MmGetProcedureAddress((PVOID)&MiFindInitializationCode);
|
|
|
|
*StartVa = NULL;
|
|
|
|
//
|
|
// Walk through the loader blocks looking for the base which
|
|
// contains this routine.
|
|
//
|
|
|
|
KeEnterCriticalRegion();
|
|
ExAcquireResourceExclusive (&PsLoadedModuleResource, TRUE);
|
|
Next = PsLoadedModuleList.Flink;
|
|
|
|
while ( Next != &PsLoadedModuleList ) {
|
|
LdrDataTableEntry = CONTAINING_RECORD( Next,
|
|
LDR_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)((ULONG)NtHeader +
|
|
sizeof(ULONG) +
|
|
sizeof(IMAGE_FILE_HEADER) +
|
|
NtHeader->FileHeader.SizeOfOptionalHeader);
|
|
|
|
//
|
|
// From the image header, locate the section named 'INIT'.
|
|
//
|
|
|
|
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 //DBG
|
|
|
|
if (*(PULONG)SectionTableEntry->Name == 'TINI') {
|
|
InitStart = (PVOID)((PCHAR)CurrentBase + SectionTableEntry->VirtualAddress);
|
|
InitEnd = (PVOID)((PCHAR)InitStart + SectionTableEntry->SizeOfRawData - 1);
|
|
|
|
InitEnd = (PVOID)((PCHAR)PAGE_ALIGN ((ULONG)InitEnd +
|
|
(NtHeader->OptionalHeader.SectionAlignment - 1)) - 1);
|
|
InitStart = (PVOID)ROUND_TO_PAGES (InitStart);
|
|
|
|
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!
|
|
//
|
|
|
|
*StartVa = InitStart;
|
|
*EndVa = InitEnd;
|
|
break;
|
|
} else {
|
|
|
|
//
|
|
// See if any more sections are discardable after this
|
|
// one.
|
|
//
|
|
|
|
while (i > 1) {
|
|
SectionTableEntry += 1;
|
|
i -= 1;
|
|
if ((SectionTableEntry->Characteristics &
|
|
IMAGE_SCN_MEM_DISCARDABLE) != 0) {
|
|
//
|
|
// Discard this too.
|
|
//
|
|
|
|
InitEnd = (PVOID)(((PCHAR)CurrentBase +
|
|
SectionTableEntry->VirtualAddress) +
|
|
(SectionTableEntry->SizeOfRawData - 1));
|
|
|
|
InitEnd = (PVOID)((PCHAR)PAGE_ALIGN ((ULONG)InitEnd +
|
|
(NtHeader->OptionalHeader.SectionAlignment - 1)) - 1);
|
|
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (InitEnd > (PVOID)((PCHAR)CurrentBase +
|
|
LdrDataTableEntry->SizeOfImage)) {
|
|
InitEnd = (PVOID)(((ULONG)CurrentBase +
|
|
(LdrDataTableEntry->SizeOfImage - 1)) |
|
|
(PAGE_SIZE - 1));
|
|
}
|
|
MiFreeInitializationCode (InitStart, InitEnd);
|
|
}
|
|
}
|
|
}
|
|
i -= 1;
|
|
SectionTableEntry += 1;
|
|
}
|
|
Next = Next->Flink;
|
|
}
|
|
ExReleaseResource (&PsLoadedModuleResource);
|
|
KeLeaveCriticalRegion();
|
|
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;
|
|
ULONG PageFrameIndex;
|
|
KIRQL OldIrql;
|
|
PVOID UnlockHandle;
|
|
ULONG ValidPages;
|
|
|
|
UnlockHandle = MmLockPagableCodeSection((PVOID)MiFreeInitializationCode);
|
|
ASSERT(UnlockHandle);
|
|
PointerPte = MiGetPteAddress (StartVa);
|
|
|
|
if (MI_IS_PHYSICAL_ADDRESS(StartVa)) {
|
|
LOCK_PFN (OldIrql);
|
|
while (StartVa < EndVa) {
|
|
|
|
//
|
|
// On certains 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);
|
|
MiInsertPageInList (MmPageLocationList[FreePageList], PageFrameIndex);
|
|
StartVa = (PVOID)((PUCHAR)StartVa + PAGE_SIZE);
|
|
}
|
|
UNLOCK_PFN (OldIrql);
|
|
} else {
|
|
MiDeleteSystemPagableVm (PointerPte,
|
|
1 + MiGetPteAddress (EndVa) -
|
|
PointerPte,
|
|
MM_ZERO_KERNEL_PTE,
|
|
&ValidPages);
|
|
}
|
|
MmUnlockPagableImageSection(UnlockHandle);
|
|
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.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
#if defined(_X86_) || defined(_PPC_)
|
|
|
|
PLDR_DATA_TABLE_ENTRY LdrDataTableEntry;
|
|
PVOID CurrentBase;
|
|
PLIST_ENTRY Next;
|
|
PIMAGE_NT_HEADERS NtHeader;
|
|
PIMAGE_SECTION_HEADER SectionTableEntry;
|
|
LONG i;
|
|
PMMPTE PointerPte;
|
|
PMMPTE LastPte;
|
|
BOOLEAN PageSection;
|
|
|
|
//
|
|
// Don't page kernel mode code if customer does not want it paged.
|
|
//
|
|
|
|
if (MmDisablePagingExecutive) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Walk through the loader blocks looking for the base which
|
|
// contains this routine.
|
|
//
|
|
|
|
KeEnterCriticalRegion();
|
|
ExAcquireResourceExclusive (&PsLoadedModuleResource, TRUE);
|
|
Next = PsLoadedModuleList.Flink;
|
|
while ( Next != &PsLoadedModuleList ) {
|
|
LdrDataTableEntry = CONTAINING_RECORD( Next,
|
|
LDR_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;
|
|
NtHeader = RtlImageNtHeader(CurrentBase);
|
|
|
|
SectionTableEntry = (PIMAGE_SECTION_HEADER)((ULONG)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) {
|
|
|
|
if (MI_IS_PHYSICAL_ADDRESS (CurrentBase)) {
|
|
|
|
//
|
|
// Mapped physically, can't be paged.
|
|
//
|
|
|
|
break;
|
|
}
|
|
|
|
PageSection = (*(PULONG)SectionTableEntry->Name == 'EGAP') ||
|
|
(*(PULONG)SectionTableEntry->Name == 'ade.');
|
|
|
|
if (*(PULONG)SectionTableEntry->Name == 'EGAP' &&
|
|
SectionTableEntry->Name[4] == 'K' &&
|
|
SectionTableEntry->Name[5] == 'D') {
|
|
|
|
//
|
|
// Only pageout PAGEKD if KdPitchDebugger is TRUE
|
|
//
|
|
|
|
PageSection = KdPitchDebugger;
|
|
}
|
|
|
|
if (PageSection) {
|
|
//
|
|
// This section is pagable, save away the start and end.
|
|
//
|
|
|
|
if (PointerPte == NULL) {
|
|
|
|
//
|
|
// Previous section was NOT pagable, get the start address.
|
|
//
|
|
|
|
PointerPte = MiGetPteAddress (ROUND_TO_PAGES (
|
|
(ULONG)CurrentBase +
|
|
SectionTableEntry->VirtualAddress));
|
|
}
|
|
LastPte = MiGetPteAddress ((ULONG)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) {
|
|
MiEnablePagingOfDriverAtInit (PointerPte, LastPte);
|
|
PointerPte = NULL;
|
|
}
|
|
}
|
|
i -= 1;
|
|
SectionTableEntry += 1;
|
|
} //end while
|
|
|
|
if (PointerPte != NULL) {
|
|
MiEnablePagingOfDriverAtInit (PointerPte, LastPte);
|
|
}
|
|
|
|
Next = Next->Flink;
|
|
} //end while
|
|
|
|
ExReleaseResource (&PsLoadedModuleResource);
|
|
KeLeaveCriticalRegion();
|
|
|
|
#endif
|
|
|
|
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.
|
|
|
|
--*/
|
|
|
|
{
|
|
PVOID Base;
|
|
ULONG PageFrameIndex;
|
|
PMMPFN Pfn;
|
|
MMPTE TempPte;
|
|
KIRQL OldIrql;
|
|
|
|
LOCK_PFN (OldIrql);
|
|
|
|
Base = MiGetVirtualAddressMappedByPte (PointerPte);
|
|
|
|
while (PointerPte <= LastPte) {
|
|
|
|
ASSERT (PointerPte->u.Hard.Valid == 1);
|
|
PageFrameIndex = PointerPte->u.Hard.PageFrameNumber;
|
|
Pfn = MI_PFN_ELEMENT (PageFrameIndex);
|
|
ASSERT (Pfn->u2.ShareCount == 1);
|
|
|
|
//
|
|
// Set the working set index to zero. This allows page table
|
|
// pages to be brough back in with the proper WSINDEX.
|
|
//
|
|
|
|
Pfn->u1.WsIndex = 0;
|
|
Pfn->OriginalPte.u.Long = MM_KERNEL_DEMAND_ZERO_PTE;
|
|
Pfn->u3.e1.Modified = 1;
|
|
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 transiton.
|
|
//
|
|
|
|
MiDecrementShareCount (PageFrameIndex);
|
|
Base = (PVOID)((PCHAR)Base + PAGE_SIZE);
|
|
PointerPte += 1;
|
|
MmResidentAvailablePages += 1;
|
|
MiChargeCommitmentCantExpand (1, TRUE);
|
|
MmTotalSystemCodePages += 1;
|
|
}
|
|
|
|
UNLOCK_PFN (OldIrql);
|
|
return;
|
|
}
|
|
|
|
|
|
MM_SYSTEMSIZE
|
|
MmQuerySystemSize(
|
|
VOID
|
|
)
|
|
{
|
|
//
|
|
// 12Mb is small
|
|
// 12-19 is medium
|
|
// > 19 is large
|
|
//
|
|
return MmSystemSize;
|
|
}
|
|
|
|
NTKERNELAPI
|
|
BOOLEAN
|
|
MmIsThisAnNtAsSystem(
|
|
VOID
|
|
)
|
|
{
|
|
return (BOOLEAN)MmProductType;
|
|
}
|