Index: kmtests/CMakeLists.txt
===================================================================
--- kmtests/CMakeLists.txt	(revision 57746)
+++ kmtests/CMakeLists.txt	(working copy)
@@ -50,6 +50,7 @@
     ntos_ke/KeSpinLock.c
     ntos_ke/KeTimer.c
     ntos_mm/MmSection.c
+    ntos_mm/ZwAllocateVirtualMemory.c
     ntos_ob/ObReference.c
     ntos_ob/ObType.c
     ntos_ob/ObTypes.c
Index: kmtests/include/kmt_public.h
===================================================================
--- kmtests/include/kmt_public.h	(revision 57746)
+++ kmtests/include/kmt_public.h	(working copy)
@@ -17,6 +17,12 @@
 #define IOCTL_KMTEST_SET_RESULTBUFFER  \
     CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_NEITHER, FILE_READ_DATA | FILE_WRITE_DATA)
 
+#define IOCTL_KMTEST_USERMODE_SEND_RESPONSE \
+    CTL_CODE(FILE_DEVICE_UNKNOWN, 0x803, METHOD_IN_DIRECT, FILE_WRITE_DATA)
+
+#define IOCTL_KMTEST_USERMODE_AWAIT_REQ \
+    CTL_CODE(FILE_DEVICE_UNKNOWN, 0x804, METHOD_BUFFERED, FILE_READ_DATA)
+
 #define KMTEST_DEVICE_NAME L"Kmtest"
 #define KMTEST_DEVICE_DRIVER_PATH L"\\Device\\" KMTEST_DEVICE_NAME
 #define KMTEST_DEVICE_PATH L"\\\\.\\Global\\GLOBALROOT" KMTEST_DEVICE_DRIVER_PATH
Index: kmtests/include/kmt_test.h
===================================================================
--- kmtests/include/kmt_test.h	(revision 57746)
+++ kmtests/include/kmt_test.h	(working copy)
@@ -38,6 +38,37 @@
     CHAR LogBuffer[ANYSIZE_ARRAY];
 } KMT_RESULTBUFFER, *PKMT_RESULTBUFFER;
 
+
+#ifndef KMT_STANDALONE_DRIVER
+/////////////////////////////////////////////////////
+// STRUCTURES FOR THE USERMODE CALL BACK MECHANISM
+/////////////////////////////////////////////////////
+
+//list of supported operations
+typedef enum _CALLBACK_INFORMATION_CLASS {
+    QueryVirtualMemory
+} CALLBACK_INFORMATION_CLASS, *PCALLBACK_INFORMATION_CLASS;
+
+typedef union _KMT_RESPONSE {
+
+    MEMORY_BASIC_INFORMATION MemInfo;
+
+} KMT_RESPONSE, *PKMT_RESPONSE;
+
+//this struct is sent from driver to usermode
+typedef struct _CALLBACK_REQUEST_PACKET {
+    ULONG RequestId;
+    CALLBACK_INFORMATION_CLASS OperationType;
+    PVOID Parameters;
+} CALLBACK_REQUEST_PACKET, *PCALLBACK_REQUEST_PACKET;
+
+PKMT_RESPONSE KmtUserModeCallback(CALLBACK_INFORMATION_CLASS Operation, PVOID Parameters);
+VOID KmtFreeCallbackResponse(PKMT_RESPONSE Response); 
+/////////////////////////////////////////////////////
+#endif
+
+
+
 #ifdef KMT_STANDALONE_DRIVER
 #define KMT_KERNEL_MODE
 
Index: kmtests/kmtest/support.c
===================================================================
--- kmtests/kmtest/support.c	(revision 57746)
+++ kmtests/kmtest/support.c	(working copy)
@@ -11,10 +11,88 @@
 #include <kmt_public.h>
 
 #include <assert.h>
