diff --git a/kmtests/CMakeLists.txt b/kmtests/CMakeLists.txt
index c80a939db76..7a4e104d397 100644
--- a/kmtests/CMakeLists.txt
+++ b/kmtests/CMakeLists.txt
@@ -1,6 +1,32 @@
include_directories(
include)
+#
+# subdirectories containing special-purpose drivers
+#
+#add_subdirectory(something)
+
+#
+# kmtest_drv.sys driver
+#
+list(APPEND KMTEST_DRV_SOURCE
+ kmtest_drv/kmtest_drv.c
+ kmtest_drv/log.c
+ kmtest_drv/testlist.c
+
+ kmtest_drv/kmtest_drv.rc)
+
+add_library(kmtest_drv SHARED ${KMTEST_DRV_SOURCE})
+
+set_module_type(kmtest_drv kernelmodedriver)
+target_link_libraries(kmtest_drv ${PSEH_LIB})
+add_importlibs(kmtest_drv ntoskrnl hal)
+
+add_cd_file(TARGET kmtest_drv DESTINATION reactos/system32/drivers FOR all)
+
+#
+# kmtest.exe loader application
+#
set_rc_compiler()
add_definitions(-D_DLL -D__USE_CRTIMP)
diff --git a/kmtests/directory.rbuild b/kmtests/directory.rbuild
index 2e657904df2..5d6c89d4c4b 100644
--- a/kmtests/directory.rbuild
+++ b/kmtests/directory.rbuild
@@ -5,4 +5,5 @@
-->
+
diff --git a/kmtests/include/kmt_log.h b/kmtests/include/kmt_log.h
new file mode 100644
index 00000000000..53f2b536277
--- /dev/null
+++ b/kmtests/include/kmt_log.h
@@ -0,0 +1,21 @@
+/*
+ * PROJECT: ReactOS kernel-mode tests
+ * LICENSE: GPLv2+ - See COPYING in the top level directory
+ * PURPOSE: Kernel-Mode Test Suite Driver logging function declarations
+ * PROGRAMMER: Thomas Faber
+ */
+
+#ifndef _KMTEST_LOG_H_
+#define _KMTEST_LOG_H_
+
+#include
+
+NTSTATUS LogInit(VOID);
+VOID LogFree(VOID);
+
+VOID LogPrint(IN PCSTR Message);
+VOID LogPrintF(IN PCSTR Format, ...);
+VOID LogVPrintF(IN PCSTR Format, va_list Arguments);
+SIZE_T LogRead(OUT PVOID Buffer, IN SIZE_T BufferSize);
+
+#endif /* !defined _KMTEST_LOG_H_ */
diff --git a/kmtests/include/kmt_test.h b/kmtests/include/kmt_test.h
new file mode 100644
index 00000000000..ff045ee91af
--- /dev/null
+++ b/kmtests/include/kmt_test.h
@@ -0,0 +1,25 @@
+/*
+ * PROJECT: ReactOS kernel-mode tests
+ * LICENSE: GPLv2+ - See COPYING in the top level directory
+ * PURPOSE: Kernel-Mode Test Suite test declarations
+ * PROGRAMMER: Thomas Faber
+ */
+
+#ifndef _KMTEST_TEST_H_
+#define _KMTEST_TEST_H_
+
+#include
+
+typedef void KMT_TESTFUNC(void);
+
+typedef struct
+{
+ const char *TestName;
+ KMT_TESTFUNC *TestFunction;
+} KMT_TEST, *PKMT_TEST;
+
+typedef const KMT_TEST CKMT_TEST, *PCKMT_TEST;
+
+extern const KMT_TEST TestList[];
+
+#endif /* !defined _KMTEST_TEST_H_ */
diff --git a/kmtests/kmtest_drv.rbuild b/kmtests/kmtest_drv.rbuild
new file mode 100644
index 00000000000..ce48ce2e4c9
--- /dev/null
+++ b/kmtests/kmtest_drv.rbuild
@@ -0,0 +1,12 @@
+
+ include
+ ntoskrnl
+ ntdll
+ hal
+ pseh
+
+ kmtest_drv.c
+ log.c
+ testlist.c
+
+
diff --git a/kmtests/kmtest_drv/kmtest_drv.c b/kmtests/kmtest_drv/kmtest_drv.c
new file mode 100644
index 00000000000..d238d4f0672
--- /dev/null
+++ b/kmtests/kmtest_drv/kmtest_drv.c
@@ -0,0 +1,282 @@
+/*
+ * PROJECT: ReactOS kernel-mode tests
+ * LICENSE: GPLv2+ - See COPYING in the top level directory
+ * PURPOSE: Kernel-Mode Test Suite Driver
+ * PROGRAMMER: Thomas Faber
+ */
+
+#include
+#include
+#include
+
+//#define NDEBUG
+#include
+
+#include
+#include
+#include
+
+/* Prototypes */
+DRIVER_INITIALIZE DriverEntry;
+static DRIVER_UNLOAD DriverUnload;
+static DRIVER_DISPATCH DriverCreateClose;
+static DRIVER_DISPATCH DriverIoControl;
+static DRIVER_DISPATCH DriverRead;
+
+/* Globals */
+static PDEVICE_OBJECT MainDeviceObject;
+
+/* Entry */
+/**
+ * @name DriverEntry
+ *
+ * Driver Entry point.
+ *
+ * @param DriverObject
+ * Driver Object
+ * @param RegistryPath
+ * Driver Registry Path
+ *
+ * @return Status
+ */
+NTSTATUS NTAPI DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ UNICODE_STRING DeviceName;
+ PAGED_CODE();
+
+ UNREFERENCED_PARAMETER(RegistryPath);
+
+ DPRINT("DriverEntry\n");
+
+ Status = LogInit();
+
+ if (!NT_SUCCESS(Status))
+ goto cleanup;
+
+ RtlInitUnicodeString(&DeviceName, L"\\Device\\Kmtest");
+ Status = IoCreateDevice(DriverObject, 0, &DeviceName, FILE_DEVICE_UNKNOWN,
+ FILE_DEVICE_SECURE_OPEN | FILE_READ_ONLY_DEVICE,
+ TRUE, &MainDeviceObject);
+
+ if (!NT_SUCCESS(Status))
+ goto cleanup;
+
+ DPRINT("DriverEntry. Created DeviceObject %p\n",
+ MainDeviceObject);
+ MainDeviceObject->Flags |= DO_DIRECT_IO;
+
+ DriverObject->DriverUnload = DriverUnload;
+ DriverObject->MajorFunction[IRP_MJ_CREATE] = DriverCreateClose;
+ DriverObject->MajorFunction[IRP_MJ_CLOSE] = DriverCreateClose;
+ DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DriverIoControl;
+ DriverObject->MajorFunction[IRP_MJ_READ] = DriverRead;
+
+cleanup:
+ if (MainDeviceObject && !NT_SUCCESS(Status))
+ {
+ IoDeleteDevice(MainDeviceObject);
+ MainDeviceObject = NULL;
+ }
+
+ return Status;
+}
+
+/* Dispatch functions */
+/**
+ * @name DriverUnload
+ *
+ * Driver cleanup funtion.
+ *
+ * @param DriverObject
+ * Driver Object
+ */
+static VOID NTAPI DriverUnload(IN PDRIVER_OBJECT DriverObject)
+{
+ PAGED_CODE();
+
+ UNREFERENCED_PARAMETER(DriverObject);
+
+ DPRINT("DriverUnload\n");
+
+ if (MainDeviceObject)
+ IoDeleteDevice(MainDeviceObject);
+
+ LogFree();
+}
+
+/**
+ * @name DriverCreateClose
+ *
+ * Driver Dispatch function for CreateFile/CloseHandle.
+ *
+ * @param DeviceObject
+ * Device Object
+ * @param Irp
+ * I/O request packet
+ *
+ * @return Status
+ */
+static NTSTATUS NTAPI DriverCreateClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PIO_STACK_LOCATION IoStackLocation;
+
+ PAGED_CODE();
+
+ IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
+
+ DPRINT("DriverCreateClose. Function=%s, DeviceObject=%p\n",
+ IoStackLocation->MajorFunction == IRP_MJ_CREATE ? "Create" : "Close",
+ DeviceObject);
+
+ Irp->IoStatus.Status = Status;
+ Irp->IoStatus.Information = 0;
+
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ return Status;
+}
+
+/**
+ * @name DriverIoControl
+ *
+ * Driver Dispatch function for DeviceIoControl.
+ *
+ * @param DeviceObject
+ * Device Object
+ * @param Irp
+ * I/O request packet
+ *
+ * @return Status
+ */
+static NTSTATUS NTAPI DriverIoControl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PIO_STACK_LOCATION IoStackLocation;
+ ULONG Length = 0;
+
+ PAGED_CODE();
+
+ IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
+
+ DPRINT("DriverIoControl. Code=0x%08X, DeviceObject=%p\n",
+ IoStackLocation->Parameters.DeviceIoControl.IoControlCode,
+ DeviceObject);
+
+ switch (IoStackLocation->Parameters.DeviceIoControl.IoControlCode)
+ {
+ case IOCTL_KMTEST_GET_TESTS:
+ {
+ PCKMT_TEST TestEntry;
+ LPSTR OutputBuffer = Irp->AssociatedIrp.SystemBuffer;
+ size_t Remaining = IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength;
+
+ DPRINT("DriverIoControl. IOCTL_KMTEST_GET_TESTS, outlen=%lu\n",
+ IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength);
+
+ for (TestEntry = TestList; TestEntry->TestName; ++TestEntry)
+ {
+ RtlStringCbCopyExA(OutputBuffer, Remaining, TestEntry->TestName, &OutputBuffer, &Remaining, 0);
+ if (Remaining)
+ {
+ *OutputBuffer++ = '\0';
+ --Remaining;
+ }
+ }
+ if (Remaining)
+ {
+ *OutputBuffer++ = '\0';
+ --Remaining;
+ }
+ Length = IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength - Remaining;
+ break;
+ }
+ case IOCTL_KMTEST_RUN_TEST:
+ {
+ ANSI_STRING TestName;
+ PCKMT_TEST TestEntry;
+
+ DPRINT("DriverIoControl. IOCTL_KMTEST_RUN_TEST, inlen=%lu, outlen=%lu\n",
+ IoStackLocation->Parameters.DeviceIoControl.InputBufferLength,
+ IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength);
+ TestName.Length = TestName.MaximumLength = (USHORT)min(IoStackLocation->Parameters.DeviceIoControl.InputBufferLength, USHRT_MAX);
+ TestName.Buffer = Irp->AssociatedIrp.SystemBuffer;
+ DPRINT("DriverIoControl. Run test: %Z\n", &TestName);
+
+ for (TestEntry = TestList; TestEntry->TestName; ++TestEntry)
+ {
+ ANSI_STRING EntryName;
+ RtlInitAnsiString(&EntryName, TestEntry->TestName);
+
+ if (!RtlCompareString(&TestName, &EntryName, FALSE))
+ {
+ DPRINT1("DriverIoControl. Starting test %Z\n", &EntryName);
+ TestEntry->TestFunction();
+ DPRINT1("DriverIoControl. Finished test %Z\n", &EntryName);
+ break;
+ }
+ }
+
+ if (!TestEntry->TestName)
+ Status = STATUS_OBJECT_NAME_INVALID;
+
+ break;
+ }
+ default:
+ DPRINT1("DriverIoControl. Invalid IoCtl code 0x%08X\n",
+ IoStackLocation->Parameters.DeviceIoControl.IoControlCode);
+ Status = STATUS_INVALID_DEVICE_REQUEST;
+ break;
+ }
+
+ Irp->IoStatus.Status = Status;
+ Irp->IoStatus.Information = Length;
+
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ return Status;
+}
+
+/**
+ * @name DriverRead
+ *
+ * Driver Dispatch function for ReadFile.
+ *
+ * @param DeviceObject
+ * Device Object
+ * @param Irp
+ * I/O request packet
+ *
+ * @return Status
+ */
+static NTSTATUS NTAPI DriverRead(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PIO_STACK_LOCATION IoStackLocation;
+ PVOID ReadBuffer;
+ SIZE_T Length;
+
+ PAGED_CODE();
+
+ IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
+
+ DPRINT("DriverRead. Offset=%I64u, Length=%lu, DeviceObject=%p\n",
+ IoStackLocation->Parameters.Read.ByteOffset.QuadPart,
+ IoStackLocation->Parameters.Read.Length,
+ DeviceObject);
+
+ ReadBuffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
+
+ Length = LogRead(ReadBuffer, IoStackLocation->Parameters.Read.Length);
+
+ DPRINT("DriverRead. Length of data read: %lu\n",
+ Length);
+
+ Irp->IoStatus.Status = Status;
+ Irp->IoStatus.Information = Length;
+
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ return Status;
+}
diff --git a/kmtests/kmtest_drv/kmtest_drv.rc b/kmtests/kmtest_drv/kmtest_drv.rc
new file mode 100644
index 00000000000..50a28eb7c50
--- /dev/null
+++ b/kmtests/kmtest_drv/kmtest_drv.rc
@@ -0,0 +1,15 @@
+/*
+ * PROJECT: ReactOS kernel-mode tests
+ * LICENSE: GPLv2+ - See COPYING in the top level directory
+ * PURPOSE: Kernel-Mode Test Suite Driver Resource File
+ * PROGRAMMER: Thomas Faber
+ */
+
+#include
+
+#define REACTOS_FILETYPE VFT_DRV
+#define REACTOS_FILESUBTYPE VFT2_DRV_SYSTEM
+#define REACTOS_STR_FILE_DESCRIPTION "ReactOS Kernel-Mode Test Suite Driver\0"
+#define REACTOS_STR_INTERNAL_NAME "kmtest.sys\0"
+#define REACTOS_STR_ORIGINAL_FILENAME "kmtest.sys\0"
+#include
diff --git a/kmtests/kmtest_drv/log.c b/kmtests/kmtest_drv/log.c
new file mode 100644
index 00000000000..edca4459882
--- /dev/null
+++ b/kmtests/kmtest_drv/log.c
@@ -0,0 +1,151 @@
+/*
+ * PROJECT: ReactOS kernel-mode tests
+ * LICENSE: GPLv2+ - See COPYING in the top level directory
+ * PURPOSE: Kernel-Mode Test Suite Driver logging functions
+ * PROGRAMMER: Thomas Faber
+ */
+
+#include
+#include
+
+#include
+
+#define LOGBUFFER_MAX (1024UL * 1024)
+static PCHAR LogBuffer;
+static SIZE_T LogOffset;
+
+#define LOG_TAG 'LtmK'
+
+/* TODO: allow concurrent log buffer access */
+
+/**
+ * @name LogInit
+ *
+ * Initialize logging mechanism. Call from DriverEntry.
+ *
+ * @return Status
+ */
+NTSTATUS LogInit(VOID)
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PAGED_CODE();
+
+ LogBuffer = ExAllocatePoolWithTag(NonPagedPool, LOGBUFFER_MAX, LOG_TAG);
+
+ if (!LogBuffer)
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ return Status;
+}
+
+/**
+ * @name LogFree
+ *
+ * Clean up logging mechanism. Call from Unload.
+ *
+ * @return None
+ */
+VOID LogFree(VOID)
+{
+ PAGED_CODE();
+
+ ExFreePoolWithTag(LogBuffer, LOG_TAG);
+}
+
+/**
+ * @name LogPrint
+ *
+ * Print a log message.
+ *
+ * @param Message
+ * Ansi string to be logged
+ *
+ * @return None
+ */
+VOID LogPrint(IN PCSTR Message)
+{
+ SIZE_T MessageLength = strlen(Message);
+ ASSERT(LogOffset + MessageLength + 1 < LOGBUFFER_MAX);
+ RtlCopyMemory(&LogBuffer[LogOffset], Message, MessageLength + 1);
+ LogOffset += MessageLength;
+}
+
+/**
+ * @name LogPrintF
+ *
+ * Print a formatted log message.
+ *
+ * @param Format
+ * printf-like format string
+ * @param ...
+ * Arguments corresponding to the format
+ *
+ * @return None
+ */
+VOID LogPrintF(IN PCSTR Format, ...)
+{
+ va_list Arguments;
+ PAGED_CODE();
+ va_start(Arguments, Format);
+ LogVPrintF(Format, Arguments);
+ va_end(Arguments);
+}
+
+/**
+ * @name LogVPrintF
+ *
+ * Print a formatted log message.
+ *
+ * @param Format
+ * printf-like format string
+ * @param Arguments
+ * Arguments corresponding to the format
+ *
+ * @return None
+ */
+VOID LogVPrintF(IN PCSTR Format, va_list Arguments)
+{
+ CHAR Buffer[1024];
+ SIZE_T BufferLength;
+ /* TODO: make this work from any IRQL */
+ PAGED_CODE();
+
+ RtlStringCbVPrintfA(Buffer, sizeof Buffer, Format, Arguments);
+
+ BufferLength = strlen(Buffer);
+ ASSERT(LogOffset + BufferLength + 1 < LOGBUFFER_MAX);
+ RtlCopyMemory(&LogBuffer[LogOffset], Buffer, BufferLength + 1);
+ LogOffset += BufferLength;
+}
+
+/**
+ * @name LogRead
+ *
+ * Retrieve data from the log buffer.
+ *
+ * @param Buffer
+ * Buffer to copy log data to
+ * @param BufferSize
+ * Maximum number of bytes to copy
+ *
+ * @return Number of bytes copied
+ */
+SIZE_T LogRead(OUT PVOID Buffer, IN SIZE_T BufferSize)
+{
+ SIZE_T Size;
+ PAGED_CODE();
+
+ Size = min(BufferSize, LogOffset);
+ RtlCopyMemory(Buffer, LogBuffer, Size);
+
+ if (BufferSize < LogOffset)
+ {
+ SIZE_T SizeLeft = LogOffset - BufferSize;
+ RtlMoveMemory(LogBuffer, &LogBuffer[LogOffset], SizeLeft);
+ LogOffset = SizeLeft;
+ }
+ else
+ LogOffset = 0;
+
+ return Size;
+}
diff --git a/kmtests/kmtest_drv/testlist.c b/kmtests/kmtest_drv/testlist.c
new file mode 100644
index 00000000000..531b3245806
--- /dev/null
+++ b/kmtests/kmtest_drv/testlist.c
@@ -0,0 +1,14 @@
+/*
+ * PROJECT: ReactOS kernel-mode tests
+ * LICENSE: GPLv2+ - See COPYING in the top level directory
+ * PURPOSE: Kernel-Mode Test Suite Driver test list
+ * PROGRAMMER: Thomas Faber
+ */
+
+#include
+#include
+
+const KMT_TEST TestList[] =
+{
+ { NULL, NULL }
+};