Sync to trunk head (r42241)
[reactos.git] / reactos / ntoskrnl / mm / virtual.c
index 377e1a1..4c04759 100644 (file)
@@ -1,5 +1,4 @@
-/* $Id$
- *
+/*
  * COPYRIGHT:       See COPYING in the top level directory
  * PROJECT:         ReactOS kernel
  * FILE:            ntoskrnl/mm/virtual.c
  * PROGRAMMERS:     David Welch
  */
 
-/* INCLUDE *****************************************************************/
+/* INCLUDE ********************************************************************/
 
 #include <ntoskrnl.h>
-
 #define NDEBUG
-#include <internal/debug.h>
+#include <debug.h>
 
-/* FUNCTIONS *****************************************************************/
+#define MI_MAPPED_COPY_PAGES  16
+#define MI_POOL_COPY_BYTES    512
+#define MI_MAX_TRANSFER_SIZE  64 * 1024
+#define TAG_VM TAG('V', 'm', 'R', 'w')
 
-NTSTATUS STDCALL
-NtFlushVirtualMemory(IN HANDLE ProcessHandle,
-                     IN OUT PVOID *BaseAddress,
-                     IN OUT PSIZE_T NumberOfBytesToFlush,
-                     OUT PIO_STATUS_BLOCK IoStatusBlock)
-/*
- * FUNCTION: Flushes virtual memory to file
- * ARGUMENTS:
- *        ProcessHandle = Points to the process that allocated the virtual
- *                        memory
- *        BaseAddress = Points to the memory address
- *        NumberOfBytesToFlush = Limits the range to flush,
- *        NumberOfBytesFlushed = Actual number of bytes flushed
- * RETURNS: Status
- */
+/* PRIVATE FUNCTIONS **********************************************************/
+
+LONG
+MiGetExceptionInfo(EXCEPTION_POINTERS *ExceptionInfo, BOOLEAN * HaveBadAddress, ULONG_PTR * BadAddress)
 {
-    /* This should be implemented once we support network filesystems */
-    DPRINT("NtFlushVirtualMemory is UNIMPLEMENTED\n");
-    return(STATUS_SUCCESS);
+    PEXCEPTION_RECORD ExceptionRecord;
+    PAGED_CODE();
+
+    /* Assume default */
+    *HaveBadAddress = FALSE;
+
+    /* Get the exception record */
+    ExceptionRecord = ExceptionInfo->ExceptionRecord;
+
+    /* Look at the exception code */
+    if ((ExceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION) ||
+        (ExceptionRecord->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION) ||
+        (ExceptionRecord->ExceptionCode == STATUS_IN_PAGE_ERROR))
+    {
+        /* We can tell the address if we have more than one parameter */
+        if (ExceptionRecord->NumberParameters > 1)
+        {
+            /* Return the address */
+            *HaveBadAddress = TRUE;
+            *BadAddress = ExceptionRecord->ExceptionInformation[1];
+        }
+    }
+
+    /* Continue executing the next handler */
+    return EXCEPTION_EXECUTE_HANDLER;
 }
 
+NTSTATUS
+NTAPI
+MiDoMappedCopy(IN PEPROCESS SourceProcess,
+               IN PVOID SourceAddress,
+               IN PEPROCESS TargetProcess,
+               OUT PVOID TargetAddress,
+               IN SIZE_T BufferSize,
+               IN KPROCESSOR_MODE PreviousMode,
+               OUT PSIZE_T ReturnSize)
+{
+    PFN_NUMBER MdlBuffer[(sizeof(MDL) / sizeof(PFN_NUMBER)) + MI_MAPPED_COPY_PAGES + 1];
+    PMDL Mdl = (PMDL)MdlBuffer;
+    SIZE_T TotalSize, CurrentSize, RemainingSize;
+    volatile BOOLEAN FailedInProbe = FALSE, FailedInMapping = FALSE, FailedInMoving;
+    volatile BOOLEAN PagesLocked;
+    PVOID CurrentAddress = SourceAddress, CurrentTargetAddress = TargetAddress;
+    volatile PVOID MdlAddress;
+    KAPC_STATE ApcState;
+    BOOLEAN HaveBadAddress;
+    ULONG_PTR BadAddress;
+    NTSTATUS Status = STATUS_SUCCESS;
+    PAGED_CODE();
+
+    /* Calculate the maximum amount of data to move */
+    TotalSize = (MI_MAPPED_COPY_PAGES - 2) * PAGE_SIZE;
+    if (BufferSize <= TotalSize) TotalSize = BufferSize;
+    CurrentSize = TotalSize;
+    RemainingSize = BufferSize;
+
+    /* Loop as long as there is still data */
+    while (RemainingSize > 0)
+    {
+        /* Check if this transfer will finish everything off */
+        if (RemainingSize < CurrentSize) CurrentSize = RemainingSize;
+
+        /* Attach to the source address space */
+        KeStackAttachProcess(&SourceProcess->Pcb, &ApcState);
+
+        /* Reset state for this pass */
+        MdlAddress = NULL;
+        PagesLocked = FALSE;
+        FailedInMoving = FALSE;
+        ASSERT(FailedInProbe == FALSE);
+
+        /* Protect user-mode copy */
+        _SEH2_TRY
+        {
+            /* If this is our first time, probe the buffer */
+            if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
+            {
+                /* Catch a failure here */
+                FailedInProbe = TRUE;
+
+                /* Do the probe */
+                ProbeForRead(SourceAddress, BufferSize, sizeof(CHAR));
+
+                /* Passed */
+                FailedInProbe = FALSE;
+            }
+
+            /* Initialize and probe and lock the MDL */
+            MmInitializeMdl (Mdl, CurrentAddress, CurrentSize);
+            MmProbeAndLockPages (Mdl, PreviousMode, IoReadAccess);
+            PagesLocked = TRUE;
+
+            /* Now map the pages */
+            MdlAddress = MmMapLockedPagesSpecifyCache(Mdl,
+                                                      KernelMode,
+                                                      MmCached,
+                                                      NULL,
+                                                      FALSE,
+                                                      HighPagePriority);
+            if (!MdlAddress)
+            {
+                /* Use our SEH handler to pick this up */
+                FailedInMapping = TRUE;
+                ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
+            }
+
+            /* Now let go of the source and grab to the target process */
+            KeUnstackDetachProcess(&ApcState);
+            KeStackAttachProcess(&TargetProcess->Pcb, &ApcState);
+
+            /* Check if this is our first time through */
+            if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
+            {
+                /* Catch a failure here */
+                FailedInProbe = TRUE;
+
+                /* Do the probe */
+                ProbeForWrite(TargetAddress, BufferSize, sizeof(CHAR));
+
+                /* Passed */
+                FailedInProbe = FALSE;
+            }
+
+            /* Now do the actual move */
+            FailedInMoving = TRUE;
+            RtlCopyMemory(CurrentTargetAddress, MdlAddress, CurrentSize);
+        }
+        _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(), &HaveBadAddress, &BadAddress))
+        {
+            /* Detach from whoever we may be attached to */
+            KeUnstackDetachProcess(&ApcState);
+
+            /* Check if we had mapped the pages */
+            if (MdlAddress) MmUnmapLockedPages(MdlAddress, Mdl);
+
+            /* Check if we had locked the pages */
+            if (PagesLocked) MmUnlockPages(Mdl);
+
+            /* Check if we failed during the probe or mapping */
+            if ((FailedInProbe) || (FailedInMapping))
+            {
+                /* Exit */
+                Status = _SEH2_GetExceptionCode();
+                _SEH2_YIELD(return Status);
+            }
+
+            /* Otherwise, we failed  probably during the move */
+            *ReturnSize = BufferSize - RemainingSize;
+            if (FailedInMoving)
+            {
+                /* Check if we know exactly where we stopped copying */
+                if (HaveBadAddress)
+                {
+                    /* Return the exact number of bytes copied */
+                    *ReturnSize = BadAddress - (ULONG_PTR)SourceAddress;
+                }
+            }
+
+            /* Return partial copy */
+            Status = STATUS_PARTIAL_COPY;
+        }
+        _SEH2_END;
+
+        /* Check for SEH status */
+        if (Status != STATUS_SUCCESS) return Status;
+
+        /* Detach from target */
+        KeUnstackDetachProcess(&ApcState);
+
+        /* Unmap and unlock */
+        MmUnmapLockedPages(MdlAddress, Mdl);
+        MmUnlockPages(Mdl);
+
+        /* Update location and size */
+        RemainingSize -= CurrentSize;
+        CurrentAddress = (PVOID)((ULONG_PTR)CurrentAddress + CurrentSize);
+        CurrentTargetAddress = (PVOID)((ULONG_PTR)CurrentTargetAddress + CurrentSize);
+    }
+
+    /* All bytes read */
+    *ReturnSize = BufferSize;
+    return STATUS_SUCCESS;
+}
 
