1 /*
2  * PROJECT:         ReactOS kernel-mode tests
3  * LICENSE:         GPLv2+ - See COPYING in the top level directory
4  * PURPOSE:         Kernel-Mode Test Suite Driver Object Test Driver
5  * PROGRAMMER:      Michael Martin <martinmnet@hotmail.com>
6  *                  Thomas Faber <thomas.faber@reactos.org>
7  */
8 
9 #include <kmt_test.h>
10 
11 //#define NDEBUG
12 #include <debug.h>
13 
14 #define BASE_POOL_TYPE_MASK 1
15 
16 typedef enum
17 {
18     DriverStatusEntry,
19     DriverStatusIrp,
20     DriverStatusUnload
21 } DRIVER_STATUS;
22 
23 static DRIVER_DISPATCH TestDispatch;
24 static VOID TestDriverObject(IN PDRIVER_OBJECT DriverObject, IN PCUNICODE_STRING RegistryPath OPTIONAL, IN DRIVER_STATUS DriverStatus);
25 static BOOLEAN TestZwLoad(IN PDRIVER_OBJECT DriverObject, IN PCUNICODE_STRING DriverRegistryPath, IN PWCHAR NewDriverRegPath);
26 static BOOLEAN TestZwUnload(IN PDRIVER_OBJECT DriverObject, IN PCUNICODE_STRING DriverRegistryPath, IN PWCHAR NewDriverRegPath);
27 static VOID TestLowerDeviceKernelAPI(IN PDEVICE_OBJECT DeviceObject);
28 static VOID TestDeviceCreated(IN PDEVICE_OBJECT DeviceObject, IN BOOLEAN ExclusiveAccess);
29 static VOID TestDeviceDeletion(IN PDEVICE_OBJECT DeviceObject, IN BOOLEAN Lower, IN BOOLEAN Attached);
30 static VOID TestDeviceCreateDelete(IN PDRIVER_OBJECT DriverObject);
31 static VOID TestAttachDevice(IN PDEVICE_OBJECT DeviceObject, IN PWCHAR NewDriverRegPath);
32 static VOID TestDetachDevice(IN PDEVICE_OBJECT AttachedDevice);
33 
34 static PDEVICE_OBJECT MainDeviceObject;
35 static PDEVICE_OBJECT AttachDeviceObject;
36 static PDRIVER_OBJECT ThisDriverObject;
37 
38 NTSTATUS
39 TestEntry(
40     IN PDRIVER_OBJECT DriverObject,
41     IN PCUNICODE_STRING RegistryPath,
42     OUT PCWSTR *DeviceName,
43     IN OUT INT *Flags)
44 {
45     NTSTATUS Status = STATUS_SUCCESS;
46     BOOLEAN Ret;
47     INT i;
48 
49     PAGED_CODE();
50 
51     UNREFERENCED_PARAMETER(DeviceName);
52 
53     *Flags = TESTENTRY_NO_CREATE_DEVICE | TESTENTRY_NO_REGISTER_DISPATCH;
54 
55     ThisDriverObject = DriverObject;
56 
57     TestDriverObject(DriverObject, RegistryPath, DriverStatusEntry);
58 
59     /* Create and delete device, on return MainDeviceObject has been created */
60     TestDeviceCreateDelete(DriverObject);
61 
62     /* Make sure a device object was created */
63     if (!skip(MainDeviceObject != NULL, "Device object creation failed\n"))
64     {
65         PWCHAR LowerDriverRegPath = L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\Kmtest-IoHelper";
66 
67         /* Load driver test and load the lower driver */
68         Ret = TestZwLoad(DriverObject, RegistryPath, LowerDriverRegPath);
69         if (!skip(Ret, "Failed to load helper driver\n"))
70         {
71             TestAttachDevice(MainDeviceObject, L"\\Device\\Kmtest-IoHelper");
72             if (!skip(AttachDeviceObject != NULL, "No attached device object\n"))
73                 TestLowerDeviceKernelAPI(MainDeviceObject);
74 
75             /* Unload lower driver without detaching from its device */
76             TestZwUnload(DriverObject, RegistryPath, LowerDriverRegPath);
77             TestLowerDeviceKernelAPI(MainDeviceObject);
78         }
79     }
80 
81     for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; ++i)
82         DriverObject->MajorFunction[i] = NULL;
83     DriverObject->MajorFunction[IRP_MJ_CREATE] = TestDispatch;
84     DriverObject->MajorFunction[IRP_MJ_CLOSE] = TestDispatch;
85     DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = TestDispatch;
86 
87     return Status;
88 }
89 
90 VOID
91 TestUnload(
92     IN PDRIVER_OBJECT DriverObject)
93 {
94     PAGED_CODE();
95 
96     if (!skip(AttachDeviceObject != NULL, "no attached device object\n"))
97     {
98         TestDeviceDeletion(MainDeviceObject, FALSE, TRUE);
99         TestDeviceDeletion(AttachDeviceObject, TRUE, FALSE);
100         TestDetachDevice(AttachDeviceObject);
101     }
102 
103     TestDeviceDeletion(MainDeviceObject, FALSE, FALSE);
104     TestDriverObject(DriverObject, NULL, DriverStatusUnload);
105 
106     if (MainDeviceObject)
107         IoDeleteDevice(MainDeviceObject);
108 }
109 
110 static
111 NTSTATUS
112 NTAPI
113 TestDispatch(
114     IN PDEVICE_OBJECT DeviceObject,
115     IN PIRP Irp)
116 {
117     NTSTATUS Status = STATUS_SUCCESS;
118     PIO_STACK_LOCATION IoStackLocation;
119 
120     PAGED_CODE();
121 
122     IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
123 
124     DPRINT("TestIrpHandler. Function=%s, DeviceObject=%p, AttachDeviceObject=%p\n",
125         KmtMajorFunctionNames[IoStackLocation->MajorFunction],
126         DeviceObject,
127         AttachDeviceObject);
128 
129     if (AttachDeviceObject)
130     {
131         IoSkipCurrentIrpStackLocation(Irp);
132         Status = IoCallDriver(AttachDeviceObject, Irp);
133         return Status;
134     }
135 
136     TestDriverObject(DeviceObject->DriverObject, NULL, DriverStatusIrp);
137 
138     Irp->IoStatus.Status = Status;
139     Irp->IoStatus.Information = 0;
140 
141     IoCompleteRequest(Irp, IO_NO_INCREMENT);
142 
143     return Status;
144 }
145 
146 extern DRIVER_INITIALIZE DriverEntry;
147 
148 static
149 VOID
150 TestDriverObject(
151     IN PDRIVER_OBJECT DriverObject,
152     IN PCUNICODE_STRING RegistryPath OPTIONAL,
153     IN DRIVER_STATUS DriverStatus)
154 {
155     BOOLEAN CheckThisDispatchRoutine;
156     PVOID FirstMajorFunc;
157     int i;
158     UNICODE_STRING HardwareDatabase = RTL_CONSTANT_STRING(L"\\REGISTRY\\MACHINE\\HARDWARE\\DESCRIPTION\\SYSTEM");
159     UNICODE_STRING RegPath = RTL_CONSTANT_STRING(L"\\REGISTRY\\MACHINE\\SYSTEM\\ControlSet001\\Services\\Kmtest-IoDeviceObject");
160     UNICODE_STRING DriverName = RTL_CONSTANT_STRING(L"\\Driver\\Kmtest-IoDeviceObject");
161     UNICODE_STRING ServiceKeyName = RTL_CONSTANT_STRING(L"Kmtest-IoDeviceObject");
162     BOOLEAN Equal;
163 
164     ok(DriverObject->Size == sizeof(DRIVER_OBJECT), "Size does not match, got %x\n",DriverObject->Size);
165     ok(DriverObject->Type == 4, "Type does not match 4. got %d\n", DriverObject->Type);
166 
167     if (DriverStatus == DriverStatusEntry)
168     {
169         ok(DriverObject->DeviceObject == NULL, "Expected DeviceObject pointer to be 0, got %p\n",
170             DriverObject->DeviceObject);
171         ok (DriverObject->Flags == DRVO_LEGACY_DRIVER,
172             "Expected Flags to be DRVO_LEGACY_DRIVER, got %lu\n",
173             DriverObject->Flags);
174 
175         ok(DriverObject->DriverStart < (PVOID)TestEntry,
176            "DriverStart is %p, expected < %p\n",
177            DriverObject->DriverStart, (PVOID)TestEntry);
178         ok(DriverObject->DriverSize > 0x2000,
179            "DriverSize 0x%lx\n", DriverObject->DriverSize);
180         ok_eq_pointer(DriverObject->DriverExtension, (PDRIVER_EXTENSION)(DriverObject + 1));
181         ok_eq_pointer(DriverObject->DriverExtension->DriverObject, DriverObject);
182         ok_eq_pointer(DriverObject->DriverExtension->AddDevice, NULL);
183         ok_eq_ulong(DriverObject->DriverExtension->Count, 0UL);
184         Equal = RtlEqualUnicodeString(RegistryPath,
185                                       &RegPath,
186                                       FALSE);
187         ok(Equal, "RegistryPath is '%wZ'\n", RegistryPath);
188         ok((ULONG_PTR)RegistryPath % PAGE_SIZE == 0, "RegistryPath %p not page-aligned\n", RegistryPath);
189         ok_eq_pointer(RegistryPath->Buffer, (PWCHAR)(RegistryPath + 1));
190         ok_eq_uint(RegistryPath->MaximumLength, RegistryPath->Length);
191         Equal = RtlEqualUnicodeString(&DriverObject->DriverExtension->ServiceKeyName,
192                                       &ServiceKeyName,
193                                       FALSE);
194         ok(Equal, "ServiceKeyName is '%wZ'\n", &DriverObject->DriverExtension->ServiceKeyName);
195         ok_eq_tag(KmtGetPoolTag(DriverObject->DriverExtension->ServiceKeyName.Buffer), '  oI');
196         ok_eq_uint((KmtGetPoolType(DriverObject->DriverExtension->ServiceKeyName.Buffer) - 1) & BASE_POOL_TYPE_MASK, NonPagedPool);
197         ok_eq_uint(DriverObject->DriverExtension->ServiceKeyName.MaximumLength, DriverObject->DriverExtension->ServiceKeyName.Length + sizeof(UNICODE_NULL));
198         ok_eq_uint(DriverObject->DriverExtension->ServiceKeyName.Buffer[DriverObject->DriverExtension->ServiceKeyName.Length / sizeof(WCHAR)], UNICODE_NULL);
199         Equal = RtlEqualUnicodeString(&DriverObject->DriverName,
200                                       &DriverName,
201                                       FALSE);
202         ok(Equal, "DriverName is '%wZ'\n", &DriverObject->DriverName);
203         ok_eq_tag(KmtGetPoolTag(DriverObject->DriverName.Buffer), '  oI');
204         ok_eq_uint((KmtGetPoolType(DriverObject->DriverName.Buffer) - 1) & BASE_POOL_TYPE_MASK, PagedPool);
205         ok_eq_uint(DriverObject->DriverName.MaximumLength, DriverObject->DriverName.Length);
206         // TODO: show that both string and buffer are constants inside ntos
207         Equal = RtlEqualUnicodeString(DriverObject->HardwareDatabase,
208                                       &HardwareDatabase,
209                                       FALSE);
210         ok(Equal, "HardwareDatabase is '%wZ'\n", DriverObject->HardwareDatabase);
211         ok_eq_uint(DriverObject->HardwareDatabase->MaximumLength, DriverObject->HardwareDatabase->Length + sizeof(UNICODE_NULL));
212         ok_eq_uint(DriverObject->HardwareDatabase->Buffer[DriverObject->HardwareDatabase->Length / sizeof(WCHAR)], UNICODE_NULL);
213         ok(DriverObject->DriverInit == DriverEntry,
214            "DriverInit is %p, expected %p\n",
215            (PVOID)DriverObject->DriverInit, (PVOID)DriverEntry);
216     }
217     else if (DriverStatus == DriverStatusIrp)
218     {
219         ok(DriverObject->DeviceObject != NULL, "Expected DeviceObject pointer to non null\n");
220         ok (DriverObject->Flags == (DRVO_LEGACY_DRIVER | DRVO_INITIALIZED),
221             "Expected Flags to be DRVO_LEGACY_DRIVER | DRVO_INITIALIZED, got %lu\n",
222             DriverObject->Flags);
223     }
224     else if (DriverStatus == DriverStatusUnload)
225     {
226         ok(DriverObject->DeviceObject != NULL, "Expected DeviceObject pointer to non null\n");
227         ok (DriverObject->Flags == (DRVO_LEGACY_DRIVER | DRVO_INITIALIZED | DRVO_UNLOAD_INVOKED),
228             "Expected Flags to be DRVO_LEGACY_DRIVER | DRVO_INITIALIZED | DRVO_UNLOAD_INVOKED, got %lu\n",
229             DriverObject->Flags);
230     }
231     else
232         ASSERT(FALSE);
233 
234     /* Select a routine that was not changed */
235     FirstMajorFunc = DriverObject->MajorFunction[1];
236     ok(FirstMajorFunc != 0, "Expected MajorFunction[1] to be non NULL\n");
237 
238     if (!skip(FirstMajorFunc != NULL, "First major function not set!\n"))
239     {
240         for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
241         {
242             if (DriverStatus > 0) CheckThisDispatchRoutine = (i > 3) && (i != 14);
243             else CheckThisDispatchRoutine = TRUE;
244 
245             if (CheckThisDispatchRoutine)
246             {
247                 ok(DriverObject->MajorFunction[i] == FirstMajorFunc, "Expected MajorFunction[%d] to match %p\n",
248                     i, FirstMajorFunc);
249             }
250         }
251     }
252 }
253 
254 static
255 BOOLEAN
256 TestZwLoad(
257     IN PDRIVER_OBJECT DriverObject,
258     IN PCUNICODE_STRING DriverRegistryPath,
259     IN PWCHAR NewDriverRegPath)
260 {
261     UNICODE_STRING RegPath;
262     NTSTATUS Status;
263 
264     /* Try to load ourself */
265     Status = ZwLoadDriver((PUNICODE_STRING)DriverRegistryPath);
266     ok (Status == STATUS_IMAGE_ALREADY_LOADED, "Expected NTSTATUS STATUS_IMAGE_ALREADY_LOADED, got 0x%lX\n", Status);
267 
268     if (Status != STATUS_IMAGE_ALREADY_LOADED)
269     {
270         DbgPrint("WARNING: Loading this a second time will cause BUGCHECK!\n");
271     }
272 
273     /* Try to load with a Registry Path that doesnt exist */
274     RtlInitUnicodeString(&RegPath, L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\deadbeef");
275     Status = ZwLoadDriver(&RegPath);
276     ok (Status == STATUS_OBJECT_NAME_NOT_FOUND, "Expected NTSTATUS STATUS_OBJECT_NAME_NOT_FOUND, got 0x%lX\n", Status);
277 
278     /* Load the driver */
279     RtlInitUnicodeString(&RegPath, NewDriverRegPath);
280     Status = ZwLoadDriver(&RegPath);
281     ok(Status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got 0x%lX\n", Status);
282 
283     return NT_SUCCESS(Status);
284 }
285 
286 static
287 BOOLEAN
288 TestZwUnload(
289     IN PDRIVER_OBJECT DriverObject,
290     IN PCUNICODE_STRING DriverRegistryPath,
291     IN PWCHAR NewDriverRegPath)
292 {
293     UNICODE_STRING RegPath;
294     NTSTATUS Status;
295 
296     /* Try to unload ourself, which should fail as our Unload routine hasnt been set yet. */
297     Status = ZwUnloadDriver((PUNICODE_STRING)DriverRegistryPath);
298     ok (Status == STATUS_INVALID_DEVICE_REQUEST, "Expected NTSTATUS STATUS_INVALID_DEVICE_REQUEST, got 0x%lX\n", Status);
299 
300     /* Try to unload with a Registry Path that doesnt exist */
301     RtlInitUnicodeString(&RegPath, L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\deadbeef");
302     Status = ZwUnloadDriver(&RegPath);
303     ok (Status == STATUS_OBJECT_NAME_NOT_FOUND, "Expected NTSTATUS STATUS_OBJECT_NAME_NOT_FOUND, got 0x%lX\n", Status);
304 
305     /* Unload the driver */
306     RtlInitUnicodeString(&RegPath, NewDriverRegPath);
307     Status = ZwUnloadDriver(&RegPath);
308     ok(Status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got 0x%lX\n", Status);
309 
310     return NT_SUCCESS(Status);
311 }
312 
313 static
314 VOID
315 TestLowerDeviceKernelAPI(
316     IN PDEVICE_OBJECT DeviceObject)
317 {
318     PDEVICE_OBJECT RetObject;
319 
320     RetObject = IoGetLowerDeviceObject(DeviceObject);
321 
322     ok(RetObject == AttachDeviceObject,
323         "Expected an Attached DeviceObject %p, got %p\n", AttachDeviceObject, RetObject);
324 
325     if (RetObject)
326     {
327         ObDereferenceObject(RetObject);
328     }
329 
330     RetObject = IoGetDeviceAttachmentBaseRef(DeviceObject);
331     ok(RetObject == AttachDeviceObject,
332         "Expected an Attached DeviceObject %p, got %p\n", AttachDeviceObject, RetObject);
333 
334     if (RetObject)
335     {
336         ObDereferenceObject(RetObject);
337     }
338 
339 }
340 
341 static
342 VOID
343 TestDeviceCreated(
344     IN PDEVICE_OBJECT DeviceObject,
345     IN BOOLEAN ExclusiveAccess)
346 {
347     PEXTENDED_DEVOBJ_EXTENSION extdev;
348 
349     /* Check the device object members */
350     ok(DeviceObject->Type == 3, "Expected Type = 3, got %x\n", DeviceObject->Type);
351     ok(DeviceObject->Size == 0xb8, "Expected Size = 0xb8, got %x\n", DeviceObject->Size);
352     ok(DeviceObject->ReferenceCount == 0, "Expected ReferenceCount = 0, got %lu\n",
353         DeviceObject->ReferenceCount);
354     ok(DeviceObject->DriverObject == ThisDriverObject,
355         "Expected DriverObject member to match this DriverObject %p, got %p\n",
356         ThisDriverObject, DeviceObject->DriverObject);
357     ok(DeviceObject->NextDevice == NULL, "Expected NextDevice to be NULL, got %p\n", DeviceObject->NextDevice);
358     ok(DeviceObject->AttachedDevice == NULL, "Expected AttachDevice to be NULL, got %p\n", DeviceObject->AttachedDevice);
359     ok(DeviceObject->Characteristics == 0, "Expected Characteristics to be 0\n");
360     if (ExclusiveAccess)
361     {
362         ok((DeviceObject->Flags == (DO_DEVICE_HAS_NAME | DO_DEVICE_INITIALIZING | DO_EXCLUSIVE)),
363             "Expected Flags DO_DEVICE_HAS_NAME | DO_DEVICE_INITIALIZING | DO_EXCLUSIVE, got %lu\n", DeviceObject->Flags);
364     }
365     else
366     {
367         ok((DeviceObject->Flags == (DO_DEVICE_HAS_NAME | DO_DEVICE_INITIALIZING)),
368             "Expected Flags DO_DEVICE_HAS_NAME | DO_DEVICE_INITIALIZING, got %lu\n", DeviceObject->Flags);
369     }
370     ok(DeviceObject->DeviceType == FILE_DEVICE_UNKNOWN,
371         "Expected DeviceType to match creation parameter FILE_DEVICE_UNKNWOWN, got %lu\n",
372         DeviceObject->DeviceType);
373     ok(DeviceObject->ActiveThreadCount == 0, "Expected ActiveThreadCount = 0, got %lu\n", DeviceObject->ActiveThreadCount);
374 
375     /* Check the extended extension */
376     extdev = (PEXTENDED_DEVOBJ_EXTENSION)DeviceObject->DeviceObjectExtension;
377     ok(extdev->ExtensionFlags == 0, "Expected Extended ExtensionFlags to be 0, got %lu\n", extdev->ExtensionFlags);
378     ok (extdev->Type == 13, "Expected Type of 13, got %d\n", extdev->Type);
379     ok (extdev->Size == 0, "Expected Size of 0, got %d\n", extdev->Size);
380     ok (extdev->DeviceObject == DeviceObject, "Expected DeviceOject to match newly created device %p, got %p\n",
381         DeviceObject, extdev->DeviceObject);
382     ok(extdev->AttachedTo == NULL, "Expected AttachTo to be NULL, got %p\n", extdev->AttachedTo);
383     ok(extdev->StartIoCount == 0, "Expected StartIoCount = 0, got %lu\n", extdev->StartIoCount);
384     ok(extdev->StartIoKey == 0, "Expected StartIoKey = 0, got %lu\n", extdev->StartIoKey);
385     ok(extdev->StartIoFlags == 0, "Expected StartIoFlags = 0, got %lu\n", extdev->StartIoFlags);
386 }
387 
388 static
389 VOID
390 TestDeviceDeletion(
391     IN PDEVICE_OBJECT DeviceObject,
392     IN BOOLEAN Lower,
393     IN BOOLEAN Attached)
394 {
395     PEXTENDED_DEVOBJ_EXTENSION extdev;
396 
397     /* Check the device object members */
398     ok(DeviceObject->Type == 3, "Expected Type = 3, got %d\n", DeviceObject->Type);
399     ok(DeviceObject->Size == 0xb8, "Expected Size = 0xb8, got %d\n", DeviceObject->Size);
400     ok(DeviceObject->ReferenceCount == 0, "Expected ReferenceCount = 0, got %lu\n",
401         DeviceObject->ReferenceCount);
402     if (!Lower)
403     {
404         ok(DeviceObject->DriverObject == ThisDriverObject,
405             "Expected DriverObject member to match this DriverObject %p, got %p\n",
406             ThisDriverObject, DeviceObject->DriverObject);
407     }
408     ok(DeviceObject->NextDevice == NULL, "Expected NextDevice to be NULL, got %p\n", DeviceObject->NextDevice);
409 
410     if (Lower)
411     {
412         ok(DeviceObject->AttachedDevice == MainDeviceObject,
413             "Expected AttachDevice to be %p, got %p\n", MainDeviceObject, DeviceObject->AttachedDevice);
414     }
415     else
416     {
417         ok(DeviceObject->AttachedDevice == NULL, "Expected AttachDevice to be NULL, got %p\n", DeviceObject->AttachedDevice);
418     }
419 
420     ok(DeviceObject->Flags == (DO_DEVICE_HAS_NAME | (Lower ? DO_EXCLUSIVE : 0)),
421         "Expected Flags DO_DEVICE_HAS_NAME, got %lu\n", DeviceObject->Flags);
422     ok(DeviceObject->DeviceType == FILE_DEVICE_UNKNOWN,
423         "Expected DeviceType to match creation parameter FILE_DEVICE_UNKNWOWN, got %lu\n",
424         DeviceObject->DeviceType);
425     ok(DeviceObject->ActiveThreadCount == 0, "Expected ActiveThreadCount = 0, got %lu\n", DeviceObject->ActiveThreadCount);
426 
427     /*Check the extended extension */
428     extdev = (PEXTENDED_DEVOBJ_EXTENSION)DeviceObject->DeviceObjectExtension;
429     ok(extdev->ExtensionFlags == DOE_UNLOAD_PENDING,
430         "Expected Extended ExtensionFlags to be DOE_UNLOAD_PENDING, got %lu\n", extdev->ExtensionFlags);
431     ok (extdev->Type == 13, "Expected Type of 13, got %d\n", extdev->Type);
432     ok (extdev->Size == 0, "Expected Size of 0, got %d\n", extdev->Size);
433     ok (extdev->DeviceObject == DeviceObject, "Expected DeviceOject to match newly created device %p, got %p\n",
434         DeviceObject, extdev->DeviceObject);
435     if (Lower || !Attached)
436     {
437         ok(extdev->AttachedTo == NULL, "Expected AttachTo to be NULL, got %p\n", extdev->AttachedTo);
438     }
439     else
440     {
441         ok(extdev->AttachedTo == AttachDeviceObject, "Expected AttachTo to %p, got %p\n", AttachDeviceObject, extdev->AttachedTo);
442     }
443     ok(extdev->StartIoCount == 0, "Expected StartIoCount = 0, got %lu\n", extdev->StartIoCount);
444     ok(extdev->StartIoKey == 0, "Expected StartIoKey = 0, got %lu\n", extdev->StartIoKey);
445     ok(extdev->StartIoFlags == 0, "Expected StartIoFlags = 0, got %lu\n", extdev->StartIoFlags);
446 }
447 
448 static
449 VOID
450 TestDeviceCreateDelete(
451     IN PDRIVER_OBJECT DriverObject)
452 {
453     NTSTATUS Status;
454     UNICODE_STRING DeviceString;
455     PDEVICE_OBJECT DeviceObject;
456 
457     /* Create using wrong directory */
458     RtlInitUnicodeString(&DeviceString, L"\\Device1\\Kmtest-IoDeviceObject");
459     Status = IoCreateDevice(DriverObject,
460                             0,
461                             &DeviceString,
462                             FILE_DEVICE_UNKNOWN,
463                             0,
464                             FALSE,
465                             &DeviceObject);
466     ok(Status == STATUS_OBJECT_PATH_NOT_FOUND, "Expected STATUS_OBJECT_PATH_NOT_FOUND, got 0x%lX\n", Status);
467 
468     /* Create using correct params with exclusice access */
469     RtlInitUnicodeString(&DeviceString, L"\\Device\\Kmtest-IoDeviceObject");
470     Status = IoCreateDevice(DriverObject,
471                             0,
472                             &DeviceString,
473                             FILE_DEVICE_UNKNOWN,
474                             0,
475                             TRUE,
476                             &DeviceObject);
477     ok(Status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got 0x%lX\n", Status);
478 
479     TestDeviceCreated(DeviceObject, TRUE);
480 
481     /* Delete the device */
482     if (NT_SUCCESS(Status))
483     {
484         IoDeleteDevice(DeviceObject);
485         ok(DriverObject->DeviceObject == 0, "Expected DriverObject->DeviceObject to be NULL, got %p\n",
486             DriverObject->DeviceObject);
487     }
488 
489     /* Create using correct params without exclusice access */
490     Status = IoCreateDevice(DriverObject,
491                             0,
492                             &DeviceString,
493                             FILE_DEVICE_UNKNOWN,
494                             0,
495                             FALSE,
496                             &DeviceObject);
497     ok(Status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got 0x%lX\n", Status);
498 
499     TestDeviceCreated(DeviceObject, FALSE);
500 
501     /* Delete the device */
502     if (NT_SUCCESS(Status))
503     {
504         IoDeleteDevice(DeviceObject);
505         ok(DriverObject->DeviceObject == 0, "Expected DriverObject->DeviceObject to be NULL, got %p\n",
506             DriverObject->DeviceObject);
507     }
508 
509     /* Recreate device */
510     Status = IoCreateDevice(DriverObject,
511                             0,
512                             &DeviceString,
513                             FILE_DEVICE_UNKNOWN,
514                             0,
515                             FALSE,
516                             &DeviceObject);
517     ok(Status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got 0x%lX\n", Status);
518 
519     if (NT_SUCCESS(Status))
520         MainDeviceObject = DeviceObject;
521 }
522 
523 static
524 VOID
525 TestAttachDevice(
526     IN PDEVICE_OBJECT DeviceObject,
527     IN PWCHAR NewDriverRegPath)
528 {
529     NTSTATUS Status;
530     UNICODE_STRING LowerDeviceName;
531 
532     RtlInitUnicodeString(&LowerDeviceName, NewDriverRegPath);
533     Status = IoAttachDevice(DeviceObject, &LowerDeviceName, &AttachDeviceObject);
534     ok_eq_hex(Status, STATUS_SUCCESS);
535 
536     /* TODO: Add more tests */
537 }
538 
539 static
540 VOID
541 TestDetachDevice(
542     IN PDEVICE_OBJECT AttachedDevice)
543 {
544     IoDetachDevice(AttachedDevice);
545 
546     /* TODO: Add more tests */
547 }
548