xref: /reactos/ntoskrnl/io/pnpmgr/pnpmgr.c (revision 7eead935)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * COPYRIGHT:       GPL - See COPYING in the top level directory
4  * FILE:            ntoskrnl/io/pnpmgr/pnpmgr.c
5  * PURPOSE:         Initializes the PnP manager
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 PDEVICE_NODE IopRootDeviceNode;
19 KSPIN_LOCK IopDeviceTreeLock;
20 ERESOURCE PpRegistryDeviceResource;
21 KGUARDED_MUTEX PpDeviceReferenceTableLock;
22 RTL_AVL_TABLE PpDeviceReferenceTable;
23 
24 extern ERESOURCE IopDriverLoadResource;
25 extern ULONG ExpInitializationPhase;
26 extern BOOLEAN PnpSystemInit;
27 
28 #define MAX_DEVICE_ID_LEN          200
29 #define MAX_SEPARATORS_INSTANCEID  0
30 #define MAX_SEPARATORS_DEVICEID    1
31 
32 /* DATA **********************************************************************/
33 
34 PDRIVER_OBJECT IopRootDriverObject;
35 PIO_BUS_TYPE_GUID_LIST PnpBusTypeGuidList = NULL;
36 LIST_ENTRY IopDeviceActionRequestList;
37 WORK_QUEUE_ITEM IopDeviceActionWorkItem;
38 BOOLEAN IopDeviceActionInProgress;
39 KSPIN_LOCK IopDeviceActionLock;
40 
41 typedef struct _DEVICE_ACTION_DATA
42 {
43     LIST_ENTRY RequestListEntry;
44     PDEVICE_OBJECT DeviceObject;
45     DEVICE_RELATION_TYPE Type;
46 } DEVICE_ACTION_DATA, *PDEVICE_ACTION_DATA;
47 
48 /* FUNCTIONS *****************************************************************/
49 NTSTATUS
50 NTAPI
51 IopCreateDeviceKeyPath(IN PCUNICODE_STRING RegistryPath,
52                        IN ULONG CreateOptions,
53                        OUT PHANDLE Handle);
54 
55 VOID
56 IopCancelPrepareDeviceForRemoval(PDEVICE_OBJECT DeviceObject);
57 
58 NTSTATUS
59 IopPrepareDeviceForRemoval(PDEVICE_OBJECT DeviceObject, BOOLEAN Force);
60 
61 PDEVICE_OBJECT
62 IopGetDeviceObjectFromDeviceInstance(PUNICODE_STRING DeviceInstance);
63 
64 PDEVICE_NODE
65 FASTCALL
66 IopGetDeviceNode(PDEVICE_OBJECT DeviceObject)
67 {
68    return ((PEXTENDED_DEVOBJ_EXTENSION)DeviceObject->DeviceObjectExtension)->DeviceNode;
69 }
70 
71 VOID
72 IopFixupDeviceId(PWCHAR String)
73 {
74     SIZE_T Length = wcslen(String), i;
75 
76     for (i = 0; i < Length; i++)
77     {
78         if (String[i] == L'\\')
79             String[i] = L'#';
80     }
81 }
82 
83 VOID
84 NTAPI
85 IopInstallCriticalDevice(PDEVICE_NODE DeviceNode)
86 {
87     NTSTATUS Status;
88     HANDLE CriticalDeviceKey, InstanceKey;
89     OBJECT_ATTRIBUTES ObjectAttributes;
90     UNICODE_STRING CriticalDeviceKeyU = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\CriticalDeviceDatabase");
91     UNICODE_STRING CompatibleIdU = RTL_CONSTANT_STRING(L"CompatibleIDs");
92     UNICODE_STRING HardwareIdU = RTL_CONSTANT_STRING(L"HardwareID");
93     UNICODE_STRING ServiceU = RTL_CONSTANT_STRING(L"Service");
94     UNICODE_STRING ClassGuidU = RTL_CONSTANT_STRING(L"ClassGUID");
95     PKEY_VALUE_PARTIAL_INFORMATION PartialInfo;
96     ULONG HidLength = 0, CidLength = 0, BufferLength;
97     PWCHAR IdBuffer, OriginalIdBuffer;
98 
99     /* Open the device instance key */
100     Status = IopCreateDeviceKeyPath(&DeviceNode->InstancePath, REG_OPTION_NON_VOLATILE, &InstanceKey);
101     if (Status != STATUS_SUCCESS)
102         return;
103 
104     Status = ZwQueryValueKey(InstanceKey,
105                              &HardwareIdU,
106                              KeyValuePartialInformation,
107                              NULL,
108                              0,
109                              &HidLength);
110     if (Status != STATUS_BUFFER_OVERFLOW && Status != STATUS_BUFFER_TOO_SMALL)
111     {
112         ZwClose(InstanceKey);
113         return;
114     }
115 
116     Status = ZwQueryValueKey(InstanceKey,
117                              &CompatibleIdU,
118                              KeyValuePartialInformation,
119                              NULL,
120                              0,
121                              &CidLength);
122     if (Status != STATUS_BUFFER_OVERFLOW && Status != STATUS_BUFFER_TOO_SMALL)
123     {
124         CidLength = 0;
125     }
126 
127     BufferLength = HidLength + CidLength;
128     BufferLength -= (((CidLength != 0) ? 2 : 1) * FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data));
129 
130     /* Allocate a buffer to hold data from both */
131     OriginalIdBuffer = IdBuffer = ExAllocatePool(PagedPool, BufferLength);
132     if (!IdBuffer)
133     {
134         ZwClose(InstanceKey);
135         return;
136     }
137 
138     /* Compute the buffer size */
139     if (HidLength > CidLength)
140         BufferLength = HidLength;
141     else
142         BufferLength = CidLength;
143 
144     PartialInfo = ExAllocatePool(PagedPool, BufferLength);
145     if (!PartialInfo)
146     {
147         ZwClose(InstanceKey);
148         ExFreePool(OriginalIdBuffer);
149         return;
150     }
151 
152     Status = ZwQueryValueKey(InstanceKey,
153                              &HardwareIdU,
154                              KeyValuePartialInformation,
155                              PartialInfo,
156                              HidLength,
157                              &HidLength);
158     if (Status != STATUS_SUCCESS)
159     {
160         ExFreePool(PartialInfo);
161         ExFreePool(OriginalIdBuffer);
162         ZwClose(InstanceKey);
163         return;
164     }
165 
166     /* Copy in HID info first (without 2nd terminating NULL if CID is present) */
167     HidLength = PartialInfo->DataLength - ((CidLength != 0) ? sizeof(WCHAR) : 0);
168     RtlCopyMemory(IdBuffer, PartialInfo->Data, HidLength);
169 
170     if (CidLength != 0)
171     {
172         Status = ZwQueryValueKey(InstanceKey,
173                                  &CompatibleIdU,
174                                  KeyValuePartialInformation,
175                                  PartialInfo,
176                                  CidLength,
177                                  &CidLength);
178         if (Status != STATUS_SUCCESS)
179         {
180             ExFreePool(PartialInfo);
181             ExFreePool(OriginalIdBuffer);
182             ZwClose(InstanceKey);
183             return;
184         }
185 
186         /* Copy CID next */
187         CidLength = PartialInfo->DataLength;
188         RtlCopyMemory(((PUCHAR)IdBuffer) + HidLength, PartialInfo->Data, CidLength);
189     }
190 
191     /* Free our temp buffer */
192     ExFreePool(PartialInfo);
193 
194     InitializeObjectAttributes(&ObjectAttributes,
195                                &CriticalDeviceKeyU,
196                                OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
197                                NULL,
198                                NULL);
199     Status = ZwOpenKey(&CriticalDeviceKey,
200                        KEY_ENUMERATE_SUB_KEYS,
201                        &ObjectAttributes);
202     if (!NT_SUCCESS(Status))
203     {
204         /* The critical device database doesn't exist because
205          * we're probably in 1st stage setup, but it's ok */
206         ExFreePool(OriginalIdBuffer);
207         ZwClose(InstanceKey);
208         return;
209     }
210 
211     while (*IdBuffer)
212     {
213         USHORT StringLength = (USHORT)wcslen(IdBuffer) + 1, Index;
214 
215         IopFixupDeviceId(IdBuffer);
216 
217         /* Look through all subkeys for a match */
218         for (Index = 0; TRUE; Index++)
219         {
220             ULONG NeededLength;
221             PKEY_BASIC_INFORMATION BasicInfo;
222 
223             Status = ZwEnumerateKey(CriticalDeviceKey,
224                                     Index,
225                                     KeyBasicInformation,
226                                     NULL,
227                                     0,
228                                     &NeededLength);
229             if (Status == STATUS_NO_MORE_ENTRIES)
230                 break;
231             else if (Status == STATUS_BUFFER_OVERFLOW || Status == STATUS_BUFFER_TOO_SMALL)
232             {
233                 UNICODE_STRING ChildIdNameU, RegKeyNameU;
234 
235                 BasicInfo = ExAllocatePool(PagedPool, NeededLength);
236                 if (!BasicInfo)
237                 {
238                     /* No memory */
239                     ExFreePool(OriginalIdBuffer);
240                     ZwClose(CriticalDeviceKey);
241                     ZwClose(InstanceKey);
242                     return;
243                 }
244 
245                 Status = ZwEnumerateKey(CriticalDeviceKey,
246                                         Index,
247                                         KeyBasicInformation,
248                                         BasicInfo,
249                                         NeededLength,
250                                         &NeededLength);
251                 if (Status != STATUS_SUCCESS)
252                 {
253                     /* This shouldn't happen */
254                     ExFreePool(BasicInfo);
255                     continue;
256                 }
257 
258                 ChildIdNameU.Buffer = IdBuffer;
259                 ChildIdNameU.MaximumLength = ChildIdNameU.Length = (StringLength - 1) * sizeof(WCHAR);
260                 RegKeyNameU.Buffer = BasicInfo->Name;
261                 RegKeyNameU.MaximumLength = RegKeyNameU.Length = (USHORT)BasicInfo->NameLength;
262 
263                 if (RtlEqualUnicodeString(&ChildIdNameU, &RegKeyNameU, TRUE))
264                 {
265                     HANDLE ChildKeyHandle;
266 
267                     InitializeObjectAttributes(&ObjectAttributes,
268                                                &ChildIdNameU,
269                                                OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
270                                                CriticalDeviceKey,
271                                                NULL);
272 
273                     Status = ZwOpenKey(&ChildKeyHandle,
274                                        KEY_QUERY_VALUE,
275                                        &ObjectAttributes);
276                     if (Status != STATUS_SUCCESS)
277                     {
278                         ExFreePool(BasicInfo);
279                         continue;
280                     }
281 
282                     /* Check if there's already a driver installed */
283                     Status = ZwQueryValueKey(InstanceKey,
284                                              &ClassGuidU,
285                                              KeyValuePartialInformation,
286                                              NULL,
287                                              0,
288                                              &NeededLength);
289                     if (Status == STATUS_BUFFER_OVERFLOW || Status == STATUS_BUFFER_TOO_SMALL)
290                     {
291                         ExFreePool(BasicInfo);
292                         continue;
293                     }
294 
295                     Status = ZwQueryValueKey(ChildKeyHandle,
296                                              &ClassGuidU,
297                                              KeyValuePartialInformation,
298                                              NULL,
299                                              0,
300                                              &NeededLength);
301                     if (Status != STATUS_BUFFER_OVERFLOW && Status != STATUS_BUFFER_TOO_SMALL)
302                     {
303                         ExFreePool(BasicInfo);
304                         continue;
305                     }
306 
307                     PartialInfo = ExAllocatePool(PagedPool, NeededLength);
308                     if (!PartialInfo)
309                     {
310                         ExFreePool(OriginalIdBuffer);
311                         ExFreePool(BasicInfo);
312                         ZwClose(InstanceKey);
313                         ZwClose(ChildKeyHandle);
314                         ZwClose(CriticalDeviceKey);
315                         return;
316                     }
317 
318                     /* Read ClassGUID entry in the CDDB */
319                     Status = ZwQueryValueKey(ChildKeyHandle,
320                                              &ClassGuidU,
321                                              KeyValuePartialInformation,
322                                              PartialInfo,
323                                              NeededLength,
324                                              &NeededLength);
325                     if (Status != STATUS_SUCCESS)
326                     {
327                         ExFreePool(BasicInfo);
328                         continue;
329                     }
330 
331                     /* Write it to the ENUM key */
332                     Status = ZwSetValueKey(InstanceKey,
333                                            &ClassGuidU,
334                                            0,
335                                            REG_SZ,
336                                            PartialInfo->Data,
337                                            PartialInfo->DataLength);
338                     if (Status != STATUS_SUCCESS)
339                     {
340                         ExFreePool(BasicInfo);
341                         ExFreePool(PartialInfo);
342                         ZwClose(ChildKeyHandle);
343                         continue;
344                     }
345 
346                     Status = ZwQueryValueKey(ChildKeyHandle,
347                                              &ServiceU,
348                                              KeyValuePartialInformation,
349                                              NULL,
350                                              0,
351                                              &NeededLength);
352                     if (Status == STATUS_BUFFER_OVERFLOW || Status == STATUS_BUFFER_TOO_SMALL)
353                     {
354                         ExFreePool(PartialInfo);
355                         PartialInfo = ExAllocatePool(PagedPool, NeededLength);
356                         if (!PartialInfo)
357                         {
358                             ExFreePool(OriginalIdBuffer);
359                             ExFreePool(BasicInfo);
360                             ZwClose(InstanceKey);
361                             ZwClose(ChildKeyHandle);
362                             ZwClose(CriticalDeviceKey);
363                             return;
364                         }
365 
366                         /* Read the service entry from the CDDB */
367                         Status = ZwQueryValueKey(ChildKeyHandle,
368                                                  &ServiceU,
369                                                  KeyValuePartialInformation,
370                                                  PartialInfo,
371                                                  NeededLength,
372                                                  &NeededLength);
373                         if (Status != STATUS_SUCCESS)
374                         {
375                             ExFreePool(BasicInfo);
376                             ExFreePool(PartialInfo);
377                             ZwClose(ChildKeyHandle);
378                             continue;
379                         }
380 
381                         /* Write it to the ENUM key */
382                         Status = ZwSetValueKey(InstanceKey,
383                                                &ServiceU,
384                                                0,
385                                                REG_SZ,
386                                                PartialInfo->Data,
387                                                PartialInfo->DataLength);
388                         if (Status != STATUS_SUCCESS)
389                         {
390                             ExFreePool(BasicInfo);
391                             ExFreePool(PartialInfo);
392                             ZwClose(ChildKeyHandle);
393                             continue;
394                         }
395 
396                         DPRINT("Installed service '%S' for critical device '%wZ'\n", PartialInfo->Data, &ChildIdNameU);
397                     }
398                     else
399                     {
400                         DPRINT1("Installed NULL service for critical device '%wZ'\n", &ChildIdNameU);
401                     }
402 
403                     ExFreePool(OriginalIdBuffer);
404                     ExFreePool(PartialInfo);
405                     ExFreePool(BasicInfo);
406                     ZwClose(InstanceKey);
407                     ZwClose(ChildKeyHandle);
408                     ZwClose(CriticalDeviceKey);
409 
410                     /* That's it */
411                     return;
412                 }
413 
414                 ExFreePool(BasicInfo);
415             }
416             else
417             {
418                 /* Umm, not sure what happened here */
419                 continue;
420             }
421         }
422 
423         /* Advance to the next ID */
424         IdBuffer += StringLength;
425     }
426 
427     ExFreePool(OriginalIdBuffer);
428     ZwClose(InstanceKey);
429     ZwClose(CriticalDeviceKey);
430 }
431 
432 NTSTATUS
433 FASTCALL
434 IopInitializeDevice(PDEVICE_NODE DeviceNode,
435                     PDRIVER_OBJECT DriverObject)
436 {
437     PDEVICE_OBJECT Fdo;
438     NTSTATUS Status;
439 
440     if (!DriverObject)
441     {
442         /* Special case for bus driven devices */
443         DeviceNode->Flags |= DNF_ADDED;
444         return STATUS_SUCCESS;
445     }
446 
447     if (!DriverObject->DriverExtension->AddDevice)
448     {
449         DeviceNode->Flags |= DNF_LEGACY_DRIVER;
450     }
451 
452     if (DeviceNode->Flags & DNF_LEGACY_DRIVER)
453     {
454         DeviceNode->Flags |= (DNF_ADDED | DNF_STARTED);
455         return STATUS_SUCCESS;
456     }
457 
458     /* This is a Plug and Play driver */
459     DPRINT("Plug and Play driver found\n");
460     ASSERT(DeviceNode->PhysicalDeviceObject);
461 
462     DPRINT("Calling %wZ->AddDevice(%wZ)\n",
463            &DriverObject->DriverName,
464            &DeviceNode->InstancePath);
465     Status = DriverObject->DriverExtension->AddDevice(DriverObject,
466                                                       DeviceNode->PhysicalDeviceObject);
467     if (!NT_SUCCESS(Status))
468     {
469         DPRINT1("%wZ->AddDevice(%wZ) failed with status 0x%x\n",
470                 &DriverObject->DriverName,
471                 &DeviceNode->InstancePath,
472                 Status);
473         IopDeviceNodeSetFlag(DeviceNode, DNF_DISABLED);
474         DeviceNode->Problem = CM_PROB_FAILED_ADD;
475         return Status;
476     }
477 
478     Fdo = IoGetAttachedDeviceReference(DeviceNode->PhysicalDeviceObject);
479 
480     /* Check if we have a ACPI device (needed for power management) */
481     if (Fdo->DeviceType == FILE_DEVICE_ACPI)
482     {
483         static BOOLEAN SystemPowerDeviceNodeCreated = FALSE;
484 
485         /* There can be only one system power device */
486         if (!SystemPowerDeviceNodeCreated)
487         {
488             PopSystemPowerDeviceNode = DeviceNode;
489             ObReferenceObject(PopSystemPowerDeviceNode->PhysicalDeviceObject);
490             SystemPowerDeviceNodeCreated = TRUE;
491         }
492     }
493 
494     ObDereferenceObject(Fdo);
495 
496     IopDeviceNodeSetFlag(DeviceNode, DNF_ADDED);
497 
498     return STATUS_SUCCESS;
499 }
500 
501 static
502 NTSTATUS
503 NTAPI
504 IopSendEject(IN PDEVICE_OBJECT DeviceObject)
505 {
506     IO_STACK_LOCATION Stack;
507     PVOID Dummy;
508 
509     RtlZeroMemory(&Stack, sizeof(IO_STACK_LOCATION));
510     Stack.MajorFunction = IRP_MJ_PNP;
511     Stack.MinorFunction = IRP_MN_EJECT;
512 
513     return IopSynchronousCall(DeviceObject, &Stack, &Dummy);
514 }
515 
516 static
517 VOID
518 NTAPI
519 IopSendSurpriseRemoval(IN PDEVICE_OBJECT DeviceObject)
520 {
521     IO_STACK_LOCATION Stack;
522     PVOID Dummy;
523 
524     RtlZeroMemory(&Stack, sizeof(IO_STACK_LOCATION));
525     Stack.MajorFunction = IRP_MJ_PNP;
526     Stack.MinorFunction = IRP_MN_SURPRISE_REMOVAL;
527 
528     /* Drivers should never fail a IRP_MN_SURPRISE_REMOVAL request */
529     IopSynchronousCall(DeviceObject, &Stack, &Dummy);
530 }
531 
532 static
533 NTSTATUS
534 NTAPI
535 IopQueryRemoveDevice(IN PDEVICE_OBJECT DeviceObject)
536 {
537     PDEVICE_NODE DeviceNode = IopGetDeviceNode(DeviceObject);
538     IO_STACK_LOCATION Stack;
539     PVOID Dummy;
540     NTSTATUS Status;
541 
542     ASSERT(DeviceNode);
543 
544     IopQueueTargetDeviceEvent(&GUID_DEVICE_REMOVE_PENDING,
545                               &DeviceNode->InstancePath);
546 
547     RtlZeroMemory(&Stack, sizeof(IO_STACK_LOCATION));
548     Stack.MajorFunction = IRP_MJ_PNP;
549     Stack.MinorFunction = IRP_MN_QUERY_REMOVE_DEVICE;
550 
551     Status = IopSynchronousCall(DeviceObject, &Stack, &Dummy);
552 
553     IopNotifyPlugPlayNotification(DeviceObject,
554                                   EventCategoryTargetDeviceChange,
555                                   &GUID_TARGET_DEVICE_QUERY_REMOVE,
556                                   NULL,
557                                   NULL);
558 
559     if (!NT_SUCCESS(Status))
560     {
561         DPRINT1("Removal vetoed by %wZ\n", &DeviceNode->InstancePath);
562         IopQueueTargetDeviceEvent(&GUID_DEVICE_REMOVAL_VETOED,
563                                   &DeviceNode->InstancePath);
564     }
565 
566     return Status;
567 }
568 
569 static
570 NTSTATUS
571 NTAPI
572 IopQueryStopDevice(IN PDEVICE_OBJECT DeviceObject)
573 {
574     IO_STACK_LOCATION Stack;
575     PVOID Dummy;
576 
577     RtlZeroMemory(&Stack, sizeof(IO_STACK_LOCATION));
578     Stack.MajorFunction = IRP_MJ_PNP;
579     Stack.MinorFunction = IRP_MN_QUERY_STOP_DEVICE;
580 
581     return IopSynchronousCall(DeviceObject, &Stack, &Dummy);
582 }
583 
584 static
585 VOID
586 NTAPI
587 IopSendRemoveDevice(IN PDEVICE_OBJECT DeviceObject)
588 {
589     IO_STACK_LOCATION Stack;
590     PVOID Dummy;
591     PDEVICE_NODE DeviceNode = IopGetDeviceNode(DeviceObject);
592 
593     /* Drop all our state for this device in case it isn't really going away */
594     DeviceNode->Flags &= DNF_ENUMERATED | DNF_PROCESSED;
595 
596     RtlZeroMemory(&Stack, sizeof(IO_STACK_LOCATION));
597     Stack.MajorFunction = IRP_MJ_PNP;
598     Stack.MinorFunction = IRP_MN_REMOVE_DEVICE;
599 
600     /* Drivers should never fail a IRP_MN_REMOVE_DEVICE request */
601     IopSynchronousCall(DeviceObject, &Stack, &Dummy);
602 
603     IopNotifyPlugPlayNotification(DeviceObject,
604                                   EventCategoryTargetDeviceChange,
605                                   &GUID_TARGET_DEVICE_REMOVE_COMPLETE,
606                                   NULL,
607                                   NULL);
608     ObDereferenceObject(DeviceObject);
609 }
610 
611 static
612 VOID
613 NTAPI
614 IopCancelRemoveDevice(IN PDEVICE_OBJECT DeviceObject)
615 {
616     IO_STACK_LOCATION Stack;
617     PVOID Dummy;
618 
619     RtlZeroMemory(&Stack, sizeof(IO_STACK_LOCATION));
620     Stack.MajorFunction = IRP_MJ_PNP;
621     Stack.MinorFunction = IRP_MN_CANCEL_REMOVE_DEVICE;
622 
623     /* Drivers should never fail a IRP_MN_CANCEL_REMOVE_DEVICE request */
624     IopSynchronousCall(DeviceObject, &Stack, &Dummy);
625 
626     IopNotifyPlugPlayNotification(DeviceObject,
627                                   EventCategoryTargetDeviceChange,
628                                   &GUID_TARGET_DEVICE_REMOVE_CANCELLED,
629                                   NULL,
630                                   NULL);
631 }
632 
633 static
634 VOID
635 NTAPI
636 IopSendStopDevice(IN PDEVICE_OBJECT DeviceObject)
637 {
638     IO_STACK_LOCATION Stack;
639     PVOID Dummy;
640 
641     RtlZeroMemory(&Stack, sizeof(IO_STACK_LOCATION));
642     Stack.MajorFunction = IRP_MJ_PNP;
643     Stack.MinorFunction = IRP_MN_STOP_DEVICE;
644 
645     /* Drivers should never fail a IRP_MN_STOP_DEVICE request */
646     IopSynchronousCall(DeviceObject, &Stack, &Dummy);
647 }
648 
649 static
650 NTSTATUS
651 IopSetServiceEnumData(PDEVICE_NODE DeviceNode)
652 {
653     UNICODE_STRING ServicesKeyPath = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\");
654     UNICODE_STRING ServiceKeyName;
655     UNICODE_STRING EnumKeyName;
656     UNICODE_STRING ValueName;
657     PKEY_VALUE_FULL_INFORMATION KeyValueInformation;
658     HANDLE ServiceKey = NULL, ServiceEnumKey = NULL;
659     ULONG Disposition;
660     ULONG Count = 0, NextInstance = 0;
661     WCHAR ValueBuffer[6];
662     NTSTATUS Status = STATUS_SUCCESS;
663 
664     DPRINT("IopSetServiceEnumData(%p)\n", DeviceNode);
665     DPRINT("Instance: %wZ\n", &DeviceNode->InstancePath);
666     DPRINT("Service: %wZ\n", &DeviceNode->ServiceName);
667 
668     if (DeviceNode->ServiceName.Buffer == NULL)
669     {
670         DPRINT1("No service!\n");
671         return STATUS_SUCCESS;
672     }
673 
674     ServiceKeyName.MaximumLength = ServicesKeyPath.Length + DeviceNode->ServiceName.Length + sizeof(UNICODE_NULL);
675     ServiceKeyName.Length = 0;
676     ServiceKeyName.Buffer = ExAllocatePool(PagedPool, ServiceKeyName.MaximumLength);
677     if (ServiceKeyName.Buffer == NULL)
678     {
679         DPRINT1("No ServiceKeyName.Buffer!\n");
680         return STATUS_INSUFFICIENT_RESOURCES;
681     }
682 
683     RtlAppendUnicodeStringToString(&ServiceKeyName, &ServicesKeyPath);
684     RtlAppendUnicodeStringToString(&ServiceKeyName, &DeviceNode->ServiceName);
685 
686     DPRINT("ServiceKeyName: %wZ\n", &ServiceKeyName);
687 
688     Status = IopOpenRegistryKeyEx(&ServiceKey, NULL, &ServiceKeyName, KEY_CREATE_SUB_KEY);
689     if (!NT_SUCCESS(Status))
690     {
691         goto done;
692     }
693 
694     RtlInitUnicodeString(&EnumKeyName, L"Enum");
695     Status = IopCreateRegistryKeyEx(&ServiceEnumKey,
696                                     ServiceKey,
697                                     &EnumKeyName,
698                                     KEY_SET_VALUE,
699                                     REG_OPTION_VOLATILE,
700                                     &Disposition);
701     if (NT_SUCCESS(Status))
702     {
703         if (Disposition == REG_OPENED_EXISTING_KEY)
704         {
705             /* Read the NextInstance value */
706             Status = IopGetRegistryValue(ServiceEnumKey,
707                                          L"Count",
708                                          &KeyValueInformation);
709             if (!NT_SUCCESS(Status))
710                 goto done;
711 
712             if ((KeyValueInformation->Type == REG_DWORD) &&
713                 (KeyValueInformation->DataLength))
714             {
715                 /* Read it */
716                 Count = *(PULONG)((ULONG_PTR)KeyValueInformation +
717                                   KeyValueInformation->DataOffset);
718             }
719 
720             ExFreePool(KeyValueInformation);
721             KeyValueInformation = NULL;
722 
723             /* Read the NextInstance value */
724             Status = IopGetRegistryValue(ServiceEnumKey,
725                                          L"NextInstance",
726                                          &KeyValueInformation);
727             if (!NT_SUCCESS(Status))
728                 goto done;
729 
730             if ((KeyValueInformation->Type == REG_DWORD) &&
731                 (KeyValueInformation->DataLength))
732             {
733                 NextInstance = *(PULONG)((ULONG_PTR)KeyValueInformation +
734                                          KeyValueInformation->DataOffset);
735             }
736 
737             ExFreePool(KeyValueInformation);
738             KeyValueInformation = NULL;
739         }
740 
741         /* Set the instance path */
742         swprintf(ValueBuffer, L"%lu", NextInstance);
743         RtlInitUnicodeString(&ValueName, ValueBuffer);
744         Status = ZwSetValueKey(ServiceEnumKey,
745                                &ValueName,
746                                0,
747                                REG_SZ,
748                                DeviceNode->InstancePath.Buffer,
749                                DeviceNode->InstancePath.MaximumLength);
750         if (!NT_SUCCESS(Status))
751             goto done;
752 
753         /* Increment Count and NextInstance */
754         Count++;
755         NextInstance++;
756 
757         /* Set the new Count value */
758         RtlInitUnicodeString(&ValueName, L"Count");
759         Status = ZwSetValueKey(ServiceEnumKey,
760                                &ValueName,
761                                0,
762                                REG_DWORD,
763                                &Count,
764                                sizeof(Count));
765         if (!NT_SUCCESS(Status))
766             goto done;
767 
768         /* Set the new NextInstance value */
769         RtlInitUnicodeString(&ValueName, L"NextInstance");
770         Status = ZwSetValueKey(ServiceEnumKey,
771                                &ValueName,
772                                0,
773                                REG_DWORD,
774                                &NextInstance,
775                                sizeof(NextInstance));
776     }
777 
778 done:
779     if (ServiceEnumKey != NULL)
780         ZwClose(ServiceEnumKey);
781 
782     if (ServiceKey != NULL)
783         ZwClose(ServiceKey);
784 
785     ExFreePool(ServiceKeyName.Buffer);
786 
787     return Status;
788 }
789 
790 VOID
791 NTAPI
792 IopStartDevice2(IN PDEVICE_OBJECT DeviceObject)
793 {
794     IO_STACK_LOCATION Stack;
795     PDEVICE_NODE DeviceNode;
796     NTSTATUS Status;
797     PVOID Dummy;
798     DEVICE_CAPABILITIES DeviceCapabilities;
799 
800     /* Get the device node */
801     DeviceNode = IopGetDeviceNode(DeviceObject);
802 
803     ASSERT(!(DeviceNode->Flags & DNF_DISABLED));
804 
805     /* Build the I/O stack location */
806     RtlZeroMemory(&Stack, sizeof(IO_STACK_LOCATION));
807     Stack.MajorFunction = IRP_MJ_PNP;
808     Stack.MinorFunction = IRP_MN_START_DEVICE;
809 
810     Stack.Parameters.StartDevice.AllocatedResources =
811          DeviceNode->ResourceList;
812     Stack.Parameters.StartDevice.AllocatedResourcesTranslated =
813          DeviceNode->ResourceListTranslated;
814 
815     /* Do the call */
816     Status = IopSynchronousCall(DeviceObject, &Stack, &Dummy);
817     if (!NT_SUCCESS(Status))
818     {
819         /* Send an IRP_MN_REMOVE_DEVICE request */
820         IopRemoveDevice(DeviceNode);
821 
822         /* Set the appropriate flag */
823         DeviceNode->Flags |= DNF_START_FAILED;
824         DeviceNode->Problem = CM_PROB_FAILED_START;
825 
826         DPRINT1("Warning: PnP Start failed (%wZ) [Status: 0x%x]\n", &DeviceNode->InstancePath, Status);
827         return;
828     }
829 
830     DPRINT("Sending IRP_MN_QUERY_CAPABILITIES to device stack (after start)\n");
831 
832     Status = IopQueryDeviceCapabilities(DeviceNode, &DeviceCapabilities);
833     if (!NT_SUCCESS(Status))
834     {
835         DPRINT("IopInitiatePnpIrp() failed (Status 0x%08lx)\n", Status);
836     }
837 
838     /* Invalidate device state so IRP_MN_QUERY_PNP_DEVICE_STATE is sent */
839     IoInvalidateDeviceState(DeviceObject);
840 
841     /* Otherwise, mark us as started */
842     DeviceNode->Flags |= DNF_STARTED;
843     DeviceNode->Flags &= ~DNF_STOPPED;
844 
845     /* We now need enumeration */
846     DeviceNode->Flags |= DNF_NEED_ENUMERATION_ONLY;
847 }
848 
849 NTSTATUS
850 NTAPI
851 IopStartAndEnumerateDevice(IN PDEVICE_NODE DeviceNode)
852 {
853     PDEVICE_OBJECT DeviceObject;
854     NTSTATUS Status;
855     PAGED_CODE();
856 
857     /* Sanity check */
858     ASSERT((DeviceNode->Flags & DNF_ADDED));
859     ASSERT((DeviceNode->Flags & (DNF_RESOURCE_ASSIGNED |
860                                  DNF_RESOURCE_REPORTED |
861                                  DNF_NO_RESOURCE_REQUIRED)));
862 
863     /* Get the device object */
864     DeviceObject = DeviceNode->PhysicalDeviceObject;
865 
866     /* Check if we're not started yet */
867     if (!(DeviceNode->Flags & DNF_STARTED))
868     {
869         /* Start us */
870         IopStartDevice2(DeviceObject);
871     }
872 
873     /* Do we need to query IDs? This happens in the case of manual reporting */
874 #if 0
875     if (DeviceNode->Flags & DNF_NEED_QUERY_IDS)
876     {
877         DPRINT1("Warning: Device node has DNF_NEED_QUERY_IDS\n");
878         /* And that case shouldn't happen yet */
879         ASSERT(FALSE);
880     }
881 #endif
882 
883     IopSetServiceEnumData(DeviceNode);
884 
885     /* Make sure we're started, and check if we need enumeration */
886     if ((DeviceNode->Flags & DNF_STARTED) &&
887         (DeviceNode->Flags & DNF_NEED_ENUMERATION_ONLY))
888     {
889         /* Enumerate us */
890         IoSynchronousInvalidateDeviceRelations(DeviceObject, BusRelations);
891         Status = STATUS_SUCCESS;
892     }
893     else
894     {
895         /* Nothing to do */
896         Status = STATUS_SUCCESS;
897     }
898 
899     /* Return */
900     return Status;
901 }
902 
903 NTSTATUS
904 IopStopDevice(
905    PDEVICE_NODE DeviceNode)
906 {
907     NTSTATUS Status;
908 
909     DPRINT("Stopping device: %wZ\n", &DeviceNode->InstancePath);
910 
911     Status = IopQueryStopDevice(DeviceNode->PhysicalDeviceObject);
912     if (NT_SUCCESS(Status))
913     {
914         IopSendStopDevice(DeviceNode->PhysicalDeviceObject);
915 
916         DeviceNode->Flags &= ~(DNF_STARTED | DNF_START_REQUEST_PENDING);
917         DeviceNode->Flags |= DNF_STOPPED;
918 
919         return STATUS_SUCCESS;
920     }
921 
922     return Status;
923 }
924 
925 NTSTATUS
926 IopStartDevice(
927    PDEVICE_NODE DeviceNode)
928 {
929     NTSTATUS Status;
930     HANDLE InstanceHandle = NULL, ControlHandle = NULL;
931     UNICODE_STRING KeyName, ValueString;
932     OBJECT_ATTRIBUTES ObjectAttributes;
933 
934     if (DeviceNode->Flags & DNF_DISABLED)
935         return STATUS_SUCCESS;
936 
937     Status = IopAssignDeviceResources(DeviceNode);
938     if (!NT_SUCCESS(Status))
939         goto ByeBye;
940 
941     /* New PnP ABI */
942     IopStartAndEnumerateDevice(DeviceNode);
943 
944     /* FIX: Should be done in new device instance code */
945     Status = IopCreateDeviceKeyPath(&DeviceNode->InstancePath, REG_OPTION_NON_VOLATILE, &InstanceHandle);
946     if (!NT_SUCCESS(Status))
947         goto ByeBye;
948 
949     /* FIX: Should be done in IoXxxPrepareDriverLoading */
950     // {
951     RtlInitUnicodeString(&KeyName, L"Control");
952     InitializeObjectAttributes(&ObjectAttributes,
953                                &KeyName,
954                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
955                                InstanceHandle,
956                                NULL);
957     Status = ZwCreateKey(&ControlHandle,
958                          KEY_SET_VALUE,
959                          &ObjectAttributes,
960                          0,
961                          NULL,
962                          REG_OPTION_VOLATILE,
963                          NULL);
964     if (!NT_SUCCESS(Status))
965         goto ByeBye;
966 
967    RtlInitUnicodeString(&KeyName, L"ActiveService");
968    ValueString = DeviceNode->ServiceName;
969    if (!ValueString.Buffer)
970        RtlInitUnicodeString(&ValueString, L"");
971    Status = ZwSetValueKey(ControlHandle, &KeyName, 0, REG_SZ, ValueString.Buffer, ValueString.Length + sizeof(UNICODE_NULL));
972    // }
973 
974 ByeBye:
975     if (ControlHandle != NULL)
976         ZwClose(ControlHandle);
977 
978     if (InstanceHandle != NULL)
979         ZwClose(InstanceHandle);
980 
981     return Status;
982 }
983 
984 NTSTATUS
985 NTAPI
986 IopQueryDeviceCapabilities(PDEVICE_NODE DeviceNode,
987                            PDEVICE_CAPABILITIES DeviceCaps)
988 {
989     IO_STATUS_BLOCK StatusBlock;
990     IO_STACK_LOCATION Stack;
991     NTSTATUS Status;
992     HANDLE InstanceKey;
993     UNICODE_STRING ValueName;
994 
995     /* Set up the Header */
996     RtlZeroMemory(DeviceCaps, sizeof(DEVICE_CAPABILITIES));
997     DeviceCaps->Size = sizeof(DEVICE_CAPABILITIES);
998     DeviceCaps->Version = 1;
999     DeviceCaps->Address = -1;
1000     DeviceCaps->UINumber = -1;
1001 
1002     /* Set up the Stack */
1003     RtlZeroMemory(&Stack, sizeof(IO_STACK_LOCATION));
1004     Stack.Parameters.DeviceCapabilities.Capabilities = DeviceCaps;
1005 
1006     /* Send the IRP */
1007     Status = IopInitiatePnpIrp(DeviceNode->PhysicalDeviceObject,
1008                                &StatusBlock,
1009                                IRP_MN_QUERY_CAPABILITIES,
1010                                &Stack);
1011     if (!NT_SUCCESS(Status))
1012     {
1013         if (Status != STATUS_NOT_SUPPORTED)
1014         {
1015             DPRINT1("IRP_MN_QUERY_CAPABILITIES failed with status 0x%lx\n", Status);
1016         }
1017         return Status;
1018     }
1019 
1020     DeviceNode->CapabilityFlags = *(PULONG)((ULONG_PTR)&DeviceCaps->Version + sizeof(DeviceCaps->Version));
1021 
1022     if (DeviceCaps->NoDisplayInUI)
1023         DeviceNode->UserFlags |= DNUF_DONT_SHOW_IN_UI;
1024     else
1025         DeviceNode->UserFlags &= ~DNUF_DONT_SHOW_IN_UI;
1026 
1027     Status = IopCreateDeviceKeyPath(&DeviceNode->InstancePath, REG_OPTION_NON_VOLATILE, &InstanceKey);
1028     if (NT_SUCCESS(Status))
1029     {
1030         /* Set 'Capabilities' value */
1031         RtlInitUnicodeString(&ValueName, L"Capabilities");
1032         Status = ZwSetValueKey(InstanceKey,
1033                                &ValueName,
1034                                0,
1035                                REG_DWORD,
1036                                &DeviceNode->CapabilityFlags,
1037                                sizeof(ULONG));
1038 
1039         /* Set 'UINumber' value */
1040         if (DeviceCaps->UINumber != MAXULONG)
1041         {
1042             RtlInitUnicodeString(&ValueName, L"UINumber");
1043             Status = ZwSetValueKey(InstanceKey,
1044                                    &ValueName,
1045                                    0,
1046                                    REG_DWORD,
1047                                    &DeviceCaps->UINumber,
1048                                    sizeof(ULONG));
1049         }
1050 
1051         ZwClose(InstanceKey);
1052     }
1053 
1054     return Status;
1055 }
1056 
1057 static
1058 VOID
1059 NTAPI
1060 IopDeviceActionWorker(
1061     _In_ PVOID Context)
1062 {
1063     PLIST_ENTRY ListEntry;
1064     PDEVICE_ACTION_DATA Data;
1065     KIRQL OldIrql;
1066 
1067     KeAcquireSpinLock(&IopDeviceActionLock, &OldIrql);
1068     while (!IsListEmpty(&IopDeviceActionRequestList))
1069     {
1070         ListEntry = RemoveHeadList(&IopDeviceActionRequestList);
1071         KeReleaseSpinLock(&IopDeviceActionLock, OldIrql);
1072         Data = CONTAINING_RECORD(ListEntry,
1073                                  DEVICE_ACTION_DATA,
1074                                  RequestListEntry);
1075 
1076         IoSynchronousInvalidateDeviceRelations(Data->DeviceObject,
1077                                                Data->Type);
1078 
1079         ObDereferenceObject(Data->DeviceObject);
1080         ExFreePoolWithTag(Data, TAG_IO);
1081         KeAcquireSpinLock(&IopDeviceActionLock, &OldIrql);
1082     }
1083     IopDeviceActionInProgress = FALSE;
1084     KeReleaseSpinLock(&IopDeviceActionLock, OldIrql);
1085 }
1086 
1087 NTSTATUS
1088 IopGetSystemPowerDeviceObject(PDEVICE_OBJECT *DeviceObject)
1089 {
1090     KIRQL OldIrql;
1091 
1092     if (PopSystemPowerDeviceNode)
1093     {
1094         KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql);
1095         *DeviceObject = PopSystemPowerDeviceNode->PhysicalDeviceObject;
1096         KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql);
1097 
1098         return STATUS_SUCCESS;
1099     }
1100 
1101     return STATUS_UNSUCCESSFUL;
1102 }
1103 
1104 USHORT
1105 NTAPI
1106 IopGetBusTypeGuidIndex(LPGUID BusTypeGuid)
1107 {
1108     USHORT i = 0, FoundIndex = 0xFFFF;
1109     ULONG NewSize;
1110     PVOID NewList;
1111 
1112     /* Acquire the lock */
1113     ExAcquireFastMutex(&PnpBusTypeGuidList->Lock);
1114 
1115     /* Loop all entries */
1116     while (i < PnpBusTypeGuidList->GuidCount)
1117     {
1118          /* Try to find a match */
1119          if (RtlCompareMemory(BusTypeGuid,
1120                               &PnpBusTypeGuidList->Guids[i],
1121                               sizeof(GUID)) == sizeof(GUID))
1122          {
1123               /* Found it */
1124               FoundIndex = i;
1125               goto Quickie;
1126          }
1127          i++;
1128     }
1129 
1130     /* Check if we have to grow the list */
1131     if (PnpBusTypeGuidList->GuidCount)
1132     {
1133         /* Calculate the new size */
1134         NewSize = sizeof(IO_BUS_TYPE_GUID_LIST) +
1135                  (sizeof(GUID) * PnpBusTypeGuidList->GuidCount);
1136 
1137         /* Allocate the new copy */
1138         NewList = ExAllocatePool(PagedPool, NewSize);
1139 
1140         if (!NewList)
1141         {
1142             /* Fail */
1143             ExFreePool(PnpBusTypeGuidList);
1144             goto Quickie;
1145         }
1146 
1147         /* Now copy them, decrease the size too */
1148         NewSize -= sizeof(GUID);
1149         RtlCopyMemory(NewList, PnpBusTypeGuidList, NewSize);
1150 
1151         /* Free the old list */
1152         ExFreePool(PnpBusTypeGuidList);
1153 
1154         /* Use the new buffer */
1155         PnpBusTypeGuidList = NewList;
1156     }
1157 
1158     /* Copy the new GUID */
1159     RtlCopyMemory(&PnpBusTypeGuidList->Guids[PnpBusTypeGuidList->GuidCount],
1160                   BusTypeGuid,
1161                   sizeof(GUID));
1162 
1163     /* The new entry is the index */
1164     FoundIndex = (USHORT)PnpBusTypeGuidList->GuidCount;
1165     PnpBusTypeGuidList->GuidCount++;
1166 
1167 Quickie:
1168     ExReleaseFastMutex(&PnpBusTypeGuidList->Lock);
1169     return FoundIndex;
1170 }
1171 
1172 /*
1173  * DESCRIPTION
1174  *     Creates a device node
1175  *
1176  * ARGUMENTS
1177  *   ParentNode           = Pointer to parent device node
1178  *   PhysicalDeviceObject = Pointer to PDO for device object. Pass NULL
1179  *                          to have the root device node create one
1180  *                          (eg. for legacy drivers)
1181  *   DeviceNode           = Pointer to storage for created device node
1182  *
1183  * RETURN VALUE
1184  *     Status
1185  */
1186 NTSTATUS
1187 IopCreateDeviceNode(PDEVICE_NODE ParentNode,
1188                     PDEVICE_OBJECT PhysicalDeviceObject,
1189                     PUNICODE_STRING ServiceName,
1190                     PDEVICE_NODE *DeviceNode)
1191 {
1192     PDEVICE_NODE Node;
1193     NTSTATUS Status;
1194     KIRQL OldIrql;
1195     UNICODE_STRING FullServiceName;
1196     UNICODE_STRING LegacyPrefix = RTL_CONSTANT_STRING(L"LEGACY_");
1197     UNICODE_STRING UnknownDeviceName = RTL_CONSTANT_STRING(L"UNKNOWN");
1198     UNICODE_STRING KeyName, ClassName;
1199     PUNICODE_STRING ServiceName1;
1200     ULONG LegacyValue;
1201     UNICODE_STRING ClassGUID;
1202     HANDLE InstanceHandle;
1203 
1204     DPRINT("ParentNode 0x%p PhysicalDeviceObject 0x%p ServiceName %wZ\n",
1205            ParentNode, PhysicalDeviceObject, ServiceName);
1206 
1207     Node = ExAllocatePoolWithTag(NonPagedPool, sizeof(DEVICE_NODE), TAG_IO_DEVNODE);
1208     if (!Node)
1209     {
1210         return STATUS_INSUFFICIENT_RESOURCES;
1211     }
1212 
1213     RtlZeroMemory(Node, sizeof(DEVICE_NODE));
1214 
1215     if (!ServiceName)
1216         ServiceName1 = &UnknownDeviceName;
1217     else
1218         ServiceName1 = ServiceName;
1219 
1220     if (!PhysicalDeviceObject)
1221     {
1222         FullServiceName.MaximumLength = LegacyPrefix.Length + ServiceName1->Length + sizeof(UNICODE_NULL);
1223         FullServiceName.Length = 0;
1224         FullServiceName.Buffer = ExAllocatePool(PagedPool, FullServiceName.MaximumLength);
1225         if (!FullServiceName.Buffer)
1226         {
1227             ExFreePoolWithTag(Node, TAG_IO_DEVNODE);
1228             return STATUS_INSUFFICIENT_RESOURCES;
1229         }
1230 
1231         RtlAppendUnicodeStringToString(&FullServiceName, &LegacyPrefix);
1232         RtlAppendUnicodeStringToString(&FullServiceName, ServiceName1);
1233         RtlUpcaseUnicodeString(&FullServiceName, &FullServiceName, FALSE);
1234 
1235         Status = PnpRootCreateDevice(&FullServiceName, NULL, &PhysicalDeviceObject, &Node->InstancePath);
1236         if (!NT_SUCCESS(Status))
1237         {
1238             DPRINT1("PnpRootCreateDevice() failed with status 0x%08X\n", Status);
1239             ExFreePool(FullServiceName.Buffer);
1240             ExFreePoolWithTag(Node, TAG_IO_DEVNODE);
1241             return Status;
1242         }
1243 
1244         /* Create the device key for legacy drivers */
1245         Status = IopCreateDeviceKeyPath(&Node->InstancePath, REG_OPTION_VOLATILE, &InstanceHandle);
1246         if (!NT_SUCCESS(Status))
1247         {
1248             ExFreePool(FullServiceName.Buffer);
1249             ExFreePoolWithTag(Node, TAG_IO_DEVNODE);
1250             return Status;
1251         }
1252 
1253         Node->ServiceName.MaximumLength = ServiceName1->Length + sizeof(UNICODE_NULL);
1254         Node->ServiceName.Length = 0;
1255         Node->ServiceName.Buffer = ExAllocatePool(PagedPool, Node->ServiceName.MaximumLength);
1256         if (!Node->ServiceName.Buffer)
1257         {
1258             ZwClose(InstanceHandle);
1259             ExFreePool(FullServiceName.Buffer);
1260             ExFreePoolWithTag(Node, TAG_IO_DEVNODE);
1261             return Status;
1262         }
1263 
1264         RtlCopyUnicodeString(&Node->ServiceName, ServiceName1);
1265 
1266         if (ServiceName)
1267         {
1268             RtlInitUnicodeString(&KeyName, L"Service");
1269             Status = ZwSetValueKey(InstanceHandle, &KeyName, 0, REG_SZ, ServiceName->Buffer, ServiceName->Length + sizeof(UNICODE_NULL));
1270         }
1271 
1272         if (NT_SUCCESS(Status))
1273         {
1274             RtlInitUnicodeString(&KeyName, L"Legacy");
1275             LegacyValue = 1;
1276             Status = ZwSetValueKey(InstanceHandle, &KeyName, 0, REG_DWORD, &LegacyValue, sizeof(LegacyValue));
1277 
1278             RtlInitUnicodeString(&KeyName, L"ConfigFlags");
1279             LegacyValue = 0;
1280             ZwSetValueKey(InstanceHandle, &KeyName, 0, REG_DWORD, &LegacyValue, sizeof(LegacyValue));
1281 
1282             if (NT_SUCCESS(Status))
1283             {
1284                 RtlInitUnicodeString(&KeyName, L"Class");
1285                 RtlInitUnicodeString(&ClassName, L"LegacyDriver");
1286                 Status = ZwSetValueKey(InstanceHandle, &KeyName, 0, REG_SZ, ClassName.Buffer, ClassName.Length + sizeof(UNICODE_NULL));
1287                 if (NT_SUCCESS(Status))
1288                 {
1289                     RtlInitUnicodeString(&KeyName, L"ClassGUID");
1290                     RtlInitUnicodeString(&ClassGUID, L"{8ECC055D-047F-11D1-A537-0000F8753ED1}");
1291                     Status = ZwSetValueKey(InstanceHandle, &KeyName, 0, REG_SZ, ClassGUID.Buffer, ClassGUID.Length + sizeof(UNICODE_NULL));
1292                     if (NT_SUCCESS(Status))
1293                     {
1294                         // FIXME: Retrieve the real "description" by looking at the "DisplayName" string
1295                         // of the corresponding CurrentControlSet\Services\xxx entry for this driver.
1296                         RtlInitUnicodeString(&KeyName, L"DeviceDesc");
1297                         Status = ZwSetValueKey(InstanceHandle, &KeyName, 0, REG_SZ, ServiceName1->Buffer, ServiceName1->Length + sizeof(UNICODE_NULL));
1298                     }
1299                 }
1300             }
1301         }
1302 
1303         ZwClose(InstanceHandle);
1304         ExFreePool(FullServiceName.Buffer);
1305 
1306         if (!NT_SUCCESS(Status))
1307         {
1308             ExFreePool(Node->ServiceName.Buffer);
1309             ExFreePoolWithTag(Node, TAG_IO_DEVNODE);
1310             return Status;
1311         }
1312 
1313         IopDeviceNodeSetFlag(Node, DNF_LEGACY_DRIVER);
1314         IopDeviceNodeSetFlag(Node, DNF_PROCESSED);
1315         IopDeviceNodeSetFlag(Node, DNF_ADDED);
1316         IopDeviceNodeSetFlag(Node, DNF_STARTED);
1317     }
1318 
1319     Node->PhysicalDeviceObject = PhysicalDeviceObject;
1320 
1321     ((PEXTENDED_DEVOBJ_EXTENSION)PhysicalDeviceObject->DeviceObjectExtension)->DeviceNode = Node;
1322 
1323     if (ParentNode)
1324     {
1325         KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql);
1326         Node->Parent = ParentNode;
1327         Node->Sibling = NULL;
1328         if (ParentNode->LastChild == NULL)
1329         {
1330             ParentNode->Child = Node;
1331             ParentNode->LastChild = Node;
1332         }
1333         else
1334         {
1335             ParentNode->LastChild->Sibling = Node;
1336             ParentNode->LastChild = Node;
1337         }
1338         KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql);
1339         Node->Level = ParentNode->Level + 1;
1340     }
1341 
1342     PhysicalDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
1343 
1344     *DeviceNode = Node;
1345 
1346     return STATUS_SUCCESS;
1347 }
1348 
1349 NTSTATUS
1350 IopFreeDeviceNode(PDEVICE_NODE DeviceNode)
1351 {
1352     KIRQL OldIrql;
1353     PDEVICE_NODE PrevSibling = NULL;
1354 
1355     /* All children must be deleted before a parent is deleted */
1356     ASSERT(!DeviceNode->Child);
1357     ASSERT(DeviceNode->PhysicalDeviceObject);
1358 
1359     KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql);
1360 
1361     /* Get previous sibling */
1362     if (DeviceNode->Parent && DeviceNode->Parent->Child != DeviceNode)
1363     {
1364         PrevSibling = DeviceNode->Parent->Child;
1365         while (PrevSibling->Sibling != DeviceNode)
1366             PrevSibling = PrevSibling->Sibling;
1367     }
1368 
1369     /* Unlink from parent if it exists */
1370     if (DeviceNode->Parent)
1371     {
1372         if (DeviceNode->Parent->LastChild == DeviceNode)
1373         {
1374             DeviceNode->Parent->LastChild = PrevSibling;
1375             if (PrevSibling)
1376                 PrevSibling->Sibling = NULL;
1377         }
1378         if (DeviceNode->Parent->Child == DeviceNode)
1379             DeviceNode->Parent->Child = DeviceNode->Sibling;
1380     }
1381 
1382     /* Unlink from sibling list */
1383     if (PrevSibling)
1384         PrevSibling->Sibling = DeviceNode->Sibling;
1385 
1386     KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql);
1387 
1388     RtlFreeUnicodeString(&DeviceNode->InstancePath);
1389 
1390     RtlFreeUnicodeString(&DeviceNode->ServiceName);
1391 
1392     if (DeviceNode->ResourceList)
1393     {
1394         ExFreePool(DeviceNode->ResourceList);
1395     }
1396 
1397     if (DeviceNode->ResourceListTranslated)
1398     {
1399         ExFreePool(DeviceNode->ResourceListTranslated);
1400     }
1401 
1402     if (DeviceNode->ResourceRequirements)
1403     {
1404         ExFreePool(DeviceNode->ResourceRequirements);
1405     }
1406 
1407     if (DeviceNode->BootResources)
1408     {
1409         ExFreePool(DeviceNode->BootResources);
1410     }
1411 
1412     ((PEXTENDED_DEVOBJ_EXTENSION)DeviceNode->PhysicalDeviceObject->DeviceObjectExtension)->DeviceNode = NULL;
1413     ExFreePoolWithTag(DeviceNode, TAG_IO_DEVNODE);
1414 
1415     return STATUS_SUCCESS;
1416 }
1417 
1418 NTSTATUS
1419 NTAPI
1420 IopSynchronousCall(IN PDEVICE_OBJECT DeviceObject,
1421                    IN PIO_STACK_LOCATION IoStackLocation,
1422                    OUT PVOID *Information)
1423 {
1424     PIRP Irp;
1425     PIO_STACK_LOCATION IrpStack;
1426     IO_STATUS_BLOCK IoStatusBlock;
1427     KEVENT Event;
1428     NTSTATUS Status;
1429     PDEVICE_OBJECT TopDeviceObject;
1430     PAGED_CODE();
1431 
1432     /* Call the top of the device stack */
1433     TopDeviceObject = IoGetAttachedDeviceReference(DeviceObject);
1434 
1435     /* Allocate an IRP */
1436     Irp = IoAllocateIrp(TopDeviceObject->StackSize, FALSE);
1437     if (!Irp) return STATUS_INSUFFICIENT_RESOURCES;
1438 
1439     /* Initialize to failure */
1440     Irp->IoStatus.Status = IoStatusBlock.Status = STATUS_NOT_SUPPORTED;
1441     Irp->IoStatus.Information = IoStatusBlock.Information = 0;
1442 
1443     /* Special case for IRP_MN_FILTER_RESOURCE_REQUIREMENTS */
1444     if (IoStackLocation->MinorFunction == IRP_MN_FILTER_RESOURCE_REQUIREMENTS)
1445     {
1446         /* Copy the resource requirements list into the IOSB */
1447         Irp->IoStatus.Information =
1448         IoStatusBlock.Information = (ULONG_PTR)IoStackLocation->Parameters.FilterResourceRequirements.IoResourceRequirementList;
1449     }
1450 
1451     /* Initialize the event */
1452     KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
1453 
1454     /* Set them up */
1455     Irp->UserIosb = &IoStatusBlock;
1456     Irp->UserEvent = &Event;
1457 
1458     /* Queue the IRP */
1459     Irp->Tail.Overlay.Thread = PsGetCurrentThread();
1460     IoQueueThreadIrp(Irp);
1461 
1462     /* Copy-in the stack */
1463     IrpStack = IoGetNextIrpStackLocation(Irp);
1464     *IrpStack = *IoStackLocation;
1465 
1466     /* Call the driver */
1467     Status = IoCallDriver(TopDeviceObject, Irp);
1468     if (Status == STATUS_PENDING)
1469     {
1470         /* Wait for it */
1471         KeWaitForSingleObject(&Event,
1472                               Executive,
1473                               KernelMode,
1474                               FALSE,
1475                               NULL);
1476         Status = IoStatusBlock.Status;
1477     }
1478 
1479     /* Remove the reference */
1480     ObDereferenceObject(TopDeviceObject);
1481 
1482     /* Return the information */
1483     *Information = (PVOID)IoStatusBlock.Information;
1484     return Status;
1485 }
1486 
1487 NTSTATUS
1488 NTAPI
1489 IopInitiatePnpIrp(IN PDEVICE_OBJECT DeviceObject,
1490                   IN OUT PIO_STATUS_BLOCK IoStatusBlock,
1491                   IN UCHAR MinorFunction,
1492                   IN PIO_STACK_LOCATION Stack OPTIONAL)
1493 {
1494     IO_STACK_LOCATION IoStackLocation;
1495 
1496     /* Fill out the stack information */
1497     RtlZeroMemory(&IoStackLocation, sizeof(IO_STACK_LOCATION));
1498     IoStackLocation.MajorFunction = IRP_MJ_PNP;
1499     IoStackLocation.MinorFunction = MinorFunction;
1500     if (Stack)
1501     {
1502         /* Copy the rest */
1503         RtlCopyMemory(&IoStackLocation.Parameters,
1504                       &Stack->Parameters,
1505                       sizeof(Stack->Parameters));
1506     }
1507 
1508     /* Do the PnP call */
1509     IoStatusBlock->Status = IopSynchronousCall(DeviceObject,
1510                                                &IoStackLocation,
1511                                                (PVOID)&IoStatusBlock->Information);
1512     return IoStatusBlock->Status;
1513 }
1514 
1515 NTSTATUS
1516 IopTraverseDeviceTreeNode(PDEVICETREE_TRAVERSE_CONTEXT Context)
1517 {
1518     PDEVICE_NODE ParentDeviceNode;
1519     PDEVICE_NODE ChildDeviceNode;
1520     NTSTATUS Status;
1521 
1522     /* Copy context data so we don't overwrite it in subsequent calls to this function */
1523     ParentDeviceNode = Context->DeviceNode;
1524 
1525     /* Call the action routine */
1526     Status = (Context->Action)(ParentDeviceNode, Context->Context);
1527     if (!NT_SUCCESS(Status))
1528     {
1529         return Status;
1530     }
1531 
1532     /* Traversal of all children nodes */
1533     for (ChildDeviceNode = ParentDeviceNode->Child;
1534          ChildDeviceNode != NULL;
1535          ChildDeviceNode = ChildDeviceNode->Sibling)
1536     {
1537         /* Pass the current device node to the action routine */
1538         Context->DeviceNode = ChildDeviceNode;
1539 
1540         Status = IopTraverseDeviceTreeNode(Context);
1541         if (!NT_SUCCESS(Status))
1542         {
1543             return Status;
1544         }
1545     }
1546 
1547     return Status;
1548 }
1549 
1550 
1551 NTSTATUS
1552 IopTraverseDeviceTree(PDEVICETREE_TRAVERSE_CONTEXT Context)
1553 {
1554     NTSTATUS Status;
1555 
1556     DPRINT("Context 0x%p\n", Context);
1557 
1558     DPRINT("IopTraverseDeviceTree(DeviceNode 0x%p  FirstDeviceNode 0x%p  Action %p  Context 0x%p)\n",
1559            Context->DeviceNode, Context->FirstDeviceNode, Context->Action, Context->Context);
1560 
1561     /* Start from the specified device node */
1562     Context->DeviceNode = Context->FirstDeviceNode;
1563 
1564     /* Recursively traverse the device tree */
1565     Status = IopTraverseDeviceTreeNode(Context);
1566     if (Status == STATUS_UNSUCCESSFUL)
1567     {
1568         /* The action routine just wanted to terminate the traversal with status
1569         code STATUS_SUCCESS */
1570         Status = STATUS_SUCCESS;
1571     }
1572 
1573     return Status;
1574 }
1575 
1576 
1577 /*
1578  * IopCreateDeviceKeyPath
1579  *
1580  * Creates a registry key
1581  *
1582  * Parameters
1583  *    RegistryPath
1584  *        Name of the key to be created.
1585  *    Handle
1586  *        Handle to the newly created key
1587  *
1588  * Remarks
1589  *     This method can create nested trees, so parent of RegistryPath can
1590  *     be not existant, and will be created if needed.
1591  */
1592 NTSTATUS
1593 NTAPI
1594 IopCreateDeviceKeyPath(IN PCUNICODE_STRING RegistryPath,
1595                        IN ULONG CreateOptions,
1596                        OUT PHANDLE Handle)
1597 {
1598     UNICODE_STRING EnumU = RTL_CONSTANT_STRING(ENUM_ROOT);
1599     HANDLE hParent = NULL, hKey;
1600     OBJECT_ATTRIBUTES ObjectAttributes;
1601     UNICODE_STRING KeyName;
1602     PCWSTR Current, Last;
1603     USHORT Length;
1604     NTSTATUS Status;
1605 
1606     /* Assume failure */
1607     *Handle = NULL;
1608 
1609     /* Open root key for device instances */
1610     Status = IopOpenRegistryKeyEx(&hParent, NULL, &EnumU, KEY_CREATE_SUB_KEY);
1611     if (!NT_SUCCESS(Status))
1612     {
1613         DPRINT1("ZwOpenKey('%wZ') failed with status 0x%08lx\n", &EnumU, Status);
1614         return Status;
1615     }
1616 
1617     Current = KeyName.Buffer = RegistryPath->Buffer;
1618     Last = &RegistryPath->Buffer[RegistryPath->Length / sizeof(WCHAR)];
1619 
1620     /* Go up to the end of the string */
1621     while (Current <= Last)
1622     {
1623         if (Current != Last && *Current != L'\\')
1624         {
1625             /* Not the end of the string and not a separator */
1626             Current++;
1627             continue;
1628         }
1629 
1630         /* Prepare relative key name */
1631         Length = (USHORT)((ULONG_PTR)Current - (ULONG_PTR)KeyName.Buffer);
1632         KeyName.MaximumLength = KeyName.Length = Length;
1633         DPRINT("Create '%wZ'\n", &KeyName);
1634 
1635         /* Open key */
1636         InitializeObjectAttributes(&ObjectAttributes,
1637                                    &KeyName,
1638                                    OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
1639                                    hParent,
1640                                    NULL);
1641         Status = ZwCreateKey(&hKey,
1642                              Current == Last ? KEY_ALL_ACCESS : KEY_CREATE_SUB_KEY,
1643                              &ObjectAttributes,
1644                              0,
1645                              NULL,
1646                              CreateOptions,
1647                              NULL);
1648 
1649         /* Close parent key handle, we don't need it anymore */
1650         if (hParent)
1651             ZwClose(hParent);
1652 
1653         /* Key opening/creating failed? */
1654         if (!NT_SUCCESS(Status))
1655         {
1656             DPRINT1("ZwCreateKey('%wZ') failed with status 0x%08lx\n", &KeyName, Status);
1657             return Status;
1658         }
1659 
1660         /* Check if it is the end of the string */
1661         if (Current == Last)
1662         {
1663             /* Yes, return success */
1664             *Handle = hKey;
1665             return STATUS_SUCCESS;
1666         }
1667 
1668         /* Start with this new parent key */
1669         hParent = hKey;
1670         Current++;
1671         KeyName.Buffer = (PWSTR)Current;
1672     }
1673 
1674     return STATUS_UNSUCCESSFUL;
1675 }
1676 
1677 NTSTATUS
1678 IopSetDeviceInstanceData(HANDLE InstanceKey,
1679                          PDEVICE_NODE DeviceNode)
1680 {
1681     OBJECT_ATTRIBUTES ObjectAttributes;
1682     UNICODE_STRING KeyName;
1683     HANDLE LogConfKey, ControlKey, DeviceParamsKey;
1684     ULONG ResCount;
1685     ULONG ResultLength;
1686     NTSTATUS Status;
1687 
1688     DPRINT("IopSetDeviceInstanceData() called\n");
1689 
1690     /* Create the 'LogConf' key */
1691     RtlInitUnicodeString(&KeyName, L"LogConf");
1692     InitializeObjectAttributes(&ObjectAttributes,
1693                                &KeyName,
1694                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
1695                                InstanceKey,
1696                                NULL);
1697     Status = ZwCreateKey(&LogConfKey,
1698                          KEY_ALL_ACCESS,
1699                          &ObjectAttributes,
1700                          0,
1701                          NULL,
1702                          // FIXME? In r53694 it was silently turned from non-volatile into this,
1703                          // without any extra warning. Is this still needed??
1704                          REG_OPTION_VOLATILE,
1705                          NULL);
1706     if (NT_SUCCESS(Status))
1707     {
1708         /* Set 'BootConfig' value */
1709         if (DeviceNode->BootResources != NULL)
1710         {
1711             ResCount = DeviceNode->BootResources->Count;
1712             if (ResCount != 0)
1713             {
1714                 RtlInitUnicodeString(&KeyName, L"BootConfig");
1715                 Status = ZwSetValueKey(LogConfKey,
1716                                        &KeyName,
1717                                        0,
1718                                        REG_RESOURCE_LIST,
1719                                        DeviceNode->BootResources,
1720                                        PnpDetermineResourceListSize(DeviceNode->BootResources));
1721             }
1722         }
1723 
1724         /* Set 'BasicConfigVector' value */
1725         if (DeviceNode->ResourceRequirements != NULL &&
1726             DeviceNode->ResourceRequirements->ListSize != 0)
1727         {
1728             RtlInitUnicodeString(&KeyName, L"BasicConfigVector");
1729             Status = ZwSetValueKey(LogConfKey,
1730                                    &KeyName,
1731                                    0,
1732                                    REG_RESOURCE_REQUIREMENTS_LIST,
1733                                    DeviceNode->ResourceRequirements,
1734                                    DeviceNode->ResourceRequirements->ListSize);
1735         }
1736 
1737         ZwClose(LogConfKey);
1738     }
1739 
1740     /* Set the 'ConfigFlags' value */
1741     RtlInitUnicodeString(&KeyName, L"ConfigFlags");
1742     Status = ZwQueryValueKey(InstanceKey,
1743                              &KeyName,
1744                              KeyValueBasicInformation,
1745                              NULL,
1746                              0,
1747                              &ResultLength);
1748     if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
1749     {
1750         /* Write the default value */
1751         ULONG DefaultConfigFlags = 0;
1752         Status = ZwSetValueKey(InstanceKey,
1753                                &KeyName,
1754                                0,
1755                                REG_DWORD,
1756                                &DefaultConfigFlags,
1757                                sizeof(DefaultConfigFlags));
1758     }
1759 
1760     /* Create the 'Control' key */
1761     RtlInitUnicodeString(&KeyName, L"Control");
1762     InitializeObjectAttributes(&ObjectAttributes,
1763                                &KeyName,
1764                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
1765                                InstanceKey,
1766                                NULL);
1767     Status = ZwCreateKey(&ControlKey,
1768                          0,
1769                          &ObjectAttributes,
1770                          0,
1771                          NULL,
1772                          REG_OPTION_VOLATILE,
1773                          NULL);
1774     if (NT_SUCCESS(Status))
1775         ZwClose(ControlKey);
1776 
1777     /* Create the 'Device Parameters' key and set the 'FirmwareIdentified' value for all ACPI-enumerated devices */
1778     if (_wcsnicmp(DeviceNode->InstancePath.Buffer, L"ACPI\\", 5) == 0)
1779     {
1780         RtlInitUnicodeString(&KeyName, L"Device Parameters");
1781         InitializeObjectAttributes(&ObjectAttributes,
1782                                    &KeyName,
1783                                    OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
1784                                    InstanceKey,
1785                                    NULL);
1786         Status = ZwCreateKey(&DeviceParamsKey,
1787                              0,
1788                              &ObjectAttributes,
1789                              0,
1790                              NULL,
1791                              REG_OPTION_NON_VOLATILE,
1792                              NULL);
1793         if (NT_SUCCESS(Status))
1794         {
1795             ULONG FirmwareIdentified = 1;
1796             RtlInitUnicodeString(&KeyName, L"FirmwareIdentified");
1797             Status = ZwSetValueKey(DeviceParamsKey,
1798                                    &KeyName,
1799                                    0,
1800                                    REG_DWORD,
1801                                    &FirmwareIdentified,
1802                                    sizeof(FirmwareIdentified));
1803 
1804             ZwClose(DeviceParamsKey);
1805         }
1806     }
1807 
1808     DPRINT("IopSetDeviceInstanceData() done\n");
1809 
1810     return Status;
1811 }
1812 
1813 /*
1814  * IopGetParentIdPrefix
1815  *
1816  * Retrieve (or create) a string which identifies a device.
1817  *
1818  * Parameters
1819  *    DeviceNode
1820  *        Pointer to device node.
1821  *    ParentIdPrefix
1822  *        Pointer to the string where is returned the parent node identifier
1823  *
1824  * Remarks
1825  *     If the return code is STATUS_SUCCESS, the ParentIdPrefix string is
1826  *     valid and its Buffer field is NULL-terminated. The caller needs to
1827  *     to free the string with RtlFreeUnicodeString when it is no longer
1828  *     needed.
1829  */
1830 
1831 NTSTATUS
1832 IopGetParentIdPrefix(PDEVICE_NODE DeviceNode,
1833                      PUNICODE_STRING ParentIdPrefix)
1834 {
1835     const UNICODE_STRING EnumKeyPath = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\System\\CurrentControlSet\\Enum\\");
1836     ULONG KeyNameBufferLength;
1837     PKEY_VALUE_PARTIAL_INFORMATION ParentIdPrefixInformation = NULL;
1838     UNICODE_STRING KeyName = {0, 0, NULL};
1839     UNICODE_STRING KeyValue;
1840     UNICODE_STRING ValueName;
1841     HANDLE hKey = NULL;
1842     ULONG crc32;
1843     NTSTATUS Status;
1844 
1845     /* HACK: As long as some devices have a NULL device
1846      * instance path, the following test is required :(
1847      */
1848     if (DeviceNode->Parent->InstancePath.Length == 0)
1849     {
1850         DPRINT1("Parent of %wZ has NULL Instance path, please report!\n",
1851                 &DeviceNode->InstancePath);
1852         return STATUS_UNSUCCESSFUL;
1853     }
1854 
1855     /* 1. Try to retrieve ParentIdPrefix from registry */
1856     KeyNameBufferLength = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data) + sizeof(L"12345678&12345678");
1857     ParentIdPrefixInformation = ExAllocatePoolWithTag(PagedPool,
1858                                                       KeyNameBufferLength + sizeof(UNICODE_NULL),
1859                                                       TAG_IO);
1860     if (!ParentIdPrefixInformation)
1861     {
1862         return STATUS_INSUFFICIENT_RESOURCES;
1863     }
1864 
1865     KeyName.Length = 0;
1866     KeyName.MaximumLength = EnumKeyPath.Length +
1867                             DeviceNode->Parent->InstancePath.Length +
1868                             sizeof(UNICODE_NULL);
1869     KeyName.Buffer = ExAllocatePoolWithTag(PagedPool,
1870                                            KeyName.MaximumLength,
1871                                            TAG_IO);
1872     if (!KeyName.Buffer)
1873     {
1874         Status = STATUS_INSUFFICIENT_RESOURCES;
1875         goto cleanup;
1876     }
1877 
1878     RtlCopyUnicodeString(&KeyName, &EnumKeyPath);
1879     RtlAppendUnicodeStringToString(&KeyName, &DeviceNode->Parent->InstancePath);
1880 
1881     Status = IopOpenRegistryKeyEx(&hKey, NULL, &KeyName, KEY_QUERY_VALUE | KEY_SET_VALUE);
1882     if (!NT_SUCCESS(Status))
1883     {
1884         goto cleanup;
1885     }
1886     RtlInitUnicodeString(&ValueName, L"ParentIdPrefix");
1887     Status = ZwQueryValueKey(hKey,
1888                              &ValueName,
1889                              KeyValuePartialInformation,
1890                              ParentIdPrefixInformation,
1891                              KeyNameBufferLength,
1892                              &KeyNameBufferLength);
1893     if (NT_SUCCESS(Status))
1894     {
1895         if (ParentIdPrefixInformation->Type != REG_SZ)
1896         {
1897             Status = STATUS_UNSUCCESSFUL;
1898         }
1899         else
1900         {
1901             KeyValue.MaximumLength = (USHORT)ParentIdPrefixInformation->DataLength;
1902             KeyValue.Length = KeyValue.MaximumLength - sizeof(UNICODE_NULL);
1903             KeyValue.Buffer = (PWSTR)ParentIdPrefixInformation->Data;
1904             ASSERT(KeyValue.Buffer[KeyValue.Length / sizeof(WCHAR)] == UNICODE_NULL);
1905         }
1906         goto cleanup;
1907     }
1908     if (Status != STATUS_OBJECT_NAME_NOT_FOUND)
1909     {
1910         /* FIXME how do we get here and why is ParentIdPrefixInformation valid? */
1911         KeyValue.MaximumLength = (USHORT)ParentIdPrefixInformation->DataLength;
1912         KeyValue.Length = KeyValue.MaximumLength - sizeof(UNICODE_NULL);
1913         KeyValue.Buffer = (PWSTR)ParentIdPrefixInformation->Data;
1914         ASSERT(KeyValue.Buffer[KeyValue.Length / sizeof(WCHAR)] == UNICODE_NULL);
1915         goto cleanup;
1916     }
1917 
1918     /* 2. Create the ParentIdPrefix value */
1919     crc32 = RtlComputeCrc32(0,
1920                             (PUCHAR)DeviceNode->Parent->InstancePath.Buffer,
1921                             DeviceNode->Parent->InstancePath.Length);
1922 
1923     RtlStringCbPrintfW((PWSTR)ParentIdPrefixInformation,
1924                        KeyNameBufferLength,
1925                        L"%lx&%lx",
1926                        DeviceNode->Parent->Level,
1927                        crc32);
1928     RtlInitUnicodeString(&KeyValue, (PWSTR)ParentIdPrefixInformation);
1929 
1930     /* 3. Try to write the ParentIdPrefix to registry */
1931     Status = ZwSetValueKey(hKey,
1932                            &ValueName,
1933                            0,
1934                            REG_SZ,
1935                            KeyValue.Buffer,
1936                            ((ULONG)wcslen(KeyValue.Buffer) + 1) * sizeof(WCHAR));
1937 
1938 cleanup:
1939     if (NT_SUCCESS(Status))
1940     {
1941         /* Duplicate the string to return it */
1942         Status = RtlDuplicateUnicodeString(RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE,
1943                                            &KeyValue,
1944                                            ParentIdPrefix);
1945     }
1946     ExFreePoolWithTag(ParentIdPrefixInformation, TAG_IO);
1947     RtlFreeUnicodeString(&KeyName);
1948     if (hKey != NULL)
1949     {
1950         ZwClose(hKey);
1951     }
1952     return Status;
1953 }
1954 
1955 static
1956 BOOLEAN
1957 IopValidateID(
1958     _In_ PWCHAR Id,
1959     _In_ BUS_QUERY_ID_TYPE QueryType)
1960 {
1961     PWCHAR PtrChar;
1962     PWCHAR StringEnd;
1963     WCHAR Char;
1964     ULONG SeparatorsCount = 0;
1965     PWCHAR PtrPrevChar = NULL;
1966     ULONG MaxSeparators;
1967     BOOLEAN IsMultiSz;
1968 
1969     PAGED_CODE();
1970 
1971     switch (QueryType)
1972     {
1973         case BusQueryDeviceID:
1974             MaxSeparators = MAX_SEPARATORS_DEVICEID;
1975             IsMultiSz = FALSE;
1976             break;
1977         case BusQueryInstanceID:
1978             MaxSeparators = MAX_SEPARATORS_INSTANCEID;
1979             IsMultiSz = FALSE;
1980             break;
1981 
1982         case BusQueryHardwareIDs:
1983         case BusQueryCompatibleIDs:
1984             MaxSeparators = MAX_SEPARATORS_DEVICEID;
1985             IsMultiSz = TRUE;
1986             break;
1987 
1988         default:
1989             DPRINT1("IopValidateID: Not handled QueryType - %x\n", QueryType);
1990             return FALSE;
1991     }
1992 
1993     StringEnd = Id + MAX_DEVICE_ID_LEN;
1994 
1995     for (PtrChar = Id; PtrChar < StringEnd; PtrChar++)
1996     {
1997         Char = *PtrChar;
1998 
1999         if (Char == UNICODE_NULL)
2000         {
2001             if (!IsMultiSz || (PtrPrevChar && PtrChar == PtrPrevChar + 1))
2002             {
2003                 if (MaxSeparators == SeparatorsCount || IsMultiSz)
2004                 {
2005                     return TRUE;
2006                 }
2007 
2008                 DPRINT1("IopValidateID: SeparatorsCount - %lu, MaxSeparators - %lu\n",
2009                         SeparatorsCount, MaxSeparators);
2010                 goto ErrorExit;
2011             }
2012 
2013             StringEnd = PtrChar + MAX_DEVICE_ID_LEN + 1;
2014             PtrPrevChar = PtrChar;
2015             SeparatorsCount = 0;
2016         }
2017         else if (Char < ' ' || Char > 0x7F || Char == ',')
2018         {
2019             DPRINT1("IopValidateID: Invalid character - %04X\n", Char);
2020             goto ErrorExit;
2021         }
2022         else if (Char == ' ')
2023         {
2024             *PtrChar = '_';
2025         }
2026         else if (Char == '\\')
2027         {
2028             SeparatorsCount++;
2029 
2030             if (SeparatorsCount > MaxSeparators)
2031             {
2032                 DPRINT1("IopValidateID: SeparatorsCount - %lu, MaxSeparators - %lu\n",
2033                         SeparatorsCount, MaxSeparators);
2034                 goto ErrorExit;
2035             }
2036         }
2037     }
2038 
2039     DPRINT1("IopValidateID: Not terminated ID\n");
2040 
2041 ErrorExit:
2042     // FIXME logging
2043     return FALSE;
2044 }
2045 
2046 NTSTATUS
2047 IopQueryHardwareIds(PDEVICE_NODE DeviceNode,
2048                     HANDLE InstanceKey)
2049 {
2050     IO_STACK_LOCATION Stack;
2051     IO_STATUS_BLOCK IoStatusBlock;
2052     PWSTR Ptr;
2053     UNICODE_STRING ValueName;
2054     NTSTATUS Status;
2055     ULONG Length, TotalLength;
2056     BOOLEAN IsValidID;
2057 
2058     DPRINT("Sending IRP_MN_QUERY_ID.BusQueryHardwareIDs to device stack\n");
2059 
2060     RtlZeroMemory(&Stack, sizeof(Stack));
2061     Stack.Parameters.QueryId.IdType = BusQueryHardwareIDs;
2062     Status = IopInitiatePnpIrp(DeviceNode->PhysicalDeviceObject,
2063                                &IoStatusBlock,
2064                                IRP_MN_QUERY_ID,
2065                                &Stack);
2066     if (NT_SUCCESS(Status))
2067     {
2068         IsValidID = IopValidateID((PWCHAR)IoStatusBlock.Information, BusQueryHardwareIDs);
2069 
2070         if (!IsValidID)
2071         {
2072             DPRINT1("Invalid HardwareIDs. DeviceNode - %p\n", DeviceNode);
2073         }
2074 
2075         TotalLength = 0;
2076 
2077         Ptr = (PWSTR)IoStatusBlock.Information;
2078         DPRINT("Hardware IDs:\n");
2079         while (*Ptr)
2080         {
2081             DPRINT("  %S\n", Ptr);
2082             Length = (ULONG)wcslen(Ptr) + 1;
2083 
2084             Ptr += Length;
2085             TotalLength += Length;
2086         }
2087         DPRINT("TotalLength: %hu\n", TotalLength);
2088         DPRINT("\n");
2089 
2090         RtlInitUnicodeString(&ValueName, L"HardwareID");
2091         Status = ZwSetValueKey(InstanceKey,
2092                                &ValueName,
2093                                0,
2094                                REG_MULTI_SZ,
2095                                (PVOID)IoStatusBlock.Information,
2096                                (TotalLength + 1) * sizeof(WCHAR));
2097         if (!NT_SUCCESS(Status))
2098         {
2099             DPRINT1("ZwSetValueKey() failed (Status %lx)\n", Status);
2100         }
2101     }
2102     else
2103     {
2104         DPRINT("IopInitiatePnpIrp() failed (Status %x)\n", Status);
2105     }
2106 
2107     return Status;
2108 }
2109 
2110 NTSTATUS
2111 IopQueryCompatibleIds(PDEVICE_NODE DeviceNode,
2112                       HANDLE InstanceKey)
2113 {
2114     IO_STACK_LOCATION Stack;
2115     IO_STATUS_BLOCK IoStatusBlock;
2116     PWSTR Ptr;
2117     UNICODE_STRING ValueName;
2118     NTSTATUS Status;
2119     ULONG Length, TotalLength;
2120     BOOLEAN IsValidID;
2121 
2122     DPRINT("Sending IRP_MN_QUERY_ID.BusQueryCompatibleIDs to device stack\n");
2123 
2124     RtlZeroMemory(&Stack, sizeof(Stack));
2125     Stack.Parameters.QueryId.IdType = BusQueryCompatibleIDs;
2126     Status = IopInitiatePnpIrp(DeviceNode->PhysicalDeviceObject,
2127                                &IoStatusBlock,
2128                                IRP_MN_QUERY_ID,
2129                                &Stack);
2130     if (NT_SUCCESS(Status) && IoStatusBlock.Information)
2131     {
2132         IsValidID = IopValidateID((PWCHAR)IoStatusBlock.Information, BusQueryCompatibleIDs);
2133 
2134         if (!IsValidID)
2135         {
2136             DPRINT1("Invalid CompatibleIDs. DeviceNode - %p\n", DeviceNode);
2137         }
2138 
2139         TotalLength = 0;
2140 
2141         Ptr = (PWSTR)IoStatusBlock.Information;
2142         DPRINT("Compatible IDs:\n");
2143         while (*Ptr)
2144         {
2145             DPRINT("  %S\n", Ptr);
2146             Length = (ULONG)wcslen(Ptr) + 1;
2147 
2148             Ptr += Length;
2149             TotalLength += Length;
2150         }
2151         DPRINT("TotalLength: %hu\n", TotalLength);
2152         DPRINT("\n");
2153 
2154         RtlInitUnicodeString(&ValueName, L"CompatibleIDs");
2155         Status = ZwSetValueKey(InstanceKey,
2156                                &ValueName,
2157                                0,
2158                                REG_MULTI_SZ,
2159                                (PVOID)IoStatusBlock.Information,
2160                                (TotalLength + 1) * sizeof(WCHAR));
2161         if (!NT_SUCCESS(Status))
2162         {
2163             DPRINT1("ZwSetValueKey() failed (Status %lx) or no Compatible ID returned\n", Status);
2164         }
2165     }
2166     else
2167     {
2168         DPRINT("IopInitiatePnpIrp() failed (Status %x)\n", Status);
2169     }
2170 
2171     return Status;
2172 }
2173 
2174 NTSTATUS
2175 IopCreateDeviceInstancePath(
2176     _In_ PDEVICE_NODE DeviceNode,
2177     _Out_ PUNICODE_STRING InstancePath)
2178 {
2179     IO_STATUS_BLOCK IoStatusBlock;
2180     UNICODE_STRING DeviceId;
2181     UNICODE_STRING InstanceId;
2182     IO_STACK_LOCATION Stack;
2183     NTSTATUS Status;
2184     UNICODE_STRING ParentIdPrefix = { 0, 0, NULL };
2185     DEVICE_CAPABILITIES DeviceCapabilities;
2186     BOOLEAN IsValidID;
2187 
2188     DPRINT("Sending IRP_MN_QUERY_ID.BusQueryDeviceID to device stack\n");
2189 
2190     Stack.Parameters.QueryId.IdType = BusQueryDeviceID;
2191     Status = IopInitiatePnpIrp(DeviceNode->PhysicalDeviceObject,
2192                                &IoStatusBlock,
2193                                IRP_MN_QUERY_ID,
2194                                &Stack);
2195     if (!NT_SUCCESS(Status))
2196     {
2197         DPRINT1("IopInitiatePnpIrp(BusQueryDeviceID) failed (Status %x)\n", Status);
2198         return Status;
2199     }
2200 
2201     IsValidID = IopValidateID((PWCHAR)IoStatusBlock.Information, BusQueryDeviceID);
2202 
2203     if (!IsValidID)
2204     {
2205         DPRINT1("Invalid DeviceID. DeviceNode - %p\n", DeviceNode);
2206     }
2207 
2208     /* Save the device id string */
2209     RtlInitUnicodeString(&DeviceId, (PWSTR)IoStatusBlock.Information);
2210 
2211     DPRINT("Sending IRP_MN_QUERY_CAPABILITIES to device stack (after enumeration)\n");
2212 
2213     Status = IopQueryDeviceCapabilities(DeviceNode, &DeviceCapabilities);
2214     if (!NT_SUCCESS(Status))
2215     {
2216         DPRINT1("IopQueryDeviceCapabilities() failed (Status 0x%08lx)\n", Status);
2217         RtlFreeUnicodeString(&DeviceId);
2218         return Status;
2219     }
2220 
2221     /* This bit is only check after enumeration */
2222     if (DeviceCapabilities.HardwareDisabled)
2223     {
2224         /* FIXME: Cleanup device */
2225         DeviceNode->Flags |= DNF_DISABLED;
2226         RtlFreeUnicodeString(&DeviceId);
2227         return STATUS_PLUGPLAY_NO_DEVICE;
2228     }
2229     else
2230     {
2231         DeviceNode->Flags &= ~DNF_DISABLED;
2232     }
2233 
2234     if (!DeviceCapabilities.UniqueID)
2235     {
2236         /* Device has not a unique ID. We need to prepend parent bus unique identifier */
2237         DPRINT("Instance ID is not unique\n");
2238         Status = IopGetParentIdPrefix(DeviceNode, &ParentIdPrefix);
2239         if (!NT_SUCCESS(Status))
2240         {
2241             DPRINT1("IopGetParentIdPrefix() failed (Status 0x%08lx)\n", Status);
2242             RtlFreeUnicodeString(&DeviceId);
2243             return Status;
2244         }
2245     }
2246 
2247     DPRINT("Sending IRP_MN_QUERY_ID.BusQueryInstanceID to device stack\n");
2248 
2249     Stack.Parameters.QueryId.IdType = BusQueryInstanceID;
2250     Status = IopInitiatePnpIrp(DeviceNode->PhysicalDeviceObject,
2251                                &IoStatusBlock,
2252                                IRP_MN_QUERY_ID,
2253                                &Stack);
2254     if (!NT_SUCCESS(Status))
2255     {
2256         DPRINT("IopInitiatePnpIrp(BusQueryInstanceID) failed (Status %lx)\n", Status);
2257         ASSERT(IoStatusBlock.Information == 0);
2258     }
2259 
2260     if (IoStatusBlock.Information)
2261     {
2262         IsValidID = IopValidateID((PWCHAR)IoStatusBlock.Information, BusQueryInstanceID);
2263 
2264         if (!IsValidID)
2265         {
2266             DPRINT1("Invalid InstanceID. DeviceNode - %p\n", DeviceNode);
2267         }
2268     }
2269 
2270     RtlInitUnicodeString(&InstanceId,
2271                          (PWSTR)IoStatusBlock.Information);
2272 
2273     InstancePath->Length = 0;
2274     InstancePath->MaximumLength = DeviceId.Length + sizeof(WCHAR) +
2275                                   ParentIdPrefix.Length +
2276                                   InstanceId.Length +
2277                                   sizeof(UNICODE_NULL);
2278     if (ParentIdPrefix.Length && InstanceId.Length)
2279     {
2280         InstancePath->MaximumLength += sizeof(WCHAR);
2281     }
2282 
2283     InstancePath->Buffer = ExAllocatePoolWithTag(PagedPool,
2284                                                  InstancePath->MaximumLength,
2285                                                  TAG_IO);
2286     if (!InstancePath->Buffer)
2287     {
2288         RtlFreeUnicodeString(&InstanceId);
2289         RtlFreeUnicodeString(&ParentIdPrefix);
2290         RtlFreeUnicodeString(&DeviceId);
2291         return STATUS_INSUFFICIENT_RESOURCES;
2292     }
2293 
2294     /* Start with the device id */
2295     RtlCopyUnicodeString(InstancePath, &DeviceId);
2296     RtlAppendUnicodeToString(InstancePath, L"\\");
2297 
2298     /* Add information from parent bus device to InstancePath */
2299     RtlAppendUnicodeStringToString(InstancePath, &ParentIdPrefix);
2300     if (ParentIdPrefix.Length && InstanceId.Length)
2301     {
2302         RtlAppendUnicodeToString(InstancePath, L"&");
2303     }
2304 
2305     /* Finally, add the id returned by the driver stack */
2306     RtlAppendUnicodeStringToString(InstancePath, &InstanceId);
2307 
2308     /*
2309      * FIXME: Check for valid characters, if there is invalid characters
2310      * then bugcheck
2311      */
2312 
2313     RtlFreeUnicodeString(&InstanceId);
2314     RtlFreeUnicodeString(&DeviceId);
2315     RtlFreeUnicodeString(&ParentIdPrefix);
2316 
2317     return STATUS_SUCCESS;
2318 }
2319 
2320 /*
2321  * IopActionInterrogateDeviceStack
2322  *
2323  * Retrieve information for all (direct) child nodes of a parent node.
2324  *
2325  * Parameters
2326  *    DeviceNode
2327  *       Pointer to device node.
2328  *    Context
2329  *       Pointer to parent node to retrieve child node information for.
2330  *
2331  * Remarks
2332  *    Any errors that occur are logged instead so that all child services have a chance
2333  *    of being interrogated.
2334  */
2335 
2336 NTSTATUS
2337 IopActionInterrogateDeviceStack(PDEVICE_NODE DeviceNode,
2338                                 PVOID Context)
2339 {
2340     IO_STATUS_BLOCK IoStatusBlock;
2341     PWSTR DeviceDescription;
2342     PWSTR LocationInformation;
2343     PDEVICE_NODE ParentDeviceNode;
2344     IO_STACK_LOCATION Stack;
2345     NTSTATUS Status;
2346     ULONG RequiredLength;
2347     LCID LocaleId;
2348     HANDLE InstanceKey = NULL;
2349     UNICODE_STRING ValueName;
2350     UNICODE_STRING InstancePathU;
2351     PDEVICE_OBJECT OldDeviceObject;
2352 
2353     DPRINT("IopActionInterrogateDeviceStack(%p, %p)\n", DeviceNode, Context);
2354     DPRINT("PDO 0x%p\n", DeviceNode->PhysicalDeviceObject);
2355 
2356     ParentDeviceNode = (PDEVICE_NODE)Context;
2357 
2358     /*
2359      * We are called for the parent too, but we don't need to do special
2360      * handling for this node
2361      */
2362     if (DeviceNode == ParentDeviceNode)
2363     {
2364         DPRINT("Success\n");
2365         return STATUS_SUCCESS;
2366     }
2367 
2368     /*
2369      * Make sure this device node is a direct child of the parent device node
2370      * that is given as an argument
2371      */
2372     if (DeviceNode->Parent != ParentDeviceNode)
2373     {
2374         DPRINT("Skipping 2+ level child\n");
2375         return STATUS_SUCCESS;
2376     }
2377 
2378     /* Skip processing if it was already completed before */
2379     if (DeviceNode->Flags & DNF_PROCESSED)
2380     {
2381         /* Nothing to do */
2382         return STATUS_SUCCESS;
2383     }
2384 
2385     /* Get Locale ID */
2386     Status = ZwQueryDefaultLocale(FALSE, &LocaleId);
2387     if (!NT_SUCCESS(Status))
2388     {
2389         DPRINT1("ZwQueryDefaultLocale() failed with status 0x%lx\n", Status);
2390         return Status;
2391     }
2392 
2393     /*
2394      * FIXME: For critical errors, cleanup and disable device, but always
2395      * return STATUS_SUCCESS.
2396      */
2397 
2398     Status = IopCreateDeviceInstancePath(DeviceNode, &InstancePathU);
2399     if (!NT_SUCCESS(Status))
2400     {
2401         if (Status != STATUS_PLUGPLAY_NO_DEVICE)
2402         {
2403             DPRINT1("IopCreateDeviceInstancePath() failed with status 0x%lx\n", Status);
2404         }
2405 
2406         /* We have to return success otherwise we abort the traverse operation */
2407         return STATUS_SUCCESS;
2408     }
2409 
2410     /* Verify that this is not a duplicate */
2411     OldDeviceObject = IopGetDeviceObjectFromDeviceInstance(&InstancePathU);
2412     if (OldDeviceObject != NULL)
2413     {
2414         PDEVICE_NODE OldDeviceNode = IopGetDeviceNode(OldDeviceObject);
2415 
2416         DPRINT1("Duplicate device instance '%wZ'\n", &InstancePathU);
2417         DPRINT1("Current instance parent: '%wZ'\n", &DeviceNode->Parent->InstancePath);
2418         DPRINT1("Old instance parent: '%wZ'\n", &OldDeviceNode->Parent->InstancePath);
2419 
2420         KeBugCheckEx(PNP_DETECTED_FATAL_ERROR,
2421                      0x01,
2422                      (ULONG_PTR)DeviceNode->PhysicalDeviceObject,
2423                      (ULONG_PTR)OldDeviceObject,
2424                      0);
2425     }
2426 
2427     DeviceNode->InstancePath = InstancePathU;
2428 
2429     DPRINT("InstancePath is %S\n", DeviceNode->InstancePath.Buffer);
2430 
2431     /*
2432      * Create registry key for the instance id, if it doesn't exist yet
2433      */
2434     Status = IopCreateDeviceKeyPath(&DeviceNode->InstancePath, REG_OPTION_NON_VOLATILE, &InstanceKey);
2435     if (!NT_SUCCESS(Status))
2436     {
2437         DPRINT1("Failed to create the instance key! (Status %lx)\n", Status);
2438 
2439         /* We have to return success otherwise we abort the traverse operation */
2440         return STATUS_SUCCESS;
2441     }
2442 
2443     IopQueryHardwareIds(DeviceNode, InstanceKey);
2444 
2445     IopQueryCompatibleIds(DeviceNode, InstanceKey);
2446 
2447     DPRINT("Sending IRP_MN_QUERY_DEVICE_TEXT.DeviceTextDescription to device stack\n");
2448 
2449     Stack.Parameters.QueryDeviceText.DeviceTextType = DeviceTextDescription;
2450     Stack.Parameters.QueryDeviceText.LocaleId = LocaleId;
2451     Status = IopInitiatePnpIrp(DeviceNode->PhysicalDeviceObject,
2452                                &IoStatusBlock,
2453                                IRP_MN_QUERY_DEVICE_TEXT,
2454                                &Stack);
2455     DeviceDescription = NT_SUCCESS(Status) ? (PWSTR)IoStatusBlock.Information
2456                                            : NULL;
2457     /* This key is mandatory, so even if the Irp fails, we still write it */
2458     RtlInitUnicodeString(&ValueName, L"DeviceDesc");
2459     if (ZwQueryValueKey(InstanceKey, &ValueName, KeyValueBasicInformation, NULL, 0, &RequiredLength) == STATUS_OBJECT_NAME_NOT_FOUND)
2460     {
2461         if (DeviceDescription &&
2462             *DeviceDescription != UNICODE_NULL)
2463         {
2464             /* This key is overriden when a driver is installed. Don't write the
2465              * new description if another one already exists */
2466             Status = ZwSetValueKey(InstanceKey,
2467                                    &ValueName,
2468                                    0,
2469                                    REG_SZ,
2470                                    DeviceDescription,
2471                                    ((ULONG)wcslen(DeviceDescription) + 1) * sizeof(WCHAR));
2472         }
2473         else
2474         {
2475             UNICODE_STRING DeviceDesc = RTL_CONSTANT_STRING(L"Unknown device");
2476             DPRINT("Driver didn't return DeviceDesc (Status 0x%08lx), so place unknown device there\n", Status);
2477 
2478             Status = ZwSetValueKey(InstanceKey,
2479                                    &ValueName,
2480                                    0,
2481                                    REG_SZ,
2482                                    DeviceDesc.Buffer,
2483                                    DeviceDesc.MaximumLength);
2484             if (!NT_SUCCESS(Status))
2485             {
2486                 DPRINT1("ZwSetValueKey() failed (Status 0x%lx)\n", Status);
2487             }
2488 
2489         }
2490     }
2491 
2492     if (DeviceDescription)
2493     {
2494         ExFreePoolWithTag(DeviceDescription, 0);
2495     }
2496 
2497     DPRINT("Sending IRP_MN_QUERY_DEVICE_TEXT.DeviceTextLocation to device stack\n");
2498 
2499     Stack.Parameters.QueryDeviceText.DeviceTextType = DeviceTextLocationInformation;
2500     Stack.Parameters.QueryDeviceText.LocaleId = LocaleId;
2501     Status = IopInitiatePnpIrp(DeviceNode->PhysicalDeviceObject,
2502                                &IoStatusBlock,
2503                                IRP_MN_QUERY_DEVICE_TEXT,
2504                                &Stack);
2505     if (NT_SUCCESS(Status) && IoStatusBlock.Information)
2506     {
2507         LocationInformation = (PWSTR)IoStatusBlock.Information;
2508         DPRINT("LocationInformation: %S\n", LocationInformation);
2509         RtlInitUnicodeString(&ValueName, L"LocationInformation");
2510         Status = ZwSetValueKey(InstanceKey,
2511                                &ValueName,
2512                                0,
2513                                REG_SZ,
2514                                LocationInformation,
2515                                ((ULONG)wcslen(LocationInformation) + 1) * sizeof(WCHAR));
2516         if (!NT_SUCCESS(Status))
2517         {
2518             DPRINT1("ZwSetValueKey() failed (Status %lx)\n", Status);
2519         }
2520 
2521         ExFreePoolWithTag(LocationInformation, 0);
2522     }
2523     else
2524     {
2525         DPRINT("IopInitiatePnpIrp() failed (Status %x) or IoStatusBlock.Information=NULL\n", Status);
2526     }
2527 
2528     DPRINT("Sending IRP_MN_QUERY_BUS_INFORMATION to device stack\n");
2529 
2530     Status = IopInitiatePnpIrp(DeviceNode->PhysicalDeviceObject,
2531                                &IoStatusBlock,
2532                                IRP_MN_QUERY_BUS_INFORMATION,
2533                                NULL);
2534     if (NT_SUCCESS(Status) && IoStatusBlock.Information)
2535     {
2536         PPNP_BUS_INFORMATION BusInformation = (PPNP_BUS_INFORMATION)IoStatusBlock.Information;
2537 
2538         DeviceNode->ChildBusNumber = BusInformation->BusNumber;
2539         DeviceNode->ChildInterfaceType = BusInformation->LegacyBusType;
2540         DeviceNode->ChildBusTypeIndex = IopGetBusTypeGuidIndex(&BusInformation->BusTypeGuid);
2541         ExFreePoolWithTag(BusInformation, 0);
2542     }
2543     else
2544     {
2545         DPRINT("IopInitiatePnpIrp() failed (Status %x) or IoStatusBlock.Information=NULL\n", Status);
2546 
2547         DeviceNode->ChildBusNumber = 0xFFFFFFF0;
2548         DeviceNode->ChildInterfaceType = InterfaceTypeUndefined;
2549         DeviceNode->ChildBusTypeIndex = -1;
2550     }
2551 
2552     DPRINT("Sending IRP_MN_QUERY_RESOURCES to device stack\n");
2553 
2554     Status = IopInitiatePnpIrp(DeviceNode->PhysicalDeviceObject,
2555                                &IoStatusBlock,
2556                                IRP_MN_QUERY_RESOURCES,
2557                                NULL);
2558     if (NT_SUCCESS(Status) && IoStatusBlock.Information)
2559     {
2560         DeviceNode->BootResources = (PCM_RESOURCE_LIST)IoStatusBlock.Information;
2561         IopDeviceNodeSetFlag(DeviceNode, DNF_HAS_BOOT_CONFIG);
2562     }
2563     else
2564     {
2565         DPRINT("IopInitiatePnpIrp() failed (Status %x) or IoStatusBlock.Information=NULL\n", Status);
2566         DeviceNode->BootResources = NULL;
2567     }
2568 
2569     DPRINT("Sending IRP_MN_QUERY_RESOURCE_REQUIREMENTS to device stack\n");
2570 
2571     Status = IopInitiatePnpIrp(DeviceNode->PhysicalDeviceObject,
2572                                &IoStatusBlock,
2573                                IRP_MN_QUERY_RESOURCE_REQUIREMENTS,
2574                                NULL);
2575     if (NT_SUCCESS(Status))
2576     {
2577         DeviceNode->ResourceRequirements = (PIO_RESOURCE_REQUIREMENTS_LIST)IoStatusBlock.Information;
2578     }
2579     else
2580     {
2581         DPRINT("IopInitiatePnpIrp() failed (Status %08lx)\n", Status);
2582         DeviceNode->ResourceRequirements = NULL;
2583     }
2584 
2585     if (InstanceKey != NULL)
2586     {
2587         IopSetDeviceInstanceData(InstanceKey, DeviceNode);
2588     }
2589 
2590     ZwClose(InstanceKey);
2591 
2592     IopDeviceNodeSetFlag(DeviceNode, DNF_PROCESSED);
2593 
2594     if (!IopDeviceNodeHasFlag(DeviceNode, DNF_LEGACY_DRIVER))
2595     {
2596         /* Report the device to the user-mode pnp manager */
2597         IopQueueTargetDeviceEvent(&GUID_DEVICE_ENUMERATED,
2598                                   &DeviceNode->InstancePath);
2599     }
2600 
2601     return STATUS_SUCCESS;
2602 }
2603 
2604 static
2605 VOID
2606 IopHandleDeviceRemoval(
2607     IN PDEVICE_NODE DeviceNode,
2608     IN PDEVICE_RELATIONS DeviceRelations)
2609 {
2610     PDEVICE_NODE Child = DeviceNode->Child, NextChild;
2611     ULONG i;
2612     BOOLEAN Found;
2613 
2614     if (DeviceNode == IopRootDeviceNode)
2615         return;
2616 
2617     while (Child != NULL)
2618     {
2619         NextChild = Child->Sibling;
2620         Found = FALSE;
2621 
2622         for (i = 0; DeviceRelations && i < DeviceRelations->Count; i++)
2623         {
2624             if (IopGetDeviceNode(DeviceRelations->Objects[i]) == Child)
2625             {
2626                 Found = TRUE;
2627                 break;
2628             }
2629         }
2630 
2631         if (!Found && !(Child->Flags & DNF_WILL_BE_REMOVED))
2632         {
2633             /* Send removal IRPs to all of its children */
2634             IopPrepareDeviceForRemoval(Child->PhysicalDeviceObject, TRUE);
2635 
2636             /* Send the surprise removal IRP */
2637             IopSendSurpriseRemoval(Child->PhysicalDeviceObject);
2638 
2639             /* Tell the user-mode PnP manager that a device was removed */
2640             IopQueueTargetDeviceEvent(&GUID_DEVICE_SURPRISE_REMOVAL,
2641                                       &Child->InstancePath);
2642 
2643             /* Send the remove device IRP */
2644             IopSendRemoveDevice(Child->PhysicalDeviceObject);
2645         }
2646 
2647         Child = NextChild;
2648     }
2649 }
2650 
2651 NTSTATUS
2652 IopEnumerateDevice(
2653     IN PDEVICE_OBJECT DeviceObject)
2654 {
2655     PDEVICE_NODE DeviceNode = IopGetDeviceNode(DeviceObject);
2656     DEVICETREE_TRAVERSE_CONTEXT Context;
2657     PDEVICE_RELATIONS DeviceRelations;
2658     PDEVICE_OBJECT ChildDeviceObject;
2659     IO_STATUS_BLOCK IoStatusBlock;
2660     PDEVICE_NODE ChildDeviceNode;
2661     IO_STACK_LOCATION Stack;
2662     NTSTATUS Status;
2663     ULONG i;
2664 
2665     DPRINT("DeviceObject 0x%p\n", DeviceObject);
2666 
2667     if (DeviceNode->Flags & DNF_NEED_ENUMERATION_ONLY)
2668     {
2669         DeviceNode->Flags &= ~DNF_NEED_ENUMERATION_ONLY;
2670 
2671         DPRINT("Sending GUID_DEVICE_ARRIVAL\n");
2672         IopQueueTargetDeviceEvent(&GUID_DEVICE_ARRIVAL,
2673                                   &DeviceNode->InstancePath);
2674     }
2675 
2676     DPRINT("Sending IRP_MN_QUERY_DEVICE_RELATIONS to device stack\n");
2677 
2678     Stack.Parameters.QueryDeviceRelations.Type = BusRelations;
2679 
2680     Status = IopInitiatePnpIrp(
2681         DeviceObject,
2682         &IoStatusBlock,
2683         IRP_MN_QUERY_DEVICE_RELATIONS,
2684         &Stack);
2685     if (!NT_SUCCESS(Status) || Status == STATUS_PENDING)
2686     {
2687         DPRINT("IopInitiatePnpIrp() failed with status 0x%08lx\n", Status);
2688         return Status;
2689     }
2690 
2691     DeviceRelations = (PDEVICE_RELATIONS)IoStatusBlock.Information;
2692 
2693     /*
2694      * Send removal IRPs for devices that have disappeared
2695      * NOTE: This code handles the case where no relations are specified
2696      */
2697     IopHandleDeviceRemoval(DeviceNode, DeviceRelations);
2698 
2699     /* Now we bail if nothing was returned */
2700     if (!DeviceRelations)
2701     {
2702         /* We're all done */
2703         DPRINT("No PDOs\n");
2704         return STATUS_SUCCESS;
2705     }
2706 
2707     DPRINT("Got %u PDOs\n", DeviceRelations->Count);
2708 
2709     /*
2710      * Create device nodes for all discovered devices
2711      */
2712     for (i = 0; i < DeviceRelations->Count; i++)
2713     {
2714         ChildDeviceObject = DeviceRelations->Objects[i];
2715         ASSERT((ChildDeviceObject->Flags & DO_DEVICE_INITIALIZING) == 0);
2716 
2717         ChildDeviceNode = IopGetDeviceNode(ChildDeviceObject);
2718         if (!ChildDeviceNode)
2719         {
2720             /* One doesn't exist, create it */
2721             Status = IopCreateDeviceNode(
2722                 DeviceNode,
2723                 ChildDeviceObject,
2724                 NULL,
2725                 &ChildDeviceNode);
2726             if (NT_SUCCESS(Status))
2727             {
2728                 /* Mark the node as enumerated */
2729                 ChildDeviceNode->Flags |= DNF_ENUMERATED;
2730 
2731                 /* Mark the DO as bus enumerated */
2732                 ChildDeviceObject->Flags |= DO_BUS_ENUMERATED_DEVICE;
2733             }
2734             else
2735             {
2736                 /* Ignore this DO */
2737                 DPRINT1("IopCreateDeviceNode() failed with status 0x%08x. Skipping PDO %u\n", Status, i);
2738                 ObDereferenceObject(ChildDeviceObject);
2739             }
2740         }
2741         else
2742         {
2743             /* Mark it as enumerated */
2744             ChildDeviceNode->Flags |= DNF_ENUMERATED;
2745             ObDereferenceObject(ChildDeviceObject);
2746         }
2747     }
2748     ExFreePool(DeviceRelations);
2749 
2750     /*
2751      * Retrieve information about all discovered children from the bus driver
2752      */
2753     IopInitDeviceTreeTraverseContext(
2754         &Context,
2755         DeviceNode,
2756         IopActionInterrogateDeviceStack,
2757         DeviceNode);
2758 
2759     Status = IopTraverseDeviceTree(&Context);
2760     if (!NT_SUCCESS(Status))
2761     {
2762         DPRINT("IopTraverseDeviceTree() failed with status 0x%08lx\n", Status);
2763         return Status;
2764     }
2765 
2766     /*
2767      * Retrieve configuration from the registry for discovered children
2768      */
2769     IopInitDeviceTreeTraverseContext(
2770         &Context,
2771         DeviceNode,
2772         IopActionConfigureChildServices,
2773         DeviceNode);
2774 
2775     Status = IopTraverseDeviceTree(&Context);
2776     if (!NT_SUCCESS(Status))
2777     {
2778         DPRINT("IopTraverseDeviceTree() failed with status 0x%08lx\n", Status);
2779         return Status;
2780     }
2781 
2782     /*
2783      * Initialize services for discovered children.
2784      */
2785     Status = IopInitializePnpServices(DeviceNode);
2786     if (!NT_SUCCESS(Status))
2787     {
2788         DPRINT("IopInitializePnpServices() failed with status 0x%08lx\n", Status);
2789         return Status;
2790     }
2791 
2792     DPRINT("IopEnumerateDevice() finished\n");
2793     return STATUS_SUCCESS;
2794 }
2795 
2796 
2797 /*
2798  * IopActionConfigureChildServices
2799  *
2800  * Retrieve configuration for all (direct) child nodes of a parent node.
2801  *
2802  * Parameters
2803  *    DeviceNode
2804  *       Pointer to device node.
2805  *    Context
2806  *       Pointer to parent node to retrieve child node configuration for.
2807  *
2808  * Remarks
2809  *    Any errors that occur are logged instead so that all child services have a chance of beeing
2810  *    configured.
2811  */
2812 
2813 NTSTATUS
2814 IopActionConfigureChildServices(PDEVICE_NODE DeviceNode,
2815                                 PVOID Context)
2816 {
2817    RTL_QUERY_REGISTRY_TABLE QueryTable[3];
2818    PDEVICE_NODE ParentDeviceNode;
2819    PUNICODE_STRING Service;
2820    UNICODE_STRING ClassGUID;
2821    NTSTATUS Status;
2822    DEVICE_CAPABILITIES DeviceCaps;
2823 
2824    DPRINT("IopActionConfigureChildServices(%p, %p)\n", DeviceNode, Context);
2825 
2826    ParentDeviceNode = (PDEVICE_NODE)Context;
2827 
2828    /*
2829     * We are called for the parent too, but we don't need to do special
2830     * handling for this node
2831     */
2832    if (DeviceNode == ParentDeviceNode)
2833    {
2834       DPRINT("Success\n");
2835       return STATUS_SUCCESS;
2836    }
2837 
2838    /*
2839     * Make sure this device node is a direct child of the parent device node
2840     * that is given as an argument
2841     */
2842 
2843    if (DeviceNode->Parent != ParentDeviceNode)
2844    {
2845       DPRINT("Skipping 2+ level child\n");
2846       return STATUS_SUCCESS;
2847    }
2848 
2849    if (!(DeviceNode->Flags & DNF_PROCESSED))
2850    {
2851        DPRINT1("Child not ready to be configured\n");
2852        return STATUS_SUCCESS;
2853    }
2854 
2855    if (!(DeviceNode->Flags & (DNF_DISABLED | DNF_STARTED | DNF_ADDED)))
2856    {
2857       UNICODE_STRING RegKey;
2858 
2859       /* Install the service for this if it's in the CDDB */
2860       IopInstallCriticalDevice(DeviceNode);
2861 
2862       /*
2863        * Retrieve configuration from Enum key
2864        */
2865 
2866       Service = &DeviceNode->ServiceName;
2867 
2868       RtlZeroMemory(QueryTable, sizeof(QueryTable));
2869       RtlInitUnicodeString(Service, NULL);
2870       RtlInitUnicodeString(&ClassGUID, NULL);
2871 
2872       QueryTable[0].Name = L"Service";
2873       QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
2874       QueryTable[0].EntryContext = Service;
2875 
2876       QueryTable[1].Name = L"ClassGUID";
2877       QueryTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT;
2878       QueryTable[1].EntryContext = &ClassGUID;
2879       QueryTable[1].DefaultType = REG_SZ;
2880       QueryTable[1].DefaultData = L"";
2881       QueryTable[1].DefaultLength = 0;
2882 
2883       RegKey.Length = 0;
2884       RegKey.MaximumLength = sizeof(ENUM_ROOT) + sizeof(WCHAR) + DeviceNode->InstancePath.Length;
2885       RegKey.Buffer = ExAllocatePoolWithTag(PagedPool,
2886                                             RegKey.MaximumLength,
2887                                             TAG_IO);
2888       if (RegKey.Buffer == NULL)
2889       {
2890           IopDeviceNodeSetFlag(DeviceNode, DNF_DISABLED);
2891           return STATUS_INSUFFICIENT_RESOURCES;
2892       }
2893 
2894       RtlAppendUnicodeToString(&RegKey, ENUM_ROOT);
2895       RtlAppendUnicodeToString(&RegKey, L"\\");
2896       RtlAppendUnicodeStringToString(&RegKey, &DeviceNode->InstancePath);
2897 
2898       Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
2899          RegKey.Buffer, QueryTable, NULL, NULL);
2900       ExFreePoolWithTag(RegKey.Buffer, TAG_IO);
2901 
2902       if (!NT_SUCCESS(Status))
2903       {
2904          /* FIXME: Log the error */
2905          DPRINT("Could not retrieve configuration for device %wZ (Status 0x%08x)\n",
2906             &DeviceNode->InstancePath, Status);
2907          IopDeviceNodeSetFlag(DeviceNode, DNF_DISABLED);
2908          return STATUS_SUCCESS;
2909       }
2910 
2911       if (Service->Buffer == NULL)
2912       {
2913          if (NT_SUCCESS(IopQueryDeviceCapabilities(DeviceNode, &DeviceCaps)) &&
2914              DeviceCaps.RawDeviceOK)
2915          {
2916             DPRINT("%wZ is using parent bus driver (%wZ)\n", &DeviceNode->InstancePath, &ParentDeviceNode->ServiceName);
2917             RtlInitEmptyUnicodeString(&DeviceNode->ServiceName, NULL, 0);
2918          }
2919          else if (ClassGUID.Length != 0)
2920          {
2921             /* Device has a ClassGUID value, but no Service value.
2922              * Suppose it is using the NULL driver, so state the
2923              * device is started */
2924             DPRINT("%wZ is using NULL driver\n", &DeviceNode->InstancePath);
2925             IopDeviceNodeSetFlag(DeviceNode, DNF_STARTED);
2926          }
2927          else
2928          {
2929             DeviceNode->Problem = CM_PROB_FAILED_INSTALL;
2930             IopDeviceNodeSetFlag(DeviceNode, DNF_DISABLED);
2931          }
2932          return STATUS_SUCCESS;
2933       }
2934 
2935       DPRINT("Got Service %S\n", Service->Buffer);
2936    }
2937 
2938    return STATUS_SUCCESS;
2939 }
2940 
2941 /*
2942  * IopActionInitChildServices
2943  *
2944  * Initialize the service for all (direct) child nodes of a parent node
2945  *
2946  * Parameters
2947  *    DeviceNode
2948  *       Pointer to device node.
2949  *    Context
2950  *       Pointer to parent node to initialize child node services for.
2951  *
2952  * Remarks
2953  *    If the driver image for a service is not loaded and initialized
2954  *    it is done here too. Any errors that occur are logged instead so
2955  *    that all child services have a chance of being initialized.
2956  */
2957 
2958 NTSTATUS
2959 IopActionInitChildServices(PDEVICE_NODE DeviceNode,
2960                            PVOID Context)
2961 {
2962    PDEVICE_NODE ParentDeviceNode;
2963    NTSTATUS Status;
2964    BOOLEAN BootDrivers = !PnpSystemInit;
2965 
2966    DPRINT("IopActionInitChildServices(%p, %p)\n", DeviceNode, Context);
2967 
2968    ParentDeviceNode = Context;
2969 
2970    /*
2971     * We are called for the parent too, but we don't need to do special
2972     * handling for this node
2973     */
2974    if (DeviceNode == ParentDeviceNode)
2975    {
2976       DPRINT("Success\n");
2977       return STATUS_SUCCESS;
2978    }
2979 
2980    /*
2981     * We don't want to check for a direct child because
2982     * this function is called during boot to reinitialize
2983     * devices with drivers that couldn't load yet due to
2984     * stage 0 limitations (ie can't load from disk yet).
2985     */
2986 
2987    if (!(DeviceNode->Flags & DNF_PROCESSED))
2988    {
2989        DPRINT1("Child not ready to be added\n");
2990        return STATUS_SUCCESS;
2991    }
2992 
2993    if (IopDeviceNodeHasFlag(DeviceNode, DNF_STARTED) ||
2994        IopDeviceNodeHasFlag(DeviceNode, DNF_ADDED) ||
2995        IopDeviceNodeHasFlag(DeviceNode, DNF_DISABLED))
2996        return STATUS_SUCCESS;
2997 
2998    if (DeviceNode->ServiceName.Buffer == NULL)
2999    {
3000       /* We don't need to worry about loading the driver because we're
3001        * being driven in raw mode so our parent must be loaded to get here */
3002       Status = IopInitializeDevice(DeviceNode, NULL);
3003       if (NT_SUCCESS(Status))
3004       {
3005           Status = IopStartDevice(DeviceNode);
3006           if (!NT_SUCCESS(Status))
3007           {
3008               DPRINT1("IopStartDevice(%wZ) failed with status 0x%08x\n",
3009                       &DeviceNode->InstancePath, Status);
3010           }
3011       }
3012    }
3013    else
3014    {
3015       PLDR_DATA_TABLE_ENTRY ModuleObject;
3016       PDRIVER_OBJECT DriverObject;
3017 
3018       KeEnterCriticalRegion();
3019       ExAcquireResourceExclusiveLite(&IopDriverLoadResource, TRUE);
3020       /* Get existing DriverObject pointer (in case the driver has
3021          already been loaded and initialized) */
3022       Status = IopGetDriverObject(
3023           &DriverObject,
3024           &DeviceNode->ServiceName,
3025           FALSE);
3026 
3027       if (!NT_SUCCESS(Status))
3028       {
3029          /* Driver is not initialized, try to load it */
3030          Status = IopLoadServiceModule(&DeviceNode->ServiceName, &ModuleObject);
3031 
3032          if (NT_SUCCESS(Status) || Status == STATUS_IMAGE_ALREADY_LOADED)
3033          {
3034             /* Initialize the driver */
3035             Status = IopInitializeDriverModule(DeviceNode, ModuleObject,
3036                   &DeviceNode->ServiceName, FALSE, &DriverObject);
3037             if (!NT_SUCCESS(Status)) DeviceNode->Problem = CM_PROB_FAILED_DRIVER_ENTRY;
3038          }
3039          else if (Status == STATUS_DRIVER_UNABLE_TO_LOAD)
3040          {
3041             DPRINT1("Service '%wZ' is disabled\n", &DeviceNode->ServiceName);
3042             DeviceNode->Problem = CM_PROB_DISABLED_SERVICE;
3043          }
3044          else
3045          {
3046             DPRINT("IopLoadServiceModule(%wZ) failed with status 0x%08x\n",
3047                     &DeviceNode->ServiceName, Status);
3048             if (!BootDrivers) DeviceNode->Problem = CM_PROB_DRIVER_FAILED_LOAD;
3049          }
3050       }
3051       ExReleaseResourceLite(&IopDriverLoadResource);
3052       KeLeaveCriticalRegion();
3053 
3054       /* Driver is loaded and initialized at this point */
3055       if (NT_SUCCESS(Status))
3056       {
3057           /* Initialize the device, including all filters */
3058           Status = PipCallDriverAddDevice(DeviceNode, FALSE, DriverObject);
3059 
3060           /* Remove the extra reference */
3061           ObDereferenceObject(DriverObject);
3062       }
3063       else
3064       {
3065          /*
3066           * Don't disable when trying to load only boot drivers
3067           */
3068          if (!BootDrivers)
3069          {
3070             IopDeviceNodeSetFlag(DeviceNode, DNF_DISABLED);
3071          }
3072       }
3073    }
3074 
3075    return STATUS_SUCCESS;
3076 }
3077 
3078 /*
3079  * IopInitializePnpServices
3080  *
3081  * Initialize services for discovered children
3082  *
3083  * Parameters
3084  *    DeviceNode
3085  *       Top device node to start initializing services.
3086  *
3087  * Return Value
3088  *    Status
3089  */
3090 NTSTATUS
3091 IopInitializePnpServices(IN PDEVICE_NODE DeviceNode)
3092 {
3093    DEVICETREE_TRAVERSE_CONTEXT Context;
3094 
3095    DPRINT("IopInitializePnpServices(%p)\n", DeviceNode);
3096 
3097    IopInitDeviceTreeTraverseContext(
3098       &Context,
3099       DeviceNode,
3100       IopActionInitChildServices,
3101       DeviceNode);
3102 
3103    return IopTraverseDeviceTree(&Context);
3104 }
3105 
3106 static
3107 INIT_FUNCTION
3108 NTSTATUS
3109 IopEnumerateDetectedDevices(
3110    IN HANDLE hBaseKey,
3111    IN PUNICODE_STRING RelativePath OPTIONAL,
3112    IN HANDLE hRootKey,
3113    IN BOOLEAN EnumerateSubKeys,
3114    IN PCM_FULL_RESOURCE_DESCRIPTOR ParentBootResources,
3115    IN ULONG ParentBootResourcesLength)
3116 {
3117    UNICODE_STRING IdentifierU = RTL_CONSTANT_STRING(L"Identifier");
3118    UNICODE_STRING HardwareIDU = RTL_CONSTANT_STRING(L"HardwareID");
3119    UNICODE_STRING ConfigurationDataU = RTL_CONSTANT_STRING(L"Configuration Data");
3120    UNICODE_STRING BootConfigU = RTL_CONSTANT_STRING(L"BootConfig");
3121    UNICODE_STRING LogConfU = RTL_CONSTANT_STRING(L"LogConf");
3122    OBJECT_ATTRIBUTES ObjectAttributes;
3123    HANDLE hDevicesKey = NULL;
3124    HANDLE hDeviceKey = NULL;
3125    HANDLE hLevel1Key, hLevel2Key = NULL, hLogConf;
3126    UNICODE_STRING Level2NameU;
3127    WCHAR Level2Name[5];
3128    ULONG IndexDevice = 0;
3129    ULONG IndexSubKey;
3130    PKEY_BASIC_INFORMATION pDeviceInformation = NULL;
3131    ULONG DeviceInfoLength = sizeof(KEY_BASIC_INFORMATION) + 50 * sizeof(WCHAR);
3132    PKEY_VALUE_PARTIAL_INFORMATION pValueInformation = NULL;
3133    ULONG ValueInfoLength = sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 50 * sizeof(WCHAR);
3134    UNICODE_STRING DeviceName, ValueName;
3135    ULONG RequiredSize;
3136    PCM_FULL_RESOURCE_DESCRIPTOR BootResources = NULL;
3137    ULONG BootResourcesLength;
3138    NTSTATUS Status;
3139 
3140    const UNICODE_STRING IdentifierSerial = RTL_CONSTANT_STRING(L"SerialController");
3141    UNICODE_STRING HardwareIdSerial = RTL_CONSTANT_STRING(L"*PNP0501\0");
3142    static ULONG DeviceIndexSerial = 0;
3143    const UNICODE_STRING IdentifierKeyboard = RTL_CONSTANT_STRING(L"KeyboardController");
3144    UNICODE_STRING HardwareIdKeyboard = RTL_CONSTANT_STRING(L"*PNP0303\0");
3145    static ULONG DeviceIndexKeyboard = 0;
3146    const UNICODE_STRING IdentifierMouse = RTL_CONSTANT_STRING(L"PointerController");
3147    UNICODE_STRING HardwareIdMouse = RTL_CONSTANT_STRING(L"*PNP0F13\0");
3148    static ULONG DeviceIndexMouse = 0;
3149    const UNICODE_STRING IdentifierParallel = RTL_CONSTANT_STRING(L"ParallelController");
3150    UNICODE_STRING HardwareIdParallel = RTL_CONSTANT_STRING(L"*PNP0400\0");
3151    static ULONG DeviceIndexParallel = 0;
3152    const UNICODE_STRING IdentifierFloppy = RTL_CONSTANT_STRING(L"FloppyDiskPeripheral");
3153    UNICODE_STRING HardwareIdFloppy = RTL_CONSTANT_STRING(L"*PNP0700\0");
3154    static ULONG DeviceIndexFloppy = 0;
3155    UNICODE_STRING HardwareIdKey;
3156    PUNICODE_STRING pHardwareId;
3157    ULONG DeviceIndex = 0;
3158    PUCHAR CmResourceList;
3159    ULONG ListCount;
3160 
3161     if (RelativePath)
3162     {
3163         Status = IopOpenRegistryKeyEx(&hDevicesKey, hBaseKey, RelativePath, KEY_ENUMERATE_SUB_KEYS);
3164         if (!NT_SUCCESS(Status))
3165         {
3166             DPRINT("ZwOpenKey() failed with status 0x%08lx\n", Status);
3167             goto cleanup;
3168         }
3169     }
3170     else
3171         hDevicesKey = hBaseKey;
3172 
3173    pDeviceInformation = ExAllocatePool(PagedPool, DeviceInfoLength);
3174    if (!pDeviceInformation)
3175    {
3176       DPRINT("ExAllocatePool() failed\n");
3177       Status = STATUS_NO_MEMORY;
3178       goto cleanup;
3179    }
3180 
3181    pValueInformation = ExAllocatePool(PagedPool, ValueInfoLength);
3182    if (!pValueInformation)
3183    {
3184       DPRINT("ExAllocatePool() failed\n");
3185       Status = STATUS_NO_MEMORY;
3186       goto cleanup;
3187    }
3188 
3189    while (TRUE)
3190    {
3191       Status = ZwEnumerateKey(hDevicesKey, IndexDevice, KeyBasicInformation, pDeviceInformation, DeviceInfoLength, &RequiredSize);
3192       if (Status == STATUS_NO_MORE_ENTRIES)
3193          break;
3194       else if (Status == STATUS_BUFFER_OVERFLOW || Status == STATUS_BUFFER_TOO_SMALL)
3195       {
3196          ExFreePool(pDeviceInformation);
3197          DeviceInfoLength = RequiredSize;
3198          pDeviceInformation = ExAllocatePool(PagedPool, DeviceInfoLength);
3199          if (!pDeviceInformation)
3200          {
3201             DPRINT("ExAllocatePool() failed\n");
3202             Status = STATUS_NO_MEMORY;
3203             goto cleanup;
3204          }
3205          Status = ZwEnumerateKey(hDevicesKey, IndexDevice, KeyBasicInformation, pDeviceInformation, DeviceInfoLength, &RequiredSize);
3206       }
3207       if (!NT_SUCCESS(Status))
3208       {
3209          DPRINT("ZwEnumerateKey() failed with status 0x%08lx\n", Status);
3210          goto cleanup;
3211       }
3212       IndexDevice++;
3213 
3214       /* Open device key */
3215       DeviceName.Length = DeviceName.MaximumLength = (USHORT)pDeviceInformation->NameLength;
3216       DeviceName.Buffer = pDeviceInformation->Name;
3217 
3218       Status = IopOpenRegistryKeyEx(&hDeviceKey, hDevicesKey, &DeviceName,
3219           KEY_QUERY_VALUE + (EnumerateSubKeys ? KEY_ENUMERATE_SUB_KEYS : 0));
3220       if (!NT_SUCCESS(Status))
3221       {
3222          DPRINT("ZwOpenKey() failed with status 0x%08lx\n", Status);
3223          goto cleanup;
3224       }
3225 
3226       /* Read boot resources, and add then to parent ones */
3227       Status = ZwQueryValueKey(hDeviceKey, &ConfigurationDataU, KeyValuePartialInformation, pValueInformation, ValueInfoLength, &RequiredSize);
3228       if (Status == STATUS_BUFFER_OVERFLOW || Status == STATUS_BUFFER_TOO_SMALL)
3229       {
3230          ExFreePool(pValueInformation);
3231          ValueInfoLength = RequiredSize;
3232          pValueInformation = ExAllocatePool(PagedPool, ValueInfoLength);
3233          if (!pValueInformation)
3234          {
3235             DPRINT("ExAllocatePool() failed\n");
3236             ZwDeleteKey(hLevel2Key);
3237             Status = STATUS_NO_MEMORY;
3238             goto cleanup;
3239          }
3240          Status = ZwQueryValueKey(hDeviceKey, &ConfigurationDataU, KeyValuePartialInformation, pValueInformation, ValueInfoLength, &RequiredSize);
3241       }
3242       if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
3243       {
3244          BootResources = ParentBootResources;
3245          BootResourcesLength = ParentBootResourcesLength;
3246       }
3247       else if (!NT_SUCCESS(Status))
3248       {
3249          DPRINT("ZwQueryValueKey() failed with status 0x%08lx\n", Status);
3250          goto nextdevice;
3251       }
3252       else if (pValueInformation->Type != REG_FULL_RESOURCE_DESCRIPTOR)
3253       {
3254          DPRINT("Wrong registry type: got 0x%lx, expected 0x%lx\n", pValueInformation->Type, REG_FULL_RESOURCE_DESCRIPTOR);
3255          goto nextdevice;
3256       }
3257       else
3258       {
3259          static const ULONG Header = FIELD_OFFSET(CM_FULL_RESOURCE_DESCRIPTOR, PartialResourceList.PartialDescriptors);
3260 
3261          /* Concatenate current resources and parent ones */
3262          if (ParentBootResourcesLength == 0)
3263             BootResourcesLength = pValueInformation->DataLength;
3264          else
3265             BootResourcesLength = ParentBootResourcesLength
3266             + pValueInformation->DataLength
3267                - Header;
3268          BootResources = ExAllocatePool(PagedPool, BootResourcesLength);
3269          if (!BootResources)
3270          {
3271             DPRINT("ExAllocatePool() failed\n");
3272             goto nextdevice;
3273          }
3274          if (ParentBootResourcesLength < sizeof(CM_FULL_RESOURCE_DESCRIPTOR))
3275          {
3276             RtlCopyMemory(BootResources, pValueInformation->Data, pValueInformation->DataLength);
3277          }
3278          else if (ParentBootResources->PartialResourceList.PartialDescriptors[ParentBootResources->PartialResourceList.Count - 1].Type == CmResourceTypeDeviceSpecific)
3279          {
3280             RtlCopyMemory(BootResources, pValueInformation->Data, pValueInformation->DataLength);
3281             RtlCopyMemory(
3282                (PVOID)((ULONG_PTR)BootResources + pValueInformation->DataLength),
3283                (PVOID)((ULONG_PTR)ParentBootResources + Header),
3284                ParentBootResourcesLength - Header);
3285             BootResources->PartialResourceList.Count += ParentBootResources->PartialResourceList.Count;
3286          }
3287          else
3288          {
3289             RtlCopyMemory(BootResources, pValueInformation->Data, Header);
3290             RtlCopyMemory(
3291                (PVOID)((ULONG_PTR)BootResources + Header),
3292                (PVOID)((ULONG_PTR)ParentBootResources + Header),
3293                ParentBootResourcesLength - Header);
3294             RtlCopyMemory(
3295                (PVOID)((ULONG_PTR)BootResources + ParentBootResourcesLength),
3296                pValueInformation->Data + Header,
3297                pValueInformation->DataLength - Header);
3298             BootResources->PartialResourceList.Count += ParentBootResources->PartialResourceList.Count;
3299          }
3300       }
3301 
3302       if (EnumerateSubKeys)
3303       {
3304          IndexSubKey = 0;
3305          while (TRUE)
3306          {
3307             Status = ZwEnumerateKey(hDeviceKey, IndexSubKey, KeyBasicInformation, pDeviceInformation, DeviceInfoLength, &RequiredSize);
3308             if (Status == STATUS_NO_MORE_ENTRIES)
3309                break;
3310             else if (Status == STATUS_BUFFER_OVERFLOW || Status == STATUS_BUFFER_TOO_SMALL)
3311             {
3312                ExFreePool(pDeviceInformation);
3313                DeviceInfoLength = RequiredSize;
3314                pDeviceInformation = ExAllocatePool(PagedPool, DeviceInfoLength);
3315                if (!pDeviceInformation)
3316                {
3317                   DPRINT("ExAllocatePool() failed\n");
3318                   Status = STATUS_NO_MEMORY;
3319                   goto cleanup;
3320                }
3321                Status = ZwEnumerateKey(hDeviceKey, IndexSubKey, KeyBasicInformation, pDeviceInformation, DeviceInfoLength, &RequiredSize);
3322             }
3323             if (!NT_SUCCESS(Status))
3324             {
3325                DPRINT("ZwEnumerateKey() failed with status 0x%08lx\n", Status);
3326                goto cleanup;
3327             }
3328             IndexSubKey++;
3329             DeviceName.Length = DeviceName.MaximumLength = (USHORT)pDeviceInformation->NameLength;
3330             DeviceName.Buffer = pDeviceInformation->Name;
3331 
3332             Status = IopEnumerateDetectedDevices(
3333                hDeviceKey,
3334                &DeviceName,
3335                hRootKey,
3336                TRUE,
3337                BootResources,
3338                BootResourcesLength);
3339             if (!NT_SUCCESS(Status))
3340                goto cleanup;
3341          }
3342       }
3343 
3344       /* Read identifier */
3345       Status = ZwQueryValueKey(hDeviceKey, &IdentifierU, KeyValuePartialInformation, pValueInformation, ValueInfoLength, &RequiredSize);
3346       if (Status == STATUS_BUFFER_OVERFLOW || Status == STATUS_BUFFER_TOO_SMALL)
3347       {
3348          ExFreePool(pValueInformation);
3349          ValueInfoLength = RequiredSize;
3350          pValueInformation = ExAllocatePool(PagedPool, ValueInfoLength);
3351          if (!pValueInformation)
3352          {
3353             DPRINT("ExAllocatePool() failed\n");
3354             Status = STATUS_NO_MEMORY;
3355             goto cleanup;
3356          }
3357          Status = ZwQueryValueKey(hDeviceKey, &IdentifierU, KeyValuePartialInformation, pValueInformation, ValueInfoLength, &RequiredSize);
3358       }
3359       if (!NT_SUCCESS(Status))
3360       {
3361          if (Status != STATUS_OBJECT_NAME_NOT_FOUND)
3362          {
3363             DPRINT("ZwQueryValueKey() failed with status 0x%08lx\n", Status);
3364             goto nextdevice;
3365          }
3366          ValueName.Length = ValueName.MaximumLength = 0;
3367       }
3368       else if (pValueInformation->Type != REG_SZ)
3369       {
3370          DPRINT("Wrong registry type: got 0x%lx, expected 0x%lx\n", pValueInformation->Type, REG_SZ);
3371          goto nextdevice;
3372       }
3373       else
3374       {
3375          /* Assign hardware id to this device */
3376          ValueName.Length = ValueName.MaximumLength = (USHORT)pValueInformation->DataLength;
3377          ValueName.Buffer = (PWCHAR)pValueInformation->Data;
3378          if (ValueName.Length >= sizeof(WCHAR) && ValueName.Buffer[ValueName.Length / sizeof(WCHAR) - 1] == UNICODE_NULL)
3379             ValueName.Length -= sizeof(WCHAR);
3380       }
3381 
3382       if (RelativePath && RtlCompareUnicodeString(RelativePath, &IdentifierSerial, FALSE) == 0)
3383       {
3384          pHardwareId = &HardwareIdSerial;
3385          DeviceIndex = DeviceIndexSerial++;
3386       }
3387       else if (RelativePath && RtlCompareUnicodeString(RelativePath, &IdentifierKeyboard, FALSE) == 0)
3388       {
3389          pHardwareId = &HardwareIdKeyboard;
3390          DeviceIndex = DeviceIndexKeyboard++;
3391       }
3392       else if (RelativePath && RtlCompareUnicodeString(RelativePath, &IdentifierMouse, FALSE) == 0)
3393       {
3394          pHardwareId = &HardwareIdMouse;
3395          DeviceIndex = DeviceIndexMouse++;
3396       }
3397       else if (RelativePath && RtlCompareUnicodeString(RelativePath, &IdentifierParallel, FALSE) == 0)
3398       {
3399          pHardwareId = &HardwareIdParallel;
3400          DeviceIndex = DeviceIndexParallel++;
3401       }
3402       else if (RelativePath && RtlCompareUnicodeString(RelativePath, &IdentifierFloppy, FALSE) == 0)
3403       {
3404          pHardwareId = &HardwareIdFloppy;
3405          DeviceIndex = DeviceIndexFloppy++;
3406       }
3407       else
3408       {
3409          /* Unknown key path */
3410          DPRINT("Unknown key path '%wZ'\n", RelativePath);
3411          goto nextdevice;
3412       }
3413 
3414       /* Prepare hardware id key (hardware id value without final \0) */
3415       HardwareIdKey = *pHardwareId;
3416       HardwareIdKey.Length -= sizeof(UNICODE_NULL);
3417 
3418       /* Add the detected device to Root key */
3419       InitializeObjectAttributes(&ObjectAttributes, &HardwareIdKey, OBJ_KERNEL_HANDLE, hRootKey, NULL);
3420       Status = ZwCreateKey(
3421          &hLevel1Key,
3422          KEY_CREATE_SUB_KEY,
3423          &ObjectAttributes,
3424          0,
3425          NULL,
3426          REG_OPTION_NON_VOLATILE,
3427          NULL);
3428       if (!NT_SUCCESS(Status))
3429       {
3430          DPRINT("ZwCreateKey() failed with status 0x%08lx\n", Status);
3431          goto nextdevice;
3432       }
3433       swprintf(Level2Name, L"%04lu", DeviceIndex);
3434       RtlInitUnicodeString(&Level2NameU, Level2Name);
3435       InitializeObjectAttributes(&ObjectAttributes, &Level2NameU, OBJ_KERNEL_HANDLE, hLevel1Key, NULL);
3436       Status = ZwCreateKey(
3437          &hLevel2Key,
3438          KEY_SET_VALUE | KEY_CREATE_SUB_KEY,
3439          &ObjectAttributes,
3440          0,
3441          NULL,
3442          REG_OPTION_NON_VOLATILE,
3443          NULL);
3444       ZwClose(hLevel1Key);
3445       if (!NT_SUCCESS(Status))
3446       {
3447          DPRINT("ZwCreateKey() failed with status 0x%08lx\n", Status);
3448          goto nextdevice;
3449       }
3450       DPRINT("Found %wZ #%lu (%wZ)\n", &ValueName, DeviceIndex, &HardwareIdKey);
3451       Status = ZwSetValueKey(hLevel2Key, &HardwareIDU, 0, REG_MULTI_SZ, pHardwareId->Buffer, pHardwareId->MaximumLength);
3452       if (!NT_SUCCESS(Status))
3453       {
3454          DPRINT("ZwSetValueKey() failed with status 0x%08lx\n", Status);
3455          ZwDeleteKey(hLevel2Key);
3456          goto nextdevice;
3457       }
3458       /* Create 'LogConf' subkey */
3459       InitializeObjectAttributes(&ObjectAttributes, &LogConfU, OBJ_KERNEL_HANDLE, hLevel2Key, NULL);
3460       Status = ZwCreateKey(
3461          &hLogConf,
3462          KEY_SET_VALUE,
3463          &ObjectAttributes,
3464          0,
3465          NULL,
3466          REG_OPTION_VOLATILE,
3467          NULL);
3468       if (!NT_SUCCESS(Status))
3469       {
3470          DPRINT("ZwCreateKey() failed with status 0x%08lx\n", Status);
3471          ZwDeleteKey(hLevel2Key);
3472          goto nextdevice;
3473       }
3474       if (BootResourcesLength >= sizeof(CM_FULL_RESOURCE_DESCRIPTOR))
3475       {
3476          CmResourceList = ExAllocatePool(PagedPool, BootResourcesLength + sizeof(ULONG));
3477          if (!CmResourceList)
3478          {
3479             ZwClose(hLogConf);
3480             ZwDeleteKey(hLevel2Key);
3481             goto nextdevice;
3482          }
3483 
3484          /* Add the list count (1st member of CM_RESOURCE_LIST) */
3485          ListCount = 1;
3486          RtlCopyMemory(CmResourceList,
3487                        &ListCount,
3488                        sizeof(ULONG));
3489 
3490          /* Now add the actual list (2nd member of CM_RESOURCE_LIST) */
3491          RtlCopyMemory(CmResourceList + sizeof(ULONG),
3492                        BootResources,
3493                        BootResourcesLength);
3494 
3495          /* Save boot resources to 'LogConf\BootConfig' */
3496          Status = ZwSetValueKey(hLogConf, &BootConfigU, 0, REG_RESOURCE_LIST, CmResourceList, BootResourcesLength + sizeof(ULONG));
3497          if (!NT_SUCCESS(Status))
3498          {
3499             DPRINT("ZwSetValueKey() failed with status 0x%08lx\n", Status);
3500             ZwClose(hLogConf);
3501             ZwDeleteKey(hLevel2Key);
3502             goto nextdevice;
3503          }
3504       }
3505       ZwClose(hLogConf);
3506 
3507 nextdevice:
3508       if (BootResources && BootResources != ParentBootResources)
3509       {
3510          ExFreePool(BootResources);
3511          BootResources = NULL;
3512       }
3513       if (hLevel2Key)
3514       {
3515          ZwClose(hLevel2Key);
3516          hLevel2Key = NULL;
3517       }
3518       if (hDeviceKey)
3519       {
3520          ZwClose(hDeviceKey);
3521          hDeviceKey = NULL;
3522       }
3523    }
3524 
3525    Status = STATUS_SUCCESS;
3526 
3527 cleanup:
3528    if (hDevicesKey && hDevicesKey != hBaseKey)
3529       ZwClose(hDevicesKey);
3530    if (hDeviceKey)
3531       ZwClose(hDeviceKey);
3532    if (pDeviceInformation)
3533       ExFreePool(pDeviceInformation);
3534    if (pValueInformation)
3535       ExFreePool(pValueInformation);
3536    return Status;
3537 }
3538 
3539 static
3540 INIT_FUNCTION
3541 BOOLEAN
3542 IopIsFirmwareMapperDisabled(VOID)
3543 {
3544    UNICODE_STRING KeyPathU = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\SYSTEM\\CURRENTCONTROLSET\\Control\\Pnp");
3545    UNICODE_STRING KeyNameU = RTL_CONSTANT_STRING(L"DisableFirmwareMapper");
3546    OBJECT_ATTRIBUTES ObjectAttributes;
3547    HANDLE hPnpKey;
3548    PKEY_VALUE_PARTIAL_INFORMATION KeyInformation;
3549    ULONG DesiredLength, Length;
3550    ULONG KeyValue = 0;
3551    NTSTATUS Status;
3552 
3553    InitializeObjectAttributes(&ObjectAttributes, &KeyPathU, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL);
3554    Status = ZwOpenKey(&hPnpKey, KEY_QUERY_VALUE, &ObjectAttributes);
3555    if (NT_SUCCESS(Status))
3556    {
3557        Status = ZwQueryValueKey(hPnpKey,
3558                                 &KeyNameU,
3559                                 KeyValuePartialInformation,
3560                                 NULL,
3561                                 0,
3562                                 &DesiredLength);
3563        if ((Status == STATUS_BUFFER_TOO_SMALL) ||
3564            (Status == STATUS_BUFFER_OVERFLOW))
3565        {
3566            Length = DesiredLength;
3567            KeyInformation = ExAllocatePool(PagedPool, Length);
3568            if (KeyInformation)
3569            {
3570                Status = ZwQueryValueKey(hPnpKey,
3571                                         &KeyNameU,
3572                                         KeyValuePartialInformation,
3573                                         KeyInformation,
3574                                         Length,
3575                                         &DesiredLength);
3576                if (NT_SUCCESS(Status) && KeyInformation->DataLength == sizeof(ULONG))
3577                {
3578                    KeyValue = (ULONG)(*KeyInformation->Data);
3579                }
3580                else
3581                {
3582                    DPRINT1("ZwQueryValueKey(%wZ%wZ) failed\n", &KeyPathU, &KeyNameU);
3583                }
3584 
3585                ExFreePool(KeyInformation);
3586            }
3587            else
3588            {
3589                DPRINT1("Failed to allocate memory for registry query\n");
3590            }
3591        }
3592        else
3593        {
3594            DPRINT1("ZwQueryValueKey(%wZ%wZ) failed with status 0x%08lx\n", &KeyPathU, &KeyNameU, Status);
3595        }
3596 
3597        ZwClose(hPnpKey);
3598    }
3599    else
3600    {
3601        DPRINT1("ZwOpenKey(%wZ) failed with status 0x%08lx\n", &KeyPathU, Status);
3602    }
3603 
3604    DPRINT("Firmware mapper is %s\n", KeyValue != 0 ? "disabled" : "enabled");
3605 
3606    return (KeyValue != 0) ? TRUE : FALSE;
3607 }
3608 
3609 INIT_FUNCTION
3610 NTSTATUS
3611 NTAPI
3612 IopUpdateRootKey(VOID)
3613 {
3614    UNICODE_STRING EnumU = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Enum");
3615    UNICODE_STRING RootPathU = RTL_CONSTANT_STRING(L"Root");
3616    UNICODE_STRING MultiKeyPathU = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\HARDWARE\\DESCRIPTION\\System\\MultifunctionAdapter");
3617    OBJECT_ATTRIBUTES ObjectAttributes;
3618    HANDLE hEnum, hRoot;
3619    NTSTATUS Status;
3620 
3621    InitializeObjectAttributes(&ObjectAttributes, &EnumU, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL);
3622    Status = ZwCreateKey(&hEnum, KEY_CREATE_SUB_KEY, &ObjectAttributes, 0, NULL, REG_OPTION_NON_VOLATILE, NULL);
3623    if (!NT_SUCCESS(Status))
3624    {
3625       DPRINT1("ZwCreateKey() failed with status 0x%08lx\n", Status);
3626       return Status;
3627    }
3628 
3629    InitializeObjectAttributes(&ObjectAttributes, &RootPathU, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, hEnum, NULL);
3630    Status = ZwCreateKey(&hRoot, KEY_CREATE_SUB_KEY, &ObjectAttributes, 0, NULL, REG_OPTION_NON_VOLATILE, NULL);
3631    ZwClose(hEnum);
3632    if (!NT_SUCCESS(Status))
3633    {
3634       DPRINT1("ZwOpenKey() failed with status 0x%08lx\n", Status);
3635       return Status;
3636    }
3637 
3638    if (!IopIsFirmwareMapperDisabled())
3639    {
3640         Status = IopOpenRegistryKeyEx(&hEnum, NULL, &MultiKeyPathU, KEY_ENUMERATE_SUB_KEYS);
3641         if (!NT_SUCCESS(Status))
3642         {
3643             /* Nothing to do, don't return with an error status */
3644             DPRINT("ZwOpenKey() failed with status 0x%08lx\n", Status);
3645             ZwClose(hRoot);
3646             return STATUS_SUCCESS;
3647         }
3648         Status = IopEnumerateDetectedDevices(
3649             hEnum,
3650             NULL,
3651             hRoot,
3652             TRUE,
3653             NULL,
3654             0);
3655         ZwClose(hEnum);
3656    }
3657    else
3658    {
3659         /* Enumeration is disabled */
3660         Status = STATUS_SUCCESS;
3661    }
3662 
3663    ZwClose(hRoot);
3664 
3665    return Status;
3666 }
3667 
3668 NTSTATUS
3669 NTAPI
3670 IopOpenRegistryKeyEx(PHANDLE KeyHandle,
3671                      HANDLE ParentKey,
3672                      PUNICODE_STRING Name,
3673                      ACCESS_MASK DesiredAccess)
3674 {
3675     OBJECT_ATTRIBUTES ObjectAttributes;
3676     NTSTATUS Status;
3677 
3678     PAGED_CODE();
3679 
3680     *KeyHandle = NULL;
3681 
3682     InitializeObjectAttributes(&ObjectAttributes,
3683         Name,
3684         OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
3685         ParentKey,
3686         NULL);
3687 
3688     Status = ZwOpenKey(KeyHandle, DesiredAccess, &ObjectAttributes);
3689 
3690     return Status;
3691 }
3692 
3693 NTSTATUS
3694 NTAPI
3695 IopCreateRegistryKeyEx(OUT PHANDLE Handle,
3696                        IN HANDLE RootHandle OPTIONAL,
3697                        IN PUNICODE_STRING KeyName,
3698                        IN ACCESS_MASK DesiredAccess,
3699                        IN ULONG CreateOptions,
3700                        OUT PULONG Disposition OPTIONAL)
3701 {
3702     OBJECT_ATTRIBUTES ObjectAttributes;
3703     ULONG KeyDisposition, RootHandleIndex = 0, i = 1, NestedCloseLevel = 0;
3704     USHORT Length;
3705     HANDLE HandleArray[2];
3706     BOOLEAN Recursing = TRUE;
3707     PWCHAR pp, p, p1;
3708     UNICODE_STRING KeyString;
3709     NTSTATUS Status = STATUS_SUCCESS;
3710     PAGED_CODE();
3711 
3712     /* P1 is start, pp is end */
3713     p1 = KeyName->Buffer;
3714     pp = (PVOID)((ULONG_PTR)p1 + KeyName->Length);
3715 
3716     /* Create the target key */
3717     InitializeObjectAttributes(&ObjectAttributes,
3718                                KeyName,
3719                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
3720                                RootHandle,
3721                                NULL);
3722     Status = ZwCreateKey(&HandleArray[i],
3723                          DesiredAccess,
3724                          &ObjectAttributes,
3725                          0,
3726                          NULL,
3727                          CreateOptions,
3728                          &KeyDisposition);
3729 
3730     /* Now we check if this failed */
3731     if ((Status == STATUS_OBJECT_NAME_NOT_FOUND) && (RootHandle))
3732     {
3733         /* Target key failed, so we'll need to create its parent. Setup array */
3734         HandleArray[0] = NULL;
3735         HandleArray[1] = RootHandle;
3736 
3737         /* Keep recursing for each missing parent */
3738         while (Recursing)
3739         {
3740             /* And if we're deep enough, close the last handle */
3741             if (NestedCloseLevel > 1) ZwClose(HandleArray[RootHandleIndex]);
3742 
3743             /* We're setup to ping-pong between the two handle array entries */
3744             RootHandleIndex = i;
3745             i = (i + 1) & 1;
3746 
3747             /* Clear the one we're attempting to open now */
3748             HandleArray[i] = NULL;
3749 
3750             /* Process the parent key name */
3751             for (p = p1; ((p < pp) && (*p != OBJ_NAME_PATH_SEPARATOR)); p++);
3752             Length = (USHORT)(p - p1) * sizeof(WCHAR);
3753 
3754             /* Is there a parent name? */
3755             if (Length)
3756             {
3757                 /* Build the unicode string for it */
3758                 KeyString.Buffer = p1;
3759                 KeyString.Length = KeyString.MaximumLength = Length;
3760 
3761                 /* Now try opening the parent */
3762                 InitializeObjectAttributes(&ObjectAttributes,
3763                                            &KeyString,
3764                                            OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
3765                                            HandleArray[RootHandleIndex],
3766                                            NULL);
3767                 Status = ZwCreateKey(&HandleArray[i],
3768                                      DesiredAccess,
3769                                      &ObjectAttributes,
3770                                      0,
3771                                      NULL,
3772                                      CreateOptions,
3773                                      &KeyDisposition);
3774                 if (NT_SUCCESS(Status))
3775                 {
3776                     /* It worked, we have one more handle */
3777                     NestedCloseLevel++;
3778                 }
3779                 else
3780                 {
3781                     /* Parent key creation failed, abandon loop */
3782                     Recursing = FALSE;
3783                     continue;
3784                 }
3785             }
3786             else
3787             {
3788                 /* We don't have a parent name, probably corrupted key name */
3789                 Status = STATUS_INVALID_PARAMETER;
3790                 Recursing = FALSE;
3791                 continue;
3792             }
3793 
3794             /* Now see if there's more parents to create */
3795             p1 = p + 1;
3796             if ((p == pp) || (p1 == pp))
3797             {
3798                 /* We're done, hopefully successfully, so stop */
3799                 Recursing = FALSE;
3800             }
3801         }
3802 
3803         /* Outer loop check for handle nesting that requires closing the top handle */
3804         if (NestedCloseLevel > 1) ZwClose(HandleArray[RootHandleIndex]);
3805     }
3806 
3807     /* Check if we broke out of the loop due to success */
3808     if (NT_SUCCESS(Status))
3809     {
3810         /* Return the target handle (we closed all the parent ones) and disposition */
3811         *Handle = HandleArray[i];
3812         if (Disposition) *Disposition = KeyDisposition;
3813     }
3814 
3815     /* Return the success state */
3816     return Status;
3817 }
3818 
3819 NTSTATUS
3820 NTAPI
3821 IopGetRegistryValue(IN HANDLE Handle,
3822                     IN PWSTR ValueName,
3823                     OUT PKEY_VALUE_FULL_INFORMATION *Information)
3824 {
3825     UNICODE_STRING ValueString;
3826     NTSTATUS Status;
3827     PKEY_VALUE_FULL_INFORMATION FullInformation;
3828     ULONG Size;
3829     PAGED_CODE();
3830 
3831     RtlInitUnicodeString(&ValueString, ValueName);
3832 
3833     Status = ZwQueryValueKey(Handle,
3834                              &ValueString,
3835                              KeyValueFullInformation,
3836                              NULL,
3837                              0,
3838                              &Size);
3839     if ((Status != STATUS_BUFFER_OVERFLOW) &&
3840         (Status != STATUS_BUFFER_TOO_SMALL))
3841     {
3842         return Status;
3843     }
3844 
3845     FullInformation = ExAllocatePool(NonPagedPool, Size);
3846     if (!FullInformation) return STATUS_INSUFFICIENT_RESOURCES;
3847 
3848     Status = ZwQueryValueKey(Handle,
3849                              &ValueString,
3850                              KeyValueFullInformation,
3851                              FullInformation,
3852                              Size,
3853                              &Size);
3854     if (!NT_SUCCESS(Status))
3855     {
3856         ExFreePool(FullInformation);
3857         return Status;
3858     }
3859 
3860     *Information = FullInformation;
3861     return STATUS_SUCCESS;
3862 }
3863 
3864 RTL_GENERIC_COMPARE_RESULTS
3865 NTAPI
3866 PiCompareInstancePath(IN PRTL_AVL_TABLE Table,
3867                       IN PVOID FirstStruct,
3868                       IN PVOID SecondStruct)
3869 {
3870     /* FIXME: TODO */
3871     ASSERT(FALSE);
3872     return 0;
3873 }
3874 
3875 //
3876 //  The allocation function is called by the generic table package whenever
3877 //  it needs to allocate memory for the table.
3878 //
3879 
3880 PVOID
3881 NTAPI
3882 PiAllocateGenericTableEntry(IN PRTL_AVL_TABLE Table,
3883                             IN CLONG ByteSize)
3884 {
3885     /* FIXME: TODO */
3886     ASSERT(FALSE);
3887     return NULL;
3888 }
3889 
3890 VOID
3891 NTAPI
3892 PiFreeGenericTableEntry(IN PRTL_AVL_TABLE Table,
3893                         IN PVOID Buffer)
3894 {
3895     /* FIXME: TODO */
3896     ASSERT(FALSE);
3897 }
3898 
3899 VOID
3900 NTAPI
3901 PpInitializeDeviceReferenceTable(VOID)
3902 {
3903     /* Setup the guarded mutex and AVL table */
3904     KeInitializeGuardedMutex(&PpDeviceReferenceTableLock);
3905     RtlInitializeGenericTableAvl(
3906         &PpDeviceReferenceTable,
3907         (PRTL_AVL_COMPARE_ROUTINE)PiCompareInstancePath,
3908         (PRTL_AVL_ALLOCATE_ROUTINE)PiAllocateGenericTableEntry,
3909         (PRTL_AVL_FREE_ROUTINE)PiFreeGenericTableEntry,
3910         NULL);
3911 }
3912 
3913 BOOLEAN
3914 NTAPI
3915 PiInitPhase0(VOID)
3916 {
3917     /* Initialize the resource when accessing device registry data */
3918     ExInitializeResourceLite(&PpRegistryDeviceResource);
3919 
3920     /* Setup the device reference AVL table */
3921     PpInitializeDeviceReferenceTable();
3922     return TRUE;
3923 }
3924 
3925 BOOLEAN
3926 NTAPI
3927 PpInitSystem(VOID)
3928 {
3929     /* Check the initialization phase */
3930     switch (ExpInitializationPhase)
3931     {
3932     case 0:
3933 
3934         /* Do Phase 0 */
3935         return PiInitPhase0();
3936 
3937     case 1:
3938 
3939         /* Do Phase 1 */
3940         return TRUE;
3941         //return PiInitPhase1();
3942 
3943     default:
3944 
3945         /* Don't know any other phase! Bugcheck! */
3946         KeBugCheck(UNEXPECTED_INITIALIZATION_CALL);
3947         return FALSE;
3948     }
3949 }
3950 
3951 LONG IopNumberDeviceNodes;
3952 
3953 PDEVICE_NODE
3954 NTAPI
3955 PipAllocateDeviceNode(IN PDEVICE_OBJECT PhysicalDeviceObject)
3956 {
3957     PDEVICE_NODE DeviceNode;
3958     PAGED_CODE();
3959 
3960     /* Allocate it */
3961     DeviceNode = ExAllocatePoolWithTag(NonPagedPool, sizeof(DEVICE_NODE), TAG_IO_DEVNODE);
3962     if (!DeviceNode) return DeviceNode;
3963 
3964     /* Statistics */
3965     InterlockedIncrement(&IopNumberDeviceNodes);
3966 
3967     /* Set it up */
3968     RtlZeroMemory(DeviceNode, sizeof(DEVICE_NODE));
3969     DeviceNode->InterfaceType = InterfaceTypeUndefined;
3970     DeviceNode->BusNumber = -1;
3971     DeviceNode->ChildInterfaceType = InterfaceTypeUndefined;
3972     DeviceNode->ChildBusNumber = -1;
3973     DeviceNode->ChildBusTypeIndex = -1;
3974 //    KeInitializeEvent(&DeviceNode->EnumerationMutex, SynchronizationEvent, TRUE);
3975     InitializeListHead(&DeviceNode->DeviceArbiterList);
3976     InitializeListHead(&DeviceNode->DeviceTranslatorList);
3977     InitializeListHead(&DeviceNode->TargetDeviceNotify);
3978     InitializeListHead(&DeviceNode->DockInfo.ListEntry);
3979     InitializeListHead(&DeviceNode->PendedSetInterfaceState);
3980 
3981     /* Check if there is a PDO */
3982     if (PhysicalDeviceObject)
3983     {
3984         /* Link it and remove the init flag */
3985         DeviceNode->PhysicalDeviceObject = PhysicalDeviceObject;
3986         ((PEXTENDED_DEVOBJ_EXTENSION)PhysicalDeviceObject->DeviceObjectExtension)->DeviceNode = DeviceNode;
3987         PhysicalDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
3988     }
3989 
3990     /* Return the node */
3991     return DeviceNode;
3992 }
3993 
3994 /* PUBLIC FUNCTIONS **********************************************************/
3995 
3996 NTSTATUS
3997 NTAPI
3998 PnpBusTypeGuidGet(IN USHORT Index,
3999                   IN LPGUID BusTypeGuid)
4000 {
4001     NTSTATUS Status = STATUS_SUCCESS;
4002 
4003     /* Acquire the lock */
4004     ExAcquireFastMutex(&PnpBusTypeGuidList->Lock);
4005 
4006     /* Validate size */
4007     if (Index < PnpBusTypeGuidList->GuidCount)
4008     {
4009         /* Copy the data */
4010         RtlCopyMemory(BusTypeGuid, &PnpBusTypeGuidList->Guids[Index], sizeof(GUID));
4011     }
4012     else
4013     {
4014         /* Failure path */
4015         Status = STATUS_OBJECT_NAME_NOT_FOUND;
4016     }
4017 
4018     /* Release lock and return status */
4019     ExReleaseFastMutex(&PnpBusTypeGuidList->Lock);
4020     return Status;
4021 }
4022 
4023 NTSTATUS
4024 NTAPI
4025 PnpDeviceObjectToDeviceInstance(IN PDEVICE_OBJECT DeviceObject,
4026                                 IN PHANDLE DeviceInstanceHandle,
4027                                 IN ACCESS_MASK DesiredAccess)
4028 {
4029     NTSTATUS Status;
4030     HANDLE KeyHandle;
4031     PDEVICE_NODE DeviceNode;
4032     UNICODE_STRING KeyName = RTL_CONSTANT_STRING(L"\\REGISTRY\\MACHINE\\SYSTEM\\CURRENTCONTROLSET\\ENUM");
4033     PAGED_CODE();
4034 
4035     /* Open the enum key */
4036     Status = IopOpenRegistryKeyEx(&KeyHandle,
4037                                   NULL,
4038                                   &KeyName,
4039                                   KEY_READ);
4040     if (!NT_SUCCESS(Status)) return Status;
4041 
4042     /* Make sure we have an instance path */
4043     DeviceNode = IopGetDeviceNode(DeviceObject);
4044     if ((DeviceNode) && (DeviceNode->InstancePath.Length))
4045     {
4046         /* Get the instance key */
4047         Status = IopOpenRegistryKeyEx(DeviceInstanceHandle,
4048                                       KeyHandle,
4049                                       &DeviceNode->InstancePath,
4050                                       DesiredAccess);
4051     }
4052     else
4053     {
4054         /* Fail */
4055         Status = STATUS_INVALID_DEVICE_REQUEST;
4056     }
4057 
4058     /* Close the handle and return status */
4059     ZwClose(KeyHandle);
4060     return Status;
4061 }
4062 
4063 ULONG
4064 NTAPI
4065 PnpDetermineResourceListSize(IN PCM_RESOURCE_LIST ResourceList)
4066 {
4067     ULONG FinalSize, PartialSize, EntrySize, i, j;
4068     PCM_FULL_RESOURCE_DESCRIPTOR FullDescriptor;
4069     PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptor;
4070 
4071     /* If we don't have one, that's easy */
4072     if (!ResourceList) return 0;
4073 
4074     /* Start with the minimum size possible */
4075     FinalSize = FIELD_OFFSET(CM_RESOURCE_LIST, List);
4076 
4077     /* Loop each full descriptor */
4078     FullDescriptor = ResourceList->List;
4079     for (i = 0; i < ResourceList->Count; i++)
4080     {
4081         /* Start with the minimum size possible */
4082         PartialSize = FIELD_OFFSET(CM_FULL_RESOURCE_DESCRIPTOR, PartialResourceList) +
4083         FIELD_OFFSET(CM_PARTIAL_RESOURCE_LIST, PartialDescriptors);
4084 
4085         /* Loop each partial descriptor */
4086         PartialDescriptor = FullDescriptor->PartialResourceList.PartialDescriptors;
4087         for (j = 0; j < FullDescriptor->PartialResourceList.Count; j++)
4088         {
4089             /* Start with the minimum size possible */
4090             EntrySize = sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR);
4091 
4092             /* Check if there is extra data */
4093             if (PartialDescriptor->Type == CmResourceTypeDeviceSpecific)
4094             {
4095                 /* Add that data */
4096                 EntrySize += PartialDescriptor->u.DeviceSpecificData.DataSize;
4097             }
4098 
4099             /* The size of partial descriptors is bigger */
4100             PartialSize += EntrySize;
4101 
4102             /* Go to the next partial descriptor */
4103             PartialDescriptor = (PVOID)((ULONG_PTR)PartialDescriptor + EntrySize);
4104         }
4105 
4106         /* The size of full descriptors is bigger */
4107         FinalSize += PartialSize;
4108 
4109         /* Go to the next full descriptor */
4110         FullDescriptor = (PVOID)((ULONG_PTR)FullDescriptor + PartialSize);
4111     }
4112 
4113     /* Return the final size */
4114     return FinalSize;
4115 }
4116 
4117 NTSTATUS
4118 NTAPI
4119 PiGetDeviceRegistryProperty(IN PDEVICE_OBJECT DeviceObject,
4120                             IN ULONG ValueType,
4121                             IN PWSTR ValueName,
4122                             IN PWSTR KeyName,
4123                             OUT PVOID Buffer,
4124                             IN PULONG BufferLength)
4125 {
4126     NTSTATUS Status;
4127     HANDLE KeyHandle, SubHandle;
4128     UNICODE_STRING KeyString;
4129     PKEY_VALUE_FULL_INFORMATION KeyValueInfo = NULL;
4130     ULONG Length;
4131     PAGED_CODE();
4132 
4133     /* Find the instance key */
4134     Status = PnpDeviceObjectToDeviceInstance(DeviceObject, &KeyHandle, KEY_READ);
4135     if (NT_SUCCESS(Status))
4136     {
4137         /* Check for name given by caller */
4138         if (KeyName)
4139         {
4140             /* Open this key */
4141             RtlInitUnicodeString(&KeyString, KeyName);
4142             Status = IopOpenRegistryKeyEx(&SubHandle,
4143                                           KeyHandle,
4144                                           &KeyString,
4145                                           KEY_READ);
4146             if (NT_SUCCESS(Status))
4147             {
4148                 /* And use this handle instead */
4149                 ZwClose(KeyHandle);
4150                 KeyHandle = SubHandle;
4151             }
4152         }
4153 
4154         /* Check if sub-key handle succeeded (or no-op if no key name given) */
4155         if (NT_SUCCESS(Status))
4156         {
4157             /* Now get the size of the property */
4158             Status = IopGetRegistryValue(KeyHandle,
4159                                          ValueName,
4160                                          &KeyValueInfo);
4161         }
4162 
4163         /* Close the key */
4164         ZwClose(KeyHandle);
4165     }
4166 
4167     /* Fail if any of the registry operations failed */
4168     if (!NT_SUCCESS(Status)) return Status;
4169 
4170     /* Check how much data we have to copy */
4171     Length = KeyValueInfo->DataLength;
4172     if (*BufferLength >= Length)
4173     {
4174         /* Check for a match in the value type */
4175         if (KeyValueInfo->Type == ValueType)
4176         {
4177             /* Copy the data */
4178             RtlCopyMemory(Buffer,
4179                           (PVOID)((ULONG_PTR)KeyValueInfo +
4180                           KeyValueInfo->DataOffset),
4181                           Length);
4182         }
4183         else
4184         {
4185             /* Invalid registry property type, fail */
4186            Status = STATUS_INVALID_PARAMETER_2;
4187         }
4188     }
4189     else
4190     {
4191         /* Buffer is too small to hold data */
4192         Status = STATUS_BUFFER_TOO_SMALL;
4193     }
4194 
4195     /* Return the required buffer length, free the buffer, and return status */
4196     *BufferLength = Length;
4197     ExFreePool(KeyValueInfo);
4198     return Status;
4199 }
4200 
4201 #define PIP_RETURN_DATA(x, y)   {ReturnLength = x; Data = y; Status = STATUS_SUCCESS; break;}
4202 #define PIP_REGISTRY_DATA(x, y) {ValueName = x; ValueType = y; break;}
4203 #define PIP_UNIMPLEMENTED()     {UNIMPLEMENTED_DBGBREAK(); break;}
4204 
4205 /*
4206  * @implemented
4207  */
4208 NTSTATUS
4209 NTAPI
4210 IoGetDeviceProperty(IN PDEVICE_OBJECT DeviceObject,
4211                     IN DEVICE_REGISTRY_PROPERTY DeviceProperty,
4212                     IN ULONG BufferLength,
4213                     OUT PVOID PropertyBuffer,
4214                     OUT PULONG ResultLength)
4215 {
4216     PDEVICE_NODE DeviceNode = IopGetDeviceNode(DeviceObject);
4217     DEVICE_CAPABILITIES DeviceCaps;
4218     ULONG ReturnLength = 0, Length = 0, ValueType;
4219     PWCHAR ValueName = NULL, EnumeratorNameEnd, DeviceInstanceName;
4220     PVOID Data = NULL;
4221     NTSTATUS Status = STATUS_BUFFER_TOO_SMALL;
4222     GUID BusTypeGuid;
4223     POBJECT_NAME_INFORMATION ObjectNameInfo = NULL;
4224     BOOLEAN NullTerminate = FALSE;
4225 
4226     DPRINT("IoGetDeviceProperty(0x%p %d)\n", DeviceObject, DeviceProperty);
4227 
4228     /* Assume failure */
4229     *ResultLength = 0;
4230 
4231     /* Only PDOs can call this */
4232     if (!DeviceNode) return STATUS_INVALID_DEVICE_REQUEST;
4233 
4234     /* Handle all properties */
4235     switch (DeviceProperty)
4236     {
4237         case DevicePropertyBusTypeGuid:
4238 
4239             /* Get the GUID from the internal cache */
4240             Status = PnpBusTypeGuidGet(DeviceNode->ChildBusTypeIndex, &BusTypeGuid);
4241             if (!NT_SUCCESS(Status)) return Status;
4242 
4243             /* This is the format of the returned data */
4244             PIP_RETURN_DATA(sizeof(GUID), &BusTypeGuid);
4245 
4246         case DevicePropertyLegacyBusType:
4247 
4248             /* Validate correct interface type */
4249             if (DeviceNode->ChildInterfaceType == InterfaceTypeUndefined)
4250                 return STATUS_OBJECT_NAME_NOT_FOUND;
4251 
4252             /* This is the format of the returned data */
4253             PIP_RETURN_DATA(sizeof(INTERFACE_TYPE), &DeviceNode->ChildInterfaceType);
4254 
4255         case DevicePropertyBusNumber:
4256 
4257             /* Validate correct bus number */
4258             if ((DeviceNode->ChildBusNumber & 0x80000000) == 0x80000000)
4259                 return STATUS_OBJECT_NAME_NOT_FOUND;
4260 
4261             /* This is the format of the returned data */
4262             PIP_RETURN_DATA(sizeof(ULONG), &DeviceNode->ChildBusNumber);
4263 
4264         case DevicePropertyEnumeratorName:
4265 
4266             /* Get the instance path */
4267             DeviceInstanceName = DeviceNode->InstancePath.Buffer;
4268 
4269             /* Sanity checks */
4270             ASSERT((BufferLength & 1) == 0);
4271             ASSERT(DeviceInstanceName != NULL);
4272 
4273             /* Get the name from the path */
4274             EnumeratorNameEnd = wcschr(DeviceInstanceName, OBJ_NAME_PATH_SEPARATOR);
4275             ASSERT(EnumeratorNameEnd);
4276 
4277             /* This string needs to be NULL-terminated */
4278             NullTerminate = TRUE;
4279 
4280             /* This is the format of the returned data */
4281             PIP_RETURN_DATA((ULONG)(EnumeratorNameEnd - DeviceInstanceName) * sizeof(WCHAR),
4282                             DeviceInstanceName);
4283 
4284         case DevicePropertyAddress:
4285 
4286             /* Query the device caps */
4287             Status = IopQueryDeviceCapabilities(DeviceNode, &DeviceCaps);
4288             if (!NT_SUCCESS(Status) || (DeviceCaps.Address == MAXULONG))
4289                 return STATUS_OBJECT_NAME_NOT_FOUND;
4290 
4291             /* This is the format of the returned data */
4292             PIP_RETURN_DATA(sizeof(ULONG), &DeviceCaps.Address);
4293 
4294         case DevicePropertyBootConfigurationTranslated:
4295 
4296             /* Validate we have resources */
4297             if (!DeviceNode->BootResources)
4298 //            if (!DeviceNode->BootResourcesTranslated) // FIXFIX: Need this field
4299             {
4300                 /* No resources will still fake success, but with 0 bytes */
4301                 *ResultLength = 0;
4302                 return STATUS_SUCCESS;
4303             }
4304 
4305             /* This is the format of the returned data */
4306             PIP_RETURN_DATA(PnpDetermineResourceListSize(DeviceNode->BootResources), // FIXFIX: Should use BootResourcesTranslated
4307                             DeviceNode->BootResources); // FIXFIX: Should use BootResourcesTranslated
4308 
4309         case DevicePropertyPhysicalDeviceObjectName:
4310 
4311             /* Sanity check for Unicode-sized string */
4312             ASSERT((BufferLength & 1) == 0);
4313 
4314             /* Allocate name buffer */
4315             Length = BufferLength + sizeof(OBJECT_NAME_INFORMATION);
4316             ObjectNameInfo = ExAllocatePool(PagedPool, Length);
4317             if (!ObjectNameInfo) return STATUS_INSUFFICIENT_RESOURCES;
4318 
4319             /* Query the PDO name */
4320             Status = ObQueryNameString(DeviceObject,
4321                                        ObjectNameInfo,
4322                                        Length,
4323                                        ResultLength);
4324             if (Status == STATUS_INFO_LENGTH_MISMATCH)
4325             {
4326                 /* It's up to the caller to try again */
4327                 Status = STATUS_BUFFER_TOO_SMALL;
4328             }
4329 
4330             /* This string needs to be NULL-terminated */
4331             NullTerminate = TRUE;
4332 
4333             /* Return if successful */
4334             if (NT_SUCCESS(Status)) PIP_RETURN_DATA(ObjectNameInfo->Name.Length,
4335                                                     ObjectNameInfo->Name.Buffer);
4336 
4337             /* Let the caller know how big the name is */
4338             *ResultLength -= sizeof(OBJECT_NAME_INFORMATION);
4339             break;
4340 
4341         case DevicePropertyRemovalPolicy:
4342             PIP_RETURN_DATA(sizeof(UCHAR), &DeviceNode->RemovalPolicy);
4343 
4344         /* Handle the registry-based properties */
4345         case DevicePropertyUINumber:
4346             PIP_REGISTRY_DATA(REGSTR_VAL_UI_NUMBER, REG_DWORD);
4347         case DevicePropertyLocationInformation:
4348             PIP_REGISTRY_DATA(REGSTR_VAL_LOCATION_INFORMATION, REG_SZ);
4349         case DevicePropertyDeviceDescription:
4350             PIP_REGISTRY_DATA(REGSTR_VAL_DEVDESC, REG_SZ);
4351         case DevicePropertyHardwareID:
4352             PIP_REGISTRY_DATA(REGSTR_VAL_HARDWAREID, REG_MULTI_SZ);
4353         case DevicePropertyCompatibleIDs:
4354             PIP_REGISTRY_DATA(REGSTR_VAL_COMPATIBLEIDS, REG_MULTI_SZ);
4355         case DevicePropertyBootConfiguration:
4356             PIP_REGISTRY_DATA(REGSTR_VAL_BOOTCONFIG, REG_RESOURCE_LIST);
4357         case DevicePropertyClassName:
4358             PIP_REGISTRY_DATA(REGSTR_VAL_CLASS, REG_SZ);
4359         case DevicePropertyClassGuid:
4360             PIP_REGISTRY_DATA(REGSTR_VAL_CLASSGUID, REG_SZ);
4361         case DevicePropertyDriverKeyName:
4362             PIP_REGISTRY_DATA(REGSTR_VAL_DRIVER, REG_SZ);
4363         case DevicePropertyManufacturer:
4364             PIP_REGISTRY_DATA(REGSTR_VAL_MFG, REG_SZ);
4365         case DevicePropertyFriendlyName:
4366             PIP_REGISTRY_DATA(REGSTR_VAL_FRIENDLYNAME, REG_SZ);
4367         case DevicePropertyContainerID:
4368             //PIP_REGISTRY_DATA(REGSTR_VAL_CONTAINERID, REG_SZ); // Win7
4369             PIP_UNIMPLEMENTED();
4370             break;
4371         case DevicePropertyInstallState:
4372             PIP_REGISTRY_DATA(REGSTR_VAL_CONFIGFLAGS, REG_DWORD);
4373             break;
4374         case DevicePropertyResourceRequirements:
4375             PIP_UNIMPLEMENTED();
4376         case DevicePropertyAllocatedResources:
4377             PIP_UNIMPLEMENTED();
4378         default:
4379             return STATUS_INVALID_PARAMETER_2;
4380     }
4381 
4382     /* Having a registry value name implies registry data */
4383     if (ValueName)
4384     {
4385         /* We know up-front how much data to expect */
4386         *ResultLength = BufferLength;
4387 
4388         /* Go get the data, use the LogConf subkey if necessary */
4389         Status = PiGetDeviceRegistryProperty(DeviceObject,
4390                                              ValueType,
4391                                              ValueName,
4392                                              (DeviceProperty ==
4393                                               DevicePropertyBootConfiguration) ?
4394                                              L"LogConf":  NULL,
4395                                              PropertyBuffer,
4396                                              ResultLength);
4397     }
4398     else if (NT_SUCCESS(Status))
4399     {
4400         /* We know up-front how much data to expect, check the caller's buffer */
4401         *ResultLength = ReturnLength + (NullTerminate ? sizeof(UNICODE_NULL) : 0);
4402         if (*ResultLength <= BufferLength)
4403         {
4404             /* Buffer is all good, copy the data */
4405             RtlCopyMemory(PropertyBuffer, Data, ReturnLength);
4406 
4407             /* Check if we need to NULL-terminate the string */
4408             if (NullTerminate)
4409             {
4410                 /* Terminate the string */
4411                 ((PWCHAR)PropertyBuffer)[ReturnLength / sizeof(WCHAR)] = UNICODE_NULL;
4412             }
4413 
4414             /* This is the success path */
4415             Status = STATUS_SUCCESS;
4416         }
4417         else
4418         {
4419             /* Failure path */
4420             Status = STATUS_BUFFER_TOO_SMALL;
4421         }
4422     }
4423 
4424     /* Free any allocation we may have made, and return the status code */
4425     if (ObjectNameInfo) ExFreePool(ObjectNameInfo);
4426     return Status;
4427 }
4428 
4429 /*
4430  * @implemented
4431  */
4432 VOID
4433 NTAPI
4434 IoInvalidateDeviceState(IN PDEVICE_OBJECT PhysicalDeviceObject)
4435 {
4436     PDEVICE_NODE DeviceNode = IopGetDeviceNode(PhysicalDeviceObject);
4437     IO_STACK_LOCATION Stack;
4438     ULONG PnPFlags;
4439     NTSTATUS Status;
4440     IO_STATUS_BLOCK IoStatusBlock;
4441 
4442     RtlZeroMemory(&Stack, sizeof(IO_STACK_LOCATION));
4443     Stack.MajorFunction = IRP_MJ_PNP;
4444     Stack.MinorFunction = IRP_MN_QUERY_PNP_DEVICE_STATE;
4445 
4446     Status = IopSynchronousCall(PhysicalDeviceObject, &Stack, (PVOID*)&PnPFlags);
4447     if (!NT_SUCCESS(Status))
4448     {
4449         if (Status != STATUS_NOT_SUPPORTED)
4450         {
4451             DPRINT1("IRP_MN_QUERY_PNP_DEVICE_STATE failed with status 0x%lx\n", Status);
4452         }
4453         return;
4454     }
4455 
4456     if (PnPFlags & PNP_DEVICE_NOT_DISABLEABLE)
4457         DeviceNode->UserFlags |= DNUF_NOT_DISABLEABLE;
4458     else
4459         DeviceNode->UserFlags &= ~DNUF_NOT_DISABLEABLE;
4460 
4461     if (PnPFlags & PNP_DEVICE_DONT_DISPLAY_IN_UI)
4462         DeviceNode->UserFlags |= DNUF_DONT_SHOW_IN_UI;
4463     else
4464         DeviceNode->UserFlags &= ~DNUF_DONT_SHOW_IN_UI;
4465 
4466     if ((PnPFlags & PNP_DEVICE_REMOVED) ||
4467         ((PnPFlags & PNP_DEVICE_FAILED) && !(PnPFlags & PNP_DEVICE_RESOURCE_REQUIREMENTS_CHANGED)))
4468     {
4469         /* Flag it if it's failed */
4470         if (PnPFlags & PNP_DEVICE_FAILED) DeviceNode->Problem = CM_PROB_FAILED_POST_START;
4471 
4472         /* Send removal IRPs to all of its children */
4473         IopPrepareDeviceForRemoval(PhysicalDeviceObject, TRUE);
4474 
4475         /* Send surprise removal */
4476         IopSendSurpriseRemoval(PhysicalDeviceObject);
4477 
4478         /* Tell the user-mode PnP manager that a device was removed */
4479         IopQueueTargetDeviceEvent(&GUID_DEVICE_SURPRISE_REMOVAL,
4480                                   &DeviceNode->InstancePath);
4481 
4482         IopSendRemoveDevice(PhysicalDeviceObject);
4483     }
4484     else if ((PnPFlags & PNP_DEVICE_FAILED) && (PnPFlags & PNP_DEVICE_RESOURCE_REQUIREMENTS_CHANGED))
4485     {
4486         /* Stop for resource rebalance */
4487         Status = IopStopDevice(DeviceNode);
4488         if (!NT_SUCCESS(Status))
4489         {
4490             DPRINT1("Failed to stop device for rebalancing\n");
4491 
4492             /* Stop failed so don't rebalance */
4493             PnPFlags &= ~PNP_DEVICE_RESOURCE_REQUIREMENTS_CHANGED;
4494         }
4495     }
4496 
4497     /* Resource rebalance */
4498     if (PnPFlags & PNP_DEVICE_RESOURCE_REQUIREMENTS_CHANGED)
4499     {
4500         DPRINT("Sending IRP_MN_QUERY_RESOURCES to device stack\n");
4501 
4502         Status = IopInitiatePnpIrp(PhysicalDeviceObject,
4503                                    &IoStatusBlock,
4504                                    IRP_MN_QUERY_RESOURCES,
4505                                    NULL);
4506         if (NT_SUCCESS(Status) && IoStatusBlock.Information)
4507         {
4508             DeviceNode->BootResources =
4509             (PCM_RESOURCE_LIST)IoStatusBlock.Information;
4510             IopDeviceNodeSetFlag(DeviceNode, DNF_HAS_BOOT_CONFIG);
4511         }
4512         else
4513         {
4514             DPRINT("IopInitiatePnpIrp() failed (Status %x) or IoStatusBlock.Information=NULL\n", Status);
4515             DeviceNode->BootResources = NULL;
4516         }
4517 
4518         DPRINT("Sending IRP_MN_QUERY_RESOURCE_REQUIREMENTS to device stack\n");
4519 
4520         Status = IopInitiatePnpIrp(PhysicalDeviceObject,
4521                                    &IoStatusBlock,
4522                                    IRP_MN_QUERY_RESOURCE_REQUIREMENTS,
4523                                    NULL);
4524         if (NT_SUCCESS(Status))
4525         {
4526             DeviceNode->ResourceRequirements =
4527             (PIO_RESOURCE_REQUIREMENTS_LIST)IoStatusBlock.Information;
4528         }
4529         else
4530         {
4531             DPRINT("IopInitiatePnpIrp() failed (Status %08lx)\n", Status);
4532             DeviceNode->ResourceRequirements = NULL;
4533         }
4534 
4535         /* IRP_MN_FILTER_RESOURCE_REQUIREMENTS is called indirectly by IopStartDevice */
4536         if (IopStartDevice(DeviceNode) != STATUS_SUCCESS)
4537         {
4538             DPRINT1("Restart after resource rebalance failed\n");
4539 
4540             DeviceNode->Flags &= ~(DNF_STARTED | DNF_START_REQUEST_PENDING);
4541             DeviceNode->Flags |= DNF_START_FAILED;
4542 
4543             IopRemoveDevice(DeviceNode);
4544         }
4545     }
4546 }
4547 
4548 /**
4549  * @name IoOpenDeviceRegistryKey
4550  *
4551  * Open a registry key unique for a specified driver or device instance.
4552  *
4553  * @param DeviceObject   Device to get the registry key for.
4554  * @param DevInstKeyType Type of the key to return.
4555  * @param DesiredAccess  Access mask (eg. KEY_READ | KEY_WRITE).
4556  * @param DevInstRegKey  Handle to the opened registry key on
4557  *                       successful return.
4558  *
4559  * @return Status.
4560  *
4561  * @implemented
4562  */
4563 NTSTATUS
4564 NTAPI
4565 IoOpenDeviceRegistryKey(IN PDEVICE_OBJECT DeviceObject,
4566                         IN ULONG DevInstKeyType,
4567                         IN ACCESS_MASK DesiredAccess,
4568                         OUT PHANDLE DevInstRegKey)
4569 {
4570    static WCHAR RootKeyName[] =
4571       L"\\Registry\\Machine\\System\\CurrentControlSet\\";
4572    static WCHAR ProfileKeyName[] =
4573       L"Hardware Profiles\\Current\\System\\CurrentControlSet\\";
4574    static WCHAR ClassKeyName[] = L"Control\\Class\\";
4575    static WCHAR EnumKeyName[] = L"Enum\\";
4576    static WCHAR DeviceParametersKeyName[] = L"Device Parameters";
4577    ULONG KeyNameLength;
4578    PWSTR KeyNameBuffer;
4579    UNICODE_STRING KeyName;
4580    ULONG DriverKeyLength;
4581    OBJECT_ATTRIBUTES ObjectAttributes;
4582    PDEVICE_NODE DeviceNode = NULL;
4583    NTSTATUS Status;
4584 
4585    DPRINT("IoOpenDeviceRegistryKey() called\n");
4586 
4587    if ((DevInstKeyType & (PLUGPLAY_REGKEY_DEVICE | PLUGPLAY_REGKEY_DRIVER)) == 0)
4588    {
4589        DPRINT1("IoOpenDeviceRegistryKey(): got wrong params, exiting... \n");
4590        return STATUS_INVALID_PARAMETER;
4591    }
4592 
4593    if (!IopIsValidPhysicalDeviceObject(DeviceObject))
4594        return STATUS_INVALID_DEVICE_REQUEST;
4595    DeviceNode = IopGetDeviceNode(DeviceObject);
4596 
4597    /*
4598     * Calculate the length of the base key name. This is the full
4599     * name for driver key or the name excluding "Device Parameters"
4600     * subkey for device key.
4601     */
4602 
4603    KeyNameLength = sizeof(RootKeyName);
4604    if (DevInstKeyType & PLUGPLAY_REGKEY_CURRENT_HWPROFILE)
4605       KeyNameLength += sizeof(ProfileKeyName) - sizeof(UNICODE_NULL);
4606    if (DevInstKeyType & PLUGPLAY_REGKEY_DRIVER)
4607    {
4608       KeyNameLength += sizeof(ClassKeyName) - sizeof(UNICODE_NULL);
4609       Status = IoGetDeviceProperty(DeviceObject, DevicePropertyDriverKeyName,
4610                                    0, NULL, &DriverKeyLength);
4611       if (Status != STATUS_BUFFER_TOO_SMALL)
4612          return Status;
4613       KeyNameLength += DriverKeyLength;
4614    }
4615    else
4616    {
4617       KeyNameLength += sizeof(EnumKeyName) - sizeof(UNICODE_NULL) +
4618                        DeviceNode->InstancePath.Length;
4619    }
4620 
4621    /*
4622     * Now allocate the buffer for the key name...
4623     */
4624 
4625    KeyNameBuffer = ExAllocatePool(PagedPool, KeyNameLength);
4626    if (KeyNameBuffer == NULL)
4627       return STATUS_INSUFFICIENT_RESOURCES;
4628 
4629    KeyName.Length = 0;
4630    KeyName.MaximumLength = (USHORT)KeyNameLength;
4631    KeyName.Buffer = KeyNameBuffer;
4632 
4633    /*
4634     * ...and build the key name.
4635     */
4636 
4637    KeyName.Length += sizeof(RootKeyName) - sizeof(UNICODE_NULL);
4638    RtlCopyMemory(KeyNameBuffer, RootKeyName, KeyName.Length);
4639 
4640    if (DevInstKeyType & PLUGPLAY_REGKEY_CURRENT_HWPROFILE)
4641       RtlAppendUnicodeToString(&KeyName, ProfileKeyName);
4642 
4643    if (DevInstKeyType & PLUGPLAY_REGKEY_DRIVER)
4644    {
4645       RtlAppendUnicodeToString(&KeyName, ClassKeyName);
4646       Status = IoGetDeviceProperty(DeviceObject, DevicePropertyDriverKeyName,
4647                                    DriverKeyLength, KeyNameBuffer +
4648                                    (KeyName.Length / sizeof(WCHAR)),
4649                                    &DriverKeyLength);
4650       if (!NT_SUCCESS(Status))
4651       {
4652          DPRINT1("Call to IoGetDeviceProperty() failed with Status 0x%08lx\n", Status);
4653          ExFreePool(KeyNameBuffer);
4654          return Status;
4655       }
4656       KeyName.Length += (USHORT)DriverKeyLength - sizeof(UNICODE_NULL);
4657    }
4658    else
4659    {
4660       RtlAppendUnicodeToString(&KeyName, EnumKeyName);
4661       Status = RtlAppendUnicodeStringToString(&KeyName, &DeviceNode->InstancePath);
4662       if (DeviceNode->InstancePath.Length == 0)
4663       {
4664          ExFreePool(KeyNameBuffer);
4665          return Status;
4666       }
4667    }
4668 
4669    /*
4670     * Open the base key.
4671     */
4672    Status = IopOpenRegistryKeyEx(DevInstRegKey, NULL, &KeyName, DesiredAccess);
4673    if (!NT_SUCCESS(Status))
4674    {
4675       DPRINT1("IoOpenDeviceRegistryKey(%wZ): Base key doesn't exist, exiting... (Status 0x%08lx)\n", &KeyName, Status);
4676       ExFreePool(KeyNameBuffer);
4677       return Status;
4678    }
4679    ExFreePool(KeyNameBuffer);
4680 
4681    /*
4682     * For driver key we're done now.
4683     */
4684 
4685    if (DevInstKeyType & PLUGPLAY_REGKEY_DRIVER)
4686       return Status;
4687 
4688    /*
4689     * Let's go further. For device key we must open "Device Parameters"
4690     * subkey and create it if it doesn't exist yet.
4691     */
4692 
4693    RtlInitUnicodeString(&KeyName, DeviceParametersKeyName);
4694    InitializeObjectAttributes(&ObjectAttributes,
4695                               &KeyName,
4696                               OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
4697                               *DevInstRegKey,
4698                               NULL);
4699    Status = ZwCreateKey(DevInstRegKey,
4700                         DesiredAccess,
4701                         &ObjectAttributes,
4702                         0,
4703                         NULL,
4704                         REG_OPTION_NON_VOLATILE,
4705                         NULL);
4706    ZwClose(ObjectAttributes.RootDirectory);
4707 
4708    return Status;
4709 }
4710 
4711 static
4712 NTSTATUS
4713 IopQueryRemoveChildDevices(PDEVICE_NODE ParentDeviceNode, BOOLEAN Force)
4714 {
4715     PDEVICE_NODE ChildDeviceNode, NextDeviceNode, FailedRemoveDevice;
4716     NTSTATUS Status;
4717     KIRQL OldIrql;
4718 
4719     KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql);
4720     ChildDeviceNode = ParentDeviceNode->Child;
4721     while (ChildDeviceNode != NULL)
4722     {
4723         NextDeviceNode = ChildDeviceNode->Sibling;
4724         KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql);
4725 
4726         Status = IopPrepareDeviceForRemoval(ChildDeviceNode->PhysicalDeviceObject, Force);
4727         if (!NT_SUCCESS(Status))
4728         {
4729             FailedRemoveDevice = ChildDeviceNode;
4730             goto cleanup;
4731         }
4732 
4733         KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql);
4734         ChildDeviceNode = NextDeviceNode;
4735     }
4736     KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql);
4737 
4738     return STATUS_SUCCESS;
4739 
4740 cleanup:
4741     KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql);
4742     ChildDeviceNode = ParentDeviceNode->Child;
4743     while (ChildDeviceNode != NULL)
4744     {
4745         NextDeviceNode = ChildDeviceNode->Sibling;
4746         KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql);
4747 
4748         IopCancelPrepareDeviceForRemoval(ChildDeviceNode->PhysicalDeviceObject);
4749 
4750         /* IRP_MN_CANCEL_REMOVE_DEVICE is also sent to the device
4751          * that failed the IRP_MN_QUERY_REMOVE_DEVICE request */
4752         if (ChildDeviceNode == FailedRemoveDevice)
4753             return Status;
4754 
4755         ChildDeviceNode = NextDeviceNode;
4756 
4757         KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql);
4758     }
4759     KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql);
4760 
4761     return Status;
4762 }
4763 
4764 static
4765 VOID
4766 IopSendRemoveChildDevices(PDEVICE_NODE ParentDeviceNode)
4767 {
4768     PDEVICE_NODE ChildDeviceNode, NextDeviceNode;
4769     KIRQL OldIrql;
4770 
4771     KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql);
4772     ChildDeviceNode = ParentDeviceNode->Child;
4773     while (ChildDeviceNode != NULL)
4774     {
4775         NextDeviceNode = ChildDeviceNode->Sibling;
4776         KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql);
4777 
4778         IopSendRemoveDevice(ChildDeviceNode->PhysicalDeviceObject);
4779 
4780         ChildDeviceNode = NextDeviceNode;
4781 
4782         KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql);
4783     }
4784     KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql);
4785 }
4786 
4787 static
4788 VOID
4789 IopCancelRemoveChildDevices(PDEVICE_NODE ParentDeviceNode)
4790 {
4791     PDEVICE_NODE ChildDeviceNode, NextDeviceNode;
4792     KIRQL OldIrql;
4793 
4794     KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql);
4795     ChildDeviceNode = ParentDeviceNode->Child;
4796     while (ChildDeviceNode != NULL)
4797     {
4798         NextDeviceNode = ChildDeviceNode->Sibling;
4799         KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql);
4800 
4801         IopCancelPrepareDeviceForRemoval(ChildDeviceNode->PhysicalDeviceObject);
4802 
4803         ChildDeviceNode = NextDeviceNode;
4804 
4805         KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql);
4806     }
4807     KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql);
4808 }
4809 
4810 static
4811 NTSTATUS
4812 IopQueryRemoveDeviceRelations(PDEVICE_RELATIONS DeviceRelations, BOOLEAN Force)
4813 {
4814     /* This function DOES NOT dereference the device objects on SUCCESS
4815      * but it DOES dereference device objects on FAILURE */
4816 
4817     ULONG i, j;
4818     NTSTATUS Status;
4819 
4820     for (i = 0; i < DeviceRelations->Count; i++)
4821     {
4822         Status = IopPrepareDeviceForRemoval(DeviceRelations->Objects[i], Force);
4823         if (!NT_SUCCESS(Status))
4824         {
4825             j = i;
4826             goto cleanup;
4827         }
4828     }
4829 
4830     return STATUS_SUCCESS;
4831 
4832 cleanup:
4833     /* IRP_MN_CANCEL_REMOVE_DEVICE is also sent to the device
4834      * that failed the IRP_MN_QUERY_REMOVE_DEVICE request */
4835     for (i = 0; i <= j; i++)
4836     {
4837         IopCancelPrepareDeviceForRemoval(DeviceRelations->Objects[i]);
4838         ObDereferenceObject(DeviceRelations->Objects[i]);
4839         DeviceRelations->Objects[i] = NULL;
4840     }
4841     for (; i < DeviceRelations->Count; i++)
4842     {
4843         ObDereferenceObject(DeviceRelations->Objects[i]);
4844         DeviceRelations->Objects[i] = NULL;
4845     }
4846     ExFreePool(DeviceRelations);
4847 
4848     return Status;
4849 }
4850 
4851 static
4852 VOID
4853 IopSendRemoveDeviceRelations(PDEVICE_RELATIONS DeviceRelations)
4854 {
4855     /* This function DOES dereference the device objects in all cases */
4856 
4857     ULONG i;
4858 
4859     for (i = 0; i < DeviceRelations->Count; i++)
4860     {
4861         IopSendRemoveDevice(DeviceRelations->Objects[i]);
4862         DeviceRelations->Objects[i] = NULL;
4863     }
4864 
4865     ExFreePool(DeviceRelations);
4866 }
4867 
4868 static
4869 VOID
4870 IopCancelRemoveDeviceRelations(PDEVICE_RELATIONS DeviceRelations)
4871 {
4872     /* This function DOES dereference the device objects in all cases */
4873 
4874     ULONG i;
4875 
4876     for (i = 0; i < DeviceRelations->Count; i++)
4877     {
4878         IopCancelPrepareDeviceForRemoval(DeviceRelations->Objects[i]);
4879         ObDereferenceObject(DeviceRelations->Objects[i]);
4880         DeviceRelations->Objects[i] = NULL;
4881     }
4882 
4883     ExFreePool(DeviceRelations);
4884 }
4885 
4886 VOID
4887 IopCancelPrepareDeviceForRemoval(PDEVICE_OBJECT DeviceObject)
4888 {
4889     IO_STACK_LOCATION Stack;
4890     IO_STATUS_BLOCK IoStatusBlock;
4891     PDEVICE_RELATIONS DeviceRelations;
4892     NTSTATUS Status;
4893 
4894     IopCancelRemoveDevice(DeviceObject);
4895 
4896     Stack.Parameters.QueryDeviceRelations.Type = RemovalRelations;
4897 
4898     Status = IopInitiatePnpIrp(DeviceObject,
4899                                &IoStatusBlock,
4900                                IRP_MN_QUERY_DEVICE_RELATIONS,
4901                                &Stack);
4902     if (!NT_SUCCESS(Status))
4903     {
4904         DPRINT("IopInitiatePnpIrp() failed with status 0x%08lx\n", Status);
4905         DeviceRelations = NULL;
4906     }
4907     else
4908     {
4909         DeviceRelations = (PDEVICE_RELATIONS)IoStatusBlock.Information;
4910     }
4911 
4912     if (DeviceRelations)
4913         IopCancelRemoveDeviceRelations(DeviceRelations);
4914 }
4915 
4916 NTSTATUS
4917 IopPrepareDeviceForRemoval(IN PDEVICE_OBJECT DeviceObject, BOOLEAN Force)
4918 {
4919     PDEVICE_NODE DeviceNode = IopGetDeviceNode(DeviceObject);
4920     IO_STACK_LOCATION Stack;
4921     IO_STATUS_BLOCK IoStatusBlock;
4922     PDEVICE_RELATIONS DeviceRelations;
4923     NTSTATUS Status;
4924 
4925     if ((DeviceNode->UserFlags & DNUF_NOT_DISABLEABLE) && !Force)
4926     {
4927         DPRINT1("Removal not allowed for %wZ\n", &DeviceNode->InstancePath);
4928         return STATUS_UNSUCCESSFUL;
4929     }
4930 
4931     if (!Force && IopQueryRemoveDevice(DeviceObject) != STATUS_SUCCESS)
4932     {
4933         DPRINT1("Removal vetoed by failing the query remove request\n");
4934 
4935         IopCancelRemoveDevice(DeviceObject);
4936 
4937         return STATUS_UNSUCCESSFUL;
4938     }
4939 
4940     Stack.Parameters.QueryDeviceRelations.Type = RemovalRelations;
4941 
4942     Status = IopInitiatePnpIrp(DeviceObject,
4943                                &IoStatusBlock,
4944                                IRP_MN_QUERY_DEVICE_RELATIONS,
4945                                &Stack);
4946     if (!NT_SUCCESS(Status))
4947     {
4948         DPRINT("IopInitiatePnpIrp() failed with status 0x%08lx\n", Status);
4949         DeviceRelations = NULL;
4950     }
4951     else
4952     {
4953         DeviceRelations = (PDEVICE_RELATIONS)IoStatusBlock.Information;
4954     }
4955 
4956     if (DeviceRelations)
4957     {
4958         Status = IopQueryRemoveDeviceRelations(DeviceRelations, Force);
4959         if (!NT_SUCCESS(Status))
4960             return Status;
4961     }
4962 
4963     Status = IopQueryRemoveChildDevices(DeviceNode, Force);
4964     if (!NT_SUCCESS(Status))
4965     {
4966         if (DeviceRelations)
4967             IopCancelRemoveDeviceRelations(DeviceRelations);
4968         return Status;
4969     }
4970 
4971     if (DeviceRelations)
4972         IopSendRemoveDeviceRelations(DeviceRelations);
4973     IopSendRemoveChildDevices(DeviceNode);
4974 
4975     return STATUS_SUCCESS;
4976 }
4977 
4978 NTSTATUS
4979 IopRemoveDevice(PDEVICE_NODE DeviceNode)
4980 {
4981     NTSTATUS Status;
4982 
4983     DPRINT("Removing device: %wZ\n", &DeviceNode->InstancePath);
4984 
4985     Status = IopPrepareDeviceForRemoval(DeviceNode->PhysicalDeviceObject, FALSE);
4986     if (NT_SUCCESS(Status))
4987     {
4988         IopSendRemoveDevice(DeviceNode->PhysicalDeviceObject);
4989         IopQueueTargetDeviceEvent(&GUID_DEVICE_SAFE_REMOVAL,
4990                                   &DeviceNode->InstancePath);
4991         return STATUS_SUCCESS;
4992     }
4993 
4994     return Status;
4995 }
4996 
4997 /*
4998  * @implemented
4999  */
5000 VOID
5001 NTAPI
5002 IoRequestDeviceEject(IN PDEVICE_OBJECT PhysicalDeviceObject)
5003 {
5004     PDEVICE_NODE DeviceNode = IopGetDeviceNode(PhysicalDeviceObject);
5005     PDEVICE_RELATIONS DeviceRelations;
5006     IO_STATUS_BLOCK IoStatusBlock;
5007     IO_STACK_LOCATION Stack;
5008     DEVICE_CAPABILITIES Capabilities;
5009     NTSTATUS Status;
5010 
5011     IopQueueTargetDeviceEvent(&GUID_DEVICE_KERNEL_INITIATED_EJECT,
5012                               &DeviceNode->InstancePath);
5013 
5014     if (IopQueryDeviceCapabilities(DeviceNode, &Capabilities) != STATUS_SUCCESS)
5015     {
5016         goto cleanup;
5017     }
5018 
5019     Stack.Parameters.QueryDeviceRelations.Type = EjectionRelations;
5020 
5021     Status = IopInitiatePnpIrp(PhysicalDeviceObject,
5022                                &IoStatusBlock,
5023                                IRP_MN_QUERY_DEVICE_RELATIONS,
5024                                &Stack);
5025     if (!NT_SUCCESS(Status))
5026     {
5027         DPRINT("IopInitiatePnpIrp() failed with status 0x%08lx\n", Status);
5028         DeviceRelations = NULL;
5029     }
5030     else
5031     {
5032         DeviceRelations = (PDEVICE_RELATIONS)IoStatusBlock.Information;
5033     }
5034 
5035     if (DeviceRelations)
5036     {
5037         Status = IopQueryRemoveDeviceRelations(DeviceRelations, FALSE);
5038         if (!NT_SUCCESS(Status))
5039             goto cleanup;
5040     }
5041 
5042     Status = IopQueryRemoveChildDevices(DeviceNode, FALSE);
5043     if (!NT_SUCCESS(Status))
5044     {
5045         if (DeviceRelations)
5046             IopCancelRemoveDeviceRelations(DeviceRelations);
5047         goto cleanup;
5048     }
5049 
5050     if (IopPrepareDeviceForRemoval(PhysicalDeviceObject, FALSE) != STATUS_SUCCESS)
5051     {
5052         if (DeviceRelations)
5053             IopCancelRemoveDeviceRelations(DeviceRelations);
5054         IopCancelRemoveChildDevices(DeviceNode);
5055         goto cleanup;
5056     }
5057 
5058     if (DeviceRelations)
5059         IopSendRemoveDeviceRelations(DeviceRelations);
5060     IopSendRemoveChildDevices(DeviceNode);
5061 
5062     DeviceNode->Problem = CM_PROB_HELD_FOR_EJECT;
5063     if (Capabilities.EjectSupported)
5064     {
5065         if (IopSendEject(PhysicalDeviceObject) != STATUS_SUCCESS)
5066         {
5067             goto cleanup;
5068         }
5069     }
5070     else
5071     {
5072         DeviceNode->Flags |= DNF_DISABLED;
5073     }
5074 
5075     IopQueueTargetDeviceEvent(&GUID_DEVICE_EJECT,
5076                               &DeviceNode->InstancePath);
5077 
5078     return;
5079 
5080 cleanup:
5081     IopQueueTargetDeviceEvent(&GUID_DEVICE_EJECT_VETOED,
5082                               &DeviceNode->InstancePath);
5083 }
5084 
5085 /*
5086  * @implemented
5087  */
5088 VOID
5089 NTAPI
5090 IoInvalidateDeviceRelations(
5091     IN PDEVICE_OBJECT DeviceObject,
5092     IN DEVICE_RELATION_TYPE Type)
5093 {
5094     PDEVICE_ACTION_DATA Data;
5095     KIRQL OldIrql;
5096 
5097     Data = ExAllocatePoolWithTag(NonPagedPool,
5098                                  sizeof(DEVICE_ACTION_DATA),
5099                                  TAG_IO);
5100     if (!Data)
5101         return;
5102 
5103     ObReferenceObject(DeviceObject);
5104     Data->DeviceObject = DeviceObject;
5105     Data->Type = Type;
5106 
5107     KeAcquireSpinLock(&IopDeviceActionLock, &OldIrql);
5108     InsertTailList(&IopDeviceActionRequestList, &Data->RequestListEntry);
5109     if (IopDeviceActionInProgress)
5110     {
5111         KeReleaseSpinLock(&IopDeviceActionLock, OldIrql);
5112         return;
5113     }
5114     IopDeviceActionInProgress = TRUE;
5115     KeReleaseSpinLock(&IopDeviceActionLock, OldIrql);
5116 
5117     ExInitializeWorkItem(&IopDeviceActionWorkItem,
5118                          IopDeviceActionWorker,
5119                          NULL);
5120     ExQueueWorkItem(&IopDeviceActionWorkItem,
5121                     DelayedWorkQueue);
5122 }
5123 
5124 /*
5125  * @implemented
5126  */
5127 NTSTATUS
5128 NTAPI
5129 IoSynchronousInvalidateDeviceRelations(
5130     IN PDEVICE_OBJECT DeviceObject,
5131     IN DEVICE_RELATION_TYPE Type)
5132 {
5133     PAGED_CODE();
5134 
5135     switch (Type)
5136     {
5137         case BusRelations:
5138             /* Enumerate the device */
5139             return IopEnumerateDevice(DeviceObject);
5140         case PowerRelations:
5141              /* Not handled yet */
5142              return STATUS_NOT_IMPLEMENTED;
5143         case TargetDeviceRelation:
5144             /* Nothing to do */
5145             return STATUS_SUCCESS;
5146         default:
5147             /* Ejection relations are not supported */
5148             return STATUS_NOT_SUPPORTED;
5149     }
5150 }
5151 
5152 /*
5153  * @implemented
5154  */
5155 BOOLEAN
5156 NTAPI
5157 IoTranslateBusAddress(IN INTERFACE_TYPE InterfaceType,
5158                       IN ULONG BusNumber,
5159                       IN PHYSICAL_ADDRESS BusAddress,
5160                       IN OUT PULONG AddressSpace,
5161                       OUT PPHYSICAL_ADDRESS TranslatedAddress)
5162 {
5163     /* FIXME: Notify the resource arbiter */
5164 
5165     return HalTranslateBusAddress(InterfaceType,
5166                                   BusNumber,
5167                                   BusAddress,
5168                                   AddressSpace,
5169                                   TranslatedAddress);
5170 }
5171