[NTOSKRNL]
[reactos.git] / reactos / ntoskrnl / mm / ARM3 / virtual.c
index 3e248ad..fa3e2fc 100644 (file)
@@ -7,6 +7,7 @@
  */
 
 /* INCLUDES *******************************************************************/
+/* So long, and Thanks for All the Fish */
 
 #include <ntoskrnl.h>
 #define NDEBUG
@@ -56,7 +57,7 @@ MiCalculatePageCommitment(IN ULONG_PTR StartingAddress,
     if (Vad->u.VadFlags.MemCommit == 1)
     {
         /* This is a committed VAD, so Assume the whole range is committed */
-        CommittedPages = BYTES_TO_PAGES(EndingAddress - StartingAddress);
+        CommittedPages = (ULONG)BYTES_TO_PAGES(EndingAddress - StartingAddress);
 
         /* Is the PDE demand-zero? */
         PointerPde = MiAddressToPte(PointerPte);
@@ -222,8 +223,8 @@ MiMakeSystemAddressValid(IN PVOID PageTableVirtualAddress,
         /* Release the working set lock */
         MiUnlockProcessWorkingSetForFault(CurrentProcess,
                                           CurrentThread,
-                                          WsSafe,
-                                          WsShared);
+                                          &WsSafe,
+                                          &WsShared);
 
         /* Fault it in */
         Status = MmAccessFault(FALSE, PageTableVirtualAddress, KernelMode, NULL);
@@ -349,26 +350,26 @@ MiDeleteSystemPageableVm(IN PMMPTE PointerPte,
                 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
 
                 /* Destroy the PTE */
-                PointerPte->u.Long = 0;
+                MI_ERASE_PTE(PointerPte);
+            }
+            else
+            {
+                /*
+                 * The only other ARM3 possibility is a demand zero page, which would
+                 * mean freeing some of the paged pool pages that haven't even been
+                 * touched yet, as part of a larger allocation.
+                 *
+                 * Right now, we shouldn't expect any page file information in the PTE
+                 */
+                ASSERT(PointerPte->u.Soft.PageFileHigh == 0);
+
+                /* Destroy the PTE */
+                MI_ERASE_PTE(PointerPte);
             }
 
             /* Actual legitimate pages */
             ActualPages++;
         }
-        else
-        {
-            /*
-             * The only other ARM3 possibility is a demand zero page, which would
-             * mean freeing some of the paged pool pages that haven't even been
-             * touched yet, as part of a larger allocation.
-             *
-             * Right now, we shouldn't expect any page file information in the PTE
-             */
-            ASSERT(PointerPte->u.Soft.PageFileHigh == 0);
-
-            /* Destroy the PTE */
-            PointerPte->u.Long = 0;
-        }
 
         /* Keep going */
         PointerPte++;
@@ -485,7 +486,7 @@ MiDeletePte(IN PMMPTE PointerPte,
     }
 
     /* Destroy the PTE and flush the TLB */
-    PointerPte->u.Long = 0;
+    MI_ERASE_PTE(PointerPte);
     KeFlushCurrentTb();
 }
 
@@ -502,7 +503,6 @@ MiDeleteVirtualAddresses(IN ULONG_PTR Va,
     KIRQL OldIrql;
     BOOLEAN AddressGap = FALSE;
     PSUBSECTION Subsection;
-    PUSHORT UsedPageTableEntries;
 
     /* Get out if this is a fake VAD, RosMm will free the marea pages */
     if ((Vad) && (Vad->u.VadFlags.Spare == 1)) return;
@@ -559,7 +559,6 @@ MiDeleteVirtualAddresses(IN ULONG_PTR Va,
         /* Now we should have a valid PDE, mapped in, and still have some VA */
         ASSERT(PointerPde->u.Hard.Valid == 1);
         ASSERT(Va <= EndingAddress);
-        UsedPageTableEntries = &MmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Va)];
 
         /* Check if this is a section VAD with gaps in it */
         if ((AddressGap) && (LastPrototypePte))
