[KMTESTS]
authorThomas Faber <thomas.faber@reactos.org>
Fri, 10 Jun 2011 05:34:00 +0000 (05:34 +0000)
committerThomas Faber <thomas.faber@reactos.org>
Fri, 10 Jun 2011 05:34:00 +0000 (05:34 +0000)
- add basic driver that can list/run tests and log messages

svn path=/branches/GSoC_2011/KMTestSuite/; revision=52168

kmtests/CMakeLists.txt
kmtests/directory.rbuild
kmtests/include/kmt_log.h [new file with mode: 0644]
kmtests/include/kmt_test.h [new file with mode: 0644]
kmtests/kmtest_drv.rbuild [new file with mode: 0644]
kmtests/kmtest_drv/kmtest_drv.c [new file with mode: 0644]
kmtests/kmtest_drv/kmtest_drv.rc [new file with mode: 0644]
kmtests/kmtest_drv/log.c [new file with mode: 0644]
kmtests/kmtest_drv/testlist.c [new file with mode: 0644]

index c80a939..7a4e104 100644 (file)
@@ -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)
index 2e65790..5d6c89d 100644 (file)
@@ -5,4 +5,5 @@
                <xi:include href="something/something_drv.rbuild" />
        </directory>-->
        <xi:include href="kmtest.rbuild" />
+       <xi:include href="kmtest_drv.rbuild" />
 </group>
diff --git a/kmtests/include/kmt_log.h b/kmtests/include/kmt_log.h
new file mode 100644 (file)
index 0000000..53f2b53
--- /dev/null
@@ -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 <thfabba@gmx.de>
+ */
+
+#ifndef _KMTEST_LOG_H_
+#define _KMTEST_LOG_H_
+
+#include <ntddk.h>
+
+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 (file)
index 0000000..ff045ee
--- /dev/null
@@ -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 <thfabba@gmx.de>
+ */
+
+#ifndef _KMTEST_TEST_H_
+#define _KMTEST_TEST_H_
+
+#include <kmt_log.h>
+
+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 (file)
index 0000000..ce48ce2
--- /dev/null
@@ -0,0 +1,12 @@
+<module name="kmtest_drv" type="kernelmodedriver" installbase="system32/drivers" installname="kmtest_drv.sys">
+       <include base="kmtest_drv">include</include>
+       <library>ntoskrnl</library>
+       <library>ntdll</library>
+       <library>hal</library>
+       <library>pseh</library>
+       <directory name="kmtest_drv">
+               <file>kmtest_drv.c</file>
+               <file>log.c</file>
+               <file>testlist.c</file>
+       </directory>
+</module>
diff --git a/kmtests/kmtest_drv/kmtest_drv.c b/kmtests/kmtest_drv/kmtest_drv.c
new file mode 100644 (file)
index 0000000..d238d4f
--- /dev/null
@@ -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 <thfabba@gmx.de>
+ */
+
+#include <ntddk.h>
+#include <ntstrsafe.h>
+#include <limits.h>
+
+//#define NDEBUG
+#include <debug.h>
+
+#include <kmt_public.h>
+#include <kmt_log.h>
+#include <kmt_test.h>
+
+/* 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 (file)
index 0000000..50a28eb
--- /dev/null
@@ -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 <thfabba@gmx.de>
+ */
+
+#include <windows.h>
+
+#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 <reactos/version.rc>
diff --git a/kmtests/kmtest_drv/log.c b/kmtests/kmtest_drv/log.c
new file mode 100644 (file)
index 0000000..edca445
--- /dev/null
@@ -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 <thfabba@gmx.de>
+ */
+
+#include <ntddk.h>
+#include <ntstrsafe.h>
+
+#include <kmt_log.h>
+
+#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 (file)
index 0000000..531b324
--- /dev/null
@@ -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 <thfabba@gmx.de>
+ */
+
+#include <stddef.h>
+#include <kmt_test.h>
+
+const KMT_TEST TestList[] =
+{
+    { NULL, NULL }
+};