-NTSTATUS STDCALL
-MiLockVirtualMemory(HANDLE ProcessHandle,
-                    PVOID BaseAddress,
-                    ULONG NumberOfBytesToLock,
-                    PULONG NumberOfBytesLocked,
-                    PObReferenceObjectByHandle pObReferenceObjectByHandle,
-                    PMmCreateMdl pMmCreateMdl,
-                    PObDereferenceObject pObDereferenceObject,
-                    PMmProbeAndLockPages pMmProbeAndLockPages,
-                    PExFreePool pExFreePool)
+NTSTATUS
+NTAPI
+MiDoPoolCopy(IN PEPROCESS SourceProcess,
+             IN PVOID SourceAddress,
+             IN PEPROCESS TargetProcess,
+             OUT PVOID TargetAddress,
+             IN SIZE_T BufferSize,
+             IN KPROCESSOR_MODE PreviousMode,
+             OUT PSIZE_T ReturnSize)
 {
-    PEPROCESS Process;
-    NTSTATUS Status;
-    PMDL Mdl;
-
-    Status = pObReferenceObjectByHandle(ProcessHandle,
-                                        PROCESS_VM_WRITE,
-                                        NULL,
-                                        UserMode,
-                                        (PVOID*)(&Process),
-                                        NULL);
-    if (!NT_SUCCESS(Status))
-        return(Status);
+    UCHAR StackBuffer[MI_POOL_COPY_BYTES];
+    SIZE_T TotalSize, CurrentSize, RemainingSize;
+    volatile BOOLEAN FailedInProbe = FALSE, FailedInMoving, HavePoolAddress = FALSE;
+    PVOID CurrentAddress = SourceAddress, CurrentTargetAddress = TargetAddress;
+    PVOID PoolAddress;
+    KAPC_STATE ApcState;
+    BOOLEAN HaveBadAddress;
+    ULONG_PTR BadAddress;
+    NTSTATUS Status = STATUS_SUCCESS;
+    PAGED_CODE();
+
+    /* Calculate the maximum amount of data to move */
+    TotalSize = MI_MAX_TRANSFER_SIZE;
+    if (BufferSize <= MI_MAX_TRANSFER_SIZE) TotalSize = BufferSize;
+    CurrentSize = TotalSize;
+    RemainingSize = BufferSize;
 
-    Mdl = pMmCreateMdl(NULL,
-                       BaseAddress,
-                       NumberOfBytesToLock);
-    if (Mdl == NULL)
+    /* Check if we can use the stack */
+    if (BufferSize <= MI_POOL_COPY_BYTES)
     {
-        pObDereferenceObject(Process);
-        return(STATUS_NO_MEMORY);
+        /* Use it */
+        PoolAddress = (PVOID)StackBuffer;
+    }
+    else
+    {
+        /* Allocate pool */
+        PoolAddress = ExAllocatePoolWithTag(NonPagedPool, TotalSize, TAG_VM);
+        if (!PoolAddress) ASSERT(FALSE);
+        HavePoolAddress = TRUE;
     }
 
-    pMmProbeAndLockPages(Mdl,
-                         UserMode,
-                         IoWriteAccess);
+    /* Loop as long as there is still data */
+    while (RemainingSize > 0)
+    {
+        /* Check if this transfer will finish everything off */
+        if (RemainingSize < CurrentSize) CurrentSize = RemainingSize;
 
-    pExFreePool(Mdl);
+        /* Attach to the source address space */
+        KeStackAttachProcess(&SourceProcess->Pcb, &ApcState);
 
-    pObDereferenceObject(Process);
+        /* Reset state for this pass */
+        FailedInMoving = FALSE;
+        ASSERT(FailedInProbe == FALSE);
 
-    *NumberOfBytesLocked = NumberOfBytesToLock;
-    return(STATUS_SUCCESS);
-}
+        /* Protect user-mode copy */
+        _SEH2_TRY
+        {
+            /* If this is our first time, probe the buffer */
+            if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
+            {
+                /* Catch a failure here */
+                FailedInProbe = TRUE;
 
+                /* Do the probe */
+                ProbeForRead(SourceAddress, BufferSize, sizeof(CHAR));
 
-NTSTATUS STDCALL
-NtLockVirtualMemory(HANDLE ProcessHandle,
-                    PVOID BaseAddress,
-                    ULONG NumberOfBytesToLock,
-                    PULONG NumberOfBytesLocked)
-{
-    DPRINT("NtLockVirtualMemory(ProcessHandle %x, BaseAddress %x, "
-        "NumberOfBytesToLock %d, NumberOfBytesLocked %x)\n",
-        ProcessHandle,
-        BaseAddress,
-        NumberOfBytesToLock,
-        NumberOfBytesLocked);
-
-    return MiLockVirtualMemory(ProcessHandle,
-                               BaseAddress,
-                               NumberOfBytesToLock,
-                               NumberOfBytesLocked,
-                               ObReferenceObjectByHandle,
-                               MmCreateMdl,
-                               (PVOID)ObfDereferenceObject,
-                               MmProbeAndLockPages,
-                               ExFreePool);
+                /* Passed */
+                FailedInProbe = FALSE;
+            }
+
+            /* Do the copy */
+            RtlCopyMemory(PoolAddress, CurrentAddress, CurrentSize);
+
+            /* Now let go of the source and grab to the target process */
+            KeUnstackDetachProcess(&ApcState);
+            KeStackAttachProcess(&TargetProcess->Pcb, &ApcState);
+
+            /* Check if this is our first time through */
+            if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
+            {
+                /* Catch a failure here */
+                FailedInProbe = TRUE;
+
+                /* Do the probe */
+                ProbeForWrite(TargetAddress, BufferSize, sizeof(CHAR));
+
+                /* Passed */
+                FailedInProbe = FALSE;
+            }
+
+            /* Now do the actual move */
+            FailedInMoving = TRUE;
+            RtlCopyMemory(CurrentTargetAddress, PoolAddress, CurrentSize);
+        }
+        _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(), &HaveBadAddress, &BadAddress))
+        {
+            /* Detach from whoever we may be attached to */
+            KeUnstackDetachProcess(&ApcState);
+
+            /* Check if we had allocated pool */
+            if (HavePoolAddress) ExFreePool(PoolAddress);
+
+            /* Check if we failed during the probe */
+            if (FailedInProbe)
+            {
+                /* Exit */
+                Status = _SEH2_GetExceptionCode();
+                _SEH2_YIELD(return Status);
+            }
+
+            /* Otherwise, we failed  probably during the move */
+            *ReturnSize = BufferSize - RemainingSize;
+            if (FailedInMoving)
+            {
+                /* Check if we know exactly where we stopped copying */
+                if (HaveBadAddress)
+                {
+                    /* Return the exact number of bytes copied */
+                    *ReturnSize = BadAddress - (ULONG_PTR)SourceAddress;
+                }
+            }
+
+            /* Return partial copy */
+            Status = STATUS_PARTIAL_COPY;
+        }
+        _SEH2_END;
+
+        /* Check for SEH status */
+        if (Status != STATUS_SUCCESS) return Status;
+
+        /* Detach from target */
+        KeUnstackDetachProcess(&ApcState);
+
+        /* Update location and size */
+        RemainingSize -= CurrentSize;
+        CurrentAddress = (PVOID)((ULONG_PTR)CurrentAddress + CurrentSize);
+        CurrentTargetAddress = (PVOID)((ULONG_PTR)CurrentTargetAddress + CurrentSize);
+    }
+
+    /* Check if we had allocated pool */
+    if (HavePoolAddress) ExFreePool(PoolAddress);
+
+    /* All bytes read */
+    *ReturnSize = BufferSize;
+    return STATUS_SUCCESS;
 }
 
