From f205f243bb7572f13caf9ed343bf33825a2ace18 Mon Sep 17 00:00:00 2001 From: Sir Richard Date: Mon, 8 Nov 2010 12:35:50 +0000 Subject: [PATCH] [NTOS]: Assign a working set to the system process and correctly initialize its address space. [NTOS]: Assign the working set list address, system-wide, but per-process (in hyperspace). [NTOS]: Give every process its working set page, and store it. Build a bogus working set list (MMWSL). [NTOS]: Use the process working set list (MMWSL) to track page table references during faults, just as Windows does. [NTOS]: Correctly initialize the colored page list heads and assert their validity. svn path=/trunk/; revision=49525 --- reactos/ntoskrnl/include/internal/i386/mm.h | 6 +- reactos/ntoskrnl/mm/ARM3/i386/init.c | 66 +++++++++++++- reactos/ntoskrnl/mm/ARM3/miarm.h | 1 + reactos/ntoskrnl/mm/ARM3/pagfault.c | 8 ++ reactos/ntoskrnl/mm/ARM3/procsup.c | 95 ++++++++++++++++++++- 5 files changed, 172 insertions(+), 4 deletions(-) diff --git a/reactos/ntoskrnl/include/internal/i386/mm.h b/reactos/ntoskrnl/include/internal/i386/mm.h index 1014884b099..f02cc787a71 100644 --- a/reactos/ntoskrnl/include/internal/i386/mm.h +++ b/reactos/ntoskrnl/include/internal/i386/mm.h @@ -84,7 +84,11 @@ PULONG MmGetPageDirectory(VOID); #define MI_MAPPING_RANGE_START (ULONG)HYPER_SPACE #define MI_MAPPING_RANGE_END (MI_MAPPING_RANGE_START + \ MI_HYPERSPACE_PTES * PAGE_SIZE) -#define MI_ZERO_PTE (PMMPTE)(MI_MAPPING_RANGE_END + \ +#define MI_DUMMY_PTE (PMMPTE)(MI_MAPPING_RANGE_END + \ + PAGE_SIZE) +#define MI_VAD_BITMAP (PMMPTE)(MI_DUMMY_PTE + \ + PAGE_SIZE) +#define MI_WORKING_SET_LIST (PMMPTE)(MI_VAD_BITMAP + \ PAGE_SIZE) /* On x86, these two are the same */ diff --git a/reactos/ntoskrnl/mm/ARM3/i386/init.c b/reactos/ntoskrnl/mm/ARM3/i386/init.c index 5ac50e77a6e..70707a6e98a 100644 --- a/reactos/ntoskrnl/mm/ARM3/i386/init.c +++ b/reactos/ntoskrnl/mm/ARM3/i386/init.c @@ -160,7 +160,9 @@ MiInitMachineDependent(IN PLOADER_PARAMETER_BLOCK LoaderBlock) MMPTE TempPde, TempPte; PVOID NonPagedPoolExpansionVa; KIRQL OldIrql; - + PMMPFN Pfn1; + ULONG Flags; + /* Check for kernel stack size that's too big */ if (MmLargeStackSize > (KERNEL_LARGE_STACK_SIZE / _1KB)) { @@ -558,6 +560,9 @@ MiInitMachineDependent(IN PLOADER_PARAMETER_BLOCK LoaderBlock) MmFirstReservedMappingPte = MiAddressToPte(MI_MAPPING_RANGE_START); MmLastReservedMappingPte = MiAddressToPte(MI_MAPPING_RANGE_END); MmFirstReservedMappingPte->u.Hard.PageFrameNumber = MI_HYPERSPACE_PTES; + + /* Set the working set address */ + MmWorkingSetList = (PVOID)MI_WORKING_SET_LIST; // // Reserve system PTEs for zeroing PTEs and clear them @@ -571,6 +576,28 @@ MiInitMachineDependent(IN PLOADER_PARAMETER_BLOCK LoaderBlock) // MiFirstReservedZeroingPte->u.Hard.PageFrameNumber = MI_ZERO_PTES - 1; + /* Lock PFN database */ + OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); + + /* Reset the ref/share count so that MmInitializeProcessAddressSpace works */ + Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(MiAddressToPde(PDE_BASE))); + Pfn1->u2.ShareCount = 0; + Pfn1->u3.e2.ReferenceCount = 0; + + /* Get a page for the working set list */ + MI_SET_USAGE(MI_USAGE_PAGE_TABLE); + MI_SET_PROCESS2("Kernel WS List"); + PageFrameIndex = MiRemoveAnyPage(0); + TempPte.u.Hard.PageFrameNumber = PageFrameIndex; + + /* Map the working set list */ + PointerPte = MiAddressToPte(MmWorkingSetList); + MI_WRITE_VALID_PTE(PointerPte, TempPte); + + /* Zero it out, and save the frame index */ + RtlZeroMemory(MiPteToAddress(PointerPte), PAGE_SIZE); + PsGetCurrentProcess()->WorkingSetPage = PageFrameIndex; + /* Check for Pentium LOCK errata */ if (KiI386PentiumLockErrataPresent) { @@ -581,6 +608,43 @@ MiInitMachineDependent(IN PLOADER_PARAMETER_BLOCK LoaderBlock) PointerPte->u.Hard.WriteThrough = 1; } + /* Release the lock */ + KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); + + /* Initialize the bogus address space */ + Flags = 0; + MmInitializeProcessAddressSpace(PsGetCurrentProcess(), NULL, NULL, &Flags, NULL); + + /* Make sure the color lists are valid */ + ASSERT(MmFreePagesByColor[0] < (PMMCOLOR_TABLES)PTE_BASE); + StartPde = MiAddressToPde(MmFreePagesByColor[0]); + ASSERT(StartPde->u.Hard.Valid == 1); + PointerPte = MiAddressToPte(MmFreePagesByColor[0]); + ASSERT(PointerPte->u.Hard.Valid == 1); + LastPte = MiAddressToPte((ULONG_PTR)&MmFreePagesByColor[1][MmSecondaryColors] - 1); + ASSERT(LastPte->u.Hard.Valid == 1); + + /* Loop the color list PTEs */ + while (PointerPte <= LastPte) + { + /* Get the PFN entry */ + Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(PointerPte)); + if (!Pfn1->u3.e2.ReferenceCount) + { + /* Fill it out */ + Pfn1->u4.PteFrame = PFN_FROM_PTE(StartPde); + Pfn1->PteAddress = PointerPte; + Pfn1->u2.ShareCount++; + Pfn1->u3.e2.ReferenceCount = 1; + Pfn1->u3.e1.PageLocation = ActiveAndValid; + Pfn1->u3.e1.CacheAttribute = MiCached; + } + + /* Keep going */ + PointerPte++; + } + + /* All done */ return STATUS_SUCCESS; } diff --git a/reactos/ntoskrnl/mm/ARM3/miarm.h b/reactos/ntoskrnl/mm/ARM3/miarm.h index 8e835503dac..22239ce40d8 100644 --- a/reactos/ntoskrnl/mm/ARM3/miarm.h +++ b/reactos/ntoskrnl/mm/ARM3/miarm.h @@ -513,6 +513,7 @@ extern BOOLEAN MmZeroingPageThreadActive; extern KEVENT MmZeroingPageEvent; extern ULONG MmSystemPageColor; extern ULONG MmProcessColorSeed; +extern PMMWSL MmWorkingSetList; // // Figures out the hardware bits for a PTE diff --git a/reactos/ntoskrnl/mm/ARM3/pagfault.c b/reactos/ntoskrnl/mm/ARM3/pagfault.c index 5cffe94cbd3..41cce6dcfc7 100644 --- a/reactos/ntoskrnl/mm/ARM3/pagfault.c +++ b/reactos/ntoskrnl/mm/ARM3/pagfault.c @@ -991,6 +991,14 @@ MmArmAccessFault(IN BOOLEAN StoreInstruction, MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread); return Status; } + + /* Is this a user address? */ + if (Address <= MM_HIGHEST_USER_ADDRESS) + { + /* Add an additional page table reference */ + MmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)]++; + ASSERT(MmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)] <= PTE_COUNT); + } /* Did we get a prototype PTE back? */ if (!ProtoPte) diff --git a/reactos/ntoskrnl/mm/ARM3/procsup.c b/reactos/ntoskrnl/mm/ARM3/procsup.c index fca03ee02e7..aec5af6e413 100644 --- a/reactos/ntoskrnl/mm/ARM3/procsup.c +++ b/reactos/ntoskrnl/mm/ARM3/procsup.c @@ -19,6 +19,7 @@ /* GLOBALS ********************************************************************/ ULONG MmProcessColorSeed = 0x12345678; +PMMWSL MmWorkingSetList; /* PRIVATE FUNCTIONS **********************************************************/ @@ -892,6 +893,32 @@ MmCreateTeb(IN PEPROCESS Process, return Status; } +VOID +NTAPI +MiInitializeWorkingSetList(IN PEPROCESS CurrentProcess) +{ + PMMPFN Pfn1; + + /* Setup some bogus list data */ + MmWorkingSetList->LastEntry = CurrentProcess->Vm.MinimumWorkingSetSize; + MmWorkingSetList->HashTable = NULL; + MmWorkingSetList->HashTableSize = 0; + MmWorkingSetList->NumberOfImageWaiters = 0; + MmWorkingSetList->Wsle = (PVOID)0xDEADBABE; + MmWorkingSetList->VadBitMapHint = 1; + MmWorkingSetList->HashTableStart = (PVOID)0xBADAB00B; + MmWorkingSetList->HighestPermittedHashAddress = (PVOID)0xCAFEBABE; + MmWorkingSetList->FirstFree = 1; + MmWorkingSetList->FirstDynamic = 2; + MmWorkingSetList->NextSlot = 3; + MmWorkingSetList->LastInitializedWsle = 4; + + /* The rule is that the owner process is always in the FLINK of the PDE's PFN entry */ + Pfn1 = MiGetPfnEntry(MiAddressToPte(PDE_BASE)->u.Hard.PageFrameNumber); + ASSERT(Pfn1->u4.PteFrame == MiGetPfnEntryIndex(Pfn1)); + Pfn1->u1.Event = (PKEVENT)CurrentProcess; +} + NTSTATUS NTAPI MmInitializeProcessAddressSpace(IN PEPROCESS Process, @@ -912,6 +939,7 @@ MmInitializeProcessAddressSpace(IN PEPROCESS Process, PWCHAR Source; PCHAR Destination; USHORT Length = 0; + MMPTE TempPte; /* We should have a PDE */ ASSERT(Process->Pcb.DirectoryTableBase[0] != 0); @@ -944,6 +972,22 @@ MmInitializeProcessAddressSpace(IN PEPROCESS Process, PointerPde = MiAddressToPde(HYPER_SPACE); PageFrameNumber = PFN_FROM_PTE(PointerPde); MiInitializePfn(PageFrameNumber, PointerPde, TRUE); + + /* Setup the PFN for the PTE for the working set */ + PointerPte = MiAddressToPte(MI_WORKING_SET_LIST); + MI_MAKE_HARDWARE_PTE(&TempPte, PointerPte, MM_READWRITE, 0); + ASSERT(PointerPte->u.Long != 0); + PageFrameNumber = PFN_FROM_PTE(PointerPte); + MI_WRITE_INVALID_PTE(PointerPte, DemandZeroPte); + MiInitializePfn(PageFrameNumber, PointerPte, TRUE); + TempPte.u.Hard.PageFrameNumber = PageFrameNumber; + MI_WRITE_VALID_PTE(PointerPte, TempPte); + + /* Now initialize the working set list */ + MiInitializeWorkingSetList(Process); + + /* Sanity check */ + ASSERT(Process->PhysicalVadRoot == NULL); /* Release PFN lock */ KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); @@ -1062,12 +1106,13 @@ MmCreateProcessAddressSpace(IN ULONG MinWs, OUT PULONG_PTR DirectoryTableBase) { KIRQL OldIrql; - PFN_NUMBER PdeIndex, HyperIndex; + PFN_NUMBER PdeIndex, HyperIndex, WsListIndex; PMMPTE PointerPte; MMPTE TempPte, PdePte; ULONG PdeOffset; - PMMPTE SystemTable; + PMMPTE SystemTable, HyperTable; ULONG Color; + PMMPFN Pfn1; /* Choose a process color */ Process->NextPageColor = RtlRandom(&MmProcessColorSeed); @@ -1105,6 +1150,21 @@ MmCreateProcessAddressSpace(IN ULONG MinWs, /* Zero it outside the PFN lock */ KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); MiZeroPhysicalPage(HyperIndex); + OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); + } + + /* Get a zero page for the woring set list, if possible */ + MI_SET_USAGE(MI_USAGE_PAGE_TABLE); + Color = MI_GET_NEXT_PROCESS_COLOR(Process); + WsListIndex = MiRemoveZeroPageSafe(Color); + if (!WsListIndex) + { + /* No zero pages, grab a free one */ + WsListIndex = MiRemoveAnyPage(Color); + + /* Zero it outside the PFN lock */ + KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); + MiZeroPhysicalPage(WsListIndex); } else { @@ -1117,11 +1177,42 @@ MmCreateProcessAddressSpace(IN ULONG MinWs, Process->AddressSpaceInitialized = 1; /* Set the base directory pointers */ + Process->WorkingSetPage = WsListIndex; DirectoryTableBase[0] = PdeIndex << PAGE_SHIFT; DirectoryTableBase[1] = HyperIndex << PAGE_SHIFT; /* Make sure we don't already have a page directory setup */ ASSERT(Process->Pcb.DirectoryTableBase[0] == 0); + + /* Get a PTE to map hyperspace */ + PointerPte = MiReserveSystemPtes(1, SystemPteSpace); + ASSERT(PointerPte != NULL); + + /* Build it */ + MI_MAKE_HARDWARE_PTE_KERNEL(&PdePte, + PointerPte, + MM_READWRITE, + HyperIndex); + + /* Set it dirty and map it */ + PdePte.u.Hard.Dirty = TRUE; + MI_WRITE_VALID_PTE(PointerPte, PdePte); + + /* Now get hyperspace's page table */ + HyperTable = MiPteToAddress(PointerPte); + + /* Now write the PTE/PDE entry for the working set list index itself */ + TempPte = ValidKernelPte; + TempPte.u.Hard.PageFrameNumber = WsListIndex; + PdeOffset = MiAddressToPteOffset(MmWorkingSetList); + HyperTable[PdeOffset] = TempPte; + + /* Let go of the system PTE */ + MiReleaseSystemPtes(PointerPte, 1, SystemPteSpace); + + /* Save the PTE address of the page directory itself */ + Pfn1 = MiGetPfnEntry(PdeIndex); + Pfn1->PteAddress = (PMMPTE)PDE_BASE; /* Insert us into the Mm process list */ InsertTailList(&MmProcessList, &Process->MmProcessLinks);