diff --git a/ntoskrnl/mm/ARM3/procsup.c b/ntoskrnl/mm/ARM3/procsup.c index dd04b68fb8..8e8dec7c09 100644 --- a/ntoskrnl/mm/ARM3/procsup.c +++ b/ntoskrnl/mm/ARM3/procsup.c @@ -1373,7 +1373,9 @@ MmDeleteProcessAddressSpace(IN PEPROCESS Process) MiDecrementShareCount(Pfn1, PageFrameIndex); /* Page table is now dead. Bye bye... */ - ASSERT((Pfn1->u3.e2.ReferenceCount == 0) || (Pfn1->u3.e1.WriteInProgress)); + if (!((Pfn1->u3.e2.ReferenceCount == 0) || (Pfn1->u3.e1.WriteInProgress))) + DPRINT1("Ref Count/Share Count '%lx/%lx'.\n", + Pfn1->u3.e2.ReferenceCount, Pfn1->u2.ShareCount); } else { diff --git a/ntoskrnl/mm/i386/page.c b/ntoskrnl/mm/i386/page.c index cf2e012389..4d68a3e587 100644 --- a/ntoskrnl/mm/i386/page.c +++ b/ntoskrnl/mm/i386/page.c @@ -20,6 +20,32 @@ #endif /* GLOBALS *****************************************************************/ + +#define PA_BIT_PRESENT (0) +#define PA_BIT_READWRITE (1) +#define PA_BIT_USER (2) +#define PA_BIT_WT (3) +#define PA_BIT_CD (4) +#define PA_BIT_ACCESSED (5) +#define PA_BIT_DIRTY (6) +#define PA_BIT_GLOBAL (8) + +#define PA_PRESENT (1 << PA_BIT_PRESENT) +#define PA_READWRITE (1 << PA_BIT_READWRITE) +#define PA_USER (1 << PA_BIT_USER) +#define PA_DIRTY (1 << PA_BIT_DIRTY) +#define PA_WT (1 << PA_BIT_WT) +#define PA_CD (1 << PA_BIT_CD) +#define PA_ACCESSED (1 << PA_BIT_ACCESSED) +#define PA_GLOBAL (1 << PA_BIT_GLOBAL) + +#define IS_HYPERSPACE(v) (((ULONG)(v) >= HYPER_SPACE && (ULONG)(v) <= HYPER_SPACE_END)) + +#define PTE_TO_PFN(X) ((X) >> PAGE_SHIFT) +#define PFN_TO_PTE(X) ((X) << PAGE_SHIFT) + +#define PAGE_MASK(x) ((x)&(~0xfff)) + const ULONG_PTR MmProtectToPteMask[32] = @@ -109,6 +135,30 @@ ULONG MmProtectToValue[32] = /* FUNCTIONS ***************************************************************/ +static BOOLEAN MmUnmapPageTable(PULONG Pt, KIRQL OldIrql) +{ + if (!IS_HYPERSPACE(Pt)) + { + return TRUE; + } + + MiUnmapPageInHyperSpace(PsGetCurrentProcess(), Pt, OldIrql); + + return FALSE; +} + +VOID +MiFlushTlb(PULONG Pt, PVOID Address, KIRQL OldIrql) +{ + if ((Pt && MmUnmapPageTable(Pt, OldIrql)) || Address >= MmSystemRangeStart) + { + KeInvalidateTlbEntry(Address); + } +} + + + + NTSTATUS NTAPI MiFillSystemPageDirectory(IN PVOID Base, @@ -217,6 +267,80 @@ MmGetPfnForProcess(PEPROCESS Process, return Page; } +static PULONG +MmGetPageTableForProcess(PEPROCESS Process, PVOID Address, BOOLEAN Create, PKIRQL OldIrql) +{ + PFN_NUMBER Pfn; + PULONG Pt; + PMMPDE PointerPde; + + if (Address < MmSystemRangeStart) + { + /* We should have a process for user land addresses */ + ASSERT(Process != NULL); + + 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); + } + + /* This is for our process */ + PointerPde = MiAddressToPde(Address); + Pt = (PULONG)MiAddressToPte(Address); + + if ((PointerPde->u.Hard.Valid == 0) && (Create == FALSE)) + { + /* Do not fault PDE in if not needed */ + return NULL; + } + + 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) + { + /* 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; +} + VOID NTAPI MmDeleteVirtualMapping(PEPROCESS Process, PVOID Address, @@ -225,102 +349,75 @@ MmDeleteVirtualMapping(PEPROCESS Process, PVOID Address, * FUNCTION: Delete a virtual mapping */ { - PMMPTE PointerPte; - MMPTE OldPte; + BOOLEAN WasValid = FALSE; + PFN_NUMBER Pfn; + 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); - - /* And we should be at low IRQL */ - ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL); + Pt = MmGetPageTableForProcess(Process, Address, FALSE, &OldIrql); - /* Make sure our PDE is valid, and that everything is going fine */ - if (Process == NULL) + if (Pt == NULL) { - if (Address < MmSystemRangeStart) + if (WasDirty != NULL) { - DPRINT1("NULL process given for user-mode mapping at %p\n", Address); - KeBugCheck(MEMORY_MANAGEMENT); + *WasDirty = FALSE; } -#if (_MI_PAGING_LEVELS == 2) - if (!MiSynchronizeSystemPde(MiAddressToPde(Address))) -#else - if (!MiIsPdeForAddressValid(Address)) -#endif + if (Page != NULL) { - /* There can't be a page if there is no PDE */ - if (WasDirty) - *WasDirty = FALSE; - if (Page) - *Page = 0; - return; + *Page = 0; } + return; } - else - { - 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); - } - - /* Only for current process !!! */ - ASSERT(Process = PsGetCurrentProcess()); - MiLockProcessWorkingSetUnsafe(Process, PsGetCurrentThread()); - /* No PDE --> No page */ - if (!MiIsPageTablePresent(Address)) - { - MiUnlockProcessWorkingSetUnsafe(Process, PsGetCurrentThread()); - if (WasDirty) - *WasDirty = 0; - if (Page) - *Page = 0; - return; - } + /* + * Atomically set the entry to zero and get the old value. + */ + Pte = InterlockedExchangePte(Pt, 0); - MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL); - } + /* 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) + { + /* 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); - PointerPte = MiAddressToPte(Address); - OldPte.u.Long = InterlockedExchangePte(PointerPte, 0); + if (Address < MmSystemRangeStart) + { + /* Remove PDE reference */ + Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)]--; + ASSERT(Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)] < PTE_PER_PAGE); + } - if (OldPte.u.Long == 0) + Pfn = PTE_TO_PFN(Pte); + } + else { - /* There was nothing here */ - if (Address < MmSystemRangeStart) - MiUnlockProcessWorkingSetUnsafe(Process, PsGetCurrentThread()); - if (WasDirty) - *WasDirty = 0; - if (Page) - *Page = 0; - return; + MmUnmapPageTable(Pt, OldIrql); + Pfn = 0; } - /* It must have been present, or not a swap entry */ - ASSERT(OldPte.u.Hard.Valid || !FlagOn(OldPte.u.Long, 0x800)); - - if (OldPte.u.Hard.Valid) - KeInvalidateTlbEntry(Address); - - if (Address < MmSystemRangeStart) + /* + * Return some information to the caller + */ + if (WasDirty != NULL) { - /* Remove PDE reference */ - if (MiDecrementPageTableReferences(Address) == 0) - { - KIRQL OldIrql = MiAcquirePfnLock(); - MiDeletePde(MiAddressToPde(Address), Process); - MiReleasePfnLock(OldIrql); - } - - MiUnlockProcessWorkingSetUnsafe(Process, PsGetCurrentThread()); + *WasDirty = ((Pte & PA_DIRTY) && (Pte & PA_PRESENT)) ? TRUE : FALSE; + } + if (Page != NULL) + { + *Page = Pfn; } - if (WasDirty) - *WasDirty = !!OldPte.u.Hard.Dirty; + *WasDirty = FlagOn(Pte, PA_DIRTY); if (Page) - *Page = OldPte.u.Hard.PageFrameNumber; + *Page = PTE_TO_PFN(Pte); }