+NTSTATUS
+NTAPI
+MmCopyVirtualMemory(IN PEPROCESS SourceProcess,
+                    IN PVOID SourceAddress,
+                    IN PEPROCESS TargetProcess,
+                    OUT PVOID TargetAddress,
+                    IN SIZE_T BufferSize,
+                    IN KPROCESSOR_MODE PreviousMode,
+                    OUT PSIZE_T ReturnSize)
+{
+    NTSTATUS Status;
+    PEPROCESS Process = SourceProcess;
+
+    /* Don't accept zero-sized buffers */
+    if (!BufferSize) return STATUS_SUCCESS;
+
+    /* If we are copying from ourselves, lock the target instead */
+    if (SourceProcess == PsGetCurrentProcess()) Process = TargetProcess;
+
+    /* Acquire rundown protection */
+    if (!ExAcquireRundownProtection(&Process->RundownProtect))
+    {
+        /* Fail */
+        return STATUS_PROCESS_IS_TERMINATING;
+    }
+
+    /* See if we should use the pool copy */
+    if (BufferSize > MI_POOL_COPY_BYTES)
+    {
+        /* Use MDL-copy */
+        Status = MiDoMappedCopy(SourceProcess,
+                                SourceAddress,
+                                TargetProcess,
+                                TargetAddress,
+                                BufferSize,
+                                PreviousMode,
+                                ReturnSize);
+    }
+    else
+    {
+        /* Do pool copy */
+        Status = MiDoPoolCopy(SourceProcess,
+                              SourceAddress,
+                              TargetProcess,
+                              TargetAddress,
+                              BufferSize,
+                              PreviousMode,
+                              ReturnSize);
+    }
+
+    /* Release the lock */
+    ExReleaseRundownProtection(&Process->RundownProtect);
+    return Status;
+}
 
 NTSTATUS FASTCALL
 MiQueryVirtualMemory(IN HANDLE ProcessHandle,
                      IN PVOID Address,
                      IN MEMORY_INFORMATION_CLASS VirtualMemoryInformationClass,
                      OUT PVOID VirtualMemoryInformation,
-                     IN ULONG Length,
-                     OUT PULONG ResultLength)
+                     IN SIZE_T Length,
+                     OUT PSIZE_T ResultLength)
 {
     NTSTATUS Status;
     PEPROCESS Process;
     MEMORY_AREA* MemoryArea;
-    PMADDRESS_SPACE AddressSpace;
+    PMMSUPPORT AddressSpace;
 
-    if (Address < MmSystemRangeStart)
-    {
-        Status = ObReferenceObjectByHandle(ProcessHandle,
-                                           PROCESS_QUERY_INFORMATION,
-                                           NULL,
-                                           UserMode,
-                                           (PVOID*)(&Process),
-                                           NULL);
+    Status = ObReferenceObjectByHandle(ProcessHandle,
+                                       PROCESS_QUERY_INFORMATION,
+                                       NULL,
+                                       UserMode,
+                                       (PVOID*)(&Process),
+                                       NULL);
 
-        if (!NT_SUCCESS(Status))
-        {
-            DPRINT("NtQueryVirtualMemory() = %x\n",Status);
-            return(Status);
-        }
-        AddressSpace = (PMADDRESS_SPACE)&Process->VadRoot;
-    }
-    else
+    if (!NT_SUCCESS(Status))
     {
-        AddressSpace = MmGetKernelAddressSpace();
+        DPRINT("NtQueryVirtualMemory() = %x\n",Status);
+        return(Status);
     }
+
+    AddressSpace = &Process->Vm;
+
     MmLockAddressSpace(AddressSpace);
     MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, Address);
     switch(VirtualMemoryInformationClass)
