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