Author: tfaber
Date: Sat Apr 27 18:18:17 2013
New Revision: 58873
URL:
http://svn.reactos.org/svn/reactos?rev=58873&view=rev
Log:
[KMTESTS]
- Implement a mechanism for kmtests to retrieve information from user-mode. Patch by
Nikolay Borisov (nib9 at aber dot ac dot uk).
- Currently supported function: QueryVirtualMemory
ROSTESTS-96 #resolve
Modified:
trunk/rostests/kmtests/include/kmt_public.h
trunk/rostests/kmtests/include/kmt_test.h
trunk/rostests/kmtests/kmtest/support.c
trunk/rostests/kmtests/kmtest_drv/kmtest_drv.c
Modified: trunk/rostests/kmtests/include/kmt_public.h
URL:
http://svn.reactos.org/svn/reactos/trunk/rostests/kmtests/include/kmt_publi…
==============================================================================
--- trunk/rostests/kmtests/include/kmt_public.h [iso-8859-1] (original)
+++ trunk/rostests/kmtests/include/kmt_public.h [iso-8859-1] Sat Apr 27 18:18:17 2013
@@ -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
Modified: trunk/rostests/kmtests/include/kmt_test.h
URL:
http://svn.reactos.org/svn/reactos/trunk/rostests/kmtests/include/kmt_test.…
==============================================================================
--- trunk/rostests/kmtests/include/kmt_test.h [iso-8859-1] (original)
+++ trunk/rostests/kmtests/include/kmt_test.h [iso-8859-1] Sat Apr 27 18:18:17 2013
@@ -37,6 +37,48 @@
LONG LogBufferMaxLength;
CHAR LogBuffer[ANYSIZE_ARRAY];
} KMT_RESULTBUFFER, *PKMT_RESULTBUFFER;
+
+#ifndef KMT_STANDALONE_DRIVER
+
+/* usermode call-back mechanism */
+
+/* list of supported operations */
+typedef enum _KMT_CALLBACK_INFORMATION_CLASS
+{
+ QueryVirtualMemory
+} KMT_CALLBACK_INFORMATION_CLASS, *PKMT_CALLBACK_INFORMATION_CLASS;
+
+/* TODO: "response" is a little generic */
+typedef union _KMT_RESPONSE
+{
+ MEMORY_BASIC_INFORMATION MemInfo;
+} KMT_RESPONSE, *PKMT_RESPONSE;
+
+/* this struct is sent from driver to usermode */
+typedef struct _KMT_CALLBACK_REQUEST_PACKET
+{
+ ULONG RequestId;
+ KMT_CALLBACK_INFORMATION_CLASS OperationClass;
+ PVOID Parameters;
+} KMT_CALLBACK_REQUEST_PACKET, *PKMT_CALLBACK_REQUEST_PACKET;
+
+PKMT_RESPONSE KmtUserModeCallback(KMT_CALLBACK_INFORMATION_CLASS Operation, PVOID
Parameters);
+VOID KmtFreeCallbackResponse(PKMT_RESPONSE Response);
+
+//macro to simplify using the mechanism
+#define Test_NtQueryVirtualMemory(BaseAddress, Size, AllocationType, ProtectionType)
\
+ do {
\
+ PKMT_RESPONSE NtQueryTest = KmtUserModeCallback(QueryVirtualMemory, BaseAddress);
\
+ if (NtQueryTest != NULL)
\
+ {
\
+ ok_eq_hex(NtQueryTest->MemInfo.Protect, ProtectionType);
\
+ ok_eq_hex(NtQueryTest->MemInfo.State, AllocationType);
\
+ ok_eq_size(NtQueryTest->MemInfo.RegionSize, Size);
\
+ KmtFreeCallbackResponse(NtQueryTest);
\
+ }
\
+ } while (0)
\
+
+#endif
#ifdef KMT_STANDALONE_DRIVER
#define KMT_KERNEL_MODE
Modified: trunk/rostests/kmtests/kmtest/support.c
URL:
http://svn.reactos.org/svn/reactos/trunk/rostests/kmtests/kmtest/support.c?…
==============================================================================
--- trunk/rostests/kmtests/kmtest/support.c [iso-8859-1] (original)
+++ trunk/rostests/kmtests/kmtest/support.c [iso-8859-1] Sat Apr 27 18:18:17 2013
@@ -11,8 +11,73 @@
#include <kmt_public.h>
#include <assert.h>
+#include <debug.h>
extern HANDLE KmtestHandle;
+
+/**
+ * @name KmtUserCallbackThread
+ *
+ * Thread routine which awaits callback requests from kernel-mode
+ *
+ * @return Win32 error code
+ */
+DWORD
+WINAPI
+KmtUserCallbackThread(
+ PVOID Parameter)
+{
+ DWORD Error = ERROR_SUCCESS;
+ /* TODO: RequestPacket? */
+ KMT_CALLBACK_REQUEST_PACKET RequestPacket;
+ KMT_RESPONSE Response;
+ DWORD BytesReturned;
+ HANDLE LocalKmtHandle;
+
+ UNREFERENCED_PARAMETER(Parameter);
+
+ /* 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);
+ if (LocalKmtHandle == INVALID_HANDLE_VALUE)
+ error_goto(Error, cleanup);
+
+ while (1)
+ {
+ if (!DeviceIoControl(LocalKmtHandle, IOCTL_KMTEST_USERMODE_AWAIT_REQ, NULL, 0,
&RequestPacket, sizeof(RequestPacket), &BytesReturned, NULL))
+ error_goto(Error, cleanup);
+ ASSERT(BytesReturned == sizeof(RequestPacket));
+
+ switch (RequestPacket.OperationClass)
+ {
+ case QueryVirtualMemory:
+ {
+ SIZE_T InfoBufferSize = VirtualQuery(RequestPacket.Parameters,
&Response.MemInfo, sizeof(Response.MemInfo));
+ /* FIXME: an error is a valid result. That should go as a response to
kernel mode instead of terminating the thread */
+ if (InfoBufferSize == 0)
+ error_goto(Error, cleanup);
+
+ if (!DeviceIoControl(LocalKmtHandle, IOCTL_KMTEST_USERMODE_SEND_RESPONSE,
&RequestPacket.RequestId, sizeof(RequestPacket.RequestId), &Response,
sizeof(Response), &BytesReturned, NULL))
+ error_goto(Error, cleanup);
+ ASSERT(BytesReturned == 0);
+
+ break;
+ }
+ default:
+ DPRINT1("Unrecognized user-mode callback request\n");
+ break;
+ }
+ }
+
+cleanup:
+ if (LocalKmtHandle != INVALID_HANDLE_VALUE)
+ CloseHandle(LocalKmtHandle);
+
+ DPRINT("Callback handler dying! Error code %lu", Error);
+ return Error;
+}
+
/**
* @name KmtRunKernelTest
@@ -28,11 +93,17 @@
KmtRunKernelTest(
IN PCSTR TestName)
{
+ HANDLE CallbackThread;
DWORD Error = ERROR_SUCCESS;
DWORD BytesRead;
+
+ 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);
+
+ if (CallbackThread != NULL)
+ CloseHandle(CallbackThread);
return Error;
}
Modified: trunk/rostests/kmtests/kmtest_drv/kmtest_drv.c
URL:
http://svn.reactos.org/svn/reactos/trunk/rostests/kmtests/kmtest_drv/kmtest…
==============================================================================
--- trunk/rostests/kmtests/kmtest_drv/kmtest_drv.c [iso-8859-1] (original)
+++ trunk/rostests/kmtests/kmtest_drv/kmtest_drv.c [iso-8859-1] Sat Apr 27 18:18:17 2013
@@ -18,6 +18,22 @@
#include <kmt_public.h>
#define KMT_DEFINE_TEST_FUNCTIONS
#include <kmt_test.h>
+
+/* Usermode callback definitions */
+typedef struct _KMT_USER_WORK_ENTRY
+{
+ LIST_ENTRY ListEntry;
+ KEVENT WorkDoneEvent;
+ KMT_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;
@@ -30,10 +46,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 +111,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,6 +145,8 @@
UNREFERENCED_PARAMETER(DriverObject);
DPRINT("DriverUnload\n");
+
+ KmtCleanUsermodeCallbacks();
if (MainDeviceObject)
{
@@ -403,6 +428,93 @@
ResultBuffer->LogBufferLength,
ResultBuffer->LogBufferMaxLength);
break;
}
+ case IOCTL_KMTEST_USERMODE_AWAIT_REQ:
+ {
+ PLIST_ENTRY Entry;
+ PKMT_USER_WORK_ENTRY WorkItem;
+
+ DPRINT("DriverIoControl. IOCTL_KMTEST_USERMODE_AWAIT_REQ,
len=%lu\n",
+ IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength);
+
+ /* TODO: prevent multiple concurrent invocations */
+ Status = KeWaitForSingleObject(&WorkList.NewWorkEvent, UserRequest,
UserMode, FALSE, NULL);
+ if (Status == STATUS_USER_APC || Status == STATUS_KERNEL_APC)
+ break;
+
+ if (IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength <
sizeof(KMT_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);
+
+ Length = sizeof(WorkItem->Request);
+ RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer, &WorkItem->Request,
Length);
+ Status = STATUS_SUCCESS;
+
+ 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;
+
+ DPRINT("DriverIoControl. IOCTL_KMTEST_USERMODE_SEND_RESPONSE, inlen=%lu,
outlen=%lu\n",
+ IoStackLocation->Parameters.DeviceIoControl.InputBufferLength,
+ IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength);
+
+ if (IoStackLocation->Parameters.DeviceIoControl.InputBufferLength !=
sizeof(ULONG) || ResponseSize != sizeof(KMT_RESPONSE))
+ {
+ Status = STATUS_INVALID_BUFFER_SIZE;
+ break;
+ }
+
+ /* FIXME: don't misuse the output buffer as an input! */
+ Response = MmGetSystemAddressForMdlSafe(Irp->MdlAddress,
NormalPagePriority);
+ if (Response == NULL)
+ {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ break;
+ }
+
+ ExAcquireFastMutex(&WorkList.Lock);
+
+ Status = STATUS_OBJECTID_NOT_FOUND;
+
+ 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)
+ {
+ 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;
+ break;
+ }
+
+ Entry = Entry->Flink;
+ }
+
+ ExReleaseFastMutex(&WorkList.Lock);
+
+ break;
+ }
default:
DPRINT1("DriverIoControl. Invalid IoCtl code 0x%08X\n",
IoStackLocation->Parameters.DeviceIoControl.IoControlCode);
@@ -417,3 +529,113 @@
return Status;
}
+
+/**
+ * @name KmtUserModeCallback
+ *
+ * Enqueue a request to the usermode callback queue and blocks until the work
+ * is finished.
+ *
+ * @param Operation
+ * TODO
+ * @param Parameters
+ * TODO
+ * TODO: why is this PVOID?
+ *
+ * @return Response from user mode
+ */
+PKMT_RESPONSE
+KmtUserModeCallback(
+ IN KMT_CALLBACK_INFORMATION_CLASS Operation,
+ IN PVOID Parameters)
+{
+ PKMT_RESPONSE Result;
+ NTSTATUS Status;
+ PKMT_USER_WORK_ENTRY WorkEntry;
+ LARGE_INTEGER Timeout;
+
+ PAGED_CODE();
+
+ WorkEntry = ExAllocatePoolWithTag(PagedPool, sizeof(KMT_USER_WORK_ENTRY),
'ekrW');
+ if (WorkEntry == NULL)
+ return NULL;
+
+ KeInitializeEvent(&WorkEntry->WorkDoneEvent, NotificationEvent, FALSE);
+ WorkEntry->Request.RequestId = RequestId++;
+ WorkEntry->Request.OperationClass = 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);
+
+ Timeout.QuadPart = -10 * 1000 * 1000 * 10; //wait for 10 seconds
+ Status = KeWaitForSingleObject(&WorkEntry->WorkDoneEvent, Executive, UserMode,
FALSE, &Timeout);
+
+ if (Status == STATUS_USER_APC || Status == STATUS_KERNEL_APC || Status ==
STATUS_TIMEOUT)
+ {
+ DPRINT1("Unexpected callback abortion! Reason: %lx\n", Status);
+ }
+
+ ExAcquireFastMutex(&WorkList.Lock);
+ RemoveEntryList(&WorkEntry->ListEntry);
+ ExReleaseFastMutex(&WorkList.Lock);
+
+ Result = WorkEntry->Response;
+
+ ExFreePoolWithTag(WorkEntry, 'ekrW');
+
+ return Result;
+}
+
+/**
+ * @name KmtFreeCallbackResponse
+ *
+ * TODO
+ *
+ * @param Response
+ * TODO
+ */
+VOID
+KmtFreeCallbackResponse(
+ PKMT_RESPONSE Response)
+{
+ PAGED_CODE();
+
+ ExFreePoolWithTag(Response, 'pseR');
+}
+
+/**
+ * @name KmtCleanUsermodeCallbacks
+ *
+ * TODO
+ */
+static
+VOID
+KmtCleanUsermodeCallbacks(VOID)
+{
+ PLIST_ENTRY Entry;
+
+ PAGED_CODE();
+
+ ExAcquireFastMutex(&WorkList.Lock);
+
+ 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);
+}