https://git.reactos.org/?p=reactos.git;a=commitdiff;h=43378411fb258a02d3384a...
commit 43378411fb258a02d3384acb3e301eb1774a119a Author: Jérôme Gardou jerome.gardou@reactos.org AuthorDate: Mon Mar 22 09:52:11 2021 +0100 Commit: Jérôme Gardou zefklop@users.noreply.github.com CommitDate: Thu Apr 8 15:40:37 2021 +0200
[NTOS:MM] Rewrite arch-specifics of the legacy Mm
Properly handle PDE refcounting Clean-up of the internal API Enforce attaching to the process when modifying its memory layout, instead of making circonvoluted mappings which always end up being broken. --- ntoskrnl/include/internal/mm.h | 48 +-- ntoskrnl/mm/i386/page.c | 843 +++++++++++++++++++---------------------- ntoskrnl/mm/marea.c | 18 - ntoskrnl/mm/rmap.c | 30 +- ntoskrnl/mm/section.c | 17 +- 5 files changed, 429 insertions(+), 527 deletions(-)
diff --git a/ntoskrnl/include/internal/mm.h b/ntoskrnl/include/internal/mm.h index 93c44e5bd05..d2cb0123bed 100644 --- a/ntoskrnl/include/internal/mm.h +++ b/ntoskrnl/include/internal/mm.h @@ -1034,8 +1034,7 @@ MmCreateVirtualMapping( struct _EPROCESS* Process, PVOID Address, ULONG flProtect, - PPFN_NUMBER Pages, - ULONG PageCount + PFN_NUMBER Page );
NTSTATUS @@ -1044,8 +1043,7 @@ MmCreateVirtualMappingUnsafe( struct _EPROCESS* Process, PVOID Address, ULONG flProtect, - PPFN_NUMBER Pages, - ULONG PageCount + PFN_NUMBER Page );
ULONG @@ -1080,13 +1078,6 @@ VOID NTAPI MmInitGlobalKernelPageDirectory(VOID);
-VOID -NTAPI -MmGetPageFileMapping( - struct _EPROCESS *Process, - PVOID Address, - SWAPENTRY* SwapEntry); - VOID NTAPI MmDeletePageFileMapping( @@ -1103,16 +1094,16 @@ MmCreatePageFileMapping( SWAPENTRY SwapEntry );
-BOOLEAN +VOID NTAPI -MmIsPageSwapEntry( - struct _EPROCESS *Process, - PVOID Address -); +MmGetPageFileMapping( + PEPROCESS Process, + PVOID Address, + SWAPENTRY *SwapEntry);
-VOID +BOOLEAN NTAPI -MmSetDirtyPage( +MmIsPageSwapEntry( struct _EPROCESS *Process, PVOID Address ); @@ -1156,6 +1147,12 @@ MmSetCleanPage( PVOID Address );
+VOID +NTAPI +MmSetDirtyBit(PEPROCESS Process, PVOID Address, BOOLEAN Bit); +#define MmSetCleanPage(__P, __A) MmSetDirtyBit(__P, __A, FALSE) +#define MmSetDirtyPage(__P, __A) MmSetDirtyBit(__P, __A, TRUE) + VOID NTAPI MmDeletePageTable( @@ -1208,21 +1205,6 @@ MmDeleteVirtualMapping( PPFN_NUMBER Page );
-BOOLEAN -NTAPI -MmIsDirtyPage( - struct _EPROCESS *Process, - PVOID Address -); - -VOID -NTAPI -MmClearPageAccessedBit(PEPROCESS Process, PVOID Address); - -BOOLEAN -NTAPI -MmIsPageAccessed(PEPROCESS Process, PVOID Address); - /* wset.c ********************************************************************/
NTSTATUS diff --git a/ntoskrnl/mm/i386/page.c b/ntoskrnl/mm/i386/page.c index e1aac494dcb..35b0921d0ae 100644 --- a/ntoskrnl/mm/i386/page.c +++ b/ntoskrnl/mm/i386/page.c @@ -134,17 +134,6 @@ ULONG MmProtectToValue[32] =
/* FUNCTIONS ***************************************************************/
-static BOOLEAN MmUnmapPageTable(PULONG Pt, KIRQL OldIrql); - -VOID -MiFlushTlb(PULONG Pt, PVOID Address, KIRQL OldIrql) -{ - if ((Pt && MmUnmapPageTable(Pt, OldIrql)) || Address >= MmSystemRangeStart) - { - KeInvalidateTlbEntry(Address); - } -} - static ULONG ProtectToPTE(ULONG flProtect) { @@ -191,120 +180,38 @@ NTAPI MiFillSystemPageDirectory(IN PVOID Base, IN SIZE_T NumberOfBytes);
-static PULONG -MmGetPageTableForProcess(PEPROCESS Process, PVOID Address, BOOLEAN Create, PKIRQL OldIrql) +PFN_NUMBER +NTAPI +MmGetPfnForProcess(PEPROCESS Process, + PVOID Address) { - PFN_NUMBER Pfn; - PULONG Pt; - PMMPDE PointerPde; - - if (Address < MmSystemRangeStart) - { - /* We should have a process for user land addresses */ - ASSERT(Process != NULL); + PMMPTE PointerPte; + PFN_NUMBER Page;
- if(Process != PsGetCurrentProcess()) - { - PMMPDE PdeBase; - ULONG PdeOffset = MiGetPdeOffset(Address); - - ASSERT(!Create); - - PdeBase = MiMapPageInHyperSpace(PsGetCurrentProcess(), - PTE_TO_PFN(Process->Pcb.DirectoryTableBase[0]), - OldIrql); - if (PdeBase == NULL) - { - KeBugCheck(MEMORY_MANAGEMENT); - } - PointerPde = PdeBase + PdeOffset; - if (PointerPde->u.Hard.Valid == 0) - { - MiUnmapPageInHyperSpace(PsGetCurrentProcess(), PdeBase, *OldIrql); - return NULL; - } - - Pfn = PointerPde->u.Hard.PageFrameNumber; - MiUnmapPageInHyperSpace(PsGetCurrentProcess(), PdeBase, *OldIrql); - Pt = MiMapPageInHyperSpace(PsGetCurrentProcess(), Pfn, OldIrql); - if (Pt == NULL) - { - KeBugCheck(MEMORY_MANAGEMENT); - } - return Pt + MiAddressToPteOffset(Address); - } + /* Must be called for user mode only */ + ASSERT(Process != NULL); + ASSERT(Address < MmSystemRangeStart);
- /* This is for our process */ - PointerPde = MiAddressToPde(Address); - Pt = (PULONG)MiAddressToPte(Address); + /* And for our process */ + ASSERT(Process == PsGetCurrentProcess());
- if ((PointerPde->u.Hard.Valid == 0) && (Create == FALSE)) - { - /* Do not fault PDE in if not needed */ - return NULL; - } + /* Lock for reading */ + MiLockProcessWorkingSetShared(Process, PsGetCurrentThread());
- return (PULONG)MiAddressToPte(Address); - } - - /* This is for kernel land address */ - ASSERT(Process == NULL); - PointerPde = MiAddressToPde(Address); - Pt = (PULONG)MiAddressToPte(Address); - if (PointerPde->u.Hard.Valid == 0) + if (MiQueryPageTableReferences(Address) == 0) { - /* Let ARM3 synchronize the PDE */ - if(!MiSynchronizeSystemPde(PointerPde)) - { - /* PDE (still) not valid, let ARM3 allocate one if asked */ - if(Create == FALSE) - return NULL; - MiFillSystemPageDirectory(Address, PAGE_SIZE); - } - } - return Pt; -} - -static BOOLEAN MmUnmapPageTable(PULONG Pt, KIRQL OldIrql) -{ - if (!IS_HYPERSPACE(Pt)) - { - return TRUE; + MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread()); + return 0; }
- MiUnmapPageInHyperSpace(PsGetCurrentProcess(), Pt, OldIrql); - - return FALSE; -} - -static ULONG MmGetPageEntryForProcess(PEPROCESS Process, PVOID Address) -{ - ULONG Pte; - PULONG Pt; - KIRQL OldIrql; + /* Make sure we can read the PTE */ + MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL);
- Pt = MmGetPageTableForProcess(Process, Address, FALSE, &OldIrql); - if (Pt) - { - Pte = *Pt; - MmUnmapPageTable(Pt, OldIrql); - return Pte; - } - return 0; -} + PointerPte = MiAddressToPte(Address); + Page = PointerPte->u.Hard.Valid ? PFN_FROM_PTE(PointerPte) : 0;
-PFN_NUMBER -NTAPI -MmGetPfnForProcess(PEPROCESS Process, - PVOID Address) -{ - ULONG Entry; - Entry = MmGetPageEntryForProcess(Process, Address); - if (!(Entry & PA_PRESENT)) - { - return 0; - } - return(PTE_TO_PFN(Entry)); + MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread()); + return Page; }
VOID @@ -315,130 +222,150 @@ MmDeleteVirtualMapping(PEPROCESS Process, PVOID Address, * FUNCTION: Delete a virtual mapping */ { - BOOLEAN WasValid = FALSE; - PFN_NUMBER Pfn; + PMMPTE PointerPte; ULONG Pte; - PULONG Pt; - KIRQL OldIrql;
- DPRINT("MmDeleteVirtualMapping(%p, %p, %p, %p)\n", - Process, Address, WasDirty, Page); + DPRINT("MmDeleteVirtualMapping(%p, %p, %p, %p)\n", Process, Address, WasDirty, Page); + + ASSERT(((ULONG_PTR)Address % PAGE_SIZE) == 0);
- Pt = MmGetPageTableForProcess(Process, Address, FALSE, &OldIrql); + /* And we should be at low IRQL */ + ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
- if (Pt == NULL) + /* Make sure our PDE is valid, and that everything is going fine */ + if (Process == NULL) { - if (WasDirty != NULL) + if (Address < MmSystemRangeStart) { - *WasDirty = FALSE; + DPRINT1("NULL process given for user-mode mapping at %p\n", Address); + KeBugCheck(MEMORY_MANAGEMENT); } - if (Page != NULL) + + if (!MiSynchronizeSystemPde(MiAddressToPde(Address))) { - *Page = 0; + /* There can't be a page if there is no PDE */ + if (WasDirty) + *WasDirty = FALSE; + if (Page) + *Page = 0; + return; } - return; } - - /* - * Atomically set the entry to zero and get the old value. - */ - Pte = InterlockedExchangePte(Pt, 0); - - /* We count a mapping as valid if it's a present page, or it's a nonzero pfn with - * the swap bit unset, indicating a valid page protected to PAGE_NOACCESS. */ - WasValid = (Pte & PA_PRESENT) || ((Pte >> PAGE_SHIFT) && !(Pte & 0x800)); - if (WasValid) + else { - /* Flush the TLB since we transitioned this PTE - * from valid to invalid so any stale translations - * are removed from the cache */ - MiFlushTlb(Pt, Address, OldIrql); + if ((Address >= MmSystemRangeStart) || Add2Ptr(Address, PAGE_SIZE) >= MmSystemRangeStart) + { + DPRINT1("Process %p given for kernel-mode mapping at %p -- %lu pages starting at %Ix\n", Process, Address); + KeBugCheck(MEMORY_MANAGEMENT); + }
- if (Address < MmSystemRangeStart) - { - /* Remove PDE reference */ - Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)]--; - ASSERT(Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)] < PTE_PER_PAGE); - } + /* Only for current process !!! */ + ASSERT(Process = PsGetCurrentProcess()); + MiLockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
- Pfn = PTE_TO_PFN(Pte); - } - else - { - MmUnmapPageTable(Pt, OldIrql); - Pfn = 0; + /* No PDE --> No page */ + if (MiQueryPageTableReferences(Address) == 0) + { + MiUnlockProcessWorkingSetUnsafe(Process, PsGetCurrentThread()); + if (WasDirty) + *WasDirty = 0; + if (Page) + *Page = 0; + return; + } + + MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL); }
- /* - * Return some information to the caller - */ - if (WasDirty != NULL) + PointerPte = MiAddressToPte(Address); + Pte = InterlockedExchangePte(PointerPte, 0); + + if (Pte == 0) { - *WasDirty = ((Pte & PA_DIRTY) && (Pte & PA_PRESENT)) ? TRUE : FALSE; + /* There was nothing here */ + if (Address < MmSystemRangeStart) + MiUnlockProcessWorkingSetUnsafe(Process, PsGetCurrentThread()); + if (WasDirty) + *WasDirty = 0; + if (Page) + *Page = 0; + return; } - if (Page != NULL) + + /* It must have been present, or not a swap entry */ + ASSERT(FlagOn(Pte, PA_PRESENT) || !FlagOn(Pte, 0x800)); + + if (FlagOn(Pte, PA_PRESENT)) + KeInvalidateTlbEntry(Address); + + if (Address < MmSystemRangeStart) { - *Page = Pfn; + /* Remove PDE reference */ + MiDecrementPageTableReferences(Address); + if (MiQueryPageTableReferences(Address) == 0) + { + KIRQL OldIrql = MiAcquirePfnLock(); + MiDeletePte(MiAddressToPte(PointerPte), PointerPte, Process, NULL); + MiReleasePfnLock(OldIrql); + } + + MiUnlockProcessWorkingSetUnsafe(Process, PsGetCurrentThread()); } -}
-VOID -NTAPI -MmGetPageFileMapping(PEPROCESS Process, PVOID Address, - SWAPENTRY* SwapEntry) -/* - * FUNCTION: Get a page file mapping - */ -{ - ULONG Entry = MmGetPageEntryForProcess(Process, Address); - *SwapEntry = Entry >> 1; + if (WasDirty) + *WasDirty = FlagOn(Pte, PA_DIRTY); + if (Page) + *Page = PTE_TO_PFN(Pte); }
+ VOID NTAPI -MmDeletePageFileMapping(PEPROCESS Process, PVOID Address, - SWAPENTRY* SwapEntry) -/* - * FUNCTION: Delete a virtual mapping - */ +MmDeletePageFileMapping( + PEPROCESS Process, + PVOID Address, + SWAPENTRY* SwapEntry) { + PMMPTE PointerPte; ULONG Pte; - PULONG Pt; - KIRQL OldIrql;
- Pt = MmGetPageTableForProcess(Process, Address, FALSE, &OldIrql); + /* This should not be called for kernel space anymore */ + ASSERT(Process != NULL); + ASSERT(Address < MmSystemRangeStart);
- if (Pt == NULL) - { - *SwapEntry = 0; - return; - } + /* And we don't support deleting for other process */ + ASSERT(Process == PsGetCurrentProcess());
- /* - * Atomically set the entry to zero and get the old value. - */ - Pte = InterlockedExchangePte(Pt, 0); + /* And we should be at low IRQL */ + ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
- if (Address < MmSystemRangeStart) - { - /* Remove PDE reference */ - Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)]--; - ASSERT(Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)] < PTE_PER_PAGE); - } + /* We are tinkering with the PDE here. Ensure it will be there */ + MiLockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
- /* We don't need to flush here because page file entries - * are invalid translations, so the processor won't cache them */ - MmUnmapPageTable(Pt, OldIrql); + /* Callers must ensure there is actually something there */ + ASSERT(MiAddressToPde(Address)->u.Long != 0);
- if ((Pte & PA_PRESENT) || !(Pte & 0x800)) + MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL); + + PointerPte = MiAddressToPte(Address); + Pte = InterlockedExchangePte(PointerPte, 0); + if (!FlagOn(Pte, 0x800) || FlagOn(Pte, PA_PRESENT)) { - DPRINT1("Pte %x (want not 1 and 0x800)\n", Pte); - KeBugCheck(MEMORY_MANAGEMENT); + KeBugCheckEx(MEMORY_MANAGEMENT, Pte, (ULONG_PTR)Process, (ULONG_PTR)Address, 0); }
- /* - * Return some information to the caller - */ + /* This used to be a non-zero PTE, now we can let the PDE go. */ + MiDecrementPageTableReferences(Address); + if (MiQueryPageTableReferences(Address) == 0) + { + /* We can let it go */ + KIRQL OldIrql = MiAcquirePfnLock(); + MiDeletePte(MiAddressToPte(PointerPte), PointerPte, Process, NULL); + MiReleasePfnLock(OldIrql); + } + + MiUnlockProcessWorkingSetUnsafe(Process, PsGetCurrentThread()); + *SwapEntry = Pte >> 1; }
@@ -459,149 +386,160 @@ Mmi386MakeKernelPageTableGlobal(PVOID Address)
BOOLEAN NTAPI -MmIsDirtyPage(PEPROCESS Process, PVOID Address) +MmIsPagePresent(PEPROCESS Process, PVOID Address) { - return MmGetPageEntryForProcess(Process, Address) & PA_DIRTY ? TRUE : FALSE; -} + BOOLEAN Ret;
-VOID -NTAPI -MmSetCleanPage(PEPROCESS Process, PVOID Address) -{ - PULONG Pt; - ULONG Pte; - KIRQL OldIrql; - - if (Address < MmSystemRangeStart && Process == NULL) + if (Address >= MmSystemRangeStart) { - DPRINT1("MmSetCleanPage is called for user space without a process.\n"); - KeBugCheck(MEMORY_MANAGEMENT); - } + ASSERT(Process == NULL); + if (!MiSynchronizeSystemPde(MiAddressToPde(Address))) + { + /* It can't be present if there is no PDE */ + return FALSE; + }
- Pt = MmGetPageTableForProcess(Process, Address, FALSE, &OldIrql); - if (Pt == NULL) - { - KeBugCheck(MEMORY_MANAGEMENT); + return MiAddressToPte(Address)->u.Hard.Valid; }
- do - { - Pte = *Pt; - } while (Pte != InterlockedCompareExchangePte(Pt, Pte & ~PA_DIRTY, Pte)); + ASSERT(Process != NULL); + ASSERT(Process == PsGetCurrentProcess());
- if (!(Pte & PA_PRESENT)) + MiLockProcessWorkingSetShared(Process, PsGetCurrentThread()); + + if (MiQueryPageTableReferences(Address) == 0) { - KeBugCheck(MEMORY_MANAGEMENT); + /* It can't be present if there is no PDE */ + MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread()); + return FALSE; } - else if (Pte & PA_DIRTY) + + MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL); + + Ret = MiAddressToPte(Address)->u.Hard.Valid; + + MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread()); + + return Ret; +} + +BOOLEAN +NTAPI +MmIsDisabledPage(PEPROCESS Process, PVOID Address) +{ + BOOLEAN Ret; + ULONG Pte; + + if (Address >= MmSystemRangeStart) { - MiFlushTlb(Pt, Address, OldIrql); + ASSERT(Process == NULL); + if (!MiSynchronizeSystemPde(MiAddressToPde(Address))) + { + /* It's not disabled if it's not present */ + return FALSE; + } + + Pte = MiAddressToPte(Address)->u.Long; } else { - MmUnmapPageTable(Pt, OldIrql); + ASSERT(Process != NULL); + ASSERT(Process == PsGetCurrentProcess()); + + MiLockProcessWorkingSetShared(Process, PsGetCurrentThread()); + + if (MiQueryPageTableReferences(Address) == 0) + { + /* It can't be disabled if there is no PDE */ + MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread()); + return FALSE; + } + + MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL); + + Pte = MiAddressToPte(Address)->u.Long; } + + Ret = !FlagOn(Pte, PA_PRESENT) && !FlagOn(Pte, 0x800) && (PAGE_MASK(Pte) != 0); + + if (Address < MmSystemRangeStart) + MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread()); + + return Ret; }
-VOID +BOOLEAN NTAPI -MmSetDirtyPage(PEPROCESS Process, PVOID Address) +MmIsPageSwapEntry(PEPROCESS Process, PVOID Address) { - PULONG Pt; + BOOLEAN Ret; ULONG Pte; - KIRQL OldIrql;
- if (Address < MmSystemRangeStart && Process == NULL) + /* We never set swap entries for kernel addresses */ + if (Address >= MmSystemRangeStart) { - DPRINT1("MmSetDirtyPage is called for user space without a process.\n"); - KeBugCheck(MEMORY_MANAGEMENT); + ASSERT(Process == NULL); + return FALSE; }
- Pt = MmGetPageTableForProcess(Process, Address, FALSE, &OldIrql); - if (Pt == NULL) - { - KeBugCheck(MEMORY_MANAGEMENT); - } + ASSERT(Process != NULL); + ASSERT(Process == PsGetCurrentProcess());
- do - { - Pte = *Pt; - } while (Pte != InterlockedCompareExchangePte(Pt, Pte | PA_DIRTY, Pte)); + MiLockProcessWorkingSetShared(Process, PsGetCurrentThread());
- if (!(Pte & PA_PRESENT)) + if (MiQueryPageTableReferences(Address) == 0) { - KeBugCheck(MEMORY_MANAGEMENT); - } - else - { - /* The processor will never clear this bit itself, therefore - * we do not need to flush the TLB here when setting it */ - MmUnmapPageTable(Pt, OldIrql); + /* There can't be a swap entry if there is no PDE */ + MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread()); + return FALSE; } + + MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL); + + Pte = MiAddressToPte(Address)->u.Long; + Ret = !FlagOn(Pte, PA_PRESENT) && FlagOn(Pte, 0x800); + + MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread()); + + return Ret; }
VOID NTAPI -MmClearPageAccessedBit(PEPROCESS Process, PVOID Address) +MmGetPageFileMapping(PEPROCESS Process, PVOID Address, SWAPENTRY* SwapEntry) { - PULONG Pt; - LONG Pte; - KIRQL OldIrql; + ULONG Pte;
- if (Address < MmSystemRangeStart && Process == NULL) + /* We never set swap entries for kernel addresses */ + if (Address >= MmSystemRangeStart) { - DPRINT1("MmClearPageAccessedBit is called for user space without a process.\n"); - KeBugCheck(MEMORY_MANAGEMENT); + ASSERT(Process == NULL); + *SwapEntry = 0; + return; }
- Pt = MmGetPageTableForProcess(Process, Address, FALSE, &OldIrql); - if (Pt == NULL) - { - KeBugCheck(MEMORY_MANAGEMENT); - } + ASSERT(Process != NULL); + ASSERT(Process == PsGetCurrentProcess());
- do - { - Pte = *Pt; - } while (Pte != InterlockedCompareExchangePte(Pt, Pte & ~PA_ACCESSED, Pte)); + MiLockProcessWorkingSetShared(Process, PsGetCurrentThread());
- if (!(Pte & PA_PRESENT)) + if (MiQueryPageTableReferences(Address) == 0) { - KeBugCheck(MEMORY_MANAGEMENT); + /* There can't be a swap entry if there is no PDE */ + MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread()); + *SwapEntry = 0; + return; }
- MiFlushTlb(Pt, Address, OldIrql); -} + MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL);
-BOOLEAN -NTAPI -MmIsPageAccessed(PEPROCESS Process, PVOID Address) -{ - return BooleanFlagOn(MmGetPageEntryForProcess(Process, Address), PA_ACCESSED); -} - -BOOLEAN -NTAPI -MmIsPagePresent(PEPROCESS Process, PVOID Address) -{ - return MmGetPageEntryForProcess(Process, Address) & PA_PRESENT; -} - -BOOLEAN -NTAPI -MmIsDisabledPage(PEPROCESS Process, PVOID Address) -{ - ULONG_PTR Entry = MmGetPageEntryForProcess(Process, Address); - return !(Entry & PA_PRESENT) && !(Entry & 0x800) && (Entry >> PAGE_SHIFT); -} + Pte = MiAddressToPte(Address)->u.Long; + if (!FlagOn(Pte, PA_PRESENT) && FlagOn(Pte, 0x800)) + *SwapEntry = Pte >> 1; + else + *SwapEntry = 0;
-BOOLEAN -NTAPI -MmIsPageSwapEntry(PEPROCESS Process, PVOID Address) -{ - ULONG Entry; - Entry = MmGetPageEntryForProcess(Process, Address); - return !(Entry & PA_PRESENT) && (Entry & 0x800); + MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread()); }
NTSTATUS @@ -610,56 +548,39 @@ MmCreatePageFileMapping(PEPROCESS Process, PVOID Address, SWAPENTRY SwapEntry) { - PULONG Pt; + PMMPTE PointerPte; ULONG Pte; - KIRQL OldIrql;
- if (Process == NULL && Address < MmSystemRangeStart) - { - DPRINT1("No process\n"); - KeBugCheck(MEMORY_MANAGEMENT); - } - if (Process != NULL && Address >= MmSystemRangeStart) - { - DPRINT1("Setting kernel address with process context\n"); - KeBugCheck(MEMORY_MANAGEMENT); - } + /* This should not be called for kernel space anymore */ + ASSERT(Process != NULL); + ASSERT(Address < MmSystemRangeStart); + + /* And we don't support creating for other process */ + ASSERT(Process == PsGetCurrentProcess());
if (SwapEntry & (1 << 31)) { KeBugCheck(MEMORY_MANAGEMENT); }
- Pt = MmGetPageTableForProcess(Process, Address, FALSE, &OldIrql); - if (Pt == NULL) - { - /* Nobody should page out an address that hasn't even been mapped */ - /* But we might place a wait entry first, requiring the page table */ - if (SwapEntry != MM_WAIT_ENTRY) - { - KeBugCheck(MEMORY_MANAGEMENT); - } - Pt = MmGetPageTableForProcess(Process, Address, TRUE, &OldIrql); - } - Pte = InterlockedExchangePte(Pt, SwapEntry << 1); + /* We are tinkering with the PDE here. Ensure it will be there */ + ASSERT(Process == PsGetCurrentProcess()); + MiLockProcessWorkingSetUnsafe(Process, PsGetCurrentThread()); + + MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL); + + PointerPte = MiAddressToPte(Address); + Pte = InterlockedExchangePte(PointerPte, SwapEntry << 1); if (Pte != 0) { KeBugCheckEx(MEMORY_MANAGEMENT, SwapEntry, (ULONG_PTR)Process, (ULONG_PTR)Address, 0); }
- if (Address < MmSystemRangeStart) - { - /* Add PDE reference */ - Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)]++; - ASSERT(Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)] <= PTE_PER_PAGE); - } + /* This used to be a 0 PTE, now we need a valid PDE to keep it around */ + MiIncrementPageTableReferences(Address); + MiUnlockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
- /* We don't need to flush the TLB here because it - * only caches valid translations and a zero PTE - * is not a valid translation */ - MmUnmapPageTable(Pt, OldIrql); - - return(STATUS_SUCCESS); + return STATUS_SUCCESS; }
@@ -668,50 +589,41 @@ NTAPI MmCreateVirtualMappingUnsafe(PEPROCESS Process, PVOID Address, ULONG flProtect, - PPFN_NUMBER Pages, - ULONG PageCount) + PFN_NUMBER Page) { ULONG Attributes; - PVOID Addr; - ULONG i; - ULONG oldPdeOffset, PdeOffset; - PULONG Pt = NULL; + PMMPTE PointerPte; ULONG Pte; - KIRQL OldIrql;
- DPRINT("MmCreateVirtualMappingUnsafe(%p, %p, %lu, %p (%x), %lu)\n", - Process, Address, flProtect, Pages, *Pages, PageCount); + DPRINT("MmCreateVirtualMappingUnsafe(%p, %p, %lu, %x)\n", + Process, Address, flProtect, Page);
ASSERT(((ULONG_PTR)Address % PAGE_SIZE) == 0);
+ /* Make sure our PDE is valid, and that everything is going fine */ if (Process == NULL) { if (Address < MmSystemRangeStart) { - DPRINT1("NULL process given for user-mode mapping at %p -- %lu pages starting at %Ix\n", Address, PageCount, *Pages); - KeBugCheck(MEMORY_MANAGEMENT); - } - if (PageCount > 0x10000 || - (ULONG_PTR) Address / PAGE_SIZE + PageCount > 0x100000) - { - DPRINT1("Page count too large for kernel-mode mapping at %p -- %lu pages starting at %Ix\n", Address, PageCount, *Pages); + DPRINT1("NULL process given for user-mode mapping at %p\n", Address); KeBugCheck(MEMORY_MANAGEMENT); } + if (!MiSynchronizeSystemPde(MiAddressToPde(Address))) + MiFillSystemPageDirectory(Address, PAGE_SIZE); } else { - if (Address >= MmSystemRangeStart) + if ((Address >= MmSystemRangeStart) || Add2Ptr(Address, PAGE_SIZE) >= MmSystemRangeStart) { - DPRINT1("Process %p given for kernel-mode mapping at %p -- %lu pages starting at %Ix\n", Process, Address, PageCount, *Pages); - KeBugCheck(MEMORY_MANAGEMENT); - } - if (PageCount > (ULONG_PTR)MmSystemRangeStart / PAGE_SIZE || - (ULONG_PTR) Address / PAGE_SIZE + PageCount > - (ULONG_PTR)MmSystemRangeStart / PAGE_SIZE) - { - DPRINT1("Page count too large for process %p user-mode mapping at %p -- %lu pages starting at %Ix\n", Process, Address, PageCount, *Pages); + DPRINT1("Process %p given for kernel-mode mapping at %p -- %lu pages starting at %Ix\n", Process, Address); KeBugCheck(MEMORY_MANAGEMENT); } + + /* Only for current process !!! */ + ASSERT(Process = PsGetCurrentProcess()); + MiLockProcessWorkingSetUnsafe(Process, PsGetCurrentThread()); + + MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL); }
Attributes = ProtectToPTE(flProtect); @@ -725,58 +637,28 @@ MmCreateVirtualMappingUnsafe(PEPROCESS Process, Attributes |= PA_USER; }
- Addr = Address; - /* MmGetPageTableForProcess should be called on the first run, so - * let this trigger it */ - oldPdeOffset = ADDR_TO_PDE_OFFSET(Addr) + 1; - for (i = 0; i < PageCount; i++, Addr = (PVOID)((ULONG_PTR)Addr + PAGE_SIZE)) - { - if (!(Attributes & PA_PRESENT) && Pages[i] != 0) - { - DPRINT1("Setting physical address but not allowing access at address " - "0x%p with attributes %x/%x.\n", - Addr, Attributes, flProtect); - KeBugCheck(MEMORY_MANAGEMENT); - } - PdeOffset = ADDR_TO_PDE_OFFSET(Addr); - if (oldPdeOffset != PdeOffset) - { - if(Pt) MmUnmapPageTable(Pt, OldIrql); - Pt = MmGetPageTableForProcess(Process, Addr, TRUE, &OldIrql); - if (Pt == NULL) - { - KeBugCheck(MEMORY_MANAGEMENT); - } - } - else - { - Pt++; - } - oldPdeOffset = PdeOffset; - - Pte = InterlockedExchangePte(Pt, PFN_TO_PTE(Pages[i]) | Attributes); + /* This must be for a valid address */ + ASSERT(FlagOn(Attributes, PA_PRESENT));
- /* There should not be anything valid here */ - if (Pte != 0) - { - DPRINT1("Bad PTE %lx at %p for %p + %lu\n", Pte, Pt, Address, i); - KeBugCheck(MEMORY_MANAGEMENT); - } + PointerPte = MiAddressToPte(Address); + Pte = InterlockedExchangePte(PointerPte, PFN_TO_PTE(Page) | Attributes); + /* There should not have been anything valid here */ + if (Pte != 0) + { + DPRINT1("Bad PTE %lx at %p for %p\n", Pte, PointerPte, Address); + KeBugCheck(MEMORY_MANAGEMENT); + }
- /* We don't need to flush the TLB here because it only caches valid translations - * and we're moving this PTE from invalid to valid so it can't be cached right now */ + /* We don't need to flush the TLB here because it only caches valid translations + * and we're moving this PTE from invalid to valid so it can't be cached right now */
- if (Addr < MmSystemRangeStart) - { - /* Add PDE reference */ - Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Addr)]++; - ASSERT(Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Addr)] <= PTE_PER_PAGE); - } + if (Address < MmSystemRangeStart) + { + /* Add PDE reference */ + MiIncrementPageTableReferences(Address); + MiUnlockProcessWorkingSetUnsafe(Process, PsGetCurrentThread()); }
- ASSERT(Addr > Address); - MmUnmapPageTable(Pt, OldIrql); - return(STATUS_SUCCESS); }
@@ -785,45 +667,60 @@ NTAPI MmCreateVirtualMapping(PEPROCESS Process, PVOID Address, ULONG flProtect, - PPFN_NUMBER Pages, - ULONG PageCount) + PFN_NUMBER Page) { - ULONG i; - ASSERT((ULONG_PTR)Address % PAGE_SIZE == 0); - for (i = 0; i < PageCount; i++) + if (!MmIsPageInUse(Page)) { - if (!MmIsPageInUse(Pages[i])) - { - DPRINT1("Page at address %x not in use\n", PFN_TO_PTE(Pages[i])); - KeBugCheck(MEMORY_MANAGEMENT); - } + DPRINT1("Page at address %x not in use\n", PFN_TO_PTE(Page)); + KeBugCheck(MEMORY_MANAGEMENT); }
- return(MmCreateVirtualMappingUnsafe(Process, - Address, - flProtect, - Pages, - PageCount)); + return MmCreateVirtualMappingUnsafe(Process, Address, flProtect, Page); }
ULONG NTAPI MmGetPageProtect(PEPROCESS Process, PVOID Address) { - ULONG Entry; + ULONG_PTR Pte; ULONG Protect;
- Entry = MmGetPageEntryForProcess(Process, Address); + if (Address >= MmSystemRangeStart) + { + ASSERT(Process == NULL); + + if (!MiSynchronizeSystemPde(MiAddressToPde(Address))) + return PAGE_NOACCESS; + } + else + { + ASSERT(Address < MmSystemRangeStart); + ASSERT(Process != NULL); + + ASSERT(Process == PsGetCurrentProcess());
+ MiLockProcessWorkingSetShared(Process, PsGetCurrentThread());
- if (!(Entry & PA_PRESENT)) + if (MiQueryPageTableReferences(Address) == 0) + { + /* It can't be present if there is no PDE */ + MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread()); + return PAGE_NOACCESS; + } + + MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL); + } + + Pte = MiAddressToPte(Address)->u.Long; + + if (!(Pte & PA_PRESENT)) { Protect = PAGE_NOACCESS; } else { - if (Entry & PA_READWRITE) + if (Pte & PA_READWRITE) { Protect = PAGE_READWRITE; } @@ -831,20 +728,23 @@ MmGetPageProtect(PEPROCESS Process, PVOID Address) { Protect = PAGE_EXECUTE_READ; } - if (Entry & PA_CD) + if (Pte & PA_CD) { Protect |= PAGE_NOCACHE; } - if (Entry & PA_WT) + if (Pte & PA_WT) { Protect |= PAGE_WRITETHROUGH; } - if (!(Entry & PA_USER)) + if (!(Pte & PA_USER)) { Protect |= PAGE_SYSTEM; } - } + + if (Address < MmSystemRangeStart) + MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread()); + return(Protect); }
@@ -853,43 +753,80 @@ NTAPI MmSetPageProtect(PEPROCESS Process, PVOID Address, ULONG flProtect) { ULONG Attributes = 0; - PULONG Pt; + PMMPTE PointerPte; ULONG Pte; - KIRQL OldIrql;
DPRINT("MmSetPageProtect(Process %p Address %p flProtect %x)\n", Process, Address, flProtect);
+ ASSERT(Process != NULL); + ASSERT(Address < MmSystemRangeStart); + + ASSERT(Process == PsGetCurrentProcess()); + + MiLockProcessWorkingSetUnsafe(Process, PsGetCurrentThread()); + + MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL); + Attributes = ProtectToPTE(flProtect);
Attributes &= 0xfff; - if (Address >= MmSystemRangeStart) - { - Attributes &= ~PA_USER; - } - else - { - Attributes |= PA_USER; - } + Attributes |= PA_USER; + + PointerPte = MiAddressToPte(Address); + Pte = InterlockedExchangePte(PointerPte, PAGE_MASK(PointerPte->u.Long) | Attributes | (PointerPte->u.Long & (PA_ACCESSED|PA_DIRTY)));
- Pt = MmGetPageTableForProcess(Process, Address, FALSE, &OldIrql); - if (Pt == NULL) + // We should be able to bring a page back from PAGE_NOACCESS + if ((Pte & 0x800) || !(Pte >> PAGE_SHIFT)) { + DPRINT1("Invalid Pte %lx\n", Pte); KeBugCheck(MEMORY_MANAGEMENT); } - Pte = InterlockedExchangePte(Pt, PAGE_MASK(*Pt) | Attributes | (*Pt & (PA_ACCESSED|PA_DIRTY)));
- // We should be able to bring a page back from PAGE_NOACCESS + if ((Pte & 0xFFF) != Attributes) + KeInvalidateTlbEntry(Address); + + MiUnlockProcessWorkingSetUnsafe(Process, PsGetCurrentThread()); +} + +VOID +NTAPI +MmSetDirtyBit(PEPROCESS Process, PVOID Address, BOOLEAN Bit) +{ + PMMPTE PointerPte; + ULONG Pte; + + DPRINT("MmSetDirtyBit(Process %p Address %p Bit %x)\n", + Process, Address, Bit); + + ASSERT(Process != NULL); + ASSERT(Address < MmSystemRangeStart); + + ASSERT(Process == PsGetCurrentProcess()); + + MiLockProcessWorkingSetUnsafe(Process, PsGetCurrentThread()); + + MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL); + + PointerPte = MiAddressToPte(Address); + Pte = PointerPte->u.Long; + if (Bit) + Pte |= PA_DIRTY; + else + Pte &= ~PA_DIRTY; + Pte = InterlockedExchangePte(PointerPte, Pte); + + // We shouldnl't set dirty bit on non-mapped adresses if ((Pte & 0x800) || !(Pte >> PAGE_SHIFT)) { DPRINT1("Invalid Pte %lx\n", Pte); KeBugCheck(MEMORY_MANAGEMENT); }
- if((Pte & Attributes) != Attributes) - MiFlushTlb(Pt, Address, OldIrql); - else - MmUnmapPageTable(Pt, OldIrql); + if (!Bit) + KeInvalidateTlbEntry(Address); + + MiUnlockProcessWorkingSetUnsafe(Process, PsGetCurrentThread()); }
CODE_SEG("INIT") diff --git a/ntoskrnl/mm/marea.c b/ntoskrnl/mm/marea.c index 92eebc173cb..1942dd76b27 100644 --- a/ntoskrnl/mm/marea.c +++ b/ntoskrnl/mm/marea.c @@ -328,24 +328,6 @@ MmFreeMemoryArea( FreePage(FreePageContext, MemoryArea, (PVOID)Address, Page, SwapEntry, (BOOLEAN)Dirty); } -#if (_MI_PAGING_LEVELS == 2) - /* Remove page table reference */ - ASSERT(KeGetCurrentIrql() <= APC_LEVEL); - if ((SwapEntry || Page) && ((PVOID)Address < MmSystemRangeStart)) - { - ASSERT(AddressSpace != MmGetKernelAddressSpace()); - if (MiQueryPageTableReferences((PVOID)Address) == 0) - { - /* No PTE relies on this PDE. Release it */ - KIRQL OldIrql = MiAcquirePfnLock(); - PMMPDE PointerPde = MiAddressToPde(Address); - ASSERT(PointerPde->u.Hard.Valid == 1); - MiDeletePte(PointerPde, MiPdeToPte(PointerPde), Process, NULL); - ASSERT(PointerPde->u.Hard.Valid == 0); - MiReleasePfnLock(OldIrql); - } - } -#endif }
if (Process != NULL && diff --git a/ntoskrnl/mm/rmap.c b/ntoskrnl/mm/rmap.c index c680a8f7a9f..64758cc5b89 100644 --- a/ntoskrnl/mm/rmap.c +++ b/ntoskrnl/mm/rmap.c @@ -102,18 +102,26 @@ GetEntry:
MmLockAddressSpace(AddressSpace);
- if (MmGetPfnForProcess(Process, Address) != Page) + MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, Address); + if (MemoryArea == NULL || MemoryArea->DeleteInProgress) { - /* This changed in the short window where we didn't have any locks */ MmUnlockAddressSpace(AddressSpace); ExReleaseRundownProtection(&Process->RundownProtect); ObDereferenceObject(Process); goto GetEntry; }
- MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, Address); - if (MemoryArea == NULL || MemoryArea->DeleteInProgress) + + /* Attach to it, if needed */ + ASSERT(PsGetCurrentProcess() == PsInitialSystemProcess); + if (Process != PsInitialSystemProcess) + KeAttachProcess(&Process->Pcb); + + if (MmGetPfnForProcess(Process, Address) != Page) { + /* This changed in the short window where we didn't have any locks */ + if (Process != PsInitialSystemProcess) + KeDetachProcess(); MmUnlockAddressSpace(AddressSpace); ExReleaseRundownProtection(&Process->RundownProtect); ObDereferenceObject(Process); @@ -140,6 +148,8 @@ GetEntry: { /* The segment is being read or something. Give up */ MmUnlockSectionSegment(Segment); + if (Process != PsInitialSystemProcess) + KeDetachProcess(); MmUnlockAddressSpace(AddressSpace); ExReleaseRundownProtection(&Process->RundownProtect); ObDereferenceObject(Process); @@ -160,11 +170,6 @@ GetEntry: /* This page is private to the process */ MmUnlockSectionSegment(Segment);
- /* Attach to it, if needed */ - ASSERT(PsGetCurrentProcess() == PsInitialSystemProcess); - if (Process != PsInitialSystemProcess) - KeAttachProcess(&Process->Pcb); - /* Check if we should write it back to the page file */ SwapEntry = MmGetSavedSwapEntryPage(Page);
@@ -179,7 +184,7 @@ GetEntry: Address, NULL);
/* We can't, so let this page in the Process VM */ - MmCreateVirtualMapping(Process, Address, Region->Protect, &Page, 1); + MmCreateVirtualMapping(Process, Address, Region->Protect, Page); MmInsertRmap(Page, Process, Address); MmSetDirtyPage(Process, Address);
@@ -219,7 +224,7 @@ GetEntry: MmFreeSwapPage(SwapEntry);
/* We can't, so let this page in the Process VM */ - MmCreateVirtualMapping(Process, Address, Region->Protect, &Page, 1); + MmCreateVirtualMapping(Process, Address, Region->Protect, Page); MmInsertRmap(Page, Process, Address); MmSetDirtyPage(Process, Address);
@@ -241,7 +246,6 @@ GetEntry: }
/* We can finally let this page go */ - MmUnlockAddressSpace(AddressSpace); if (Process != PsInitialSystemProcess) KeDetachProcess(); @@ -262,6 +266,8 @@ GetEntry: Released = MmUnsharePageEntrySectionSegment(MemoryArea, Segment, &Offset, Dirty, TRUE, NULL);
MmUnlockSectionSegment(Segment); + if (Process != PsInitialSystemProcess) + KeDetachProcess(); MmUnlockAddressSpace(AddressSpace);
ExReleaseRundownProtection(&Process->RundownProtect); diff --git a/ntoskrnl/mm/section.c b/ntoskrnl/mm/section.c index 2485d5bb5f5..0b6b8e80416 100644 --- a/ntoskrnl/mm/section.c +++ b/ntoskrnl/mm/section.c @@ -1636,8 +1636,7 @@ MmNotPresentFaultSectionView(PMMSUPPORT AddressSpace, Status = MmCreateVirtualMapping(Process, PAddress, Region->Protect, - &Page, - 1); + Page); if (!NT_SUCCESS(Status)) { DPRINT("MmCreateVirtualMapping failed, not out of memory\n"); @@ -1679,8 +1678,7 @@ MmNotPresentFaultSectionView(PMMSUPPORT AddressSpace, Status = MmCreateVirtualMappingUnsafe(Process, PAddress, Region->Protect, - &Page, - 1); + Page); if (!NT_SUCCESS(Status)) { DPRINT("MmCreateVirtualMappingUnsafe failed, not out of memory\n"); @@ -1729,7 +1727,7 @@ MmNotPresentFaultSectionView(PMMSUPPORT AddressSpace, MmSetPageEntrySectionSegment(Segment, &Offset, MAKE_SSE(Page << PAGE_SHIFT, 1)); MmUnlockSectionSegment(Segment);
- Status = MmCreateVirtualMapping(Process, PAddress, Attributes, &Page, 1); + Status = MmCreateVirtualMapping(Process, PAddress, Attributes, Page); if (!NT_SUCCESS(Status)) { DPRINT1("Unable to create virtual mapping\n"); @@ -1828,8 +1826,7 @@ MmNotPresentFaultSectionView(PMMSUPPORT AddressSpace, Status = MmCreateVirtualMapping(Process, PAddress, Attributes, - &Page, - 1); + Page); if (!NT_SUCCESS(Status)) { DPRINT1("Unable to create virtual mapping\n"); @@ -1857,8 +1854,7 @@ MmNotPresentFaultSectionView(PMMSUPPORT AddressSpace, Status = MmCreateVirtualMapping(Process, PAddress, Attributes, - &Page, - 1); + Page); if (!NT_SUCCESS(Status)) { DPRINT1("Unable to create virtual mapping\n"); @@ -1985,8 +1981,7 @@ MmAccessFaultSectionView(PMMSUPPORT AddressSpace, Status = MmCreateVirtualMapping(Process, PAddress, Region->Protect, - &NewPage, - 1); + NewPage); if (!NT_SUCCESS(Status)) { DPRINT1("MmCreateVirtualMapping failed, unable to create virtual mapping, not out of memory\n");