Author: sir_richard Date: Mon Oct 4 18:51:07 2010 New Revision: 48979
URL: http://svn.reactos.org/svn/reactos?rev=48979&view=rev Log: [NTOS]: Implement/fixup the code paths during page faults that are needed to succesfuly resolve a demand page associated with a pagefile backed ARM3 section (which uses Prototype PTEs). A lot of the code was already there but assumed we were using Prototype PTEs only for the shared user data page. By combining that code with the typical demand-zero fault code, we obtain the needed paths. For now, only tested with ARM3 sections that are page-filed backed (not image or data-file backed) mapped into system view space (MmMapViewOfSectionInSystemSpace), not user-mode addresses (which need VADs). The code to actually create/map these doesn't exist in trunk yet, the purpose of this checkin is to test the new fault changes to make sure they don't cause negative effects to already-working faults.
Modified: trunk/reactos/ntoskrnl/mm/ARM3/pagfault.c
Modified: trunk/reactos/ntoskrnl/mm/ARM3/pagfault.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/mm/ARM3/pagfault.c... ============================================================================== --- trunk/reactos/ntoskrnl/mm/ARM3/pagfault.c [iso-8859-1] (original) +++ trunk/reactos/ntoskrnl/mm/ARM3/pagfault.c [iso-8859-1] Mon Oct 4 18:51:07 2010 @@ -176,15 +176,14 @@ { PFN_NUMBER PageFrameNumber = 0; MMPTE TempPte; - BOOLEAN NeedZero = FALSE; + BOOLEAN NeedZero = FALSE, HaveLock = FALSE; ULONG Color; DPRINT("ARM3 Demand Zero Page Fault Handler for address: %p in process: %p\n", Address, Process);
/* Must currently only be called by paging path */ - ASSERT(OldIrql == MM_NOIRQL); - if (Process) + if ((Process) && (OldIrql == MM_NOIRQL)) { /* Sanity check */ ASSERT(MI_IS_PAGE_TABLE_ADDRESS(PointerPte)); @@ -194,45 +193,64 @@
/* Get process color */ Color = MI_GET_NEXT_PROCESS_COLOR(Process); + ASSERT(Color != 0xFFFFFFFF);
/* We'll need a zero page */ NeedZero = TRUE; } else { + /* Check if we need a zero page */ + NeedZero = (OldIrql != MM_NOIRQL); + /* Get the next system page color */ Color = MI_GET_NEXT_COLOR(); } - - // - // Lock the PFN database - // - OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); + + /* Check if the PFN database should be acquired */ + if (OldIrql == MM_NOIRQL) + { + /* Acquire it and remember we should release it after */ + OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); + HaveLock = TRUE; + } + + /* We either manually locked the PFN DB, or already came with it locked */ + ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); + + /* Do we need a zero page? */ ASSERT(PointerPte->u.Hard.Valid == 0); - - /* Do we need a zero page? */ - if (NeedZero) + if ((NeedZero) && (Process)) { /* Try to get one, if we couldn't grab a free page and zero it */ PageFrameNumber = MiRemoveZeroPageSafe(Color); - if (PageFrameNumber) NeedZero = FALSE; - } - - /* Did we get a page? */ - if (!PageFrameNumber) - { - /* We either failed to find a zero page, or this is a system request */ + if (PageFrameNumber) + { + /* We got a genuine zero page, stop worrying about it */ + NeedZero = FALSE; + } + else + { + /* We'll need a free page and zero it manually */ + PageFrameNumber = MiRemoveAnyPage(Color); + } + } + else if (!NeedZero) + { + /* Process or system doesn't want a zero page, grab anything */ PageFrameNumber = MiRemoveAnyPage(Color); - DPRINT("New pool page: %lx\n", PageFrameNumber); + } + else + { + /* System wants a zero page, obtain one */ + PageFrameNumber = MiRemoveZeroPage(Color); }
/* Initialize it */ MiInitializePfn(PageFrameNumber, PointerPte, TRUE);
- // - // Release PFN lock - // - KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); + /* Release PFN lock if needed */ + if (HaveLock) KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
// // Increment demand zero faults @@ -283,24 +301,53 @@ IN PMMPFN Pfn1) { MMPTE TempPte; + PMMPTE OriginalPte; + ULONG Protection; PFN_NUMBER PageFrameIndex;
/* Must be called with an valid prototype PTE, with the PFN lock held */ ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); ASSERT(PointerProtoPte->u.Hard.Valid == 1);
- /* Quick-n-dirty */ - ASSERT(PointerPte->u.Soft.PageFileHigh == 0xFFFFF); - /* Get the page */ PageFrameIndex = PFN_FROM_PTE(PointerProtoPte); + + /* Get the PFN entry and set it as a prototype PTE */ + Pfn1 = MiGetPfnEntry(PageFrameIndex); + Pfn1->u3.e1.PrototypePte = 1; + + /* FIXME: Increment the share count for the page table */ + + /* Check where we should be getting the protection information from */ + if (PointerPte->u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED) + { + /* Get the protection from the PTE, there's no real Proto PTE data */ + Protection = PointerPte->u.Soft.Protection; + } + else + { + /* Get the protection from the original PTE link */ + OriginalPte = &Pfn1->OriginalPte; + Protection = OriginalPte->u.Soft.Protection; + }
/* Release the PFN lock */ KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
- /* Build the user PTE */ - ASSERT(Address < MmSystemRangeStart); - MI_MAKE_HARDWARE_PTE_USER(&TempPte, PointerPte, MM_READONLY, PageFrameIndex); + /* Remove caching bits */ + Protection &= ~(MM_NOCACHE | MM_NOACCESS); + + /* Check if this is a kernel or user address */ + if (Address < MmSystemRangeStart) + { + /* Build the user PTE */ + MI_MAKE_HARDWARE_PTE_USER(&TempPte, PointerPte, Protection, PageFrameIndex); + } + else + { + /* Build the kernel PTE */ + MI_MAKE_HARDWARE_PTE(&TempPte, PointerPte, Protection, PageFrameIndex); + }
/* Write the PTE */ MI_WRITE_VALID_PTE(PointerPte, TempPte); @@ -325,25 +372,56 @@ MMPTE TempPte; PMMPFN Pfn1; PFN_NUMBER PageFrameIndex; + NTSTATUS Status;
/* Must be called with an invalid, prototype PTE, with the PFN lock held */ ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); ASSERT(PointerPte->u.Hard.Valid == 0); ASSERT(PointerPte->u.Soft.Prototype == 1);
- /* Read the prototype PTE -- it must be valid since we only handle shared data */ + /* Read the prototype PTE and check if it's valid */ TempPte = *PointerProtoPte; - ASSERT(TempPte.u.Hard.Valid == 1); - - /* One more user of this mapped page */ - PageFrameIndex = PFN_FROM_PTE(&TempPte); - Pfn1 = MiGetPfnEntry(PageFrameIndex); - Pfn1->u2.ShareCount++; - - /* Call it a transition */ - InterlockedIncrement(&KeGetCurrentPrcb()->MmTransitionCount); + if (TempPte.u.Hard.Valid == 1) + { + /* One more user of this mapped page */ + PageFrameIndex = PFN_FROM_PTE(&TempPte); + Pfn1 = MiGetPfnEntry(PageFrameIndex); + Pfn1->u2.ShareCount++; + + /* Call it a transition */ + InterlockedIncrement(&KeGetCurrentPrcb()->MmTransitionCount); + + /* Complete the prototype PTE fault -- this will release the PFN lock */ + return MiCompleteProtoPteFault(StoreInstruction, + Address, + PointerPte, + PointerProtoPte, + OldIrql, + NULL); + } + + /* Make sure there's some protection mask */ + if (TempPte.u.Long == 0) + { + /* Release the lock */ + DPRINT1("Access on reserved section?\n"); + KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); + return STATUS_ACCESS_VIOLATION; + } + + /* This is the only thing we support right now */ + ASSERT(TempPte.u.Soft.PageFileHigh == 0); + ASSERT(TempPte.u.Proto.ReadOnly == 0); + ASSERT(PointerPte > MiHighestUserPte); + ASSERT(TempPte.u.Soft.Prototype == 0); + ASSERT(TempPte.u.Soft.Transition == 0); + + /* Resolve the demand zero fault */ + Status = MiResolveDemandZeroFault(Address, PointerProtoPte, Process, OldIrql); + ASSERT(NT_SUCCESS(Status));
/* Complete the prototype PTE fault -- this will release the PFN lock */ + ASSERT(PointerPte->u.Hard.Valid == 0); return MiCompleteProtoPteFault(StoreInstruction, Address, PointerPte, @@ -388,39 +466,76 @@ { /* This should never happen */ ASSERT(!MI_IS_PHYSICAL_ADDRESS(PointerProtoPte)); + + /* Check if this is a kernel-mode address */ + SuperProtoPte = MiAddressToPte(PointerProtoPte); + if (Address >= MmSystemRangeStart) + { + /* Lock the PFN database */ + LockIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
- /* We currently only handle the shared user data PTE path */ - ASSERT(Address < MmSystemRangeStart); - ASSERT(PointerPte->u.Soft.Prototype == 1); - ASSERT(PointerPte->u.Soft.PageFileHigh == 0xFFFFF); - ASSERT(Vad == NULL); - - /* Lock the PFN database */ - LockIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); - - /* For the shared data page, this should be true */ - SuperProtoPte = MiAddressToPte(PointerProtoPte); - ASSERT(SuperProtoPte->u.Hard.Valid == 1); - ASSERT(TempPte.u.Hard.Valid == 0); - - /* Resolve the fault -- this will release the PFN lock */ - Status = MiResolveProtoPteFault(StoreInstruction, - Address, - PointerPte, - PointerProtoPte, - NULL, - NULL, - NULL, - Process, - LockIrql, - TrapInformation); - ASSERT(Status == STATUS_SUCCESS); - - /* Complete this as a transition fault */ - ASSERT(OldIrql == KeGetCurrentIrql()); - ASSERT(OldIrql <= APC_LEVEL); - ASSERT(KeAreAllApcsDisabled() == TRUE); - return STATUS_PAGE_FAULT_TRANSITION; + /* Has the PTE been made valid yet? */ + if (!SuperProtoPte->u.Hard.Valid) + { + UNIMPLEMENTED; + while (TRUE); + } + else + { + ASSERT(PointerPte->u.Hard.Valid == 0); + /* Resolve the fault -- this will release the PFN lock */ + Status = MiResolveProtoPteFault(StoreInstruction, + Address, + PointerPte, + PointerProtoPte, + NULL, + NULL, + NULL, + Process, + LockIrql, + TrapInformation); + ASSERT(Status == STATUS_SUCCESS); + + /* Complete this as a transition fault */ + ASSERT(OldIrql == KeGetCurrentIrql()); + ASSERT(OldIrql <= APC_LEVEL); + ASSERT(KeAreAllApcsDisabled() == TRUE); + return Status; + } + } + else + { + /* We currently only handle the shared user data PTE path */ + ASSERT(PointerPte->u.Soft.Prototype == 1); + ASSERT(PointerPte->u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED); + ASSERT(Vad == NULL); + + /* Lock the PFN database */ + LockIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); + + /* For the shared data page, this should be true */ + ASSERT(SuperProtoPte->u.Hard.Valid == 1); + ASSERT(TempPte.u.Hard.Valid == 0); + + /* Resolve the fault -- this will release the PFN lock */ + Status = MiResolveProtoPteFault(StoreInstruction, + Address, + PointerPte, + PointerProtoPte, + NULL, + NULL, + NULL, + Process, + LockIrql, + TrapInformation); + ASSERT(Status == STATUS_SUCCESS); + + /* Complete this as a transition fault */ + ASSERT(OldIrql == KeGetCurrentIrql()); + ASSERT(OldIrql <= APC_LEVEL); + ASSERT(KeAreAllApcsDisabled() == TRUE); + return STATUS_PAGE_FAULT_TRANSITION; + } }
// @@ -469,7 +584,7 @@ IN PVOID TrapInformation) { KIRQL OldIrql = KeGetCurrentIrql(), LockIrql; - PMMPTE PointerPte, ProtoPte; + PMMPTE PointerPte, ProtoPte = NULL; PMMPDE PointerPde; MMPTE TempPte; PETHREAD CurrentThread; @@ -645,9 +760,6 @@ /* Check one kind of prototype PTE */ if (TempPte.u.Soft.Prototype) { - /* The one used for protected pool... */ - ASSERT(MmProtectFreedNonPagedPool == TRUE); - /* Make sure protected pool is on, and that this is a pool address */ if ((MmProtectFreedNonPagedPool) && (((Address >= MmNonPagedPoolStart) && @@ -663,12 +775,41 @@ Mode, 4); } + + /* Get the prototype PTE! */ + ProtoPte = MiProtoPteToPte(&TempPte); }
// // We don't implement transition PTEs // ASSERT(TempPte.u.Soft.Transition == 0); + + /* Check for no-access PTE */ + if (TempPte.u.Soft.Protection == MM_NOACCESS) + { + /* Bad boy, bad boy, whatcha gonna do, whatcha gonna do when ARM3 comes for you! */ + KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA, + (ULONG_PTR)Address, + StoreInstruction, + (ULONG_PTR)TrapInformation, + 1); + } + + /* Check for demand page */ + if ((StoreInstruction) && !(ProtoPte) && !(TempPte.u.Hard.Valid)) + { + /* Get the protection code */ + if (!(TempPte.u.Soft.Protection & MM_READWRITE)) + { + /* Bad boy, bad boy, whatcha gonna do, whatcha gonna do when ARM3 comes for you! */ + KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY, + (ULONG_PTR)Address, + TempPte.u.Long, + (ULONG_PTR)TrapInformation, + 14); + } + }
// // Now do the real fault handling @@ -676,7 +817,7 @@ Status = MiDispatchFault(StoreInstruction, Address, PointerPte, - NULL, + ProtoPte, FALSE, NULL, TrapInformation,