Index: include/kmt_public.h
===================================================================
--- include/kmt_public.h	(revision 57563)
+++ include/kmt_public.h	(working copy)
@@ -16,7 +16,13 @@
 
 #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_BUFFERED, 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: include/kmt_test.h
===================================================================
--- include/kmt_test.h	(revision 57563)
+++ include/kmt_test.h	(working copy)
@@ -38,6 +38,32 @@
     CHAR LogBuffer[ANYSIZE_ARRAY];
 } KMT_RESULTBUFFER, *PKMT_RESULTBUFFER;
 
+/////////////////////////////////////////////////////
+// STRUCTURES FOR THE USERMODE CALL BACK MECHANISM
+/////////////////////////////////////////////////////
+
+//list of supported operations
+typedef enum _CALLBACK_INFORMATION_CLASS {
+    QueryVirtualMemory
+} CallbackOperation;
+
+//this struct is sent from driver to usermode
+typedef struct _CALLBACK_REQUEST_PACKET {
+    CallbackOperation OperationType;
+    PVOID Parameters;
+} CALLBACK_REQUEST_PACKET, *PCALLBACK_REQUEST_PACKET;
+
+//this struct is sent from usermode to driver
+typedef struct _CALLBACK_RESPONSE_PACKET {
+    CallbackOperation OperationType;
+    PVOID Response;
+} CALLBACK_RESPONSE_PACKET, *PCALLBACK_RESPONSE_PACKET;
+
+/////////////////////////////////////////////////////
+
+
+
+
 #ifdef KMT_STANDALONE_DRIVER
 #define KMT_KERNEL_MODE
 
@@ -86,6 +112,7 @@
 extern PDRIVER_OBJECT KmtDriverObject;
 
 VOID KmtSetIrql(IN KIRQL NewIrql);
+PVOID KmtUserModeCallback(CallbackOperation Operation, PVOID Parameters);
 BOOLEAN KmtAreInterruptsEnabled(VOID);
 ULONG KmtGetPoolTag(PVOID Memory);
 #elif defined KMT_USER_MODE
Index: kmtest/support.c
===================================================================
--- kmtest/support.c	(revision 57693)
+++ kmtest/support.c	(working copy)
@@ -14,7 +14,80 @@
 
 extern HANDLE KmtestHandle;
 