+#include <debug.h>
 
 extern HANDLE KmtestHandle;
 
 /**
+* @name KmtUserCallbackThread
+*
+* Routine which awaits callback requests from kernel-mode
+*
+*
+* @return Win32 error code as returned by DeviceIoControl/HeapAlloc/VirtualQuery
+*/
+DWORD 
+WINAPI 
+KmtUserCallbackThread(PVOID Unused) 
+{ 
+    DWORD Error = ERROR_SUCCESS;
+    CALLBACK_REQUEST_PACKET OutputBuffer;
+    SIZE_T UserReturned;
+    PKMT_RESPONSE Response;
+    DWORD BytesReturned;
+    HANDLE LocalKmtHandle;
+
+    
+    UNREFERENCED_PARAMETER(Unused);
+
+    //concurrent ioctls on the same (non-overlapped) handle aren't possible, so open a separate one. For more info http://www.osronline.com/showthread.cfm?link=230782
+    LocalKmtHandle = CreateFile(KMTEST_DEVICE_PATH, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
+    
+    while (1) 
+    {
+        if (DeviceIoControl(LocalKmtHandle, IOCTL_KMTEST_USERMODE_AWAIT_REQ, NULL, 0,  &OutputBuffer, sizeof(OutputBuffer), &BytesReturned, NULL)) 
+        {
+            switch (OutputBuffer.OperationType) 
+            {
+                case QueryVirtualMemory:
+                { 
+                    Response = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(KMT_RESPONSE));
+                    if (Response == NULL) 
+                        goto cleanup;
+
+                    UserReturned = VirtualQuery(OutputBuffer.Parameters, (PMEMORY_BASIC_INFORMATION)Response, sizeof(KMT_RESPONSE));
+                    if (UserReturned == 0) 
+                        error_goto(Error, cleanup);
+
+                    if (!DeviceIoControl(LocalKmtHandle, IOCTL_KMTEST_USERMODE_SEND_RESPONSE,  &OutputBuffer.RequestId, sizeof(ULONG), Response, sizeof(KMT_RESPONSE), &BytesReturned, NULL))
+                        error_goto(Error, cleanup);
+
+                    break;
+                }    
+                default: 
+                {
+                    DPRINT("UNRECOGNISED USER-MODE CALLBACK REQUEST\n");
+                    break;
+                }
+
+            }
+        } 
+        else 
+        {
+            error_goto(Error, cleanup);
+        }
+    }
+
+
+cleanup:
+    if (Response != NULL) 
+    {
+        HeapFree(GetProcessHeap(),  0, Response);
+    }
+
+    if (LocalKmtHandle != INVALID_HANDLE_VALUE) 
+    {
+        CloseHandle(LocalKmtHandle);
+    }
+
+    DPRINT("Callback handler dying! Error code %x", Error);
+    return Error;
+}
+
+
+/**
  * @name KmtRunKernelTest
  *
  * Run the specified kernel-mode test part
@@ -28,12 +106,25 @@
 KmtRunKernelTest(
     IN PCSTR TestName)
 {
+    HANDLE CallbackThread;
     DWORD Error = ERROR_SUCCESS;
     DWORD BytesRead;
+    
+    
+    CallbackThread = CreateThread(NULL, 0, KmtUserCallbackThread, NULL, 0, NULL);
+    if (CallbackThread == NULL) 
+    {
+        //to-do?
+    }
 
     if (!DeviceIoControl(KmtestHandle, IOCTL_KMTEST_RUN_TEST, (PVOID)TestName, (DWORD)strlen(TestName), NULL, 0, &BytesRead, NULL))
         error(Error);
-
+    
+    if (CallbackThread != NULL) 
+    {
+         CloseHandle(CallbackThread);
+    }
+   
     return Error;
 }
 
Index: kmtests/kmtest_drv/kmtest_drv.c
===================================================================
--- kmtests/kmtest_drv/kmtest_drv.c	(revision 57746)
+++ kmtests/kmtest_drv/kmtest_drv.c	(working copy)
@@ -19,6 +19,23 @@
 #define KMT_DEFINE_TEST_FUNCTIONS
 #include <kmt_test.h>
 
+/* Usermode callback definitions*/
+typedef struct _KMT_USER_WORK_ENTRY
+{
+    LIST_ENTRY ListEntry;
+    KEVENT WorkDoneEvent;
+    CALLBACK_REQUEST_PACKET Request;
+    PKMT_RESPONSE Response;
+} KMT_USER_WORK_ENTRY, *PKMT_USER_WORK_ENTRY;
+
+typedef struct _KMT_USER_WORK_LIST
+{
+    LIST_ENTRY ListHead;
+    FAST_MUTEX Lock;
+    KEVENT NewWorkEvent;
+} KMT_USER_WORK_LIST, *PKMT_USER_WORK_LIST;
+
+
 /* Prototypes */
 DRIVER_INITIALIZE DriverEntry;
 static DRIVER_UNLOAD DriverUnload;
@@ -30,10 +47,13 @@
 static DRIVER_DISPATCH DriverClose;
 __drv_dispatchType(IRP_MJ_DEVICE_CONTROL)
 static DRIVER_DISPATCH DriverIoControl;
+static VOID KmtCleanUsermodeCallbacks(VOID);
 
 /* Globals */
 static PDEVICE_OBJECT MainDeviceObject;
 PDRIVER_OBJECT KmtDriverObject = NULL;
