2 * PROJECT: ReactOS kernel-mode tests
3 * LICENSE: GPLv2+ - See COPYING in the top level directory
4 * PURPOSE: Kernel-Mode Test Suite Driver
5 * PROGRAMMER: Thomas Faber <thomas.faber@reactos.org>
10 #include <ndk/ketypes.h>
11 #include <ntstrsafe.h>
13 #include <pseh/pseh2.h>
18 #include <kmt_public.h>
19 #define KMT_DEFINE_TEST_FUNCTIONS
22 /* Usermode callback definitions */
23 typedef struct _KMT_USER_WORK_ENTRY
27 KMT_CALLBACK_REQUEST_PACKET Request
;
28 PKMT_RESPONSE Response
;
29 } KMT_USER_WORK_ENTRY
, *PKMT_USER_WORK_ENTRY
;
31 typedef struct _KMT_USER_WORK_LIST
36 } KMT_USER_WORK_LIST
, *PKMT_USER_WORK_LIST
;
39 DRIVER_INITIALIZE DriverEntry
;
40 static DRIVER_UNLOAD DriverUnload
;
41 __drv_dispatchType(IRP_MJ_CREATE
)
42 static DRIVER_DISPATCH DriverCreate
;
43 __drv_dispatchType(IRP_MJ_CLEANUP
)
44 static DRIVER_DISPATCH DriverCleanup
;
45 __drv_dispatchType(IRP_MJ_CLOSE
)
46 static DRIVER_DISPATCH DriverClose
;
47 __drv_dispatchType(IRP_MJ_DEVICE_CONTROL
)
48 static DRIVER_DISPATCH DriverIoControl
;
49 static VOID
KmtCleanUsermodeCallbacks(VOID
);
52 static PDEVICE_OBJECT MainDeviceObject
;
53 PDRIVER_OBJECT KmtDriverObject
= NULL
;
54 static KMT_USER_WORK_LIST WorkList
;
55 static ULONG RequestId
= 0;
66 * Driver Registry Path
73 IN PDRIVER_OBJECT DriverObject
,
74 IN PUNICODE_STRING RegistryPath
)
76 NTSTATUS Status
= STATUS_SUCCESS
;
77 UNICODE_STRING DeviceName
;
78 PKMT_DEVICE_EXTENSION DeviceExtension
;
83 UNREFERENCED_PARAMETER(RegistryPath
);
85 DPRINT("DriverEntry\n");
87 Prcb
= KeGetCurrentPrcb();
88 KmtIsCheckedBuild
= (Prcb
->BuildType
& PRCB_BUILD_DEBUG
) != 0;
89 KmtIsMultiProcessorBuild
= (Prcb
->BuildType
& PRCB_BUILD_UNIPROCESSOR
) == 0;
90 KmtDriverObject
= DriverObject
;
92 RtlInitUnicodeString(&DeviceName
, KMTEST_DEVICE_DRIVER_PATH
);
93 Status
= IoCreateDevice(DriverObject
, sizeof(KMT_DEVICE_EXTENSION
),
96 FILE_DEVICE_SECURE_OPEN
| FILE_READ_ONLY_DEVICE
,
97 FALSE
, &MainDeviceObject
);
99 if (!NT_SUCCESS(Status
))
102 DPRINT("DriverEntry. Created DeviceObject %p. DeviceExtension %p\n",
103 MainDeviceObject
, MainDeviceObject
->DeviceExtension
);
104 DeviceExtension
= MainDeviceObject
->DeviceExtension
;
105 DeviceExtension
->ResultBuffer
= NULL
;
106 DeviceExtension
->Mdl
= NULL
;
108 DriverObject
->DriverUnload
= DriverUnload
;
109 DriverObject
->MajorFunction
[IRP_MJ_CREATE
] = DriverCreate
;
110 DriverObject
->MajorFunction
[IRP_MJ_CLEANUP
] = DriverCleanup
;
111 DriverObject
->MajorFunction
[IRP_MJ_CLOSE
] = DriverClose
;
112 DriverObject
->MajorFunction
[IRP_MJ_DEVICE_CONTROL
] = DriverIoControl
;
114 ExInitializeFastMutex(&WorkList
.Lock
);
115 KeInitializeEvent(&WorkList
.NewWorkEvent
, NotificationEvent
, FALSE
);
116 InitializeListHead(&WorkList
.ListHead
);
119 if (MainDeviceObject
&& !NT_SUCCESS(Status
))
121 IoDeleteDevice(MainDeviceObject
);
122 MainDeviceObject
= NULL
;
128 /* Dispatch functions */
132 * Driver cleanup funtion.
134 * @param DriverObject
141 IN PDRIVER_OBJECT DriverObject
)
145 UNREFERENCED_PARAMETER(DriverObject
);
147 DPRINT("DriverUnload\n");
149 KmtCleanUsermodeCallbacks();
151 if (MainDeviceObject
)
154 PKMT_DEVICE_EXTENSION DeviceExtension
= MainDeviceObject
->DeviceExtension
;
155 ASSERT(!DeviceExtension
->Mdl
);
156 ASSERT(!DeviceExtension
->ResultBuffer
);
158 ASSERT(!ResultBuffer
);
159 IoDeleteDevice(MainDeviceObject
);
166 * Driver Dispatch function for IRP_MJ_CREATE
168 * @param DeviceObject
179 IN PDEVICE_OBJECT DeviceObject
,
182 NTSTATUS Status
= STATUS_SUCCESS
;
183 PIO_STACK_LOCATION IoStackLocation
;
187 IoStackLocation
= IoGetCurrentIrpStackLocation(Irp
);
189 DPRINT("DriverCreate. DeviceObject=%p, RequestorMode=%d, FileObject=%p, FsContext=%p, FsContext2=%p\n",
190 DeviceObject
, Irp
->RequestorMode
, IoStackLocation
->FileObject
,
191 IoStackLocation
->FileObject
->FsContext
, IoStackLocation
->FileObject
->FsContext2
);
193 Irp
->IoStatus
.Status
= Status
;
194 Irp
->IoStatus
.Information
= 0;
196 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
202 * @name DriverCleanup
204 * Driver Dispatch function for IRP_MJ_CLEANUP
206 * @param DeviceObject
217 IN PDEVICE_OBJECT DeviceObject
,
220 NTSTATUS Status
= STATUS_SUCCESS
;
221 PIO_STACK_LOCATION IoStackLocation
;
222 PKMT_DEVICE_EXTENSION DeviceExtension
;
226 IoStackLocation
= IoGetCurrentIrpStackLocation(Irp
);
228 DPRINT("DriverCleanup. DeviceObject=%p, RequestorMode=%d, FileObject=%p, FsContext=%p, FsContext2=%p\n",
229 DeviceObject
, Irp
->RequestorMode
, IoStackLocation
->FileObject
,
230 IoStackLocation
->FileObject
->FsContext
, IoStackLocation
->FileObject
->FsContext2
);
232 ASSERT(IoStackLocation
->FileObject
->FsContext2
== NULL
);
233 DeviceExtension
= DeviceObject
->DeviceExtension
;
234 if (DeviceExtension
->Mdl
&& IoStackLocation
->FileObject
->FsContext
== DeviceExtension
->Mdl
)
236 MmUnlockPages(DeviceExtension
->Mdl
);
237 IoFreeMdl(DeviceExtension
->Mdl
);
238 DeviceExtension
->Mdl
= NULL
;
239 ResultBuffer
= DeviceExtension
->ResultBuffer
= NULL
;
243 ASSERT(IoStackLocation
->FileObject
->FsContext
== NULL
);
246 Irp
->IoStatus
.Status
= Status
;
247 Irp
->IoStatus
.Information
= 0;
249 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
257 * Driver Dispatch function for IRP_MJ_CLOSE
259 * @param DeviceObject
270 IN PDEVICE_OBJECT DeviceObject
,
273 NTSTATUS Status
= STATUS_SUCCESS
;
277 DPRINT("DriverClose. DeviceObject=%p, RequestorMode=%d\n",
278 DeviceObject
, Irp
->RequestorMode
);
280 Irp
->IoStatus
.Status
= Status
;
281 Irp
->IoStatus
.Information
= 0;
283 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
289 * @name DriverIoControl
291 * Driver Dispatch function for IRP_MJ_DEVICE_CONTROL
293 * @param DeviceObject
304 IN PDEVICE_OBJECT DeviceObject
,
307 NTSTATUS Status
= STATUS_SUCCESS
;
308 PIO_STACK_LOCATION IoStackLocation
;
313 IoStackLocation
= IoGetCurrentIrpStackLocation(Irp
);
315 DPRINT("DriverIoControl. Code=0x%08X, DeviceObject=%p, FileObject=%p, FsContext=%p, FsContext2=%p\n",
316 IoStackLocation
->Parameters
.DeviceIoControl
.IoControlCode
,
317 DeviceObject
, IoStackLocation
->FileObject
,
318 IoStackLocation
->FileObject
->FsContext
, IoStackLocation
->FileObject
->FsContext2
);
320 switch (IoStackLocation
->Parameters
.DeviceIoControl
.IoControlCode
)
322 case IOCTL_KMTEST_GET_TESTS
:
324 PCKMT_TEST TestEntry
;
325 LPSTR OutputBuffer
= Irp
->AssociatedIrp
.SystemBuffer
;
326 size_t Remaining
= IoStackLocation
->Parameters
.DeviceIoControl
.OutputBufferLength
;
328 DPRINT("DriverIoControl. IOCTL_KMTEST_GET_TESTS, outlen=%lu\n",
329 IoStackLocation
->Parameters
.DeviceIoControl
.OutputBufferLength
);
331 for (TestEntry
= TestList
; TestEntry
->TestName
; ++TestEntry
)
333 RtlStringCbCopyExA(OutputBuffer
, Remaining
, TestEntry
->TestName
, &OutputBuffer
, &Remaining
, 0);
336 *OutputBuffer
++ = '\0';
342 *OutputBuffer
++ = '\0';
345 Length
= IoStackLocation
->Parameters
.DeviceIoControl
.OutputBufferLength
- Remaining
;
348 case IOCTL_KMTEST_RUN_TEST
:
350 ANSI_STRING TestName
;
351 PCKMT_TEST TestEntry
;
353 DPRINT("DriverIoControl. IOCTL_KMTEST_RUN_TEST, inlen=%lu, outlen=%lu\n",
354 IoStackLocation
->Parameters
.DeviceIoControl
.InputBufferLength
,
355 IoStackLocation
->Parameters
.DeviceIoControl
.OutputBufferLength
);
356 TestName
.Length
= TestName
.MaximumLength
= (USHORT
)min(IoStackLocation
->Parameters
.DeviceIoControl
.InputBufferLength
, USHRT_MAX
);
357 TestName
.Buffer
= Irp
->AssociatedIrp
.SystemBuffer
;
358 DPRINT("DriverIoControl. Run test: %Z\n", &TestName
);
360 for (TestEntry
= TestList
; TestEntry
->TestName
; ++TestEntry
)
362 ANSI_STRING EntryName
;
363 if (TestEntry
->TestName
[0] == '-')
364 RtlInitAnsiString(&EntryName
, TestEntry
->TestName
+ 1);
366 RtlInitAnsiString(&EntryName
, TestEntry
->TestName
);
368 if (!RtlCompareString(&TestName
, &EntryName
, FALSE
))
370 DPRINT1("DriverIoControl. Starting test %Z\n", &EntryName
);
371 TestEntry
->TestFunction();
372 DPRINT1("DriverIoControl. Finished test %Z\n", &EntryName
);
377 if (!TestEntry
->TestName
)
378 Status
= STATUS_OBJECT_NAME_INVALID
;
382 case IOCTL_KMTEST_SET_RESULTBUFFER
:
384 PKMT_DEVICE_EXTENSION DeviceExtension
= DeviceObject
->DeviceExtension
;
386 DPRINT("DriverIoControl. IOCTL_KMTEST_SET_RESULTBUFFER, buffer=%p, inlen=%lu, outlen=%lu\n",
387 IoStackLocation
->Parameters
.DeviceIoControl
.Type3InputBuffer
,
388 IoStackLocation
->Parameters
.DeviceIoControl
.InputBufferLength
,
389 IoStackLocation
->Parameters
.DeviceIoControl
.OutputBufferLength
);
391 if (DeviceExtension
->Mdl
)
393 if (IoStackLocation
->FileObject
->FsContext
!= DeviceExtension
->Mdl
)
395 Status
= STATUS_ACCESS_DENIED
;
398 MmUnlockPages(DeviceExtension
->Mdl
);
399 IoFreeMdl(DeviceExtension
->Mdl
);
400 IoStackLocation
->FileObject
->FsContext
= NULL
;
401 ResultBuffer
= DeviceExtension
->ResultBuffer
= NULL
;
404 DeviceExtension
->Mdl
= IoAllocateMdl(IoStackLocation
->Parameters
.DeviceIoControl
.Type3InputBuffer
,
405 IoStackLocation
->Parameters
.DeviceIoControl
.InputBufferLength
,
407 if (!DeviceExtension
->Mdl
)
409 Status
= STATUS_INSUFFICIENT_RESOURCES
;
415 MmProbeAndLockPages(DeviceExtension
->Mdl
, UserMode
, IoModifyAccess
);
417 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
419 Status
= _SEH2_GetExceptionCode();
420 IoFreeMdl(DeviceExtension
->Mdl
);
421 DeviceExtension
->Mdl
= NULL
;
425 ResultBuffer
= DeviceExtension
->ResultBuffer
= MmGetSystemAddressForMdlSafe(DeviceExtension
->Mdl
, NormalPagePriority
);
426 IoStackLocation
->FileObject
->FsContext
= DeviceExtension
->Mdl
;
428 DPRINT("DriverIoControl. ResultBuffer: %ld %ld %ld %ld\n",
429 ResultBuffer
->Successes
, ResultBuffer
->Failures
,
430 ResultBuffer
->LogBufferLength
, ResultBuffer
->LogBufferMaxLength
);
433 case IOCTL_KMTEST_USERMODE_AWAIT_REQ
:
436 PKMT_USER_WORK_ENTRY WorkItem
;
438 DPRINT("DriverIoControl. IOCTL_KMTEST_USERMODE_AWAIT_REQ, len=%lu\n",
439 IoStackLocation
->Parameters
.DeviceIoControl
.OutputBufferLength
);
441 /* TODO: prevent multiple concurrent invocations */
442 Status
= KeWaitForSingleObject(&WorkList
.NewWorkEvent
, UserRequest
, UserMode
, FALSE
, NULL
);
443 if (Status
== STATUS_USER_APC
|| Status
== STATUS_KERNEL_APC
)
446 if (IoStackLocation
->Parameters
.DeviceIoControl
.OutputBufferLength
< sizeof(KMT_CALLBACK_REQUEST_PACKET
))
448 Status
= STATUS_INVALID_BUFFER_SIZE
;
452 ASSERT(!IsListEmpty(&WorkList
.ListHead
));
454 Entry
= WorkList
.ListHead
.Flink
;
455 WorkItem
= CONTAINING_RECORD(Entry
, KMT_USER_WORK_ENTRY
, ListEntry
);
457 Length
= sizeof(WorkItem
->Request
);
458 RtlCopyMemory(Irp
->AssociatedIrp
.SystemBuffer
, &WorkItem
->Request
, Length
);
459 Status
= STATUS_SUCCESS
;
461 KeResetEvent(&WorkList
.NewWorkEvent
);
465 case IOCTL_KMTEST_USERMODE_SEND_RESPONSE
:
468 PKMT_USER_WORK_ENTRY WorkEntry
;
470 ULONG ResponseSize
= IoStackLocation
->Parameters
.DeviceIoControl
.OutputBufferLength
;
472 DPRINT("DriverIoControl. IOCTL_KMTEST_USERMODE_SEND_RESPONSE, inlen=%lu, outlen=%lu\n",
473 IoStackLocation
->Parameters
.DeviceIoControl
.InputBufferLength
,
474 IoStackLocation
->Parameters
.DeviceIoControl
.OutputBufferLength
);
476 if (IoStackLocation
->Parameters
.DeviceIoControl
.InputBufferLength
!= sizeof(ULONG
) || ResponseSize
!= sizeof(KMT_RESPONSE
))
478 Status
= STATUS_INVALID_BUFFER_SIZE
;
482 /* FIXME: don't misuse the output buffer as an input! */
483 Response
= MmGetSystemAddressForMdlSafe(Irp
->MdlAddress
, NormalPagePriority
);
484 if (Response
== NULL
)
486 Status
= STATUS_INSUFFICIENT_RESOURCES
;
490 ExAcquireFastMutex(&WorkList
.Lock
);
492 Status
= STATUS_OBJECTID_NOT_FOUND
;
494 Entry
= WorkList
.ListHead
.Flink
;
495 while (Entry
!= &WorkList
.ListHead
)
497 WorkEntry
= CONTAINING_RECORD(Entry
, KMT_USER_WORK_ENTRY
, ListEntry
);
498 if (WorkEntry
->Request
.RequestId
== *(PULONG
)Irp
->AssociatedIrp
.SystemBuffer
)
500 WorkEntry
->Response
= ExAllocatePoolWithTag(PagedPool
, sizeof(KMT_RESPONSE
), 'pseR');
501 if (WorkEntry
->Response
== NULL
)
503 Status
= STATUS_INSUFFICIENT_RESOURCES
;
507 RtlCopyMemory(WorkEntry
->Response
, Response
, ResponseSize
);
508 KeSetEvent(&WorkEntry
->WorkDoneEvent
, IO_NO_INCREMENT
, FALSE
);
509 Status
= STATUS_SUCCESS
;
513 Entry
= Entry
->Flink
;
516 ExReleaseFastMutex(&WorkList
.Lock
);
521 DPRINT1("DriverIoControl. Invalid IoCtl code 0x%08X\n",
522 IoStackLocation
->Parameters
.DeviceIoControl
.IoControlCode
);
523 Status
= STATUS_INVALID_DEVICE_REQUEST
;
527 Irp
->IoStatus
.Status
= Status
;
528 Irp
->IoStatus
.Information
= Length
;
530 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
536 * @name KmtUserModeCallback
538 * Enqueue a request to the usermode callback queue and blocks until the work
545 * TODO: why is this PVOID?
547 * @return Response from user mode
551 IN KMT_CALLBACK_INFORMATION_CLASS Operation
,
554 PKMT_RESPONSE Result
;
556 PKMT_USER_WORK_ENTRY WorkEntry
;
557 LARGE_INTEGER Timeout
;
561 WorkEntry
= ExAllocatePoolWithTag(PagedPool
, sizeof(KMT_USER_WORK_ENTRY
), 'ekrW');
562 if (WorkEntry
== NULL
)
565 KeInitializeEvent(&WorkEntry
->WorkDoneEvent
, NotificationEvent
, FALSE
);
566 WorkEntry
->Request
.RequestId
= RequestId
++;
567 WorkEntry
->Request
.OperationClass
= Operation
;
568 WorkEntry
->Request
.Parameters
= Parameters
;
569 WorkEntry
->Response
= NULL
;
571 ExAcquireFastMutex(&WorkList
.Lock
);
572 InsertTailList(&WorkList
.ListHead
, &WorkEntry
->ListEntry
);
573 ExReleaseFastMutex(&WorkList
.Lock
);
575 KeSetEvent(&WorkList
.NewWorkEvent
, IO_NO_INCREMENT
, FALSE
);
577 Timeout
.QuadPart
= -10 * 1000 * 1000 * 10; //wait for 10 seconds
578 Status
= KeWaitForSingleObject(&WorkEntry
->WorkDoneEvent
, Executive
, UserMode
, FALSE
, &Timeout
);
580 if (Status
== STATUS_USER_APC
|| Status
== STATUS_KERNEL_APC
|| Status
== STATUS_TIMEOUT
)
582 DPRINT1("Unexpected callback abortion! Reason: %lx\n", Status
);
585 ExAcquireFastMutex(&WorkList
.Lock
);
586 RemoveEntryList(&WorkEntry
->ListEntry
);
587 ExReleaseFastMutex(&WorkList
.Lock
);
589 Result
= WorkEntry
->Response
;
591 ExFreePoolWithTag(WorkEntry
, 'ekrW');
597 * @name KmtFreeCallbackResponse
605 KmtFreeCallbackResponse(
606 PKMT_RESPONSE Response
)
610 ExFreePoolWithTag(Response
, 'pseR');
614 * @name KmtCleanUsermodeCallbacks
620 KmtCleanUsermodeCallbacks(VOID
)
626 ExAcquireFastMutex(&WorkList
.Lock
);
628 Entry
= WorkList
.ListHead
.Flink
;
629 while (Entry
!= &WorkList
.ListHead
)
631 PKMT_USER_WORK_ENTRY WorkEntry
= CONTAINING_RECORD(Entry
, KMT_USER_WORK_ENTRY
, ListEntry
);
632 if (WorkEntry
->Response
!= NULL
)
634 KmtFreeCallbackResponse(WorkEntry
->Response
);
637 Entry
= Entry
->Flink
;
639 ExFreePoolWithTag(WorkEntry
, 'ekrW');
642 ExReleaseFastMutex(&WorkList
.Lock
);