+// set to true when the current test run is finished.
+BOOLEAN KmtFinishedTest;
+
 /**
+* @name KmtUserCallbackThread
+*
+* Routine which awaits callback requests from kernel-mode
+*
+*
+* @return Win32 error code as returned by DeviceIoControl/HeapAlloc/VirtualQuery
+*/
+DWORD WINAPI 
+KmtUserCallbackThread(LPVOID Unused) 
+{ 
+    DWORD Error = ERROR_SUCCESS;
+    CALLBACK_REQUEST_PACKET OutputBuffer;
+    SIZE_T UserReturned;
+    PVOID Response;
+    DWORD BytesReturned;
+    HANDLE LocalKmtHandle = INVALID_HANDLE_VALUE;
+
+    printf("[USERMODE CALLBACK] Thread started\n");
+
+    //avoid a deadlock shitty deadlock. 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 (!KmtFinishedTest) 
+    {
+        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(MEMORY_BASIC_INFORMATION));
+                    if (Response == NULL) 
+                        error_goto(Error, cleanup);
+
+                    UserReturned = VirtualQuery(OutputBuffer.Parameters, Response, sizeof(MEMORY_BASIC_INFORMATION));
+                    if (UserReturned == 0) 
+                        error_goto(Error, cleanup);
+
+                    if (!DeviceIoControl(LocalKmtHandle, IOCTL_KMTEST_USERMODE_SEND_RESPONSE, Response, sizeof(MEMORY_BASIC_INFORMATION), NULL, 0, NULL, NULL))
+                        error_goto(Error, cleanup);
+
+                    HeapFree(GetProcessHeap(),  0, Response);
+                    break;
+                }    
+                default: 
+                {
+                    printf("UNRECOGNISED USER-MODE CALLBACK REQUEST\n");
+                    break;
+                }
+
+            }
+        } 
+        else 
+        {
+            error_goto(Error, cleanup);
+        }
+    }
+
+
+cleanup:
+    if (LocalKmtHandle != INVALID_HANDLE_VALUE) 
+    {
+        CloseHandle(LocalKmtHandle);
+    }
+
+    return Error;
+}
+
+
+/**
  * @name KmtRunKernelTest
  *
  * Run the specified kernel-mode test part
@@ -28,12 +101,18 @@
 KmtRunKernelTest(
     IN PCSTR TestName)
 {
+    HANDLE CallbackThread = INVALID_HANDLE_VALUE;
     DWORD Error = ERROR_SUCCESS;
     DWORD BytesRead;
-
+    KmtFinishedTest = FALSE;
+    
+    
+    CallbackThread = CreateThread(NULL, 0, KmtUserCallbackThread, NULL, 0, NULL);
     if (!DeviceIoControl(KmtestHandle, IOCTL_KMTEST_RUN_TEST, (PVOID)TestName, (DWORD)strlen(TestName), NULL, 0, &BytesRead, NULL))
         error(Error);
-
+    
+    KmtFinishedTest = TRUE;
+    CloseHandle(CallbackThread);
     return Error;
 }
 
Index: kmtest_drv/kmtest_drv.c
===================================================================
--- kmtest_drv/kmtest_drv.c	(revision 57693)
+++ 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;
+    PCALLBACK_RESPONSE_PACKET 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,12 @@
 static DRIVER_DISPATCH DriverClose;
 __drv_dispatchType(IRP_MJ_DEVICE_CONTROL)
 static DRIVER_DISPATCH DriverIoControl;
+static VOID KmtCleanUsermodeCallbacks();
 
 /* Globals */
 static PDEVICE_OBJECT MainDeviceObject;
 PDRIVER_OBJECT KmtDriverObject = NULL;
+PKMT_USER_WORK_LIST WorkList = NULL;
 
 /* Entry */
 /**
@@ -92,6 +111,17 @@
     DriverObject->MajorFunction[IRP_MJ_CLOSE] = DriverClose;
     DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DriverIoControl;
 
+	/* Init the usermode callback structure */
+    WorkList = (PKMT_USER_WORK_LIST) ExAllocatePoolWithTag(NonPagedPool, sizeof(KMT_USER_WORK_LIST), 'kroW');
+    if (WorkList == NULL) 
+    {
+        goto cleanup;
+    }
+
+    ExInitializeFastMutex(&WorkList->Lock);
+    KeInitializeEvent(&WorkList->NewWorkEvent, SynchronizationEvent, FALSE);
+    InitializeListHead(&WorkList->ListHead);
+	
 cleanup:
     if (MainDeviceObject && !NT_SUCCESS(Status))
     {
@@ -122,7 +152,7 @@
     UNREFERENCED_PARAMETER(DriverObject);
 
     DPRINT("DriverUnload\n");
-
+	KmtCleanUsermodeCallbacks();
     if (MainDeviceObject)
     {
         PKMT_DEVICE_EXTENSION DeviceExtension = MainDeviceObject->DeviceExtension;
@@ -403,6 +433,61 @@
                     ResultBuffer->LogBufferLength, ResultBuffer->LogBufferMaxLength);
             break;
         }
