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 } +};