/* 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);
ASSERT(Vad);
return STATUS_PAGE_FAULT_DEMAND_ZERO;
}
+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);
//
// 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
IN PVOID TrapInformation)
{
KIRQL OldIrql = KeGetCurrentIrql(), LockIrql;
- PMMPTE PointerPte;
+ PMMPTE PointerPte, ProtoPte;
PMMPDE PointerPde;
MMPTE TempPte;
PETHREAD CurrentThread;
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);
+ /* Did we get a prototype PTE back? */
+ if (!ProtoPte)
+ {
+ /* No, create a new PTE. First, write the protection */
+ PointerPte->u.Soft.Protection = ProtectionCode;
- /* Grab a page out of there. Later we should grab a colored zero page */
- PageFrameIndex = MiRemoveAnyPage(0);
- ASSERT(PageFrameIndex);
+ /* Lock the PFN database since we're going to grab a page */
+ OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
- /* Release the lock since we need to do some zeroing */
- KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
+ /* Grab a page out of there. Later we should grab a colored zero page */
+ PageFrameIndex = MiRemoveAnyPage(0);
+ ASSERT(PageFrameIndex);
- /* Zero out the page, since it's for user-mode */
- MiZeroPfn(PageFrameIndex);
+ /* Release the lock since we need to do some zeroing */
+ KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
- /* Grab the lock again so we can initialize the PFN entry */
- OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
+ /* Zero out the page, since it's for user-mode */
+ MiZeroPfn(PageFrameIndex);
- /* Initialize the PFN entry now */
- MiInitializePfn(PageFrameIndex, PointerPte, 1);
+ /* Grab the lock again so we can initialize the PFN entry */
+ OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
- /* And we're done with the lock */
- KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
+ /* Initialize the PFN entry now */
+ MiInitializePfn(PageFrameIndex, PointerPte, 1);
- /* One more demand-zero fault */
- InterlockedIncrement(&KeGetCurrentPrcb()->MmDemandZeroCount);
+ /* And we're done with the lock */
+ KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
- /* 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);
+ /* 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);
+ /* 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);
}
-
- /* 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);
/* Release the working set */
MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
- return STATUS_PAGE_FAULT_DEMAND_ZERO;
+ return Status;
}
/* EOF */