+        case IOCTL_KMTEST_USERMODE_AWAIT_REQ:
+        {
+            PLIST_ENTRY Entry = NULL;
+            PKMT_USER_WORK_ENTRY WorkItem = NULL;
+
+            Status = KeWaitForSingleObject(&WorkList->NewWorkEvent, UserRequest, UserMode, FALSE, NULL);
+            if (Status == STATUS_USER_APC || Status == STATUS_KERNEL_APC)
+            {
+                break;
+            }
+
+            trace("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;
+                Length = 0;
+                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);
+
+
+            break;
+
+        }
+        case IOCTL_KMTEST_USERMODE_SEND_RESPONSE: 
+        {
+            PLIST_ENTRY Entry = NULL; 
+            PKMT_USER_WORK_ENTRY WorkItem = NULL;
+
+            Entry = WorkList->ListHead.Flink;
+            WorkItem = CONTAINING_RECORD(Entry, KMT_USER_WORK_ENTRY, ListEntry);
+            WorkItem->Response = ExAllocatePoolWithTag(PagedPool, IoStackLocation->Parameters.DeviceIoControl.InputBufferLength, 'pseR');
+
+            if (WorkItem->Response == NULL) 
+            {
+                Status = STATUS_INSUFFICIENT_RESOURCES;
+                Length = 0;
+                break;
+            }
+
+            RtlCopyMemory(WorkItem->Response, Irp->AssociatedIrp.SystemBuffer, IoStackLocation->Parameters.DeviceIoControl.InputBufferLength);
+            Status = STATUS_SUCCESS;
+            Length = IoStackLocation->Parameters.DeviceIoControl.InputBufferLength;
+            KeSetEvent(&WorkItem->WorkDoneEvent, IO_NO_INCREMENT, FALSE);
+            break;
+        }
         default:
             DPRINT1("DriverIoControl. Invalid IoCtl code 0x%08X\n",
                      IoStackLocation->Parameters.DeviceIoControl.IoControlCode);
@@ -417,3 +502,97 @@
 
     return Status;
 }
+
+//Enqueus a request to the usermode callback queue and blocks until the work is finished.
+PVOID KmtUserModeCallback(IN CallbackOperation Operation, IN PVOID Parameters) 
+{
+    PVOID Result = NULL;
+
+    PAGED_CODE();
+    switch(Operation) {
+
+    case QueryVirtualMemory: 
+        {
+            NTSTATUS Status;
+            PLIST_ENTRY Entry = NULL;
+            PKMT_USER_WORK_ENTRY WorkEntry = NULL;
+
+            WorkEntry = ExAllocatePoolWithTag(PagedPool, sizeof(KMT_USER_WORK_ENTRY), 'ekrW');
+            
+            if (WorkEntry == NULL) 
+            {
+                break;
+            }
+            
+            KeInitializeEvent(&WorkEntry->WorkDoneEvent, SynchronizationEvent, FALSE);
+            
+            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, NULL);
+            if (Status == STATUS_USER_APC || Status == STATUS_KERNEL_APC)
+            {
+                break;
+            }
+
+            WorkEntry = NULL; //pointer reuse
+
+            ExAcquireFastMutex(&WorkList->Lock);
+            Entry = RemoveHeadList(&WorkList->ListHead);
+            ExReleaseFastMutex(&WorkList->Lock);
+
+            WorkEntry = CONTAINING_RECORD(Entry, KMT_USER_WORK_ENTRY, ListEntry);
+            Result = WorkEntry->Response;
+            
+            ExFreePoolWithTag(WorkEntry, 'ekrW');
+            break;
+        }
+
+    default: 
+        {
+            trace("UNRECOGNISED USERMODE CALLBACK\n");
+            break;
+        }
+
+    }
+
+    return Result;
+}
+
+static VOID KmtCleanUsermodeCallbacks() 
+{
+    PAGED_CODE();
+
+    if(!IsListEmpty(&WorkList->ListHead)) 
+    {
+        PLIST_ENTRY Entry = WorkList->ListHead.Flink;
+
+        while (Entry != &WorkList->ListHead) 
+        {
+            PLIST_ENTRY EntryCopy;
+
+            PKMT_USER_WORK_ENTRY WorkEntry = (PKMT_USER_WORK_ENTRY)CONTAINING_RECORD(Entry, KMT_USER_WORK_ENTRY, ListEntry);
+            if(WorkEntry->Response != NULL)
+            {
+                ExFreePoolWithTag(WorkEntry->Response, 'pseR');
+            }
+
+            EntryCopy = Entry;
+            Entry = Entry->Flink;
+            
+            ExFreePoolWithTag(EntryCopy, 'ekrW');
+
+        }
+    }
+
+    ExFreePoolWithTag(WorkList, 'kroW');
+
+}
+
