[NTOS:MM] Fix typo in MiCheckForUserStackOverflow
[reactos.git] / ntoskrnl / mm / ARM3 / pagfault.c
index b99f77e..de64251 100644 (file)
@@ -13,7 +13,7 @@
 #include <debug.h>
 
 #define MODULE_INVOLVED_IN_ARM3
-#include "../ARM3/miarm.h"
+#include <mm/ARM3/miarm.h>
 
 /* GLOBALS ********************************************************************/
 
@@ -24,6 +24,214 @@ BOOLEAN UserPdeFault = FALSE;
 
 /* 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,
@@ -126,33 +334,13 @@ 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;
 
@@ -336,10 +524,77 @@ MiZeroPfn(IN PFN_NUMBER PageFrameNumber)
     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)
 {
@@ -389,12 +644,12 @@ MiResolveDemandZeroFault(IN PVOID Address,
     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 */
@@ -404,8 +659,9 @@ MiResolveDemandZeroFault(IN PVOID Address,
     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)
@@ -438,19 +694,19 @@ MiResolveDemandZeroFault(IN PVOID Address,
     /* 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);
 
@@ -460,7 +716,7 @@ MiResolveDemandZeroFault(IN PVOID Address,
         /* User fault, build a user PTE */
         MI_MAKE_HARDWARE_PTE_USER(&TempPte,
                                   PointerPte,
-                                  PointerPte->u.Soft.Protection,
+                                  Protection,
                                   PageFrameNumber);
     }
     else
@@ -468,7 +724,7 @@ MiResolveDemandZeroFault(IN PVOID Address,
         /* This is a user-mode PDE, create a kernel PTE for it */
         MI_MAKE_HARDWARE_PTE(&TempPte,
                              PointerPte,
-                             PointerPte->u.Soft.Protection,
+                             Protection,
                              PageFrameNumber);
     }
 
@@ -496,6 +752,7 @@ MiResolveDemandZeroFault(IN PVOID Address,
     return STATUS_PAGE_FAULT_DEMAND_ZERO;
 }
 
