#include <debug.h>
#define MODULE_INVOLVED_IN_ARM3
-#include "../ARM3/miarm.h"
+#include <mm/ARM3/miarm.h>
/* GLOBALS ********************************************************************/
/* PRIVATE FUNCTIONS **********************************************************/
+static
+NTSTATUS
+NTAPI
+MiCheckForUserStackOverflow(IN PVOID Address,
+ IN PVOID TrapInformation)
+{
+ PETHREAD CurrentThread = PsGetCurrentThread();
+ PTEB Teb = CurrentThread->Tcb.Teb;
+ PVOID StackBase, DeallocationStack, NextStackAddress;
+ SIZE_T GuaranteedSize;
+ NTSTATUS Status;
+
+ /* Do we own the address space lock? */
+ if (CurrentThread->AddressSpaceOwner == 1)
+ {
+ /* This isn't valid */
+ DPRINT1("Process owns address space lock\n");
+ ASSERT(KeAreAllApcsDisabled() == TRUE);
+ return STATUS_GUARD_PAGE_VIOLATION;
+ }
+
+ /* Are we attached? */
+ if (KeIsAttachedProcess())
+ {
+ /* This isn't valid */
+ DPRINT1("Process is attached\n");
+ return STATUS_GUARD_PAGE_VIOLATION;
+ }
+
+ /* Read the current settings */
+ StackBase = Teb->NtTib.StackBase;
+ DeallocationStack = Teb->DeallocationStack;
+ GuaranteedSize = Teb->GuaranteedStackBytes;
+ DPRINT("Handling guard page fault with Stacks Addresses 0x%p and 0x%p, guarantee: %lx\n",
+ StackBase, DeallocationStack, GuaranteedSize);
+
+ /* Guarantees make this code harder, for now, assume there aren't any */
+ ASSERT(GuaranteedSize == 0);
+
+ /* So allocate only the minimum guard page size */
+ GuaranteedSize = PAGE_SIZE;
+
+ /* Does this faulting stack address actually exist in the stack? */
+ if ((Address >= StackBase) || (Address < DeallocationStack))
+ {
+ /* That's odd... */
+ DPRINT1("Faulting address outside of stack bounds. Address=%p, StackBase=%p, DeallocationStack=%p\n",
+ Address, StackBase, DeallocationStack);
+ return STATUS_GUARD_PAGE_VIOLATION;
+ }
+
+ /* This is where the stack will start now */
+ NextStackAddress = (PVOID)((ULONG_PTR)PAGE_ALIGN(Address) - GuaranteedSize);
+
+ /* Do we have at least one page between here and the end of the stack? */
+ if (((ULONG_PTR)NextStackAddress - PAGE_SIZE) <= (ULONG_PTR)DeallocationStack)
+ {
+ /* We don't -- Trying to make this guard page valid now */
+ DPRINT1("Close to our death...\n");
+
+ /* Calculate the next memory address */
+ NextStackAddress = (PVOID)((ULONG_PTR)PAGE_ALIGN(DeallocationStack) + GuaranteedSize);
+
+ /* Allocate the memory */
+ Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
+ &NextStackAddress,
+ 0,
+ &GuaranteedSize,
+ MEM_COMMIT,
+ PAGE_READWRITE);
+ if (NT_SUCCESS(Status))
+ {
+ /* Success! */
+ Teb->NtTib.StackLimit = NextStackAddress;
+ }
+ else
+ {
+ DPRINT1("Failed to allocate memory\n");
+ }
+
+ return STATUS_STACK_OVERFLOW;
+ }
+
+ /* Don't handle this flag yet */
+ ASSERT((PsGetCurrentProcess()->Peb->NtGlobalFlag & FLG_DISABLE_STACK_EXTENSION) == 0);
+
+ /* Update the stack limit */
+ Teb->NtTib.StackLimit = (PVOID)((ULONG_PTR)NextStackAddress + GuaranteedSize);
+
+ /* Now move the guard page to the next page */
+ Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
+ &NextStackAddress,
+ 0,
+ &GuaranteedSize,
+ MEM_COMMIT,
+ PAGE_READWRITE | PAGE_GUARD);
+ if ((NT_SUCCESS(Status) || (Status == STATUS_ALREADY_COMMITTED)))
+ {
+ /* We did it! */
+ DPRINT("Guard page handled successfully for %p\n", Address);
+ return STATUS_PAGE_FAULT_GUARD_PAGE;
+ }
+
+ /* Fail, we couldn't move the guard page */
+ DPRINT1("Guard page failure: %lx\n", Status);
+ ASSERT(FALSE);
+ return STATUS_STACK_OVERFLOW;
+}
+
+FORCEINLINE
+BOOLEAN
+MiIsAccessAllowed(
+ _In_ ULONG ProtectionMask,
+ _In_ BOOLEAN Write,
+ _In_ BOOLEAN Execute)
+{
+ #define _BYTE_MASK(Bit0, Bit1, Bit2, Bit3, Bit4, Bit5, Bit6, Bit7) \
+ (Bit0) | ((Bit1) << 1) | ((Bit2) << 2) | ((Bit3) << 3) | \
+ ((Bit4) << 4) | ((Bit5) << 5) | ((Bit6) << 6) | ((Bit7) << 7)
+ static const UCHAR AccessAllowedMask[2][2] =
+ {
+ { // Protect 0 1 2 3 4 5 6 7
+ _BYTE_MASK(0, 1, 1, 1, 1, 1, 1, 1), // READ
+ _BYTE_MASK(0, 0, 1, 1, 0, 0, 1, 1), // EXECUTE READ
+ },
+ {
+ _BYTE_MASK(0, 0, 0, 0, 1, 1, 1, 1), // WRITE
+ _BYTE_MASK(0, 0, 0, 0, 0, 0, 1, 1), // EXECUTE WRITE
+ }
+ };
+
+ /* We want only the lower access bits */
+ ProtectionMask &= MM_PROTECT_ACCESS;
+
+ /* Look it up in the table */
+ return (AccessAllowedMask[Write != 0][Execute != 0] >> ProtectionMask) & 1;
+}
+
+static
+NTSTATUS
+NTAPI
+MiAccessCheck(IN PMMPTE PointerPte,
+ IN BOOLEAN StoreInstruction,
+ IN KPROCESSOR_MODE PreviousMode,
+ IN ULONG_PTR ProtectionMask,
+ IN PVOID TrapFrame,
+ IN BOOLEAN LockHeld)
+{
+ MMPTE TempPte;
+
+ /* Check for invalid user-mode access */
+ if ((PreviousMode == UserMode) && (PointerPte > MiHighestUserPte))
+ {
+ return STATUS_ACCESS_VIOLATION;
+ }
+
+ /* Capture the PTE -- is it valid? */
+ TempPte = *PointerPte;
+ if (TempPte.u.Hard.Valid)
+ {
+ /* Was someone trying to write to it? */
+ if (StoreInstruction)
+ {
+ /* Is it writable?*/
+ if (MI_IS_PAGE_WRITEABLE(&TempPte) ||
+ MI_IS_PAGE_COPY_ON_WRITE(&TempPte))
+ {
+ /* Then there's nothing to worry about */
+ return STATUS_SUCCESS;
+ }
+
+ /* Oops! This isn't allowed */
+ return STATUS_ACCESS_VIOLATION;
+ }
+
+ /* Someone was trying to read from a valid PTE, that's fine too */
+ return STATUS_SUCCESS;
+ }
+
+ /* Check if the protection on the page allows what is being attempted */
+ if (!MiIsAccessAllowed(ProtectionMask, StoreInstruction, FALSE))
+ {
+ return STATUS_ACCESS_VIOLATION;
+ }
+
+ /* Check if this is a guard page */
+ if ((ProtectionMask & MM_PROTECT_SPECIAL) == MM_GUARDPAGE)
+ {
+ ASSERT(ProtectionMask != MM_DECOMMIT);
+
+ /* Attached processes can't expand their stack */
+ if (KeIsAttachedProcess()) return STATUS_ACCESS_VIOLATION;
+
+ /* No support for prototype PTEs yet */
+ ASSERT(TempPte.u.Soft.Prototype == 0);
+
+ /* Remove the guard page bit, and return a guard page violation */
+ TempPte.u.Soft.Protection = ProtectionMask & ~MM_GUARDPAGE;
+ ASSERT(TempPte.u.Long != 0);
+ MI_WRITE_INVALID_PTE(PointerPte, TempPte);
+ return STATUS_GUARD_PAGE_VIOLATION;
+ }
+
+ /* Nothing to do */
+ return STATUS_SUCCESS;
+}
+
+static
PMMPTE
NTAPI
MiCheckVirtualAddress(IN PVOID VirtualAddress,
}
#if (_MI_PAGING_LEVELS == 2)
-BOOLEAN
-FORCEINLINE
-MiSynchronizeSystemPde(PMMPDE PointerPde)
-{
- MMPDE SystemPde;
- ULONG Index;
-
- /* Get the Index from the PDE */
- Index = ((ULONG_PTR)PointerPde & (SYSTEM_PD_SIZE - 1)) / sizeof(MMPTE);
-
- /* Copy the PDE from the double-mapped system page directory */
- SystemPde = MmSystemPagePtes[Index];
- *PointerPde = SystemPde;
-
- /* Make sure we re-read the PDE and PTE */
- KeMemoryBarrierWithoutFence();
-
- /* Return, if we had success */
- return (BOOLEAN)SystemPde.u.Hard.Valid;
-}
-
+static
NTSTATUS
FASTCALL
MiCheckPdeForSessionSpace(IN PVOID Address)
{
MMPTE TempPde;
- PMMPTE PointerPde;
+ PMMPDE PointerPde;
PVOID SessionAddress;
ULONG Index;
MiReleaseSystemPtes(ZeroPte, 1, SystemPteSpace);
}
+VOID
+NTAPI
+MiCopyPfn(
+ _In_ PFN_NUMBER DestPage,
+ _In_ PFN_NUMBER SrcPage)
+{
+ PMMPTE SysPtes;
+ MMPTE TempPte;
+ PMMPFN DestPfn, SrcPfn;
+ PVOID DestAddress;
+ const VOID* SrcAddress;
+
+ /* Get the PFNs */
+ DestPfn = MiGetPfnEntry(DestPage);
+ ASSERT(DestPfn);
+ SrcPfn = MiGetPfnEntry(SrcPage);
+ ASSERT(SrcPfn);
+
+ /* Grab 2 system PTEs */
+ SysPtes = MiReserveSystemPtes(2, SystemPteSpace);
+ ASSERT(SysPtes);
+
+ /* Initialize the destination PTE */
+ TempPte = ValidKernelPte;
+ TempPte.u.Hard.PageFrameNumber = DestPage;
+
+ /* Setup caching */
+ if (DestPfn->u3.e1.CacheAttribute == MiWriteCombined)
+ {
+ /* Write combining, no caching */
+ MI_PAGE_DISABLE_CACHE(&TempPte);
+ MI_PAGE_WRITE_COMBINED(&TempPte);
+ }
+ else if (DestPfn->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(&SysPtes[0], TempPte);
+
+ /* Initialize the source PTE */
+ TempPte = ValidKernelPte;
+ TempPte.u.Hard.PageFrameNumber = SrcPage;
+
+ /* Setup caching */
+ if (SrcPfn->u3.e1.CacheAttribute == MiNonCached)
+ {
+ MI_PAGE_DISABLE_CACHE(&TempPte);
+ }
+
+ /* Make the system PTE valid with our PFN */
+ MI_WRITE_VALID_PTE(&SysPtes[1], TempPte);
+
+ /* Get the addresses and perform the copy */
+ DestAddress = MiPteToAddress(&SysPtes[0]);
+ SrcAddress = MiPteToAddress(&SysPtes[1]);
+ RtlCopyMemory(DestAddress, SrcAddress, PAGE_SIZE);
+
+ /* Now get rid of it */
+ MiReleaseSystemPtes(SysPtes, 2, SystemPteSpace);
+}
+
+static
NTSTATUS
NTAPI
MiResolveDemandZeroFault(IN PVOID Address,
IN PMMPTE PointerPte,
+ IN ULONG Protection,
IN PEPROCESS Process,
IN KIRQL OldIrql)
{
if (OldIrql == MM_NOIRQL)
{
/* Acquire it and remember we should release it after */
- OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
+ OldIrql = MiAcquirePfnLock();
HaveLock = TRUE;
}
/* We either manually locked the PFN DB, or already came with it locked */
- ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
+ MI_ASSERT_PFN_LOCK_HELD();
ASSERT(PointerPte->u.Hard.Valid == 0);
/* Assert we have enough pages */
if (UserPdeFault) MI_SET_USAGE(MI_USAGE_PAGE_TABLE);
if (!UserPdeFault) MI_SET_USAGE(MI_USAGE_DEMAND_ZERO);
#endif
- if (Process) MI_SET_PROCESS2(Process->ImageFileName);
- if (!Process) MI_SET_PROCESS2("Kernel Demand 0");
+ if (Process == HYDRA_PROCESS) MI_SET_PROCESS2("Hydra");
+ else if (Process) MI_SET_PROCESS2(Process->ImageFileName);
+ else MI_SET_PROCESS2("Kernel Demand 0");
/* Do we need a zero page? */
if (Color != 0xFFFFFFFF)
/* Initialize it */
MiInitializePfn(PageFrameNumber, PointerPte, TRUE);
+ /* Increment demand zero faults */
+ KeGetCurrentPrcb()->MmDemandZeroCount++;
+
/* Do we have the lock? */
if (HaveLock)
{
/* Release it */
- KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
+ MiReleasePfnLock(OldIrql);
/* Update performance counters */
if (Process > HYDRA_PROCESS) Process->NumberOfPrivatePages++;
}
- /* Increment demand zero faults */
- InterlockedIncrement(&KeGetCurrentPrcb()->MmDemandZeroCount);
-
/* Zero the page if need be */
if (NeedZero) MiZeroPfn(PageFrameNumber);
/* User fault, build a user PTE */
MI_MAKE_HARDWARE_PTE_USER(&TempPte,
PointerPte,
- PointerPte->u.Soft.Protection,
+ Protection,
PageFrameNumber);
}
else
/* This is a user-mode PDE, create a kernel PTE for it */
MI_MAKE_HARDWARE_PTE(&TempPte,
PointerPte,
- PointerPte->u.Soft.Protection,
+ Protection,
PageFrameNumber);
}
return STATUS_PAGE_FAULT_DEMAND_ZERO;
}
+static
NTSTATUS
NTAPI
MiCompleteProtoPteFault(IN BOOLEAN StoreInstruction,
BOOLEAN OriginalProtection, DirtyPage;
/* Must be called with an valid prototype PTE, with the PFN lock held */
- ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
+ MI_ASSERT_PFN_LOCK_HELD();
ASSERT(PointerProtoPte->u.Hard.Valid == 1);
/* Get the page */
Pfn1->u3.e1.PrototypePte = 1;
/* Increment the share count for the page table */
- // FIXME: This doesn't work because we seem to bump the sharecount to two, and MiDeletePte gets annoyed and ASSERTs.
- // This could be beause MiDeletePte is now being called from strange code in Rosmm
PageTablePte = MiAddressToPte(PointerPte);
Pfn2 = MiGetPfnEntry(PageTablePte->u.Hard.PageFrameNumber);
- //Pfn2->u2.ShareCount++;
+ Pfn2->u2.ShareCount++;
/* Check where we should be getting the protection information from */
if (PointerPte->u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)
}
/* Release the PFN lock */
- KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
+ MiReleasePfnLock(OldIrql);
- /* Remove caching bits */
- Protection &= ~(MM_NOCACHE | MM_NOACCESS);
+ /* Remove special/caching bits */
+ Protection &= ~MM_PROTECT_SPECIAL;
/* Setup caching */
if (Pfn1->u3.e1.CacheAttribute == MiWriteCombined)
}
/* Set the dirty flag if needed */
- if (DirtyPage) TempPte.u.Hard.Dirty = TRUE;
+ if (DirtyPage) MI_MAKE_DIRTY_PAGE(&TempPte);
/* Write the PTE */
MI_WRITE_VALID_PTE(PointerPte, TempPte);
return STATUS_SUCCESS;
}
+static
NTSTATUS
NTAPI
-MiResolveTransitionFault(IN PVOID FaultingAddress,
+MiResolvePageFileFault(_In_ BOOLEAN StoreInstruction,
+ _In_ PVOID FaultingAddress,
+ _In_ PMMPTE PointerPte,
+ _In_ PEPROCESS CurrentProcess,
+ _Inout_ KIRQL *OldIrql)
+{
+ ULONG Color;
+ PFN_NUMBER Page;
+ NTSTATUS Status;
+ MMPTE TempPte = *PointerPte;
+ PMMPFN Pfn1;
+ ULONG PageFileIndex = TempPte.u.Soft.PageFileLow;
+ ULONG_PTR PageFileOffset = TempPte.u.Soft.PageFileHigh;
+ ULONG Protection = TempPte.u.Soft.Protection;
+
+ /* Things we don't support yet */
+ ASSERT(CurrentProcess > HYDRA_PROCESS);
+ ASSERT(*OldIrql != MM_NOIRQL);
+
+ /* We must hold the PFN lock */
+ MI_ASSERT_PFN_LOCK_HELD();
+
+ /* Some sanity checks */
+ ASSERT(TempPte.u.Hard.Valid == 0);
+ ASSERT(TempPte.u.Soft.PageFileHigh != 0);
+ ASSERT(TempPte.u.Soft.PageFileHigh != MI_PTE_LOOKUP_NEEDED);
+
+ /* Get any page, it will be overwritten */
+ Color = MI_GET_NEXT_PROCESS_COLOR(CurrentProcess);
+ Page = MiRemoveAnyPage(Color);
+
+ /* Initialize this PFN */
+ MiInitializePfn(Page, PointerPte, StoreInstruction);
+
+ /* Sets the PFN as being in IO operation */
+ Pfn1 = MI_PFN_ELEMENT(Page);
+ ASSERT(Pfn1->u1.Event == NULL);
+ ASSERT(Pfn1->u3.e1.ReadInProgress == 0);
+ ASSERT(Pfn1->u3.e1.WriteInProgress == 0);
+ Pfn1->u3.e1.ReadInProgress = 1;
+
+ /* We must write the PTE now as the PFN lock will be released while performing the IO operation */
+ MI_MAKE_TRANSITION_PTE(&TempPte, Page, Protection);
+
+ MI_WRITE_INVALID_PTE(PointerPte, TempPte);
+
+ /* Release the PFN lock while we proceed */
+ MiReleasePfnLock(*OldIrql);
+
+ /* Do the paging IO */
+ Status = MiReadPageFile(Page, PageFileIndex, PageFileOffset);
+
+ /* Lock the PFN database again */
+ *OldIrql = MiAcquirePfnLock();
+
+ /* Nobody should have changed that while we were not looking */
+ ASSERT(Pfn1->u3.e1.ReadInProgress == 1);
+ ASSERT(Pfn1->u3.e1.WriteInProgress == 0);
+
+ if (!NT_SUCCESS(Status))
+ {
+ /* Malheur! */
+ ASSERT(FALSE);
+ Pfn1->u4.InPageError = 1;
+ Pfn1->u1.ReadStatus = Status;
+ }
+
+ /* And the PTE can finally be valid */
+ MI_MAKE_HARDWARE_PTE(&TempPte, PointerPte, Protection, Page);
+ MI_WRITE_VALID_PTE(PointerPte, TempPte);
+
+ Pfn1->u3.e1.ReadInProgress = 0;
+ /* Did someone start to wait on us while we proceeded ? */
+ if (Pfn1->u1.Event)
+ {
+ /* Tell them we're done */
+ KeSetEvent(Pfn1->u1.Event, IO_NO_INCREMENT, FALSE);
+ }
+
+ return Status;
+}
+
+static
+NTSTATUS
+NTAPI
+MiResolveTransitionFault(IN BOOLEAN StoreInstruction,
+ IN PVOID FaultingAddress,
IN PMMPTE PointerPte,
IN PEPROCESS CurrentProcess,
IN KIRQL OldIrql,
- OUT PVOID *InPageBlock)
+ OUT PKEVENT **InPageBlock)
{
PFN_NUMBER PageFrameIndex;
PMMPFN Pfn1;
MMPTE TempPte;
PMMPTE PointerToPteForProtoPage;
- DPRINT1("Transition fault on 0x%p with PTE 0x%lx in process %s\n", FaultingAddress, PointerPte, CurrentProcess->ImageFileName);
+ DPRINT("Transition fault on 0x%p with PTE 0x%p in process %s\n",
+ FaultingAddress, PointerPte, CurrentProcess->ImageFileName);
/* Windowss does this check */
ASSERT(*InPageBlock == NULL);
/* Get the PFN and the PFN entry */
PageFrameIndex = TempPte.u.Trans.PageFrameNumber;
- DPRINT1("Transition PFN: %lx\n", PageFrameIndex);
+ DPRINT("Transition PFN: %lx\n", PageFrameIndex);
Pfn1 = MiGetPfnEntry(PageFrameIndex);
/* One more transition fault! */
/* This is from ARM3 -- Windows normally handles this here */
ASSERT(Pfn1->u4.InPageError == 0);
- /* Not supported in ARM3 */
- ASSERT(Pfn1->u3.e1.ReadInProgress == 0);
+ /* See if we should wait before terminating the fault */
+ if ((Pfn1->u3.e1.ReadInProgress == 1)
+ || ((Pfn1->u3.e1.WriteInProgress == 1) && StoreInstruction))
+ {
+ DPRINT1("The page is currently in a page transition !\n");
+ *InPageBlock = &Pfn1->u1.Event;
+ if (PointerPte == Pfn1->PteAddress)
+ {
+ DPRINT1("And this if for this particular PTE.\n");
+ /* The PTE will be made valid by the thread serving the fault */
+ return STATUS_SUCCESS; // FIXME: Maybe something more descriptive
+ }
+ }
/* Windows checks there's some free pages and this isn't an in-page error */
ASSERT(MmAvailablePages > 0);
if (Pfn1->u3.e1.PageLocation == ActiveAndValid)
{
/* All Windows does here is a bunch of sanity checks */
- DPRINT1("Transition in active list\n");
+ DPRINT("Transition in active list\n");
ASSERT((Pfn1->PteAddress >= MiAddressToPte(MmPagedPoolStart)) &&
(Pfn1->PteAddress <= MiAddressToPte(MmPagedPoolEnd)));
ASSERT(Pfn1->u2.ShareCount != 0);
else
{
/* Otherwise, the page is removed from its list */
- DPRINT1("Transition page in free/zero list\n");
+ DPRINT("Transition page in free/zero list\n");
MiUnlinkPageFromList(Pfn1);
MiReferenceUnusedPageAndBumpLockCount(Pfn1);
}
}
}
- /* Build the transition PTE -- maybe a macro? */
+ /* Build the final PTE */
ASSERT(PointerPte->u.Hard.Valid == 0);
ASSERT(PointerPte->u.Trans.Prototype == 0);
ASSERT(PointerPte->u.Trans.Transition == 1);
MiDetermineUserGlobalPteMask(PointerPte);
/* Is the PTE writeable? */
- if (((Pfn1->u3.e1.Modified) && (TempPte.u.Hard.Write)) &&
- (TempPte.u.Hard.CopyOnWrite == 0))
+ if ((Pfn1->u3.e1.Modified) &&
+ MI_IS_PAGE_WRITEABLE(&TempPte) &&
+ !MI_IS_PAGE_COPY_ON_WRITE(&TempPte))
{
/* Make it dirty */
- TempPte.u.Hard.Dirty = TRUE;
+ MI_MAKE_DIRTY_PAGE(&TempPte);
}
else
{
/* Make it clean */
- TempPte.u.Hard.Dirty = FALSE;
+ MI_MAKE_CLEAN_PAGE(&TempPte);
}
/* Write the valid PTE */
return STATUS_PAGE_FAULT_TRANSITION;
}
+static
NTSTATUS
NTAPI
MiResolveProtoPteFault(IN BOOLEAN StoreInstruction,
PMMPFN Pfn1;
PFN_NUMBER PageFrameIndex;
NTSTATUS Status;
- PVOID InPageBlock = NULL;
+ PKEVENT* InPageBlock = NULL;
+ ULONG Protection;
/* Must be called with an invalid, prototype PTE, with the PFN lock held */
- ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
+ MI_ASSERT_PFN_LOCK_HELD();
ASSERT(PointerPte->u.Hard.Valid == 0);
ASSERT(PointerPte->u.Soft.Prototype == 1);
{
/* Release the lock */
DPRINT1("Access on reserved section?\n");
- KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
+ MiReleasePfnLock(OldIrql);
return STATUS_ACCESS_VIOLATION;
}
+ /* There is no such thing as a decommitted prototype PTE */
+ ASSERT(TempPte.u.Long != MmDecommittedPte.u.Long);
+
/* Check for access rights on the PTE proper */
PteContents = *PointerPte;
if (PteContents.u.Soft.PageFileHigh != MI_PTE_LOOKUP_NEEDED)
{
if (!PteContents.u.Proto.ReadOnly)
{
- /* FIXME: CHECK FOR ACCESS */
-
- /* Check for copy on write page */
- if ((TempPte.u.Soft.Protection & MM_WRITECOPY) == MM_WRITECOPY)
- {
- /* Not yet supported */
- ASSERT(FALSE);
- }
+ Protection = TempPte.u.Soft.Protection;
+ }
+ else
+ {
+ Protection = MM_READONLY;
}
+ /* Check for page acess in software */
+ Status = MiAccessCheck(PointerProtoPte,
+ StoreInstruction,
+ KernelMode,
+ TempPte.u.Soft.Protection,
+ TrapInformation,
+ TRUE);
+ ASSERT(Status == STATUS_SUCCESS);
}
else
{
- /* Check for copy on write page */
- if ((PteContents.u.Soft.Protection & MM_WRITECOPY) == MM_WRITECOPY)
+ Protection = PteContents.u.Soft.Protection;
+ }
+
+ /* Check for writing copy on write page */
+ if (((Protection & MM_WRITECOPY) == MM_WRITECOPY) && StoreInstruction)
+ {
+ PFN_NUMBER PageFrameIndex, ProtoPageFrameIndex;
+ ULONG Color;
+
+ /* Resolve the proto fault as if it was a read operation */
+ Status = MiResolveProtoPteFault(FALSE,
+ Address,
+ PointerPte,
+ PointerProtoPte,
+ OutPfn,
+ PageFileData,
+ PteValue,
+ Process,
+ OldIrql,
+ TrapInformation);
+
+ if (!NT_SUCCESS(Status))
{
- /* Not yet supported */
- ASSERT(FALSE);
+ return Status;
}
+
+ /* Lock again the PFN lock, MiResolveProtoPteFault unlocked it */
+ OldIrql = MiAcquirePfnLock();
+
+ /* And re-read the proto PTE */
+ TempPte = *PointerProtoPte;
+ ASSERT(TempPte.u.Hard.Valid == 1);
+ ProtoPageFrameIndex = PFN_FROM_PTE(&TempPte);
+
+ /* Get a new page for the private copy */
+ if (Process > HYDRA_PROCESS)
+ Color = MI_GET_NEXT_PROCESS_COLOR(Process);
+ else
+ Color = MI_GET_NEXT_COLOR();
+
+ PageFrameIndex = MiRemoveAnyPage(Color);
+
+ /* Perform the copy */
+ MiCopyPfn(PageFrameIndex, ProtoPageFrameIndex);
+
+ /* This will drop everything MiResolveProtoPteFault referenced */
+ MiDeletePte(PointerPte, Address, Process, PointerProtoPte);
+
+ /* Because now we use this */
+ Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
+ MiInitializePfn(PageFrameIndex, PointerPte, TRUE);
+
+ /* Fix the protection */
+ Protection &= ~MM_WRITECOPY;
+ Protection |= MM_READWRITE;
+ if (Address < MmSystemRangeStart)
+ {
+ /* Build the user PTE */
+ MI_MAKE_HARDWARE_PTE_USER(&PteContents, PointerPte, Protection, PageFrameIndex);
+ }
+ else
+ {
+ /* Build the kernel PTE */
+ MI_MAKE_HARDWARE_PTE(&PteContents, PointerPte, Protection, PageFrameIndex);
+ }
+
+ /* And finally, write the valid PTE */
+ MI_WRITE_VALID_PTE(PointerPte, PteContents);
+
+ /* The caller expects us to release the PFN lock */
+ MiReleasePfnLock(OldIrql);
+ return Status;
}
/* Check for clone PTEs */
{
/* Resolve the transition fault */
ASSERT(OldIrql != MM_NOIRQL);
- Status = MiResolveTransitionFault(Address,
+ Status = MiResolveTransitionFault(StoreInstruction,
+ Address,
PointerProtoPte,
Process,
OldIrql,
/* Resolve the demand zero fault */
Status = MiResolveDemandZeroFault(Address,
PointerProtoPte,
+ (ULONG)TempPte.u.Soft.Protection,
Process,
OldIrql);
ASSERT(NT_SUCCESS(Status));
NTSTATUS
NTAPI
-MiDispatchFault(IN BOOLEAN StoreInstruction,
+MiDispatchFault(IN ULONG FaultCode,
IN PVOID Address,
IN PMMPTE PointerPte,
IN PMMPTE PointerProtoPte,
if (Address >= MmSystemRangeStart)
{
/* Lock the PFN database */
- LockIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
+ LockIrql = MiAcquirePfnLock();
/* Has the PTE been made valid yet? */
if (!SuperProtoPte->u.Hard.Valid)
}
/* Resolve the fault -- this will release the PFN lock */
- Status = MiResolveProtoPteFault(StoreInstruction,
+ Status = MiResolveProtoPteFault(!MI_IS_NOT_PRESENT_FAULT(FaultCode),
Address,
PointerPte,
PointerProtoPte,
ProcessedPtes = 0;
/* Lock the PFN database */
- LockIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
+ LockIrql = MiAcquirePfnLock();
/* We only handle the valid path */
ASSERT(SuperProtoPte->u.Hard.Valid == 1);
DPRINT("oooh, shiny, a soft fault! 0x%lx\n", PageFrameIndex);
Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
ASSERT(Pfn1->u3.e1.PageLocation != ActiveAndValid);
-
+
/* Should not yet happen in ReactOS */
ASSERT(Pfn1->u3.e1.ReadInProgress == 0);
ASSERT(Pfn1->u4.InPageError == 0);
-
+
/* Get the page */
MiUnlinkPageFromList(Pfn1);
-
+
/* Bump its reference count */
ASSERT(Pfn1->u2.ShareCount == 0);
InterlockedIncrement16((PSHORT)&Pfn1->u3.e2.ReferenceCount);
Pfn1->u2.ShareCount++;
-
+
/* Make it valid again */
/* This looks like another macro.... */
Pfn1->u3.e1.PageLocation = ActiveAndValid;
TempPte.u.Long = (PointerProtoPte->u.Long & ~0xFFF) |
MmProtectToPteMask[PointerProtoPte->u.Trans.Protection];
TempPte.u.Hard.Valid = 1;
- TempPte.u.Hard.Accessed = 1;
-
+ MI_MAKE_ACCESSED_PAGE(&TempPte);
+
/* Is the PTE writeable? */
- if (((Pfn1->u3.e1.Modified) && (TempPte.u.Hard.Write)) &&
- (TempPte.u.Hard.CopyOnWrite == 0))
+ if ((Pfn1->u3.e1.Modified) &&
+ MI_IS_PAGE_WRITEABLE(&TempPte) &&
+ !MI_IS_PAGE_COPY_ON_WRITE(&TempPte))
{
/* Make it dirty */
- TempPte.u.Hard.Dirty = TRUE;
+ MI_MAKE_DIRTY_PAGE(&TempPte);
}
else
{
/* Make it clean */
- TempPte.u.Hard.Dirty = FALSE;
+ MI_MAKE_CLEAN_PAGE(&TempPte);
}
/* Write the valid PTE */
if (++ProcessedPtes == PteCount)
{
/* Complete the fault */
- MiCompleteProtoPteFault(StoreInstruction,
+ MiCompleteProtoPteFault(!MI_IS_NOT_PRESENT_FAULT(FaultCode),
Address,
PointerPte,
PointerProtoPte,
ASSERT(PointerPte->u.Hard.Valid == 0);
/* Resolve the fault -- this will release the PFN lock */
- Status = MiResolveProtoPteFault(StoreInstruction,
+ Status = MiResolveProtoPteFault(!MI_IS_NOT_PRESENT_FAULT(FaultCode),
Address,
PointerPte,
PointerProtoPte,
{
/* We had a locked PFN, so acquire the PFN lock to dereference it */
ASSERT(PointerProtoPte != NULL);
- OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
+ OldIrql = MiAcquirePfnLock();
/* Dereference the locked PFN */
MiDereferencePfnAndDropLockCount(OutPfn);
ASSERT(OutPfn->u3.e2.ReferenceCount >= 1);
/* And now release the lock */
- KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
+ MiReleasePfnLock(OldIrql);
}
/* Complete this as a transition fault */
}
}
+ /* Is this a transition PTE */
+ if (TempPte.u.Soft.Transition)
+ {
+ PKEVENT* InPageBlock = NULL;
+ PKEVENT PreviousPageEvent;
+ KEVENT CurrentPageEvent;
+
+ /* Lock the PFN database */
+ LockIrql = MiAcquirePfnLock();
+
+ /* Resolve */
+ Status = MiResolveTransitionFault(!MI_IS_NOT_PRESENT_FAULT(FaultCode), Address, PointerPte, Process, LockIrql, &InPageBlock);
+
+ ASSERT(NT_SUCCESS(Status));
+
+ if (InPageBlock != NULL)
+ {
+ /* Another thread is reading or writing this page. Put us into the waiting queue. */
+ KeInitializeEvent(&CurrentPageEvent, NotificationEvent, FALSE);
+ PreviousPageEvent = *InPageBlock;
+ *InPageBlock = &CurrentPageEvent;
+ }
+
+ /* And now release the lock and leave*/
+ MiReleasePfnLock(LockIrql);
+
+ if (InPageBlock != NULL)
+ {
+ KeWaitForSingleObject(&CurrentPageEvent, WrPageIn, KernelMode, FALSE, NULL);
+
+ /* Let's the chain go on */
+ if (PreviousPageEvent)
+ {
+ KeSetEvent(PreviousPageEvent, IO_NO_INCREMENT, FALSE);
+ }
+ }
+
+ ASSERT(OldIrql == KeGetCurrentIrql());
+ ASSERT(OldIrql <= APC_LEVEL);
+ ASSERT(KeAreAllApcsDisabled() == TRUE);
+ return Status;
+ }
+
+ /* Should we page the data back in ? */
+ if (TempPte.u.Soft.PageFileHigh != 0)
+ {
+ /* Lock the PFN database */
+ LockIrql = MiAcquirePfnLock();
+
+ /* Resolve */
+ Status = MiResolvePageFileFault(!MI_IS_NOT_PRESENT_FAULT(FaultCode), Address, PointerPte, Process, &LockIrql);
+
+ /* And now release the lock and leave*/
+ MiReleasePfnLock(LockIrql);
+
+ ASSERT(OldIrql == KeGetCurrentIrql());
+ ASSERT(OldIrql <= APC_LEVEL);
+ ASSERT(KeAreAllApcsDisabled() == TRUE);
+ return Status;
+ }
+
//
// The PTE must be invalid but not completely empty. It must also not be a
- // prototype PTE as that scenario should've been handled above. These are
- // all Windows checks
+ // prototype a transition or a paged-out PTE as those scenarii should've been handled above.
+ // These are all Windows checks
//
ASSERT(TempPte.u.Hard.Valid == 0);
ASSERT(TempPte.u.Soft.Prototype == 0);
- ASSERT(TempPte.u.Long != 0);
-
- //
- // No transition or page file software PTEs in ARM3 yet, so this must be a
- // demand zero page. These are all ReactOS checks
- //
ASSERT(TempPte.u.Soft.Transition == 0);
ASSERT(TempPte.u.Soft.PageFileHigh == 0);
+ ASSERT(TempPte.u.Long != 0);
//
// If we got this far, the PTE can only be a demand zero PTE, which is what
//
Status = MiResolveDemandZeroFault(Address,
PointerPte,
+ (ULONG)TempPte.u.Soft.Protection,
Process,
MM_NOIRQL);
ASSERT(KeAreAllApcsDisabled() == TRUE);
NTSTATUS
NTAPI
-MmArmAccessFault(IN BOOLEAN StoreInstruction,
+MmArmAccessFault(IN ULONG FaultCode,
IN PVOID Address,
IN KPROCESSOR_MODE Mode,
IN PVOID TrapInformation)
NTSTATUS Status;
PMMSUPPORT WorkingSet;
ULONG ProtectionCode;
- PMMVAD Vad;
+ PMMVAD Vad = NULL;
PFN_NUMBER PageFrameIndex;
ULONG Color;
BOOLEAN IsSessionAddress;
#if (_MI_PAGING_LEVELS >= 3)
(PointerPpe->u.Hard.Valid == 0) ||
#endif
- (PointerPde->u.Hard.Valid == 0))
+ (PointerPde->u.Hard.Valid == 0) ||
+ (PointerPte->u.Hard.Valid == 0))
{
- /* This fault is not valid, printf out some debugging help */
+ /* This fault is not valid, print out some debugging help */
DbgPrint("MM:***PAGE FAULT AT IRQL > 1 Va %p, IRQL %lx\n",
Address,
OldIrql);
DbgPrint("MM:***RIP %p, EFL %p\n", TrapFrame->Rip, TrapFrame->EFlags);
DbgPrint("MM:***RAX %p, RCX %p RDX %p\n", TrapFrame->Rax, TrapFrame->Rcx, TrapFrame->Rdx);
DbgPrint("MM:***RBX %p, RSI %p RDI %p\n", TrapFrame->Rbx, TrapFrame->Rsi, TrapFrame->Rdi);
+#elif defined(_M_ARM)
+ DbgPrint("MM:***PC %p\n", TrapFrame->Pc);
+ DbgPrint("MM:***R0 %p, R1 %p R2 %p, R3 %p\n", TrapFrame->R0, TrapFrame->R1, TrapFrame->R2, TrapFrame->R3);
+ DbgPrint("MM:***R11 %p, R12 %p SP %p, LR %p\n", TrapFrame->R11, TrapFrame->R12, TrapFrame->Sp, TrapFrame->Lr);
#endif
}
/* Not yet implemented in ReactOS */
ASSERT(MI_IS_PAGE_LARGE(PointerPde) == FALSE);
- ASSERT(((StoreInstruction) && (PointerPte->u.Hard.CopyOnWrite)) == FALSE);
+ ASSERT((!MI_IS_NOT_PRESENT_FAULT(FaultCode) && MI_IS_PAGE_COPY_ON_WRITE(PointerPte)) == FALSE);
/* Check if this was a write */
- if (StoreInstruction)
+ if (MI_IS_WRITE_ACCESS(FaultCode))
{
/* Was it to a read-only page? */
Pfn1 = MI_PFN_ELEMENT(PointerPte->u.Hard.PageFrameNumber);
}
/* Nothing is actually wrong */
- DPRINT1("Fault at IRQL1 is ok\n");
+ DPRINT1("Fault at IRQL %u is ok (%p)\n", OldIrql, Address);
return STATUS_SUCCESS;
}
/* Bail out, if the fault came from user mode */
if (Mode == UserMode) return STATUS_ACCESS_VIOLATION;
-#if (_MI_PAGING_LEVELS == 4)
- /* AMD64 system, check if PXE is invalid */
- if (PointerPxe->u.Hard.Valid == 0)
- {
- KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
- (ULONG_PTR)Address,
- StoreInstruction,
- (ULONG_PTR)TrapInformation,
- 7);
- }
-#endif
-#if (_MI_PAGING_LEVELS == 4)
- /* PAE/AMD64 system, check if PPE is invalid */
- if (PointerPpe->u.Hard.Valid == 0)
- {
- KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
- (ULONG_PTR)Address,
- StoreInstruction,
- (ULONG_PTR)TrapInformation,
- 5);
- }
-#endif
#if (_MI_PAGING_LEVELS == 2)
if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address)) MiSynchronizeSystemPde((PMMPDE)PointerPte);
MiCheckPdeForPagedPool(Address);
#endif
- /* Check if the PDE is invalid */
- if (PointerPde->u.Hard.Valid == 0)
+ /* Check if the higher page table entries are invalid */
+ if (
+#if (_MI_PAGING_LEVELS == 4)
+ /* AMD64 system, check if PXE is invalid */
+ (PointerPxe->u.Hard.Valid == 0) ||
+#endif
+#if (_MI_PAGING_LEVELS >= 3)
+ /* PAE/AMD64 system, check if PPE is invalid */
+ (PointerPpe->u.Hard.Valid == 0) ||
+#endif
+ /* Always check if the PDE is valid */
+ (PointerPde->u.Hard.Valid == 0))
{
- /* PDE (still) not valid, kill the system */
+ /* PXE/PPE/PDE (still) not valid, kill the system */
KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
(ULONG_PTR)Address,
- StoreInstruction,
+ FaultCode,
(ULONG_PTR)TrapInformation,
2);
}
if (!IsSessionAddress)
{
/* Check if the PTE is still valid under PFN lock */
- OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
+ OldIrql = MiAcquirePfnLock();
TempPte = *PointerPte;
if (TempPte.u.Hard.Valid)
{
/* Check if this was a write */
- if (StoreInstruction)
+ if (MI_IS_WRITE_ACCESS(FaultCode))
{
/* Was it to a read-only page? */
Pfn1 = MI_PFN_ELEMENT(PointerPte->u.Hard.PageFrameNumber);
11);
}
}
+
+ /* Check for execution of non-executable memory */
+ if (MI_IS_INSTRUCTION_FETCH(FaultCode) &&
+ !MI_IS_PAGE_EXECUTABLE(&TempPte))
+ {
+ KeBugCheckEx(ATTEMPTED_EXECUTE_OF_NOEXECUTE_MEMORY,
+ (ULONG_PTR)Address,
+ (ULONG_PTR)TempPte.u.Long,
+ (ULONG_PTR)TrapInformation,
+ 1);
+ }
}
/* Release PFN lock and return all good */
- KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
+ MiReleasePfnLock(OldIrql);
return STATUS_SUCCESS;
}
}
-
+#if (_MI_PAGING_LEVELS == 2)
/* Check if this was a session PTE that needs to remap the session PDE */
if (MI_IS_SESSION_PTE(Address))
{
/* It failed, this address is invalid */
KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
(ULONG_PTR)Address,
- StoreInstruction,
+ FaultCode,
(ULONG_PTR)TrapInformation,
6);
}
}
+#else
+
+_WARN("Session space stuff is not implemented yet!")
+
+#endif
/* Check for a fault on the page table or hyperspace */
if (MI_IS_PAGE_TABLE_OR_HYPER_ADDRESS(Address))
if (TempPte.u.Hard.Valid == 1)
{
/* Check if this was a write */
- if (StoreInstruction)
+ if (MI_IS_WRITE_ACCESS(FaultCode))
{
/* Was it to a read-only page that is not copy on write? */
Pfn1 = MI_PFN_ELEMENT(PointerPte->u.Hard.PageFrameNumber);
if (!(TempPte.u.Long & PTE_READWRITE) &&
!(Pfn1->OriginalPte.u.Soft.Protection & MM_READWRITE) &&
- !(TempPte.u.Hard.CopyOnWrite))
+ !MI_IS_PAGE_COPY_ON_WRITE(&TempPte))
{
/* Case not yet handled */
ASSERT(!IsSessionAddress);
}
}
+ /* Check for execution of non-executable memory */
+ if (MI_IS_INSTRUCTION_FETCH(FaultCode) &&
+ !MI_IS_PAGE_EXECUTABLE(&TempPte))
+ {
+ KeBugCheckEx(ATTEMPTED_EXECUTE_OF_NOEXECUTE_MEMORY,
+ (ULONG_PTR)Address,
+ (ULONG_PTR)TempPte.u.Long,
+ (ULONG_PTR)TrapInformation,
+ 2);
+ }
+
/* Check for read-only write in session space */
if ((IsSessionAddress) &&
- (StoreInstruction) &&
- !(TempPte.u.Hard.Write))
+ MI_IS_WRITE_ACCESS(FaultCode) &&
+ !MI_IS_PAGE_WRITEABLE(&TempPte))
{
/* Sanity check */
ASSERT(MI_IS_SESSION_IMAGE_ADDRESS(Address));
/* Was this COW? */
- if (TempPte.u.Hard.CopyOnWrite == 0)
+ if (!MI_IS_PAGE_COPY_ON_WRITE(&TempPte))
{
/* Then this is not allowed */
KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY,
/* Bad boy, bad boy, whatcha gonna do, whatcha gonna do when ARM3 comes for you! */
KeBugCheckEx(DRIVER_CAUGHT_MODIFYING_FREED_POOL,
(ULONG_PTR)Address,
- StoreInstruction,
+ FaultCode,
Mode,
4);
}
/* Bugcheck the system! */
KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
(ULONG_PTR)Address,
- StoreInstruction,
+ FaultCode,
(ULONG_PTR)TrapInformation,
1);
}
+
+ /* Check for no protecton at all */
+ if (TempPte.u.Soft.Protection == MM_ZERO_ACCESS)
+ {
+ /* Bugcheck the system! */
+ KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
+ (ULONG_PTR)Address,
+ FaultCode,
+ (ULONG_PTR)TrapInformation,
+ 0);
+ }
}
/* Check for demand page */
- if ((StoreInstruction) &&
+ if (MI_IS_WRITE_ACCESS(FaultCode) &&
!(ProtoPte) &&
!(IsSessionAddress) &&
!(TempPte.u.Hard.Valid))
}
/* Now do the real fault handling */
- Status = MiDispatchFault(StoreInstruction,
+ Status = MiDispatchFault(FaultCode,
Address,
PointerPte,
ProtoPte,
/* Lock the working set */
MiLockProcessWorkingSet(CurrentProcess, CurrentThread);
+ ProtectionCode = MM_INVALID_PROTECTION;
+
#if (_MI_PAGING_LEVELS == 4)
-// Note to Timo: You should call MiCheckVirtualAddress and also check if it's zero pte
-// also this is missing the page count increment
/* Check if the PXE is valid */
if (PointerPxe->u.Hard.Valid == 0)
{
/* Right now, we only handle scenarios where the PXE is totally empty */
ASSERT(PointerPxe->u.Long == 0);
-#if 0
+
+ /* This is only possible for user mode addresses! */
+ ASSERT(PointerPte <= MiHighestUserPte);
+
+ /* Check if we have a VAD */
+ MiCheckVirtualAddress(Address, &ProtectionCode, &Vad);
+ if (ProtectionCode == MM_NOACCESS)
+ {
+ MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
+ return STATUS_ACCESS_VIOLATION;
+ }
+
/* Resolve a demand zero fault */
- Status = MiResolveDemandZeroFault(PointerPpe,
- MM_READWRITE,
- CurrentProcess,
- MM_NOIRQL);
-#endif
+ MiResolveDemandZeroFault(PointerPpe,
+ PointerPxe,
+ MM_READWRITE,
+ CurrentProcess,
+ MM_NOIRQL);
+
/* We should come back with a valid PXE */
ASSERT(PointerPxe->u.Hard.Valid == 1);
}
#endif
#if (_MI_PAGING_LEVELS >= 3)
-// Note to Timo: You should call MiCheckVirtualAddress and also check if it's zero pte
-// also this is missing the page count increment
/* Check if the PPE is valid */
if (PointerPpe->u.Hard.Valid == 0)
{
/* Right now, we only handle scenarios where the PPE is totally empty */
ASSERT(PointerPpe->u.Long == 0);
-#if 0
+
+ /* This is only possible for user mode addresses! */
+ ASSERT(PointerPte <= MiHighestUserPte);
+
+ /* Check if we have a VAD, unless we did this already */
+ if (ProtectionCode == MM_INVALID_PROTECTION)
+ {
+ MiCheckVirtualAddress(Address, &ProtectionCode, &Vad);
+ }
+
+ if (ProtectionCode == MM_NOACCESS)
+ {
+ MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
+ return STATUS_ACCESS_VIOLATION;
+ }
+
/* Resolve a demand zero fault */
- Status = MiResolveDemandZeroFault(PointerPde,
- MM_READWRITE,
- CurrentProcess,
- MM_NOIRQL);
-#endif
+ MiResolveDemandZeroFault(PointerPde,
+ PointerPpe,
+ MM_READWRITE,
+ CurrentProcess,
+ MM_NOIRQL);
+
/* We should come back with a valid PPE */
ASSERT(PointerPpe->u.Hard.Valid == 1);
}
#endif
- /* Check if the PDE is valid */
+ /* Check if the PDE is invalid */
if (PointerPde->u.Hard.Valid == 0)
{
/* Right now, we only handle scenarios where the PDE is totally empty */
#if MI_TRACE_PFNS
UserPdeFault = TRUE;
#endif
- MiCheckVirtualAddress(Address, &ProtectionCode, &Vad);
+ /* Check if we have a VAD, unless we did this already */
+ if (ProtectionCode == MM_INVALID_PROTECTION)
+ {
+ MiCheckVirtualAddress(Address, &ProtectionCode, &Vad);
+ }
+
if (ProtectionCode == MM_NOACCESS)
{
#if (_MI_PAGING_LEVELS == 2)
return Status;
}
- /* Write a demand-zero PDE */
- MI_WRITE_INVALID_PTE(PointerPde, DemandZeroPde);
-
- /* Dispatch the fault */
- Status = MiDispatchFault(TRUE,
- PointerPte,
+ /* Resolve a demand zero fault */
+ MiResolveDemandZeroFault(PointerPte,
PointerPde,
- NULL,
- FALSE,
- PsGetCurrentProcess(),
- TrapInformation,
- NULL);
+ MM_READWRITE,
+ CurrentProcess,
+ MM_NOIRQL);
#if MI_TRACE_PFNS
UserPdeFault = FALSE;
#endif
ASSERT(MI_IS_PAGE_LARGE(PointerPde) == FALSE);
}
- /* Now capture the PTE. Ignore virtual faults for now */
+ /* Now capture the PTE. */
TempPte = *PointerPte;
- ASSERT(TempPte.u.Hard.Valid == 0);
+
+ /* Check if the PTE is valid */
+ if (TempPte.u.Hard.Valid)
+ {
+ /* Check if this is a write on a readonly PTE */
+ if (MI_IS_WRITE_ACCESS(FaultCode))
+ {
+ /* Is this a copy on write PTE? */
+ if (MI_IS_PAGE_COPY_ON_WRITE(&TempPte))
+ {
+ PFN_NUMBER PageFrameIndex, OldPageFrameIndex;
+ PMMPFN Pfn1;
+
+ LockIrql = MiAcquirePfnLock();
+
+ ASSERT(MmAvailablePages > 0);
+
+ /* Allocate a new page and copy it */
+ PageFrameIndex = MiRemoveAnyPage(MI_GET_NEXT_PROCESS_COLOR(CurrentProcess));
+ OldPageFrameIndex = PFN_FROM_PTE(&TempPte);
+
+ MiCopyPfn(PageFrameIndex, OldPageFrameIndex);
+
+ /* Dereference whatever this PTE is referencing */
+ Pfn1 = MI_PFN_ELEMENT(OldPageFrameIndex);
+ ASSERT(Pfn1->u3.e1.PrototypePte == 1);
+ ASSERT(!MI_IS_PFN_DELETED(Pfn1));
+ ProtoPte = Pfn1->PteAddress;
+ MiDeletePte(PointerPte, Address, CurrentProcess, ProtoPte);
+
+ /* And make a new shiny one with our page */
+ MiInitializePfn(PageFrameIndex, PointerPte, TRUE);
+ TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
+ TempPte.u.Hard.Write = 1;
+ TempPte.u.Hard.CopyOnWrite = 0;
+
+ MI_WRITE_VALID_PTE(PointerPte, TempPte);
+
+ MiReleasePfnLock(LockIrql);
+
+ /* Return the status */
+ MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
+ return STATUS_PAGE_FAULT_COPY_ON_WRITE;
+ }
+
+ /* Is this a read-only PTE? */
+ if (!MI_IS_PAGE_WRITEABLE(&TempPte))
+ {
+ /* Return the status */
+ MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
+ return STATUS_ACCESS_VIOLATION;
+ }
+ }
+
+ /* Check for execution of non-executable memory */
+ if (MI_IS_INSTRUCTION_FETCH(FaultCode) &&
+ !MI_IS_PAGE_EXECUTABLE(&TempPte))
+ {
+ /* Return the status */
+ MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
+ return STATUS_ACCESS_VIOLATION;
+ }
+
+ /* The fault has already been resolved by a different thread */
+ MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
+ return STATUS_SUCCESS;
+ }
/* Quick check for demand-zero */
if (TempPte.u.Long == (MM_READWRITE << MM_PTE_SOFTWARE_PROTECTION_BITS))
/* Resolve the fault */
MiResolveDemandZeroFault(Address,
PointerPte,
+ MM_READWRITE,
CurrentProcess,
MM_NOIRQL);
return Status;
}
- /* No guard page support yet */
- ASSERT((ProtectionCode & MM_DECOMMIT) == 0);
-
/*
* Check if this is a real user-mode address or actually a kernel-mode
* page table for a user mode address
MiIncrementPageTableReferences(Address);
}
+ /* Is this a guard page? */
+ if ((ProtectionCode & MM_PROTECT_SPECIAL) == MM_GUARDPAGE)
+ {
+ /* The VAD protection cannot be MM_DECOMMIT! */
+ ASSERT(ProtectionCode != MM_DECOMMIT);
+
+ /* Remove the bit */
+ TempPte.u.Soft.Protection = ProtectionCode & ~MM_GUARDPAGE;
+ MI_WRITE_INVALID_PTE(PointerPte, TempPte);
+
+ /* Not supported */
+ ASSERT(ProtoPte == NULL);
+ ASSERT(CurrentThread->ApcNeeded == 0);
+
+ /* Drop the working set lock */
+ MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
+ ASSERT(KeGetCurrentIrql() == OldIrql);
+
+ /* Handle stack expansion */
+ return MiCheckForUserStackOverflow(Address, TrapInformation);
+ }
+
/* Did we get a prototype PTE back? */
if (!ProtoPte)
{
if (PointerPde == MiAddressToPde(PTE_BASE))
{
/* Then it's really a demand-zero PDE (on behalf of user-mode) */
+#ifdef _M_ARM
+ _WARN("This is probably completely broken!");
+ MI_WRITE_INVALID_PDE((PMMPDE)PointerPte, DemandZeroPde);
+#else
MI_WRITE_INVALID_PTE(PointerPte, DemandZeroPde);
+#endif
}
else
{
/* No, create a new PTE. First, write the protection */
- PointerPte->u.Soft.Protection = ProtectionCode;
+ TempPte.u.Soft.Protection = ProtectionCode;
+ MI_WRITE_INVALID_PTE(PointerPte, TempPte);
}
/* Lock the PFN database since we're going to grab a page */
- OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
+ OldIrql = MiAcquirePfnLock();
/* Make sure we have enough pages */
ASSERT(MmAvailablePages >= 32);
ASSERT(PageFrameIndex);
/* Release the lock since we need to do some zeroing */
- KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
+ MiReleasePfnLock(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);
+ OldIrql = MiAcquirePfnLock();
}
/* Initialize the PFN entry now */
MiInitializePfn(PageFrameIndex, PointerPte, 1);
- /* And we're done with the lock */
- KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
-
/* Increment the count of pages in the process */
CurrentProcess->NumberOfPrivatePages++;
/* One more demand-zero fault */
- InterlockedIncrement(&KeGetCurrentPrcb()->MmDemandZeroCount);
+ KeGetCurrentPrcb()->MmDemandZeroCount++;
+
+ /* And we're done with the lock */
+ MiReleasePfnLock(OldIrql);
/* Fault on user PDE, or fault on user PTE? */
if (PointerPte <= MiHighestUserPte)
return STATUS_PAGE_FAULT_DEMAND_ZERO;
}
- /* No guard page support yet */
- ASSERT((ProtectionCode & MM_DECOMMIT) == 0);
+ /* We should have a valid protection here */
ASSERT(ProtectionCode != 0x100);
/* Write the prototype PTE */
TempPte = PrototypePte;
TempPte.u.Soft.Protection = ProtectionCode;
+ ASSERT(TempPte.u.Long != 0);
MI_WRITE_INVALID_PTE(PointerPte, TempPte);
}
else
{
/* Get the protection code and check if this is a proto PTE */
- ProtectionCode = TempPte.u.Soft.Protection;
+ ProtectionCode = (ULONG)TempPte.u.Soft.Protection;
if (TempPte.u.Soft.Prototype)
{
/* Do we need to go find the real PTE? */
}
}
- /* FIXME: Run MiAccessCheck */
+ /* Do we have a valid protection code? */
+ if (ProtectionCode != 0x100)
+ {
+ /* Run a software access check first, including to detect guard pages */
+ Status = MiAccessCheck(PointerPte,
+ !MI_IS_NOT_PRESENT_FAULT(FaultCode),
+ Mode,
+ ProtectionCode,
+ TrapInformation,
+ FALSE);
+ if (Status != STATUS_SUCCESS)
+ {
+ /* Not supported */
+ ASSERT(CurrentThread->ApcNeeded == 0);
+
+ /* Drop the working set lock */
+ MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
+ ASSERT(KeGetCurrentIrql() == OldIrql);
+
+ /* Did we hit a guard page? */
+ if (Status == STATUS_GUARD_PAGE_VIOLATION)
+ {
+ /* Handle stack expansion */
+ return MiCheckForUserStackOverflow(Address, TrapInformation);
+ }
+
+ /* Otherwise, fail back to the caller directly */
+ return Status;
+ }
+ }
/* Dispatch the fault */
- Status = MiDispatchFault(StoreInstruction,
+ Status = MiDispatchFault(FaultCode,
Address,
PointerPte,
ProtoPte,