+static KMT_USER_WORK_LIST WorkList;
+static ULONG RequestId = 0;
 
 /* Entry */
 /**
@@ -92,6 +112,10 @@
     DriverObject->MajorFunction[IRP_MJ_CLOSE] = DriverClose;
     DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DriverIoControl;
 
+    ExInitializeFastMutex(&WorkList.Lock);
+    KeInitializeEvent(&WorkList.NewWorkEvent, NotificationEvent, FALSE);
+    InitializeListHead(&WorkList.ListHead);
+
 cleanup:
     if (MainDeviceObject && !NT_SUCCESS(Status))
     {
@@ -122,7 +146,7 @@
     UNREFERENCED_PARAMETER(DriverObject);
 
     DPRINT("DriverUnload\n");
-
+    KmtCleanUsermodeCallbacks();
     if (MainDeviceObject)
     {
         PKMT_DEVICE_EXTENSION DeviceExtension = MainDeviceObject->DeviceExtension;
@@ -403,10 +427,104 @@
                     ResultBuffer->LogBufferLength, ResultBuffer->LogBufferMaxLength);
             break;
         }
+        case IOCTL_KMTEST_USERMODE_AWAIT_REQ:
+        {
+            PLIST_ENTRY Entry;
+            PKMT_USER_WORK_ENTRY WorkItem;
+
+            Status = KeWaitForSingleObject(&WorkList.NewWorkEvent, UserRequest, UserMode, FALSE, NULL);
+
+            if (Status == STATUS_USER_APC || Status == STATUS_KERNEL_APC)
+            {
+                break;
+            }
+
+            DPRINT1("DriverIoControl. IOCTL_KMTEST_USERMODE_AWAIT_REQ, len=%lu\n", IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength);
+
+            if (IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength < sizeof(CALLBACK_REQUEST_PACKET)) 
+            {
+                Status = STATUS_INVALID_BUFFER_SIZE;
+                break;
+            }
+
+            ASSERT(!IsListEmpty(&WorkList.ListHead));
+
+            Entry = WorkList.ListHead.Flink;
+            WorkItem = CONTAINING_RECORD(Entry, KMT_USER_WORK_ENTRY, ListEntry);
+
+            RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer, &WorkItem->Request, sizeof(CALLBACK_REQUEST_PACKET));
+            Status = STATUS_SUCCESS;
+            Length = sizeof(CALLBACK_REQUEST_PACKET);
+
+            KeResetEvent(&WorkList.NewWorkEvent);
+            break;
+
+        }
+        case IOCTL_KMTEST_USERMODE_SEND_RESPONSE: 
+        {
+            PLIST_ENTRY Entry; 
+            PKMT_USER_WORK_ENTRY WorkEntry;
+            PVOID Response;
+            ULONG ResponseSize = IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength;
+            BOOLEAN FoundEntry = FALSE;
+
+            if (IoStackLocation->Parameters.DeviceIoControl.InputBufferLength == sizeof(ULONG) && ResponseSize == sizeof(KMT_RESPONSE)) 
+            {
+                Response = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
+                if (Response == NULL) 
+                {
+                    Status = STATUS_INSUFFICIENT_RESOURCES;
+                    break;
+                }
+
+                ExAcquireFastMutex(&WorkList.Lock);
+
+                Entry = WorkList.ListHead.Flink;
+
+                while (Entry != &WorkList.ListHead) 
+                {
+
+                    WorkEntry = CONTAINING_RECORD(Entry, KMT_USER_WORK_ENTRY, ListEntry);
+                    if (WorkEntry->Request.RequestId == *((PULONG)Irp->AssociatedIrp.SystemBuffer))
+                    {
+
+                        FoundEntry = TRUE;
+                        WorkEntry->Response = ExAllocatePoolWithTag(PagedPool, sizeof(KMT_RESPONSE), 'pseR');
+                        if (WorkEntry->Response == NULL)
+                        {
+                            Status = STATUS_INSUFFICIENT_RESOURCES;
+                            break;
+                        }
+
+                        RtlCopyMemory(WorkEntry->Response, Response, ResponseSize);
+                        KeSetEvent(&WorkEntry->WorkDoneEvent, IO_NO_INCREMENT, FALSE);
+                        Status = STATUS_SUCCESS;
+                        Length = IoStackLocation->Parameters.DeviceIoControl.InputBufferLength;
+                        break;
+                    }
+
+                    Entry = Entry->Flink;
+
+                }
+
+                ExReleaseFastMutex(&WorkList.Lock);
+
+                if (!FoundEntry) 
+                {
+                    Status = STATUS_OBJECTID_NOT_FOUND;
+                }
+            }
+            else 
+            {
+                Status = STATUS_INVALID_BUFFER_SIZE;
+            }
+           
+            break;
+        }
         default:
             DPRINT1("DriverIoControl. Invalid IoCtl code 0x%08X\n",
                      IoStackLocation->Parameters.DeviceIoControl.IoControlCode);
-            Status = STATUS_INVALID_DEVICE_REQUEST;
+            Status = STATUS_OBJECTID_NOT_FOUND;
             break;
     }
 
@@ -417,3 +535,111 @@
 
     return Status;
 }
+
+//Enqueue a request to the usermode callback queue and blocks until the work is finished.
+PKMT_RESPONSE 
+KmtUserModeCallback(
+    IN CALLBACK_INFORMATION_CLASS Operation, 
+    IN PVOID Parameters) 
+{
+    PKMT_RESPONSE Result = NULL;
+
+    PAGED_CODE();
+
+    switch (Operation) 
+    {
+
+        case QueryVirtualMemory: 
+        {
+            NTSTATUS Status;
+            PLIST_ENTRY Entry = NULL;
+            PKMT_USER_WORK_ENTRY WorkEntry = NULL;
+            LARGE_INTEGER Timeout;
+
+            Timeout.QuadPart = Int32x32To64(-10, 1000 * 1000 * 10); //make the timeout 10 seconds relative 
+
+            WorkEntry = ExAllocatePoolWithTag(PagedPool, sizeof(KMT_USER_WORK_ENTRY), 'ekrW');
+
+            if (WorkEntry == NULL) 
+            {
+                break;
+            }
+
+            KeInitializeEvent(&WorkEntry->WorkDoneEvent, NotificationEvent, FALSE);
+
+            WorkEntry->Request.RequestId = RequestId++;
+            WorkEntry->Request.OperationType = Operation;
+            WorkEntry->Request.Parameters = Parameters;
+            WorkEntry->Response = NULL;
+
+            ExAcquireFastMutex(&WorkList.Lock);
+            InsertTailList(&WorkList.ListHead, &WorkEntry->ListEntry);
+            ExReleaseFastMutex(&WorkList.Lock);
+
+            KeSetEvent(&WorkList.NewWorkEvent, IO_NO_INCREMENT, FALSE);
+
+            Status = KeWaitForSingleObject(&WorkEntry->WorkDoneEvent, Executive, UserMode, FALSE, &Timeout);
+
+            if (Status == STATUS_USER_APC || Status == STATUS_KERNEL_APC || Status == STATUS_TIMEOUT)
+            {
+                DbgPrint("Unexpected callback abortion! Reason: %x\n", Status);
+                break;
+            }
+
+            ExAcquireFastMutex(&WorkList.Lock);
+            RemoveEntryList(&WorkEntry->ListEntry);
+            ExReleaseFastMutex(&WorkList.Lock);
+
+            Result = WorkEntry->Response;
+
+            ExFreePoolWithTag(WorkEntry, 'ekrW');
+            break;
+        }
+        default: 
+        {
+            DPRINT1("UNRECOGNISED USERMODE CALLBACK\n");
+            break;
+        }
+
+    }
+
+    return Result;
+}
+
+VOID
+KmtFreeCallbackResponse(PKMT_RESPONSE Response) 
+{
+    PAGED_CODE();
+
+    ExFreePoolWithTag(Response, 'pseR');
+
+}
+
+static 
+VOID 
+KmtCleanUsermodeCallbacks(VOID) 
+{
+    PAGED_CODE();
+
+    ExAcquireFastMutex(&WorkList.Lock);
+    if (!IsListEmpty(&WorkList.ListHead)) 
+    {
+        PLIST_ENTRY Entry = WorkList.ListHead.Flink;
+
+        while (Entry != &WorkList.ListHead) 
+        {
+
+            PKMT_USER_WORK_ENTRY WorkEntry = CONTAINING_RECORD(Entry, KMT_USER_WORK_ENTRY, ListEntry);
+            if (WorkEntry->Response != NULL)
+            {
+                KmtFreeCallbackResponse(WorkEntry->Response);
+            }
+
+            Entry = Entry->Flink;
+            
+            ExFreePoolWithTag(WorkEntry, 'ekrW');
+        }
+    }
+
+    ExReleaseFastMutex(&WorkList.Lock);
+}
\ No newline at end of file
Index: kmtests/kmtest_drv/testlist.c
===================================================================
--- kmtests/kmtest_drv/testlist.c	(revision 57746)
+++ kmtests/kmtest_drv/testlist.c	(working copy)
@@ -45,6 +45,7 @@
 KMT_TESTFUNC Test_RtlException;
 KMT_TESTFUNC Test_RtlMemory;
 KMT_TESTFUNC Test_RtlSplayTree;
+KMT_TESTFUNC Test_ZwAllocateVirtualMemory;
 
 const KMT_TEST TestList[] =
 {
@@ -86,5 +87,6 @@
     { "RtlExceptionKM",                     Test_RtlException },
     { "RtlMemoryKM",                        Test_RtlMemory },
     { "RtlSplayTreeKM",                     Test_RtlSplayTree },
+	{"ZwAllocateVirtualMemory", 			Test_ZwAllocateVirtualMemory},
     { NULL,                                 NULL }
 };
