xref: /reactos/ntoskrnl/io/pnpmgr/pnpmgr.c (revision a6726659)
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[0]) + MAX_PATH * sizeof(WCHAR);
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       WCHAR RegKeyBuffer[MAX_PATH];
2858       UNICODE_STRING RegKey;
2859 
2860       /* Install the service for this if it's in the CDDB */
2861       IopInstallCriticalDevice(DeviceNode);
2862 
2863       RegKey.Length = 0;
2864       RegKey.MaximumLength = sizeof(RegKeyBuffer);
2865       RegKey.Buffer = RegKeyBuffer;
2866 
2867       /*
2868        * Retrieve configuration from Enum key
2869        */
2870 
2871       Service = &DeviceNode->ServiceName;
2872 
2873       RtlZeroMemory(QueryTable, sizeof(QueryTable));
2874       RtlInitUnicodeString(Service, NULL);
2875       RtlInitUnicodeString(&ClassGUID, NULL);
2876 
2877       QueryTable[0].Name = L"Service";
2878       QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
2879       QueryTable[0].EntryContext = Service;
2880 
2881       QueryTable[1].Name = L"ClassGUID";
2882       QueryTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT;
2883       QueryTable[1].EntryContext = &ClassGUID;
2884       QueryTable[1].DefaultType = REG_SZ;
2885       QueryTable[1].DefaultData = L"";
2886       QueryTable[1].DefaultLength = 0;
2887 
2888       RtlAppendUnicodeToString(&RegKey, L"\\Registry\\Machine\\System\\CurrentControlSet\\Enum\\");
2889       RtlAppendUnicodeStringToString(&RegKey, &DeviceNode->InstancePath);
2890 
2891       Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
2892          RegKey.Buffer, QueryTable, NULL, NULL);
2893 
2894       if (!NT_SUCCESS(Status))
2895       {
2896          /* FIXME: Log the error */
2897          DPRINT("Could not retrieve configuration for device %wZ (Status 0x%08x)\n",
2898             &DeviceNode->InstancePath, Status);
2899          IopDeviceNodeSetFlag(DeviceNode, DNF_DISABLED);
2900          return STATUS_SUCCESS;
2901       }
2902 
2903       if (Service->Buffer == NULL)
2904       {
2905          if (NT_SUCCESS(IopQueryDeviceCapabilities(DeviceNode, &DeviceCaps)) &&
2906              DeviceCaps.RawDeviceOK)
2907          {
2908             DPRINT("%wZ is using parent bus driver (%wZ)\n", &DeviceNode->InstancePath, &ParentDeviceNode->ServiceName);
2909             RtlInitEmptyUnicodeString(&DeviceNode->ServiceName, NULL, 0);
2910          }
2911          else if (ClassGUID.Length != 0)
2912          {
2913             /* Device has a ClassGUID value, but no Service value.
2914              * Suppose it is using the NULL driver, so state the
2915              * device is started */
2916             DPRINT("%wZ is using NULL driver\n", &DeviceNode->InstancePath);
2917             IopDeviceNodeSetFlag(DeviceNode, DNF_STARTED);
2918          }
2919          else
2920          {
2921             DeviceNode->Problem = CM_PROB_FAILED_INSTALL;
2922             IopDeviceNodeSetFlag(DeviceNode, DNF_DISABLED);
2923          }
2924          return STATUS_SUCCESS;
2925       }
2926 
2927       DPRINT("Got Service %S\n", Service->Buffer);
2928    }
2929 
2930    return STATUS_SUCCESS;
2931 }
2932 
2933 /*
2934  * IopActionInitChildServices
2935  *
2936  * Initialize the service for all (direct) child nodes of a parent node
2937  *
2938  * Parameters
2939  *    DeviceNode
2940  *       Pointer to device node.
2941  *    Context
2942  *       Pointer to parent node to initialize child node services for.
2943  *
2944  * Remarks
2945  *    If the driver image for a service is not loaded and initialized
2946  *    it is done here too. Any errors that occur are logged instead so
2947  *    that all child services have a chance of being initialized.
2948  */
2949 
2950 NTSTATUS
2951 IopActionInitChildServices(PDEVICE_NODE DeviceNode,
2952                            PVOID Context)
2953 {
2954    PDEVICE_NODE ParentDeviceNode;
2955    NTSTATUS Status;
2956    BOOLEAN BootDrivers = !PnpSystemInit;
2957 
2958    DPRINT("IopActionInitChildServices(%p, %p)\n", DeviceNode, Context);
2959 
2960    ParentDeviceNode = Context;
2961 
2962    /*
2963     * We are called for the parent too, but we don't need to do special
2964     * handling for this node
2965     */
2966    if (DeviceNode == ParentDeviceNode)
2967    {
2968       DPRINT("Success\n");
2969       return STATUS_SUCCESS;
2970    }
2971 
2972    /*
2973     * We don't want to check for a direct child because
2974     * this function is called during boot to reinitialize
2975     * devices with drivers that couldn't load yet due to
2976     * stage 0 limitations (ie can't load from disk yet).
2977     */
2978 
2979    if (!(DeviceNode->Flags & DNF_PROCESSED))
2980    {
2981        DPRINT1("Child not ready to be added\n");
2982        return STATUS_SUCCESS;
2983    }
2984 
2985    if (IopDeviceNodeHasFlag(DeviceNode, DNF_STARTED) ||
2986        IopDeviceNodeHasFlag(DeviceNode, DNF_ADDED) ||
2987        IopDeviceNodeHasFlag(DeviceNode, DNF_DISABLED))
2988        return STATUS_SUCCESS;
2989 
2990    if (DeviceNode->ServiceName.Buffer == NULL)
2991    {
2992       /* We don't need to worry about loading the driver because we're
2993        * being driven in raw mode so our parent must be loaded to get here */
2994       Status = IopInitializeDevice(DeviceNode, NULL);
2995       if (NT_SUCCESS(Status))
2996       {
2997           Status = IopStartDevice(DeviceNode);
2998           if (!NT_SUCCESS(Status))
2999           {
3000               DPRINT1("IopStartDevice(%wZ) failed with status 0x%08x\n",
3001                       &DeviceNode->InstancePath, Status);
3002           }
3003       }
3004    }
3005    else
3006    {
3007       PLDR_DATA_TABLE_ENTRY ModuleObject;
3008       PDRIVER_OBJECT DriverObject;
3009 
3010       KeEnterCriticalRegion();
3011       ExAcquireResourceExclusiveLite(&IopDriverLoadResource, TRUE);
3012       /* Get existing DriverObject pointer (in case the driver has
3013          already been loaded and initialized) */
3014       Status = IopGetDriverObject(
3015           &DriverObject,
3016           &DeviceNode->ServiceName,
3017           FALSE);
3018 
3019       if (!NT_SUCCESS(Status))
3020       {
3021          /* Driver is not initialized, try to load it */
3022          Status = IopLoadServiceModule(&DeviceNode->ServiceName, &ModuleObject);
3023 
3024          if (NT_SUCCESS(Status) || Status == STATUS_IMAGE_ALREADY_LOADED)
3025          {
3026             /* Initialize the driver */
3027             Status = IopInitializeDriverModule(DeviceNode, ModuleObject,
3028                   &DeviceNode->ServiceName, FALSE, &DriverObject);
3029             if (!NT_SUCCESS(Status)) DeviceNode->Problem = CM_PROB_FAILED_DRIVER_ENTRY;
3030          }
3031          else if (Status == STATUS_DRIVER_UNABLE_TO_LOAD)
3032          {
3033             DPRINT1("Service '%wZ' is disabled\n", &DeviceNode->ServiceName);
3034             DeviceNode->Problem = CM_PROB_DISABLED_SERVICE;
3035          }
3036          else
3037          {
3038             DPRINT("IopLoadServiceModule(%wZ) failed with status 0x%08x\n",
3039                     &DeviceNode->ServiceName, Status);
3040             if (!BootDrivers) DeviceNode->Problem = CM_PROB_DRIVER_FAILED_LOAD;
3041          }
3042       }
3043       ExReleaseResourceLite(&IopDriverLoadResource);
3044       KeLeaveCriticalRegion();
3045 
3046       /* Driver is loaded and initialized at this point */
3047       if (NT_SUCCESS(Status))
3048       {
3049           /* Initialize the device, including all filters */
3050           Status = PipCallDriverAddDevice(DeviceNode, FALSE, DriverObject);
3051 
3052           /* Remove the extra reference */
3053           ObDereferenceObject(DriverObject);
3054       }
3055       else
3056       {
3057          /*
3058           * Don't disable when trying to load only boot drivers
3059           */
3060          if (!BootDrivers)
3061          {
3062             IopDeviceNodeSetFlag(DeviceNode, DNF_DISABLED);
3063          }
3064       }
3065    }
3066 
3067    return STATUS_SUCCESS;
3068 }
3069 
3070 /*
3071  * IopInitializePnpServices
3072  *
3073  * Initialize services for discovered children
3074  *
3075  * Parameters
3076  *    DeviceNode
3077  *       Top device node to start initializing services.
3078  *
3079  * Return Value
3080  *    Status
3081  */
3082 NTSTATUS
3083 IopInitializePnpServices(IN PDEVICE_NODE DeviceNode)
3084 {
3085    DEVICETREE_TRAVERSE_CONTEXT Context;
3086 
3087    DPRINT("IopInitializePnpServices(%p)\n", DeviceNode);
3088 
3089    IopInitDeviceTreeTraverseContext(
3090       &Context,
3091       DeviceNode,
3092       IopActionInitChildServices,
3093       DeviceNode);
3094 
3095    return IopTraverseDeviceTree(&Context);
3096 }
3097 
3098 static
3099 INIT_FUNCTION
3100 NTSTATUS
3101 IopEnumerateDetectedDevices(
3102    IN HANDLE hBaseKey,
3103    IN PUNICODE_STRING RelativePath OPTIONAL,
3104    IN HANDLE hRootKey,
3105    IN BOOLEAN EnumerateSubKeys,
3106    IN PCM_FULL_RESOURCE_DESCRIPTOR ParentBootResources,
3107    IN ULONG ParentBootResourcesLength)
3108 {
3109    UNICODE_STRING IdentifierU = RTL_CONSTANT_STRING(L"Identifier");
3110    UNICODE_STRING HardwareIDU = RTL_CONSTANT_STRING(L"HardwareID");
3111    UNICODE_STRING ConfigurationDataU = RTL_CONSTANT_STRING(L"Configuration Data");
3112    UNICODE_STRING BootConfigU = RTL_CONSTANT_STRING(L"BootConfig");
3113    UNICODE_STRING LogConfU = RTL_CONSTANT_STRING(L"LogConf");
3114    OBJECT_ATTRIBUTES ObjectAttributes;
3115    HANDLE hDevicesKey = NULL;
3116    HANDLE hDeviceKey = NULL;
3117    HANDLE hLevel1Key, hLevel2Key = NULL, hLogConf;
3118    UNICODE_STRING Level2NameU;
3119    WCHAR Level2Name[5];
3120    ULONG IndexDevice = 0;
3121    ULONG IndexSubKey;
3122    PKEY_BASIC_INFORMATION pDeviceInformation = NULL;
3123    ULONG DeviceInfoLength = sizeof(KEY_BASIC_INFORMATION) + 50 * sizeof(WCHAR);
3124    PKEY_VALUE_PARTIAL_INFORMATION pValueInformation = NULL;
3125    ULONG ValueInfoLength = sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 50 * sizeof(WCHAR);
3126    UNICODE_STRING DeviceName, ValueName;
3127    ULONG RequiredSize;
3128    PCM_FULL_RESOURCE_DESCRIPTOR BootResources = NULL;
3129    ULONG BootResourcesLength;
3130    NTSTATUS Status;
3131 
3132    const UNICODE_STRING IdentifierSerial = RTL_CONSTANT_STRING(L"SerialController");
3133    UNICODE_STRING HardwareIdSerial = RTL_CONSTANT_STRING(L"*PNP0501\0");
3134    static ULONG DeviceIndexSerial = 0;
3135    const UNICODE_STRING IdentifierKeyboard = RTL_CONSTANT_STRING(L"KeyboardController");
3136    UNICODE_STRING HardwareIdKeyboard = RTL_CONSTANT_STRING(L"*PNP0303\0");
3137    static ULONG DeviceIndexKeyboard = 0;
3138    const UNICODE_STRING IdentifierMouse = RTL_CONSTANT_STRING(L"PointerController");
3139    UNICODE_STRING HardwareIdMouse = RTL_CONSTANT_STRING(L"*PNP0F13\0");
3140    static ULONG DeviceIndexMouse = 0;
3141    const UNICODE_STRING IdentifierParallel = RTL_CONSTANT_STRING(L"ParallelController");
3142    UNICODE_STRING HardwareIdParallel = RTL_CONSTANT_STRING(L"*PNP0400\0");
3143    static ULONG DeviceIndexParallel = 0;
3144    const UNICODE_STRING IdentifierFloppy = RTL_CONSTANT_STRING(L"FloppyDiskPeripheral");
3145    UNICODE_STRING HardwareIdFloppy = RTL_CONSTANT_STRING(L"*PNP0700\0");
3146    static ULONG DeviceIndexFloppy = 0;
3147    UNICODE_STRING HardwareIdKey;
3148    PUNICODE_STRING pHardwareId;
3149    ULONG DeviceIndex = 0;
3150    PUCHAR CmResourceList;
3151    ULONG ListCount;
3152 
3153     if (RelativePath)
3154     {
3155         Status = IopOpenRegistryKeyEx(&hDevicesKey, hBaseKey, RelativePath, KEY_ENUMERATE_SUB_KEYS);
3156         if (!NT_SUCCESS(Status))
3157         {
3158             DPRINT("ZwOpenKey() failed with status 0x%08lx\n", Status);
3159             goto cleanup;
3160         }
3161     }
3162     else
3163         hDevicesKey = hBaseKey;
3164 
3165    pDeviceInformation = ExAllocatePool(PagedPool, DeviceInfoLength);
3166    if (!pDeviceInformation)
3167    {
3168       DPRINT("ExAllocatePool() failed\n");
3169       Status = STATUS_NO_MEMORY;
3170       goto cleanup;
3171    }
3172 
3173    pValueInformation = ExAllocatePool(PagedPool, ValueInfoLength);
3174    if (!pValueInformation)
3175    {
3176       DPRINT("ExAllocatePool() failed\n");
3177       Status = STATUS_NO_MEMORY;
3178       goto cleanup;
3179    }
3180 
3181    while (TRUE)
3182    {
3183       Status = ZwEnumerateKey(hDevicesKey, IndexDevice, KeyBasicInformation, pDeviceInformation, DeviceInfoLength, &RequiredSize);
3184       if (Status == STATUS_NO_MORE_ENTRIES)
3185          break;
3186       else if (Status == STATUS_BUFFER_OVERFLOW || Status == STATUS_BUFFER_TOO_SMALL)
3187       {
3188          ExFreePool(pDeviceInformation);
3189          DeviceInfoLength = RequiredSize;
3190          pDeviceInformation = ExAllocatePool(PagedPool, DeviceInfoLength);
3191          if (!pDeviceInformation)
3192          {
3193             DPRINT("ExAllocatePool() failed\n");
3194             Status = STATUS_NO_MEMORY;
3195             goto cleanup;
3196          }
3197          Status = ZwEnumerateKey(hDevicesKey, IndexDevice, KeyBasicInformation, pDeviceInformation, DeviceInfoLength, &RequiredSize);
3198       }
3199       if (!NT_SUCCESS(Status))
3200       {
3201          DPRINT("ZwEnumerateKey() failed with status 0x%08lx\n", Status);
3202          goto cleanup;
3203       }
3204       IndexDevice++;
3205 
3206       /* Open device key */
3207       DeviceName.Length = DeviceName.MaximumLength = (USHORT)pDeviceInformation->NameLength;
3208       DeviceName.Buffer = pDeviceInformation->Name;
3209 
3210       Status = IopOpenRegistryKeyEx(&hDeviceKey, hDevicesKey, &DeviceName,
3211           KEY_QUERY_VALUE + (EnumerateSubKeys ? KEY_ENUMERATE_SUB_KEYS : 0));
3212       if (!NT_SUCCESS(Status))
3213       {
3214          DPRINT("ZwOpenKey() failed with status 0x%08lx\n", Status);
3215          goto cleanup;
3216       }
3217 
3218       /* Read boot resources, and add then to parent ones */
3219       Status = ZwQueryValueKey(hDeviceKey, &ConfigurationDataU, KeyValuePartialInformation, pValueInformation, ValueInfoLength, &RequiredSize);
3220       if (Status == STATUS_BUFFER_OVERFLOW || Status == STATUS_BUFFER_TOO_SMALL)
3221       {
3222          ExFreePool(pValueInformation);
3223          ValueInfoLength = RequiredSize;
3224          pValueInformation = ExAllocatePool(PagedPool, ValueInfoLength);
3225          if (!pValueInformation)
3226          {
3227             DPRINT("ExAllocatePool() failed\n");
3228             ZwDeleteKey(hLevel2Key);
3229             Status = STATUS_NO_MEMORY;
3230             goto cleanup;
3231          }
3232          Status = ZwQueryValueKey(hDeviceKey, &ConfigurationDataU, KeyValuePartialInformation, pValueInformation, ValueInfoLength, &RequiredSize);
3233       }
3234       if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
3235       {
3236          BootResources = ParentBootResources;
3237          BootResourcesLength = ParentBootResourcesLength;
3238       }
3239       else if (!NT_SUCCESS(Status))
3240       {
3241          DPRINT("ZwQueryValueKey() failed with status 0x%08lx\n", Status);
3242          goto nextdevice;
3243       }
3244       else if (pValueInformation->Type != REG_FULL_RESOURCE_DESCRIPTOR)
3245       {
3246          DPRINT("Wrong registry type: got 0x%lx, expected 0x%lx\n", pValueInformation->Type, REG_FULL_RESOURCE_DESCRIPTOR);
3247          goto nextdevice;
3248       }
3249       else
3250       {
3251          static const ULONG Header = FIELD_OFFSET(CM_FULL_RESOURCE_DESCRIPTOR, PartialResourceList.PartialDescriptors);
3252 
3253          /* Concatenate current resources and parent ones */
3254          if (ParentBootResourcesLength == 0)
3255             BootResourcesLength = pValueInformation->DataLength;
3256          else
3257             BootResourcesLength = ParentBootResourcesLength
3258             + pValueInformation->DataLength
3259                - Header;
3260          BootResources = ExAllocatePool(PagedPool, BootResourcesLength);
3261          if (!BootResources)
3262          {
3263             DPRINT("ExAllocatePool() failed\n");
3264             goto nextdevice;
3265          }
3266          if (ParentBootResourcesLength < sizeof(CM_FULL_RESOURCE_DESCRIPTOR))
3267          {
3268             RtlCopyMemory(BootResources, pValueInformation->Data, pValueInformation->DataLength);
3269          }
3270          else if (ParentBootResources->PartialResourceList.PartialDescriptors[ParentBootResources->PartialResourceList.Count - 1].Type == CmResourceTypeDeviceSpecific)
3271          {
3272             RtlCopyMemory(BootResources, pValueInformation->Data, pValueInformation->DataLength);
3273             RtlCopyMemory(
3274                (PVOID)((ULONG_PTR)BootResources + pValueInformation->DataLength),
3275                (PVOID)((ULONG_PTR)ParentBootResources + Header),
3276                ParentBootResourcesLength - Header);
3277             BootResources->PartialResourceList.Count += ParentBootResources->PartialResourceList.Count;
3278          }
3279          else
3280          {
3281             RtlCopyMemory(BootResources, pValueInformation->Data, Header);
3282             RtlCopyMemory(
3283                (PVOID)((ULONG_PTR)BootResources + Header),
3284                (PVOID)((ULONG_PTR)ParentBootResources + Header),
3285                ParentBootResourcesLength - Header);
3286             RtlCopyMemory(
3287                (PVOID)((ULONG_PTR)BootResources + ParentBootResourcesLength),
3288                pValueInformation->Data + Header,
3289                pValueInformation->DataLength - Header);
3290             BootResources->PartialResourceList.Count += ParentBootResources->PartialResourceList.Count;
3291          }
3292       }
3293 
3294       if (EnumerateSubKeys)
3295       {
3296          IndexSubKey = 0;
3297          while (TRUE)
3298          {
3299             Status = ZwEnumerateKey(hDeviceKey, IndexSubKey, KeyBasicInformation, pDeviceInformation, DeviceInfoLength, &RequiredSize);
3300             if (Status == STATUS_NO_MORE_ENTRIES)
3301                break;
3302             else if (Status == STATUS_BUFFER_OVERFLOW || Status == STATUS_BUFFER_TOO_SMALL)
3303             {
3304                ExFreePool(pDeviceInformation);
3305                DeviceInfoLength = RequiredSize;
3306                pDeviceInformation = ExAllocatePool(PagedPool, DeviceInfoLength);
3307                if (!pDeviceInformation)
3308                {
3309                   DPRINT("ExAllocatePool() failed\n");
3310                   Status = STATUS_NO_MEMORY;
3311                   goto cleanup;
3312                }
3313                Status = ZwEnumerateKey(hDeviceKey, IndexSubKey, KeyBasicInformation, pDeviceInformation, DeviceInfoLength, &RequiredSize);
3314             }
3315             if (!NT_SUCCESS(Status))
3316             {
3317                DPRINT("ZwEnumerateKey() failed with status 0x%08lx\n", Status);
3318                goto cleanup;
3319             }
3320             IndexSubKey++;
3321             DeviceName.Length = DeviceName.MaximumLength = (USHORT)pDeviceInformation->NameLength;
3322             DeviceName.Buffer = pDeviceInformation->Name;
3323 
3324             Status = IopEnumerateDetectedDevices(
3325                hDeviceKey,
3326                &DeviceName,
3327                hRootKey,
3328                TRUE,
3329                BootResources,
3330                BootResourcesLength);
3331             if (!NT_SUCCESS(Status))
3332                goto cleanup;
3333          }
3334       }
3335 
3336       /* Read identifier */
3337       Status = ZwQueryValueKey(hDeviceKey, &IdentifierU, KeyValuePartialInformation, pValueInformation, ValueInfoLength, &RequiredSize);
3338       if (Status == STATUS_BUFFER_OVERFLOW || Status == STATUS_BUFFER_TOO_SMALL)
3339       {
3340          ExFreePool(pValueInformation);
3341          ValueInfoLength = RequiredSize;
3342          pValueInformation = ExAllocatePool(PagedPool, ValueInfoLength);
3343          if (!pValueInformation)
3344          {
3345             DPRINT("ExAllocatePool() failed\n");
3346             Status = STATUS_NO_MEMORY;
3347             goto cleanup;
3348          }
3349          Status = ZwQueryValueKey(hDeviceKey, &IdentifierU, KeyValuePartialInformation, pValueInformation, ValueInfoLength, &RequiredSize);
3350       }
3351       if (!NT_SUCCESS(Status))
3352       {
3353          if (Status != STATUS_OBJECT_NAME_NOT_FOUND)
3354          {
3355             DPRINT("ZwQueryValueKey() failed with status 0x%08lx\n", Status);
3356             goto nextdevice;
3357          }
3358          ValueName.Length = ValueName.MaximumLength = 0;
3359       }
3360       else if (pValueInformation->Type != REG_SZ)
3361       {
3362          DPRINT("Wrong registry type: got 0x%lx, expected 0x%lx\n", pValueInformation->Type, REG_SZ);
3363          goto nextdevice;
3364       }
3365       else
3366       {
3367          /* Assign hardware id to this device */
3368          ValueName.Length = ValueName.MaximumLength = (USHORT)pValueInformation->DataLength;
3369          ValueName.Buffer = (PWCHAR)pValueInformation->Data;
3370          if (ValueName.Length >= sizeof(WCHAR) && ValueName.Buffer[ValueName.Length / sizeof(WCHAR) - 1] == UNICODE_NULL)
3371             ValueName.Length -= sizeof(WCHAR);
3372       }
3373 
3374       if (RelativePath && RtlCompareUnicodeString(RelativePath, &IdentifierSerial, FALSE) == 0)
3375       {
3376          pHardwareId = &HardwareIdSerial;
3377          DeviceIndex = DeviceIndexSerial++;
3378       }
3379       else if (RelativePath && RtlCompareUnicodeString(RelativePath, &IdentifierKeyboard, FALSE) == 0)
3380       {
3381          pHardwareId = &HardwareIdKeyboard;
3382          DeviceIndex = DeviceIndexKeyboard++;
3383       }
3384       else if (RelativePath && RtlCompareUnicodeString(RelativePath, &IdentifierMouse, FALSE) == 0)
3385       {
3386          pHardwareId = &HardwareIdMouse;
3387          DeviceIndex = DeviceIndexMouse++;
3388       }
3389       else if (RelativePath && RtlCompareUnicodeString(RelativePath, &IdentifierParallel, FALSE) == 0)
3390       {
3391          pHardwareId = &HardwareIdParallel;
3392          DeviceIndex = DeviceIndexParallel++;
3393       }
3394       else if (RelativePath && RtlCompareUnicodeString(RelativePath, &IdentifierFloppy, FALSE) == 0)
3395       {
3396          pHardwareId = &HardwareIdFloppy;
3397          DeviceIndex = DeviceIndexFloppy++;
3398       }
3399       else
3400       {
3401          /* Unknown key path */
3402          DPRINT("Unknown key path '%wZ'\n", RelativePath);
3403          goto nextdevice;
3404       }
3405 
3406       /* Prepare hardware id key (hardware id value without final \0) */
3407       HardwareIdKey = *pHardwareId;
3408       HardwareIdKey.Length -= sizeof(UNICODE_NULL);
3409 
3410       /* Add the detected device to Root key */
3411       InitializeObjectAttributes(&ObjectAttributes, &HardwareIdKey, OBJ_KERNEL_HANDLE, hRootKey, NULL);
3412       Status = ZwCreateKey(
3413          &hLevel1Key,
3414          KEY_CREATE_SUB_KEY,
3415          &ObjectAttributes,
3416          0,
3417          NULL,
3418          REG_OPTION_NON_VOLATILE,
3419          NULL);
3420       if (!NT_SUCCESS(Status))
3421       {
3422          DPRINT("ZwCreateKey() failed with status 0x%08lx\n", Status);
3423          goto nextdevice;
3424       }
3425       swprintf(Level2Name, L"%04lu", DeviceIndex);
3426       RtlInitUnicodeString(&Level2NameU, Level2Name);
3427       InitializeObjectAttributes(&ObjectAttributes, &Level2NameU, OBJ_KERNEL_HANDLE, hLevel1Key, NULL);
3428       Status = ZwCreateKey(
3429          &hLevel2Key,
3430          KEY_SET_VALUE | KEY_CREATE_SUB_KEY,
3431          &ObjectAttributes,
3432          0,
3433          NULL,
3434          REG_OPTION_NON_VOLATILE,
3435          NULL);
3436       ZwClose(hLevel1Key);
3437       if (!NT_SUCCESS(Status))
3438       {
3439          DPRINT("ZwCreateKey() failed with status 0x%08lx\n", Status);
3440          goto nextdevice;
3441       }
3442       DPRINT("Found %wZ #%lu (%wZ)\n", &ValueName, DeviceIndex, &HardwareIdKey);
3443       Status = ZwSetValueKey(hLevel2Key, &HardwareIDU, 0, REG_MULTI_SZ, pHardwareId->Buffer, pHardwareId->MaximumLength);
3444       if (!NT_SUCCESS(Status))
3445       {
3446          DPRINT("ZwSetValueKey() failed with status 0x%08lx\n", Status);
3447          ZwDeleteKey(hLevel2Key);
3448          goto nextdevice;
3449       }
3450       /* Create 'LogConf' subkey */
3451       InitializeObjectAttributes(&ObjectAttributes, &LogConfU, OBJ_KERNEL_HANDLE, hLevel2Key, NULL);
3452       Status = ZwCreateKey(
3453          &hLogConf,
3454          KEY_SET_VALUE,
3455          &ObjectAttributes,
3456          0,
3457          NULL,
3458          REG_OPTION_VOLATILE,
3459          NULL);
3460       if (!NT_SUCCESS(Status))
3461       {
3462          DPRINT("ZwCreateKey() failed with status 0x%08lx\n", Status);
3463          ZwDeleteKey(hLevel2Key);
3464          goto nextdevice;
3465       }
3466       if (BootResourcesLength >= sizeof(CM_FULL_RESOURCE_DESCRIPTOR))
3467       {
3468          CmResourceList = ExAllocatePool(PagedPool, BootResourcesLength + sizeof(ULONG));
3469          if (!CmResourceList)
3470          {
3471             ZwClose(hLogConf);
3472             ZwDeleteKey(hLevel2Key);
3473             goto nextdevice;
3474          }
3475 
3476          /* Add the list count (1st member of CM_RESOURCE_LIST) */
3477          ListCount = 1;
3478          RtlCopyMemory(CmResourceList,
3479                        &ListCount,
3480                        sizeof(ULONG));
3481 
3482          /* Now add the actual list (2nd member of CM_RESOURCE_LIST) */
3483          RtlCopyMemory(CmResourceList + sizeof(ULONG),
3484                        BootResources,
3485                        BootResourcesLength);
3486 
3487          /* Save boot resources to 'LogConf\BootConfig' */
3488          Status = ZwSetValueKey(hLogConf, &BootConfigU, 0, REG_RESOURCE_LIST, CmResourceList, BootResourcesLength + sizeof(ULONG));
3489          if (!NT_SUCCESS(Status))
3490          {
3491             DPRINT("ZwSetValueKey() failed with status 0x%08lx\n", Status);
3492             ZwClose(hLogConf);
3493             ZwDeleteKey(hLevel2Key);
3494             goto nextdevice;
3495          }
3496       }
3497       ZwClose(hLogConf);
3498 
3499 nextdevice:
3500       if (BootResources && BootResources != ParentBootResources)
3501       {
3502          ExFreePool(BootResources);
3503          BootResources = NULL;
3504       }
3505       if (hLevel2Key)
3506       {
3507          ZwClose(hLevel2Key);
3508          hLevel2Key = NULL;
3509       }
3510       if (hDeviceKey)
3511       {
3512          ZwClose(hDeviceKey);
3513          hDeviceKey = NULL;
3514       }
3515    }
3516 
3517    Status = STATUS_SUCCESS;
3518 
3519 cleanup:
3520    if (hDevicesKey && hDevicesKey != hBaseKey)
3521       ZwClose(hDevicesKey);
3522    if (hDeviceKey)
3523       ZwClose(hDeviceKey);
3524    if (pDeviceInformation)
3525       ExFreePool(pDeviceInformation);
3526    if (pValueInformation)
3527       ExFreePool(pValueInformation);
3528    return Status;
3529 }
3530 
3531 static
3532 INIT_FUNCTION
3533 BOOLEAN
3534 IopIsFirmwareMapperDisabled(VOID)
3535 {
3536    UNICODE_STRING KeyPathU = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\SYSTEM\\CURRENTCONTROLSET\\Control\\Pnp");
3537    UNICODE_STRING KeyNameU = RTL_CONSTANT_STRING(L"DisableFirmwareMapper");
3538    OBJECT_ATTRIBUTES ObjectAttributes;
3539    HANDLE hPnpKey;
3540    PKEY_VALUE_PARTIAL_INFORMATION KeyInformation;
3541    ULONG DesiredLength, Length;
3542    ULONG KeyValue = 0;
3543    NTSTATUS Status;
3544 
3545    InitializeObjectAttributes(&ObjectAttributes, &KeyPathU, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL);
3546    Status = ZwOpenKey(&hPnpKey, KEY_QUERY_VALUE, &ObjectAttributes);
3547    if (NT_SUCCESS(Status))
3548    {
3549        Status = ZwQueryValueKey(hPnpKey,
3550                                 &KeyNameU,
3551                                 KeyValuePartialInformation,
3552                                 NULL,
3553                                 0,
3554                                 &DesiredLength);
3555        if ((Status == STATUS_BUFFER_TOO_SMALL) ||
3556            (Status == STATUS_BUFFER_OVERFLOW))
3557        {
3558            Length = DesiredLength;
3559            KeyInformation = ExAllocatePool(PagedPool, Length);
3560            if (KeyInformation)
3561            {
3562                Status = ZwQueryValueKey(hPnpKey,
3563                                         &KeyNameU,
3564                                         KeyValuePartialInformation,
3565                                         KeyInformation,
3566                                         Length,
3567                                         &DesiredLength);
3568                if (NT_SUCCESS(Status) && KeyInformation->DataLength == sizeof(ULONG))
3569                {
3570                    KeyValue = (ULONG)(*KeyInformation->Data);
3571                }
3572                else
3573                {
3574                    DPRINT1("ZwQueryValueKey(%wZ%wZ) failed\n", &KeyPathU, &KeyNameU);
3575                }
3576 
3577                ExFreePool(KeyInformation);
3578            }
3579            else
3580            {
3581                DPRINT1("Failed to allocate memory for registry query\n");
3582            }
3583        }
3584        else
3585        {
3586            DPRINT1("ZwQueryValueKey(%wZ%wZ) failed with status 0x%08lx\n", &KeyPathU, &KeyNameU, Status);
3587        }
3588 
3589        ZwClose(hPnpKey);
3590    }
3591    else
3592    {
3593        DPRINT1("ZwOpenKey(%wZ) failed with status 0x%08lx\n", &KeyPathU, Status);
3594    }
3595 
3596    DPRINT("Firmware mapper is %s\n", KeyValue != 0 ? "disabled" : "enabled");
3597 
3598    return (KeyValue != 0) ? TRUE : FALSE;
3599 }
3600 
3601 INIT_FUNCTION
3602 NTSTATUS
3603 NTAPI
3604 IopUpdateRootKey(VOID)
3605 {
3606    UNICODE_STRING EnumU = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Enum");
3607    UNICODE_STRING RootPathU = RTL_CONSTANT_STRING(L"Root");
3608    UNICODE_STRING MultiKeyPathU = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\HARDWARE\\DESCRIPTION\\System\\MultifunctionAdapter");
3609    OBJECT_ATTRIBUTES ObjectAttributes;
3610    HANDLE hEnum, hRoot;
3611    NTSTATUS Status;
3612 
3613    InitializeObjectAttributes(&ObjectAttributes, &EnumU, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL);
3614    Status = ZwCreateKey(&hEnum, KEY_CREATE_SUB_KEY, &ObjectAttributes, 0, NULL, REG_OPTION_NON_VOLATILE, NULL);
3615    if (!NT_SUCCESS(Status))
3616    {
3617       DPRINT1("ZwCreateKey() failed with status 0x%08lx\n", Status);
3618       return Status;
3619    }
3620 
3621    InitializeObjectAttributes(&ObjectAttributes, &RootPathU, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, hEnum, NULL);
3622    Status = ZwCreateKey(&hRoot, KEY_CREATE_SUB_KEY, &ObjectAttributes, 0, NULL, REG_OPTION_NON_VOLATILE, NULL);
3623    ZwClose(hEnum);
3624    if (!NT_SUCCESS(Status))
3625    {
3626       DPRINT1("ZwOpenKey() failed with status 0x%08lx\n", Status);
3627       return Status;
3628    }
3629 
3630    if (!IopIsFirmwareMapperDisabled())
3631    {
3632         Status = IopOpenRegistryKeyEx(&hEnum, NULL, &MultiKeyPathU, KEY_ENUMERATE_SUB_KEYS);
3633         if (!NT_SUCCESS(Status))
3634         {
3635             /* Nothing to do, don't return with an error status */
3636             DPRINT("ZwOpenKey() failed with status 0x%08lx\n", Status);
3637             ZwClose(hRoot);
3638             return STATUS_SUCCESS;
3639         }
3640         Status = IopEnumerateDetectedDevices(
3641             hEnum,
3642             NULL,
3643             hRoot,
3644             TRUE,
3645             NULL,
3646             0);
3647         ZwClose(hEnum);
3648    }
3649    else
3650    {
3651         /* Enumeration is disabled */
3652         Status = STATUS_SUCCESS;
3653    }
3654 
3655    ZwClose(hRoot);
3656 
3657    return Status;
3658 }
3659 
3660 NTSTATUS
3661 NTAPI
3662 IopOpenRegistryKeyEx(PHANDLE KeyHandle,
3663                      HANDLE ParentKey,
3664                      PUNICODE_STRING Name,
3665                      ACCESS_MASK DesiredAccess)
3666 {
3667     OBJECT_ATTRIBUTES ObjectAttributes;
3668     NTSTATUS Status;
3669 
3670     PAGED_CODE();
3671 
3672     *KeyHandle = NULL;
3673 
3674     InitializeObjectAttributes(&ObjectAttributes,
3675         Name,
3676         OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
3677         ParentKey,
3678         NULL);
3679 
3680     Status = ZwOpenKey(KeyHandle, DesiredAccess, &ObjectAttributes);
3681 
3682     return Status;
3683 }
3684 
3685 NTSTATUS
3686 NTAPI
3687 IopCreateRegistryKeyEx(OUT PHANDLE Handle,
3688                        IN HANDLE RootHandle OPTIONAL,
3689                        IN PUNICODE_STRING KeyName,
3690                        IN ACCESS_MASK DesiredAccess,
3691                        IN ULONG CreateOptions,
3692                        OUT PULONG Disposition OPTIONAL)
3693 {
3694     OBJECT_ATTRIBUTES ObjectAttributes;
3695     ULONG KeyDisposition, RootHandleIndex = 0, i = 1, NestedCloseLevel = 0;
3696     USHORT Length;
3697     HANDLE HandleArray[2];
3698     BOOLEAN Recursing = TRUE;
3699     PWCHAR pp, p, p1;
3700     UNICODE_STRING KeyString;
3701     NTSTATUS Status = STATUS_SUCCESS;
3702     PAGED_CODE();
3703 
3704     /* P1 is start, pp is end */
3705     p1 = KeyName->Buffer;
3706     pp = (PVOID)((ULONG_PTR)p1 + KeyName->Length);
3707 
3708     /* Create the target key */
3709     InitializeObjectAttributes(&ObjectAttributes,
3710                                KeyName,
3711                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
3712                                RootHandle,
3713                                NULL);
3714     Status = ZwCreateKey(&HandleArray[i],
3715                          DesiredAccess,
3716                          &ObjectAttributes,
3717                          0,
3718                          NULL,
3719                          CreateOptions,
3720                          &KeyDisposition);
3721 
3722     /* Now we check if this failed */
3723     if ((Status == STATUS_OBJECT_NAME_NOT_FOUND) && (RootHandle))
3724     {
3725         /* Target key failed, so we'll need to create its parent. Setup array */
3726         HandleArray[0] = NULL;
3727         HandleArray[1] = RootHandle;
3728 
3729         /* Keep recursing for each missing parent */
3730         while (Recursing)
3731         {
3732             /* And if we're deep enough, close the last handle */
3733             if (NestedCloseLevel > 1) ZwClose(HandleArray[RootHandleIndex]);
3734 
3735             /* We're setup to ping-pong between the two handle array entries */
3736             RootHandleIndex = i;
3737             i = (i + 1) & 1;
3738 
3739             /* Clear the one we're attempting to open now */
3740             HandleArray[i] = NULL;
3741 
3742             /* Process the parent key name */
3743             for (p = p1; ((p < pp) && (*p != OBJ_NAME_PATH_SEPARATOR)); p++);
3744             Length = (USHORT)(p - p1) * sizeof(WCHAR);
3745 
3746             /* Is there a parent name? */
3747             if (Length)
3748             {
3749                 /* Build the unicode string for it */
3750                 KeyString.Buffer = p1;
3751                 KeyString.Length = KeyString.MaximumLength = Length;
3752 
3753                 /* Now try opening the parent */
3754                 InitializeObjectAttributes(&ObjectAttributes,
3755                                            &KeyString,
3756                                            OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
3757                                            HandleArray[RootHandleIndex],
3758                                            NULL);
3759                 Status = ZwCreateKey(&HandleArray[i],
3760                                      DesiredAccess,
3761                                      &ObjectAttributes,
3762                                      0,
3763                                      NULL,
3764                                      CreateOptions,
3765                                      &KeyDisposition);
3766                 if (NT_SUCCESS(Status))
3767                 {
3768                     /* It worked, we have one more handle */
3769                     NestedCloseLevel++;
3770                 }
3771                 else
3772                 {
3773                     /* Parent key creation failed, abandon loop */
3774                     Recursing = FALSE;
3775                     continue;
3776                 }
3777             }
3778             else
3779             {
3780                 /* We don't have a parent name, probably corrupted key name */
3781                 Status = STATUS_INVALID_PARAMETER;
3782                 Recursing = FALSE;
3783                 continue;
3784             }
3785 
3786             /* Now see if there's more parents to create */
3787             p1 = p + 1;
3788             if ((p == pp) || (p1 == pp))
3789             {
3790                 /* We're done, hopefully successfully, so stop */
3791                 Recursing = FALSE;
3792             }
3793         }
3794 
3795         /* Outer loop check for handle nesting that requires closing the top handle */
3796         if (NestedCloseLevel > 1) ZwClose(HandleArray[RootHandleIndex]);
3797     }
3798 
3799     /* Check if we broke out of the loop due to success */
3800     if (NT_SUCCESS(Status))
3801     {
3802         /* Return the target handle (we closed all the parent ones) and disposition */
3803         *Handle = HandleArray[i];
3804         if (Disposition) *Disposition = KeyDisposition;
3805     }
3806 
3807     /* Return the success state */
3808     return Status;
3809 }
3810 
3811 NTSTATUS
3812 NTAPI
3813 IopGetRegistryValue(IN HANDLE Handle,
3814                     IN PWSTR ValueName,
3815                     OUT PKEY_VALUE_FULL_INFORMATION *Information)
3816 {
3817     UNICODE_STRING ValueString;
3818     NTSTATUS Status;
3819     PKEY_VALUE_FULL_INFORMATION FullInformation;
3820     ULONG Size;
3821     PAGED_CODE();
3822 
3823     RtlInitUnicodeString(&ValueString, ValueName);
3824 
3825     Status = ZwQueryValueKey(Handle,
3826                              &ValueString,
3827                              KeyValueFullInformation,
3828                              NULL,
3829                              0,
3830                              &Size);
3831     if ((Status != STATUS_BUFFER_OVERFLOW) &&
3832         (Status != STATUS_BUFFER_TOO_SMALL))
3833     {
3834         return Status;
3835     }
3836 
3837     FullInformation = ExAllocatePool(NonPagedPool, Size);
3838     if (!FullInformation) return STATUS_INSUFFICIENT_RESOURCES;
3839 
3840     Status = ZwQueryValueKey(Handle,
3841                              &ValueString,
3842                              KeyValueFullInformation,
3843                              FullInformation,
3844                              Size,
3845                              &Size);
3846     if (!NT_SUCCESS(Status))
3847     {
3848         ExFreePool(FullInformation);
3849         return Status;
3850     }
3851 
3852     *Information = FullInformation;
3853     return STATUS_SUCCESS;
3854 }
3855 
3856 RTL_GENERIC_COMPARE_RESULTS
3857 NTAPI
3858 PiCompareInstancePath(IN PRTL_AVL_TABLE Table,
3859                       IN PVOID FirstStruct,
3860                       IN PVOID SecondStruct)
3861 {
3862     /* FIXME: TODO */
3863     ASSERT(FALSE);
3864     return 0;
3865 }
3866 
3867 //
3868 //  The allocation function is called by the generic table package whenever
3869 //  it needs to allocate memory for the table.
3870 //
3871 
3872 PVOID
3873 NTAPI
3874 PiAllocateGenericTableEntry(IN PRTL_AVL_TABLE Table,
3875                             IN CLONG ByteSize)
3876 {
3877     /* FIXME: TODO */
3878     ASSERT(FALSE);
3879     return NULL;
3880 }
3881 
3882 VOID
3883 NTAPI
3884 PiFreeGenericTableEntry(IN PRTL_AVL_TABLE Table,
3885                         IN PVOID Buffer)
3886 {
3887     /* FIXME: TODO */
3888     ASSERT(FALSE);
3889 }
3890 
3891 VOID
3892 NTAPI
3893 PpInitializeDeviceReferenceTable(VOID)
3894 {
3895     /* Setup the guarded mutex and AVL table */
3896     KeInitializeGuardedMutex(&PpDeviceReferenceTableLock);
3897     RtlInitializeGenericTableAvl(
3898         &PpDeviceReferenceTable,
3899         (PRTL_AVL_COMPARE_ROUTINE)PiCompareInstancePath,
3900         (PRTL_AVL_ALLOCATE_ROUTINE)PiAllocateGenericTableEntry,
3901         (PRTL_AVL_FREE_ROUTINE)PiFreeGenericTableEntry,
3902         NULL);
3903 }
3904 
3905 BOOLEAN
3906 NTAPI
3907 PiInitPhase0(VOID)
3908 {
3909     /* Initialize the resource when accessing device registry data */
3910     ExInitializeResourceLite(&PpRegistryDeviceResource);
3911 
3912     /* Setup the device reference AVL table */
3913     PpInitializeDeviceReferenceTable();
3914     return TRUE;
3915 }
3916 
3917 BOOLEAN
3918 NTAPI
3919 PpInitSystem(VOID)
3920 {
3921     /* Check the initialization phase */
3922     switch (ExpInitializationPhase)
3923     {
3924     case 0:
3925 
3926         /* Do Phase 0 */
3927         return PiInitPhase0();
3928 
3929     case 1:
3930 
3931         /* Do Phase 1 */
3932         return TRUE;
3933         //return PiInitPhase1();
3934 
3935     default:
3936 
3937         /* Don't know any other phase! Bugcheck! */
3938         KeBugCheck(UNEXPECTED_INITIALIZATION_CALL);
3939         return FALSE;
3940     }
3941 }
3942 
3943 LONG IopNumberDeviceNodes;
3944 
3945 PDEVICE_NODE
3946 NTAPI
3947 PipAllocateDeviceNode(IN PDEVICE_OBJECT PhysicalDeviceObject)
3948 {
3949     PDEVICE_NODE DeviceNode;
3950     PAGED_CODE();
3951 
3952     /* Allocate it */
3953     DeviceNode = ExAllocatePoolWithTag(NonPagedPool, sizeof(DEVICE_NODE), TAG_IO_DEVNODE);
3954     if (!DeviceNode) return DeviceNode;
3955 
3956     /* Statistics */
3957     InterlockedIncrement(&IopNumberDeviceNodes);
3958 
3959     /* Set it up */
3960     RtlZeroMemory(DeviceNode, sizeof(DEVICE_NODE));
3961     DeviceNode->InterfaceType = InterfaceTypeUndefined;
3962     DeviceNode->BusNumber = -1;
3963     DeviceNode->ChildInterfaceType = InterfaceTypeUndefined;
3964     DeviceNode->ChildBusNumber = -1;
3965     DeviceNode->ChildBusTypeIndex = -1;
3966 //    KeInitializeEvent(&DeviceNode->EnumerationMutex, SynchronizationEvent, TRUE);
3967     InitializeListHead(&DeviceNode->DeviceArbiterList);
3968     InitializeListHead(&DeviceNode->DeviceTranslatorList);
3969     InitializeListHead(&DeviceNode->TargetDeviceNotify);
3970     InitializeListHead(&DeviceNode->DockInfo.ListEntry);
3971     InitializeListHead(&DeviceNode->PendedSetInterfaceState);
3972 
3973     /* Check if there is a PDO */
3974     if (PhysicalDeviceObject)
3975     {
3976         /* Link it and remove the init flag */
3977         DeviceNode->PhysicalDeviceObject = PhysicalDeviceObject;
3978         ((PEXTENDED_DEVOBJ_EXTENSION)PhysicalDeviceObject->DeviceObjectExtension)->DeviceNode = DeviceNode;
3979         PhysicalDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
3980     }
3981 
3982     /* Return the node */
3983     return DeviceNode;
3984 }
3985 
3986 /* PUBLIC FUNCTIONS **********************************************************/
3987 
3988 NTSTATUS
3989 NTAPI
3990 PnpBusTypeGuidGet(IN USHORT Index,
3991                   IN LPGUID BusTypeGuid)
3992 {
3993     NTSTATUS Status = STATUS_SUCCESS;
3994 
3995     /* Acquire the lock */
3996     ExAcquireFastMutex(&PnpBusTypeGuidList->Lock);
3997 
3998     /* Validate size */
3999     if (Index < PnpBusTypeGuidList->GuidCount)
4000     {
4001         /* Copy the data */
4002         RtlCopyMemory(BusTypeGuid, &PnpBusTypeGuidList->Guids[Index], sizeof(GUID));
4003     }
4004     else
4005     {
4006         /* Failure path */
4007         Status = STATUS_OBJECT_NAME_NOT_FOUND;
4008     }
4009 
4010     /* Release lock and return status */
4011     ExReleaseFastMutex(&PnpBusTypeGuidList->Lock);
4012     return Status;
4013 }
4014 
4015 NTSTATUS
4016 NTAPI
4017 PnpDeviceObjectToDeviceInstance(IN PDEVICE_OBJECT DeviceObject,
4018                                 IN PHANDLE DeviceInstanceHandle,
4019                                 IN ACCESS_MASK DesiredAccess)
4020 {
4021     NTSTATUS Status;
4022     HANDLE KeyHandle;
4023     PDEVICE_NODE DeviceNode;
4024     UNICODE_STRING KeyName = RTL_CONSTANT_STRING(L"\\REGISTRY\\MACHINE\\SYSTEM\\CURRENTCONTROLSET\\ENUM");
4025     PAGED_CODE();
4026 
4027     /* Open the enum key */
4028     Status = IopOpenRegistryKeyEx(&KeyHandle,
4029                                   NULL,
4030                                   &KeyName,
4031                                   KEY_READ);
4032     if (!NT_SUCCESS(Status)) return Status;
4033 
4034     /* Make sure we have an instance path */
4035     DeviceNode = IopGetDeviceNode(DeviceObject);
4036     if ((DeviceNode) && (DeviceNode->InstancePath.Length))
4037     {
4038         /* Get the instance key */
4039         Status = IopOpenRegistryKeyEx(DeviceInstanceHandle,
4040                                       KeyHandle,
4041                                       &DeviceNode->InstancePath,
4042                                       DesiredAccess);
4043     }
4044     else
4045     {
4046         /* Fail */
4047         Status = STATUS_INVALID_DEVICE_REQUEST;
4048     }
4049 
4050     /* Close the handle and return status */
4051     ZwClose(KeyHandle);
4052     return Status;
4053 }
4054 
4055 ULONG
4056 NTAPI
4057 PnpDetermineResourceListSize(IN PCM_RESOURCE_LIST ResourceList)
4058 {
4059     ULONG FinalSize, PartialSize, EntrySize, i, j;
4060     PCM_FULL_RESOURCE_DESCRIPTOR FullDescriptor;
4061     PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptor;
4062 
4063     /* If we don't have one, that's easy */
4064     if (!ResourceList) return 0;
4065 
4066     /* Start with the minimum size possible */
4067     FinalSize = FIELD_OFFSET(CM_RESOURCE_LIST, List);
4068 
4069     /* Loop each full descriptor */
4070     FullDescriptor = ResourceList->List;
4071     for (i = 0; i < ResourceList->Count; i++)
4072     {
4073         /* Start with the minimum size possible */
4074         PartialSize = FIELD_OFFSET(CM_FULL_RESOURCE_DESCRIPTOR, PartialResourceList) +
4075         FIELD_OFFSET(CM_PARTIAL_RESOURCE_LIST, PartialDescriptors);
4076 
4077         /* Loop each partial descriptor */
4078         PartialDescriptor = FullDescriptor->PartialResourceList.PartialDescriptors;
4079         for (j = 0; j < FullDescriptor->PartialResourceList.Count; j++)
4080         {
4081             /* Start with the minimum size possible */
4082             EntrySize = sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR);
4083 
4084             /* Check if there is extra data */
4085             if (PartialDescriptor->Type == CmResourceTypeDeviceSpecific)
4086             {
4087                 /* Add that data */
4088                 EntrySize += PartialDescriptor->u.DeviceSpecificData.DataSize;
4089             }
4090 
4091             /* The size of partial descriptors is bigger */
4092             PartialSize += EntrySize;
4093 
4094             /* Go to the next partial descriptor */
4095             PartialDescriptor = (PVOID)((ULONG_PTR)PartialDescriptor + EntrySize);
4096         }
4097 
4098         /* The size of full descriptors is bigger */
4099         FinalSize += PartialSize;
4100 
4101         /* Go to the next full descriptor */
4102         FullDescriptor = (PVOID)((ULONG_PTR)FullDescriptor + PartialSize);
4103     }
4104 
4105     /* Return the final size */
4106     return FinalSize;
4107 }
4108 
4109 NTSTATUS
4110 NTAPI
4111 PiGetDeviceRegistryProperty(IN PDEVICE_OBJECT DeviceObject,
4112                             IN ULONG ValueType,
4113                             IN PWSTR ValueName,
4114                             IN PWSTR KeyName,
4115                             OUT PVOID Buffer,
4116                             IN PULONG BufferLength)
4117 {
4118     NTSTATUS Status;
4119     HANDLE KeyHandle, SubHandle;
4120     UNICODE_STRING KeyString;
4121     PKEY_VALUE_FULL_INFORMATION KeyValueInfo = NULL;
4122     ULONG Length;
4123     PAGED_CODE();
4124 
4125     /* Find the instance key */
4126     Status = PnpDeviceObjectToDeviceInstance(DeviceObject, &KeyHandle, KEY_READ);
4127     if (NT_SUCCESS(Status))
4128     {
4129         /* Check for name given by caller */
4130         if (KeyName)
4131         {
4132             /* Open this key */
4133             RtlInitUnicodeString(&KeyString, KeyName);
4134             Status = IopOpenRegistryKeyEx(&SubHandle,
4135                                           KeyHandle,
4136                                           &KeyString,
4137                                           KEY_READ);
4138             if (NT_SUCCESS(Status))
4139             {
4140                 /* And use this handle instead */
4141                 ZwClose(KeyHandle);
4142                 KeyHandle = SubHandle;
4143             }
4144         }
4145 
4146         /* Check if sub-key handle succeeded (or no-op if no key name given) */
4147         if (NT_SUCCESS(Status))
4148         {
4149             /* Now get the size of the property */
4150             Status = IopGetRegistryValue(KeyHandle,
4151                                          ValueName,
4152                                          &KeyValueInfo);
4153         }
4154 
4155         /* Close the key */
4156         ZwClose(KeyHandle);
4157     }
4158 
4159     /* Fail if any of the registry operations failed */
4160     if (!NT_SUCCESS(Status)) return Status;
4161 
4162     /* Check how much data we have to copy */
4163     Length = KeyValueInfo->DataLength;
4164     if (*BufferLength >= Length)
4165     {
4166         /* Check for a match in the value type */
4167         if (KeyValueInfo->Type == ValueType)
4168         {
4169             /* Copy the data */
4170             RtlCopyMemory(Buffer,
4171                           (PVOID)((ULONG_PTR)KeyValueInfo +
4172                           KeyValueInfo->DataOffset),
4173                           Length);
4174         }
4175         else
4176         {
4177             /* Invalid registry property type, fail */
4178            Status = STATUS_INVALID_PARAMETER_2;
4179         }
4180     }
4181     else
4182     {
4183         /* Buffer is too small to hold data */
4184         Status = STATUS_BUFFER_TOO_SMALL;
4185     }
4186 
4187     /* Return the required buffer length, free the buffer, and return status */
4188     *BufferLength = Length;
4189     ExFreePool(KeyValueInfo);
4190     return Status;
4191 }
4192 
4193 #define PIP_RETURN_DATA(x, y)   {ReturnLength = x; Data = y; Status = STATUS_SUCCESS; break;}
4194 #define PIP_REGISTRY_DATA(x, y) {ValueName = x; ValueType = y; break;}
4195 #define PIP_UNIMPLEMENTED()     {UNIMPLEMENTED_DBGBREAK(); break;}
4196 
4197 /*
4198  * @implemented
4199  */
4200 NTSTATUS
4201 NTAPI
4202 IoGetDeviceProperty(IN PDEVICE_OBJECT DeviceObject,
4203                     IN DEVICE_REGISTRY_PROPERTY DeviceProperty,
4204                     IN ULONG BufferLength,
4205                     OUT PVOID PropertyBuffer,
4206                     OUT PULONG ResultLength)
4207 {
4208     PDEVICE_NODE DeviceNode = IopGetDeviceNode(DeviceObject);
4209     DEVICE_CAPABILITIES DeviceCaps;
4210     ULONG ReturnLength = 0, Length = 0, ValueType;
4211     PWCHAR ValueName = NULL, EnumeratorNameEnd, DeviceInstanceName;
4212     PVOID Data = NULL;
4213     NTSTATUS Status = STATUS_BUFFER_TOO_SMALL;
4214     GUID BusTypeGuid;
4215     POBJECT_NAME_INFORMATION ObjectNameInfo = NULL;
4216     BOOLEAN NullTerminate = FALSE;
4217 
4218     DPRINT("IoGetDeviceProperty(0x%p %d)\n", DeviceObject, DeviceProperty);
4219 
4220     /* Assume failure */
4221     *ResultLength = 0;
4222 
4223     /* Only PDOs can call this */
4224     if (!DeviceNode) return STATUS_INVALID_DEVICE_REQUEST;
4225 
4226     /* Handle all properties */
4227     switch (DeviceProperty)
4228     {
4229         case DevicePropertyBusTypeGuid:
4230 
4231             /* Get the GUID from the internal cache */
4232             Status = PnpBusTypeGuidGet(DeviceNode->ChildBusTypeIndex, &BusTypeGuid);
4233             if (!NT_SUCCESS(Status)) return Status;
4234 
4235             /* This is the format of the returned data */
4236             PIP_RETURN_DATA(sizeof(GUID), &BusTypeGuid);
4237 
4238         case DevicePropertyLegacyBusType:
4239 
4240             /* Validate correct interface type */
4241             if (DeviceNode->ChildInterfaceType == InterfaceTypeUndefined)
4242                 return STATUS_OBJECT_NAME_NOT_FOUND;
4243 
4244             /* This is the format of the returned data */
4245             PIP_RETURN_DATA(sizeof(INTERFACE_TYPE), &DeviceNode->ChildInterfaceType);
4246 
4247         case DevicePropertyBusNumber:
4248 
4249             /* Validate correct bus number */
4250             if ((DeviceNode->ChildBusNumber & 0x80000000) == 0x80000000)
4251                 return STATUS_OBJECT_NAME_NOT_FOUND;
4252 
4253             /* This is the format of the returned data */
4254             PIP_RETURN_DATA(sizeof(ULONG), &DeviceNode->ChildBusNumber);
4255 
4256         case DevicePropertyEnumeratorName:
4257 
4258             /* Get the instance path */
4259             DeviceInstanceName = DeviceNode->InstancePath.Buffer;
4260 
4261             /* Sanity checks */
4262             ASSERT((BufferLength & 1) == 0);
4263             ASSERT(DeviceInstanceName != NULL);
4264 
4265             /* Get the name from the path */
4266             EnumeratorNameEnd = wcschr(DeviceInstanceName, OBJ_NAME_PATH_SEPARATOR);
4267             ASSERT(EnumeratorNameEnd);
4268 
4269             /* This string needs to be NULL-terminated */
4270             NullTerminate = TRUE;
4271 
4272             /* This is the format of the returned data */
4273             PIP_RETURN_DATA((ULONG)(EnumeratorNameEnd - DeviceInstanceName) * sizeof(WCHAR),
4274                             DeviceInstanceName);
4275 
4276         case DevicePropertyAddress:
4277 
4278             /* Query the device caps */
4279             Status = IopQueryDeviceCapabilities(DeviceNode, &DeviceCaps);
4280             if (!NT_SUCCESS(Status) || (DeviceCaps.Address == MAXULONG))
4281                 return STATUS_OBJECT_NAME_NOT_FOUND;
4282 
4283             /* This is the format of the returned data */
4284             PIP_RETURN_DATA(sizeof(ULONG), &DeviceCaps.Address);
4285 
4286         case DevicePropertyBootConfigurationTranslated:
4287 
4288             /* Validate we have resources */
4289             if (!DeviceNode->BootResources)
4290 //            if (!DeviceNode->BootResourcesTranslated) // FIXFIX: Need this field
4291             {
4292                 /* No resources will still fake success, but with 0 bytes */
4293                 *ResultLength = 0;
4294                 return STATUS_SUCCESS;
4295             }
4296 
4297             /* This is the format of the returned data */
4298             PIP_RETURN_DATA(PnpDetermineResourceListSize(DeviceNode->BootResources), // FIXFIX: Should use BootResourcesTranslated
4299                             DeviceNode->BootResources); // FIXFIX: Should use BootResourcesTranslated
4300 
4301         case DevicePropertyPhysicalDeviceObjectName:
4302 
4303             /* Sanity check for Unicode-sized string */
4304             ASSERT((BufferLength & 1) == 0);
4305 
4306             /* Allocate name buffer */
4307             Length = BufferLength + sizeof(OBJECT_NAME_INFORMATION);
4308             ObjectNameInfo = ExAllocatePool(PagedPool, Length);
4309             if (!ObjectNameInfo) return STATUS_INSUFFICIENT_RESOURCES;
4310 
4311             /* Query the PDO name */
4312             Status = ObQueryNameString(DeviceObject,
4313                                        ObjectNameInfo,
4314                                        Length,
4315                                        ResultLength);
4316             if (Status == STATUS_INFO_LENGTH_MISMATCH)
4317             {
4318                 /* It's up to the caller to try again */
4319                 Status = STATUS_BUFFER_TOO_SMALL;
4320             }
4321 
4322             /* This string needs to be NULL-terminated */
4323             NullTerminate = TRUE;
4324 
4325             /* Return if successful */
4326             if (NT_SUCCESS(Status)) PIP_RETURN_DATA(ObjectNameInfo->Name.Length,
4327                                                     ObjectNameInfo->Name.Buffer);
4328 
4329             /* Let the caller know how big the name is */
4330             *ResultLength -= sizeof(OBJECT_NAME_INFORMATION);
4331             break;
4332 
4333         case DevicePropertyRemovalPolicy:
4334             PIP_RETURN_DATA(sizeof(UCHAR), &DeviceNode->RemovalPolicy);
4335 
4336         /* Handle the registry-based properties */
4337         case DevicePropertyUINumber:
4338             PIP_REGISTRY_DATA(REGSTR_VAL_UI_NUMBER, REG_DWORD);
4339         case DevicePropertyLocationInformation:
4340             PIP_REGISTRY_DATA(REGSTR_VAL_LOCATION_INFORMATION, REG_SZ);
4341         case DevicePropertyDeviceDescription:
4342             PIP_REGISTRY_DATA(REGSTR_VAL_DEVDESC, REG_SZ);
4343         case DevicePropertyHardwareID:
4344             PIP_REGISTRY_DATA(REGSTR_VAL_HARDWAREID, REG_MULTI_SZ);
4345         case DevicePropertyCompatibleIDs:
4346             PIP_REGISTRY_DATA(REGSTR_VAL_COMPATIBLEIDS, REG_MULTI_SZ);
4347         case DevicePropertyBootConfiguration:
4348             PIP_REGISTRY_DATA(REGSTR_VAL_BOOTCONFIG, REG_RESOURCE_LIST);
4349         case DevicePropertyClassName:
4350             PIP_REGISTRY_DATA(REGSTR_VAL_CLASS, REG_SZ);
4351         case DevicePropertyClassGuid:
4352             PIP_REGISTRY_DATA(REGSTR_VAL_CLASSGUID, REG_SZ);
4353         case DevicePropertyDriverKeyName:
4354             PIP_REGISTRY_DATA(REGSTR_VAL_DRIVER, REG_SZ);
4355         case DevicePropertyManufacturer:
4356             PIP_REGISTRY_DATA(REGSTR_VAL_MFG, REG_SZ);
4357         case DevicePropertyFriendlyName:
4358             PIP_REGISTRY_DATA(REGSTR_VAL_FRIENDLYNAME, REG_SZ);
4359         case DevicePropertyContainerID:
4360             //PIP_REGISTRY_DATA(REGSTR_VAL_CONTAINERID, REG_SZ); // Win7
4361             PIP_UNIMPLEMENTED();
4362             break;
4363         case DevicePropertyInstallState:
4364             PIP_REGISTRY_DATA(REGSTR_VAL_CONFIGFLAGS, REG_DWORD);
4365             break;
4366         case DevicePropertyResourceRequirements:
4367             PIP_UNIMPLEMENTED();
4368         case DevicePropertyAllocatedResources:
4369             PIP_UNIMPLEMENTED();
4370         default:
4371             return STATUS_INVALID_PARAMETER_2;
4372     }
4373 
4374     /* Having a registry value name implies registry data */
4375     if (ValueName)
4376     {
4377         /* We know up-front how much data to expect */
4378         *ResultLength = BufferLength;
4379 
4380         /* Go get the data, use the LogConf subkey if necessary */
4381         Status = PiGetDeviceRegistryProperty(DeviceObject,
4382                                              ValueType,
4383                                              ValueName,
4384                                              (DeviceProperty ==
4385                                               DevicePropertyBootConfiguration) ?
4386                                              L"LogConf":  NULL,
4387                                              PropertyBuffer,
4388                                              ResultLength);
4389     }
4390     else if (NT_SUCCESS(Status))
4391     {
4392         /* We know up-front how much data to expect, check the caller's buffer */
4393         *ResultLength = ReturnLength + (NullTerminate ? sizeof(UNICODE_NULL) : 0);
4394         if (*ResultLength <= BufferLength)
4395         {
4396             /* Buffer is all good, copy the data */
4397             RtlCopyMemory(PropertyBuffer, Data, ReturnLength);
4398 
4399             /* Check if we need to NULL-terminate the string */
4400             if (NullTerminate)
4401             {
4402                 /* Terminate the string */
4403                 ((PWCHAR)PropertyBuffer)[ReturnLength / sizeof(WCHAR)] = UNICODE_NULL;
4404             }
4405 
4406             /* This is the success path */
4407             Status = STATUS_SUCCESS;
4408         }
4409         else
4410         {
4411             /* Failure path */
4412             Status = STATUS_BUFFER_TOO_SMALL;
4413         }
4414     }
4415 
4416     /* Free any allocation we may have made, and return the status code */
4417     if (ObjectNameInfo) ExFreePool(ObjectNameInfo);
4418     return Status;
4419 }
4420 
4421 /*
4422  * @implemented
4423  */
4424 VOID
4425 NTAPI
4426 IoInvalidateDeviceState(IN PDEVICE_OBJECT PhysicalDeviceObject)
4427 {
4428     PDEVICE_NODE DeviceNode = IopGetDeviceNode(PhysicalDeviceObject);
4429     IO_STACK_LOCATION Stack;
4430     ULONG PnPFlags;
4431     NTSTATUS Status;
4432     IO_STATUS_BLOCK IoStatusBlock;
4433 
4434     RtlZeroMemory(&Stack, sizeof(IO_STACK_LOCATION));
4435     Stack.MajorFunction = IRP_MJ_PNP;
4436     Stack.MinorFunction = IRP_MN_QUERY_PNP_DEVICE_STATE;
4437 
4438     Status = IopSynchronousCall(PhysicalDeviceObject, &Stack, (PVOID*)&PnPFlags);
4439     if (!NT_SUCCESS(Status))
4440     {
4441         if (Status != STATUS_NOT_SUPPORTED)
4442         {
4443             DPRINT1("IRP_MN_QUERY_PNP_DEVICE_STATE failed with status 0x%lx\n", Status);
4444         }
4445         return;
4446     }
4447 
4448     if (PnPFlags & PNP_DEVICE_NOT_DISABLEABLE)
4449         DeviceNode->UserFlags |= DNUF_NOT_DISABLEABLE;
4450     else
4451         DeviceNode->UserFlags &= ~DNUF_NOT_DISABLEABLE;
4452 
4453     if (PnPFlags & PNP_DEVICE_DONT_DISPLAY_IN_UI)
4454         DeviceNode->UserFlags |= DNUF_DONT_SHOW_IN_UI;
4455     else
4456         DeviceNode->UserFlags &= ~DNUF_DONT_SHOW_IN_UI;
4457 
4458     if ((PnPFlags & PNP_DEVICE_REMOVED) ||
4459         ((PnPFlags & PNP_DEVICE_FAILED) && !(PnPFlags & PNP_DEVICE_RESOURCE_REQUIREMENTS_CHANGED)))
4460     {
4461         /* Flag it if it's failed */
4462         if (PnPFlags & PNP_DEVICE_FAILED) DeviceNode->Problem = CM_PROB_FAILED_POST_START;
4463 
4464         /* Send removal IRPs to all of its children */
4465         IopPrepareDeviceForRemoval(PhysicalDeviceObject, TRUE);
4466 
4467         /* Send surprise removal */
4468         IopSendSurpriseRemoval(PhysicalDeviceObject);
4469 
4470         /* Tell the user-mode PnP manager that a device was removed */
4471         IopQueueTargetDeviceEvent(&GUID_DEVICE_SURPRISE_REMOVAL,
4472                                   &DeviceNode->InstancePath);
4473 
4474         IopSendRemoveDevice(PhysicalDeviceObject);
4475     }
4476     else if ((PnPFlags & PNP_DEVICE_FAILED) && (PnPFlags & PNP_DEVICE_RESOURCE_REQUIREMENTS_CHANGED))
4477     {
4478         /* Stop for resource rebalance */
4479         Status = IopStopDevice(DeviceNode);
4480         if (!NT_SUCCESS(Status))
4481         {
4482             DPRINT1("Failed to stop device for rebalancing\n");
4483 
4484             /* Stop failed so don't rebalance */
4485             PnPFlags &= ~PNP_DEVICE_RESOURCE_REQUIREMENTS_CHANGED;
4486         }
4487     }
4488 
4489     /* Resource rebalance */
4490     if (PnPFlags & PNP_DEVICE_RESOURCE_REQUIREMENTS_CHANGED)
4491     {
4492         DPRINT("Sending IRP_MN_QUERY_RESOURCES to device stack\n");
4493 
4494         Status = IopInitiatePnpIrp(PhysicalDeviceObject,
4495                                    &IoStatusBlock,
4496                                    IRP_MN_QUERY_RESOURCES,
4497                                    NULL);
4498         if (NT_SUCCESS(Status) && IoStatusBlock.Information)
4499         {
4500             DeviceNode->BootResources =
4501             (PCM_RESOURCE_LIST)IoStatusBlock.Information;
4502             IopDeviceNodeSetFlag(DeviceNode, DNF_HAS_BOOT_CONFIG);
4503         }
4504         else
4505         {
4506             DPRINT("IopInitiatePnpIrp() failed (Status %x) or IoStatusBlock.Information=NULL\n", Status);
4507             DeviceNode->BootResources = NULL;
4508         }
4509 
4510         DPRINT("Sending IRP_MN_QUERY_RESOURCE_REQUIREMENTS to device stack\n");
4511 
4512         Status = IopInitiatePnpIrp(PhysicalDeviceObject,
4513                                    &IoStatusBlock,
4514                                    IRP_MN_QUERY_RESOURCE_REQUIREMENTS,
4515                                    NULL);
4516         if (NT_SUCCESS(Status))
4517         {
4518             DeviceNode->ResourceRequirements =
4519             (PIO_RESOURCE_REQUIREMENTS_LIST)IoStatusBlock.Information;
4520         }
4521         else
4522         {
4523             DPRINT("IopInitiatePnpIrp() failed (Status %08lx)\n", Status);
4524             DeviceNode->ResourceRequirements = NULL;
4525         }
4526 
4527         /* IRP_MN_FILTER_RESOURCE_REQUIREMENTS is called indirectly by IopStartDevice */
4528         if (IopStartDevice(DeviceNode) != STATUS_SUCCESS)
4529         {
4530             DPRINT1("Restart after resource rebalance failed\n");
4531 
4532             DeviceNode->Flags &= ~(DNF_STARTED | DNF_START_REQUEST_PENDING);
4533             DeviceNode->Flags |= DNF_START_FAILED;
4534 
4535             IopRemoveDevice(DeviceNode);
4536         }
4537     }
4538 }
4539 
4540 /**
4541  * @name IoOpenDeviceRegistryKey
4542  *
4543  * Open a registry key unique for a specified driver or device instance.
4544  *
4545  * @param DeviceObject   Device to get the registry key for.
4546  * @param DevInstKeyType Type of the key to return.
4547  * @param DesiredAccess  Access mask (eg. KEY_READ | KEY_WRITE).
4548  * @param DevInstRegKey  Handle to the opened registry key on
4549  *                       successful return.
4550  *
4551  * @return Status.
4552  *
4553  * @implemented
4554  */
4555 NTSTATUS
4556 NTAPI
4557 IoOpenDeviceRegistryKey(IN PDEVICE_OBJECT DeviceObject,
4558                         IN ULONG DevInstKeyType,
4559                         IN ACCESS_MASK DesiredAccess,
4560                         OUT PHANDLE DevInstRegKey)
4561 {
4562    static WCHAR RootKeyName[] =
4563       L"\\Registry\\Machine\\System\\CurrentControlSet\\";
4564    static WCHAR ProfileKeyName[] =
4565       L"Hardware Profiles\\Current\\System\\CurrentControlSet\\";
4566    static WCHAR ClassKeyName[] = L"Control\\Class\\";
4567    static WCHAR EnumKeyName[] = L"Enum\\";
4568    static WCHAR DeviceParametersKeyName[] = L"Device Parameters";
4569    ULONG KeyNameLength;
4570    PWSTR KeyNameBuffer;
4571    UNICODE_STRING KeyName;
4572    ULONG DriverKeyLength;
4573    OBJECT_ATTRIBUTES ObjectAttributes;
4574    PDEVICE_NODE DeviceNode = NULL;
4575    NTSTATUS Status;
4576 
4577    DPRINT("IoOpenDeviceRegistryKey() called\n");
4578 
4579    if ((DevInstKeyType & (PLUGPLAY_REGKEY_DEVICE | PLUGPLAY_REGKEY_DRIVER)) == 0)
4580    {
4581        DPRINT1("IoOpenDeviceRegistryKey(): got wrong params, exiting... \n");
4582        return STATUS_INVALID_PARAMETER;
4583    }
4584 
4585    if (!IopIsValidPhysicalDeviceObject(DeviceObject))
4586        return STATUS_INVALID_DEVICE_REQUEST;
4587    DeviceNode = IopGetDeviceNode(DeviceObject);
4588 
4589    /*
4590     * Calculate the length of the base key name. This is the full
4591     * name for driver key or the name excluding "Device Parameters"
4592     * subkey for device key.
4593     */
4594 
4595    KeyNameLength = sizeof(RootKeyName);
4596    if (DevInstKeyType & PLUGPLAY_REGKEY_CURRENT_HWPROFILE)
4597       KeyNameLength += sizeof(ProfileKeyName) - sizeof(UNICODE_NULL);
4598    if (DevInstKeyType & PLUGPLAY_REGKEY_DRIVER)
4599    {
4600       KeyNameLength += sizeof(ClassKeyName) - sizeof(UNICODE_NULL);
4601       Status = IoGetDeviceProperty(DeviceObject, DevicePropertyDriverKeyName,
4602                                    0, NULL, &DriverKeyLength);
4603       if (Status != STATUS_BUFFER_TOO_SMALL)
4604          return Status;
4605       KeyNameLength += DriverKeyLength;
4606    }
4607    else
4608    {
4609       KeyNameLength += sizeof(EnumKeyName) - sizeof(UNICODE_NULL) +
4610                        DeviceNode->InstancePath.Length;
4611    }
4612 
4613    /*
4614     * Now allocate the buffer for the key name...
4615     */
4616 
4617    KeyNameBuffer = ExAllocatePool(PagedPool, KeyNameLength);
4618    if (KeyNameBuffer == NULL)
4619       return STATUS_INSUFFICIENT_RESOURCES;
4620 
4621    KeyName.Length = 0;
4622    KeyName.MaximumLength = (USHORT)KeyNameLength;
4623    KeyName.Buffer = KeyNameBuffer;
4624 
4625    /*
4626     * ...and build the key name.
4627     */
4628 
4629    KeyName.Length += sizeof(RootKeyName) - sizeof(UNICODE_NULL);
4630    RtlCopyMemory(KeyNameBuffer, RootKeyName, KeyName.Length);
4631 
4632    if (DevInstKeyType & PLUGPLAY_REGKEY_CURRENT_HWPROFILE)
4633       RtlAppendUnicodeToString(&KeyName, ProfileKeyName);
4634 
4635    if (DevInstKeyType & PLUGPLAY_REGKEY_DRIVER)
4636    {
4637       RtlAppendUnicodeToString(&KeyName, ClassKeyName);
4638       Status = IoGetDeviceProperty(DeviceObject, DevicePropertyDriverKeyName,
4639                                    DriverKeyLength, KeyNameBuffer +
4640                                    (KeyName.Length / sizeof(WCHAR)),
4641                                    &DriverKeyLength);
4642       if (!NT_SUCCESS(Status))
4643       {
4644          DPRINT1("Call to IoGetDeviceProperty() failed with Status 0x%08lx\n", Status);
4645          ExFreePool(KeyNameBuffer);
4646          return Status;
4647       }
4648       KeyName.Length += (USHORT)DriverKeyLength - sizeof(UNICODE_NULL);
4649    }
4650    else
4651    {
4652       RtlAppendUnicodeToString(&KeyName, EnumKeyName);
4653       Status = RtlAppendUnicodeStringToString(&KeyName, &DeviceNode->InstancePath);
4654       if (DeviceNode->InstancePath.Length == 0)
4655       {
4656          ExFreePool(KeyNameBuffer);
4657          return Status;
4658       }
4659    }
4660 
4661    /*
4662     * Open the base key.
4663     */
4664    Status = IopOpenRegistryKeyEx(DevInstRegKey, NULL, &KeyName, DesiredAccess);
4665    if (!NT_SUCCESS(Status))
4666    {
4667       DPRINT1("IoOpenDeviceRegistryKey(%wZ): Base key doesn't exist, exiting... (Status 0x%08lx)\n", &KeyName, Status);
4668       ExFreePool(KeyNameBuffer);
4669       return Status;
4670    }
4671    ExFreePool(KeyNameBuffer);
4672 
4673    /*
4674     * For driver key we're done now.
4675     */
4676 
4677    if (DevInstKeyType & PLUGPLAY_REGKEY_DRIVER)
4678       return Status;
4679 
4680    /*
4681     * Let's go further. For device key we must open "Device Parameters"
4682     * subkey and create it if it doesn't exist yet.
4683     */
4684 
4685    RtlInitUnicodeString(&KeyName, DeviceParametersKeyName);
4686    InitializeObjectAttributes(&ObjectAttributes,
4687                               &KeyName,
4688                               OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
4689                               *DevInstRegKey,
4690                               NULL);
4691    Status = ZwCreateKey(DevInstRegKey,
4692                         DesiredAccess,
4693                         &ObjectAttributes,
4694                         0,
4695                         NULL,
4696                         REG_OPTION_NON_VOLATILE,
4697                         NULL);
4698    ZwClose(ObjectAttributes.RootDirectory);
4699 
4700    return Status;
4701 }
4702 
4703 static
4704 NTSTATUS
4705 IopQueryRemoveChildDevices(PDEVICE_NODE ParentDeviceNode, BOOLEAN Force)
4706 {
4707     PDEVICE_NODE ChildDeviceNode, NextDeviceNode, FailedRemoveDevice;
4708     NTSTATUS Status;
4709     KIRQL OldIrql;
4710 
4711     KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql);
4712     ChildDeviceNode = ParentDeviceNode->Child;
4713     while (ChildDeviceNode != NULL)
4714     {
4715         NextDeviceNode = ChildDeviceNode->Sibling;
4716         KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql);
4717 
4718         Status = IopPrepareDeviceForRemoval(ChildDeviceNode->PhysicalDeviceObject, Force);
4719         if (!NT_SUCCESS(Status))
4720         {
4721             FailedRemoveDevice = ChildDeviceNode;
4722             goto cleanup;
4723         }
4724 
4725         KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql);
4726         ChildDeviceNode = NextDeviceNode;
4727     }
4728     KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql);
4729 
4730     return STATUS_SUCCESS;
4731 
4732 cleanup:
4733     KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql);
4734     ChildDeviceNode = ParentDeviceNode->Child;
4735     while (ChildDeviceNode != NULL)
4736     {
4737         NextDeviceNode = ChildDeviceNode->Sibling;
4738         KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql);
4739 
4740         IopCancelPrepareDeviceForRemoval(ChildDeviceNode->PhysicalDeviceObject);
4741 
4742         /* IRP_MN_CANCEL_REMOVE_DEVICE is also sent to the device
4743          * that failed the IRP_MN_QUERY_REMOVE_DEVICE request */
4744         if (ChildDeviceNode == FailedRemoveDevice)
4745             return Status;
4746 
4747         ChildDeviceNode = NextDeviceNode;
4748 
4749         KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql);
4750     }
4751     KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql);
4752 
4753     return Status;
4754 }
4755 
4756 static
4757 VOID
4758 IopSendRemoveChildDevices(PDEVICE_NODE ParentDeviceNode)
4759 {
4760     PDEVICE_NODE ChildDeviceNode, NextDeviceNode;
4761     KIRQL OldIrql;
4762 
4763     KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql);
4764     ChildDeviceNode = ParentDeviceNode->Child;
4765     while (ChildDeviceNode != NULL)
4766     {
4767         NextDeviceNode = ChildDeviceNode->Sibling;
4768         KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql);
4769 
4770         IopSendRemoveDevice(ChildDeviceNode->PhysicalDeviceObject);
4771 
4772         ChildDeviceNode = NextDeviceNode;
4773 
4774         KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql);
4775     }
4776     KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql);
4777 }
4778 
4779 static
4780 VOID
4781 IopCancelRemoveChildDevices(PDEVICE_NODE ParentDeviceNode)
4782 {
4783     PDEVICE_NODE ChildDeviceNode, NextDeviceNode;
4784     KIRQL OldIrql;
4785 
4786     KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql);
4787     ChildDeviceNode = ParentDeviceNode->Child;
4788     while (ChildDeviceNode != NULL)
4789     {
4790         NextDeviceNode = ChildDeviceNode->Sibling;
4791         KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql);
4792 
4793         IopCancelPrepareDeviceForRemoval(ChildDeviceNode->PhysicalDeviceObject);
4794 
4795         ChildDeviceNode = NextDeviceNode;
4796 
4797         KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql);
4798     }
4799     KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql);
4800 }
4801 
4802 static
4803 NTSTATUS
4804 IopQueryRemoveDeviceRelations(PDEVICE_RELATIONS DeviceRelations, BOOLEAN Force)
4805 {
4806     /* This function DOES NOT dereference the device objects on SUCCESS
4807      * but it DOES dereference device objects on FAILURE */
4808 
4809     ULONG i, j;
4810     NTSTATUS Status;
4811 
4812     for (i = 0; i < DeviceRelations->Count; i++)
4813     {
4814         Status = IopPrepareDeviceForRemoval(DeviceRelations->Objects[i], Force);
4815         if (!NT_SUCCESS(Status))
4816         {
4817             j = i;
4818             goto cleanup;
4819         }
4820     }
4821 
4822     return STATUS_SUCCESS;
4823 
4824 cleanup:
4825     /* IRP_MN_CANCEL_REMOVE_DEVICE is also sent to the device
4826      * that failed the IRP_MN_QUERY_REMOVE_DEVICE request */
4827     for (i = 0; i <= j; i++)
4828     {
4829         IopCancelPrepareDeviceForRemoval(DeviceRelations->Objects[i]);
4830         ObDereferenceObject(DeviceRelations->Objects[i]);
4831         DeviceRelations->Objects[i] = NULL;
4832     }
4833     for (; i < DeviceRelations->Count; i++)
4834     {
4835         ObDereferenceObject(DeviceRelations->Objects[i]);
4836         DeviceRelations->Objects[i] = NULL;
4837     }
4838     ExFreePool(DeviceRelations);
4839 
4840     return Status;
4841 }
4842 
4843 static
4844 VOID
4845 IopSendRemoveDeviceRelations(PDEVICE_RELATIONS DeviceRelations)
4846 {
4847     /* This function DOES dereference the device objects in all cases */
4848 
4849     ULONG i;
4850 
4851     for (i = 0; i < DeviceRelations->Count; i++)
4852     {
4853         IopSendRemoveDevice(DeviceRelations->Objects[i]);
4854         DeviceRelations->Objects[i] = NULL;
4855     }
4856 
4857     ExFreePool(DeviceRelations);
4858 }
4859 
4860 static
4861 VOID
4862 IopCancelRemoveDeviceRelations(PDEVICE_RELATIONS DeviceRelations)
4863 {
4864     /* This function DOES dereference the device objects in all cases */
4865 
4866     ULONG i;
4867 
4868     for (i = 0; i < DeviceRelations->Count; i++)
4869     {
4870         IopCancelPrepareDeviceForRemoval(DeviceRelations->Objects[i]);
4871         ObDereferenceObject(DeviceRelations->Objects[i]);
4872         DeviceRelations->Objects[i] = NULL;
4873     }
4874 
4875     ExFreePool(DeviceRelations);
4876 }
4877 
4878 VOID
4879 IopCancelPrepareDeviceForRemoval(PDEVICE_OBJECT DeviceObject)
4880 {
4881     IO_STACK_LOCATION Stack;
4882     IO_STATUS_BLOCK IoStatusBlock;
4883     PDEVICE_RELATIONS DeviceRelations;
4884     NTSTATUS Status;
4885 
4886     IopCancelRemoveDevice(DeviceObject);
4887 
4888     Stack.Parameters.QueryDeviceRelations.Type = RemovalRelations;
4889 
4890     Status = IopInitiatePnpIrp(DeviceObject,
4891                                &IoStatusBlock,
4892                                IRP_MN_QUERY_DEVICE_RELATIONS,
4893                                &Stack);
4894     if (!NT_SUCCESS(Status))
4895     {
4896         DPRINT("IopInitiatePnpIrp() failed with status 0x%08lx\n", Status);
4897         DeviceRelations = NULL;
4898     }
4899     else
4900     {
4901         DeviceRelations = (PDEVICE_RELATIONS)IoStatusBlock.Information;
4902     }
4903 
4904     if (DeviceRelations)
4905         IopCancelRemoveDeviceRelations(DeviceRelations);
4906 }
4907 
4908 NTSTATUS
4909 IopPrepareDeviceForRemoval(IN PDEVICE_OBJECT DeviceObject, BOOLEAN Force)
4910 {
4911     PDEVICE_NODE DeviceNode = IopGetDeviceNode(DeviceObject);
4912     IO_STACK_LOCATION Stack;
4913     IO_STATUS_BLOCK IoStatusBlock;
4914     PDEVICE_RELATIONS DeviceRelations;
4915     NTSTATUS Status;
4916 
4917     if ((DeviceNode->UserFlags & DNUF_NOT_DISABLEABLE) && !Force)
4918     {
4919         DPRINT1("Removal not allowed for %wZ\n", &DeviceNode->InstancePath);
4920         return STATUS_UNSUCCESSFUL;
4921     }
4922 
4923     if (!Force && IopQueryRemoveDevice(DeviceObject) != STATUS_SUCCESS)
4924     {
4925         DPRINT1("Removal vetoed by failing the query remove request\n");
4926 
4927         IopCancelRemoveDevice(DeviceObject);
4928 
4929         return STATUS_UNSUCCESSFUL;
4930     }
4931 
4932     Stack.Parameters.QueryDeviceRelations.Type = RemovalRelations;
4933 
4934     Status = IopInitiatePnpIrp(DeviceObject,
4935                                &IoStatusBlock,
4936                                IRP_MN_QUERY_DEVICE_RELATIONS,
4937                                &Stack);
4938     if (!NT_SUCCESS(Status))
4939     {
4940         DPRINT("IopInitiatePnpIrp() failed with status 0x%08lx\n", Status);
4941         DeviceRelations = NULL;
4942     }
4943     else
4944     {
4945         DeviceRelations = (PDEVICE_RELATIONS)IoStatusBlock.Information;
4946     }
4947 
4948     if (DeviceRelations)
4949     {
4950         Status = IopQueryRemoveDeviceRelations(DeviceRelations, Force);
4951         if (!NT_SUCCESS(Status))
4952             return Status;
4953     }
4954 
4955     Status = IopQueryRemoveChildDevices(DeviceNode, Force);
4956     if (!NT_SUCCESS(Status))
4957     {
4958         if (DeviceRelations)
4959             IopCancelRemoveDeviceRelations(DeviceRelations);
4960         return Status;
4961     }
4962 
4963     if (DeviceRelations)
4964         IopSendRemoveDeviceRelations(DeviceRelations);
4965     IopSendRemoveChildDevices(DeviceNode);
4966 
4967     return STATUS_SUCCESS;
4968 }
4969 
4970 NTSTATUS
4971 IopRemoveDevice(PDEVICE_NODE DeviceNode)
4972 {
4973     NTSTATUS Status;
4974 
4975     DPRINT("Removing device: %wZ\n", &DeviceNode->InstancePath);
4976 
4977     Status = IopPrepareDeviceForRemoval(DeviceNode->PhysicalDeviceObject, FALSE);
4978     if (NT_SUCCESS(Status))
4979     {
4980         IopSendRemoveDevice(DeviceNode->PhysicalDeviceObject);
4981         IopQueueTargetDeviceEvent(&GUID_DEVICE_SAFE_REMOVAL,
4982                                   &DeviceNode->InstancePath);
4983         return STATUS_SUCCESS;
4984     }
4985 
4986     return Status;
4987 }
4988 
4989 /*
4990  * @implemented
4991  */
4992 VOID
4993 NTAPI
4994 IoRequestDeviceEject(IN PDEVICE_OBJECT PhysicalDeviceObject)
4995 {
4996     PDEVICE_NODE DeviceNode = IopGetDeviceNode(PhysicalDeviceObject);
4997     PDEVICE_RELATIONS DeviceRelations;
4998     IO_STATUS_BLOCK IoStatusBlock;
4999     IO_STACK_LOCATION Stack;
5000     DEVICE_CAPABILITIES Capabilities;
5001     NTSTATUS Status;
5002 
5003     IopQueueTargetDeviceEvent(&GUID_DEVICE_KERNEL_INITIATED_EJECT,
5004                               &DeviceNode->InstancePath);
5005 
5006     if (IopQueryDeviceCapabilities(DeviceNode, &Capabilities) != STATUS_SUCCESS)
5007     {
5008         goto cleanup;
5009     }
5010 
5011     Stack.Parameters.QueryDeviceRelations.Type = EjectionRelations;
5012 
5013     Status = IopInitiatePnpIrp(PhysicalDeviceObject,
5014                                &IoStatusBlock,
5015                                IRP_MN_QUERY_DEVICE_RELATIONS,
5016                                &Stack);
5017     if (!NT_SUCCESS(Status))
5018     {
5019         DPRINT("IopInitiatePnpIrp() failed with status 0x%08lx\n", Status);
5020         DeviceRelations = NULL;
5021     }
5022     else
5023     {
5024         DeviceRelations = (PDEVICE_RELATIONS)IoStatusBlock.Information;
5025     }
5026 
5027     if (DeviceRelations)
5028     {
5029         Status = IopQueryRemoveDeviceRelations(DeviceRelations, FALSE);
5030         if (!NT_SUCCESS(Status))
5031             goto cleanup;
5032     }
5033 
5034     Status = IopQueryRemoveChildDevices(DeviceNode, FALSE);
5035     if (!NT_SUCCESS(Status))
5036     {
5037         if (DeviceRelations)
5038             IopCancelRemoveDeviceRelations(DeviceRelations);
5039         goto cleanup;
5040     }
5041 
5042     if (IopPrepareDeviceForRemoval(PhysicalDeviceObject, FALSE) != STATUS_SUCCESS)
5043     {
5044         if (DeviceRelations)
5045             IopCancelRemoveDeviceRelations(DeviceRelations);
5046         IopCancelRemoveChildDevices(DeviceNode);
5047         goto cleanup;
5048     }
5049 
5050     if (DeviceRelations)
5051         IopSendRemoveDeviceRelations(DeviceRelations);
5052     IopSendRemoveChildDevices(DeviceNode);
5053 
5054     DeviceNode->Problem = CM_PROB_HELD_FOR_EJECT;
5055     if (Capabilities.EjectSupported)
5056     {
5057         if (IopSendEject(PhysicalDeviceObject) != STATUS_SUCCESS)
5058         {
5059             goto cleanup;
5060         }
5061     }
5062     else
5063     {
5064         DeviceNode->Flags |= DNF_DISABLED;
5065     }
5066 
5067     IopQueueTargetDeviceEvent(&GUID_DEVICE_EJECT,
5068                               &DeviceNode->InstancePath);
5069 
5070     return;
5071 
5072 cleanup:
5073     IopQueueTargetDeviceEvent(&GUID_DEVICE_EJECT_VETOED,
5074                               &DeviceNode->InstancePath);
5075 }
5076 
5077 /*
5078  * @implemented
5079  */
5080 VOID
5081 NTAPI
5082 IoInvalidateDeviceRelations(
5083     IN PDEVICE_OBJECT DeviceObject,
5084     IN DEVICE_RELATION_TYPE Type)
5085 {
5086     PDEVICE_ACTION_DATA Data;
5087     KIRQL OldIrql;
5088 
5089     Data = ExAllocatePoolWithTag(NonPagedPool,
5090                                  sizeof(DEVICE_ACTION_DATA),
5091                                  TAG_IO);
5092     if (!Data)
5093         return;
5094 
5095     ObReferenceObject(DeviceObject);
5096     Data->DeviceObject = DeviceObject;
5097     Data->Type = Type;
5098 
5099     KeAcquireSpinLock(&IopDeviceActionLock, &OldIrql);
5100     InsertTailList(&IopDeviceActionRequestList, &Data->RequestListEntry);
5101     if (IopDeviceActionInProgress)
5102     {
5103         KeReleaseSpinLock(&IopDeviceActionLock, OldIrql);
5104         return;
5105     }
5106     IopDeviceActionInProgress = TRUE;
5107     KeReleaseSpinLock(&IopDeviceActionLock, OldIrql);
5108 
5109     ExInitializeWorkItem(&IopDeviceActionWorkItem,
5110                          IopDeviceActionWorker,
5111                          NULL);
5112     ExQueueWorkItem(&IopDeviceActionWorkItem,
5113                     DelayedWorkQueue);
5114 }
5115 
5116 /*
5117  * @implemented
5118  */
5119 NTSTATUS
5120 NTAPI
5121 IoSynchronousInvalidateDeviceRelations(
5122     IN PDEVICE_OBJECT DeviceObject,
5123     IN DEVICE_RELATION_TYPE Type)
5124 {
5125     PAGED_CODE();
5126 
5127     switch (Type)
5128     {
5129         case BusRelations:
5130             /* Enumerate the device */
5131             return IopEnumerateDevice(DeviceObject);
5132         case PowerRelations:
5133              /* Not handled yet */
5134              return STATUS_NOT_IMPLEMENTED;
5135         case TargetDeviceRelation:
5136             /* Nothing to do */
5137             return STATUS_SUCCESS;
5138         default:
5139             /* Ejection relations are not supported */
5140             return STATUS_NOT_SUPPORTED;
5141     }
5142 }
5143 
5144 /*
5145  * @implemented
5146  */
5147 BOOLEAN
5148 NTAPI
5149 IoTranslateBusAddress(IN INTERFACE_TYPE InterfaceType,
5150                       IN ULONG BusNumber,
5151                       IN PHYSICAL_ADDRESS BusAddress,
5152                       IN OUT PULONG AddressSpace,
5153                       OUT PPHYSICAL_ADDRESS TranslatedAddress)
5154 {
5155     /* FIXME: Notify the resource arbiter */
5156 
5157     return HalTranslateBusAddress(InterfaceType,
5158                                   BusNumber,
5159                                   BusAddress,
5160                                   AddressSpace,
5161                                   TranslatedAddress);
5162 }
5163