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