From 0e90ca943ec20c5434f35f1f31d9498822da2743 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Gardou?= Date: Fri, 25 Jul 2014 22:13:35 +0000 Subject: [PATCH] [NTOS/MM] - First implementation of NtProtectVirtualMemory with Transition PTE - Release PTE frame page after erasing the PTE to avoid useless page fault svn path=/trunk/; revision=63736 --- reactos/ntoskrnl/mm/ARM3/pagfault.c | 31 +++++++++-- reactos/ntoskrnl/mm/ARM3/pfnlist.c | 2 - reactos/ntoskrnl/mm/ARM3/virtual.c | 86 +++++++++++++++++++++-------- 3 files changed, 90 insertions(+), 29 deletions(-) diff --git a/reactos/ntoskrnl/mm/ARM3/pagfault.c b/reactos/ntoskrnl/mm/ARM3/pagfault.c index 662d1ea9702..0236bbf1655 100644 --- a/reactos/ntoskrnl/mm/ARM3/pagfault.c +++ b/reactos/ntoskrnl/mm/ARM3/pagfault.c @@ -1310,20 +1310,41 @@ MiDispatchFault(IN BOOLEAN StoreInstruction, } } + /* Is this a transition PTE */ + if (TempPte.u.Soft.Transition) + { + PVOID InPageBlock = NULL; + /* Lock the PFN database */ + LockIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); + + /* Resolve */ + Status = MiResolveTransitionFault(Address, PointerPte, Process, LockIrql, &InPageBlock); + + NT_ASSERT(NT_SUCCESS(Status)); + + /* And now release the lock and leave*/ + KeReleaseQueuedSpinLock(LockQueuePfnLock, LockIrql); + + ASSERT(OldIrql == KeGetCurrentIrql()); + ASSERT(OldIrql <= APC_LEVEL); + ASSERT(KeAreAllApcsDisabled() == TRUE); + return Status; + } + // // The PTE must be invalid but not completely empty. It must also not be a - // prototype PTE as that scenario should've been handled above. These are - // all Windows checks + // prototype or transition PTE as those scenarii should've been handled above. + // These are all Windows checks // ASSERT(TempPte.u.Hard.Valid == 0); ASSERT(TempPte.u.Soft.Prototype == 0); + ASSERT(TempPte.u.Soft.Transition == 0); ASSERT(TempPte.u.Long != 0); // - // No transition or page file software PTEs in ARM3 yet, so this must be a - // demand zero page. These are all ReactOS checks + // No page file software PTEs in ARM3 yet, so this must be a + // demand zero page. This is a ReactOS check. // - ASSERT(TempPte.u.Soft.Transition == 0); ASSERT(TempPte.u.Soft.PageFileHigh == 0); // diff --git a/reactos/ntoskrnl/mm/ARM3/pfnlist.c b/reactos/ntoskrnl/mm/ARM3/pfnlist.c index af132b2e4a7..12b33cec0e7 100644 --- a/reactos/ntoskrnl/mm/ARM3/pfnlist.c +++ b/reactos/ntoskrnl/mm/ARM3/pfnlist.c @@ -292,7 +292,6 @@ MiUnlinkPageFromList(IN PMMPFN Pfn) ListHead = &MmModifiedPageListByColor[0]; /* Decrease transition page counter */ - ASSERT(Pfn->u3.e1.PrototypePte == 1); /* Only supported ARM3 case */ MmTransitionSharedPages--; } else if (ListHead == &MmModifiedNoWritePageListHead) @@ -984,7 +983,6 @@ MiInsertPageInList(IN PMMPFNLIST ListHead, ASSERT(Pfn1->OriginalPte.u.Soft.PageFileHigh == 0); /* One more transition page */ - ASSERT(Pfn1->u3.e1.PrototypePte == 1); MmTransitionSharedPages++; /* Increment the number of per-process modified pages */ diff --git a/reactos/ntoskrnl/mm/ARM3/virtual.c b/reactos/ntoskrnl/mm/ARM3/virtual.c index fc8762971a4..97d76011596 100644 --- a/reactos/ntoskrnl/mm/ARM3/virtual.c +++ b/reactos/ntoskrnl/mm/ARM3/virtual.c @@ -404,13 +404,36 @@ MiDeletePte(IN PMMPTE PointerPte, /* Capture the PTE */ TempPte = *PointerPte; - /* We only support valid PTEs for now */ - ASSERT(TempPte.u.Hard.Valid == 1); + /* See if the PTE is valid */ if (TempPte.u.Hard.Valid == 0) { - /* Invalid PTEs not supported yet */ + /* Prototype PTEs not supported yet */ ASSERT(TempPte.u.Soft.Prototype == 0); - ASSERT(TempPte.u.Soft.Transition == 0); + if (TempPte.u.Soft.Transition) + { + /* Get the PFN entry */ + PageFrameIndex = PFN_FROM_PTE(&TempPte); + Pfn1 = MiGetPfnEntry(PageFrameIndex); + + DPRINT1("Pte %p is transitional!\n", PointerPte); + + /* Destroy the PTE */ + MI_ERASE_PTE(PointerPte); + + /* Drop the reference on the page table. */ + MiDecrementShareCount(MiGetPfnEntry(Pfn1->u4.PteFrame), Pfn1->u4.PteFrame); + + if (Pfn1->u2.ShareCount == 0) + { + NT_ASSERT(Pfn1->u3.e2.ReferenceCount == 0); + /* Mark the page temporarily as valid, we're going to make it free soon */ + Pfn1->u3.e1.PageLocation = ActiveAndValid; + + /* Bring it back into the free list */ + MiInsertPageInFreeList(PageFrameIndex); + } + return; + } } /* Get the PFN entry */ @@ -457,6 +480,9 @@ MiDeletePte(IN PMMPTE PointerPte, (ULONG_PTR)Pfn1->PteAddress); } } + + /* Erase it */ + MI_ERASE_PTE(PointerPte); } else { @@ -471,6 +497,9 @@ MiDeletePte(IN PMMPTE PointerPte, (ULONG_PTR)Pfn1->PteAddress); } + /* Erase the PTE */ + MI_ERASE_PTE(PointerPte); + /* There should only be 1 shared reference count */ ASSERT(Pfn1->u2.ShareCount == 1); @@ -485,8 +514,7 @@ MiDeletePte(IN PMMPTE PointerPte, //CurrentProcess->NumberOfPrivatePages--; } - /* Destroy the PTE and flush the TLB */ - MI_ERASE_PTE(PointerPte); + /* Flush the TLB */ KeFlushCurrentTb(); } @@ -2053,7 +2081,7 @@ MiProtectVirtualMemory(IN PEPROCESS Process, /* Check for ROS specific memory area */ MemoryArea = MmLocateMemoryAreaByAddress(&Process->Vm, *BaseAddress); - if ((MemoryArea) && (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW)) + if ((MemoryArea) && (MemoryArea->Type != MEMORY_AREA_OWNED_BY_ARM3)) { /* Evil hack */ return MiRosProtectVirtualMemory(Process, @@ -2231,27 +2259,41 @@ MiProtectVirtualMemory(IN PEPROCESS Process, if ((NewAccessProtection & PAGE_NOACCESS) || (NewAccessProtection & PAGE_GUARD)) { - /* The page should be in the WS and we should make it transition now */ - DPRINT1("Making valid page invalid is not yet supported!\n"); - Status = STATUS_NOT_IMPLEMENTED; - /* Unlock the working set */ - MiUnlockProcessWorkingSetUnsafe(Process, Thread); - goto FailPath; - } + KIRQL OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); - /* Write the protection mask and write it with a TLB flush */ - Pfn1->OriginalPte.u.Soft.Protection = ProtectionMask; - MiFlushTbAndCapture(Vad, - PointerPte, - ProtectionMask, - Pfn1, - TRUE); + /* Mark the PTE as transition and change its protection */ + PteContents.u.Hard.Valid = 0; + PteContents.u.Soft.Transition = 1; + PteContents.u.Trans.Protection = ProtectionMask; + /* Decrease PFN share count and write the PTE */ + MiDecrementShareCount(Pfn1, PFN_FROM_PTE(&PteContents)); + // FIXME: remove the page from the WS + MI_WRITE_INVALID_PTE(PointerPte, PteContents); +#ifdef CONFIG_SMP + // FIXME: Should invalidate entry in every CPU TLB + ASSERT(FALSE); +#endif + KeInvalidateTlbEntry(MiPteToAddress(PointerPte)); + + /* We are done for this PTE */ + KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); + } + else + { + /* Write the protection mask and write it with a TLB flush */ + Pfn1->OriginalPte.u.Soft.Protection = ProtectionMask; + MiFlushTbAndCapture(Vad, + PointerPte, + ProtectionMask, + Pfn1, + TRUE); + } } else { /* We don't support these cases yet */ ASSERT(PteContents.u.Soft.Prototype == 0); - ASSERT(PteContents.u.Soft.Transition == 0); + //ASSERT(PteContents.u.Soft.Transition == 0); /* The PTE is already demand-zero, just update the protection mask */ PteContents.u.Soft.Protection = ProtectionMask;