From 4cba65d760a730ea4b77ab6fee857e563c0984d8 Mon Sep 17 00:00:00 2001 From: Justin Miller Date: Thu, 5 Mar 2026 14:57:09 +0000 Subject: [PATCH] [NTOS:ARM3] Change calculation for more system PTEs (#8613) * [NTOSKRNL] Increase MI_NUMBER_SYSTEM_PTES for AMD64 * [NTOS:MM] Move the system PTE space on x86 to a different spot * [KMTESTS] Adjust KmTest --- modules/rostests/kmtests/ntos_mm/MmMdl.c | 12 ++- ntoskrnl/include/internal/amd64/mm.h | 2 +- ntoskrnl/mm/ARM3/i386/init.c | 93 +++++++++++++----------- ntoskrnl/mm/ARM3/miarm.h | 1 + ntoskrnl/mm/ARM3/mminit.c | 26 ++++++- ntoskrnl/mm/mminit.c | 15 +++- 6 files changed, 97 insertions(+), 52 deletions(-) diff --git a/modules/rostests/kmtests/ntos_mm/MmMdl.c b/modules/rostests/kmtests/ntos_mm/MmMdl.c index cdbc4f294ac..934247f8d33 100644 --- a/modules/rostests/kmtests/ntos_mm/MmMdl.c +++ b/modules/rostests/kmtests/ntos_mm/MmMdl.c @@ -134,7 +134,15 @@ TestMmAllocatePagesForMdl(VOID) FALSE, NormalPagePriority); #ifdef _M_IX86 - ok(SystemVa == NULL, "MmMapLockedPagesSpecifyCache succeeded for 2 GB\n"); + /* + * MmAllocatePagesForMdl is allowed to return fewer pages than requested. + * Only enforce the x86 mapping expectation if we actually got ~2GB. + */ + if (MmGetMdlByteCount(Mdl) >= (1UL << 31)) + ok(SystemVa == NULL, "MmMapLockedPagesSpecifyCache succeeded for 2 GB\n"); + else + trace("Skipping 2GB mapping expectation (allocated %lu bytes)\n", + MmGetMdlByteCount(Mdl)); #endif if (SystemVa != NULL) MmUnmapLockedPages(SystemVa, Mdl); @@ -173,7 +181,7 @@ TestMmAllocatePagesForMdl(VOID) trace("MmMapLockedPagesSpecifyCache failed with i = %lu\n", i); break; } - ok(MmGetMdlByteCount(Mdls[i]) == 32 * 1024 * 1024, "Byte count: %lu\n", MmGetMdlByteCount(Mdls[i])); + ok(MmGetMdlByteCount(Mdls[i]) <= 32 * 1024 * 1024, "Byte count: %lu\n", MmGetMdlByteCount(Mdls[i])); ok(MmGetMdlVirtualAddress(Mdls[i]) == NULL, "Virtual address: %p, System VA: %p\n", MmGetMdlVirtualAddress(Mdls[i]), SystemVas[i]); ok(Mdls[i]->MappedSystemVa == SystemVas[i], "MappedSystemVa: %p\n", Mdls[i]->MappedSystemVa, SystemVas[i]); ok((Mdls[i]->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA), "MdlFlags: %lx\n", Mdls[i]->MdlFlags); diff --git a/ntoskrnl/include/internal/amd64/mm.h b/ntoskrnl/include/internal/amd64/mm.h index 62b4e246e9a..260da054815 100644 --- a/ntoskrnl/include/internal/amd64/mm.h +++ b/ntoskrnl/include/internal/amd64/mm.h @@ -76,7 +76,7 @@ #define MI_MIN_SECONDARY_COLORS 8 #define MI_SECONDARY_COLORS 64 #define MI_MAX_SECONDARY_COLORS 1024 -#define MI_NUMBER_SYSTEM_PTES 22000 +#define MI_NUMBER_SYSTEM_PTES (22000 * 22) #define MI_MAX_FREE_PAGE_LISTS 4 #define MI_HYPERSPACE_PTES (256 - 1) #define MI_ZERO_PTES (32) diff --git a/ntoskrnl/mm/ARM3/i386/init.c b/ntoskrnl/mm/ARM3/i386/init.c index 143a6705b17..70fa928ea27 100644 --- a/ntoskrnl/mm/ARM3/i386/init.c +++ b/ntoskrnl/mm/ARM3/i386/init.c @@ -245,7 +245,6 @@ MiInitMachineDependent(IN PLOADER_PARAMETER_BLOCK LoaderBlock) PMMPTE StartPde, EndPde, PointerPte, LastPte; MMPTE TempPde, TempPte; PVOID NonPagedPoolExpansionVa; - SIZE_T NonPagedSystemSize; KIRQL OldIrql; PMMPFN Pfn1; ULONG Flags; @@ -294,43 +293,12 @@ MiInitMachineDependent(IN PLOADER_PARAMETER_BLOCK LoaderBlock) DPRINT("NP Pool has been tuned to: %lu bytes and %lu bytes\n", MmSizeOfNonPagedPoolInBytes, MmMaximumNonPagedPoolInBytes); - // - // Now calculate the nonpaged system VA region, which includes the - // nonpaged pool expansion (above) and the system PTEs. Note that it is - // then aligned to a PDE boundary (4MB). - // - NonPagedSystemSize = (MmNumberOfSystemPtes + 1) * PAGE_SIZE; - MmNonPagedSystemStart = (PVOID)((ULONG_PTR)MmNonPagedPoolStart - - NonPagedSystemSize); - MmNonPagedSystemStart = (PVOID)((ULONG_PTR)MmNonPagedSystemStart & - ~(PDE_MAPPED_VA - 1)); - - // - // Don't let it go below the minimum - // - if (MmNonPagedSystemStart < (PVOID)0xEB000000) - { - // - // This is a hard-coded limit in the Windows NT address space - // - MmNonPagedSystemStart = (PVOID)0xEB000000; - - // - // Reduce the amount of system PTEs to reach this point - // - MmNumberOfSystemPtes = ((ULONG_PTR)MmNonPagedPoolStart - - (ULONG_PTR)MmNonPagedSystemStart) >> - PAGE_SHIFT; - MmNumberOfSystemPtes--; - ASSERT(MmNumberOfSystemPtes > 1000); - } - // // Check if we are in a situation where the size of the paged pool - // is so large that it overflows into nonpaged pool + // is so large that it overflows into nonpaged pool expansion VA // if (MmSizeOfPagedPoolInBytes > - ((ULONG_PTR)MmNonPagedSystemStart - (ULONG_PTR)MmPagedPoolStart)) + ((ULONG_PTR)NonPagedPoolExpansionVa - (ULONG_PTR)MmPagedPoolStart)) { // // We need some recalculations here @@ -347,6 +315,29 @@ MiInitMachineDependent(IN PLOADER_PARAMETER_BLOCK LoaderBlock) MmPfnDatabase = (PVOID)0xB0000000; ASSERT(((ULONG_PTR)MmPfnDatabase & (PDE_MAPPED_VA - 1)) == 0); + // + // Use the gap between loader mappings and PFN database for + // System PTEs: place it right after the loader mappings + // using MmBootImageSize (LoaderPagesSpanned, PDE-aligned). + // + MmSystemPteSpaceStart = (PVOID)((ULONG_PTR)KSEG0_BASE + MmBootImageSize); + ASSERT(((ULONG_PTR)MmSystemPteSpaceStart & (PDE_MAPPED_VA - 1)) == 0); + ASSERT((ULONG_PTR)MmSystemPteSpaceStart < (ULONG_PTR)MmPfnDatabase); + + /* Make sure the System PTE VA space doesn't overlap the PFN DB */ + SIZE_T MaxSystemPtePages = ((ULONG_PTR)MmPfnDatabase - + (ULONG_PTR)MmSystemPteSpaceStart) >> PAGE_SHIFT; + ASSERT(MaxSystemPtePages > 1000); + + MmNumberOfSystemPtes = (ULONG)(MaxSystemPtePages - 1); + ASSERT(MmNumberOfSystemPtes > 1000); + + /* + * Keep MmNonPagedSystemStart initialized for debugger (KD/Windbg) use. + * On x86 ARM3, the System PTE space is placed in the loader-gap region. + */ + MmNonPagedSystemStart = MmSystemPteSpaceStart; + // // Non paged pool comes after the PFN database // @@ -368,9 +359,27 @@ MiInitMachineDependent(IN PLOADER_PARAMETER_BLOCK LoaderBlock) // // Now we need some pages to create the page tables for the NP system VA - // which includes system PTEs and expansion NP + // which includes the System PTE VA region (below the PFN DB) // - StartPde = MiAddressToPde(MmNonPagedSystemStart); + StartPde = MiAddressToPde(MmSystemPteSpaceStart); + EndPde = MiAddressToPde((PVOID)((ULONG_PTR)MmSystemPteSpaceStart + + ((MmNumberOfSystemPtes + 1) * PAGE_SIZE) - 1)); + while (StartPde <= EndPde) + { + TempPde.u.Hard.PageFrameNumber = MxGetNextPage(1); + MI_WRITE_VALID_PTE(StartPde, TempPde); + + PointerPte = MiPteToAddress(StartPde); + RtlZeroMemory(PointerPte, PAGE_SIZE); + + StartPde++; + } + + // + // Now allocate the page tables for the nonpaged pool expansion VA region + // (top-of-kernel VA). This keeps existing nonpaged pool expansion behavior. + // + StartPde = MiAddressToPde(NonPagedPoolExpansionVa); EndPde = MiAddressToPde((PVOID)((ULONG_PTR)MmNonPagedPoolEnd - 1)); while (StartPde <= EndPde) { @@ -443,8 +452,9 @@ MiInitMachineDependent(IN PLOADER_PARAMETER_BLOCK LoaderBlock) // // Sanity check: make sure we have properly defined the system PTE space // - ASSERT(MiAddressToPte(MmNonPagedSystemStart) < - MiAddressToPte(MmNonPagedPoolExpansionStart)); + ASSERT(((ULONG_PTR)MmSystemPteSpaceStart + + ((MmNumberOfSystemPtes + 1) * PAGE_SIZE)) <= + (ULONG_PTR)MmPfnDatabase); /* Now go ahead and initialize the nonpaged pool */ MiInitializeNonPagedPool(); @@ -471,12 +481,9 @@ MiInitMachineDependent(IN PLOADER_PARAMETER_BLOCK LoaderBlock) InitializePool(NonPagedPool, 0); // - // We PDE-aligned the nonpaged system start VA, so haul some extra PTEs! + // Initialize the System PTE allocator at the requested VA start. // - PointerPte = MiAddressToPte(MmNonPagedSystemStart); - MmNumberOfSystemPtes = MiAddressToPte(MmNonPagedPoolExpansionStart) - - PointerPte; - MmNumberOfSystemPtes--; + PointerPte = MiAddressToPte(MmSystemPteSpaceStart); DPRINT("Final System PTE count: %lu (%lu bytes)\n", MmNumberOfSystemPtes, MmNumberOfSystemPtes * PAGE_SIZE); diff --git a/ntoskrnl/mm/ARM3/miarm.h b/ntoskrnl/mm/ARM3/miarm.h index dc0ae98abec..50f9201f409 100644 --- a/ntoskrnl/mm/ARM3/miarm.h +++ b/ntoskrnl/mm/ARM3/miarm.h @@ -554,6 +554,7 @@ extern SIZE_T MmMaximumNonPagedPoolInBytes; extern PFN_NUMBER MmMaximumNonPagedPoolInPages; extern PFN_NUMBER MmSizeOfPagedPoolInPages; extern PVOID MmNonPagedSystemStart; +extern PVOID MmSystemPteSpaceStart; extern PVOID MmNonPagedPoolStart; extern PVOID MmNonPagedPoolExpansionStart; extern PVOID MmNonPagedPoolEnd; diff --git a/ntoskrnl/mm/ARM3/mminit.c b/ntoskrnl/mm/ARM3/mminit.c index c35669cbb43..3aedc6ff929 100644 --- a/ntoskrnl/mm/ARM3/mminit.c +++ b/ntoskrnl/mm/ARM3/mminit.c @@ -94,6 +94,7 @@ ULONG MmMaxAdditionNonPagedPoolPerMb = 400 * 1024; // https://web.archive.org/web/20130412053421/http://www.ditii.com/2007/09/28/windows-memory-management-x86-virtual-address-space/ // PVOID MmNonPagedSystemStart; +PVOID MmSystemPteSpaceStart; PVOID MmNonPagedPoolStart; PVOID MmNonPagedPoolExpansionStart; PVOID MmNonPagedPoolEnd = MI_NONPAGED_POOL_END; @@ -1787,7 +1788,7 @@ MiBuildPagedPool(VOID) // By default, it should be twice as big as nonpaged pool. // MmSizeOfPagedPoolInBytes = 2 * MmMaximumNonPagedPoolInBytes; - if (MmSizeOfPagedPoolInBytes > ((ULONG_PTR)MmNonPagedSystemStart - + if (MmSizeOfPagedPoolInBytes > ((ULONG_PTR)MmNonPagedPoolExpansionStart - (ULONG_PTR)MmPagedPoolStart)) { // @@ -1795,7 +1796,7 @@ MiBuildPagedPool(VOID) // for paged pool doesn't overflow into nonpaged pool VA. Otherwise, set // whatever maximum is possible. // - MmSizeOfPagedPoolInBytes = (ULONG_PTR)MmNonPagedSystemStart - + MmSizeOfPagedPoolInBytes = (ULONG_PTR)MmNonPagedPoolExpansionStart - (ULONG_PTR)MmPagedPoolStart; } #endif // _M_IX86 @@ -1812,6 +1813,17 @@ MiBuildPagedPool(VOID) // NumberOfPdes = (NumberOfPages + (PTE_PER_PAGE - 1)) / PTE_PER_PAGE; +#ifdef _M_IX86 + SIZE_T MaxNumberOfPdes; + SIZE_T MaxBytes; + + MaxBytes = (ULONG_PTR)MmNonPagedPoolExpansionStart - (ULONG_PTR)MmPagedPoolStart; + MaxNumberOfPdes = MaxBytes / PDE_MAPPED_VA; + + ASSERT(MaxNumberOfPdes != 0); + NumberOfPdes = min(NumberOfPdes, MaxNumberOfPdes); +#endif // _M_IX86 + // // Recompute the PDE-aligned size of the paged pool, in bytes and pages. // @@ -1820,10 +1832,10 @@ MiBuildPagedPool(VOID) #ifdef _M_IX86 // - // Let's be really sure this doesn't overflow into nonpaged system VA + // Let's be really sure this doesn't overflow into nonpaged pool expansion VA // ASSERT((MmSizeOfPagedPoolInBytes + (ULONG_PTR)MmPagedPoolStart) <= - (ULONG_PTR)MmNonPagedSystemStart); + (ULONG_PTR)MmNonPagedPoolExpansionStart); #endif // _M_IX86 // @@ -2294,6 +2306,12 @@ MmArmInitSystem(IN ULONG Phase, /* Initialize the platform-specific parts */ MiInitMachineDependent(LoaderBlock); + // + // x86 uses the loader-gap region + // + if (!MmSystemPteSpaceStart) + MmSystemPteSpaceStart = MmNonPagedSystemStart; + #if DBG /* Prototype PTEs are assumed to be in paged pool, so check if the math works */ PointerPte = (PMMPTE)MmPagedPoolStart; diff --git a/ntoskrnl/mm/mminit.c b/ntoskrnl/mm/mminit.c index 009383992b7..2bd34c58f15 100644 --- a/ntoskrnl/mm/mminit.c +++ b/ntoskrnl/mm/mminit.c @@ -91,7 +91,7 @@ MiInitSystemMemoryAreas(VOID) MiCreateArm3StaticMemoryArea(MmNonPagedPoolStart, MmSizeOfNonPagedPoolInBytes, FALSE); // System PTE space - MiCreateArm3StaticMemoryArea(MmNonPagedSystemStart, (MmNumberOfSystemPtes + 1) * PAGE_SIZE, FALSE); + MiCreateArm3StaticMemoryArea(MmSystemPteSpaceStart, (MmNumberOfSystemPtes + 1) * PAGE_SIZE, FALSE); // Nonpaged pool expansion space MiCreateArm3StaticMemoryArea(MmNonPagedPoolExpansionStart, (ULONG_PTR)MmNonPagedPoolEnd - (ULONG_PTR)MmNonPagedPoolExpansionStart, FALSE); @@ -136,6 +136,13 @@ MiDbgDumpAddressSpace(VOID) KSEG0_BASE, (ULONG_PTR)KSEG0_BASE + MmBootImageSize, "Boot Loaded Image"); +#ifdef _M_IX86 + DPRINT1(" 0x%p - 0x%p\t%s\n", + MmSystemPteSpaceStart, + (PVOID)((ULONG_PTR)MmSystemPteSpaceStart + + (MmNumberOfSystemPtes + 1) * PAGE_SIZE), + "System PTE Space"); +#endif DPRINT1(" 0x%p - 0x%p\t%s\n", MmPfnDatabase, (ULONG_PTR)MmPfnDatabase + (MxPfnAllocation << PAGE_SHIFT), @@ -168,9 +175,13 @@ MiDbgDumpAddressSpace(VOID) MmPagedPoolStart, (ULONG_PTR)MmPagedPoolStart + MmSizeOfPagedPoolInBytes, "ARM3 Paged Pool"); +#ifndef _M_IX86 DPRINT1(" 0x%p - 0x%p\t%s\n", - MmNonPagedSystemStart, MmNonPagedPoolExpansionStart, + MmSystemPteSpaceStart, + (PVOID)((ULONG_PTR)MmSystemPteSpaceStart + + ((MmNumberOfSystemPtes + 1) * PAGE_SIZE)), "System PTE Space"); +#endif DPRINT1(" 0x%p - 0x%p\t%s\n", MmNonPagedPoolExpansionStart, MmNonPagedPoolEnd, "Non Paged Pool Expansion PTE Space");