add_subdirectory(apitests)
#add_subdirectory(dibtests)
+ #add_subdirectory(drivers)
#add_subdirectory(dxtest)
+ add_subdirectory(kmtests)
#add_subdirectory(regtests)
if(NOT MSVC) # FIXME: msvc build
add_subdirectory(rosautotest)
<?xml version="1.0"?>
<!DOCTYPE group SYSTEM "../../../tools/rbuild/project.dtd">
<group xmlns:xi="http://www.w3.org/2001/XInclude">
+ <directory name="apitests">
+ <xi:include href="apitests/directory.rbuild" />
+ </directory>
<directory name="drivers">
<xi:include href="drivers/directory.rbuild" />
</directory>
<directory name="dxtest">
<xi:include href="dxtest/directory.rbuild" />
</directory>
+ <directory name="kmtests">
+ <xi:include href="kmtests/directory.rbuild" />
+ </directory>
<directory name="regtests">
<xi:include href="regtests/directory.rbuild" />
</directory>
<directory name="winetests">
<xi:include href="winetests/directory.rbuild" />
</directory>
- <directory name="apitests">
- <xi:include href="apitests/directory.rbuild" />
- </directory>
</group>
<directory name="csqtest">
<xi:include href="csqtest/csqtest.rbuild" />
</directory>
- <directory name="kmtest">
- <xi:include href="kmtest/kmtest.rbuild" />
- </directory>
- <directory name="kmtest">
- <xi:include href="kmtest/kmtestassist.rbuild" />
- </directory>
<directory name="memtest">
<xi:include href="memtest/memtest.rbuild" />
</directory>
--- /dev/null
+ include_directories(
+ include)
+
+ #
+ # subdirectories containing special-purpose drivers
+ #
+ add_subdirectory(example)
+ add_subdirectory(ntos_io)
+
+ list(APPEND COMMON_SOURCE
+ rtl/RtlAvlTree.c
+ rtl/RtlMemory.c
+ rtl/RtlSplayTree.c)
+
+ #
+ # kmtest_drv.sys driver
+ #
+ list(APPEND KMTEST_DRV_SOURCE
+ kmtest_drv/kmtest_drv.c
+ kmtest_drv/testlist.c
+
+ example/Example.c
+ example/KernelType.c
+ ntos_ex/ExDoubleList.c
+ ntos_ex/ExFastMutex.c
+ ntos_ex/ExHardError.c
+ ntos_ex/ExInterlocked.c
+ ntos_ex/ExPools.c
+ ntos_ex/ExResource.c
+ ntos_ex/ExSequencedList.c
+ ntos_ex/ExSingleList.c
+ ntos_ex/ExTimer.c
+ ntos_fsrtl/FsRtlExpression.c
+ ntos_io/IoDeviceInterface.c
+ ntos_io/IoInterrupt.c
+ ntos_io/IoIrp.c
+ ntos_io/IoMdl.c
+ ntos_ke/KeApc.c
+ ntos_ke/KeDpc.c
+ ntos_ke/KeEvent.c
+ ntos_ke/KeGuardedMutex.c
+ ntos_ke/KeIrql.c
+ ntos_ke/KeProcessor.c
+ ntos_ke/KeSpinLock.c
+ ntos_ob/ObReference.c
+ ntos_ob/ObType.c
+ ${COMMON_SOURCE}
+
+ kmtest_drv/kmtest_drv.rc)
+
+ add_library(kmtest_drv SHARED ${KMTEST_DRV_SOURCE})
+
+ set_module_type(kmtest_drv kernelmodedriver)
+ target_link_libraries(kmtest_drv kmtest_printf ${PSEH_LIB})
+ add_importlibs(kmtest_drv ntoskrnl hal)
+ add_target_compile_definitions(kmtest_drv KMT_KERNEL_MODE NTDDI_VERSION=NTDDI_WS03SP1)
+ #add_pch(kmtest_drv include/kmt_test.h)
+
+ add_cd_file(TARGET kmtest_drv DESTINATION reactos/bin FOR all)
+
+ add_library(kmtest_printf
+ kmtest_drv/printf_stubs.c
+ ${REACTOS_SOURCE_DIR}/lib/sdk/crt/printf/streamout.c)
+ add_target_compile_definitions(kmtest_printf _LIBCNT_ _USER32_WSPRINTF wctomb=KmtWcToMb)
+ add_target_include_directories(kmtest_printf ${REACTOS_SOURCE_DIR}/lib/sdk/crt/include)
+
+ #
+ # kmtest.exe loader application
+ #
+ set_rc_compiler()
+
+ list(APPEND KMTEST_SOURCE
+ kmtest/kmtest.c
+ kmtest/service.c
+ kmtest/support.c
+ kmtest/testlist.c
+
+ example/Example_user.c
+ ntos_io/IoDeviceObject_user.c
+ ${COMMON_SOURCE}
+
+ kmtest/kmtest.rc)
+
+ add_executable(kmtest ${KMTEST_SOURCE})
+ set_module_type(kmtest win32cui)
+ target_link_libraries(kmtest ${PSEH_LIB})
+ add_importlibs(kmtest advapi32 msvcrt kernel32 ntdll)
+ add_target_compile_definitions(kmtest KMT_USER_MODE)
+ #add_pch(kmtest include/kmt_test.h)
+
+ set_target_properties(kmtest PROPERTIES OUTPUT_NAME "kmtest_")
+ add_cd_file(TARGET kmtest DESTINATION reactos/bin FOR all)
+
+ #
+ # Group targets
+ #
+ add_custom_target(kmtest_drivers)
+ add_dependencies(kmtest_drivers
+ kmtest_drv
+ example_drv
+ iodeviceobject_drv
+ iohelper_drv)
+
+ add_custom_target(kmtest_all)
+ add_dependencies(kmtest_all kmtest_drivers kmtest)
--- /dev/null
+ <?xml version="1.0"?>
+ <!DOCTYPE group SYSTEM "../../../tools/rbuild/project.dtd">
+ <group xmlns:xi="http://www.w3.org/2001/XInclude">
+ <directory name="example">
+ <xi:include href="example/example_drv.rbuild" />
+ </directory>
+ <directory name="ntos_io">
+ <xi:include href="ntos_io/iodeviceobject_drv.rbuild" />
+ <xi:include href="ntos_io/iohelper_drv.rbuild" />
+ </directory>
+ <xi:include href="kmtest.rbuild" />
+ <xi:include href="kmtest_drv.rbuild" />
+ </group>
--- /dev/null
+ include_directories(
+ ../include)
+
+ list(APPEND EXAMPLE_DRV_SOURCE
+ ../kmtest_drv/kmtest_standalone.c
+ Example_drv.c)
+
+ add_library(example_drv SHARED ${EXAMPLE_DRV_SOURCE})
+
+ set_module_type(example_drv kernelmodedriver)
+ target_link_libraries(example_drv kmtest_printf ${PSEH_LIB})
+ add_importlibs(example_drv ntoskrnl hal)
+ add_target_compile_definitions(example_drv KMT_STANDALONE_DRIVER)
+ #add_pch(example_drv ../include/kmt_test.h)
+
+ add_cd_file(TARGET example_drv DESTINATION reactos/bin FOR all)
--- /dev/null
+ /*
+ * PROJECT: ReactOS kernel-mode tests
+ * LICENSE: GPLv2+ - See COPYING in the top level directory
+ * PURPOSE: Kernel-Mode Test Suite Example kernel-mode test part
+ * PROGRAMMER: Thomas Faber <thfabba@gmx.de>
+ */
+
+ #include <kmt_test.h>
+
+ START_TEST(Example)
+ {
+ KIRQL Irql;
+
+ ok(1, "This test should succeed.\n");
+ ok(0, "This test should fail.\n");
+ trace("Message from kernel, low-irql. %s. %ls.\n", "Format strings work", L"Even with Unicode");
+ KeRaiseIrql(HIGH_LEVEL, &Irql);
+ trace("Message from kernel, high-irql. %s. %ls.\n", "Format strings work", L"Even with Unicode");
+
+ ok_irql(DISPATCH_LEVEL);
+ ok_eq_int(5, 6);
+ ok_eq_uint(6U, 7U);
+ ok_eq_long(1L, 2L);
+ ok_eq_ulong(3LU, 4LU);
+ ok_eq_pointer((PVOID)8, (PVOID)9);
+ ok_eq_hex(0x1234LU, 0x5678LU);
+ ok_eq_bool(TRUE, TRUE);
+ ok_eq_bool(TRUE, FALSE);
+ ok_eq_bool(FALSE, TRUE);
+ ok_bool_true(FALSE, "foo");
+ ok_bool_false(TRUE, "bar");
+ ok_eq_print(1, 2, "%i");
+ ok_eq_str("Hello", "world");
+ ok_eq_wstr(L"ABC", L"DEF");
+
+ if (!skip(KeGetCurrentIrql() == HIGH_LEVEL, "This should only work on HIGH_LEVEL\n"))
+ {
+ /* do tests depending on HIGH_LEVEL here */
+ ok(1, "This is fine\n");
+ }
+
+ KeLowerIrql(Irql);
+ }
--- /dev/null
+ /*
+ * PROJECT: ReactOS kernel-mode tests
+ * LICENSE: GPLv2+ - See COPYING in the top level directory
+ * PURPOSE: Kernel-Mode Test Suite Example Test declarations
+ * PROGRAMMER: Thomas Faber <thfabba@gmx.de>
+ */
+
+ #ifndef _KMTEST_EXAMPLE_H_
+ #define _KMTEST_EXAMPLE_H_
+
+ typedef struct
+ {
+ int a;
+ char b[8];
+ } MY_STRUCT, *PMY_STRUCT;
+
+ #define IOCTL_NOTIFY 1
+ #define IOCTL_SEND_STRING 2
+ #define IOCTL_SEND_MYSTRUCT 3
+
+ #endif /* !defined _KMTEST_EXAMPLE_H_ */
--- /dev/null
+ /*
+ * PROJECT: ReactOS kernel-mode tests
+ * LICENSE: GPLv2+ - See COPYING in the top level directory
+ * PURPOSE: Kernel-Mode Test Suite Example Test Driver
+ * PROGRAMMER: Thomas Faber <thfabba@gmx.de>
+ */
+
+ #include <kmt_test.h>
+
+ //#define NDEBUG
+ #include <debug.h>
+
+ #include "Example.h"
+
+ /* prototypes */
+ static KMT_MESSAGE_HANDLER TestMessageHandler;
+ static KMT_IRP_HANDLER TestIrpHandler;
+
+ /* globals */
+ static PDRIVER_OBJECT TestDriverObject;
+
+ /**
+ * @name TestEntry
+ *
+ * Test entry point.
+ * This is called by DriverEntry as early as possible, but with ResultBuffer
+ * initialized, so that test macros work correctly
+ *
+ * @param DriverObject
+ * Driver Object.
+ * This is guaranteed not to have been touched by DriverEntry before
+ * the call to TestEntry
+ * @param RegistryPath
+ * Driver Registry Path
+ * This is guaranteed not to have been touched by DriverEntry before
+ * the call to TestEntry
+ * @param DeviceName
+ * Pointer to receive a test-specific name for the device to create
+ * @param Flags
+ * Pointer to a flags variable instructing DriverEntry how to proceed.
+ * See the KMT_TESTENTRY_FLAGS enumeration for possible values
+ * Initialized to zero on entry
+ *
+ * @return Status.
+ * DriverEntry will fail if this is a failure status
+ */
+ NTSTATUS
+ TestEntry(
+ IN PDRIVER_OBJECT DriverObject,
+ IN PCUNICODE_STRING RegistryPath,
+ OUT PCWSTR *DeviceName,
+ IN OUT INT *Flags)
+ {
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ PAGED_CODE();
+
+ UNREFERENCED_PARAMETER(RegistryPath);
+ UNREFERENCED_PARAMETER(Flags);
+
+ DPRINT("Entry!\n");
+
+ ok_irql(PASSIVE_LEVEL);
+ TestDriverObject = DriverObject;
+
+ *DeviceName = L"Example";
+
+ trace("Hi, this is the example driver\n");
+
+ KmtRegisterIrpHandler(IRP_MJ_CREATE, NULL, TestIrpHandler);
+ KmtRegisterIrpHandler(IRP_MJ_CLOSE, NULL, TestIrpHandler);
+ KmtRegisterMessageHandler(0, NULL, TestMessageHandler);
+
+ return Status;
+ }
+
+ /**
+ * @name TestUnload
+ *
+ * Test unload routine.
+ * This is called by the driver's Unload routine as early as possible, with
+ * ResultBuffer and the test device object still valid, so that test macros
+ * work correctly
+ *
+ * @param DriverObject
+ * Driver Object.
+ * This is guaranteed not to have been touched by Unload before the call
+ * to TestEntry
+ *
+ * @return Status
+ */
+ VOID
+ TestUnload(
+ IN PDRIVER_OBJECT DriverObject)
+ {
+ PAGED_CODE();
+
+ DPRINT("Unload!\n");
+
+ ok_irql(PASSIVE_LEVEL);
+ ok_eq_pointer(DriverObject, TestDriverObject);
+
+ trace("Unloading example driver\n");
+ }
+
+ /**
+ * @name TestMessageHandler
+ *
+ * Test message handler routine
+ *
+ * @param DeviceObject
+ * Device Object.
+ * This is guaranteed not to have been touched by the dispatch function
+ * before the call to the IRP handler
+ * @param Irp
+ * Device Object.
+ * This is guaranteed not to have been touched by the dispatch function
+ * before the call to the IRP handler, except for passing it to
+ * IoGetCurrentStackLocation
+ * @param IoStackLocation
+ * Device Object.
+ * This is guaranteed not to have been touched by the dispatch function
+ * before the call to the IRP handler
+ *
+ * @return Status
+ */
+ static
+ NTSTATUS
+ TestMessageHandler(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN ULONG ControlCode,
+ IN PVOID Buffer OPTIONAL,
+ IN SIZE_T InLength,
+ IN OUT PSIZE_T OutLength)
+ {
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ switch (ControlCode)
+ {
+ case IOCTL_NOTIFY:
+ {
+ static int TimesReceived = 0;
+
+ ++TimesReceived;
+ ok(TimesReceived == 1, "Received control code 1 %d times\n", TimesReceived);
+ ok_eq_pointer(Buffer, NULL);
+ ok_eq_ulong((ULONG)InLength, 0LU);
+ ok_eq_ulong((ULONG)*OutLength, 0LU);
+ break;
+ }
+ case IOCTL_SEND_STRING:
+ {
+ static int TimesReceived = 0;
+ ANSI_STRING ExpectedString = RTL_CONSTANT_STRING("yay");
+ ANSI_STRING ReceivedString;
+
+ ++TimesReceived;
+ ok(TimesReceived == 1, "Received control code 2 %d times\n", TimesReceived);
+ ok(Buffer != NULL, "Buffer is NULL\n");
+ ok_eq_ulong((ULONG)InLength, (ULONG)ExpectedString.Length);
+ ok_eq_ulong((ULONG)*OutLength, 0LU);
+ ReceivedString.MaximumLength = ReceivedString.Length = (USHORT)InLength;
+ ReceivedString.Buffer = Buffer;
+ ok(RtlCompareString(&ExpectedString, &ReceivedString, FALSE) == 0, "Received string: %Z\n", &ReceivedString);
+ break;
+ }
+ case IOCTL_SEND_MYSTRUCT:
+ {
+ static int TimesReceived = 0;
+ MY_STRUCT ExpectedStruct = { 123, ":D" };
+ MY_STRUCT ResultStruct = { 456, "!!!" };
+
+ ++TimesReceived;
+ ok(TimesReceived == 1, "Received control code 3 %d times\n", TimesReceived);
+ ok(Buffer != NULL, "Buffer is NULL\n");
+ ok_eq_ulong((ULONG)InLength, (ULONG)sizeof ExpectedStruct);
+ ok_eq_ulong((ULONG)*OutLength, 2LU * sizeof ExpectedStruct);
+ if (!skip(Buffer && InLength >= sizeof ExpectedStruct, "Cannot read from buffer!\n"))
+ ok(RtlCompareMemory(&ExpectedStruct, Buffer, sizeof ExpectedStruct) == sizeof ExpectedStruct, "Buffer does not contain expected values\n");
+
+ if (!skip(Buffer && *OutLength >= 2 * sizeof ExpectedStruct, "Cannot write to buffer!\n"))
+ {
+ RtlCopyMemory((PCHAR)Buffer + sizeof ExpectedStruct, &ResultStruct, sizeof ResultStruct);
+ *OutLength = 2 * sizeof ExpectedStruct;
+ }
+ break;
+ }
+ default:
+ ok(0, "Got an unknown message! DeviceObject=%p, ControlCode=%lu, Buffer=%p, In=%lu, Out=%lu bytes\n",
+ DeviceObject, ControlCode, Buffer, InLength, *OutLength);
+ break;
+ }
+
+ return Status;
+ }
+
+ /**
+ * @name TestIrpHandler
+ *
+ * Test IRP handler routine
+ *
+ * @param DeviceObject
+ * Device Object.
+ * This is guaranteed not to have been touched by the dispatch function
+ * before the call to the IRP handler
+ * @param Irp
+ * Device Object.
+ * This is guaranteed not to have been touched by the dispatch function
+ * before the call to the IRP handler, except for passing it to
+ * IoGetCurrentStackLocation
+ * @param IoStackLocation
+ * Device Object.
+ * This is guaranteed not to have been touched by the dispatch function
+ * before the call to the IRP handler
+ *
+ * @return Status
+ */
+ static
+ NTSTATUS
+ TestIrpHandler(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IoStackLocation)
+ {
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ DPRINT("IRP!\n");
+
+ ok_irql(PASSIVE_LEVEL);
+ ok_eq_pointer(DeviceObject->DriverObject, TestDriverObject);
+
+ if (IoStackLocation->MajorFunction == IRP_MJ_CREATE)
+ trace("Got IRP_MJ_CREATE!\n");
+ else if (IoStackLocation->MajorFunction == IRP_MJ_CLOSE)
+ trace("Got IRP_MJ_CLOSE!\n");
+ else
+ trace("Got an IRP!\n");
+
+ Irp->IoStatus.Status = Status;
+ Irp->IoStatus.Information = 0;
+
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ return Status;
+ }
--- /dev/null
+ /*
+ * PROJECT: ReactOS kernel-mode tests
+ * LICENSE: GPLv2+ - See COPYING in the top level directory
+ * PURPOSE: Kernel-Mode Test Suite Example user-mode test part
+ * PROGRAMMER: Thomas Faber <thfabba@gmx.de>
+ */
+
+ #include <kmt_test.h>
+
+ #include "Example.h"
+
+ START_TEST(Example)
+ {
+ /* do some user-mode stuff */
+ SYSTEM_INFO SystemInfo;
+ MY_STRUCT MyStruct[2] = { { 123, ":D" }, { 0 } };
+ DWORD Length = sizeof MyStruct;
+
+ trace("Message from user-mode\n");
+
+ GetSystemInfo(&SystemInfo);
+ ok(SystemInfo.dwActiveProcessorMask != 0, "No active processors?!\n");
+
+ /* now run the kernel-mode part (see Example.c).
+ * If no user-mode part exists, this is what's done automatically */
+ KmtRunKernelTest("Example");
+
+ /* now start the special-purpose driver */
+ KmtLoadDriver(L"Example", FALSE);
+ trace("After Entry\n");
+ KmtOpenDriver();
+ trace("After Create\n");
+
+ ok(KmtSendToDriver(IOCTL_NOTIFY) == ERROR_SUCCESS, "\n");
+ ok(KmtSendStringToDriver(IOCTL_SEND_STRING, "yay") == ERROR_SUCCESS, "\n");
+ ok(KmtSendBufferToDriver(IOCTL_SEND_MYSTRUCT, MyStruct, sizeof MyStruct[0], &Length) == ERROR_SUCCESS, "\n");
+ ok_eq_int(MyStruct[1].a, 456);
+ ok_eq_str(MyStruct[1].b, "!!!");
+
+ KmtCloseDriver();
+ trace("After Close\n");
+ KmtUnloadDriver();
+ trace("After Unload\n");
+ }
--- /dev/null
+ /*
+ * PROJECT: ReactOS kernel-mode tests
+ * LICENSE: GPLv2+ - See COPYING in the top level directory
+ * PURPOSE: Kernel-Mode Test Suite Kernel Type example test
+ * PROGRAMMER: Thomas Faber <thfabba@gmx.de>
+ */
+
+ #include <kmt_test.h>
+
+ START_TEST(KernelType)
+ {
+ if (KmtIsMultiProcessorBuild)
+ trace("This is a MultiProcessor kernel\n");
+ else
+ trace("This is a Uniprocessor kernel\n");
+ if (KmtIsCheckedBuild)
+ trace("This is a Checked kernel\n");
+ else
+ trace("This is a Free kernel\n");
+ }
--- /dev/null
+ <module name="example_drv" type="kernelmodedriver" installbase="bin" installname="example_drv.sys">
+ <include base="kmtest_drv">include</include>
+ <library>ntoskrnl</library>
+ <library>hal</library>
+ <library>pseh</library>
+ <library>kmtest_printf</library>
+ <define name="KMT_STANDALONE_DRIVER" />
+ <file>Example_drv.c</file>
+ <directory name="..">
+ <directory name="kmtest_drv">
+ <file>kmtest_standalone.c</file>
+ </directory>
+ </directory>
+ </module>
--- /dev/null
+ /*
+ * PROJECT: ReactOS kernel-mode tests
+ * LICENSE: GPLv2+ - See COPYING in the top level directory
+ * PURPOSE: Kernel-Mode Test Suite platform declarations
+ * PROGRAMMER: Thomas Faber <thfabba@gmx.de>
+ */
+
+ #ifndef _KMTEST_PLATFORM_H_
+ #define _KMTEST_PLATFORM_H_
+
+ #if !defined _KMTEST_TEST_H_
+ #error include kmt_test.h instead of including kmt_platform.h!
+ #endif /* !defined _KMTEST_TEST_H_ */
+
+ #if defined KMT_KERNEL_MODE || defined KMT_STANDALONE_DRIVER
+ #include <ntddk.h>
+ #include <ntifs.h>
+ #include <ndk/ntndk.h>
+ #include <ntstrsafe.h>
+
+ #elif defined KMT_USER_MODE
+ #define WIN32_LEAN_AND_MEAN
+ #define WIN32_NO_STATUS
+ #define UNICODE
+ #include <windows.h>
+ #include <ndk/ntndk.h>
+ #include <strsafe.h>
+ #include <winioctl.h>
+
+ #ifdef KMT_EMULATE_KERNEL
+ #define ok_irql(i)
+ #define KIRQL int
+ typedef const UCHAR CUCHAR, *PCUCHAR;
+ typedef ULONG LOGICAL, *PLOGICAL;
+
+ #undef KeRaiseIrql
+ #define KeRaiseIrql(new, old) *(old) = 123
+ #undef KeLowerIrql
+ #define KeLowerIrql(i) (void)(i)
+ #define ExAllocatePool(type, size) HeapAlloc(GetProcessHeap(), 0, size)
+ #define ExAllocatePoolWithTag(type, size, tag) HeapAlloc(GetProcessHeap(), 0, size)
+ #define ExFreePool(p) HeapFree(GetProcessHeap(), 0, p)
+ #define ExFreePoolWithTag(p, tag) HeapFree(GetProcessHeap(), 0, p)
+ #define RtlCopyMemoryNonTemporal RtlCopyMemory
+ #define RtlPrefetchMemoryNonTemporal(s, l)
+ #endif /* defined KMT_EMULATE_KERNEL */
+
+ #endif /* defined KMT_USER_MODE */
+
+ #include <pseh/pseh2.h>
+ #include <limits.h>
+ #include <stdarg.h>
+
+ #endif /* !defined _KMTEST_PLATFORM_H_ */
--- /dev/null
+ /*
+ * PROJECT: ReactOS kernel-mode tests
+ * LICENSE: GPLv2+ - See COPYING in the top level directory
+ * PURPOSE: Kernel-Mode Test Suite public declarations
+ * PROGRAMMER: Thomas Faber <thfabba@gmx.de>
+ */
+
+ #ifndef _KMTEST_PUBLIC_H_
+ #define _KMTEST_PUBLIC_H_
+
+ #define IOCTL_KMTEST_GET_TESTS \
+ CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_READ_DATA)
+
+ #define IOCTL_KMTEST_RUN_TEST \
+ CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
+
+ #define IOCTL_KMTEST_SET_RESULTBUFFER \
+ CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_NEITHER, FILE_READ_DATA | FILE_WRITE_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
+
+ #endif /* !defined _KMTEST_PUBLIC_H_ */
--- /dev/null
+ /*
+ * PROJECT: ReactOS kernel-mode tests
+ * LICENSE: GPLv2+ - See COPYING in the top level directory
+ * PURPOSE: Kernel-Mode Test Suite test framework declarations
+ * PROGRAMMER: Thomas Faber <thfabba@gmx.de>
+ */
+
+ /* Inspired by Wine C unit tests, Copyright (C) 2002 Alexandre Julliard
+ * Inspired by ReactOS kernel-mode regression tests,
+ * Copyright (C) Aleksey Bragin, Filip Navara
+ */
+
+ #ifndef _KMTEST_TEST_H_
+ #define _KMTEST_TEST_H_
+
+ #include <kmt_platform.h>
+
+ typedef VOID KMT_TESTFUNC(VOID);
+ typedef KMT_TESTFUNC *PKMT_TESTFUNC;
+
+ 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[];
+
+ typedef struct
+ {
+ volatile LONG Successes;
+ volatile LONG Failures;
+ volatile LONG Skipped;
+ volatile LONG LogBufferLength;
+ LONG LogBufferMaxLength;
+ CHAR LogBuffer[ANYSIZE_ARRAY];
+ } KMT_RESULTBUFFER, *PKMT_RESULTBUFFER;
+
+ #ifdef KMT_STANDALONE_DRIVER
+ #define KMT_KERNEL_MODE
+
+ typedef NTSTATUS (KMT_IRP_HANDLER)(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IoStackLocation);
+ typedef KMT_IRP_HANDLER *PKMT_IRP_HANDLER;
+
+ NTSTATUS KmtRegisterIrpHandler(IN UCHAR MajorFunction, IN PDEVICE_OBJECT DeviceObject OPTIONAL, IN PKMT_IRP_HANDLER IrpHandler);
+ NTSTATUS KmtUnregisterIrpHandler(IN UCHAR MajorFunction, IN PDEVICE_OBJECT DeviceObject OPTIONAL, IN PKMT_IRP_HANDLER IrpHandler);
+
+ typedef NTSTATUS (KMT_MESSAGE_HANDLER)(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN ULONG ControlCode,
+ IN PVOID Buffer OPTIONAL,
+ IN SIZE_T InLength,
+ IN OUT PSIZE_T OutLength);
+ typedef KMT_MESSAGE_HANDLER *PKMT_MESSAGE_HANDLER;
+
+ NTSTATUS KmtRegisterMessageHandler(IN ULONG ControlCode OPTIONAL, IN PDEVICE_OBJECT DeviceObject OPTIONAL, IN PKMT_MESSAGE_HANDLER MessageHandler);
+ NTSTATUS KmtUnregisterMessageHandler(IN ULONG ControlCode OPTIONAL, IN PDEVICE_OBJECT DeviceObject OPTIONAL, IN PKMT_MESSAGE_HANDLER MessageHandler);
+
+ typedef enum
+ {
+ TESTENTRY_NO_CREATE_DEVICE = 1,
+ TESTENTRY_NO_REGISTER_DISPATCH = 2,
+ TESTENTRY_NO_REGISTER_UNLOAD = 4,
+ } KMT_TESTENTRY_FLAGS;
+
+ NTSTATUS TestEntry(IN PDRIVER_OBJECT DriverObject, IN PCUNICODE_STRING RegistryPath, OUT PCWSTR *DeviceName, IN OUT INT *Flags);
+ VOID TestUnload(IN PDRIVER_OBJECT DriverObject);
+ #endif /* defined KMT_STANDALONE_DRIVER */
+
+ #ifdef KMT_KERNEL_MODE
+ /* Device Extension layout */
+ typedef struct
+ {
+ PKMT_RESULTBUFFER ResultBuffer;
+ PMDL Mdl;
+ } KMT_DEVICE_EXTENSION, *PKMT_DEVICE_EXTENSION;
+
+ extern BOOLEAN KmtIsCheckedBuild;
+ extern BOOLEAN KmtIsMultiProcessorBuild;
+ extern PCSTR KmtMajorFunctionNames[];
+
+ VOID KmtSetIrql(IN KIRQL NewIrql);
+ BOOLEAN KmtAreInterruptsEnabled(VOID);
+ #elif defined KMT_USER_MODE
+ DWORD KmtRunKernelTest(IN PCSTR TestName);
+
+ VOID KmtLoadDriver(IN PCWSTR ServiceName, IN BOOLEAN RestartIfRunning);
+ VOID KmtUnloadDriver(VOID);
+ VOID KmtOpenDriver(VOID);
+ VOID KmtCloseDriver(VOID);
+
+ DWORD KmtSendToDriver(IN DWORD ControlCode);
+ DWORD KmtSendStringToDriver(IN DWORD ControlCode, IN PCSTR String);
+ DWORD KmtSendBufferToDriver(IN DWORD ControlCode, IN OUT PVOID Buffer OPTIONAL, IN DWORD InLength, IN OUT PDWORD OutLength);
+ #else /* if !defined KMT_KERNEL_MODE && !defined KMT_USER_MODE */
+ #error either KMT_KERNEL_MODE or KMT_USER_MODE must be defined
+ #endif /* !defined KMT_KERNEL_MODE && !defined KMT_USER_MODE */
+
+ extern PKMT_RESULTBUFFER ResultBuffer;
+
+ #ifdef __GNUC__
+ /* TODO: GCC doesn't understand %wZ :( */
+ #define KMT_FORMAT(type, fmt, first) /*__attribute__((__format__(type, fmt, first)))*/
+ #elif !defined __GNUC__
+ #define KMT_FORMAT(type, fmt, first)
+ #endif /* !defined __GNUC__ */
+
+ #define START_TEST(name) VOID Test_##name(VOID)
+
+ #ifndef KMT_STRINGIZE
+ #define KMT_STRINGIZE(x) #x
+ #endif /* !defined KMT_STRINGIZE */
+ #define ok(test, ...) ok_(test, __FILE__, __LINE__, __VA_ARGS__)
+ #define trace(...) trace_( __FILE__, __LINE__, __VA_ARGS__)
+ #define skip(test, ...) skip_(test, __FILE__, __LINE__, __VA_ARGS__)
+
+ #define ok_(test, file, line, ...) KmtOk(test, file ":" KMT_STRINGIZE(line), __VA_ARGS__)
+ #define trace_(file, line, ...) KmtTrace( file ":" KMT_STRINGIZE(line), __VA_ARGS__)
+ #define skip_(test, file, line, ...) KmtSkip(test, file ":" KMT_STRINGIZE(line), __VA_ARGS__)
+
+ VOID KmtVOk(INT Condition, PCSTR FileAndLine, PCSTR Format, va_list Arguments) KMT_FORMAT(ms_printf, 3, 0);
+ VOID KmtOk(INT Condition, PCSTR FileAndLine, PCSTR Format, ...) KMT_FORMAT(ms_printf, 3, 4);
+ VOID KmtVTrace(PCSTR FileAndLine, PCSTR Format, va_list Arguments) KMT_FORMAT(ms_printf, 2, 0);
+ VOID KmtTrace(PCSTR FileAndLine, PCSTR Format, ...) KMT_FORMAT(ms_printf, 2, 3);
+ BOOLEAN KmtVSkip(INT Condition, PCSTR FileAndLine, PCSTR Format, va_list Arguments) KMT_FORMAT(ms_printf, 3, 0);
+ BOOLEAN KmtSkip(INT Condition, PCSTR FileAndLine, PCSTR Format, ...) KMT_FORMAT(ms_printf, 3, 4);
+
+ #ifdef KMT_KERNEL_MODE
+ #define ok_irql(irql) ok(KeGetCurrentIrql() == irql, "IRQL is %d, expected %d\n", KeGetCurrentIrql(), irql)
+ #endif /* defined KMT_KERNEL_MODE */
+ #define ok_eq_print(value, expected, spec) ok((value) == (expected), #value " = " spec ", expected " spec "\n", value, expected)
+ #define ok_eq_pointer(value, expected) ok_eq_print(value, expected, "%p")
+ #define ok_eq_int(value, expected) ok_eq_print(value, expected, "%d")
+ #define ok_eq_uint(value, expected) ok_eq_print(value, expected, "%u")
+ #define ok_eq_long(value, expected) ok_eq_print(value, expected, "%ld")
+ #define ok_eq_ulong(value, expected) ok_eq_print(value, expected, "%lu")
+ #define ok_eq_longlong(value, expected) ok_eq_print(value, expected, "%I64d")
+ #define ok_eq_ulonglong(value, expected) ok_eq_print(value, expected, "%I64u")
+ #ifndef _WIN64
+ #define ok_eq_size(value, expected) ok_eq_print(value, (SIZE_T)(expected), "%lu")
+ #define ok_eq_longptr(value, expected) ok_eq_print(value, (LONG_PTR)(expected), "%ld")
+ #define ok_eq_ulongptr(value, expected) ok_eq_print(value, (ULONG_PTR)(expected), "%lu")
+ #elif defined _WIN64
+ #define ok_eq_size(value, expected) ok_eq_print(value, (SIZE_T)(expected), "%I64u")
+ #define ok_eq_longptr(value, expected) ok_eq_print(value, (LONG_PTR)(expected), "%I64d")
+ #define ok_eq_ulongptr(value, expected) ok_eq_print(value, (ULONG_PTR)(expected), "%I64u")
+ #endif /* defined _WIN64 */
+ #define ok_eq_hex(value, expected) ok_eq_print(value, expected, "0x%08lx")
+ #define ok_bool_true(value, desc) ok((value) == TRUE, desc " FALSE, expected TRUE\n")
+ #define ok_bool_false(value, desc) ok((value) == FALSE, desc " TRUE, expected FALSE\n")
+ #define ok_eq_bool(value, expected) ok((value) == (expected), #value " = %s, expected %s\n", \
+ (value) ? "TRUE" : "FALSE", \
+ (expected) ? "TRUE" : "FALSE")
+ #define ok_eq_str(value, expected) ok(!strcmp(value, expected), #value " = \"%s\", expected \"%s\"\n", value, expected)
+ #define ok_eq_wstr(value, expected) ok(!wcscmp(value, expected), #value " = \"%ls\", expected \"%ls\"\n", value, expected)
+
+ #define KMT_MAKE_CODE(ControlCode) CTL_CODE(FILE_DEVICE_UNKNOWN, \
+ 0xC00 + (ControlCode), \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+ #define MICROSECOND 10
+ #define MILLISECOND (1000 * MICROSECOND)
+ #define SECOND (1000 * MILLISECOND)
+
+ #if defined KMT_DEFINE_TEST_FUNCTIONS
+
+ #if defined KMT_KERNEL_MODE
+ BOOLEAN KmtIsCheckedBuild;
+ BOOLEAN KmtIsMultiProcessorBuild;
+ PCSTR KmtMajorFunctionNames[] =
+ {
+ "Create",
+ "CreateNamedPipe",
+ "Close",
+ "Read",
+ "Write",
+ "QueryInformation",
+ "SetInformation",
+ "QueryEa",
+ "SetEa",
+ "FlushBuffers",
+ "QueryVolumeInformation",
+ "SetVolumeInformation",
+ "DirectoryControl",
+ "FileSystemControl",
+ "DeviceControl",
+ "InternalDeviceControl/Scsi",
+ "Shutdown",
+ "LockControl",
+ "Cleanup",
+ "CreateMailslot",
+ "QuerySecurity",
+ "SetSecurity",
+ "Power",
+ "SystemControl",
+ "DeviceChange",
+ "QueryQuota",
+ "SetQuota",
+ "Pnp/PnpPower"
+ };
+
+ VOID KmtSetIrql(IN KIRQL NewIrql)
+ {
+ KIRQL Irql = KeGetCurrentIrql();
+ if (Irql > NewIrql)
+ KeLowerIrql(NewIrql);
+ else if (Irql < NewIrql)
+ KeRaiseIrql(NewIrql, &Irql);
+ }
+
+ BOOLEAN KmtAreInterruptsEnabled(VOID)
+ {
+ return (__readeflags() & (1 << 9)) != 0;
+ }
+
+ INT __cdecl KmtVSNPrintF(PSTR Buffer, SIZE_T BufferMaxLength, PCSTR Format, va_list Arguments) KMT_FORMAT(ms_printf, 3, 0);
+ #elif defined KMT_USER_MODE
+ static PKMT_RESULTBUFFER KmtAllocateResultBuffer(SIZE_T ResultBufferSize)
+ {
+ PKMT_RESULTBUFFER Buffer = HeapAlloc(GetProcessHeap(), 0, ResultBufferSize);
+ if (!Buffer)
+ return NULL;
+
+ Buffer->Successes = 0;
+ Buffer->Failures = 0;
+ Buffer->Skipped = 0;
+ Buffer->LogBufferLength = 0;
+ Buffer->LogBufferMaxLength = ResultBufferSize - FIELD_OFFSET(KMT_RESULTBUFFER, LogBuffer);
+
+ return Buffer;
+ }
+
+ static VOID KmtFreeResultBuffer(PKMT_RESULTBUFFER Buffer)
+ {
+ HeapFree(GetProcessHeap(), 0, Buffer);
+ }
+
+ #define KmtVSNPrintF vsnprintf
+ #endif /* defined KMT_USER_MODE */
+
+ PKMT_RESULTBUFFER ResultBuffer = NULL;
+
+ static VOID KmtAddToLogBuffer(PKMT_RESULTBUFFER Buffer, PCSTR String, SIZE_T Length)
+ {
+ LONG OldLength;
+ LONG NewLength;
+
+ if (!Buffer)
+ return;
+
+ do
+ {
+ OldLength = Buffer->LogBufferLength;
+ NewLength = OldLength + Length;
+ if (NewLength > Buffer->LogBufferMaxLength)
+ return;
+ } while (InterlockedCompareExchange(&Buffer->LogBufferLength, NewLength, OldLength) != OldLength);
+
+ memcpy(&Buffer->LogBuffer[OldLength], String, Length);
+ }
+
+ KMT_FORMAT(ms_printf, 5, 0)
+ static SIZE_T KmtXVSNPrintF(PSTR Buffer, SIZE_T BufferMaxLength, PCSTR FileAndLine, PCSTR Prepend, PCSTR Format, va_list Arguments)
+ {
+ SIZE_T BufferLength = 0;
+ SIZE_T Length;
+
+ if (FileAndLine)
+ {
+ PCSTR Slash;
+ Slash = strrchr(FileAndLine, '\\');
+ if (Slash)
+ FileAndLine = Slash + 1;
+ Slash = strrchr(FileAndLine, '/');
+ if (Slash)
+ FileAndLine = Slash + 1;
+
+ Length = min(BufferMaxLength, strlen(FileAndLine));
+ memcpy(Buffer, FileAndLine, Length);
+ Buffer += Length;
+ BufferLength += Length;
+ BufferMaxLength -= Length;
+ }
+ if (Prepend)
+ {
+ Length = min(BufferMaxLength, strlen(Prepend));
+ memcpy(Buffer, Prepend, Length);
+ Buffer += Length;
+ BufferLength += Length;
+ BufferMaxLength -= Length;
+ }
+ if (Format)
+ {
+ Length = KmtVSNPrintF(Buffer, BufferMaxLength, Format, Arguments);
+ /* vsnprintf can return more than maxLength, we don't want to do that */
+ BufferLength += min(Length, BufferMaxLength);
+ }
+ return BufferLength;
+ }
+
+ KMT_FORMAT(ms_printf, 5, 6)
+ static SIZE_T KmtXSNPrintF(PSTR Buffer, SIZE_T BufferMaxLength, PCSTR FileAndLine, PCSTR Prepend, PCSTR Format, ...)
+ {
+ SIZE_T BufferLength;
+ va_list Arguments;
+ va_start(Arguments, Format);
+ BufferLength = KmtXVSNPrintF(Buffer, BufferMaxLength, FileAndLine, Prepend, Format, Arguments);
+ va_end(Arguments);
+ return BufferLength;
+ }
+
+ VOID KmtFinishTest(PCSTR TestName)
+ {
+ CHAR MessageBuffer[512];
+ SIZE_T MessageLength;
+
+ if (!ResultBuffer)
+ return;
+
+ MessageLength = KmtXSNPrintF(MessageBuffer, sizeof MessageBuffer, NULL, NULL,
+ "%s: %ld tests executed (0 marked as todo, %ld failures), %ld skipped.\n",
+ TestName,
+ ResultBuffer->Successes + ResultBuffer->Failures,
+ ResultBuffer->Failures,
+ ResultBuffer->Skipped);
+ KmtAddToLogBuffer(ResultBuffer, MessageBuffer, MessageLength);
+ }
+
+ VOID KmtVOk(INT Condition, PCSTR FileAndLine, PCSTR Format, va_list Arguments)
+ {
+ CHAR MessageBuffer[512];
+ SIZE_T MessageLength;
+
+ if (!ResultBuffer)
+ return;
+
+ if (Condition)
+ {
+ InterlockedIncrement(&ResultBuffer->Successes);
+
+ if (0/*KmtReportSuccess*/)
+ {
+ MessageLength = KmtXSNPrintF(MessageBuffer, sizeof MessageBuffer, FileAndLine, ": Test succeeded\n", NULL);
+ KmtAddToLogBuffer(ResultBuffer, MessageBuffer, MessageLength);
+ }
+ }
+ else
+ {
+ InterlockedIncrement(&ResultBuffer->Failures);
+ MessageLength = KmtXVSNPrintF(MessageBuffer, sizeof MessageBuffer, FileAndLine, ": Test failed: ", Format, Arguments);
+ KmtAddToLogBuffer(ResultBuffer, MessageBuffer, MessageLength);
+ }
+ }
+
+ VOID KmtOk(INT Condition, PCSTR FileAndLine, PCSTR Format, ...)
+ {
+ va_list Arguments;
+ va_start(Arguments, Format);
+ KmtVOk(Condition, FileAndLine, Format, Arguments);
+ va_end(Arguments);
+ }
+
+ VOID KmtVTrace(PCSTR FileAndLine, PCSTR Format, va_list Arguments)
+ {
+ CHAR MessageBuffer[512];
+ SIZE_T MessageLength;
+
+ MessageLength = KmtXVSNPrintF(MessageBuffer, sizeof MessageBuffer, FileAndLine, ": ", Format, Arguments);
+ KmtAddToLogBuffer(ResultBuffer, MessageBuffer, MessageLength);
+ }
+
+ VOID KmtTrace(PCSTR FileAndLine, PCSTR Format, ...)
+ {
+ va_list Arguments;
+ va_start(Arguments, Format);
+ KmtVTrace(FileAndLine, Format, Arguments);
+ va_end(Arguments);
+ }
+
+ BOOLEAN KmtVSkip(INT Condition, PCSTR FileAndLine, PCSTR Format, va_list Arguments)
+ {
+ CHAR MessageBuffer[512];
+ SIZE_T MessageLength;
+
+ if (!ResultBuffer)
+ return !Condition;
+
+ if (!Condition)
+ {
+ InterlockedIncrement(&ResultBuffer->Skipped);
+ MessageLength = KmtXVSNPrintF(MessageBuffer, sizeof MessageBuffer, FileAndLine, ": Tests skipped: ", Format, Arguments);
+ KmtAddToLogBuffer(ResultBuffer, MessageBuffer, MessageLength);
+ }
+
+ return !Condition;
+ }
+
+ BOOLEAN KmtSkip(INT Condition, PCSTR FileAndLine, PCSTR Format, ...)
+ {
+ BOOLEAN Ret;
+ va_list Arguments;
+ va_start(Arguments, Format);
+ Ret = KmtVSkip(Condition, FileAndLine, Format, Arguments);
+ va_end(Arguments);
+ return Ret;
+ }
+
+ #endif /* defined KMT_DEFINE_TEST_FUNCTIONS */
+
+ #endif /* !defined _KMTEST_TEST_H_ */
--- /dev/null
+ <module name="kmtest" type="win32cui" installbase="bin" installname="kmtest_.exe">
+ <include base="kmtest">include</include>
+ <library>advapi32</library>
+ <library>ntdll</library>
+ <library>pseh</library>
+ <define name="KMT_USER_MODE" />
+ <directory name="kmtest">
+ <file>kmtest.c</file>
+ <file>service.c</file>
+ <file>support.c</file>
+ <file>testlist.c</file>
+ </directory>
+ <directory name="example">
+ <file>Example_user.c</file>
+ </directory>
+ <directory name="ntos_io">
+ <file>IoDeviceObject_user.c</file>
+ </directory>
+ <directory name="rtl">
+ <file>RtlAvlTree.c</file>
+ <file>RtlMemory.c</file>
+ <file>RtlSplayTree.c</file>
+ </directory>
+ </module>
--- /dev/null
+ /*
+ * PROJECT: ReactOS kernel-mode tests
+ * LICENSE: GPLv2+ - See COPYING in the top level directory
+ * PURPOSE: Kernel-Mode Test Suite Loader Application
+ * PROGRAMMER: Thomas Faber <thfabba@gmx.de>
+ */
+
+ #define KMT_DEFINE_TEST_FUNCTIONS
+ #include <kmt_test.h>
+
+ #include "kmtest.h"
+ #include <kmt_public.h>
+
+ #include <assert.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+
+ #define SERVICE_NAME L"Kmtest"
+ #define SERVICE_PATH L"kmtest_drv.sys"
+ #define SERVICE_DESCRIPTION L"ReactOS Kernel-Mode Test Suite Driver"
+
+ #define RESULTBUFFER_SIZE (1024 * 1024)
+
+ typedef enum
+ {
+ KMT_DO_NOTHING,
+ KMT_LIST_TESTS,
+ KMT_LIST_ALL_TESTS,
+ KMT_RUN_TEST,
+ } KMT_OPERATION;
+
+ HANDLE KmtestHandle;
+ SC_HANDLE KmtestServiceHandle;
+ PCSTR ErrorFileAndLine = "No error";
+
+ static void OutputError(IN DWORD Error);
+ static DWORD ListTests(IN BOOLEAN IncludeHidden);
+ static PKMT_TESTFUNC FindTest(IN PCSTR TestName);
+ static DWORD OutputResult(IN PCSTR TestName);
+ static DWORD RunTest(IN PCSTR TestName);
+ int __cdecl main(int ArgCount, char **Arguments);
+
+ /**
+ * @name OutputError
+ *
+ * Output an error message to the console.
+ *
+ * @param Error
+ * Win32 error code
+ */
+ static
+ void
+ OutputError(
+ IN DWORD Error)
+ {
+ PSTR Message;
+ if (!FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER,
+ NULL, Error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&Message, 0, NULL))
+ {
+ fprintf(stderr, "%s: Could not retrieve error message (error 0x%08lx). Original error: 0x%08lx\n",
+ ErrorFileAndLine, GetLastError(), Error);
+ return;
+ }
+
+ fprintf(stderr, "%s: error 0x%08lx: %s\n", ErrorFileAndLine, Error, Message);
+
+ LocalFree(Message);
+ }
+
+ /**
+ * @name CompareTestNames
+ *
+ * strcmp that skips a leading '-' on either string if present
+ *
+ * @param Str1
+ * @param Str2
+ * @return see strcmp
+ */
+ static
+ INT
+ CompareTestNames(
+ IN PCSTR Str1,
+ IN PCSTR Str2)
+ {
+ if (*Str1 == '-')
+ ++Str1;
+ if (*Str2 == '-')
+ ++Str2;
+ while (*Str1 && *Str1 == *Str2)
+ {
+ ++Str1;
+ ++Str2;
+ }
+ return *Str1 - *Str2;
+ }
+
+ /**
+ * @name ListTests
+ *
+ * Output the list of tests to the console.
+ * The list will comprise tests as listed by the driver
+ * in addition to user-mode tests in TestList.
+ *
+ * @param IncludeHidden
+ * TRUE to include "hidden" tests prefixed with a '-'
+ *
+ * @return Win32 error code
+ */
+ static
+ DWORD
+ ListTests(
+ IN BOOLEAN IncludeHidden)
+ {
+ DWORD Error = ERROR_SUCCESS;
+ CHAR Buffer[1024];
+ DWORD BytesRead;
+ PCSTR TestName = Buffer;
+ PCKMT_TEST TestEntry = TestList;
+ PCSTR NextTestName;
+
+ puts("Valid test names:");
+
+ // get test list from driver
+ if (!DeviceIoControl(KmtestHandle, IOCTL_KMTEST_GET_TESTS, NULL, 0, Buffer, sizeof Buffer, &BytesRead, NULL))
+ error_goto(Error, cleanup);
+
+ // output test list plus user-mode tests
+ while (TestEntry->TestName || *TestName)
+ {
+ if (!TestEntry->TestName)
+ {
+ NextTestName = TestName;
+ TestName += strlen(TestName) + 1;
+ }
+ else if (!*TestName)
+ {
+ NextTestName = TestEntry->TestName;
+ ++TestEntry;
+ }
+ else
+ {
+ INT Result = CompareTestNames(TestEntry->TestName, TestName);
+
+ if (Result == 0)
+ {
+ NextTestName = TestEntry->TestName;
+ TestName += strlen(TestName) + 1;
+ ++TestEntry;
+ }
+ else if (Result < 0)
+ {
+ NextTestName = TestEntry->TestName;
+ ++TestEntry;
+ }
+ else
+ {
+ NextTestName = TestName;
+ TestName += strlen(TestName) + 1;
+ }
+ }
+
+ if (IncludeHidden && NextTestName[0] == '-')
+ ++NextTestName;
+
+ if (NextTestName[0] != '-')
+ printf(" %s\n", NextTestName);
+ }
+
+ cleanup:
+ return Error;
+ }
+
+ /**
+ * @name FindTest
+ *
+ * Find a test in TestList by name.
+ *
+ * @param TestName
+ * Name of the test to look for. Case sensitive
+ *
+ * @return pointer to test function, or NULL if not found
+ */
+ static
+ PKMT_TESTFUNC
+ FindTest(
+ IN PCSTR TestName)
+ {
+ PCKMT_TEST TestEntry = TestList;
+
+ for (TestEntry = TestList; TestEntry->TestName; ++TestEntry)
+ {
+ PCSTR TestEntryName = TestEntry->TestName;
+
+ // skip leading '-' if present
+ if (*TestEntryName == '-')
+ ++TestEntryName;
+
+ if (!lstrcmpA(TestEntryName, TestName))
+ break;
+ }
+
+ return TestEntry->TestFunction;
+ }
+
+ /**
+ * @name OutputResult
+ *
+ * Output the test results in ResultBuffer to the console.
+ *
+ * @param TestName
+ * Name of the test whose result is to be printed
+ *
+ * @return Win32 error code
+ */
+ static
+ DWORD
+ OutputResult(
+ IN PCSTR TestName)
+ {
+ DWORD Error = ERROR_SUCCESS;
+ DWORD BytesWritten;
+ DWORD LogBufferLength;
+ DWORD Offset = 0;
+ /* A console window can't handle a single
+ * huge block of data, so split it up */
+ const DWORD BlockSize = 8 * 1024;
+
+ KmtFinishTest(TestName);
+
+ LogBufferLength = ResultBuffer->LogBufferLength;
+ for (Offset = 0; Offset < LogBufferLength; Offset += BlockSize)
+ {
+ DWORD Length = min(LogBufferLength - Offset, BlockSize);
+ if (!WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), ResultBuffer->LogBuffer + Offset, Length, &BytesWritten, NULL))
+ error(Error);
+ }
+
+ return Error;
+ }
+
+ /**
+ * @name RunTest
+ *
+ * Run the named test and output its results.
+ *
+ * @param TestName
+ * Name of the test to run. Case sensitive
+ *
+ * @return Win32 error code
+ */
+ static
+ DWORD
+ RunTest(
+ IN PCSTR TestName)
+ {
+ DWORD Error = ERROR_SUCCESS;
+ PKMT_TESTFUNC TestFunction;
+ DWORD BytesRead;
+
+ assert(TestName != NULL);
+
+ if (!ResultBuffer)
+ {
+ ResultBuffer = KmtAllocateResultBuffer(RESULTBUFFER_SIZE);
+ if (!ResultBuffer)
+ error_goto(Error, cleanup);
+ if (!DeviceIoControl(KmtestHandle, IOCTL_KMTEST_SET_RESULTBUFFER, ResultBuffer, RESULTBUFFER_SIZE, NULL, 0, &BytesRead, NULL))
+ error_goto(Error, cleanup);
+ }
+
+ // check test list
+ TestFunction = FindTest(TestName);
+
+ if (TestFunction)
+ {
+ TestFunction();
+ goto cleanup;
+ }
+
+ // not found in user-mode test list, call driver
+ Error = KmtRunKernelTest(TestName);
+
+ cleanup:
+ if (!Error)
+ Error = OutputResult(TestName);
+
+ return Error;
+ }
+
+ /**
+ * @name main
+ *
+ * Program entry point
+ *
+ * @param ArgCount
+ * @param Arguments
+ *
+ * @return EXIT_SUCCESS on success, EXIT_FAILURE on failure
+ */
+ int
+ main(
+ int ArgCount,
+ char **Arguments)
+ {
+ INT Status = EXIT_SUCCESS;
+ DWORD Error = ERROR_SUCCESS;
+ PCSTR AppName = "kmtest.exe";
+ PCSTR TestName = NULL;
+ KMT_OPERATION Operation = KMT_DO_NOTHING;
+ BOOLEAN ShowHidden = FALSE;
+
+ Error = KmtServiceInit();
+ if (Error)
+ goto cleanup;
+
+ if (ArgCount >= 1)
+ AppName = Arguments[0];
+
+ if (ArgCount <= 1)
+ {
+ printf("Usage: %s <test_name> - run the specified test (creates/starts the driver(s) as appropriate)\n", AppName);
+ printf(" %s --list - list available tests\n", AppName);
+ printf(" %s --list-all - list available tests, including hidden\n", AppName);
+ printf(" %s <create|delete|start|stop> - manage the kmtest driver\n\n", AppName);
+ Operation = KMT_LIST_TESTS;
+ }
+ else
+ {
+ TestName = Arguments[1];
+ if (!lstrcmpA(TestName, "create"))
+ Error = KmtCreateService(SERVICE_NAME, SERVICE_PATH, SERVICE_DESCRIPTION, &KmtestServiceHandle);
+ else if (!lstrcmpA(TestName, "delete"))
+ Error = KmtDeleteService(SERVICE_NAME, &KmtestServiceHandle);
+ else if (!lstrcmpA(TestName, "start"))
+ Error = KmtStartService(SERVICE_NAME, &KmtestServiceHandle);
+ else if (!lstrcmpA(TestName, "stop"))
+ Error = KmtStopService(SERVICE_NAME, &KmtestServiceHandle);
+
+ else if (!lstrcmpA(TestName, "--list"))
+ Operation = KMT_LIST_TESTS;
+ else if (!lstrcmpA(TestName, "--list-all"))
+ Operation = KMT_LIST_ALL_TESTS;
+ else
+ Operation = KMT_RUN_TEST;
+ }
+
+ if (Operation)
+ {
+ Error = KmtCreateAndStartService(SERVICE_NAME, SERVICE_PATH, SERVICE_DESCRIPTION, &KmtestServiceHandle, FALSE);
+ if (Error)
+ goto cleanup;
+
+ KmtestHandle = CreateFile(KMTEST_DEVICE_PATH, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
+ if (KmtestHandle == INVALID_HANDLE_VALUE)
+ error_goto(Error, cleanup);
+
+ switch (Operation)
+ {
+ case KMT_LIST_ALL_TESTS:
+ ShowHidden = TRUE;
+ /* fall through */
+ case KMT_LIST_TESTS:
+ Error = ListTests(ShowHidden);
+ break;
+ case KMT_RUN_TEST:
+ Error = RunTest(TestName);
+ break;
+ default:
+ assert(FALSE);
+ }
+ }
+
+ cleanup:
+ if (KmtestHandle)
+ CloseHandle(KmtestHandle);
+
+ if (ResultBuffer)
+ KmtFreeResultBuffer(ResultBuffer);
+
+ KmtCloseService(&KmtestServiceHandle);
+
+ if (Error)
+ KmtServiceCleanup(TRUE);
+ else
+ Error = KmtServiceCleanup(FALSE);
+
+ if (Error)
+ {
+ OutputError(Error);
+
+ Status = EXIT_FAILURE;
+ }
+
+ return Status;
+ }
--- /dev/null
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+ <assemblyIdentity version="1.0.0.0" processorArchitecture="x86" name="kmtest"/>
+ <description>ReactOS Kernel-Mode Test Suite Loader Application</description>
+ <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
+ <security>
+ <requestedPrivileges>
+ <requestedExecutionLevel level="requireAdministrator" uiAccess="false"/>
+ </requestedPrivileges>
+ </security>
+ </trustInfo>
+ </assembly>
--- /dev/null
+ /*
+ * PROJECT: ReactOS kernel-mode tests
+ * LICENSE: GPLv2+ - See COPYING in the top level directory
+ * PURPOSE: Kernel-Mode Test Suite Loader Application
+ * PROGRAMMER: Thomas Faber <thfabba@gmx.de>
+ */
+
+ #ifndef _KMTESTS_H_
+ #define _KMTESTS_H_
+
+ extern PCSTR ErrorFileAndLine;
+
+ #ifndef KMT_STRINGIZE
+ #define KMT_STRINGIZE(x) #x
+ #endif /* !defined KMT_STRINGIZE */
+
+ #define location(file, line) do { ErrorFileAndLine = file ":" KMT_STRINGIZE(line); } while (0)
+ #define error_value(Error, value) do { location(__FILE__, __LINE__); Error = value; } while (0)
+ #define error(Error) error_value(Error, GetLastError())
+ #define error_goto(Error, label) do { error(Error); goto label; } while (0)
+ #define error_value_goto(Error, value, label) do { error_value(Error, value); goto label; } while (0)
+
+ /* service management functions */
+ DWORD
+ KmtServiceInit(VOID);
+
+ DWORD
+ KmtServiceCleanup(
+ BOOLEAN IgnoreErrors);
+
+ DWORD
+ KmtCreateService(
+ IN PCWSTR ServiceName,
+ IN PCWSTR ServicePath,
+ IN PCWSTR DisplayName OPTIONAL,
+ OUT SC_HANDLE *ServiceHandle);
+
+ DWORD
+ KmtStartService(
+ IN PCWSTR ServiceName OPTIONAL,
+ IN OUT SC_HANDLE *ServiceHandle);
+
+ DWORD
+ KmtCreateAndStartService(
+ IN PCWSTR ServiceName,
+ IN PCWSTR ServicePath,
+ IN PCWSTR DisplayName OPTIONAL,
+ OUT SC_HANDLE *ServiceHandle,
+ IN BOOLEAN RestartIfRunning);
+
+ DWORD
+ KmtStopService(
+ IN PCWSTR ServiceName OPTIONAL,
+ IN OUT SC_HANDLE *ServiceHandle);
+
+ DWORD
+ KmtDeleteService(
+ IN PCWSTR ServiceName OPTIONAL,
+ IN OUT SC_HANDLE *ServiceHandle);
+
+ DWORD KmtCloseService(
+ IN OUT SC_HANDLE *ServiceHandle);
+
+ #endif /* !defined _KMTESTS_H_ */
--- /dev/null
+ /*
+ * PROJECT: ReactOS kernel-mode tests
+ * LICENSE: GPLv2+ - See COPYING in the top level directory
+ * PURPOSE: Kernel-Mode Test Suite Loader Resource File
+ * PROGRAMMER: Thomas Faber <thfabba@gmx.de>
+ */
+
+ #include <windows.h>
+
+ #define REACTOS_STR_FILE_DESCRIPTION "ReactOS Kernel-Mode Test Suite Loader Application\0"
+ #define REACTOS_STR_INTERNAL_NAME "kmtest.exe\0"
+ #define REACTOS_STR_ORIGINAL_FILENAME "kmtest.exe\0"
+ #include <reactos/version.rc>
+
+ CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST DISCARDABLE PURE "kmtest/kmtest.exe.manifest"
--- /dev/null
+ /*
+ * PROJECT: ReactOS kernel-mode tests
+ * LICENSE: GPLv2+ - See COPYING in the top level directory
+ * PURPOSE: Kernel-Mode Test Suite Loader service control functions
+ * PROGRAMMER: Thomas Faber <thfabba@gmx.de>
+ */
+
+ #include <kmt_test.h>
+ #include "kmtest.h"
+
+ #include <assert.h>
+
+ #define SERVICE_ACCESS (SERVICE_START | SERVICE_STOP | DELETE)
+
+ static SC_HANDLE ScmHandle;
+
+ /**
+ * @name KmtServiceInit
+ *
+ * Initialize service management routines (by opening the service control manager)
+ *
+ * @return Win32 error code
+ */
+ DWORD
+ KmtServiceInit(VOID)
+ {
+ DWORD Error = ERROR_SUCCESS;
+
+ assert(!ScmHandle);
+
+ ScmHandle = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
+ if (!ScmHandle)
+ error(Error);
+
+ return Error;
+ }
+
+ /**
+ * @name KmtServiceCleanup
+ *
+ * Clean up resources used by service management routines.
+ *
+ * @param IgnoreErrors
+ * If TRUE, the function will never set ErrorLineAndFile, and always return ERROR_SUCCESS
+ *
+ * @return Win32 error code
+ */
+ DWORD
+ KmtServiceCleanup(
+ BOOLEAN IgnoreErrors)
+ {
+ DWORD Error = ERROR_SUCCESS;
+
+ if (ScmHandle && !CloseServiceHandle(ScmHandle) && !IgnoreErrors)
+ error(Error);
+
+ return Error;
+ }
+
+ /**
+ * @name KmtCreateService
+ *
+ * Create the specified driver service and return a handle to it
+ *
+ * @param ServiceName
+ * Name of the service to create
+ * @param ServicePath
+ * File name of the driver, relative to the current directory
+ * @param DisplayName
+ * Service display name
+ * @param ServiceHandle
+ * Pointer to a variable to receive the handle to the service
+ *
+ * @return Win32 error code
+ */
+ DWORD
+ KmtCreateService(
+ IN PCWSTR ServiceName,
+ IN PCWSTR ServicePath,
+ IN PCWSTR DisplayName OPTIONAL,
+ OUT SC_HANDLE *ServiceHandle)
+ {
+ DWORD Error = ERROR_SUCCESS;
+ WCHAR DriverPath[MAX_PATH];
+ HRESULT result = S_OK;
+
+ assert(ServiceHandle);
+ assert(ServiceName && ServicePath);
+
+ if (!GetCurrentDirectory(sizeof DriverPath / sizeof DriverPath[0], DriverPath))
+ error_goto(Error, cleanup);
+
+ if (DriverPath[wcslen(DriverPath) - 1] != L'\\')
+ {
+ DriverPath[wcslen(DriverPath) + 1] = L'\0';
+ DriverPath[wcslen(DriverPath)] = L'\\';
+ }
+
+ result = StringCbCat(DriverPath, sizeof DriverPath, ServicePath);
+ if (FAILED(result))
+ error_value_goto(Error, result, cleanup);
+
+ *ServiceHandle = CreateService(ScmHandle, ServiceName, DisplayName,
+ SERVICE_ACCESS, SERVICE_KERNEL_DRIVER, SERVICE_DEMAND_START,
+ SERVICE_ERROR_NORMAL, DriverPath, NULL, NULL, NULL, NULL, NULL);
+
+ if (!*ServiceHandle)
+ error_goto(Error, cleanup);
+
+ cleanup:
+ return Error;
+ }
+
+ /**
+ * @name KmtStartService
+ *
+ * Start the specified driver service by handle or name (and return a handle to it)
+ *
+ * @param ServiceName
+ * If *ServiceHandle is NULL, name of the service to start
+ * @param ServiceHandle
+ * Pointer to a variable containing the service handle,
+ * or NULL (in which case it will be filled with a handle to the service)
+ *
+ * @return Win32 error code
+ */
+ DWORD
+ KmtStartService(
+ IN PCWSTR ServiceName OPTIONAL,
+ IN OUT SC_HANDLE *ServiceHandle)
+ {
+ DWORD Error = ERROR_SUCCESS;
+
+ assert(ServiceHandle);
+ assert(ServiceName || *ServiceHandle);
+
+ if (!*ServiceHandle)
+ *ServiceHandle = OpenService(ScmHandle, ServiceName, SERVICE_ACCESS);
+
+ if (!*ServiceHandle)
+ error_goto(Error, cleanup);
+
+ if (!StartService(*ServiceHandle, 0, NULL))
+ error_goto(Error, cleanup);
+
+ cleanup:
+ return Error;
+ }
+
+ /**
+ * @name KmtCreateAndStartService
+ *
+ * Create and start the specified driver service and return a handle to it
+ *
+ * @param ServiceName
+ * Name of the service to create
+ * @param ServicePath
+ * File name of the driver, relative to the current directory
+ * @param DisplayName
+ * Service display name
+ * @param ServiceHandle
+ * Pointer to a variable to receive the handle to the service
+ * @param RestartIfRunning
+ * TRUE to stop and restart the service if it is already running
+ *
+ * @return Win32 error code
+ */
+ DWORD
+ KmtCreateAndStartService(
+ IN PCWSTR ServiceName,
+ IN PCWSTR ServicePath,
+ IN PCWSTR DisplayName OPTIONAL,
+ OUT SC_HANDLE *ServiceHandle,
+ IN BOOLEAN RestartIfRunning)
+ {
+ DWORD Error = ERROR_SUCCESS;
+
+ assert(ServiceHandle);
+
+ Error = KmtCreateService(ServiceName, ServicePath, DisplayName, ServiceHandle);
+
+ if (Error && Error != ERROR_SERVICE_EXISTS)
+ goto cleanup;
+
+ Error = KmtStartService(ServiceName, ServiceHandle);
+
+ if (Error != ERROR_SERVICE_ALREADY_RUNNING)
+ goto cleanup;
+
+ Error = ERROR_SUCCESS;
+
+ if (!RestartIfRunning)
+ goto cleanup;
+
+ Error = KmtStopService(ServiceName, ServiceHandle);
+ if (Error)
+ goto cleanup;
+
+ Error = KmtStartService(ServiceName, ServiceHandle);
+ if (Error)
+ goto cleanup;
+
+ cleanup:
+ assert(Error || *ServiceHandle);
+ return Error;
+ }
+
+ /**
+ * @name KmtStopService
+ *
+ * Stop the specified driver service by handle or name (and return a handle to it)
+ *
+ * @param ServiceName
+ * If *ServiceHandle is NULL, name of the service to stop
+ * @param ServiceHandle
+ * Pointer to a variable containing the service handle,
+ * or NULL (in which case it will be filled with a handle to the service)
+ *
+ * @return Win32 error code
+ */
+ DWORD
+ KmtStopService(
+ IN PCWSTR ServiceName OPTIONAL,
+ IN OUT SC_HANDLE *ServiceHandle)
+ {
+ DWORD Error = ERROR_SUCCESS;
+ SERVICE_STATUS ServiceStatus;
+
+ assert(ServiceHandle);
+ assert(ServiceName || *ServiceHandle);
+
+ if (!*ServiceHandle)
+ *ServiceHandle = OpenService(ScmHandle, ServiceName, SERVICE_ACCESS);
+
+ if (!*ServiceHandle)
+ error_goto(Error, cleanup);
+
+ if (!ControlService(*ServiceHandle, SERVICE_CONTROL_STOP, &ServiceStatus))
+ error_goto(Error, cleanup);
+
+ cleanup:
+ return Error;
+ }
+
+ /**
+ * @name KmtDeleteService
+ *
+ * Delete the specified driver service by handle or name (and return a handle to it)
+ *
+ * @param ServiceName
+ * If *ServiceHandle is NULL, name of the service to delete
+ * @param ServiceHandle
+ * Pointer to a variable containing the service handle.
+ * Will be set to NULL on success
+ *
+ * @return Win32 error code
+ */
+ DWORD
+ KmtDeleteService(
+ IN PCWSTR ServiceName OPTIONAL,
+ IN OUT SC_HANDLE *ServiceHandle)
+ {
+ DWORD Error = ERROR_SUCCESS;
+
+ assert(ServiceHandle);
+ assert(ServiceName || *ServiceHandle);
+
+ if (!*ServiceHandle)
+ *ServiceHandle = OpenService(ScmHandle, ServiceName, SERVICE_ACCESS);
+
+ if (!*ServiceHandle)
+ error_goto(Error, cleanup);
+
+ if (!DeleteService(*ServiceHandle))
+ error_goto(Error, cleanup);
+
+ if (*ServiceHandle)
+ CloseServiceHandle(*ServiceHandle);
+
+ cleanup:
+ return Error;
+ }
+
+ /**
+ * @name KmtCloseService
+ *
+ * Close the specified driver service handle
+ *
+ * @param ServiceHandle
+ * Pointer to a variable containing the service handle.
+ * Will be set to NULL on success
+ *
+ * @return Win32 error code
+ */
+ DWORD KmtCloseService(
+ IN OUT SC_HANDLE *ServiceHandle)
+ {
+ DWORD Error = ERROR_SUCCESS;
+
+ assert(ServiceHandle);
+
+ if (*ServiceHandle && !CloseServiceHandle(*ServiceHandle))
+ error_goto(Error, cleanup);
+
+ *ServiceHandle = NULL;
+
+ cleanup:
+ return Error;
+ }
--- /dev/null
+ /*
+ * 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 <kmt_test.h>
+
+ #include "kmtest.h"
+ #include <kmt_public.h>
+
+ #include <assert.h>
+
+ extern HANDLE KmtestHandle;
+
+ /**
+ * @name KmtRunKernelTest
+ *
+ * Run the specified kernel-mode test part
+ *
+ * @param TestName
+ * Name of the test to run
+ *
+ * @return Win32 error code as returned by DeviceIoControl
+ */
+ DWORD
+ KmtRunKernelTest(
+ IN PCSTR TestName)
+ {
+ DWORD Error = ERROR_SUCCESS;
+ DWORD BytesRead;
+
+ if (!DeviceIoControl(KmtestHandle, IOCTL_KMTEST_RUN_TEST, (PVOID)TestName, strlen(TestName), NULL, 0, &BytesRead, NULL))
+ error(Error);
+
+ return Error;
+ }
+
+ static WCHAR TestServiceName[MAX_PATH];
+ static SC_HANDLE TestServiceHandle;
+ static HANDLE TestDeviceHandle;
+
+ /**
+ * @name KmtLoadDriver
+ *
+ * Load the specified special-purpose driver (create/start the service)
+ *
+ * @param ServiceName
+ * Name of the driver service (Kmtest- prefix will be added automatically)
+ * @param RestartIfRunning
+ * TRUE to stop and restart the service if it is already running
+ */
+ VOID
+ KmtLoadDriver(
+ IN PCWSTR ServiceName,
+ IN BOOLEAN RestartIfRunning)
+ {
+ DWORD Error = ERROR_SUCCESS;
+ WCHAR ServicePath[MAX_PATH];
+
+ StringCbCopy(ServicePath, sizeof ServicePath, ServiceName);
+ StringCbCat(ServicePath, sizeof ServicePath, L"_drv.sys");
+
+ StringCbCopy(TestServiceName, sizeof TestServiceName, L"Kmtest-");
+ StringCbCat(TestServiceName, sizeof TestServiceName, ServiceName);
+
+ Error = KmtCreateAndStartService(TestServiceName, ServicePath, NULL, &TestServiceHandle, RestartIfRunning);
+
+ if (Error)
+ {
+ // TODO
+ __debugbreak();
+ }
+ }
+
+ /**
+ * @name KmtUnloadDriver
+ *
+ * Unload special-purpose driver (stop the service)
+ */
+ VOID
+ KmtUnloadDriver(VOID)
+ {
+ DWORD Error = ERROR_SUCCESS;
+
+ Error = KmtStopService(TestServiceName, &TestServiceHandle);
+
+ if (Error)
+ {
+ // TODO
+ __debugbreak();
+ }
+ }
+
+ /**
+ * @name KmtOpenDriver
+ *
+ * Open special-purpose driver (acquire a device handle)
+ */
+ VOID
+ KmtOpenDriver(VOID)
+ {
+ DWORD Error = ERROR_SUCCESS;
+ WCHAR DevicePath[MAX_PATH];
+
+ StringCbCopy(DevicePath, sizeof DevicePath, L"\\\\.\\Global\\GLOBALROOT\\Device\\");
+ StringCbCat(DevicePath, sizeof DevicePath, TestServiceName);
+
+ TestDeviceHandle = CreateFile(DevicePath, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
+ if (TestDeviceHandle == INVALID_HANDLE_VALUE)
+ error(Error);
+
+ if (Error)
+ {
+ // TODO
+ __debugbreak();
+ }
+
+ }
+
+ /**
+ * @name KmtCloseDriver
+ *
+ * Close special-purpose driver (close device handle)
+ */
+ VOID
+ KmtCloseDriver(VOID)
+ {
+ DWORD Error = ERROR_SUCCESS;
+
+ if (TestDeviceHandle && !CloseHandle(TestDeviceHandle))
+ error(Error);
+
+ if (Error)
+ {
+ // TODO
+ __debugbreak();
+ }
+ }
+
+ /**
+ * @name KmtSendToDriver
+ *
+ * Unload special-purpose driver (stop the service)
+ *
+ * @param ControlCode
+ *
+ * @return Win32 error code as returned by DeviceIoControl
+ */
+ DWORD
+ KmtSendToDriver(
+ IN DWORD ControlCode)
+ {
+ DWORD BytesRead;
+
+ assert(ControlCode < 0x400);
+
+ if (!DeviceIoControl(TestDeviceHandle, KMT_MAKE_CODE(ControlCode), NULL, 0, NULL, 0, &BytesRead, NULL))
+ return GetLastError();
+
+ return ERROR_SUCCESS;
+ }
+
+ /**
+ * @name KmtSendStringToDriver
+ *
+ * Unload special-purpose driver (stop the service)
+ *
+ * @param ControlCode
+ * @param String
+ *
+ * @return Win32 error code as returned by DeviceIoControl
+ */
+ DWORD
+ KmtSendStringToDriver(
+ IN DWORD ControlCode,
+ IN PCSTR String)
+ {
+ DWORD BytesRead;
+
+ assert(ControlCode < 0x400);
+
+ if (!DeviceIoControl(TestDeviceHandle, KMT_MAKE_CODE(ControlCode), (PVOID)String, strlen(String), NULL, 0, &BytesRead, NULL))
+ return GetLastError();
+
+ return ERROR_SUCCESS;
+ }
+
+ /**
+ * @name KmtSendBufferToDriver
+ *
+ * @param ControlCode
+ * @param Buffer
+ * @param InLength
+ * @param OutLength
+ *
+ * @return Win32 error code as returned by DeviceIoControl
+ */
+ DWORD
+ KmtSendBufferToDriver(
+ IN DWORD ControlCode,
+ IN OUT PVOID Buffer OPTIONAL,
+ IN DWORD InLength,
+ IN OUT PDWORD OutLength)
+ {
+ assert(OutLength);
+ assert(Buffer || (!InLength && !*OutLength));
+ assert(ControlCode < 0x400);
+
+ if (!DeviceIoControl(TestDeviceHandle, KMT_MAKE_CODE(ControlCode), Buffer, InLength, Buffer, *OutLength, OutLength, NULL))
+ return GetLastError();
+
+ return ERROR_SUCCESS;
+ }
--- /dev/null
+ /*
+ * PROJECT: ReactOS kernel-mode tests
+ * LICENSE: GPLv2+ - See COPYING in the top level directory
+ * PURPOSE: Kernel-Mode Test Suite user-mode test list
+ * PROGRAMMER: Thomas Faber <thfabba@gmx.de>
+ */
+
+ #include <kmt_test.h>
+
+ KMT_TESTFUNC Test_Example;
+ KMT_TESTFUNC Test_IoDeviceObject;
+ KMT_TESTFUNC Test_RtlAvlTree;
+ KMT_TESTFUNC Test_RtlMemory;
+ KMT_TESTFUNC Test_RtlSplayTree;
+
+ /* tests with a leading '-' will not be listed */
+ const KMT_TEST TestList[] =
+ {
+ { "Example", Test_Example },
+ { "IoDeviceObject", Test_IoDeviceObject },
+ { "RtlAvlTree", Test_RtlAvlTree },
+ { "RtlMemory", Test_RtlMemory },
+ { "RtlSplayTree", Test_RtlSplayTree },
+ { NULL, NULL },
+ };
--- /dev/null
+ <module name="kmtest_drv" type="kernelmodedriver" installbase="bin" installname="kmtest_drv.sys">
+ <include base="kmtest_drv">include</include>
+ <library>ntoskrnl</library>
+ <library>hal</library>
+ <library>pseh</library>
+ <library>kmtest_printf</library>
+ <define name="KMT_KERNEL_MODE" />
+ <define name="NTDDI_VERSION">NTDDI_WS03SP1</define>
+ <directory name="kmtest_drv">
+ <file>kmtest_drv.c</file>
+ <file>testlist.c</file>
+ </directory>
+ <directory name="example">
+ <file>Example.c</file>
+ <file>KernelType.c</file>
+ </directory>
+ <directory name="ntos_ex">
+ <file>ExDoubleList.c</file>
+ <file>ExFastMutex.c</file>
+ <file>ExHardError.c</file>
+ <file>ExInterlocked.c</file>
+ <file>ExPools.c</file>
+ <file>ExResource.c</file>
+ <file>ExSequencedList.c</file>
+ <file>ExSingleList.c</file>
+ <file>ExTimer.c</file>
+ </directory>
+ <directory name="ntos_fsrtl">
+ <file>FsRtlExpression.c</file>
+ </directory>
+ <directory name="ntos_io">
+ <file>IoDeviceInterface.c</file>
+ <file>IoInterrupt.c</file>
+ <file>IoIrp.c</file>
+ <file>IoMdl.c</file>
+ </directory>
+ <directory name="ntos_ke">
+ <file>KeApc.c</file>
+ <file>KeDpc.c</file>
+ <file>KeEvent.c</file>
+ <file>KeGuardedMutex.c</file>
+ <file>KeIrql.c</file>
+ <file>KeProcessor.c</file>
+ <file>KeSpinLock.c</file>
+ </directory>
+ <directory name="ntos_ob">
+ <file>ObReference.c</file>
+ <file>ObType.c</file>
+ </directory>
+ <directory name="rtl">
+ <file>RtlAvlTree.c</file>
+ <file>RtlMemory.c</file>
+ <file>RtlSplayTree.c</file>
+ </directory>
+ </module>
+ <module name="kmtest_printf" type="staticlibrary">
+ <include base="crt">include</include>
+ <define name="_LIBCNT_" />
+ <define name="_USER32_WSPRINTF" />
+ <define name="wctomb">KmtWcToMb</define>
+ <directory name="kmtest_drv">
+ <file>printf_stubs.c</file>
+ </directory>
+ <directory name="..">
+ <directory name="..">
+ <directory name="..">
+ <directory name="lib">
+ <directory name="sdk">
+ <directory name="crt">
+ <directory name="printf">
+ <file>streamout.c</file>
+ </directory>
+ </directory>
+ </directory>
+ </directory>
+ </directory>
+ </directory>
+ </directory>
+ </module>
--- /dev/null
+ /*
+ * 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 <ntifs.h>
+ #include <ndk/ketypes.h>
+ #include <ntstrsafe.h>
+ #include <limits.h>
+ #include <pseh/pseh2.h>
+
+ //#define NDEBUG
+ #include <debug.h>
+
+ #include <kmt_public.h>
+ #define KMT_DEFINE_TEST_FUNCTIONS
+ #include <kmt_test.h>
+
+ /* Prototypes */
+ DRIVER_INITIALIZE DriverEntry;
+ static DRIVER_UNLOAD DriverUnload;
+ static DRIVER_DISPATCH DriverCreate;
+ static DRIVER_DISPATCH DriverClose;
+ static DRIVER_DISPATCH DriverIoControl;
+
+ /* 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;
+ PKMT_DEVICE_EXTENSION DeviceExtension;
+ PKPRCB Prcb;
+
+ PAGED_CODE();
+
+ UNREFERENCED_PARAMETER(RegistryPath);
+
+ DPRINT("DriverEntry\n");
+
+ Prcb = KeGetCurrentPrcb();
+ KmtIsCheckedBuild = (Prcb->BuildType & PRCB_BUILD_DEBUG) != 0;
+ KmtIsMultiProcessorBuild = (Prcb->BuildType & PRCB_BUILD_UNIPROCESSOR) == 0;
+
+ RtlInitUnicodeString(&DeviceName, KMTEST_DEVICE_DRIVER_PATH);
+ Status = IoCreateDevice(DriverObject, sizeof(KMT_DEVICE_EXTENSION),
+ &DeviceName,
+ FILE_DEVICE_UNKNOWN,
+ FILE_DEVICE_SECURE_OPEN | FILE_READ_ONLY_DEVICE,
+ FALSE, &MainDeviceObject);
+
+ if (!NT_SUCCESS(Status))
+ goto cleanup;
+
+ DPRINT("DriverEntry. Created DeviceObject %p. DeviceExtension %p\n",
+ MainDeviceObject, MainDeviceObject->DeviceExtension);
+ DeviceExtension = MainDeviceObject->DeviceExtension;
+ DeviceExtension->ResultBuffer = NULL;
+ DeviceExtension->Mdl = NULL;
+
+ DriverObject->DriverUnload = DriverUnload;
+ DriverObject->MajorFunction[IRP_MJ_CREATE] = DriverCreate;
+ DriverObject->MajorFunction[IRP_MJ_CLOSE] = DriverClose;
+ DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DriverIoControl;
+
+ 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)
+ {
+ PKMT_DEVICE_EXTENSION DeviceExtension = MainDeviceObject->DeviceExtension;
+ ASSERT(!DeviceExtension->Mdl);
+ ASSERT(!DeviceExtension->ResultBuffer);
+ ASSERT(!ResultBuffer);
+ IoDeleteDevice(MainDeviceObject);
+ }
+ }
+
+ /**
+ * @name DriverCreate
+ *
+ * Driver Dispatch function for CreateFile
+ *
+ * @param DeviceObject
+ * Device Object
+ * @param Irp
+ * I/O request packet
+ *
+ * @return Status
+ */
+ static
+ NTSTATUS
+ NTAPI
+ DriverCreate(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp)
+ {
+ NTSTATUS Status = STATUS_SUCCESS;
+ PIO_STACK_LOCATION IoStackLocation;
+
+ PAGED_CODE();
+
+ IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
+
+ DPRINT("DriverCreate. DeviceObject=%p, RequestorMode=%d, FileObject=%p, FsContext=%p, FsContext2=%p\n",
+ DeviceObject, Irp->RequestorMode, IoStackLocation->FileObject,
+ IoStackLocation->FileObject->FsContext, IoStackLocation->FileObject->FsContext2);
+
+ Irp->IoStatus.Status = Status;
+ Irp->IoStatus.Information = 0;
+
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ return Status;
+ }
+
+ /**
+ * @name DriverClose
+ *
+ * Driver Dispatch function for CloseHandle.
+ *
+ * @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;
+ PIO_STACK_LOCATION IoStackLocation;
+ PKMT_DEVICE_EXTENSION DeviceExtension;
+
+ PAGED_CODE();
+
+ IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
+
+ DPRINT("DriverClose. DeviceObject=%p, RequestorMode=%d, FileObject=%p, FsContext=%p, FsContext2=%p\n",
+ DeviceObject, Irp->RequestorMode, IoStackLocation->FileObject,
+ IoStackLocation->FileObject->FsContext, IoStackLocation->FileObject->FsContext2);
+
+ ASSERT(IoStackLocation->FileObject->FsContext2 == NULL);
+ DeviceExtension = DeviceObject->DeviceExtension;
+ if (DeviceExtension->Mdl && IoStackLocation->FileObject->FsContext == DeviceExtension->Mdl)
+ {
+ MmUnlockPages(DeviceExtension->Mdl);
+ IoFreeMdl(DeviceExtension->Mdl);
+ DeviceExtension->Mdl = NULL;
+ ResultBuffer = DeviceExtension->ResultBuffer = NULL;
+ }
+ else
+ {
+ ASSERT(IoStackLocation->FileObject->FsContext == NULL);
+ }
+
+ 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, FileObject=%p, FsContext=%p, FsContext2=%p\n",
+ IoStackLocation->Parameters.DeviceIoControl.IoControlCode,
+ DeviceObject, IoStackLocation->FileObject,
+ IoStackLocation->FileObject->FsContext, IoStackLocation->FileObject->FsContext2);
+
+ 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;
+ if (TestEntry->TestName[0] == '-')
+ RtlInitAnsiString(&EntryName, TestEntry->TestName + 1);
+ else
+ 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;
+ }
+ case IOCTL_KMTEST_SET_RESULTBUFFER:
+ {
+ PKMT_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
+
+ DPRINT("DriverIoControl. IOCTL_KMTEST_SET_RESULTBUFFER, buffer=%p, inlen=%lu, outlen=%lu\n",
+ IoStackLocation->Parameters.DeviceIoControl.Type3InputBuffer,
+ IoStackLocation->Parameters.DeviceIoControl.InputBufferLength,
+ IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength);
+
+ if (DeviceExtension->Mdl)
+ {
+ if (IoStackLocation->FileObject->FsContext != DeviceExtension->Mdl)
+ {
+ Status = STATUS_ACCESS_DENIED;
+ break;
+ }
+ MmUnlockPages(DeviceExtension->Mdl);
+ IoFreeMdl(DeviceExtension->Mdl);
+ IoStackLocation->FileObject->FsContext = NULL;
+ ResultBuffer = DeviceExtension->ResultBuffer = NULL;
+ }
+
+ DeviceExtension->Mdl = IoAllocateMdl(IoStackLocation->Parameters.DeviceIoControl.Type3InputBuffer,
+ IoStackLocation->Parameters.DeviceIoControl.InputBufferLength,
+ FALSE, FALSE, NULL);
+ if (!DeviceExtension->Mdl)
+ {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ break;
+ }
+
+ _SEH2_TRY
+ {
+ MmProbeAndLockPages(DeviceExtension->Mdl, UserMode, IoModifyAccess);
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ Status = _SEH2_GetExceptionCode();
+ IoFreeMdl(DeviceExtension->Mdl);
+ DeviceExtension->Mdl = NULL;
+ break;
+ } _SEH2_END;
+
+ ResultBuffer = DeviceExtension->ResultBuffer = MmGetSystemAddressForMdlSafe(DeviceExtension->Mdl, NormalPagePriority);
+ IoStackLocation->FileObject->FsContext = DeviceExtension->Mdl;
+
+ DPRINT("DriverIoControl. ResultBuffer: %ld %ld %ld %ld\n",
+ ResultBuffer->Successes, ResultBuffer->Failures,
+ ResultBuffer->LogBufferLength, ResultBuffer->LogBufferMaxLength);
+ 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;
+ }
--- /dev/null
+ /*
+ * 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>
--- /dev/null
+ /*
+ * PROJECT: ReactOS kernel-mode tests
+ * LICENSE: GPLv2+ - See COPYING in the top level directory
+ * PURPOSE: Kernel-Mode Test Suite Example Test Driver
+ * PROGRAMMER: Thomas Faber <thfabba@gmx.de>
+ */
+
+ #include <ntddk.h>
+ #include <ntifs.h>
+ #include <ndk/ketypes.h>
+
+ #define KMT_DEFINE_TEST_FUNCTIONS
+ #include <kmt_test.h>
+
+ //#define NDEBUG
+ #include <debug.h>
+
+ #include <kmt_public.h>
+
+ /* types */
+ typedef struct
+ {
+ UCHAR MajorFunction;
+ PDEVICE_OBJECT DeviceObject;
+ PKMT_IRP_HANDLER IrpHandler;
+ } KMT_IRP_HANDLER_ENTRY, *PKMT_IRP_HANDLER_ENTRY;
+
+ typedef struct
+ {
+ ULONG ControlCode;
+ PDEVICE_OBJECT DeviceObject;
+ PKMT_MESSAGE_HANDLER MessageHandler;
+ } KMT_MESSAGE_HANDLER_ENTRY, *PKMT_MESSAGE_HANDLER_ENTRY;
+
+ /* Prototypes */
+ DRIVER_INITIALIZE DriverEntry;
+ static DRIVER_UNLOAD DriverUnload;
+ static DRIVER_DISPATCH DriverDispatch;
+ static KMT_IRP_HANDLER DeviceControlHandler;
+
+ /* Globals */
+ static PDEVICE_OBJECT TestDeviceObject;
+ static PDEVICE_OBJECT KmtestDeviceObject;
+
+ #define KMT_MAX_IRP_HANDLERS 256
+ static KMT_IRP_HANDLER_ENTRY IrpHandlers[KMT_MAX_IRP_HANDLERS] = { { 0 } };
+ #define KMT_MAX_MESSAGE_HANDLERS 256
+ static KMT_MESSAGE_HANDLER_ENTRY MessageHandlers[KMT_MAX_MESSAGE_HANDLERS] = { { 0 } };
+
+ /**
+ * @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;
+ WCHAR DeviceNameBuffer[128] = L"\\Device\\Kmtest-";
+ UNICODE_STRING KmtestDeviceName;
+ PFILE_OBJECT KmtestFileObject;
+ PKMT_DEVICE_EXTENSION KmtestDeviceExtension;
+ UNICODE_STRING DeviceName;
+ PCWSTR DeviceNameSuffix;
+ INT Flags = 0;
+ int i;
+ PKPRCB Prcb;
+
+ PAGED_CODE();
+
+ DPRINT("DriverEntry\n");
+
+ Prcb = KeGetCurrentPrcb();
+ KmtIsCheckedBuild = (Prcb->BuildType & PRCB_BUILD_DEBUG) != 0;
+ KmtIsMultiProcessorBuild = (Prcb->BuildType & PRCB_BUILD_UNIPROCESSOR) == 0;
+
+ /* get the Kmtest device, so that we get a ResultBuffer pointer */
+ RtlInitUnicodeString(&KmtestDeviceName, KMTEST_DEVICE_DRIVER_PATH);
+ Status = IoGetDeviceObjectPointer(&KmtestDeviceName, FILE_ALL_ACCESS, &KmtestFileObject, &KmtestDeviceObject);
+
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT1("Failed to get Kmtest device object pointer\n");
+ goto cleanup;
+ }
+
+ Status = ObReferenceObjectByPointer(KmtestDeviceObject, FILE_ALL_ACCESS, NULL, KernelMode);
+
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT1("Failed to reference Kmtest device object\n");
+ goto cleanup;
+ }
+
+ ObDereferenceObject(KmtestFileObject);
+ KmtestFileObject = NULL;
+ KmtestDeviceExtension = KmtestDeviceObject->DeviceExtension;
+ ResultBuffer = KmtestDeviceExtension->ResultBuffer;
+ DPRINT("KmtestDeviceObject: %p\n", (PVOID)KmtestDeviceObject);
+ DPRINT("KmtestDeviceExtension: %p\n", (PVOID)KmtestDeviceExtension);
+ DPRINT("Setting ResultBuffer: %p\n", (PVOID)ResultBuffer);
+
+ /* call TestEntry */
+ RtlInitUnicodeString(&DeviceName, DeviceNameBuffer);
+ DeviceName.MaximumLength = sizeof DeviceNameBuffer;
+ TestEntry(DriverObject, RegistryPath, &DeviceNameSuffix, &Flags);
+
+ /* create test device */
+ if (!(Flags & TESTENTRY_NO_CREATE_DEVICE))
+ {
+ RtlAppendUnicodeToString(&DeviceName, DeviceNameSuffix);
+ Status = IoCreateDevice(DriverObject, 0, &DeviceName,
+ FILE_DEVICE_UNKNOWN,
+ FILE_DEVICE_SECURE_OPEN | FILE_READ_ONLY_DEVICE,
+ TRUE, &TestDeviceObject);
+
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT1("Could not create device object %wZ\n", &DeviceName);
+ goto cleanup;
+ }
+
+ DPRINT("DriverEntry. Created DeviceObject %p\n",
+ TestDeviceObject);
+ }
+
+ /* initialize dispatch functions */
+ if (!(Flags & TESTENTRY_NO_REGISTER_UNLOAD))
+ DriverObject->DriverUnload = DriverUnload;
+ if (!(Flags & TESTENTRY_NO_REGISTER_DISPATCH))
+ for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; ++i)
+ DriverObject->MajorFunction[i] = DriverDispatch;
+
+ cleanup:
+ if (TestDeviceObject && !NT_SUCCESS(Status))
+ {
+ IoDeleteDevice(TestDeviceObject);
+ TestDeviceObject = NULL;
+ }
+
+ if (KmtestDeviceObject && !NT_SUCCESS(Status))
+ {
+ ObDereferenceObject(KmtestDeviceObject);
+ KmtestDeviceObject = NULL;
+ if (KmtestFileObject)
+ ObDereferenceObject(KmtestFileObject);
+ }
+
+ return Status;
+ }
+
+ /**
+ * @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");
+
+ TestUnload(DriverObject);
+
+ if (TestDeviceObject)
+ IoDeleteDevice(TestDeviceObject);
+
+ if (KmtestDeviceObject)
+ ObDereferenceObject(KmtestDeviceObject);
+ }
+
+ /**
+ * @name KmtRegisterIrpHandler
+ *
+ * Register a handler with the IRP Dispatcher.
+ * If multiple registered handlers match an IRP, it is unspecified which of
+ * them is called on IRP reception
+ *
+ * @param MajorFunction
+ * IRP major function code to be handled
+ * @param DeviceObject
+ * Device Object to handle IRPs for.
+ * Can be NULL to indicate any device object
+ * @param IrpHandler
+ * Handler function to register.
+ *
+ * @return Status
+ */
+ NTSTATUS
+ KmtRegisterIrpHandler(
+ IN UCHAR MajorFunction,
+ IN PDEVICE_OBJECT DeviceObject OPTIONAL,
+ IN PKMT_IRP_HANDLER IrpHandler)
+ {
+ NTSTATUS Status = STATUS_SUCCESS;
+ int i;
+
+ if (MajorFunction > IRP_MJ_MAXIMUM_FUNCTION)
+ {
+ Status = STATUS_INVALID_PARAMETER_1;
+ goto cleanup;
+ }
+
+ if (IrpHandler == NULL)
+ {
+ Status = STATUS_INVALID_PARAMETER_3;
+ goto cleanup;
+ }
+
+ for (i = 0; i < sizeof IrpHandlers / sizeof IrpHandlers[0]; ++i)
+ if (IrpHandlers[i].IrpHandler == NULL)
+ {
+ IrpHandlers[i].MajorFunction = MajorFunction;
+ IrpHandlers[i].DeviceObject = DeviceObject;
+ IrpHandlers[i].IrpHandler = IrpHandler;
+ goto cleanup;
+ }
+
+ Status = STATUS_ALLOTTED_SPACE_EXCEEDED;
+
+ cleanup:
+ return Status;
+ }
+
+ /**
+ * @name KmtUnregisterIrpHandler
+ *
+ * Unregister a handler with the IRP Dispatcher.
+ * Parameters must be specified exactly as in the call to
+ * KmtRegisterIrpHandler. Only the first matching entry will be removed
+ * if multiple exist
+ *
+ * @param MajorFunction
+ * IRP major function code of the handler to be removed
+ * @param DeviceObject
+ * Device Object to of the handler to be removed
+ * @param IrpHandler
+ * Handler function of the handler to be removed
+ *
+ * @return Status
+ */
+ NTSTATUS
+ KmtUnregisterIrpHandler(
+ IN UCHAR MajorFunction,
+ IN PDEVICE_OBJECT DeviceObject OPTIONAL,
+ IN PKMT_IRP_HANDLER IrpHandler)
+ {
+ NTSTATUS Status = STATUS_SUCCESS;
+ int i;
+
+ for (i = 0; i < sizeof IrpHandlers / sizeof IrpHandlers[0]; ++i)
+ if (IrpHandlers[i].MajorFunction == MajorFunction &&
+ IrpHandlers[i].DeviceObject == DeviceObject &&
+ IrpHandlers[i].IrpHandler == IrpHandler)
+ {
+ IrpHandlers[i].IrpHandler = NULL;
+ goto cleanup;
+ }
+
+ Status = STATUS_NOT_FOUND;
+
+ cleanup:
+ return Status;
+ }
+
+ /**
+ * @name DriverDispatch
+ *
+ * Driver Dispatch function
+ *
+ * @param DeviceObject
+ * Device Object
+ * @param Irp
+ * I/O request packet
+ *
+ * @return Status
+ */
+ static
+ NTSTATUS
+ NTAPI
+ DriverDispatch(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp)
+ {
+ NTSTATUS Status = STATUS_SUCCESS;
+ PIO_STACK_LOCATION IoStackLocation;
+ int i;
+
+ PAGED_CODE();
+
+ IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
+
+ DPRINT("DriverDispatch: Function=%s, Device=%p\n",
+ KmtMajorFunctionNames[IoStackLocation->MajorFunction],
+ DeviceObject);
+
+ for (i = 0; i < sizeof IrpHandlers / sizeof IrpHandlers[0]; ++i)
+ {
+ if (IrpHandlers[i].MajorFunction == IoStackLocation->MajorFunction &&
+ (IrpHandlers[i].DeviceObject == NULL || IrpHandlers[i].DeviceObject == DeviceObject) &&
+ IrpHandlers[i].IrpHandler != NULL)
+ return IrpHandlers[i].IrpHandler(DeviceObject, Irp, IoStackLocation);
+ }
+
+ /* default handler for DeviceControl */
+ if (IoStackLocation->MajorFunction == IRP_MJ_DEVICE_CONTROL ||
+ IoStackLocation->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL)
+ return DeviceControlHandler(DeviceObject, Irp, IoStackLocation);
+
+ /* default handler */
+ Irp->IoStatus.Status = Status;
+ Irp->IoStatus.Information = 0;
+
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ return Status;
+ }
+
+ /**
+ * @name KmtRegisterMessageHandler
+ *
+ * Register a handler with the DeviceControl Dispatcher.
+ * If multiple registered handlers match a message, it is unspecified which of
+ * them is called on message reception.
+ * NOTE: message handlers registered with this function will not be called
+ * if a custom IRP handler matching the corresponding IRP is installed!
+ *
+ * @param ControlCode
+ * Control code to be handled, as passed by the application.
+ * Can be 0 to indicate any control code
+ * @param DeviceObject
+ * Device Object to handle IRPs for.
+ * Can be NULL to indicate any device object
+ * @param MessageHandler
+ * Handler function to register.
+ *
+ * @return Status
+ */
+ NTSTATUS
+ KmtRegisterMessageHandler(
+ IN ULONG ControlCode OPTIONAL,
+ IN PDEVICE_OBJECT DeviceObject OPTIONAL,
+ IN PKMT_MESSAGE_HANDLER MessageHandler)
+ {
+ NTSTATUS Status = STATUS_SUCCESS;
+ int i;
+
+ if (ControlCode >= 0x400)
+ {
+ Status = STATUS_INVALID_PARAMETER_1;
+ goto cleanup;
+ }
+
+ if (MessageHandler == NULL)
+ {
+ Status = STATUS_INVALID_PARAMETER_2;
+ goto cleanup;
+ }
+
+ for (i = 0; i < sizeof MessageHandlers / sizeof MessageHandlers[0]; ++i)
+ if (MessageHandlers[i].MessageHandler == NULL)
+ {
+ MessageHandlers[i].ControlCode = ControlCode;
+ MessageHandlers[i].DeviceObject = DeviceObject;
+ MessageHandlers[i].MessageHandler = MessageHandler;
+ goto cleanup;
+ }
+
+ Status = STATUS_ALLOTTED_SPACE_EXCEEDED;
+
+ cleanup:
+ return Status;
+ }
+
+ /**
+ * @name KmtUnregisterMessageHandler
+ *
+ * Unregister a handler with the DeviceControl Dispatcher.
+ * Parameters must be specified exactly as in the call to
+ * KmtRegisterMessageHandler. Only the first matching entry will be removed
+ * if multiple exist
+ *
+ * @param ControlCode
+ * Control code of the handler to be removed
+ * @param DeviceObject
+ * Device Object to of the handler to be removed
+ * @param MessageHandler
+ * Handler function of the handler to be removed
+ *
+ * @return Status
+ */
+ NTSTATUS
+ KmtUnregisterMessageHandler(
+ IN ULONG ControlCode OPTIONAL,
+ IN PDEVICE_OBJECT DeviceObject OPTIONAL,
+ IN PKMT_MESSAGE_HANDLER MessageHandler)
+ {
+ NTSTATUS Status = STATUS_SUCCESS;
+ int i;
+
+ for (i = 0; i < sizeof MessageHandlers / sizeof MessageHandlers[0]; ++i)
+ if (MessageHandlers[i].ControlCode == ControlCode &&
+ MessageHandlers[i].DeviceObject == DeviceObject &&
+ MessageHandlers[i].MessageHandler == MessageHandler)
+ {
+ MessageHandlers[i].MessageHandler = NULL;
+ goto cleanup;
+ }
+
+ Status = STATUS_NOT_FOUND;
+
+ cleanup:
+ return Status;
+ }
+
+ /**
+ * @name DeviceControlHandler
+ *
+ * Default IRP_MJ_DEVICE_CONTROL/IRP_MJ_INTERNAL_DEVICE_CONTROL handler
+ *
+ * @param DeviceObject
+ * Device Object.
+ * This is guaranteed not to have been touched by the dispatch function
+ * before the call to the IRP handler
+ * @param Irp
+ * Device Object.
+ * This is guaranteed not to have been touched by the dispatch function
+ * before the call to the IRP handler, except for passing it to
+ * IoGetCurrentStackLocation
+ * @param IoStackLocation
+ * Device Object.
+ * This is guaranteed not to have been touched by the dispatch function
+ * before the call to the IRP handler
+ *
+ * @return Status
+ */
+ static
+ NTSTATUS
+ DeviceControlHandler(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IoStackLocation)
+ {
+ NTSTATUS Status = STATUS_SUCCESS;
+ ULONG ControlCode = (IoStackLocation->Parameters.DeviceIoControl.IoControlCode & 0x00000FFC) >> 2;
+ ULONG OutLength = IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength;
+ int i;
+
+ for (i = 0; i < sizeof MessageHandlers / sizeof MessageHandlers[0]; ++i)
+ {
+ if ((MessageHandlers[i].ControlCode == 0 ||
+ MessageHandlers[i].ControlCode == ControlCode) &&
+ (MessageHandlers[i].DeviceObject == NULL || MessageHandlers[i].DeviceObject == DeviceObject) &&
+ MessageHandlers[i].MessageHandler != NULL)
+ {
+ Status = MessageHandlers[i].MessageHandler(DeviceObject, ControlCode, Irp->AssociatedIrp.SystemBuffer,
+ IoStackLocation->Parameters.DeviceIoControl.InputBufferLength,
+ &OutLength);
+ break;
+ }
+ }
+
+ Irp->IoStatus.Status = Status;
+ Irp->IoStatus.Information = OutLength;
+
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ return Status;
+ }
--- /dev/null
+ /*
+ * PROJECT: ReactOS kernel-mode tests
+ * LICENSE: GPLv2+ - See COPYING in the top level directory
+ * PURPOSE: Kernel-Mode Test Suite stub functions for any-IRQL vsnprintf
+ * PROGRAMMER: Thomas Faber <thfabba@gmx.de>
+ */
+
+ #undef wctomb
+ #include <stdarg.h>
+ #include <stdio.h>
+ #include <wchar.h>
+
+ int __cdecl KmtWcToMb(char *mbchar, wchar_t wchar)
+ {
+ *mbchar = (char)wchar;
+ return 1;
+ }
+
+ int __cdecl streamout(FILE *stream, const char *format, va_list argptr);
+
+ int __cdecl KmtVSNPrintF(char *buffer, size_t count, const char *format, va_list argptr)
+ {
+ int result;
+ FILE stream;
+
+ stream._base = (char *)buffer;
+ stream._ptr = stream._base;
+ stream._charbuf = 0;
+ stream._cnt = count;
+ stream._bufsiz = 0;
+ stream._flag = _IOSTRG | _IOWRT;
+ stream._tmpfname = 0;
+
+ result = streamout(&stream, format, argptr);
+
+ /* Only zero terminate if there is enough space left */
+ if (stream._cnt) *(char *)stream._ptr = '\0';
+
+ return result;
+ }
--- /dev/null
+ /*
+ * 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 <kmt_test.h>
+
+ KMT_TESTFUNC Test_Example;
+ KMT_TESTFUNC Test_ExDoubleList;
+ KMT_TESTFUNC Test_ExFastMutex;
+ KMT_TESTFUNC Test_ExHardError;
+ KMT_TESTFUNC Test_ExHardErrorInteractive;
+ KMT_TESTFUNC Test_ExInterlocked;
+ KMT_TESTFUNC Test_ExPools;
+ KMT_TESTFUNC Test_ExResource;
+ KMT_TESTFUNC Test_ExSequencedList;
+ KMT_TESTFUNC Test_ExSingleList;
+ KMT_TESTFUNC Test_ExTimer;
+ KMT_TESTFUNC Test_FsRtlExpression;
+ KMT_TESTFUNC Test_IoDeviceInterface;
+ KMT_TESTFUNC Test_IoInterrupt;
+ KMT_TESTFUNC Test_IoIrp;
+ KMT_TESTFUNC Test_IoMdl;
+ KMT_TESTFUNC Test_KeApc;
+ KMT_TESTFUNC Test_KeDpc;
+ KMT_TESTFUNC Test_KeEvent;
+ KMT_TESTFUNC Test_KeGuardedMutex;
+ KMT_TESTFUNC Test_KeIrql;
+ KMT_TESTFUNC Test_KeProcessor;
+ KMT_TESTFUNC Test_KernelType;
+ KMT_TESTFUNC Test_ObReference;
+ KMT_TESTFUNC Test_ObType;
+ KMT_TESTFUNC Test_ObTypeClean;
+ KMT_TESTFUNC Test_ObTypeNoClean;
+ KMT_TESTFUNC Test_RtlAvlTree;
+ KMT_TESTFUNC Test_RtlMemory;
+ KMT_TESTFUNC Test_RtlSplayTree;
+
+ const KMT_TEST TestList[] =
+ {
+ { "ExDoubleList", Test_ExDoubleList },
+ { "ExFastMutex", Test_ExFastMutex },
+ { "ExHardError", Test_ExHardError },
+ { "-ExHardErrorInteractive", Test_ExHardErrorInteractive },
+ { "ExInterlocked", Test_ExInterlocked },
+ { "ExPools", Test_ExPools },
+ { "ExResource", Test_ExResource },
+ { "ExSequencedList", Test_ExSequencedList },
+ { "ExSingleList", Test_ExSingleList },
+ { "-ExTimer", Test_ExTimer },
+ { "Example", Test_Example },
+ { "FsRtlExpression", Test_FsRtlExpression },
+ { "IoDeviceInterface", Test_IoDeviceInterface },
+ { "IoInterrupt", Test_IoInterrupt },
+ { "IoIrp", Test_IoIrp },
+ { "IoMdl", Test_IoMdl },
+ { "KeApc", Test_KeApc },
+ { "KeDpc", Test_KeDpc },
+ { "KeEvent", Test_KeEvent },
+ { "KeGuardedMutex", Test_KeGuardedMutex },
+ { "KeIrql", Test_KeIrql },
+ { "-KeProcessor", Test_KeProcessor },
+ { "-KernelType", Test_KernelType },
+ { "ObReference", Test_ObReference },
+ { "ObType", Test_ObType },
+ { "-ObTypeClean", Test_ObTypeClean },
+ { "-ObTypeNoClean", Test_ObTypeNoClean },
+ { "RtlAvlTreeKM", Test_RtlAvlTree },
+ { "RtlMemoryKM", Test_RtlMemory },
+ { "RtlSplayTreeKM", Test_RtlSplayTree },
+ { NULL, NULL }
+ };
--- /dev/null
+ /*
+ * PROJECT: ReactOS kernel-mode tests
+ * LICENSE: GPLv2+ - See COPYING in the top level directory
+ * PURPOSE: Kernel-Mode Test Suite Doubly-linked list test
+ * PROGRAMMER: Thomas Faber <thfabba@gmx.de>
+ */
+
+ struct _LIST_ENTRY;
+ struct _LIST_ENTRY *__stdcall ExInterlockedInsertHeadList(struct _LIST_ENTRY *, struct _LIST_ENTRY *, unsigned long *);
+ struct _LIST_ENTRY *__stdcall ExInterlockedInsertTailList(struct _LIST_ENTRY *, struct _LIST_ENTRY *, unsigned long *);
+ struct _LIST_ENTRY *__stdcall ExInterlockedRemoveHeadList(struct _LIST_ENTRY *, unsigned long *);
+
+ #include <kmt_test.h>
+
+ LIST_ENTRY Entries[5];
+
+ #define ok_eq_free(Value, Expected) do \
+ { \
+ if (KmtIsCheckedBuild) \
+ ok_eq_pointer(Value, (PVOID)0x0BADD0FF); \
+ else \
+ ok_eq_pointer(Value, Expected); \
+ } while (0)
+
+ #define ok_eq_free2(Value, Expected) do \
+ { \
+ if (KmtIsCheckedBuild) \
+ ok_eq_pointer(Value, (PVOID)0xBADDD0FF); \
+ else \
+ ok_eq_pointer(Value, Expected); \
+ } while (0)
+
+ START_TEST(ExDoubleList)
+ {
+ KSPIN_LOCK SpinLock;
+ LIST_ENTRY ListHead;
+ PLIST_ENTRY Ret;
+
+ KeInitializeSpinLock(&SpinLock);
+
+ memset(&ListHead, 0x55, sizeof ListHead);
+ InitializeListHead(&ListHead);
+ ok_eq_pointer(ListHead.Flink, &ListHead);
+ ok_eq_pointer(ListHead.Blink, &ListHead);
+ ok_bool_true(KmtAreInterruptsEnabled(), "Interrupts enabled:");
+ ok_irql(PASSIVE_LEVEL);
+
+ Ret = ExInterlockedInsertHeadList(&ListHead, &Entries[0], &SpinLock);
+ ok_eq_pointer(Ret, NULL);
+ ok_eq_pointer(ListHead.Flink, &Entries[0]);
+ ok_eq_pointer(ListHead.Blink, &Entries[0]);
+ ok_eq_pointer(Entries[0].Flink, &ListHead);
+ ok_eq_pointer(Entries[0].Blink, &ListHead);
+ ok_bool_true(KmtAreInterruptsEnabled(), "Interrupts enabled:");
+ ok_irql(PASSIVE_LEVEL);
+
+ Ret = ExInterlockedRemoveHeadList(&ListHead, &SpinLock);
+ ok_eq_pointer(Ret, &Entries[0]);
+ ok_eq_pointer(ListHead.Flink, &ListHead);
+ ok_eq_pointer(ListHead.Blink, &ListHead);
+ ok_eq_free(Entries[0].Flink, &ListHead);
+ ok_eq_free(Entries[0].Blink, &ListHead);
+ ok_bool_true(KmtAreInterruptsEnabled(), "Interrupts enabled:");
+ ok_irql(PASSIVE_LEVEL);
+
+ Ret = ExInterlockedRemoveHeadList(&ListHead, &SpinLock);
+ ok_eq_pointer(Ret, NULL);
+ ok_eq_pointer(ListHead.Flink, &ListHead);
+ ok_eq_pointer(ListHead.Blink, &ListHead);
+ ok_eq_free(Entries[0].Flink, &ListHead);
+ ok_eq_free(Entries[0].Blink, &ListHead);
+ ok_bool_true(KmtAreInterruptsEnabled(), "Interrupts enabled:");
+ ok_irql(PASSIVE_LEVEL);
+
+ Ret = ExInterlockedInsertTailList(&ListHead, &Entries[0], &SpinLock);
+ ok_eq_pointer(Ret, NULL);
+ ok_eq_pointer(ListHead.Flink, &Entries[0]);
+ ok_eq_pointer(ListHead.Blink, &Entries[0]);
+ ok_eq_pointer(Entries[0].Flink, &ListHead);
+ ok_eq_pointer(Entries[0].Blink, &ListHead);
+ ok_bool_true(KmtAreInterruptsEnabled(), "Interrupts enabled:");
+ ok_irql(PASSIVE_LEVEL);
+
+ Ret = ExInterlockedRemoveHeadList(&ListHead, &SpinLock);
+ ok_eq_pointer(Ret, &Entries[0]);
+ ok_eq_pointer(ListHead.Flink, &ListHead);
+ ok_eq_pointer(ListHead.Blink, &ListHead);
+ ok_eq_free(Entries[0].Flink, &ListHead);
+ ok_eq_free(Entries[0].Blink, &ListHead);
+ ok_bool_true(KmtAreInterruptsEnabled(), "Interrupts enabled:");
+ ok_irql(PASSIVE_LEVEL);
+
+ Ret = ExInterlockedRemoveHeadList(&ListHead, &SpinLock);
+ ok_eq_pointer(Ret, NULL);
+ ok_eq_pointer(ListHead.Flink, &ListHead);
+ ok_eq_pointer(ListHead.Blink, &ListHead);
+ ok_eq_free(Entries[0].Flink, &ListHead);
+ ok_eq_free(Entries[0].Blink, &ListHead);
+ ok_bool_true(KmtAreInterruptsEnabled(), "Interrupts enabled:");
+ ok_irql(PASSIVE_LEVEL);
+
+ Ret = ExInterlockedInsertTailList(&ListHead, &Entries[0], &SpinLock);
+ ok_eq_pointer(Ret, NULL);
+ ok_eq_pointer(ListHead.Flink, &Entries[0]);
+ ok_eq_pointer(ListHead.Blink, &Entries[0]);
+ ok_eq_pointer(Entries[0].Flink, &ListHead);
+ ok_eq_pointer(Entries[0].Blink, &ListHead);
+ ok_bool_true(KmtAreInterruptsEnabled(), "Interrupts enabled:");
+ ok_irql(PASSIVE_LEVEL);
+
+ Ret = ExInterlockedInsertHeadList(&ListHead, &Entries[1], &SpinLock);
+ ok_eq_pointer(Ret, &Entries[0]);
+ ok_eq_pointer(ListHead.Flink, &Entries[1]);
+ ok_eq_pointer(ListHead.Blink, &Entries[0]);
+ ok_eq_pointer(Entries[0].Flink, &ListHead);
+ ok_eq_pointer(Entries[0].Blink, &Entries[1]);
+ ok_eq_pointer(Entries[1].Flink, &Entries[0]);
+ ok_eq_pointer(Entries[1].Blink, &ListHead);
+ ok_bool_true(KmtAreInterruptsEnabled(), "Interrupts enabled:");
+ ok_irql(PASSIVE_LEVEL);
+
+ Ret = ExInterlockedInsertTailList(&ListHead, &Entries[2], &SpinLock);
+ ok_eq_pointer(Ret, &Entries[0]);
+ ok_eq_pointer(ListHead.Flink, &Entries[1]);
+ ok_eq_pointer(ListHead.Blink, &Entries[2]);
+ ok_eq_pointer(Entries[0].Flink, &Entries[2]);
+ ok_eq_pointer(Entries[0].Blink, &Entries[1]);
+ ok_eq_pointer(Entries[1].Flink, &Entries[0]);
+ ok_eq_pointer(Entries[1].Blink, &ListHead);
+ ok_eq_pointer(Entries[2].Flink, &ListHead);
+ ok_eq_pointer(Entries[2].Blink, &Entries[0]);
+ ok_bool_true(KmtAreInterruptsEnabled(), "Interrupts enabled:");
+ ok_irql(PASSIVE_LEVEL);
+
+ memset(Entries, 0x55, sizeof Entries);
+ #undef ExInterlockedInsertHeadList
+ #undef ExInterlockedInsertTailList
+ #undef ExInterlockedRemoveHeadList
+
+ memset(&ListHead, 0x55, sizeof ListHead);
+ InitializeListHead(&ListHead);
+ ok_eq_pointer(ListHead.Flink, &ListHead);
+ ok_eq_pointer(ListHead.Blink, &ListHead);
+ ok_bool_true(KmtAreInterruptsEnabled(), "Interrupts enabled:");
+ ok_irql(PASSIVE_LEVEL);
+
+ Ret = ExInterlockedInsertHeadList(&ListHead, &Entries[0], &SpinLock);
+ ok_eq_pointer(Ret, NULL);
+ ok_eq_pointer(ListHead.Flink, &Entries[0]);
+ ok_eq_pointer(ListHead.Blink, &Entries[0]);
+ ok_eq_pointer(Entries[0].Flink, &ListHead);
+ ok_eq_pointer(Entries[0].Blink, &ListHead);
+ ok_bool_true(KmtAreInterruptsEnabled(), "Interrupts enabled:");
+ ok_irql(PASSIVE_LEVEL);
+
+ Ret = ExInterlockedRemoveHeadList(&ListHead, &SpinLock);
+ ok_eq_pointer(Ret, &Entries[0]);
+ ok_eq_pointer(ListHead.Flink, &ListHead);
+ ok_eq_pointer(ListHead.Blink, &ListHead);
+ ok_eq_free2(Entries[0].Flink, &ListHead);
+ ok_eq_free2(Entries[0].Blink, &ListHead);
+ ok_bool_true(KmtAreInterruptsEnabled(), "Interrupts enabled:");
+ ok_irql(PASSIVE_LEVEL);
+
+ Ret = ExInterlockedRemoveHeadList(&ListHead, &SpinLock);
+ ok_eq_pointer(Ret, NULL);
+ ok_eq_pointer(ListHead.Flink, &ListHead);
+ ok_eq_pointer(ListHead.Blink, &ListHead);
+ ok_eq_free2(Entries[0].Flink, &ListHead);
+ ok_eq_free2(Entries[0].Blink, &ListHead);
+ ok_bool_true(KmtAreInterruptsEnabled(), "Interrupts enabled:");
+ ok_irql(PASSIVE_LEVEL);
+
+ Ret = ExInterlockedInsertTailList(&ListHead, &Entries[0], &SpinLock);
+ ok_eq_pointer(Ret, NULL);
+ ok_eq_pointer(ListHead.Flink, &Entries[0]);
+ ok_eq_pointer(ListHead.Blink, &Entries[0]);
+ ok_eq_pointer(Entries[0].Flink, &ListHead);
+ ok_eq_pointer(Entries[0].Blink, &ListHead);
+ ok_bool_true(KmtAreInterruptsEnabled(), "Interrupts enabled:");
+ ok_irql(PASSIVE_LEVEL);
+
+ Ret = ExInterlockedRemoveHeadList(&ListHead, &SpinLock);
+ ok_eq_pointer(Ret, &Entries[0]);
+ ok_eq_pointer(ListHead.Flink, &ListHead);
+ ok_eq_pointer(ListHead.Blink, &ListHead);
+ ok_eq_free2(Entries[0].Flink, &ListHead);
+ ok_eq_free2(Entries[0].Blink, &ListHead);
+ ok_bool_true(KmtAreInterruptsEnabled(), "Interrupts enabled:");
+ ok_irql(PASSIVE_LEVEL);
+
+ Ret = ExInterlockedRemoveHeadList(&ListHead, &SpinLock);
+ ok_eq_pointer(Ret, NULL);
+ ok_eq_pointer(ListHead.Flink, &ListHead);
+ ok_eq_pointer(ListHead.Blink, &ListHead);
+ ok_eq_free2(Entries[0].Flink, &ListHead);
+ ok_eq_free2(Entries[0].Blink, &ListHead);
+ ok_bool_true(KmtAreInterruptsEnabled(), "Interrupts enabled:");
+ ok_irql(PASSIVE_LEVEL);
+
+ Ret = ExInterlockedInsertTailList(&ListHead, &Entries[0], &SpinLock);
+ ok_eq_pointer(Ret, NULL);
+ ok_eq_pointer(ListHead.Flink, &Entries[0]);
+ ok_eq_pointer(ListHead.Blink, &Entries[0]);
+ ok_eq_pointer(Entries[0].Flink, &ListHead);
+ ok_eq_pointer(Entries[0].Blink, &ListHead);
+ ok_bool_true(KmtAreInterruptsEnabled(), "Interrupts enabled:");
+ ok_irql(PASSIVE_LEVEL);
+
+ Ret = ExInterlockedInsertHeadList(&ListHead, &Entries[1], &SpinLock);
+ ok_eq_pointer(Ret, &Entries[0]);
+ ok_eq_pointer(ListHead.Flink, &Entries[1]);
+ ok_eq_pointer(ListHead.Blink, &Entries[0]);
+ ok_eq_pointer(Entries[0].Flink, &ListHead);
+ ok_eq_pointer(Entries[0].Blink, &Entries[1]);
+ ok_eq_pointer(Entries[1].Flink, &Entries[0]);
+ ok_eq_pointer(Entries[1].Blink, &ListHead);
+ ok_bool_true(KmtAreInterruptsEnabled(), "Interrupts enabled:");
+ ok_irql(PASSIVE_LEVEL);
+
+ Ret = ExInterlockedInsertTailList(&ListHead, &Entries[2], &SpinLock);
+ ok_eq_pointer(Ret, &Entries[0]);
+ ok_eq_pointer(ListHead.Flink, &Entries[1]);
+ ok_eq_pointer(ListHead.Blink, &Entries[2]);
+ ok_eq_pointer(Entries[0].Flink, &Entries[2]);
+ ok_eq_pointer(Entries[0].Blink, &Entries[1]);
+ ok_eq_pointer(Entries[1].Flink, &Entries[0]);
+ ok_eq_pointer(Entries[1].Blink, &ListHead);
+ ok_eq_pointer(Entries[2].Flink, &ListHead);
+ ok_eq_pointer(Entries[2].Blink, &Entries[0]);
+ ok_bool_true(KmtAreInterruptsEnabled(), "Interrupts enabled:");
+ ok_irql(PASSIVE_LEVEL);
+
+ KmtSetIrql(PASSIVE_LEVEL);
+ }
--- /dev/null
+ /*
+ * PROJECT: ReactOS kernel-mode tests
+ * LICENSE: GPLv2+ - See COPYING in the top level directory
+ * PURPOSE: Kernel-Mode Test Suite Fast Mutex test
+ * PROGRAMMER: Thomas Faber <thfabba@gmx.de>
+ */
+
+ #include <kmt_test.h>
+
+ //#define NDEBUG
+ #include <debug.h>
+
+ NTKERNELAPI VOID FASTCALL ExiAcquireFastMutex(IN OUT PFAST_MUTEX FastMutex);
+ NTKERNELAPI VOID FASTCALL ExiReleaseFastMutex(IN OUT PFAST_MUTEX FastMutex);
+ NTKERNELAPI BOOLEAN FASTCALL ExiTryToAcquireFastMutex(IN OUT PFAST_MUTEX FastMutex);
+
+ #define CheckMutex(Mutex, ExpectedCount, ExpectedOwner, \
+ ExpectedContention, ExpectedOldIrql, \
+ ExpectedIrql) do \
+ { \
+ ok_eq_long((Mutex)->Count, ExpectedCount); \
+ ok_eq_pointer((Mutex)->Owner, ExpectedOwner); \
+ ok_eq_ulong((Mutex)->Contention, ExpectedContention); \
+ ok_eq_ulong((Mutex)->OldIrql, (ULONG)ExpectedOldIrql); \
+ ok_bool_false(KeAreApcsDisabled(), "KeAreApcsDisabled returned"); \
+ ok_irql(ExpectedIrql); \
+ } while (0)
+
+ static
+ VOID
+ TestFastMutex(
+ PFAST_MUTEX Mutex,
+ KIRQL OriginalIrql)
+ {
+ PKTHREAD Thread = KeGetCurrentThread();
+
+ ok_irql(OriginalIrql);
+
+ /* acquire/release normally */
+ ExAcquireFastMutex(Mutex);
+ CheckMutex(Mutex, 0L, Thread, 0LU, OriginalIrql, APC_LEVEL);
+ ok_bool_false(ExTryToAcquireFastMutex(Mutex), "ExTryToAcquireFastMutex returned");
+ CheckMutex(Mutex, 0L, Thread, 0LU, OriginalIrql, APC_LEVEL);
+ ExReleaseFastMutex(Mutex);
+ CheckMutex(Mutex, 1L, NULL, 0LU, OriginalIrql, OriginalIrql);
+
+ /* ntoskrnl's fastcall version */
+ ExiAcquireFastMutex(Mutex);
+ CheckMutex(Mutex, 0L, Thread, 0LU, OriginalIrql, APC_LEVEL);
+ ok_bool_false(ExiTryToAcquireFastMutex(Mutex), "ExiTryToAcquireFastMutex returned");
+ CheckMutex(Mutex, 0L, Thread, 0LU, OriginalIrql, APC_LEVEL);
+ ExiReleaseFastMutex(Mutex);
+ CheckMutex(Mutex, 1L, NULL, 0LU, OriginalIrql, OriginalIrql);
+
+ /* try to acquire */
+ ok_bool_true(ExTryToAcquireFastMutex(Mutex), "ExTryToAcquireFastMutex returned");
+ CheckMutex(Mutex, 0L, Thread, 0LU, OriginalIrql, APC_LEVEL);
+ ExReleaseFastMutex(Mutex);
+ CheckMutex(Mutex, 1L, NULL, 0LU, OriginalIrql, OriginalIrql);
+
+ /* shortcut functions with critical region */
+ ExEnterCriticalRegionAndAcquireFastMutexUnsafe(Mutex);
+ ok_bool_true(KeAreApcsDisabled(), "KeAreApcsDisabled returned");
+ ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(Mutex);
+
+ /* acquire/release unsafe */
+ if (!KmtIsCheckedBuild || OriginalIrql == APC_LEVEL)
+ {
+ ExAcquireFastMutexUnsafe(Mutex);
+ CheckMutex(Mutex, 0L, Thread, 0LU, OriginalIrql, OriginalIrql);
+ ExReleaseFastMutexUnsafe(Mutex);
+ CheckMutex(Mutex, 1L, NULL, 0LU, OriginalIrql, OriginalIrql);
+
+ /* mismatched acquire/release */
+ ExAcquireFastMutex(Mutex);
+ CheckMutex(Mutex, 0L, Thread, 0LU, OriginalIrql, APC_LEVEL);
+ ExReleaseFastMutexUnsafe(Mutex);
+ CheckMutex(Mutex, 1L, NULL, 0LU, OriginalIrql, APC_LEVEL);
+ KmtSetIrql(OriginalIrql);
+ CheckMutex(Mutex, 1L, NULL, 0LU, OriginalIrql, OriginalIrql);
+
+ Mutex->OldIrql = 0x55555555LU;
+ ExAcquireFastMutexUnsafe(Mutex);
+ CheckMutex(Mutex, 0L, Thread, 0LU, 0x55555555LU, OriginalIrql);
+ Mutex->OldIrql = PASSIVE_LEVEL;
+ ExReleaseFastMutex(Mutex);
+ CheckMutex(Mutex, 1L, NULL, 0LU, PASSIVE_LEVEL, PASSIVE_LEVEL);
+ KmtSetIrql(OriginalIrql);
+ CheckMutex(Mutex, 1L, NULL, 0LU, PASSIVE_LEVEL, OriginalIrql);
+ }
+
+ if (!KmtIsCheckedBuild)
+ {
+ /* release without acquire */
+ ExReleaseFastMutexUnsafe(Mutex);
+ CheckMutex(Mutex, 2L, NULL, 0LU, PASSIVE_LEVEL, OriginalIrql);
+ --Mutex->Count;
+ Mutex->OldIrql = OriginalIrql;
+ ExReleaseFastMutex(Mutex);
+ CheckMutex(Mutex, 2L, NULL, 0LU, OriginalIrql, OriginalIrql);
+ ExReleaseFastMutex(Mutex);
+ CheckMutex(Mutex, 3L, NULL, 0LU, OriginalIrql, OriginalIrql);
+ Mutex->Count -= 2;
+ }
+
+ /* make sure we survive this in case of error */
+ ok_eq_long(Mutex->Count, 1L);
+ Mutex->Count = 1;
+ ok_irql(OriginalIrql);
+ KmtSetIrql(OriginalIrql);
+ }
+
+ typedef VOID (FASTCALL *PMUTEX_FUNCTION)(PFAST_MUTEX);
+ typedef BOOLEAN (FASTCALL *PMUTEX_TRY_FUNCTION)(PFAST_MUTEX);
+
+ typedef struct
+ {
+ HANDLE Handle;
+ PKTHREAD Thread;
+ KIRQL Irql;
+ PFAST_MUTEX Mutex;
+ PMUTEX_FUNCTION Acquire;
+ PMUTEX_TRY_FUNCTION TryAcquire;
+ PMUTEX_FUNCTION Release;
+ BOOLEAN Try;
+ BOOLEAN RetExpected;
+ KEVENT InEvent;
+ KEVENT OutEvent;
+ } THREAD_DATA, *PTHREAD_DATA;
+
+ static
+ VOID
+ NTAPI
+ AcquireMutexThread(
+ PVOID Parameter)
+ {
+ PTHREAD_DATA ThreadData = Parameter;
+ KIRQL Irql;
+ BOOLEAN Ret = FALSE;
+ NTSTATUS Status;
+
+ KeRaiseIrql(ThreadData->Irql, &Irql);
+
+ if (ThreadData->Try)
+ {
+ Ret = ThreadData->TryAcquire(ThreadData->Mutex);
+ ok_eq_bool(Ret, ThreadData->RetExpected);
+ }
+ else
+ ThreadData->Acquire(ThreadData->Mutex);
+
+ ok_bool_false(KeSetEvent(&ThreadData->OutEvent, 0, TRUE), "KeSetEvent returned");
+ Status = KeWaitForSingleObject(&ThreadData->InEvent, Executive, KernelMode, FALSE, NULL);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+
+ if (!ThreadData->Try || Ret)
+ ThreadData->Release(ThreadData->Mutex);
+
+ KeLowerIrql(Irql);
+ }
+
+ static
+ VOID
+ InitThreadData(
+ PTHREAD_DATA ThreadData,
+ PFAST_MUTEX Mutex,
+ PMUTEX_FUNCTION Acquire,
+ PMUTEX_TRY_FUNCTION TryAcquire,
+ PMUTEX_FUNCTION Release)
+ {
+ ThreadData->Mutex = Mutex;
+ KeInitializeEvent(&ThreadData->InEvent, NotificationEvent, FALSE);
+ KeInitializeEvent(&ThreadData->OutEvent, NotificationEvent, FALSE);
+ ThreadData->Acquire = Acquire;
+ ThreadData->TryAcquire = TryAcquire;
+ ThreadData->Release = Release;
+ }
+
+ static
+ NTSTATUS
+ StartThread(
+ PTHREAD_DATA ThreadData,
+ PLARGE_INTEGER Timeout,
+ KIRQL Irql,
+ BOOLEAN Try,
+ BOOLEAN RetExpected)
+ {
+ NTSTATUS Status = STATUS_SUCCESS;
+ OBJECT_ATTRIBUTES Attributes;
+
+ ThreadData->Try = Try;
+ ThreadData->Irql = Irql;
+ ThreadData->RetExpected = RetExpected;
+ InitializeObjectAttributes(&Attributes, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
+ Status = PsCreateSystemThread(&ThreadData->Handle, GENERIC_ALL, &Attributes, NULL, NULL, AcquireMutexThread, ThreadData);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ Status = ObReferenceObjectByHandle(ThreadData->Handle, SYNCHRONIZE, PsThreadType, KernelMode, (PVOID *)&ThreadData->Thread, NULL);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+
+ return KeWaitForSingleObject(&ThreadData->OutEvent, Executive, KernelMode, FALSE, Timeout);
+ }
+
+ static
+ VOID
+ FinishThread(
+ PTHREAD_DATA ThreadData)
+ {
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ KeSetEvent(&ThreadData->InEvent, 0, TRUE);
+ Status = KeWaitForSingleObject(ThreadData->Thread, Executive, KernelMode, FALSE, NULL);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+
+ ObDereferenceObject(ThreadData->Thread);
+ Status = ZwClose(ThreadData->Handle);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ KeClearEvent(&ThreadData->InEvent);
+ KeClearEvent(&ThreadData->OutEvent);
+ }
+
+ static
+ VOID
+ TestFastMutexConcurrent(
+ PFAST_MUTEX Mutex)
+ {
+ NTSTATUS Status;
+ THREAD_DATA ThreadData;
+ THREAD_DATA ThreadData2;
+ THREAD_DATA ThreadDataUnsafe;
+ THREAD_DATA ThreadDataTry;
+ LARGE_INTEGER Timeout;
+ Timeout.QuadPart = -10 * 1000 * 10; /* 10 ms */
+
+ InitThreadData(&ThreadData, Mutex, ExAcquireFastMutex, NULL, ExReleaseFastMutex);
+ InitThreadData(&ThreadData2, Mutex, ExAcquireFastMutex, NULL, ExReleaseFastMutex);
+ InitThreadData(&ThreadDataUnsafe, Mutex, ExAcquireFastMutexUnsafe, NULL, ExReleaseFastMutexUnsafe);
+ InitThreadData(&ThreadDataTry, Mutex, NULL, ExTryToAcquireFastMutex, ExReleaseFastMutex);
+
+ /* have a thread acquire the mutex */
+ Status = StartThread(&ThreadData, NULL, PASSIVE_LEVEL, FALSE, FALSE);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ CheckMutex(Mutex, 0L, ThreadData.Thread, 0LU, PASSIVE_LEVEL, PASSIVE_LEVEL);
+ /* have a second thread try to acquire it -- should fail */
+ Status = StartThread(&ThreadDataTry, NULL, PASSIVE_LEVEL, TRUE, FALSE);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ CheckMutex(Mutex, 0L, ThreadData.Thread, 0LU, PASSIVE_LEVEL, PASSIVE_LEVEL);
+ FinishThread(&ThreadDataTry);
+
+ /* have another thread acquire it -- should block */
+ Status = StartThread(&ThreadData2, &Timeout, APC_LEVEL, FALSE, FALSE);
+ ok_eq_hex(Status, STATUS_TIMEOUT);
+ CheckMutex(Mutex, -1L, ThreadData.Thread, 1LU, PASSIVE_LEVEL, PASSIVE_LEVEL);
+
+ /* finish the first thread -- now the second should become available */
+ FinishThread(&ThreadData);
+ Status = KeWaitForSingleObject(&ThreadData2.OutEvent, Executive, KernelMode, FALSE, NULL);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ CheckMutex(Mutex, 0L, ThreadData2.Thread, 1LU, APC_LEVEL, PASSIVE_LEVEL);
+
+ /* block two more threads */
+ Status = StartThread(&ThreadDataUnsafe, &Timeout, APC_LEVEL, FALSE, FALSE);
+ ok_eq_hex(Status, STATUS_TIMEOUT);
+ CheckMutex(Mutex, -1L, ThreadData2.Thread, 2LU, APC_LEVEL, PASSIVE_LEVEL);
+
+ Status = StartThread(&ThreadData, &Timeout, PASSIVE_LEVEL, FALSE, FALSE);
+ ok_eq_hex(Status, STATUS_TIMEOUT);
+ CheckMutex(Mutex, -2L, ThreadData2.Thread, 3LU, APC_LEVEL, PASSIVE_LEVEL);
+
+ /* finish 1 */
+ FinishThread(&ThreadData2);
+ Status = KeWaitForSingleObject(&ThreadDataUnsafe.OutEvent, Executive, KernelMode, FALSE, NULL);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ CheckMutex(Mutex, -1L, ThreadDataUnsafe.Thread, 3LU, APC_LEVEL, PASSIVE_LEVEL);
+
+ /* finish 2 */
+ FinishThread(&ThreadDataUnsafe);
+ Status = KeWaitForSingleObject(&ThreadData.OutEvent, Executive, KernelMode, FALSE, NULL);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ CheckMutex(Mutex, 0L, ThreadData.Thread, 3LU, PASSIVE_LEVEL, PASSIVE_LEVEL);
+
+ /* finish 3 */
+ FinishThread(&ThreadData);
+
+ CheckMutex(Mutex, 1L, NULL, 3LU, PASSIVE_LEVEL, PASSIVE_LEVEL);
+ }
+
+ START_TEST(ExFastMutex)
+ {
+ FAST_MUTEX Mutex;
+ KIRQL Irql;
+
+ memset(&Mutex, 0x55, sizeof Mutex);
+ ExInitializeFastMutex(&Mutex);
+ CheckMutex(&Mutex, 1L, NULL, 0LU, 0x55555555LU, PASSIVE_LEVEL);
+
+ TestFastMutex(&Mutex, PASSIVE_LEVEL);
+ KeRaiseIrql(APC_LEVEL, &Irql);
+ TestFastMutex(&Mutex, APC_LEVEL);
+ if (!KmtIsCheckedBuild)
+ {
+ KeRaiseIrql(DISPATCH_LEVEL, &Irql);
+ TestFastMutex(&Mutex, DISPATCH_LEVEL);
+ KeRaiseIrql(HIGH_LEVEL, &Irql);
+ TestFastMutex(&Mutex, HIGH_LEVEL);
+ }
+ KeLowerIrql(PASSIVE_LEVEL);
+
+ TestFastMutexConcurrent(&Mutex);
+ }
--- /dev/null
+ /*
+ * PROJECT: ReactOS kernel-mode tests
+ * LICENSE: GPLv2+ - See COPYING in the top level directory
+ * PURPOSE: Kernel-Mode Test Suite Hard error message test
+ * PROGRAMMER: Thomas Faber <thfabba@gmx.de>
+ */
+
+ #include <kmt_test.h>
+
+ /* TODO: don't require user interaction, test Io* routines,
+ * test NTSTATUS values with special handling */
+
+ static
+ VOID
+ SetParameters(
+ OUT PULONG_PTR Parameters,
+ IN INT Count,
+ ...)
+ {
+ INT i;
+ va_list Arguments;
+ va_start(Arguments, Count);
+ for (i = 0; i < Count; ++i)
+ Parameters[i] = va_arg(Arguments, ULONG_PTR);
+ va_end(Arguments);
+ }
+
+ #define NoResponse 27
+
+ #define CheckHardError(ErrStatus, UnicodeStringMask, ResponseOption, \
+ ExpectedStatus, ExpectedResponse, \
+ NumberOfParameters, ...) do \
+ { \
+ SetParameters(HardErrorParameters, NumberOfParameters, __VA_ARGS__);\
+ Response = NoResponse; \
+ _SEH2_TRY { \
+ Status = ExRaiseHardError(ErrStatus, \
+ NumberOfParameters, \
+ UnicodeStringMask, \
+ HardErrorParameters, \
+ ResponseOption, \
+ &Response); \
+ } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { \
+ Status = _SEH2_GetExceptionCode(); \
+ } _SEH2_END; \
+ ok_eq_hex(Status, ExpectedStatus); \
+ ok_eq_ulong(Response, (ULONG)ExpectedResponse); \
+ } while (0)
+
+ #define CheckInformationalHardError(ErrStatus, String, Thread, \
+ ExpectedStatus, ExpectedRet) do \
+ { \
+ Status = STATUS_SUCCESS; \
+ Ret = !ExpectedRet; \
+ _SEH2_TRY { \
+ Ret = IoRaiseInformationalHardError(ErrStatus, \
+ String, \
+ Thread); \
+ } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { \
+ Status = _SEH2_GetExceptionCode(); \
+ } _SEH2_END; \
+ ok_eq_hex(Status, ExpectedStatus); \
+ ok_eq_bool(Ret, ExpectedRet); \
+ } while (0)
+
+ static
+ VOID
+ TestHardError(
+ BOOLEAN InteractivePart1,
+ BOOLEAN InteractivePart2,
+ BOOLEAN InteractivePart3,
+ BOOLEAN InteractivePart4)
+ {
+ NTSTATUS Status;
+ ULONG Response;
+ WCHAR StringBuffer1[] = L"Parameter1+Garbage";
+ CHAR StringBuffer1Ansi[] = "Parameter1+Garbage";
+ WCHAR StringBuffer2[] = L"Parameter2+Garbage";
+ UNICODE_STRING String1 = RTL_CONSTANT_STRING(StringBuffer1);
+ ANSI_STRING String1Ansi = RTL_CONSTANT_STRING(StringBuffer1Ansi);
+ UNICODE_STRING String2 = RTL_CONSTANT_STRING(StringBuffer2);
+ ULONG_PTR HardErrorParameters[6];
+ BOOLEAN Ret;
+
+ String1.Length = sizeof L"Parameter1" - sizeof UNICODE_NULL;
+ String1Ansi.Length = sizeof "Parameter1" - sizeof ANSI_NULL;
+ String2.Length = sizeof L"Parameter2" - sizeof UNICODE_NULL;
+
+ if (InteractivePart1)
+ {
+ CheckHardError(0x40000000, 0, OptionOk, STATUS_SUCCESS, ResponseOk, 0, 0); // outputs a box :|
+ CheckHardError(0x40000001, 0, OptionOk, STATUS_SUCCESS, ResponseOk, 4, 1, 2, 3, 4); // outputs a box :|
+ CheckHardError(0x40000002, 0, OptionOk, STATUS_SUCCESS, ResponseOk, 5, 1, 2, 3, 4, 5); // outputs a box :|
+ CheckHardError(0x40000003, 0, OptionOk, STATUS_SUCCESS, ResponseNotHandled, 6, 1, 2, 3, 4, 5, 6); // TODO: interactive on ROS
+ }
+
+ CheckHardError(0x40000004, 0, OptionShutdownSystem, STATUS_PRIVILEGE_NOT_HELD, ResponseNotHandled, 0, 0);
+ if (InteractivePart1)
+ {
+ // TODO: these 2 are interactive on ROS
+ CheckHardError(0x40000005, 0, OptionOkNoWait, STATUS_SUCCESS, ResponseOk, 0, 0); // outputs a balloon notification
+ CheckHardError(0x4000000f, 0, OptionOkNoWait, STATUS_SUCCESS, ResponseOk, 0, 0); // outputs a balloon notification
+ CheckHardError(0x40000006, 0, OptionAbortRetryIgnore, STATUS_SUCCESS, ResponseAbort, 0, 0); // outputs a box :|
+ CheckHardError(0x40000006, 0, OptionAbortRetryIgnore, STATUS_SUCCESS, ResponseRetry, 0, 0); // outputs a box :|
+ CheckHardError(0x40000006, 0, OptionAbortRetryIgnore, STATUS_SUCCESS, ResponseIgnore, 0, 0); // outputs a box :|
+ CheckHardError(0x40000008, 0, OptionCancelTryContinue, STATUS_SUCCESS, ResponseCancel, 0, 0); // outputs a box :|
+ CheckHardError(0x40000008, 0, OptionCancelTryContinue, STATUS_SUCCESS, ResponseTryAgain, 0, 0); // outputs a box :|
+ CheckHardError(0x40000008, 0, OptionCancelTryContinue, STATUS_SUCCESS, ResponseContinue, 0, 0); // outputs a box :|
+ CheckHardError(0x40000010, 0, OptionOkCancel, STATUS_SUCCESS, ResponseOk, 0, 0); // outputs a box :|
+ CheckHardError(0x40000010, 0, OptionOkCancel, STATUS_SUCCESS, ResponseCancel, 0, 0); // outputs a box :|
+ CheckHardError(0x40000011, 0, OptionRetryCancel, STATUS_SUCCESS, ResponseRetry, 0, 0); // outputs a box :|
+ CheckHardError(0x40000011, 0, OptionRetryCancel, STATUS_SUCCESS, ResponseCancel, 0, 0); // outputs a box :|
+ CheckHardError(0x40000012, 0, OptionYesNo, STATUS_SUCCESS, ResponseYes, 0, 0); // outputs a box :|
+ CheckHardError(0x40000012, 0, OptionYesNo, STATUS_SUCCESS, ResponseNo, 0, 0); // outputs a box :|
+ CheckHardError(0x40000013, 0, OptionYesNoCancel, STATUS_SUCCESS, ResponseYes, 0, 0); // outputs a box :|
+ CheckHardError(0x40000013, 0, OptionYesNoCancel, STATUS_SUCCESS, ResponseNo, 0, 0); // outputs a box :|
+ CheckHardError(0x40000013, 0, OptionYesNoCancel, STATUS_SUCCESS, ResponseCancel, 0, 0); // outputs a box :|
+ }
+ CheckHardError(0x40000009, 0, 9, STATUS_SUCCESS, ResponseNotHandled, 0, 0);
+ CheckHardError(0x4000000a, 0, 10, STATUS_SUCCESS, ResponseNotHandled, 0, 0);
+ CheckHardError(0x4000000b, 0, 11, STATUS_SUCCESS, ResponseNotHandled, 0, 0);
+ CheckHardError(0x4000000c, 0, 12, STATUS_SUCCESS, ResponseNotHandled, 0, 0);
+ CheckHardError(0x4000000d, 0, MAXULONG / 2 + 1, STATUS_SUCCESS, ResponseNotHandled, 0, 0);
+ CheckHardError(0x4000000d, 0, MAXULONG, STATUS_SUCCESS, ResponseNotHandled, 0, 0);
+
+ if (InteractivePart2)
+ {
+ /* try a message with one parameter */
+ CheckHardError(STATUS_DLL_NOT_FOUND, 1, OptionYesNoCancel, STATUS_SUCCESS, ResponseYes, 1, &String1); // outputs a box :|
+ CheckHardError(STATUS_DLL_NOT_FOUND, 0, OptionYesNoCancel, STATUS_SUCCESS, ResponseCancel, 1, &String1); // outputs a box :|
+ CheckHardError(STATUS_DLL_NOT_FOUND, 1, OptionYesNoCancel, STATUS_SUCCESS, ResponseCancel, 0, &String1); // outputs a box :|
+ CheckHardError(STATUS_DLL_NOT_FOUND, 0, OptionYesNoCancel, STATUS_SUCCESS, ResponseCancel, 0, &String1); // outputs a box :|
+ /* give too many parameters */
+ CheckHardError(STATUS_DLL_NOT_FOUND, 1, OptionYesNoCancel, STATUS_SUCCESS, ResponseYes, 2, &String1, &String2); // outputs a box :|
+ CheckHardError(STATUS_DLL_NOT_FOUND, 2, OptionYesNoCancel, STATUS_SUCCESS, ResponseCancel, 2, &String1, &String2); // outputs a box :|
+ CheckHardError(STATUS_DLL_NOT_FOUND, 3, OptionYesNoCancel, STATUS_SUCCESS, ResponseYes, 2, &String1, &String2); // outputs a box :|
+ CheckHardError(STATUS_DLL_NOT_FOUND, 3, OptionYesNoCancel, STATUS_SUCCESS, ResponseYes, 4, &String1, &String2, 0, 0); // outputs a box :|
+ /* try with stuff that's not a UNICODE_STRING */
+ CheckHardError(STATUS_DLL_NOT_FOUND, 1, OptionYesNoCancel, STATUS_SUCCESS, ResponseNo, 1, &String1Ansi); // outputs a box :|
+ CheckHardError(STATUS_DLL_NOT_FOUND, 1, OptionYesNoCancel, STATUS_SUCCESS, ResponseNo, 1, L"Parameter1"); // outputs a box :|
+ CheckHardError(STATUS_DLL_NOT_FOUND, 1, OptionYesNoCancel, STATUS_SUCCESS, ResponseNo, 1, "Parameter1"); // outputs a box :|
+ CheckHardError(STATUS_DLL_NOT_FOUND, 1, OptionYesNoCancel, STATUS_ACCESS_VIOLATION, NoResponse, 1, 1234); // outputs a box :|
+ CheckHardError(STATUS_DLL_NOT_FOUND, 1, OptionYesNoCancel, STATUS_ACCESS_VIOLATION, NoResponse, 1, NULL); // outputs a box :|
+ CheckHardError(STATUS_DLL_NOT_FOUND, 0, OptionYesNoCancel, STATUS_SUCCESS, ResponseCancel, 1, &String1Ansi); // outputs a box :|
+ CheckHardError(STATUS_DLL_NOT_FOUND, 0, OptionYesNoCancel, STATUS_SUCCESS, ResponseCancel, 1, L"Parameter1"); // outputs a box :|
+ CheckHardError(STATUS_DLL_NOT_FOUND, 0, OptionYesNoCancel, STATUS_SUCCESS, ResponseCancel, 1, "Parameter1"); // outputs a box :|
+ CheckHardError(STATUS_DLL_NOT_FOUND, 0, OptionYesNoCancel, STATUS_SUCCESS, ResponseCancel, 1, 1234); // outputs a box :|
+ CheckHardError(STATUS_DLL_NOT_FOUND, 0, OptionYesNoCancel, STATUS_SUCCESS, ResponseCancel, 1, NULL); // outputs a box :|
+ }
+ if (InteractivePart3)
+ {
+ /* try a message with one parameter */
+ CheckHardError(STATUS_SERVICE_NOTIFICATION, 1, OptionYesNoCancel, STATUS_SUCCESS, ResponseNotHandled, 1, &String1); // outputs a box :|
+ CheckHardError(STATUS_SERVICE_NOTIFICATION, 0, OptionYesNoCancel, STATUS_SUCCESS, ResponseNotHandled, 1, &String1); // outputs a box :|
+ CheckHardError(STATUS_SERVICE_NOTIFICATION, 1, OptionYesNoCancel, STATUS_SUCCESS, ResponseNotHandled, 0, &String1); // outputs a box :|
+ CheckHardError(STATUS_SERVICE_NOTIFICATION, 0, OptionYesNoCancel, STATUS_SUCCESS, ResponseNotHandled, 0, &String1); // outputs a box :|
+ /* give too many parameters */
+ CheckHardError(STATUS_SERVICE_NOTIFICATION, 1, OptionYesNoCancel, STATUS_SUCCESS, ResponseNotHandled, 2, &String1, &String2); // outputs a box :|
+ CheckHardError(STATUS_SERVICE_NOTIFICATION, 2, OptionYesNoCancel, STATUS_SUCCESS, ResponseNotHandled, 2, &String1, &String2); // outputs a box :|
+ CheckHardError(STATUS_SERVICE_NOTIFICATION, 3, OptionYesNoCancel, STATUS_SUCCESS, ResponseNotHandled, 2, &String1, &String2); // outputs a box :|
+ CheckHardError(STATUS_SERVICE_NOTIFICATION, 3, OptionYesNoCancel, STATUS_SUCCESS, ResponseOk, 3, &String1, &String2, 0); // outputs a box :|
+ CheckHardError(STATUS_SERVICE_NOTIFICATION, 3, OptionYesNoCancel, STATUS_SUCCESS, ResponseOk, 4, &String1, &String2, 0, 0); // outputs a box :|
+ CheckHardError(STATUS_SERVICE_NOTIFICATION, 3, OptionOkNoWait, STATUS_SUCCESS, ResponseOk, 4, &String1, &String2, 0, 0); // outputs a balloon notification
+ /* try with stuff that's not a UNICODE_STRING */
+ CheckHardError(STATUS_SERVICE_NOTIFICATION, 1, OptionYesNoCancel, STATUS_SUCCESS, ResponseNotHandled, 1, &String1Ansi); // outputs a box :|
+ CheckHardError(STATUS_SERVICE_NOTIFICATION, 1, OptionYesNoCancel, STATUS_SUCCESS, ResponseNotHandled, 1, L"Parameter1"); // outputs a box :|
+ CheckHardError(STATUS_SERVICE_NOTIFICATION, 1, OptionYesNoCancel, STATUS_SUCCESS, ResponseNotHandled, 1, "Parameter1"); // outputs a box :|
+ CheckHardError(STATUS_SERVICE_NOTIFICATION, 1, OptionYesNoCancel, STATUS_ACCESS_VIOLATION, NoResponse, 1, 1234); // outputs a box :|
+ CheckHardError(STATUS_SERVICE_NOTIFICATION, 1, OptionYesNoCancel, STATUS_ACCESS_VIOLATION, NoResponse, 1, NULL); // outputs a box :|
+ CheckHardError(STATUS_SERVICE_NOTIFICATION, 0, OptionYesNoCancel, STATUS_SUCCESS, ResponseNotHandled, 1, &String1Ansi); // outputs a box :|
+ CheckHardError(STATUS_SERVICE_NOTIFICATION, 0, OptionYesNoCancel, STATUS_SUCCESS, ResponseNotHandled, 1, L"Parameter1"); // outputs a box :|
+ CheckHardError(STATUS_SERVICE_NOTIFICATION, 0, OptionYesNoCancel, STATUS_SUCCESS, ResponseNotHandled, 1, "Parameter1"); // outputs a box :|
+ CheckHardError(STATUS_SERVICE_NOTIFICATION, 0, OptionYesNoCancel, STATUS_SUCCESS, ResponseNotHandled, 1, 1234); // outputs a box :|
+ CheckHardError(STATUS_SERVICE_NOTIFICATION, 0, OptionYesNoCancel, STATUS_SUCCESS, ResponseNotHandled, 1, NULL); // outputs a box :|
+ }
+ if (InteractivePart4)
+ {
+ /* try a message with one parameter */
+ CheckHardError(STATUS_FATAL_APP_EXIT, 1, OptionYesNoCancel, STATUS_SUCCESS, ResponseYes, 1, &String1); // outputs a box :|
+ CheckHardError(STATUS_FATAL_APP_EXIT, 0, OptionYesNoCancel, STATUS_SUCCESS, ResponseCancel, 1, &String1); // outputs a box :|
+ CheckHardError(STATUS_FATAL_APP_EXIT, 1, OptionYesNoCancel, STATUS_SUCCESS, ResponseCancel, 0, &String1); // outputs a box :|
+ CheckHardError(STATUS_FATAL_APP_EXIT, 0, OptionYesNoCancel, STATUS_SUCCESS, ResponseCancel, 0, &String1); // outputs a box :|
+ /* give too many parameters */
+ CheckHardError(STATUS_FATAL_APP_EXIT, 1, OptionYesNoCancel, STATUS_SUCCESS, ResponseYes, 2, &String1, &String2); // outputs a box :|
+ CheckHardError(STATUS_FATAL_APP_EXIT, 2, OptionYesNoCancel, STATUS_SUCCESS, ResponseCancel, 2, &String1, &String2); // outputs a box :|
+ CheckHardError(STATUS_FATAL_APP_EXIT, 3, OptionYesNoCancel, STATUS_SUCCESS, ResponseYes, 2, &String1, &String2); // outputs a box :|
+ CheckHardError(STATUS_FATAL_APP_EXIT, 3, OptionYesNoCancel, STATUS_SUCCESS, ResponseYes, 4, &String1, &String2, 0, 0); // outputs a box :|
+ /* try with stuff that's not a UNICODE_STRING */
+ CheckHardError(STATUS_FATAL_APP_EXIT, 1, OptionYesNoCancel, STATUS_SUCCESS, ResponseNo, 1, &String1Ansi); // outputs a box :|
+ CheckHardError(STATUS_FATAL_APP_EXIT, 1, OptionYesNoCancel, STATUS_SUCCESS, ResponseNo, 1, L"Parameter1"); // outputs a box :|
+ CheckHardError(STATUS_FATAL_APP_EXIT, 1, OptionYesNoCancel, STATUS_SUCCESS, ResponseNo, 1, "Parameter1"); // outputs a box :|
+ CheckHardError(STATUS_FATAL_APP_EXIT, 1, OptionYesNoCancel, STATUS_ACCESS_VIOLATION, NoResponse, 1, 1234); // outputs a box :|
+ CheckHardError(STATUS_FATAL_APP_EXIT, 1, OptionYesNoCancel, STATUS_ACCESS_VIOLATION, NoResponse, 1, NULL); // outputs a box :|
+ CheckHardError(STATUS_FATAL_APP_EXIT, 0, OptionYesNoCancel, STATUS_SUCCESS, ResponseCancel, 1, &String1Ansi); // outputs a box :|
+ CheckHardError(STATUS_FATAL_APP_EXIT, 0, OptionYesNoCancel, STATUS_SUCCESS, ResponseCancel, 1, L"Parameter1"); // outputs a box :|
+ CheckHardError(STATUS_FATAL_APP_EXIT, 0, OptionYesNoCancel, STATUS_SUCCESS, ResponseCancel, 1, "Parameter1"); // outputs a box :|
+ CheckHardError(STATUS_FATAL_APP_EXIT, 0, OptionYesNoCancel, STATUS_SUCCESS, ResponseCancel, 1, 1234); // outputs a box :|
+ CheckHardError(STATUS_FATAL_APP_EXIT, 0, OptionYesNoCancel, STATUS_SUCCESS, ResponseCancel, 1, NULL); // outputs a box :|
+
+ // TODO: these 3 are interactive on ROS
+ CheckInformationalHardError(STATUS_WAIT_0, NULL, NULL, STATUS_SUCCESS, TRUE); // outputs a balloon notification
+ CheckInformationalHardError(STATUS_DLL_NOT_FOUND, &String1, NULL, STATUS_SUCCESS, TRUE); // outputs a balloon notification
+ CheckInformationalHardError(STATUS_DLL_NOT_FOUND, NULL, NULL, STATUS_SUCCESS, TRUE); // outputs a balloon notification
+ }
+ CheckInformationalHardError(STATUS_SERVICE_NOTIFICATION, &String1, NULL, STATUS_SUCCESS, FALSE);
+
+ ok_bool_true(IoSetThreadHardErrorMode(TRUE), "IoSetThreadHardErrorMode returned");
+ ok_bool_true(IoSetThreadHardErrorMode(FALSE), "IoSetThreadHardErrorMode returned");
+ ok_bool_false(IoSetThreadHardErrorMode(FALSE), "IoSetThreadHardErrorMode returned");
+ CheckHardError(STATUS_FATAL_APP_EXIT, 0, OptionYesNoCancel, STATUS_SUCCESS, ResponseReturnToCaller, 0, 0);
+ CheckHardError(STATUS_FATAL_APP_EXIT, 1, OptionYesNoCancel, STATUS_ACCESS_VIOLATION, NoResponse, 1, NULL);
+ CheckInformationalHardError(STATUS_WAIT_0, NULL, NULL, STATUS_SUCCESS, FALSE);
+ CheckInformationalHardError(STATUS_DLL_NOT_FOUND, &String1, NULL, STATUS_SUCCESS, FALSE);
+ CheckInformationalHardError(STATUS_DLL_NOT_FOUND, NULL, NULL, STATUS_SUCCESS, FALSE);
+ CheckInformationalHardError(STATUS_SERVICE_NOTIFICATION, &String1, NULL, STATUS_SUCCESS, FALSE);
+ ok_bool_false(IoSetThreadHardErrorMode(TRUE), "IoSetThreadHardErrorMode returned");
+ }
+
+ START_TEST(ExHardError)
+ {
+ TestHardError(FALSE, FALSE, FALSE, FALSE);
+ }
+
+ /* Here's how to do the interactive test:
+ * - First there will be a few messages random messages. If there's
+ * multiple options available, the same box will appear multiple times --
+ * click the buttons in order from left to right
+ * - After that, you must verify the error parameters. You should always
+ * see Parameter1 or Parameter2 for strings, and 0x12345678 for numbers.
+ * if there's a message saying an exception occured during processing,
+ * click cancel. If there's a bad parameter (Parameter1+, Parameter1+Garbage
+ * or an empty string for example), click no. Otherwise click yes. */
+ START_TEST(ExHardErrorInteractive)
+ {
+ TestHardError(TRUE, TRUE, TRUE, TRUE);
+ }
--- /dev/null
+ /*
+ * PROJECT: ReactOS kernel-mode tests
+ * LICENSE: GPLv2+ - See COPYING in the top level directory
+ * PURPOSE: Kernel-Mode Test Suite Interlocked function test
+ * PROGRAMMER: Thomas Faber <thfabba@gmx.de>
+ */
+
+ /* missing prototypes >:| */
+ #ifndef _MSC_VER
+ typedef long long __int64;
+ #endif
+ struct _KSPIN_LOCK;
+ __declspec(dllimport) long __fastcall InterlockedCompareExchange(volatile long *, long, long);
+ __declspec(dllimport) __int64 __fastcall ExInterlockedCompareExchange64(volatile __int64 *, __int64 *, __int64 *, void *);
+ __declspec(dllimport) __int64 __fastcall ExfInterlockedCompareExchange64(volatile __int64 *, __int64 *, __int64 *);
+ __declspec(dllimport) long __fastcall InterlockedExchange(volatile long *, long);
+ __declspec(dllimport) unsigned long __stdcall ExInterlockedExchangeUlong(unsigned long *, unsigned long, void *);
+ __declspec(dllimport) long __fastcall InterlockedExchangeAdd(volatile long *, long);
+ __declspec(dllimport) unsigned long __stdcall ExInterlockedAddUlong(unsigned long *, unsigned long, void *);
+ __declspec(dllimport) unsigned long __stdcall Exi386InterlockedExchangeUlong(unsigned long *, unsigned long);
+ __declspec(dllimport) long __fastcall InterlockedIncrement(long *);
+ __declspec(dllimport) long __fastcall InterlockedDecrement(long *);
+ __declspec(dllimport) int __stdcall ExInterlockedIncrementLong(long *, void *);
+ __declspec(dllimport) int __stdcall ExInterlockedDecrementLong(long *, void *);
+ __declspec(dllimport) int __stdcall Exi386InterlockedIncrementLong(long *);
+ __declspec(dllimport) int __stdcall Exi386InterlockedDecrementLong(long *);
+
+ #include <kmt_test.h>
+
+ /* TODO: There are quite some changes needed for other architectures!
+ ExInterlockedAddLargeInteger, ExInterlockedAddUlong are the only two
+ functions actually exported by my win7/x64 kernel! */
+
+ /* TODO: stress-testing */
+
+ static KSPIN_LOCK SpinLock;
+
+ #ifdef _M_IX86
+ typedef struct
+ {
+ unsigned long esi, edi, ebx, ebp, esp;
+ } PROCESSOR_STATE;
+ #elif defined(_M_AMD64)
+ typedef struct
+ {
+ unsigned long long rsi, rdi, rbx, rbp, rsp, r12, r13, r14, r15;
+ } PROCESSOR_STATE;
+ #else
+ // dummy
+ typedef int PROCESSOR_STATE;
+ #endif
+
+ #if defined(_MSC_VER) && defined(_M_IX86)
+ #define SaveState(State) do \
+ { \
+ __asm lea ecx, [State] \
+ __asm mov [ecx], esi \
+ __asm mov [ecx+4], edi \
+ __asm mov [ecx+8], ebx \
+ __asm mov [ecx+12], ebp \
+ __asm mov [ecx+16], esp \
+ } while (0)
+
+ #define CheckState(OldState, NewState) do \
+ { \
+ ok_eq_hex((OldState)->esi, (NewState)->esi); \
+ ok_eq_hex((OldState)->edi, (NewState)->edi); \
+ ok_eq_hex((OldState)->ebx, (NewState)->ebx); \
+ ok_eq_hex((OldState)->ebp, (NewState)->ebp); \
+ ok_eq_hex((OldState)->esp, (NewState)->esp); \
+ } while (0)
+
+ #elif defined(__GNUC__) && defined(_M_IX86)
+ #define SaveState(State) \
+ asm volatile( \
+ ".intel_syntax noprefix\n\t" \
+ "mov\t[ecx], esi\n\t" \
+ "mov\t[ecx+4], edi\n\t" \
+ "mov\t[ecx+8], ebx\n\t" \
+ "mov\t[ecx+12], ebp\n\t" \
+ "mov\t[ecx+16], esp\n\t" \
+ ".att_syntax prefix" \
+ : : "c" (&State) : "memory" \
+ );
+
+ #define CheckState(OldState, NewState) do \
+ { \
+ ok_eq_hex((OldState)->esi, (NewState)->esi); \
+ ok_eq_hex((OldState)->edi, (NewState)->edi); \
+ ok_eq_hex((OldState)->ebx, (NewState)->ebx); \
+ ok_eq_hex((OldState)->ebp, (NewState)->ebp); \
+ ok_eq_hex((OldState)->esp, (NewState)->esp); \
+ } while (0)
+ #elif defined(__GNUC__) && defined(_M_AMD64)
+ #define SaveState(State) \
+ asm volatile( \
+ ".intel_syntax noprefix\n\t" \
+ "mov\t[rcx], rsi\n\t" \
+ "mov\t[rcx+8], rdi\n\t" \
+ "mov\t[rcx+16], rbx\n\t" \
+ "mov\t[rcx+24], rbp\n\t" \
+ "mov\t[rcx+32], rsp\n\t" \
+ "mov\t[rcx+40], r12\n\t" \
+ "mov\t[rcx+48], r13\n\t" \
+ "mov\t[rcx+56], r14\n\t" \
+ "mov\t[rcx+64], r15\n\t" \
+ ".att_syntax prefix" \
+ : : "c" (&State) : "memory" \
+ );
+
+ #define CheckState(OldState, NewState) do \
+ { \
+ ok_eq_hex((OldState)->rsi, (NewState)->rsi); \
+ ok_eq_hex((OldState)->rdi, (NewState)->rdi); \
+ ok_eq_hex((OldState)->rbx, (NewState)->rbx); \
+ ok_eq_hex((OldState)->rbp, (NewState)->rbp); \
+ ok_eq_hex((OldState)->rsp, (NewState)->rsp); \
+ ok_eq_hex((OldState)->r12, (NewState)->r12); \
+ ok_eq_hex((OldState)->r13, (NewState)->r13); \
+ ok_eq_hex((OldState)->r14, (NewState)->r14); \
+ ok_eq_hex((OldState)->r15, (NewState)->r15); \
+ } while (0)
+ #else
+ #define SaveState(State)
+ #define CheckState(OldState, NewState)
+ #endif
+
+ static
+ LARGE_INTEGER
+ Large(
+ ULONGLONG Value)
+ {
+ LARGE_INTEGER Ret;
+ Ret.QuadPart = Value;
+ return Ret;
+ }
+
+ #define CheckInterlockedCmpXchg(Function, Type, Print, Val, Cmp, Xchg, \
+ ExpectedValue, ExpectedRet) do \
+ { \
+ Type Ret##Type = 0; \
+ Type Value##Type = Val; \
+ Status = STATUS_SUCCESS; \
+ _SEH2_TRY { \
+ SaveState(OldState); \
+ Ret##Type = Function(&Value##Type, Xchg, Cmp); \
+ SaveState(NewState); \
+ CheckState(&OldState, &NewState); \
+ } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { \
+ Status = _SEH2_GetExceptionCode(); \
+ } _SEH2_END; \
+ ok_eq_hex(Status, STATUS_SUCCESS); \
+ ok_eq_print(Ret##Type, ExpectedRet, Print); \
+ ok_eq_print(Value##Type, ExpectedValue, Print); \
+ } while (0)
+
+ #define CheckInterlockedCmpXchgI(Function, Type, Print, Val, Cmp, Xchg, \
+ ExpectedValue, ExpectedRet, ...) do \
+ { \
+ Type Ret##Type = 0; \
+ Type Value##Type = Val; \
+ Type Compare##Type = Cmp; \
+ Type Exchange##Type = Xchg; \
+ Status = STATUS_SUCCESS; \
+ _SEH2_TRY { \
+ SaveState(OldState); \
+ Ret##Type = Function(&Value##Type, &Exchange##Type, \
+ &Compare##Type, ##__VA_ARGS__); \
+ SaveState(NewState); \
+ CheckState(&OldState, &NewState); \
+ } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { \
+ Status = _SEH2_GetExceptionCode(); \
+ } _SEH2_END; \
+ ok_eq_hex(Status, STATUS_SUCCESS); \
+ ok_eq_print(Ret##Type, ExpectedRet, Print); \
+ ok_eq_print(Value##Type, ExpectedValue, Print); \
+ ok_eq_print(Exchange##Type, Xchg, Print); \
+ ok_eq_print(Compare##Type, Cmp, Print); \
+ } while(0)
+
+ #define CheckInterlockedOp(Function, Type, Print, Val, Op, \
+ ExpectedValue, ExpectedRet, ...) do \
+ { \
+ Type Ret##Type = 0; \
+ Type Value##Type = Val; \
+ Status = STATUS_SUCCESS; \
+ _SEH2_TRY { \
+ SaveState(OldState); \
+ Ret##Type = Function(&Value##Type, Op, ##__VA_ARGS__); \
+ SaveState(NewState); \
+ CheckState(&OldState, &NewState); \
+ } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { \
+ Status = _SEH2_GetExceptionCode(); \
+ } _SEH2_END; \
+ ok_eq_hex(Status, STATUS_SUCCESS); \
+ ok_eq_print(Ret##Type, ExpectedRet, Print); \
+ ok_eq_print(Value##Type, ExpectedValue, Print); \
+ } while (0)
+
+ #define CheckInterlockedOpNoArg(Function, Type, Print, Val, \
+ ExpectedValue, ExpectedRet, ...) do \
+ { \
+ Type Ret##Type = 0; \
+ Type Value##Type = Val; \
+ Status = STATUS_SUCCESS; \
+ _SEH2_TRY { \
+ SaveState(OldState); \
+ Ret##Type = Function(&Value##Type, ##__VA_ARGS__); \
+ SaveState(NewState); \
+ CheckState(&OldState, &NewState); \
+ } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { \
+ Status = _SEH2_GetExceptionCode(); \
+ } _SEH2_END; \
+ ok_eq_hex(Status, STATUS_SUCCESS); \
+ ok_eq_print(Ret##Type, ExpectedRet, Print); \
+ ok_eq_print(Value##Type, ExpectedValue, Print); \
+ } while (0)
+
+ #define CheckInterlockedOpLarge(Function, Type, Print, Val, Op, \
+ ExpectedValue, ExpectedRet, ...) do \
+ { \
+ Type Ret##Type = Large(0); \
+ Type Value##Type = Val; \
+ Status = STATUS_SUCCESS; \
+ _SEH2_TRY { \
+ SaveState(OldState); \
+ Ret##Type = Function(&Value##Type, Op, ##__VA_ARGS__); \
+ SaveState(NewState); \
+ CheckState(&OldState, &NewState); \
+ } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { \
+ Status = _SEH2_GetExceptionCode(); \
+ } _SEH2_END; \
+ ok_eq_hex(Status, STATUS_SUCCESS); \
+ ok_eq_print(Ret##Type.QuadPart, ExpectedRet, Print); \
+ ok_eq_print(Value##Type.QuadPart, ExpectedValue, Print); \
+ } while (0)
+
+ #define CheckInterlockedOpLargeNoRet(Function, Type, Print, Val, Op, \
+ ExpectedValue) do \
+ { \
+ Type Value##Type = Val; \
+ Status = STATUS_SUCCESS; \
+ _SEH2_TRY { \
+ SaveState(OldState); \
+ Function(&Value##Type, Op); \
+ SaveState(NewState); \
+ CheckState(&OldState, &NewState); \
+ } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { \
+ Status = _SEH2_GetExceptionCode(); \
+ } _SEH2_END; \
+ ok_eq_hex(Status, STATUS_SUCCESS); \
+ ok_eq_print(Value##Type.QuadPart, ExpectedValue, Print); \
+ } while (0)
+
+ /* TODO: missing in wdm.h! */
+ #define InterlockedCompareExchangeAcquire InterlockedCompareExchange
+ #define InterlockedCompareExchangeRelease InterlockedCompareExchange
+ #define InterlockedIncrementAcquire InterlockedIncrement
+ #define InterlockedIncrementRelease InterlockedIncrement
+ #define InterlockedDecrementAcquire InterlockedDecrement
+ #define InterlockedDecrementRelease InterlockedDecrement
+
+ static
+ VOID
+ TestInterlockedFunctional(VOID)
+ {
+ NTSTATUS Status;
+ PKSPIN_LOCK pSpinLock = &SpinLock;
+ PROCESSOR_STATE OldState, NewState;
+
+ /* on x86, most of these are supported intrinsically and don't need a spinlock! */
+ #if defined _M_IX86 || defined _M_AMD64
+ pSpinLock = NULL;
+ #endif
+
+ /* CompareExchange */
+ /* macro version */
+ CheckInterlockedCmpXchg(InterlockedCompareExchange, LONG, "%ld", 5, 6, 8, 5L, 5L);
+ CheckInterlockedCmpXchg(InterlockedCompareExchange, LONG, "%ld", 5, 5, 9, 9L, 5L);
+ /* these only exist as macros on x86 */
+ CheckInterlockedCmpXchg(InterlockedCompareExchangeAcquire, LONG, "%ld", 16, 9, 12, 16L, 16L);
+ CheckInterlockedCmpXchg(InterlockedCompareExchangeAcquire, LONG, "%ld", 16, 16, 4, 4L, 16L);
+ CheckInterlockedCmpXchg(InterlockedCompareExchangeRelease, LONG, "%ld", 27, 123, 38, 27L, 27L);
+ CheckInterlockedCmpXchg(InterlockedCompareExchangeRelease, LONG, "%ld", 27, 27, 39, 39L, 27L);
+ /* exported function */
+ #undef InterlockedCompareExchange
+ #ifdef _M_IX86
+ CheckInterlockedCmpXchg(InterlockedCompareExchange, LONG, "%ld", 5, 6, 8, 5L, 5L);
+ CheckInterlockedCmpXchg(InterlockedCompareExchange, LONG, "%ld", 5, 5, 9, 9L, 5L);
+ #endif
+ /* only exists as a macro */
+ CheckInterlockedCmpXchg(InterlockedCompareExchangePointer, PVOID, "%p", (PVOID)117, (PVOID)711, (PVOID)12, (PVOID)117, (PVOID)117);
+ CheckInterlockedCmpXchg(InterlockedCompareExchangePointer, PVOID, "%p", (PVOID)117, (PVOID)117, (PVOID)228, (PVOID)228, (PVOID)117);
+ /* macro version */
+ CheckInterlockedCmpXchgI(ExInterlockedCompareExchange64, LONGLONG, "%I64d", 17, 4LL, 20LL, 17LL, 17LL, pSpinLock);
+ CheckInterlockedCmpXchgI(ExInterlockedCompareExchange64, LONGLONG, "%I64d", 17, 17LL, 21LL, 21LL, 17LL, pSpinLock);
+ #ifdef _M_IX86
+ /* exported function */
+ CheckInterlockedCmpXchgI((ExInterlockedCompareExchange64), LONGLONG, "%I64d", 17, 4LL, 20LL, 17LL, 17LL, pSpinLock);
+ CheckInterlockedCmpXchgI((ExInterlockedCompareExchange64), LONGLONG, "%I64d", 17, 17LL, 21LL, 21LL, 17LL, pSpinLock);
+ /* fastcall version */
+ CheckInterlockedCmpXchgI(ExfInterlockedCompareExchange64, LONGLONG, "%I64d", 17, 4LL, 20LL, 17LL, 17LL);
+ CheckInterlockedCmpXchgI(ExfInterlockedCompareExchange64, LONGLONG, "%I64d", 17, 17LL, 21LL, 21LL, 17LL);
+ #endif
+
+ /* Exchange */
+ CheckInterlockedOp(InterlockedExchange, LONG, "%ld", 5, 8, 8L, 5L);
+ CheckInterlockedOpNoArg(InterlockedExchangePointer, PVOID, "%p", (PVOID)700, (PVOID)93, (PVOID)700, (PVOID)93);
+ #undef InterlockedExchange
+ #ifdef _M_IX86
+ CheckInterlockedOp(InterlockedExchange, LONG, "%ld", 5, 8, 8L, 5L);
+ CheckInterlockedOp(ExInterlockedExchangeUlong, ULONG, "%lu", 212, 121, 121LU, 212LU, pSpinLock);
+ CheckInterlockedOp((ExInterlockedExchangeUlong), ULONG, "%lu", 212, 121, 121LU, 212LU, pSpinLock);
+ CheckInterlockedOp(Exi386InterlockedExchangeUlong, ULONG, "%lu", 212, 121, 121LU, 212LU);
+ CheckInterlockedOp(Exfi386InterlockedExchangeUlong, ULONG, "%lu", 212, 121, 121LU, 212LU);
+ #endif
+
+ /* ExchangeAdd */
+ /* TODO: ExInterlockedExchangeAddLargeInteger? */
+ CheckInterlockedOp(InterlockedExchangeAdd, LONG, "%ld", 312, 7, 319L, 312L);
+ #undef InterlockedExchangeAdd
+ #ifdef _M_IX86
+ CheckInterlockedOp(InterlockedExchangeAdd, LONG, "%ld", 312, 7, 319L, 312L);
+ #endif
+
+ /* Add */
+ /* these DO need a valid spinlock even on x86 */
+ CheckInterlockedOpLarge(ExInterlockedAddLargeInteger, LARGE_INTEGER, "%I64d", Large(23), Large(7), 30LL, 23LL, &SpinLock);
+ CheckInterlockedOpLargeNoRet(ExInterlockedAddLargeStatistic, LARGE_INTEGER, "%I64d", Large(15), 17LL, 32LL);
+ CheckInterlockedOp(ExInterlockedAddUlong, ULONG, "%lu", 239, 44, 283LU, 239LU, &SpinLock);
+ #undef ExInterlockedAddUlong
+ CheckInterlockedOp(ExInterlockedAddUlong, ULONG, "%lu", 239, 44, 283LU, 239LU, &SpinLock);
+
+ /* Increment */
+ CheckInterlockedOpNoArg(InterlockedIncrement, LONG, "%ld", 2341L, 2342L, 2342L);
+ CheckInterlockedOpNoArg(InterlockedIncrement, LONG, "%ld", (LONG)MAXLONG, (LONG)MINLONG, (LONG)MINLONG);
+ CheckInterlockedOpNoArg(InterlockedIncrementAcquire, LONG, "%ld", 2341L, 2342L, 2342L);
+ CheckInterlockedOpNoArg(InterlockedIncrementRelease, LONG, "%ld", 2341L, 2342L, 2342L);
+ #undef InterlockedIncrement
+ #ifdef _M_IX86
+ CheckInterlockedOpNoArg(InterlockedIncrement, LONG, "%ld", 2341L, 2342L, 2342L);
+ CheckInterlockedOpNoArg(InterlockedIncrement, LONG, "%ld", (LONG)MAXLONG, (LONG)MINLONG, (LONG)MINLONG);
+ CheckInterlockedOpNoArg(ExInterlockedIncrementLong, LONG, "%ld", -2L, -1L, (LONG)ResultNegative, pSpinLock);
+ CheckInterlockedOpNoArg(ExInterlockedIncrementLong, LONG, "%ld", -1L, 0L, (LONG)ResultZero, pSpinLock);
+ CheckInterlockedOpNoArg(ExInterlockedIncrementLong, LONG, "%ld", 0L, 1L, (LONG)ResultPositive, pSpinLock);
+ CheckInterlockedOpNoArg(ExInterlockedIncrementLong, LONG, "%ld", (LONG)MAXLONG, (LONG)MINLONG, (LONG)ResultNegative, pSpinLock);
+ CheckInterlockedOpNoArg((ExInterlockedIncrementLong), LONG, "%ld", -2L, -1L, (LONG)ResultNegative, pSpinLock);
+ CheckInterlockedOpNoArg((ExInterlockedIncrementLong), LONG, "%ld", -1L, 0L, (LONG)ResultZero, pSpinLock);
+ CheckInterlockedOpNoArg((ExInterlockedIncrementLong), LONG, "%ld", 0L, 1L, (LONG)ResultPositive, pSpinLock);
+ CheckInterlockedOpNoArg((ExInterlockedIncrementLong), LONG, "%ld", (LONG)MAXLONG, (LONG)MINLONG, (LONG)ResultNegative, pSpinLock);
+ CheckInterlockedOpNoArg(Exi386InterlockedIncrementLong, LONG, "%ld", -2L, -1L, (LONG)ResultNegative);
+ CheckInterlockedOpNoArg(Exi386InterlockedIncrementLong, LONG, "%ld", -1L, 0L, (LONG)ResultZero);
+ CheckInterlockedOpNoArg(Exi386InterlockedIncrementLong, LONG, "%ld", 0L, 1L, (LONG)ResultPositive);
+ CheckInterlockedOpNoArg(Exi386InterlockedIncrementLong, LONG, "%ld", (LONG)MAXLONG, (LONG)MINLONG, (LONG)ResultNegative);
+ #endif
+
+ /* Decrement */
+ CheckInterlockedOpNoArg(InterlockedDecrement, LONG, "%ld", 1745L, 1744L, 1744L);
+ CheckInterlockedOpNoArg(InterlockedDecrement, LONG, "%ld", (LONG)MINLONG, (LONG)MAXLONG, (LONG)MAXLONG);
+ CheckInterlockedOpNoArg(InterlockedDecrementAcquire, LONG, "%ld", 1745L, 1744L, 1744L);
+ CheckInterlockedOpNoArg(InterlockedDecrementRelease, LONG, "%ld", 1745L, 1744L, 1744L);
+ #undef InterlockedDecrement
+ #ifdef _M_IX86
+ CheckInterlockedOpNoArg(InterlockedDecrement, LONG, "%ld", 1745L, 1744L, 1744L);
+ CheckInterlockedOpNoArg(InterlockedDecrement, LONG, "%ld", (LONG)MINLONG, (LONG)MAXLONG, (LONG)MAXLONG);
+ CheckInterlockedOpNoArg(ExInterlockedDecrementLong, LONG, "%ld", (LONG)MINLONG, (LONG)MAXLONG, (LONG)ResultPositive, pSpinLock);
+ CheckInterlockedOpNoArg(ExInterlockedDecrementLong, LONG, "%ld", 0L, -1L, (LONG)ResultNegative, pSpinLock);
+ CheckInterlockedOpNoArg(ExInterlockedDecrementLong, LONG, "%ld", 1L, 0L, (LONG)ResultZero, pSpinLock);
+ CheckInterlockedOpNoArg(ExInterlockedDecrementLong, LONG, "%ld", 2L, 1L, (LONG)ResultPositive, pSpinLock);
+ CheckInterlockedOpNoArg((ExInterlockedDecrementLong), LONG, "%ld", (LONG)MINLONG, (LONG)MAXLONG, (LONG)ResultPositive, pSpinLock);
+ CheckInterlockedOpNoArg((ExInterlockedDecrementLong), LONG, "%ld", 0L, -1L, (LONG)ResultNegative, pSpinLock);
+ CheckInterlockedOpNoArg((ExInterlockedDecrementLong), LONG, "%ld", 1L, 0L, (LONG)ResultZero, pSpinLock);
+ CheckInterlockedOpNoArg((ExInterlockedDecrementLong), LONG, "%ld", 2L, 1L, (LONG)ResultPositive, pSpinLock);
+ CheckInterlockedOpNoArg(Exi386InterlockedDecrementLong, LONG, "%ld", (LONG)MINLONG, (LONG)MAXLONG, (LONG)ResultPositive);
+ CheckInterlockedOpNoArg(Exi386InterlockedDecrementLong, LONG, "%ld", 0L, -1L, (LONG)ResultNegative);
+ CheckInterlockedOpNoArg(Exi386InterlockedDecrementLong, LONG, "%ld", 1L, 0L, (LONG)ResultZero);
+ CheckInterlockedOpNoArg(Exi386InterlockedDecrementLong, LONG, "%ld", 2L, 1L, (LONG)ResultPositive);
+ #endif
+
+ /* And, Or, Xor */
+ CheckInterlockedOp(InterlockedAnd, LONG, "0x%lx", 0x1234L, 0x1111L, 0x1010L, 0x1234L);
+ CheckInterlockedOp(InterlockedOr, LONG, "0x%lx", 0x1234L, 0x1111L, 0x1335L, 0x1234L);
+ CheckInterlockedOp(InterlockedXor, LONG, "0x%lx", 0x1234L, 0x1111L, 0x0325L, 0x1234L);
+ #ifdef _WIN64
+ CheckInterlockedOp(InterlockedXor64, LONGLONG, "0x%I64x", 0x200001234LL, 0x100001111LL, 0x300000325LL, 0x200001234LL);
+ #endif
+ }
+
+ START_TEST(ExInterlocked)
+ {
+ KIRQL Irql;
+ KeInitializeSpinLock(&SpinLock);
+
+ /* functional testing */
+ TestInterlockedFunctional();
+ KeRaiseIrql(HIGH_LEVEL, &Irql);
+ TestInterlockedFunctional();
+ KeLowerIrql(Irql);
+ }
/*
- * NTOSKRNL Pools test routines KM-Test
- * ReactOS Kernel Mode Regression Testing framework
- *
- * Copyright 2008 Aleksey Bragin <aleksey@reactos.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; see the file COPYING.LIB.
- * If not, write to the Free Software Foundation,
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * PROJECT: ReactOS kernel-mode tests
+ * LICENSE: LGPLv2+ - See COPYING.LIB in the top level directory
+ * PURPOSE: Kernel-Mode Test Suite Pools test routines KM-Test
+ * PROGRAMMER: Aleksey Bragin <aleksey@reactos.org>
*/
- /* INCLUDES *******************************************************************/
+ /* TODO: PoolsCorruption tests fail because accessing invalid memory doesn't necessarily cause an access violation */
- #include <ddk/ntddk.h>
- #include <ntifs.h>
- #include <ndk/ntndk.h>
- /* SEH support with PSEH */
- #include <pseh/pseh2.h>
- #include "kmtest.h"
+ #include <kmt_test.h>
#define NDEBUG
- #include "debug.h"
+ #include <debug.h>
#define TAG_POOLTEST 'tstP'
- /* PUBLIC FUNCTIONS ***********************************************************/
-
- VOID
- PoolsTest(HANDLE KeyHandle)
+ static VOID PoolsTest(VOID)
{
PVOID Ptr;
ULONG AllocSize, i, AllocNumber;
PVOID *Allocs;
- StartTest();
-
// Stress-test nonpaged pool
for (i=1; i<10000; i++)
{
ExFreePoolWithTag(Allocs, TAG_POOLTEST);
-
-
- FinishTest(KeyHandle, L"MmPoolAllocTest");
}
- VOID
- PoolsCorruption(HANDLE KeyHandle)
+ static VOID PoolsCorruption(VOID)
{
PULONG Ptr, TestPtr;
ULONG AllocSize;
NTSTATUS Status = STATUS_SUCCESS;
- StartTest();
-
// start with non-paged pool
AllocSize = 4096 + 0x10;
Ptr = ExAllocatePoolWithTag(NonPagedPool, AllocSize, TAG_POOLTEST);
// free the pool
ExFreePoolWithTag(Ptr, TAG_POOLTEST);
+ }
- FinishTest(KeyHandle, L"MmPoolCorruptionTest");
+ START_TEST(ExPools)
+ {
+ PoolsTest();
+ PoolsCorruption();
}
--- /dev/null
+ /*
+ * PROJECT: ReactOS kernel-mode tests
+ * LICENSE: GPLv2+ - See COPYING in the top level directory
+ * PURPOSE: Kernel-Mode Test Suite Executive Resource test
+ * PROGRAMMER: Thomas Faber <thfabba@gmx.de>
+ */
+
+ #include <kmt_test.h>
+
+ //#define NDEBUG
+ #include <debug.h>
+
+ /* TODO: This is getting pretty long, make it somehow easier to read if possible */
+
+ /* TODO: this is the Windows Server 2003 version! ROS should use this!
+ * This declaration can be removed once ROS headers are corrected */
+ typedef struct _ERESOURCE_2K3 {
+ LIST_ENTRY SystemResourcesList;
+ POWNER_ENTRY OwnerTable;
+ SHORT ActiveCount;
+ USHORT Flag;
+ volatile PKSEMAPHORE SharedWaiters;
+ volatile PKEVENT ExclusiveWaiters;
+ OWNER_ENTRY OwnerThreads[2];
+ ULONG ContentionCount;
+ USHORT NumberOfSharedWaiters;
+ USHORT NumberOfExclusiveWaiters;
+ _ANONYMOUS_UNION union {
+ PVOID Address;
+ ULONG_PTR CreatorBackTraceIndex;
+ } DUMMYUNIONNAME;
+ KSPIN_LOCK SpinLock;
+ } ERESOURCE_2K3, *PERESOURCE_2K3;
+
+ #define CheckResourceFields(Res, Reinit) do \
+ { \
+ ok_eq_pointer((Res)->SystemResourcesList.Flink->Blink, &(Res)->SystemResourcesList); \
+ ok_eq_pointer((Res)->SystemResourcesList.Blink->Flink, &(Res)->SystemResourcesList); \
+ if (!Reinit) ok_eq_pointer((Res)->OwnerTable, NULL); \
+ ok_eq_int((Res)->ActiveCount, 0); \
+ ok_eq_uint((Res)->Flag, 0); \
+ if (!Reinit) ok_eq_pointer((Res)->SharedWaiters, NULL); \
+ if (!Reinit) ok_eq_pointer((Res)->ExclusiveWaiters, NULL); \
+ ok_eq_ulongptr((Res)->OwnerThreads[0].OwnerThread, 0); \
+ ok_eq_ulong((Res)->OwnerThreads[0].TableSize, 0LU); \
+ ok_eq_ulongptr((Res)->OwnerThreads[1].OwnerThread, 0); \
+ ok_eq_ulong((Res)->OwnerThreads[1].TableSize, 0LU); \
+ ok_eq_ulong((Res)->ContentionCount, 0LU); \
+ ok_eq_uint((Res)->NumberOfSharedWaiters, 0); \
+ ok_eq_uint((Res)->NumberOfExclusiveWaiters, 0); \
+ ok_eq_pointer((Res)->Address, NULL); \
+ ok_eq_ulongptr((Res)->SpinLock, 0); \
+ } while (0)
+
+ #define CheckResourceStatus(Res, Exclusive, Shared, ExclusiveWaiters, SharedWaiters) do \
+ { \
+ if (Exclusive) \
+ ok_bool_true(ExIsResourceAcquiredExclusiveLite(Res), "ExIsResourceAcquiredExclusiveLite returned"); \
+ else \
+ ok_bool_false(ExIsResourceAcquiredExclusiveLite(Res), "ExIsResourceAcquiredExclusiveLite returned"); \
+ ok_eq_ulong(ExIsResourceAcquiredSharedLite(Res), Shared); \
+ ok_eq_ulong(ExGetExclusiveWaiterCount(Res), ExclusiveWaiters); \
+ ok_eq_ulong(ExGetSharedWaiterCount(Res), SharedWaiters); \
+ } while (0)
+
+ static
+ VOID
+ TestResourceSharedAccess(
+ IN PERESOURCE Res)
+ {
+ LONG Count = 0;
+
+ KeEnterCriticalRegion();
+ ok_bool_true(ExAcquireResourceSharedLite(Res, FALSE), "ExAcquireResourceSharedLite returned"); ++Count;
+ CheckResourceStatus(Res, FALSE, Count, 0LU, 0LU);
+
+ ok_bool_true(ExAcquireResourceSharedLite(Res, FALSE), "ExAcquireResourceSharedLite returned"); ++Count;
+ ok_bool_true(ExAcquireResourceSharedLite(Res, TRUE), "ExAcquireResourceSharedLite returned"); ++Count;
+ ok_bool_true(ExAcquireSharedStarveExclusive(Res, FALSE), "ExAcquireSharedStarveExclusive returned"); ++Count;
+ ok_bool_true(ExAcquireSharedStarveExclusive(Res, TRUE), "ExAcquireSharedStarveExclusive returned"); ++Count;
+ ok_bool_true(ExAcquireSharedWaitForExclusive(Res, FALSE), "ExAcquireSharedWaitForExclusive returned"); ++Count;
+ ok_bool_true(ExAcquireSharedWaitForExclusive(Res, TRUE), "ExAcquireSharedWaitForExclusive returned"); ++Count;
+ CheckResourceStatus(Res, FALSE, Count, 0LU, 0LU);
+
+ /* this one fails, TRUE would deadlock */
+ ok_bool_false(ExAcquireResourceExclusiveLite(Res, FALSE), "ExAcquireResourceExclusiveLite returned");
+ CheckResourceStatus(Res, FALSE, Count, 0LU, 0LU);
+
+ /* this asserts */
+ if (!KmtIsCheckedBuild)
+ ExConvertExclusiveToSharedLite(Res);
+ CheckResourceStatus(Res, FALSE, Count, 0LU, 0LU);
+
+ while (Count--)
+ ExReleaseResourceLite(Res);
+ KeLeaveCriticalRegion();
+ }
+
+ static
+ VOID
+ TestResourceExclusiveAccess(
+ IN PERESOURCE Res)
+ {
+ LONG Count = 0;
+
+ KeEnterCriticalRegion();
+ ok_bool_true(ExAcquireResourceExclusiveLite(Res, FALSE), "ExAcquireResourceExclusiveLite returned"); ++Count;
+
+ CheckResourceStatus(Res, TRUE, Count, 0LU, 0LU);
+
+ ok_bool_true(ExAcquireResourceExclusiveLite(Res, TRUE), "ExAcquireResourceExclusiveLite returned"); ++Count;
+ CheckResourceStatus(Res, TRUE, Count, 0LU, 0LU);
+
+ ok_bool_true(ExAcquireResourceSharedLite(Res, FALSE), "ExAcquireResourceSharedLite returned"); ++Count;
+ ok_bool_true(ExAcquireResourceSharedLite(Res, TRUE), "ExAcquireResourceSharedLite returned"); ++Count;
+ ok_bool_true(ExAcquireSharedStarveExclusive(Res, FALSE), "ExAcquireSharedStarveExclusive returned"); ++Count;
+ ok_bool_true(ExAcquireSharedStarveExclusive(Res, TRUE), "ExAcquireSharedStarveExclusive returned"); ++Count;
+ ok_bool_true(ExAcquireSharedWaitForExclusive(Res, FALSE), "ExAcquireSharedWaitForExclusive returned"); ++Count;
+ ok_bool_true(ExAcquireSharedWaitForExclusive(Res, TRUE), "ExAcquireSharedWaitForExclusive returned"); ++Count;
+ CheckResourceStatus(Res, TRUE, Count, 0LU, 0LU);
+
+ ExConvertExclusiveToSharedLite(Res);
+ CheckResourceStatus(Res, FALSE, Count, 0LU, 0LU);
+
+ while (Count--)
+ ExReleaseResourceLite(Res);
+ KeLeaveCriticalRegion();
+ }
+
+ static
+ VOID
+ TestResourceUndocumentedShortcuts(
+ IN PERESOURCE Res,
+ IN BOOLEAN AreApcsDisabled)
+ {
+ PVOID Ret;
+ LONG Count = 0;
+
+ ok_bool_false(KeAreApcsDisabled(), "KeAreApcsDisabled returned");
+ ok_eq_uint(KeAreAllApcsDisabled(), AreApcsDisabled);
+
+ /* ExEnterCriticalRegionAndAcquireResourceShared, ExEnterCriticalRegionAndAcquireSharedWaitForExclusive */
+ Count = 0;
+ Ret = ExEnterCriticalRegionAndAcquireResourceShared(Res); ++Count;
+ ok_eq_pointer(Ret, KeGetCurrentThread()->Win32Thread);
+ ok_bool_true(KeAreApcsDisabled(), "KeAreApcsDisabled returned");
+ ok_eq_bool(KeAreAllApcsDisabled(), AreApcsDisabled);
+ CheckResourceStatus(Res, FALSE, Count, 0LU, 0LU);
+
+ Ret = ExEnterCriticalRegionAndAcquireResourceShared(Res); ++Count;
+ ok_eq_pointer(Ret, KeGetCurrentThread()->Win32Thread);
+ ok_bool_true(KeAreApcsDisabled(), "KeAreApcsDisabled returned");
+ ok_eq_bool(KeAreAllApcsDisabled(), AreApcsDisabled);
+ CheckResourceStatus(Res, FALSE, Count, 0LU, 0LU);
+
+ ExEnterCriticalRegionAndAcquireSharedWaitForExclusive(Res); ++Count;
+ ok_eq_pointer(Ret, KeGetCurrentThread()->Win32Thread);
+ ok_bool_true(KeAreApcsDisabled(), "KeAreApcsDisabled returned");
+ ok_eq_bool(KeAreAllApcsDisabled(), AreApcsDisabled);
+ CheckResourceStatus(Res, FALSE, Count, 0LU, 0LU);
+
+ while (Count-- > 1)
+ {
+ ExReleaseResourceAndLeaveCriticalRegion(Res);
+ ok_bool_true(KeAreApcsDisabled(), "KeAreApcsDisabled returned");
+ ok_eq_bool(KeAreAllApcsDisabled(), AreApcsDisabled);
+ CheckResourceStatus(Res, FALSE, Count, 0LU, 0LU);
+ }
+
+ ExReleaseResourceAndLeaveCriticalRegion(Res);
+ ok_bool_false(KeAreApcsDisabled(), "KeAreApcsDisabled returned");
+ ok_eq_bool(KeAreAllApcsDisabled(), AreApcsDisabled);
+ CheckResourceStatus(Res, FALSE, Count, 0LU, 0LU);
+
+ /* ExEnterCriticalRegionAndAcquireResourceExclusive */
+ Count = 0;
+ ok_bool_false(KeAreApcsDisabled(), "KeAreApcsDisabled returned");
+ ok_eq_bool(KeAreAllApcsDisabled(), AreApcsDisabled);
+ Ret = ExEnterCriticalRegionAndAcquireResourceExclusive(Res); ++Count;
+ ok_eq_pointer(Ret, KeGetCurrentThread()->Win32Thread);
+ ok_bool_true(KeAreApcsDisabled(), "KeAreApcsDisabled returned");
+ ok_eq_bool(KeAreAllApcsDisabled(), AreApcsDisabled);
+ CheckResourceStatus(Res, TRUE, Count, 0LU, 0LU);
+
+ Ret = ExEnterCriticalRegionAndAcquireResourceExclusive(Res); ++Count;
+ ok_eq_pointer(Ret, KeGetCurrentThread()->Win32Thread);
+ ok_bool_true(KeAreApcsDisabled(), "KeAreApcsDisabled returned");
+ ok_eq_bool(KeAreAllApcsDisabled(), AreApcsDisabled);
+ CheckResourceStatus(Res, TRUE, Count, 0LU, 0LU);
+
+ ExReleaseResourceAndLeaveCriticalRegion(Res); --Count;
+ ok_bool_true(KeAreApcsDisabled(), "KeAreApcsDisabled returned");
+ ok_eq_bool(KeAreAllApcsDisabled(), AreApcsDisabled);
+ CheckResourceStatus(Res, TRUE, Count, 0LU, 0LU);
+
+ ExReleaseResourceAndLeaveCriticalRegion(Res); --Count;
+ ok_bool_false(KeAreApcsDisabled(), "KeAreApcsDisabled returned");
+ ok_eq_uint(KeAreAllApcsDisabled(), AreApcsDisabled);
+ CheckResourceStatus(Res, FALSE, Count, 0LU, 0LU);
+ }
+
+ typedef BOOLEAN (NTAPI *PACQUIRE_FUNCTION)(PERESOURCE, BOOLEAN);
+
+ typedef struct
+ {
+ HANDLE Handle;
+ PKTHREAD Thread;
+ PERESOURCE Res;
+ KEVENT InEvent;
+ KEVENT OutEvent;
+ PACQUIRE_FUNCTION AcquireResource;
+ BOOLEAN Wait;
+ BOOLEAN RetExpected;
+ } THREAD_DATA, *PTHREAD_DATA;
+
+ static
+ VOID
+ NTAPI
+ AcquireResourceThread(
+ PVOID Context)
+ {
+ NTSTATUS Status = STATUS_SUCCESS;
+ PTHREAD_DATA ThreadData = Context;
+ BOOLEAN Ret;
+
+ KeEnterCriticalRegion();
+ Ret = ThreadData->AcquireResource(ThreadData->Res, ThreadData->Wait);
+ if (ThreadData->RetExpected)
+ ok_bool_true(Ret, "AcquireResource returned");
+ else
+ ok_bool_false(Ret, "AcquireResource returned");
+
+ ok_bool_false(KeSetEvent(&ThreadData->OutEvent, 0, TRUE), "KeSetEvent returned");
+ Status = KeWaitForSingleObject(&ThreadData->InEvent, Executive, KernelMode, FALSE, NULL);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+
+ if (Ret)
+ ExReleaseResource(ThreadData->Res);
+ KeLeaveCriticalRegion();
+ }
+
+ static
+ VOID
+ InitThreadData(
+ PTHREAD_DATA ThreadData,
+ PERESOURCE Res,
+ PACQUIRE_FUNCTION AcquireFunction)
+ {
+ ThreadData->Res = Res;
+ KeInitializeEvent(&ThreadData->InEvent, NotificationEvent, FALSE);
+ KeInitializeEvent(&ThreadData->OutEvent, NotificationEvent, FALSE);
+ ThreadData->AcquireResource = AcquireFunction;
+ }
+
+ static
+ NTSTATUS
+ StartThread(
+ PTHREAD_DATA ThreadData,
+ PLARGE_INTEGER Timeout,
+ BOOLEAN Wait,
+ BOOLEAN RetExpected)
+ {
+ NTSTATUS Status = STATUS_SUCCESS;
+ OBJECT_ATTRIBUTES Attributes;
+
+ ThreadData->Wait = Wait;
+ ThreadData->RetExpected = RetExpected;
+ InitializeObjectAttributes(&Attributes, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
+ Status = PsCreateSystemThread(&ThreadData->Handle, GENERIC_ALL, &Attributes, NULL, NULL, AcquireResourceThread, ThreadData);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ Status = ObReferenceObjectByHandle(ThreadData->Handle, SYNCHRONIZE, PsThreadType, KernelMode, (PVOID *)&ThreadData->Thread, NULL);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+
+ return KeWaitForSingleObject(&ThreadData->OutEvent, Executive, KernelMode, FALSE, Timeout);
+ }
+
+ static
+ VOID
+ FinishThread(
+ PTHREAD_DATA ThreadData)
+ {
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ KeSetEvent(&ThreadData->InEvent, 0, TRUE);
+ Status = KeWaitForSingleObject(ThreadData->Thread, Executive, KernelMode, FALSE, NULL);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+
+ ObDereferenceObject(ThreadData->Thread);
+ Status = ZwClose(ThreadData->Handle);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ KeClearEvent(&ThreadData->InEvent);
+ KeClearEvent(&ThreadData->OutEvent);
+ }
+
+ static
+ VOID
+ TestResourceWithThreads(
+ IN PERESOURCE Res)
+ {
+ NTSTATUS Status = STATUS_SUCCESS;
+ THREAD_DATA ThreadDataShared;
+ THREAD_DATA ThreadDataShared2;
+ THREAD_DATA ThreadDataExclusive;
+ THREAD_DATA ThreadDataSharedStarve;
+ THREAD_DATA ThreadDataSharedWait;
+ LARGE_INTEGER Timeout;
+ Timeout.QuadPart = -10 * 1000 * 10; /* 10 ms */
+
+ InitThreadData(&ThreadDataShared, Res, ExAcquireResourceSharedLite);
+ InitThreadData(&ThreadDataShared2, Res, ExAcquireResourceSharedLite);
+ InitThreadData(&ThreadDataExclusive, Res, ExAcquireResourceExclusiveLite);
+ InitThreadData(&ThreadDataSharedStarve, Res, ExAcquireSharedStarveExclusive);
+ InitThreadData(&ThreadDataSharedWait, Res, ExAcquireSharedWaitForExclusive);
+
+ /* have a thread acquire the resource shared */
+ Status = StartThread(&ThreadDataShared, NULL, FALSE, TRUE);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ CheckResourceStatus(Res, FALSE, 0LU, 0LU, 0LU);
+ ok_eq_int(Res->ActiveCount, 1);
+
+ /* a second thread should be able to acquire the resource shared */
+ Status = StartThread(&ThreadDataShared2, NULL, FALSE, TRUE);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ CheckResourceStatus(Res, FALSE, 0LU, 0LU, 0LU);
+ ok_eq_int(Res->ActiveCount, 2);
+ FinishThread(&ThreadDataShared2);
+ CheckResourceStatus(Res, FALSE, 0LU, 0LU, 0LU);
+ ok_eq_int(Res->ActiveCount, 1);
+
+ /* now have a thread that tries to acquire the resource exclusive -- it should fail */
+ Status = StartThread(&ThreadDataExclusive, NULL, FALSE, FALSE);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ CheckResourceStatus(Res, FALSE, 0LU, 0LU, 0LU);
+ ok_eq_int(Res->ActiveCount, 1);
+ FinishThread(&ThreadDataExclusive);
+ CheckResourceStatus(Res, FALSE, 0LU, 0LU, 0LU);
+ ok_eq_int(Res->ActiveCount, 1);
+
+ /* as above, but this time it should block */
+ Status = StartThread(&ThreadDataExclusive, &Timeout, TRUE, TRUE);
+ ok_eq_hex(Status, STATUS_TIMEOUT);
+ CheckResourceStatus(Res, FALSE, 0LU, 1LU, 0LU);
+ ok_eq_int(Res->ActiveCount, 1);
+
+ /* now try another shared one -- it should fail */
+ Status = StartThread(&ThreadDataShared2, NULL, FALSE, FALSE);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ CheckResourceStatus(Res, FALSE, 0LU, 1LU, 0LU);
+ ok_eq_int(Res->ActiveCount, 1);
+ FinishThread(&ThreadDataShared2);
+
+ /* same for ExAcquireSharedWaitForExclusive */
+ Status = StartThread(&ThreadDataSharedWait, NULL, FALSE, FALSE);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ CheckResourceStatus(Res, FALSE, 0LU, 1LU, 0LU);
+ ok_eq_int(Res->ActiveCount, 1);
+ FinishThread(&ThreadDataSharedWait);
+
+ /* ExAcquireSharedStarveExclusive must get access though! */
+ Status = StartThread(&ThreadDataSharedStarve, NULL, TRUE, TRUE);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ CheckResourceStatus(Res, FALSE, 0LU, 1LU, 0LU);
+ ok_eq_int(Res->ActiveCount, 2);
+ FinishThread(&ThreadDataSharedStarve);
+ CheckResourceStatus(Res, FALSE, 0LU, 1LU, 0LU);
+ ok_eq_int(Res->ActiveCount, 1);
+
+ /* block another shared one */
+ Status = StartThread(&ThreadDataShared2, &Timeout, TRUE, TRUE);
+ ok_eq_hex(Status, STATUS_TIMEOUT);
+ CheckResourceStatus(Res, FALSE, 0LU, 1LU, 1LU);
+ ok_eq_int(Res->ActiveCount, 1);
+
+ /* finish the very first one */
+ FinishThread(&ThreadDataShared);
+
+ /* now the blocked exclusive one should get the resource */
+ Status = KeWaitForSingleObject(&ThreadDataExclusive.OutEvent, Executive, KernelMode, FALSE, NULL);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ CheckResourceStatus(Res, FALSE, 0LU, 0LU, 1LU);
+ ok_eq_int(Res->ActiveCount, 1);
+ ok_eq_uint((Res->Flag & ResourceOwnedExclusive) != 0, 1);
+
+ FinishThread(&ThreadDataExclusive);
+ CheckResourceStatus(Res, FALSE, 0LU, 0LU, 0LU);
+
+ /* now the blocked shared one should resume */
+ Status = KeWaitForSingleObject(&ThreadDataShared2.OutEvent, Executive, KernelMode, FALSE, NULL);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ CheckResourceStatus(Res, FALSE, 0LU, 0LU, 0LU);
+ ok_eq_int(Res->ActiveCount, 1);
+ FinishThread(&ThreadDataShared2);
+ CheckResourceStatus(Res, FALSE, 0LU, 0LU, 0LU);
+ ok_eq_int(Res->ActiveCount, 0);
+ }
+
+ START_TEST(ExResource)
+ {
+ NTSTATUS Status;
+ ERESOURCE Res;
+ KIRQL Irql;
+
+ /* this must be true even with the different structure versions */
+ ASSERT(sizeof(ERESOURCE) == sizeof(ERESOURCE_2K3));
+
+ /* functional tests & internals */
+ Irql = KeRaiseIrqlToDpcLevel();
+ Status = ExInitializeResourceLite(&Res);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ KeLowerIrql(APC_LEVEL);
+
+ Status = ExDeleteResourceLite(&Res);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ KeLowerIrql(Irql);
+
+ memset(&Res, 0x55, sizeof Res);
+ Status = ExInitializeResourceLite(&Res);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ CheckResourceFields((PERESOURCE_2K3)&Res, FALSE);
+
+ CheckResourceStatus(&Res, FALSE, 0LU, 0LU, 0LU);
+
+ TestResourceSharedAccess(&Res);
+ CheckResourceStatus(&Res, FALSE, 0LU, 0LU, 0LU);
+
+ TestResourceExclusiveAccess(&Res);
+ CheckResourceStatus(&Res, FALSE, 0LU, 0LU, 0LU);
+
+ TestResourceUndocumentedShortcuts(&Res, FALSE);
+ CheckResourceStatus(&Res, FALSE, 0LU, 0LU, 0LU);
+ KeRaiseIrql(APC_LEVEL, &Irql);
+ TestResourceUndocumentedShortcuts(&Res, TRUE);
+ KeLowerIrql(Irql);
+ ok_bool_false(KeAreApcsDisabled(), "KeAreApcsDisabled returned");
+ CheckResourceStatus(&Res, FALSE, 0LU, 0LU, 0LU);
+
+ TestResourceWithThreads(&Res);
+
+ /* ExReinitializeResourceLite cleans up after us */
+ Status = ExReinitializeResourceLite(&Res);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ CheckResourceFields((PERESOURCE_2K3)&Res, TRUE);
+ CheckResourceStatus(&Res, FALSE, 0LU, 0LU, 0LU);
+
+ Status = ExDeleteResourceLite(&Res);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+
+ /* parameter checks */
+ Status = STATUS_SUCCESS;
+ _SEH2_TRY {
+ ExInitializeResourceLite(NULL);
+ } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
+ Status = _SEH2_GetExceptionCode();
+ } _SEH2_END;
+ ok_eq_hex(Status, STATUS_ACCESS_VIOLATION);
+
+ /* these bugcheck
+ ExDeleteResourceLite(NULL);
+ Status = ExDeleteResourceLite(&Res);*/
+ }
--- /dev/null
+ /*
+ * PROJECT: ReactOS kernel-mode tests
+ * LICENSE: GPLv2+ - See COPYING in the top level directory
+ * PURPOSE: Kernel-Mode Test Suite sequenced singly-linked list test
+ * PROGRAMMER: Thomas Faber <thfabba@gmx.de>
+ */
+
+ struct _SINGLE_LIST_ENTRY;
+ union _SLIST_HEADER;
+ struct _SINGLE_LIST_ENTRY *__fastcall ExInterlockedPushEntrySList(union _SLIST_HEADER *, struct _SINGLE_LIST_ENTRY *, unsigned long *);
+ struct _SINGLE_LIST_ENTRY *__fastcall ExInterlockedPopEntrySList(union _SLIST_HEADER *, unsigned long *);
+
+ #include <kmt_test.h>
+
+ /* TODO: SLIST_HEADER is a lot different for x64 */
+
+ #define CheckSListHeader(ListHead, ExpectedPointer, ExpectedDepth) do \
+ { \
+ ok_eq_pointer((ListHead)->Next.Next, ExpectedPointer); \
+ /*ok_eq_pointer(FirstEntrySList(ListHead), ExpectedPointer);*/ \
+ ok_eq_uint((ListHead)->Depth, ExpectedDepth); \
+ ok_eq_uint((ListHead)->Sequence, ExpectedSequence); \
+ ok_eq_uint(ExQueryDepthSList(ListHead), ExpectedDepth); \
+ ok_irql(HIGH_LEVEL); \
+ ok_bool_true(KmtAreInterruptsEnabled(), "Interrupts enabled:"); \
+ } while (0)
+
+ #define PXLIST_HEADER PSLIST_HEADER
+ #define PXLIST_ENTRY PSLIST_ENTRY
+ #define PushXList ExInterlockedPushEntrySList
+ #define PopXList ExInterlockedPopEntrySList
+ #define FlushXList ExInterlockedFlushSList
+ #define ok_free_xlist ok_eq_pointer
+ #define CheckXListHeader CheckSListHeader
+ #define TestXListFunctional TestSListFunctional
+ #include "ExXList.h"
+
+ #undef ExInterlockedPushEntrySList
+ #undef ExInterlockedPopEntrySList
+ #define TestXListFunctional TestSListFunctionalExports
+ #include "ExXList.h"
+
+ START_TEST(ExSequencedList)
+ {
+ PSLIST_HEADER ListHead;
+ KSPIN_LOCK SpinLock;
+ USHORT ExpectedSequence = 0;
+ PKSPIN_LOCK pSpinLock = &SpinLock;
+ PCHAR Buffer;
+ PSLIST_ENTRY Entries;
+ SIZE_T EntriesSize = 5 * sizeof *Entries;
+ KIRQL Irql;
+
+ KeInitializeSpinLock(&SpinLock);
+ #ifdef _M_IX86
+ pSpinLock = NULL;
+ #endif
+
+ /* make sure stuff is as un-aligned as possible ;) */
+ Buffer = ExAllocatePoolWithTag(NonPagedPool, sizeof *ListHead + EntriesSize + 1, 'TLqS');
+ ListHead = (PVOID)&Buffer[1];
+ Entries = (PVOID)&ListHead[1];
+ KeRaiseIrql(HIGH_LEVEL, &Irql);
+
+ RtlFillMemory(Entries, EntriesSize, 0x55);
+ RtlFillMemory(ListHead, sizeof *ListHead, 0x55);
+ InitializeSListHead(ListHead);
+ CheckSListHeader(ListHead, NULL, 0);
+ TestSListFunctional(ListHead, Entries, pSpinLock);
+
+ RtlFillMemory(Entries, EntriesSize, 0x55);
+ RtlFillMemory(ListHead, sizeof *ListHead, 0x55);
+ ExInitializeSListHead(ListHead);
+ CheckSListHeader(ListHead, NULL, 0);
+ TestSListFunctionalExports(ListHead, Entries, pSpinLock);
+
+ KeLowerIrql(Irql);
+ ExFreePoolWithTag(Buffer, 'TLqS');
+ }
--- /dev/null
+ /*
+ * PROJECT: ReactOS kernel-mode tests
+ * LICENSE: GPLv2+ - See COPYING in the top level directory
+ * PURPOSE: Kernel-Mode Test Suite Singly-linked list test
+ * PROGRAMMER: Thomas Faber <thfabba@gmx.de>
+ */
+
+ struct _SINGLE_LIST_ENTRY;
+ struct _SINGLE_LIST_ENTRY *__stdcall ExInterlockedPushEntryList(struct _SINGLE_LIST_ENTRY *, struct _SINGLE_LIST_ENTRY *, unsigned long *);
+ struct _SINGLE_LIST_ENTRY *__stdcall ExInterlockedPopEntryList(struct _SINGLE_LIST_ENTRY *, unsigned long *);
+
+ #include <kmt_test.h>
+
+ #define ok_eq_free2(Value, Expected) do \
+ { \
+ if (KmtIsCheckedBuild) \
+ ok_eq_pointer(Value, (PVOID)0xBADDD0FF); \
+ else \
+ ok_eq_pointer(Value, Expected); \
+ } while (0)
+
+ PSINGLE_LIST_ENTRY FlushList(PSINGLE_LIST_ENTRY ListHead)
+ {
+ PSINGLE_LIST_ENTRY Ret = ListHead->Next;
+ ListHead->Next = NULL;
+ return Ret;
+ }
+
+ USHORT QueryDepthList(PSINGLE_LIST_ENTRY ListHead)
+ {
+ USHORT Depth = 0;
+ while (ListHead->Next)
+ {
+ ++Depth;
+ ListHead = ListHead->Next;
+ }
+ return Depth;
+ }
+
+ PSINGLE_LIST_ENTRY PushEntryListWrapper(PSINGLE_LIST_ENTRY ListHead, PSINGLE_LIST_ENTRY Entry, PKSPIN_LOCK Lock)
+ {
+ PSINGLE_LIST_ENTRY Ret;
+ UNREFERENCED_PARAMETER(Lock);
+ Ret = ListHead->Next;
+ PushEntryList(ListHead, Entry);
+ return Ret;
+ }
+
+ #define CheckListHeader(ListHead, ExpectedPointer, ExpectedDepth) do \
+ { \
+ ok_eq_pointer((ListHead)->Next, ExpectedPointer); \
+ ok_eq_uint(QueryDepthList(ListHead), ExpectedDepth); \
+ ok_irql(HIGH_LEVEL); \
+ ok_bool_true(KmtAreInterruptsEnabled(), "Interrupts enabled:"); \
+ } while (0)
+
+ #define PXLIST_HEADER PSINGLE_LIST_ENTRY
+ #define PXLIST_ENTRY PSINGLE_LIST_ENTRY
+ #define PushXList ExInterlockedPushEntryList
+ #define PopXList ExInterlockedPopEntryList
+ #define FlushXList FlushList
+ #define ok_free_xlist ok_eq_free2
+ #define CheckXListHeader CheckListHeader
+ #define TestXListFunctional TestListFunctional
+ #include "ExXList.h"
+
+ #undef ExInterlockedPushEntryList
+ #undef ExInterlockedPopEntryList
+ #define TestXListFunctional TestListFunctionalExports
+ #include "ExXList.h"
+
+ #undef PushXList
+ #define PushXList PushEntryListWrapper
+ #undef PopXList
+ #define PopXList(h, s) PopEntryList(h)
+ #undef ok_free_xlist
+ #define ok_free_xlist ok_eq_pointer
+ #define TestXListFunctional TestListFunctionalNoInterlocked
+ #include "ExXList.h"
+
+ START_TEST(ExSingleList)
+ {
+ KSPIN_LOCK SpinLock;
+ PSINGLE_LIST_ENTRY ListHead;
+ PSINGLE_LIST_ENTRY Entries;
+ SIZE_T EntriesSize = 5 * sizeof *Entries;
+ PCHAR Buffer;
+ KIRQL Irql;
+
+ KeInitializeSpinLock(&SpinLock);
+
+ /* make sure stuff is as un-aligned as possible ;) */
+ Buffer = ExAllocatePoolWithTag(NonPagedPool, sizeof *ListHead + EntriesSize + 1, 'TLiS');
+ ListHead = (PVOID)&Buffer[1];
+ Entries = (PVOID)&ListHead[1];
+ KeRaiseIrql(HIGH_LEVEL, &Irql);
+
+ RtlFillMemory(Entries, sizeof Entries, 0x55);
+ ListHead->Next = NULL;
+ TestListFunctional(ListHead, Entries, &SpinLock);
+
+ RtlFillMemory(Entries, sizeof Entries, 0x55);
+ ListHead->Next = NULL;
+ TestListFunctionalExports(ListHead, Entries, &SpinLock);
+
+ RtlFillMemory(Entries, sizeof Entries, 0x55);
+ ListHead->Next = NULL;
+ TestListFunctionalNoInterlocked(ListHead, Entries, &SpinLock);
+
+ KeLowerIrql(Irql);
+ ExFreePoolWithTag(Buffer, 'TLiS');
+ }
/*
- * NTOSKRNL Executive Regressions KM-Test
- * ReactOS Kernel Mode Regression Testing framework
- *
- * Copyright 2006 Aleksey Bragin <aleksey@reactos.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; see the file COPYING.LIB.
- * If not, write to the Free Software Foundation,
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * PROJECT: ReactOS kernel-mode tests
+ * LICENSE: LGPLv2+ - See COPYING.LIB in the top level directory
+ * PURPOSE: Kernel-Mode Test Suite Executive Regressions KM-Test
+ * PROGRAMMER: Aleksey Bragin <aleksey@reactos.org>
*/
- /* INCLUDES *******************************************************************/
-
- #include <ddk/ntddk.h>
- #include <ntifs.h>
- #include <ndk/ntndk.h>
- #include "kmtest.h"
+ #include <kmt_test.h>
#define NDEBUG
- #include "debug.h"
-
- /* PRIVATE FUNCTIONS ***********************************************************/
+ #include <debug.h>
+ static
VOID
NTAPI
TestTimerApcRoutine(IN PVOID TimerContext,
(*ApcCount)++;
}
- /* PUBLIC FUNCTIONS *************************************************************/
-
- VOID
- ExTimerTest(HANDLE KeyHandle)
+ START_TEST(ExTimer)
{
UNICODE_STRING TimerName;
OBJECT_ATTRIBUTES ObjectAttributes;
NTSTATUS Status;
ULONG ApcCount;
- StartTest();
-
// Create the timer
RtlInitUnicodeString(&TimerName, L"\\TestTimer");
InitializeObjectAttributes(&ObjectAttributes, &TimerName, 0, NULL, NULL);
Status = ZwClose(TimerHandle);
ok(Status == STATUS_SUCCESS, "ZwClose failed with Status=0x%08lX", Status);
-
- FinishTest(KeyHandle, L"ExTimerTest");
}
--- /dev/null
+ /* used by ExSingleList and ExSequencedList tests */
+ static
+ VOID
+ TestXListFunctional(
+ IN PXLIST_HEADER ListHead,
+ IN PXLIST_ENTRY Entries,
+ IN PKSPIN_LOCK pSpinLock)
+ {
+ USHORT ExpectedSequence = 0;
+ PXLIST_ENTRY Ret;
+
+ Ret = FlushXList(ListHead);
+ ok_eq_pointer(Ret, NULL);
+ CheckXListHeader(ListHead, NULL, 0);
+
+ Ret = PopXList(ListHead, pSpinLock);
+ ok_eq_pointer(Ret, NULL);
+ CheckXListHeader(ListHead, NULL, 0);
+
+ Ret = PushXList(ListHead, &Entries[0], pSpinLock);
+ ++ExpectedSequence;
+ ok_eq_pointer(Ret, NULL);
+ ok_eq_pointer(Entries[0].Next, NULL);
+ CheckXListHeader(ListHead, &Entries[0], 1);
+
+ Ret = PushXList(ListHead, &Entries[1], pSpinLock);
+ ++ExpectedSequence;
+ ok_eq_pointer(Ret, &Entries[0]);
+ ok_eq_pointer(Entries[0].Next, NULL);
+ ok_eq_pointer(Entries[1].Next, &Entries[0]);
+ CheckXListHeader(ListHead, &Entries[1], 2);
+
+ Ret = PopXList(ListHead, pSpinLock);
+ ok_eq_pointer(Ret, &Entries[1]);
+ ok_eq_pointer(Entries[0].Next, NULL);
+ ok_free_xlist(Entries[1].Next, &Entries[0]);
+ CheckXListHeader(ListHead, &Entries[0], 1);
+
+ Ret = PopXList(ListHead, pSpinLock);
+ ok_eq_pointer(Ret, &Entries[0]);
+ ok_free_xlist(Entries[0].Next, NULL);
+ ok_free_xlist(Entries[1].Next, &Entries[0]);
+ CheckXListHeader(ListHead, NULL, 0);
+
+ Ret = PopXList(ListHead, pSpinLock);
+ ok_eq_pointer(Ret, NULL);
+ ok_free_xlist(Entries[0].Next, NULL);
+ ok_free_xlist(Entries[1].Next, &Entries[0]);
+ CheckXListHeader(ListHead, NULL, 0);
+
+ /* add entries again */
+ Ret = PushXList(ListHead, &Entries[0], pSpinLock);
+ ++ExpectedSequence;
+ ok_eq_pointer(Ret, NULL);
+ ok_eq_pointer(Entries[0].Next, NULL);
+ CheckXListHeader(ListHead, &Entries[0], 1);
+
+ Ret = PushXList(ListHead, &Entries[1], pSpinLock);
+ ++ExpectedSequence;
+ ok_eq_pointer(Ret, &Entries[0]);
+ ok_eq_pointer(Entries[0].Next, NULL);
+ ok_eq_pointer(Entries[1].Next, &Entries[0]);
+ CheckXListHeader(ListHead, &Entries[1], 2);
+
+ Ret = PopXList(ListHead, pSpinLock);
+ ok_eq_pointer(Ret, &Entries[1]);
+ ok_eq_pointer(Entries[0].Next, NULL);
+ ok_free_xlist(Entries[1].Next, &Entries[0]);
+ CheckXListHeader(ListHead, &Entries[0], 1);
+
+ Ret = PushXList(ListHead, &Entries[1], pSpinLock);
+ ++ExpectedSequence;
+ ok_eq_pointer(Ret, &Entries[0]);
+ ok_eq_pointer(Entries[0].Next, NULL);
+ ok_eq_pointer(Entries[1].Next, &Entries[0]);
+ CheckXListHeader(ListHead, &Entries[1], 2);
+
+ Ret = PushXList(ListHead, &Entries[2], pSpinLock);
+ ++ExpectedSequence;
+ ok_eq_pointer(Ret, &Entries[1]);
+ ok_eq_pointer(Entries[0].Next, NULL);
+ ok_eq_pointer(Entries[1].Next, &Entries[0]);
+ ok_eq_pointer(Entries[2].Next, &Entries[1]);
+ CheckXListHeader(ListHead, &Entries[2], 3);
+
+ Ret = FlushXList(ListHead);
+ ok_eq_pointer(Ret, &Entries[2]);
+ CheckXListHeader(ListHead, NULL, 0);
+ }
+
+ #undef TestXListFunctional
/*
- * FsRtl Test
- *
- * Copyright 2010 Pierre Schweitzer <pierre.schweitzer@reactos.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; see the file COPYING.LIB.
- * If not, write to the Free Software Foundation,
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * PROJECT: ReactOS kernel-mode tests
+ * LICENSE: LGPLv2+ - See COPYING.LIB in the top level directory
+ * PURPOSE: Kernel-Mode Test Suite FsRtl Test
+ * PROGRAMMER: Pierre Schweitzer <pierre.schweitzer@reactos.org>
*/
- /* INCLUDES *******************************************************************/
+ /* TODO: most of these calls fail the Windows checked build's !islower assertion and others */
- #include "kmtest.h"
- #include <ntifs.h>
+ #include <kmt_test.h>
#define NDEBUG
- #include "debug.h"
+ #include <debug.h>
- /* PRIVATE FUNCTIONS **********************************************************/
-
- VOID FsRtlIsNameInExpressionTest()
+ static VOID FsRtlIsNameInExpressionTest()
{
UNICODE_STRING Expression, Name;
- RtlInitUnicodeString(&Expression, L"*");
- RtlInitUnicodeString(&Name, L"");
- ok(FsRtlIsNameInExpression(&Expression, &Name, FALSE, NULL) == FALSE, "expected FALSE, got TRUE");
- RtlInitUnicodeString(&Expression, L"");
- ok(FsRtlIsNameInExpression(&Expression, &Name, FALSE, NULL) == TRUE, "expected TRUE, got FALSE");
+ /* !Name->Length || !Expression->Length asserts */
+ if (!KmtIsCheckedBuild)
+ {
+ RtlInitUnicodeString(&Expression, L"*");
+ RtlInitUnicodeString(&Name, L"");
+ ok(FsRtlIsNameInExpression(&Expression, &Name, FALSE, NULL) == FALSE, "expected FALSE, got TRUE");
+ RtlInitUnicodeString(&Expression, L"");
+ ok(FsRtlIsNameInExpression(&Expression, &Name, FALSE, NULL) == TRUE, "expected TRUE, got FALSE");
+ }
RtlInitUnicodeString(&Expression, L"ntdll.dll");
RtlInitUnicodeString(&Name, L".");
RtlInitUnicodeString(&Expression, L"*.?.c.d");
ok(FsRtlIsNameInExpression(&Expression, &Name, FALSE, NULL) == TRUE, "expected TRUE, got FALSE");
RtlInitUnicodeString(&Expression, L"*?");
- RtlInitUnicodeString(&Name, L"");
- ok(FsRtlIsNameInExpression(&Expression, &Name, FALSE, NULL) == FALSE, "expected FALSE, got TRUE");
+ if (!KmtIsCheckedBuild)
+ {
+ RtlInitUnicodeString(&Name, L"");
+ ok(FsRtlIsNameInExpression(&Expression, &Name, FALSE, NULL) == FALSE, "expected FALSE, got TRUE");
+ }
RtlInitUnicodeString(&Name, L"a");
ok(FsRtlIsNameInExpression(&Expression, &Name, FALSE, NULL) == TRUE, "expected TRUE, got FALSE");
RtlInitUnicodeString(&Name, L"aa");
RtlInitUnicodeString(&Name, L"aaa");
ok(FsRtlIsNameInExpression(&Expression, &Name, FALSE, NULL) == TRUE, "expected TRUE, got FALSE");
RtlInitUnicodeString(&Expression, L"?*?");
- RtlInitUnicodeString(&Name, L"");
- ok(FsRtlIsNameInExpression(&Expression, &Name, FALSE, NULL) == FALSE, "expected FALSE, got TRUE");
+ if (!KmtIsCheckedBuild)
+ {
+ RtlInitUnicodeString(&Name, L"");
+ ok(FsRtlIsNameInExpression(&Expression, &Name, FALSE, NULL) == FALSE, "expected FALSE, got TRUE");
+ }
RtlInitUnicodeString(&Name, L"a");
ok(FsRtlIsNameInExpression(&Expression, &Name, FALSE, NULL) == FALSE, "expected FALSE, got TRUE");
RtlInitUnicodeString(&Name, L"aa");
ok(FsRtlIsNameInExpression(&Expression, &Name, FALSE, NULL) == FALSE, "expected FALSE, got TRUE");
}
- VOID FsRtlIsDbcsInExpressionTest()
+ static VOID FsRtlIsDbcsInExpressionTest()
{
ANSI_STRING Expression, Name;
- RtlInitAnsiString(&Expression, "*");
- RtlInitAnsiString(&Name, "");
- ok(FsRtlIsDbcsInExpression(&Expression, &Name) == FALSE, "expected FALSE, got TRUE");
- RtlInitAnsiString(&Expression, "");
- ok(FsRtlIsDbcsInExpression(&Expression, &Name) == TRUE, "expected TRUE, got FALSE");
+ if (!KmtIsCheckedBuild)
+ {
+ RtlInitAnsiString(&Expression, "*");
+ RtlInitAnsiString(&Name, "");
+ ok(FsRtlIsDbcsInExpression(&Expression, &Name) == FALSE, "expected FALSE, got TRUE");
+ RtlInitAnsiString(&Expression, "");
+ ok(FsRtlIsDbcsInExpression(&Expression, &Name) == TRUE, "expected TRUE, got FALSE");
+ }
RtlInitAnsiString(&Expression, "ntdll.dll");
RtlInitAnsiString(&Name, ".");
RtlInitAnsiString(&Expression, "*.?.c.d");
ok(FsRtlIsDbcsInExpression(&Expression, &Name) == TRUE, "expected TRUE, got FALSE");
RtlInitAnsiString(&Expression, "*?");
- RtlInitAnsiString(&Name, "");
- ok(FsRtlIsDbcsInExpression(&Expression, &Name) == FALSE, "expected FALSE, got TRUE");
+ if (!KmtIsCheckedBuild)
+ {
+ RtlInitAnsiString(&Name, "");
+ ok(FsRtlIsDbcsInExpression(&Expression, &Name) == FALSE, "expected FALSE, got TRUE");
+ }
RtlInitAnsiString(&Name, "a");
ok(FsRtlIsDbcsInExpression(&Expression, &Name) == TRUE, "expected TRUE, got FALSE");
RtlInitAnsiString(&Name, "aa");
RtlInitAnsiString(&Name, "aaa");
ok(FsRtlIsDbcsInExpression(&Expression, &Name) == TRUE, "expected TRUE, got FALSE");
RtlInitAnsiString(&Expression, "?*?");
- RtlInitAnsiString(&Name, "");
- ok(FsRtlIsDbcsInExpression(&Expression, &Name) == FALSE, "expected FALSE, got TRUE");
+ if (!KmtIsCheckedBuild)
+ {
+ RtlInitAnsiString(&Name, "");
+ ok(FsRtlIsDbcsInExpression(&Expression, &Name) == FALSE, "expected FALSE, got TRUE");
+ }
RtlInitAnsiString(&Name, "a");
ok(FsRtlIsDbcsInExpression(&Expression, &Name) == FALSE, "expected FALSE, got TRUE");
RtlInitAnsiString(&Name, "aa");
ok(FsRtlIsDbcsInExpression(&Expression, &Name) == FALSE, "expected FALSE, got TRUE");
}
- /* PUBLIC FUNCTIONS ***********************************************************/
-
- VOID
- NtoskrnlFsRtlTest(HANDLE KeyHandle)
+ START_TEST(FsRtlExpression)
{
- StartTest();
-
FsRtlIsNameInExpressionTest();
FsRtlIsDbcsInExpressionTest();
-
- FinishTest(KeyHandle, L"FsRtlTest");
}
--- /dev/null
+ include_directories(
+ ../include)
+
+ #
+ # IoDeviceObject
+ #
+ list(APPEND IODEVICEOBJECT_DRV_SOURCE
+ ../kmtest_drv/kmtest_standalone.c
+ IoDeviceObject_drv.c)
+
+ add_library(iodeviceobject_drv SHARED ${IODEVICEOBJECT_DRV_SOURCE})
+
+ set_module_type(iodeviceobject_drv kernelmodedriver)
+ target_link_libraries(iodeviceobject_drv kmtest_printf ${PSEH_LIB})
+ add_importlibs(iodeviceobject_drv ntoskrnl hal)
+ add_target_compile_definitions(iodeviceobject_drv KMT_STANDALONE_DRIVER)
+ #add_pch(iodeviceobject_drv ../include/kmt_test.h)
+
+ add_cd_file(TARGET iodeviceobject_drv DESTINATION reactos/bin FOR all)
+
+ #
+ # IoHelper
+ #
+ list(APPEND IOHELPER_DRV_SOURCE
+ ../kmtest_drv/kmtest_standalone.c
+ IoHelper_drv.c)
+
+ add_library(iohelper_drv SHARED ${IOHELPER_DRV_SOURCE})
+
+ set_module_type(iohelper_drv kernelmodedriver)
+ target_link_libraries(iohelper_drv kmtest_printf ${PSEH_LIB})
+ add_importlibs(iohelper_drv ntoskrnl hal)
+ add_target_compile_definitions(iohelper_drv KMT_STANDALONE_DRIVER)
+ #add_pch(iohelper_drv ../include/kmt_test.h)
+
+ add_cd_file(TARGET iohelper_drv DESTINATION reactos/bin FOR all)
/*
- * PnP Test
- * Device Interface functions test
- *
- * Copyright 2004 Filip Navara <xnavara@volny.cz>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; see the file COPYING.LIB.
- * If not, write to the Free Software Foundation,
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * PROJECT: ReactOS kernel-mode tests
+ * LICENSE: LGPLv2+ - See COPYING.LIB in the top level directory
+ * PURPOSE: Kernel-Mode Test Suite Device Interface functions test
+ * PROGRAMMER: Filip Navara <xnavara@volny.cz>
*/
- /* INCLUDES *******************************************************************/
+ /* TODO: what's with the prototypes at the top, what's with the if-ed out part? Doesn't process most results */
- #include <ddk/ntifs.h>
- #include <ndk/iotypes.h>
- #include "kmtest.h"
+ #include <kmt_test.h>
#define NDEBUG
- #include "debug.h"
-
- /* PRIVATE FUNCTIONS **********************************************************/
+ #include <debug.h>
+ #if 0
NTSTATUS
(NTAPI *IoGetDeviceInterfaces_Func)(
IN CONST GUID *InterfaceClassGuid,
IN PDEVICE_OBJECT PhysicalDeviceObject OPTIONAL,
IN ULONG Flags,
OUT PWSTR *SymbolicLinkList);
+ #endif /* 0 */
- VOID DeviceInterfaceTest_Func()
+ static VOID DeviceInterfaceTest_Func()
{
NTSTATUS Status;
PWSTR SymbolicLinkList;
ExFreePool(SymbolicLinkList);
}
- VOID RegisterDI_Test(HANDLE KeyHandle)
+ START_TEST(IoDeviceInterface)
{
GUID Guid = {0x378de44c, 0x56ef, 0x11d1, {0xbc, 0x8c, 0x00, 0xa0, 0xc9, 0x14, 0x05, 0xdd}};
DEVICE_OBJECT DeviceObject;
UNICODE_STRING SymbolicLinkName;
NTSTATUS Status;
- StartTest();
-
RtlInitUnicodeString(&SymbolicLinkName, L"");
// Prepare our surrogate of a Device Object
"IoRegisterDeviceInterface returned 0x%08lX\n", Status);
DeviceInterfaceTest_Func();
-
- FinishTest(KeyHandle, L"IoDeviceInterfaceTest");
}
--- /dev/null
+ /*
+ * PROJECT: ReactOS kernel-mode tests
+ * LICENSE: GPLv2+ - See COPYING in the top level directory
+ * PURPOSE: Kernel-Mode Test Suite Driver Object Test Driver
+ * PROGRAMMER: Michael Martin <martinmnet@hotmail.com>
+ * Thomas Faber <thfabba@gmx.de>
+ */
+
+ #include <kmt_test.h>
+
+ //#define NDEBUG
+ #include <debug.h>
+
+ typedef enum
+ {
+ DriverEntry,
+ DriverIrp,
+ DriverUnload
+ } DRIVER_STATUS;
+
+ static DRIVER_DISPATCH TestDispatch;
+ static VOID TestDriverObject(IN PDRIVER_OBJECT DriverObject, IN DRIVER_STATUS DriverStatus);
+ static BOOLEAN TestZwLoad(IN PDRIVER_OBJECT DriverObject, IN PCUNICODE_STRING DriverRegistryPath, IN PWCHAR NewDriverRegPath);
+ static BOOLEAN TestZwUnload(IN PDRIVER_OBJECT DriverObject, IN PCUNICODE_STRING DriverRegistryPath, IN PWCHAR NewDriverRegPath);
+ static VOID TestLowerDeviceKernelAPI(IN PDEVICE_OBJECT DeviceObject);
+ static VOID TestDeviceCreated(IN PDEVICE_OBJECT DeviceObject, IN BOOLEAN ExclusiveAccess);
+ static VOID TestDeviceDeletion(IN PDEVICE_OBJECT DeviceObject, IN BOOLEAN Lower, IN BOOLEAN Attached);
+ static VOID TestDeviceCreateDelete(IN PDRIVER_OBJECT DriverObject);
+ static VOID TestAttachDevice(IN PDEVICE_OBJECT DeviceObject, IN PWCHAR NewDriverRegPath);
+ static VOID TestDetachDevice(IN PDEVICE_OBJECT AttachedDevice);
+
+ static PDEVICE_OBJECT MainDeviceObject;
+ static PDEVICE_OBJECT AttachDeviceObject;
+ static PDRIVER_OBJECT ThisDriverObject;
+
+ NTSTATUS
+ TestEntry(
+ IN PDRIVER_OBJECT DriverObject,
+ IN PCUNICODE_STRING RegistryPath,
+ OUT PCWSTR *DeviceName,
+ IN OUT INT *Flags)
+ {
+ NTSTATUS Status = STATUS_SUCCESS;
+ BOOLEAN Ret;
+ INT i;
+
+ PAGED_CODE();
+
+ UNREFERENCED_PARAMETER(DeviceName);
+
+ *Flags = TESTENTRY_NO_CREATE_DEVICE | TESTENTRY_NO_REGISTER_DISPATCH;
+
+ ThisDriverObject = DriverObject;
+
+ TestDriverObject(DriverObject, DriverEntry);
+
+ /* Create and delete device, on return MainDeviceObject has been created */
+ TestDeviceCreateDelete(DriverObject);
+
+ /* Make sure a device object was created */
+ if (!skip(MainDeviceObject != NULL, "Device object creation failed\n"))
+ {
+ PWCHAR LowerDriverRegPath = L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\Kmtest-IoHelper";
+
+ /* Load driver test and load the lower driver */
+ Ret = TestZwLoad(DriverObject, RegistryPath, LowerDriverRegPath);
+ if (!skip(Ret, "Failed to load helper driver\n"))
+ {
+ TestAttachDevice(MainDeviceObject, L"\\Device\\Kmtest-IoHelper");
+ if (!skip(AttachDeviceObject != NULL, "No attached device object\n"))
+ TestLowerDeviceKernelAPI(MainDeviceObject);
+
+ /* Unload lower driver without detaching from its device */
+ TestZwUnload(DriverObject, RegistryPath, LowerDriverRegPath);
+ TestLowerDeviceKernelAPI(MainDeviceObject);
+ }
+ }
+
+ for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; ++i)
+ DriverObject->MajorFunction[i] = NULL;
+ DriverObject->MajorFunction[IRP_MJ_CREATE] = TestDispatch;
+ DriverObject->MajorFunction[IRP_MJ_CLOSE] = TestDispatch;
+ DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = TestDispatch;
+
+ return Status;
+ }
+
+ VOID
+ TestUnload(
+ IN PDRIVER_OBJECT DriverObject)
+ {
+ PAGED_CODE();
+
+ if (!skip(AttachDeviceObject != NULL, "no attached device object\n"))
+ {
+ TestDeviceDeletion(MainDeviceObject, FALSE, TRUE);
+ TestDeviceDeletion(AttachDeviceObject, TRUE, FALSE);
+ TestDetachDevice(AttachDeviceObject);
+ }
+
+ TestDeviceDeletion(MainDeviceObject, FALSE, FALSE);
+ TestDriverObject(DriverObject, DriverUnload);
+
+ if (MainDeviceObject)
+ IoDeleteDevice(MainDeviceObject);
+ }
+
+ static
+ NTSTATUS
+ NTAPI
+ TestDispatch(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp)
+ {
+ NTSTATUS Status = STATUS_SUCCESS;
+ PIO_STACK_LOCATION IoStackLocation;
+
+ PAGED_CODE();
+
+ IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
+
+ DPRINT("TestIrpHandler. Function=%s, DeviceObject=%p, AttachDeviceObject=%p\n",
+ KmtMajorFunctionNames[IoStackLocation->MajorFunction],
+ DeviceObject,
+ AttachDeviceObject);
+
+ if (AttachDeviceObject)
+ {
+ IoSkipCurrentIrpStackLocation(Irp);
+ Status = IoCallDriver(AttachDeviceObject, Irp);
+ return Status;
+ }
+
+ TestDriverObject(DeviceObject->DriverObject, DriverIrp);
+
+ Irp->IoStatus.Status = Status;
+ Irp->IoStatus.Information = 0;
+
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ return Status;
+ }
+
+ static
+ VOID
+ TestDriverObject(
+ IN PDRIVER_OBJECT DriverObject,
+ IN DRIVER_STATUS DriverStatus)
+ {
+ BOOLEAN CheckThisDispatchRoutine;
+ PVOID FirstMajorFunc;
+ int i;
+
+ ok(DriverObject->Size == sizeof(DRIVER_OBJECT), "Size does not match, got %x\n",DriverObject->Size);
+ ok(DriverObject->Type == 4, "Type does not match 4. got %d\n", DriverObject->Type);
+
+ if (DriverStatus == DriverEntry)
+ {
+ ok(DriverObject->DeviceObject == NULL, "Expected DeviceObject pointer to be 0, got %p\n",
+ DriverObject->DeviceObject);
+ ok (DriverObject->Flags == DRVO_LEGACY_DRIVER,
+ "Expected Flags to be DRVO_LEGACY_DRIVER, got %lu\n",
+ DriverObject->Flags);
+ }
+ else if (DriverStatus == DriverIrp)
+ {
+ ok(DriverObject->DeviceObject != NULL, "Expected DeviceObject pointer to non null\n");
+ ok (DriverObject->Flags == (DRVO_LEGACY_DRIVER | DRVO_INITIALIZED),
+ "Expected Flags to be DRVO_LEGACY_DRIVER | DRVO_INITIALIZED, got %lu\n",
+ DriverObject->Flags);
+ }
+ else if (DriverStatus == DriverUnload)
+ {
+ ok(DriverObject->DeviceObject != NULL, "Expected DeviceObject pointer to non null\n");
+ ok (DriverObject->Flags == (DRVO_LEGACY_DRIVER | DRVO_INITIALIZED | DRVO_UNLOAD_INVOKED),
+ "Expected Flags to be DRVO_LEGACY_DRIVER | DRVO_INITIALIZED | DRVO_UNLOAD_INVOKED, got %lu\n",
+ DriverObject->Flags);
+ }
+ else
+ ASSERT(FALSE);
+
+ /* Select a routine that was not changed */
+ FirstMajorFunc = DriverObject->MajorFunction[1];
+ ok(FirstMajorFunc != 0, "Expected MajorFunction[1] to be non NULL\n");
+
+ if (!skip(FirstMajorFunc != NULL, "First major function not set!\n"))
+ {
+ for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
+ {
+ if (DriverStatus > 0) CheckThisDispatchRoutine = (i > 3) && (i != 14);
+ else CheckThisDispatchRoutine = TRUE;
+
+ if (CheckThisDispatchRoutine)
+ {
+ ok(DriverObject->MajorFunction[i] == FirstMajorFunc, "Expected MajorFunction[%d] to match %p\n",
+ i, FirstMajorFunc);
+ }
+ }
+ }
+ }
+
+ static
+ BOOLEAN
+ TestZwLoad(
+ IN PDRIVER_OBJECT DriverObject,
+ IN PCUNICODE_STRING DriverRegistryPath,
+ IN PWCHAR NewDriverRegPath)
+ {
+ UNICODE_STRING RegPath;
+ NTSTATUS Status;
+
+ /* Try to load ourself */
+ Status = ZwLoadDriver((PUNICODE_STRING)DriverRegistryPath);
+ ok (Status == STATUS_IMAGE_ALREADY_LOADED, "Expected NTSTATUS STATUS_IMAGE_ALREADY_LOADED, got 0x%lX\n", Status);
+
+ if (Status != STATUS_IMAGE_ALREADY_LOADED)
+ {
+ DbgPrint("WARNING: Loading this a second time will cause BUGCHECK!\n");
+ }
+
+ /* Try to load with a Registry Path that doesnt exist */
+ RtlInitUnicodeString(&RegPath, L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\deadbeef");
+ Status = ZwLoadDriver(&RegPath);
+ ok (Status == STATUS_OBJECT_NAME_NOT_FOUND, "Expected NTSTATUS STATUS_OBJECT_NAME_NOT_FOUND, got 0x%lX\n", Status);
+
+ /* Load the driver */
+ RtlInitUnicodeString(&RegPath, NewDriverRegPath);
+ Status = ZwLoadDriver(&RegPath);
+ ok(Status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got 0x%lX\n", Status);
+
+ return NT_SUCCESS(Status);
+ }
+
+ static
+ BOOLEAN
+ TestZwUnload(
+ IN PDRIVER_OBJECT DriverObject,
+ IN PCUNICODE_STRING DriverRegistryPath,
+ IN PWCHAR NewDriverRegPath)
+ {
+ UNICODE_STRING RegPath;
+ NTSTATUS Status;
+
+ /* Try to unload ourself, which should fail as our Unload routine hasnt been set yet. */
+ Status = ZwUnloadDriver((PUNICODE_STRING)DriverRegistryPath);
+ ok (Status == STATUS_INVALID_DEVICE_REQUEST, "Expected NTSTATUS STATUS_INVALID_DEVICE_REQUEST, got 0x%lX\n", Status);
+
+ /* Try to unload with a Registry Path that doesnt exist */
+ RtlInitUnicodeString(&RegPath, L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\deadbeef");
+ Status = ZwUnloadDriver(&RegPath);
+ ok (Status == STATUS_OBJECT_NAME_NOT_FOUND, "Expected NTSTATUS STATUS_OBJECT_NAME_NOT_FOUND, got 0x%lX\n", Status);
+
+ /* Unload the driver */
+ RtlInitUnicodeString(&RegPath, NewDriverRegPath);
+ Status = ZwUnloadDriver(&RegPath);
+ ok(Status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got 0x%lX\n", Status);
+
+ return NT_SUCCESS(Status);
+ }
+
+ static
+ VOID
+ TestLowerDeviceKernelAPI(
+ IN PDEVICE_OBJECT DeviceObject)
+ {
+ PDEVICE_OBJECT RetObject;
+
+ RetObject = IoGetLowerDeviceObject(DeviceObject);
+
+ ok(RetObject == AttachDeviceObject,
+ "Expected an Attached DeviceObject %p, got %p\n", AttachDeviceObject, RetObject);
+
+ if (RetObject)
+ {
+ ObDereferenceObject(RetObject);
+ }
+
+ RetObject = IoGetDeviceAttachmentBaseRef(DeviceObject);
+ ok(RetObject == AttachDeviceObject,
+ "Expected an Attached DeviceObject %p, got %p\n", AttachDeviceObject, RetObject);
+
+ if (RetObject)
+ {
+ ObDereferenceObject(RetObject);
+ }
+
+ }
+
+ static
+ VOID
+ TestDeviceCreated(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN BOOLEAN ExclusiveAccess)
+ {
+ PEXTENDED_DEVOBJ_EXTENSION extdev;
+
+ /* Check the device object members */
+ ok(DeviceObject->Type == 3, "Expected Type = 3, got %x\n", DeviceObject->Type);
+ ok(DeviceObject->Size == 0xb8, "Expected Size = 0xb8, got %x\n", DeviceObject->Size);
+ ok(DeviceObject->ReferenceCount == 0, "Expected ReferenceCount = 0, got %lu\n",
+ DeviceObject->ReferenceCount);
+ ok(DeviceObject->DriverObject == ThisDriverObject,
+ "Expected DriverObject member to match this DriverObject %p, got %p\n",
+ ThisDriverObject, DeviceObject->DriverObject);
+ ok(DeviceObject->NextDevice == NULL, "Expected NextDevice to be NULL, got %p\n", DeviceObject->NextDevice);
+ ok(DeviceObject->AttachedDevice == NULL, "Expected AttachDevice to be NULL, got %p\n", DeviceObject->AttachedDevice);
+ ok(DeviceObject->Characteristics == 0, "Expected Characteristics to be 0\n");
+ if (ExclusiveAccess)
+ {
+ ok((DeviceObject->Flags == (DO_DEVICE_HAS_NAME | DO_DEVICE_INITIALIZING | DO_EXCLUSIVE)),
+ "Expected Flags DO_DEVICE_HAS_NAME | DO_DEVICE_INITIALIZING | DO_EXCLUSIVE, got %lu\n", DeviceObject->Flags);
+ }
+ else
+ {
+ ok((DeviceObject->Flags == (DO_DEVICE_HAS_NAME | DO_DEVICE_INITIALIZING)),
+ "Expected Flags DO_DEVICE_HAS_NAME | DO_DEVICE_INITIALIZING, got %lu\n", DeviceObject->Flags);
+ }
+ ok(DeviceObject->DeviceType == FILE_DEVICE_UNKNOWN,
+ "Expected DeviceType to match creation parameter FILE_DEVICE_UNKNWOWN, got %lu\n",
+ DeviceObject->DeviceType);
+ ok(DeviceObject->ActiveThreadCount == 0, "Expected ActiveThreadCount = 0, got %lu\n", DeviceObject->ActiveThreadCount);
+
+ /* Check the extended extension */
+ extdev = (PEXTENDED_DEVOBJ_EXTENSION)DeviceObject->DeviceObjectExtension;
+ ok(extdev->ExtensionFlags == 0, "Expected Extended ExtensionFlags to be 0, got %lu\n", extdev->ExtensionFlags);
+ ok (extdev->Type == 13, "Expected Type of 13, got %d\n", extdev->Type);
+ ok (extdev->Size == 0, "Expected Size of 0, got %d\n", extdev->Size);
+ ok (extdev->DeviceObject == DeviceObject, "Expected DeviceOject to match newly created device %p, got %p\n",
+ DeviceObject, extdev->DeviceObject);
+ ok(extdev->AttachedTo == NULL, "Expected AttachTo to be NULL, got %p\n", extdev->AttachedTo);
+ ok(extdev->StartIoCount == 0, "Expected StartIoCount = 0, got %lu\n", extdev->StartIoCount);
+ ok(extdev->StartIoKey == 0, "Expected StartIoKey = 0, got %lu\n", extdev->StartIoKey);
+ ok(extdev->StartIoFlags == 0, "Expected StartIoFlags = 0, got %lu\n", extdev->StartIoFlags);
+ }
+
+ static
+ VOID
+ TestDeviceDeletion(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN BOOLEAN Lower,
+ IN BOOLEAN Attached)
+ {
+ PEXTENDED_DEVOBJ_EXTENSION extdev;
+
+ /* Check the device object members */
+ ok(DeviceObject->Type == 3, "Expected Type = 3, got %d\n", DeviceObject->Type);
+ ok(DeviceObject->Size == 0xb8, "Expected Size = 0xb8, got %d\n", DeviceObject->Size);
+ ok(DeviceObject->ReferenceCount == 0, "Expected ReferenceCount = 0, got %lu\n",
+ DeviceObject->ReferenceCount);
+ if (!Lower)
+ {
+ ok(DeviceObject->DriverObject == ThisDriverObject,
+ "Expected DriverObject member to match this DriverObject %p, got %p\n",
+ ThisDriverObject, DeviceObject->DriverObject);
+ }
+ ok(DeviceObject->NextDevice == NULL, "Expected NextDevice to be NULL, got %p\n", DeviceObject->NextDevice);
+
+ if (Lower)
+ {
+ ok(DeviceObject->AttachedDevice == MainDeviceObject,
+ "Expected AttachDevice to be %p, got %p\n", MainDeviceObject, DeviceObject->AttachedDevice);
+ }
+ else
+ {
+ ok(DeviceObject->AttachedDevice == NULL, "Expected AttachDevice to be NULL, got %p\n", DeviceObject->AttachedDevice);
+ }
+
+ ok(DeviceObject->Flags == (DO_DEVICE_HAS_NAME | (Lower ? DO_EXCLUSIVE : 0)),
+ "Expected Flags DO_DEVICE_HAS_NAME, got %lu\n", DeviceObject->Flags);
+ ok(DeviceObject->DeviceType == FILE_DEVICE_UNKNOWN,
+ "Expected DeviceType to match creation parameter FILE_DEVICE_UNKNWOWN, got %lu\n",
+ DeviceObject->DeviceType);
+ ok(DeviceObject->ActiveThreadCount == 0, "Expected ActiveThreadCount = 0, got %lu\n", DeviceObject->ActiveThreadCount);
+
+ /*Check the extended extension */
+ extdev = (PEXTENDED_DEVOBJ_EXTENSION)DeviceObject->DeviceObjectExtension;
+ ok(extdev->ExtensionFlags == DOE_UNLOAD_PENDING,
+ "Expected Extended ExtensionFlags to be DOE_UNLOAD_PENDING, got %lu\n", extdev->ExtensionFlags);
+ ok (extdev->Type == 13, "Expected Type of 13, got %d\n", extdev->Type);
+ ok (extdev->Size == 0, "Expected Size of 0, got %d\n", extdev->Size);
+ ok (extdev->DeviceObject == DeviceObject, "Expected DeviceOject to match newly created device %p, got %p\n",
+ DeviceObject, extdev->DeviceObject);
+ if (Lower || !Attached)
+ {
+ ok(extdev->AttachedTo == NULL, "Expected AttachTo to be NULL, got %p\n", extdev->AttachedTo);
+ }
+ else
+ {
+ ok(extdev->AttachedTo == AttachDeviceObject, "Expected AttachTo to %p, got %p\n", AttachDeviceObject, extdev->AttachedTo);
+ }
+ ok(extdev->StartIoCount == 0, "Expected StartIoCount = 0, got %lu\n", extdev->StartIoCount);
+ ok(extdev->StartIoKey == 0, "Expected StartIoKey = 0, got %lu\n", extdev->StartIoKey);
+ ok(extdev->StartIoFlags == 0, "Expected StartIoFlags = 0, got %lu\n", extdev->StartIoFlags);
+ }
+
+ static
+ VOID
+ TestDeviceCreateDelete(
+ IN PDRIVER_OBJECT DriverObject)
+ {
+ NTSTATUS Status;
+ UNICODE_STRING DeviceString;
+ PDEVICE_OBJECT DeviceObject;
+
+ /* Create using wrong directory */
+ RtlInitUnicodeString(&DeviceString, L"\\Device1\\Kmtest-IoDeviceObject");
+ Status = IoCreateDevice(DriverObject,
+ 0,
+ &DeviceString,
+ FILE_DEVICE_UNKNOWN,
+ 0,
+ FALSE,
+ &DeviceObject);
+ ok(Status == STATUS_OBJECT_PATH_NOT_FOUND, "Expected STATUS_OBJECT_PATH_NOT_FOUND, got 0x%lX\n", Status);
+
+ /* Create using correct params with exclusice access */
+ RtlInitUnicodeString(&DeviceString, L"\\Device\\Kmtest-IoDeviceObject");
+ Status = IoCreateDevice(DriverObject,
+ 0,
+ &DeviceString,
+ FILE_DEVICE_UNKNOWN,
+ 0,
+ TRUE,
+ &DeviceObject);
+ ok(Status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got 0x%lX\n", Status);
+
+ TestDeviceCreated(DeviceObject, TRUE);
+
+ /* Delete the device */
+ if (NT_SUCCESS(Status))
+ {
+ IoDeleteDevice(DeviceObject);
+ ok(DriverObject->DeviceObject == 0, "Expected DriverObject->DeviceObject to be NULL, got %p\n",
+ DriverObject->DeviceObject);
+ }
+
+ /* Create using correct params without exclusice access */
+ Status = IoCreateDevice(DriverObject,
+ 0,
+ &DeviceString,
+ FILE_DEVICE_UNKNOWN,
+ 0,
+ FALSE,
+ &DeviceObject);
+ ok(Status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got 0x%lX\n", Status);
+
+ TestDeviceCreated(DeviceObject, FALSE);
+
+ /* Delete the device */
+ if (NT_SUCCESS(Status))
+ {
+ IoDeleteDevice(DeviceObject);
+ ok(DriverObject->DeviceObject == 0, "Expected DriverObject->DeviceObject to be NULL, got %p\n",
+ DriverObject->DeviceObject);
+ }
+
+ /* Recreate device */
+ Status = IoCreateDevice(DriverObject,
+ 0,
+ &DeviceString,
+ FILE_DEVICE_UNKNOWN,
+ 0,
+ FALSE,
+ &DeviceObject);
+ ok(Status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got 0x%lX\n", Status);
+
+ if (NT_SUCCESS(Status))
+ MainDeviceObject = DeviceObject;
+ }
+
+ static
+ VOID
+ TestAttachDevice(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PWCHAR NewDriverRegPath)
+ {
+ NTSTATUS Status;
+ UNICODE_STRING LowerDeviceName;
+
+ RtlInitUnicodeString(&LowerDeviceName, NewDriverRegPath);
+ Status = IoAttachDevice(DeviceObject, &LowerDeviceName, &AttachDeviceObject);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+
+ /* TODO: Add more tests */
+ }
+
+ static
+ VOID
+ TestDetachDevice(
+ IN PDEVICE_OBJECT AttachedDevice)
+ {
+ IoDetachDevice(AttachedDevice);
+
+ /* TODO: Add more tests */
+ }
--- /dev/null
+ /*
+ * PROJECT: ReactOS kernel-mode tests
+ * LICENSE: GPLv2+ - See COPYING in the top level directory
+ * PURPOSE: Kernel-Mode Test Suite Driver Object test user-mode part
+ * PROGRAMMER: Thomas Faber <thfabba@gmx.de>
+ */
+
+ #include <kmt_test.h>
+
+ START_TEST(IoDeviceObject)
+ {
+ /* make sure IoHelper has an existing service key, but is not started */
+ KmtLoadDriver(L"IoHelper", FALSE);
+ KmtUnloadDriver();
+
+ KmtLoadDriver(L"IoDeviceObject", TRUE);
+ KmtOpenDriver();
+ KmtCloseDriver();
+ KmtUnloadDriver();
+ }
--- /dev/null
+ /*
+ * PROJECT: ReactOS kernel-mode tests
+ * LICENSE: GPLv2+ - See COPYING in the top level directory
+ * PURPOSE: Kernel-Mode Test Suite I/O Test Helper driver
+ * PROGRAMMER: Thomas Faber <thfabba@gmx.de>
+ */
+
+ #include <kmt_test.h>
+
+ //#define NDEBUG
+ #include <debug.h>
+
+ static KMT_IRP_HANDLER TestIrpHandler;
+
+ NTSTATUS
+ TestEntry(
+ IN PDRIVER_OBJECT DriverObject,
+ IN PCUNICODE_STRING RegistryPath,
+ OUT PCWSTR *DeviceName,
+ IN OUT INT *Flags)
+ {
+ NTSTATUS Status = STATUS_SUCCESS;
+ INT i;
+
+ PAGED_CODE();
+
+ UNREFERENCED_PARAMETER(DriverObject);
+ UNREFERENCED_PARAMETER(RegistryPath);
+ UNREFERENCED_PARAMETER(Flags);
+
+ DPRINT("TestEntry. DriverObject=%p, RegistryPath=%wZ\n", DriverObject, RegistryPath);
+
+ *DeviceName = L"IoHelper";
+
+ for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; ++i)
+ KmtRegisterIrpHandler(i, NULL, TestIrpHandler);
+
+ return Status;
+ }
+
+ VOID
+ TestUnload(
+ IN PDRIVER_OBJECT DriverObject)
+ {
+ PAGED_CODE();
+
+ UNREFERENCED_PARAMETER(DriverObject);
+
+ DPRINT("TestUnload. DriverObject=%p\n", DriverObject);
+ }
+
+ static
+ NTSTATUS
+ TestIrpHandler(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IoStackLocation)
+ {
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ DPRINT("TestIrpHandler. Function=%s, DeviceObject=%p\n",
+ KmtMajorFunctionNames[IoStackLocation->MajorFunction],
+ DeviceObject);
+
+ Irp->IoStatus.Status = Status;
+ Irp->IoStatus.Information = 0;
+
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ return Status;
+ }
--- /dev/null
+ /*
+ * PROJECT: ReactOS kernel-mode tests
+ * LICENSE: GPLv2+ - See COPYING in the top level directory
+ * PURPOSE: Kernel-Mode Test Suite Interrupt test
+ * PROGRAMMER: Thomas Faber <thfabba@gmx.de>
+ */
+
+ #include <kmt_test.h>
+
+ #define NDEBUG
+ #include <debug.h>
+
+ #define CheckSpinLock(Lock, Locked) do \
+ { \
+ if (KmtIsMultiProcessorBuild) \
+ ok_eq_ulongptr(*(Lock), (Locked) != 0); \
+ else \
+ ok_eq_ulongptr(*(Lock), 0); \
+ } while (0)
+
+ typedef struct
+ {
+ BOOLEAN ReturnValue;
+ KIRQL ExpectedIrql;
+ PKINTERRUPT Interrupt;
+ } TEST_CONTEXT, *PTEST_CONTEXT;
+
+ static KSYNCHRONIZE_ROUTINE SynchronizeRoutine;
+
+ static
+ BOOLEAN
+ NTAPI
+ SynchronizeRoutine(
+ IN PVOID Context)
+ {
+ PTEST_CONTEXT TestContext = Context;
+
+ ok_irql(TestContext->ExpectedIrql);
+
+ CheckSpinLock(TestContext->Interrupt->ActualLock, TRUE);
+
+ return TestContext->ReturnValue;
+ }
+
+ static
+ VOID
+ TestSynchronizeExecution(VOID)
+ {
+ KINTERRUPT Interrupt;
+ TEST_CONTEXT TestContext;
+ KIRQL SynchIrql;
+ KIRQL OriginalIrql;
+ KIRQL Irql;
+ KSPIN_LOCK ActualLock;
+ BOOLEAN Ret;
+
+ RtlFillMemory(&Interrupt, sizeof Interrupt, 0x55);
+ Interrupt.ActualLock = &ActualLock;
+ KeInitializeSpinLock(Interrupt.ActualLock);
+ CheckSpinLock(Interrupt.ActualLock, FALSE);
+
+ TestContext.Interrupt = &Interrupt;
+ TestContext.ReturnValue = TRUE;
+
+ for (TestContext.ReturnValue = 0; TestContext.ReturnValue <= 2; ++TestContext.ReturnValue)
+ {
+ for (OriginalIrql = PASSIVE_LEVEL; OriginalIrql <= HIGH_LEVEL; ++OriginalIrql)
+ {
+ /* TODO: don't hardcode this :| */
+ if (OriginalIrql == 3 || (OriginalIrql >= 11 && OriginalIrql <= 26) || OriginalIrql == 30)
+ continue;
+ KeRaiseIrql(OriginalIrql, &Irql);
+ for (SynchIrql = max(DISPATCH_LEVEL, OriginalIrql); SynchIrql <= HIGH_LEVEL; ++SynchIrql)
+ {
+ if (SynchIrql == 3 || (SynchIrql >= 11 && SynchIrql <= 26) || SynchIrql == 30)
+ continue;
+ Interrupt.SynchronizeIrql = SynchIrql;
+ ok_irql(OriginalIrql);
+ CheckSpinLock(Interrupt.ActualLock, FALSE);
+ TestContext.ExpectedIrql = SynchIrql;
+ Ret = KeSynchronizeExecution(&Interrupt, SynchronizeRoutine, &TestContext);
+ ok_eq_int(Ret, TestContext.ReturnValue);
+ ok_irql(OriginalIrql);
+ CheckSpinLock(Interrupt.ActualLock, FALSE);
+ /* TODO: Check that all other fields of the interrupt are untouched */
+ }
+ KeLowerIrql(Irql);
+ }
+ }
+ }
+
+ START_TEST(IoInterrupt)
+ {
+ TestSynchronizeExecution();
+ }
/*
- * NTOSKRNL Io Regressions KM-Test
- * ReactOS Kernel Mode Regression Testing framework
- *
- * Copyright 2006 Aleksey Bragin <aleksey@reactos.org>
- * Copyright 2008 Etersoft (Alexander Morozov)
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; see the file COPYING.LIB.
- * If not, write to the Free Software Foundation,
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * PROJECT: ReactOS kernel-mode tests
+ * LICENSE: LGPLv2+ - See COPYING.LIB in the top level directory
+ * PURPOSE: Kernel-Mode Test Suite Io Regressions KM-Test (Irp)
+ * PROGRAMMER: Aleksey Bragin <aleksey@reactos.org>
*/
+ /* Based on code Copyright 2008 Etersoft (Alexander Morozov) */
- /* INCLUDES *******************************************************************/
-
- #include <ddk/ntddk.h>
- #include "kmtest.h"
+ #include <kmt_test.h>
#define NDEBUG
- #include "debug.h"
-
-
- /* PUBLIC FUNCTIONS ***********************************************************/
-
- VOID NtoskrnlIoMdlTest(HANDLE KeyHandle)
- {
- PMDL Mdl;
- PIRP Irp;
- PVOID VirtualAddress;
- ULONG MdlSize = 2*4096+184; // 2 pages and some random value
-
- StartTest();
-
- // Try to alloc 2Gb MDL
- Mdl = IoAllocateMdl(NULL, 2048UL*0x100000, FALSE, FALSE, NULL);
-
- ok(Mdl == NULL,
- "IoAllocateMdl should fail allocation of 2Gb or more, but got Mdl=0x%X",
- (UINT_PTR)Mdl);
-
- if (Mdl)
- IoFreeMdl(Mdl);
+ #include <debug.h>
- // Now create a valid MDL
- VirtualAddress = ExAllocatePool(NonPagedPool, MdlSize);
- Mdl = IoAllocateMdl(VirtualAddress, MdlSize, FALSE, FALSE, NULL);
- ok(Mdl != NULL, "Mdl allocation failed");
- // Check fields of the allocated struct
- ok(Mdl->Next == NULL, "Mdl->Next should be NULL, but is 0x%X",
- (UINT_PTR)Mdl->Next);
- ok(Mdl->ByteCount == MdlSize,
- "Mdl->ByteCount should be equal to MdlSize, but is 0x%X",
- (UINT_PTR)Mdl->ByteCount);
- // TODO: Check other fields of MDL struct
-
- IoFreeMdl(Mdl);
- // Allocate now with pointer to an Irp
- Irp = IoAllocateIrp(1, FALSE);
- ok(Irp != NULL, "IRP allocation failed");
- Mdl = IoAllocateMdl(VirtualAddress, MdlSize, FALSE, FALSE, Irp);
- ok(Mdl != NULL, "Mdl allocation failed");
- ok(Irp->MdlAddress == Mdl, "Irp->MdlAddress should be 0x%X, but is 0x%X",
- (UINT_PTR)Mdl, (UINT_PTR)Irp->MdlAddress);
-
- IoFreeMdl(Mdl);
-
- // TODO: Check a case when SecondaryBuffer == TRUE
-
- IoFreeIrp(Irp);
- ExFreePool(VirtualAddress);
-
- FinishTest(KeyHandle, L"IoMdlTest");
- }
-
- VOID NtoskrnlIoIrpTest(HANDLE KeyHandle)
+ START_TEST(IoIrp)
{
USHORT size;
IRP *iorp;
- StartTest();
-
// 1st test
size = sizeof(IRP) + 5 * sizeof(IO_STACK_LOCATION);
iorp = ExAllocatePool(NonPagedPool, size);
IoFreeIrp(iorp);
}
-
- FinishTest(KeyHandle, L"IoIrpTest");
}
--- /dev/null
+ /*
+ * PROJECT: ReactOS kernel-mode tests
+ * LICENSE: LGPLv2+ - See COPYING.LIB in the top level directory
+ * PURPOSE: Kernel-Mode Test Suite Io Regressions KM-Test (Mdl)
+ * PROGRAMMER: Aleksey Bragin <aleksey@reactos.org>
+ */
+
+ #include <kmt_test.h>
+
+ #define NDEBUG
+ #include <debug.h>
+
+ START_TEST(IoMdl)
+ {
+ PMDL Mdl;
+ PIRP Irp;
+ PVOID VirtualAddress;
+ ULONG MdlSize = 2*4096+184; // 2 pages and some random value
+
+ // Try to alloc 2Gb MDL
+ Mdl = IoAllocateMdl(NULL, 2048UL*0x100000, FALSE, FALSE, NULL);
+
+ ok(Mdl == NULL,
+ "IoAllocateMdl should fail allocation of 2Gb or more, but got Mdl=0x%X",
+ (UINT_PTR)Mdl);
+
+ if (Mdl)
+ IoFreeMdl(Mdl);
+
+ // Now create a valid MDL
+ VirtualAddress = ExAllocatePool(NonPagedPool, MdlSize);
+ Mdl = IoAllocateMdl(VirtualAddress, MdlSize, FALSE, FALSE, NULL);
+ ok(Mdl != NULL, "Mdl allocation failed");
+ // Check fields of the allocated struct
+ ok(Mdl->Next == NULL, "Mdl->Next should be NULL, but is 0x%X",
+ (UINT_PTR)Mdl->Next);
+ ok(Mdl->ByteCount == MdlSize,
+ "Mdl->ByteCount should be equal to MdlSize, but is 0x%X",
+ (UINT_PTR)Mdl->ByteCount);
+ // TODO: Check other fields of MDL struct
+
+ IoFreeMdl(Mdl);
+ // Allocate now with pointer to an Irp
+ Irp = IoAllocateIrp(1, FALSE);
+ ok(Irp != NULL, "IRP allocation failed");
+ Mdl = IoAllocateMdl(VirtualAddress, MdlSize, FALSE, FALSE, Irp);
+ ok(Mdl != NULL, "Mdl allocation failed");
+ ok(Irp->MdlAddress == Mdl, "Irp->MdlAddress should be 0x%X, but is 0x%X",
+ (UINT_PTR)Mdl, (UINT_PTR)Irp->MdlAddress);
+
+ IoFreeMdl(Mdl);
+
+ // TODO: Check a case when SecondaryBuffer == TRUE
+
+ IoFreeIrp(Irp);
+ ExFreePool(VirtualAddress);
+ }
--- /dev/null
+ <module name="iodeviceobject_drv" type="kernelmodedriver" installbase="bin" installname="iodeviceobject_drv.sys">
+ <include base="kmtest_drv">include</include>
+ <library>ntoskrnl</library>
+ <library>hal</library>
+ <library>pseh</library>
+ <library>kmtest_printf</library>
+ <define name="KMT_STANDALONE_DRIVER" />
+ <file>IoDeviceObject_drv.c</file>
+ <directory name="..">
+ <directory name="kmtest_drv">
+ <file>kmtest_standalone.c</file>
+ </directory>
+ </directory>
+ </module>
--- /dev/null
+ <module name="iohelper_drv" type="kernelmodedriver" installbase="bin" installname="iohelper_drv.sys">
+ <include base="kmtest_drv">include</include>
+ <library>ntoskrnl</library>
+ <library>hal</library>
+ <library>pseh</library>
+ <library>kmtest_printf</library>
+ <define name="KMT_STANDALONE_DRIVER" />
+ <file>IoHelper_drv.c</file>
+ <directory name="..">
+ <directory name="kmtest_drv">
+ <file>kmtest_standalone.c</file>
+ </directory>
+ </directory>
+ </module>
--- /dev/null
+ /*
+ * PROJECT: ReactOS kernel-mode tests
+ * LICENSE: GPLv2+ - See COPYING in the top level directory
+ * PURPOSE: Kernel-Mode Test Suite Asynchronous Procedure Call test
+ * PROGRAMMER: Thomas Faber <thfabba@gmx.de>
+ */
+
+ #include <kmt_test.h>
+
+ #define CheckApcs(KernelApcsDisabled, SpecialApcsDisabled, AllApcsDisabled, Irql) do \
+ { \
+ ok_eq_bool(KeAreApcsDisabled(), KernelApcsDisabled || SpecialApcsDisabled); \
+ ok_eq_int(Thread->KernelApcDisable, KernelApcsDisabled); \
+ ok_eq_bool(KeAreAllApcsDisabled(), AllApcsDisabled); \
+ ok_eq_int(Thread->SpecialApcDisable, SpecialApcsDisabled); \
+ ok_irql(Irql); \
+ } while (0)
+
+ START_TEST(KeApc)
+ {
+ KIRQL Irql;
+ PKTHREAD Thread = KeGetCurrentThread();
+
+ CheckApcs(0, 0, FALSE, PASSIVE_LEVEL);
+
+ /* critical region */
+ KeEnterCriticalRegion();
+ CheckApcs(-1, 0, FALSE, PASSIVE_LEVEL);
+ KeEnterCriticalRegion();
+ CheckApcs(-2, 0, FALSE, PASSIVE_LEVEL);
+ KeEnterCriticalRegion();
+ CheckApcs(-3, 0, FALSE, PASSIVE_LEVEL);
+ KeLeaveCriticalRegion();
+ CheckApcs(-2, 0, FALSE, PASSIVE_LEVEL);
+ KeLeaveCriticalRegion();
+ CheckApcs(-1, 0, FALSE, PASSIVE_LEVEL);
+ KeLeaveCriticalRegion();
+ CheckApcs(0, 0, FALSE, PASSIVE_LEVEL);
+
+ /* guarded region */
+ KeEnterGuardedRegion();
+ CheckApcs(0, -1, TRUE, PASSIVE_LEVEL);
+ KeEnterGuardedRegion();
+ CheckApcs(0, -2, TRUE, PASSIVE_LEVEL);
+ KeEnterGuardedRegion();
+ CheckApcs(0, -3, TRUE, PASSIVE_LEVEL);
+ KeLeaveGuardedRegion();
+ CheckApcs(0, -2, TRUE, PASSIVE_LEVEL);
+ KeLeaveGuardedRegion();
+ CheckApcs(0, -1, TRUE, PASSIVE_LEVEL);
+ KeLeaveGuardedRegion();
+ CheckApcs(0, 0, FALSE, PASSIVE_LEVEL);
+
+ /* mix them */
+ KeEnterGuardedRegion();
+ CheckApcs(0, -1, TRUE, PASSIVE_LEVEL);
+ KeEnterCriticalRegion();
+ CheckApcs(-1, -1, TRUE, PASSIVE_LEVEL);
+ KeLeaveCriticalRegion();
+ CheckApcs(0, -1, TRUE, PASSIVE_LEVEL);
+ KeLeaveGuardedRegion();
+ CheckApcs(0, 0, FALSE, PASSIVE_LEVEL);
+
+ KeEnterCriticalRegion();
+ CheckApcs(-1, 0, FALSE, PASSIVE_LEVEL);
+ KeEnterGuardedRegion();
+ CheckApcs(-1, -1, TRUE, PASSIVE_LEVEL);
+ KeLeaveGuardedRegion();
+ CheckApcs(-1, 0, FALSE, PASSIVE_LEVEL);
+ KeLeaveCriticalRegion();
+ CheckApcs(0, 0, FALSE, PASSIVE_LEVEL);
+
+ /* leave without entering */
+ if (!KmtIsCheckedBuild)
+ {
+ KeLeaveCriticalRegion();
+ CheckApcs(1, 0, FALSE, PASSIVE_LEVEL);
+ KeEnterCriticalRegion();
+ CheckApcs(0, 0, FALSE, PASSIVE_LEVEL);
+
+ KeLeaveGuardedRegion();
+ CheckApcs(0, 1, TRUE, PASSIVE_LEVEL);
+ KeEnterGuardedRegion();
+ CheckApcs(0, 0, FALSE, PASSIVE_LEVEL);
+
+ KeLeaveCriticalRegion();
+ CheckApcs(1, 0, FALSE, PASSIVE_LEVEL);
+ KeLeaveGuardedRegion();
+ CheckApcs(1, 1, TRUE, PASSIVE_LEVEL);
+ KeEnterCriticalRegion();
+ CheckApcs(0, 1, TRUE, PASSIVE_LEVEL);
+ KeEnterGuardedRegion();
+ CheckApcs(0, 0, FALSE, PASSIVE_LEVEL);
+ }
+
+ /* manually disable APCs */
+ Thread->KernelApcDisable = -1;
+ CheckApcs(-1, 0, FALSE, PASSIVE_LEVEL);
+ Thread->SpecialApcDisable = -1;
+ CheckApcs(-1, -1, TRUE, PASSIVE_LEVEL);
+ Thread->KernelApcDisable = 0;
+ CheckApcs(0, -1, TRUE, PASSIVE_LEVEL);
+ Thread->SpecialApcDisable = 0;
+ CheckApcs(0, 0, FALSE, PASSIVE_LEVEL);
+
+ /* raised irql - APC_LEVEL should disable APCs */
+ KeRaiseIrql(APC_LEVEL, &Irql);
+ CheckApcs(0, 0, TRUE, APC_LEVEL);
+ KeLowerIrql(Irql);
+ CheckApcs(0, 0, FALSE, PASSIVE_LEVEL);
+
+ /* KeAre*ApcsDisabled are documented to work up to DISPATCH_LEVEL... */
+ KeRaiseIrql(DISPATCH_LEVEL, &Irql);
+ CheckApcs(0, 0, TRUE, DISPATCH_LEVEL);
+ KeLowerIrql(Irql);
+ CheckApcs(0, 0, FALSE, PASSIVE_LEVEL);
+
+ /* ... but also work on higher levels! */
+ KeRaiseIrql(HIGH_LEVEL, &Irql);
+ CheckApcs(0, 0, TRUE, HIGH_LEVEL);
+ KeLowerIrql(Irql);
+ CheckApcs(0, 0, FALSE, PASSIVE_LEVEL);
+
+ /* now comes the crazy stuff */
+ KeRaiseIrql(HIGH_LEVEL, &Irql);
+ CheckApcs(0, 0, TRUE, HIGH_LEVEL);
+ KeEnterCriticalRegion();
+ CheckApcs(-1, 0, TRUE, HIGH_LEVEL);
+ KeLeaveCriticalRegion();
+ CheckApcs(0, 0, TRUE, HIGH_LEVEL);
+
+ /* Ke*GuardedRegion assert at > APC_LEVEL */
+ if (!KmtIsCheckedBuild)
+ {
+ KeEnterGuardedRegion();
+ CheckApcs(0, -1, TRUE, HIGH_LEVEL);
+ KeLeaveGuardedRegion();
+ }
+ CheckApcs(0, 0, TRUE, HIGH_LEVEL);
+ KeLowerIrql(Irql);
+ CheckApcs(0, 0, FALSE, PASSIVE_LEVEL);
+
+ if (!KmtIsCheckedBuild)
+ {
+ KeRaiseIrql(HIGH_LEVEL, &Irql);
+ CheckApcs(0, 0, TRUE, HIGH_LEVEL);
+ KeEnterCriticalRegion();
+ CheckApcs(-1, 0, TRUE, HIGH_LEVEL);
+ KeEnterGuardedRegion();
+ CheckApcs(-1, -1, TRUE, HIGH_LEVEL);
+ KeLowerIrql(Irql);
+ CheckApcs(-1, -1, TRUE, PASSIVE_LEVEL);
+ KeLeaveCriticalRegion();
+ CheckApcs(0, -1, TRUE, PASSIVE_LEVEL);
+ KeLeaveGuardedRegion();
+ CheckApcs(0, 0, FALSE, PASSIVE_LEVEL);
+
+ KeEnterGuardedRegion();
+ CheckApcs(0, -1, TRUE, PASSIVE_LEVEL);
+ KeRaiseIrql(HIGH_LEVEL, &Irql);
+ CheckApcs(0, -1, TRUE, HIGH_LEVEL);
+ KeEnterCriticalRegion();
+ CheckApcs(-1, -1, TRUE, HIGH_LEVEL);
+ KeLeaveGuardedRegion();
+ CheckApcs(-1, 0, TRUE, HIGH_LEVEL);
+ KeLowerIrql(Irql);
+ CheckApcs(-1, 0, FALSE, PASSIVE_LEVEL);
+ KeLeaveCriticalRegion();
+ CheckApcs(0, 0, FALSE, PASSIVE_LEVEL);
+
+ KeEnterCriticalRegion();
+ CheckApcs(-1, 0, FALSE, PASSIVE_LEVEL);
+ KeRaiseIrql(HIGH_LEVEL, &Irql);
+ CheckApcs(-1, 0, TRUE, HIGH_LEVEL);
+ KeEnterGuardedRegion();
+ CheckApcs(-1, -1, TRUE, HIGH_LEVEL);
+ KeLeaveCriticalRegion();
+ CheckApcs(0, -1, TRUE, HIGH_LEVEL);
+ KeLowerIrql(Irql);
+ CheckApcs(0, -1, TRUE, PASSIVE_LEVEL);
+ KeLeaveGuardedRegion();
+ CheckApcs(0, 0, FALSE, PASSIVE_LEVEL);
+ }
+
+ KeEnterCriticalRegion();
+ CheckApcs(-1, 0, FALSE, PASSIVE_LEVEL);
+ KeRaiseIrql(HIGH_LEVEL, &Irql);
+ CheckApcs(-1, 0, TRUE, HIGH_LEVEL);
+ KeLeaveCriticalRegion();
+ CheckApcs(0, 0, TRUE, HIGH_LEVEL);
+ KeLowerIrql(Irql);
+ CheckApcs(0, 0, FALSE, PASSIVE_LEVEL);
+ }
--- /dev/null
+ /*
+ * PROJECT: ReactOS kernel-mode tests
+ * LICENSE: GPLv2+ - See COPYING in the top level directory
+ * PURPOSE: Kernel-Mode Test Suite Deferred Procedure Call test
+ * PROGRAMMER: Thomas Faber <thfabba@gmx.de>
+ */
+
+ #include <kmt_test.h>
+
+ //#define NDEBUG
+ #include <debug.h>
+
+ /* TODO: DPC importance */
+
+ static volatile LONG DpcCount;
+ static volatile UCHAR DpcImportance;
+
+ static KDEFERRED_ROUTINE DpcHandler;
+
+ static
+ VOID
+ NTAPI
+ DpcHandler(
+ IN PRKDPC Dpc,
+ IN PVOID DeferredContext,
+ IN PVOID SystemArgument1,
+ IN PVOID SystemArgument2)
+ {
+ PKPRCB Prcb = KeGetCurrentPrcb();
+
+ ok_irql(DISPATCH_LEVEL);
+ InterlockedIncrement(&DpcCount);
+ ok(DeferredContext == Dpc, "DeferredContext = %p, Dpc = %p, expected equal\n", DeferredContext, Dpc);
+ ok_eq_pointer(SystemArgument1, (PVOID)0xabc123);
+ ok_eq_pointer(SystemArgument2, (PVOID)0x5678);
+
+ /* KDPC object contents */
+ ok_eq_uint(Dpc->Type, DpcObject);
+ ok_eq_uint(Dpc->Importance, DpcImportance);
+ ok_eq_uint(Dpc->Number, 0);
+ ok(Dpc->DpcListEntry.Blink != NULL, "\n");
+ ok(Dpc->DpcListEntry.Blink != &Dpc->DpcListEntry, "\n");
+ if (!skip(Dpc->DpcListEntry.Blink != NULL, "DpcListEntry.Blink == NULL\n"))
+ ok_eq_pointer(Dpc->DpcListEntry.Flink, Dpc->DpcListEntry.Blink->Flink);
+
+ ok(Dpc->DpcListEntry.Flink != NULL, "\n");
+ ok(Dpc->DpcListEntry.Flink != &Dpc->DpcListEntry, "\n");
+ if (!skip(Dpc->DpcListEntry.Flink != NULL, "DpcListEntry.Flink == NULL\n"))
+ ok_eq_pointer(Dpc->DpcListEntry.Blink, Dpc->DpcListEntry.Flink->Blink);
+
+ ok_eq_pointer(Dpc->DeferredRoutine, DpcHandler);
+ ok_eq_pointer(Dpc->DeferredContext, DeferredContext);
+ ok_eq_pointer(Dpc->SystemArgument1, SystemArgument1);
+ ok_eq_pointer(Dpc->SystemArgument2, SystemArgument2);
+ ok_eq_pointer(Dpc->DpcData, NULL);
+
+ ok_eq_uint(Prcb->DpcRoutineActive, 1);
+ /* this DPC is not in the list anymore, but it was at the head! */
+ ok_eq_pointer(Prcb->DpcData[DPC_NORMAL].DpcListHead.Flink, Dpc->DpcListEntry.Flink);
+ ok_eq_pointer(Prcb->DpcData[DPC_NORMAL].DpcListHead.Blink, Dpc->DpcListEntry.Blink);
+ }
+
+ START_TEST(KeDpc)
+ {
+ NTSTATUS Status = STATUS_SUCCESS;
+ KDPC Dpc;
+ KIRQL Irql, Irql2, Irql3;
+ LONG ExpectedDpcCount = 0;
+ BOOLEAN Ret;
+ int i;
+
+ DpcCount = 0;
+ DpcImportance = MediumImportance;
+
+ #define ok_dpccount() ok(DpcCount == ExpectedDpcCount, "DpcCount = %ld, expected %ld\n", DpcCount, ExpectedDpcCount);
+ trace("Dpc = %p\n", &Dpc);
+ memset(&Dpc, 0x55, sizeof Dpc);
+ KeInitializeDpc(&Dpc, DpcHandler, &Dpc);
+ /* check the Dpc object's fields */
+ ok_eq_uint(Dpc.Type, DpcObject);
+ ok_eq_uint(Dpc.Importance, DpcImportance);
+ ok_eq_uint(Dpc.Number, 0);
+ ok_eq_pointer(Dpc.DpcListEntry.Flink, (LIST_ENTRY *)0x5555555555555555LL);
+ ok_eq_pointer(Dpc.DpcListEntry.Blink, (LIST_ENTRY *)0x5555555555555555LL);
+ ok_eq_pointer(Dpc.DeferredRoutine, DpcHandler);
+ ok_eq_pointer(Dpc.DeferredContext, &Dpc);
+ ok_eq_pointer(Dpc.SystemArgument1, (PVOID)0x5555555555555555LL);
+ ok_eq_pointer(Dpc.SystemArgument2, (PVOID)0x5555555555555555LL);
+ ok_eq_pointer(Dpc.DpcData, NULL);
+
+ /* simply run the Dpc a few times */
+ for (i = 0; i < 5; ++i)
+ {
+ ok_dpccount();
+ Ret = KeInsertQueueDpc(&Dpc, (PVOID)0xabc123, (PVOID)0x5678);
+ ok_bool_true(Ret, "KeInsertQueueDpc returned");
+ ++ExpectedDpcCount;
+ ok_dpccount();
+ }
+
+ /* insert into queue at high irql
+ * -> should only run when lowered to APC_LEVEL,
+ * inserting a second time should fail
+ */
+ KeRaiseIrql(APC_LEVEL, &Irql);
+ for (i = 0; i < 5; ++i)
+ {
+ KeRaiseIrql(DISPATCH_LEVEL, &Irql2);
+ ok_dpccount();
+ Ret = KeInsertQueueDpc(&Dpc, (PVOID)0xabc123, (PVOID)0x5678);
+ ok_bool_true(Ret, "KeInsertQueueDpc returned");
+ Ret = KeInsertQueueDpc(&Dpc, (PVOID)0xdef, (PVOID)0x123);
+ ok_bool_false(Ret, "KeInsertQueueDpc returned");
+ ok_dpccount();
+ KeRaiseIrql(HIGH_LEVEL, &Irql3);
+ ok_dpccount();
+ KeLowerIrql(Irql3);
+ ok_dpccount();
+ KeLowerIrql(Irql2);
+ ++ExpectedDpcCount;
+ ok_dpccount();
+ }
+ KeLowerIrql(Irql);
+
+ /* now test removing from the queue */
+ KeRaiseIrql(APC_LEVEL, &Irql);
+ for (i = 0; i < 5; ++i)
+ {
+ KeRaiseIrql(DISPATCH_LEVEL, &Irql2);
+ ok_dpccount();
+ Ret = KeRemoveQueueDpc(&Dpc);
+ ok_bool_false(Ret, "KeRemoveQueueDpc returned");
+ Ret = KeInsertQueueDpc(&Dpc, (PVOID)0xabc123, (PVOID)0x5678);
+ ok_bool_true(Ret, "KeInsertQueueDpc returned");
+ ok_dpccount();
+ KeRaiseIrql(HIGH_LEVEL, &Irql3);
+ ok_dpccount();
+ KeLowerIrql(Irql3);
+ ok_dpccount();
+ Ret = KeRemoveQueueDpc(&Dpc);
+ ok_bool_true(Ret, "KeRemoveQueueDpc returned");
+ KeLowerIrql(Irql2);
+ ok_dpccount();
+ }
+ KeLowerIrql(Irql);
+
+ /* parameter checks */
+ Status = STATUS_SUCCESS;
+ _SEH2_TRY {
+ KeInitializeDpc(&Dpc, NULL, NULL);
+ } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
+ Status = _SEH2_GetExceptionCode();
+ } _SEH2_END;
+ ok_eq_hex(Status, STATUS_SUCCESS);
+
+ if (!skip(Status == STATUS_SUCCESS, "KeInitializeDpc failed\n"))
+ {
+ KeRaiseIrql(HIGH_LEVEL, &Irql);
+ Ret = KeInsertQueueDpc(&Dpc, NULL, NULL);
+ ok_bool_true(Ret, "KeInsertQueueDpc returned");
+ Ret = KeRemoveQueueDpc(&Dpc);
+ ok_bool_true(Ret, "KeRemoveQueueDpc returned");
+ KeLowerIrql(Irql);
+ }
+
+ Status = STATUS_SUCCESS;
+ _SEH2_TRY {
+ KeInitializeDpc(NULL, NULL, NULL);
+ } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
+ Status = _SEH2_GetExceptionCode();
+ } _SEH2_END;
+ ok_eq_hex(Status, STATUS_ACCESS_VIOLATION);
+
+ /* These result in IRQL_NOT_LESS_OR_EQUAL on 2k3 -- IRQLs 0x1f and 0xff (?)
+ Ret = KeInsertQueueDpc(NULL, NULL, NULL);
+ Ret = KeRemoveQueueDpc(NULL);*/
+
+ ok_dpccount();
+ ok_irql(PASSIVE_LEVEL);
+ trace("Final Dpc count: %ld, expected %ld\n", DpcCount, ExpectedDpcCount);
+ }
--- /dev/null
+ /*
+ * PROJECT: ReactOS kernel-mode tests
+ * LICENSE: GPLv2+ - See COPYING in the top level directory
+ * PURPOSE: Kernel-Mode Test Suite Event test
+ * PROGRAMMER: Thomas Faber <thfabba@gmx.de>
+ */
+
+ #include <kmt_test.h>
+
+ /* TODO: why does GCC have 3 tests less than MSVC?! */
+
+ #define CheckEvent(Event, ExpectedType, State, ExpectedWaitNext, \
+ Irql, ThreadList, ThreadCount) do \
+ { \
+ INT TheIndex; \
+ PLIST_ENTRY TheEntry; \
+ PKTHREAD TheThread; \
+ ok_eq_uint((Event)->Header.Type, ExpectedType); \
+ ok_eq_uint((Event)->Header.Hand, sizeof *(Event) / sizeof(ULONG)); \
+ ok_eq_hex((Event)->Header.Lock & 0xFF00FF00L, 0x55005500L); \
+ ok_eq_long((Event)->Header.SignalState, State); \
+ TheEntry = (Event)->Header.WaitListHead.Flink; \
+ for (TheIndex = 0; TheIndex < (ThreadCount); ++TheIndex) \
+ { \
+ TheThread = CONTAINING_RECORD(TheEntry, KTHREAD, \
+ WaitBlock[0].WaitListEntry); \
+ ok_eq_pointer(TheThread, (ThreadList)[TheIndex]); \
+ ok_eq_pointer(TheEntry->Flink->Blink, TheEntry); \
+ TheEntry = TheEntry->Flink; \
+ } \
+ ok_eq_pointer(TheEntry, &(Event)->Header.WaitListHead); \
+ ok_eq_pointer(TheEntry->Flink->Blink, TheEntry); \
+ ok_eq_long(KeReadStateEvent(Event), State); \
+ ok_eq_bool(Thread->WaitNext, ExpectedWaitNext); \
+ ok_irql(Irql); \
+ } while (0)
+
+ static
+ VOID
+ TestEventFunctional(
+ IN PKEVENT Event,
+ IN EVENT_TYPE Type,
+ IN KIRQL OriginalIrql)
+ {
+ LONG State;
+ PKTHREAD Thread = KeGetCurrentThread();
+
+ memset(Event, 0x55, sizeof *Event);
+ KeInitializeEvent(Event, Type, FALSE);
+ CheckEvent(Event, Type, 0L, FALSE, OriginalIrql, (PVOID *)NULL, 0);
+
+ memset(Event, 0x55, sizeof *Event);
+ KeInitializeEvent(Event, Type, TRUE);
+ CheckEvent(Event, Type, 1L, FALSE, OriginalIrql, (PVOID *)NULL, 0);
+
+ Event->Header.SignalState = 0x12345678L;
+ CheckEvent(Event, Type, 0x12345678L, FALSE, OriginalIrql, (PVOID *)NULL, 0);
+
+ State = KePulseEvent(Event, 0, FALSE);
+ CheckEvent(Event, Type, 0L, FALSE, OriginalIrql, (PVOID *)NULL, 0);
+ ok_eq_long(State, 0x12345678L);
+
+ Event->Header.SignalState = 0x12345678L;
+ KeClearEvent(Event);
+ CheckEvent(Event, Type, 0L, FALSE, OriginalIrql, (PVOID *)NULL, 0);
+
+ State = KeSetEvent(Event, 0, FALSE);
+ CheckEvent(Event, Type, 1L, FALSE, OriginalIrql, (PVOID *)NULL, 0);
+ ok_eq_long(State, 0L);
+
+ State = KeResetEvent(Event);
+ CheckEvent(Event, Type, 0L, FALSE, OriginalIrql, (PVOID *)NULL, 0);
+ ok_eq_long(State, 1L);
+
+ Event->Header.SignalState = 0x23456789L;
+ State = KeSetEvent(Event, 0, FALSE);
+ CheckEvent(Event, Type, 1L, FALSE, OriginalIrql, (PVOID *)NULL, 0);
+ ok_eq_long(State, 0x23456789L);
+
+ Event->Header.SignalState = 0x3456789AL;
+ State = KeResetEvent(Event);
+ CheckEvent(Event, Type, 0L, FALSE, OriginalIrql, (PVOID *)NULL, 0);
+ ok_eq_long(State, 0x3456789AL);
+
+ /* Irql is raised to DISPATCH_LEVEL here, which kills checked build,
+ * a spinlock is acquired and never released, which kills MP build */
+ if ((OriginalIrql <= DISPATCH_LEVEL || !KmtIsCheckedBuild) &&
+ !KmtIsMultiProcessorBuild)
+ {
+ Event->Header.SignalState = 0x456789ABL;
+ State = KeSetEvent(Event, 0, TRUE);
+ CheckEvent(Event, Type, 1L, TRUE, DISPATCH_LEVEL, (PVOID *)NULL, 0);
+ ok_eq_long(State, 0x456789ABL);
+ ok_eq_uint(Thread->WaitIrql, OriginalIrql);
+ /* repair the "damage" */
+ Thread->WaitNext = FALSE;
+ KmtSetIrql(OriginalIrql);
+
+ Event->Header.SignalState = 0x56789ABCL;
+ State = KePulseEvent(Event, 0, TRUE);
+ CheckEvent(Event, Type, 0L, TRUE, DISPATCH_LEVEL, (PVOID *)NULL, 0);
+ ok_eq_long(State, 0x56789ABCL);
+ ok_eq_uint(Thread->WaitIrql, OriginalIrql);
+ /* repair the "damage" */
+ Thread->WaitNext = FALSE;
+ KmtSetIrql(OriginalIrql);
+ }
+
+ ok_irql(OriginalIrql);
+ KmtSetIrql(OriginalIrql);
+ }
+
+ typedef struct
+ {
+ HANDLE Handle;
+ PKTHREAD Thread;
+ PKEVENT Event;
+ volatile BOOLEAN Signal;
+ } THREAD_DATA, *PTHREAD_DATA;
+
+ static
+ VOID
+ NTAPI
+ WaitForEventThread(
+ IN OUT PVOID Context)
+ {
+ NTSTATUS Status;
+ PTHREAD_DATA ThreadData = Context;
+
+ ok_irql(PASSIVE_LEVEL);
+ ThreadData->Signal = TRUE;
+ Status = KeWaitForSingleObject(ThreadData->Event, Executive, KernelMode, FALSE, NULL);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ ok_irql(PASSIVE_LEVEL);
+ }
+
+ typedef LONG (NTAPI *PSET_EVENT_FUNCTION)(PRKEVENT, KPRIORITY, BOOLEAN);
+
+ static
+ VOID
+ TestEventConcurrent(
+ IN PKEVENT Event,
+ IN EVENT_TYPE Type,
+ IN KIRQL OriginalIrql,
+ PSET_EVENT_FUNCTION SetEvent,
+ KPRIORITY PriorityIncrement,
+ LONG ExpectedState,
+ BOOLEAN SatisfiesAll)
+ {
+ NTSTATUS Status;
+ THREAD_DATA Threads[5];
+ const INT ThreadCount = sizeof Threads / sizeof Threads[0];
+ KPRIORITY Priority;
+ LARGE_INTEGER LongTimeout, ShortTimeout;
+ INT i;
+ KWAIT_BLOCK WaitBlock[MAXIMUM_WAIT_OBJECTS];
+ PVOID ThreadObjects[MAXIMUM_WAIT_OBJECTS];
+ LONG State;
+ PKTHREAD Thread = KeGetCurrentThread();
+
+ LongTimeout.QuadPart = -100 * MILLISECOND;
+ ShortTimeout.QuadPart = -1 * MILLISECOND;
+
+ KeInitializeEvent(Event, Type, FALSE);
+
+ for (i = 0; i < ThreadCount; ++i)
+ {
+ Threads[i].Event = Event;
+ Threads[i].Signal = FALSE;
+ Status = PsCreateSystemThread(&Threads[i].Handle, GENERIC_ALL, NULL, NULL, NULL, WaitForEventThread, &Threads[i]);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ Status = ObReferenceObjectByHandle(Threads[i].Handle, SYNCHRONIZE, PsThreadType, KernelMode, (PVOID *)&Threads[i].Thread, NULL);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ ThreadObjects[i] = Threads[i].Thread;
+ Priority = KeQueryPriorityThread(Threads[i].Thread);
+ ok_eq_long(Priority, 8L);
+ while (!Threads[i].Signal)
+ {
+ Status = KeDelayExecutionThread(KernelMode, FALSE, &ShortTimeout);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ }
+ CheckEvent(Event, Type, 0L, FALSE, OriginalIrql, ThreadObjects, i + 1);
+ }
+
+ /* the threads shouldn't wake up on their own */
+ Status = KeDelayExecutionThread(KernelMode, FALSE, &ShortTimeout);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+
+ for (i = 0; i < ThreadCount; ++i)
+ {
+ CheckEvent(Event, Type, 0L, FALSE, OriginalIrql, ThreadObjects + i, ThreadCount - i);
+ State = SetEvent(Event, PriorityIncrement + i, FALSE);
+
+ ok_eq_long(State, 0L);
+ CheckEvent(Event, Type, ExpectedState, FALSE, OriginalIrql, ThreadObjects + i + 1, SatisfiesAll ? 0 : ThreadCount - i - 1);
+ Status = KeWaitForMultipleObjects(ThreadCount, ThreadObjects, SatisfiesAll ? WaitAll : WaitAny, Executive, KernelMode, FALSE, &LongTimeout, WaitBlock);
+ ok_eq_hex(Status, STATUS_WAIT_0 + i);
+ if (SatisfiesAll)
+ {
+ for (; i < ThreadCount; ++i)
+ {
+ Priority = KeQueryPriorityThread(Threads[i].Thread);
+ ok_eq_long(Priority, max(min(8L + PriorityIncrement, 15L), 8L));
+ }
+ break;
+ }
+ Priority = KeQueryPriorityThread(Threads[i].Thread);
+ ok_eq_long(Priority, max(min(8L + PriorityIncrement + i, 15L), 8L));
+ /* replace the thread with the current thread - which will never signal */
+ if (!skip((Status & 0x3F) < ThreadCount, "Index out of bounds"))
+ ThreadObjects[Status & 0x3F] = Thread;
+ Status = KeWaitForMultipleObjects(ThreadCount, ThreadObjects, WaitAny, Executive, KernelMode, FALSE, &ShortTimeout, WaitBlock);
+ ok_eq_hex(Status, STATUS_TIMEOUT);
+ }
+
+ for (i = 0; i < ThreadCount; ++i)
+ {
+ ObDereferenceObject(Threads[i].Thread);
+ Status = ZwClose(Threads[i].Handle);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ }
+ }
+
+ START_TEST(KeEvent)
+ {
+ KEVENT Event;
+ KIRQL Irql;
+ KIRQL Irqls[] = { PASSIVE_LEVEL, APC_LEVEL, DISPATCH_LEVEL, HIGH_LEVEL };
+ INT i;
+ KPRIORITY PriorityIncrement;
+
+ for (i = 0; i < sizeof Irqls / sizeof Irqls[0]; ++i)
+ {
+ /* DRIVER_IRQL_NOT_LESS_OR_EQUAL (TODO: on MP only?) */
+ if (Irqls[i] > DISPATCH_LEVEL && KmtIsCheckedBuild)
+ return;
+ KeRaiseIrql(Irqls[i], &Irql);
+ TestEventFunctional(&Event, NotificationEvent, Irqls[i]);
+ TestEventFunctional(&Event, SynchronizationEvent, Irqls[i]);
+ KeLowerIrql(Irql);
+ }
+
+ for (i = 0; i < sizeof Irqls / sizeof Irqls[0]; ++i)
+ {
+ /* creating threads above DISPATCH_LEVEL... nope */
+ if (Irqls[i] >= DISPATCH_LEVEL && KmtIsCheckedBuild)
+ continue;
+ KeRaiseIrql(Irqls[i], &Irql);
+ trace("IRQL: %u\n", Irqls[i]);
+ for (PriorityIncrement = -1; PriorityIncrement <= 8; ++PriorityIncrement)
+ {
+ trace("PriorityIncrement: %ld\n", PriorityIncrement);
+ trace("-> Checking KeSetEvent, NotificationEvent\n");
+ TestEventConcurrent(&Event, NotificationEvent, Irqls[i], KeSetEvent, PriorityIncrement, 1, TRUE);
+ trace("-> Checking KeSetEvent, SynchronizationEvent\n");
+ TestEventConcurrent(&Event, SynchronizationEvent, Irqls[i], KeSetEvent, PriorityIncrement, 0, FALSE);
+ trace("-> Checking KePulseEvent, NotificationEvent\n");
+ TestEventConcurrent(&Event, NotificationEvent, Irqls[i], KePulseEvent, PriorityIncrement, 0, TRUE);
+ trace("-> Checking KePulseEvent, SynchronizationEvent\n");
+ TestEventConcurrent(&Event, SynchronizationEvent, Irqls[i], KePulseEvent, PriorityIncrement, 0, FALSE);
+ }
+ KeLowerIrql(Irql);
+ }
+
+ ok_irql(PASSIVE_LEVEL);
+ KmtSetIrql(PASSIVE_LEVEL);
+ }
--- /dev/null
+ /*
+ * PROJECT: ReactOS kernel-mode tests
+ * LICENSE: GPLv2+ - See COPYING in the top level directory
+ * PURPOSE: Kernel-Mode Test Suite Guarded Mutex test
+ * PROGRAMMER: Thomas Faber <thfabba@gmx.de>
+ */
+
+ #include <kmt_test.h>
+
+ #define NDEBUG
+ #include <debug.h>
+
+ #define CheckMutex(Mutex, ExpectedCount, ExpectedOwner, ExpectedContention, \
+ ExpectedKernelApcDisable, ExpectedSpecialApcDisable, \
+ KernelApcsDisabled, SpecialApcsDisabled, AllApcsDisabled, \
+ ExpectedIrql) do \
+ { \
+ ok_eq_long((Mutex)->Count, ExpectedCount); \
+ ok_eq_pointer((Mutex)->Owner, ExpectedOwner); \
+ ok_eq_ulong((Mutex)->Contention, ExpectedContention); \
+ ok_eq_int((Mutex)->KernelApcDisable, ExpectedKernelApcDisable); \
+ if (KmtIsCheckedBuild) \
+ ok_eq_int((Mutex)->SpecialApcDisable, ExpectedSpecialApcDisable); \
+ else \
+ ok_eq_int((Mutex)->SpecialApcDisable, 0x5555); \
+ ok_eq_bool(KeAreApcsDisabled(), KernelApcsDisabled || SpecialApcsDisabled); \
+ ok_eq_int(Thread->KernelApcDisable, KernelApcsDisabled); \
+ ok_eq_bool(KeAreAllApcsDisabled(), AllApcsDisabled); \
+ ok_eq_int(Thread->SpecialApcDisable, SpecialApcsDisabled); \
+ ok_irql(ExpectedIrql); \
+ } while (0)
+
+ static
+ VOID
+ TestGuardedMutex(
+ PKGUARDED_MUTEX Mutex,
+ SHORT KernelApcsDisabled,
+ SHORT SpecialApcsDisabled,
+ SHORT AllApcsDisabled,
+ KIRQL OriginalIrql)
+ {
+ PKTHREAD Thread = KeGetCurrentThread();
+
+ ok_irql(OriginalIrql);
+ CheckMutex(Mutex, 1L, NULL, 0LU, 0x5555, 0x5555, KernelApcsDisabled, SpecialApcsDisabled, AllApcsDisabled, OriginalIrql);
+
+ /* these ASSERT */
+ if (!KmtIsCheckedBuild || OriginalIrql <= APC_LEVEL)
+ {
+ /* acquire/release normally */
+ KeAcquireGuardedMutex(Mutex);
+ CheckMutex(Mutex, 0L, Thread, 0LU, 0x5555, SpecialApcsDisabled - 1, KernelApcsDisabled, SpecialApcsDisabled - 1, TRUE, OriginalIrql);
+ ok_bool_false(KeTryToAcquireGuardedMutex(Mutex), "KeTryToAcquireGuardedMutex returned");
+ CheckMutex(Mutex, 0L, Thread, 0LU, 0x5555, SpecialApcsDisabled - 1, KernelApcsDisabled, SpecialApcsDisabled - 1, TRUE, OriginalIrql);
+ KeReleaseGuardedMutex(Mutex);
+ CheckMutex(Mutex, 1L, NULL, 0LU, 0x5555, SpecialApcsDisabled - 1, KernelApcsDisabled, SpecialApcsDisabled, AllApcsDisabled, OriginalIrql);
+
+ /* try to acquire */
+ ok_bool_true(KeTryToAcquireGuardedMutex(Mutex), "KeTryToAcquireGuardedMutex returned");
+ CheckMutex(Mutex, 0L, Thread, 0LU, 0x5555, SpecialApcsDisabled - 1, KernelApcsDisabled, SpecialApcsDisabled - 1, TRUE, OriginalIrql);
+ KeReleaseGuardedMutex(Mutex);
+ CheckMutex(Mutex, 1L, NULL, 0LU, 0x5555, SpecialApcsDisabled - 1, KernelApcsDisabled, SpecialApcsDisabled, AllApcsDisabled, OriginalIrql);
+ }
+ else
+ /* Make the following test happy */
+ Mutex->SpecialApcDisable = SpecialApcsDisabled - 1;
+
+ /* ASSERT */
+ if (!KmtIsCheckedBuild || OriginalIrql == APC_LEVEL || SpecialApcsDisabled < 0)
+ {
+ /* acquire/release unsafe */
+ KeAcquireGuardedMutexUnsafe(Mutex);
+ CheckMutex(Mutex, 0L, Thread, 0LU, 0x5555, SpecialApcsDisabled - 1, KernelApcsDisabled, SpecialApcsDisabled, AllApcsDisabled, OriginalIrql);
+ KeReleaseGuardedMutexUnsafe(Mutex);
+ CheckMutex(Mutex, 1L, NULL, 0LU, 0x5555, SpecialApcsDisabled - 1, KernelApcsDisabled, SpecialApcsDisabled, AllApcsDisabled, OriginalIrql);
+ }
+
+ /* Bugchecks >= DISPATCH_LEVEL */
+ if (!KmtIsCheckedBuild)
+ {
+ /* mismatched acquire/release */
+ KeAcquireGuardedMutex(Mutex);
+ CheckMutex(Mutex, 0L, Thread, 0LU, 0x5555, SpecialApcsDisabled - 1, KernelApcsDisabled, SpecialApcsDisabled - 1, TRUE, OriginalIrql);
+ KeReleaseGuardedMutexUnsafe(Mutex);
+ CheckMutex(Mutex, 1L, NULL, 0LU, 0x5555, SpecialApcsDisabled - 1, KernelApcsDisabled, SpecialApcsDisabled - 1, TRUE, OriginalIrql);
+ KeLeaveGuardedRegion();
+ CheckMutex(Mutex, 1L, NULL, 0LU, 0x5555, SpecialApcsDisabled - 1, KernelApcsDisabled, SpecialApcsDisabled, AllApcsDisabled, OriginalIrql);
+
+ KeAcquireGuardedMutexUnsafe(Mutex);
+ CheckMutex(Mutex, 0L, Thread, 0LU, 0x5555, SpecialApcsDisabled - 1, KernelApcsDisabled, SpecialApcsDisabled, AllApcsDisabled, OriginalIrql);
+ KeReleaseGuardedMutex(Mutex);
+ CheckMutex(Mutex, 1L, NULL, 0LU, 0x5555, SpecialApcsDisabled - 1, KernelApcsDisabled, SpecialApcsDisabled + 1, OriginalIrql >= APC_LEVEL || SpecialApcsDisabled != -1, OriginalIrql);
+ KeEnterGuardedRegion();
+ CheckMutex(Mutex, 1L, NULL, 0LU, 0x5555, SpecialApcsDisabled - 1, KernelApcsDisabled, SpecialApcsDisabled, AllApcsDisabled, OriginalIrql);
+
+ /* release without acquire */
+ KeReleaseGuardedMutexUnsafe(Mutex);
+ CheckMutex(Mutex, 0L, NULL, 0LU, 0x5555, SpecialApcsDisabled - 1, KernelApcsDisabled, SpecialApcsDisabled, AllApcsDisabled, OriginalIrql);
+ KeReleaseGuardedMutex(Mutex);
+ CheckMutex(Mutex, 1L, NULL, 0LU, 0x5555, SpecialApcsDisabled, KernelApcsDisabled, SpecialApcsDisabled + 1, OriginalIrql >= APC_LEVEL || SpecialApcsDisabled != -1, OriginalIrql);
+ KeReleaseGuardedMutex(Mutex);
+ /* TODO: here we see that Mutex->Count isn't actually just a count. Test the bits correctly! */
+ CheckMutex(Mutex, 0L, NULL, 0LU, 0x5555, SpecialApcsDisabled, KernelApcsDisabled, SpecialApcsDisabled + 2, OriginalIrql >= APC_LEVEL || SpecialApcsDisabled != -2, OriginalIrql);
+ KeReleaseGuardedMutex(Mutex);
+ CheckMutex(Mutex, 1L, NULL, 0LU, 0x5555, SpecialApcsDisabled, KernelApcsDisabled, SpecialApcsDisabled + 3, OriginalIrql >= APC_LEVEL || SpecialApcsDisabled != -3, OriginalIrql);
+ Thread->SpecialApcDisable -= 3;
+ }
+
+ /* make sure we survive this in case of error */
+ ok_eq_long(Mutex->Count, 1L);
+ Mutex->Count = 1;
+ ok_eq_int(Thread->KernelApcDisable, KernelApcsDisabled);
+ Thread->KernelApcDisable = KernelApcsDisabled;
+ ok_eq_int(Thread->SpecialApcDisable, SpecialApcsDisabled);
+ Thread->SpecialApcDisable = SpecialApcsDisabled;
+ ok_irql(OriginalIrql);
+ }
+
+ typedef VOID (FASTCALL *PMUTEX_FUNCTION)(PKGUARDED_MUTEX);
+ typedef BOOLEAN (FASTCALL *PMUTEX_TRY_FUNCTION)(PKGUARDED_MUTEX);
+
+ typedef struct
+ {
+ HANDLE Handle;
+ PKTHREAD Thread;
+ KIRQL Irql;
+ PKGUARDED_MUTEX Mutex;
+ PMUTEX_FUNCTION Acquire;
+ PMUTEX_TRY_FUNCTION TryAcquire;
+ PMUTEX_FUNCTION Release;
+ BOOLEAN Try;
+ BOOLEAN RetExpected;
+ KEVENT InEvent;
+ KEVENT OutEvent;
+ } THREAD_DATA, *PTHREAD_DATA;
+
+ static
+ VOID
+ NTAPI
+ AcquireMutexThread(
+ PVOID Parameter)
+ {
+ PTHREAD_DATA ThreadData = Parameter;
+ KIRQL Irql;
+ BOOLEAN Ret = FALSE;
+ NTSTATUS Status;
+
+ DPRINT("Thread starting\n");
+ KeRaiseIrql(ThreadData->Irql, &Irql);
+
+ if (ThreadData->Try)
+ {
+ Ret = ThreadData->TryAcquire(ThreadData->Mutex);
+ ok_eq_bool(Ret, ThreadData->RetExpected);
+ }
+ else
+ ThreadData->Acquire(ThreadData->Mutex);
+
+ ok_bool_false(KeSetEvent(&ThreadData->OutEvent, 0, TRUE), "KeSetEvent returned");
+ DPRINT("Thread now waiting\n");
+ Status = KeWaitForSingleObject(&ThreadData->InEvent, Executive, KernelMode, FALSE, NULL);
+ DPRINT("Thread done waiting\n");
+ ok_eq_hex(Status, STATUS_SUCCESS);
+
+ if (!ThreadData->Try || Ret)
+ ThreadData->Release(ThreadData->Mutex);
+
+ KeLowerIrql(Irql);
+ DPRINT("Thread exiting\n");
+ }
+
+ static
+ VOID
+ InitThreadData(
+ PTHREAD_DATA ThreadData,
+ PKGUARDED_MUTEX Mutex,
+ PMUTEX_FUNCTION Acquire,
+ PMUTEX_TRY_FUNCTION TryAcquire,
+ PMUTEX_FUNCTION Release)
+ {
+ ThreadData->Mutex = Mutex;
+ KeInitializeEvent(&ThreadData->InEvent, NotificationEvent, FALSE);
+ KeInitializeEvent(&ThreadData->OutEvent, NotificationEvent, FALSE);
+ ThreadData->Acquire = Acquire;
+ ThreadData->TryAcquire = TryAcquire;
+ ThreadData->Release = Release;
+ }
+
+ static
+ NTSTATUS
+ StartThread(
+ PTHREAD_DATA ThreadData,
+ PLARGE_INTEGER Timeout,
+ KIRQL Irql,
+ BOOLEAN Try,
+ BOOLEAN RetExpected)
+ {
+ NTSTATUS Status = STATUS_SUCCESS;
+ OBJECT_ATTRIBUTES Attributes;
+
+ ThreadData->Try = Try;
+ ThreadData->Irql = Irql;
+ ThreadData->RetExpected = RetExpected;
+ InitializeObjectAttributes(&Attributes, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
+ Status = PsCreateSystemThread(&ThreadData->Handle, GENERIC_ALL, &Attributes, NULL, NULL, AcquireMutexThread, ThreadData);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ Status = ObReferenceObjectByHandle(ThreadData->Handle, SYNCHRONIZE, PsThreadType, KernelMode, (PVOID *)&ThreadData->Thread, NULL);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+
+ return KeWaitForSingleObject(&ThreadData->OutEvent, Executive, KernelMode, FALSE, Timeout);
+ }
+
+ static
+ VOID
+ FinishThread(
+ PTHREAD_DATA ThreadData)
+ {
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ KeSetEvent(&ThreadData->InEvent, 0, TRUE);
+ Status = KeWaitForSingleObject(ThreadData->Thread, Executive, KernelMode, FALSE, NULL);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+
+ ObDereferenceObject(ThreadData->Thread);
+ Status = ZwClose(ThreadData->Handle);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ KeClearEvent(&ThreadData->InEvent);
+ KeClearEvent(&ThreadData->OutEvent);
+ }
+
+ static
+ VOID
+ TestGuardedMutexConcurrent(
+ PKGUARDED_MUTEX Mutex)
+ {
+ NTSTATUS Status;
+ THREAD_DATA ThreadData;
+ THREAD_DATA ThreadData2;
+ THREAD_DATA ThreadDataUnsafe;
+ THREAD_DATA ThreadDataTry;
+ PKTHREAD Thread = KeGetCurrentThread();
+ LARGE_INTEGER Timeout;
+ Timeout.QuadPart = -10 * 1000 * 10; /* 10 ms */
+
+ InitThreadData(&ThreadData, Mutex, KeAcquireGuardedMutex, NULL, KeReleaseGuardedMutex);
+ InitThreadData(&ThreadData2, Mutex, KeAcquireGuardedMutex, NULL, KeReleaseGuardedMutex);
+ InitThreadData(&ThreadDataUnsafe, Mutex, KeAcquireGuardedMutexUnsafe, NULL, KeReleaseGuardedMutexUnsafe);
+ InitThreadData(&ThreadDataTry, Mutex, NULL, KeTryToAcquireGuardedMutex, KeReleaseGuardedMutex);
+
+ /* have a thread acquire the mutex */
+ Status = StartThread(&ThreadData, NULL, PASSIVE_LEVEL, FALSE, FALSE);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ CheckMutex(Mutex, 0L, ThreadData.Thread, 0LU, 0x5555, -1, 0, 0, FALSE, PASSIVE_LEVEL);
+ /* have a second thread try to acquire it -- should fail */
+ Status = StartThread(&ThreadDataTry, NULL, PASSIVE_LEVEL, TRUE, FALSE);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ CheckMutex(Mutex, 0L, ThreadData.Thread, 0LU, 0x5555, -1, 0, 0, FALSE, PASSIVE_LEVEL);
+ FinishThread(&ThreadDataTry);
+
+ /* have another thread acquire it -- should block */
+ Status = StartThread(&ThreadData2, &Timeout, APC_LEVEL, FALSE, FALSE);
+ ok_eq_hex(Status, STATUS_TIMEOUT);
+ CheckMutex(Mutex, 4L, ThreadData.Thread, 1LU, 0x5555, -1, 0, 0, FALSE, PASSIVE_LEVEL);
+
+ /* finish the first thread -- now the second should become available */
+ FinishThread(&ThreadData);
+ Status = KeWaitForSingleObject(&ThreadData2.OutEvent, Executive, KernelMode, FALSE, NULL);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ CheckMutex(Mutex, 0L, ThreadData2.Thread, 1LU, 0x5555, -1, 0, 0, FALSE, PASSIVE_LEVEL);
+
+ /* block two more threads */
+ Status = StartThread(&ThreadDataUnsafe, &Timeout, APC_LEVEL, FALSE, FALSE);
+ ok_eq_hex(Status, STATUS_TIMEOUT);
+ CheckMutex(Mutex, 4L, ThreadData2.Thread, 2LU, 0x5555, -1, 0, 0, FALSE, PASSIVE_LEVEL);
+
+ Status = StartThread(&ThreadData, &Timeout, PASSIVE_LEVEL, FALSE, FALSE);
+ ok_eq_hex(Status, STATUS_TIMEOUT);
+ CheckMutex(Mutex, 8L, ThreadData2.Thread, 3LU, 0x5555, -1, 0, 0, FALSE, PASSIVE_LEVEL);
+
+ /* finish 1 */
+ FinishThread(&ThreadData2);
+ Status = KeWaitForSingleObject(&ThreadDataUnsafe.OutEvent, Executive, KernelMode, FALSE, NULL);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ CheckMutex(Mutex, 4L, ThreadDataUnsafe.Thread, 3LU, 0x5555, -1, 0, 0, FALSE, PASSIVE_LEVEL);
+
+ /* finish 2 */
+ FinishThread(&ThreadDataUnsafe);
+ Status = KeWaitForSingleObject(&ThreadData.OutEvent, Executive, KernelMode, FALSE, NULL);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ CheckMutex(Mutex, 0L, ThreadData.Thread, 3LU, 0x5555, -1, 0, 0, FALSE, PASSIVE_LEVEL);
+
+ /* finish 3 */
+ FinishThread(&ThreadData);
+
+ CheckMutex(Mutex, 1L, NULL, 3LU, 0x5555, -1, 0, 0, FALSE, PASSIVE_LEVEL);
+ }
+
+ START_TEST(KeGuardedMutex)
+ {
+ KGUARDED_MUTEX Mutex;
+ KIRQL OldIrql;
+ PKTHREAD Thread = KeGetCurrentThread();
+ struct {
+ KIRQL Irql;
+ SHORT KernelApcsDisabled;
+ SHORT SpecialApcsDisabled;
+ BOOLEAN AllApcsDisabled;
+ } TestIterations[] =
+ {
+ { PASSIVE_LEVEL, 0, 0, FALSE },
+ { PASSIVE_LEVEL, -1, 0, FALSE },
+ { PASSIVE_LEVEL, -3, 0, FALSE },
+ { PASSIVE_LEVEL, 0, -1, TRUE },
+ { PASSIVE_LEVEL, -1, -1, TRUE },
+ { PASSIVE_LEVEL, -3, -2, TRUE },
+ // 6
+ { APC_LEVEL, 0, 0, TRUE },
+ { APC_LEVEL, -1, 0, TRUE },
+ { APC_LEVEL, -3, 0, TRUE },
+ { APC_LEVEL, 0, -1, TRUE },
+ { APC_LEVEL, -1, -1, TRUE },
+ { APC_LEVEL, -3, -2, TRUE },
+ // 12
+ { DISPATCH_LEVEL, 0, 0, TRUE },
+ { DISPATCH_LEVEL, -1, 0, TRUE },
+ { DISPATCH_LEVEL, -3, 0, TRUE },
+ { DISPATCH_LEVEL, 0, -1, TRUE },
+ { DISPATCH_LEVEL, -1, -1, TRUE },
+ { DISPATCH_LEVEL, -3, -2, TRUE },
+ // 18
+ { HIGH_LEVEL, 0, 0, TRUE },
+ { HIGH_LEVEL, -1, 0, TRUE },
+ { HIGH_LEVEL, -3, 0, TRUE },
+ { HIGH_LEVEL, 0, -1, TRUE },
+ { HIGH_LEVEL, -1, -1, TRUE },
+ { HIGH_LEVEL, -3, -2, TRUE },
+ };
+ int i;
+
+ for (i = 0; i < sizeof TestIterations / sizeof TestIterations[0]; ++i)
+ {
+ trace("Run %d\n", i);
+ KeRaiseIrql(TestIterations[i].Irql, &OldIrql);
+ Thread->KernelApcDisable = TestIterations[i].KernelApcsDisabled;
+ Thread->SpecialApcDisable = TestIterations[i].SpecialApcsDisabled;
+
+ RtlFillMemory(&Mutex, sizeof Mutex, 0x55);
+ KeInitializeGuardedMutex(&Mutex);
+ CheckMutex(&Mutex, 1L, NULL, 0LU, 0x5555, 0x5555, TestIterations[i].KernelApcsDisabled, TestIterations[i].SpecialApcsDisabled, TestIterations[i].AllApcsDisabled, TestIterations[i].Irql);
+ TestGuardedMutex(&Mutex, TestIterations[i].KernelApcsDisabled, TestIterations[i].SpecialApcsDisabled, TestIterations[i].AllApcsDisabled, TestIterations[i].Irql);
+
+ Thread->SpecialApcDisable = 0;
+ Thread->KernelApcDisable = 0;
+ KeLowerIrql(OldIrql);
+ }
+
+ trace("Concurrent test\n");
+ RtlFillMemory(&Mutex, sizeof Mutex, 0x55);
+ KeInitializeGuardedMutex(&Mutex);
+ TestGuardedMutexConcurrent(&Mutex);
+ }
--- /dev/null
+ /*
+ * PROJECT: ReactOS kernel-mode tests
+ * LICENSE: GPLv2+ - See COPYING in the top level directory
+ * PURPOSE: Kernel-Mode Test Suite Interrupt Request Level test
+ * PROGRAMMER: Thomas Faber <thfabba@gmx.de>
+ */
+
+ __declspec(dllimport) void __stdcall KeRaiseIrql(unsigned char, unsigned char *);
+ __declspec(dllimport) void __stdcall KeLowerIrql(unsigned char);
+
+ #include <kmt_test.h>
+
+ #define NDEBUG
+ #include <debug.h>
+
+ START_TEST(KeIrql)
+ {
+ KIRQL Irql, Irql2, PrevIrql, SynchIrql;
+
+ /* we should be called at PASSIVE_LEVEL */
+ ok_irql(PASSIVE_LEVEL);
+
+ PrevIrql = KeGetCurrentIrql();
+
+ /* SYNCH_LEVEL is different for UP/MP */
+ if (KmtIsMultiProcessorBuild)
+ SynchIrql = IPI_LEVEL - 2;
+ else
+ SynchIrql = DISPATCH_LEVEL;
+
+ /* some Irqls MUST work */
+ {
+ const KIRQL Irqls[] = { LOW_LEVEL, PASSIVE_LEVEL, APC_LEVEL, DISPATCH_LEVEL,
+ CMCI_LEVEL, CLOCK1_LEVEL, CLOCK2_LEVEL, CLOCK_LEVEL,
+ PROFILE_LEVEL, IPI_LEVEL, /*POWER_LEVEL,*/ SynchIrql, HIGH_LEVEL };
+ int i;
+ for (i = 0; i < sizeof Irqls / sizeof Irqls[0]; ++i)
+ {
+ KeRaiseIrql(Irqls[i], &Irql2);
+ ok_eq_uint(Irql2, PrevIrql);
+ ok_irql(Irqls[i]);
+ KeLowerIrql(Irql2);
+ ok_irql(PrevIrql);
+ }
+ }
+
+ /* raising/lowering to the current level should have no effect */
+ ok_irql(PASSIVE_LEVEL);
+ KeRaiseIrql(PASSIVE_LEVEL, &Irql);
+ ok_eq_uint(Irql, PASSIVE_LEVEL);
+ KeLowerIrql(PASSIVE_LEVEL);
+ ok_irql(PASSIVE_LEVEL);
+
+ /* try to raise to each Irql and back */
+ for (Irql = PASSIVE_LEVEL; Irql <= HIGH_LEVEL; ++Irql)
+ {
+ DPRINT("Raising to %u\n", Irql);
+ KeRaiseIrql(Irql, &Irql2);
+ ok_eq_uint(Irql2, PrevIrql);
+ KeLowerIrql(Irql2);
+ ok_irql(PrevIrql);
+ }
+
+ /* go through all Irqls in order, skip the ones that the system doesn't accept */
+ for (Irql = PASSIVE_LEVEL; Irql <= HIGH_LEVEL; ++Irql)
+ {
+ DPRINT("Raising to %u\n", Irql);
+ KeRaiseIrql(Irql, &Irql2);
+ ok_eq_uint(Irql2, PrevIrql);
+ Irql2 = KeGetCurrentIrql();
+ ok(Irql2 <= Irql, "New Irql is %u, expected <= requested value of %u\n", Irql2, Irql);
+ PrevIrql = Irql2;
+ }
+
+ ok_irql(HIGH_LEVEL);
+
+ /* now go back again, skipping the ones that don't work */
+ for (Irql = HIGH_LEVEL; Irql > PASSIVE_LEVEL;)
+ {
+ DPRINT("Lowering to %u\n", Irql - 1);
+ KeLowerIrql(Irql - 1);
+ Irql2 = KeGetCurrentIrql();
+ ok(Irql2 < Irql, "New Irql is %u, expected <= requested value of %u\n", Irql2, Irql - 1);
+ if (Irql2 < Irql)
+ Irql = Irql2;
+ else
+ --Irql;
+ }
+
+ DPRINT("Alive!\n");
+ /* on x86, you can raise to _any_ possible KIRQL value */
+ /* on x64, anything with more than the least significant 4 bits set bugchecked, last time I tried */
+ /* TODO: other platforms? */
+ #if defined _M_X86
+ for (Irql = PASSIVE_LEVEL; Irql <= (KIRQL)-1; ++Irql)
+ {
+ DPRINT("Raising to %u\n", Irql);
+ KeRaiseIrql(Irql, &Irql2);
+ ok_eq_uint(Irql2, PrevIrql);
+ KeLowerIrql(Irql2);
+ ok_irql(PrevIrql);
+ }
+ #endif /* defined _M_X86 */
+
+ /* test KeRaiseIrqlToDpcLevel */
+ ok_irql(PASSIVE_LEVEL);
+ Irql = KeRaiseIrqlToDpcLevel();
+ ok_irql(DISPATCH_LEVEL);
+ ok_eq_uint(Irql, PASSIVE_LEVEL);
+ Irql = KeRaiseIrqlToDpcLevel();
+ ok_irql(DISPATCH_LEVEL);
+ ok_eq_uint(Irql, DISPATCH_LEVEL);
+ KeLowerIrql(PASSIVE_LEVEL);
+
+ /* test KeRaiseIrqlToSynchLevel */
+ ok_irql(PASSIVE_LEVEL);
+ Irql = KeRaiseIrqlToSynchLevel();
+ ok_irql(SynchIrql);
+ ok_eq_uint(Irql, PASSIVE_LEVEL);
+ Irql = KeRaiseIrqlToSynchLevel();
+ ok_irql(SynchIrql);
+ ok_eq_uint(Irql, SynchIrql);
+ KeLowerIrql(PASSIVE_LEVEL);
+
+ /* these bugcheck on a checked build but run fine on free! */
+ if (!KmtIsCheckedBuild)
+ {
+ KeRaiseIrql(HIGH_LEVEL, &Irql);
+ KeRaiseIrql(APC_LEVEL, &Irql);
+ ok_irql(APC_LEVEL);
+ KeLowerIrql(HIGH_LEVEL);
+ ok_irql(HIGH_LEVEL);
+ KeLowerIrql(PASSIVE_LEVEL);
+ }
+
+ /* try the actual exports, not only the fastcall versions */
+ ok_irql(PASSIVE_LEVEL);
+ (KeRaiseIrql)(HIGH_LEVEL, &Irql);
+ ok_irql(HIGH_LEVEL);
+ ok_eq_uint(Irql, PASSIVE_LEVEL);
+ (KeLowerIrql)(Irql);
+ ok_irql(PASSIVE_LEVEL);
+
+ /* make sure we exit gracefully */
+ ok_irql(PASSIVE_LEVEL);
+ KeLowerIrql(PASSIVE_LEVEL);
+ }
/*
- * NTOSKRNL Executive Regressions KM-Test
- * ReactOS Kernel Mode Regression Testing framework
- *
- * Copyright 2006 Aleksey Bragin <aleksey@reactos.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; see the file COPYING.LIB.
- * If not, write to the Free Software Foundation,
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * PROJECT: ReactOS kernel-mode tests
+ * LICENSE: LGPLv2+ - See COPYING.LIB in the top level directory
+ * PURPOSE: Kernel-Mode Test Suite Executive Regressions KM-Test
+ * PROGRAMMER: Aleksey Bragin <aleksey@reactos.org>
*/
- /* INCLUDES *******************************************************************/
+ /* TODO: this test doesn't process any test results; it also takes very long */
- #include <ddk/ntddk.h>
- #include <ntifs.h>
- #include <ndk/ntndk.h>
- #include "kmtest.h"
+ #include <kmt_test.h>
#define NDEBUG
- #include "debug.h"
+ #include <debug.h>
- /* PUBLIC FUNCTIONS ***********************************************************/
-
- VOID
- KeStallTest(HANDLE KeyHandle)
+ static VOID KeStallExecutionProcessorTest(VOID)
{
ULONG i;
LARGE_INTEGER TimeStart, TimeFinish;
- StartTest();
-
DPRINT1("Waiting for 30 secs with 50us stalls...\n");
KeQuerySystemTime(&TimeStart);
for (i = 0; i < (30*1000*20); i++)
KeStallExecutionProcessor(30*1000000);
KeQuerySystemTime(&TimeFinish);
DPRINT1("Time elapsed: %d secs\n", (TimeFinish.QuadPart - TimeStart.QuadPart) / 10000000); // 30
-
- FinishTest(KeyHandle, L"KeStallmanExecutionTest");
}
+
+ START_TEST(KeProcessor)
+ {
+ KeStallExecutionProcessorTest();
+ }
--- /dev/null
+ /*
+ * PROJECT: ReactOS kernel-mode tests
+ * LICENSE: GPLv2+ - See COPYING in the top level directory
+ * PURPOSE: Kernel-Mode Test Suite Spin lock test
+ * PROGRAMMER: Thomas Faber <thfabba@gmx.de>
+ */
+
+ #ifndef _WIN64
+ __declspec(dllimport) void __stdcall KeAcquireSpinLock(unsigned long *, unsigned char *);
+ __declspec(dllimport) void __stdcall KeReleaseSpinLock(unsigned long *, unsigned char);
+ __declspec(dllimport) void __stdcall KeAcquireSpinLockAtDpcLevel(unsigned long *);
+ __declspec(dllimport) void __stdcall KeReleaseSpinLockFromDpcLevel(unsigned long *);
+ #endif
+
+ /* this define makes KeInitializeSpinLock not use the inlined version */
+ #define WIN9X_COMPAT_SPINLOCK
+ #include <kmt_test.h>
+ #include <limits.h>
+
+ //#define NDEBUG
+ #include <debug.h>
+
+ /* TODO: these are documented for Vista+ */
+ NTKERNELAPI
+ VOID
+ FASTCALL
+ KeAcquireInStackQueuedSpinLockForDpc(
+ IN OUT PKSPIN_LOCK SpinLock,
+ OUT PKLOCK_QUEUE_HANDLE LockHandle);
+
+ NTKERNELAPI
+ VOID
+ FASTCALL
+ KeReleaseInStackQueuedSpinLockForDpc(
+ IN PKLOCK_QUEUE_HANDLE LockHandle);
+
+ /* TODO: multiprocessor testing */
+
+ struct _CHECK_DATA;
+ typedef struct _CHECK_DATA CHECK_DATA, *PCHECK_DATA;
+
+ typedef VOID (*PACQUIRE_FUNCTION)(PKSPIN_LOCK, PCHECK_DATA);
+ typedef VOID (*PRELEASE_FUNCTION)(PKSPIN_LOCK, PCHECK_DATA);
+ typedef BOOLEAN (*PTRY_FUNCTION)(PKSPIN_LOCK, PCHECK_DATA);
+
+ struct _CHECK_DATA
+ {
+ enum
+ {
+ CheckQueueHandle,
+ CheckQueue,
+ CheckLock
+ } Check;
+ KIRQL IrqlWhenAcquired;
+ PACQUIRE_FUNCTION Acquire;
+ PRELEASE_FUNCTION Release;
+ PTRY_FUNCTION TryAcquire;
+ PACQUIRE_FUNCTION AcquireNoRaise;
+ PRELEASE_FUNCTION ReleaseNoLower;
+ PTRY_FUNCTION TryAcquireNoRaise;
+ KSPIN_LOCK_QUEUE_NUMBER QueueNumber;
+ BOOLEAN TryRetOnFailure;
+ KIRQL OriginalIrql;
+ BOOLEAN IsAcquired;
+ _ANONYMOUS_UNION union
+ {
+ KLOCK_QUEUE_HANDLE QueueHandle;
+ PKSPIN_LOCK_QUEUE Queue;
+ KIRQL Irql;
+ } DUMMYUNIONNAME;
+ PVOID UntouchedValue;
+ };
+
+ #define DEFINE_ACQUIRE(LocalName, SetIsAcquired, DoCall) \
+ static VOID LocalName(PKSPIN_LOCK SpinLock, PCHECK_DATA CheckData) \
+ { \
+ ASSERT(!CheckData->IsAcquired); \
+ DoCall; \
+ if (SetIsAcquired) CheckData->IsAcquired = TRUE; \
+ }
+
+ #define DEFINE_RELEASE(LocalName, SetIsAcquired, DoCall) \
+ static VOID LocalName(PKSPIN_LOCK SpinLock, PCHECK_DATA CheckData) \
+ { \
+ DoCall; \
+ if (SetIsAcquired) CheckData->IsAcquired = FALSE; \
+ }
+
+ DEFINE_ACQUIRE(AcquireNormal, TRUE, KeAcquireSpinLock(SpinLock, &CheckData->Irql))
+ DEFINE_RELEASE(ReleaseNormal, TRUE, KeReleaseSpinLock(SpinLock, CheckData->Irql))
+ DEFINE_ACQUIRE(AcquireExp, TRUE, (KeAcquireSpinLock)(SpinLock, &CheckData->Irql))
+ DEFINE_RELEASE(ReleaseExp, TRUE, (KeReleaseSpinLock)(SpinLock, CheckData->Irql))
+ DEFINE_ACQUIRE(AcquireSynch, TRUE, CheckData->Irql = KeAcquireSpinLockRaiseToSynch(SpinLock))
+
+ DEFINE_ACQUIRE(AcquireInStackQueued, TRUE, KeAcquireInStackQueuedSpinLock(SpinLock, &CheckData->QueueHandle))
+ DEFINE_ACQUIRE(AcquireInStackSynch, TRUE, KeAcquireInStackQueuedSpinLockRaiseToSynch(SpinLock, &CheckData->QueueHandle))
+ DEFINE_RELEASE(ReleaseInStackQueued, TRUE, KeReleaseInStackQueuedSpinLock(&CheckData->QueueHandle))
+
+ DEFINE_ACQUIRE(AcquireQueued, TRUE, CheckData->Irql = KeAcquireQueuedSpinLock(CheckData->QueueNumber))
+ DEFINE_ACQUIRE(AcquireQueuedSynch, TRUE, CheckData->Irql = KeAcquireQueuedSpinLockRaiseToSynch(CheckData->QueueNumber))
+ DEFINE_RELEASE(ReleaseQueued, TRUE, KeReleaseQueuedSpinLock(CheckData->QueueNumber, CheckData->Irql))
+
+ DEFINE_ACQUIRE(AcquireNoRaise, FALSE, KeAcquireSpinLockAtDpcLevel(SpinLock))
+ DEFINE_RELEASE(ReleaseNoLower, FALSE, KeReleaseSpinLockFromDpcLevel(SpinLock))
+ DEFINE_ACQUIRE(AcquireExpNoRaise, FALSE, (KeAcquireSpinLockAtDpcLevel)(SpinLock))
+ DEFINE_RELEASE(ReleaseExpNoLower, FALSE, (KeReleaseSpinLockFromDpcLevel)(SpinLock))
+
+ DEFINE_ACQUIRE(AcquireInStackNoRaise, FALSE, KeAcquireInStackQueuedSpinLockAtDpcLevel(SpinLock, &CheckData->QueueHandle))
+ DEFINE_RELEASE(ReleaseInStackNoRaise, FALSE, KeReleaseInStackQueuedSpinLockFromDpcLevel(&CheckData->QueueHandle))
+
+ /* TODO: test these functions. They behave weirdly, though */
+ #if 0
+ DEFINE_ACQUIRE(AcquireForDpc, TRUE, CheckData->Irql = KeAcquireSpinLockForDpc(SpinLock))
+ DEFINE_RELEASE(ReleaseForDpc, TRUE, KeReleaseSpinLockForDpc(SpinLock, CheckData->Irql))
+ #endif
+
+ DEFINE_ACQUIRE(AcquireInStackForDpc, FALSE, KeAcquireInStackQueuedSpinLockForDpc(SpinLock, &CheckData->QueueHandle))
+ DEFINE_RELEASE(ReleaseInStackForDpc, FALSE, KeReleaseInStackQueuedSpinLockForDpc(&CheckData->QueueHandle))
+
+ DEFINE_ACQUIRE(AcquireInt, FALSE, KiAcquireSpinLock(SpinLock))
+ DEFINE_RELEASE(ReleaseInt, FALSE, KiReleaseSpinLock(SpinLock))
+
+ BOOLEAN TryQueued(PKSPIN_LOCK SpinLock, PCHECK_DATA CheckData) {
+ LOGICAL Ret = KeTryToAcquireQueuedSpinLock(CheckData->QueueNumber, &CheckData->Irql);
+ CheckData->IsAcquired = TRUE;
+ ASSERT(Ret == FALSE || Ret == TRUE);
+ return (BOOLEAN)Ret;
+ }
+ BOOLEAN TryQueuedSynch(PKSPIN_LOCK SpinLock, PCHECK_DATA CheckData) {
+ BOOLEAN Ret = KeTryToAcquireQueuedSpinLockRaiseToSynch(CheckData->QueueNumber, &CheckData->Irql);
+ CheckData->IsAcquired = TRUE;
+ return Ret;
+ }
+ BOOLEAN TryNoRaise(PKSPIN_LOCK SpinLock, PCHECK_DATA CheckData) {
+ BOOLEAN Ret = KeTryToAcquireSpinLockAtDpcLevel(SpinLock);
+ return Ret;
+ }
+
+ #define CheckSpinLockLock(SpinLock, CheckData, Value) do \
+ { \
+ PKTHREAD Thread = KeGetCurrentThread(); \
+ if (KmtIsMultiProcessorBuild) \
+ { \
+ ok_eq_bool(Ret, (Value) == 0); \
+ if (SpinLock) \
+ ok_eq_ulongptr(*(SpinLock), \
+ (Value) ? (ULONG_PTR)Thread | 1 : 0); \
+ } \
+ else \
+ { \
+ ok_bool_true(Ret, "KeTestSpinLock returned"); \
+ if (SpinLock) \
+ ok_eq_ulongptr(*(SpinLock), 0); \
+ } \
+ ok_eq_uint((CheckData)->Irql, (CheckData)->OriginalIrql); \
+ } while (0)
+
+ #define CheckSpinLockQueue(SpinLock, CheckData, Value) do \
+ { \
+ ok_eq_pointer((CheckData)->Queue->Next, NULL); \
+ ok_eq_pointer((CheckData)->Queue->Lock, NULL); \
+ ok_eq_uint((CheckData)->Irql, (CheckData)->OriginalIrql); \
+ } while (0)
+
+ #define CheckSpinLockQueueHandle(SpinLock, CheckData, Value) do \
+ { \
+ if (KmtIsMultiProcessorBuild) \
+ { \
+ ok_eq_bool(Ret, (Value) == 0); \
+ if (SpinLock) \
+ ok_eq_ulongptr(*(SpinLock), \
+ (Value) ? &(CheckData)->QueueHandle : 0); \
+ ok_eq_pointer((CheckData)->QueueHandle.LockQueue.Next, NULL); \
+ ok_eq_pointer((CheckData)->QueueHandle.LockQueue.Lock, \
+ (PVOID)((ULONG_PTR)SpinLock | ((Value) ? 2 : 0))); \
+ } \
+ else \
+ { \
+ ok_bool_true(Ret, "KeTestSpinLock returned"); \
+ if (SpinLock) \
+ ok_eq_ulongptr(*(SpinLock), 0); \
+ ok_eq_pointer((CheckData)->QueueHandle.LockQueue.Next, (CheckData)->UntouchedValue); \
+ ok_eq_pointer((CheckData)->QueueHandle.LockQueue.Lock, (CheckData)->UntouchedValue); \
+ } \
+ ok_eq_uint((CheckData)->QueueHandle.OldIrql, (CheckData)->OriginalIrql); \
+ } while (0)
+
+ #define CheckSpinLock(SpinLock, CheckData, Value) do \
+ { \
+ BOOLEAN Ret = SpinLock ? KeTestSpinLock(SpinLock) : TRUE; \
+ KIRQL ExpectedIrql = (CheckData)->OriginalIrql; \
+ \
+ switch ((CheckData)->Check) \
+ { \
+ case CheckLock: \
+ CheckSpinLockLock(SpinLock, CheckData, Value); \
+ break; \
+ case CheckQueue: \
+ CheckSpinLockQueue(SpinLock, CheckData, Value); \
+ break; \
+ case CheckQueueHandle: \
+ CheckSpinLockQueueHandle(SpinLock, CheckData, Value); \
+ break; \
+ } \
+ \
+ if ((CheckData)->IsAcquired) \
+ ExpectedIrql = (CheckData)->IrqlWhenAcquired; \
+ ok_irql(ExpectedIrql); \
+ ok_bool_false(KeAreApcsDisabled(), "KeAreApcsDisabled returned"); \
+ ok_bool_true(KmtAreInterruptsEnabled(), "Interrupts enabled:"); \
+ } while (0)
+
+ static
+ VOID
+ TestSpinLock(
+ PKSPIN_LOCK SpinLock,
+ PCHECK_DATA CheckData)
+ {
+ static INT Run = 0;
+ trace("Test SpinLock run %d\n", Run++);
+
+ ok_irql(CheckData->OriginalIrql);
+
+ if (SpinLock)
+ ok_eq_ulongptr(*SpinLock, 0);
+ CheckData->Acquire(SpinLock, CheckData);
+ CheckSpinLock(SpinLock, CheckData, 1);
+ CheckData->Release(SpinLock, CheckData);
+ CheckSpinLock(SpinLock, CheckData, 0);
+
+ if (CheckData->TryAcquire)
+ {
+ CheckSpinLock(SpinLock, CheckData, 0);
+ ok_bool_true(CheckData->TryAcquire(SpinLock, CheckData), "TryAcquire returned");
+ CheckSpinLock(SpinLock, CheckData, 1);
+ if (!KmtIsCheckedBuild)
+ {
+ /* SPINLOCK_ALREADY_OWNED on checked build */
+ ok_bool_true(CheckData->TryAcquire(SpinLock, CheckData), "TryAcquire returned");
+ /* even a failing acquire sets irql */
+ ok_eq_uint(CheckData->Irql, CheckData->IrqlWhenAcquired);
+ CheckData->Irql = CheckData->OriginalIrql;
+ CheckSpinLock(SpinLock, CheckData, 1);
+ }
+ CheckData->Release(SpinLock, CheckData);
+ CheckSpinLock(SpinLock, CheckData, 0);
+ }
+
+ if (CheckData->AcquireNoRaise &&
+ (CheckData->OriginalIrql >= DISPATCH_LEVEL || !KmtIsCheckedBuild))
+ {
+ /* acquire/release without irql change */
+ CheckData->AcquireNoRaise(SpinLock, CheckData);
+ CheckSpinLock(SpinLock, CheckData, 1);
+ CheckData->ReleaseNoLower(SpinLock, CheckData);
+ CheckSpinLock(SpinLock, CheckData, 0);
+
+ /* acquire without raise, but normal release */
+ CheckData->AcquireNoRaise(SpinLock, CheckData);
+ CheckSpinLock(SpinLock, CheckData, 1);
+ CheckData->Release(SpinLock, CheckData);
+ CheckSpinLock(SpinLock, CheckData, 0);
+
+ /* acquire normally but release without lower */
+ CheckData->Acquire(SpinLock, CheckData);
+ CheckSpinLock(SpinLock, CheckData, 1);
+ CheckData->ReleaseNoLower(SpinLock, CheckData);
+ CheckSpinLock(SpinLock, CheckData, 0);
+ CheckData->IsAcquired = FALSE;
+ KmtSetIrql(CheckData->OriginalIrql);
+
+ if (CheckData->TryAcquireNoRaise)
+ {
+ CheckSpinLock(SpinLock, CheckData, 0);
+ ok_bool_true(CheckData->TryAcquireNoRaise(SpinLock, CheckData), "TryAcquireNoRaise returned");
+ CheckSpinLock(SpinLock, CheckData, 1);
+ if (!KmtIsCheckedBuild)
+ {
+ ok_bool_true(CheckData->TryAcquireNoRaise(SpinLock, CheckData), "TryAcquireNoRaise returned");
+ CheckSpinLock(SpinLock, CheckData, 1);
+ }
+ CheckData->ReleaseNoLower(SpinLock, CheckData);
+ CheckSpinLock(SpinLock, CheckData, 0);
+ }
+ }
+
+ ok_irql(CheckData->OriginalIrql);
+ /* make sure we survive this in case of error */
+ KmtSetIrql(CheckData->OriginalIrql);
+ }
+
+ START_TEST(KeSpinLock)
+ {
+ KSPIN_LOCK SpinLock = (KSPIN_LOCK)0x5555555555555555LL;
+ PKSPIN_LOCK pSpinLock = &SpinLock;
+ KIRQL Irql, SynchIrql = KmtIsMultiProcessorBuild ? IPI_LEVEL - 2 : DISPATCH_LEVEL;
+ KIRQL OriginalIrqls[] = { PASSIVE_LEVEL, APC_LEVEL, DISPATCH_LEVEL, HIGH_LEVEL };
+ CHECK_DATA TestData[] =
+ {
+ { CheckLock, DISPATCH_LEVEL, AcquireNormal, ReleaseNormal, NULL, AcquireNoRaise, ReleaseNoLower, TryNoRaise },
+ { CheckLock, DISPATCH_LEVEL, AcquireExp, ReleaseExp, NULL, AcquireExpNoRaise, ReleaseExpNoLower, NULL },
+ /* TODO: this one is just weird!
+ { CheckLock, DISPATCH_LEVEL, AcquireNormal, ReleaseNormal, NULL, AcquireForDpc, ReleaseForDpc, NULL },*/
+ { CheckLock, DISPATCH_LEVEL, AcquireNormal, ReleaseNormal, NULL, AcquireInt, ReleaseInt, NULL },
+ { CheckLock, SynchIrql, AcquireSynch, ReleaseNormal, NULL, NULL, NULL, NULL },
+ { CheckQueueHandle, DISPATCH_LEVEL, AcquireInStackQueued, ReleaseInStackQueued, NULL, AcquireInStackNoRaise, ReleaseInStackNoRaise, NULL },
+ { CheckQueueHandle, SynchIrql, AcquireInStackSynch, ReleaseInStackQueued, NULL, NULL, NULL, NULL },
+ { CheckQueueHandle, DISPATCH_LEVEL, AcquireInStackQueued, ReleaseInStackQueued, NULL, AcquireInStackForDpc, ReleaseInStackForDpc, NULL },
+ { CheckQueue, DISPATCH_LEVEL, AcquireQueued, ReleaseQueued, TryQueued, NULL, NULL, NULL, LockQueuePfnLock },
+ { CheckQueue, SynchIrql, AcquireQueuedSynch, ReleaseQueued, TryQueuedSynch, NULL, NULL, NULL, LockQueuePfnLock },
+ };
+ int i, iIrql;
+ PKPRCB Prcb = KeGetCurrentPrcb();
+
+ /* KeInitializeSpinLock */
+ memset(&SpinLock, 0x55, sizeof SpinLock);
+ KeInitializeSpinLock(&SpinLock);
+ ok_eq_ulongptr(SpinLock, 0);
+
+ /* KeTestSpinLock */
+ ok_bool_true(KeTestSpinLock(&SpinLock), "KeTestSpinLock returned");
+ SpinLock = 1;
+ ok_bool_false(KeTestSpinLock(&SpinLock), "KeTestSpinLock returned");
+ SpinLock = 2;
+ ok_bool_false(KeTestSpinLock(&SpinLock), "KeTestSpinLock returned");
+ SpinLock = (ULONG_PTR)-1;
+ ok_bool_false(KeTestSpinLock(&SpinLock), "KeTestSpinLock returned");
+ SpinLock = (ULONG_PTR)1 << (sizeof(ULONG_PTR) * CHAR_BIT - 1);
+ ok_bool_false(KeTestSpinLock(&SpinLock), "KeTestSpinLock returned");
+ SpinLock = 0;
+ ok_bool_true(KeTestSpinLock(&SpinLock), "KeTestSpinLock returned");
+
+ /* on UP none of the following functions actually looks at the spinlock! */
+ if (!KmtIsMultiProcessorBuild && !KmtIsCheckedBuild)
+ pSpinLock = NULL;
+
+ for (i = 0; i < sizeof TestData / sizeof TestData[0]; ++i)
+ {
+ memset(&SpinLock, 0x55, sizeof SpinLock);
+ KeInitializeSpinLock(&SpinLock);
+ if (TestData[i].Check == CheckQueueHandle)
+ memset(&TestData[i].QueueHandle, 0x55, sizeof TestData[i].QueueHandle);
+ if (TestData[i].Check == CheckQueue)
+ {
+ TestData[i].Queue = &Prcb->LockQueue[TestData[i].QueueNumber];
+ TestData[i].UntouchedValue = NULL;
+ }
+ else
+ TestData[i].UntouchedValue = (PVOID)0x5555555555555555LL;
+
+ for (iIrql = 0; iIrql < sizeof OriginalIrqls / sizeof OriginalIrqls[0]; ++iIrql)
+ {
+ if (KmtIsCheckedBuild && OriginalIrqls[iIrql] > DISPATCH_LEVEL)
+ continue;
+ KeRaiseIrql(OriginalIrqls[iIrql], &Irql);
+ TestData[i].OriginalIrql = OriginalIrqls[iIrql];
+ TestData[i].IsAcquired = FALSE;
+ TestSpinLock(pSpinLock, &TestData[i]);
+ KeLowerIrql(Irql);
+ }
+ }
+
+ KmtSetIrql(PASSIVE_LEVEL);
+ }
--- /dev/null
+ /*
+ * PROJECT: ReactOS kernel-mode tests
+ * LICENSE: GPLv2+ - See COPYING in the top level directory
+ * PURPOSE: Kernel-Mode Test Suite Object Referencing test
+ * PROGRAMMER: Thomas Faber <thfabba@gmx.de>
+ */
+
+ #include <kmt_test.h>
+ #define NDEBUG
+ #include <debug.h>
+
+ #define CheckObject(Handle, Pointers, Handles) do \
+ { \
+ PUBLIC_OBJECT_BASIC_INFORMATION ObjectInfo; \
+ Status = ZwQueryObject(Handle, ObjectBasicInformation, \
+ &ObjectInfo, sizeof ObjectInfo, NULL); \
+ ok_eq_hex(Status, STATUS_SUCCESS); \
+ ok_eq_ulong(ObjectInfo.PointerCount, Pointers); \
+ ok_eq_ulong(ObjectInfo.HandleCount, Handles); \
+ } while (0)
+
+ static POBJECT_TYPE ObDirectoryObjectType;
+
+ static
+ VOID
+ TestReference(
+ IN HANDLE Handle,
+ IN PUNICODE_STRING Name OPTIONAL,
+ IN PUNICODE_STRING NameUpper OPTIONAL,
+ IN BOOLEAN CaseSensitive,
+ IN ULONG AdditionalReferences,
+ IN BOOLEAN Permanent)
+ {
+ NTSTATUS Status;
+ LONG_PTR Ret;
+ PVOID Object = NULL;
+ PVOID Object2 = NULL;
+ PVOID Object3 = NULL;
+ PVOID Object4 = NULL;
+
+ CheckObject(Handle, 2LU + AdditionalReferences, 1LU);
+
+ Status = ObReferenceObjectByHandle(Handle, DIRECTORY_ALL_ACCESS, NULL, KernelMode, &Object, NULL);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ ok(Object != NULL, "ObReferenceObjectByHandle returned NULL object\n");
+ CheckObject(Handle, 3LU + AdditionalReferences, 1LU);
+
+ Status = ObReferenceObjectByHandle(Handle, DIRECTORY_ALL_ACCESS, NULL, KernelMode, &Object2, NULL);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ ok(Object != NULL, "ObReferenceObjectByHandle returned NULL object\n");
+ ok_eq_pointer(Object, Object2);
+ CheckObject(Handle, 4LU + AdditionalReferences, 1LU);
+
+ if (!skip(Object != NULL, "No object to reference!\n"))
+ {
+ Ret = ObReferenceObject(Object);
+ ok_eq_longptr(Ret, (LONG_PTR)4 + AdditionalReferences);
+ CheckObject(Handle, 5LU + AdditionalReferences, 1LU);
+
+ Ret = ObReferenceObject(Object);
+ ok_eq_longptr(Ret, (LONG_PTR)5 + AdditionalReferences);
+ CheckObject(Handle, 6LU + AdditionalReferences, 1LU);
+
+ Status = ObReferenceObjectByPointer(Object, DIRECTORY_ALL_ACCESS, NULL, KernelMode);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ CheckObject(Handle, 7LU + AdditionalReferences, 1LU);
+
+ Status = ObReferenceObjectByPointer(Object, DIRECTORY_ALL_ACCESS, NULL, KernelMode);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ CheckObject(Handle, 8LU + AdditionalReferences, 1LU);
+
+ Ret = ObDereferenceObject(Object);
+ ok_eq_longptr(Ret, (LONG_PTR)6 + AdditionalReferences);
+ CheckObject(Handle, 7LU + AdditionalReferences, 1LU);
+
+ Ret = ObDereferenceObject(Object);
+ ok_eq_longptr(Ret, (LONG_PTR)5 + AdditionalReferences);
+ CheckObject(Handle, 6LU + AdditionalReferences, 1LU);
+
+ Ret = ObDereferenceObject(Object);
+ ok_eq_longptr(Ret, (LONG_PTR)4 + AdditionalReferences);
+ CheckObject(Handle, 5LU + AdditionalReferences, 1LU);
+
+ Ret = ObDereferenceObject(Object);
+ ok_eq_longptr(Ret, (LONG_PTR)3 + AdditionalReferences);
+ CheckObject(Handle, 4LU + AdditionalReferences, 1LU);
+ }
+
+ if (Name && !skip(ObDirectoryObjectType != NULL, "No directory object type\n"))
+ {
+ Status = ObReferenceObjectByName(Name, 0, NULL, DIRECTORY_ALL_ACCESS, ObDirectoryObjectType, KernelMode, NULL, &Object3);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ CheckObject(Handle, 5LU + AdditionalReferences, 1LU);
+ }
+
+ if (NameUpper && !skip(ObDirectoryObjectType != NULL, "No directory object type\n"))
+ {
+ Status = ObReferenceObjectByName(NameUpper, 0, NULL, DIRECTORY_ALL_ACCESS, ObDirectoryObjectType, KernelMode, NULL, &Object4);
+ ok_eq_hex(Status, CaseSensitive ? STATUS_OBJECT_NAME_NOT_FOUND : STATUS_SUCCESS);
+ CheckObject(Handle, 5LU + AdditionalReferences + !CaseSensitive, 1LU);
+ }
+
+ if (NameUpper && !skip(Object4 != NULL, "No object to dereference\n"))
+ {
+ Ret = ObDereferenceObject(Object4);
+ ok_eq_longptr(Ret, (LONG_PTR)4 + AdditionalReferences);
+ CheckObject(Handle, 5LU + AdditionalReferences, 1LU);
+ }
+ if (Name && !skip(Object3 != NULL, "No object to dereference\n"))
+ {
+ Ret = ObDereferenceObject(Object3);
+ ok_eq_longptr(Ret, (LONG_PTR)3 + AdditionalReferences);
+ CheckObject(Handle, 4LU + AdditionalReferences, 1LU);
+ }
+ if (!skip(Object2 != NULL, "No object to dereference\n"))
+ {
+ Ret = ObDereferenceObject(Object2);
+ ok_eq_longptr(Ret, (LONG_PTR)2 + AdditionalReferences);
+ CheckObject(Handle, 3LU + AdditionalReferences, 1LU);
+ }
+ if (!skip(Object != NULL, "No object to dereference\n"))
+ {
+ Ret = ObDereferenceObject(Object);
+ ok_eq_longptr(Ret, (LONG_PTR)1 + AdditionalReferences);
+ CheckObject(Handle, 2LU + AdditionalReferences, 1LU);
+ }
+
+ CheckObject(Handle, 2LU + AdditionalReferences, 1LU);
+
+ if (Permanent)
+ {
+ Status = ZwMakeTemporaryObject(Handle);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ CheckObject(Handle, 2LU + AdditionalReferences, 1LU);
+
+ Status = ZwMakeTemporaryObject(Handle);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ CheckObject(Handle, 2LU + AdditionalReferences, 1LU);
+ }
+ }
+
+ START_TEST(ObReference)
+ {
+ NTSTATUS Status;
+ NTSTATUS ExceptionStatus;
+ HANDLE DirectoryHandle = NULL;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ UNICODE_STRING Name, *pName;
+ UNICODE_STRING NameUpper, *pNameUpper;
+ ULONG i;
+ struct
+ {
+ PWSTR Name;
+ ULONG Flags;
+ ULONG AdditionalReferences;
+ } Tests[] =
+ {
+ { NULL, 0, 0 },
+ { NULL, OBJ_CASE_INSENSITIVE, 0 },
+ { NULL, OBJ_KERNEL_HANDLE, 0 },
+ { NULL, OBJ_PERMANENT, 0 },
+ { NULL, OBJ_CASE_INSENSITIVE | OBJ_PERMANENT, 0 },
+ { NULL, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 0 },
+ { NULL, OBJ_KERNEL_HANDLE | OBJ_PERMANENT, 0 },
+ { NULL, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE | OBJ_PERMANENT, 0 },
+ { L"\\YayDirectory0", 0, 1 },
+ { L"\\YayDirectory1", OBJ_CASE_INSENSITIVE, 1 },
+ { L"\\YayDirectory2", OBJ_KERNEL_HANDLE, 1 },
+ { L"\\YayDirectory3", OBJ_PERMANENT, 1 },
+ { L"\\YayDirectory4", OBJ_CASE_INSENSITIVE | OBJ_PERMANENT, 1 },
+ { L"\\YayDirectory5", OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 1 },
+ { L"\\YayDirectory6", OBJ_KERNEL_HANDLE | OBJ_PERMANENT, 1 },
+ { L"\\YayDirectory7", OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE | OBJ_PERMANENT, 1 },
+ };
+ HANDLE ObjectTypeHandle;
+
+ /* ObReferenceObjectByName needs the object type... so get it... */
+ RtlInitUnicodeString(&Name, L"\\ObjectTypes\\Directory");
+ InitializeObjectAttributes(&ObjectAttributes, &Name, OBJ_KERNEL_HANDLE, NULL, NULL);
+ Status = ObOpenObjectByName(&ObjectAttributes, NULL, KernelMode, NULL, 0, NULL, &ObjectTypeHandle);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ ok(ObjectTypeHandle != NULL, "ObjectTypeHandle = NULL\n");
+ if (!skip(Status == STATUS_SUCCESS && ObjectTypeHandle, "No handle\n"))
+ {
+ Status = ObReferenceObjectByHandle(ObjectTypeHandle, 0, NULL, KernelMode, (PVOID)&ObDirectoryObjectType, NULL);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ ok(ObDirectoryObjectType != NULL, "ObDirectoryObjectType = NULL\n");
+ Status = ZwClose(ObjectTypeHandle);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ }
+
+ for (i = 0; i < sizeof Tests / sizeof Tests[0]; ++i)
+ {
+ DPRINT("Run %d\n", i);
+ if (Tests[i].Name)
+ {
+ RtlInitUnicodeString(&Name, Tests[i].Name);
+ pName = &Name;
+ Status = RtlUpcaseUnicodeString(&NameUpper, &Name, TRUE);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ if (skip(Status == STATUS_SUCCESS, "No upper case name\n"))
+ pNameUpper = NULL;
+ else
+ pNameUpper = &NameUpper;
+ }
+ else
+ {
+ pName = NULL;
+ pNameUpper = NULL;
+ }
+ InitializeObjectAttributes(&ObjectAttributes, pName, Tests[i].Flags, NULL, NULL);
+ Status = ZwCreateDirectoryObject(&DirectoryHandle, DIRECTORY_ALL_ACCESS, &ObjectAttributes);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ ok(DirectoryHandle != NULL, "DirectoryHandle = NULL\n");
+
+ if (!skip(Status == STATUS_SUCCESS && DirectoryHandle, "Cannot proceed without an object"))
+ {
+ TestReference(DirectoryHandle, pName, pNameUpper, FALSE, Tests[i].AdditionalReferences, (Tests[i].Flags & OBJ_PERMANENT) != 0);
+ /* try again for good measure */
+ TestReference(DirectoryHandle, pName, pNameUpper, FALSE, Tests[i].AdditionalReferences, FALSE);
+
+ Status = ZwClose(DirectoryHandle);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ DirectoryHandle = NULL;
+ }
+
+ if (pNameUpper)
+ RtlFreeUnicodeString(pNameUpper);
+ }
+
+ if (DirectoryHandle)
+ {
+ Status = ZwClose(DirectoryHandle);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ }
+
+ /* parameter tests */
+ /* bugcheck at APC_LEVEL
+ Status = ObReferenceObject(NULL);
+ Status = ObReferenceObjectByPointer(NULL, 0, NULL, UserMode);
+ Status = ObReferenceObjectByPointer(NULL, 0, NULL, KernelMode);*/
+
+ ExceptionStatus = STATUS_SUCCESS;
+ _SEH2_TRY {
+ /* TODO: this belongs in an ObHandle test if we ever have one */
+ /* NtClose must accept everything */
+ DPRINT("Closing null handle (NtClose)\n");
+ Status = NtClose(NULL);
+ ok_eq_hex(Status, STATUS_INVALID_HANDLE);
+ DPRINT("Closing null kernel handle (NtClose)\n");
+ Status = NtClose((HANDLE)0x80000000);
+ ok_eq_hex(Status, STATUS_INVALID_HANDLE);
+ DPRINT("Closing -1 handle (NtClose)\n");
+ Status = NtClose((HANDLE)0x7FFFFFFF);
+ ok_eq_hex(Status, STATUS_INVALID_HANDLE);
+ DPRINT("Closing -1 kernel handle (NtClose)\n");
+ Status = NtClose((HANDLE)0xFFFFFFFF);
+ ok_eq_hex(Status, STATUS_INVALID_HANDLE);
+ DPRINT("Closing 123 handle (NtClose)\n");
+ Status = NtClose((HANDLE)123);
+ ok_eq_hex(Status, STATUS_INVALID_HANDLE);
+ DPRINT("Closing 123 kernel handle (NtClose)\n");
+ Status = NtClose((HANDLE)(123 | 0x80000000));
+ ok_eq_hex(Status, STATUS_INVALID_HANDLE);
+
+ /* ObCloseHandle with UserMode accepts everything */
+ DPRINT("Closing null handle (ObCloseHandle, UserMode)\n");
+ Status = ObCloseHandle(NULL, UserMode);
+ ok_eq_hex(Status, STATUS_INVALID_HANDLE);
+ DPRINT("Closing null kernel handle (ObCloseHandle, UserMode)\n");
+ Status = ObCloseHandle((HANDLE)0x80000000, UserMode);
+ ok_eq_hex(Status, STATUS_INVALID_HANDLE);
+ DPRINT("Closing -1 handle (ObCloseHandle, UserMode)\n");
+ Status = ObCloseHandle((HANDLE)0x7FFFFFFF, UserMode);
+ ok_eq_hex(Status, STATUS_INVALID_HANDLE);
+ DPRINT("Closing -1 kernel handle (ObCloseHandle, UserMode)\n");
+ Status = ObCloseHandle((HANDLE)0xFFFFFFFF, UserMode);
+ ok_eq_hex(Status, STATUS_INVALID_HANDLE);
+ DPRINT("Closing 123 handle (ObCloseHandle, UserMode)\n");
+ Status = ObCloseHandle((HANDLE)123, UserMode);
+ ok_eq_hex(Status, STATUS_INVALID_HANDLE);
+ DPRINT("Closing 123 kernel handle (ObCloseHandle, UserMode)\n");
+ Status = ObCloseHandle((HANDLE)(123 | 0x80000000), UserMode);
+ ok_eq_hex(Status, STATUS_INVALID_HANDLE);
+
+ /* ZwClose only accepts 0 and -1 */
+ DPRINT("Closing null handle (ZwClose)\n");
+ Status = ZwClose(NULL);
+ ok_eq_hex(Status, STATUS_INVALID_HANDLE);
+ DPRINT("Closing null kernel handle (ZwClose)\n");
+ Status = ZwClose((HANDLE)0x80000000);
+ ok_eq_hex(Status, STATUS_INVALID_HANDLE);
+ /* INVALID_KERNEL_HANDLE, 0x7FFFFFFF
+ Status = ZwClose((HANDLE)0x7FFFFFFF);*/
+ DPRINT("Closing -1 kernel handle (ZwClose)\n");
+ Status = ZwClose((HANDLE)0xFFFFFFFF);
+ ok_eq_hex(Status, STATUS_INVALID_HANDLE);
+ /* INVALID_KERNEL_HANDLE, 0x7B, 1, 0, 0
+ Status = ZwClose((HANDLE)123);
+ Status = ZwClose((HANDLE)(123 | 0x80000000));*/
+
+ /* ObCloseHandle with KernelMode accepts only 0 and -1 */
+ DPRINT("Closing null handle (ObCloseHandle, KernelMode)\n");
+ Status = ObCloseHandle(NULL, KernelMode);
+ ok_eq_hex(Status, STATUS_INVALID_HANDLE);
+ DPRINT("Closing null kernel handle (ObCloseHandle, KernelMode)\n");
+ Status = ObCloseHandle((HANDLE)0x80000000, KernelMode);
+ ok_eq_hex(Status, STATUS_INVALID_HANDLE);
+ /* INVALID_KERNEL_HANDLE, 0x7FFFFFFF, 1, 0, 0
+ Status = ObCloseHandle((HANDLE)0x7FFFFFFF, KernelMode);*/
+ DPRINT("Closing -1 kernel handle (ObCloseHandle, KernelMode)\n");
+ Status = ObCloseHandle((HANDLE)0xFFFFFFFF, KernelMode);
+ ok_eq_hex(Status, STATUS_INVALID_HANDLE);
+ /* INVALID_KERNEL_HANDLE, 0x7B, 1, 0, 0
+ Status = ObCloseHandle((HANDLE)123, KernelMode);
+ Status = ObCloseHandle((HANDLE)(123 | 0x80000000), KernelMode);*/
+ } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
+ ExceptionStatus = _SEH2_GetExceptionCode();
+ } _SEH2_END;
+ ok_eq_hex(ExceptionStatus, STATUS_SUCCESS);
+
+ if (ObDirectoryObjectType)
+ {
+ ObDereferenceObject(ObDirectoryObjectType);
+ ObDirectoryObjectType = NULL;
+ }
+ }
--- /dev/null
+ /*
+ * PROJECT: ReactOS kernel-mode tests
+ * LICENSE: LGPLv2+ - See COPYING.LIB in the top level directory
+ * PURPOSE: Kernel-Mode Test Suite Ob Regressions KM-Test
+ * PROGRAMMER: Aleksey Bragin <aleksey@reactos.org>
+ * Thomas Faber <thfabba@gmx.de>
+ */
+
+ /* TODO: split this into multiple tests! ObLife, ObHandle, ObName, ... */
+
+ #include <kmt_test.h>
+
+ #define NDEBUG
+ #include <debug.h>
+
+ #define CheckObject(Handle, Pointers, Handles) do \
+ { \
+ PUBLIC_OBJECT_BASIC_INFORMATION ObjectInfo; \
+ Status = ZwQueryObject(Handle, ObjectBasicInformation, \
+ &ObjectInfo, sizeof ObjectInfo, NULL); \
+ ok_eq_hex(Status, STATUS_SUCCESS); \
+ ok_eq_ulong(ObjectInfo.PointerCount, Pointers); \
+ ok_eq_ulong(ObjectInfo.HandleCount, Handles); \
+ } while (0)
+
+ #define NUM_OBTYPES 5
+
+ typedef struct _MY_OBJECT1
+ {
+ ULONG Something1;
+ } MY_OBJECT1, *PMY_OBJECT1;
+
+ typedef struct _MY_OBJECT2
+ {
+ ULONG Something1;
+ ULONG SomeLong[10];
+ } MY_OBJECT2, *PMY_OBJECT2;
+
+ static POBJECT_TYPE ObTypes[NUM_OBTYPES];
+ static UNICODE_STRING ObTypeName[NUM_OBTYPES];
+ static UNICODE_STRING ObName[NUM_OBTYPES];
+ static OBJECT_TYPE_INITIALIZER ObTypeInitializer[NUM_OBTYPES];
+ static UNICODE_STRING ObDirectoryName;
+ static OBJECT_ATTRIBUTES ObDirectoryAttributes;
+ static OBJECT_ATTRIBUTES ObAttributes[NUM_OBTYPES];
+ static PVOID ObBody[NUM_OBTYPES];
+ static HANDLE ObHandle1[NUM_OBTYPES];
+ static HANDLE DirectoryHandle;
+
+ typedef struct _COUNTS
+ {
+ USHORT Dump;
+ USHORT Open;
+ USHORT Close;
+ USHORT Delete;
+ USHORT Parse;
+ USHORT OkayToClose;
+ USHORT QueryName;
+ } COUNTS, *PCOUNTS;
+ static COUNTS Counts;
+
+ static
+ VOID
+ NTAPI
+ DumpProc(
+ IN PVOID Object,
+ IN POB_DUMP_CONTROL DumpControl)
+ {
+ DPRINT("DumpProc() called\n");
+ ++Counts.Dump;
+ }
+
+ static
+ NTSTATUS
+ NTAPI
+ OpenProc(
+ IN OB_OPEN_REASON OpenReason,
+ IN PEPROCESS Process,
+ IN PVOID Object,
+ IN ACCESS_MASK GrantedAccess,
+ IN ULONG HandleCount)
+ {
+ DPRINT("OpenProc() 0x%p, OpenReason %d, HandleCount %lu, AccessMask 0x%lX\n",
+ Object, OpenReason, HandleCount, GrantedAccess);
+ ++Counts.Open;
+ return STATUS_SUCCESS;
+ }
+
+ static
+ VOID
+ NTAPI
+ CloseProc(
+ IN PEPROCESS Process,
+ IN PVOID Object,
+ IN ACCESS_MASK GrantedAccess,
+ IN ULONG ProcessHandleCount,
+ IN ULONG SystemHandleCount)
+ {
+ DPRINT("CloseProc() 0x%p, ProcessHandleCount %lu, SystemHandleCount %lu, AccessMask 0x%lX\n",
+ Object, ProcessHandleCount, SystemHandleCount, GrantedAccess);
+ ++Counts.Close;
+ }
+
+ static
+ VOID
+ NTAPI
+ DeleteProc(
+ IN PVOID Object)
+ {
+ DPRINT("DeleteProc() 0x%p\n", Object);
+ ++Counts.Delete;
+ }
+
+ static
+ NTSTATUS
+ NTAPI
+ ParseProc(
+ IN PVOID ParseObject,
+ IN PVOID ObjectType,
+ IN OUT PACCESS_STATE AccessState,
+ IN KPROCESSOR_MODE AccessMode,
+ IN ULONG Attributes,
+ IN OUT PUNICODE_STRING CompleteName,
+ IN OUT PUNICODE_STRING RemainingName,
+ IN OUT PVOID Context OPTIONAL,
+ IN PSECURITY_QUALITY_OF_SERVICE SecurityQos OPTIONAL,
+ OUT PVOID *Object)
+ {
+ DPRINT("ParseProc() called\n");
+ *Object = NULL;
+
+ ++Counts.Parse;
+ return STATUS_OBJECT_NAME_NOT_FOUND;//STATUS_SUCCESS;
+ }
+
+ static
+ BOOLEAN
+ NTAPI
+ OkayToCloseProc(
+ IN PEPROCESS Process OPTIONAL,
+ IN PVOID Object,
+ IN HANDLE Handle,
+ IN KPROCESSOR_MODE AccessMode)
+ {
+ DPRINT("OkayToCloseProc() 0x%p, Handle 0x%p, AccessMask 0x%lX\n",
+ Object, Handle, AccessMode);
+ ++Counts.OkayToClose;
+ return TRUE;
+ }
+
+ static
+ NTSTATUS
+ NTAPI
+ QueryNameProc(
+ IN PVOID Object,
+ IN BOOLEAN HasObjectName,
+ OUT POBJECT_NAME_INFORMATION ObjectNameInfo,
+ IN ULONG Length,
+ OUT PULONG ReturnLength,
+ IN KPROCESSOR_MODE AccessMode)
+ {
+ DPRINT("QueryNameProc() 0x%p, HasObjectName %d, Len %lu, AccessMask 0x%lX\n",
+ Object, HasObjectName, Length, AccessMode);
+ ++Counts.QueryName;
+
+ ObjectNameInfo = NULL;
+ ReturnLength = 0;
+ return STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ static
+ VOID
+ ObtCreateObjectTypes(VOID)
+ {
+ INT i;
+ NTSTATUS Status;
+ struct
+ {
+ WCHAR DirectoryName[sizeof "\\ObjectTypes\\" - 1];
+ WCHAR TypeName[15];
+ } Name;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ HANDLE ObjectTypeHandle;
+ UNICODE_STRING ObjectPath;
+
+ RtlCopyMemory(&Name.DirectoryName, L"\\ObjectTypes\\", sizeof Name.DirectoryName);
+
+ for (i = 0; i < NUM_OBTYPES; ++i)
+ {
+ Status = RtlStringCbPrintfW(Name.TypeName, sizeof Name.TypeName, L"MyObjectType%x", i);
+ ASSERT(NT_SUCCESS(Status));
+ RtlInitUnicodeString(&ObTypeName[i], Name.TypeName);
+ DPRINT("Creating object type %wZ\n", &ObTypeName[i]);
+
+ RtlZeroMemory(&ObTypeInitializer[i], sizeof ObTypeInitializer[i]);
+ ObTypeInitializer[i].Length = sizeof ObTypeInitializer[i];
+ ObTypeInitializer[i].PoolType = NonPagedPool;
+ ObTypeInitializer[i].MaintainHandleCount = TRUE;
+ ObTypeInitializer[i].ValidAccessMask = OBJECT_TYPE_ALL_ACCESS;
+
+ // Test for invalid parameter
+ // FIXME: Make it more exact, to see which params Win2k3 checks
+ // existence of
+ Status = ObCreateObjectType(&ObTypeName[i], &ObTypeInitializer[i], NULL, &ObTypes[i]);
+ ok_eq_hex(Status, STATUS_INVALID_PARAMETER);
+
+ ObTypeInitializer[i].CloseProcedure = CloseProc;
+ ObTypeInitializer[i].DeleteProcedure = DeleteProc;
+ ObTypeInitializer[i].DumpProcedure = DumpProc;
+ ObTypeInitializer[i].OpenProcedure = OpenProc;
+ ObTypeInitializer[i].ParseProcedure = ParseProc;
+ ObTypeInitializer[i].OkayToCloseProcedure = OkayToCloseProc;
+ ObTypeInitializer[i].QueryNameProcedure = QueryNameProc;
+ //ObTypeInitializer[i].SecurityProcedure = SecurityProc;
+
+ Status = ObCreateObjectType(&ObTypeName[i], &ObTypeInitializer[i], NULL, &ObTypes[i]);
+ if (Status == STATUS_OBJECT_NAME_COLLISION)
+ {
+ /* as we cannot delete the object types, get a pointer if they
+ * already exist */
+ RtlInitUnicodeString(&ObjectPath, Name.DirectoryName);
+ InitializeObjectAttributes(&ObjectAttributes, &ObjectPath, OBJ_KERNEL_HANDLE, NULL, NULL);
+ Status = ObOpenObjectByName(&ObjectAttributes, NULL, KernelMode, NULL, 0, NULL, &ObjectTypeHandle);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ ok(ObjectTypeHandle != NULL, "ObjectTypeHandle = NULL\n");
+ if (!skip(Status == STATUS_SUCCESS && ObjectTypeHandle, "No handle\n"))
+ {
+ Status = ObReferenceObjectByHandle(ObjectTypeHandle, 0, NULL, KernelMode, (PVOID)&ObTypes[i], NULL);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ if (!skip(Status == STATUS_SUCCESS && ObTypes[i], "blah\n"))
+ {
+ ObTypes[i]->TypeInfo.CloseProcedure = CloseProc;
+ ObTypes[i]->TypeInfo.DeleteProcedure = DeleteProc;
+ ObTypes[i]->TypeInfo.DumpProcedure = DumpProc;
+ ObTypes[i]->TypeInfo.OpenProcedure = OpenProc;
+ ObTypes[i]->TypeInfo.ParseProcedure = ParseProc;
+ ObTypes[i]->TypeInfo.OkayToCloseProcedure = OkayToCloseProc;
+ ObTypes[i]->TypeInfo.QueryNameProcedure = QueryNameProc;
+ }
+ Status = ZwClose(ObjectTypeHandle);
+ }
+ }
+
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ ok(ObTypes[i] != NULL, "ObType = NULL\n");
+ }
+ }
+
+ static
+ VOID
+ ObtCreateDirectory(VOID)
+ {
+ NTSTATUS Status;
+
+ RtlInitUnicodeString(&ObDirectoryName, L"\\ObtDirectory");
+ InitializeObjectAttributes(&ObDirectoryAttributes, &ObDirectoryName, OBJ_KERNEL_HANDLE | OBJ_PERMANENT | OBJ_CASE_INSENSITIVE, NULL, NULL);
+ Status = ZwCreateDirectoryObject(&DirectoryHandle, DELETE, &ObDirectoryAttributes);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ CheckObject(DirectoryHandle, 3LU, 1LU);
+ }
+
+ #define CheckCounts(OpenCount, CloseCount, DeleteCount, ParseCount, \
+ OkayToCloseCount, QueryNameCount) do \
+ { \
+ ok_eq_uint(Counts.Open, OpenCount); \
+ ok_eq_uint(Counts.Close, CloseCount); \
+ ok_eq_uint(Counts.Delete, DeleteCount); \
+ ok_eq_uint(Counts.Parse, ParseCount); \
+ ok_eq_uint(Counts.OkayToClose, OkayToCloseCount); \
+ ok_eq_uint(Counts.QueryName, QueryNameCount); \
+ } while (0)
+
+ #define SaveCounts(Save) memcpy(&Save, &Counts, sizeof Counts)
+
+ /* TODO: make this the same as NUM_OBTYPES */
+ #define NUM_OBTYPES2 2
+ static
+ VOID
+ ObtCreateObjects(VOID)
+ {
+ NTSTATUS Status;
+ WCHAR Name[NUM_OBTYPES2][MAX_PATH];
+ COUNTS SaveCounts;
+ INT i;
+ ACCESS_MASK Access[NUM_OBTYPES2] = { STANDARD_RIGHTS_ALL, GENERIC_ALL };
+ ULONG ObjectSize[NUM_OBTYPES2] = { sizeof(MY_OBJECT1), sizeof(MY_OBJECT2) };
+
+ // Create two objects
+ for (i = 0; i < NUM_OBTYPES2; ++i)
+ {
+ ASSERT(sizeof Name[i] == MAX_PATH * sizeof(WCHAR));
+ Status = RtlStringCbPrintfW(Name[i], sizeof Name[i], L"\\ObtDirectory\\MyObject%d", i + 1);
+ ASSERT(Status == STATUS_SUCCESS);
+ RtlInitUnicodeString(&ObName[i], Name[i]);
+ InitializeObjectAttributes(&ObAttributes[i], &ObName[i], OBJ_CASE_INSENSITIVE, NULL, NULL);
+ }
+ CheckObject(DirectoryHandle, 3LU, 1LU);
+
+ for (i = 0; i < NUM_OBTYPES2; ++i)
+ {
+ Status = ObCreateObject(KernelMode, ObTypes[i], &ObAttributes[i], KernelMode, NULL, ObjectSize[i], 0L, 0L, &ObBody[i]);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ }
+
+ SaveCounts(SaveCounts);
+
+ // Insert them
+ for (i = 0; i < NUM_OBTYPES2; ++i)
+ {
+ CheckObject(DirectoryHandle, 3LU + i, 1LU);
+ Status = ObInsertObject(ObBody[i], NULL, Access[i], 0, &ObBody[i], &ObHandle1[i]);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ ok(ObBody[i] != NULL, "Object body = NULL\n");
+ ok(ObHandle1[i] != NULL, "Handle = NULL\n");
+ CheckObject(ObHandle1[i], 3LU, 1LU);
+ CheckCounts(SaveCounts.Open + 1, SaveCounts.Close, SaveCounts.Delete, SaveCounts.Parse, SaveCounts.OkayToClose, SaveCounts.QueryName);
+ SaveCounts(SaveCounts);
+ CheckObject(DirectoryHandle, 4LU + i, 1LU);
+ }
+
+ //DPRINT1("%d %d %d %d %d %d %d\n", DumpCount, OpenCount, CloseCount, DeleteCount, ParseCount, OkayToCloseCount, QueryNameCount);
+ CheckCounts(SaveCounts.Open, SaveCounts.Close, SaveCounts.Delete, SaveCounts.Parse, SaveCounts.OkayToClose, SaveCounts.QueryName);
+ }
+
+ static
+ VOID
+ ObtClose(
+ BOOLEAN Clean,
+ BOOLEAN AlternativeMethod)
+ {
+ NTSTATUS Status;
+ LONG_PTR Ret;
+ PVOID TypeObject;
+ INT i;
+ UNICODE_STRING ObPathName[NUM_OBTYPES];
+ WCHAR Name[MAX_PATH];
+
+ // Close what we have opened and free what we allocated
+ for (i = 0; i < NUM_OBTYPES2; ++i)
+ {
+ if (!skip(ObBody[i] != NULL, "Nothing to dereference\n"))
+ {
+ if (ObHandle1[i]) CheckObject(ObHandle1[i], 3LU, 1LU);
+ Ret = ObDereferenceObject(ObBody[i]);
+ ok_eq_longptr(Ret, (LONG_PTR)1);
+ if (ObHandle1[i]) CheckObject(ObHandle1[i], 2LU, 1LU);
+ ObBody[i] = NULL;
+ }
+ if (!skip(ObHandle1[i] != NULL, "Nothing to close\n"))
+ {
+ Status = ZwClose(ObHandle1[i]);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ ObHandle1[i] = NULL;
+ }
+ }
+
+ if (skip(Clean, "Not cleaning up, as requested. Use ObTypeClean to clean up\n"))
+ return;
+
+ // Now we have to get rid of a directory object
+ // Since it is permanent, we have to firstly make it temporary
+ // and only then kill
+ // (this procedure is described in DDK)
+ if (!skip(DirectoryHandle != NULL, "No directory handle\n"))
+ {
+ CheckObject(DirectoryHandle, 3LU, 1LU);
+
+ Status = ZwMakeTemporaryObject(DirectoryHandle);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ CheckObject(DirectoryHandle, 3LU, 1LU);
+
+ Status = ZwClose(DirectoryHandle);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ }
+
+ /* we don't delete the object types we created. It makes Windows unstable.
+ * TODO: perhaps make it work in ROS anyway */
+ return;
+ if (!AlternativeMethod)
+ {
+ for (i = 0; i < NUM_OBTYPES; ++i)
+ if (!skip(ObTypes[i] != NULL, "No object type to delete\n"))
+ {
+ Ret = ObDereferenceObject(ObTypes[i]);
+ ok_eq_longptr(Ret, (LONG_PTR)0);
+ ObTypes[i] = NULL;
+ }
+ }
+ else
+ {
+ for (i = 0; i < NUM_OBTYPES; ++i)
+ {
+ if (!skip(ObTypes[i] != NULL, "No object type to delete\n"))
+ {
+ Status = RtlStringCbPrintfW(Name, sizeof Name, L"\\ObjectTypes\\MyObjectType%d", i);
+ RtlInitUnicodeString(&ObPathName[i], Name);
+ Status = ObReferenceObjectByName(&ObPathName[i], OBJ_CASE_INSENSITIVE, NULL, 0L, NULL, KernelMode, NULL, &TypeObject);
+
+ Ret = ObDereferenceObject(TypeObject);
+ ok_eq_longptr(Ret, (LONG_PTR)2);
+ Ret = ObDereferenceObject(TypeObject);
+ ok_eq_longptr(Ret, (LONG_PTR)1);
+ DPRINT("Reference Name %wZ = %p, ObTypes[%d] = %p\n",
+ ObPathName[i], TypeObject, i, ObTypes[i]);
+ ObTypes[i] = NULL;
+ }
+ }
+ }
+ }
+
+ static
+ VOID
+ TestObjectType(
+ IN BOOLEAN Clean)
+ {
+ RtlZeroMemory(&Counts, sizeof Counts);
+
+ ObtCreateObjectTypes();
+ DPRINT("ObtCreateObjectTypes() done\n");
+
+ ObtCreateDirectory();
+ DPRINT("ObtCreateDirectory() done\n");
+
+ if (!skip(ObTypes[0] != NULL, "No object types!\n"))
+ ObtCreateObjects();
+ DPRINT("ObtCreateObjects() done\n");
+
+ ObtClose(Clean, FALSE);
+ }
+
+ START_TEST(ObType)
+ {
+ TestObjectType(TRUE);
+ }
+
+ /* run this to see the objects created in user mode */
+ START_TEST(ObTypeNoClean)
+ {
+ TestObjectType(FALSE);
+ }
+
+ /* run this to clean up after ObTypeNoClean */
+ START_TEST(ObTypeClean)
+ {
+ ObtClose(TRUE, FALSE);
+ }
--- /dev/null
+ This directory contains the ReactOS Kernel-Mode Test Suite.
+
+ The kmtest, kmtest_drv and include subdirectories contain the
+ testing framework infrastructure, other directories contain tests.
+
+ The example subdirectory contains a set of small tests that can be used as
+ examples and templates.
+
+
+ See http://www.reactos.org/wiki/User:ThFabba/KmtestsHowto for more
+ information and a guide on how to use the framework.
--- /dev/null
+ /* this is a little hacky, but better than duplicating the code (for now) */
+ #define RTL_USE_AVL_TABLES
+ #define Test_RtlSplayTree Test_RtlAvlTree
+ #include "RtlSplayTree.c"
--- /dev/null
+ /*
+ * PROJECT: ReactOS kernel-mode tests
+ * LICENSE: GPLv2+ - See COPYING in the top level directory
+ * PURPOSE: Kernel-Mode Test Suite Runtime library memory functions test
+ * PROGRAMMER: Thomas Faber <thfabba@gmx.de>
+ */
+
+ #include <stddef.h>
+ __declspec(dllimport) void __stdcall RtlMoveMemory(void *, const void *, size_t);
+ __declspec(dllimport) void __stdcall RtlFillMemory(void *, size_t, unsigned char);
+
+ #define KMT_EMULATE_KERNEL
+ #include <kmt_test.h>
+
+ #ifdef __GNUC__
+ #pragma GCC diagnostic ignored "-Wnonnull"
+ #endif /* defined __GNUC__ */
+
+ static
+ VOID
+ MakeBuffer(
+ OUT PVOID Buffer,
+ ...)
+ {
+ PUCHAR OutBuffer = Buffer;
+ INT Count;
+ INT Value;
+ va_list Arguments;
+
+ va_start(Arguments, Buffer);
+
+ while (1)
+ {
+ Count = va_arg(Arguments, INT);
+ if (!Count)
+ break;
+ ASSERT(Count > 0);
+
+ Value = va_arg(Arguments, INT);
+ while (Count--)
+ *OutBuffer++ = Value;
+ }
+
+ va_end(Arguments);
+ }
+
+ static
+ BOOLEAN
+ CheckBuffer(
+ IN const VOID *Buffer,
+ ...)
+ {
+ PCUCHAR OutBuffer = Buffer;
+ INT Count;
+ INT Value;
+ va_list Arguments;
+
+ va_start(Arguments, Buffer);
+
+ while (1)
+ {
+ Count = va_arg(Arguments, INT);
+ if (!Count)
+ break;
+ ASSERT(Count > 0);
+
+ Value = va_arg(Arguments, INT);
+ while (Count--)
+ if (*OutBuffer++ != Value)
+ {
+ --OutBuffer;
+ trace("CheckBuffer failed at offset %d, value %x, expected %x\n", OutBuffer - (PCUCHAR)Buffer, *OutBuffer, Value);
+ return FALSE;
+ }
+ }
+
+ va_end(Arguments);
+ return TRUE;
+ }
+
+ static
+ VOID
+ MakePattern(
+ OUT PVOID Buffer,
+ ...)
+ {
+ PUCHAR OutBuffer = Buffer;
+ INT Count, Repeat, i;
+ INT Values[16];
+ va_list Arguments;
+
+ va_start(Arguments, Buffer);
+
+ while (1)
+ {
+ Count = va_arg(Arguments, INT);
+ if (!Count)
+ break;
+ ASSERT(Count > 0 && Count < sizeof Values / sizeof Values[0]);
+
+ Repeat = va_arg(Arguments, INT);
+ ASSERT(Repeat > 0);
+
+ for (i = 0; i < Count; ++i)
+ Values[i] = va_arg(Arguments, INT);
+
+ while (Repeat--)
+ for (i = 0; i < Count; ++i)
+ *OutBuffer++ = Values[i];
+ }
+
+ va_end(Arguments);
+ }
+
+ static
+ BOOLEAN
+ CheckPattern(
+ IN const VOID *Buffer,
+ ...)
+ {
+ PCUCHAR OutBuffer = Buffer;
+ INT Count, Repeat, i;
+ INT Values[16];
+ va_list Arguments;
+
+ va_start(Arguments, Buffer);
+
+ while (1)
+ {
+ Count = va_arg(Arguments, INT);
+ if (!Count)
+ break;
+ ASSERT(Count > 0 && Count < sizeof Values / sizeof Values[0]);
+
+ Repeat = va_arg(Arguments, INT);
+ ASSERT(Repeat > 0);
+
+ for (i = 0; i < Count; ++i)
+ Values[i] = va_arg(Arguments, INT);
+
+ while (Repeat--)
+ for (i = 0; i < Count; ++i)
+ if (*OutBuffer++ != Values[i])
+ {
+ --OutBuffer;
+ trace("CheckPattern failed at offset %d, value %x, expected %x\n", OutBuffer - (PCUCHAR)Buffer, *OutBuffer, Values[i]);
+ return FALSE;
+ }
+ }
+
+ va_end(Arguments);
+ return TRUE;
+ }
+
+ START_TEST(RtlMemory)
+ {
+ NTSTATUS Status;
+ UCHAR Buffer[513];
+ const SIZE_T Size = 512;
+ const SIZE_T HalfSize = Size / 2;
+ SIZE_T RetSize;
+ KIRQL Irql;
+ ULONG i;
+
+ KeRaiseIrql(HIGH_LEVEL, &Irql);
+ /* zero everything behind 'Size'. Tests will check that this wasn't changed.
+ * TODO: use guarded memory for this! */
+ MakeBuffer(Buffer + Size, sizeof Buffer - Size, 0, 0);
+
+ /* test our helper functions first */
+ MakeBuffer(Buffer, HalfSize, 0x55, HalfSize, 0xAA, 0);
+ for (i = 0; i < HalfSize; ++i)
+ ok_eq_uint(Buffer[i], 0x55);
+ for (i = HalfSize; i < Size; ++i)
+ ok_eq_uint(Buffer[i], 0xAA);
+ ok_bool_true(CheckBuffer(Buffer, HalfSize, 0x55, HalfSize, 0xAA, 1, 0, 0), "CheckBuffer");
+
+ MakePattern(Buffer, 3, 20, 0x11, 0x22, 0x33, 1, 4, 0x44, 0);
+ for (i = 0; i < 60; i += 3)
+ {
+ ok_eq_uint(Buffer[i+0], 0x11);
+ ok_eq_uint(Buffer[i+1], 0x22);
+ ok_eq_uint(Buffer[i+2], 0x33);
+ }
+ for (i = 60; i < 64; ++i)
+ ok_eq_uint(Buffer[i], 0x44);
+ for (i = 64; i < HalfSize; ++i)
+ ok_eq_uint(Buffer[i], 0x55);
+ for (i = HalfSize; i < Size; ++i)
+ ok_eq_uint(Buffer[i], 0xAA);
+ ok_bool_true(CheckPattern(Buffer, 3, 20, 0x11, 0x22, 0x33, 1, 4, 0x44, 0), "CheckPattern");
+
+ /* RtlMoveMemory */
+ MakePattern(Buffer, 2, 64, 0x12, 0x34, 2, 192, 0x56, 0x78, 0);
+ RtlMoveMemory(Buffer + 13, Buffer + 62, 95);
+ ok_bool_true(CheckPattern(Buffer, 2, 6, 0x12, 0x34, 1, 1, 0x12, 2, 33, 0x12, 0x34, 2, 14, 0x56, 0x78, 1, 1, 0x56, 2, 10, 0x12, 0x34, 2, 192, 0x56, 0x78, 1, 1, 0, 0), "CheckPattern");
+
+ MakePattern(Buffer, 2, 32, 0x12, 0x34, 2, 32, 0x56, 0x78, 2, 192, 0x9A, 0xAB, 0);
+ RtlMoveMemory(Buffer + 78, Buffer + 43, 107);
+ ok_bool_true(CheckPattern(Buffer, 2, 32, 0x12, 0x34, 2, 7, 0x56, 0x78, 1, 1, 0x34, 2, 10, 0x12, 0x34, 2, 32, 0x56, 0x78, 2, 11, 0x9A, 0xAB, 1, 1, 0xAB, 2, 163, 0x9A, 0xAB, 1, 1, 0, 0), "CheckPattern");
+
+ KeLowerIrql(Irql);
+ Status = STATUS_SUCCESS;
+ _SEH2_TRY {
+ RtlMoveMemory(NULL, NULL, 0);
+ } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
+ Status = _SEH2_GetExceptionCode();
+ } _SEH2_END;
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ KeRaiseIrql(HIGH_LEVEL, &Irql);
+
+ #undef RtlMoveMemory
+ /* RtlMoveMemory export */
+ MakePattern(Buffer, 2, 64, 0x12, 0x34, 2, 192, 0x56, 0x78, 0);
+ RtlMoveMemory(Buffer + 13, Buffer + 62, 95);
+ ok_bool_true(CheckPattern(Buffer, 2, 6, 0x12, 0x34, 1, 1, 0x12, 2, 33, 0x12, 0x34, 2, 14, 0x56, 0x78, 1, 1, 0x56, 2, 10, 0x12, 0x34, 2, 192, 0x56, 0x78, 1, 1, 0, 0), "CheckPattern");
+
+ MakePattern(Buffer, 2, 32, 0x12, 0x34, 2, 32, 0x56, 0x78, 2, 192, 0x9A, 0xAB, 0);
+ RtlMoveMemory(Buffer + 78, Buffer + 43, 107);
+ ok_bool_true(CheckPattern(Buffer, 2, 32, 0x12, 0x34, 2, 7, 0x56, 0x78, 1, 1, 0x34, 2, 10, 0x12, 0x34, 2, 32, 0x56, 0x78, 2, 11, 0x9A, 0xAB, 1, 1, 0xAB, 2, 163, 0x9A, 0xAB, 1, 1, 0, 0), "CheckPattern");
+
+ KeLowerIrql(Irql);
+ Status = STATUS_SUCCESS;
+ _SEH2_TRY {
+ RtlMoveMemory(NULL, NULL, 0);
+ } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
+ Status = _SEH2_GetExceptionCode();
+ } _SEH2_END;
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ KeRaiseIrql(HIGH_LEVEL, &Irql);
+
+ /* RtlCopyMemory */
+ MakePattern(Buffer, 2, 64, 0x12, 0x34, 2, 192, 0x56, 0x78, 0);
+ RtlCopyMemory(Buffer + 13, Buffer + 62, 95);
+ ok_bool_true(CheckPattern(Buffer, 2, 6, 0x12, 0x34, 1, 1, 0x12, 2, 33, 0x12, 0x34, 2, 14, 0x56, 0x78, 1, 1, 0x56, 2, 10, 0x12, 0x34, 2, 192, 0x56, 0x78, 1, 1, 0, 0), "CheckPattern");
+
+ MakePattern(Buffer, 2, 32, 0x12, 0x34, 2, 32, 0x56, 0x78, 2, 192, 0x9A, 0xAB, 0);
+ RtlCopyMemory(Buffer + 78, Buffer + 43, 107);
+ ok_bool_true(CheckPattern(Buffer, 2, 32, 0x12, 0x34, 2, 7, 0x56, 0x78, 1, 1, 0x34, 2, 10, 0x12, 0x34, 2, 32, 0x56, 0x78, 2, 11, 0x9A, 0xAB, 1, 1, 0xAB, 2, 163, 0x9A, 0xAB, 1, 1, 0, 0), "CheckPattern");
+
+ KeLowerIrql(Irql);
+ Status = STATUS_SUCCESS;
+ _SEH2_TRY {
+ RtlCopyMemory(NULL, NULL, 0);
+ } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
+ Status = _SEH2_GetExceptionCode();
+ } _SEH2_END;
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ KeRaiseIrql(HIGH_LEVEL, &Irql);
+
+ /* RtlCopyMemoryNonTemporal */
+ MakePattern(Buffer, 2, 64, 0x12, 0x34, 2, 192, 0x56, 0x78, 0);
+ RtlCopyMemoryNonTemporal(Buffer + 13, Buffer + 62, 95);
+ ok_bool_true(CheckPattern(Buffer, 2, 6, 0x12, 0x34, 1, 1, 0x12, 2, 33, 0x12, 0x34, 2, 14, 0x56, 0x78, 1, 1, 0x56, 2, 10, 0x12, 0x34, 2, 192, 0x56, 0x78, 1, 1, 0, 0), "CheckPattern");
+
+ MakePattern(Buffer, 2, 32, 0x12, 0x34, 2, 32, 0x56, 0x78, 2, 192, 0x9A, 0xAB, 0);
+ RtlCopyMemoryNonTemporal(Buffer + 78, Buffer + 43, 107);
+ ok_bool_true(CheckPattern(Buffer, 2, 32, 0x12, 0x34, 2, 7, 0x56, 0x78, 1, 1, 0x34, 2, 10, 0x12, 0x34, 2, 32, 0x56, 0x78, 2, 11, 0x9A, 0xAB, 1, 1, 0xAB, 2, 163, 0x9A, 0xAB, 1, 1, 0, 0), "CheckPattern");
+
+ KeLowerIrql(Irql);
+ Status = STATUS_SUCCESS;
+ _SEH2_TRY {
+ RtlCopyMemoryNonTemporal(NULL, NULL, 0);
+ } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
+ Status = _SEH2_GetExceptionCode();
+ } _SEH2_END;
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ KeRaiseIrql(HIGH_LEVEL, &Irql);
+
+ /* RtlCopyBytes */
+ MakePattern(Buffer, 2, 64, 0x12, 0x34, 2, 192, 0x56, 0x78, 0);
+ RtlCopyBytes(Buffer + 13, Buffer + 62, 95);
+ ok_bool_true(CheckPattern(Buffer, 2, 6, 0x12, 0x34, 1, 1, 0x12, 2, 33, 0x12, 0x34, 2, 14, 0x56, 0x78, 1, 1, 0x56, 2, 10, 0x12, 0x34, 2, 192, 0x56, 0x78, 1, 1, 0, 0), "CheckPattern");
+
+ MakePattern(Buffer, 2, 32, 0x12, 0x34, 2, 32, 0x56, 0x78, 2, 192, 0x9A, 0xAB, 0);
+ RtlCopyBytes(Buffer + 78, Buffer + 43, 107);
+ ok_bool_true(CheckPattern(Buffer, 2, 32, 0x12, 0x34, 2, 7, 0x56, 0x78, 1, 1, 0x34, 2, 10, 0x12, 0x34, 2, 32, 0x56, 0x78, 2, 11, 0x9A, 0xAB, 1, 1, 0xAB, 2, 163, 0x9A, 0xAB, 1, 1, 0, 0), "CheckPattern");
+
+ KeLowerIrql(Irql);
+ Status = STATUS_SUCCESS;
+ _SEH2_TRY {
+ RtlCopyBytes(NULL, NULL, 0);
+ } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
+ Status = _SEH2_GetExceptionCode();
+ } _SEH2_END;
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ KeRaiseIrql(HIGH_LEVEL, &Irql);
+
+ /* RtlEqualMemory */
+ /* TODO: where is memcmp? */
+
+ /* RtlCompareMemory */
+ MakePattern(Buffer, 8, HalfSize / 8 - 1, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0,
+ 1, 1, 0x12,
+ 8, HalfSize / 8, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0,
+ 1, 7, 0x12, 0);
+
+ RetSize = RtlCompareMemory(Buffer, Buffer + HalfSize - 7, HalfSize - 8);
+ ok_eq_size(RetSize, HalfSize - 8);
+ RetSize = RtlCompareMemory(Buffer, Buffer + HalfSize - 7, HalfSize - 8 + 1);
+ ok_eq_size(RetSize, HalfSize - 8 + 1);
+ RetSize = RtlCompareMemory(Buffer, Buffer + HalfSize - 7, HalfSize - 8 + 2);
+ ok_eq_size(RetSize, HalfSize - 8 + 1);
+
+ KeLowerIrql(Irql);
+ Status = STATUS_SUCCESS;
+ _SEH2_TRY {
+ RetSize = RtlCompareMemory(Buffer, Buffer + HalfSize - 7, SIZE_MAX);
+ ok_eq_size(RetSize, HalfSize - 8 + 1);
+ RetSize = RtlCompareMemory(NULL, NULL, 0);
+ ok_eq_size(RetSize, 0);
+ } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
+ Status = _SEH2_GetExceptionCode();
+ } _SEH2_END;
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ KeRaiseIrql(HIGH_LEVEL, &Irql);
+
+ /* RtlCompareMemoryUlong */
+ MakeBuffer(Buffer, 8, 0x55, Size - 4, 0, 0);
+ RetSize = RtlCompareMemoryUlong(Buffer, sizeof(ULONG), 0x55555555LU);
+ ok_eq_size(RetSize, 4);
+ RetSize = RtlCompareMemoryUlong(Buffer + 1, sizeof(ULONG), 0x55555555LU);
+ ok_eq_size(RetSize, 4);
+ RetSize = RtlCompareMemoryUlong(Buffer + 2, sizeof(ULONG), 0x55555555LU);
+ ok_eq_size(RetSize, 4);
+ RetSize = RtlCompareMemoryUlong(Buffer + 3, sizeof(ULONG), 0x55555555LU);
+ ok_eq_size(RetSize, 4);
+ RetSize = RtlCompareMemoryUlong(Buffer + 5, sizeof(ULONG), 0x55555555LU);
+ ok_eq_size(RetSize, 0);
+ RetSize = RtlCompareMemoryUlong(Buffer + 5, sizeof(ULONG), 0x00555555LU);
+ ok_eq_size(RetSize, 4);
+ RetSize = RtlCompareMemoryUlong(Buffer, 1, 0x55555555LU);
+ ok_eq_size(RetSize, 0);
+ RetSize = RtlCompareMemoryUlong(Buffer, 2, 0x55555555LU);
+ ok_eq_size(RetSize, 0);
+ RetSize = RtlCompareMemoryUlong(Buffer, 3, 0x55555555LU);
+ ok_eq_size(RetSize, 0);
+ RetSize = RtlCompareMemoryUlong(Buffer, 5, 0x55555555LU);
+ ok_eq_size(RetSize, 4);
+
+ KeLowerIrql(Irql);
+ Status = STATUS_SUCCESS;
+ _SEH2_TRY {
+ RetSize = RtlCompareMemoryUlong(NULL, 0, 0x55555555LU);
+ ok_eq_size(RetSize, 0);
+ } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
+ Status = _SEH2_GetExceptionCode();
+ } _SEH2_END;
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ KeRaiseIrql(HIGH_LEVEL, &Irql);
+
+ /* RtlZeroMemory */
+ MakeBuffer(Buffer, Size, 0x11, 0);
+ RtlZeroMemory(Buffer, 1);
+ ok_bool_true(CheckBuffer(Buffer, 1, 0, Size - 1, 0x11, 1, 0, 0), "CheckBuffer");
+ Buffer[0] = 0x11;
+ RtlZeroMemory(Buffer, Size - 1);
+ ok_bool_true(CheckBuffer(Buffer, Size - 1, 0, 1, 0x11, 1, 0, 0), "CheckBuffer");
+
+ KeLowerIrql(Irql);
+ Status = STATUS_SUCCESS;
+ _SEH2_TRY {
+ RtlZeroMemory(NULL, 0);
+ } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
+ Status = _SEH2_GetExceptionCode();
+ } _SEH2_END;
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ KeRaiseIrql(HIGH_LEVEL, &Irql);
+
+ /* RtlSecureZeroMemory */
+ MakeBuffer(Buffer, Size, 0x11, 0);
+ RtlSecureZeroMemory(Buffer, 1);
+ ok_bool_true(CheckBuffer(Buffer, 1, 0, Size - 1, 0x11, 1, 0, 0), "CheckBuffer");
+ Buffer[0] = 0x11;
+ RtlSecureZeroMemory(Buffer, Size - 1);
+ ok_bool_true(CheckBuffer(Buffer, Size - 1, 0, 1, 0x11, 1, 0, 0), "CheckBuffer");
+
+ KeLowerIrql(Irql);
+ Status = STATUS_SUCCESS;
+ _SEH2_TRY {
+ RtlSecureZeroMemory(NULL, 0);
+ } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
+ Status = _SEH2_GetExceptionCode();
+ } _SEH2_END;
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ KeRaiseIrql(HIGH_LEVEL, &Irql);
+
+ /* RtlZeroBytes */
+ MakeBuffer(Buffer, Size, 0x11, 0);
+ RtlZeroBytes(Buffer, 1);
+ ok_bool_true(CheckBuffer(Buffer, 1, 0, Size - 1, 0x11, 1, 0, 0), "CheckBuffer");
+ Buffer[0] = 0x11;
+ RtlZeroBytes(Buffer, Size - 1);
+ ok_bool_true(CheckBuffer(Buffer, Size - 1, 0, 1, 0x11, 1, 0, 0), "CheckBuffer");
+
+ KeLowerIrql(Irql);
+ Status = STATUS_SUCCESS;
+ _SEH2_TRY {
+ RtlZeroBytes(NULL, 0);
+ } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
+ Status = _SEH2_GetExceptionCode();
+ } _SEH2_END;
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ KeRaiseIrql(HIGH_LEVEL, &Irql);
+
+ /* RtlFillMemory */
+ MakeBuffer(Buffer, Size, 0, 0);
+ RtlFillMemory(Buffer, HalfSize, 0x55);
+ RtlFillMemory(Buffer + HalfSize, HalfSize, 0xAA);
+ ok_bool_true(CheckBuffer(Buffer, HalfSize, 0x55, HalfSize, 0xAA, 1, 0, 0), "CheckBuffer");
+ RtlFillMemory(Buffer + 3, 7, 0x88);
+ ok_bool_true(CheckBuffer(Buffer, 3, 0x55, 7, 0x88, HalfSize - 10, 0x55, HalfSize, 0xAA, 1, 0, 0), "CheckBuffer");
+
+ KeLowerIrql(Irql);
+ Status = STATUS_SUCCESS;
+ _SEH2_TRY {
+ RtlFillMemory(NULL, 0, 0x55);
+ } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
+ Status = _SEH2_GetExceptionCode();
+ } _SEH2_END;
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ KeRaiseIrql(HIGH_LEVEL, &Irql);
+
+ #undef RtlFillMemory
+ /* RtlFillMemory export */
+ MakeBuffer(Buffer, Size, 0, 0);
+ RtlFillMemory(Buffer, HalfSize, 0x55);
+ RtlFillMemory(Buffer + HalfSize, HalfSize, 0xAA);
+ ok_bool_true(CheckBuffer(Buffer, HalfSize, 0x55, HalfSize, 0xAA, 1, 0, 0), "CheckBuffer");
+ RtlFillMemory(Buffer + 3, 7, 0x88);
+ ok_bool_true(CheckBuffer(Buffer, 3, 0x55, 7, 0x88, HalfSize - 10, 0x55, HalfSize, 0xAA, 1, 0, 0), "CheckBuffer");
+
+ KeLowerIrql(Irql);
+ Status = STATUS_SUCCESS;
+ _SEH2_TRY {
+ RtlFillMemory(NULL, 0, 0x55);
+ } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
+ Status = _SEH2_GetExceptionCode();
+ } _SEH2_END;
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ KeRaiseIrql(HIGH_LEVEL, &Irql);
+
+ /* RtlFillMemoryUlong */
+ MakeBuffer(Buffer, Size, 0, 0);
+ RtlFillMemoryUlong(Buffer, HalfSize, 0x01234567LU);
+ RtlFillMemoryUlong(Buffer + HalfSize, HalfSize, 0x89ABCDEFLU);
+ ok_bool_true(CheckPattern(Buffer, 4, HalfSize / 4, 0x67, 0x45, 0x23, 0x01, 4, HalfSize / 4, 0xEF, 0xCD, 0xAB, 0x89, 1, 1, 0, 0), "CheckPattern");
+
+ KeLowerIrql(Irql);
+ Status = STATUS_SUCCESS;
+ _SEH2_TRY {
+ MakeBuffer(Buffer, Size, 0, 0);
+ RtlFillMemoryUlong(Buffer + 1, sizeof(ULONG), 0xAAAAAAAALU);
+ ok_bool_true(CheckBuffer(Buffer, 1, 0, sizeof(ULONG), 0xAA, Size - sizeof(ULONG) - 1, 0, 1, 0, 0), "CheckBuffer");
+
+ RtlFillMemoryUlong(NULL, 0, 0x55555555LU);
+ } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
+ Status = _SEH2_GetExceptionCode();
+ } _SEH2_END;
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ KeRaiseIrql(HIGH_LEVEL, &Irql);
+
+ /* RtlFillMemoryUlonglong */
+ /* TODO: this function doesn't exist in 2k3/x86? wdm.h error? */
+
+ /* RtlFillBytes */
+ MakeBuffer(Buffer, Size, 0, 0);
+ RtlFillBytes(Buffer, HalfSize, 0x55);
+ RtlFillBytes(Buffer + HalfSize, HalfSize, 0xAA);
+ ok_bool_true(CheckBuffer(Buffer, HalfSize, 0x55, HalfSize, 0xAA, 1, 0, 0), "CheckBuffer");
+ RtlFillBytes(Buffer + 3, 7, 0x88);
+ ok_bool_true(CheckBuffer(Buffer, 3, 0x55, 7, 0x88, HalfSize - 10, 0x55, HalfSize, 0xAA, 1, 0, 0), "CheckBuffer");
+
+ KeLowerIrql(Irql);
+ Status = STATUS_SUCCESS;
+ _SEH2_TRY {
+ RtlFillBytes(NULL, 0, 0x55);
+ } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
+ Status = _SEH2_GetExceptionCode();
+ } _SEH2_END;
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ KeRaiseIrql(HIGH_LEVEL, &Irql);
+
+ /* RtlPrefetchMemoryNonTemporal */
+ RtlPrefetchMemoryNonTemporal(Buffer, Size);
+
+ KeLowerIrql(Irql);
+ Status = STATUS_SUCCESS;
+ _SEH2_TRY {
+ RtlPrefetchMemoryNonTemporal(NULL, 0);
+ } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
+ Status = _SEH2_GetExceptionCode();
+ } _SEH2_END;
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ KeRaiseIrql(HIGH_LEVEL, &Irql);
+
+ KeLowerIrql(Irql);
+ }
--- /dev/null
+ /*
+ * PROJECT: ReactOS kernel-mode tests
+ * LICENSE: LGPLv2+ - See COPYING.LIB in the top level directory
+ * PURPOSE: Kernel-Mode Test Suite RtlGenericTable
+ * PROGRAMMER: arty
+ */
+
+ #define KMT_EMULATE_KERNEL
+ #include <kmt_test.h>
+
+ #define NDEBUG
+ #include <debug.h>
+
+ static LIST_ENTRY Allocations;
+
+ static RTL_GENERIC_COMPARE_RESULTS NTAPI
+ CompareCharTable(PRTL_GENERIC_TABLE Table, PVOID A, PVOID B)
+ {
+ RTL_GENERIC_COMPARE_RESULTS Result = (*((PCHAR)A) < *((PCHAR)B)) ? GenericLessThan :
+ (*((PCHAR)A) > *((PCHAR)B)) ? GenericGreaterThan :
+ GenericEqual;
+ return Result;
+ }
+
+ static PVOID NTAPI
+ AllocRoutine(PRTL_GENERIC_TABLE Table, CLONG ByteSize)
+ {
+ PLIST_ENTRY Entry = ExAllocatePool
+ (NonPagedPool, sizeof(LIST_ENTRY) + ByteSize);
+ InsertTailList(&Allocations, Entry);
+ return &Entry[1];
+ }
+
+ static VOID NTAPI
+ FreeRoutine(PRTL_GENERIC_TABLE Table, PVOID Buffer)
+ {
+ PLIST_ENTRY Entry = (PLIST_ENTRY)(((PCHAR)Buffer) - sizeof(LIST_ENTRY));
+ RemoveEntryList(Entry);
+ ExFreePool(Entry);
+ }
+
+ static void RtlSplayTreeTest()
+ {
+ ULONG i, del;
+ PCHAR Ch;
+ CHAR Text[] = "the quick_brown!fOx-jUmp3d/0vER+THe^lazy.D@g";
+ CHAR NewE[] = "11111111111111111111111111111111110111111111";
+ RTL_GENERIC_TABLE Table;
+ RtlInitializeGenericTable
+ (&Table,
+ CompareCharTable,
+ AllocRoutine,
+ FreeRoutine,
+ NULL);
+ for (i = 0; Text[i]; i++) {
+ BOOLEAN WasNew;
+ Ch = (PCHAR)RtlInsertElementGenericTable
+ (&Table,
+ &Text[i],
+ sizeof(Text[i]),
+ &WasNew);
+ ok(Ch && *Ch == Text[i], "Copy character into node\n");
+ ok(WasNew == (NewE[i] == '1'), "Character newness didn't match\n");
+ }
+ for (Ch = (PCHAR)RtlEnumerateGenericTable(&Table, TRUE), i = 0;
+ Ch;
+ Ch = (PCHAR)RtlEnumerateGenericTable(&Table, FALSE), i++) {
+ ok(strchr(Text, *Ch) != NULL, "Nonexistent character\n");
+ }
+ ok(RtlNumberGenericTableElements(&Table) == strlen(Text) - 1, "Not the right number of elements\n");
+ ok(RtlLookupElementGenericTable(&Table, "q") != NULL, "Could not lookup q\n");
+ ok(!RtlLookupElementGenericTable(&Table, "#"), "Found a character that shouldn't appear\n");
+ ok(strlen(Text) == i + 1, "Didn't enumerate enough characters\n");
+ del = 0;
+ for (i = 0; Text[i]; i++) {
+ if (NewE[i] == '1') {
+ BOOLEAN WasDeleted;
+ WasDeleted = RtlDeleteElementGenericTable(&Table, &Text[i]);
+ del += WasDeleted;
+ }
+ }
+ ok(!RtlNumberGenericTableElements(&Table), "Not zero elements\n");
+ ok(!RtlGetElementGenericTable(&Table, 0), "Elements left when we removed them all\n");
+ ok(strlen(Text) == del + 1, "Deleted too many times\n");
+ ok(IsListEmpty(&Allocations), "Didn't free all memory\n");
+ }
+
+ START_TEST(RtlSplayTree)
+ {
+ InitializeListHead(&Allocations);
+ RtlSplayTreeTest();
+ }
<directory name="kernel32">
<xi:include href="kernel32/directory.rbuild" />
</directory>
- <directory name="kmtloader">
- <xi:include href="kmtloader/kmtloader.rbuild" />
- </directory>
<directory name="smss">
<xi:include href="smss/smss.rbuild" />
</directory>