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