diff --git a/reactos/ntoskrnl/mm/ARM3/pagfault.c b/reactos/ntoskrnl/mm/ARM3/pagfault.c index 34fb242d87e..ac56738feb0 100644 --- a/reactos/ntoskrnl/mm/ARM3/pagfault.c +++ b/reactos/ntoskrnl/mm/ARM3/pagfault.c @@ -34,6 +34,14 @@ MiCheckVirtualAddress(IN PVOID VirtualAddress, /* 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); @@ -242,20 +250,100 @@ MiResolveDemandZeroFault(IN PVOID Address, 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); @@ -263,17 +351,54 @@ MiDispatchFault(IN BOOLEAN StoreInstruction, // // 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 @@ MmArmAccessFault(IN BOOLEAN StoreInstruction, IN PVOID TrapInformation) { KIRQL OldIrql = KeGetCurrentIrql(), LockIrql; - PMMPTE PointerPte; + PMMPTE PointerPte, ProtoPte; PMMPDE PointerPde; MMPTE TempPte; PETHREAD CurrentThread; @@ -562,64 +687,94 @@ MmArmAccessFault(IN BOOLEAN StoreInstruction, 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) + /* Did we get a prototype PTE back? */ + if (!ProtoPte) { - /* User fault, build a user PTE */ - MI_MAKE_HARDWARE_PTE_USER(&TempPte, - PointerPte, - PointerPte->u.Soft.Protection, - PageFrameIndex); + /* 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); + /* 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 */