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