@@ -261,6 +555,7 @@ MiQueryVirtualMemory(IN HANDLE ProcessHandle,
 
         default:
         {
+            DPRINT1("Unsupported or unimplemented class: %lx\n", VirtualMemoryInformationClass);
             Status = STATUS_INVALID_INFO_CLASS;
             *ResultLength = 0;
             break;
@@ -268,140 +563,29 @@ MiQueryVirtualMemory(IN HANDLE ProcessHandle,
     }
 
     MmUnlockAddressSpace(AddressSpace);
-    if (Address < MmSystemRangeStart)
-    {
-        ASSERT(Process);
-        ObDereferenceObject(Process);
-    }
+    ObDereferenceObject(Process);
 
     return Status;
 }
 
-/* (tMk 2004.II.4)
- * FUNCTION:
- * Called from VirtualQueryEx (lib\kernel32\mem\virtual.c)
- *
- */
-NTSTATUS STDCALL
-NtQueryVirtualMemory(IN HANDLE ProcessHandle,
-                     IN PVOID Address,
-                     IN MEMORY_INFORMATION_CLASS VirtualMemoryInformationClass,
-                     OUT PVOID VirtualMemoryInformation,
-                     IN ULONG Length,
-                     OUT PULONG UnsafeResultLength)
-{
-    NTSTATUS Status = STATUS_SUCCESS;
-    ULONG ResultLength = 0;
-    KPROCESSOR_MODE PreviousMode;
-    union
-    {
-        MEMORY_BASIC_INFORMATION BasicInfo;
-    }
-    VirtualMemoryInfo;
-
-    DPRINT("NtQueryVirtualMemory(ProcessHandle %x, Address %x, "
-           "VirtualMemoryInformationClass %d, VirtualMemoryInformation %x, "
-           "Length %lu ResultLength %x)\n",ProcessHandle,Address,
-           VirtualMemoryInformationClass,VirtualMemoryInformation,
-           Length,ResultLength);
-
-    PreviousMode =  ExGetPreviousMode();
-
-    if (PreviousMode != KernelMode && UnsafeResultLength != NULL)
-    {
-        _SEH_TRY
-        {
-            ProbeForWriteUlong(UnsafeResultLength);
-        }
-        _SEH_HANDLE
-        {
-            Status = _SEH_GetExceptionCode();
-        }
-        _SEH_END;
-
-        if (!NT_SUCCESS(Status))
-        {
-            return Status;
-        }
-    }
-
-    if (Address >= MmSystemRangeStart)
-    {
-        DPRINT1("Invalid parameter\n");
-        return STATUS_INVALID_PARAMETER;
-    }
-
-    Status = MiQueryVirtualMemory(ProcessHandle,
-                                  Address,
-                                  VirtualMemoryInformationClass,
-                                  &VirtualMemoryInfo,
-                                  Length,
-                                  &ResultLength );
-
-    if (NT_SUCCESS(Status))
-    {
-        if (PreviousMode != KernelMode)
-        {
-            _SEH_TRY
-            {
-                if (ResultLength > 0)
-                {
-                    ProbeForWrite(VirtualMemoryInformation,
-                                  ResultLength,
-                                  1);
-                    RtlCopyMemory(VirtualMemoryInformation,
-                                  &VirtualMemoryInfo,
-                                  ResultLength);
-                }
-                if (UnsafeResultLength != NULL)
-                {
-                    *UnsafeResultLength = ResultLength;
-                }
-            }
-            _SEH_HANDLE
-            {
-                Status = _SEH_GetExceptionCode();
-            }
-            _SEH_END;
-        }
-        else
-        {
-            if (ResultLength > 0)
-            {
-                RtlCopyMemory(VirtualMemoryInformation,
-                              &VirtualMemoryInfo,
-                              ResultLength);
-            }
-
-            if (UnsafeResultLength != NULL)
-            {
-                *UnsafeResultLength = ResultLength;
-            }
-        }
-    }
-
-   return(Status);
-}
-
-
-NTSTATUS STDCALL
+NTSTATUS NTAPI
 MiProtectVirtualMemory(IN PEPROCESS Process,
                        IN OUT PVOID *BaseAddress,
-                       IN OUT PULONG NumberOfBytesToProtect,
+                       IN OUT PSIZE_T NumberOfBytesToProtect,
                        IN ULONG NewAccessProtection,
                        OUT PULONG OldAccessProtection  OPTIONAL)
 {
     PMEMORY_AREA MemoryArea;
-    PMADDRESS_SPACE AddressSpace;
+    PMMSUPPORT AddressSpace;
     ULONG OldAccessProtection_;
     NTSTATUS Status;
 
     *NumberOfBytesToProtect =
-        PAGE_ROUND_UP((ULONG_PTR)(*BaseAddress) + (*NumberOfBytesToProtect)) -
-        PAGE_ROUND_DOWN(*BaseAddress);
+    PAGE_ROUND_UP((ULONG_PTR)(*BaseAddress) + (*NumberOfBytesToProtect)) -
+    PAGE_ROUND_DOWN(*BaseAddress);
     *BaseAddress = (PVOID)PAGE_ROUND_DOWN(*BaseAddress);
 
-    AddressSpace = (PMADDRESS_SPACE)&(Process)->VadRoot;
+    AddressSpace = &Process->Vm;
 
     MmLockAddressSpace(AddressSpace);
     MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, *BaseAddress);
@@ -438,620 +622,695 @@ MiProtectVirtualMemory(IN PEPROCESS Process,
     return Status;
 }
 
-
-/* (tMk 2004.II.5)
- * FUNCTION:
- * Called from VirtualProtectEx (lib\kernel32\mem\virtual.c)
- *
- */
-NTSTATUS STDCALL
-NtProtectVirtualMemory(IN HANDLE ProcessHandle,
-                       IN OUT PVOID *UnsafeBaseAddress,
-                       IN OUT ULONG *UnsafeNumberOfBytesToProtect,
-                       IN ULONG NewAccessProtection,
-                       OUT PULONG UnsafeOldAccessProtection)
+PVOID
+NTAPI
+MiMapLockedPagesInUserSpace(IN PMDL Mdl,
+                            IN PVOID BaseVa,
+                            IN MEMORY_CACHING_TYPE CacheType,
+                            IN PVOID BaseAddress)
 {
-    PEPROCESS Process;
-    ULONG OldAccessProtection;
-    PVOID BaseAddress = NULL;
-    ULONG NumberOfBytesToProtect = 0;
-    KPROCESSOR_MODE PreviousMode;
-    NTSTATUS Status = STATUS_SUCCESS;
-
-    PreviousMode = ExGetPreviousMode();
-
-    if (PreviousMode != KernelMode)
+    PVOID Base;
+    PPFN_NUMBER MdlPages;
+    ULONG PageCount;   
+    PEPROCESS CurrentProcess;
+    NTSTATUS Status;
+    ULONG Protect;
+    MEMORY_AREA *Result;
+    LARGE_INTEGER BoundaryAddressMultiple;
+    
+    /* Calculate the number of pages required. */
+    MdlPages = (PPFN_NUMBER)(Mdl + 1);
+    PageCount = PAGE_ROUND_UP(Mdl->ByteCount + Mdl->ByteOffset) / PAGE_SIZE;
+    
+    /* Set default page protection */
+    Protect = PAGE_READWRITE;
+    if (CacheType == MmNonCached) Protect |= PAGE_NOCACHE;
+    
+    BoundaryAddressMultiple.QuadPart = 0;
+    Base = BaseAddress;
+    
+    CurrentProcess = PsGetCurrentProcess();
+    
+    MmLockAddressSpace(&CurrentProcess->Vm);
+    Status = MmCreateMemoryArea(&CurrentProcess->Vm,
+                                MEMORY_AREA_MDL_MAPPING,
+                                &Base,
+                                PageCount * PAGE_SIZE,
+                                Protect,
+                                &Result,
+                                (Base != NULL),
+                                0,
+                                BoundaryAddressMultiple);
+    MmUnlockAddressSpace(&CurrentProcess->Vm);
+    if (!NT_SUCCESS(Status))
     {
-        _SEH_TRY
+        if (Mdl->MdlFlags & MDL_MAPPING_CAN_FAIL)
         {
-            ProbeForWritePointer(UnsafeBaseAddress);
-            ProbeForWriteUlong(UnsafeNumberOfBytesToProtect);
-            ProbeForWriteUlong(UnsafeOldAccessProtection);
-
-            BaseAddress = *UnsafeBaseAddress;
-            NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect;
-        }
-        _SEH_HANDLE
-        {
-            Status = _SEH_GetExceptionCode();
-        }
-        _SEH_END;
-
-        if (!NT_SUCCESS(Status))
-        {
-            return Status;
+            return NULL;
         }
+        
+        /* Throw exception */
+        ExRaiseStatus(STATUS_ACCESS_VIOLATION);
+        ASSERT(0);
     }
-    else
-    {
-        BaseAddress = *UnsafeBaseAddress;
-        NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect;
-    }
-
-    if ((ULONG_PTR)BaseAddress + NumberOfBytesToProtect - 1 < (ULONG_PTR)BaseAddress ||
-        (ULONG_PTR)BaseAddress + NumberOfBytesToProtect - 1 >= MmUserProbeAddress)
+    
+    /* Set the virtual mappings for the MDL pages. */
+    if (Mdl->MdlFlags & MDL_IO_SPACE)
     {
-        /* Don't allow to change the protection of a kernel mode address */
-        return STATUS_INVALID_PARAMETER_2;
+        /* Map the pages */
+        Status = MmCreateVirtualMappingUnsafe(CurrentProcess,
+                                              Base,
+                                              Protect,
+                                              MdlPages,
+                                              PageCount);
     }
-
-    /* (tMk 2004.II.5) in Microsoft SDK I read:
-     * 'if this parameter is NULL or does not point to a valid variable, the function fails'
-     */
-    if(UnsafeOldAccessProtection == NULL)
+    else
     {
-        return(STATUS_INVALID_PARAMETER);
+        /* Map the pages */
+        Status = MmCreateVirtualMapping(CurrentProcess,
+                                        Base,
+                                        Protect,
+                                        MdlPages,
+                                        PageCount);
     }
-
-    Status = ObReferenceObjectByHandle(ProcessHandle,
-                                       PROCESS_VM_OPERATION,
-                                       PsProcessType,
-                                       UserMode,
-                                       (PVOID*)(&Process),
-                                       NULL);
+    
+    /* Check if the mapping suceeded */
     if (!NT_SUCCESS(Status))
     {
-        DPRINT("NtProtectVirtualMemory() = %x\n",Status);
-        return(Status);
+        /* If it can fail, return NULL */
+        if (Mdl->MdlFlags & MDL_MAPPING_CAN_FAIL) return NULL;
+        
+        /* Throw exception */
+        ExRaiseStatus(STATUS_ACCESS_VIOLATION);
     }
+    
+    /* Return the base */
+    Base = (PVOID)((ULONG_PTR)Base + Mdl->ByteOffset);
+    return Base;
+}
 
-    Status = MiProtectVirtualMemory(Process,
-                                    &BaseAddress,
-                                    &NumberOfBytesToProtect,
-                                    NewAccessProtection,
-                                    &OldAccessProtection);
-
-    ObDereferenceObject(Process);
+VOID
+NTAPI
+MiUnmapLockedPagesInUserSpace(IN PVOID BaseAddress,
+                              IN PMDL Mdl)
+{
+    PMEMORY_AREA MemoryArea;
+    
+    /* Sanity check */
+    ASSERT(Mdl->Process == PsGetCurrentProcess());
+    
+    /* Find the memory area */
+    MemoryArea = MmLocateMemoryAreaByAddress(&Mdl->Process->Vm,
+                                             BaseAddress);
+    ASSERT(MemoryArea);
+    
+    /* Free it */
+    MmFreeMemoryArea(&Mdl->Process->Vm,
+                     MemoryArea,
+                     NULL,
+                     NULL);    
+}
 
-    if (PreviousMode != KernelMode)
-    {
-        _SEH_TRY
-        {
-            *UnsafeOldAccessProtection = OldAccessProtection;
-            *UnsafeBaseAddress = BaseAddress;
-            *UnsafeNumberOfBytesToProtect = NumberOfBytesToProtect;
-        }
-        _SEH_HANDLE
-        {
-            Status = _SEH_GetExceptionCode();
-        }
-        _SEH_END;
-    }
-    else
-    {
-        *UnsafeOldAccessProtection = OldAccessProtection;
-        *UnsafeBaseAddress = BaseAddress;
-        *UnsafeNumberOfBytesToProtect = NumberOfBytesToProtect;
-    }
+/* PUBLIC FUNCTIONS ***********************************************************/
 
-    return(Status);
+/*
+ * @unimplemented
+ */
+PVOID
+NTAPI
+MmGetVirtualForPhysical(IN PHYSICAL_ADDRESS PhysicalAddress)
+{
+    UNIMPLEMENTED;
+    return 0;
 }
 
+/*
+ * @unimplemented
+ */
+PVOID
+NTAPI
+MmSecureVirtualMemory(IN PVOID Address,
+                      IN SIZE_T Length,
+                      IN ULONG Mode)
+{
+    UNIMPLEMENTED;
+    return NULL;
+}
 
-/* (tMk 2004.II.05)
- * FUNCTION:
- * Called from ReadProcessMemory (lib\kernel32\mem\procmem.c) and KlInitPeb(lib\kernel32\process\create.c)
- *
- * NOTE: This function will be correct if MmProbeAndLockPages() would be fully IMPLEMENTED.
+/*
+ * @unimplemented
  */
-NTSTATUS STDCALL
+VOID
+NTAPI
+MmUnsecureVirtualMemory(IN PVOID SecureMem)
+{
+    UNIMPLEMENTED;
+}
+
+/* SYSTEM CALLS ***************************************************************/
+
+NTSTATUS
+NTAPI
 NtReadVirtualMemory(IN HANDLE ProcessHandle,
                     IN PVOID BaseAddress,
                     OUT PVOID Buffer,
-                    IN ULONG NumberOfBytesToRead,
-                    OUT PULONG NumberOfBytesRead OPTIONAL)
+                    IN SIZE_T NumberOfBytesToRead,
+                    OUT PSIZE_T NumberOfBytesRead OPTIONAL)
 {
-    PMDL Mdl;
-    PVOID SystemAddress;
-    KPROCESSOR_MODE PreviousMode;
-    PEPROCESS Process, CurrentProcess;
+    KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
+    PEPROCESS Process;
     NTSTATUS Status = STATUS_SUCCESS;
-
+    SIZE_T BytesRead = 0;
     PAGED_CODE();
 
-    DPRINT("NtReadVirtualMemory(ProcessHandle %x, BaseAddress %x, "
-           "Buffer %x, NumberOfBytesToRead %d)\n",ProcessHandle,BaseAddress,
-           Buffer,NumberOfBytesToRead);
-
-    if ((ULONG_PTR)BaseAddress + NumberOfBytesToRead - 1 < (ULONG_PTR)BaseAddress ||
-        (ULONG_PTR)BaseAddress + NumberOfBytesToRead - 1 >= MmUserProbeAddress)
-    {
-        /* Don't allow to read from kernel space */
-        return STATUS_ACCESS_VIOLATION;
-    }
-
-    PreviousMode = ExGetPreviousMode();
-
+    /* Check if we came from user mode */
     if (PreviousMode != KernelMode)
     {
-        if ((ULONG_PTR)Buffer + NumberOfBytesToRead - 1 < (ULONG_PTR)Buffer ||
-            (ULONG_PTR)Buffer + NumberOfBytesToRead - 1 >= MmUserProbeAddress)
+        /* Validate the read addresses */
+        if ((((ULONG_PTR)BaseAddress + NumberOfBytesToRead) < (ULONG_PTR)BaseAddress) ||
+            (((ULONG_PTR)Buffer + NumberOfBytesToRead) < (ULONG_PTR)Buffer) ||
+            (((ULONG_PTR)BaseAddress + NumberOfBytesToRead) > MmUserProbeAddress) ||
+            (((ULONG_PTR)Buffer + NumberOfBytesToRead) > MmUserProbeAddress))
         {
             /* Don't allow to write into kernel space */
             return STATUS_ACCESS_VIOLATION;
         }
+
+        /* Enter SEH for probe */
+        _SEH2_TRY
+        {
+            /* Probe the output value */
+            if (NumberOfBytesRead) ProbeForWriteSize_t(NumberOfBytesRead);
+        }
+        _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+        {
+            /* Get exception code */
+            Status = _SEH2_GetExceptionCode();
+        }
+        _SEH2_END;
+
+        /* Return if we failed */
+        if (!NT_SUCCESS(Status)) return Status;
     }
 
+    /* Reference the process */
     Status = ObReferenceObjectByHandle(ProcessHandle,
                                        PROCESS_VM_READ,
-                                       NULL,
+                                       PsProcessType,
                                        PreviousMode,
                                        (PVOID*)(&Process),
                                        NULL);
-    if (!NT_SUCCESS(Status))
+    if (NT_SUCCESS(Status))
     {
-        return(Status);
+        /* Do the copy */
+        Status = MmCopyVirtualMemory(Process,
+                                     BaseAddress,
+                                     PsGetCurrentProcess(),
+                                     Buffer,
+                                     NumberOfBytesToRead,
+                                     PreviousMode,
+                                     &BytesRead);
+
+        /* Dereference the process */
+        ObDereferenceObject(Process);
     }
 
-    CurrentProcess = PsGetCurrentProcess();
-
-    if(PreviousMode != KernelMode)
+    /* Check if the caller sent this parameter */
+    if (NumberOfBytesRead)
     {
-        _SEH_TRY
-        {
-            if(NumberOfBytesRead != NULL)
-            {
-                ProbeForWriteUlong(NumberOfBytesRead);
-            }
-        }
-        _SEH_HANDLE
+        /* Enter SEH to guard write */
+        _SEH2_TRY
         {
-            Status = _SEH_GetExceptionCode();
+            /* Return the number of bytes read */
+            *NumberOfBytesRead = BytesRead;
         }
-        _SEH_END;
-
-        if(!NT_SUCCESS(Status))
+        _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
         {
-            return Status;
+            /* Handle exception */
+            Status = _SEH2_GetExceptionCode();
         }
+        _SEH2_END;
     }
 
+    /* Return status */
+    return Status;
+}
+
+NTSTATUS
+NTAPI
+NtWriteVirtualMemory(IN HANDLE ProcessHandle,
+                     IN PVOID BaseAddress,
+                     IN PVOID Buffer,
+                     IN SIZE_T NumberOfBytesToWrite,
+                     OUT PSIZE_T NumberOfBytesWritten OPTIONAL)
+{
+    KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
+    PEPROCESS Process;
+    NTSTATUS Status = STATUS_SUCCESS;
+    SIZE_T BytesWritten = 0;
+    PAGED_CODE();
 
-    if (Process == CurrentProcess)
+    /* Check if we came from user mode */
+    if (PreviousMode != KernelMode)
     {
-        _SEH_TRY
+        /* Validate the read addresses */
+        if ((((ULONG_PTR)BaseAddress + NumberOfBytesToWrite) < (ULONG_PTR)BaseAddress) ||
+            (((ULONG_PTR)Buffer + NumberOfBytesToWrite) < (ULONG_PTR)Buffer) ||
+            (((ULONG_PTR)BaseAddress + NumberOfBytesToWrite) > MmUserProbeAddress) ||
+            (((ULONG_PTR)Buffer + NumberOfBytesToWrite) > MmUserProbeAddress))
         {
-            RtlCopyMemory(Buffer, BaseAddress, NumberOfBytesToRead);
+            /* Don't allow to write into kernel space */
+            return STATUS_ACCESS_VIOLATION;
         }
-        _SEH_HANDLE
+
+        /* Enter SEH for probe */
+        _SEH2_TRY
         {
-            Status = _SEH_GetExceptionCode();
+            /* Probe the output value */
+            if (NumberOfBytesWritten) ProbeForWriteSize_t(NumberOfBytesWritten);
         }
-        _SEH_END;
-    }
-    else
-    {
-        Mdl = MmCreateMdl(NULL,
-                          Buffer,
-                          NumberOfBytesToRead);
-        if(Mdl == NULL)
+        _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
         {
-            ObDereferenceObject(Process);
-            return(STATUS_NO_MEMORY);
+            /* Get exception code */
+            Status = _SEH2_GetExceptionCode();
         }
+        _SEH2_END;
 
-        _SEH_TRY
+        /* Return if we failed */
+        if (!NT_SUCCESS(Status)) return Status;
+    }
+
+    /* Reference the process */
+    Status = ObReferenceObjectByHandle(ProcessHandle,
+                                       PROCESS_VM_WRITE,
+                                       PsProcessType,
+                                       PreviousMode,
+                                       (PVOID*)&Process,
+                                       NULL);
+    if (NT_SUCCESS(Status))
+    {
+        /* Do the copy */
+        Status = MmCopyVirtualMemory(PsGetCurrentProcess(),
+                                     Buffer,
+                                     Process,
+                                     BaseAddress,
+                                     NumberOfBytesToWrite,
+                                     PreviousMode,
+                                     &BytesWritten);
+
+        /* Dereference the process */
+        ObDereferenceObject(Process);
+    }
+
+    /* Check if the caller sent this parameter */
+    if (NumberOfBytesWritten)
+    {
+        /* Enter SEH to guard write */
+        _SEH2_TRY
         {
-            MmProbeAndLockPages(Mdl, PreviousMode, IoWriteAccess);
+            /* Return the number of bytes read */
+            *NumberOfBytesWritten = BytesWritten;
         }
-        _SEH_HANDLE
+        _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
         {
-            Status = _SEH_GetExceptionCode();
+            /* Handle exception */
+            Status = _SEH2_GetExceptionCode();
         }
-        _SEH_END;
-
-        if(NT_SUCCESS(Status))
-        {
-            KeAttachProcess(&Process->Pcb);
-
-            SystemAddress = MmGetSystemAddressForMdl(Mdl);
+        _SEH2_END;
+    }
 
-            Status = STATUS_SUCCESS;
-            _SEH_TRY
-            {
-                Status = STATUS_PARTIAL_COPY;
-                RtlCopyMemory(SystemAddress, BaseAddress, NumberOfBytesToRead);
-                Status = STATUS_SUCCESS;
-            }
-            _SEH_HANDLE
-            {
-                if(Status != STATUS_PARTIAL_COPY)
-                    Status = _SEH_GetExceptionCode();
-            }
-            _SEH_END;
+    /* Return status */
+    return Status;
+}
 
-            KeDetachProcess();
+NTSTATUS
+NTAPI
+NtProtectVirtualMemory(IN HANDLE ProcessHandle,
+                       IN OUT PVOID *UnsafeBaseAddress,
+                       IN OUT SIZE_T *UnsafeNumberOfBytesToProtect,
+                       IN ULONG NewAccessProtection,
+                       OUT PULONG UnsafeOldAccessProtection)
+{
+    PEPROCESS Process;
+    ULONG OldAccessProtection;
+    ULONG Protection;
+    PVOID BaseAddress = NULL;
+    SIZE_T NumberOfBytesToProtect = 0;
+    KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
+    NTSTATUS Status = STATUS_SUCCESS;
+    PAGED_CODE();
 
-            if (Mdl->MappedSystemVa != NULL)
-            {
-                MmUnmapLockedPages(Mdl->MappedSystemVa, Mdl);
-            }
-            MmUnlockPages(Mdl);
-        }
-        ExFreePool(Mdl);
+    /* Check for valid protection flags */
+    Protection = NewAccessProtection & ~(PAGE_GUARD|PAGE_NOCACHE);
+    if (Protection != PAGE_NOACCESS &&
+        Protection != PAGE_READONLY &&
+        Protection != PAGE_READWRITE &&
+        Protection != PAGE_WRITECOPY &&
+        Protection != PAGE_EXECUTE &&
+        Protection != PAGE_EXECUTE_READ &&
+        Protection != PAGE_EXECUTE_READWRITE &&
+        Protection != PAGE_EXECUTE_WRITECOPY)
+    {
+        return STATUS_INVALID_PAGE_PROTECTION;
     }
 
-    ObDereferenceObject(Process);
-
-    if ((NT_SUCCESS(Status) || Status == STATUS_PARTIAL_COPY) &&
-        NumberOfBytesRead != NULL)
+    /* Check if we came from user mode */
+    if (PreviousMode != KernelMode)
     {
-        _SEH_TRY
+        /* Enter SEH for probing */
+        _SEH2_TRY
         {
-            *NumberOfBytesRead = NumberOfBytesToRead;
+            /* Validate all outputs */
+            ProbeForWritePointer(UnsafeBaseAddress);
+            ProbeForWriteSize_t(UnsafeNumberOfBytesToProtect);
+            ProbeForWriteUlong(UnsafeOldAccessProtection);
+
+            /* Capture them */
+            BaseAddress = *UnsafeBaseAddress;
+            NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect;
         }
-        _SEH_HANDLE
+        _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
         {
-            Status = _SEH_GetExceptionCode();
+            /* Get exception code */
+            Status = _SEH2_GetExceptionCode();
         }
-        _SEH_END;
-    }
+        _SEH2_END;
 
-    return(Status);
-}
+        /* Return on exception */
+        if (!NT_SUCCESS(Status)) return Status;
+    }
+    else
+    {
+        /* Capture directly */
+        BaseAddress = *UnsafeBaseAddress;
+        NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect;
+    }
 
