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