[KMTESTS:KE]
[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 <thomas.faber@reactos.org>
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 #if DBG
154 PKMT_DEVICE_EXTENSION DeviceExtension = MainDeviceObject->DeviceExtension;
155 ASSERT(!DeviceExtension->Mdl);
156 ASSERT(!DeviceExtension->ResultBuffer);
157 #endif
158 ASSERT(!ResultBuffer);
159 IoDeleteDevice(MainDeviceObject);
160 }
161 }
162
163 /**
164 * @name DriverCreate
165 *
166 * Driver Dispatch function for IRP_MJ_CREATE
167 *
168 * @param DeviceObject
169 * Device Object
170 * @param Irp
171 * I/O request packet
172 *
173 * @return Status
174 */
175 static
176 NTSTATUS
177 NTAPI
178 DriverCreate(
179 IN PDEVICE_OBJECT DeviceObject,
180 IN PIRP Irp)
181 {
182 NTSTATUS Status = STATUS_SUCCESS;
183 PIO_STACK_LOCATION IoStackLocation;
184
185 PAGED_CODE();
186
187 IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
188
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);
192
193 Irp->IoStatus.Status = Status;
194 Irp->IoStatus.Information = 0;
195
196 IoCompleteRequest(Irp, IO_NO_INCREMENT);
197
198 return Status;
199 }
200
201 /**
202 * @name DriverCleanup
203 *
204 * Driver Dispatch function for IRP_MJ_CLEANUP
205 *
206 * @param DeviceObject
207 * Device Object
208 * @param Irp
209 * I/O request packet
210 *
211 * @return Status
212 */
213 static
214 NTSTATUS
215 NTAPI
216 DriverCleanup(
217 IN PDEVICE_OBJECT DeviceObject,
218 IN PIRP Irp)
219 {
220 NTSTATUS Status = STATUS_SUCCESS;
221 PIO_STACK_LOCATION IoStackLocation;
222 PKMT_DEVICE_EXTENSION DeviceExtension;
223
224 PAGED_CODE();
225
226 IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
227
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);
231
232 ASSERT(IoStackLocation->FileObject->FsContext2 == NULL);
233 DeviceExtension = DeviceObject->DeviceExtension;
234 if (DeviceExtension->Mdl && IoStackLocation->FileObject->FsContext == DeviceExtension->Mdl)
235 {
236 MmUnlockPages(DeviceExtension->Mdl);
237 IoFreeMdl(DeviceExtension->Mdl);
238 DeviceExtension->Mdl = NULL;
239 ResultBuffer = DeviceExtension->ResultBuffer = NULL;
240 }
241 else
242 {
243 ASSERT(IoStackLocation->FileObject->FsContext == NULL);
244 }
245
246 Irp->IoStatus.Status = Status;
247 Irp->IoStatus.Information = 0;
248
249 IoCompleteRequest(Irp, IO_NO_INCREMENT);
250
251 return Status;
252 }
253
254 /**
255 * @name DriverClose
256 *
257 * Driver Dispatch function for IRP_MJ_CLOSE
258 *
259 * @param DeviceObject
260 * Device Object
261 * @param Irp
262 * I/O request packet
263 *
264 * @return Status
265 */
266 static
267 NTSTATUS
268 NTAPI
269 DriverClose(
270 IN PDEVICE_OBJECT DeviceObject,
271 IN PIRP Irp)
272 {
273 NTSTATUS Status = STATUS_SUCCESS;
274
275 PAGED_CODE();
276
277 DPRINT("DriverClose. DeviceObject=%p, RequestorMode=%d\n",
278 DeviceObject, Irp->RequestorMode);
279
280 Irp->IoStatus.Status = Status;
281 Irp->IoStatus.Information = 0;
282
283 IoCompleteRequest(Irp, IO_NO_INCREMENT);
284
285 return Status;
286 }
287
288 /**
289 * @name DriverIoControl
290 *
291 * Driver Dispatch function for IRP_MJ_DEVICE_CONTROL
292 *
293 * @param DeviceObject
294 * Device Object
295 * @param Irp
296 * I/O request packet
297 *
298 * @return Status
299 */
300 static
301 NTSTATUS
302 NTAPI
303 DriverIoControl(
304 IN PDEVICE_OBJECT DeviceObject,
305 IN PIRP Irp)
306 {
307 NTSTATUS Status = STATUS_SUCCESS;
308 PIO_STACK_LOCATION IoStackLocation;
309 SIZE_T Length = 0;
310
311 PAGED_CODE();
312
313 IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
314
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);
319
320 switch (IoStackLocation->Parameters.DeviceIoControl.IoControlCode)
321 {
322 case IOCTL_KMTEST_GET_TESTS:
323 {
324 PCKMT_TEST TestEntry;
325 LPSTR OutputBuffer = Irp->AssociatedIrp.SystemBuffer;
326 size_t Remaining = IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength;
327
328 DPRINT("DriverIoControl. IOCTL_KMTEST_GET_TESTS, outlen=%lu\n",
329 IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength);
330
331 for (TestEntry = TestList; TestEntry->TestName; ++TestEntry)
332 {
333 RtlStringCbCopyExA(OutputBuffer, Remaining, TestEntry->TestName, &OutputBuffer, &Remaining, 0);
334 if (Remaining)
335 {
336 *OutputBuffer++ = '\0';
337 --Remaining;
338 }
339 }
340 if (Remaining)
341 {
342 *OutputBuffer++ = '\0';
343 --Remaining;
344 }
345 Length = IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength - Remaining;
346 break;
347 }
348 case IOCTL_KMTEST_RUN_TEST:
349 {
350 ANSI_STRING TestName;
351 PCKMT_TEST TestEntry;
352
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);
359
360 for (TestEntry = TestList; TestEntry->TestName; ++TestEntry)
361 {
362 ANSI_STRING EntryName;
363 if (TestEntry->TestName[0] == '-')
364 RtlInitAnsiString(&EntryName, TestEntry->TestName + 1);
365 else
366 RtlInitAnsiString(&EntryName, TestEntry->TestName);
367
368 if (!RtlCompareString(&TestName, &EntryName, FALSE))
369 {
370 DPRINT1("DriverIoControl. Starting test %Z\n", &EntryName);
371 TestEntry->TestFunction();
372 DPRINT1("DriverIoControl. Finished test %Z\n", &EntryName);
373 break;
374 }
375 }
376
377 if (!TestEntry->TestName)
378 Status = STATUS_OBJECT_NAME_INVALID;
379
380 break;
381 }
382 case IOCTL_KMTEST_SET_RESULTBUFFER:
383 {
384 PKMT_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
385
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);
390
391 if (DeviceExtension->Mdl)
392 {
393 if (IoStackLocation->FileObject->FsContext != DeviceExtension->Mdl)
394 {
395 Status = STATUS_ACCESS_DENIED;
396 break;
397 }
398 MmUnlockPages(DeviceExtension->Mdl);
399 IoFreeMdl(DeviceExtension->Mdl);
400 IoStackLocation->FileObject->FsContext = NULL;
401 ResultBuffer = DeviceExtension->ResultBuffer = NULL;
402 }
403
404 DeviceExtension->Mdl = IoAllocateMdl(IoStackLocation->Parameters.DeviceIoControl.Type3InputBuffer,
405 IoStackLocation->Parameters.DeviceIoControl.InputBufferLength,
406 FALSE, FALSE, NULL);
407 if (!DeviceExtension->Mdl)
408 {
409 Status = STATUS_INSUFFICIENT_RESOURCES;
410 break;
411 }
412
413 _SEH2_TRY
414 {
415 MmProbeAndLockPages(DeviceExtension->Mdl, UserMode, IoModifyAccess);
416 }
417 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
418 {
419 Status = _SEH2_GetExceptionCode();
420 IoFreeMdl(DeviceExtension->Mdl);
421 DeviceExtension->Mdl = NULL;
422 break;
423 } _SEH2_END;
424
425 ResultBuffer = DeviceExtension->ResultBuffer = MmGetSystemAddressForMdlSafe(DeviceExtension->Mdl, NormalPagePriority);
426 IoStackLocation->FileObject->FsContext = DeviceExtension->Mdl;
427
428 DPRINT("DriverIoControl. ResultBuffer: %ld %ld %ld %ld\n",
429 ResultBuffer->Successes, ResultBuffer->Failures,
430 ResultBuffer->LogBufferLength, ResultBuffer->LogBufferMaxLength);
431 break;
432 }
433 case IOCTL_KMTEST_USERMODE_AWAIT_REQ:
434 {
435 PLIST_ENTRY Entry;
436 PKMT_USER_WORK_ENTRY WorkItem;
437
438 DPRINT("DriverIoControl. IOCTL_KMTEST_USERMODE_AWAIT_REQ, len=%lu\n",
439 IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength);
440
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)
444 break;
445
446 if (IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength < sizeof(KMT_CALLBACK_REQUEST_PACKET))
447 {
448 Status = STATUS_INVALID_BUFFER_SIZE;
449 break;
450 }
451
452 ASSERT(!IsListEmpty(&WorkList.ListHead));
453
454 Entry = WorkList.ListHead.Flink;
455 WorkItem = CONTAINING_RECORD(Entry, KMT_USER_WORK_ENTRY, ListEntry);
456
457 Length = sizeof(WorkItem->Request);
458 RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer, &WorkItem->Request, Length);
459 Status = STATUS_SUCCESS;
460
461 KeResetEvent(&WorkList.NewWorkEvent);
462 break;
463
464 }
465 case IOCTL_KMTEST_USERMODE_SEND_RESPONSE:
466 {
467 PLIST_ENTRY Entry;
468 PKMT_USER_WORK_ENTRY WorkEntry;
469 PVOID Response;
470 ULONG ResponseSize = IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength;
471
472 DPRINT("DriverIoControl. IOCTL_KMTEST_USERMODE_SEND_RESPONSE, inlen=%lu, outlen=%lu\n",
473 IoStackLocation->Parameters.DeviceIoControl.InputBufferLength,
474 IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength);
475
476 if (IoStackLocation->Parameters.DeviceIoControl.InputBufferLength != sizeof(ULONG) || ResponseSize != sizeof(KMT_RESPONSE))
477 {
478 Status = STATUS_INVALID_BUFFER_SIZE;
479 break;
480 }
481
482 /* FIXME: don't misuse the output buffer as an input! */
483 Response = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
484 if (Response == NULL)
485 {
486 Status = STATUS_INSUFFICIENT_RESOURCES;
487 break;
488 }
489
490 ExAcquireFastMutex(&WorkList.Lock);
491
492 Status = STATUS_OBJECTID_NOT_FOUND;
493
494 Entry = WorkList.ListHead.Flink;
495 while (Entry != &WorkList.ListHead)
496 {
497 WorkEntry = CONTAINING_RECORD(Entry, KMT_USER_WORK_ENTRY, ListEntry);
498 if (WorkEntry->Request.RequestId == *(PULONG)Irp->AssociatedIrp.SystemBuffer)
499 {
500 WorkEntry->Response = ExAllocatePoolWithTag(PagedPool, sizeof(KMT_RESPONSE), 'pseR');
501 if (WorkEntry->Response == NULL)
502 {
503 Status = STATUS_INSUFFICIENT_RESOURCES;
504 break;
505 }
506
507 RtlCopyMemory(WorkEntry->Response, Response, ResponseSize);
508 KeSetEvent(&WorkEntry->WorkDoneEvent, IO_NO_INCREMENT, FALSE);
509 Status = STATUS_SUCCESS;
510 break;
511 }
512
513 Entry = Entry->Flink;
514 }
515
516 ExReleaseFastMutex(&WorkList.Lock);
517
518 break;
519 }
520 default:
521 DPRINT1("DriverIoControl. Invalid IoCtl code 0x%08X\n",
522 IoStackLocation->Parameters.DeviceIoControl.IoControlCode);
523 Status = STATUS_INVALID_DEVICE_REQUEST;
524 break;
525 }
526
527 Irp->IoStatus.Status = Status;
528 Irp->IoStatus.Information = Length;
529
530 IoCompleteRequest(Irp, IO_NO_INCREMENT);
531
532 return Status;
533 }
534
535 /**
536 * @name KmtUserModeCallback
537 *
538 * Enqueue a request to the usermode callback queue and blocks until the work
539 * is finished.
540 *
541 * @param Operation
542 * TODO
543 * @param Parameters
544 * TODO
545 * TODO: why is this PVOID?
546 *
547 * @return Response from user mode
548 */
549 PKMT_RESPONSE
550 KmtUserModeCallback(
551 IN KMT_CALLBACK_INFORMATION_CLASS Operation,
552 IN PVOID Parameters)
553 {
554 PKMT_RESPONSE Result;
555 NTSTATUS Status;
556 PKMT_USER_WORK_ENTRY WorkEntry;
557 LARGE_INTEGER Timeout;
558
559 PAGED_CODE();
560
561 WorkEntry = ExAllocatePoolWithTag(PagedPool, sizeof(KMT_USER_WORK_ENTRY), 'ekrW');
562 if (WorkEntry == NULL)
563 return NULL;
564
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;
570
571 ExAcquireFastMutex(&WorkList.Lock);
572 InsertTailList(&WorkList.ListHead, &WorkEntry->ListEntry);
573 ExReleaseFastMutex(&WorkList.Lock);
574
575 KeSetEvent(&WorkList.NewWorkEvent, IO_NO_INCREMENT, FALSE);
576
577 Timeout.QuadPart = -10 * 1000 * 1000 * 10; //wait for 10 seconds
578 Status = KeWaitForSingleObject(&WorkEntry->WorkDoneEvent, Executive, UserMode, FALSE, &Timeout);
579
580 if (Status == STATUS_USER_APC || Status == STATUS_KERNEL_APC || Status == STATUS_TIMEOUT)
581 {
582 DPRINT1("Unexpected callback abortion! Reason: %lx\n", Status);
583 }
584
585 ExAcquireFastMutex(&WorkList.Lock);
586 RemoveEntryList(&WorkEntry->ListEntry);
587 ExReleaseFastMutex(&WorkList.Lock);
588
589 Result = WorkEntry->Response;
590
591 ExFreePoolWithTag(WorkEntry, 'ekrW');
592
593 return Result;
594 }
595
596 /**
597 * @name KmtFreeCallbackResponse
598 *
599 * TODO
600 *
601 * @param Response
602 * TODO
603 */
604 VOID
605 KmtFreeCallbackResponse(
606 PKMT_RESPONSE Response)
607 {
608 PAGED_CODE();
609
610 ExFreePoolWithTag(Response, 'pseR');
611 }
612
613 /**
614 * @name KmtCleanUsermodeCallbacks
615 *
616 * TODO
617 */
618 static
619 VOID
620 KmtCleanUsermodeCallbacks(VOID)
621 {
622 PLIST_ENTRY Entry;
623
624 PAGED_CODE();
625
626 ExAcquireFastMutex(&WorkList.Lock);
627
628 Entry = WorkList.ListHead.Flink;
629 while (Entry != &WorkList.ListHead)
630 {
631 PKMT_USER_WORK_ENTRY WorkEntry = CONTAINING_RECORD(Entry, KMT_USER_WORK_ENTRY, ListEntry);
632 if (WorkEntry->Response != NULL)
633 {
634 KmtFreeCallbackResponse(WorkEntry->Response);
635 }
636
637 Entry = Entry->Flink;
638
639 ExFreePoolWithTag(WorkEntry, 'ekrW');
640 }
641
642 ExReleaseFastMutex(&WorkList.Lock);
643 }