-/* (tMk 2004.II.05)
- * FUNCTION:  THIS function doesn't make a sense...
- * Called from VirtualUnlock (lib\kernel32\mem\virtual.c)
- */
-NTSTATUS STDCALL
-NtUnlockVirtualMemory(HANDLE ProcessHandle,
-                      PVOID BaseAddress,
-                      ULONG NumberOfBytesToUnlock,
-                      PULONG NumberOfBytesUnlocked OPTIONAL)
-{
-    // AG [08-20-03] : I have *no* idea if this is correct, I just used the
-    // other functions as a template and made a few intelligent guesses...
+    /* Catch illegal base address */
+    if (BaseAddress > (PVOID)MmUserProbeAddress) return STATUS_INVALID_PARAMETER_2;
 
-    NTSTATUS Status;
-    PMDL Mdl;
-    PEPROCESS Process;
+    /* Catch illegal region size  */
+    if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < NumberOfBytesToProtect)
+    {
+        /* Fail */
+        return STATUS_INVALID_PARAMETER_3;
+    }
 
-    DPRINT("NtUnlockVirtualMemory(ProcessHandle %x, BaseAddress %x, "
-           "NumberOfBytesToUnlock %d), NumberOfBytesUnlocked %x\n",ProcessHandle,BaseAddress,
-           NumberOfBytesToUnlock, NumberOfBytesUnlocked);
+    /* 0 is also illegal */
+    if (!NumberOfBytesToProtect) return STATUS_INVALID_PARAMETER_3;
 