@@ -589,11 +588,10 @@ MiDeleteVirtualAddresses(IN ULONG_PTR Va,
             TempPte = *PointerPte;
             if (TempPte.u.Long)
             {
-                *UsedPageTableEntries -= 1;
-                ASSERT((*UsedPageTableEntries) < PTE_COUNT);
+                MiDecrementPageTableReferences((PVOID)Va);
 
                 /* Check if the PTE is actually mapped in */
-                if (TempPte.u.Long & 0xFFFFFC01)
+                if (MI_IS_MAPPED_PTE(&TempPte))
                 {
                     /* Are we dealing with section VAD? */
                     if ((LastPrototypePte) && (PrototypePte > LastPrototypePte))
@@ -620,7 +618,7 @@ MiDeleteVirtualAddresses(IN ULONG_PTR Va,
                         (TempPte.u.Soft.Prototype == 1))
                     {
                         /* Just nuke it */
-                        PointerPte->u.Long = 0;
+                        MI_ERASE_PTE(PointerPte);
                     }
                     else
                     {
@@ -634,7 +632,7 @@ MiDeleteVirtualAddresses(IN ULONG_PTR Va,
                 else
                 {
                     /* The PTE was never mapped, just nuke it here */
-                    PointerPte->u.Long = 0;
+                    MI_ERASE_PTE(PointerPte);
                 }
             }
 
@@ -651,7 +649,8 @@ MiDeleteVirtualAddresses(IN ULONG_PTR Va,
         /* The PDE should still be valid at this point */
         ASSERT(PointerPde->u.Hard.Valid == 1);
 
-        if (*UsedPageTableEntries == 0)
+        /* Check remaining PTE count (go back 1 page due to above loop) */
+        if (MiQueryPageTableReferences((PVOID)(Va - PAGE_SIZE)) == 0)
         {
             if (PointerPde->u.Long != 0)
             {
@@ -882,7 +881,7 @@ MiDoMappedCopy(IN PEPROCESS SourceProcess,
                 //
                 // Return the error
                 //
-                return STATUS_WORKING_SET_QUOTA;
+                _SEH2_YIELD(return STATUS_WORKING_SET_QUOTA);
             }
 
             //
@@ -1262,6 +1261,11 @@ MiGetPageProtection(IN PMMPTE PointerPte)
 {
     MMPTE TempPte;
     PMMPFN Pfn;
+    PEPROCESS CurrentProcess;
+    PETHREAD CurrentThread;
+    BOOLEAN WsSafe, WsShared;
+    ULONG Protect;
+    KIRQL OldIrql;
     PAGED_CODE();
 
     /* Copy this PTE's contents */
@@ -1271,12 +1275,79 @@ MiGetPageProtection(IN PMMPTE PointerPte)
     ASSERT(TempPte.u.Long);
 
     /* Check for a special prototype format */
-    if (TempPte.u.Soft.Valid == 0 &&
-        TempPte.u.Soft.Prototype == 1)
+    if ((TempPte.u.Soft.Valid == 0) &&
+        (TempPte.u.Soft.Prototype == 1))
     {
-        /* Unsupported now */
-        UNIMPLEMENTED;
-        ASSERT(FALSE);
+        /* Check if the prototype PTE is not yet pointing to a PTE */
+        if (TempPte.u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)
+        {
+            /* The prototype PTE contains the protection */
+            return MmProtectToValue[TempPte.u.Soft.Protection];
+        }
+
+        /* Get a pointer to the underlying shared PTE */
+        PointerPte = MiProtoPteToPte(&TempPte);
+
+        /* Since the PTE we want to read can be paged out at any time, we need
+           to release the working set lock first, so that it can be paged in */
+        CurrentThread = PsGetCurrentThread();
+        CurrentProcess = PsGetCurrentProcess();
+        MiUnlockProcessWorkingSetForFault(CurrentProcess,
+                                          CurrentThread,
+                                          &WsSafe,
+                                          &WsShared);
+
+        /* Now read the PTE value */
+        TempPte = *PointerPte;
+
+        /* Check if that one is invalid */
+        if (!TempPte.u.Hard.Valid)
+        {
+            /* We get the protection directly from this PTE */
+            Protect = MmProtectToValue[TempPte.u.Soft.Protection];
+        }
+        else
+        {
+            /* The PTE is valid, so we might need to get the protection from
+               the PFN. Lock the PFN database */
+            OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
+
+            /* Check if the PDE is still valid */
+            if (MiAddressToPte(PointerPte)->u.Hard.Valid == 0)
+            {
+                /* It's not, make it valid */
+                MiMakeSystemAddressValidPfn(PointerPte, OldIrql);
+            }
+
+            /* Now it's safe to read the PTE value again */
+            TempPte = *PointerPte;
+            ASSERT(TempPte.u.Long != 0);
+
+            /* Check again if the PTE is invalid */
+            if (!TempPte.u.Hard.Valid)
+            {
+                /* The PTE is not valid, so we can use it's protection field */
+                Protect = MmProtectToValue[TempPte.u.Soft.Protection];
+            }
+            else
+            {
+                /* The PTE is valid, so we can find the protection in the
+                   OriginalPte field of the PFN */
+                Pfn = MI_PFN_ELEMENT(TempPte.u.Hard.PageFrameNumber);
+                Protect = MmProtectToValue[Pfn->OriginalPte.u.Soft.Protection];
+            }
+
+            /* Release the PFN database */
+            KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
+        }
+
+        /* Lock the working set again */
+        MiLockProcessWorkingSetForFault(CurrentProcess,
+                                        CurrentThread,
+                                        WsSafe,
+                                        WsShared);
+
+        return Protect;
     }
 
     /* In the easy case of transition or demand zero PTE just return its protection */
@@ -1292,10 +1363,10 @@ MiGetPageProtection(IN PMMPTE PointerPte)
     }
 
     /* This is software PTE */
-    DPRINT1("Prototype PTE: %lx %p\n", TempPte.u.Hard.PageFrameNumber, Pfn);
-    DPRINT1("VA: %p\n", MiPteToAddress(&TempPte));
-    DPRINT1("Mask: %lx\n", TempPte.u.Soft.Protection);
-    DPRINT1("Mask2: %lx\n", Pfn->OriginalPte.u.Soft.Protection);
+    DPRINT("Prototype PTE: %lx %p\n", TempPte.u.Hard.PageFrameNumber, Pfn);
+    DPRINT("VA: %p\n", MiPteToAddress(&TempPte));
+    DPRINT("Mask: %lx\n", TempPte.u.Soft.Protection);
+    DPRINT("Mask2: %lx\n", Pfn->OriginalPte.u.Soft.Protection);
     return MmProtectToValue[TempPte.u.Soft.Protection];
 }
 
@@ -1310,6 +1381,12 @@ MiQueryAddressState(IN PVOID Va,
 
     PMMPTE PointerPte, ProtoPte;
     PMMPDE PointerPde;
+#if (_MI_PAGING_LEVELS >= 3)
+    PMMPPE PointerPpe;
+#endif
+#if (_MI_PAGING_LEVELS >= 4)
+    PMMPXE PointerPxe;
+#endif
     MMPTE TempPte, TempProtoPte;
     BOOLEAN DemandZeroPte = TRUE, ValidPte = FALSE;
     ULONG State = MEM_RESERVE, Protect = 0;
@@ -1322,27 +1399,70 @@ MiQueryAddressState(IN PVOID Va,
     /* Get the PDE and PTE for the address */
     PointerPde = MiAddressToPde(Va);
     PointerPte = MiAddressToPte(Va);
+#if (_MI_PAGING_LEVELS >= 3)
+    PointerPpe = MiAddressToPpe(Va);
+#endif
+#if (_MI_PAGING_LEVELS >= 4)
+    PointerPxe = MiAddressToPxe(Va);
+#endif
 
     /* Return the next range */
     *NextVa = (PVOID)((ULONG_PTR)Va + PAGE_SIZE);
 
-    /* Is the PDE demand-zero? */
-    if (PointerPde->u.Long != 0)
+    do
     {
-        /* It is not. Is it valid? */
+#if (_MI_PAGING_LEVELS >= 4)
+        /* Does the PXE exist? */
+        if (PointerPxe->u.Long == 0)
+        {
+            /* It does not, next range starts at the next PXE */
+            *NextVa = MiPxeToAddress(PointerPxe + 1);
+            break;
+        }
+
+        /* Is the PXE valid? */
+        if (PointerPxe->u.Hard.Valid == 0)
+        {
+            /* Is isn't, fault it in (make the PPE accessible) */
+            MiMakeSystemAddressValid(PointerPpe, TargetProcess);
+        }
+#endif
+#if (_MI_PAGING_LEVELS >= 3)
+        /* Does the PPE exist? */
+        if (PointerPpe->u.Long == 0)
+        {
+            /* It does not, next range starts at the next PPE */
+            *NextVa = MiPpeToAddress(PointerPpe + 1);
+            break;
+        }
+
+        /* Is the PPE valid? */
+        if (PointerPpe->u.Hard.Valid == 0)
+        {
+            /* Is isn't, fault it in (make the PDE accessible) */
+            MiMakeSystemAddressValid(PointerPde, TargetProcess);
+        }
+#endif
+
+        /* Does the PDE exist? */
+        if (PointerPde->u.Long == 0)
+        {
+            /* It does not, next range starts at the next PDE */
+            *NextVa = MiPdeToAddress(PointerPde + 1);
+            break;
+        }
+
+        /* Is the PDE valid? */
         if (PointerPde->u.Hard.Valid == 0)
         {
-            /* Is isn't, fault it in */
-            PointerPte = MiPteToAddress(PointerPde);
+            /* Is isn't, fault it in (make the PTE accessible) */
             MiMakeSystemAddressValid(PointerPte, TargetProcess);
-            ValidPte = TRUE;
         }
-    }
-    else
-    {
-        /* It is, skip it and move to the next PDE */
-        *NextVa = MiPdeToAddress(PointerPde + 1);
-    }
+
+        /* We have a PTE that we can access now! */
+        ValidPte = TRUE;
+
+    } while (FALSE);
 
     /* Is it safe to try reading the PTE? */
     if (ValidPte)
@@ -1532,6 +1652,26 @@ MiQueryMemoryBasicInformation(IN HANDLE ProcessHandle,
         KeStackAttachProcess(&TargetProcess->Pcb, &ApcState);
     }
 
+    /* Lock the address space and make sure the process isn't already dead */
+    MmLockAddressSpace(&TargetProcess->Vm);
+    if (TargetProcess->VmDeleted)
+    {
+        /* Unlock the address space of the process */
+        MmUnlockAddressSpace(&TargetProcess->Vm);
+
+        /* Check if we were attached */
+        if (ProcessHandle != NtCurrentProcess())
+        {
+            /* Detach and dereference the process */
+            KeUnstackDetachProcess(&ApcState);
+            ObDereferenceObject(TargetProcess);
+        }
+
+        /* Bail out */
+        DPRINT1("Process is dying\n");
+        return STATUS_PROCESS_IS_TERMINATING;
+    }
+
     /* Loop the VADs */
     ASSERT(TargetProcess->VadRoot.NumberGenericTableElements);
     if (TargetProcess->VadRoot.NumberGenericTableElements)
@@ -1608,6 +1748,9 @@ MiQueryMemoryBasicInformation(IN HANDLE ProcessHandle,
             MemoryInfo.RegionSize = (PCHAR)MM_HIGHEST_VAD_ADDRESS + 1 - (PCHAR)Address;
         }
 
+        /* Unlock the address space of the process */
+        MmUnlockAddressSpace(&TargetProcess->Vm);
+
         /* Check if we were attached */
         if (ProcessHandle != NtCurrentProcess())
         {
@@ -1662,9 +1805,6 @@ MiQueryMemoryBasicInformation(IN HANDLE ProcessHandle,
         MemoryInfo.Type = MEM_MAPPED;
     }
 
-    /* Lock the address space of the process */
-    MmLockAddressSpace(&TargetProcess->Vm);
-
     /* Find the memory area the specified address belongs to */
     MemoryArea = MmLocateMemoryAreaByAddress(&TargetProcess->Vm, BaseAddress);
     ASSERT(MemoryArea != NULL);
@@ -1673,7 +1813,12 @@ MiQueryMemoryBasicInformation(IN HANDLE ProcessHandle,
     if (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW)
     {
         Status = MmQuerySectionView(MemoryArea, BaseAddress, &MemoryInfo, &ResultLength);
-        ASSERT(NT_SUCCESS(Status));
+        if (!NT_SUCCESS(Status))
+        {
+            DPRINT1("MmQuerySectionView failed. MemoryArea=%p (%p-%p), BaseAddress=%p\n",
+                    MemoryArea, MemoryArea->StartingAddress, MemoryArea->EndingAddress, BaseAddress);
+            NT_ASSERT(NT_SUCCESS(Status));
+        }
     }
     else
     {
@@ -1684,6 +1829,9 @@ MiQueryMemoryBasicInformation(IN HANDLE ProcessHandle,
         MemoryInfo.AllocationProtect = MmProtectToValue[Vad->u.VadFlags.Protection];
         MemoryInfo.Type = MEM_PRIVATE;
 
+        /* Acquire the working set lock (shared is enough) */
+        MiLockProcessWorkingSetShared(TargetProcess, PsGetCurrentThread());
+
         /* Find the largest chunk of memory which has the same state and protection mask */
         MemoryInfo.State = MiQueryAddressState(Address,
                                                Vad,
@@ -1699,6 +1847,16 @@ MiQueryMemoryBasicInformation(IN HANDLE ProcessHandle,
             Address = NextAddress;
         }
 
+        /* Release the working set lock */
+        MiUnlockProcessWorkingSetShared(TargetProcess, PsGetCurrentThread());
+
+        /* Check if we went outside of the VAD */
+         if (((ULONG_PTR)Address >> PAGE_SHIFT) > Vad->EndingVpn)
+         {
+            /* Set the end of the VAD as the end address */
+            Address = (PVOID)((Vad->EndingVpn + 1) << PAGE_SHIFT);
+         }
+
         /* Now that we know the last VA address, calculate the region size */
         MemoryInfo.RegionSize = ((ULONG_PTR)Address - (ULONG_PTR)MemoryInfo.BaseAddress);
     }
@@ -1714,7 +1872,7 @@ MiQueryMemoryBasicInformation(IN HANDLE ProcessHandle,
         ObDereferenceObject(TargetProcess);
     }
 
-    /* Return the data, NtQueryInformation already probed it*/
+    /* Return the data, NtQueryInformation already probed it */
     if (PreviousMode != KernelMode)
     {
         _SEH2_TRY
@@ -1744,7 +1902,7 @@ MiQueryMemoryBasicInformation(IN HANDLE ProcessHandle,
     return Status;
 }
 
-ULONG
+BOOLEAN
 NTAPI
 MiIsEntireRangeCommitted(IN ULONG_PTR StartingAddress,
                          IN ULONG_PTR EndingAddress,
@@ -1879,6 +2037,7 @@ MiProtectVirtualMemory(IN PEPROCESS Process,
     BOOLEAN Committed;
     NTSTATUS Status = STATUS_SUCCESS;
     PETHREAD Thread = PsGetCurrentThread();
+    TABLE_SEARCH_RESULT Result;
 
     /* Calculate base address for the VAD */
     StartingAddress = (ULONG_PTR)PAGE_ALIGN((*BaseAddress));
@@ -1915,10 +2074,11 @@ MiProtectVirtualMemory(IN PEPROCESS Process,
     }
 
     /* Get the VAD for this address range, and make sure it exists */
-    Vad = (PMMVAD)MiCheckForConflictingNode(StartingAddress >> PAGE_SHIFT,
-                                            EndingAddress >> PAGE_SHIFT,
-                                            &Process->VadRoot);
-    if (!Vad)
+    Result = MiCheckForConflictingNode(StartingAddress >> PAGE_SHIFT,
+                                       EndingAddress >> PAGE_SHIFT,
+                                       &Process->VadRoot,
+                                       (PMMADDRESS_NODE*)&Vad);
+    if (Result != TableFoundNode)
     {
         DPRINT("Could not find a VAD for this allocation\n");
         Status = STATUS_CONFLICTING_ADDRESSES;
@@ -2094,7 +2254,8 @@ MiProtectVirtualMemory(IN PEPROCESS Process,
                 ASSERT(PteContents.u.Soft.Transition == 0);
 
                 /* The PTE is already demand-zero, just update the protection mask */
-                PointerPte->u.Soft.Protection = ProtectionMask;
+                PteContents.u.Soft.Protection = ProtectionMask;
+                MI_WRITE_INVALID_PTE(PointerPte, PteContents);
                 ASSERT(PointerPte->u.Long != 0);
             }
 
@@ -2834,6 +2995,258 @@ NtProtectVirtualMemory(IN HANDLE ProcessHandle,
     return Status;
 }
 
+FORCEINLINE
+BOOLEAN
+MI_IS_LOCKED_VA(
+    PMMPFN Pfn1,
+    ULONG LockType)
+{
+    // HACK until we have proper WSLIST support
+    PMMWSLE Wsle = &Pfn1->Wsle;
+
+    if ((LockType & MAP_PROCESS) && (Wsle->u1.e1.LockedInWs))
+        return TRUE;
+    if ((LockType & MAP_SYSTEM) && (Wsle->u1.e1.LockedInMemory))
+        return TRUE;
+
+    return FALSE;
+}
+
+FORCEINLINE
+VOID
+MI_LOCK_VA(
+    PMMPFN Pfn1,
+    ULONG LockType)
+{
+    // HACK until we have proper WSLIST support
+    PMMWSLE Wsle = &Pfn1->Wsle;
+
+    if (!Wsle->u1.e1.LockedInWs &&
+        !Wsle->u1.e1.LockedInMemory)
+    {
+        MiReferenceProbedPageAndBumpLockCount(Pfn1);
+    }
+
+    if (LockType & MAP_PROCESS)
+        Wsle->u1.e1.LockedInWs = 1;
+    if (LockType & MAP_SYSTEM)
+        Wsle->u1.e1.LockedInMemory = 1;
+}
+
+FORCEINLINE
+VOID
+MI_UNLOCK_VA(
+    PMMPFN Pfn1,
+    ULONG LockType)
+{
+    // HACK until we have proper WSLIST support
+    PMMWSLE Wsle = &Pfn1->Wsle;
+
+    if (LockType & MAP_PROCESS)
+        Wsle->u1.e1.LockedInWs = 0;
+    if (LockType & MAP_SYSTEM)
+        Wsle->u1.e1.LockedInMemory = 0;
+
+    if (!Wsle->u1.e1.LockedInWs &&
+        !Wsle->u1.e1.LockedInMemory)
+    {
+        MiDereferencePfnAndDropLockCount(Pfn1);
+    }
+}
+
+static
+NTSTATUS
+MiCheckVadsForLockOperation(
+    _Inout_ PVOID *BaseAddress,
+    _Inout_ PSIZE_T RegionSize,
+    _Inout_ PVOID *EndAddress)
+
+{
+    PMMVAD Vad;
+    PVOID CurrentVa;
+
+    /* Get the base address and align the start address */
+    *EndAddress = (PUCHAR)*BaseAddress + *RegionSize;
+    *EndAddress = ALIGN_UP_POINTER_BY(*EndAddress, PAGE_SIZE);
+    *BaseAddress = ALIGN_DOWN_POINTER_BY(*BaseAddress, PAGE_SIZE);
+
+    /* First loop and check all VADs */
+    CurrentVa = *BaseAddress;
+    while (CurrentVa < *EndAddress)
+    {
+        /* Get VAD */
+        Vad = MiLocateAddress(CurrentVa);
+        if (Vad == NULL)
+        {
+            /// FIXME: this might be a memory area for a section view...
+            return STATUS_ACCESS_VIOLATION;
+        }
+
+        /* Check VAD type */
+        if ((Vad->u.VadFlags.VadType != VadNone) &&
+            (Vad->u.VadFlags.VadType != VadImageMap) &&
+            (Vad->u.VadFlags.VadType != VadWriteWatch))
+        {
+            *EndAddress = CurrentVa;
+            *RegionSize = (PUCHAR)*EndAddress - (PUCHAR)*BaseAddress;
+            return STATUS_INCOMPATIBLE_FILE_MAP;
+        }
+
+        CurrentVa = (PVOID)((Vad->EndingVpn + 1) << PAGE_SHIFT);
+    }
+
+    *RegionSize = (PUCHAR)*EndAddress - (PUCHAR)*BaseAddress;
+    return STATUS_SUCCESS;
+}
+
+static
+NTSTATUS
+MiLockVirtualMemory(
+    IN OUT PVOID *BaseAddress,
+    IN OUT PSIZE_T RegionSize,
+    IN ULONG MapType)
+{
+    PEPROCESS CurrentProcess;
+    PMMSUPPORT AddressSpace;
+    PVOID CurrentVa, EndAddress;
+    PMMPTE PointerPte, LastPte;
+    PMMPDE PointerPde;
+#if (_MI_PAGING_LEVELS >= 3)
+    PMMPDE PointerPpe;
+#endif
+#if (_MI_PAGING_LEVELS == 4)
+    PMMPDE PointerPxe;
+#endif
+    PMMPFN Pfn1;
+    NTSTATUS Status, TempStatus;
+
+    /* Lock the address space */
+    AddressSpace = MmGetCurrentAddressSpace();
+    MmLockAddressSpace(AddressSpace);
+
+    /* Make sure we still have an address space */
+    CurrentProcess = PsGetCurrentProcess();
+    if (CurrentProcess->VmDeleted)
+    {
+        Status = STATUS_PROCESS_IS_TERMINATING;
+        goto Cleanup;
+    }
+
+    /* Check the VADs in the requested range */
+    Status = MiCheckVadsForLockOperation(BaseAddress, RegionSize, &EndAddress);
+    if (!NT_SUCCESS(Status))
+    {
+        goto Cleanup;
+    }
+
+    /* Enter SEH for probing */
+    _SEH2_TRY
+    {
+        /* Loop all pages and probe them */
+        CurrentVa = *BaseAddress;
+        while (CurrentVa < EndAddress)
+        {
+            (void)(*(volatile CHAR*)CurrentVa);
+            CurrentVa = (PUCHAR)CurrentVa + PAGE_SIZE;
+        }
+    }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+    {
+        Status = _SEH2_GetExceptionCode();
+        goto Cleanup;
+    }
+    _SEH2_END;
+
+    /* All pages were accessible, since we hold the address space lock, nothing
+       can be de-committed. Assume success for now. */
+    Status = STATUS_SUCCESS;
+
+    /* Get the PTE and PDE */
+    PointerPte = MiAddressToPte(*BaseAddress);
+    PointerPde = MiAddressToPde(*BaseAddress);
+#if (_MI_PAGING_LEVELS >= 3)
+    PointerPpe = MiAddressToPpe(*BaseAddress);
+#endif
+#if (_MI_PAGING_LEVELS == 4)
+    PointerPxe = MiAddressToPxe(*BaseAddress);
+#endif
+
+    /* Get the last PTE */
+    LastPte = MiAddressToPte((PVOID)((ULONG_PTR)EndAddress - 1));
+
+    /* Lock the process working set */
+    MiLockProcessWorkingSet(CurrentProcess, PsGetCurrentThread());
+
+    /* Loop the pages */
+    do
+    {
+        /* Check for a page that is not accessible */
+        while (
+#if (_MI_PAGING_LEVELS == 4)
+               (PointerPxe->u.Hard.Valid == 0) ||
+#endif
+#if (_MI_PAGING_LEVELS >= 3)
+               (PointerPpe->u.Hard.Valid == 0) ||
+#endif
+               (PointerPde->u.Hard.Valid == 0) ||
+               (PointerPte->u.Hard.Valid == 0))
+        {
+            /* Release process working set */
+            MiUnlockProcessWorkingSet(CurrentProcess, PsGetCurrentThread());
+
+            /* Access the page */
+            CurrentVa = MiPteToAddress(PointerPte);
+
+            //HACK: Pass a placeholder TrapInformation so the fault handler knows we're unlocked
+            TempStatus = MmAccessFault(TRUE, CurrentVa, KernelMode, (PVOID)0xBADBADA3);
+            if (!NT_SUCCESS(TempStatus))
+            {
+                // This should only happen, when remote backing storage is not accessible
+                ASSERT(FALSE);
+                Status = TempStatus;
+                goto Cleanup;
+            }
+
+            /* Lock the process working set */
+            MiLockProcessWorkingSet(CurrentProcess, PsGetCurrentThread());
+        }
+
+        /* Get the PFN */
+        Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(PointerPte));
+        ASSERT(Pfn1 != NULL);
+
+        /* Check the previous lock status */
+        if (MI_IS_LOCKED_VA(Pfn1, MapType))
+        {
+            Status = STATUS_WAS_LOCKED;
+        }
+
+        /* Lock it */
+        MI_LOCK_VA(Pfn1, MapType);
+
+        /* Go to the next PTE */
+        PointerPte++;
+
+        /* Check if we're on a PDE boundary */
+        if (MiIsPteOnPdeBoundary(PointerPte)) PointerPde++;
+#if (_MI_PAGING_LEVELS >= 3)
+        if (MiIsPteOnPpeBoundary(PointerPte)) PointerPpe++;
+#endif
+#if (_MI_PAGING_LEVELS == 4)
+        if (MiIsPteOnPxeBoundary(PointerPte)) PointerPxe++;
+#endif
+    } while (PointerPte <= LastPte);
+
+    /* Release process working set */
+    MiUnlockProcessWorkingSet(CurrentProcess, PsGetCurrentThread());
+
+Cleanup:
+    /* Unlock address space */
+    MmUnlockAddressSpace(AddressSpace);
+
+    return Status;
+}
+
 NTSTATUS
 NTAPI
 NtLockVirtualMemory(IN HANDLE ProcessHandle,
@@ -2962,9 +3375,11 @@ NtLockVirtualMemory(IN HANDLE ProcessHandle,
     }
 
     //
-    // Oops :(
+    // Call the internal function
     //
-    UNIMPLEMENTED;
+    Status = MiLockVirtualMemory(&CapturedBaseAddress,
+                                 &CapturedBytesToLock,
+                                 MapType);
 
     //
     // Detach if needed
@@ -2985,7 +3400,7 @@ NtLockVirtualMemory(IN HANDLE ProcessHandle,
         // Return data to user
         //
         *BaseAddress = CapturedBaseAddress;
-        *NumberOfBytesToLock = 0;
+        *NumberOfBytesToLock = CapturedBytesToLock;
     }
     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
     {
@@ -2999,9 +3414,175 @@ NtLockVirtualMemory(IN HANDLE ProcessHandle,
     //
     // Return status
     //
-    return STATUS_SUCCESS;
+    return Status;
 }
 
+
+static
+NTSTATUS
+MiUnlockVirtualMemory(
+    IN OUT PVOID *BaseAddress,
+    IN OUT PSIZE_T RegionSize,
+    IN ULONG MapType)
+{
+    PEPROCESS CurrentProcess;
+    PMMSUPPORT AddressSpace;
+    PVOID EndAddress;
+    PMMPTE PointerPte, LastPte;
+    PMMPDE PointerPde;
+#if (_MI_PAGING_LEVELS >= 3)
+    PMMPDE PointerPpe;
+#endif
+#if (_MI_PAGING_LEVELS == 4)
+    PMMPDE PointerPxe;
+#endif
+    PMMPFN Pfn1;
+    NTSTATUS Status;
+
+    /* Lock the address space */
+    AddressSpace = MmGetCurrentAddressSpace();
+    MmLockAddressSpace(AddressSpace);
+
+    /* Make sure we still have an address space */
+    CurrentProcess = PsGetCurrentProcess();
+    if (CurrentProcess->VmDeleted)
+    {
+        Status = STATUS_PROCESS_IS_TERMINATING;
+        goto Cleanup;
+    }
+
+    /* Check the VADs in the requested range */
+    Status = MiCheckVadsForLockOperation(BaseAddress, RegionSize, &EndAddress);
+
+    /* Note: only bail out, if we hit an area without a VAD. If we hit an
+       incompatible VAD we continue, like Windows does */
+    if (Status == STATUS_ACCESS_VIOLATION)
+    {
+        Status = STATUS_NOT_LOCKED;
+        goto Cleanup;
+    }
+
+    /* Get the PTE and PDE */
+    PointerPte = MiAddressToPte(*BaseAddress);
+    PointerPde = MiAddressToPde(*BaseAddress);
+#if (_MI_PAGING_LEVELS >= 3)
+    PointerPpe = MiAddressToPpe(*BaseAddress);
+#endif
+#if (_MI_PAGING_LEVELS == 4)
+    PointerPxe = MiAddressToPxe(*BaseAddress);
+#endif
+
+    /* Get the last PTE */
+    LastPte = MiAddressToPte((PVOID)((ULONG_PTR)EndAddress - 1));
+
+    /* Lock the process working set */
+    MiLockProcessWorkingSet(CurrentProcess, PsGetCurrentThread());
+
+    /* Loop the pages */
+    do
+    {
+        /* Check for a page that is not present */
+        if (
+#if (_MI_PAGING_LEVELS == 4)
+               (PointerPxe->u.Hard.Valid == 0) ||
+#endif
+#if (_MI_PAGING_LEVELS >= 3)
+               (PointerPpe->u.Hard.Valid == 0) ||
+#endif
+               (PointerPde->u.Hard.Valid == 0) ||
+               (PointerPte->u.Hard.Valid == 0))
+        {
+            /* Remember it, but keep going */
+            Status = STATUS_NOT_LOCKED;
+        }
+        else
+        {
+            /* Get the PFN */
+            Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(PointerPte));
+            ASSERT(Pfn1 != NULL);
+
+            /* Check if all of the requested locks are present */
+            if (((MapType & MAP_SYSTEM) && !MI_IS_LOCKED_VA(Pfn1, MAP_SYSTEM)) ||
+                ((MapType & MAP_PROCESS) && !MI_IS_LOCKED_VA(Pfn1, MAP_PROCESS)))
+            {
+                /* Remember it, but keep going */
+                Status = STATUS_NOT_LOCKED;
+
+                /* Check if no lock is present */
+                if (!MI_IS_LOCKED_VA(Pfn1, MAP_PROCESS | MAP_SYSTEM))
+                {
+                    DPRINT1("FIXME: Should remove the page from WS\n");
+                }
+            }
+        }
+
+        /* Go to the next PTE */
+        PointerPte++;
+
+        /* Check if we're on a PDE boundary */
+        if (MiIsPteOnPdeBoundary(PointerPte)) PointerPde++;
+#if (_MI_PAGING_LEVELS >= 3)
+        if (MiIsPteOnPpeBoundary(PointerPte)) PointerPpe++;
+#endif
+#if (_MI_PAGING_LEVELS == 4)
+        if (MiIsPteOnPxeBoundary(PointerPte)) PointerPxe++;
+#endif
+    } while (PointerPte <= LastPte);
+
+    /* Check if we hit a page that was not locked */
+    if (Status == STATUS_NOT_LOCKED)
+    {
+        goto CleanupWithWsLock;
+    }
+
+    /* All pages in the region were locked, so unlock them all */
+
+    /* Get the PTE and PDE */
+    PointerPte = MiAddressToPte(*BaseAddress);
+    PointerPde = MiAddressToPde(*BaseAddress);
+#if (_MI_PAGING_LEVELS >= 3)
+    PointerPpe = MiAddressToPpe(*BaseAddress);
+#endif
+#if (_MI_PAGING_LEVELS == 4)
+    PointerPxe = MiAddressToPxe(*BaseAddress);
+#endif
+
+    /* Loop the pages */
+    do
+    {
+        /* Unlock it */
+        Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(PointerPte));
+        MI_UNLOCK_VA(Pfn1, MapType);
+
+        /* Go to the next PTE */
+        PointerPte++;
+
+        /* Check if we're on a PDE boundary */
+        if (MiIsPteOnPdeBoundary(PointerPte)) PointerPde++;
+#if (_MI_PAGING_LEVELS >= 3)
+        if (MiIsPteOnPpeBoundary(PointerPte)) PointerPpe++;
+#endif
+#if (_MI_PAGING_LEVELS == 4)
+        if (MiIsPteOnPxeBoundary(PointerPte)) PointerPxe++;
+#endif
+    } while (PointerPte <= LastPte);
+
+    /* Everything is done */
+    Status = STATUS_SUCCESS;
+
+CleanupWithWsLock:
+
+    /* Release process working set */
+    MiUnlockProcessWorkingSet(CurrentProcess, PsGetCurrentThread());
+
+Cleanup:
+    /* Unlock address space */
+    MmUnlockAddressSpace(AddressSpace);
+
+    return Status;
+}
+
+
 NTSTATUS
 NTAPI
 NtUnlockVirtualMemory(IN HANDLE ProcessHandle,
@@ -3130,9 +3711,11 @@ NtUnlockVirtualMemory(IN HANDLE ProcessHandle,
     }
 
     //
-    // Oops :(
+    // Call the internal function
     //
-    UNIMPLEMENTED;
+    Status = MiUnlockVirtualMemory(&CapturedBaseAddress,
+                                   &CapturedBytesToUnlock,
+                                   MapType);
 
     //
     // Detach if needed
@@ -3152,8 +3735,8 @@ NtUnlockVirtualMemory(IN HANDLE ProcessHandle,
         //
         // Return data to user
         //
-        *BaseAddress = PAGE_ALIGN(CapturedBaseAddress);
-        *NumberOfBytesToUnlock = 0;
+        *BaseAddress = CapturedBaseAddress;
+        *NumberOfBytesToUnlock = CapturedBytesToUnlock;
     }
     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
     {
@@ -3322,7 +3905,7 @@ NtGetWriteWatch(IN HANDLE ProcessHandle,
             //
             // Catch illegal base address
             //
-            if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER_2;
+            if (BaseAddress > MM_HIGHEST_USER_ADDRESS) _SEH2_YIELD(return STATUS_INVALID_PARAMETER_2);
 
             //
             // Catch illegal region size
@@ -3332,7 +3915,7 @@ NtGetWriteWatch(IN HANDLE ProcessHandle,
                 //
                 // Fail
                 //
-                return STATUS_INVALID_PARAMETER_3;
+                _SEH2_YIELD(return STATUS_INVALID_PARAMETER_3);
             }
 
             //
@@ -3349,7 +3932,7 @@ NtGetWriteWatch(IN HANDLE ProcessHandle,
             //
             // Must have a count
             //
-            if (CapturedEntryCount == 0) return STATUS_INVALID_PARAMETER_5;
+            if (CapturedEntryCount == 0) _SEH2_YIELD(return STATUS_INVALID_PARAMETER_5);
 
             //
             // Can't be larger than the maximum
@@ -3359,7 +3942,7 @@ NtGetWriteWatch(IN HANDLE ProcessHandle,
                 //
                 // Fail
                 //
-                return STATUS_INVALID_PARAMETER_5;
+                _SEH2_YIELD(return STATUS_INVALID_PARAMETER_5);
             }
 
             //
@@ -3639,11 +4222,11 @@ NtAllocateVirtualMemory(IN HANDLE ProcessHandle,
     PEPROCESS Process;
     PMEMORY_AREA MemoryArea;
     PFN_NUMBER PageCount;
-    PMMVAD Vad, FoundVad;
+    PMMVAD Vad = NULL, FoundVad;
     NTSTATUS Status;
     PMMSUPPORT AddressSpace;
     PVOID PBaseAddress;
-    ULONG_PTR PRegionSize, StartingAddress, EndingAddress;
+    ULONG_PTR PRegionSize, StartingAddress, EndingAddress, HighestAddress;
     PEPROCESS CurrentProcess = PsGetCurrentProcess();
     KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
     PETHREAD CurrentThread = PsGetCurrentThread();
@@ -3652,10 +4235,12 @@ NtAllocateVirtualMemory(IN HANDLE ProcessHandle,
     BOOLEAN Attached = FALSE, ChangeProtection = FALSE;
     MMPTE TempPte;
     PMMPTE PointerPte, PointerPde, LastPte;
+    TABLE_SEARCH_RESULT Result;
+    PMMADDRESS_NODE Parent;
     PAGED_CODE();
 
     /* Check for valid Zero bits */
-    if (ZeroBits > 21)
+    if (ZeroBits > MI_MAX_ZERO_BITS)
     {
         DPRINT1("Too many zero bits\n");
         return STATUS_INVALID_PARAMETER_3;
@@ -3663,7 +4248,7 @@ NtAllocateVirtualMemory(IN HANDLE ProcessHandle,
 
     /* Check for valid Allocation Types */
     if ((AllocationType & ~(MEM_COMMIT | MEM_RESERVE | MEM_RESET | MEM_PHYSICAL |
-                    MEM_TOP_DOWN | MEM_WRITE_WATCH)))
+                    MEM_TOP_DOWN | MEM_WRITE_WATCH | MEM_LARGE_PAGES)))
     {
         DPRINT1("Invalid Allocation Type\n");
         return STATUS_INVALID_PARAMETER_5;
@@ -3708,16 +4293,16 @@ NtAllocateVirtualMemory(IN HANDLE ProcessHandle,
         return STATUS_INVALID_PARAMETER_5;
     }
 
-    /* MEM_PHYSICAL can only be used if MEM_RESERVE is also used */
-    if ((AllocationType & MEM_PHYSICAL) && !(AllocationType & MEM_RESERVE))
-    {
-        DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
-        return STATUS_INVALID_PARAMETER_5;
-    }
-
     /* Check for valid MEM_PHYSICAL usage */
     if (AllocationType & MEM_PHYSICAL)
     {
+        /* MEM_PHYSICAL can only be used if MEM_RESERVE is also used */
+        if (!(AllocationType & MEM_RESERVE))
+        {
+            DPRINT1("MEM_PHYSICAL used without MEM_RESERVE\n");
+            return STATUS_INVALID_PARAMETER_5;
+        }
+
         /* Only these flags are allowed with MEM_PHYSIAL */
         if (AllocationType & ~(MEM_RESERVE | MEM_TOP_DOWN | MEM_PHYSICAL))
         {
@@ -3749,7 +4334,7 @@ NtAllocateVirtualMemory(IN HANDLE ProcessHandle,
         {
             /* Make sure they are writable */
             ProbeForWritePointer(UBaseAddress);
-            ProbeForWriteUlong(URegionSize);
+            ProbeForWriteSize_t(URegionSize);
         }
 
         /* Capture their values */
@@ -3764,7 +4349,7 @@ NtAllocateVirtualMemory(IN HANDLE ProcessHandle,
     _SEH2_END;
 
     /* Make sure the allocation isn't past the VAD area */
-    if (PBaseAddress >= MM_HIGHEST_VAD_ADDRESS)
+    if (PBaseAddress > MM_HIGHEST_VAD_ADDRESS)
     {
         DPRINT1("Virtual allocation base above User Space\n");
         return STATUS_INVALID_PARAMETER_2;
@@ -3829,12 +4414,6 @@ NtAllocateVirtualMemory(IN HANDLE ProcessHandle,
     //
     // Fail on the things we don't yet support
     //
-    if (ZeroBits != 0)
-    {
-        DPRINT1("Zero bits not supported\n");
-        Status = STATUS_INVALID_PARAMETER;
-        goto FailPathNoLock;
-    }
     if ((AllocationType & MEM_LARGE_PAGES) == MEM_LARGE_PAGES)
     {
         DPRINT1("MEM_LARGE_PAGES not supported\n");
@@ -3853,24 +4432,6 @@ NtAllocateVirtualMemory(IN HANDLE ProcessHandle,
         Status = STATUS_INVALID_PARAMETER;
         goto FailPathNoLock;
     }
-    if ((AllocationType & MEM_TOP_DOWN) == MEM_TOP_DOWN)
-    {
-        DPRINT1("MEM_TOP_DOWN not supported\n");
-        Status = STATUS_INVALID_PARAMETER;
-        goto FailPathNoLock;
-    }
-    if ((AllocationType & MEM_RESET) == MEM_RESET)
-    {
-        DPRINT1("MEM_RESET not supported\n");
-        Status = STATUS_INVALID_PARAMETER;
-        goto FailPathNoLock;
-    }
-    if (Process->VmTopDown == 1)
-    {
-        DPRINT1("VmTopDown not supported\n");
-        Status = STATUS_INVALID_PARAMETER;
-        goto FailPathNoLock;
-    }
 
     //
     // Check if the caller is reserving memory, or committing memory and letting
@@ -3881,7 +4442,7 @@ NtAllocateVirtualMemory(IN HANDLE ProcessHandle,
         //
         //  Do not allow COPY_ON_WRITE through this API
         //
-        if ((Protect & PAGE_WRITECOPY) || (Protect & PAGE_EXECUTE_WRITECOPY))
+        if (Protect & (PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY))
         {
             DPRINT1("Copy on write not allowed through this path\n");
             Status = STATUS_INVALID_PAGE_PROTECTION;
@@ -3900,6 +4461,29 @@ NtAllocateVirtualMemory(IN HANDLE ProcessHandle,
             PageCount = BYTES_TO_PAGES(PRegionSize);
             EndingAddress = 0;
             StartingAddress = 0;
+
+            //
+            // Check if ZeroBits were specified
+            //
+            if (ZeroBits != 0)
+            {
+                //
+                // Calculate the highest address and check if it's valid
+                //
+                HighestAddress = MAXULONG_PTR >> ZeroBits;
+                if (HighestAddress > (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS)
+                {
+                    Status = STATUS_INVALID_PARAMETER_3;
+                    goto FailPathNoLock;
+                }
+            }
+            else
+            {
+                //
+                // Use the highest VAD address as maximum
+                //
+                HighestAddress = (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS;
+            }
         }
         else
         {
@@ -3908,8 +4492,8 @@ NtAllocateVirtualMemory(IN HANDLE ProcessHandle,
             // expected 64KB granularity, and see where the ending address will
             // fall based on the aligned address and the passed in region size
             //
-            StartingAddress = ROUND_DOWN((ULONG_PTR)PBaseAddress, _64K);
             EndingAddress = ((ULONG_PTR)PBaseAddress + PRegionSize - 1) | (PAGE_SIZE - 1);
+            StartingAddress = ROUND_DOWN((ULONG_PTR)PBaseAddress, _64K);
             PageCount = BYTES_TO_PAGES(EndingAddress - StartingAddress);
         }
 
@@ -3917,7 +4501,13 @@ NtAllocateVirtualMemory(IN HANDLE ProcessHandle,
         // Allocate and initialize the VAD
         //
         Vad = ExAllocatePoolWithTag(NonPagedPool, sizeof(MMVAD_LONG), 'SdaV');
-        ASSERT(Vad != NULL);
+        if (Vad == NULL)
+        {
+            DPRINT1("Failed to allocate a VAD!\n");
+            Status = STATUS_INSUFFICIENT_RESOURCES;
+            goto FailPathNoLock;
+        }
+
         Vad->u.LongFlags = 0;
         if (AllocationType & MEM_COMMIT) Vad->u.VadFlags.MemCommit = 1;
         Vad->u.VadFlags.Protection = ProtectionMask;
@@ -3943,40 +4533,69 @@ NtAllocateVirtualMemory(IN HANDLE ProcessHandle,
         //
         if (!PBaseAddress)
         {
-            Status = MiFindEmptyAddressRangeInTree(PRegionSize,
-                                                   _64K,
-                                                   &Process->VadRoot,
-                                                   (PMMADDRESS_NODE*)&Process->VadFreeHint,
-                                                   &StartingAddress);
-            if (!NT_SUCCESS(Status)) goto FailPath;
+            /* Which way should we search? */
+            if ((AllocationType & MEM_TOP_DOWN) || Process->VmTopDown)
+            {
+                /* Find an address top-down */
+                Result = MiFindEmptyAddressRangeDownTree(PRegionSize,
+                                                         HighestAddress,
+                                                         _64K,
+                                                         &Process->VadRoot,
+                                                         &StartingAddress,
+                                                         &Parent);
+            }
+            else
+            {
+                /* Find an address bottom-up */
+                Result = MiFindEmptyAddressRangeInTree(PRegionSize,
+                                                       _64K,
+                                                       &Process->VadRoot,
+                                                       &Parent,
+                                                       &StartingAddress);
+            }
+
+            if (Result == TableFoundNode)
+            {
+                Status = STATUS_NO_MEMORY;
+                goto FailPath;
+            }
 
             //
             // Now we know where the allocation ends. Make sure it doesn't end up
             // somewhere in kernel mode.
             //
-            EndingAddress = ((ULONG_PTR)StartingAddress + PRegionSize - 1) | (PAGE_SIZE - 1);
-            if ((PVOID)EndingAddress > MM_HIGHEST_VAD_ADDRESS)
+            ASSERT(StartingAddress != 0);
+            ASSERT(StartingAddress < (ULONG_PTR)MM_HIGHEST_USER_ADDRESS);
+            EndingAddress = (StartingAddress + PRegionSize - 1) | (PAGE_SIZE - 1);
+            ASSERT(EndingAddress > StartingAddress);
+            if (EndingAddress > HighestAddress)
             {
                 Status = STATUS_NO_MEMORY;
                 goto FailPath;
             }
         }
-        else if (MiCheckForConflictingNode(StartingAddress >> PAGE_SHIFT,
-                                           EndingAddress >> PAGE_SHIFT,
-                                           &Process->VadRoot))
+        else
         {
-            //
-            // The address specified is in conflict!
-            //
-            Status = STATUS_CONFLICTING_ADDRESSES;
-            goto FailPath;
+            /* Make sure it doesn't conflict with an existing allocation */
+            Result = MiCheckForConflictingNode(StartingAddress >> PAGE_SHIFT,
+                                               EndingAddress >> PAGE_SHIFT,
+                                               &Process->VadRoot,
+                                               &Parent);
+            if (Result == TableFoundNode)
+            {
+                //
+                // The address specified is in conflict!
+                //
+                Status = STATUS_CONFLICTING_ADDRESSES;
+                goto FailPath;
+            }
         }
 
         //
         // Write out the VAD fields for this allocation
         //
-        Vad->StartingVpn = (ULONG_PTR)StartingAddress >> PAGE_SHIFT;
-        Vad->EndingVpn = (ULONG_PTR)EndingAddress >> PAGE_SHIFT;
+        Vad->StartingVpn = StartingAddress >> PAGE_SHIFT;
+        Vad->EndingVpn = EndingAddress >> PAGE_SHIFT;
 
         //
         // FIXME: Should setup VAD bitmap
@@ -3988,9 +4607,17 @@ NtAllocateVirtualMemory(IN HANDLE ProcessHandle,
         //
         MiLockProcessWorkingSetUnsafe(Process, CurrentThread);
         Vad->ControlArea = NULL; // For Memory-Area hack
-        MiInsertVad(Vad, Process);
+        Process->VadRoot.NodeHint = Vad;
+        MiInsertNode(&Process->VadRoot, (PVOID)Vad, Parent, Result);
         MiUnlockProcessWorkingSetUnsafe(Process, CurrentThread);
 
+        //
+        // Make sure the actual region size is at least as big as the
+        // requested region size and update the value
+        //
+        ASSERT(PRegionSize <= (EndingAddress + 1 - StartingAddress));
+        PRegionSize = (EndingAddress + 1 - StartingAddress);
+
         //
         // Update the virtual size of the process, and if this is now the highest
         // virtual size we have ever seen, update the peak virtual size to reflect
@@ -4024,6 +4651,9 @@ NtAllocateVirtualMemory(IN HANDLE ProcessHandle,
         }
         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
         {
+            //
+            // Ignore exception!
+            //
         }
         _SEH2_END;
         return STATUS_SUCCESS;
@@ -4035,8 +4665,8 @@ NtAllocateVirtualMemory(IN HANDLE ProcessHandle,
     // on the user input, and then compute the actual region size once all the
     // alignments have been done.
     //
-    StartingAddress = (ULONG_PTR)PAGE_ALIGN(PBaseAddress);
     EndingAddress = (((ULONG_PTR)PBaseAddress + PRegionSize - 1) | (PAGE_SIZE - 1));
+    StartingAddress = (ULONG_PTR)PAGE_ALIGN(PBaseAddress);
     PRegionSize = EndingAddress - StartingAddress + 1;
 
     //
@@ -4054,16 +4684,25 @@ NtAllocateVirtualMemory(IN HANDLE ProcessHandle,
     //
     // Get the VAD for this address range, and make sure it exists
     //
-    FoundVad = (PMMVAD)MiCheckForConflictingNode(StartingAddress >> PAGE_SHIFT,
-                                                 EndingAddress >> PAGE_SHIFT,
-                                                 &Process->VadRoot);
-    if (!FoundVad)
+    Result = MiCheckForConflictingNode(StartingAddress >> PAGE_SHIFT,
+                                       EndingAddress >> PAGE_SHIFT,
+                                       &Process->VadRoot,
+                                       (PMMADDRESS_NODE*)&FoundVad);
+    if (Result != TableFoundNode)
     {
         DPRINT1("Could not find a VAD for this allocation\n");
         Status = STATUS_CONFLICTING_ADDRESSES;
         goto FailPath;
     }
 
+       if ((AllocationType & MEM_RESET) == MEM_RESET)
+    {
+        /// @todo HACK: pretend success
+        DPRINT("MEM_RESET not supported\n");
+        Status = STATUS_SUCCESS;
+        goto FailPath;
+    }
+
     //
     // These kinds of VADs are illegal for this Windows function when trying to
     // commit an existing range
@@ -4080,7 +4719,7 @@ NtAllocateVirtualMemory(IN HANDLE ProcessHandle,
     //
     // Make sure that this address range actually fits within the VAD for it
     //
-    if (((StartingAddress >> PAGE_SHIFT) < FoundVad->StartingVpn) &&
+    if (((StartingAddress >> PAGE_SHIFT) < FoundVad->StartingVpn) ||
         ((EndingAddress >> PAGE_SHIFT) > FoundVad->EndingVpn))
     {
         DPRINT1("Address range does not fit into the VAD\n");
@@ -4092,7 +4731,12 @@ NtAllocateVirtualMemory(IN HANDLE ProcessHandle,
     // Make sure this is an ARM3 section
     //
     MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, (PVOID)PAGE_ROUND_DOWN(PBaseAddress));
-    ASSERT(MemoryArea->Type == MEMORY_AREA_OWNED_BY_ARM3);
+    if (MemoryArea->Type != MEMORY_AREA_OWNED_BY_ARM3)
+    {
+        DPRINT1("Illegal commit of non-ARM3 section!\n");
+        Status = STATUS_ALREADY_COMMITTED;
+        goto FailPath;
+    }
 
     // Is this a previously reserved section being committed? If so, enter the
     // special section path
@@ -4128,6 +4772,12 @@ NtAllocateVirtualMemory(IN HANDLE ProcessHandle,
         {
             //
             // Make sure it's okay to touch it
+            // Note: The Windows 2003 kernel has a bug here, passing the
+            // unaligned base address together with the aligned size,
+            // potentially covering a region larger than the actual allocation.
+            // Might be exposed through NtGdiCreateDIBSection w/ section handle
+            // For now we keep this behavior.
+            // TODO: analyze possible implications, create test case
             //
             Status = MiCheckSecuredVad(FoundVad,
                                        PBaseAddress,
@@ -4225,6 +4875,7 @@ NtAllocateVirtualMemory(IN HANDLE ProcessHandle,
     //
     TempPte.u.Long = 0;
     TempPte.u.Soft.Protection = ProtectionMask;
+    NT_ASSERT(TempPte.u.Long != 0);
 
     //
     // Get the PTE, PDE and the last PTE for this address range
@@ -4307,7 +4958,6 @@ NtAllocateVirtualMemory(IN HANDLE ProcessHandle,
             // There's a change in protection, remember this for later, but do
             // not yet handle it.
             //
-            DPRINT1("Protection change to: 0x%lx not implemented\n", Protect);
             ChangeProtection = TRUE;
         }
 
@@ -4317,11 +4967,6 @@ NtAllocateVirtualMemory(IN HANDLE ProcessHandle,
         PointerPte++;
     }
 
-    //
-    // This path is not yet handled
-    //
-    ASSERT(ChangeProtection == FALSE);
-
     //
     // Release the working set lock, unlock the address space, and detach from
     // the target process if it was not the current process. Also dereference the
@@ -4331,26 +4976,61 @@ NtAllocateVirtualMemory(IN HANDLE ProcessHandle,
     Status = STATUS_SUCCESS;
 FailPath:
     MmUnlockAddressSpace(AddressSpace);
+
+    if (!NT_SUCCESS(Status))
+    {
+        if (Vad != NULL)
+        {
+            ExFreePoolWithTag(Vad, 'SdaV');
+        }
+    }
+
+    //
+    // Check if we need to update the protection
+    //
+    if (ChangeProtection)
+    {
+        PVOID ProtectBaseAddress = (PVOID)StartingAddress;
+        SIZE_T ProtectSize = PRegionSize;
+        ULONG OldProtection;
+
+        //
+        // Change the protection of the region
+        //
+        MiProtectVirtualMemory(Process,
+                               &ProtectBaseAddress,
+                               &ProtectSize,
+                               Protect,
+                               &OldProtection);
+    }
+
 FailPathNoLock:
     if (Attached) KeUnstackDetachProcess(&ApcState);
     if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
 
     //
-    // Use SEH to write back the base address and the region size. In the case
-    // of an exception, we strangely do return back the exception code, even
-    // though the memory *has* been allocated. This mimics Windows behavior and
-    // there is not much we can do about it.
+    // Only write back results on success
     //
-    _SEH2_TRY
+    if (NT_SUCCESS(Status))
     {
-        *URegionSize = PRegionSize;
-        *UBaseAddress = (PVOID)StartingAddress;
-    }
-    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
-    {
-        Status = _SEH2_GetExceptionCode();
+        //
+        // Use SEH to write back the base address and the region size. In the case
+        // of an exception, we strangely do return back the exception code, even
+        // though the memory *has* been allocated. This mimics Windows behavior and
+        // there is not much we can do about it.
+        //
+        _SEH2_TRY
+        {
+            *URegionSize = PRegionSize;
+            *UBaseAddress = (PVOID)StartingAddress;
+        }
+        _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+        {
+            Status = _SEH2_GetExceptionCode();
+        }
+        _SEH2_END;
     }
-    _SEH2_END;
+
     return Status;
 }
 
@@ -4367,7 +5047,7 @@ NtFreeVirtualMemory(IN HANDLE ProcessHandle,
     PMEMORY_AREA MemoryArea;
     SIZE_T PRegionSize;
     PVOID PBaseAddress;
-    ULONG_PTR CommitReduction = 0;
+    LONG_PTR CommitReduction = 0;
     ULONG_PTR StartingAddress, EndingAddress;
     PMMVAD Vad;
     NTSTATUS Status;
@@ -4790,4 +5470,51 @@ FailPath:
     return Status;
 }
 
+
+PHYSICAL_ADDRESS
+NTAPI
+MmGetPhysicalAddress(PVOID Address)
+{
+    PHYSICAL_ADDRESS PhysicalAddress;
+    MMPDE TempPde;
+    MMPTE TempPte;
+
+    /* Check if the PXE/PPE/PDE is valid */
+    if (
+#if (_MI_PAGING_LEVELS == 4)
+        (MiAddressToPxe(Address)->u.Hard.Valid) &&
+#endif
+#if (_MI_PAGING_LEVELS >= 3)
+        (MiAddressToPpe(Address)->u.Hard.Valid) &&
+#endif
+        (MiAddressToPde(Address)->u.Hard.Valid))
+    {
+        /* Check for large pages */
+        TempPde = *MiAddressToPde(Address);
+        if (TempPde.u.Hard.LargePage)
+        {
+            /* Physical address is base page + large page offset */
+            PhysicalAddress.QuadPart = (ULONG64)TempPde.u.Hard.PageFrameNumber << PAGE_SHIFT;
+            PhysicalAddress.QuadPart += ((ULONG_PTR)Address & (PAGE_SIZE * PTE_PER_PAGE - 1));
+            return PhysicalAddress;
+        }
+
+        /* Check if the PTE is valid */
+        TempPte = *MiAddressToPte(Address);
+        if (TempPte.u.Hard.Valid)
+        {
+            /* Physical address is base page + page offset */
+            PhysicalAddress.QuadPart = (ULONG64)TempPte.u.Hard.PageFrameNumber << PAGE_SHIFT;
+            PhysicalAddress.QuadPart += ((ULONG_PTR)Address & (PAGE_SIZE - 1));
+            return PhysicalAddress;
+        }
+    }
+
+    KeRosDumpStackFrames(NULL, 20);
+    DPRINT1("MM:MmGetPhysicalAddressFailed base address was %p\n", Address);
+    PhysicalAddress.QuadPart = 0;
+    return PhysicalAddress;
+}
+
+
 /* EOF */