+static
 NTSTATUS
 NTAPI
 MiCompleteProtoPteFault(IN BOOLEAN StoreInstruction,
@@ -513,7 +770,7 @@ 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 */
@@ -524,11 +781,9 @@ MiCompleteProtoPteFault(IN BOOLEAN StoreInstruction,
     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)
@@ -577,10 +832,10 @@ MiCompleteProtoPteFault(IN BOOLEAN StoreInstruction,
     }
 
     /* 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)
@@ -609,7 +864,7 @@ MiCompleteProtoPteFault(IN BOOLEAN StoreInstruction,
     }
 
     /* 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);
@@ -622,19 +877,107 @@ MiCompleteProtoPteFault(IN BOOLEAN StoreInstruction,
     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);
@@ -650,7 +993,7 @@ MiResolveTransitionFault(IN PVOID FaultingAddress,
 
     /* 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! */
@@ -659,8 +1002,19 @@ MiResolveTransitionFault(IN PVOID FaultingAddress,
     /* 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);
@@ -673,7 +1027,7 @@ MiResolveTransitionFault(IN PVOID FaultingAddress,
     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);
@@ -682,7 +1036,7 @@ MiResolveTransitionFault(IN PVOID FaultingAddress,
     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);
     }
@@ -711,7 +1065,7 @@ MiResolveTransitionFault(IN PVOID FaultingAddress,
         }
     }
 
-    /* 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);
@@ -720,16 +1074,17 @@ MiResolveTransitionFault(IN PVOID FaultingAddress,
                      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 */
@@ -739,6 +1094,7 @@ MiResolveTransitionFault(IN PVOID FaultingAddress,
     return STATUS_PAGE_FAULT_TRANSITION;
 }
 
+static
 NTSTATUS
 NTAPI
 MiResolveProtoPteFault(IN BOOLEAN StoreInstruction,
@@ -756,10 +1112,11 @@ 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);
 
@@ -789,34 +1146,108 @@ MiResolveProtoPteFault(IN BOOLEAN StoreInstruction,
     {
         /* 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 */
@@ -830,7 +1261,8 @@ MiResolveProtoPteFault(IN BOOLEAN StoreInstruction,
     {
         /* Resolve the transition fault */
         ASSERT(OldIrql != MM_NOIRQL);
-        Status = MiResolveTransitionFault(Address,
+        Status = MiResolveTransitionFault(StoreInstruction,
+                                          Address,
                                           PointerProtoPte,
                                           Process,
                                           OldIrql,
@@ -845,6 +1277,7 @@ MiResolveProtoPteFault(IN BOOLEAN StoreInstruction,
         /* Resolve the demand zero fault */
         Status = MiResolveDemandZeroFault(Address,
                                           PointerProtoPte,
+                                          (ULONG)TempPte.u.Soft.Protection,
                                           Process,
                                           OldIrql);
         ASSERT(NT_SUCCESS(Status));
@@ -862,7 +1295,7 @@ MiResolveProtoPteFault(IN BOOLEAN StoreInstruction,
 
 NTSTATUS
 NTAPI
-MiDispatchFault(IN BOOLEAN StoreInstruction,
+MiDispatchFault(IN ULONG FaultCode,
                 IN PVOID Address,
                 IN PMMPTE PointerPte,
                 IN PMMPTE PointerProtoPte,
@@ -908,7 +1341,7 @@ MiDispatchFault(IN BOOLEAN StoreInstruction,
         if (Address >= MmSystemRangeStart)
         {
             /* Lock the PFN database */
-            LockIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
+            LockIrql = MiAcquirePfnLock();
 
             /* Has the PTE been made valid yet? */
             if (!SuperProtoPte->u.Hard.Valid)
@@ -921,7 +1354,7 @@ MiDispatchFault(IN BOOLEAN StoreInstruction,
             }
 
             /* Resolve the fault -- this will release the PFN lock */
-            Status = MiResolveProtoPteFault(StoreInstruction,
+            Status = MiResolveProtoPteFault(!MI_IS_NOT_PRESENT_FAULT(FaultCode),
                                             Address,
                                             PointerPte,
                                             PointerProtoPte,
@@ -959,7 +1392,7 @@ MiDispatchFault(IN BOOLEAN StoreInstruction,
             ProcessedPtes = 0;
 
             /* Lock the PFN database */
-            LockIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
+            LockIrql = MiAcquirePfnLock();
 
             /* We only handle the valid path */
             ASSERT(SuperProtoPte->u.Hard.Valid == 1);
@@ -986,19 +1419,19 @@ MiDispatchFault(IN BOOLEAN StoreInstruction,
                     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;
@@ -1008,19 +1441,20 @@ MiDispatchFault(IN BOOLEAN StoreInstruction,
                     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 */
@@ -1037,7 +1471,7 @@ MiDispatchFault(IN BOOLEAN StoreInstruction,
                 if (++ProcessedPtes == PteCount)
                 {
                     /* Complete the fault */
-                    MiCompleteProtoPteFault(StoreInstruction,
+                    MiCompleteProtoPteFault(!MI_IS_NOT_PRESENT_FAULT(FaultCode),
                                             Address,
                                             PointerPte,
                                             PointerProtoPte,
@@ -1076,7 +1510,7 @@ MiDispatchFault(IN BOOLEAN StoreInstruction,
             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,
@@ -1095,14 +1529,14 @@ MiDispatchFault(IN BOOLEAN StoreInstruction,
             {
                 /* 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 */
@@ -1113,21 +1547,77 @@ MiDispatchFault(IN BOOLEAN StoreInstruction,
         }
     }
 
+    /* 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
@@ -1135,6 +1625,7 @@ MiDispatchFault(IN BOOLEAN StoreInstruction,
     //
     Status = MiResolveDemandZeroFault(Address,
                                       PointerPte,
+                                      (ULONG)TempPte.u.Soft.Protection,
                                       Process,
                                       MM_NOIRQL);
     ASSERT(KeAreAllApcsDisabled() == TRUE);
@@ -1156,7 +1647,7 @@ MiDispatchFault(IN BOOLEAN StoreInstruction,
 
 NTSTATUS
 NTAPI
-MmArmAccessFault(IN BOOLEAN StoreInstruction,
+MmArmAccessFault(IN ULONG FaultCode,
                  IN PVOID Address,
                  IN KPROCESSOR_MODE Mode,
                  IN PVOID TrapInformation)
@@ -1177,7 +1668,7 @@ MmArmAccessFault(IN BOOLEAN StoreInstruction,
     NTSTATUS Status;
     PMMSUPPORT WorkingSet;
     ULONG ProtectionCode;
-    PMMVAD Vad;
+    PMMVAD Vad = NULL;
     PFN_NUMBER PageFrameIndex;
     ULONG Color;
     BOOLEAN IsSessionAddress;
@@ -1200,9 +1691,10 @@ MmArmAccessFault(IN BOOLEAN StoreInstruction,
 #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);
@@ -1217,6 +1709,10 @@ MmArmAccessFault(IN BOOLEAN StoreInstruction,
                 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
             }
 
@@ -1226,10 +1722,10 @@ MmArmAccessFault(IN BOOLEAN StoreInstruction,
 
         /* 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);
@@ -1246,7 +1742,7 @@ MmArmAccessFault(IN BOOLEAN StoreInstruction,
         }
 
         /* 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;
     }
 
@@ -1256,40 +1752,28 @@ MmArmAccessFault(IN BOOLEAN StoreInstruction,
         /* 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);
         }
@@ -1305,12 +1789,12 @@ MmArmAccessFault(IN BOOLEAN StoreInstruction,
             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);
@@ -1325,14 +1809,25 @@ MmArmAccessFault(IN BOOLEAN StoreInstruction,
                                          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))
         {
@@ -1343,11 +1838,16 @@ MmArmAccessFault(IN BOOLEAN StoreInstruction,
                 /* 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))
@@ -1406,13 +1906,13 @@ MmArmAccessFault(IN BOOLEAN StoreInstruction,
         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);
@@ -1426,16 +1926,27 @@ MmArmAccessFault(IN BOOLEAN StoreInstruction,
                 }
             }
 
+            /* 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,
@@ -1471,7 +1982,7 @@ MmArmAccessFault(IN BOOLEAN StoreInstruction,
                 /* 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);
             }
@@ -1501,14 +2012,25 @@ MmArmAccessFault(IN BOOLEAN StoreInstruction,
                 /* 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))
@@ -1527,7 +2049,7 @@ MmArmAccessFault(IN BOOLEAN StoreInstruction,
         }
 
         /* Now do the real fault handling */
-        Status = MiDispatchFault(StoreInstruction,
+        Status = MiDispatchFault(FaultCode,
                                  Address,
                                  PointerPte,
                                  ProtoPte,
@@ -1554,47 +2076,73 @@ UserFault:
     /* 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 */
@@ -1604,7 +2152,12 @@ UserFault:
 #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)
@@ -1619,18 +2172,12 @@ UserFault:
             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
@@ -1644,9 +2191,75 @@ UserFault:
         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))
@@ -1654,6 +2267,7 @@ UserFault:
         /* Resolve the fault */
         MiResolveDemandZeroFault(Address,
                                  PointerPte,
+                                 MM_READWRITE,
                                  CurrentProcess,
                                  MM_NOIRQL);
 
@@ -1681,9 +2295,6 @@ UserFault:
             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
@@ -1694,6 +2305,28 @@ UserFault:
             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)
         {
@@ -1701,16 +2334,22 @@ UserFault:
             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);
@@ -1727,26 +2366,26 @@ UserFault:
                 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)
@@ -1780,19 +2419,19 @@ UserFault:
             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? */
@@ -1830,10 +2469,39 @@ UserFault:
         }
     }
 
-    /* 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,