+    /* Get a reference to the process */
     Status = ObReferenceObjectByHandle(ProcessHandle,
-                                       PROCESS_VM_WRITE,
-                                       NULL,
-                                       UserMode,
+                                       PROCESS_VM_OPERATION,
+                                       PsProcessType,
+                                       PreviousMode,
                                        (PVOID*)(&Process),
                                        NULL);
-    if (!NT_SUCCESS(Status))
-    {
-        return(Status);
-    }
+    if (!NT_SUCCESS(Status)) return Status;
 
-    Mdl = MmCreateMdl(NULL,
-                      BaseAddress,
-                      NumberOfBytesToUnlock);
-    if(Mdl == NULL)
-    {
-        ObDereferenceObject(Process);
-        return(STATUS_NO_MEMORY);
-    }
+    /* Do the actual work */
+    Status = MiProtectVirtualMemory(Process,
+                                    &BaseAddress,
+                                    &NumberOfBytesToProtect,
+                                    NewAccessProtection,
+                                    &OldAccessProtection);
 
+    /* Release reference */
     ObDereferenceObject(Process);
 
-    if (Mdl->MappedSystemVa != NULL)
+    /* Enter SEH to return data */
+    _SEH2_TRY
+    {
+        /* Return data to user */
+        *UnsafeOldAccessProtection = OldAccessProtection;
+        *UnsafeBaseAddress = BaseAddress;
+        *UnsafeNumberOfBytesToProtect = NumberOfBytesToProtect;
+    }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
     {
-        MmUnmapLockedPages(Mdl->MappedSystemVa, Mdl);
+        /* Catch exception */
+        Status = _SEH2_GetExceptionCode();
     }
-    MmUnlockPages(Mdl);
-    ExFreePool(Mdl);
+    _SEH2_END;
 
-    *NumberOfBytesUnlocked = NumberOfBytesToUnlock;
-
-    return(STATUS_SUCCESS);
+    /* Return status */
+    return Status;
 }
 
