* PROJECT: ReactOS kernel-mode tests
* LICENSE: GPLv2+ - See COPYING in the top level directory
* PURPOSE: Kernel-Mode Test Suite Driver
- * PROGRAMMER: Thomas Faber <thfabba@gmx.de>
+ * PROGRAMMER: Thomas Faber <thomas.faber@reactos.org>
*/
#include <ntddk.h>
#include <limits.h>
#include <pseh/pseh2.h>
-//#define NDEBUG
+#define NDEBUG
#include <debug.h>
#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;
static DRIVER_UNLOAD DriverUnload;
+__drv_dispatchType(IRP_MJ_CREATE)
static DRIVER_DISPATCH DriverCreate;
+__drv_dispatchType(IRP_MJ_CLEANUP)
+static DRIVER_DISPATCH DriverCleanup;
+__drv_dispatchType(IRP_MJ_CLOSE)
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 */
/**
DriverObject->DriverUnload = DriverUnload;
DriverObject->MajorFunction[IRP_MJ_CREATE] = DriverCreate;
+ DriverObject->MajorFunction[IRP_MJ_CLEANUP] = DriverCleanup;
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))
{
DPRINT("DriverUnload\n");
+ KmtCleanUsermodeCallbacks();
+
if (MainDeviceObject)
{
+#if DBG
PKMT_DEVICE_EXTENSION DeviceExtension = MainDeviceObject->DeviceExtension;
ASSERT(!DeviceExtension->Mdl);
ASSERT(!DeviceExtension->ResultBuffer);
+#endif
ASSERT(!ResultBuffer);
IoDeleteDevice(MainDeviceObject);
}
/**
* @name DriverCreate
*
- * Driver Dispatch function for CreateFile
+ * Driver Dispatch function for IRP_MJ_CREATE
*
* @param DeviceObject
* Device Object
}
/**
- * @name DriverClose
+ * @name DriverCleanup
*
- * Driver Dispatch function for CloseHandle.
+ * Driver Dispatch function for IRP_MJ_CLEANUP
*
* @param DeviceObject
* Device Object
static
NTSTATUS
NTAPI
-DriverClose(
+DriverCleanup(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
{
IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
- DPRINT("DriverClose. DeviceObject=%p, RequestorMode=%d, FileObject=%p, FsContext=%p, FsContext2=%p\n",
+ DPRINT("DriverCleanup. DeviceObject=%p, RequestorMode=%d, FileObject=%p, FsContext=%p, FsContext2=%p\n",
DeviceObject, Irp->RequestorMode, IoStackLocation->FileObject,
IoStackLocation->FileObject->FsContext, IoStackLocation->FileObject->FsContext2);
return Status;
}
+/**
+ * @name DriverClose
+ *
+ * Driver Dispatch function for IRP_MJ_CLOSE
+ *
+ * @param DeviceObject
+ * Device Object
+ * @param Irp
+ * I/O request packet
+ *
+ * @return Status
+ */
+static
+NTSTATUS
+NTAPI
+DriverClose(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp)
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ PAGED_CODE();
+
+ DPRINT("DriverClose. DeviceObject=%p, RequestorMode=%d\n",
+ DeviceObject, Irp->RequestorMode);
+
+ Irp->IoStatus.Status = Status;
+ Irp->IoStatus.Information = 0;
+
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ return Status;
+}
+
/**
* @name DriverIoControl
*
- * Driver Dispatch function for DeviceIoControl.
+ * Driver Dispatch function for IRP_MJ_DEVICE_CONTROL
*
* @param DeviceObject
* Device Object
{
NTSTATUS Status = STATUS_SUCCESS;
PIO_STACK_LOCATION IoStackLocation;
- ULONG Length = 0;
+ SIZE_T Length = 0;
PAGED_CODE();
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);
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);
+}