[KMTESTS]
[reactos.git] / rostests / kmtests / kmtest_drv / kmtest_drv.c
1 /*
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 <thfabba@gmx.de>
6 */
7
8 #include <ntddk.h>
9 #include <ntifs.h>
10 #include <ndk/ketypes.h>
11 #include <ntstrsafe.h>
12 #include <limits.h>
13 #include <pseh/pseh2.h>
14
15 #define NDEBUG
16 #include <debug.h>
17
18 #include <kmt_public.h>
19 #define KMT_DEFINE_TEST_FUNCTIONS
20 #include <kmt_test.h>
21
22 /* Usermode callback definitions */
23 typedef struct _KMT_USER_WORK_ENTRY
24 {
25 LIST_ENTRY ListEntry;
26 KEVENT WorkDoneEvent;
27 KMT_CALLBACK_REQUEST_PACKET Request;
28 PKMT_RESPONSE Response;
29 } KMT_USER_WORK_ENTRY, *PKMT_USER_WORK_ENTRY;
30
31 typedef struct _KMT_USER_WORK_LIST
32 {
33 LIST_ENTRY ListHead;
34 FAST_MUTEX Lock;
35 KEVENT NewWorkEvent;
36 } KMT_USER_WORK_LIST, *PKMT_USER_WORK_LIST;
37
38 /* Prototypes */
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);
50
51 /* Globals */
52 static PDEVICE_OBJECT MainDeviceObject;
53 PDRIVER_OBJECT KmtDriverObject = NULL;
54 static KMT_USER_WORK_LIST WorkList;
55 static ULONG RequestId = 0;
56
57 /* Entry */
58 /**
59 * @name DriverEntry
60 *
61 * Driver Entry point.
62 *
63 * @param DriverObject
64 * Driver Object
65 * @param RegistryPath
66 * Driver Registry Path
67 *
68 * @return Status
69 */
70 NTSTATUS
71 NTAPI
72 DriverEntry(
73 IN PDRIVER_OBJECT DriverObject,
74 IN PUNICODE_STRING RegistryPath)
75 {
76 NTSTATUS Status = STATUS_SUCCESS;
77 UNICODE_STRING DeviceName;
78 PKMT_DEVICE_EXTENSION DeviceExtension;
79 PKPRCB Prcb;
80
81 PAGED_CODE();
82
83 UNREFERENCED_PARAMETER(RegistryPath);
84
85 DPRINT("DriverEntry\n");
86
87 Prcb = KeGetCurrentPrcb();
88 KmtIsCheckedBuild = (Prcb->BuildType & PRCB_BUILD_DEBUG) != 0;
89 KmtIsMultiProcessorBuild = (Prcb->BuildType & PRCB_BUILD_UNIPROCESSOR) == 0;
90 KmtDriverObject = DriverObject;
91
92 RtlInitUnicodeString(&DeviceName, KMTEST_DEVICE_DRIVER_PATH);
93 Status = IoCreateDevice(DriverObject, sizeof(KMT_DEVICE_EXTENSION),
94 &DeviceName,
95 FILE_DEVICE_UNKNOWN,
96 FILE_DEVICE_SECURE_OPEN | FILE_READ_ONLY_DEVICE,
97 FALSE, &MainDeviceObject);
98
99 if (!NT_SUCCESS(Status))
100 goto cleanup;
101
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;
107
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;
113
114 ExInitializeFastMutex(&WorkList.Lock);
115 KeInitializeEvent(&WorkList.NewWorkEvent, NotificationEvent, FALSE);
116 InitializeListHead(&WorkList.ListHead);
117
118 cleanup:
119 if (MainDeviceObject && !NT_SUCCESS(Status))
120 {
121 IoDeleteDevice(MainDeviceObject);
122 MainDeviceObject = NULL;
123 }
124
125 return Status;
126 }
127
128 /* Dispatch functions */
129 /**
130 * @name DriverUnload
131 *
132 * Driver cleanup funtion.
133 *
134 * @param DriverObject
135 * Driver Object
136 */
137 static
138 VOID
139 NTAPI
140 DriverUnload(
141 IN PDRIVER_OBJECT DriverObject)
142 {
143 PAGED_CODE();
144
145 UNREFERENCED_PARAMETER(DriverObject);
146
147 DPRINT("DriverUnload\n");
148
149 KmtCleanUsermodeCallbacks();
150
151 if (MainDeviceObject)
152 {
153 PKMT_DEVICE_EXTENSION DeviceExtension = MainDeviceObject->DeviceExtension;
154 ASSERT(!DeviceExtension->Mdl);
155 ASSERT(!DeviceExtension->ResultBuffer);
156 ASSERT(!ResultBuffer);
157 IoDeleteDevice(MainDeviceObject);
158 }
159 }
160
161 /**
162 * @name DriverCreate
163 *
164 * Driver Dispatch function for IRP_MJ_CREATE
165 *
166 * @param DeviceObject
167 * Device Object
168 * @param Irp
169 * I/O request packet
170 *
171 * @return Status
172 */
173 static
174 NTSTATUS
175 NTAPI
176 DriverCreate(
177 IN PDEVICE_OBJECT DeviceObject,
178 IN PIRP Irp)
179 {
180 NTSTATUS Status = STATUS_SUCCESS;
181 PIO_STACK_LOCATION IoStackLocation;
182
183 PAGED_CODE();
184
185 IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
186
187 DPRINT("DriverCreate. DeviceObject=%p, RequestorMode=%d, FileObject=%p, FsContext=%p, FsContext2=%p\n",
188 DeviceObject, Irp->RequestorMode, IoStackLocation->FileObject,
189 IoStackLocation->FileObject->FsContext, IoStackLocation->FileObject->FsContext2);
190
191 Irp->IoStatus.Status = Status;
192 Irp->IoStatus.Information = 0;
193
194 IoCompleteRequest(Irp, IO_NO_INCREMENT);
195
196 return Status;
197 }
198
199 /**
200 * @name DriverCleanup
201 *
202 * Driver Dispatch function for IRP_MJ_CLEANUP
203 *
204 * @param DeviceObject
205 * Device Object
206 * @param Irp
207 * I/O request packet
208 *
209 * @return Status
210 */
211 static
212 NTSTATUS
213 NTAPI
214 DriverCleanup(
215 IN PDEVICE_OBJECT DeviceObject,
216 IN PIRP Irp)
217 {
218 NTSTATUS Status = STATUS_SUCCESS;
219 PIO_STACK_LOCATION IoStackLocation;
220 PKMT_DEVICE_EXTENSION DeviceExtension;
221
222 PAGED_CODE();
223
224 IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
225
226 DPRINT("DriverCleanup. DeviceObject=%p, RequestorMode=%d, FileObject=%p, FsContext=%p, FsContext2=%p\n",
227 DeviceObject, Irp->RequestorMode, IoStackLocation->FileObject,
228 IoStackLocation->FileObject->FsContext, IoStackLocation->FileObject->FsContext2);
229
230 ASSERT(IoStackLocation->FileObject->FsContext2 == NULL);
231 DeviceExtension = DeviceObject->DeviceExtension;
232 if (DeviceExtension->Mdl && IoStackLocation->FileObject->FsContext == DeviceExtension->Mdl)
233 {
234 MmUnlockPages(DeviceExtension->Mdl);
235 IoFreeMdl(DeviceExtension->Mdl);
236 DeviceExtension->Mdl = NULL;
237 ResultBuffer = DeviceExtension->ResultBuffer = NULL;
238 }
239 else
240 {
241 ASSERT(IoStackLocation->FileObject->FsContext == NULL);
242 }
243
244 Irp->IoStatus.Status = Status;
245 Irp->IoStatus.Information = 0;
246
247 IoCompleteRequest(Irp, IO_NO_INCREMENT);
248
249 return Status;
250 }
251
252 /**
253 * @name DriverClose
254 *
255 * Driver Dispatch function for IRP_MJ_CLOSE
256 *
257 * @param DeviceObject
258 * Device Object
259 * @param Irp
260 * I/O request packet
261 *
262 * @return Status
263 */
264 static
265 NTSTATUS
266 NTAPI
267 DriverClose(
268 IN PDEVICE_OBJECT DeviceObject,
269 IN PIRP Irp)
270 {
271 NTSTATUS Status = STATUS_SUCCESS;
272
273 PAGED_CODE();
274
275 DPRINT("DriverClose. DeviceObject=%p, RequestorMode=%d\n",
276 DeviceObject, Irp->RequestorMode);
277
278 Irp->IoStatus.Status = Status;
279 Irp->IoStatus.Information = 0;
280
281 IoCompleteRequest(Irp, IO_NO_INCREMENT);
282
283 return Status;
284 }
285
286 /**
287 * @name DriverIoControl
288 *
289 * Driver Dispatch function for IRP_MJ_DEVICE_CONTROL
290 *
291 * @param DeviceObject
292 * Device Object
293 * @param Irp
294 * I/O request packet
295 *
296 * @return Status
297 */
298 static
299 NTSTATUS
300 NTAPI
301 DriverIoControl(
302 IN PDEVICE_OBJECT DeviceObject,
303 IN PIRP Irp)
304 {
305 NTSTATUS Status = STATUS_SUCCESS;
306 PIO_STACK_LOCATION IoStackLocation;
307 SIZE_T Length = 0;
308
309 PAGED_CODE();
310
311 IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
312
313 DPRINT("DriverIoControl. Code=0x%08X, DeviceObject=%p, FileObject=%p, FsContext=%p, FsContext2=%p\n",
314 IoStackLocation->Parameters.DeviceIoControl.IoControlCode,
315 DeviceObject, IoStackLocation->FileObject,
316 IoStackLocation->FileObject->FsContext, IoStackLocation->FileObject->FsContext2);
317
318 switch (IoStackLocation->Parameters.DeviceIoControl.IoControlCode)
319 {
320 case IOCTL_KMTEST_GET_TESTS:
321 {
322 PCKMT_TEST TestEntry;
323 LPSTR OutputBuffer = Irp->AssociatedIrp.SystemBuffer;
324 size_t Remaining = IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength;
325
326 DPRINT("DriverIoControl. IOCTL_KMTEST_GET_TESTS, outlen=%lu\n",
327 IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength);
328
329 for (TestEntry = TestList; TestEntry->TestName; ++TestEntry)
330 {
331 RtlStringCbCopyExA(OutputBuffer, Remaining, TestEntry->TestName, &OutputBuffer, &Remaining, 0);
332 if (Remaining)
333 {
334 *OutputBuffer++ = '\0';
335 --Remaining;
336 }
337 }
338 if (Remaining)
339 {
340 *OutputBuffer++ = '\0';
341 --Remaining;
342 }
343 Length = IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength - Remaining;
344 break;
345 }
346 case IOCTL_KMTEST_RUN_TEST:
347 {
348 ANSI_STRING TestName;
349 PCKMT_TEST TestEntry;
350
351 DPRINT("DriverIoControl. IOCTL_KMTEST_RUN_TEST, inlen=%lu, outlen=%lu\n",
352 IoStackLocation->Parameters.DeviceIoControl.InputBufferLength,
353 IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength);
354 TestName.Length = TestName.MaximumLength = (USHORT)min(IoStackLocation->Parameters.DeviceIoControl.InputBufferLength, USHRT_MAX);
355 TestName.Buffer = Irp->AssociatedIrp.SystemBuffer;
356 DPRINT("DriverIoControl. Run test: %Z\n", &TestName);
357
358 for (TestEntry = TestList; TestEntry->TestName; ++TestEntry)
359 {
360 ANSI_STRING EntryName;
361 if (TestEntry->TestName[0] == '-')
362 RtlInitAnsiString(&EntryName, TestEntry->TestName + 1);
363 else
364 RtlInitAnsiString(&EntryName, TestEntry->TestName);
365
366 if (!RtlCompareString(&TestName, &EntryName, FALSE))
367 {
368 DPRINT1("DriverIoControl. Starting test %Z\n", &EntryName);
369 TestEntry->TestFunction();
370 DPRINT1("DriverIoControl. Finished test %Z\n", &EntryName);
371 break;
372 }
373 }
374
375 if (!TestEntry->TestName)
376 Status = STATUS_OBJECT_NAME_INVALID;
377
378 break;
379 }
380 case IOCTL_KMTEST_SET_RESULTBUFFER:
381 {
382 PKMT_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
383
384 DPRINT("DriverIoControl. IOCTL_KMTEST_SET_RESULTBUFFER, buffer=%p, inlen=%lu, outlen=%lu\n",
385 IoStackLocation->Parameters.DeviceIoControl.Type3InputBuffer,
386 IoStackLocation->Parameters.DeviceIoControl.InputBufferLength,
387 IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength);
388
389 if (DeviceExtension->Mdl)
390 {
391 if (IoStackLocation->FileObject->FsContext != DeviceExtension->Mdl)
392 {
393 Status = STATUS_ACCESS_DENIED;
394 break;
395 }
396 MmUnlockPages(DeviceExtension->Mdl);
397 IoFreeMdl(DeviceExtension->Mdl);
398 IoStackLocation->FileObject->FsContext = NULL;
399 ResultBuffer = DeviceExtension->ResultBuffer = NULL;
400 }
401
402 DeviceExtension->Mdl = IoAllocateMdl(IoStackLocation->Parameters.DeviceIoControl.Type3InputBuffer,
403 IoStackLocation->Parameters.DeviceIoControl.InputBufferLength,
404 FALSE, FALSE, NULL);
405 if (!DeviceExtension->Mdl)
406 {
407 Status = STATUS_INSUFFICIENT_RESOURCES;
408 break;
409 }
410
411 _SEH2_TRY
412 {
413 MmProbeAndLockPages(DeviceExtension->Mdl, UserMode, IoModifyAccess);
414 }
415 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
416 {
417 Status = _SEH2_GetExceptionCode();
418 IoFreeMdl(DeviceExtension->Mdl);
419 DeviceExtension->Mdl = NULL;
420 break;
421 } _SEH2_END;
422
423 ResultBuffer = DeviceExtension->ResultBuffer = MmGetSystemAddressForMdlSafe(DeviceExtension->Mdl, NormalPagePriority);
424 IoStackLocation->FileObject->FsContext = DeviceExtension->Mdl;
425
426 DPRINT("DriverIoControl. ResultBuffer: %ld %ld %ld %ld\n",
427 ResultBuffer->Successes, ResultBuffer->Failures,
428 ResultBuffer->LogBufferLength, ResultBuffer->LogBufferMaxLength);
429 break;
430 }
431 case IOCTL_KMTEST_USERMODE_AWAIT_REQ:
432 {
433 PLIST_ENTRY Entry;
434 PKMT_USER_WORK_ENTRY WorkItem;
435
436 DPRINT("DriverIoControl. IOCTL_KMTEST_USERMODE_AWAIT_REQ, len=%lu\n",
437 IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength);
438
439 /* TODO: prevent multiple concurrent invocations */
440 Status = KeWaitForSingleObject(&WorkList.NewWorkEvent, UserRequest, UserMode, FALSE, NULL);
441 if (Status == STATUS_USER_APC || Status == STATUS_KERNEL_APC)
442 break;
443
444 if (IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength < sizeof(KMT_CALLBACK_REQUEST_PACKET))
445 {
446 Status = STATUS_INVALID_BUFFER_SIZE;
447 break;
448 }
449
450 ASSERT(!IsListEmpty(&WorkList.ListHead));
451
452 Entry = WorkList.ListHead.Flink;
453 WorkItem = CONTAINING_RECORD(Entry, KMT_USER_WORK_ENTRY, ListEntry);
454
455 Length = sizeof(WorkItem->Request);
456 RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer, &WorkItem->Request, Length);
457 Status = STATUS_SUCCESS;
458
459 KeResetEvent(&WorkList.NewWorkEvent);
460 break;
461
462 }
463 case IOCTL_KMTEST_USERMODE_SEND_RESPONSE:
464 {
465 PLIST_ENTRY Entry;
466 PKMT_USER_WORK_ENTRY WorkEntry;
467 PVOID Response;
468 ULONG ResponseSize = IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength;
469
470 DPRINT("DriverIoControl. IOCTL_KMTEST_USERMODE_SEND_RESPONSE, inlen=%lu, outlen=%lu\n",
471 IoStackLocation->Parameters.DeviceIoControl.InputBufferLength,
472 IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength);
473
474 if (IoStackLocation->Parameters.DeviceIoControl.InputBufferLength != sizeof(ULONG) || ResponseSize != sizeof(KMT_RESPONSE))
475 {
476 Status = STATUS_INVALID_BUFFER_SIZE;
477 break;
478 }
479
480 /* FIXME: don't misuse the output buffer as an input! */
481 Response = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
482 if (Response == NULL)
483 {
484 Status = STATUS_INSUFFICIENT_RESOURCES;
485 break;
486 }
487
488 ExAcquireFastMutex(&WorkList.Lock);
489
490 Status = STATUS_OBJECTID_NOT_FOUND;
491
492 Entry = WorkList.ListHead.Flink;
493 while (Entry != &WorkList.ListHead)
494 {
495 WorkEntry = CONTAINING_RECORD(Entry, KMT_USER_WORK_ENTRY, ListEntry);
496 if (WorkEntry->Request.RequestId == *(PULONG)Irp->AssociatedIrp.SystemBuffer)
497 {
498 WorkEntry->Response = ExAllocatePoolWithTag(PagedPool, sizeof(KMT_RESPONSE), 'pseR');
499 if (WorkEntry->Response == NULL)
500 {
501 Status = STATUS_INSUFFICIENT_RESOURCES;
502 break;
503 }
504
505 RtlCopyMemory(WorkEntry->Response, Response, ResponseSize);
506 KeSetEvent(&WorkEntry->WorkDoneEvent, IO_NO_INCREMENT, FALSE);
507 Status = STATUS_SUCCESS;
508 break;
509 }
510
511 Entry = Entry->Flink;
512 }
513
514 ExReleaseFastMutex(&WorkList.Lock);
515
516 break;
517 }
518 default:
519 DPRINT1("DriverIoControl. Invalid IoCtl code 0x%08X\n",
520 IoStackLocation->Parameters.DeviceIoControl.IoControlCode);
521 Status = STATUS_INVALID_DEVICE_REQUEST;
522 break;
523 }
524
525 Irp->IoStatus.Status = Status;
526 Irp->IoStatus.Information = Length;
527
528 IoCompleteRequest(Irp, IO_NO_INCREMENT);
529
530 return Status;
531 }
532
533 /**
534 * @name KmtUserModeCallback
535 *
536 * Enqueue a request to the usermode callback queue and blocks until the work
537 * is finished.
538 *
539 * @param Operation
540 * TODO
541 * @param Parameters
542 * TODO
543 * TODO: why is this PVOID?
544 *
545 * @return Response from user mode
546 */
547 PKMT_RESPONSE
548 KmtUserModeCallback(
549 IN KMT_CALLBACK_INFORMATION_CLASS Operation,
550 IN PVOID Parameters)
551 {
552 PKMT_RESPONSE Result;
553 NTSTATUS Status;
554 PKMT_USER_WORK_ENTRY WorkEntry;
555 LARGE_INTEGER Timeout;
556
557 PAGED_CODE();
558
559 WorkEntry = ExAllocatePoolWithTag(PagedPool, sizeof(KMT_USER_WORK_ENTRY), 'ekrW');
560 if (WorkEntry == NULL)
561 return NULL;
562
563 KeInitializeEvent(&WorkEntry->WorkDoneEvent, NotificationEvent, FALSE);
564 WorkEntry->Request.RequestId = RequestId++;
565 WorkEntry->Request.OperationClass = Operation;
566 WorkEntry->Request.Parameters = Parameters;
567 WorkEntry->Response = NULL;
568
569 ExAcquireFastMutex(&WorkList.Lock);
570 InsertTailList(&WorkList.ListHead, &WorkEntry->ListEntry);
571 ExReleaseFastMutex(&WorkList.Lock);
572
573 KeSetEvent(&WorkList.NewWorkEvent, IO_NO_INCREMENT, FALSE);
574
575 Timeout.QuadPart = -10 * 1000 * 1000 * 10; //wait for 10 seconds
576 Status = KeWaitForSingleObject(&WorkEntry->WorkDoneEvent, Executive, UserMode, FALSE, &Timeout);
577
578 if (Status == STATUS_USER_APC || Status == STATUS_KERNEL_APC || Status == STATUS_TIMEOUT)
579 {
580 DPRINT1("Unexpected callback abortion! Reason: %lx\n", Status);
581 }
582
583 ExAcquireFastMutex(&WorkList.Lock);
584 RemoveEntryList(&WorkEntry->ListEntry);
585 ExReleaseFastMutex(&WorkList.Lock);
586
587 Result = WorkEntry->Response;
588
589 ExFreePoolWithTag(WorkEntry, 'ekrW');
590
591 return Result;
592 }
593
594 /**
595 * @name KmtFreeCallbackResponse
596 *
597 * TODO
598 *
599 * @param Response
600 * TODO
601 */
602 VOID
603 KmtFreeCallbackResponse(
604 PKMT_RESPONSE Response)
605 {
606 PAGED_CODE();
607
608 ExFreePoolWithTag(Response, 'pseR');
609 }
610
611 /**
612 * @name KmtCleanUsermodeCallbacks
613 *
614 * TODO
615 */
616 static
617 VOID
618 KmtCleanUsermodeCallbacks(VOID)
619 {
620 PLIST_ENTRY Entry;
621
622 PAGED_CODE();
623
624 ExAcquireFastMutex(&WorkList.Lock);
625
626 Entry = WorkList.ListHead.Flink;
627 while (Entry != &WorkList.ListHead)
628 {
629 PKMT_USER_WORK_ENTRY WorkEntry = CONTAINING_RECORD(Entry, KMT_USER_WORK_ENTRY, ListEntry);
630 if (WorkEntry->Response != NULL)
631 {
632 KmtFreeCallbackResponse(WorkEntry->Response);
633 }
634
635 Entry = Entry->Flink;
636
637 ExFreePoolWithTag(WorkEntry, 'ekrW');
638 }
639
640 ExReleaseFastMutex(&WorkList.Lock);
641 }