-
-/* (tMk 2004.II.05)
- * FUNCTION:
- * Called from WriteProcessMemory (lib\kernel32\mem\procmem.c) and KlInitPeb(lib\kernel32\process\create.c)
- *
- * NOTE: This function will be correct if MmProbeAndLockPages() would be fully IMPLEMENTED.
- */
-NTSTATUS STDCALL
-NtWriteVirtualMemory(IN HANDLE ProcessHandle,
-                     IN PVOID BaseAddress,
-                     IN PVOID Buffer,
-                     IN ULONG NumberOfBytesToWrite,
-                     OUT PULONG NumberOfBytesWritten  OPTIONAL)
+NTSTATUS NTAPI
+NtQueryVirtualMemory(IN HANDLE ProcessHandle,
+                     IN PVOID Address,
+                     IN MEMORY_INFORMATION_CLASS VirtualMemoryInformationClass,
+                     OUT PVOID VirtualMemoryInformation,
+                     IN SIZE_T Length,
+                     OUT PSIZE_T UnsafeResultLength)
 {
-    PMDL Mdl;
-    PVOID SystemAddress;
-    PEPROCESS Process;
+    NTSTATUS Status = STATUS_SUCCESS;
+    SIZE_T ResultLength = 0;
     KPROCESSOR_MODE PreviousMode;
-    NTSTATUS CopyStatus, Status = STATUS_SUCCESS;
-
-    DPRINT("NtWriteVirtualMemory(ProcessHandle %x, BaseAddress %x, "
-           "Buffer %x, NumberOfBytesToWrite %d)\n",ProcessHandle,BaseAddress,
-           Buffer,NumberOfBytesToWrite);
-
-    if ((ULONG_PTR)BaseAddress + NumberOfBytesToWrite - 1 < (ULONG_PTR)BaseAddress ||
-        (ULONG_PTR)BaseAddress + NumberOfBytesToWrite - 1 >= MmUserProbeAddress)
+    WCHAR ModuleFileNameBuffer[MAX_PATH] = {0};
+    UNICODE_STRING ModuleFileName;
+    PMEMORY_SECTION_NAME SectionName = NULL;
+    PEPROCESS Process;
+    union
     {
-        /* Don't allow to write into kernel space */
-        return STATUS_ACCESS_VIOLATION;
+        MEMORY_BASIC_INFORMATION BasicInfo;
     }
+    VirtualMemoryInfo;
 
-    PreviousMode = ExGetPreviousMode();
+    DPRINT("NtQueryVirtualMemory(ProcessHandle %x, Address %x, "
+           "VirtualMemoryInformationClass %d, VirtualMemoryInformation %x, "
+           "Length %lu ResultLength %x)\n",ProcessHandle,Address,
+           VirtualMemoryInformationClass,VirtualMemoryInformation,
+           Length,ResultLength);
+
+    PreviousMode =  ExGetPreviousMode();
 
     if (PreviousMode != KernelMode)
     {
-        if ((ULONG_PTR)Buffer + NumberOfBytesToWrite - 1 < (ULONG_PTR)Buffer ||
-            (ULONG_PTR)Buffer + NumberOfBytesToWrite - 1 >= MmUserProbeAddress)
+        _SEH2_TRY
         {
-            /* Don't allow to read from kernel space */
-            return STATUS_ACCESS_VIOLATION;
+            ProbeForWrite(VirtualMemoryInformation,
+                          Length,
+                          sizeof(ULONG_PTR));
+
+            if (UnsafeResultLength) ProbeForWriteSize_t(UnsafeResultLength);
         }
-        if (NumberOfBytesWritten != NULL)
+        _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
         {
-            _SEH_TRY
-            {
-                ProbeForWriteUlong(NumberOfBytesWritten);
-            }
-            _SEH_HANDLE
-            {
-                Status = _SEH_GetExceptionCode();
-            }
-            _SEH_END;
+            Status = _SEH2_GetExceptionCode();
+        }
+        _SEH2_END;
 
-            if (!NT_SUCCESS(Status))
-            {
-                return Status;
-            }
+        if (!NT_SUCCESS(Status))
+        {
+            return Status;
         }
     }
 
-    Status = ObReferenceObjectByHandle(ProcessHandle,
-                                       PROCESS_VM_WRITE,
-                                       NULL,
-                                       UserMode,
-                                       (PVOID*)(&Process),
-                                       NULL);
-    if (!NT_SUCCESS(Status))
+    if (Address >= MmSystemRangeStart)
     {
-        return(Status);
+        DPRINT1("Invalid parameter\n");
+        return STATUS_INVALID_PARAMETER;
     }
 
-    CopyStatus = STATUS_SUCCESS;
-
-    /* Write memory */
-    if (Process == PsGetCurrentProcess())
+    /* FIXME: Move this inside MiQueryVirtualMemory */
+    if (VirtualMemoryInformationClass == MemorySectionName)
     {
-        if (PreviousMode != KernelMode)
-        {
-            _SEH_TRY
-            {
-                memcpy(BaseAddress, Buffer, NumberOfBytesToWrite);
-            }
-            _SEH_HANDLE
-            {
-                CopyStatus = _SEH_GetExceptionCode();
-            }
-            _SEH_END;
-        }
-        else
-        {
-            memcpy(BaseAddress, Buffer, NumberOfBytesToWrite);
-        }
-    }
-    else
-    {
-        /* Create MDL describing the source buffer. */
-        Mdl = MmCreateMdl(NULL,
-                          Buffer,
-                          NumberOfBytesToWrite);
-        if (Mdl == NULL)
-        {
-            ObDereferenceObject(Process);
-            return(STATUS_NO_MEMORY);
-        }
-        _SEH_TRY
-        {
-            /* Map the MDL. */
-            MmProbeAndLockPages(Mdl, UserMode, IoReadAccess);
-        }
-        _SEH_HANDLE
+        Status = ObReferenceObjectByHandle(ProcessHandle,
+                                           PROCESS_QUERY_INFORMATION,
+                                           NULL,
+                                           PreviousMode,
+                                           (PVOID*)(&Process),
+                                           NULL);
+
+        if (!NT_SUCCESS(Status))
         {
-            CopyStatus = _SEH_GetExceptionCode();
+            DPRINT("NtQueryVirtualMemory() = %x\n",Status);
+            return(Status);
         }
-        _SEH_END;
 
-        if (NT_SUCCESS(CopyStatus))
-        {
-            /* Copy memory from the mapped MDL into the target buffer. */
-            KeAttachProcess(&Process->Pcb);
+        RtlInitEmptyUnicodeString(&ModuleFileName, ModuleFileNameBuffer, sizeof(ModuleFileNameBuffer));
+        Status = MmGetFileNameForAddress(Address, &ModuleFileName);
 
-            SystemAddress = MmGetSystemAddressForMdl(Mdl);
+        if (NT_SUCCESS(Status))
+        {
+            SectionName = VirtualMemoryInformation;
             if (PreviousMode != KernelMode)
             {
-                _SEH_TRY
+                _SEH2_TRY
                 {
-                    memcpy(BaseAddress, SystemAddress, NumberOfBytesToWrite);
+                    RtlInitUnicodeString(&SectionName->SectionFileName, SectionName->NameBuffer);
+                    SectionName->SectionFileName.MaximumLength = Length;
+                    RtlCopyUnicodeString(&SectionName->SectionFileName, &ModuleFileName);
+
+                    if (UnsafeResultLength != NULL)
+                    {
+                        *UnsafeResultLength = ModuleFileName.Length;
+                    }
                 }
-                _SEH_HANDLE
+                _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
                 {
-                    CopyStatus = _SEH_GetExceptionCode();
+                    Status = _SEH2_GetExceptionCode();
                 }
-                _SEH_END;
+                _SEH2_END;
             }
             else
             {
-                memcpy(BaseAddress, SystemAddress, NumberOfBytesToWrite);
-            }
-
-            KeDetachProcess();
-        }
+                RtlInitUnicodeString(&SectionName->SectionFileName, SectionName->NameBuffer);
+                SectionName->SectionFileName.MaximumLength = Length;
+                RtlCopyUnicodeString(&SectionName->SectionFileName, &ModuleFileName);
 
-        /* Free the MDL. */
-        if (Mdl->MappedSystemVa != NULL)
-        {
-            MmUnmapLockedPages(Mdl->MappedSystemVa, Mdl);
+                if (UnsafeResultLength != NULL)
+                {
+                    *UnsafeResultLength = ModuleFileName.Length;
+                }
+            }
         }
-        MmUnlockPages(Mdl);
-        ExFreePool(Mdl);
+        ObDereferenceObject(Process);
+        return Status;
+    }
+    else
+    {
+        Status = MiQueryVirtualMemory(ProcessHandle,
+                                      Address,
+                                      VirtualMemoryInformationClass,
+                                      &VirtualMemoryInfo,
+                                      Length,
+                                      &ResultLength);
     }
-    ObDereferenceObject(Process);
 
-    if (NT_SUCCESS(CopyStatus) && NumberOfBytesWritten != NULL)
+    if (NT_SUCCESS(Status))
     {
         if (PreviousMode != KernelMode)
         {
-            _SEH_TRY
+            _SEH2_TRY
             {
-                *NumberOfBytesWritten = NumberOfBytesToWrite;
+                if (ResultLength > 0)
+                {
+                    ProbeForWrite(VirtualMemoryInformation,
+                                  ResultLength,
+                                  1);
+                    RtlCopyMemory(VirtualMemoryInformation,
+                                  &VirtualMemoryInfo,
+                                  ResultLength);
+                }
+                if (UnsafeResultLength != NULL)
+                {
+                    *UnsafeResultLength = ResultLength;
+                }
             }
-            _SEH_HANDLE
+            _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
             {
-                Status = _SEH_GetExceptionCode();
+                Status = _SEH2_GetExceptionCode();
             }
-            _SEH_END;
+            _SEH2_END;
         }
         else
         {
-            *NumberOfBytesWritten = NumberOfBytesToWrite;
+            if (ResultLength > 0)
+            {
+                RtlCopyMemory(VirtualMemoryInformation,
+                              &VirtualMemoryInfo,
+                              ResultLength);
+            }
+
+            if (UnsafeResultLength != NULL)
+            {
+                *UnsafeResultLength = ResultLength;
+            }
         }
     }
 
-    return(NT_SUCCESS(CopyStatus) ? Status : CopyStatus);
+    return(Status);
 }
 
