/* PRIVATE FUNCTIONS **********************************************************/
+PMMPTE
+NTAPI
+MiCheckVirtualAddress(IN PVOID VirtualAddress,
+ OUT PULONG ProtectCode,
+ OUT PMMVAD *ProtoVad)
+{
+ PMMVAD Vad;
+
+ /* No prototype/section support for now */
+ *ProtoVad = NULL;
+
+ /* Only valid for user VADs for now */
+ ASSERT(VirtualAddress <= MM_HIGHEST_USER_ADDRESS);
+
+ /* Find the VAD, it must exist, since we only handle PEB/TEB */
+ Vad = MiLocateAddress(VirtualAddress);
+ ASSERT(Vad);
+
+ /* This must be a TEB/PEB VAD */
+ ASSERT(Vad->u.VadFlags.PrivateMemory == TRUE);
+ ASSERT(Vad->u.VadFlags.MemCommit == TRUE);
+ ASSERT(Vad->u.VadFlags.VadType == VadNone);
+
+ /* Return the protection on it */
+ *ProtectCode = Vad->u.VadFlags.Protection;
+ return NULL;
+}
+
NTSTATUS
FASTCALL
MiCheckPdeForPagedPool(IN PVOID Address)
//
// Check if this is a fault while trying to access the page table itself
//
- if ((Address >= (PVOID)MiAddressToPte(MmSystemRangeStart)) &&
- (Address < (PVOID)PTE_TOP))
+ if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address))
{
//
// Send a hint to the page fault handler that this is only a valid fault
//
if (PointerPde->u.Hard.Valid == 0)
{
+#ifndef _M_AMD64
/* This seems to be making the assumption that one PDE is one page long */
C_ASSERT(PAGE_SIZE == (PD_COUNT * (sizeof(MMPTE) * PDE_COUNT)));
+#endif
//
// Copy it from our double-mapped system page directory
return Status;
}
+VOID
+NTAPI
+MiZeroPfn(IN PFN_NUMBER PageFrameNumber)
+{
+ PMMPTE ZeroPte;
+ MMPTE TempPte;
+ PMMPFN Pfn1;
+ PVOID ZeroAddress;
+
+ /* Get the PFN for this page */
+ Pfn1 = MiGetPfnEntry(PageFrameNumber);
+ ASSERT(Pfn1);
+
+ /* Grab a system PTE we can use to zero the page */
+ ZeroPte = MiReserveSystemPtes(1, SystemPteSpace);
+ ASSERT(ZeroPte);
+
+ /* Initialize the PTE for it */
+ TempPte = ValidKernelPte;
+ TempPte.u.Hard.PageFrameNumber = PageFrameNumber;
+
+ /* Setup caching */
+ if (Pfn1->u3.e1.CacheAttribute == MiWriteCombined)
+ {
+ /* Write combining, no caching */
+ MI_PAGE_DISABLE_CACHE(&TempPte);
+ MI_PAGE_WRITE_COMBINED(&TempPte);
+ }
+ else if (Pfn1->u3.e1.CacheAttribute == MiNonCached)
+ {
+ /* Write through, no caching */
+ MI_PAGE_DISABLE_CACHE(&TempPte);
+ MI_PAGE_WRITE_THROUGH(&TempPte);
+ }
+
+ /* Make the system PTE valid with our PFN */
+ MI_WRITE_VALID_PTE(ZeroPte, TempPte);
+
+ /* Get the address it maps to, and zero it out */
+ ZeroAddress = MiPteToAddress(ZeroPte);
+ KeZeroPages(ZeroAddress, PAGE_SIZE);
+
+ /* Now get rid of it */
+ MiReleaseSystemPtes(ZeroPte, 1, SystemPteSpace);
+}
+
NTSTATUS
NTAPI
MiResolveDemandZeroFault(IN PVOID Address,
{
PFN_NUMBER PageFrameNumber;
MMPTE TempPte;
+ BOOLEAN NeedZero = FALSE;
DPRINT("ARM3 Demand Zero Page Fault Handler for address: %p in process: %p\n",
Address,
Process);
- /* Must currently only be called by paging path, for system addresses only */
+ /* Must currently only be called by paging path */
ASSERT(OldIrql == MM_NOIRQL);
- ASSERT(Process == NULL);
+ if (Process)
+ {
+ /* Sanity check */
+ ASSERT(MI_IS_PAGE_TABLE_ADDRESS(PointerPte));
+
+ /* No forking yet */
+ ASSERT(Process->ForkInProgress == NULL);
+
+ /* We'll need a zero page */
+ NeedZero = TRUE;
+ }
//
// Lock the PFN database
//
InterlockedIncrement(&KeGetCurrentPrcb()->MmDemandZeroCount);
- /* Shouldn't see faults for user PTEs yet */
- ASSERT(PointerPte > MiHighestUserPte);
+ /* Zero the page if need be */
+ if (NeedZero) MiZeroPfn(PageFrameNumber);
/* Build the PTE */
- MI_MAKE_HARDWARE_PTE(&TempPte, PointerPte, PointerPte->u.Soft.Protection, PageFrameNumber);
- ASSERT(TempPte.u.Hard.Valid == 1);
- ASSERT(PointerPte->u.Hard.Valid == 0);
- *PointerPte = TempPte;
- ASSERT(PointerPte->u.Hard.Valid == 1);
+ if (PointerPte <= MiHighestUserPte)
+ {
+ /* For user mode */
+ MI_MAKE_HARDWARE_PTE_USER(&TempPte,
+ PointerPte,
+ PointerPte->u.Soft.Protection,
+ PageFrameNumber);
+ }
+ else
+ {
+ /* For kernel mode */
+ MI_MAKE_HARDWARE_PTE(&TempPte,
+ PointerPte,
+ PointerPte->u.Soft.Protection,
+ PageFrameNumber);
+ }
+
+ /* Set it dirty if it's a writable page */
+ if (TempPte.u.Hard.Write) TempPte.u.Hard.Dirty = TRUE;
+ /* Write it */
+ MI_WRITE_VALID_PTE(PointerPte, TempPte);
+
//
// It's all good now
//
PMMPDE PointerPde;
MMPTE TempPte;
PETHREAD CurrentThread;
+ PEPROCESS CurrentProcess;
NTSTATUS Status;
+ PMMSUPPORT WorkingSet;
+ ULONG ProtectionCode;
+ PMMVAD Vad;
+ PFN_NUMBER PageFrameIndex;
DPRINT("ARM3 FAULT AT: %p\n", Address);
//
//
// Check for a fault on the page table or hyperspace itself
//
- if ((Address >= (PVOID)PTE_BASE) && (Address <= MmHyperSpaceEnd))
+ if (MI_IS_PAGE_TABLE_OR_HYPER_ADDRESS(Address))
{
//
// This might happen...not sure yet
return STATUS_ACCESS_VIOLATION;
}
- //
- // Now we must raise to APC_LEVEL and mark the thread as owner
- // We don't actually implement a working set pushlock, so this is only
- // for internal consistency (and blocking APCs)
- //
- KeRaiseIrql(APC_LEVEL, &LockIrql);
+ /* In this path, we are using the system working set */
CurrentThread = PsGetCurrentThread();
- KeEnterGuardedRegion();
- ASSERT((CurrentThread->OwnsSystemWorkingSetExclusive == 0) &&
- (CurrentThread->OwnsSystemWorkingSetShared == 0));
- CurrentThread->OwnsSystemWorkingSetExclusive = 1;
+ WorkingSet = &MmSystemCacheWs;
+
+ /* Acquire it */
+ KeRaiseIrql(APC_LEVEL, &LockIrql);
+ MiLockWorkingSet(CurrentThread, WorkingSet);
//
// Re-read PTE now that the IRQL has been raised
return STATUS_ACCESS_VIOLATION;
}
+ /* Release the working set */
+ MiUnlockWorkingSet(CurrentThread, WorkingSet);
+ KeLowerIrql(LockIrql);
+
//
// Otherwise, the PDE was probably invalid, and all is good now
//
NULL,
TrapInformation,
NULL);
-
- //
- // Re-enable APCs
- //
+
+ /* Release the working set */
ASSERT(KeAreAllApcsDisabled() == TRUE);
- CurrentThread->OwnsSystemWorkingSetExclusive = 0;
- KeLeaveGuardedRegion();
+ MiUnlockWorkingSet(CurrentThread, WorkingSet);
KeLowerIrql(LockIrql);
//
return Status;
}
- //
- // DIE DIE DIE
- //
- DPRINT1("WARNING: USER MODE FAULT IN ARM3???\n");
- return STATUS_ACCESS_VIOLATION;
+ /* This is a user fault */
+ CurrentThread = PsGetCurrentThread();
+ CurrentProcess = PsGetCurrentProcess();
+
+ /* Lock the working set */
+ MiLockProcessWorkingSet(CurrentProcess, CurrentThread);
+
+ /* First things first, is the PDE valid? */
+ ASSERT(PointerPde != MiAddressToPde(PTE_BASE));
+ ASSERT(PointerPde->u.Hard.LargePage == 0);
+ if (PointerPde->u.Hard.Valid == 0)
+ {
+ /* Right now, we only handle scenarios where the PDE is totally empty */
+ ASSERT(PointerPde->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 */
+ ASSERT(ProtectionCode != MM_NOACCESS);
+
+ /* Make the PDE demand-zero */
+ MI_WRITE_INVALID_PTE(PointerPde, DemandZeroPde);
+
+ /* And go dispatch the fault on the PDE. This should handle the demand-zero */
+ Status = MiDispatchFault(TRUE,
+ PointerPte,
+ PointerPde,
+ NULL,
+ FALSE,
+ PsGetCurrentProcess(),
+ TrapInformation,
+ NULL);
+
+ /* We should come back with APCs enabled, and with a valid PDE */
+ ASSERT(KeAreAllApcsDisabled() == TRUE);
+ ASSERT(PointerPde->u.Hard.Valid == 1);
+ }
+
+ /* Now capture the PTE. We only handle cases where it's totally empty */
+ TempPte = *PointerPte;
+ 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 */
+ 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);
+ }
+ 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);
+
+ /* Release the working set */
+ MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
+ return STATUS_PAGE_FAULT_DEMAND_ZERO;
}
/* EOF */