/*++ Copyright (c) 1989 Microsoft Corporation Module Name: mi.h Abstract: This module contains the private data structures and procedure prototypes for the memory management system. Author: Lou Perazzoli (loup) 20-Mar-1989 Landy Wang (landyw) 02-Jun-1997 Revision History: --*/ #ifndef _MI_ #define _MI_ #pragma warning(disable:4214) // bit field types other than int #pragma warning(disable:4201) // nameless struct/union #pragma warning(disable:4324) // alignment sensitive to declspec #pragma warning(disable:4127) // condition expression is constant #pragma warning(disable:4115) // named type definition in parentheses #pragma warning(disable:4232) // dllimport not static #pragma warning(disable:4206) // translation unit empty #include "ntos.h" #include "ntimage.h" #include "ki.h" #include "fsrtl.h" #include "zwapi.h" #include "pool.h" #include "stdio.h" #include "string.h" #include "safeboot.h" #include "triage.h" #include "xip.h" #if defined(_X86_) #include "..\mm\i386\mi386.h" #elif defined(_AMD64_) #include "..\mm\amd64\miamd.h" #elif defined(_IA64_) #include "..\mm\ia64\miia64.h" #else #error "mm: a target architecture must be defined." #endif #if defined (_WIN64) #define ASSERT32(exp) #define ASSERT64(exp) ASSERT(exp) #else #define ASSERT32(exp) ASSERT(exp) #define ASSERT64(exp) #endif // // Special pool constants // #define MI_SPECIAL_POOL_PAGABLE 0x8000 #define MI_SPECIAL_POOL_VERIFIER 0x4000 #define MI_SPECIAL_POOL_IN_SESSION 0x2000 #define MI_SPECIAL_POOL_PTE_PAGABLE 0x0002 #define MI_SPECIAL_POOL_PTE_NONPAGABLE 0x0004 #define _2gb 0x80000000 // 2 gigabytes #define _4gb 0x100000000 // 4 gigabytes #define MM_FLUSH_COUNTER_MASK (0xFFFFF) #define MM_FREE_WSLE_SHIFT 4 #define WSLE_NULL_INDEX ((((WSLE_NUMBER)-1) >> MM_FREE_WSLE_SHIFT)) #define MM_FREE_POOL_SIGNATURE (0x50554F4C) #define MM_MINIMUM_PAGED_POOL_NTAS ((SIZE_T)(48*1024*1024)) #define MM_ALLOCATION_FILLS_VAD ((PMMPTE)(ULONG_PTR)~3) #define MM_WORKING_SET_LIST_SEARCH 17 #define MM_FLUID_WORKING_SET 8 #define MM_FLUID_PHYSICAL_PAGES 32 //see MmResidentPages below. #define MM_USABLE_PAGES_FREE 32 #define X64K (ULONG)65536 #define MM_HIGHEST_VAD_ADDRESS ((PVOID)((ULONG_PTR)MM_HIGHEST_USER_ADDRESS - (64 * 1024))) #define MM_NO_WS_EXPANSION ((PLIST_ENTRY)0) #define MM_WS_EXPANSION_IN_PROGRESS ((PLIST_ENTRY)35) #define MM_WS_SWAPPED_OUT ((PLIST_ENTRY)37) #define MM_IO_IN_PROGRESS ((PLIST_ENTRY)97) // MUST HAVE THE HIGHEST VALUE #define MM4K_SHIFT 12 //MUST BE LESS THAN OR EQUAL TO PAGE_SHIFT #define MM4K_MASK 0xfff #define MMSECTOR_SHIFT 9 //MUST BE LESS THAN OR EQUAL TO PAGE_SHIFT #define MMSECTOR_MASK 0x1ff #define MM_LOCK_BY_REFCOUNT 0 #define MM_LOCK_BY_NONPAGE 1 #define MM_FORCE_TRIM 6 #define MM_GROW_WSLE_HASH 20 #define MM_MAXIMUM_WRITE_CLUSTER (MM_MAXIMUM_DISK_IO_SIZE / PAGE_SIZE) // // Number of PTEs to flush singularly before flushing the entire TB. // #define MM_MAXIMUM_FLUSH_COUNT (FLUSH_MULTIPLE_MAXIMUM-1) // // Page protections // #define MM_ZERO_ACCESS 0 // this value is not used. #define MM_READONLY 1 #define MM_EXECUTE 2 #define MM_EXECUTE_READ 3 #define MM_READWRITE 4 // bit 2 is set if this is writable. #define MM_WRITECOPY 5 #define MM_EXECUTE_READWRITE 6 #define MM_EXECUTE_WRITECOPY 7 #define MM_NOCACHE 0x8 #define MM_GUARD_PAGE 0x10 #define MM_DECOMMIT 0x10 //NO_ACCESS, Guard page #define MM_NOACCESS 0x18 //NO_ACCESS, Guard_page, nocache. #define MM_UNKNOWN_PROTECTION 0x100 //bigger than 5 bits! #define MM_LARGE_PAGES 0x111 #define MM_INVALID_PROTECTION ((ULONG)-1) //bigger than 5 bits! #define MM_KSTACK_OUTSWAPPED 0x1F // Denotes outswapped kernel stack pages. #define MM_PROTECTION_WRITE_MASK 4 #define MM_PROTECTION_COPY_MASK 1 #define MM_PROTECTION_OPERATION_MASK 7 // mask off guard page and nocache. #define MM_PROTECTION_EXECUTE_MASK 2 #define MM_SECURE_DELETE_CHECK 0x55 // // Debug flags // #define MM_DBG_WRITEFAULT 0x1 #define MM_DBG_PTE_UPDATE 0x2 #define MM_DBG_DUMP_WSL 0x4 #define MM_DBG_PAGEFAULT 0x8 #define MM_DBG_WS_EXPANSION 0x10 #define MM_DBG_MOD_WRITE 0x20 #define MM_DBG_CHECK_PTE 0x40 #define MM_DBG_VAD_CONFLICT 0x80 #define MM_DBG_SECTIONS 0x100 #define MM_DBG_SYS_PTES 0x400 #define MM_DBG_CLEAN_PROCESS 0x800 #define MM_DBG_COLLIDED_PAGE 0x1000 #define MM_DBG_DUMP_BOOT_PTES 0x2000 #define MM_DBG_FORK 0x4000 #define MM_DBG_DIR_BASE 0x8000 #define MM_DBG_FLUSH_SECTION 0x10000 #define MM_DBG_PRINTS_MODWRITES 0x20000 #define MM_DBG_PAGE_IN_LIST 0x40000 #define MM_DBG_CHECK_PFN_LOCK 0x80000 #define MM_DBG_PRIVATE_PAGES 0x100000 #define MM_DBG_WALK_VAD_TREE 0x200000 #define MM_DBG_SWAP_PROCESS 0x400000 #define MM_DBG_LOCK_CODE 0x800000 #define MM_DBG_STOP_ON_ACCVIO 0x1000000 #define MM_DBG_PAGE_REF_COUNT 0x2000000 #define MM_DBG_SHOW_FAULTS 0x40000000 #define MM_DBG_SESSIONS 0x80000000 // // If the PTE.protection & MM_COPY_ON_WRITE_MASK == MM_COPY_ON_WRITE_MASK // then the PTE is copy on write. // #define MM_COPY_ON_WRITE_MASK 5 extern ULONG MmProtectToValue[32]; extern #if (defined(_WIN64) || defined(_X86PAE_)) ULONGLONG #else ULONG #endif MmProtectToPteMask[32]; extern ULONG MmMakeProtectNotWriteCopy[32]; extern ACCESS_MASK MmMakeSectionAccess[8]; extern ACCESS_MASK MmMakeFileAccess[8]; // // Time constants // extern const LARGE_INTEGER MmSevenMinutes; extern LARGE_INTEGER MmWorkingSetProtectionTime; const extern LARGE_INTEGER MmOneSecond; const extern LARGE_INTEGER MmTwentySeconds; const extern LARGE_INTEGER MmShortTime; const extern LARGE_INTEGER MmHalfSecond; const extern LARGE_INTEGER Mm30Milliseconds; extern LARGE_INTEGER MmCriticalSectionTimeout; // // A month's worth // extern ULONG MmCritsectTimeoutSeconds; // // this is the csrss process ! // extern PEPROCESS ExpDefaultErrorPortProcess; extern SIZE_T MmExtendedCommit; extern SIZE_T MmTotalProcessCommit; // // The total number of pages needed for the loader to successfully hibernate. // extern PFN_NUMBER MmHiberPages; // // The counters and reasons to retry IO to protect against verifier induced // failures and temporary conditions. // extern ULONG MiIoRetryMask; extern ULONG MiFaultRetryMask; extern ULONG MiUserFaultRetryMask; #define MmIsRetryIoStatus(S) (((S) == STATUS_INSUFFICIENT_RESOURCES) || \ ((S) == STATUS_WORKING_SET_QUOTA) || \ ((S) == STATUS_NO_MEMORY)) #if defined (_MI_MORE_THAN_4GB_) extern PFN_NUMBER MiNoLowMemory; #if defined (_WIN64) #define MI_MAGIC_4GB_RECLAIM 0xffffedf0 #else #define MI_MAGIC_4GB_RECLAIM 0xffedf0 #endif #define MI_LOWMEM_MAGIC_BIT (0x80000000) extern PRTL_BITMAP MiLowMemoryBitMap; #endif // // This is a version of COMPUTE_PAGES_SPANNED that works for 32 and 64 ranges. // #define MI_COMPUTE_PAGES_SPANNED(Va, Size) \ ((((ULONG_PTR)(Va) & (PAGE_SIZE -1)) + (Size) + (PAGE_SIZE - 1)) >> PAGE_SHIFT) //++ // // ULONG // MI_CONVERT_FROM_PTE_PROTECTION ( // IN ULONG PROTECTION_MASK // ) // // Routine Description: // // This routine converts a PTE protection into a Protect value. // // Arguments: // // // Return Value: // // Returns the // //-- #define MI_CONVERT_FROM_PTE_PROTECTION(PROTECTION_MASK) \ (MmProtectToValue[PROTECTION_MASK]) #define MI_MASK_TO_PTE(PROTECTION_MASK) MmProtectToPteMask[PROTECTION_MASK] #define MI_IS_PTE_PROTECTION_COPY_WRITE(PROTECTION_MASK) \ (((PROTECTION_MASK) & MM_COPY_ON_WRITE_MASK) == MM_COPY_ON_WRITE_MASK) //++ // // ULONG // MI_ROUND_TO_64K ( // IN ULONG LENGTH // ) // // Routine Description: // // // The ROUND_TO_64k macro takes a LENGTH in bytes and rounds it up to a multiple // of 64K. // // Arguments: // // LENGTH - LENGTH in bytes to round up to 64k. // // Return Value: // // Returns the LENGTH rounded up to a multiple of 64k. // //-- #define MI_ROUND_TO_64K(LENGTH) (((LENGTH) + X64K - 1) & ~((ULONG_PTR)X64K - 1)) extern ULONG MiLastVadBit; //++ // // ULONG // MI_ROUND_TO_SIZE ( // IN ULONG LENGTH, // IN ULONG ALIGNMENT // ) // // Routine Description: // // // The ROUND_TO_SIZE macro takes a LENGTH in bytes and rounds it up to a // multiple of the alignment. // // Arguments: // // LENGTH - LENGTH in bytes to round up to. // // ALIGNMENT - alignment to round to, must be a power of 2, e.g, 2**n. // // Return Value: // // Returns the LENGTH rounded up to a multiple of the alignment. // //-- #define MI_ROUND_TO_SIZE(LENGTH,ALIGNMENT) \ (((LENGTH) + ((ALIGNMENT) - 1)) & ~((ALIGNMENT) - 1)) //++ // // PVOID // MI_64K_ALIGN ( // IN PVOID VA // ) // // Routine Description: // // // The MI_64K_ALIGN macro takes a virtual address and returns a 64k-aligned // virtual address for that page. // // Arguments: // // VA - Virtual address. // // Return Value: // // Returns the 64k aligned virtual address. // //-- #define MI_64K_ALIGN(VA) ((PVOID)((ULONG_PTR)(VA) & ~((LONG)X64K - 1))) //++ // // PVOID // MI_ALIGN_TO_SIZE ( // IN PVOID VA // IN ULONG ALIGNMENT // ) // // Routine Description: // // // The MI_ALIGN_TO_SIZE macro takes a virtual address and returns a // virtual address for that page with the specified alignment. // // Arguments: // // VA - Virtual address. // // ALIGNMENT - alignment to round to, must be a power of 2, e.g, 2**n. // // Return Value: // // Returns the aligned virtual address. // //-- #define MI_ALIGN_TO_SIZE(VA,ALIGNMENT) ((PVOID)((ULONG_PTR)(VA) & ~((ULONG_PTR) ALIGNMENT - 1))) //++ // // LONGLONG // MI_STARTING_OFFSET ( // IN PSUBSECTION SUBSECT // IN PMMPTE PTE // ) // // Routine Description: // // This macro takes a pointer to a PTE within a subsection and a pointer // to that subsection and calculates the offset for that PTE within the // file. // // Arguments: // // PTE - PTE within subsection. // // SUBSECT - Subsection // // Return Value: // // Offset for issuing I/O from. // //-- #define MI_STARTING_OFFSET(SUBSECT,PTE) \ (((LONGLONG)((ULONG_PTR)((PTE) - ((SUBSECT)->SubsectionBase))) << PAGE_SHIFT) + \ ((LONGLONG)((SUBSECT)->StartingSector) << MMSECTOR_SHIFT)); // NTSTATUS // MiFindEmptyAddressRangeDown ( // IN ULONG_PTR SizeOfRange, // IN PVOID HighestAddressToEndAt, // IN ULONG_PTR Alignment, // OUT PVOID *Base // ) // // // Routine Description: // // The function examines the virtual address descriptors to locate // an unused range of the specified size and returns the starting // address of the range. This routine looks from the top down. // // Arguments: // // SizeOfRange - Supplies the size in bytes of the range to locate. // // HighestAddressToEndAt - Supplies the virtual address to begin looking // at. // // Alignment - Supplies the alignment for the address. Must be // a power of 2 and greater than the page_size. // //Return Value: // // Returns the starting address of a suitable range. // #define MiFindEmptyAddressRangeDown(Root,SizeOfRange,HighestAddressToEndAt,Alignment,Base) \ (MiFindEmptyAddressRangeDownTree( \ (SizeOfRange), \ (HighestAddressToEndAt), \ (Alignment), \ (PMMADDRESS_NODE)(Root), \ (Base))) // PMMVAD // MiGetPreviousVad ( // IN PMMVAD Vad // ) // // Routine Description: // // This function locates the virtual address descriptor which contains // the address range which logically precedes the specified virtual // address descriptor. // // Arguments: // // Vad - Supplies a pointer to a virtual address descriptor. // // Return Value: // // Returns a pointer to the virtual address descriptor containing the // next address range, NULL if none. // // #define MiGetPreviousVad(VAD) ((PMMVAD)MiGetPreviousNode((PMMADDRESS_NODE)(VAD))) // PMMVAD // MiGetNextVad ( // IN PMMVAD Vad // ) // // Routine Description: // // This function locates the virtual address descriptor which contains // the address range which logically follows the specified address range. // // Arguments: // // VAD - Supplies a pointer to a virtual address descriptor. // // Return Value: // // Returns a pointer to the virtual address descriptor containing the // next address range, NULL if none. // #define MiGetNextVad(VAD) ((PMMVAD)MiGetNextNode((PMMADDRESS_NODE)(VAD))) // PMMVAD // MiGetFirstVad ( // Process // ) // // Routine Description: // // This function locates the virtual address descriptor which contains // the address range which logically is first within the address space. // // Arguments: // // Process - Specifies the process in which to locate the VAD. // // Return Value: // // Returns a pointer to the virtual address descriptor containing the // first address range, NULL if none. #define MiGetFirstVad(Process) \ ((PMMVAD)MiGetFirstNode((PMMADDRESS_NODE)(Process->VadRoot))) LOGICAL MiCheckForConflictingVadExistence ( IN PEPROCESS Process, IN PVOID StartingAddress, IN PVOID EndingAddress ); // PMMVAD // MiCheckForConflictingVad ( // IN PVOID StartingAddress, // IN PVOID EndingAddress // ) // // Routine Description: // // The function determines if any addresses between a given starting and // ending address is contained within a virtual address descriptor. // // Arguments: // // StartingAddress - Supplies the virtual address to locate a containing // descriptor. // // EndingAddress - Supplies the virtual address to locate a containing // descriptor. // // Return Value: // // Returns a pointer to the first conflicting virtual address descriptor // if one is found, otherwise a NULL value is returned. // #define MiCheckForConflictingVad(CurrentProcess,StartingAddress,EndingAddress) \ ((PMMVAD)MiCheckForConflictingNode( \ MI_VA_TO_VPN(StartingAddress), \ MI_VA_TO_VPN(EndingAddress), \ (PMMADDRESS_NODE)(CurrentProcess->VadRoot))) // PMMCLONE_DESCRIPTOR // MiGetNextClone ( // IN PMMCLONE_DESCRIPTOR Clone // ) // // Routine Description: // // This function locates the virtual address descriptor which contains // the address range which logically follows the specified address range. // // Arguments: // // Clone - Supplies a pointer to a virtual address descriptor. // // Return Value: // // Returns a pointer to the virtual address descriptor containing the // next address range, NULL if none. // // #define MiGetNextClone(CLONE) \ ((PMMCLONE_DESCRIPTOR)MiGetNextNode((PMMADDRESS_NODE)(CLONE))) // PMMCLONE_DESCRIPTOR // MiGetPreviousClone ( // IN PMMCLONE_DESCRIPTOR Clone // ) // // Routine Description: // // This function locates the virtual address descriptor which contains // the address range which logically precedes the specified virtual // address descriptor. // // Arguments: // // Clone - Supplies a pointer to a virtual address descriptor. // // Return Value: // // Returns a pointer to the virtual address descriptor containing the // next address range, NULL if none. #define MiGetPreviousClone(CLONE) \ ((PMMCLONE_DESCRIPTOR)MiGetPreviousNode((PMMADDRESS_NODE)(CLONE))) // PMMCLONE_DESCRIPTOR // MiGetFirstClone ( // ) // // Routine Description: // // This function locates the virtual address descriptor which contains // the address range which logically is first within the address space. // // Arguments: // // None. // // Return Value: // // Returns a pointer to the virtual address descriptor containing the // first address range, NULL if none. // #define MiGetFirstClone(_CurrentProcess) \ ((PMMCLONE_DESCRIPTOR)MiGetFirstNode((PMMADDRESS_NODE)(_CurrentProcess->CloneRoot))) // VOID // MiInsertClone ( // IN PMMCLONE_DESCRIPTOR Clone // ) // // Routine Description: // // This function inserts a virtual address descriptor into the tree and // reorders the splay tree as appropriate. // // Arguments: // // Clone - Supplies a pointer to a virtual address descriptor // // // Return Value: // // None. // #define MiInsertClone(_CurrentProcess, CLONE) \ { \ ASSERT ((CLONE)->NumberOfPtes != 0); \ MiInsertNode(((PMMADDRESS_NODE)(CLONE)),(PMMADDRESS_NODE *)&(_CurrentProcess->CloneRoot)); \ } // VOID // MiRemoveClone ( // IN PMMCLONE_DESCRIPTOR Clone // ) // // Routine Description: // // This function removes a virtual address descriptor from the tree and // reorders the splay tree as appropriate. // // Arguments: // // Clone - Supplies a pointer to a virtual address descriptor. // // Return Value: // // None. // #define MiRemoveClone(_CurrentProcess, CLONE) \ MiRemoveNode((PMMADDRESS_NODE)(CLONE),(PMMADDRESS_NODE *)&(_CurrentProcess->CloneRoot)); // PMMCLONE_DESCRIPTOR // MiLocateCloneAddress ( // IN PVOID VirtualAddress // ) // // /*++ // // Routine Description: // // The function locates the virtual address descriptor which describes // a given address. // // Arguments: // // VirtualAddress - Supplies the virtual address to locate a descriptor // for. // // Return Value: // // Returns a pointer to the virtual address descriptor which contains // the supplied virtual address or NULL if none was located. // #define MiLocateCloneAddress(_CurrentProcess, VA) \ (_CurrentProcess->CloneRoot ? \ ((PMMCLONE_DESCRIPTOR)MiLocateAddressInTree(((ULONG_PTR)VA), \ (PMMADDRESS_NODE *)&(_CurrentProcess->CloneRoot))) : \ NULL) #define MI_VA_TO_PAGE(va) ((ULONG_PTR)(va) >> PAGE_SHIFT) #define MI_VA_TO_VPN(va) ((ULONG_PTR)(va) >> PAGE_SHIFT) #define MI_VPN_TO_VA(vpn) (PVOID)((vpn) << PAGE_SHIFT) #define MI_VPN_TO_VA_ENDING(vpn) (PVOID)(((vpn) << PAGE_SHIFT) | (PAGE_SIZE - 1)) #define MiGetByteOffset(va) ((ULONG_PTR)(va) & (PAGE_SIZE - 1)) #define MI_PFN_ELEMENT(index) (&MmPfnDatabase[index]) // // Make a write-copy PTE, only writable. // #define MI_MAKE_PROTECT_NOT_WRITE_COPY(PROTECT) \ (MmMakeProtectNotWriteCopy[PROTECT]) // // Define macros to lock and unlock the PFN database. // #define MiLockPfnDatabase(OldIrql) \ OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock) #define MiUnlockPfnDatabase(OldIrql) \ KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql) #define MiLockPfnDatabaseAtDpcLevel() \ KeAcquireQueuedSpinLockAtDpcLevel(&KeGetCurrentPrcb()->LockQueue[LockQueuePfnLock]) #define MiUnlockPfnDatabaseFromDpcLevel() \ KeReleaseQueuedSpinLockFromDpcLevel(&KeGetCurrentPrcb()->LockQueue[LockQueuePfnLock]) #define MiTryToLockPfnDatabase(OldIrql) \ KeTryToAcquireQueuedSpinLock(LockQueuePfnLock, &OldIrql) #define MiReleasePfnLock() \ KeReleaseQueuedSpinLockFromDpcLevel(&KeGetCurrentPrcb()->LockQueue[LockQueuePfnLock]) #define MiLockSystemSpace(OldIrql) \ OldIrql = KeAcquireQueuedSpinLock(LockQueueSystemSpaceLock) #define MiUnlockSystemSpace(OldIrql) \ KeReleaseQueuedSpinLock(LockQueueSystemSpaceLock, OldIrql) #define MiLockSystemSpaceAtDpcLevel() \ KeAcquireQueuedSpinLockAtDpcLevel(&KeGetCurrentPrcb()->LockQueue[LockQueueSystemSpaceLock]) #define MiUnlockSystemSpaceFromDpcLevel() \ KeReleaseQueuedSpinLockFromDpcLevel(&KeGetCurrentPrcb()->LockQueue[LockQueueSystemSpaceLock]) // #define _MI_INSTRUMENT_PFN 1 #if defined (_MI_INSTRUMENT_PFN) #define MI_MAX_PFN_CALLERS 500 typedef struct _MMPFNTIMINGS { LARGE_INTEGER HoldTime; // Low bit is set if another processor waited ULONG_PTR AcquiredAddress; ULONG_PTR ReleasedAddress; } MMPFNTIMINGS, *PMMPFNTIMINGS; extern ULONG MiPfnTimings; extern ULONG_PTR MiPfnAcquiredAddress; extern MMPFNTIMINGS MiPfnSorted[]; extern LARGE_INTEGER MiPfnAcquired; extern LARGE_INTEGER MiPfnThreshold; ULONG_PTR MiGetExecutionAddress( VOID ); #if defined(_X86_) #define MI_GET_EXECUTION_ADDRESS(varname) varname = MiGetExecutionAddress(); #else #define MI_GET_EXECUTION_ADDRESS(varname) varname = 0; #endif #define LOCK_PFN_TIMESTAMP() \ { \ MiPfnAcquired = KeQueryPerformanceCounter (NULL);\ MI_GET_EXECUTION_ADDRESS(MiPfnAcquiredAddress); \ } #define UNLOCK_PFN_TIMESTAMP() \ { \ ULONG i; \ ULONG_PTR ExecutionAddress; \ LARGE_INTEGER PfnReleased; \ LARGE_INTEGER PfnHoldTime; \ PfnReleased = KeQueryPerformanceCounter (NULL); \ MI_GET_EXECUTION_ADDRESS(ExecutionAddress); \ PfnHoldTime.QuadPart = (PfnReleased.QuadPart - MiPfnAcquired.QuadPart) & ~0x1; \ i = MI_MAX_PFN_CALLERS - 1; \ do { \ if (PfnHoldTime.QuadPart < MiPfnSorted[i].HoldTime.QuadPart) { \ break; \ } \ i -= 1; \ } while (i != (ULONG)-1); \ if (i != MI_MAX_PFN_CALLERS - 1) { \ i += 1; \ if (i != MI_MAX_PFN_CALLERS - 1) { \ RtlMoveMemory (&MiPfnSorted[i+1], &MiPfnSorted[i], (MI_MAX_PFN_CALLERS-(i+1)) * sizeof(MMPFNTIMINGS)); \ } \ MiPfnSorted[i].HoldTime = PfnHoldTime; \ if (KeTestForWaitersQueuedSpinLock(LockQueuePfnLock) == TRUE) {\ MiPfnSorted[i].HoldTime.LowPart |= 0x1; \ } \ MiPfnSorted[i].AcquiredAddress = MiPfnAcquiredAddress; \ MiPfnSorted[i].ReleasedAddress = ExecutionAddress; \ } \ if ((MiPfnTimings & 0x2) && (PfnHoldTime.QuadPart >= MiPfnThreshold.QuadPart)) { \ DbgBreakPoint (); \ } \ if (MiPfnTimings & 0x1) { \ MiPfnTimings &= ~0x1; \ RtlZeroMemory (&MiPfnSorted[0], MI_MAX_PFN_CALLERS * sizeof(MMPFNTIMINGS)); \ } \ } #else #define LOCK_PFN_TIMESTAMP() #define UNLOCK_PFN_TIMESTAMP() #endif #define LOCK_PFN(OLDIRQL) ASSERT (KeGetCurrentIrql() <= APC_LEVEL); \ MiLockPfnDatabase(OLDIRQL); \ LOCK_PFN_TIMESTAMP(); #define LOCK_PFN_WITH_TRY(OLDIRQL) \ ASSERT (KeGetCurrentIrql() <= APC_LEVEL); \ do { \ } while (MiTryToLockPfnDatabase(OLDIRQL) == FALSE); \ LOCK_PFN_TIMESTAMP(); #define UNLOCK_PFN(OLDIRQL) \ UNLOCK_PFN_TIMESTAMP(); \ MiUnlockPfnDatabase(OLDIRQL); \ ASSERT(KeGetCurrentIrql() <= APC_LEVEL); #define LOCK_PFN2(OLDIRQL) ASSERT (KeGetCurrentIrql() <= DISPATCH_LEVEL); \ MiLockPfnDatabase(OLDIRQL); \ LOCK_PFN_TIMESTAMP(); #define UNLOCK_PFN2(OLDIRQL) \ UNLOCK_PFN_TIMESTAMP(); \ MiUnlockPfnDatabase(OLDIRQL); \ ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); #define LOCK_PFN_AT_DPC() ASSERT (KeGetCurrentIrql() == DISPATCH_LEVEL); \ MiLockPfnDatabaseAtDpcLevel(); \ LOCK_PFN_TIMESTAMP(); #define UNLOCK_PFN_FROM_DPC() \ UNLOCK_PFN_TIMESTAMP(); \ MiUnlockPfnDatabaseFromDpcLevel(); \ ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); #define UNLOCK_PFN_AND_THEN_WAIT(OLDIRQL) \ { \ KIRQL XXX; \ ASSERT (KeGetCurrentIrql() == DISPATCH_LEVEL); \ ASSERT (OLDIRQL <= APC_LEVEL); \ KiLockDispatcherDatabase (&XXX); \ UNLOCK_PFN_TIMESTAMP(); \ MiReleasePfnLock(); \ (KeGetCurrentThread())->WaitIrql = OLDIRQL; \ (KeGetCurrentThread())->WaitNext = TRUE; \ } extern KMUTANT MmSystemLoadLock; #if DBG #define SYSLOAD_LOCK_OWNED_BY_ME() ((PETHREAD)MmSystemLoadLock.OwnerThread == PsGetCurrentThread()) #else #define SYSLOAD_LOCK_OWNED_BY_ME() #endif #if DBG #if defined (_MI_COMPRESSION) extern KIRQL MiCompressionIrql; #define MM_PFN_LOCK_ASSERT() \ if (MmDebug & 0x80000) { \ KIRQL _OldIrql; \ _OldIrql = KeGetCurrentIrql(); \ ASSERT ((_OldIrql == DISPATCH_LEVEL) || \ ((MiCompressionIrql != 0) && (_OldIrql == MiCompressionIrql))); \ } #else #define MM_PFN_LOCK_ASSERT() \ if (MmDebug & 0x80000) { \ KIRQL _OldIrql; \ _OldIrql = KeGetCurrentIrql(); \ ASSERT (_OldIrql == DISPATCH_LEVEL); \ } #endif extern PETHREAD MiExpansionLockOwner; #define MM_SET_EXPANSION_OWNER() ASSERT (MiExpansionLockOwner == NULL); \ MiExpansionLockOwner = PsGetCurrentThread(); #define MM_CLEAR_EXPANSION_OWNER() ASSERT (MiExpansionLockOwner == PsGetCurrentThread()); \ MiExpansionLockOwner = NULL; #else #define MM_PFN_LOCK_ASSERT() #define MM_SET_EXPANSION_OWNER() #define MM_CLEAR_EXPANSION_OWNER() #endif //DBG #define LOCK_EXPANSION(OLDIRQL) ASSERT (KeGetCurrentIrql() <= APC_LEVEL); \ ExAcquireSpinLock (&MmExpansionLock, &OLDIRQL);\ MM_SET_EXPANSION_OWNER (); #define UNLOCK_EXPANSION(OLDIRQL) MM_CLEAR_EXPANSION_OWNER (); \ ExReleaseSpinLock (&MmExpansionLock, OLDIRQL); \ ASSERT (KeGetCurrentIrql() <= APC_LEVEL); #define UNLOCK_EXPANSION_AND_THEN_WAIT(OLDIRQL) \ { \ KIRQL XXX; \ ASSERT (KeGetCurrentIrql() == 2); \ ASSERT (OLDIRQL <= APC_LEVEL); \ KiLockDispatcherDatabase (&XXX); \ MM_CLEAR_EXPANSION_OWNER (); \ KiReleaseSpinLock (&MmExpansionLock); \ (KeGetCurrentThread())->WaitIrql = OLDIRQL; \ (KeGetCurrentThread())->WaitNext = TRUE; \ } extern PETHREAD MmSystemLockOwner; #define LOCK_SYSTEM_WS(OLDIRQL,_Thread) \ ASSERT (KeGetCurrentIrql() <= APC_LEVEL); \ KeRaiseIrql(APC_LEVEL,&OLDIRQL); \ ExAcquireResourceExclusiveLite(&MmSystemWsLock,TRUE); \ ASSERT (MmSystemLockOwner == NULL); \ MmSystemLockOwner = _Thread; #define LOCK_SYSTEM_WS_UNSAFE(_Thread) \ ASSERT (KeGetCurrentIrql() == APC_LEVEL); \ ExAcquireResourceExclusiveLite(&MmSystemWsLock,TRUE); \ ASSERT (MmSystemLockOwner == NULL); \ MmSystemLockOwner = _Thread; #define UNLOCK_SYSTEM_WS(OLDIRQL) \ ASSERT (MmSystemLockOwner == PsGetCurrentThread()); \ MmSystemLockOwner = NULL; \ ExReleaseResourceLite (&MmSystemWsLock); \ KeLowerIrql (OLDIRQL); \ ASSERT (KeGetCurrentIrql() <= APC_LEVEL); #define UNLOCK_SYSTEM_WS_NO_IRQL() \ ASSERT (MmSystemLockOwner == PsGetCurrentThread()); \ MmSystemLockOwner = NULL; \ ExReleaseResourceLite (&MmSystemWsLock); #define MM_SYSTEM_WS_LOCK_ASSERT() \ ASSERT (PsGetCurrentThread() == MmSystemLockOwner); #define LOCK_HYPERSPACE(_Process, OLDIRQL) \ ASSERT (_Process == PsGetCurrentProcess ()); \ ExAcquireSpinLock (&_Process->HyperSpaceLock, OLDIRQL); #define UNLOCK_HYPERSPACE(_Process, VA, OLDIRQL) \ ASSERT (_Process == PsGetCurrentProcess ()); \ MiGetPteAddress(VA)->u.Long = 0; \ ExReleaseSpinLock (&_Process->HyperSpaceLock, OLDIRQL); #define LOCK_HYPERSPACE_AT_DPC(_Process) \ ASSERT (KeGetCurrentIrql() == DISPATCH_LEVEL); \ ASSERT (_Process == PsGetCurrentProcess ()); \ ExAcquireSpinLockAtDpcLevel (&_Process->HyperSpaceLock); #define UNLOCK_HYPERSPACE_FROM_DPC(_Process, VA) \ ASSERT (KeGetCurrentIrql() == DISPATCH_LEVEL); \ ASSERT (_Process == PsGetCurrentProcess ()); \ MiGetPteAddress(VA)->u.Long = 0; \ ExReleaseSpinLockFromDpcLevel (&_Process->HyperSpaceLock); #define MiUnmapPageInHyperSpace(_Process, VA, OLDIRQL) UNLOCK_HYPERSPACE(_Process, VA, OLDIRQL) #define MiUnmapPageInHyperSpaceFromDpc(_Process, VA) UNLOCK_HYPERSPACE_FROM_DPC(_Process, VA) #if defined (_AXP64_) #define MI_WS_OWNER(PROCESS) ((PROCESS)->WorkingSetLock.Owner == KeGetCurrentThread()) #define MI_NOT_WS_OWNER(PROCESS) (!MI_WS_OWNER(PROCESS)) #else #define MI_WS_OWNER(PROCESS) 1 #define MI_NOT_WS_OWNER(PROCESS) 1 #endif #define MI_MUTEX_ACQUIRED_UNSAFE 0x88 #define MI_IS_WS_UNSAFE(PROCESS) ((PROCESS)->WorkingSetAcquiredUnsafe == MI_MUTEX_ACQUIRED_UNSAFE) #define LOCK_WS(PROCESS) \ ASSERT (MI_NOT_WS_OWNER(PROCESS)); \ ExAcquireFastMutex( &((PROCESS)->WorkingSetLock)); \ ASSERT (!MI_IS_WS_UNSAFE(PROCESS)); #define LOCK_WS_UNSAFE(PROCESS) \ ASSERT (MI_NOT_WS_OWNER(PROCESS)); \ ASSERT (KeGetCurrentIrql() == APC_LEVEL); \ ExAcquireFastMutexUnsafe( &((PROCESS)->WorkingSetLock));\ (PROCESS)->WorkingSetAcquiredUnsafe = MI_MUTEX_ACQUIRED_UNSAFE; #define MI_MUST_BE_UNSAFE(PROCESS) \ ASSERT (KeGetCurrentIrql() == APC_LEVEL); \ ASSERT (MI_WS_OWNER(PROCESS)); \ ASSERT (MI_IS_WS_UNSAFE(PROCESS)); #define MI_MUST_BE_SAFE(PROCESS) \ ASSERT (MI_WS_OWNER(PROCESS)); \ ASSERT (!MI_IS_WS_UNSAFE(PROCESS)); #if 0 #define MI_MUST_BE_UNSAFE(PROCESS) \ if (KeGetCurrentIrql() != APC_LEVEL) { \ KeBugCheckEx(MEMORY_MANAGEMENT, 0x32, (ULONG_PTR)PROCESS, KeGetCurrentIrql(), 0); \ } \ if (!MI_WS_OWNER(PROCESS)) { \ KeBugCheckEx(MEMORY_MANAGEMENT, 0x33, (ULONG_PTR)PROCESS, 0, 0); \ } \ if (!MI_IS_WS_UNSAFE(PROCESS)) { \ KeBugCheckEx(MEMORY_MANAGEMENT, 0x34, (ULONG_PTR)PROCESS, 0, 0); \ } #define MI_MUST_BE_SAFE(PROCESS) \ if (!MI_WS_OWNER(PROCESS)) { \ KeBugCheckEx(MEMORY_MANAGEMENT, 0x42, (ULONG_PTR)PROCESS, 0, 0); \ } \ if (MI_IS_WS_UNSAFE(PROCESS)) { \ KeBugCheckEx(MEMORY_MANAGEMENT, 0x43, (ULONG_PTR)PROCESS, 0, 0); \ } #endif #define UNLOCK_WS(PROCESS) \ MI_MUST_BE_SAFE(PROCESS); \ ExReleaseFastMutex(&((PROCESS)->WorkingSetLock)); #define UNLOCK_WS_UNSAFE(PROCESS) \ MI_MUST_BE_UNSAFE(PROCESS); \ (PROCESS)->WorkingSetAcquiredUnsafe = 0; \ ExReleaseFastMutexUnsafe(&((PROCESS)->WorkingSetLock)); \ ASSERT (KeGetCurrentIrql() == APC_LEVEL); #define LOCK_ADDRESS_SPACE(PROCESS) \ ExAcquireFastMutex( &((PROCESS)->AddressCreationLock)) #define LOCK_WS_AND_ADDRESS_SPACE(PROCESS) \ LOCK_ADDRESS_SPACE(PROCESS); \ LOCK_WS_UNSAFE(PROCESS); #define UNLOCK_WS_AND_ADDRESS_SPACE(PROCESS) \ UNLOCK_WS_UNSAFE(PROCESS); \ UNLOCK_ADDRESS_SPACE(PROCESS); #define UNLOCK_ADDRESS_SPACE(PROCESS) \ ExReleaseFastMutex( &((PROCESS)->AddressCreationLock)) // // The working set lock may have been acquired safely or unsafely. // Release and reacquire it regardless. // #define UNLOCK_WS_REGARDLESS(PROCESS, WSHELDSAFE) \ ASSERT (MI_WS_OWNER (PROCESS)); \ if (MI_IS_WS_UNSAFE (PROCESS)) { \ UNLOCK_WS_UNSAFE (PROCESS); \ WSHELDSAFE = FALSE; \ } \ else { \ UNLOCK_WS (PROCESS); \ WSHELDSAFE = TRUE; \ } #define LOCK_WS_REGARDLESS(PROCESS, WSHELDSAFE) \ if (WSHELDSAFE == TRUE) { \ LOCK_WS (PROCESS); \ } \ else { \ LOCK_WS_UNSAFE (PROCESS); \ } #define ZERO_LARGE(LargeInteger) \ (LargeInteger).LowPart = 0; \ (LargeInteger).HighPart = 0; #define NO_BITS_FOUND ((ULONG)-1) //++ // // ULONG // MI_CHECK_BIT ( // IN PULONG ARRAY // IN ULONG BIT // ) // // Routine Description: // // The MI_CHECK_BIT macro checks to see if the specified bit is // set within the specified array. // // Arguments: // // ARRAY - First element of the array to check. // // BIT - bit number (first bit is 0) to check. // // Return Value: // // Returns the value of the bit (0 or 1). // //-- #define MI_CHECK_BIT(ARRAY,BIT) \ (((ULONG)ARRAY[(BIT) / (sizeof(ULONG)*8)] >> ((BIT) & 0x1F)) & 1) //++ // // VOID // MI_SET_BIT ( // IN PULONG ARRAY // IN ULONG BIT // ) // // Routine Description: // // The MI_SET_BIT macro sets the specified bit within the // specified array. // // Arguments: // // ARRAY - First element of the array to set. // // BIT - bit number. // // Return Value: // // None. // //-- #define MI_SET_BIT(ARRAY,BIT) \ ARRAY[(BIT) / (sizeof(ULONG)*8)] |= (1 << ((BIT) & 0x1F)) //++ // // VOID // MI_CLEAR_BIT ( // IN PULONG ARRAY // IN ULONG BIT // ) // // Routine Description: // // The MI_CLEAR_BIT macro sets the specified bit within the // specified array. // // Arguments: // // ARRAY - First element of the array to clear. // // BIT - bit number. // // Return Value: // // None. // //-- #define MI_CLEAR_BIT(ARRAY,BIT) \ ARRAY[(BIT) / (sizeof(ULONG)*8)] &= ~(1 << ((BIT) & 0x1F)) // // These control the mirroring capabilities. // extern ULONG MmMirroring; #define MM_MIRRORING_ENABLED 0x1 // Enable mirroring capabilities. #define MM_MIRRORING_VERIFYING 0x2 // The HAL wants to verify the copy. extern PRTL_BITMAP MiMirrorBitMap; extern PRTL_BITMAP MiMirrorBitMap2; extern LOGICAL MiMirroringActive; #if defined (_WIN64) #define MI_MAGIC_AWE_PTEFRAME 0xffffedcb #else #define MI_MAGIC_AWE_PTEFRAME 0xffedcb #endif #define MI_PFN_IS_AWE(Pfn1) \ ((Pfn1->u2.ShareCount <= 3) && \ (Pfn1->u3.e1.PageLocation == ActiveAndValid) && \ (Pfn1->u4.VerifierAllocation == 0) && \ (Pfn1->u3.e1.LargeSessionAllocation == 0) && \ (Pfn1->u3.e1.StartOfAllocation == 1) && \ (Pfn1->u3.e1.EndOfAllocation == 1) && \ (Pfn1->u4.PteFrame == MI_MAGIC_AWE_PTEFRAME)) // // PFN database element. // // // Define pseudo fields for start and end of allocation. // #define StartOfAllocation ReadInProgress #define EndOfAllocation WriteInProgress #define LargeSessionAllocation PrototypePte typedef struct _MMPFNENTRY { ULONG Modified : 1; ULONG ReadInProgress : 1; ULONG WriteInProgress : 1; ULONG PrototypePte: 1; ULONG PageColor : 3; ULONG ParityError : 1; ULONG PageLocation : 3; ULONG RemovalRequested : 1; ULONG CacheAttribute : 2; ULONG Rom : 1; ULONG LockCharged : 1; // maintained for DBG only ULONG DontUse : 16; // overlays USHORT for reference count field. } MMPFNENTRY; // // The cache type definitions are carefully chosen to line up with the // MEMORY_CACHING_TYPE definitions to ease conversions. Any changes here must // be reflected throughout the code. // typedef enum _MI_PFN_CACHE_ATTRIBUTE { MiNonCached, MiCached, MiWriteCombined, MiNotMapped } MI_PFN_CACHE_ATTRIBUTE, *PMI_PFN_CACHE_ATTRIBUTE; // // This conversion array is unfortunately needed because not all // hardware platforms support all possible cache values. Note that // the first range is for system RAM, the second range is for I/O space. // extern MI_PFN_CACHE_ATTRIBUTE MiPlatformCacheAttributes[2 * MmMaximumCacheType]; //++ // // MI_PFN_CACHE_ATTRIBUTE // MI_TRANSLATE_CACHETYPE ( // IN MEMORY_CACHING_TYPE InputCacheType, // IN ULONG IoSpace // ) // // Routine Description: // // Returns the hardware supported cache type for the requested cachetype. // // Arguments: // // InputCacheType - Supplies the desired cache type. // // IoSpace - Supplies nonzero (not necessarily 1 though) if this is // I/O space, zero if it is main memory. // // Return Value: // // The actual cache type. // //-- FORCEINLINE MI_PFN_CACHE_ATTRIBUTE MI_TRANSLATE_CACHETYPE( IN MEMORY_CACHING_TYPE InputCacheType, IN ULONG IoSpace ) { ASSERT (InputCacheType <= MmWriteCombined); if (IoSpace != 0) { IoSpace = MmMaximumCacheType; } return MiPlatformCacheAttributes[IoSpace + InputCacheType]; } //++ // // VOID // MI_SET_CACHETYPE_TRANSLATION ( // IN MEMORY_CACHING_TYPE InputCacheType, // IN ULONG IoSpace, // IN MI_PFN_CACHE_ATTRIBUTE NewAttribute // ) // // Routine Description: // // This function sets the hardware supported cachetype for the // specified cachetype. // // Arguments: // // InputCacheType - Supplies the desired cache type. // // IoSpace - Supplies nonzero (not necessarily 1 though) if this is // I/O space, zero if it is main memory. // // NewAttribute - Supplies the desired attribute. // // Return Value: // // None. // //-- FORCEINLINE VOID MI_SET_CACHETYPE_TRANSLATION( IN MEMORY_CACHING_TYPE InputCacheType, IN ULONG IoSpace, IN MI_PFN_CACHE_ATTRIBUTE NewAttribute ) { ASSERT (InputCacheType <= MmWriteCombined); if (IoSpace != 0) { IoSpace = MmMaximumCacheType; } MiPlatformCacheAttributes[IoSpace + InputCacheType] = NewAttribute; } #if defined (_X86PAE_) #pragma pack(1) #endif typedef struct _MMPFN { union { PFN_NUMBER Flink; WSLE_NUMBER WsIndex; PKEVENT Event; NTSTATUS ReadStatus; SINGLE_LIST_ENTRY NextStackPfn; } u1; PMMPTE PteAddress; union { PFN_NUMBER Blink; ULONG_PTR ShareCount; } u2; union { MMPFNENTRY e1; struct { USHORT ShortFlags; USHORT ReferenceCount; } e2; } u3; #if defined (_WIN64) ULONG UsedPageTableEntries; #endif MMPTE OriginalPte; union { ULONG_PTR EntireFrame; struct { #if defined (_WIN64) ULONG_PTR PteFrame: 58; #else ULONG_PTR PteFrame: 26; #endif ULONG_PTR InPageError : 1; ULONG_PTR VerifierAllocation : 1; ULONG_PTR AweAllocation : 1; ULONG_PTR LockCharged : 1; // maintained for DBG only ULONG_PTR KernelStack : 1; // only for valid (not trans) pages ULONG_PTR Reserved : 1; }; } u4; } MMPFN, *PMMPFN; #if defined (_X86PAE_) #pragma pack() #endif // #define _MI_DEBUG_DIRTY 1 // Uncomment this for dirty bit logging #if defined (_MI_DEBUG_DIRTY) extern ULONG MiTrackDirtys; #define MI_DIRTY_BACKTRACE_LENGTH 17 typedef struct _MI_DIRTY_TRACES { PETHREAD Thread; PEPROCESS Process; PMMPFN Pfn; PMMPTE PointerPte; ULONG_PTR CallerId; ULONG_PTR ShareCount; USHORT ShortFlags; USHORT ReferenceCount; PVOID StackTrace [MI_DIRTY_BACKTRACE_LENGTH]; } MI_DIRTY_TRACES, *PMI_DIRTY_TRACES; extern LONG MiDirtyIndex; extern PMI_DIRTY_TRACES MiDirtyTraces; VOID FORCEINLINE MiSnapDirty ( IN PMMPFN Pfn, IN ULONG NewValue, IN ULONG CallerId ) { PEPROCESS Process; PMI_DIRTY_TRACES Information; ULONG Index; ULONG Hash; if (MiDirtyTraces == NULL) { return; } Index = InterlockedIncrement(&MiDirtyIndex); Index &= (MiTrackDirtys - 1); Information = &MiDirtyTraces[Index]; Process = PsGetCurrentProcess (); Information->Thread = PsGetCurrentThread (); Information->Process = Process; Information->Pfn = Pfn; Information->PointerPte = Pfn->PteAddress; Information->CallerId = CallerId; Information->ShareCount = Pfn->u2.ShareCount; Information->ShortFlags = Pfn->u3.e2.ShortFlags; Information->ReferenceCount = Pfn->u3.e2.ReferenceCount; if (NewValue != 0) { Information->Process = (PEPROCESS) ((ULONG_PTR)Process | 0x1); } RtlZeroMemory (&Information->StackTrace[0], MI_DIRTY_BACKTRACE_LENGTH * sizeof(PVOID)); RtlCaptureStackBackTrace (0, MI_DIRTY_BACKTRACE_LENGTH, Information->StackTrace, &Hash); } #define MI_SNAP_DIRTY(_Pfn, _NewValue, _Callerid) MiSnapDirty(_Pfn, _NewValue, _Callerid) #else #define MI_SNAP_DIRTY(_Pfn, _NewValue, _Callerid) #endif #if 0 #define MI_STAMP_MODIFIED(Pfn,id) (Pfn)->u4.Reserved = (id); #else #define MI_STAMP_MODIFIED(Pfn,id) #endif //++ // // VOID // MI_SET_MODIFIED ( // IN PMMPFN Pfn, // IN ULONG NewValue, // IN ULONG CallerId // ) // // Routine Description: // // Set (or clear) the modified bit in the PFN database element. // The PFN lock must be held. // // Arguments: // // Pfn - Supplies the PFN to operate on. // // NewValue - Supplies 1 to set the modified bit, 0 to clear it. // // CallerId - Supplies a caller ID useful for debugging purposes. // // Return Value: // // None. // //-- // #define MI_SET_MODIFIED(_Pfn, _NewValue, _CallerId) \ MI_SNAP_DIRTY (_Pfn, _NewValue, _CallerId); \ if ((_NewValue) != 0) { \ MI_STAMP_MODIFIED (_Pfn, _CallerId); \ } \ (_Pfn)->u3.e1.Modified = (_NewValue); // // ccNUMA is supported in multiprocessor PAE and WIN64 systems only. // #if (defined(_WIN64) || defined(_X86PAE_)) && !defined(NT_UP) #define MI_MULTINODE VOID MiDetermineNode ( IN PFN_NUMBER Page, IN PMMPFN Pfn ); #else #define MiDetermineNode(x,y) ((y)->u3.e1.PageColor = 0) #endif #if defined (_WIN64) // // Note there are some places where these portable macros are not currently // used because we are not in the correct address space required. // #define MI_CAPTURE_USED_PAGETABLE_ENTRIES(PFN) \ ASSERT ((PFN)->UsedPageTableEntries <= PTE_PER_PAGE); \ (PFN)->OriginalPte.u.Soft.UsedPageTableEntries = (PFN)->UsedPageTableEntries; #define MI_RETRIEVE_USED_PAGETABLE_ENTRIES_FROM_PTE(RBL, PTE) \ ASSERT ((PTE)->u.Soft.UsedPageTableEntries <= PTE_PER_PAGE); \ (RBL)->UsedPageTableEntries = (ULONG)(((PMMPTE)(PTE))->u.Soft.UsedPageTableEntries); #define MI_ZERO_USED_PAGETABLE_ENTRIES_IN_INPAGE_SUPPORT(INPAGE_SUPPORT) \ (INPAGE_SUPPORT)->UsedPageTableEntries = 0; #define MI_ZERO_USED_PAGETABLE_ENTRIES_IN_PFN(PFN) (PFN)->UsedPageTableEntries = 0; #define MI_INSERT_USED_PAGETABLE_ENTRIES_IN_PFN(PFN, INPAGE_SUPPORT) \ ASSERT ((INPAGE_SUPPORT)->UsedPageTableEntries <= PTE_PER_PAGE); \ (PFN)->UsedPageTableEntries = (INPAGE_SUPPORT)->UsedPageTableEntries; #define MI_ZERO_USED_PAGETABLE_ENTRIES(PFN) \ (PFN)->UsedPageTableEntries = 0; #define MI_CHECK_USED_PTES_HANDLE(VA) \ ASSERT (MiGetPdeAddress(VA)->u.Hard.Valid == 1); #define MI_GET_USED_PTES_HANDLE(VA) \ ((PVOID)MI_PFN_ELEMENT((PFN_NUMBER)MiGetPdeAddress(VA)->u.Hard.PageFrameNumber)) #define MI_GET_USED_PTES_FROM_HANDLE(PFN) \ ((ULONG)(((PMMPFN)(PFN))->UsedPageTableEntries)) #define MI_INCREMENT_USED_PTES_BY_HANDLE(PFN) \ (((PMMPFN)(PFN))->UsedPageTableEntries += 1); \ ASSERT (((PMMPFN)(PFN))->UsedPageTableEntries <= PTE_PER_PAGE) #define MI_DECREMENT_USED_PTES_BY_HANDLE(PFN) \ (((PMMPFN)(PFN))->UsedPageTableEntries -= 1); \ ASSERT (((PMMPFN)(PFN))->UsedPageTableEntries < PTE_PER_PAGE) #else #define MI_CAPTURE_USED_PAGETABLE_ENTRIES(PFN) #define MI_RETRIEVE_USED_PAGETABLE_ENTRIES_FROM_PTE(RBL, PTE) #define MI_ZERO_USED_PAGETABLE_ENTRIES_IN_INPAGE_SUPPORT(INPAGE_SUPPORT) #define MI_ZERO_USED_PAGETABLE_ENTRIES_IN_PFN(PFN) #define MI_INSERT_USED_PAGETABLE_ENTRIES_IN_PFN(PFN, INPAGE_SUPPORT) #define MI_CHECK_USED_PTES_HANDLE(VA) #define MI_GET_USED_PTES_HANDLE(VA) ((PVOID)&MmWorkingSetList->UsedPageTableEntries[MiGetPdeIndex(VA)]) #define MI_GET_USED_PTES_FROM_HANDLE(PDSHORT) ((ULONG)(*(PUSHORT)(PDSHORT))) #define MI_INCREMENT_USED_PTES_BY_HANDLE(PDSHORT) \ ((*(PUSHORT)(PDSHORT)) += 1); \ ASSERT (((*(PUSHORT)(PDSHORT)) <= PTE_PER_PAGE)) #define MI_DECREMENT_USED_PTES_BY_HANDLE(PDSHORT) \ ((*(PUSHORT)(PDSHORT)) -= 1); \ ASSERT (((*(PUSHORT)(PDSHORT)) < PTE_PER_PAGE)) #endif extern PFN_NUMBER MmDynamicPfn; extern FAST_MUTEX MmDynamicMemoryMutex; extern PFN_NUMBER MmSystemLockPagesCount; #if DBG #define MI_LOCK_ID_COUNTER_MAX 64 ULONG MiLockIds[MI_LOCK_ID_COUNTER_MAX]; #define MI_MARK_PFN_AS_LOCK_CHARGED(Pfn, CallerId) \ ASSERT (Pfn->u3.e1.LockCharged == 0); \ ASSERT (CallerId < MI_LOCK_ID_COUNTER_MAX); \ MiLockIds[CallerId] += 1; \ Pfn->u3.e1.LockCharged = 1; #define MI_UNMARK_PFN_AS_LOCK_CHARGED(Pfn, CallerId) \ ASSERT (Pfn->u3.e1.LockCharged == 1); \ ASSERT (CallerId < MI_LOCK_ID_COUNTER_MAX); \ MiLockIds[CallerId] += 1; \ Pfn->u3.e1.LockCharged = 0; #else #define MI_MARK_PFN_AS_LOCK_CHARGED(Pfn, CallerId) #define MI_UNMARK_PFN_AS_LOCK_CHARGED(Pfn, CallerId) #endif //++ // // VOID // MI_ADD_LOCKED_PAGE_CHARGE ( // IN PMMPFN Pfn // ) // // Routine Description: // // Charge the systemwide count of locked pages if this is the initial // lock for this page (multiple concurrent locks are only charged once). // // Arguments: // // Pfn - the PFN index to operate on. // // Return Value: // // None. // //-- // #define MI_ADD_LOCKED_PAGE_CHARGE(Pfn, CallerId) \ ASSERT (Pfn->u3.e2.ReferenceCount != 0); \ if (Pfn->u3.e2.ReferenceCount == 1) { \ if (Pfn->u2.ShareCount != 0) { \ ASSERT (Pfn->u3.e1.PageLocation == ActiveAndValid); \ MI_MARK_PFN_AS_LOCK_CHARGED(Pfn, CallerId); \ MmSystemLockPagesCount += 1; \ } \ else { \ ASSERT (Pfn->u3.e1.LockCharged == 1); \ } \ } #define MI_ADD_LOCKED_PAGE_CHARGE_FOR_MODIFIED_PAGE(Pfn, CallerId) \ ASSERT (Pfn->u3.e1.PageLocation != ActiveAndValid); \ ASSERT (Pfn->u2.ShareCount == 0); \ if (Pfn->u3.e2.ReferenceCount == 0) { \ MI_MARK_PFN_AS_LOCK_CHARGED(Pfn, CallerId); \ MmSystemLockPagesCount += 1; \ } #define MI_ADD_LOCKED_PAGE_CHARGE_FOR_TRANSITION_PAGE(Pfn, CallerId) \ ASSERT (Pfn->u3.e1.PageLocation == ActiveAndValid); \ ASSERT (Pfn->u2.ShareCount == 0); \ ASSERT (Pfn->u3.e2.ReferenceCount != 0); \ if (Pfn->u3.e2.ReferenceCount == 1) { \ MI_MARK_PFN_AS_LOCK_CHARGED(Pfn, CallerId); \ MmSystemLockPagesCount += 1; \ } //++ // // VOID // MI_REMOVE_LOCKED_PAGE_CHARGE ( // IN PMMPFN Pfn // ) // // Routine Description: // // Remove the charge from the systemwide count of locked pages if this // is the last lock for this page (multiple concurrent locks are only // charged once). // // The PFN reference checks are carefully ordered so the most common case // is handled first, the next most common case next, etc. The case of // a reference count of 2 occurs more than 1000x (yes, 3 orders of // magnitude) more than a reference count of 1. And reference counts of >2 // occur 3 orders of magnitude more frequently than reference counts of // exactly 1. // // Arguments: // // Pfn - the PFN index to operate on. // // Return Value: // // None. // //-- // #define MI_REMOVE_LOCKED_PAGE_CHARGE(Pfn, CallerId) \ ASSERT (Pfn->u3.e2.ReferenceCount != 0); \ if (Pfn->u3.e2.ReferenceCount == 2) { \ if (Pfn->u2.ShareCount >= 1) { \ ASSERT (Pfn->u3.e1.PageLocation == ActiveAndValid); \ MI_UNMARK_PFN_AS_LOCK_CHARGED(Pfn, CallerId); \ MmSystemLockPagesCount -= 1; \ } \ else { \ /* \ * There are multiple referencers to this page and the \ * page is no longer valid in any process address space. \ * The systemwide lock count can only be decremented \ * by the last dereference. \ */ \ NOTHING; \ } \ } \ else if (Pfn->u3.e2.ReferenceCount != 1) { \ /* \ * There are still multiple referencers to this page (it may \ * or may not be resident in any process address space). \ * Since the systemwide lock count can only be decremented \ * by the last dereference (and this is not it), no action \ * is taken here. \ */ \ ASSERT (Pfn->u3.e2.ReferenceCount > 2); \ NOTHING; \ } \ else { \ /* \ * This page has already been deleted from all process address \ * spaces. It is sitting in limbo (not on any list) awaiting \ * this last dereference. \ */ \ ASSERT (Pfn->u3.e2.ReferenceCount == 1); \ ASSERT (Pfn->u3.e1.PageLocation != ActiveAndValid); \ ASSERT (Pfn->u2.ShareCount == 0); \ MI_UNMARK_PFN_AS_LOCK_CHARGED(Pfn, CallerId); \ MmSystemLockPagesCount -= 1; \ } //++ // // VOID // MI_REMOVE_LOCKED_PAGE_CHARGE_AND_DECREF ( // IN PMMPFN Pfn // ) // // Routine Description: // // Remove the charge from the systemwide count of locked pages if this // is the last lock for this page (multiple concurrent locks are only // charged once). // // The PFN reference checks are carefully ordered so the most common case // is handled first, the next most common case next, etc. The case of // a reference count of 2 occurs more than 1000x (yes, 3 orders of // magnitude) more than a reference count of 1. And reference counts of >2 // occur 3 orders of magnitude more frequently than reference counts of // exactly 1. // // The PFN reference count is then decremented. // // Arguments: // // Pfn - the PFN index to operate on. // // Return Value: // // None. // //-- // #define MI_REMOVE_LOCKED_PAGE_CHARGE_AND_DECREF(Pfn, CallerId) \ ASSERT (Pfn->u3.e2.ReferenceCount != 0); \ if (Pfn->u3.e2.ReferenceCount == 2) { \ if (Pfn->u2.ShareCount >= 1) { \ ASSERT (Pfn->u3.e1.PageLocation == ActiveAndValid); \ MI_UNMARK_PFN_AS_LOCK_CHARGED(Pfn, CallerId); \ MmSystemLockPagesCount -= 1; \ } \ else { \ /* \ * There are multiple referencers to this page and the \ * page is no longer valid in any process address space. \ * The systemwide lock count can only be decremented \ * by the last dereference. \ */ \ NOTHING; \ } \ Pfn->u3.e2.ReferenceCount -= 1; \ } \ else if (Pfn->u3.e2.ReferenceCount != 1) { \ /* \ * There are still multiple referencers to this page (it may \ * or may not be resident in any process address space). \ * Since the systemwide lock count can only be decremented \ * by the last dereference (and this is not it), no action \ * is taken here. \ */ \ ASSERT (Pfn->u3.e2.ReferenceCount > 2); \ Pfn->u3.e2.ReferenceCount -= 1; \ } \ else { \ /* \ * This page has already been deleted from all process address \ * spaces. It is sitting in limbo (not on any list) awaiting \ * this last dereference. \ */ \ PFN_NUMBER _PageFrameIndex; \ ASSERT (Pfn->u3.e2.ReferenceCount == 1); \ ASSERT (Pfn->u3.e1.PageLocation != ActiveAndValid); \ ASSERT (Pfn->u2.ShareCount == 0); \ MI_UNMARK_PFN_AS_LOCK_CHARGED(Pfn, CallerId); \ MmSystemLockPagesCount -= 1; \ _PageFrameIndex = Pfn - MmPfnDatabase; \ MiDecrementReferenceCount (_PageFrameIndex); \ } //++ // // VOID // MI_ZERO_WSINDEX ( // IN PMMPFN Pfn // ) // // Routine Description: // // Zero the Working Set Index field of the argument PFN entry. // There is a subtlety here on systems where the WsIndex ULONG is // overlaid with an Event pointer and sizeof(ULONG) != sizeof(PKEVENT). // Note this will need to be updated if we ever decide to allocate bodies of // thread objects on 4GB boundaries. // // Arguments: // // Pfn - the PFN index to operate on. // // Return Value: // // None. // //-- // #define MI_ZERO_WSINDEX(Pfn) \ Pfn->u1.Event = NULL; typedef enum _MMSHARE_TYPE { Normal, ShareCountOnly, AndValid } MMSHARE_TYPE; typedef struct _MMWSLE_HASH { PVOID Key; WSLE_NUMBER Index; } MMWSLE_HASH, *PMMWSLE_HASH; //++ // // WSLE_NUMBER // MI_WSLE_HASH ( // IN ULONG_PTR VirtualAddress, // IN PMMWSL WorkingSetList // ) // // Routine Description: // // Hash the address // // Arguments: // // VirtualAddress - the address to hash. // // WorkingSetList - the working set to hash the address into. // // Return Value: // // The hash key. // //-- // #define MI_WSLE_HASH(Address, Wsl) \ ((WSLE_NUMBER)(((ULONG_PTR)PAGE_ALIGN(Address) >> (PAGE_SHIFT - 2)) % \ ((Wsl)->HashTableSize - 1))) // // Working Set List Entry. // typedef struct _MMWSLENTRY { ULONG_PTR Valid : 1; ULONG_PTR LockedInWs : 1; ULONG_PTR LockedInMemory : 1; ULONG_PTR Protection : 5; ULONG_PTR SameProtectAsProto : 1; ULONG_PTR Direct : 1; ULONG_PTR Age : 2; #if MM_VIRTUAL_PAGE_FILLER ULONG_PTR Filler : MM_VIRTUAL_PAGE_FILLER; #endif ULONG_PTR VirtualPageNumber : MM_VIRTUAL_PAGE_SIZE; } MMWSLENTRY; typedef struct _MMWSLE { union { PVOID VirtualAddress; ULONG_PTR Long; MMWSLENTRY e1; } u1; } MMWSLE; #define MI_GET_PROTECTION_FROM_WSLE(Wsl) ((Wsl)->u1.e1.Protection) typedef MMWSLE *PMMWSLE; // // Working Set List. Must be quadword sized. // typedef struct _MMWSL { SIZE_T Quota; WSLE_NUMBER FirstFree; WSLE_NUMBER FirstDynamic; WSLE_NUMBER LastEntry; WSLE_NUMBER NextSlot; // The next slot to trim PMMWSLE Wsle; WSLE_NUMBER LastInitializedWsle; WSLE_NUMBER NonDirectCount; PMMWSLE_HASH HashTable; ULONG HashTableSize; ULONG NumberOfCommittedPageTables; PVOID HashTableStart; PVOID HighestPermittedHashAddress; ULONG NumberOfImageWaiters; ULONG VadBitMapHint; #if (_MI_PAGING_LEVELS >= 4) PULONG CommittedPageTables; ULONG NumberOfCommittedPageDirectories; PULONG CommittedPageDirectories; ULONG NumberOfCommittedPageDirectoryParents; ULONG CommittedPageDirectoryParents[(MM_USER_PAGE_DIRECTORY_PARENT_PAGES + sizeof(ULONG)*8-1)/(sizeof(ULONG)*8)]; #elif (_MI_PAGING_LEVELS >= 3) PULONG CommittedPageTables; ULONG NumberOfCommittedPageDirectories; ULONG CommittedPageDirectories[(MM_USER_PAGE_DIRECTORY_PAGES + sizeof(ULONG)*8-1)/(sizeof(ULONG)*8)]; #else // // This must be at the end. // Not used in system cache or session working set lists. // USHORT UsedPageTableEntries[MM_USER_PAGE_TABLE_PAGES]; ULONG CommittedPageTables[MM_USER_PAGE_TABLE_PAGES/(sizeof(ULONG)*8)]; #endif } MMWSL, *PMMWSL; #if defined(_X86_) extern PMMWSL MmWorkingSetList; #endif extern PKEVENT MiHighMemoryEvent; extern PKEVENT MiLowMemoryEvent; // // The claim estimate of unused pages in a working set is limited // to grow by this amount per estimation period. // #define MI_CLAIM_INCR 30 // // The maximum number of different ages a page can be. // #define MI_USE_AGE_COUNT 4 #define MI_USE_AGE_MAX (MI_USE_AGE_COUNT - 1) // // If more than this "percentage" of the working set is estimated to // be used then allow it to grow freely. // #define MI_REPLACEMENT_FREE_GROWTH_SHIFT 5 // // If more than this "percentage" of the working set has been claimed // then force replacement in low memory. // #define MI_REPLACEMENT_CLAIM_THRESHOLD_SHIFT 3 // // If more than this "percentage" of the working set is estimated to // be available then force replacement in low memory. // #define MI_REPLACEMENT_EAVAIL_THRESHOLD_SHIFT 3 // // If while doing replacement a page is found of this age or older then // replace it. Otherwise the oldest is selected. // #define MI_IMMEDIATE_REPLACEMENT_AGE 2 // // When trimming, use these ages for different passes. // #define MI_MAX_TRIM_PASSES 4 #define MI_PASS0_TRIM_AGE 2 #define MI_PASS1_TRIM_AGE 1 #define MI_PASS2_TRIM_AGE 1 #define MI_PASS3_TRIM_AGE 1 #define MI_PASS4_TRIM_AGE 0 // // If not a forced trim, trim pages older than this age. // #define MI_TRIM_AGE_THRESHOLD 2 // // This "percentage" of a claim is up for grabs in a foreground process. // #define MI_FOREGROUND_CLAIM_AVAILABLE_SHIFT 3 // // This "percentage" of a claim is up for grabs in a background process. // #define MI_BACKGROUND_CLAIM_AVAILABLE_SHIFT 1 //++ // // DWORD // MI_CALC_NEXT_VALID_ESTIMATION_SLOT ( // DWORD Previous, // DWORD Minimum, // DWORD Maximum, // MI_NEXT_ESTIMATION_SLOT_CONST NextEstimationSlotConst, // PMMWSLE Wsle // ) // // Routine Description: // // We iterate through the working set array in a non-sequential // manner so that the sample is independent of any aging or trimming. // // This algorithm walks through the working set with a stride of // 2^MiEstimationShift elements. // // Arguments: // // Previous - Last slot used // // Minimum - Minimum acceptable slot (ie. the first dynamic one) // // Maximum - max slot number + 1 // // NextEstimationSlotConst - for this algorithm it contains the stride // // Wsle - the working set array // // Return Value: // // Next slot. // // Environment: // // Kernel mode, APCs disabled, working set lock held and PFN lock held. // //-- typedef struct _MI_NEXT_ESTIMATION_SLOT_CONST { WSLE_NUMBER Stride; } MI_NEXT_ESTIMATION_SLOT_CONST; #define MI_CALC_NEXT_ESTIMATION_SLOT_CONST(NextEstimationSlotConst, WorkingSetList) \ (NextEstimationSlotConst).Stride = 1 << MiEstimationShift; #define MI_NEXT_VALID_ESTIMATION_SLOT(Previous, StartEntry, Minimum, Maximum, NextEstimationSlotConst, Wsle) \ ASSERT(((Previous) >= Minimum) && ((Previous) <= Maximum)); \ ASSERT(((StartEntry) >= Minimum) && ((StartEntry) <= Maximum)); \ do { \ (Previous) += (NextEstimationSlotConst).Stride; \ if ((Previous) > Maximum) { \ (Previous) = Minimum + ((Previous + 1) & (NextEstimationSlotConst.Stride - 1)); \ StartEntry += 1; \ (Previous) = StartEntry; \ } \ if ((Previous) > Maximum || (Previous) < Minimum) { \ StartEntry = Minimum; \ (Previous) = StartEntry; \ } \ } while (Wsle[Previous].u1.e1.Valid == 0); //++ // // WSLE_NUMBER // MI_NEXT_VALID_AGING_SLOT ( // DWORD Previous, // DWORD Minimum, // DWORD Maximum, // PMMWSLE Wsle // ) // // Routine Description: // // This finds the next slot to valid slot to age. It walks // through the slots sequentialy. // // Arguments: // // Previous - Last slot used // // Minimum - Minimum acceptable slot (ie. the first dynamic one) // // Maximum - Max slot number + 1 // // Wsle - the working set array // // Return Value: // // None. // // Environment: // // Kernel mode, APCs disabled, working set lock held and PFN lock held. // //-- #define MI_NEXT_VALID_AGING_SLOT(Previous, Minimum, Maximum, Wsle) \ ASSERT(((Previous) >= Minimum) && ((Previous) <= Maximum)); \ do { \ (Previous) += 1; \ if ((Previous) > Maximum) { \ Previous = Minimum; \ } \ } while ((Wsle[Previous].u1.e1.Valid == 0)); //++ // // ULONG // MI_CALCULATE_USAGE_ESTIMATE ( // IN PULONG SampledAgeCounts. // IN ULONG CounterShift // ) // // Routine Description: // // In Usage Estimation, we count the number of pages of each age in // a sample. The function turns the SampledAgeCounts into an // estimate of the unused pages. // // Arguments: // // SampledAgeCounts - counts of pages of each different age in the sample // // CounterShift - shift necessary to apply sample to entire WS // // Return Value: // // The number of pages to walk in the working set to get a good // estimate of the number available. // //-- #define MI_CALCULATE_USAGE_ESTIMATE(SampledAgeCounts, CounterShift) \ (((SampledAgeCounts)[1] + \ (SampledAgeCounts)[2] + (SampledAgeCounts)[3]) \ << (CounterShift)) //++ // // VOID // MI_RESET_WSLE_AGE ( // IN PMMPTE PointerPte, // IN PMMWSLE Wsle // ) // // Routine Description: // // Clear the age counter for the working set entry. // // Arguments: // // PointerPte - pointer to the working set list entry's PTE. // // Wsle - pointer to the working set list entry. // // Return Value: // // None. // //-- #define MI_RESET_WSLE_AGE(PointerPte, Wsle) \ (Wsle)->u1.e1.Age = 0; //++ // // ULONG // MI_GET_WSLE_AGE ( // IN PMMPTE PointerPte, // IN PMMWSLE Wsle // ) // // Routine Description: // // Clear the age counter for the working set entry. // // Arguments: // // PointerPte - pointer to the working set list entry's PTE // Wsle - pointer to the working set list entry // // Return Value: // // Age group of the working set entry // //-- #define MI_GET_WSLE_AGE(PointerPte, Wsle) \ ((ULONG)((Wsle)->u1.e1.Age)) //++ // // VOID // MI_INC_WSLE_AGE ( // IN PMMPTE PointerPte, // IN PMMWSLE Wsle, // ) // // Routine Description: // // Increment the age counter for the working set entry. // // Arguments: // // PointerPte - pointer to the working set list entry's PTE. // // Wsle - pointer to the working set list entry. // // Return Value: // // None // //-- #define MI_INC_WSLE_AGE(PointerPte, Wsle) \ if ((Wsle)->u1.e1.Age < 3) { \ (Wsle)->u1.e1.Age += 1; \ } //++ // // VOID // MI_UPDATE_USE_ESTIMATE ( // IN PMMPTE PointerPte, // IN PMMWSLE Wsle, // IN ULONG *SampledAgeCounts // ) // // Routine Description: // // Update the sampled age counts. // // Arguments: // // PointerPte - pointer to the working set list entry's PTE. // // Wsle - pointer to the working set list entry. // // SampledAgeCounts - array of age counts to be updated. // // Return Value: // // None // //-- #define MI_UPDATE_USE_ESTIMATE(PointerPte, Wsle, SampledAgeCounts) \ (SampledAgeCounts)[(Wsle)->u1.e1.Age] += 1; //++ // // BOOLEAN // MI_WS_GROWING_TOO_FAST ( // IN PMMSUPPORT VmSupport // ) // // Routine Description: // // Limit the growth rate of processes as the // available memory approaches zero. Note the caller must ensure that // MmAvailablePages is low enough so this calculation does not wrap. // // Arguments: // // VmSupport - a working set. // // Return Value: // // TRUE if the growth rate is too fast, FALSE otherwise. // //-- #define MI_WS_GROWING_TOO_FAST(VmSupport) \ ((VmSupport)->GrowthSinceLastEstimate > \ (((MI_CLAIM_INCR * (MmAvailablePages*MmAvailablePages)) / (64*64)) + 1)) #define SECTION_BASE_ADDRESS(_NtSection) \ (*((PVOID *)&(_NtSection)->PointerToRelocations)) #define SECTION_LOCK_COUNT_POINTER(_NtSection) \ ((PLONG)&(_NtSection)->NumberOfRelocations) // // Memory Management Object structures. // typedef enum _SECTION_CHECK_TYPE { CheckDataSection, CheckImageSection, CheckUserDataSection, CheckBothSection } SECTION_CHECK_TYPE; typedef struct _MMEXTEND_INFO { UINT64 CommittedSize; ULONG ReferenceCount; } MMEXTEND_INFO, *PMMEXTEND_INFO; typedef struct _SEGMENT { struct _CONTROL_AREA *ControlArea; ULONG TotalNumberOfPtes; ULONG NonExtendedPtes; ULONG WritableUserReferences; UINT64 SizeOfSegment; MMPTE SegmentPteTemplate; SIZE_T NumberOfCommittedPages; PMMEXTEND_INFO ExtendInfo; PVOID SystemImageBase; // could replace with single bit in ldrmodlist PVOID BasedAddress; // // The fields below are for image & pagefile-backed sections only. // Common fields are above and new common entries must be added to // both the SEGMENT and MAPPED_FILE_SEGMENT declarations. // union { SIZE_T ImageCommitment; // for image-backed sections only PEPROCESS CreatingProcess; // for pagefile-backed sections only } u1; union { PSECTION_IMAGE_INFORMATION ImageInformation; // for images only PVOID FirstMappedVa; // for pagefile-backed sections only } u2; PMMPTE PrototypePte; MMPTE ThePtes[MM_PROTO_PTE_ALIGNMENT / PAGE_SIZE]; } SEGMENT, *PSEGMENT; typedef struct _MAPPED_FILE_SEGMENT { struct _CONTROL_AREA *ControlArea; ULONG TotalNumberOfPtes; ULONG NonExtendedPtes; ULONG WritableUserReferences; UINT64 SizeOfSegment; MMPTE SegmentPteTemplate; SIZE_T NumberOfCommittedPages; PMMEXTEND_INFO ExtendInfo; PVOID SystemImageBase; // could replace with single bit in ldrmodlist PVOID BasedAddress; struct _MSUBSECTION *LastSubsectionHint; } MAPPED_FILE_SEGMENT, *PMAPPED_FILE_SEGMENT; typedef struct _EVENT_COUNTER { SINGLE_LIST_ENTRY ListEntry; ULONG RefCount; KEVENT Event; } EVENT_COUNTER, *PEVENT_COUNTER; typedef struct _MMSECTION_FLAGS { unsigned BeingDeleted : 1; unsigned BeingCreated : 1; unsigned BeingPurged : 1; unsigned NoModifiedWriting : 1; unsigned FailAllIo : 1; unsigned Image : 1; unsigned Based : 1; unsigned File : 1; unsigned Networked : 1; unsigned NoCache : 1; unsigned PhysicalMemory : 1; unsigned CopyOnWrite : 1; unsigned Reserve : 1; // not a spare bit! unsigned Commit : 1; unsigned FloppyMedia : 1; unsigned WasPurged : 1; unsigned UserReference : 1; unsigned GlobalMemory : 1; unsigned DeleteOnClose : 1; unsigned FilePointerNull : 1; unsigned DebugSymbolsLoaded : 1; unsigned SetMappedFileIoComplete : 1; unsigned CollidedFlush : 1; unsigned NoChange : 1; unsigned HadUserReference : 1; unsigned ImageMappedInSystemSpace : 1; unsigned UserWritable : 1; unsigned Accessed : 1; unsigned GlobalOnlyPerSession : 1; unsigned Rom : 1; unsigned filler : 2; } MMSECTION_FLAGS; typedef struct _CONTROL_AREA { // must be quadword sized. PSEGMENT Segment; LIST_ENTRY DereferenceList; ULONG NumberOfSectionReferences; ULONG NumberOfPfnReferences; ULONG NumberOfMappedViews; USHORT NumberOfSubsections; USHORT FlushInProgressCount; ULONG NumberOfUserReferences; union { ULONG LongFlags; MMSECTION_FLAGS Flags; } u; PFILE_OBJECT FilePointer; PEVENT_COUNTER WaitingForDeletion; USHORT ModifiedWriteCount; USHORT NumberOfSystemCacheViews; } CONTROL_AREA, *PCONTROL_AREA; typedef struct _LARGE_CONTROL_AREA { // must be quadword sized. PSEGMENT Segment; LIST_ENTRY DereferenceList; ULONG NumberOfSectionReferences; ULONG NumberOfPfnReferences; ULONG NumberOfMappedViews; USHORT NumberOfSubsections; USHORT FlushInProgressCount; ULONG NumberOfUserReferences; union { ULONG LongFlags; MMSECTION_FLAGS Flags; } u; PFILE_OBJECT FilePointer; PEVENT_COUNTER WaitingForDeletion; USHORT ModifiedWriteCount; USHORT NumberOfSystemCacheViews; PFN_NUMBER StartingFrame; // only used if Flags.Rom == 1. LIST_ENTRY UserGlobalList; ULONG SessionId; } LARGE_CONTROL_AREA, *PLARGE_CONTROL_AREA; typedef struct _MMSUBSECTION_FLAGS { unsigned ReadOnly : 1; unsigned ReadWrite : 1; unsigned SubsectionStatic : 1; unsigned GlobalMemory: 1; unsigned Protection : 5; unsigned LargePages : 1; unsigned StartingSector4132 : 10; // 2 ** (42+12) == 4MB*4GB == 16K TB unsigned SectorEndOffset : 12; } MMSUBSECTION_FLAGS; typedef struct _SUBSECTION { // Must start on quadword boundary and be quad sized PCONTROL_AREA ControlArea; union { ULONG LongFlags; MMSUBSECTION_FLAGS SubsectionFlags; } u; ULONG StartingSector; ULONG NumberOfFullSectors; // (4GB-1) * 4K == 16TB-4K limit per subsection PMMPTE SubsectionBase; ULONG UnusedPtes; ULONG PtesInSubsection; struct _SUBSECTION *NextSubsection; } SUBSECTION, *PSUBSECTION; extern const ULONG MMSECT; // // Accesses to MMSUBSECTION_FLAGS2 are synchronized via the PFN lock // (unlike MMSUBSECTION_FLAGS access which is not lock protected at all). // typedef struct _MMSUBSECTION_FLAGS2 { unsigned SubsectionAccessed : 1; unsigned SubsectionConverted : 1; // only needed for debug unsigned Reserved : 30; } MMSUBSECTION_FLAGS2; // // Mapped data file subsection structure. Not used for images // or pagefile-backed shared memory. // typedef struct _MSUBSECTION { // Must start on quadword boundary and be quad sized PCONTROL_AREA ControlArea; union { ULONG LongFlags; MMSUBSECTION_FLAGS SubsectionFlags; } u; ULONG StartingSector; ULONG NumberOfFullSectors; // (4GB-1) * 4K == 16TB-4K limit per subsection PMMPTE SubsectionBase; ULONG UnusedPtes; ULONG PtesInSubsection; struct _SUBSECTION *NextSubsection; LIST_ENTRY DereferenceList; ULONG_PTR NumberOfMappedViews; union { ULONG LongFlags2; MMSUBSECTION_FLAGS2 SubsectionFlags2; } u2; } MSUBSECTION, *PMSUBSECTION; #define MI_MAXIMUM_SECTION_SIZE ((UINT64)16*1024*1024*1024*1024*1024 - (1<StartingSector = ((PLARGE_INTEGER)address)->LowPart; \ subsection->u.SubsectionFlags.StartingSector4132 = \ (((PLARGE_INTEGER)(address))->HighPart & 0x3ff); //++ //ULONG //Mi4KStartFromSubsection ( // IN OUT PLARGE_INTEGER address, // IN PSUBSECTION subsection // ); // // Routine Description: // // This macro gets the start 4K offset from the specified subsection. // // Arguments // // address - Supplies the 64-bit address (in 4K units) to place the // start of this subsection into. // // subsection - Supplies the subsection address to get the address from. // // Return Value: // // None. // //-- #define Mi4KStartFromSubsection(address, subsection) \ ((PLARGE_INTEGER)address)->LowPart = subsection->StartingSector; \ ((PLARGE_INTEGER)address)->HighPart = subsection->u.SubsectionFlags.StartingSector4132; typedef struct _MMDEREFERENCE_SEGMENT_HEADER { KSPIN_LOCK Lock; KSEMAPHORE Semaphore; LIST_ENTRY ListHead; } MMDEREFERENCE_SEGMENT_HEADER; // // This entry is used for calling the segment dereference thread // to perform page file expansion. It has a similar structure // to a control area to allow either a control area or a page file // expansion entry to be placed on the list. Note that for a control // area the segment pointer is valid whereas for page file expansion // it is null. // typedef struct _MMPAGE_FILE_EXPANSION { PSEGMENT Segment; LIST_ENTRY DereferenceList; SIZE_T RequestedExpansionSize; SIZE_T ActualExpansion; KEVENT Event; LONG InProgress; ULONG PageFileNumber; } MMPAGE_FILE_EXPANSION, *PMMPAGE_FILE_EXPANSION; #define MI_EXTEND_ANY_PAGEFILE ((ULONG)-1) #define MI_CONTRACT_PAGEFILES ((SIZE_T)-1) typedef struct _MMWORKING_SET_EXPANSION_HEAD { LIST_ENTRY ListHead; } MMWORKING_SET_EXPANSION_HEAD; #define SUBSECTION_READ_ONLY 1L #define SUBSECTION_READ_WRITE 2L #define SUBSECTION_COPY_ON_WRITE 4L #define SUBSECTION_SHARE_ALLOW 8L // // The MMINPAGE_FLAGS relies on the fact that a pool allocation is always // QUADWORD aligned so the low 3 bits are always available. // typedef struct _MMINPAGE_FLAGS { ULONG_PTR Completed : 1; ULONG_PTR Available1 : 1; ULONG_PTR Available2 : 1; #if defined (_WIN64) ULONG_PTR PrefetchMdlHighBits : 61; #else ULONG_PTR PrefetchMdlHighBits : 29; #endif } MMINPAGE_FLAGS, *PMMINPAGE_FLAGS; #define MI_EXTRACT_PREFETCH_MDL(_Support) ((PMDL)((ULONG_PTR)(_Support->u1.PrefetchMdl) & ~(sizeof(QUAD) - 1))) typedef struct _MMINPAGE_SUPPORT { KEVENT Event; IO_STATUS_BLOCK IoStatus; LARGE_INTEGER ReadOffset; LONG WaitCount; #if defined (_WIN64) ULONG UsedPageTableEntries; #endif PETHREAD Thread; PFILE_OBJECT FilePointer; PMMPTE BasePte; PMMPFN Pfn; union { MMINPAGE_FLAGS e1; ULONG_PTR LongFlags; PMDL PrefetchMdl; // Only used under _PREFETCH_ } u1; MDL Mdl; PFN_NUMBER Page[MM_MAXIMUM_READ_CLUSTER_SIZE + 1]; SINGLE_LIST_ENTRY ListEntry; } MMINPAGE_SUPPORT, *PMMINPAGE_SUPPORT; #define MI_PF_DUMMY_PAGE_PTE ((PMMPTE)0x23452345) // Only used by _PREFETCH_ // // Address Node. // typedef struct _MMADDRESS_NODE { ULONG_PTR StartingVpn; ULONG_PTR EndingVpn; struct _MMADDRESS_NODE *Parent; struct _MMADDRESS_NODE *LeftChild; struct _MMADDRESS_NODE *RightChild; } MMADDRESS_NODE, *PMMADDRESS_NODE; typedef struct _SECTION { MMADDRESS_NODE Address; PSEGMENT Segment; LARGE_INTEGER SizeOfSection; union { ULONG LongFlags; MMSECTION_FLAGS Flags; } u; ULONG InitialPageProtection; } SECTION, *PSECTION; // // Banked memory descriptor. Pointed to by VAD which has // the PhysicalMemory flags set and the Banked pointer field as // non-NULL. // typedef struct _MMBANKED_SECTION { PFN_NUMBER BasePhysicalPage; PMMPTE BasedPte; ULONG BankSize; ULONG BankShift; //shift for PTEs to calculate bank number PBANKED_SECTION_ROUTINE BankedRoutine; PVOID Context; PMMPTE CurrentMappedPte; MMPTE BankTemplate[1]; } MMBANKED_SECTION, *PMMBANKED_SECTION; // // Virtual address descriptor // // ***** NOTE ********** // The first part of a virtual address descriptor is a MMADDRESS_NODE!!! // #if defined (_WIN64) #define COMMIT_SIZE 51 #if ((COMMIT_SIZE + PAGE_SHIFT) < 63) #error COMMIT_SIZE too small #endif #else #define COMMIT_SIZE 19 #if ((COMMIT_SIZE + PAGE_SHIFT) < 31) #error COMMIT_SIZE too small #endif #endif #define MM_MAX_COMMIT (((ULONG_PTR) 1 << COMMIT_SIZE) - 1) #define MM_VIEW_UNMAP 0 #define MM_VIEW_SHARE 1 typedef struct _MMVAD_FLAGS { ULONG_PTR CommitCharge : COMMIT_SIZE; //limits system to 4k pages or bigger! ULONG_PTR PhysicalMapping : 1; ULONG_PTR ImageMap : 1; ULONG_PTR UserPhysicalPages : 1; ULONG_PTR NoChange : 1; ULONG_PTR WriteWatch : 1; ULONG_PTR Protection : 5; ULONG_PTR LargePages : 1; ULONG_PTR MemCommit: 1; ULONG_PTR PrivateMemory : 1; //used to tell VAD from VAD_SHORT } MMVAD_FLAGS; typedef struct _MMVAD_FLAGS2 { unsigned FileOffset : 24; // number of 64k units into file unsigned SecNoChange : 1; // set if SEC_NOCHANGE specified unsigned OneSecured : 1; // set if u3 field is a range unsigned MultipleSecured : 1; // set if u3 field is a list head unsigned ReadOnly : 1; // protected as ReadOnly unsigned LongVad : 1; // set if VAD is a long VAD unsigned ExtendableFile : 1; unsigned Inherit : 1; //1 = ViewShare, 0 = ViewUnmap unsigned CopyOnWrite : 1; } MMVAD_FLAGS2; typedef struct _MMADDRESS_LIST { ULONG_PTR StartVpn; ULONG_PTR EndVpn; } MMADDRESS_LIST, *PMMADDRESS_LIST; typedef struct _MMSECURE_ENTRY { union { ULONG_PTR LongFlags2; MMVAD_FLAGS2 VadFlags2; } u2; ULONG_PTR StartVpn; ULONG_PTR EndVpn; LIST_ENTRY List; } MMSECURE_ENTRY, *PMMSECURE_ENTRY; typedef struct _ALIAS_VAD_INFO { KAPC Apc; ULONG NumberOfEntries; ULONG MaximumEntries; } ALIAS_VAD_INFO, *PALIAS_VAD_INFO; typedef struct _ALIAS_VAD_INFO2 { ULONG BaseAddress; HANDLE SecureHandle; } ALIAS_VAD_INFO2, *PALIAS_VAD_INFO2; typedef struct _MMVAD { ULONG_PTR StartingVpn; ULONG_PTR EndingVpn; struct _MMVAD *Parent; struct _MMVAD *LeftChild; struct _MMVAD *RightChild; union { ULONG_PTR LongFlags; MMVAD_FLAGS VadFlags; } u; PCONTROL_AREA ControlArea; PMMPTE FirstPrototypePte; PMMPTE LastContiguousPte; union { ULONG LongFlags2; MMVAD_FLAGS2 VadFlags2; } u2; } MMVAD, *PMMVAD; typedef struct _MMVAD_LONG { ULONG_PTR StartingVpn; ULONG_PTR EndingVpn; struct _MMVAD *Parent; struct _MMVAD *LeftChild; struct _MMVAD *RightChild; union { ULONG_PTR LongFlags; MMVAD_FLAGS VadFlags; } u; PCONTROL_AREA ControlArea; PMMPTE FirstPrototypePte; PMMPTE LastContiguousPte; union { ULONG LongFlags2; MMVAD_FLAGS2 VadFlags2; } u2; union { LIST_ENTRY List; MMADDRESS_LIST Secured; } u3; union { PMMBANKED_SECTION Banked; PMMEXTEND_INFO ExtendedInfo; } u4; #if defined(_MIALT4K_) PALIAS_VAD_INFO AliasInformation; #endif } MMVAD_LONG, *PMMVAD_LONG; typedef struct _MMVAD_SHORT { ULONG_PTR StartingVpn; ULONG_PTR EndingVpn; struct _MMVAD *Parent; struct _MMVAD *LeftChild; struct _MMVAD *RightChild; union { ULONG_PTR LongFlags; MMVAD_FLAGS VadFlags; } u; } MMVAD_SHORT, *PMMVAD_SHORT; #define MI_GET_PROTECTION_FROM_VAD(_Vad) ((ULONG)(_Vad)->u.VadFlags.Protection) #define MI_PHYSICAL_VIEW_AWE 0x1 // AWE region #define MI_PHYSICAL_VIEW_PHYS 0x2 // Device\PhysicalMemory region typedef struct _MI_PHYSICAL_VIEW { LIST_ENTRY ListEntry; PMMVAD Vad; PCHAR StartVa; PCHAR EndVa; union { ULONG_PTR LongFlags; // physical or AWE Vad identification PRTL_BITMAP BitMap; // only if Vad->u.VadFlags.WriteWatch == 1 } u; } MI_PHYSICAL_VIEW, *PMI_PHYSICAL_VIEW; #define MI_PHYSICAL_VIEW_KEY 'vpmM' #define MI_WRITEWATCH_VIEW_KEY 'wWmM' // // Stuff for support of Write Watch. // extern ULONG_PTR MiActiveWriteWatch; VOID MiCaptureWriteWatchDirtyBit ( IN PEPROCESS Process, IN PVOID VirtualAddress ); // // Stuff for support of AWE (Address Windowing Extensions). // typedef struct _AWEINFO { PRTL_BITMAP VadPhysicalPagesBitMap; ULONG_PTR VadPhysicalPages; // // The PushLock is used to allow most of the NtMapUserPhysicalPages{Scatter} // to execute in parallel as this is acquired shared for these calls. // Exclusive acquisitions are used to protect maps against frees of the // pages as well as to protect updates to the AweVadList. Collisions // should be rare because the exclusive acquisitions should be rare. // PEX_PUSH_LOCK_CACHE_AWARE PushLock; LIST_ENTRY AweVadList; } AWEINFO, *PAWEINFO; VOID MiAweViewInserter ( IN PEPROCESS Process, IN PMI_PHYSICAL_VIEW PhysicalView ); VOID MiAweViewRemover ( IN PEPROCESS Process, IN PMMVAD Vad ); // // Stuff for support of POSIX Fork. // typedef struct _MMCLONE_BLOCK { MMPTE ProtoPte; LONG CloneRefCount; } MMCLONE_BLOCK, *PMMCLONE_BLOCK; typedef struct _MMCLONE_HEADER { ULONG NumberOfPtes; LONG NumberOfProcessReferences; PMMCLONE_BLOCK ClonePtes; } MMCLONE_HEADER, *PMMCLONE_HEADER; typedef struct _MMCLONE_DESCRIPTOR { ULONG_PTR StartingVpn; ULONG_PTR EndingVpn; struct _MMCLONE_DESCRIPTOR *Parent; struct _MMCLONE_DESCRIPTOR *LeftChild; struct _MMCLONE_DESCRIPTOR *RightChild; ULONG NumberOfPtes; PMMCLONE_HEADER CloneHeader; LONG NumberOfReferences; LONG FinalNumberOfReferences; SIZE_T PagedPoolQuotaCharge; } MMCLONE_DESCRIPTOR, *PMMCLONE_DESCRIPTOR; // // The following macro allocates and initializes a bitmap from the // specified pool of the specified size. // // VOID // MiCreateBitMap ( // OUT PRTL_BITMAP *BitMapHeader, // IN SIZE_T SizeOfBitMap, // IN POOL_TYPE PoolType // ); // #define MiCreateBitMap(BMH,S,P) { \ ULONG _S; \ ASSERT ((ULONG64)(S) < _4gb); \ _S = sizeof(RTL_BITMAP) + (ULONG)((((S) + 31) / 32) * 4); \ *(BMH) = (PRTL_BITMAP)ExAllocatePoolWithTag( (P), _S, ' mM'); \ if (*(BMH)) { \ RtlInitializeBitMap( *(BMH), (PULONG)((*(BMH))+1), (ULONG)(S)); \ } \ } #define MiRemoveBitMap(BMH) { \ ExFreePool(*(BMH)); \ *(BMH) = NULL; \ } #define MI_INITIALIZE_ZERO_MDL(MDL) { \ MDL->Next = (PMDL) NULL; \ MDL->MdlFlags = 0; \ MDL->StartVa = NULL; \ MDL->ByteOffset = 0; \ MDL->ByteCount = 0; \ } // // Page File structures. // typedef struct _MMMOD_WRITER_LISTHEAD { LIST_ENTRY ListHead; KEVENT Event; } MMMOD_WRITER_LISTHEAD, *PMMMOD_WRITER_LISTHEAD; typedef struct _MMMOD_WRITER_MDL_ENTRY { LIST_ENTRY Links; LARGE_INTEGER WriteOffset; union { IO_STATUS_BLOCK IoStatus; LARGE_INTEGER LastByte; } u; PIRP Irp; ULONG_PTR LastPageToWrite; PMMMOD_WRITER_LISTHEAD PagingListHead; PLIST_ENTRY CurrentList; struct _MMPAGING_FILE *PagingFile; PFILE_OBJECT File; PCONTROL_AREA ControlArea; PERESOURCE FileResource; MDL Mdl; PFN_NUMBER Page[1]; } MMMOD_WRITER_MDL_ENTRY, *PMMMOD_WRITER_MDL_ENTRY; #define MM_PAGING_FILE_MDLS 2 typedef struct _MMPAGING_FILE { PFN_NUMBER Size; PFN_NUMBER MaximumSize; PFN_NUMBER MinimumSize; PFN_NUMBER FreeSpace; PFN_NUMBER CurrentUsage; PFN_NUMBER PeakUsage; PFN_NUMBER Hint; PFN_NUMBER HighestPage; PMMMOD_WRITER_MDL_ENTRY Entry[MM_PAGING_FILE_MDLS]; PRTL_BITMAP Bitmap; PFILE_OBJECT File; UNICODE_STRING PageFileName; ULONG PageFileNumber; BOOLEAN Extended; BOOLEAN HintSetToZero; BOOLEAN BootPartition; HANDLE FileHandle; } MMPAGING_FILE, *PMMPAGING_FILE; // // System PTE structures. // typedef enum _MMSYSTEM_PTE_POOL_TYPE { SystemPteSpace, NonPagedPoolExpansion, MaximumPtePoolTypes } MMSYSTEM_PTE_POOL_TYPE; typedef struct _MMFREE_POOL_ENTRY { LIST_ENTRY List; // maintained free&chk, 1st entry only PFN_NUMBER Size; // maintained free&chk, 1st entry only ULONG Signature; // maintained chk only, all entries struct _MMFREE_POOL_ENTRY *Owner; // maintained free&chk, all entries } MMFREE_POOL_ENTRY, *PMMFREE_POOL_ENTRY; typedef struct _MMLOCK_CONFLICT { LIST_ENTRY List; PETHREAD Thread; } MMLOCK_CONFLICT, *PMMLOCK_CONFLICT; // // System view structures // typedef struct _MMVIEW { ULONG_PTR Entry; PCONTROL_AREA ControlArea; } MMVIEW, *PMMVIEW; // // The MMSESSION structure represents kernel memory that is only valid on a // per-session basis, thus the calling thread must be in the proper session // to access this structure. // typedef struct _MMSESSION { // // Never refer to the SystemSpaceViewLock directly - always use the pointer // following it or you will break support for multiple concurrent sessions. // FAST_MUTEX SystemSpaceViewLock; // // This points to the mutex above and is needed because the MMSESSION // is mapped in session space on Hydra and the mutex needs to be globally // visible for proper KeWaitForSingleObject & KeSetEvent operation. // PFAST_MUTEX SystemSpaceViewLockPointer; PCHAR SystemSpaceViewStart; PMMVIEW SystemSpaceViewTable; ULONG SystemSpaceHashSize; ULONG SystemSpaceHashEntries; ULONG SystemSpaceHashKey; PRTL_BITMAP SystemSpaceBitMap; } MMSESSION, *PMMSESSION; extern MMSESSION MmSession; #define LOCK_SYSTEM_VIEW_SPACE(_Session) \ ExAcquireFastMutex (_Session->SystemSpaceViewLockPointer) #define UNLOCK_SYSTEM_VIEW_SPACE(_Session) \ ExReleaseFastMutex (_Session->SystemSpaceViewLockPointer) // // List for flushing TBs singularly. // typedef struct _MMPTE_FLUSH_LIST { ULONG Count; PMMPTE FlushPte[MM_MAXIMUM_FLUSH_COUNT]; PVOID FlushVa[MM_MAXIMUM_FLUSH_COUNT]; } MMPTE_FLUSH_LIST, *PMMPTE_FLUSH_LIST; typedef struct _LOCK_TRACKER { LIST_ENTRY ListEntry; PMDL Mdl; PVOID StartVa; PFN_NUMBER Count; ULONG Offset; ULONG Length; PFN_NUMBER Page; PVOID CallingAddress; PVOID CallersCaller; LIST_ENTRY GlobalListEntry; ULONG Who; PEPROCESS Process; } LOCK_TRACKER, *PLOCK_TRACKER; extern LOGICAL MmTrackLockedPages; extern BOOLEAN MiTrackingAborted; extern KSPIN_LOCK MiTrackLockedPagesLock; typedef struct _LOCK_HEADER { LIST_ENTRY ListHead; PFN_NUMBER Count; } LOCK_HEADER, *PLOCK_HEADER; extern LOGICAL MmSnapUnloads; #define MI_UNLOADED_DRIVERS 50 extern ULONG MmLastUnloadedDriver; extern PUNLOADED_DRIVERS MmUnloadedDrivers; VOID MiInitMachineDependent ( IN PLOADER_PARAMETER_BLOCK LoaderBlock ); VOID MiReportPhysicalMemory ( VOID ); extern PFN_NUMBER MiNumberOfCompressionPages; NTSTATUS MiArmCompressionInterrupt ( VOID ); VOID MiBuildPagedPool ( VOID ); VOID MiInitializeNonPagedPool ( VOID ); LOGICAL MiInitializeSystemSpaceMap ( PVOID Session OPTIONAL ); VOID MiFindInitializationCode ( OUT PVOID *StartVa, OUT PVOID *EndVa ); VOID MiFreeInitializationCode ( IN PVOID StartVa, IN PVOID EndVa ); extern ULONG MiNonCachedCollisions; // // If /NOLOWMEM is used, this is set to the boundary PFN (pages below this // value are not used whenever possible). // extern PFN_NUMBER MiNoLowMemory; PVOID MiAllocateLowMemory ( IN SIZE_T NumberOfBytes, IN PFN_NUMBER LowestAcceptablePfn, IN PFN_NUMBER HighestAcceptablePfn, IN PFN_NUMBER BoundaryPfn, IN PVOID CallingAddress, IN MEMORY_CACHING_TYPE CacheType, IN ULONG Tag ); LOGICAL MiFreeLowMemory ( IN PVOID BaseAddress, IN ULONG Tag ); // // Move drivers out of the low 16mb that ntldr placed them at - this makes more // memory below 16mb available for ISA-type drivers that cannot run without it. // extern LOGICAL MmMakeLowMemory; VOID MiRemoveLowPages ( IN ULONG RemovePhase ); ULONG MiSectionInitialization ( VOID ); #define MI_MAX_DEREFERENCE_CHUNK (64 * 1024 / PAGE_SIZE) typedef struct _MI_PFN_DEREFERENCE_CHUNK { SINGLE_LIST_ENTRY ListEntry; CSHORT Flags; USHORT NumberOfPages; PFN_NUMBER Pfns[MI_MAX_DEREFERENCE_CHUNK]; } MI_PFN_DEREFERENCE_CHUNK, *PMI_PFN_DEREFERENCE_CHUNK; extern SLIST_HEADER MmPfnDereferenceSListHead; extern PSINGLE_LIST_ENTRY MmPfnDeferredList; #define MI_DEFER_PFN_HELD 0x1 #define MI_DEFER_DRAIN_LOCAL_ONLY 0x2 VOID MiDeferredUnlockPages ( ULONG Flags ); LOGICAL MiFreeAllExpansionNonPagedPool ( IN LOGICAL PoolLockHeld ); VOID FASTCALL MiDecrementReferenceCount ( IN PFN_NUMBER PageFrameIndex ); //++ //VOID //MiDecrementReferenceCountInline ( // IN PMMPFN PFN // IN PFN_NUMBER FRAME // ); // // Routine Description: // // MiDecrementReferenceCountInline decrements the reference count inline, // only calling MiDecrementReferenceCount if the count would go to zero // which would cause the page to be released. // // Arguments: // // PFN - Supplies the PFN to decrement. // // FRAME - Supplies the frame matching the above PFN. // // Return Value: // // None. // // Environment: // // PFN lock held. // //-- #define MiDecrementReferenceCountInline(PFN, FRAME) \ MM_PFN_LOCK_ASSERT(); \ ASSERT (MI_PFN_ELEMENT(FRAME) == (PFN)); \ ASSERT ((FRAME) <= MmHighestPhysicalPage); \ ASSERT ((PFN)->u3.e2.ReferenceCount != 0); \ if ((PFN)->u3.e2.ReferenceCount != 1) { \ (PFN)->u3.e2.ReferenceCount -= 1; \ } \ else { \ MiDecrementReferenceCount (FRAME); \ } VOID FASTCALL MiDecrementShareCount ( IN PFN_NUMBER PageFrameIndex ); #define MiDecrementShareCountOnly(P) MiDecrementShareCount(P) #define MiDecrementShareAndValidCount(P) MiDecrementShareCount(P) //++ //VOID //MiDecrementShareCountInline ( // IN PMMPFN PFN, // IN PFN_NUMBER FRAME // ); // // Routine Description: // // MiDecrementShareCountInline decrements the share count inline, // only calling MiDecrementShareCount if the count would go to zero // which would cause the page to be released. // // Arguments: // // PFN - Supplies the PFN to decrement. // // FRAME - Supplies the frame matching the above PFN. // // Return Value: // // None. // // Environment: // // PFN lock held. // //-- #define MiDecrementShareCountInline(PFN, FRAME) \ MM_PFN_LOCK_ASSERT(); \ ASSERT (((FRAME) <= MmHighestPhysicalPage) && ((FRAME) > 0)); \ ASSERT (MI_PFN_ELEMENT(FRAME) == (PFN)); \ ASSERT ((PFN)->u2.ShareCount != 0); \ if ((PFN)->u3.e1.PageLocation != ActiveAndValid && (PFN)->u3.e1.PageLocation != StandbyPageList) { \ KeBugCheckEx (PFN_LIST_CORRUPT, 0x99, FRAME, (PFN)->u3.e1.PageLocation, 0); \ } \ if ((PFN)->u2.ShareCount != 1) { \ (PFN)->u2.ShareCount -= 1; \ PERFINFO_DECREFCNT((PFN), PERF_SOFT_TRIM, PERFINFO_LOG_TYPE_DECSHARCNT); \ ASSERT ((PFN)->u2.ShareCount < 0xF000000); \ } \ else { \ MiDecrementShareCount (FRAME); \ } // // Routines which operate on the Page Frame Database Lists // VOID FASTCALL MiInsertPageInList ( IN PMMPFNLIST ListHead, IN PFN_NUMBER PageFrameIndex ); VOID FASTCALL MiInsertPageInFreeList ( IN PFN_NUMBER PageFrameIndex ); VOID FASTCALL MiInsertStandbyListAtFront ( IN PFN_NUMBER PageFrameIndex ); PFN_NUMBER //PageFrameIndex FASTCALL MiRemovePageFromList ( IN PMMPFNLIST ListHead ); VOID FASTCALL MiUnlinkPageFromList ( IN PMMPFN Pfn ); VOID MiUnlinkFreeOrZeroedPage ( IN PFN_NUMBER Page ); VOID FASTCALL MiInsertFrontModifiedNoWrite ( IN PFN_NUMBER PageFrameIndex ); #define MM_MEDIUM_LIMIT 32 #define MM_HIGH_LIMIT 128 ULONG FASTCALL MiEnsureAvailablePageOrWait ( IN PEPROCESS Process, IN PVOID VirtualAddress ); PFN_NUMBER MiAllocatePfn ( IN PMMPTE PointerPte, IN ULONG Protection ); PFN_NUMBER FASTCALL MiRemoveAnyPage ( IN ULONG PageColor ); PFN_NUMBER FASTCALL MiRemoveZeroPage ( IN ULONG PageColor ); VOID MiPurgeTransitionList ( VOID ); PVOID MiFindContiguousMemory ( IN PFN_NUMBER LowestPfn, IN PFN_NUMBER HighestPfn, IN PFN_NUMBER BoundaryPfn, IN PFN_NUMBER SizeInPages, IN MEMORY_CACHING_TYPE CacheType, IN PVOID CallingAddress ); PVOID MiCheckForContiguousMemory ( IN PVOID BaseAddress, IN PFN_NUMBER BaseAddressPages, IN PFN_NUMBER SizeInPages, IN PFN_NUMBER LowestPfn, IN PFN_NUMBER HighestPfn, IN PFN_NUMBER BoundaryPfn, IN MI_PFN_CACHE_ATTRIBUTE CacheAttribute ); // // Routines which operate on the page frame database entry. // VOID MiInitializePfn ( IN PFN_NUMBER PageFrameIndex, IN PMMPTE PointerPte, IN ULONG ModifiedState ); VOID MiInitializePfnForOtherProcess ( IN PFN_NUMBER PageFrameIndex, IN PMMPTE PointerPte, IN PFN_NUMBER ContainingPageFrame ); VOID MiInitializeCopyOnWritePfn ( IN PFN_NUMBER PageFrameIndex, IN PMMPTE PointerPte, IN WSLE_NUMBER WorkingSetIndex, IN PVOID SessionSpace ); VOID MiInitializeTransitionPfn ( IN PFN_NUMBER PageFrameIndex, IN PMMPTE PointerPte ); extern SLIST_HEADER MmInPageSupportSListHead; VOID MiFreeInPageSupportBlock ( IN PMMINPAGE_SUPPORT Support ); PMMINPAGE_SUPPORT MiGetInPageSupportBlock ( IN LOGICAL PfnHeld, IN PEPROCESS Process ); // // Routines which require a physical page to be mapped into hyperspace // within the current process. // VOID FASTCALL MiZeroPhysicalPage ( IN PFN_NUMBER PageFrameIndex, IN ULONG Color ); VOID FASTCALL MiRestoreTransitionPte ( IN PFN_NUMBER PageFrameIndex ); PSUBSECTION MiGetSubsectionAndProtoFromPte ( IN PMMPTE PointerPte, IN PMMPTE *ProtoPte ); PVOID MiMapPageInHyperSpace ( IN PEPROCESS Process, IN PFN_NUMBER PageFrameIndex, OUT PKIRQL OldIrql ); PVOID MiMapPageInHyperSpaceAtDpc ( IN PEPROCESS Process, IN PFN_NUMBER PageFrameIndex ); #define MiUnmapPageInZeroSpace(VA) \ MiGetPteAddress(VA)->u.Long = 0; PVOID MiMapImageHeaderInHyperSpace ( IN PFN_NUMBER PageFrameIndex ); VOID MiUnmapImageHeaderInHyperSpace ( VOID ); VOID MiUpdateImageHeaderPage ( IN PMMPTE PointerPte, IN PFN_NUMBER PageFrameNumber, IN PCONTROL_AREA ControlArea ); PFN_NUMBER MiGetPageForHeader ( VOID ); VOID MiRemoveImageHeaderPage ( IN PFN_NUMBER PageFrameNumber ); PVOID MiMapPageToZeroInHyperSpace ( IN PFN_NUMBER PageFrameIndex ); NTSTATUS MiGetWritablePagesInSection( IN PSECTION Section, OUT PULONG WritablePages ); // // Routines to obtain and release system PTEs. // PMMPTE MiReserveSystemPtes ( IN ULONG NumberOfPtes, IN MMSYSTEM_PTE_POOL_TYPE SystemPteType ); PMMPTE MiReserveAlignedSystemPtes ( IN ULONG NumberOfPtes, IN MMSYSTEM_PTE_POOL_TYPE SystemPtePoolType, IN ULONG Alignment ); VOID MiReleaseSystemPtes ( IN PMMPTE StartingPte, IN ULONG NumberOfPtes, IN MMSYSTEM_PTE_POOL_TYPE SystemPteType ); VOID MiReleaseSplitSystemPtes ( IN PMMPTE StartingPte, IN ULONG NumberOfPtes, IN MMSYSTEM_PTE_POOL_TYPE SystemPtePoolType ); VOID MiIncrementSystemPtes ( IN ULONG NumberOfPtes ); LOGICAL MiGetSystemPteAvailability ( IN ULONG NumberOfPtes, IN MM_PAGE_PRIORITY Priority ); VOID MiIssueNoPtesBugcheck ( IN ULONG NumberOfPtes, IN MMSYSTEM_PTE_POOL_TYPE SystemPteType ); VOID MiInitializeSystemPtes ( IN PMMPTE StartingPte, IN ULONG NumberOfPtes, IN MMSYSTEM_PTE_POOL_TYPE SystemPteType ); NTSTATUS MiAddMappedPtes ( IN PMMPTE FirstPte, IN ULONG NumberOfPtes, IN PCONTROL_AREA ControlArea ); VOID MiInitializeIoTrackers ( VOID ); PVOID MiMapSinglePage ( IN PVOID VirtualAddress OPTIONAL, IN PFN_NUMBER PageFrameIndex, IN MEMORY_CACHING_TYPE CacheType, IN MM_PAGE_PRIORITY Priority ); VOID MiUnmapSinglePage ( IN PVOID BaseAddress ); typedef struct _MM_PTE_MAPPING { LIST_ENTRY ListEntry; PVOID SystemVa; PVOID SystemEndVa; ULONG Protection; } MM_PTE_MAPPING, *PMM_PTE_MAPPING; extern LIST_ENTRY MmProtectedPteList; extern KSPIN_LOCK MmProtectedPteLock; LOGICAL MiCheckSystemPteProtection ( IN ULONG_PTR StoreInstruction, IN PVOID VirtualAddress ); // // Access Fault routines. // #define STATUS_ISSUE_PAGING_IO (0xC0033333) NTSTATUS MiDispatchFault ( IN ULONG_PTR FaultStatus, IN PVOID VirtualAdress, IN PMMPTE PointerPte, IN PMMPTE PointerProtoPte, IN PEPROCESS Process, OUT PLOGICAL ApcNeeded ); NTSTATUS MiResolveDemandZeroFault ( IN PVOID VirtualAddress, IN PMMPTE PointerPte, IN PEPROCESS Process, IN ULONG PrototypePte ); NTSTATUS MiResolveTransitionFault ( IN PVOID FaultingAddress, IN PMMPTE PointerPte, IN PEPROCESS Process, IN ULONG PfnLockHeld, OUT PLOGICAL ApcNeeded, OUT PMMINPAGE_SUPPORT *InPageBlock ); NTSTATUS MiResolvePageFileFault ( IN PVOID FaultingAddress, IN PMMPTE PointerPte, IN PMMINPAGE_SUPPORT *ReadBlock, IN PEPROCESS Process ); NTSTATUS MiResolveProtoPteFault ( IN ULONG_PTR StoreInstruction, IN PVOID VirtualAddress, IN PMMPTE PointerPte, IN PMMPTE PointerProtoPte, IN PMMINPAGE_SUPPORT *ReadBlock, IN PEPROCESS Process, OUT PLOGICAL ApcNeeded ); NTSTATUS MiResolveMappedFileFault ( IN PVOID FaultingAddress, IN PMMPTE PointerPte, IN PMMINPAGE_SUPPORT *ReadBlock, IN PEPROCESS Process ); VOID MiAddValidPageToWorkingSet ( IN PVOID VirtualAddress, IN PMMPTE PointerPte, IN PMMPFN Pfn1, IN ULONG WsleMask ); NTSTATUS MiWaitForInPageComplete ( IN PMMPFN Pfn, IN PMMPTE PointerPte, IN PVOID FaultingAddress, IN PMMPTE PointerPteContents, IN PMMINPAGE_SUPPORT InPageSupport, IN PEPROCESS CurrentProcess ); LOGICAL FASTCALL MiCopyOnWrite ( IN PVOID FaultingAddress, IN PMMPTE PointerPte ); VOID MiSetDirtyBit ( IN PVOID FaultingAddress, IN PMMPTE PointerPte, IN ULONG PfnHeld ); VOID MiSetModifyBit ( IN PMMPFN Pfn ); PMMPTE MiFindActualFaultingPte ( IN PVOID FaultingAddress ); VOID MiInitializeReadInProgressSinglePfn ( IN PFN_NUMBER PageFrameIndex, IN PMMPTE BasePte, IN PKEVENT Event, IN WSLE_NUMBER WorkingSetIndex ); VOID MiInitializeReadInProgressPfn ( IN PMDL Mdl, IN PMMPTE BasePte, IN PKEVENT Event, IN WSLE_NUMBER WorkingSetIndex ); NTSTATUS MiAccessCheck ( IN PMMPTE PointerPte, IN ULONG_PTR WriteOperation, IN KPROCESSOR_MODE PreviousMode, IN ULONG Protection, IN BOOLEAN CallerHoldsPfnLock ); NTSTATUS FASTCALL MiCheckForUserStackOverflow ( IN PVOID FaultingAddress ); PMMPTE MiCheckVirtualAddress ( IN PVOID VirtualAddress, OUT PULONG ProtectCode ); NTSTATUS FASTCALL MiCheckPdeForPagedPool ( IN PVOID VirtualAddress ); // // Routines which operate on an address tree. // PMMADDRESS_NODE FASTCALL MiGetNextNode ( IN PMMADDRESS_NODE Node ); PMMADDRESS_NODE FASTCALL MiGetPreviousNode ( IN PMMADDRESS_NODE Node ); PMMADDRESS_NODE FASTCALL MiGetFirstNode ( IN PMMADDRESS_NODE Root ); PMMADDRESS_NODE MiGetLastNode ( IN PMMADDRESS_NODE Root ); VOID FASTCALL MiInsertNode ( IN PMMADDRESS_NODE Node, IN OUT PMMADDRESS_NODE *Root ); VOID FASTCALL MiRemoveNode ( IN PMMADDRESS_NODE Node, IN OUT PMMADDRESS_NODE *Root ); PMMADDRESS_NODE FASTCALL MiLocateAddressInTree ( IN ULONG_PTR Vpn, IN PMMADDRESS_NODE *Root ); PMMADDRESS_NODE MiCheckForConflictingNode ( IN ULONG_PTR StartVpn, IN ULONG_PTR EndVpn, IN PMMADDRESS_NODE Root ); NTSTATUS MiFindEmptyAddressRangeInTree ( IN SIZE_T SizeOfRange, IN ULONG_PTR Alignment, IN PMMADDRESS_NODE Root, OUT PMMADDRESS_NODE *PreviousVad, OUT PVOID *Base ); NTSTATUS MiFindEmptyAddressRangeDownTree ( IN SIZE_T SizeOfRange, IN PVOID HighestAddressToEndAt, IN ULONG_PTR Alignment, IN PMMADDRESS_NODE Root, OUT PVOID *Base ); VOID NodeTreeWalk ( PMMADDRESS_NODE Start ); // // Routines which operate on the tree of virtual address descriptors. // NTSTATUS MiInsertVad ( IN PMMVAD Vad ); VOID MiRemoveVad ( IN PMMVAD Vad ); PMMVAD FASTCALL MiLocateAddress ( IN PVOID Vad ); NTSTATUS MiFindEmptyAddressRange ( IN SIZE_T SizeOfRange, IN ULONG_PTR Alignment, IN ULONG QuickCheck, IN PVOID *Base ); // // Routines which operate on the clone tree structure. // NTSTATUS MiCloneProcessAddressSpace ( IN PEPROCESS ProcessToClone, IN PEPROCESS ProcessToInitialize, IN PFN_NUMBER PdePhysicalPage, IN PFN_NUMBER HyperPhysicalPage ); ULONG MiDecrementCloneBlockReference ( IN PMMCLONE_DESCRIPTOR CloneDescriptor, IN PMMCLONE_BLOCK CloneBlock, IN PEPROCESS CurrentProcess ); LOGICAL MiWaitForForkToComplete ( IN PEPROCESS CurrentProcess, IN LOGICAL PfnHeld ); // // Routines which operate on the working set list. // WSLE_NUMBER MiLocateAndReserveWsle ( IN PMMSUPPORT WsInfo ); VOID MiReleaseWsle ( IN WSLE_NUMBER WorkingSetIndex, IN PMMSUPPORT WsInfo ); VOID MiUpdateWsle ( IN PWSLE_NUMBER DesiredIndex, IN PVOID VirtualAddress, IN PMMWSL WorkingSetList, IN PMMPFN Pfn ); VOID MiInitializeWorkingSetList ( IN PEPROCESS CurrentProcess ); VOID MiGrowWsleHash ( IN PMMSUPPORT WsInfo ); WSLE_NUMBER MiTrimWorkingSet ( IN WSLE_NUMBER Reduction, IN PMMSUPPORT WsInfo, IN ULONG TrimAge ); LOGICAL MmTrimProcessMemory ( IN LOGICAL PurgeTransition ); LOGICAL MmTrimSessionMemory ( IN LOGICAL PurgeTransition ); VOID MiRemoveWorkingSetPages ( IN PMMWSL WorkingSetList, IN PMMSUPPORT WsInfo ); VOID MiAgeAndEstimateAvailableInWorkingSet ( IN PMMSUPPORT VmSupport, IN LOGICAL DoAging, IN PWSLE_NUMBER WslesScanned, IN OUT PPFN_NUMBER TotalClaim, IN OUT PPFN_NUMBER TotalEstimatedAvailable ); VOID FASTCALL MiInsertWsleHash ( IN WSLE_NUMBER Entry, IN PMMWSL WorkingSetList ); VOID FASTCALL MiRemoveWsle ( IN WSLE_NUMBER Entry, IN PMMWSL WorkingSetList ); WSLE_NUMBER FASTCALL MiLocateWsle ( IN PVOID VirtualAddress, IN PMMWSL WorkingSetList, IN WSLE_NUMBER WsPfnIndex ); ULONG MiFreeWsle ( IN WSLE_NUMBER WorkingSetIndex, IN PMMSUPPORT WsInfo, IN PMMPTE PointerPte ); VOID MiSwapWslEntries ( IN WSLE_NUMBER SwapEntry, IN WSLE_NUMBER Entry, IN PMMSUPPORT WsInfo ); VOID MiRemoveWsleFromFreeList ( IN WSLE_NUMBER Entry, IN PMMWSLE Wsle, IN PMMWSL WorkingSetList ); ULONG MiRemovePageFromWorkingSet ( IN PMMPTE PointerPte, IN PMMPFN Pfn1, IN PMMSUPPORT WsInfo ); PFN_NUMBER MiDeleteSystemPagableVm ( IN PMMPTE PointerPte, IN PFN_NUMBER NumberOfPtes, IN MMPTE NewPteValue, IN LOGICAL SessionAllocation, OUT PPFN_NUMBER ResidentPages OPTIONAL ); VOID MiLockCode ( IN PMMPTE FirstPte, IN PMMPTE LastPte, IN ULONG LockType ); PKLDR_DATA_TABLE_ENTRY MiLookupDataTableEntry ( IN PVOID AddressWithinSection, IN ULONG ResourceHeld ); // // Routines which perform working set management. // VOID MiObtainFreePages ( VOID ); VOID MiModifiedPageWriter ( IN PVOID StartContext ); VOID MiMappedPageWriter ( IN PVOID StartContext ); LOGICAL MiIssuePageExtendRequest ( IN PMMPAGE_FILE_EXPANSION PageExtend ); VOID MiIssuePageExtendRequestNoWait ( IN PFN_NUMBER SizeInPages ); SIZE_T MiExtendPagingFiles ( IN PMMPAGE_FILE_EXPANSION PageExpand ); VOID MiContractPagingFiles ( VOID ); VOID MiAttemptPageFileReduction ( VOID ); LOGICAL MiCancelWriteOfMappedPfn ( IN PFN_NUMBER PageToStop ); // // Routines to delete address space. // VOID MiDeletePteRange ( IN PEPROCESS Process, IN PMMPTE PointerPte, IN PMMPTE LastPte, IN LOGICAL AddressSpaceDeletion ); VOID MiDeleteVirtualAddresses ( IN PUCHAR StartingAddress, IN PUCHAR EndingAddress, IN ULONG AddressSpaceDeletion, IN PMMVAD Vad ); ULONG MiDeletePte ( IN PMMPTE PointerPte, IN PVOID VirtualAddress, IN ULONG AddressSpaceDeletion, IN PEPROCESS CurrentProcess, IN PMMPTE PrototypePte, IN PMMPTE_FLUSH_LIST PteFlushList OPTIONAL ); VOID MiDeletePageTablesForPhysicalRange ( IN PVOID StartingAddress, IN PVOID EndingAddress ); VOID MiFlushPteList ( IN PMMPTE_FLUSH_LIST PteFlushList, IN ULONG AllProcessors, IN MMPTE FillPte ); ULONG FASTCALL MiReleasePageFileSpace ( IN MMPTE PteContents ); VOID FASTCALL MiReleaseConfirmedPageFileSpace ( IN MMPTE PteContents ); VOID FASTCALL MiUpdateModifiedWriterMdls ( IN ULONG PageFileNumber ); PVOID MiAllocateAweInfo ( VOID ); VOID MiRemoveUserPhysicalPagesVad ( IN PMMVAD_SHORT FoundVad ); VOID MiCleanPhysicalProcessPages ( IN PEPROCESS Process ); VOID MiPhysicalViewRemover ( IN PEPROCESS Process, IN PMMVAD Vad ); VOID MiPhysicalViewAdjuster ( IN PEPROCESS Process, IN PMMVAD OldVad, IN PMMVAD NewVad ); LOGICAL MiIsPhysicalMemoryAddress ( IN PFN_NUMBER PageFrameIndex, IN OUT PULONG Hint, IN LOGICAL PfnLockNeeded ); // // MM_SYSTEM_PAGE_COLOR - MmSystemPageColor // // This variable is updated frequently, on MP systems we keep // a separate system color per processor to avoid cache line // thrashing. // #if defined(NT_UP) #define MI_SYSTEM_PAGE_COLOR MmSystemPageColor #else #define MI_SYSTEM_PAGE_COLOR (KeGetCurrentPrcb()->PageColor) #endif #if defined(MI_MULTINODE) extern PKNODE KeNodeBlock[]; #define MI_NODE_FROM_COLOR(c) \ (KeNodeBlock[(c) >> MmSecondaryColorNodeShift]) #define MI_GET_COLOR_FROM_LIST_ENTRY(index,pfn) \ ((ULONG)(((pfn)->u3.e1.PageColor << MmSecondaryColorNodeShift) | \ MI_GET_SECONDARY_COLOR((index),(pfn)))) #define MI_ADJUST_COLOR_FOR_NODE(c,n) ((c) | (n)->Color) #define MI_CURRENT_NODE_COLOR (KeGetCurrentNode()->MmShiftedColor) #define MiRemoveZeroPageIfAny(c) \ (KeGetCurrentNode()->FreeCount[ZeroedPageList] ? MiRemoveZeroPage(c) : 0) #define MI_GET_PAGE_COLOR_NODE(n) \ (((MI_SYSTEM_PAGE_COLOR++) & MmSecondaryColorMask) | \ KeNodeBlock[n]->MmShiftedColor) #else #define MI_NODE_FROM_COLOR(c) #define MI_GET_COLOR_FROM_LIST_ENTRY(index,pfn) \ ((ULONG)MI_GET_SECONDARY_COLOR((index),(pfn))) #define MI_ADJUST_COLOR_FOR_NODE(c,n) (c) #define MI_CURRENT_NODE_COLOR 0 #define MiRemoveZeroPageIfAny(COLOR) \ (MmFreePagesByColor[ZeroedPageList][COLOR].Flink != MM_EMPTY_LIST) ? \ MiRemoveZeroPage(COLOR) : 0 #define MI_GET_PAGE_COLOR_NODE(n) \ ((MI_SYSTEM_PAGE_COLOR++) & MmSecondaryColorMask) #endif FORCEINLINE PFN_NUMBER MiRemoveZeroPageMayReleaseLocks ( IN ULONG Color, IN KIRQL OldIrql ) /*++ Routine Description: This routine returns a zeroed page. It may release and reacquire the PFN lock to do so, as well as mapping the page in hyperspace to perform the actual zeroing if necessary. Environment: Kernel mode. PFN lock held, hyperspace lock NOT held. --*/ { PFN_NUMBER PageFrameIndex; PageFrameIndex = MiRemoveZeroPageIfAny (Color); if (PageFrameIndex == 0) { PageFrameIndex = MiRemoveAnyPage (Color); UNLOCK_PFN (OldIrql); MiZeroPhysicalPage (PageFrameIndex, Color); LOCK_PFN (OldIrql); } return PageFrameIndex; } // // General support routines. // #if (_MI_PAGING_LEVELS <= 3) //++ //PMMPTE //MiGetPxeAddress ( // IN PVOID va // ); // // Routine Description: // // MiGetPxeAddress returns the address of the extended page directory parent // entry which maps the given virtual address. This is one level above the // page parent directory. // // Arguments // // Va - Supplies the virtual address to locate the PXE for. // // Return Value: // // The address of the PXE. // //-- #define MiGetPxeAddress(va) ((PMMPTE)0) //++ //LOGICAL //MiIsPteOnPxeBoundary ( // IN PVOID PTE // ); // // Routine Description: // // MiIsPteOnPxeBoundary returns TRUE if the PTE is // on an extended page directory parent entry boundary. // // Arguments // // PTE - Supplies the PTE to check. // // Return Value: // // TRUE if on a boundary, FALSE if not. // //-- #define MiIsPteOnPxeBoundary(PTE) (FALSE) #endif #if (_MI_PAGING_LEVELS <= 2) //++ //PMMPTE //MiGetPpeAddress ( // IN PVOID va // ); // // Routine Description: // // MiGetPpeAddress returns the address of the page directory parent entry // which maps the given virtual address. This is one level above the // page directory. // // Arguments // // Va - Supplies the virtual address to locate the PPE for. // // Return Value: // // The address of the PPE. // //-- #define MiGetPpeAddress(va) ((PMMPTE)0) //++ //LOGICAL //MiIsPteOnPpeBoundary ( // IN PVOID VA // ); // // Routine Description: // // MiIsPteOnPpeBoundary returns TRUE if the PTE is // on a page directory parent entry boundary. // // Arguments // // VA - Supplies the virtual address to check. // // Return Value: // // TRUE if on a boundary, FALSE if not. // //-- #define MiIsPteOnPpeBoundary(PTE) (FALSE) #endif ULONG MiDoesPdeExistAndMakeValid ( IN PMMPTE PointerPde, IN PEPROCESS TargetProcess, IN LOGICAL PfnLockHeld, OUT PULONG Waited ); #if (_MI_PAGING_LEVELS >= 3) #define MiDoesPpeExistAndMakeValid(PPE, PROCESS, PFNLOCKHELD, WAITED) \ MiDoesPdeExistAndMakeValid(PPE, PROCESS, PFNLOCKHELD, WAITED) #else #define MiDoesPpeExistAndMakeValid(PPE, PROCESS, PFNLOCKHELD, WAITED) 1 #endif #if (_MI_PAGING_LEVELS >= 4) #define MiDoesPxeExistAndMakeValid(PXE, PROCESS, PFNLOCKHELD, WAITED) \ MiDoesPdeExistAndMakeValid(PXE, PROCESS, PFNLOCKHELD, WAITED) #else #define MiDoesPxeExistAndMakeValid(PXE, PROCESS, PFNLOCKHELD, WAITED) 1 #endif VOID MiMakePdeExistAndMakeValid ( IN PMMPTE PointerPde, IN PEPROCESS TargetProcess, IN LOGICAL PfnLockHeld ); #if (_MI_PAGING_LEVELS >= 4) VOID MiMakePxeExistAndMakeValid ( IN PMMPTE PointerPpe, IN PEPROCESS TargetProcess, IN LOGICAL PfnLockHeld ); #else #define MiMakePxeExistAndMakeValid(PDE, PROCESS, PFNLOCKHELD) #endif #if (_MI_PAGING_LEVELS >= 3) VOID MiMakePpeExistAndMakeValid ( IN PMMPTE PointerPpe, IN PEPROCESS TargetProcess, IN LOGICAL PfnLockHeld ); #else #define MiMakePpeExistAndMakeValid(PDE, PROCESS, PFNLOCKHELD) #endif ULONG FASTCALL MiMakeSystemAddressValid ( IN PVOID VirtualAddress, IN PEPROCESS CurrentProcess ); ULONG FASTCALL MiMakeSystemAddressValidPfnWs ( IN PVOID VirtualAddress, IN PEPROCESS CurrentProcess OPTIONAL ); ULONG FASTCALL MiMakeSystemAddressValidPfnSystemWs ( IN PVOID VirtualAddress ); ULONG FASTCALL MiMakeSystemAddressValidPfn ( IN PVOID VirtualAddress ); ULONG FASTCALL MiLockPagedAddress ( IN PVOID VirtualAddress, IN ULONG PfnLockHeld ); VOID FASTCALL MiUnlockPagedAddress ( IN PVOID VirtualAddress, IN ULONG PfnLockHeld ); ULONG FASTCALL MiIsPteDecommittedPage ( IN PMMPTE PointerPte ); ULONG FASTCALL MiIsProtectionCompatible ( IN ULONG OldProtect, IN ULONG NewProtect ); ULONG FASTCALL MiIsPteProtectionCompatible ( IN ULONG OldPteProtection, IN ULONG NewProtect ); ULONG FASTCALL MiMakeProtectionMask ( IN ULONG Protect ); ULONG MiIsEntireRangeCommitted ( IN PVOID StartingAddress, IN PVOID EndingAddress, IN PMMVAD Vad, IN PEPROCESS Process ); ULONG MiIsEntireRangeDecommitted ( IN PVOID StartingAddress, IN PVOID EndingAddress, IN PMMVAD Vad, IN PEPROCESS Process ); LOGICAL MiCheckProtoPtePageState ( IN PMMPTE PrototypePte, IN LOGICAL PfnLockHeld, OUT PLOGICAL DroppedPfnLock ); //++ //PMMPTE //MiGetProtoPteAddress ( // IN PMMPTE VAD, // IN PVOID VA // ); // // Routine Description: // // MiGetProtoPteAddress returns a pointer to the prototype PTE which // is mapped by the given virtual address descriptor and address within // the virtual address descriptor. // // Arguments // // VAD - Supplies a pointer to the virtual address descriptor that contains // the VA. // // VPN - Supplies the virtual page number. // // Return Value: // // A pointer to the proto PTE which corresponds to the VA. // //-- #define MiGetProtoPteAddress(VAD,VPN) \ ((((((VPN) - (VAD)->StartingVpn) << PTE_SHIFT) + \ (ULONG_PTR)(VAD)->FirstPrototypePte) <= (ULONG_PTR)(VAD)->LastContiguousPte) ? \ ((PMMPTE)(((((VPN) - (VAD)->StartingVpn) << PTE_SHIFT) + \ (ULONG_PTR)(VAD)->FirstPrototypePte))) : \ MiGetProtoPteAddressExtended ((VAD),(VPN))) PMMPTE FASTCALL MiGetProtoPteAddressExtended ( IN PMMVAD Vad, IN ULONG_PTR Vpn ); PSUBSECTION FASTCALL MiLocateSubsection ( IN PMMVAD Vad, IN ULONG_PTR Vpn ); VOID MiInitializeSystemCache ( IN ULONG MinimumWorkingSet, IN ULONG MaximumWorkingSet ); VOID MiAdjustWorkingSetManagerParameters( IN LOGICAL WorkStation ); VOID MiNotifyMemoryEvents ( VOID ); extern PFN_NUMBER MmLowMemoryThreshold; extern PFN_NUMBER MmHighMemoryThreshold; // // Section support // VOID FASTCALL MiInsertBasedSection ( IN PSECTION Section ); NTSTATUS MiMapViewOfPhysicalSection ( IN PCONTROL_AREA ControlArea, IN PEPROCESS Process, IN PVOID *CapturedBase, IN PLARGE_INTEGER SectionOffset, IN PSIZE_T CapturedViewSize, IN ULONG ProtectionMask, IN ULONG_PTR ZeroBits, IN ULONG AllocationType, IN LOGICAL WriteCombined ); NTSTATUS MiMapViewOfDataSection ( IN PCONTROL_AREA ControlArea, IN PEPROCESS Process, IN PVOID *CapturedBase, IN PLARGE_INTEGER SectionOffset, IN PSIZE_T CapturedViewSize, IN PSECTION Section, IN SECTION_INHERIT InheritDisposition, IN ULONG ProtectionMask, IN SIZE_T CommitSize, IN ULONG_PTR ZeroBits, IN ULONG AllocationType ); NTSTATUS MiUnmapViewOfSection ( IN PEPROCESS Process, IN PVOID BaseAddress, IN LOGICAL AddressSpaceMutexHeld ); VOID MiRemoveImageSectionObject( IN PFILE_OBJECT File, IN PCONTROL_AREA ControlArea ); VOID MiAddSystemPtes( IN PMMPTE StartingPte, IN ULONG NumberOfPtes, IN MMSYSTEM_PTE_POOL_TYPE SystemPtePoolType ); VOID MiRemoveMappedView ( IN PEPROCESS CurrentProcess, IN PMMVAD Vad ); VOID MiSegmentDelete ( PSEGMENT Segment ); VOID MiSectionDelete ( IN PVOID Object ); VOID MiDereferenceSegmentThread ( IN PVOID StartContext ); NTSTATUS MiCreateImageFileMap ( IN PFILE_OBJECT File, OUT PSEGMENT *Segment ); NTSTATUS MiCreateDataFileMap ( IN PFILE_OBJECT File, OUT PSEGMENT *Segment, IN PUINT64 MaximumSize, IN ULONG SectionPageProtection, IN ULONG AllocationAttributes, IN ULONG IgnoreFileSizing ); NTSTATUS MiCreatePagingFileMap ( OUT PSEGMENT *Segment, IN PUINT64 MaximumSize, IN ULONG ProtectionMask, IN ULONG AllocationAttributes ); VOID MiPurgeSubsectionInternal ( IN PSUBSECTION Subsection, IN ULONG PteOffset ); VOID MiPurgeImageSection ( IN PCONTROL_AREA ControlArea, IN PEPROCESS Process ); VOID MiCleanSection ( IN PCONTROL_AREA ControlArea, IN LOGICAL DirtyDataPagesOk ); VOID MiDereferenceControlArea ( IN PCONTROL_AREA ControlArea ); VOID MiCheckControlArea ( IN PCONTROL_AREA ControlArea, IN PEPROCESS CurrentProcess, IN KIRQL PreviousIrql ); LOGICAL MiCheckPurgeAndUpMapCount ( IN PCONTROL_AREA ControlArea ); VOID MiCheckForControlAreaDeletion ( IN PCONTROL_AREA ControlArea ); LOGICAL MiCheckControlAreaStatus ( IN SECTION_CHECK_TYPE SectionCheckType, IN PSECTION_OBJECT_POINTERS SectionObjectPointers, IN ULONG DelayClose, OUT PCONTROL_AREA *ControlArea, OUT PKIRQL OldIrql ); extern SLIST_HEADER MmEventCountSListHead; PEVENT_COUNTER MiGetEventCounter ( VOID ); VOID MiFreeEventCounter ( IN PEVENT_COUNTER Support ); ULONG MiCanFileBeTruncatedInternal ( IN PSECTION_OBJECT_POINTERS SectionPointer, IN PLARGE_INTEGER NewFileSize OPTIONAL, IN LOGICAL BlockNewViews, OUT PKIRQL PreviousIrql ); #define STATUS_MAPPED_WRITER_COLLISION (0xC0033333) NTSTATUS MiFlushSectionInternal ( IN PMMPTE StartingPte, IN PMMPTE FinalPte, IN PSUBSECTION FirstSubsection, IN PSUBSECTION LastSubsection, IN ULONG Synchronize, IN LOGICAL WriteInProgressOk, OUT PIO_STATUS_BLOCK IoStatus ); // // protection stuff... // NTSTATUS MiProtectVirtualMemory ( IN PEPROCESS Process, IN PVOID *CapturedBase, IN PSIZE_T CapturedRegionSize, IN ULONG Protect, IN PULONG LastProtect ); ULONG MiGetPageProtection ( IN PMMPTE PointerPte, IN PEPROCESS Process, IN LOGICAL PteCapturedToLocalStack ); NTSTATUS MiSetProtectionOnSection ( IN PEPROCESS Process, IN PMMVAD Vad, IN PVOID StartingAddress, IN PVOID EndingAddress, IN ULONG NewProtect, OUT PULONG CapturedOldProtect, IN ULONG DontCharge, OUT PULONG Locked ); NTSTATUS MiCheckSecuredVad ( IN PMMVAD Vad, IN PVOID Base, IN ULONG_PTR Size, IN ULONG ProtectionMask ); HANDLE MiSecureVirtualMemory ( IN PVOID Address, IN SIZE_T Size, IN ULONG ProbeMode, IN LOGICAL AddressSpaceMutexHeld ); VOID MiUnsecureVirtualMemory ( IN HANDLE SecureHandle, IN LOGICAL AddressSpaceMutexHeld ); ULONG MiChangeNoAccessForkPte ( IN PMMPTE PointerPte, IN ULONG ProtectionMask ); VOID MiSetImageProtect ( IN PSEGMENT Segment, IN ULONG Protection ); // // Routines for charging quota and commitment. // VOID MiTrimSegmentCache ( VOID ); VOID MiInitializeCommitment ( VOID ); LOGICAL FASTCALL MiChargeCommitment ( IN SIZE_T QuotaCharge, IN PEPROCESS Process OPTIONAL ); LOGICAL FASTCALL MiChargeCommitmentCantExpand ( IN SIZE_T QuotaCharge, IN ULONG MustSucceed ); LOGICAL FASTCALL MiChargeTemporaryCommitmentForReduction ( IN SIZE_T QuotaCharge ); #if defined (_MI_DEBUG_COMMIT_LEAKS) VOID FASTCALL MiReturnCommitment ( IN SIZE_T QuotaCharge ); #else #define MiReturnCommitment(_QuotaCharge) \ ASSERT ((SSIZE_T)(_QuotaCharge) >= 0); \ ASSERT (MmTotalCommittedPages >= (_QuotaCharge)); \ InterlockedExchangeAddSizeT (&MmTotalCommittedPages, 0-((SIZE_T)(_QuotaCharge))); \ MM_TRACK_COMMIT (MM_DBG_COMMIT_RETURN_NORMAL, (_QuotaCharge)); #endif VOID MiCauseOverCommitPopup ( VOID ); extern SIZE_T MmPeakCommitment; extern SIZE_T MmTotalCommitLimitMaximum; SIZE_T MiCalculatePageCommitment ( IN PVOID StartingAddress, IN PVOID EndingAddress, IN PMMVAD Vad, IN PEPROCESS Process ); VOID MiReturnPageTablePageCommitment ( IN PVOID StartingAddress, IN PVOID EndingAddress, IN PEPROCESS CurrentProcess, IN PMMVAD PreviousVad, IN PMMVAD NextVad ); VOID MiFlushAllPages ( VOID ); VOID MiModifiedPageWriterTimerDispatch ( IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2 ); LONGLONG MiStartingOffset( IN PSUBSECTION Subsection, IN PMMPTE PteAddress ); LARGE_INTEGER MiEndingOffset( IN PSUBSECTION Subsection ); VOID MiReloadBootLoadedDrivers ( IN PLOADER_PARAMETER_BLOCK LoaderBlock ); LOGICAL MiInitializeLoadedModuleList ( IN PLOADER_PARAMETER_BLOCK LoaderBlock ); extern ULONG MmSpecialPoolTag; extern PVOID MmSpecialPoolStart; extern PVOID MmSpecialPoolEnd; extern PVOID MmSessionSpecialPoolStart; extern PVOID MmSessionSpecialPoolEnd; LOGICAL MiInitializeSpecialPool ( IN POOL_TYPE PoolType ); LOGICAL MiIsSpecialPoolAddressNonPaged ( IN PVOID VirtualAddress ); #if defined (_WIN64) LOGICAL MiInitializeSessionSpecialPool ( VOID ); VOID MiDeleteSessionSpecialPool ( VOID ); #endif #if defined (_X86_) LOGICAL MiRecoverSpecialPtes ( IN ULONG NumberOfPtes ); #endif VOID MiEnableRandomSpecialPool ( IN LOGICAL Enable ); LOGICAL MiTriageSystem ( IN PLOADER_PARAMETER_BLOCK LoaderBlock ); LOGICAL MiTriageAddDrivers ( IN PLOADER_PARAMETER_BLOCK LoaderBlock ); LOGICAL MiTriageVerifyDriver ( IN PKLDR_DATA_TABLE_ENTRY DataTableEntry ); extern ULONG MmTriageActionTaken; #if defined (_WIN64) #define MM_SPECIAL_POOL_PTES ((1024 * 1024) / sizeof (MMPTE)) #else #define MM_SPECIAL_POOL_PTES (24 * PTE_PER_PAGE) #endif #define MI_SUSPECT_DRIVER_BUFFER_LENGTH 512 extern WCHAR MmVerifyDriverBuffer[]; extern ULONG MmVerifyDriverBufferLength; extern ULONG MmVerifyDriverLevel; extern LOGICAL MmDontVerifyRandomDrivers; extern LOGICAL MmSnapUnloads; extern LOGICAL MmProtectFreedNonPagedPool; extern ULONG MmEnforceWriteProtection; extern LOGICAL MmTrackLockedPages; extern ULONG MmTrackPtes; #define VI_POOL_FREELIST_END ((ULONG_PTR)-1) typedef struct _VI_POOL_ENTRY_INUSE { PVOID VirtualAddress; PVOID CallingAddress; SIZE_T NumberOfBytes; ULONG_PTR Tag; } VI_POOL_ENTRY_INUSE, *PVI_POOL_ENTRY_INUSE; typedef struct _VI_POOL_ENTRY { union { VI_POOL_ENTRY_INUSE InUse; ULONG_PTR FreeListNext; }; } VI_POOL_ENTRY, *PVI_POOL_ENTRY; #define MI_VERIFIER_ENTRY_SIGNATURE 0x98761940 typedef struct _MI_VERIFIER_DRIVER_ENTRY { LIST_ENTRY Links; ULONG Loads; ULONG Unloads; UNICODE_STRING BaseName; PVOID StartAddress; PVOID EndAddress; #define VI_VERIFYING_DIRECTLY 0x1 #define VI_VERIFYING_INVERSELY 0x2 #define VI_DISABLE_VERIFICATION 0x4 ULONG Flags; ULONG_PTR Signature; ULONG_PTR Reserved; KSPIN_LOCK VerifierPoolLock; PVI_POOL_ENTRY PoolHash; ULONG_PTR PoolHashSize; ULONG_PTR PoolHashFree; ULONG_PTR PoolHashReserved; ULONG CurrentPagedPoolAllocations; ULONG CurrentNonPagedPoolAllocations; ULONG PeakPagedPoolAllocations; ULONG PeakNonPagedPoolAllocations; SIZE_T PagedBytes; SIZE_T NonPagedBytes; SIZE_T PeakPagedBytes; SIZE_T PeakNonPagedBytes; } MI_VERIFIER_DRIVER_ENTRY, *PMI_VERIFIER_DRIVER_ENTRY; typedef struct _MI_VERIFIER_POOL_HEADER { ULONG_PTR ListIndex; PMI_VERIFIER_DRIVER_ENTRY Verifier; } MI_VERIFIER_POOL_HEADER, *PMI_VERIFIER_POOL_HEADER; typedef struct _MM_DRIVER_VERIFIER_DATA { ULONG Level; ULONG RaiseIrqls; ULONG AcquireSpinLocks; ULONG SynchronizeExecutions; ULONG AllocationsAttempted; ULONG AllocationsSucceeded; ULONG AllocationsSucceededSpecialPool; ULONG AllocationsWithNoTag; ULONG TrimRequests; ULONG Trims; ULONG AllocationsFailed; ULONG AllocationsFailedDeliberately; ULONG Loads; ULONG Unloads; ULONG UnTrackedPool; ULONG UserTrims; ULONG CurrentPagedPoolAllocations; ULONG CurrentNonPagedPoolAllocations; ULONG PeakPagedPoolAllocations; ULONG PeakNonPagedPoolAllocations; SIZE_T PagedBytes; SIZE_T NonPagedBytes; SIZE_T PeakPagedBytes; SIZE_T PeakNonPagedBytes; ULONG BurstAllocationsFailedDeliberately; ULONG SessionTrims; ULONG Reserved[2]; } MM_DRIVER_VERIFIER_DATA, *PMM_DRIVER_VERIFIER_DATA; LOGICAL MiInitializeDriverVerifierList ( IN PLOADER_PARAMETER_BLOCK LoaderBlock ); LOGICAL MiInitializeVerifyingComponents ( IN PLOADER_PARAMETER_BLOCK LoaderBlock ); LOGICAL MiApplyDriverVerifier ( IN PKLDR_DATA_TABLE_ENTRY, IN PMI_VERIFIER_DRIVER_ENTRY Verifier ); VOID MiReApplyVerifierToLoadedModules( IN PLIST_ENTRY ModuleListHead ); VOID MiVerifyingDriverUnloading ( IN PKLDR_DATA_TABLE_ENTRY DataTableEntry ); VOID MiVerifierCheckThunks ( IN PKLDR_DATA_TABLE_ENTRY DataTableEntry ); extern ULONG MiActiveVerifierThunks; extern LIST_ENTRY MiSuspectDriverList; extern ULONG MiVerifierThunksAdded; VOID MiEnableKernelVerifier ( VOID ); extern LOGICAL KernelVerifier; extern MM_DRIVER_VERIFIER_DATA MmVerifierData; #define MI_FREED_SPECIAL_POOL_SIGNATURE 0x98764321 #define MI_STACK_BYTES 1024 typedef struct _MI_FREED_SPECIAL_POOL { POOL_HEADER OverlaidPoolHeader; MI_VERIFIER_POOL_HEADER OverlaidVerifierPoolHeader; ULONG Signature; ULONG TickCount; ULONG NumberOfBytesRequested; ULONG Pagable; PVOID VirtualAddress; PVOID StackPointer; ULONG StackBytes; PETHREAD Thread; UCHAR StackData[MI_STACK_BYTES]; } MI_FREED_SPECIAL_POOL, *PMI_FREED_SPECIAL_POOL; #define MM_DBG_COMMIT_NONPAGED_POOL_EXPANSION 0 #define MM_DBG_COMMIT_PAGED_POOL_PAGETABLE 1 #define MM_DBG_COMMIT_PAGED_POOL_PAGES 2 #define MM_DBG_COMMIT_SESSION_POOL_PAGE_TABLES 3 #define MM_DBG_COMMIT_ALLOCVM1 4 #define MM_DBG_COMMIT_ALLOCVM_SEGMENT 5 #define MM_DBG_COMMIT_IMAGE 6 #define MM_DBG_COMMIT_PAGEFILE_BACKED_SHMEM 7 #define MM_DBG_COMMIT_INDEPENDENT_PAGES 8 #define MM_DBG_COMMIT_CONTIGUOUS_PAGES 9 #define MM_DBG_COMMIT_MDL_PAGES 0xA #define MM_DBG_COMMIT_NONCACHED_PAGES 0xB #define MM_DBG_COMMIT_MAPVIEW_DATA 0xC #define MM_DBG_COMMIT_FILL_SYSTEM_DIRECTORY 0xD #define MM_DBG_COMMIT_EXTRA_SYSTEM_PTES 0xE #define MM_DBG_COMMIT_DRIVER_PAGING_AT_INIT 0xF #define MM_DBG_COMMIT_PAGEFILE_FULL 0x10 #define MM_DBG_COMMIT_PROCESS_CREATE 0x11 #define MM_DBG_COMMIT_KERNEL_STACK_CREATE 0x12 #define MM_DBG_COMMIT_SET_PROTECTION 0x13 #define MM_DBG_COMMIT_SESSION_CREATE 0x14 #define MM_DBG_COMMIT_SESSION_IMAGE_PAGES 0x15 #define MM_DBG_COMMIT_SESSION_PAGETABLE_PAGES 0x16 #define MM_DBG_COMMIT_SESSION_SHARED_IMAGE 0x17 #define MM_DBG_COMMIT_DRIVER_PAGES 0x18 #define MM_DBG_COMMIT_INSERT_VAD 0x19 #define MM_DBG_COMMIT_SESSION_WS_INIT 0x1A #define MM_DBG_COMMIT_SESSION_ADDITIONAL_WS_PAGES 0x1B #define MM_DBG_COMMIT_SESSION_ADDITIONAL_WS_HASHPAGES 0x1C #define MM_DBG_COMMIT_SPECIAL_POOL_PAGES 0x1D #define MM_DBG_COMMIT_SPECIAL_POOL_MAPPING_PAGES 0x1E #define MM_DBG_COMMIT_SMALL 0x1F #define MM_DBG_COMMIT_EXTRA_WS_PAGES 0x20 #define MM_DBG_COMMIT_EXTRA_INITIAL_SESSION_WS_PAGES 0x21 #define MM_DBG_COMMIT_ALLOCVM_PROCESS 0x22 #define MM_DBG_COMMIT_INSERT_VAD_PT 0x23 #define MM_DBG_COMMIT_ALLOCVM_PROCESS2 0x24 #define MM_DBG_COMMIT_CHARGE_NORMAL 0x25 #define MM_DBG_COMMIT_CHARGE_CAUSE_POPUP 0x26 #define MM_DBG_COMMIT_CHARGE_CANT_EXPAND 0x27 #define MM_DBG_COMMIT_RETURN_NONPAGED_POOL_EXPANSION 0x40 #define MM_DBG_COMMIT_RETURN_PAGED_POOL_PAGES 0x41 #define MM_DBG_COMMIT_RETURN_SESSION_DATAPAGE 0x42 #define MM_DBG_COMMIT_RETURN_ALLOCVM_SEGMENT 0x43 #define MM_DBG_COMMIT_RETURN_ALLOCVM2 0x44 #define MM_DBG_COMMIT_RETURN_IMAGE_NO_LARGE_CA 0x46 #define MM_DBG_COMMIT_RETURN_PTE_RANGE 0x47 #define MM_DBG_COMMIT_RETURN_NTFREEVM1 0x48 #define MM_DBG_COMMIT_RETURN_NTFREEVM2 0x49 #define MM_DBG_COMMIT_RETURN_INDEPENDENT_PAGES 0x4A #define MM_DBG_COMMIT_RETURN_AWE_EXCESS 0x4B #define MM_DBG_COMMIT_RETURN_MDL_PAGES 0x4C #define MM_DBG_COMMIT_RETURN_NONCACHED_PAGES 0x4D #define MM_DBG_COMMIT_RETURN_SESSION_CREATE_FAILURE 0x4E #define MM_DBG_COMMIT_RETURN_PAGETABLES 0x4F #define MM_DBG_COMMIT_RETURN_PROTECTION 0x50 #define MM_DBG_COMMIT_RETURN_SEGMENT_DELETE1 0x51 #define MM_DBG_COMMIT_RETURN_SEGMENT_DELETE2 0x52 #define MM_DBG_COMMIT_RETURN_PAGEFILE_FULL 0x53 #define MM_DBG_COMMIT_RETURN_SESSION_DEREFERENCE 0x54 #define MM_DBG_COMMIT_RETURN_VAD 0x55 #define MM_DBG_COMMIT_RETURN_PROCESS_CREATE_FAILURE1 0x56 #define MM_DBG_COMMIT_RETURN_PROCESS_DELETE 0x57 #define MM_DBG_COMMIT_RETURN_PROCESS_CLEAN_PAGETABLES 0x58 #define MM_DBG_COMMIT_RETURN_KERNEL_STACK_DELETE 0x59 #define MM_DBG_COMMIT_RETURN_SESSION_DRIVER_LOAD_FAILURE1 0x5A #define MM_DBG_COMMIT_RETURN_DRIVER_INIT_CODE 0x5B #define MM_DBG_COMMIT_RETURN_DRIVER_UNLOAD 0x5C #define MM_DBG_COMMIT_RETURN_DRIVER_UNLOAD1 0x5D #define MM_DBG_COMMIT_RETURN_NORMAL 0x5E #define MM_DBG_COMMIT_RETURN_PF_FULL_EXTEND 0x5F #define MM_DBG_COMMIT_RETURN_EXTENDED 0x60 #define MM_DBG_COMMIT_RETURN_SEGMENT_DELETE3 0x61 #if 0 #define MM_COMMIT_COUNTER_MAX 0x80 #define MM_TRACK_COMMIT(_index, bump) \ if (_index >= MM_COMMIT_COUNTER_MAX) { \ DbgPrint("Mm: Invalid commit counter %d %d\n", _index, MM_COMMIT_COUNTER_MAX); \ DbgBreakPoint(); \ } \ else { \ InterlockedExchangeAddSizeT (&MmTrackCommit[_index], bump); \ } #define MM_TRACK_COMMIT_REDUCTION(_index, bump) \ if (_index >= MM_COMMIT_COUNTER_MAX) { \ DbgPrint("Mm: Invalid commit counter %d %d\n", _index, MM_COMMIT_COUNTER_MAX); \ DbgBreakPoint(); \ } \ else { \ InterlockedExchangeAddSizeT (&MmTrackCommit[_index], 0 - (bump)); \ } extern SIZE_T MmTrackCommit[MM_COMMIT_COUNTER_MAX]; #define MI_INCREMENT_TOTAL_PROCESS_COMMIT(_charge) InterlockedExchangeAddSizeT (&MmTotalProcessCommit, (_charge)); #else #define MM_TRACK_COMMIT(_index, bump) #define MM_TRACK_COMMIT_REDUCTION(_index, bump) #define MI_INCREMENT_TOTAL_PROCESS_COMMIT(_charge) #endif #define MM_BUMP_COUNTER_MAX 60 extern SIZE_T MmResTrack[MM_BUMP_COUNTER_MAX]; #define MM_BUMP_COUNTER(_index, bump) \ ASSERT (_index < MM_BUMP_COUNTER_MAX); \ InterlockedExchangeAddSizeT (&MmResTrack[_index], (SIZE_T)(bump)); extern ULONG MiSpecialPagesNonPaged; extern ULONG MiSpecialPagesNonPagedMaximum; //++ //PFN_NUMBER //MI_NONPAGABLE_MEMORY_AVAILABLE( // VOID // ); // // Routine Description: // // This routine lets callers know how many pages can be charged against // the resident available, factoring in earlier Mm promises that // may not have been redeemed at this point (ie: nonpaged pool expansion, // etc, that must be honored at a later point if requested). // // Arguments // // None. // // Return Value: // // The number of currently available pages in the resident available. // // N.B. This is a signed quantity and can be negative. // //-- #define MI_NONPAGABLE_MEMORY_AVAILABLE() \ ((SPFN_NUMBER) \ (MmResidentAvailablePages - \ MmSystemLockPagesCount)) extern ULONG MmLargePageMinimum; // // hack stuff for testing. // VOID MiDumpValidAddresses ( VOID ); VOID MiDumpPfn ( VOID ); VOID MiDumpWsl ( VOID ); VOID MiFormatPte ( IN PMMPTE PointerPte ); VOID MiCheckPfn ( VOID ); VOID MiCheckPte ( VOID ); VOID MiFormatPfn ( IN PMMPFN PointerPfn ); extern const MMPTE ZeroPte; extern const MMPTE ZeroKernelPte; extern const MMPTE ValidKernelPteLocal; extern MMPTE ValidKernelPte; extern MMPTE ValidKernelPde; extern const MMPTE ValidKernelPdeLocal; extern const MMPTE ValidUserPte; extern const MMPTE ValidPtePte; extern const MMPTE ValidPdePde; extern MMPTE DemandZeroPde; extern const MMPTE DemandZeroPte; extern MMPTE KernelPrototypePte; extern const MMPTE TransitionPde; extern MMPTE PrototypePte; extern const MMPTE NoAccessPte; extern ULONG_PTR MmSubsectionBase; extern ULONG_PTR MmSubsectionTopPage; extern ULONG ExpMultiUserTS; // // Virtual alignment for PTEs (machine specific) minimum value is // 4k maximum value is 64k. The maximum value can be raised by // changing the MM_PROTO_PTE_ALIGNMENT constant and adding more // reserved mapping PTEs in hyperspace. // // // Total number of physical pages on the system. // extern PFN_COUNT MmNumberOfPhysicalPages; // // Lowest physical page number on the system. // extern PFN_NUMBER MmLowestPhysicalPage; // // Highest physical page number on the system. // extern PFN_NUMBER MmHighestPhysicalPage; // // Highest possible physical page number in the system. // extern PFN_NUMBER MmHighestPossiblePhysicalPage; #if defined (_WIN64) #define MI_DTC_MAX_PAGES ((PFN_NUMBER)(((ULONG64)128 * 1024 * 1024 * 1024) >> PAGE_SHIFT)) #define MI_DTC_BOOTED_3GB_MAX_PAGES MI_DTC_MAX_PAGES #define MI_ADS_MAX_PAGES ((PFN_NUMBER)(((ULONG64)64 * 1024 * 1024 * 1024) >> PAGE_SHIFT)) #define MI_DEFAULT_MAX_PAGES ((PFN_NUMBER)(((ULONG64)16 * 1024 * 1024 * 1024) >> PAGE_SHIFT)) #else #define MI_DTC_MAX_PAGES ((PFN_NUMBER)(((ULONG64)64 * 1024 * 1024 * 1024) >> PAGE_SHIFT)) #define MI_DTC_BOOTED_3GB_MAX_PAGES ((PFN_NUMBER)(((ULONG64)16 * 1024 * 1024 * 1024) >> PAGE_SHIFT)) #define MI_ADS_MAX_PAGES ((PFN_NUMBER)(((ULONG64)32 * 1024 * 1024 * 1024) >> PAGE_SHIFT)) #define MI_DEFAULT_MAX_PAGES ((PFN_NUMBER)(((ULONG64)4 * 1024 * 1024 * 1024) >> PAGE_SHIFT)) #endif #define MI_BLADE_MAX_PAGES ((PFN_NUMBER)(((ULONG64)2 * 1024 * 1024 * 1024) >> PAGE_SHIFT)) // // Total number of available pages on the system. This // is the sum of the pages on the zeroed, free and standby lists. // extern PFN_COUNT MmAvailablePages; // // Total number of free pages to base working set trimming on. // extern PFN_NUMBER MmMoreThanEnoughFreePages; // // Total number physical pages which would be usable if every process // was at it's minimum working set size. This value is initialized // at system initialization to MmAvailablePages - MM_FLUID_PHYSICAL_PAGES. // Everytime a thread is created, the kernel stack is subtracted from // this and every time a process is created, the minimum working set // is subtracted from this. If the value would become negative, the // operation (create process/kernel stack/ adjust working set) fails. // The PFN LOCK must be owned to manipulate this value. // extern SPFN_NUMBER MmResidentAvailablePages; // // The total number of pages which would be removed from working sets // if every working set was at its minimum. // extern PFN_NUMBER MmPagesAboveWsMinimum; // // The total number of pages which would be removed from working sets // if every working set above its maximum was at its maximum. // extern PFN_NUMBER MmPagesAboveWsMaximum; // // If memory is becoming short and MmPagesAboveWsMinimum is // greater than MmPagesAboveWsThreshold, trim working sets. // extern PFN_NUMBER MmPagesAboveWsThreshold; // // The number of pages to add to a working set if there are ample // available pages and the working set is below its maximum. // extern PFN_NUMBER MmWorkingSetSizeIncrement; // // The number of pages to extend the maximum working set size by // if the working set at its maximum and there are ample available pages. extern PFN_NUMBER MmWorkingSetSizeExpansion; extern ULONG MmPlentyFreePages; // // The number of pages required to be freed by working set reduction // before working set reduction is attempted. // extern PFN_NUMBER MmWsAdjustThreshold; // // The number of pages available to allow the working set to be // expanded above its maximum. // extern PFN_NUMBER MmWsExpandThreshold; // // The total number of pages to reduce by working set trimming. // extern PFN_NUMBER MmWsTrimReductionGoal; extern LONG MiDelayPageFaults; extern PMMPFN MmPfnDatabase; extern MMPFNLIST MmZeroedPageListHead; extern MMPFNLIST MmFreePageListHead; extern MMPFNLIST MmStandbyPageListHead; extern MMPFNLIST MmRomPageListHead; extern MMPFNLIST MmModifiedPageListHead; extern MMPFNLIST MmModifiedNoWritePageListHead; extern MMPFNLIST MmBadPageListHead; extern PMMPFNLIST MmPageLocationList[NUMBER_OF_PAGE_LISTS]; extern MMPFNLIST MmModifiedPageListByColor[MM_MAXIMUM_NUMBER_OF_COLORS]; // // Mask for isolating secondary color from physical page number. // extern ULONG MmSecondaryColorMask; // // Mask for isolating node color from combined node and secondary // color. // extern ULONG MmSecondaryColorNodeMask; // // Width of MmSecondaryColorMask in bits. In multi node systems, // the node number is combined with the secondary color to make up // the page color. // extern UCHAR MmSecondaryColorNodeShift; // // Event for available pages, set means pages are available. // extern KEVENT MmAvailablePagesEvent; extern KEVENT MmAvailablePagesEventMedium; extern KEVENT MmAvailablePagesEventHigh; // // Event for the zeroing page thread. // extern KEVENT MmZeroingPageEvent; // // Boolean to indicate if the zeroing page thread is currently // active. This is set to true when the zeroing page event is // set and set to false when the zeroing page thread is done // zeroing all the pages on the free list. // extern BOOLEAN MmZeroingPageThreadActive; // // Minimum number of free pages before zeroing page thread starts. // extern PFN_NUMBER MmMinimumFreePagesToZero; // // Global event to synchronize mapped writing with cleaning segments. // extern KEVENT MmMappedFileIoComplete; // // Hyper space items. // extern PMMPTE MmFirstReservedMappingPte; extern PMMPTE MmLastReservedMappingPte; // // System space sizes - MmNonPagedSystemStart to MM_NON_PAGED_SYSTEM_END // defines the ranges of PDEs which must be copied into a new process's // address space. // extern PVOID MmNonPagedSystemStart; extern PCHAR MmSystemSpaceViewStart; extern LOGICAL MmProtectFreedNonPagedPool; // // Pool sizes. // extern SIZE_T MmSizeOfNonPagedPoolInBytes; extern SIZE_T MmMinimumNonPagedPoolSize; extern SIZE_T MmDefaultMaximumNonPagedPool; extern ULONG MmMaximumNonPagedPoolPercent; extern ULONG MmMinAdditionNonPagedPoolPerMb; extern ULONG MmMaxAdditionNonPagedPoolPerMb; extern SIZE_T MmSizeOfPagedPoolInBytes; extern SIZE_T MmMaximumNonPagedPoolInBytes; extern PFN_NUMBER MmAllocatedNonPagedPool; extern PVOID MmNonPagedPoolExpansionStart; extern ULONG MmExpandedPoolBitPosition; extern PFN_NUMBER MmNumberOfFreeNonPagedPool; extern ULONG MmNumberOfSystemPtes; extern ULONG MiRequestedSystemPtes; extern ULONG MmTotalFreeSystemPtes[MaximumPtePoolTypes]; extern PMMPTE MmSystemPagePtes; extern ULONG MmSystemPageDirectory[]; extern SIZE_T MmHeapSegmentReserve; extern SIZE_T MmHeapSegmentCommit; extern SIZE_T MmHeapDeCommitTotalFreeThreshold; extern SIZE_T MmHeapDeCommitFreeBlockThreshold; #define MI_MAX_FREE_LIST_HEADS 4 extern LIST_ENTRY MmNonPagedPoolFreeListHead[MI_MAX_FREE_LIST_HEADS]; // // Counter for flushes of the entire TB. // extern ULONG MmFlushCounter; // // Pool start and end. // extern PVOID MmNonPagedPoolStart; extern PVOID MmNonPagedPoolEnd; extern PVOID MmPagedPoolStart; extern PVOID MmPagedPoolEnd; // // Pool bit maps and other related structures. // typedef struct _MM_PAGED_POOL_INFO { PRTL_BITMAP PagedPoolAllocationMap; PRTL_BITMAP EndOfPagedPoolBitmap; PRTL_BITMAP PagedPoolLargeSessionAllocationMap; // HYDRA only PMMPTE FirstPteForPagedPool; PMMPTE LastPteForPagedPool; PMMPTE NextPdeForPagedPoolExpansion; ULONG PagedPoolHint; SIZE_T PagedPoolCommit; SIZE_T AllocatedPagedPool; } MM_PAGED_POOL_INFO, *PMM_PAGED_POOL_INFO; extern MM_PAGED_POOL_INFO MmPagedPoolInfo; extern PVOID MmPageAlignedPoolBase[2]; extern PRTL_BITMAP VerifierLargePagedPoolMap; // // MmFirstFreeSystemPte contains the offset from the // Nonpaged system base to the first free system PTE. // Note, that an offset of zero indicates an empty list. // extern MMPTE MmFirstFreeSystemPte[MaximumPtePoolTypes]; extern ULONG_PTR MiSystemViewStart; // // System cache sizes. // //extern MMSUPPORT MmSystemCacheWs; extern PMMWSL MmSystemCacheWorkingSetList; extern PMMWSLE MmSystemCacheWsle; extern PVOID MmSystemCacheStart; extern PVOID MmSystemCacheEnd; extern PFN_NUMBER MmSystemCacheWsMinimum; extern PFN_NUMBER MmSystemCacheWsMaximum; // // Virtual alignment for PTEs (machine specific) minimum value is // 0 (no alignment) maximum value is 64k. The maximum value can be raised by // changing the MM_PROTO_PTE_ALIGNMENT constant and adding more // reserved mapping PTEs in hyperspace. // extern ULONG MmAliasAlignment; // // Mask to AND with virtual address to get an offset to go // with the alignment. This value is page aligned. // extern ULONG MmAliasAlignmentOffset; // // Mask to and with PTEs to determine if the alias mapping is compatible. // This value is usually (MmAliasAlignment - 1) // extern ULONG MmAliasAlignmentMask; // // Cells to track unused thread kernel stacks to avoid TB flushes // every time a thread terminates. // extern ULONG MmMaximumDeadKernelStacks; extern SLIST_HEADER MmDeadStackSListHead; // // MmSystemPteBase contains the address of 1 PTE before // the first free system PTE (zero indicates an empty list). // The value of this field does not change once set. // extern PMMPTE MmSystemPteBase; // // Root of system space virtual address descriptors. These define // the pagable portion of the system. // extern PMMVAD MmVirtualAddressDescriptorRoot; extern PMMADDRESS_NODE MmSectionBasedRoot; extern PVOID MmHighSectionBase; // // Section commit mutex. // extern FAST_MUTEX MmSectionCommitMutex; // // Section base address mutex. // extern FAST_MUTEX MmSectionBasedMutex; // // Resource for section extension. // extern ERESOURCE MmSectionExtendResource; extern ERESOURCE MmSectionExtendSetResource; // // Inpage cluster sizes for executable pages (set based on memory size). // extern ULONG MmDataClusterSize; extern ULONG MmCodeClusterSize; // // Pagefile creation mutex. // extern FAST_MUTEX MmPageFileCreationLock; // // Event to set when first paging file is created. // extern PKEVENT MmPagingFileCreated; // // Paging file debug information. // extern ULONG_PTR MmPagingFileDebug[]; // // Spinlock which guards PFN database. This spinlock is used by // memory management for accessing the PFN database. The I/O // system makes use of it for unlocking pages during I/O complete. // // extern KSPIN_LOCK MmPfnLock; // // Spinlock which guards the working set list for the system shared // address space (paged pool, system cache, pagable drivers). // extern ERESOURCE MmSystemWsLock; // // Spin lock for allowing working set expansion. // extern KSPIN_LOCK MmExpansionLock; // // To prevent optimizations. // extern MMPTE GlobalPte; // // Page color for system working set. // extern ULONG MmSystemPageColor; extern ULONG MmSecondaryColors; extern ULONG MmProcessColorSeed; // // Set from ntos\config\CMDAT3.C Used by customers to disable paging // of executive on machines with lots of memory. Worth a few TPS on a // data base server. // #define MM_SYSTEM_CODE_LOCKED_DOWN 0x1 #define MM_PAGED_POOL_LOCKED_DOWN 0x2 extern ULONG MmDisablePagingExecutive; // // For debugging. #if DBG extern ULONG MmDebug; #endif // // Unused segment management // extern MMDEREFERENCE_SEGMENT_HEADER MmDereferenceSegmentHeader; extern LIST_ENTRY MmUnusedSegmentList; extern LIST_ENTRY MmUnusedSubsectionList; extern KEVENT MmUnusedSegmentCleanup; extern ULONG MmConsumedPoolPercentage; extern ULONG MmUnusedSegmentCount; extern ULONG MmUnusedSubsectionCount; extern ULONG MmUnusedSubsectionCountPeak; extern SIZE_T MiUnusedSubsectionPagedPool; extern SIZE_T MiUnusedSubsectionPagedPoolPeak; #define MI_UNUSED_SUBSECTIONS_COUNT_INSERT(_MappedSubsection) \ MmUnusedSubsectionCount += 1; \ if (MmUnusedSubsectionCount > MmUnusedSubsectionCountPeak) { \ MmUnusedSubsectionCountPeak = MmUnusedSubsectionCount; \ } \ MiUnusedSubsectionPagedPool += EX_REAL_POOL_USAGE((_MappedSubsection->PtesInSubsection + _MappedSubsection->UnusedPtes) * sizeof (MMPTE)); \ if (MiUnusedSubsectionPagedPool > MiUnusedSubsectionPagedPoolPeak) { \ MiUnusedSubsectionPagedPoolPeak = MiUnusedSubsectionPagedPool; \ } \ #define MI_UNUSED_SUBSECTIONS_COUNT_REMOVE(_MappedSubsection) \ MmUnusedSubsectionCount -= 1; \ MiUnusedSubsectionPagedPool -= EX_REAL_POOL_USAGE((_MappedSubsection->PtesInSubsection + _MappedSubsection->UnusedPtes) * sizeof (MMPTE)); #define MI_FILESYSTEM_NONPAGED_POOL_CHARGE 150 #define MI_FILESYSTEM_PAGED_POOL_CHARGE 1024 //++ //LOGICAL //MI_UNUSED_SEGMENTS_SURPLUS ( // IN PVOID va // ); // // Routine Description: // // This routine determines whether a surplus of unused // segments exist. If so, the caller can initiate a trim to free pool. // // Arguments // // None. // // Return Value: // // TRUE if unused segment trimming should be initiated, FALSE if not. // //-- #define MI_UNUSED_SEGMENTS_SURPLUS() \ (((ULONG)((MmPagedPoolInfo.AllocatedPagedPool * 100) / (MmSizeOfPagedPoolInBytes >> PAGE_SHIFT)) > MmConsumedPoolPercentage) || \ ((ULONG)((MmAllocatedNonPagedPool * 100) / (MmMaximumNonPagedPoolInBytes >> PAGE_SHIFT)) > MmConsumedPoolPercentage)) VOID MiConvertStaticSubsections ( IN PCONTROL_AREA ControlArea ); //++ //VOID //MI_INSERT_UNUSED_SEGMENT ( // IN PCONTROL_AREA _ControlArea // ); // // Routine Description: // // This routine inserts a control area into the unused segment list, // also managing the associated pool charges. // // Arguments // // _ControlArea - Supplies the control area to obtain the pool charges from. // // Return Value: // // None. // //-- #define MI_INSERT_UNUSED_SEGMENT(_ControlArea) \ { \ MM_PFN_LOCK_ASSERT(); \ if ((_ControlArea->u.Flags.Image == 0) && \ (_ControlArea->FilePointer != NULL) && \ (_ControlArea->u.Flags.PhysicalMemory == 0)) { \ MiConvertStaticSubsections(_ControlArea); \ } \ InsertTailList (&MmUnusedSegmentList, &_ControlArea->DereferenceList); \ MmUnusedSegmentCount += 1; \ } //++ //VOID //MI_UNUSED_SEGMENTS_REMOVE_CHARGE ( // IN PCONTROL_AREA _ControlArea // ); // // Routine Description: // // This routine manages pool charges during removals of segments from // the unused segment list. // // Arguments // // _ControlArea - Supplies the control area to obtain the pool charges from. // // Return Value: // // None. // //-- #define MI_UNUSED_SEGMENTS_REMOVE_CHARGE(_ControlArea) \ { \ MM_PFN_LOCK_ASSERT(); \ MmUnusedSegmentCount -= 1; \ } // // List heads // extern MMWORKING_SET_EXPANSION_HEAD MmWorkingSetExpansionHead; extern MMPAGE_FILE_EXPANSION MmAttemptForCantExtend; // // Paging files // extern MMMOD_WRITER_LISTHEAD MmPagingFileHeader; extern MMMOD_WRITER_LISTHEAD MmMappedFileHeader; extern PMMPAGING_FILE MmPagingFile[MAX_PAGE_FILES]; extern LIST_ENTRY MmFreePagingSpaceLow; extern ULONG MmNumberOfActiveMdlEntries; extern ULONG MmNumberOfPagingFiles; extern KEVENT MmModifiedPageWriterEvent; extern KEVENT MmCollidedFlushEvent; extern KEVENT MmCollidedLockEvent; // // Total number of committed pages. // extern SIZE_T MmTotalCommittedPages; extern SIZE_T MmTotalCommitLimit; extern SIZE_T MmOverCommit; extern SIZE_T MmSharedCommit; // #define _MI_DEBUG_DATA 1 // Uncomment this for data logging #if defined (_MI_DEBUG_DATA) #define MI_DATA_BACKTRACE_LENGTH 8 typedef struct _MI_DATA_TRACES { PETHREAD Thread; PMMPFN Pfn; PMMPTE PointerPte; MMPFN PfnData; ULONG CallerId; ULONG DataInThePage[2]; PVOID StackTrace [MI_DATA_BACKTRACE_LENGTH]; } MI_DATA_TRACES, *PMI_DATA_TRACES; extern LONG MiDataIndex; extern ULONG MiTrackData; extern PMI_DATA_TRACES MiDataTraces; VOID FORCEINLINE MiSnapData ( IN PMMPFN Pfn, IN PMMPTE PointerPte, IN ULONG CallerId ) { KIRQL OldIrql; PVOID Va; PMI_DATA_TRACES Information; ULONG Index; ULONG Hash; PEPROCESS CurrentProcess; if (MiDataTraces == NULL) { return; } Index = InterlockedIncrement(&MiDataIndex); Index &= (MiTrackData - 1); Information = &MiDataTraces[Index]; Information->Thread = PsGetCurrentThread (); Information->Pfn = Pfn; Information->PointerPte = PointerPte; Information->PfnData = *Pfn; Information->CallerId = CallerId; CurrentProcess = PsGetCurrentProcess (); Va = MiMapPageInHyperSpace (CurrentProcess, Pfn - MmPfnDatabase, &OldIrql); RtlCopyMemory (&Information->DataInThePage[0], Va, sizeof (Information->DataInThePage)); MiUnmapPageInHyperSpace (CurrentProcess, Va, OldIrql); RtlZeroMemory (&Information->StackTrace[0], MI_DATA_BACKTRACE_LENGTH * sizeof(PVOID)); \ RtlCaptureStackBackTrace (0, MI_DATA_BACKTRACE_LENGTH, Information->StackTrace, &Hash); } #define MI_SNAP_DATA(_Pfn, _Pte, _CallerId) MiSnapData(_Pfn, _Pte, _CallerId) #else #define MI_SNAP_DATA(_Pfn, _Pte, _CallerId) #endif // // Modified page writer. // extern PFN_NUMBER MmMinimumFreePages; extern PFN_NUMBER MmFreeGoal; extern PFN_NUMBER MmModifiedPageMaximum; extern PFN_NUMBER MmModifiedPageMinimum; extern ULONG MmModifiedWriteClusterSize; extern ULONG MmMinimumFreeDiskSpace; extern ULONG MmPageFileExtension; extern ULONG MmMinimumPageFileReduction; extern LARGE_INTEGER MiModifiedPageLife; extern BOOLEAN MiTimerPending; extern KEVENT MiMappedPagesTooOldEvent; extern KDPC MiModifiedPageWriterTimerDpc; extern KTIMER MiModifiedPageWriterTimer; // // System process working set sizes. // extern PFN_NUMBER MmSystemProcessWorkingSetMin; extern PFN_NUMBER MmSystemProcessWorkingSetMax; extern PFN_NUMBER MmMinimumWorkingSetSize; // // Support for debugger's mapping physical memory. // extern PMMPTE MmDebugPte; extern PMMPTE MmCrashDumpPte; extern ULONG MiOverCommitCallCount; // // Event tracing routines // extern PPAGE_FAULT_NOTIFY_ROUTINE MmPageFaultNotifyRoutine; extern SIZE_T MmSystemViewSize; VOID FASTCALL MiIdentifyPfn ( IN PMMPFN Pfn1, OUT PMMPFN_IDENTITY PfnIdentity ); #if defined (_WIN64) #define InterlockedExchangeAddSizeT(a, b) InterlockedExchangeAdd64((PLONGLONG)a, b) #else #define InterlockedExchangeAddSizeT(a, b) InterlockedExchangeAdd((PLONG)(a), b) #endif // // This is a special value loaded into an EPROCESS pointer to indicate that // the action underway is for a Hydra session, not really the current process. // (Any value could be used here that is not a valid system pointer or NULL). // #define HYDRA_PROCESS ((PEPROCESS)1) #define PREFETCH_PROCESS ((PEPROCESS)2) #define MI_SESSION_SPACE_STRUCT_SIZE MM_ALLOCATION_GRANULARITY #if defined (_WIN64) /*++ Virtual memory layout of session space when loaded down from 0x2000.0002.0000.0000 (IA64) or FFFF.F980.0000.0000 (AMD64) : Note that the sizes of mapped views, paged pool & images are registry tunable. +------------------------------------+ 2000.0002.0000.0000 | | | win32k.sys & video drivers | | (16MB) | | | +------------------------------------+ 2000.0001.FF00.0000 | | | MM_SESSION_SPACE & Session WSLs | | (16MB) | | | 2000.0001.FEFF.0000 +------------------------------------+ | | | ... | | | +------------------------------------+ 2000.0001.FE80.0000 | | | Mapped Views for this session | | (104MB) | | | +------------------------------------+ 2000.0001.F800.0000 | | | Paged Pool for this session | | (64MB) | | | 2000.0001.F400.0000 +------------------------------------+ | Special Pool for this session | | (64MB) | | | 2000.0000.0000.0000 +------------------------------------+ --*/ #define MI_SESSION_SPACE_WS_SIZE ((ULONG_PTR)(16*1024*1024) - MI_SESSION_SPACE_STRUCT_SIZE) #define MI_SESSION_DEFAULT_IMAGE_SIZE ((ULONG_PTR)(16*1024*1024)) #define MI_SESSION_DEFAULT_VIEW_SIZE ((ULONG_PTR)(104*1024*1024)) #define MI_SESSION_DEFAULT_POOL_SIZE ((ULONG_PTR)(64*1024*1024)) #define MI_SESSION_SPACE_MAXIMUM_TOTAL_SIZE (MM_VA_MAPPED_BY_PPE) #else /*++ Virtual memory layout of session space when loaded down from 0xC0000000. Note that the sizes of mapped views, paged pool and images are registry tunable on 32-bit systems (if NOT booted /3GB, as 3GB has very limited address space). +------------------------------------+ C0000000 | | | win32k.sys, video drivers and any | | rebased NT4 printer drivers. | | | | (8MB) | | | +------------------------------------+ BF800000 | | | MM_SESSION_SPACE & Session WSLs | | (4MB) | | | +------------------------------------+ BF400000 | | | Mapped views for this session | | (20MB by default, but is | | registry configurable) | | | +------------------------------------+ BE000000 | | | Paged pool for this session | | (16MB by default, but is | | registry configurable) | | | BD000000 +------------------------------------+ --*/ #define MI_SESSION_SPACE_WS_SIZE (4*1024*1024 - MI_SESSION_SPACE_STRUCT_SIZE) #define MI_SESSION_DEFAULT_IMAGE_SIZE (8*1024*1024) #define MI_SESSION_DEFAULT_VIEW_SIZE (20*1024*1024) #define MI_SESSION_DEFAULT_POOL_SIZE (16*1024*1024) #define MI_SESSION_SPACE_MAXIMUM_TOTAL_SIZE \ (MM_SYSTEM_CACHE_END_EXTRA - MM_KSEG2_BASE) #endif #define MI_SESSION_SPACE_DEFAULT_TOTAL_SIZE \ (MI_SESSION_DEFAULT_IMAGE_SIZE + \ MI_SESSION_SPACE_STRUCT_SIZE + \ MI_SESSION_SPACE_WS_SIZE + \ MI_SESSION_DEFAULT_VIEW_SIZE + \ MI_SESSION_DEFAULT_POOL_SIZE) extern ULONG_PTR MmSessionBase; extern PMMPTE MiSessionBasePte; extern PMMPTE MiSessionLastPte; extern ULONG_PTR MiSessionSpaceWs; extern ULONG_PTR MiSessionViewStart; extern SIZE_T MmSessionViewSize; extern ULONG_PTR MiSessionImageStart; extern ULONG_PTR MiSessionImageEnd; extern SIZE_T MmSessionImageSize; extern PMMPTE MiSessionImagePteStart; extern PMMPTE MiSessionImagePteEnd; extern ULONG_PTR MiSessionPoolStart; extern ULONG_PTR MiSessionPoolEnd; extern SIZE_T MmSessionPoolSize; extern ULONG_PTR MiSessionSpaceEnd; extern ULONG MiSessionSpacePageTables; // // The number of page table pages required to map all of session space. // #define MI_SESSION_SPACE_MAXIMUM_PAGE_TABLES \ (MI_SESSION_SPACE_MAXIMUM_TOTAL_SIZE / MM_VA_MAPPED_BY_PDE) extern SIZE_T MmSessionSize; // size of the entire session space. // // Macros to determine if a given address lies in the specified session range. // #define MI_IS_SESSION_IMAGE_ADDRESS(VirtualAddress) \ ((PVOID)(VirtualAddress) >= (PVOID)MiSessionImageStart && (PVOID)(VirtualAddress) < (PVOID)(MiSessionImageEnd)) #define MI_IS_SESSION_POOL_ADDRESS(VirtualAddress) \ ((PVOID)(VirtualAddress) >= (PVOID)MiSessionPoolStart && (PVOID)(VirtualAddress) < (PVOID)MiSessionPoolEnd) #define MI_IS_SESSION_ADDRESS(_VirtualAddress) \ ((PVOID)(_VirtualAddress) >= (PVOID)MmSessionBase && (PVOID)(_VirtualAddress) < (PVOID)(MiSessionSpaceEnd)) #define MI_IS_SESSION_PTE(_Pte) \ ((PMMPTE)(_Pte) >= MiSessionBasePte && (PMMPTE)(_Pte) < MiSessionLastPte) #define MI_IS_SESSION_IMAGE_PTE(_Pte) \ ((PMMPTE)(_Pte) >= MiSessionImagePteStart && (PMMPTE)(_Pte) < MiSessionImagePteEnd) #define SESSION_GLOBAL(_Session) (_Session->GlobalVirtualAddress) #define MM_DBG_SESSION_INITIAL_PAGETABLE_ALLOC 0 #define MM_DBG_SESSION_INITIAL_PAGETABLE_FREE_RACE 1 #define MM_DBG_SESSION_INITIAL_PAGE_ALLOC 2 #define MM_DBG_SESSION_INITIAL_PAGE_FREE_FAIL1 3 #define MM_DBG_SESSION_INITIAL_PAGETABLE_FREE_FAIL1 4 #define MM_DBG_SESSION_WS_PAGE_FREE 5 #define MM_DBG_SESSION_PAGETABLE_ALLOC 6 #define MM_DBG_SESSION_SYSMAPPED_PAGES_ALLOC 7 #define MM_DBG_SESSION_WS_PAGETABLE_ALLOC 8 #define MM_DBG_SESSION_PAGEDPOOL_PAGETABLE_ALLOC 9 #define MM_DBG_SESSION_PAGEDPOOL_PAGETABLE_FREE_FAIL1 10 #define MM_DBG_SESSION_WS_PAGE_ALLOC 11 #define MM_DBG_SESSION_WS_PAGE_ALLOC_GROWTH 12 #define MM_DBG_SESSION_INITIAL_PAGE_FREE 13 #define MM_DBG_SESSION_PAGETABLE_FREE 14 #define MM_DBG_SESSION_PAGEDPOOL_PAGETABLE_ALLOC1 15 #define MM_DBG_SESSION_DRIVER_PAGES_LOCKED 16 #define MM_DBG_SESSION_DRIVER_PAGES_UNLOCKED 17 #define MM_DBG_SESSION_WS_HASHPAGE_ALLOC 18 #define MM_DBG_SESSION_SYSMAPPED_PAGES_COMMITTED 19 #define MM_DBG_SESSION_COMMIT_PAGEDPOOL_PAGES 30 #define MM_DBG_SESSION_COMMIT_DELETE_VM_RETURN 31 #define MM_DBG_SESSION_COMMIT_POOL_FREED 32 #define MM_DBG_SESSION_COMMIT_IMAGE_UNLOAD 33 #define MM_DBG_SESSION_COMMIT_IMAGELOAD_FAILED1 34 #define MM_DBG_SESSION_COMMIT_IMAGELOAD_FAILED2 35 #define MM_DBG_SESSION_COMMIT_IMAGELOAD_NOACCESS 36 #define MM_DBG_SESSION_NP_LOCK_CODE1 38 #define MM_DBG_SESSION_NP_LOCK_CODE2 39 #define MM_DBG_SESSION_NP_SESSION_CREATE 40 #define MM_DBG_SESSION_NP_PAGETABLE_ALLOC 41 #define MM_DBG_SESSION_NP_POOL_CREATE 42 #define MM_DBG_SESSION_NP_COMMIT_IMAGE 43 #define MM_DBG_SESSION_NP_COMMIT_IMAGE_PT 44 #define MM_DBG_SESSION_NP_INIT_WS 45 #define MM_DBG_SESSION_NP_WS_GROW 46 #define MM_DBG_SESSION_NP_HASH_GROW 47 #define MM_DBG_SESSION_NP_PAGE_DRIVER 48 #define MM_DBG_SESSION_NP_POOL_CREATE_FAILED 49 #define MM_DBG_SESSION_NP_WS_PAGE_FREE 50 #define MM_DBG_SESSION_NP_SESSION_DESTROY 51 #define MM_DBG_SESSION_NP_SESSION_PTDESTROY 52 #define MM_DBG_SESSION_NP_DELVA 53 #if DBG #define MM_SESS_COUNTER_MAX 54 #define MM_BUMP_SESS_COUNTER(_index, bump) \ if (_index >= MM_SESS_COUNTER_MAX) { \ DbgPrint("Mm: Invalid bump counter %d %d\n", _index, MM_SESS_COUNTER_MAX); \ DbgBreakPoint(); \ } \ MmSessionSpace->Debug[_index] += (bump); typedef struct _MM_SESSION_MEMORY_COUNTERS { SIZE_T NonPagablePages; SIZE_T CommittedPages; } MM_SESSION_MEMORY_COUNTERS, *PMM_SESSION_MEMORY_COUNTERS; #define MM_SESS_MEMORY_COUNTER_MAX 8 #define MM_SNAP_SESS_MEMORY_COUNTERS(_index) \ if (_index >= MM_SESS_MEMORY_COUNTER_MAX) { \ DbgPrint("Mm: Invalid session mem counter %d %d\n", _index, MM_SESS_MEMORY_COUNTER_MAX); \ DbgBreakPoint(); \ } \ else { \ MmSessionSpace->Debug2[_index].NonPagablePages = MmSessionSpace->NonPagablePages; \ MmSessionSpace->Debug2[_index].CommittedPages = MmSessionSpace->CommittedPages; \ } #else #define MM_BUMP_SESS_COUNTER(_index, bump) #define MM_SNAP_SESS_MEMORY_COUNTERS(_index) #endif #define MM_SESSION_FAILURE_NO_IDS 0 #define MM_SESSION_FAILURE_NO_COMMIT 1 #define MM_SESSION_FAILURE_NO_RESIDENT 2 #define MM_SESSION_FAILURE_RACE_DETECTED 3 #define MM_SESSION_FAILURE_NO_SYSPTES 4 #define MM_SESSION_FAILURE_NO_PAGED_POOL 5 #define MM_SESSION_FAILURE_NO_NONPAGED_POOL 6 #define MM_SESSION_FAILURE_NO_IMAGE_VA_SPACE 7 #define MM_SESSION_FAILURE_NO_SESSION_PAGED_POOL 8 #define MM_SESSION_FAILURE_NO_AVAILABLE 9 #define MM_SESSION_FAILURE_CAUSES 10 ULONG MmSessionFailureCauses[MM_SESSION_FAILURE_CAUSES]; #define MM_BUMP_SESSION_FAILURES(_index) MmSessionFailureCauses[_index] += 1; typedef struct _MM_SESSION_SPACE_FLAGS { ULONG Initialized : 1; ULONG Filler0 : 1; ULONG WorkingSetInserted : 1; ULONG SessionListInserted : 1; ULONG HasWsLock : 1; ULONG DeletePending : 1; ULONG Filler : 26; } MM_SESSION_SPACE_FLAGS; // // The session space data structure - allocated per session and only visible at // MM_SESSION_SPACE_BASE when in the context of a process from the session. // This virtual address space is rotated at context switch time when switching // from a process in session A to a process in session B. This rotation is // useful for things like providing paged pool per session so many sessions // won't exhaust the VA space which backs the system global pool. // // A kernel PTE is also allocated to double map this page so that global // pointers can be maintained to provide system access from any process context. // This is needed for things like mutexes and WSL chains. // typedef struct _MM_SESSION_SPACE { ULONG ReferenceCount; union { ULONG LongFlags; MM_SESSION_SPACE_FLAGS Flags; } u; ULONG SessionId; // // All the page tables for session space use this as their parent. // Note that it's not really a page directory - it's really a page // table page itself (the one used to map this very structure). // // This provides a reference to something that won't go away and // is relevant regardless of which process within the session is current. // PFN_NUMBER SessionPageDirectoryIndex; // // This is a pointer in global system address space, used to make various // fields that can be referenced from any process visible from any process // context. This is for things like mutexes, WSL chains, etc. // struct _MM_SESSION_SPACE *GlobalVirtualAddress; // // This is the list of the processes in this group that have // session space entries. // LIST_ENTRY ProcessList; // // Pool allocation counts - these are always valid. // SIZE_T NonPagedPoolBytes; SIZE_T PagedPoolBytes; ULONG NonPagedPoolAllocations; ULONG PagedPoolAllocations; // // This is the count of non paged allocations to support this session // space. This includes the session structure page table and data pages, // WSL page table and data pages, session pool page table pages and session // image page table pages. These are all charged against // MmResidentAvailable. // SIZE_T NonPagablePages; // // This is the count of pages in this session that have been charged against // the systemwide commit. This includes all the NonPagablePages plus the // data pages they typically map. // SIZE_T CommittedPages; LARGE_INTEGER LastProcessSwappedOutTime; #if (_MI_PAGING_LEVELS >= 3) // // The page directory that maps session space is saved here so // trimmers can attach. // MMPTE PageDirectory; #else // // The second level page tables that map session space are shared // by all processes in the session. // PMMPTE PageTables; #endif // // Session space paged pool support. // FAST_MUTEX PagedPoolMutex; // // Start of session paged pool virtual space. // PVOID PagedPoolStart; // // Current end of pool virtual space. Can be extended to the // end of the session space. // PVOID PagedPoolEnd; // // PTE pointers for pool. // PMMPTE PagedPoolBasePde; MM_PAGED_POOL_INFO PagedPoolInfo; ULONG Color; ULONG ProcessOutSwapCount; // // This is the list of system images currently valid in // this session space. This information is in addition // to the module global information in PsLoadedModuleList. // LIST_ENTRY ImageList; // // The system PTE self-map entry. // PMMPTE GlobalPteEntry; ULONG CopyOnWriteCount; ULONG SessionPoolAllocationFailures[4]; // // The count of "known attachers and the associated event. // ULONG AttachCount; KEVENT AttachEvent; PEPROCESS LastProcess; // // Working set information. // MMSUPPORT Vm; PMMWSLE Wsle; ERESOURCE WsLock; // owned by WorkingSetLockOwner // // This chain is in global system addresses (not session VAs) and can // be walked from any system context, ie: for WSL trimming. // LIST_ENTRY WsListEntry; // // Support for mapping system views into session space. Each desktop // allocates a 3MB heap and the global system view space is only 48M // total. This would limit us to only 20-30 users - rotating the // system view space with each session removes this limitation. // MMSESSION Session; // // This is the driver object entry for WIN32K.SYS // // It is not a real driver object, but contained here // for information such as the DriverUnload routine. // DRIVER_OBJECT Win32KDriverObject; PETHREAD WorkingSetLockOwner; // // Pool descriptor for less than 1 page allocations. // POOL_DESCRIPTOR PagedPool; // // This is generally decremented in process delete (not clean) so that // the session data page and mapping PTE can finally be freed when this // reaches zero. smss is the only process that decrements it in other // places as smss never exits. // LONG ProcessReferenceToSession; LCID LocaleId; #if defined (_WIN64) // // NT64 has enough virtual address space to support per-session special // pool. // PMMPTE SpecialPoolFirstPte; PMMPTE SpecialPoolLastPte; PMMPTE NextPdeForSpecialPoolExpansion; PMMPTE LastPdeForSpecialPoolExpansion; PFN_NUMBER SpecialPagesInUse; #endif #if defined(_IA64_) REGION_MAP_INFO SessionMapInfo; PFN_NUMBER PageDirectoryParentPage; #endif #if DBG ULONG Debug[MM_SESS_COUNTER_MAX]; MM_SESSION_MEMORY_COUNTERS Debug2[MM_SESS_MEMORY_COUNTER_MAX]; #endif } MM_SESSION_SPACE, *PMM_SESSION_SPACE; extern PMM_SESSION_SPACE MmSessionSpace; extern ULONG MiSessionCount; // // This could be improved to just flush the non-global TB entries. // #define MI_FLUSH_SESSION_TB() KeFlushEntireTb (TRUE, TRUE); // // The default number of pages for the session working set minimum & maximum. // #define MI_SESSION_SPACE_WORKING_SET_MINIMUM 20 #define MI_SESSION_SPACE_WORKING_SET_MAXIMUM 384 NTSTATUS MiSessionCommitPageTables ( PVOID StartVa, PVOID EndVa ); NTSTATUS MiInitializeAndChargePfn ( OUT PPFN_NUMBER PageFrameIndex, IN PMMPTE PointerPde, IN PFN_NUMBER ContainingPageFrame, IN LOGICAL SessionAllocation ); VOID MiSessionPageTableRelease ( IN PFN_NUMBER PageFrameIndex ); NTSTATUS MiInitializeSessionPool ( VOID ); VOID MiCheckSessionPoolAllocations ( VOID ); VOID MiFreeSessionPoolBitMaps ( VOID ); VOID MiDetachSession ( VOID ); VOID MiAttachSession ( IN PMM_SESSION_SPACE SessionGlobal ); PVOID MiAttachToSecureProcessInSession ( IN PRKAPC_STATE ApcState ); VOID MiDetachFromSecureProcessInSession ( IN PVOID OpaqueSession, IN PRKAPC_STATE ApcState ); VOID MiReleaseProcessReferenceToSessionDataPage ( PMM_SESSION_SPACE SessionGlobal ); #define MM_SET_SESSION_RESOURCE_OWNER(_Thread) \ ASSERT (MmSessionSpace->WorkingSetLockOwner == NULL); \ MmSessionSpace->WorkingSetLockOwner = _Thread; #define MM_CLEAR_SESSION_RESOURCE_OWNER() \ ASSERT (MmSessionSpace->WorkingSetLockOwner == PsGetCurrentThread()); \ MmSessionSpace->WorkingSetLockOwner = NULL; #define MM_SESSION_SPACE_WS_LOCK_ASSERT() \ ASSERT (MmSessionSpace->WorkingSetLockOwner == PsGetCurrentThread()) #define LOCK_SESSION_SPACE_WS(OLDIRQL,_Thread) \ ASSERT (KeGetCurrentIrql() <= APC_LEVEL); \ KeRaiseIrql(APC_LEVEL,&OLDIRQL); \ ExAcquireResourceExclusiveLite(&MmSessionSpace->WsLock, TRUE); \ MM_SET_SESSION_RESOURCE_OWNER (_Thread); #define UNLOCK_SESSION_SPACE_WS(OLDIRQL) \ MM_CLEAR_SESSION_RESOURCE_OWNER (); \ ExReleaseResourceLite (&MmSessionSpace->WsLock); \ KeLowerIrql (OLDIRQL); \ ASSERT (KeGetCurrentIrql() <= APC_LEVEL); extern PMMPTE MiHighestUserPte; extern PMMPTE MiHighestUserPde; #if (_MI_PAGING_LEVELS >= 4) extern PMMPTE MiHighestUserPpe; extern PMMPTE MiHighestUserPxe; #endif NTSTATUS MiEmptyWorkingSet ( IN PMMSUPPORT WsInfo, IN LOGICAL WaitOk ); //++ //ULONG //MiGetPdeSessionIndex ( // IN PVOID va // ); // // Routine Description: // // MiGetPdeSessionIndex returns the session structure index for the PDE // will (or does) map the given virtual address. // // Arguments // // Va - Supplies the virtual address to locate the PDE index for. // // Return Value: // // The index of the PDE entry. // //-- #define MiGetPdeSessionIndex(va) ((ULONG)(((ULONG_PTR)(va) - (ULONG_PTR)MmSessionBase) >> PDI_SHIFT)) // // Session space contains the image loader and tracker, virtual // address allocator, paged pool allocator, system view image mappings, // and working set for kernel mode virtual addresses that are instanced // for groups of processes in a Session process group. This // process group is identified by a SessionId. // // Each Session process group's loaded kernel modules, paged pool // allocations, working set, and mapped system views are separate from // other Session process groups, even though they have the same // virtual addresses. // // This is to support the Hydra multi-user Windows NT system by // replicating WIN32K.SYS, and its complement of video and printer drivers, // desktop heaps, memory allocations, etc. // // // Structure linked into a session space structure to describe // which system images in PsLoadedModuleTable and // SESSION_DRIVER_GLOBAL_LOAD_ADDRESS's // have been allocated for the current session space. // // The reference count tracks the number of loads of this image within // this session. // typedef struct _IMAGE_ENTRY_IN_SESSION { LIST_ENTRY Link; PVOID Address; PVOID LastAddress; ULONG ImageCountInThisSession; PMMPTE PrototypePtes; PKLDR_DATA_TABLE_ENTRY DataTableEntry; } IMAGE_ENTRY_IN_SESSION, *PIMAGE_ENTRY_IN_SESSION; extern LIST_ENTRY MiSessionWsList; NTSTATUS FASTCALL MiCheckPdeForSessionSpace( IN PVOID VirtualAddress ); NTSTATUS MiShareSessionImage ( IN PSECTION Section, IN OUT PSIZE_T ViewSize ); VOID MiSessionWideInitializeAddresses ( VOID ); NTSTATUS MiSessionWideReserveImageAddress ( IN PUNICODE_STRING pImageName, IN PSECTION Section, IN ULONG_PTR Alignment, OUT PVOID *ppAddr, OUT PLOGICAL pAlreadyLoaded ); VOID MiInitializeSessionIds ( VOID ); VOID MiInitializeSessionWsSupport( VOID ); VOID MiSessionAddProcess ( IN PEPROCESS NewProcess ); VOID MiSessionRemoveProcess ( VOID ); NTSTATUS MiRemovePsLoadedModule( PKLDR_DATA_TABLE_ENTRY DataTableEntry ); NTSTATUS MiRemoveImageSessionWide( IN PVOID BaseAddr ); NTSTATUS MiDeleteSessionVirtualAddresses( IN PVOID VirtualAddress, IN SIZE_T NumberOfBytes ); NTSTATUS MiUnloadSessionImageByForce ( IN SIZE_T NumberOfPtes, IN PVOID ImageBase ); NTSTATUS MiSessionWideGetImageSize( IN PVOID BaseAddress, OUT PSIZE_T NumberOfBytes OPTIONAL, OUT PSIZE_T CommitPages OPTIONAL ); PIMAGE_ENTRY_IN_SESSION MiSessionLookupImage ( IN PVOID BaseAddress ); NTSTATUS MiSessionCommitImagePages( PVOID BaseAddr, SIZE_T Size ); VOID MiSessionUnloadAllImages ( VOID ); VOID MiFreeSessionSpaceMap ( VOID ); NTSTATUS MiSessionInitializeWorkingSetList ( VOID ); VOID MiSessionUnlinkWorkingSet ( VOID ); NTSTATUS MiSessionCopyOnWrite ( IN PVOID FaultingAddress, IN PMMPTE PointerPte ); VOID MiSessionOutSwapProcess ( IN PEPROCESS Process ); VOID MiSessionInSwapProcess ( IN PEPROCESS Process ); #if !defined (_X86PAE_) #define MI_GET_DIRECTORY_FRAME_FROM_PROCESS(_Process) \ MI_GET_PAGE_FRAME_FROM_PTE((PMMPTE)(&((_Process)->Pcb.DirectoryTableBase[0]))) #define MI_GET_HYPER_PAGE_TABLE_FRAME_FROM_PROCESS(_Process) \ MI_GET_PAGE_FRAME_FROM_PTE((PMMPTE)(&((_Process)->Pcb.DirectoryTableBase[1]))) #else #define MI_GET_DIRECTORY_FRAME_FROM_PROCESS(_Process) \ (MI_GET_PAGE_FRAME_FROM_PTE(((PMMPTE)((_Process)->PaeTop)) + PD_PER_SYSTEM - 1)) #define MI_GET_HYPER_PAGE_TABLE_FRAME_FROM_PROCESS(_Process) \ ((PFN_NUMBER)((_Process)->Pcb.DirectoryTableBase[1])) #endif #if defined(_MIALT4K_) NTSTATUS MiSetCopyPagesFor4kPage ( IN PEPROCESS Process, IN PMMVAD Vad, IN OUT PVOID StartingAddress, IN OUT PVOID EndingAddress, IN ULONG ProtectionMask, OUT PMMVAD *CallerNewVad ); VOID MiRemoveAliasedVads ( IN PEPROCESS Process, IN PMMVAD Vad ); PVOID MiDuplicateAliasVadList ( IN PMMVAD Vad ); #endif // // The LDR_DATA_TABLE_ENTRY->LoadedImports is used as a list of imported DLLs. // // This field is zero if the module was loaded at boot time and the // import information was never filled in. // // This field is -1 if no imports are defined by the module. // // This field contains a valid paged pool PLDR_DATA_TABLE_ENTRY pointer // with a low-order (bit 0) tag of 1 if there is only 1 usable import needed // by this driver. // // This field will contain a valid paged pool PLOAD_IMPORTS pointer in all // other cases (ie: where at least 2 imports exist). // typedef struct _LOAD_IMPORTS { SIZE_T Count; PKLDR_DATA_TABLE_ENTRY Entry[1]; } LOAD_IMPORTS, *PLOAD_IMPORTS; #define LOADED_AT_BOOT ((PLOAD_IMPORTS)0) #define NO_IMPORTS_USED ((PLOAD_IMPORTS)-2) #define SINGLE_ENTRY(ImportVoid) ((ULONG)((ULONG_PTR)(ImportVoid) & 0x1)) #define SINGLE_ENTRY_TO_POINTER(ImportVoid) ((PKLDR_DATA_TABLE_ENTRY)((ULONG_PTR)(ImportVoid) & ~0x1)) #define POINTER_TO_SINGLE_ENTRY(Pointer) ((PKLDR_DATA_TABLE_ENTRY)((ULONG_PTR)(Pointer) | 0x1)) // // This tracks allocated group virtual addresses. The term SESSIONWIDE is used // to denote data that is the same across all sessions (as opposed to // per-session data which can vary from session to session). // // Since each driver loaded into a session space is linked and fixed up // against the system image, it must remain at the same virtual address // across the system regardless of the session. // // A list is maintained by the group allocator of which virtual // addresses are in use and by which DLL. // // The reference count tracks the number of sessions that have loaded // this image. // // Access to this structure is guarded by the MmSystemLoadLock. // typedef struct _SESSIONWIDE_DRIVER_ADDRESS { LIST_ENTRY Link; ULONG ReferenceCount; PVOID Address; ULONG_PTR Size; ULONG_PTR WritablePages; UNICODE_STRING FullDllName; } SESSIONWIDE_DRIVER_ADDRESS, *PSESSIONWIDE_DRIVER_ADDRESS; // #define _MI_DEBUG_RONLY 1 // Uncomment this for session readonly tracking #if _MI_DEBUG_RONLY VOID MiAssertNotSessionData ( IN PMMPTE PointerPte ); VOID MiLogSessionDataStart ( IN PKLDR_DATA_TABLE_ENTRY DataTableEntry ); #define MI_ASSERT_NOT_SESSION_DATA(PTE) MiAssertNotSessionData(PTE) #define MI_LOG_SESSION_DATA_START(DataTableEntry) MiLogSessionDataStart(DataTableEntry) #else #define MI_ASSERT_NOT_SESSION_DATA(PTE) #define MI_LOG_SESSION_DATA_START(DataTableEntry) #endif // // This tracks driver-specified individual verifier thunks. // typedef struct _DRIVER_SPECIFIED_VERIFIER_THUNKS { LIST_ENTRY ListEntry; PKLDR_DATA_TABLE_ENTRY DataTableEntry; ULONG NumberOfThunks; } DRIVER_SPECIFIED_VERIFIER_THUNKS, *PDRIVER_SPECIFIED_VERIFIER_THUNKS; // #define _MI_DEBUG_SUB 1 // Uncomment this for subsection logging #if defined (_MI_DEBUG_SUB) extern ULONG MiTrackSubs; #define MI_SUB_BACKTRACE_LENGTH 8 typedef struct _MI_SUB_TRACES { PETHREAD Thread; PMSUBSECTION Subsection; PCONTROL_AREA ControlArea; ULONG_PTR CallerId; PVOID StackTrace [MI_SUB_BACKTRACE_LENGTH]; MSUBSECTION SubsectionContents; CONTROL_AREA ControlAreaContents; } MI_SUB_TRACES, *PMI_SUB_TRACES; extern LONG MiSubsectionIndex; extern PMI_SUB_TRACES MiSubsectionTraces; VOID FORCEINLINE MiSnapSubsection ( IN PMSUBSECTION Subsection, IN ULONG CallerId ) { PMI_SUB_TRACES Information; PCONTROL_AREA ControlArea; ULONG Index; ULONG Hash; if (MiSubsectionTraces == NULL) { return; } ControlArea = Subsection->ControlArea; #if 0 if (ControlArea->u.Flags.Mft == 0) { return; } #endif Index = InterlockedIncrement(&MiSubsectionIndex); Index &= (MiTrackSubs - 1); Information = &MiSubsectionTraces[Index]; Information->Subsection = Subsection; Information->ControlArea = ControlArea; *(PMSUBSECTION)&Information->SubsectionContents = *Subsection; *(PCONTROL_AREA)&Information->ControlAreaContents = *ControlArea; Information->Thread = PsGetCurrentThread(); Information->CallerId = CallerId; RtlCaptureStackBackTrace (0, MI_SUB_BACKTRACE_LENGTH, Information->StackTrace, &Hash); } #define MI_SNAP_SUB(_Sub, callerid) MiSnapSubsection(_Sub, callerid) #else #define MI_SNAP_SUB(_Sub, callerid) #endif #ifdef _MI_MESSAGE_SERVER LOGICAL MiQueueMessage ( IN PVOID Message ); PVOID MiRemoveMessage ( VOID ); #define MI_INSTRUMENT_QUEUE(Message) MiQueueMessage (Message) #define MI_INSTRUMENTR_QUEUE() MiRemoveMessage () #else #define MI_INSTRUMENT_QUEUE(Message) #define MI_INSTRUMENTR_QUEUE() #endif #endif // MI