From 2bbb9b29301ba903972fc4e41b353c83c7070082 Mon Sep 17 00:00:00 2001 From: Art Yerkes Date: Fri, 1 Jun 2012 09:01:41 +0000 Subject: [PATCH] [NTOSKRNL] - Allow placement of a wait entry in virgin address space prior to mapping a page. - Add a wait entry in the address space when paging in section view. - Correct a problem that previously prevented us from evicting or querying pages that had been protected into PAGE_NOACCESS protection by adding a query for disabled mappings and handling disabled mappings in a few places (this may be controversial). svn path=/trunk/; revision=56682 --- reactos/ntoskrnl/cache/section/newmm.h | 1 - reactos/ntoskrnl/include/internal/mm.h | 11 +++++++++++ reactos/ntoskrnl/mm/i386/page.c | 24 ++++++++++++++++++++---- reactos/ntoskrnl/mm/section.c | 14 +++++++++++++- 4 files changed, 44 insertions(+), 6 deletions(-) diff --git a/reactos/ntoskrnl/cache/section/newmm.h b/reactos/ntoskrnl/cache/section/newmm.h index 77447d94a1f..3cd6cfc934c 100644 --- a/reactos/ntoskrnl/cache/section/newmm.h +++ b/reactos/ntoskrnl/cache/section/newmm.h @@ -4,7 +4,6 @@ /* TYPES *********************************************************************/ -#define MM_WAIT_ENTRY 0x7ffffc00 #define PFN_FROM_SSE(E) ((PFN_NUMBER)((E) >> PAGE_SHIFT)) #define IS_SWAP_FROM_SSE(E) ((E) & 0x00000001) #define MM_IS_WAIT_PTE(E) \ diff --git a/reactos/ntoskrnl/include/internal/mm.h b/reactos/ntoskrnl/include/internal/mm.h index 5e09fe474ab..f306da2d1c5 100644 --- a/reactos/ntoskrnl/include/internal/mm.h +++ b/reactos/ntoskrnl/include/internal/mm.h @@ -179,6 +179,10 @@ typedef ULONG_PTR SWAPENTRY; (PAGE_WRITECOPY | \ PAGE_EXECUTE_WRITECOPY) +// +// Wait entry for marking pages that are being serviced +// +#define MM_WAIT_ENTRY 0x7ffffc00 #define InterlockedCompareExchangePte(PointerPte, Exchange, Comperand) \ InterlockedCompareExchange((PLONG)(PointerPte), Exchange, Comperand) @@ -1263,6 +1267,13 @@ MmIsPagePresent( PVOID Address ); +BOOLEAN +NTAPI +MmIsDisabledPage( + struct _EPROCESS* Process, + PVOID Address +); + VOID NTAPI MmInitGlobalKernelPageDirectory(VOID); diff --git a/reactos/ntoskrnl/mm/i386/page.c b/reactos/ntoskrnl/mm/i386/page.c index 5136870cdbb..2047f03f0db 100644 --- a/reactos/ntoskrnl/mm/i386/page.c +++ b/reactos/ntoskrnl/mm/i386/page.c @@ -467,7 +467,9 @@ MmDeleteVirtualMapping(PEPROCESS Process, PVOID Address, BOOLEAN FreePage, */ Pte = InterlockedExchangePte(Pt, 0); - WasValid = (Pte & PA_PRESENT); + /* 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 @@ -700,6 +702,14 @@ 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); +} + BOOLEAN NTAPI MmIsPageSwapEntry(PEPROCESS Process, PVOID Address) @@ -738,12 +748,17 @@ MmCreatePageFileMapping(PEPROCESS Process, if (Pt == NULL) { /* Nobody should page out an address that hasn't even been mapped */ - KeBugCheck(MEMORY_MANAGEMENT); + /* 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); } Pte = InterlockedExchangePte(Pt, SwapEntry << 1); if (Pte != 0) { - KeBugCheck(MEMORY_MANAGEMENT); + KeBugCheckEx(MEMORY_MANAGEMENT, SwapEntry, (ULONG_PTR)Process, (ULONG_PTR)Address, 0); } if (Address < MmSystemRangeStart) @@ -975,7 +990,8 @@ MmSetPageProtect(PEPROCESS Process, PVOID Address, ULONG flProtect) } Pte = InterlockedExchangePte(Pt, PAGE_MASK(*Pt) | Attributes | (*Pt & (PA_ACCESSED|PA_DIRTY))); - if (!(Pte & PA_PRESENT)) + // 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); diff --git a/reactos/ntoskrnl/mm/section.c b/reactos/ntoskrnl/mm/section.c index 97bc7473401..2fb1f4ca674 100644 --- a/reactos/ntoskrnl/mm/section.c +++ b/reactos/ntoskrnl/mm/section.c @@ -1223,6 +1223,11 @@ MmNotPresentFaultSectionView(PMMSUPPORT AddressSpace, return(STATUS_SUCCESS); } + if (MmIsDisabledPage(Process, Address)) + { + return(STATUS_ACCESS_VIOLATION); + } + /* * Check for the virtual memory area being deleted. */ @@ -1438,6 +1443,8 @@ MmNotPresentFaultSectionView(PMMSUPPORT AddressSpace, if (Entry == 0) { + SWAPENTRY FakeSwapEntry; + /* * If the entry is zero (and it can't change because we have * locked the segment) then we need to load the page. @@ -1448,6 +1455,7 @@ MmNotPresentFaultSectionView(PMMSUPPORT AddressSpace, */ MmSetPageEntrySectionSegment(Segment, &Offset, MAKE_SWAP_SSE(MM_WAIT_ENTRY)); MmUnlockSectionSegment(Segment); + MmCreatePageFileMapping(Process, PAddress, MM_WAIT_ENTRY); MmUnlockAddressSpace(AddressSpace); if ((Segment->Flags & MM_PAGEFILE_SEGMENT) || @@ -1496,6 +1504,9 @@ MmNotPresentFaultSectionView(PMMSUPPORT AddressSpace, MmSetPageEntrySectionSegment(Segment, &Offset, Entry); MmUnlockSectionSegment(Segment); + MmDeletePageFileMapping(Process, PAddress, &FakeSwapEntry); + DPRINT("CreateVirtualMapping Page %x Process %p PAddress %p Attributes %x\n", + Page, Process, PAddress, Attributes); Status = MmCreateVirtualMapping(Process, PAddress, Attributes, @@ -1506,6 +1517,7 @@ MmNotPresentFaultSectionView(PMMSUPPORT AddressSpace, DPRINT1("Unable to create virtual mapping\n"); KeBugCheck(MEMORY_MANAGEMENT); } + ASSERT(MmIsPagePresent(Process, PAddress)); MmInsertRmap(Page, Process, Address); MiSetPageEvent(Process, Address); @@ -2441,7 +2453,7 @@ MmAlterViewAttributes(PMMSUPPORT AddressSpace, } } - if (MmIsPagePresent(Process, Address)) + if (MmIsPagePresent(Process, Address) || MmIsDisabledPage(Process, Address)) { MmSetPageProtect(Process, Address, Protect);