xref: /reactos/ntoskrnl/io/pnpmgr/pnproot.c (revision 81db5e1d)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * COPYRIGHT:       GPL - See COPYING in the top level directory
4  * FILE:            ntoskrnl/io/pnpmgr/pnproot.c
5  * PURPOSE:         PnP manager root device
6  * PROGRAMMERS:     Casper S. Hornstrup (chorns@users.sourceforge.net)
7  *                  Copyright 2007 Herv? Poussineau (hpoussin@reactos.org)
8  */
9 
10 /* INCLUDES ******************************************************************/
11 
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15 
16 /* GLOBALS *******************************************************************/
17 
18 #define ENUM_NAME_ROOT L"Root"
19 
20 /* DATA **********************************************************************/
21 
22 typedef struct _PNPROOT_DEVICE
23 {
24     // Entry on device list
25     LIST_ENTRY ListEntry;
26     // Physical Device Object of device
27     PDEVICE_OBJECT Pdo;
28     // Device ID
29     UNICODE_STRING DeviceID;
30     // Instance ID
31     UNICODE_STRING InstanceID;
32     // Device description
33     UNICODE_STRING DeviceDescription;
34     // Resource requirement list
35     PIO_RESOURCE_REQUIREMENTS_LIST ResourceRequirementsList;
36     // Associated resource list
37     PCM_RESOURCE_LIST ResourceList;
38     ULONG ResourceListSize;
39 } PNPROOT_DEVICE, *PPNPROOT_DEVICE;
40 
41 typedef enum
42 {
43     dsStopped,
44     dsStarted,
45     dsPaused,
46     dsRemoved,
47     dsSurpriseRemoved
48 } PNPROOT_DEVICE_STATE;
49 
50 typedef struct _PNPROOT_COMMON_DEVICE_EXTENSION
51 {
52     // Wether this device extension is for an FDO or PDO
53     BOOLEAN IsFDO;
54 } PNPROOT_COMMON_DEVICE_EXTENSION, *PPNPROOT_COMMON_DEVICE_EXTENSION;
55 
56 /* Physical Device Object device extension for a child device */
57 typedef struct _PNPROOT_PDO_DEVICE_EXTENSION
58 {
59     // Common device data
60     PNPROOT_COMMON_DEVICE_EXTENSION Common;
61     // Informations about the device
62     PPNPROOT_DEVICE DeviceInfo;
63 } PNPROOT_PDO_DEVICE_EXTENSION, *PPNPROOT_PDO_DEVICE_EXTENSION;
64 
65 /* Physical Device Object device extension for the Root bus device object */
66 typedef struct _PNPROOT_FDO_DEVICE_EXTENSION
67 {
68     // Common device data
69     PNPROOT_COMMON_DEVICE_EXTENSION Common;
70     // Lower device object
71     PDEVICE_OBJECT Ldo;
72     // Current state of the driver
73     PNPROOT_DEVICE_STATE State;
74     // Namespace device list
75     LIST_ENTRY DeviceListHead;
76     // Number of (not removed) devices in device list
77     ULONG DeviceListCount;
78     // Lock for namespace device list
79     KGUARDED_MUTEX DeviceListLock;
80 } PNPROOT_FDO_DEVICE_EXTENSION, *PPNPROOT_FDO_DEVICE_EXTENSION;
81 
82 typedef struct _BUFFER
83 {
84     PVOID *Data;
85     PULONG Length;
86 } BUFFER, *PBUFFER;
87 
88 static PDEVICE_OBJECT PnpRootDeviceObject = NULL;
89 
90 /* FUNCTIONS *****************************************************************/
91 
92 static NTSTATUS
93 LocateChildDevice(
94     IN PPNPROOT_FDO_DEVICE_EXTENSION DeviceExtension,
95     IN PCUNICODE_STRING DeviceId,
96     IN PCWSTR InstanceId,
97     OUT PPNPROOT_DEVICE* ChildDevice OPTIONAL)
98 {
99     PPNPROOT_DEVICE Device;
100     UNICODE_STRING InstanceIdU;
101     PLIST_ENTRY NextEntry;
102 
103     /* Initialize the string to compare */
104     RtlInitUnicodeString(&InstanceIdU, InstanceId);
105 
106     /* Start looping */
107     for (NextEntry = DeviceExtension->DeviceListHead.Flink;
108          NextEntry != &DeviceExtension->DeviceListHead;
109          NextEntry = NextEntry->Flink)
110     {
111         /* Get the entry */
112         Device = CONTAINING_RECORD(NextEntry, PNPROOT_DEVICE, ListEntry);
113 
114         /* See if the strings match */
115         if (RtlEqualUnicodeString(DeviceId, &Device->DeviceID, TRUE) &&
116             RtlEqualUnicodeString(&InstanceIdU, &Device->InstanceID, TRUE))
117         {
118             /* They do, so set the pointer and return success */
119             if (ChildDevice)
120                 *ChildDevice = Device;
121             return STATUS_SUCCESS;
122         }
123     }
124 
125     /* No device found */
126     return STATUS_NO_SUCH_DEVICE;
127 }
128 
129 NTSTATUS
130 PnpRootRegisterDevice(
131     IN PDEVICE_OBJECT DeviceObject)
132 {
133     PPNPROOT_FDO_DEVICE_EXTENSION DeviceExtension = PnpRootDeviceObject->DeviceExtension;
134     PPNPROOT_DEVICE Device;
135     PDEVICE_NODE DeviceNode;
136     PWSTR InstancePath;
137     UNICODE_STRING InstancePathCopy;
138 
139     Device = ExAllocatePoolWithTag(PagedPool, sizeof(PNPROOT_DEVICE), TAG_PNP_ROOT);
140     if (!Device) return STATUS_NO_MEMORY;
141 
142     DeviceNode = IopGetDeviceNode(DeviceObject);
143     if (!RtlCreateUnicodeString(&InstancePathCopy, DeviceNode->InstancePath.Buffer))
144     {
145         ExFreePoolWithTag(Device, TAG_PNP_ROOT);
146         return STATUS_NO_MEMORY;
147     }
148 
149     InstancePath = wcsrchr(InstancePathCopy.Buffer, L'\\');
150     ASSERT(InstancePath);
151 
152     if (!RtlCreateUnicodeString(&Device->InstanceID, InstancePath + 1))
153     {
154         RtlFreeUnicodeString(&InstancePathCopy);
155         ExFreePoolWithTag(Device, TAG_PNP_ROOT);
156         return STATUS_NO_MEMORY;
157     }
158 
159     InstancePath[0] = UNICODE_NULL;
160 
161     if (!RtlCreateUnicodeString(&Device->DeviceID, InstancePathCopy.Buffer))
162     {
163         RtlFreeUnicodeString(&InstancePathCopy);
164         RtlFreeUnicodeString(&Device->InstanceID);
165         ExFreePoolWithTag(Device, TAG_PNP_ROOT);
166         return STATUS_NO_MEMORY;
167     }
168 
169     InstancePath[0] = L'\\';
170 
171     Device->Pdo = DeviceObject;
172 
173     KeAcquireGuardedMutex(&DeviceExtension->DeviceListLock);
174     InsertTailList(&DeviceExtension->DeviceListHead,
175                    &Device->ListEntry);
176     DeviceExtension->DeviceListCount++;
177     KeReleaseGuardedMutex(&DeviceExtension->DeviceListLock);
178 
179     RtlFreeUnicodeString(&InstancePathCopy);
180 
181     return STATUS_SUCCESS;
182 }
183 
184 /* Creates a new PnP device for a legacy driver */
185 NTSTATUS
186 PnpRootCreateDevice(
187     IN PUNICODE_STRING ServiceName,
188     IN OPTIONAL PDRIVER_OBJECT DriverObject,
189     OUT PDEVICE_OBJECT *PhysicalDeviceObject,
190     OUT OPTIONAL PUNICODE_STRING FullInstancePath)
191 {
192     PPNPROOT_FDO_DEVICE_EXTENSION DeviceExtension;
193     PPNPROOT_PDO_DEVICE_EXTENSION PdoDeviceExtension;
194     UNICODE_STRING DevicePath;
195     WCHAR InstancePath[5];
196     PPNPROOT_DEVICE Device = NULL;
197     NTSTATUS Status;
198     UNICODE_STRING PathSep = RTL_CONSTANT_STRING(L"\\");
199     ULONG NextInstance;
200     UNICODE_STRING EnumKeyName = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\" REGSTR_PATH_SYSTEMENUM);
201     HANDLE EnumHandle, DeviceKeyHandle = NULL, InstanceKeyHandle;
202     RTL_QUERY_REGISTRY_TABLE QueryTable[2];
203     OBJECT_ATTRIBUTES ObjectAttributes;
204 
205     DeviceExtension = PnpRootDeviceObject->DeviceExtension;
206     KeAcquireGuardedMutex(&DeviceExtension->DeviceListLock);
207 
208     DPRINT("Creating a PnP root device for service '%wZ'\n", ServiceName);
209 
210     DevicePath.Length = 0;
211     DevicePath.MaximumLength = sizeof(REGSTR_KEY_ROOTENUM) + sizeof(L'\\') + ServiceName->Length;
212     DevicePath.Buffer = ExAllocatePoolWithTag(PagedPool,
213                                               DevicePath.MaximumLength,
214                                               TAG_PNP_ROOT);
215     if (DevicePath.Buffer == NULL)
216     {
217         DPRINT1("ExAllocatePoolWithTag() failed\n");
218         Status = STATUS_NO_MEMORY;
219         goto cleanup;
220     }
221     RtlAppendUnicodeToString(&DevicePath, REGSTR_KEY_ROOTENUM L"\\");
222     RtlAppendUnicodeStringToString(&DevicePath, ServiceName);
223 
224     /* Initialize a PNPROOT_DEVICE structure */
225     Device = ExAllocatePoolWithTag(PagedPool, sizeof(PNPROOT_DEVICE), TAG_PNP_ROOT);
226     if (!Device)
227     {
228         DPRINT("ExAllocatePoolWithTag() failed\n");
229         Status = STATUS_NO_MEMORY;
230         goto cleanup;
231     }
232     RtlZeroMemory(Device, sizeof(PNPROOT_DEVICE));
233     Device->DeviceID = DevicePath;
234     RtlInitEmptyUnicodeString(&DevicePath, NULL, 0);
235 
236     Status = IopOpenRegistryKeyEx(&EnumHandle, NULL, &EnumKeyName, KEY_READ);
237     if (NT_SUCCESS(Status))
238     {
239         InitializeObjectAttributes(&ObjectAttributes,
240                                    &Device->DeviceID,
241                                    OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
242                                    EnumHandle,
243                                    NULL);
244         Status = ZwCreateKey(&DeviceKeyHandle, KEY_SET_VALUE, &ObjectAttributes, 0, NULL, REG_OPTION_NON_VOLATILE, NULL);
245         ObCloseHandle(EnumHandle, KernelMode);
246     }
247 
248     if (!NT_SUCCESS(Status))
249     {
250         DPRINT1("Failed to open registry key\n");
251         goto cleanup;
252     }
253 
254 tryagain:
255     RtlZeroMemory(QueryTable, sizeof(QueryTable));
256     QueryTable[0].Name = L"NextInstance";
257     QueryTable[0].EntryContext = &NextInstance;
258     QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
259 
260     Status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
261                                     (PWSTR)DeviceKeyHandle,
262                                     QueryTable,
263                                     NULL,
264                                     NULL);
265     for (NextInstance = 0; NextInstance <= 9999; NextInstance++)
266     {
267         _snwprintf(InstancePath, sizeof(InstancePath) / sizeof(WCHAR), L"%04lu", NextInstance);
268         Status = LocateChildDevice(DeviceExtension, &Device->DeviceID, InstancePath, NULL);
269         if (Status == STATUS_NO_SUCH_DEVICE)
270             break;
271     }
272 
273     if (NextInstance > 9999)
274     {
275         DPRINT1("Too many legacy devices reported for service '%wZ'\n", ServiceName);
276         Status = STATUS_INSUFFICIENT_RESOURCES;
277         goto cleanup;
278     }
279 
280     _snwprintf(InstancePath, sizeof(InstancePath) / sizeof(WCHAR), L"%04lu", NextInstance);
281     Status = LocateChildDevice(DeviceExtension, &Device->DeviceID, InstancePath, NULL);
282     if (Status != STATUS_NO_SUCH_DEVICE || NextInstance > 9999)
283     {
284         DPRINT1("NextInstance value is corrupt! (%lu)\n", NextInstance);
285         RtlDeleteRegistryValue(RTL_REGISTRY_HANDLE,
286                                (PWSTR)DeviceKeyHandle,
287                                L"NextInstance");
288         goto tryagain;
289     }
290 
291     NextInstance++;
292     Status = RtlWriteRegistryValue(RTL_REGISTRY_HANDLE,
293                                    (PWSTR)DeviceKeyHandle,
294                                    L"NextInstance",
295                                    REG_DWORD,
296                                    &NextInstance,
297                                    sizeof(NextInstance));
298     if (!NT_SUCCESS(Status))
299     {
300         DPRINT1("Failed to write new NextInstance value! (0x%x)\n", Status);
301         goto cleanup;
302     }
303 
304     if (!RtlCreateUnicodeString(&Device->InstanceID, InstancePath))
305     {
306         Status = STATUS_NO_MEMORY;
307         goto cleanup;
308     }
309 
310     /* Finish creating the instance path in the registry */
311     InitializeObjectAttributes(&ObjectAttributes,
312                                &Device->InstanceID,
313                                OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
314                                DeviceKeyHandle,
315                                NULL);
316     Status = ZwCreateKey(&InstanceKeyHandle, KEY_QUERY_VALUE, &ObjectAttributes, 0, NULL, REG_OPTION_NON_VOLATILE, NULL);
317     if (!NT_SUCCESS(Status))
318     {
319         DPRINT1("Failed to create instance path (0x%x)\n", Status);
320         goto cleanup;
321     }
322 
323     /* Just close the handle */
324     ObCloseHandle(InstanceKeyHandle, KernelMode);
325 
326     if (FullInstancePath)
327     {
328         FullInstancePath->MaximumLength = Device->DeviceID.Length + PathSep.Length + Device->InstanceID.Length;
329         FullInstancePath->Length = 0;
330         FullInstancePath->Buffer = ExAllocatePool(PagedPool, FullInstancePath->MaximumLength);
331         if (!FullInstancePath->Buffer)
332         {
333             Status = STATUS_NO_MEMORY;
334             goto cleanup;
335         }
336 
337         RtlAppendUnicodeStringToString(FullInstancePath, &Device->DeviceID);
338         RtlAppendUnicodeStringToString(FullInstancePath, &PathSep);
339         RtlAppendUnicodeStringToString(FullInstancePath, &Device->InstanceID);
340     }
341 
342     /* Initialize a device object */
343     Status = IoCreateDevice(
344         DriverObject ? DriverObject : PnpRootDeviceObject->DriverObject,
345         sizeof(PNPROOT_PDO_DEVICE_EXTENSION),
346         NULL,
347         FILE_DEVICE_CONTROLLER,
348         FILE_AUTOGENERATED_DEVICE_NAME,
349         FALSE,
350         &Device->Pdo);
351     if (!NT_SUCCESS(Status))
352     {
353         DPRINT("IoCreateDevice() failed with status 0x%08lx\n", Status);
354         Status = STATUS_NO_MEMORY;
355         goto cleanup;
356     }
357 
358     PdoDeviceExtension = (PPNPROOT_PDO_DEVICE_EXTENSION)Device->Pdo->DeviceExtension;
359     RtlZeroMemory(PdoDeviceExtension, sizeof(PNPROOT_PDO_DEVICE_EXTENSION));
360     PdoDeviceExtension->Common.IsFDO = FALSE;
361     PdoDeviceExtension->DeviceInfo = Device;
362 
363     Device->Pdo->Flags |= DO_BUS_ENUMERATED_DEVICE;
364     Device->Pdo->Flags &= ~DO_DEVICE_INITIALIZING;
365 
366     InsertTailList(
367         &DeviceExtension->DeviceListHead,
368         &Device->ListEntry);
369     DeviceExtension->DeviceListCount++;
370 
371     *PhysicalDeviceObject = Device->Pdo;
372     DPRINT("Created PDO %p (%wZ\\%wZ)\n", *PhysicalDeviceObject, &Device->DeviceID, &Device->InstanceID);
373     Device = NULL;
374     Status = STATUS_SUCCESS;
375 
376 cleanup:
377     KeReleaseGuardedMutex(&DeviceExtension->DeviceListLock);
378     if (Device)
379     {
380         if (Device->Pdo)
381             IoDeleteDevice(Device->Pdo);
382         RtlFreeUnicodeString(&Device->DeviceID);
383         RtlFreeUnicodeString(&Device->InstanceID);
384         ExFreePoolWithTag(Device, TAG_PNP_ROOT);
385     }
386     RtlFreeUnicodeString(&DevicePath);
387     if (DeviceKeyHandle != NULL)
388         ObCloseHandle(DeviceKeyHandle, KernelMode);
389     return Status;
390 }
391 
392 static NTSTATUS NTAPI
393 QueryStringCallback(
394     IN PWSTR ValueName,
395     IN ULONG ValueType,
396     IN PVOID ValueData,
397     IN ULONG ValueLength,
398     IN PVOID Context,
399     IN PVOID EntryContext)
400 {
401     PUNICODE_STRING Destination = (PUNICODE_STRING)EntryContext;
402     UNICODE_STRING Source;
403 
404     if (ValueType != REG_SZ || ValueLength == 0 || ValueLength % sizeof(WCHAR) != 0)
405     {
406         Destination->Length = 0;
407         Destination->MaximumLength = 0;
408         Destination->Buffer = NULL;
409         return STATUS_SUCCESS;
410     }
411 
412     Source.MaximumLength = Source.Length = (USHORT)ValueLength;
413     Source.Buffer = ValueData;
414 
415     return RtlDuplicateUnicodeString(RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE, &Source, Destination);
416 }
417 
418 static NTSTATUS NTAPI
419 QueryBinaryValueCallback(
420     IN PWSTR ValueName,
421     IN ULONG ValueType,
422     IN PVOID ValueData,
423     IN ULONG ValueLength,
424     IN PVOID Context,
425     IN PVOID EntryContext)
426 {
427     PBUFFER Buffer = (PBUFFER)EntryContext;
428     PVOID BinaryValue;
429 
430     if (ValueLength == 0)
431     {
432         *Buffer->Data = NULL;
433         return STATUS_SUCCESS;
434     }
435 
436     BinaryValue = ExAllocatePoolWithTag(PagedPool, ValueLength, TAG_PNP_ROOT);
437     if (BinaryValue == NULL)
438         return STATUS_NO_MEMORY;
439     RtlCopyMemory(BinaryValue, ValueData, ValueLength);
440     *Buffer->Data = BinaryValue;
441     if (Buffer->Length) *Buffer->Length = ValueLength;
442     return STATUS_SUCCESS;
443 }
444 
445 static
446 NTSTATUS
447 CreateDeviceFromRegistry(
448     _Inout_ PPNPROOT_FDO_DEVICE_EXTENSION DeviceExtension,
449     _Inout_ PUNICODE_STRING DevicePath,
450     _In_ PCWSTR InstanceId,
451     _In_ HANDLE SubKeyHandle)
452 {
453     NTSTATUS Status;
454     PPNPROOT_DEVICE Device;
455     HANDLE DeviceKeyHandle = NULL;
456     RTL_QUERY_REGISTRY_TABLE QueryTable[4];
457     BUFFER Buffer1, Buffer2;
458 
459     /* If the device already exists, there's nothing to do */
460     Status = LocateChildDevice(DeviceExtension, DevicePath, InstanceId, &Device);
461     if (Status != STATUS_NO_SUCH_DEVICE)
462     {
463         return STATUS_SUCCESS;
464     }
465 
466     /* Create a PPNPROOT_DEVICE object, and add it to the list of known devices */
467     Device = ExAllocatePoolWithTag(PagedPool, sizeof(PNPROOT_DEVICE), TAG_PNP_ROOT);
468     if (!Device)
469     {
470         DPRINT("ExAllocatePoolWithTag() failed\n");
471         Status = STATUS_NO_MEMORY;
472         goto cleanup;
473     }
474     RtlZeroMemory(Device, sizeof(PNPROOT_DEVICE));
475 
476     /* Fill device ID and instance ID */
477     Device->DeviceID = *DevicePath;
478     RtlInitEmptyUnicodeString(DevicePath, NULL, 0);
479     if (!RtlCreateUnicodeString(&Device->InstanceID, InstanceId))
480     {
481         DPRINT1("RtlCreateUnicodeString() failed\n");
482         Status = STATUS_NO_MEMORY;
483         goto cleanup;
484     }
485 
486     /* Open registry key to fill other informations */
487     Status = IopOpenRegistryKeyEx(&DeviceKeyHandle, SubKeyHandle, &Device->InstanceID, KEY_READ);
488     if (!NT_SUCCESS(Status))
489     {
490         /* If our key disappeared, let the caller go on */
491         DPRINT1("IopOpenRegistryKeyEx() failed for '%wZ' with status 0x%lx\n",
492                 &Device->InstanceID, Status);
493         Status = STATUS_SUCCESS;
494         goto cleanup;
495     }
496 
497     /* Fill information from the device instance key */
498     RtlZeroMemory(QueryTable, sizeof(QueryTable));
499     QueryTable[0].QueryRoutine = QueryStringCallback;
500     QueryTable[0].Name = L"DeviceDesc";
501     QueryTable[0].EntryContext = &Device->DeviceDescription;
502 
503     RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
504                            (PCWSTR)DeviceKeyHandle,
505                            QueryTable,
506                            NULL,
507                            NULL);
508 
509     /* Fill information from the LogConf subkey */
510     Buffer1.Data = (PVOID *)&Device->ResourceRequirementsList;
511     Buffer1.Length = NULL;
512     Buffer2.Data = (PVOID *)&Device->ResourceList;
513     Buffer2.Length = &Device->ResourceListSize;
514     RtlZeroMemory(QueryTable, sizeof(QueryTable));
515     QueryTable[0].Flags = RTL_QUERY_REGISTRY_SUBKEY;
516     QueryTable[0].Name = L"LogConf";
517     QueryTable[1].QueryRoutine = QueryBinaryValueCallback;
518     QueryTable[1].Name = L"BasicConfigVector";
519     QueryTable[1].EntryContext = &Buffer1;
520     QueryTable[2].QueryRoutine = QueryBinaryValueCallback;
521     QueryTable[2].Name = L"BootConfig";
522     QueryTable[2].EntryContext = &Buffer2;
523 
524     if (!NT_SUCCESS(RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
525                                            (PCWSTR)DeviceKeyHandle,
526                                            QueryTable,
527                                            NULL,
528                                            NULL)))
529     {
530         /* Non-fatal error */
531         DPRINT1("Failed to read the LogConf key for %wZ\\%S\n", &Device->DeviceID, InstanceId);
532     }
533 
534     /* Insert the newly created device into the list */
535     InsertTailList(&DeviceExtension->DeviceListHead,
536                    &Device->ListEntry);
537     DeviceExtension->DeviceListCount++;
538     Device = NULL;
539 
540 cleanup:
541     if (DeviceKeyHandle != NULL)
542     {
543         ZwClose(DeviceKeyHandle);
544     }
545     if (Device != NULL)
546     {
547         /* We have a device that has not been added to device list. We need to clean it up */
548         RtlFreeUnicodeString(&Device->DeviceID);
549         RtlFreeUnicodeString(&Device->InstanceID);
550         ExFreePoolWithTag(Device, TAG_PNP_ROOT);
551     }
552     return Status;
553 }
554 
555 static NTSTATUS
556 IopShouldProcessDevice(
557     IN HANDLE SubKey,
558     IN PCWSTR InstanceID)
559 {
560     UNICODE_STRING DeviceReportedValue = RTL_CONSTANT_STRING(L"DeviceReported");
561     UNICODE_STRING Control = RTL_CONSTANT_STRING(L"Control");
562     UNICODE_STRING InstanceIDU;
563     PKEY_VALUE_FULL_INFORMATION pKeyValueFullInformation;
564     HANDLE InstanceKey, ControlKey;
565     OBJECT_ATTRIBUTES ObjectAttributes;
566     ULONG Size, DeviceReported, ResultLength;
567     NTSTATUS Status;
568 
569     Size = 128;
570     pKeyValueFullInformation = ExAllocatePool(PagedPool, Size);
571     if (!pKeyValueFullInformation)
572         return STATUS_INSUFFICIENT_RESOURCES;
573 
574     /* Open Instance key */
575     RtlInitUnicodeString(&InstanceIDU, InstanceID);
576     InitializeObjectAttributes(&ObjectAttributes,
577                                &InstanceIDU,
578                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
579                                SubKey,
580                                NULL);
581     Status = ZwOpenKey(&InstanceKey,
582                        KEY_QUERY_VALUE,
583                        &ObjectAttributes);
584     if (!NT_SUCCESS(Status))
585     {
586         ExFreePool(pKeyValueFullInformation);
587         return Status;
588     }
589 
590     /* Read 'DeviceReported' Key */
591     Status = ZwQueryValueKey(InstanceKey, &DeviceReportedValue, KeyValueFullInformation, pKeyValueFullInformation, Size, &ResultLength);
592     if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
593     {
594         ZwClose(InstanceKey);
595         ExFreePool(pKeyValueFullInformation);
596         DPRINT("No 'DeviceReported' value\n");
597         return STATUS_SUCCESS;
598     }
599     else if (!NT_SUCCESS(Status))
600     {
601         ZwClose(InstanceKey);
602         ExFreePool(pKeyValueFullInformation);
603         return Status;
604     }
605     if (pKeyValueFullInformation->Type != REG_DWORD || pKeyValueFullInformation->DataLength != sizeof(DeviceReported))
606     {
607         ZwClose(InstanceKey);
608         ExFreePool(pKeyValueFullInformation);
609         return STATUS_UNSUCCESSFUL;
610     }
611     RtlCopyMemory(&DeviceReported, (PVOID)((ULONG_PTR)pKeyValueFullInformation + pKeyValueFullInformation->DataOffset), sizeof(DeviceReported));
612     /* FIXME: Check DeviceReported value? */
613     ASSERT(DeviceReported == 1);
614 
615     /* Open Control key */
616     InitializeObjectAttributes(&ObjectAttributes,
617                                &Control,
618                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
619                                InstanceKey,
620                                NULL);
621     Status = ZwOpenKey(&ControlKey,
622                        KEY_QUERY_VALUE,
623                        &ObjectAttributes);
624     ZwClose(InstanceKey);
625     if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
626     {
627         DPRINT("No 'Control' key\n");
628         return STATUS_NO_SUCH_DEVICE;
629     }
630     else if (!NT_SUCCESS(Status))
631     {
632         ExFreePool(pKeyValueFullInformation);
633         return Status;
634     }
635 
636     /* Read 'DeviceReported' Key */
637     Status = ZwQueryValueKey(ControlKey, &DeviceReportedValue, KeyValueFullInformation, pKeyValueFullInformation, Size, &ResultLength);
638     ZwClose(ControlKey);
639     if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
640     {
641         ExFreePool(pKeyValueFullInformation);
642         DPRINT("No 'DeviceReported' value\n");
643         return STATUS_NO_SUCH_DEVICE;
644     }
645     else if (!NT_SUCCESS(Status))
646     {
647         ExFreePool(pKeyValueFullInformation);
648         return Status;
649     }
650     if (pKeyValueFullInformation->Type != REG_DWORD || pKeyValueFullInformation->DataLength != sizeof(DeviceReported))
651     {
652         ExFreePool(pKeyValueFullInformation);
653         return STATUS_UNSUCCESSFUL;
654     }
655     RtlCopyMemory(&DeviceReported, (PVOID)((ULONG_PTR)pKeyValueFullInformation + pKeyValueFullInformation->DataOffset), sizeof(DeviceReported));
656     /* FIXME: Check DeviceReported value? */
657     ASSERT(DeviceReported == 1);
658 
659     ExFreePool(pKeyValueFullInformation);
660     return STATUS_SUCCESS;
661 }
662 
663 static NTSTATUS
664 EnumerateDevices(
665     IN PDEVICE_OBJECT DeviceObject)
666 {
667     PPNPROOT_FDO_DEVICE_EXTENSION DeviceExtension;
668     PKEY_BASIC_INFORMATION KeyInfo = NULL, SubKeyInfo = NULL;
669     UNICODE_STRING LegacyU = RTL_CONSTANT_STRING(L"LEGACY_");
670     UNICODE_STRING KeyName = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\" REGSTR_PATH_SYSTEMENUM L"\\" REGSTR_KEY_ROOTENUM);
671     UNICODE_STRING SubKeyName;
672     UNICODE_STRING DevicePath;
673     HANDLE KeyHandle = NULL;
674     HANDLE SubKeyHandle = NULL;
675     ULONG KeyInfoSize, SubKeyInfoSize;
676     ULONG ResultSize;
677     ULONG Index1, Index2;
678     NTSTATUS Status = STATUS_UNSUCCESSFUL;
679 
680     DPRINT("EnumerateDevices(FDO %p)\n", DeviceObject);
681 
682     DeviceExtension = (PPNPROOT_FDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
683     KeAcquireGuardedMutex(&DeviceExtension->DeviceListLock);
684 
685     /* Should hold most key names, but we reallocate below if it's too small */
686     KeyInfoSize = FIELD_OFFSET(KEY_BASIC_INFORMATION, Name) + 64 * sizeof(WCHAR);
687     KeyInfo = ExAllocatePoolWithTag(PagedPool,
688                                     KeyInfoSize + sizeof(UNICODE_NULL),
689                                     TAG_PNP_ROOT);
690     if (!KeyInfo)
691     {
692         DPRINT("ExAllocatePoolWithTag() failed\n");
693         Status = STATUS_NO_MEMORY;
694         goto cleanup;
695     }
696     SubKeyInfoSize = KeyInfoSize;
697     SubKeyInfo = ExAllocatePoolWithTag(PagedPool,
698                                        SubKeyInfoSize + sizeof(UNICODE_NULL),
699                                        TAG_PNP_ROOT);
700     if (!SubKeyInfo)
701     {
702         DPRINT("ExAllocatePoolWithTag() failed\n");
703         Status = STATUS_NO_MEMORY;
704         goto cleanup;
705     }
706 
707     Status = IopOpenRegistryKeyEx(&KeyHandle, NULL, &KeyName, KEY_ENUMERATE_SUB_KEYS);
708     if (!NT_SUCCESS(Status))
709     {
710         DPRINT("IopOpenRegistryKeyEx(%wZ) failed with status 0x%08lx\n", &KeyName, Status);
711         goto cleanup;
712     }
713 
714     /* Devices are sub-sub-keys of 'KeyName'. KeyName is already opened as
715      * KeyHandle. We'll first do a first enumeration to have first level keys,
716      * and an inner one to have the real devices list.
717      */
718     Index1 = 0;
719     while (TRUE)
720     {
721         Status = ZwEnumerateKey(
722             KeyHandle,
723             Index1,
724             KeyBasicInformation,
725             KeyInfo,
726             KeyInfoSize,
727             &ResultSize);
728         if (Status == STATUS_NO_MORE_ENTRIES)
729         {
730             Status = STATUS_SUCCESS;
731             break;
732         }
733         else if (Status == STATUS_BUFFER_OVERFLOW ||
734                  Status == STATUS_BUFFER_TOO_SMALL)
735         {
736             ASSERT(KeyInfoSize < ResultSize);
737             KeyInfoSize = ResultSize;
738             ExFreePoolWithTag(KeyInfo, TAG_PNP_ROOT);
739             KeyInfo = ExAllocatePoolWithTag(PagedPool,
740                                             KeyInfoSize + sizeof(UNICODE_NULL),
741                                             TAG_PNP_ROOT);
742             if (!KeyInfo)
743             {
744                 DPRINT1("ExAllocatePoolWithTag(%lu) failed\n", KeyInfoSize);
745                 Status = STATUS_NO_MEMORY;
746                 goto cleanup;
747             }
748             continue;
749         }
750         else if (!NT_SUCCESS(Status))
751         {
752             DPRINT("ZwEnumerateKey() failed with status 0x%08lx\n", Status);
753             goto cleanup;
754         }
755 
756         /* Terminate the string */
757         KeyInfo->Name[KeyInfo->NameLength / sizeof(WCHAR)] = 0;
758 
759         /* Check if it is a legacy driver */
760         RtlInitUnicodeString(&SubKeyName, KeyInfo->Name);
761         if (RtlPrefixUnicodeString(&LegacyU, &SubKeyName, FALSE))
762         {
763             DPRINT("Ignoring legacy driver '%wZ'\n", &SubKeyName);
764             Index1++;
765             continue;
766         }
767 
768         /* Open the key */
769         Status = IopOpenRegistryKeyEx(&SubKeyHandle, KeyHandle, &SubKeyName, KEY_ENUMERATE_SUB_KEYS);
770         if (!NT_SUCCESS(Status))
771         {
772             DPRINT("IopOpenRegistryKeyEx() failed for '%wZ' with status 0x%lx\n",
773                    &SubKeyName, Status);
774             break;
775         }
776 
777         /* Enumerate the sub-keys */
778         Index2 = 0;
779         while (TRUE)
780         {
781             Status = ZwEnumerateKey(
782                 SubKeyHandle,
783                 Index2,
784                 KeyBasicInformation,
785                 SubKeyInfo,
786                 SubKeyInfoSize,
787                 &ResultSize);
788             if (Status == STATUS_NO_MORE_ENTRIES)
789             {
790                 break;
791             }
792             else if (Status == STATUS_BUFFER_OVERFLOW ||
793                      Status == STATUS_BUFFER_TOO_SMALL)
794             {
795                 ASSERT(SubKeyInfoSize < ResultSize);
796                 SubKeyInfoSize = ResultSize;
797                 ExFreePoolWithTag(SubKeyInfo, TAG_PNP_ROOT);
798                 SubKeyInfo = ExAllocatePoolWithTag(PagedPool,
799                                                    SubKeyInfoSize + sizeof(UNICODE_NULL),
800                                                    TAG_PNP_ROOT);
801                 if (!SubKeyInfo)
802                 {
803                     DPRINT1("ExAllocatePoolWithTag(%lu) failed\n", SubKeyInfoSize);
804                     Status = STATUS_NO_MEMORY;
805                     goto cleanup;
806                 }
807                 continue;
808             }
809             else if (!NT_SUCCESS(Status))
810             {
811                 DPRINT("ZwEnumerateKey() failed with status 0x%08lx\n", Status);
812                 break;
813             }
814 
815             /* Terminate the string */
816             SubKeyInfo->Name[SubKeyInfo->NameLength / sizeof(WCHAR)] = 0;
817 
818             /* Compute device ID */
819             DevicePath.Length = 0;
820             DevicePath.MaximumLength = sizeof(REGSTR_KEY_ROOTENUM) + sizeof(L'\\') + SubKeyName.Length;
821             DevicePath.Buffer = ExAllocatePoolWithTag(PagedPool,
822                                                       DevicePath.MaximumLength,
823                                                       TAG_PNP_ROOT);
824             if (DevicePath.Buffer == NULL)
825             {
826                 DPRINT1("ExAllocatePoolWithTag() failed\n");
827                 Status = STATUS_NO_MEMORY;
828                 goto cleanup;
829             }
830 
831             RtlAppendUnicodeToString(&DevicePath, REGSTR_KEY_ROOTENUM L"\\");
832             RtlAppendUnicodeStringToString(&DevicePath, &SubKeyName);
833             DPRINT("Found device %wZ\\%S!\n", &DevicePath, SubKeyInfo->Name);
834 
835             Status = IopShouldProcessDevice(SubKeyHandle, SubKeyInfo->Name);
836             if (NT_SUCCESS(Status))
837             {
838                 Status = CreateDeviceFromRegistry(DeviceExtension,
839                                                   &DevicePath,
840                                                   SubKeyInfo->Name,
841                                                   SubKeyHandle);
842 
843                 /* If CreateDeviceFromRegistry didn't take ownership and zero this,
844                  * we need to free it
845                  */
846                 RtlFreeUnicodeString(&DevicePath);
847 
848                 if (!NT_SUCCESS(Status))
849                 {
850                     goto cleanup;
851                 }
852             }
853             else if (Status == STATUS_NO_SUCH_DEVICE)
854             {
855                 DPRINT("Skipping device %wZ\\%S (not reported yet)\n", &DevicePath, SubKeyInfo->Name);
856             }
857             else
858             {
859                 goto cleanup;
860             }
861 
862             Index2++;
863         }
864 
865         ZwClose(SubKeyHandle);
866         SubKeyHandle = NULL;
867         Index1++;
868     }
869 
870 cleanup:
871     if (SubKeyHandle != NULL)
872         ZwClose(SubKeyHandle);
873     if (KeyHandle != NULL)
874         ZwClose(KeyHandle);
875     if (KeyInfo)
876         ExFreePoolWithTag(KeyInfo, TAG_PNP_ROOT);
877     if (SubKeyInfo)
878         ExFreePoolWithTag(SubKeyInfo, TAG_PNP_ROOT);
879     KeReleaseGuardedMutex(&DeviceExtension->DeviceListLock);
880     return Status;
881 }
882 
883 /* FUNCTION: Handle IRP_MN_QUERY_DEVICE_RELATIONS IRPs for the root bus device object
884  * ARGUMENTS:
885  *     DeviceObject = Pointer to functional device object of the root bus driver
886  *     Irp          = Pointer to IRP that should be handled
887  * RETURNS:
888  *     Status
889  */
890 static NTSTATUS
891 PnpRootQueryDeviceRelations(
892     IN PDEVICE_OBJECT DeviceObject,
893     IN PIRP Irp)
894 {
895     PPNPROOT_PDO_DEVICE_EXTENSION PdoDeviceExtension;
896     PPNPROOT_FDO_DEVICE_EXTENSION DeviceExtension;
897     PDEVICE_RELATIONS Relations = NULL, OtherRelations = (PDEVICE_RELATIONS)Irp->IoStatus.Information;
898     PPNPROOT_DEVICE Device = NULL;
899     ULONG Size;
900     NTSTATUS Status;
901     PLIST_ENTRY NextEntry;
902 
903     DPRINT("PnpRootQueryDeviceRelations(FDO %p, Irp %p)\n", DeviceObject, Irp);
904 
905     Status = EnumerateDevices(DeviceObject);
906     if (!NT_SUCCESS(Status))
907     {
908         DPRINT("EnumerateDevices() failed with status 0x%08lx\n", Status);
909         return Status;
910     }
911 
912     DeviceExtension = (PPNPROOT_FDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
913 
914     Size = FIELD_OFFSET(DEVICE_RELATIONS, Objects) + sizeof(PDEVICE_OBJECT) * DeviceExtension->DeviceListCount;
915     if (OtherRelations)
916     {
917         /* Another bus driver has already created a DEVICE_RELATIONS
918          * structure so we must merge this structure with our own */
919 
920         Size += sizeof(PDEVICE_OBJECT) * OtherRelations->Count;
921     }
922     Relations = (PDEVICE_RELATIONS)ExAllocatePool(PagedPool, Size);
923     if (!Relations)
924     {
925         DPRINT("ExAllocatePoolWithTag() failed\n");
926         Status = STATUS_NO_MEMORY;
927         goto cleanup;
928     }
929     RtlZeroMemory(Relations, Size);
930     if (OtherRelations)
931     {
932         Relations->Count = OtherRelations->Count;
933         RtlCopyMemory(Relations->Objects, OtherRelations->Objects, sizeof(PDEVICE_OBJECT) * OtherRelations->Count);
934     }
935 
936     KeAcquireGuardedMutex(&DeviceExtension->DeviceListLock);
937 
938     /* Start looping */
939     for (NextEntry = DeviceExtension->DeviceListHead.Flink;
940          NextEntry != &DeviceExtension->DeviceListHead;
941          NextEntry = NextEntry->Flink)
942     {
943         /* Get the entry */
944         Device = CONTAINING_RECORD(NextEntry, PNPROOT_DEVICE, ListEntry);
945 
946         if (!Device->Pdo)
947         {
948             /* Create a physical device object for the
949              * device as it does not already have one */
950             Status = IoCreateDevice(
951                 DeviceObject->DriverObject,
952                 sizeof(PNPROOT_PDO_DEVICE_EXTENSION),
953                 NULL,
954                 FILE_DEVICE_CONTROLLER,
955                 FILE_AUTOGENERATED_DEVICE_NAME,
956                 FALSE,
957                 &Device->Pdo);
958             if (!NT_SUCCESS(Status))
959             {
960                 DPRINT("IoCreateDevice() failed with status 0x%08lx\n", Status);
961                 break;
962             }
963 
964             PdoDeviceExtension = (PPNPROOT_PDO_DEVICE_EXTENSION)Device->Pdo->DeviceExtension;
965             RtlZeroMemory(PdoDeviceExtension, sizeof(PNPROOT_PDO_DEVICE_EXTENSION));
966             PdoDeviceExtension->Common.IsFDO = FALSE;
967             PdoDeviceExtension->DeviceInfo = Device;
968 
969             Device->Pdo->Flags |= DO_BUS_ENUMERATED_DEVICE;
970             Device->Pdo->Flags &= ~DO_DEVICE_INITIALIZING;
971         }
972 
973         /* Reference the physical device object. The PnP manager
974          will dereference it again when it is no longer needed */
975         ObReferenceObject(Device->Pdo);
976 
977         Relations->Objects[Relations->Count++] = Device->Pdo;
978     }
979     KeReleaseGuardedMutex(&DeviceExtension->DeviceListLock);
980 
981     Irp->IoStatus.Information = (ULONG_PTR)Relations;
982 
983 cleanup:
984     if (!NT_SUCCESS(Status))
985     {
986         if (OtherRelations)
987             ExFreePool(OtherRelations);
988         if (Relations)
989             ExFreePool(Relations);
990         if (Device && Device->Pdo)
991         {
992             IoDeleteDevice(Device->Pdo);
993             Device->Pdo = NULL;
994         }
995     }
996 
997     return Status;
998 }
999 
1000 /*
1001  * FUNCTION: Handle Plug and Play IRPs for the root bus device object
1002  * ARGUMENTS:
1003  *     DeviceObject = Pointer to functional device object of the root bus driver
1004  *     Irp          = Pointer to IRP that should be handled
1005  * RETURNS:
1006  *     Status
1007  */
1008 static NTSTATUS
1009 PnpRootFdoPnpControl(
1010     IN PDEVICE_OBJECT DeviceObject,
1011     IN PIRP Irp)
1012 {
1013     PPNPROOT_FDO_DEVICE_EXTENSION DeviceExtension;
1014     PIO_STACK_LOCATION IrpSp;
1015     NTSTATUS Status;
1016 
1017     DeviceExtension = (PPNPROOT_FDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
1018     Status = Irp->IoStatus.Status;
1019     IrpSp = IoGetCurrentIrpStackLocation(Irp);
1020 
1021     switch (IrpSp->MinorFunction)
1022     {
1023         case IRP_MN_QUERY_DEVICE_RELATIONS:
1024             DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS\n");
1025             Status = PnpRootQueryDeviceRelations(DeviceObject, Irp);
1026             break;
1027 
1028         case IRP_MN_START_DEVICE:
1029             DPRINT("IRP_MJ_PNP / IRP_MN_START_DEVICE\n");
1030             if (!IoForwardIrpSynchronously(DeviceExtension->Ldo, Irp))
1031                 Status = STATUS_UNSUCCESSFUL;
1032             else
1033             {
1034                 Status = Irp->IoStatus.Status;
1035                 if (NT_SUCCESS(Status))
1036                     DeviceExtension->State = dsStarted;
1037             }
1038 
1039             Irp->IoStatus.Status = Status;
1040             IoCompleteRequest(Irp, IO_NO_INCREMENT);
1041             return Status;
1042 
1043          case IRP_MN_STOP_DEVICE:
1044              DPRINT("IRP_MJ_PNP / IRP_MN_STOP_DEVICE\n");
1045              /* Root device cannot be stopped */
1046              Irp->IoStatus.Status = Status = STATUS_INVALID_DEVICE_REQUEST;
1047              IoCompleteRequest(Irp, IO_NO_INCREMENT);
1048              return Status;
1049 
1050         default:
1051             DPRINT("IRP_MJ_PNP / Unknown minor function 0x%lx\n", IrpSp->MinorFunction);
1052             break;
1053     }
1054 
1055     if (Status != STATUS_PENDING)
1056     {
1057        Irp->IoStatus.Status = Status;
1058        IoCompleteRequest(Irp, IO_NO_INCREMENT);
1059     }
1060 
1061     return Status;
1062 }
1063 
1064 static NTSTATUS
1065 PdoQueryDeviceRelations(
1066     IN PDEVICE_OBJECT DeviceObject,
1067     IN PIRP Irp,
1068     IN PIO_STACK_LOCATION IrpSp)
1069 {
1070     PDEVICE_RELATIONS Relations;
1071     NTSTATUS Status = Irp->IoStatus.Status;
1072 
1073     if (IrpSp->Parameters.QueryDeviceRelations.Type != TargetDeviceRelation)
1074         return Status;
1075 
1076     DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / TargetDeviceRelation\n");
1077     Relations = (PDEVICE_RELATIONS)ExAllocatePool(PagedPool, sizeof(DEVICE_RELATIONS));
1078     if (!Relations)
1079     {
1080         DPRINT("ExAllocatePoolWithTag() failed\n");
1081         Status = STATUS_NO_MEMORY;
1082     }
1083     else
1084     {
1085         ObReferenceObject(DeviceObject);
1086         Relations->Count = 1;
1087         Relations->Objects[0] = DeviceObject;
1088         Status = STATUS_SUCCESS;
1089         Irp->IoStatus.Information = (ULONG_PTR)Relations;
1090     }
1091 
1092     return Status;
1093 }
1094 
1095 static NTSTATUS
1096 PdoQueryCapabilities(
1097     IN PDEVICE_OBJECT DeviceObject,
1098     IN PIRP Irp,
1099     IN PIO_STACK_LOCATION IrpSp)
1100 {
1101     PDEVICE_CAPABILITIES DeviceCapabilities;
1102 
1103     DeviceCapabilities = IrpSp->Parameters.DeviceCapabilities.Capabilities;
1104 
1105     if (DeviceCapabilities->Version != 1)
1106         return STATUS_REVISION_MISMATCH;
1107 
1108     DeviceCapabilities->UniqueID = TRUE;
1109     /* FIXME: Fill other fields */
1110 
1111     return STATUS_SUCCESS;
1112 }
1113 
1114 static NTSTATUS
1115 PdoQueryResources(
1116     IN PDEVICE_OBJECT DeviceObject,
1117     IN PIRP Irp,
1118     IN PIO_STACK_LOCATION IrpSp)
1119 {
1120     PPNPROOT_PDO_DEVICE_EXTENSION DeviceExtension;
1121     PCM_RESOURCE_LIST ResourceList;
1122 
1123     DeviceExtension = (PPNPROOT_PDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
1124 
1125     if (DeviceExtension->DeviceInfo->ResourceList)
1126     {
1127         /* Copy existing resource requirement list */
1128         ResourceList = ExAllocatePool(
1129             PagedPool,
1130             DeviceExtension->DeviceInfo->ResourceListSize);
1131         if (!ResourceList)
1132             return STATUS_NO_MEMORY;
1133 
1134         RtlCopyMemory(
1135             ResourceList,
1136             DeviceExtension->DeviceInfo->ResourceList,
1137             DeviceExtension->DeviceInfo->ResourceListSize);
1138 
1139         Irp->IoStatus.Information = (ULONG_PTR)ResourceList;
1140 
1141         return STATUS_SUCCESS;
1142     }
1143     else
1144     {
1145         /* No resources so just return without changing the status */
1146         return Irp->IoStatus.Status;
1147     }
1148 }
1149 
1150 static NTSTATUS
1151 PdoQueryResourceRequirements(
1152     IN PDEVICE_OBJECT DeviceObject,
1153     IN PIRP Irp,
1154     IN PIO_STACK_LOCATION IrpSp)
1155 {
1156     PPNPROOT_PDO_DEVICE_EXTENSION DeviceExtension;
1157     PIO_RESOURCE_REQUIREMENTS_LIST ResourceList;
1158 
1159     DeviceExtension = (PPNPROOT_PDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
1160 
1161     if (DeviceExtension->DeviceInfo->ResourceRequirementsList)
1162     {
1163         /* Copy existing resource requirement list */
1164         ResourceList = ExAllocatePool(PagedPool, DeviceExtension->DeviceInfo->ResourceRequirementsList->ListSize);
1165         if (!ResourceList)
1166             return STATUS_NO_MEMORY;
1167 
1168         RtlCopyMemory(
1169             ResourceList,
1170             DeviceExtension->DeviceInfo->ResourceRequirementsList,
1171             DeviceExtension->DeviceInfo->ResourceRequirementsList->ListSize);
1172 
1173         Irp->IoStatus.Information = (ULONG_PTR)ResourceList;
1174 
1175         return STATUS_SUCCESS;
1176     }
1177     else
1178     {
1179         /* No resource requirements so just return without changing the status */
1180         return Irp->IoStatus.Status;
1181     }
1182 }
1183 
1184 static NTSTATUS
1185 PdoQueryDeviceText(
1186     IN PDEVICE_OBJECT DeviceObject,
1187     IN PIRP Irp,
1188     IN PIO_STACK_LOCATION IrpSp)
1189 {
1190     PPNPROOT_PDO_DEVICE_EXTENSION DeviceExtension;
1191     DEVICE_TEXT_TYPE DeviceTextType;
1192     NTSTATUS Status = Irp->IoStatus.Status;
1193 
1194     DeviceExtension = (PPNPROOT_PDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
1195     DeviceTextType = IrpSp->Parameters.QueryDeviceText.DeviceTextType;
1196 
1197     switch (DeviceTextType)
1198     {
1199         case DeviceTextDescription:
1200         {
1201             UNICODE_STRING String;
1202             DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_TEXT / DeviceTextDescription\n");
1203 
1204             if (DeviceExtension->DeviceInfo->DeviceDescription.Buffer != NULL)
1205             {
1206                 Status = RtlDuplicateUnicodeString(RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE,
1207                                                    &DeviceExtension->DeviceInfo->DeviceDescription,
1208                                                    &String);
1209                 Irp->IoStatus.Information = (ULONG_PTR)String.Buffer;
1210             }
1211             break;
1212         }
1213 
1214         case DeviceTextLocationInformation:
1215         {
1216             DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_TEXT / DeviceTextLocationInformation\n");
1217             break;
1218         }
1219 
1220         default:
1221         {
1222             DPRINT1("IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_TEXT / unknown query id type 0x%lx\n", DeviceTextType);
1223         }
1224     }
1225 
1226     return Status;
1227 }
1228 
1229 static NTSTATUS
1230 PdoQueryId(
1231     IN PDEVICE_OBJECT DeviceObject,
1232     IN PIRP Irp,
1233     IN PIO_STACK_LOCATION IrpSp)
1234 {
1235     PPNPROOT_PDO_DEVICE_EXTENSION DeviceExtension;
1236     BUS_QUERY_ID_TYPE IdType;
1237     NTSTATUS Status = Irp->IoStatus.Status;
1238 
1239     DeviceExtension = (PPNPROOT_PDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
1240     IdType = IrpSp->Parameters.QueryId.IdType;
1241 
1242     switch (IdType)
1243     {
1244         case BusQueryDeviceID:
1245         {
1246             UNICODE_STRING String;
1247             DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_ID / BusQueryDeviceID\n");
1248 
1249             Status = RtlDuplicateUnicodeString(
1250                 RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE,
1251                 &DeviceExtension->DeviceInfo->DeviceID,
1252                 &String);
1253             Irp->IoStatus.Information = (ULONG_PTR)String.Buffer;
1254             break;
1255         }
1256 
1257         case BusQueryHardwareIDs:
1258         case BusQueryCompatibleIDs:
1259         {
1260             /* Optional, do nothing */
1261             break;
1262         }
1263 
1264         case BusQueryInstanceID:
1265         {
1266             UNICODE_STRING String;
1267             DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_ID / BusQueryInstanceID\n");
1268 
1269             Status = RtlDuplicateUnicodeString(
1270                 RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE,
1271                 &DeviceExtension->DeviceInfo->InstanceID,
1272                 &String);
1273             Irp->IoStatus.Information = (ULONG_PTR)String.Buffer;
1274             break;
1275         }
1276 
1277         default:
1278         {
1279             DPRINT1("IRP_MJ_PNP / IRP_MN_QUERY_ID / unknown query id type 0x%lx\n", IdType);
1280         }
1281     }
1282 
1283     return Status;
1284 }
1285 
1286 static NTSTATUS
1287 PdoQueryBusInformation(
1288     IN PDEVICE_OBJECT DeviceObject,
1289     IN PIRP Irp,
1290     IN PIO_STACK_LOCATION IrpSp)
1291 {
1292     PPNP_BUS_INFORMATION BusInfo;
1293     NTSTATUS Status;
1294 
1295     BusInfo = (PPNP_BUS_INFORMATION)ExAllocatePoolWithTag(PagedPool, sizeof(PNP_BUS_INFORMATION), TAG_PNP_ROOT);
1296     if (!BusInfo)
1297         Status = STATUS_NO_MEMORY;
1298     else
1299     {
1300         RtlCopyMemory(
1301             &BusInfo->BusTypeGuid,
1302             &GUID_BUS_TYPE_INTERNAL,
1303             sizeof(BusInfo->BusTypeGuid));
1304         BusInfo->LegacyBusType = PNPBus;
1305         /* We're the only root bus enumerator on the computer */
1306         BusInfo->BusNumber = 0;
1307         Irp->IoStatus.Information = (ULONG_PTR)BusInfo;
1308         Status = STATUS_SUCCESS;
1309     }
1310 
1311     return Status;
1312 }
1313 
1314 /*
1315  * FUNCTION: Handle Plug and Play IRPs for the child device
1316  * ARGUMENTS:
1317  *     DeviceObject = Pointer to physical device object of the child device
1318  *     Irp          = Pointer to IRP that should be handled
1319  * RETURNS:
1320  *     Status
1321  */
1322 static NTSTATUS
1323 PnpRootPdoPnpControl(
1324   IN PDEVICE_OBJECT DeviceObject,
1325   IN PIRP Irp)
1326 {
1327     PPNPROOT_PDO_DEVICE_EXTENSION DeviceExtension;
1328     PPNPROOT_FDO_DEVICE_EXTENSION FdoDeviceExtension;
1329     PIO_STACK_LOCATION IrpSp;
1330     NTSTATUS Status;
1331 
1332     DeviceExtension = DeviceObject->DeviceExtension;
1333     FdoDeviceExtension = PnpRootDeviceObject->DeviceExtension;
1334     Status = Irp->IoStatus.Status;
1335     IrpSp = IoGetCurrentIrpStackLocation(Irp);
1336 
1337     switch (IrpSp->MinorFunction)
1338     {
1339         case IRP_MN_START_DEVICE: /* 0x00 */
1340             DPRINT("IRP_MJ_PNP / IRP_MN_START_DEVICE\n");
1341             Status = STATUS_SUCCESS;
1342             break;
1343 
1344         case IRP_MN_QUERY_DEVICE_RELATIONS: /* 0x07 */
1345             Status = PdoQueryDeviceRelations(DeviceObject, Irp, IrpSp);
1346             break;
1347 
1348         case IRP_MN_QUERY_CAPABILITIES: /* 0x09 */
1349             DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_CAPABILITIES\n");
1350             Status = PdoQueryCapabilities(DeviceObject, Irp, IrpSp);
1351             break;
1352 
1353         case IRP_MN_QUERY_RESOURCES: /* 0x0a */
1354             DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_RESOURCES\n");
1355             Status = PdoQueryResources(DeviceObject, Irp, IrpSp);
1356             break;
1357 
1358         case IRP_MN_QUERY_RESOURCE_REQUIREMENTS: /* 0x0b */
1359             DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_RESOURCE_REQUIREMENTS\n");
1360             Status = PdoQueryResourceRequirements(DeviceObject, Irp, IrpSp);
1361             break;
1362 
1363         case IRP_MN_QUERY_DEVICE_TEXT: /* 0x0c */
1364             DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_RESOURCE_REQUIREMENTS\n");
1365             Status = PdoQueryDeviceText(DeviceObject, Irp, IrpSp);
1366             break;
1367 
1368         case IRP_MN_FILTER_RESOURCE_REQUIREMENTS: /* 0x0d */
1369             DPRINT("IRP_MJ_PNP / IRP_MN_FILTER_RESOURCE_REQUIREMENTS\n");
1370             break;
1371 
1372         case IRP_MN_REMOVE_DEVICE:
1373             /* Remove the device from the device list and decrement the device count*/
1374             KeAcquireGuardedMutex(&FdoDeviceExtension->DeviceListLock);
1375             RemoveEntryList(&DeviceExtension->DeviceInfo->ListEntry);
1376             FdoDeviceExtension->DeviceListCount--;
1377             KeReleaseGuardedMutex(&FdoDeviceExtension->DeviceListLock);
1378 
1379             /* Free some strings we created */
1380             RtlFreeUnicodeString(&DeviceExtension->DeviceInfo->DeviceDescription);
1381             RtlFreeUnicodeString(&DeviceExtension->DeviceInfo->DeviceID);
1382             RtlFreeUnicodeString(&DeviceExtension->DeviceInfo->InstanceID);
1383 
1384             /* Free the resource requirements list */
1385             if (DeviceExtension->DeviceInfo->ResourceRequirementsList != NULL)
1386             ExFreePool(DeviceExtension->DeviceInfo->ResourceRequirementsList);
1387 
1388             /* Free the boot resources list */
1389             if (DeviceExtension->DeviceInfo->ResourceList != NULL)
1390             ExFreePool(DeviceExtension->DeviceInfo->ResourceList);
1391 
1392             /* Free the device info */
1393             ExFreePool(DeviceExtension->DeviceInfo);
1394 
1395             /* Finally, delete the device object */
1396             IoDeleteDevice(DeviceObject);
1397 
1398             /* Return success */
1399             Status = STATUS_SUCCESS;
1400             break;
1401 
1402         case IRP_MN_QUERY_ID: /* 0x13 */
1403             Status = PdoQueryId(DeviceObject, Irp, IrpSp);
1404             break;
1405 
1406         case IRP_MN_QUERY_PNP_DEVICE_STATE: /* 0x14 */
1407             DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_PNP_DEVICE_STATE\n");
1408             break;
1409 
1410         case IRP_MN_QUERY_BUS_INFORMATION: /* 0x15 */
1411             DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_BUS_INFORMATION\n");
1412             Status = PdoQueryBusInformation(DeviceObject, Irp, IrpSp);
1413             break;
1414 
1415         default:
1416             DPRINT1("IRP_MJ_PNP / Unknown minor function 0x%lx\n", IrpSp->MinorFunction);
1417             break;
1418     }
1419 
1420     if (Status != STATUS_PENDING)
1421     {
1422         Irp->IoStatus.Status = Status;
1423         IoCompleteRequest(Irp, IO_NO_INCREMENT);
1424     }
1425 
1426     return Status;
1427 }
1428 
1429 /*
1430  * FUNCTION: Handle Plug and Play IRPs
1431  * ARGUMENTS:
1432  *     DeviceObject = Pointer to PDO or FDO
1433  *     Irp          = Pointer to IRP that should be handled
1434  * RETURNS:
1435  *     Status
1436  */
1437 static NTSTATUS NTAPI
1438 PnpRootPnpControl(
1439     IN PDEVICE_OBJECT DeviceObject,
1440     IN PIRP Irp)
1441 {
1442     PPNPROOT_COMMON_DEVICE_EXTENSION DeviceExtension;
1443     NTSTATUS Status;
1444 
1445     DeviceExtension = (PPNPROOT_COMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
1446 
1447     if (DeviceExtension->IsFDO)
1448         Status = PnpRootFdoPnpControl(DeviceObject, Irp);
1449     else
1450         Status = PnpRootPdoPnpControl(DeviceObject, Irp);
1451 
1452     return Status;
1453 }
1454 
1455 /*
1456  * FUNCTION: Handle Power IRPs
1457  * ARGUMENTS:
1458  *     DeviceObject = Pointer to PDO or FDO
1459  *     Irp          = Pointer to IRP that should be handled
1460  * RETURNS:
1461  *     Status
1462  */
1463 static NTSTATUS NTAPI
1464 PnpRootPowerControl(
1465     IN PDEVICE_OBJECT DeviceObject,
1466     IN PIRP Irp)
1467 {
1468     PPNPROOT_FDO_DEVICE_EXTENSION DeviceExtension;
1469     PIO_STACK_LOCATION IrpSp;
1470     NTSTATUS Status;
1471 
1472     DeviceExtension = DeviceObject->DeviceExtension;
1473     Status = Irp->IoStatus.Status;
1474     IrpSp = IoGetCurrentIrpStackLocation(Irp);
1475 
1476     if (DeviceExtension->Common.IsFDO)
1477     {
1478         ASSERT(!DeviceExtension->Common.IsFDO);
1479         PoStartNextPowerIrp(Irp);
1480         IoCopyCurrentIrpStackLocationToNext(Irp);
1481         Status = PoCallDriver(DeviceExtension->Ldo, Irp);
1482     }
1483     else
1484     {
1485         switch (IrpSp->MinorFunction)
1486         {
1487             case IRP_MN_QUERY_POWER:
1488             case IRP_MN_SET_POWER:
1489                 Status = STATUS_SUCCESS;
1490                 break;
1491         }
1492         Irp->IoStatus.Status = Status;
1493         PoStartNextPowerIrp(Irp);
1494         IoCompleteRequest(Irp, IO_NO_INCREMENT);
1495     }
1496 
1497     return Status;
1498 }
1499 
1500 NTSTATUS
1501 NTAPI
1502 PnpRootAddDevice(
1503     IN PDRIVER_OBJECT DriverObject,
1504     IN PDEVICE_OBJECT PhysicalDeviceObject)
1505 {
1506     PPNPROOT_FDO_DEVICE_EXTENSION DeviceExtension;
1507     NTSTATUS Status;
1508 
1509     DPRINT("PnpRootAddDevice(DriverObject %p, Pdo %p)\n", DriverObject, PhysicalDeviceObject);
1510 
1511     if (!PhysicalDeviceObject)
1512     {
1513         DPRINT("PhysicalDeviceObject 0x%p\n", PhysicalDeviceObject);
1514         Status = STATUS_INSUFFICIENT_RESOURCES;
1515         KeBugCheckEx(PHASE1_INITIALIZATION_FAILED, Status, 0, 0, 0);
1516     }
1517 
1518     Status = IoCreateDevice(
1519         DriverObject,
1520         sizeof(PNPROOT_FDO_DEVICE_EXTENSION),
1521         NULL,
1522         FILE_DEVICE_BUS_EXTENDER,
1523         FILE_DEVICE_SECURE_OPEN,
1524         TRUE,
1525         &PnpRootDeviceObject);
1526     if (!NT_SUCCESS(Status))
1527     {
1528         DPRINT("IoCreateDevice() failed with status 0x%08lx\n", Status);
1529         KeBugCheckEx(PHASE1_INITIALIZATION_FAILED, Status, 0, 0, 0);
1530     }
1531     DPRINT("Created FDO %p\n", PnpRootDeviceObject);
1532 
1533     DeviceExtension = (PPNPROOT_FDO_DEVICE_EXTENSION)PnpRootDeviceObject->DeviceExtension;
1534     RtlZeroMemory(DeviceExtension, sizeof(PNPROOT_FDO_DEVICE_EXTENSION));
1535 
1536     DeviceExtension->Common.IsFDO = TRUE;
1537     DeviceExtension->State = dsStopped;
1538     InitializeListHead(&DeviceExtension->DeviceListHead);
1539     DeviceExtension->DeviceListCount = 0;
1540     KeInitializeGuardedMutex(&DeviceExtension->DeviceListLock);
1541 
1542     Status = IoAttachDeviceToDeviceStackSafe(
1543         PnpRootDeviceObject,
1544         PhysicalDeviceObject,
1545         &DeviceExtension->Ldo);
1546     if (!NT_SUCCESS(Status))
1547     {
1548         DPRINT("IoAttachDeviceToDeviceStackSafe() failed with status 0x%08lx\n", Status);
1549         KeBugCheckEx(PHASE1_INITIALIZATION_FAILED, Status, 0, 0, 0);
1550     }
1551 
1552     PnpRootDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
1553 
1554     DPRINT("Done AddDevice()\n");
1555 
1556     return STATUS_SUCCESS;
1557 }
1558 
1559 #if MI_TRACE_PFNS
1560 PDEVICE_OBJECT IopPfnDumpDeviceObject;
1561 
1562 NTSTATUS NTAPI
1563 PnpRootCreateClose(
1564     _In_ PDEVICE_OBJECT DeviceObject,
1565     _In_ PIRP Irp)
1566 {
1567     PIO_STACK_LOCATION IoStack;
1568 
1569     if (DeviceObject != IopPfnDumpDeviceObject)
1570     {
1571         Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
1572         IoCompleteRequest(Irp, IO_NO_INCREMENT);
1573         return STATUS_INVALID_DEVICE_REQUEST;
1574     }
1575 
1576     IoStack = IoGetCurrentIrpStackLocation(Irp);
1577     if (IoStack->MajorFunction == IRP_MJ_CREATE)
1578     {
1579         MmDumpArmPfnDatabase(TRUE);
1580     }
1581 
1582     Irp->IoStatus.Status = STATUS_SUCCESS;
1583     IoCompleteRequest(Irp, IO_NO_INCREMENT);
1584     return STATUS_SUCCESS;
1585 }
1586 #endif
1587 
1588 NTSTATUS NTAPI
1589 PnpRootDriverEntry(
1590     IN PDRIVER_OBJECT DriverObject,
1591     IN PUNICODE_STRING RegistryPath)
1592 {
1593 #if MI_TRACE_PFNS
1594     NTSTATUS Status;
1595     UNICODE_STRING PfnDumpDeviceName = RTL_CONSTANT_STRING(L"\\Device\\PfnDump");
1596 #endif
1597 
1598     DPRINT("PnpRootDriverEntry(%p %wZ)\n", DriverObject, RegistryPath);
1599 
1600     IopRootDriverObject = DriverObject;
1601 
1602     DriverObject->DriverExtension->AddDevice = PnpRootAddDevice;
1603 
1604 #if MI_TRACE_PFNS
1605     DriverObject->MajorFunction[IRP_MJ_CREATE] = PnpRootCreateClose;
1606     DriverObject->MajorFunction[IRP_MJ_CLOSE] = PnpRootCreateClose;
1607 #endif
1608     DriverObject->MajorFunction[IRP_MJ_PNP] = PnpRootPnpControl;
1609     DriverObject->MajorFunction[IRP_MJ_POWER] = PnpRootPowerControl;
1610 
1611 #if MI_TRACE_PFNS
1612     Status = IoCreateDevice(DriverObject,
1613                             0,
1614                             &PfnDumpDeviceName,
1615                             FILE_DEVICE_UNKNOWN,
1616                             0,
1617                             FALSE,
1618                             &IopPfnDumpDeviceObject);
1619     if (!NT_SUCCESS(Status))
1620     {
1621         DPRINT1("Creating PFN Dump device failed with %lx\n", Status);
1622     }
1623     else
1624     {
1625         IopPfnDumpDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
1626     }
1627 #endif
1628 
1629     return STATUS_SUCCESS;
1630 }
1631