Author: sir_richard
Date: Thu Jul 22 20:52:23 2010
New Revision: 48201
URL:
http://svn.reactos.org/svn/reactos?rev=48201&view=rev
Log:
Note: this patch only implements the code paths, they are not excercised yet.
[NTOS]: Implement handling a very special case of "prototype PTE", the one used
to map the shared user data for user-mode applications.
[NTOS]: MiCheckVirtualAddress detects this (Windows behavior) and returns a prototype PTE
that's marked MM_READONLY. This is our MmSharedUserDataPte from before. This gets sent
to MiDispatchFault which calls MiResolveProtoPteFault to handle it. In turn, this calls
MiCompleteProtoPteFault. All these code paths have heavy ASSERTions to only allow them to
be hit for the shared user data page, however, in the far distant future when prototype
PTEs are used for section objects, we'll at least have the right checks and code flow
(many of these ASSERTions will then have to be removed).
[NTOS]: The end result is that we also now have STATUS_PAGE_FAULT_TRANSITION, not just
STATUS_PAGE_FAULT_DEMAND_ZERO, and that prototype PTEs are somewhat understood and some
assumptions have been removed.
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.…
==============================================================================
--- trunk/reactos/ntoskrnl/mm/ARM3/pagfault.c [iso-8859-1] (original)
+++ trunk/reactos/ntoskrnl/mm/ARM3/pagfault.c [iso-8859-1] Thu Jul 22 20:52:23 2010
@@ -33,6 +33,14 @@
/* Only valid for user VADs for now */
ASSERT(VirtualAddress <= MM_HIGHEST_USER_ADDRESS);
+
+ /* Special case for shared data */
+ if (PAGE_ALIGN(VirtualAddress) == (PVOID)USER_SHARED_DATA)
+ {
+ /* It's a read-only page */
+ *ProtectCode = MM_READONLY;
+ return MmSharedUserDataPte;
+ }
/* Find the VAD, it must exist, since we only handle PEB/TEB */
Vad = MiLocateAddress(VirtualAddress);
@@ -244,18 +252,98 @@
NTSTATUS
NTAPI
+MiCompleteProtoPteFault(IN BOOLEAN StoreInstruction,
+ IN PVOID Address,
+ IN PMMPTE PointerPte,
+ IN PMMPTE PointerProtoPte,
+ IN KIRQL OldIrql,
+ IN PMMPFN Pfn1)
+{
+ MMPTE TempPte;
+ 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);
+
+ /* Release the PFN lock */
+ KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
+
+ /* Build the user PTE */
+ ASSERT(Address < MmSystemRangeStart);
+ MI_MAKE_HARDWARE_PTE_USER(&TempPte, PointerPte, MM_READONLY, PageFrameIndex);
+
+ /* Write the PTE */
+ MI_WRITE_VALID_PTE(PointerPte, TempPte);
+
+ /* Return success */
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+NTAPI
+MiResolveProtoPteFault(IN BOOLEAN StoreInstruction,
+ IN PVOID Address,
+ IN PMMPTE PointerPte,
+ IN PMMPTE PointerProtoPte,
+ IN OUT PMMPFN *OutPfn,
+ OUT PVOID *PageFileData,
+ OUT PMMPTE PteValue,
+ IN PEPROCESS Process,
+ IN KIRQL OldIrql,
+ IN PVOID TrapInformation)
+{
+ MMPTE TempPte;
+ PMMPFN Pfn1;
+ PFN_NUMBER PageFrameIndex;
+
+ /* 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 */
+ 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);
+
+ /* Complete the prototype PTE fault -- this will release the PFN lock */
+ return MiCompleteProtoPteFault(StoreInstruction,
+ Address,
+ PointerPte,
+ PointerProtoPte,
+ OldIrql,
+ NULL);
+}
+
+NTSTATUS
+NTAPI
MiDispatchFault(IN BOOLEAN StoreInstruction,
IN PVOID Address,
IN PMMPTE PointerPte,
- IN PMMPTE PrototypePte,
+ IN PMMPTE PointerProtoPte,
IN BOOLEAN Recursive,
IN PEPROCESS Process,
IN PVOID TrapInformation,
IN PVOID Vad)
{
MMPTE TempPte;
- KIRQL OldIrql;
+ KIRQL OldIrql, LockIrql;
NTSTATUS Status;
+ PMMPTE SuperProtoPte;
DPRINT("ARM3 Page Fault Dispatcher for address: %p in process: %p\n",
Address,
Process);
@@ -263,17 +351,54 @@
//
// Make sure APCs are off and we're not at dispatch
//
- OldIrql = KeGetCurrentIrql ();
+ OldIrql = KeGetCurrentIrql();
ASSERT(OldIrql <= APC_LEVEL);
- ASSERT(KeAreAllApcsDisabled () == TRUE);
+ ASSERT(KeAreAllApcsDisabled() == TRUE);
//
// Grab a copy of the PTE
//
TempPte = *PointerPte;
- /* No prototype */
- ASSERT(PrototypePte == NULL);
+ /* Do we have a prototype PTE? */
+ if (PointerProtoPte)
+ {
+ /* This should never happen */
+ ASSERT(!MI_IS_PHYSICAL_ADDRESS(PointerProtoPte));
+
+ /* 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;
+ }
//
// The PTE must be invalid, but not totally blank
@@ -321,7 +446,7 @@
IN PVOID TrapInformation)
{
KIRQL OldIrql = KeGetCurrentIrql(), LockIrql;
- PMMPTE PointerPte;
+ PMMPTE PointerPte, ProtoPte;
PMMPDE PointerPde;
MMPTE TempPte;
PETHREAD CurrentThread;
@@ -562,64 +687,94 @@
ASSERT(TempPte.u.Long == 0);
/* Check if this address range belongs to a valid allocation (VAD) */
- MiCheckVirtualAddress(Address, &ProtectionCode, &Vad);
-
- /* Right now, we expect a valid protection mask on the VAD */
+ ProtoPte = MiCheckVirtualAddress(Address, &ProtectionCode, &Vad);
ASSERT(ProtectionCode != MM_NOACCESS);
- PointerPte->u.Soft.Protection = ProtectionCode;
-
- /* Lock the PFN database since we're going to grab a page */
- OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
-
- /* Grab a page out of there. Later we should grab a colored zero page */
- PageFrameIndex = MiRemoveAnyPage(0);
- ASSERT(PageFrameIndex);
-
- /* Release the lock since we need to do some zeroing */
- KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
-
- /* Zero out the page, since it's for user-mode */
- MiZeroPfn(PageFrameIndex);
-
- /* Grab the lock again so we can initialize the PFN entry */
- OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
-
- /* Initialize the PFN entry now */
- MiInitializePfn(PageFrameIndex, PointerPte, 1);
-
- /* And we're done with the lock */
- KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
-
- /* One more demand-zero fault */
- InterlockedIncrement(&KeGetCurrentPrcb()->MmDemandZeroCount);
-
- /* Was the fault on an actual user page, or a kernel page for the user? */
- if (PointerPte <= MiHighestUserPte)
- {
- /* User fault, build a user PTE */
- MI_MAKE_HARDWARE_PTE_USER(&TempPte,
- PointerPte,
- PointerPte->u.Soft.Protection,
- PageFrameIndex);
+
+ /* Did we get a prototype PTE back? */
+ if (!ProtoPte)
+ {
+ /* No, create a new PTE. First, write the protection */
+ PointerPte->u.Soft.Protection = ProtectionCode;
+
+ /* Lock the PFN database since we're going to grab a page */
+ OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
+
+ /* Grab a page out of there. Later we should grab a colored zero page */
+ PageFrameIndex = MiRemoveAnyPage(0);
+ ASSERT(PageFrameIndex);
+
+ /* Release the lock since we need to do some zeroing */
+ KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
+
+ /* Zero out the page, since it's for user-mode */
+ MiZeroPfn(PageFrameIndex);
+
+ /* Grab the lock again so we can initialize the PFN entry */
+ OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
+
+ /* Initialize the PFN entry now */
+ MiInitializePfn(PageFrameIndex, PointerPte, 1);
+
+ /* And we're done with the lock */
+ KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
+
+ /* One more demand-zero fault */
+ InterlockedIncrement(&KeGetCurrentPrcb()->MmDemandZeroCount);
+
+ /* Was the fault on an actual user page, or a kernel page for the user? */
+ if (PointerPte <= MiHighestUserPte)
+ {
+ /* User fault, build a user PTE */
+ MI_MAKE_HARDWARE_PTE_USER(&TempPte,
+ PointerPte,
+ PointerPte->u.Soft.Protection,
+ PageFrameIndex);
+ }
+ else
+ {
+ /* Session, kernel, or user PTE, figure it out and build it */
+ MI_MAKE_HARDWARE_PTE(&TempPte,
+ PointerPte,
+ PointerPte->u.Soft.Protection,
+ PageFrameIndex);
+ }
+
+ /* Write the dirty bit for writeable pages */
+ if (TempPte.u.Hard.Write) TempPte.u.Hard.Dirty = TRUE;
+
+ /* And now write down the PTE, making the address valid */
+ MI_WRITE_VALID_PTE(PointerPte, TempPte);
+
+ /* Demand zero */
+ Status = STATUS_PAGE_FAULT_DEMAND_ZERO;
}
else
{
- /* Session, kernel, or user PTE, figure it out and build it */
- MI_MAKE_HARDWARE_PTE(&TempPte,
- PointerPte,
- PointerPte->u.Soft.Protection,
- PageFrameIndex);
- }
-
- /* Write the dirty bit for writeable pages */
- if (TempPte.u.Hard.Write) TempPte.u.Hard.Dirty = TRUE;
-
- /* And now write down the PTE, making the address valid */
- MI_WRITE_VALID_PTE(PointerPte, TempPte);
+ /* The only "prototype PTE" we support is the shared user data path */
+ ASSERT(ProtectionCode == MM_READONLY);
+
+ /* Write the prototype PTE */
+ TempPte = PrototypePte;
+ TempPte.u.Soft.Protection = ProtectionCode;
+ MI_WRITE_INVALID_PTE(PointerPte, TempPte);
+
+ /* Handle the fault */
+ Status = MiDispatchFault(StoreInstruction,
+ Address,
+ PointerPte,
+ ProtoPte,
+ FALSE,
+ CurrentProcess,
+ TrapInformation,
+ Vad);
+ ASSERT(Status == STATUS_PAGE_FAULT_TRANSITION);
+ ASSERT(PointerPte->u.Hard.Valid == 1);
+ ASSERT(PointerPte->u.Hard.PageFrameNumber ==
MmSharedUserDataPte->u.Hard.PageFrameNumber);
+ }
/* Release the working set */
MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
- return STATUS_PAGE_FAULT_DEMAND_ZERO;
+ return Status;
}
/* EOF */