-/*
- * @unimplemented
- */
-
-PVOID
-STDCALL
-MmGetVirtualForPhysical(
-    IN PHYSICAL_ADDRESS PhysicalAddress
-    )
+NTSTATUS
+NTAPI
+NtLockVirtualMemory(IN HANDLE ProcessHandle,
+                    IN PVOID BaseAddress,
+                    IN SIZE_T NumberOfBytesToLock,
+                    OUT PSIZE_T NumberOfBytesLocked OPTIONAL)
 {
     UNIMPLEMENTED;
-    return 0;
+    if (NumberOfBytesLocked) *NumberOfBytesLocked = 0;
+    return STATUS_SUCCESS;
 }
 
-/* FUNCTION:
- * Called from EngSecureMem (subsys\win32k\eng\mem.c)
- * @unimplemented
- */
-PVOID STDCALL
-MmSecureVirtualMemory(PVOID  Address,
-                      SIZE_T Length,
-                      ULONG  Mode)
+NTSTATUS
+NTAPI
+NtUnlockVirtualMemory(IN HANDLE ProcessHandle,
+                      IN PVOID BaseAddress,
+                      IN SIZE_T NumberOfBytesToUnlock,
+                      OUT PSIZE_T NumberOfBytesUnlocked OPTIONAL)
 {
-    /* Only works for user space */
-    if (MmHighestUserAddress < Address)
-    {
-        return NULL;
-    }
-
     UNIMPLEMENTED;
-
-    return 0;
+    if (NumberOfBytesUnlocked) *NumberOfBytesUnlocked = 0;
+    return STATUS_SUCCESS;
 }
 
-
-/* FUNCTION:
- * Called from EngUnsecureMem (subsys\win32k\eng\mem.c)
- * @unimplemented
- */
-VOID STDCALL
-MmUnsecureVirtualMemory(PVOID SecureMem)
+NTSTATUS
+NTAPI
+NtFlushVirtualMemory(IN HANDLE ProcessHandle,
+                     IN OUT PVOID *BaseAddress,
+                     IN OUT PSIZE_T NumberOfBytesToFlush,
+                     OUT PIO_STATUS_BLOCK IoStatusBlock)
 {
-    if (NULL == SecureMem)
-    {
-        return;
-    }
-
     UNIMPLEMENTED;
+    return STATUS_SUCCESS;
 }
 
-
 /*
- * @implemented
+ * @unimplemented
  */
-VOID STDCALL
-ProbeForRead(IN CONST VOID *Address,
-             IN ULONG Length,
-             IN ULONG Alignment)
+NTSTATUS
+NTAPI
+NtGetWriteWatch(IN HANDLE ProcessHandle,
+                IN ULONG Flags,
+                IN PVOID BaseAddress,
+                IN ULONG RegionSize,
+                IN PVOID *UserAddressArray,
+                OUT PULONG EntriesInUserAddressArray,
+                OUT PULONG Granularity)
 {
-    if (Length != 0)
+    if (!EntriesInUserAddressArray || !Granularity)
     {
-        ASSERT(Alignment == 1 || Alignment == 2 || Alignment == 4 || Alignment == 8);
-
-        if (((ULONG_PTR)Address & (Alignment - 1)) != 0)
-        {
-            ExRaiseStatus (STATUS_DATATYPE_MISALIGNMENT);
-        }
-        else if ((ULONG_PTR)Address + Length - 1 < (ULONG_PTR)Address ||
-                 (ULONG_PTR)Address + Length - 1 >= (ULONG_PTR)MmUserProbeAddress)
-        {
-            ExRaiseStatus (STATUS_ACCESS_VIOLATION);
-        }
+        return STATUS_ACCESS_VIOLATION;
+    }
+    
+    if (!*EntriesInUserAddressArray || !RegionSize)
+    {
+        return STATUS_INVALID_PARAMETER;
     }
+    
+    if (!UserAddressArray)
+    {
+        return STATUS_ACCESS_VIOLATION;
+    }
+    
+    /* HACK: Set granularity to PAGE_SIZE */
+    *Granularity = PAGE_SIZE;
+    
+    UNIMPLEMENTED;
+    return STATUS_NOT_IMPLEMENTED;
 }
 
-
 /*
- * @implemented
+ * @unimplemented
  */
-VOID STDCALL
-ProbeForWrite(IN PVOID Address,
-              IN ULONG Length,
-              IN ULONG Alignment)
+NTSTATUS
+NTAPI
+NtResetWriteWatch(IN HANDLE ProcessHandle,
+                  IN PVOID BaseAddress,
+                  IN ULONG RegionSize)
 {
-    volatile CHAR *Current;
-    PCHAR Last;
-
-    if (Length != 0)
+    if (!RegionSize)
     {
-        ASSERT(Alignment == 1 || Alignment == 2 || Alignment == 4 || Alignment == 8);
-
-        if (((ULONG_PTR)Address & (Alignment - 1)) != 0)
-        {
-            ExRaiseStatus (STATUS_DATATYPE_MISALIGNMENT);
-        }
-
-        Last = (PCHAR)((ULONG_PTR)Address + Length - 1);
-        if ((ULONG_PTR)Last < (ULONG_PTR)Address ||
-            (ULONG_PTR)Last >= (ULONG_PTR)MmUserProbeAddress)
-        {
-            ExRaiseStatus (STATUS_ACCESS_VIOLATION);
-        }
-
-        /* Check for accessible pages, do *not* touch any memory outside of the
-           range!*/
-        Current = (volatile CHAR*)Address;
-        Last = (PCHAR)(PAGE_ROUND_DOWN(Last));
-        do
-        {
-            *Current = *Current;
-            Current = (volatile CHAR*)(PAGE_ROUND_DOWN(Current) + PAGE_SIZE);
-        } while (Current <= Last);
+        return STATUS_INVALID_PARAMETER;
     }
+    
+    UNIMPLEMENTED;
+    return STATUS_NOT_IMPLEMENTED;
 }
 
 /* EOF */