xref: /reactos/ntoskrnl/io/pnpmgr/pnpmgr.c (revision bae2bac6)
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             IsMultiSz = TRUE;
1797             break;
1798 
1799         default:
1800             DPRINT1("IopValidateID: Not handled QueryType - %x\n", QueryType);
1801             return FALSE;
1802     }
1803 
1804     StringEnd = Id + MAX_DEVICE_ID_LEN;
1805 
1806     for (PtrChar = Id; PtrChar < StringEnd; PtrChar++)
1807     {
1808         Char = *PtrChar;
1809 
1810         if (Char == UNICODE_NULL)
1811         {
1812             if (!IsMultiSz || (PtrPrevChar && PtrChar == PtrPrevChar + 1))
1813             {
1814                 if (MaxSeparators == SeparatorsCount || IsMultiSz)
1815                 {
1816                     return TRUE;
1817                 }
1818 
1819                 DPRINT1("IopValidateID: SeparatorsCount - %lu, MaxSeparators - %lu\n",
1820                         SeparatorsCount, MaxSeparators);
1821                 goto ErrorExit;
1822             }
1823 
1824             StringEnd = PtrChar + MAX_DEVICE_ID_LEN + 1;
1825             PtrPrevChar = PtrChar;
1826         }
1827         else if (Char < ' ' || Char > 0x7F || Char == ',')
1828         {
1829             DPRINT1("IopValidateID: Invalid character - %04X\n", Char);
1830             goto ErrorExit;
1831         }
1832         else if (Char == ' ')
1833         {
1834             *PtrChar = '_';
1835         }
1836         else if (Char == '\\')
1837         {
1838             SeparatorsCount++;
1839 
1840             if (SeparatorsCount > MaxSeparators)
1841             {
1842                 DPRINT1("IopValidateID: SeparatorsCount - %lu, MaxSeparators - %lu\n",
1843                         SeparatorsCount, MaxSeparators);
1844                 goto ErrorExit;
1845             }
1846         }
1847     }
1848 
1849     DPRINT1("IopValidateID: Not terminated ID\n");
1850 
1851 ErrorExit:
1852     // FIXME logging
1853     return FALSE;
1854 }
1855 
1856 NTSTATUS
1857 IopQueryHardwareIds(PDEVICE_NODE DeviceNode,
1858                     HANDLE InstanceKey)
1859 {
1860    IO_STACK_LOCATION Stack;
1861    IO_STATUS_BLOCK IoStatusBlock;
1862    PWSTR Ptr;
1863    UNICODE_STRING ValueName;
1864    NTSTATUS Status;
1865    ULONG Length, TotalLength;
1866    BOOLEAN IsValidID;
1867 
1868    DPRINT("Sending IRP_MN_QUERY_ID.BusQueryHardwareIDs to device stack\n");
1869 
1870    RtlZeroMemory(&Stack, sizeof(Stack));
1871    Stack.Parameters.QueryId.IdType = BusQueryHardwareIDs;
1872    Status = IopInitiatePnpIrp(DeviceNode->PhysicalDeviceObject,
1873                               &IoStatusBlock,
1874                               IRP_MN_QUERY_ID,
1875                               &Stack);
1876    if (NT_SUCCESS(Status))
1877    {
1878       IsValidID = IopValidateID((PWCHAR)IoStatusBlock.Information, BusQueryHardwareIDs);
1879 
1880       if (!IsValidID)
1881       {
1882          DPRINT1("Invalid HardwareIDs. DeviceNode - %p\n", DeviceNode);
1883       }
1884 
1885       TotalLength = 0;
1886 
1887       Ptr = (PWSTR)IoStatusBlock.Information;
1888       DPRINT("Hardware IDs:\n");
1889       while (*Ptr)
1890       {
1891          DPRINT("  %S\n", Ptr);
1892          Length = (ULONG)wcslen(Ptr) + 1;
1893 
1894          Ptr += Length;
1895          TotalLength += Length;
1896       }
1897       DPRINT("TotalLength: %hu\n", TotalLength);
1898       DPRINT("\n");
1899 
1900       RtlInitUnicodeString(&ValueName, L"HardwareID");
1901       Status = ZwSetValueKey(InstanceKey,
1902                  &ValueName,
1903                  0,
1904                  REG_MULTI_SZ,
1905                  (PVOID)IoStatusBlock.Information,
1906                  (TotalLength + 1) * sizeof(WCHAR));
1907       if (!NT_SUCCESS(Status))
1908       {
1909          DPRINT1("ZwSetValueKey() failed (Status %lx)\n", Status);
1910       }
1911    }
1912    else
1913    {
1914       DPRINT("IopInitiatePnpIrp() failed (Status %x)\n", Status);
1915    }
1916 
1917    return Status;
1918 }
1919 
1920 NTSTATUS
1921 IopQueryCompatibleIds(PDEVICE_NODE DeviceNode,
1922                       HANDLE InstanceKey)
1923 {
1924    IO_STACK_LOCATION Stack;
1925    IO_STATUS_BLOCK IoStatusBlock;
1926    PWSTR Ptr;
1927    UNICODE_STRING ValueName;
1928    NTSTATUS Status;
1929    ULONG Length, TotalLength;
1930    BOOLEAN IsValidID;
1931 
1932    DPRINT("Sending IRP_MN_QUERY_ID.BusQueryCompatibleIDs to device stack\n");
1933 
1934    RtlZeroMemory(&Stack, sizeof(Stack));
1935    Stack.Parameters.QueryId.IdType = BusQueryCompatibleIDs;
1936    Status = IopInitiatePnpIrp(
1937       DeviceNode->PhysicalDeviceObject,
1938       &IoStatusBlock,
1939       IRP_MN_QUERY_ID,
1940       &Stack);
1941    if (NT_SUCCESS(Status) && IoStatusBlock.Information)
1942    {
1943       IsValidID = IopValidateID((PWCHAR)IoStatusBlock.Information, BusQueryCompatibleIDs);
1944 
1945       if (!IsValidID)
1946       {
1947          DPRINT1("Invalid CompatibleIDs. DeviceNode - %p\n", DeviceNode);
1948       }
1949 
1950       TotalLength = 0;
1951 
1952       Ptr = (PWSTR)IoStatusBlock.Information;
1953       DPRINT("Compatible IDs:\n");
1954       while (*Ptr)
1955       {
1956          DPRINT("  %S\n", Ptr);
1957          Length = (ULONG)wcslen(Ptr) + 1;
1958 
1959          Ptr += Length;
1960          TotalLength += Length;
1961       }
1962       DPRINT("TotalLength: %hu\n", TotalLength);
1963       DPRINT("\n");
1964 
1965       RtlInitUnicodeString(&ValueName, L"CompatibleIDs");
1966       Status = ZwSetValueKey(InstanceKey,
1967                              &ValueName,
1968                              0,
1969                              REG_MULTI_SZ,
1970                              (PVOID)IoStatusBlock.Information,
1971                              (TotalLength + 1) * sizeof(WCHAR));
1972       if (!NT_SUCCESS(Status))
1973       {
1974          DPRINT1("ZwSetValueKey() failed (Status %lx) or no Compatible ID returned\n", Status);
1975       }
1976    }
1977    else
1978    {
1979       DPRINT("IopInitiatePnpIrp() failed (Status %x)\n", Status);
1980    }
1981 
1982    return Status;
1983 }
1984 
1985 NTSTATUS
1986 IopCreateDeviceInstancePath(
1987     _In_ PDEVICE_NODE DeviceNode,
1988     _Out_ PUNICODE_STRING InstancePath)
1989 {
1990     IO_STATUS_BLOCK IoStatusBlock;
1991     UNICODE_STRING DeviceId;
1992     UNICODE_STRING InstanceId;
1993     IO_STACK_LOCATION Stack;
1994     NTSTATUS Status;
1995     UNICODE_STRING ParentIdPrefix = { 0, 0, NULL };
1996     DEVICE_CAPABILITIES DeviceCapabilities;
1997     BOOLEAN IsValidID;
1998 
1999     DPRINT("Sending IRP_MN_QUERY_ID.BusQueryDeviceID to device stack\n");
2000 
2001     Stack.Parameters.QueryId.IdType = BusQueryDeviceID;
2002     Status = IopInitiatePnpIrp(DeviceNode->PhysicalDeviceObject,
2003                                &IoStatusBlock,
2004                                IRP_MN_QUERY_ID,
2005                                &Stack);
2006     if (!NT_SUCCESS(Status))
2007     {
2008         DPRINT1("IopInitiatePnpIrp(BusQueryDeviceID) failed (Status %x)\n", Status);
2009         return Status;
2010     }
2011 
2012     IsValidID = IopValidateID((PWCHAR)IoStatusBlock.Information, BusQueryDeviceID);
2013 
2014     if (!IsValidID)
2015     {
2016         DPRINT1("Invalid DeviceID. DeviceNode - %p\n", DeviceNode);
2017     }
2018 
2019     /* Save the device id string */
2020     RtlInitUnicodeString(&DeviceId, (PWSTR)IoStatusBlock.Information);
2021 
2022     DPRINT("Sending IRP_MN_QUERY_CAPABILITIES to device stack (after enumeration)\n");
2023 
2024     Status = IopQueryDeviceCapabilities(DeviceNode, &DeviceCapabilities);
2025     if (!NT_SUCCESS(Status))
2026     {
2027         DPRINT1("IopQueryDeviceCapabilities() failed (Status 0x%08lx)\n", Status);
2028         RtlFreeUnicodeString(&DeviceId);
2029         return Status;
2030     }
2031 
2032     /* This bit is only check after enumeration */
2033     if (DeviceCapabilities.HardwareDisabled)
2034     {
2035         /* FIXME: Cleanup device */
2036         DeviceNode->Flags |= DNF_DISABLED;
2037         RtlFreeUnicodeString(&DeviceId);
2038         return STATUS_PLUGPLAY_NO_DEVICE;
2039     }
2040     else
2041     {
2042         DeviceNode->Flags &= ~DNF_DISABLED;
2043     }
2044 
2045     if (!DeviceCapabilities.UniqueID)
2046     {
2047         /* Device has not a unique ID. We need to prepend parent bus unique identifier */
2048         DPRINT("Instance ID is not unique\n");
2049         Status = IopGetParentIdPrefix(DeviceNode, &ParentIdPrefix);
2050         if (!NT_SUCCESS(Status))
2051         {
2052             DPRINT1("IopGetParentIdPrefix() failed (Status 0x%08lx)\n", Status);
2053             RtlFreeUnicodeString(&DeviceId);
2054             return Status;
2055         }
2056     }
2057 
2058     DPRINT("Sending IRP_MN_QUERY_ID.BusQueryInstanceID to device stack\n");
2059 
2060     Stack.Parameters.QueryId.IdType = BusQueryInstanceID;
2061     Status = IopInitiatePnpIrp(DeviceNode->PhysicalDeviceObject,
2062                                &IoStatusBlock,
2063                                IRP_MN_QUERY_ID,
2064                                &Stack);
2065     if (!NT_SUCCESS(Status))
2066     {
2067         DPRINT("IopInitiatePnpIrp(BusQueryInstanceID) failed (Status %lx)\n", Status);
2068         ASSERT(IoStatusBlock.Information == 0);
2069     }
2070 
2071     if (IoStatusBlock.Information)
2072     {
2073         IsValidID = IopValidateID((PWCHAR)IoStatusBlock.Information, BusQueryInstanceID);
2074 
2075         if (!IsValidID)
2076         {
2077             DPRINT1("Invalid InstanceID. DeviceNode - %p\n", DeviceNode);
2078         }
2079     }
2080 
2081     RtlInitUnicodeString(&InstanceId,
2082                          (PWSTR)IoStatusBlock.Information);
2083 
2084     InstancePath->Length = 0;
2085     InstancePath->MaximumLength = DeviceId.Length + sizeof(WCHAR) +
2086                                   ParentIdPrefix.Length +
2087                                   InstanceId.Length +
2088                                   sizeof(UNICODE_NULL);
2089     if (ParentIdPrefix.Length && InstanceId.Length)
2090     {
2091         InstancePath->MaximumLength += sizeof(WCHAR);
2092     }
2093 
2094     InstancePath->Buffer = ExAllocatePoolWithTag(PagedPool,
2095                                                  InstancePath->MaximumLength,
2096                                                  TAG_IO);
2097     if (!InstancePath->Buffer)
2098     {
2099         RtlFreeUnicodeString(&InstanceId);
2100         RtlFreeUnicodeString(&ParentIdPrefix);
2101         RtlFreeUnicodeString(&DeviceId);
2102         return STATUS_INSUFFICIENT_RESOURCES;
2103     }
2104 
2105     /* Start with the device id */
2106     RtlCopyUnicodeString(InstancePath, &DeviceId);
2107     RtlAppendUnicodeToString(InstancePath, L"\\");
2108 
2109     /* Add information from parent bus device to InstancePath */
2110     RtlAppendUnicodeStringToString(InstancePath, &ParentIdPrefix);
2111     if (ParentIdPrefix.Length && InstanceId.Length)
2112     {
2113         RtlAppendUnicodeToString(InstancePath, L"&");
2114     }
2115 
2116     /* Finally, add the id returned by the driver stack */
2117     RtlAppendUnicodeStringToString(InstancePath, &InstanceId);
2118 
2119     /*
2120      * FIXME: Check for valid characters, if there is invalid characters
2121      * then bugcheck
2122      */
2123 
2124     RtlFreeUnicodeString(&InstanceId);
2125     RtlFreeUnicodeString(&DeviceId);
2126     RtlFreeUnicodeString(&ParentIdPrefix);
2127 
2128     return STATUS_SUCCESS;
2129 }
2130 
2131 /*
2132  * IopActionInterrogateDeviceStack
2133  *
2134  * Retrieve information for all (direct) child nodes of a parent node.
2135  *
2136  * Parameters
2137  *    DeviceNode
2138  *       Pointer to device node.
2139  *    Context
2140  *       Pointer to parent node to retrieve child node information for.
2141  *
2142  * Remarks
2143  *    Any errors that occur are logged instead so that all child services have a chance
2144  *    of being interrogated.
2145  */
2146 
2147 NTSTATUS
2148 IopActionInterrogateDeviceStack(PDEVICE_NODE DeviceNode,
2149                                 PVOID Context)
2150 {
2151     IO_STATUS_BLOCK IoStatusBlock;
2152     PWSTR DeviceDescription;
2153     PWSTR LocationInformation;
2154     PDEVICE_NODE ParentDeviceNode;
2155     IO_STACK_LOCATION Stack;
2156     NTSTATUS Status;
2157     ULONG RequiredLength;
2158     LCID LocaleId;
2159     HANDLE InstanceKey = NULL;
2160     UNICODE_STRING ValueName;
2161     UNICODE_STRING InstancePathU;
2162     PDEVICE_OBJECT OldDeviceObject;
2163 
2164     DPRINT("IopActionInterrogateDeviceStack(%p, %p)\n", DeviceNode, Context);
2165     DPRINT("PDO 0x%p\n", DeviceNode->PhysicalDeviceObject);
2166 
2167     ParentDeviceNode = (PDEVICE_NODE)Context;
2168 
2169     /*
2170      * We are called for the parent too, but we don't need to do special
2171      * handling for this node
2172      */
2173     if (DeviceNode == ParentDeviceNode)
2174     {
2175         DPRINT("Success\n");
2176         return STATUS_SUCCESS;
2177     }
2178 
2179     /*
2180      * Make sure this device node is a direct child of the parent device node
2181      * that is given as an argument
2182      */
2183     if (DeviceNode->Parent != ParentDeviceNode)
2184     {
2185         DPRINT("Skipping 2+ level child\n");
2186         return STATUS_SUCCESS;
2187     }
2188 
2189     /* Skip processing if it was already completed before */
2190     if (DeviceNode->Flags & DNF_PROCESSED)
2191     {
2192         /* Nothing to do */
2193         return STATUS_SUCCESS;
2194     }
2195 
2196     /* Get Locale ID */
2197     Status = ZwQueryDefaultLocale(FALSE, &LocaleId);
2198     if (!NT_SUCCESS(Status))
2199     {
2200         DPRINT1("ZwQueryDefaultLocale() failed with status 0x%lx\n", Status);
2201         return Status;
2202     }
2203 
2204     /*
2205      * FIXME: For critical errors, cleanup and disable device, but always
2206      * return STATUS_SUCCESS.
2207      */
2208 
2209     Status = IopCreateDeviceInstancePath(DeviceNode, &InstancePathU);
2210     if (!NT_SUCCESS(Status))
2211     {
2212         if (Status != STATUS_PLUGPLAY_NO_DEVICE)
2213         {
2214             DPRINT1("IopCreateDeviceInstancePath() failed with status 0x%lx\n", Status);
2215         }
2216 
2217         /* We have to return success otherwise we abort the traverse operation */
2218         return STATUS_SUCCESS;
2219     }
2220 
2221     /* Verify that this is not a duplicate */
2222     OldDeviceObject = IopGetDeviceObjectFromDeviceInstance(&InstancePathU);
2223     if (OldDeviceObject != NULL)
2224     {
2225         PDEVICE_NODE OldDeviceNode = IopGetDeviceNode(OldDeviceObject);
2226 
2227         DPRINT1("Duplicate device instance '%wZ'\n", &InstancePathU);
2228         DPRINT1("Current instance parent: '%wZ'\n", &DeviceNode->Parent->InstancePath);
2229         DPRINT1("Old instance parent: '%wZ'\n", &OldDeviceNode->Parent->InstancePath);
2230 
2231         KeBugCheckEx(PNP_DETECTED_FATAL_ERROR,
2232                      0x01,
2233                      (ULONG_PTR)DeviceNode->PhysicalDeviceObject,
2234                      (ULONG_PTR)OldDeviceObject,
2235                      0);
2236     }
2237 
2238     DeviceNode->InstancePath = InstancePathU;
2239 
2240     DPRINT("InstancePath is %S\n", DeviceNode->InstancePath.Buffer);
2241 
2242     /*
2243      * Create registry key for the instance id, if it doesn't exist yet
2244      */
2245     Status = IopCreateDeviceKeyPath(&DeviceNode->InstancePath, 0, &InstanceKey);
2246     if (!NT_SUCCESS(Status))
2247     {
2248         DPRINT1("Failed to create the instance key! (Status %lx)\n", Status);
2249 
2250         /* We have to return success otherwise we abort the traverse operation */
2251         return STATUS_SUCCESS;
2252     }
2253 
2254     IopQueryHardwareIds(DeviceNode, InstanceKey);
2255 
2256     IopQueryCompatibleIds(DeviceNode, InstanceKey);
2257 
2258     DPRINT("Sending IRP_MN_QUERY_DEVICE_TEXT.DeviceTextDescription to device stack\n");
2259 
2260     Stack.Parameters.QueryDeviceText.DeviceTextType = DeviceTextDescription;
2261     Stack.Parameters.QueryDeviceText.LocaleId = LocaleId;
2262     Status = IopInitiatePnpIrp(DeviceNode->PhysicalDeviceObject,
2263                                &IoStatusBlock,
2264                                IRP_MN_QUERY_DEVICE_TEXT,
2265                                &Stack);
2266     DeviceDescription = NT_SUCCESS(Status) ? (PWSTR)IoStatusBlock.Information
2267                                            : NULL;
2268     /* This key is mandatory, so even if the Irp fails, we still write it */
2269     RtlInitUnicodeString(&ValueName, L"DeviceDesc");
2270     if (ZwQueryValueKey(InstanceKey, &ValueName, KeyValueBasicInformation, NULL, 0, &RequiredLength) == STATUS_OBJECT_NAME_NOT_FOUND)
2271     {
2272         if (DeviceDescription &&
2273             *DeviceDescription != UNICODE_NULL)
2274         {
2275             /* This key is overriden when a driver is installed. Don't write the
2276              * new description if another one already exists */
2277             Status = ZwSetValueKey(InstanceKey,
2278                                    &ValueName,
2279                                    0,
2280                                    REG_SZ,
2281                                    DeviceDescription,
2282                                    ((ULONG)wcslen(DeviceDescription) + 1) * sizeof(WCHAR));
2283         }
2284         else
2285         {
2286             UNICODE_STRING DeviceDesc = RTL_CONSTANT_STRING(L"Unknown device");
2287             DPRINT("Driver didn't return DeviceDesc (Status 0x%08lx), so place unknown device there\n", Status);
2288 
2289             Status = ZwSetValueKey(InstanceKey,
2290                                    &ValueName,
2291                                    0,
2292                                    REG_SZ,
2293                                    DeviceDesc.Buffer,
2294                                    DeviceDesc.MaximumLength);
2295             if (!NT_SUCCESS(Status))
2296             {
2297                 DPRINT1("ZwSetValueKey() failed (Status 0x%lx)\n", Status);
2298             }
2299 
2300         }
2301     }
2302 
2303     if (DeviceDescription)
2304     {
2305         ExFreePoolWithTag(DeviceDescription, 0);
2306     }
2307 
2308     DPRINT("Sending IRP_MN_QUERY_DEVICE_TEXT.DeviceTextLocation to device stack\n");
2309 
2310     Stack.Parameters.QueryDeviceText.DeviceTextType = DeviceTextLocationInformation;
2311     Stack.Parameters.QueryDeviceText.LocaleId = LocaleId;
2312     Status = IopInitiatePnpIrp(DeviceNode->PhysicalDeviceObject,
2313                                &IoStatusBlock,
2314                                IRP_MN_QUERY_DEVICE_TEXT,
2315                                &Stack);
2316     if (NT_SUCCESS(Status) && IoStatusBlock.Information)
2317     {
2318         LocationInformation = (PWSTR)IoStatusBlock.Information;
2319         DPRINT("LocationInformation: %S\n", LocationInformation);
2320         RtlInitUnicodeString(&ValueName, L"LocationInformation");
2321         Status = ZwSetValueKey(InstanceKey,
2322                                &ValueName,
2323                                0,
2324                                REG_SZ,
2325                                LocationInformation,
2326                                ((ULONG)wcslen(LocationInformation) + 1) * sizeof(WCHAR));
2327         if (!NT_SUCCESS(Status))
2328         {
2329             DPRINT1("ZwSetValueKey() failed (Status %lx)\n", Status);
2330         }
2331 
2332         ExFreePoolWithTag(LocationInformation, 0);
2333     }
2334     else
2335     {
2336         DPRINT("IopInitiatePnpIrp() failed (Status %x) or IoStatusBlock.Information=NULL\n", Status);
2337     }
2338 
2339     DPRINT("Sending IRP_MN_QUERY_BUS_INFORMATION to device stack\n");
2340 
2341     Status = IopInitiatePnpIrp(DeviceNode->PhysicalDeviceObject,
2342                                &IoStatusBlock,
2343                                IRP_MN_QUERY_BUS_INFORMATION,
2344                                NULL);
2345     if (NT_SUCCESS(Status) && IoStatusBlock.Information)
2346     {
2347         PPNP_BUS_INFORMATION BusInformation = (PPNP_BUS_INFORMATION)IoStatusBlock.Information;
2348 
2349         DeviceNode->ChildBusNumber = BusInformation->BusNumber;
2350         DeviceNode->ChildInterfaceType = BusInformation->LegacyBusType;
2351         DeviceNode->ChildBusTypeIndex = IopGetBusTypeGuidIndex(&BusInformation->BusTypeGuid);
2352         ExFreePoolWithTag(BusInformation, 0);
2353     }
2354     else
2355     {
2356         DPRINT("IopInitiatePnpIrp() failed (Status %x) or IoStatusBlock.Information=NULL\n", Status);
2357 
2358         DeviceNode->ChildBusNumber = 0xFFFFFFF0;
2359         DeviceNode->ChildInterfaceType = InterfaceTypeUndefined;
2360         DeviceNode->ChildBusTypeIndex = -1;
2361     }
2362 
2363     DPRINT("Sending IRP_MN_QUERY_RESOURCES to device stack\n");
2364 
2365     Status = IopInitiatePnpIrp(DeviceNode->PhysicalDeviceObject,
2366                                &IoStatusBlock,
2367                                IRP_MN_QUERY_RESOURCES,
2368                                NULL);
2369     if (NT_SUCCESS(Status) && IoStatusBlock.Information)
2370     {
2371         DeviceNode->BootResources = (PCM_RESOURCE_LIST)IoStatusBlock.Information;
2372         IopDeviceNodeSetFlag(DeviceNode, DNF_HAS_BOOT_CONFIG);
2373     }
2374     else
2375     {
2376         DPRINT("IopInitiatePnpIrp() failed (Status %x) or IoStatusBlock.Information=NULL\n", Status);
2377         DeviceNode->BootResources = NULL;
2378     }
2379 
2380     DPRINT("Sending IRP_MN_QUERY_RESOURCE_REQUIREMENTS to device stack\n");
2381 
2382     Status = IopInitiatePnpIrp(DeviceNode->PhysicalDeviceObject,
2383                                &IoStatusBlock,
2384                                IRP_MN_QUERY_RESOURCE_REQUIREMENTS,
2385                                NULL);
2386     if (NT_SUCCESS(Status))
2387     {
2388         DeviceNode->ResourceRequirements = (PIO_RESOURCE_REQUIREMENTS_LIST)IoStatusBlock.Information;
2389     }
2390     else
2391     {
2392         DPRINT("IopInitiatePnpIrp() failed (Status %08lx)\n", Status);
2393         DeviceNode->ResourceRequirements = NULL;
2394     }
2395 
2396     if (InstanceKey != NULL)
2397     {
2398         IopSetDeviceInstanceData(InstanceKey, DeviceNode);
2399     }
2400 
2401     ZwClose(InstanceKey);
2402 
2403     IopDeviceNodeSetFlag(DeviceNode, DNF_PROCESSED);
2404 
2405     if (!IopDeviceNodeHasFlag(DeviceNode, DNF_LEGACY_DRIVER))
2406     {
2407         /* Report the device to the user-mode pnp manager */
2408         IopQueueTargetDeviceEvent(&GUID_DEVICE_ENUMERATED,
2409                                   &DeviceNode->InstancePath);
2410     }
2411 
2412     return STATUS_SUCCESS;
2413 }
2414 
2415 static
2416 VOID
2417 IopHandleDeviceRemoval(
2418     IN PDEVICE_NODE DeviceNode,
2419     IN PDEVICE_RELATIONS DeviceRelations)
2420 {
2421     PDEVICE_NODE Child = DeviceNode->Child, NextChild;
2422     ULONG i;
2423     BOOLEAN Found;
2424 
2425     if (DeviceNode == IopRootDeviceNode)
2426         return;
2427 
2428     while (Child != NULL)
2429     {
2430         NextChild = Child->Sibling;
2431         Found = FALSE;
2432 
2433         for (i = 0; DeviceRelations && i < DeviceRelations->Count; i++)
2434         {
2435             if (IopGetDeviceNode(DeviceRelations->Objects[i]) == Child)
2436             {
2437                 Found = TRUE;
2438                 break;
2439             }
2440         }
2441 
2442         if (!Found && !(Child->Flags & DNF_WILL_BE_REMOVED))
2443         {
2444             /* Send removal IRPs to all of its children */
2445             IopPrepareDeviceForRemoval(Child->PhysicalDeviceObject, TRUE);
2446 
2447             /* Send the surprise removal IRP */
2448             IopSendSurpriseRemoval(Child->PhysicalDeviceObject);
2449 
2450             /* Tell the user-mode PnP manager that a device was removed */
2451             IopQueueTargetDeviceEvent(&GUID_DEVICE_SURPRISE_REMOVAL,
2452                                       &Child->InstancePath);
2453 
2454             /* Send the remove device IRP */
2455             IopSendRemoveDevice(Child->PhysicalDeviceObject);
2456         }
2457 
2458         Child = NextChild;
2459     }
2460 }
2461 
2462 NTSTATUS
2463 IopEnumerateDevice(
2464     IN PDEVICE_OBJECT DeviceObject)
2465 {
2466     PDEVICE_NODE DeviceNode = IopGetDeviceNode(DeviceObject);
2467     DEVICETREE_TRAVERSE_CONTEXT Context;
2468     PDEVICE_RELATIONS DeviceRelations;
2469     PDEVICE_OBJECT ChildDeviceObject;
2470     IO_STATUS_BLOCK IoStatusBlock;
2471     PDEVICE_NODE ChildDeviceNode;
2472     IO_STACK_LOCATION Stack;
2473     NTSTATUS Status;
2474     ULONG i;
2475 
2476     DPRINT("DeviceObject 0x%p\n", DeviceObject);
2477 
2478     if (DeviceNode->Flags & DNF_NEED_ENUMERATION_ONLY)
2479     {
2480         DeviceNode->Flags &= ~DNF_NEED_ENUMERATION_ONLY;
2481 
2482         DPRINT("Sending GUID_DEVICE_ARRIVAL\n");
2483         IopQueueTargetDeviceEvent(&GUID_DEVICE_ARRIVAL,
2484                                   &DeviceNode->InstancePath);
2485     }
2486 
2487     DPRINT("Sending IRP_MN_QUERY_DEVICE_RELATIONS to device stack\n");
2488 
2489     Stack.Parameters.QueryDeviceRelations.Type = BusRelations;
2490 
2491     Status = IopInitiatePnpIrp(
2492         DeviceObject,
2493         &IoStatusBlock,
2494         IRP_MN_QUERY_DEVICE_RELATIONS,
2495         &Stack);
2496     if (!NT_SUCCESS(Status) || Status == STATUS_PENDING)
2497     {
2498         DPRINT("IopInitiatePnpIrp() failed with status 0x%08lx\n", Status);
2499         return Status;
2500     }
2501 
2502     DeviceRelations = (PDEVICE_RELATIONS)IoStatusBlock.Information;
2503 
2504     /*
2505      * Send removal IRPs for devices that have disappeared
2506      * NOTE: This code handles the case where no relations are specified
2507      */
2508     IopHandleDeviceRemoval(DeviceNode, DeviceRelations);
2509 
2510     /* Now we bail if nothing was returned */
2511     if (!DeviceRelations)
2512     {
2513         /* We're all done */
2514         DPRINT("No PDOs\n");
2515         return STATUS_SUCCESS;
2516     }
2517 
2518     DPRINT("Got %u PDOs\n", DeviceRelations->Count);
2519 
2520     /*
2521      * Create device nodes for all discovered devices
2522      */
2523     for (i = 0; i < DeviceRelations->Count; i++)
2524     {
2525         ChildDeviceObject = DeviceRelations->Objects[i];
2526         ASSERT((ChildDeviceObject->Flags & DO_DEVICE_INITIALIZING) == 0);
2527 
2528         ChildDeviceNode = IopGetDeviceNode(ChildDeviceObject);
2529         if (!ChildDeviceNode)
2530         {
2531             /* One doesn't exist, create it */
2532             Status = IopCreateDeviceNode(
2533                 DeviceNode,
2534                 ChildDeviceObject,
2535                 NULL,
2536                 &ChildDeviceNode);
2537             if (NT_SUCCESS(Status))
2538             {
2539                 /* Mark the node as enumerated */
2540                 ChildDeviceNode->Flags |= DNF_ENUMERATED;
2541 
2542                 /* Mark the DO as bus enumerated */
2543                 ChildDeviceObject->Flags |= DO_BUS_ENUMERATED_DEVICE;
2544             }
2545             else
2546             {
2547                 /* Ignore this DO */
2548                 DPRINT1("IopCreateDeviceNode() failed with status 0x%08x. Skipping PDO %u\n", Status, i);
2549                 ObDereferenceObject(ChildDeviceObject);
2550             }
2551         }
2552         else
2553         {
2554             /* Mark it as enumerated */
2555             ChildDeviceNode->Flags |= DNF_ENUMERATED;
2556             ObDereferenceObject(ChildDeviceObject);
2557         }
2558     }
2559     ExFreePool(DeviceRelations);
2560 
2561     /*
2562      * Retrieve information about all discovered children from the bus driver
2563      */
2564     IopInitDeviceTreeTraverseContext(
2565         &Context,
2566         DeviceNode,
2567         IopActionInterrogateDeviceStack,
2568         DeviceNode);
2569 
2570     Status = IopTraverseDeviceTree(&Context);
2571     if (!NT_SUCCESS(Status))
2572     {
2573         DPRINT("IopTraverseDeviceTree() failed with status 0x%08lx\n", Status);
2574         return Status;
2575     }
2576 
2577     /*
2578      * Retrieve configuration from the registry for discovered children
2579      */
2580     IopInitDeviceTreeTraverseContext(
2581         &Context,
2582         DeviceNode,
2583         IopActionConfigureChildServices,
2584         DeviceNode);
2585 
2586     Status = IopTraverseDeviceTree(&Context);
2587     if (!NT_SUCCESS(Status))
2588     {
2589         DPRINT("IopTraverseDeviceTree() failed with status 0x%08lx\n", Status);
2590         return Status;
2591     }
2592 
2593     /*
2594      * Initialize services for discovered children.
2595      */
2596     Status = IopInitializePnpServices(DeviceNode);
2597     if (!NT_SUCCESS(Status))
2598     {
2599         DPRINT("IopInitializePnpServices() failed with status 0x%08lx\n", Status);
2600         return Status;
2601     }
2602 
2603     DPRINT("IopEnumerateDevice() finished\n");
2604     return STATUS_SUCCESS;
2605 }
2606 
2607 
2608 /*
2609  * IopActionConfigureChildServices
2610  *
2611  * Retrieve configuration for all (direct) child nodes of a parent node.
2612  *
2613  * Parameters
2614  *    DeviceNode
2615  *       Pointer to device node.
2616  *    Context
2617  *       Pointer to parent node to retrieve child node configuration for.
2618  *
2619  * Remarks
2620  *    Any errors that occur are logged instead so that all child services have a chance of beeing
2621  *    configured.
2622  */
2623 
2624 NTSTATUS
2625 IopActionConfigureChildServices(PDEVICE_NODE DeviceNode,
2626                                 PVOID Context)
2627 {
2628    RTL_QUERY_REGISTRY_TABLE QueryTable[3];
2629    PDEVICE_NODE ParentDeviceNode;
2630    PUNICODE_STRING Service;
2631    UNICODE_STRING ClassGUID;
2632    NTSTATUS Status;
2633    DEVICE_CAPABILITIES DeviceCaps;
2634 
2635    DPRINT("IopActionConfigureChildServices(%p, %p)\n", DeviceNode, Context);
2636 
2637    ParentDeviceNode = (PDEVICE_NODE)Context;
2638 
2639    /*
2640     * We are called for the parent too, but we don't need to do special
2641     * handling for this node
2642     */
2643    if (DeviceNode == ParentDeviceNode)
2644    {
2645       DPRINT("Success\n");
2646       return STATUS_SUCCESS;
2647    }
2648 
2649    /*
2650     * Make sure this device node is a direct child of the parent device node
2651     * that is given as an argument
2652     */
2653 
2654    if (DeviceNode->Parent != ParentDeviceNode)
2655    {
2656       DPRINT("Skipping 2+ level child\n");
2657       return STATUS_SUCCESS;
2658    }
2659 
2660    if (!(DeviceNode->Flags & DNF_PROCESSED))
2661    {
2662        DPRINT1("Child not ready to be configured\n");
2663        return STATUS_SUCCESS;
2664    }
2665 
2666    if (!(DeviceNode->Flags & (DNF_DISABLED | DNF_STARTED | DNF_ADDED)))
2667    {
2668       WCHAR RegKeyBuffer[MAX_PATH];
2669       UNICODE_STRING RegKey;
2670 
2671       /* Install the service for this if it's in the CDDB */
2672       IopInstallCriticalDevice(DeviceNode);
2673 
2674       RegKey.Length = 0;
2675       RegKey.MaximumLength = sizeof(RegKeyBuffer);
2676       RegKey.Buffer = RegKeyBuffer;
2677 
2678       /*
2679        * Retrieve configuration from Enum key
2680        */
2681 
2682       Service = &DeviceNode->ServiceName;
2683 
2684       RtlZeroMemory(QueryTable, sizeof(QueryTable));
2685       RtlInitUnicodeString(Service, NULL);
2686       RtlInitUnicodeString(&ClassGUID, NULL);
2687 
2688       QueryTable[0].Name = L"Service";
2689       QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
2690       QueryTable[0].EntryContext = Service;
2691 
2692       QueryTable[1].Name = L"ClassGUID";
2693       QueryTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT;
2694       QueryTable[1].EntryContext = &ClassGUID;
2695       QueryTable[1].DefaultType = REG_SZ;
2696       QueryTable[1].DefaultData = L"";
2697       QueryTable[1].DefaultLength = 0;
2698 
2699       RtlAppendUnicodeToString(&RegKey, L"\\Registry\\Machine\\System\\CurrentControlSet\\Enum\\");
2700       RtlAppendUnicodeStringToString(&RegKey, &DeviceNode->InstancePath);
2701 
2702       Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
2703          RegKey.Buffer, QueryTable, NULL, NULL);
2704 
2705       if (!NT_SUCCESS(Status))
2706       {
2707          /* FIXME: Log the error */
2708          DPRINT("Could not retrieve configuration for device %wZ (Status 0x%08x)\n",
2709             &DeviceNode->InstancePath, Status);
2710          IopDeviceNodeSetFlag(DeviceNode, DNF_DISABLED);
2711          return STATUS_SUCCESS;
2712       }
2713 
2714       if (Service->Buffer == NULL)
2715       {
2716          if (NT_SUCCESS(IopQueryDeviceCapabilities(DeviceNode, &DeviceCaps)) &&
2717              DeviceCaps.RawDeviceOK)
2718          {
2719             DPRINT("%wZ is using parent bus driver (%wZ)\n", &DeviceNode->InstancePath, &ParentDeviceNode->ServiceName);
2720 
2721             DeviceNode->ServiceName.Length = 0;
2722             DeviceNode->ServiceName.MaximumLength = 0;
2723             DeviceNode->ServiceName.Buffer = NULL;
2724          }
2725          else if (ClassGUID.Length != 0)
2726          {
2727             /* Device has a ClassGUID value, but no Service value.
2728              * Suppose it is using the NULL driver, so state the
2729              * device is started */
2730             DPRINT("%wZ is using NULL driver\n", &DeviceNode->InstancePath);
2731             IopDeviceNodeSetFlag(DeviceNode, DNF_STARTED);
2732          }
2733          else
2734          {
2735             DeviceNode->Problem = CM_PROB_FAILED_INSTALL;
2736             IopDeviceNodeSetFlag(DeviceNode, DNF_DISABLED);
2737          }
2738          return STATUS_SUCCESS;
2739       }
2740 
2741       DPRINT("Got Service %S\n", Service->Buffer);
2742    }
2743 
2744    return STATUS_SUCCESS;
2745 }
2746 
2747 /*
2748  * IopActionInitChildServices
2749  *
2750  * Initialize the service for all (direct) child nodes of a parent node
2751  *
2752  * Parameters
2753  *    DeviceNode
2754  *       Pointer to device node.
2755  *    Context
2756  *       Pointer to parent node to initialize child node services for.
2757  *
2758  * Remarks
2759  *    If the driver image for a service is not loaded and initialized
2760  *    it is done here too. Any errors that occur are logged instead so
2761  *    that all child services have a chance of being initialized.
2762  */
2763 
2764 NTSTATUS
2765 IopActionInitChildServices(PDEVICE_NODE DeviceNode,
2766                            PVOID Context)
2767 {
2768    PDEVICE_NODE ParentDeviceNode;
2769    NTSTATUS Status;
2770    BOOLEAN BootDrivers = !PnpSystemInit;
2771 
2772    DPRINT("IopActionInitChildServices(%p, %p)\n", DeviceNode, Context);
2773 
2774    ParentDeviceNode = Context;
2775 
2776    /*
2777     * We are called for the parent too, but we don't need to do special
2778     * handling for this node
2779     */
2780    if (DeviceNode == ParentDeviceNode)
2781    {
2782       DPRINT("Success\n");
2783       return STATUS_SUCCESS;
2784    }
2785 
2786    /*
2787     * We don't want to check for a direct child because
2788     * this function is called during boot to reinitialize
2789     * devices with drivers that couldn't load yet due to
2790     * stage 0 limitations (ie can't load from disk yet).
2791     */
2792 
2793    if (!(DeviceNode->Flags & DNF_PROCESSED))
2794    {
2795        DPRINT1("Child not ready to be added\n");
2796        return STATUS_SUCCESS;
2797    }
2798 
2799    if (IopDeviceNodeHasFlag(DeviceNode, DNF_STARTED) ||
2800        IopDeviceNodeHasFlag(DeviceNode, DNF_ADDED) ||
2801        IopDeviceNodeHasFlag(DeviceNode, DNF_DISABLED))
2802        return STATUS_SUCCESS;
2803 
2804    if (DeviceNode->ServiceName.Buffer == NULL)
2805    {
2806       /* We don't need to worry about loading the driver because we're
2807        * being driven in raw mode so our parent must be loaded to get here */
2808       Status = IopInitializeDevice(DeviceNode, NULL);
2809       if (NT_SUCCESS(Status))
2810       {
2811           Status = IopStartDevice(DeviceNode);
2812           if (!NT_SUCCESS(Status))
2813           {
2814               DPRINT1("IopStartDevice(%wZ) failed with status 0x%08x\n",
2815                       &DeviceNode->InstancePath, Status);
2816           }
2817       }
2818    }
2819    else
2820    {
2821       PLDR_DATA_TABLE_ENTRY ModuleObject;
2822       PDRIVER_OBJECT DriverObject;
2823 
2824       KeEnterCriticalRegion();
2825       ExAcquireResourceExclusiveLite(&IopDriverLoadResource, TRUE);
2826       /* Get existing DriverObject pointer (in case the driver has
2827          already been loaded and initialized) */
2828       Status = IopGetDriverObject(
2829           &DriverObject,
2830           &DeviceNode->ServiceName,
2831           FALSE);
2832 
2833       if (!NT_SUCCESS(Status))
2834       {
2835          /* Driver is not initialized, try to load it */
2836          Status = IopLoadServiceModule(&DeviceNode->ServiceName, &ModuleObject);
2837 
2838          if (NT_SUCCESS(Status) || Status == STATUS_IMAGE_ALREADY_LOADED)
2839          {
2840             /* Initialize the driver */
2841             Status = IopInitializeDriverModule(DeviceNode, ModuleObject,
2842                   &DeviceNode->ServiceName, FALSE, &DriverObject);
2843             if (!NT_SUCCESS(Status)) DeviceNode->Problem = CM_PROB_FAILED_DRIVER_ENTRY;
2844          }
2845          else if (Status == STATUS_DRIVER_UNABLE_TO_LOAD)
2846          {
2847             DPRINT1("Service '%wZ' is disabled\n", &DeviceNode->ServiceName);
2848             DeviceNode->Problem = CM_PROB_DISABLED_SERVICE;
2849          }
2850          else
2851          {
2852             DPRINT("IopLoadServiceModule(%wZ) failed with status 0x%08x\n",
2853                     &DeviceNode->ServiceName, Status);
2854             if (!BootDrivers) DeviceNode->Problem = CM_PROB_DRIVER_FAILED_LOAD;
2855          }
2856       }
2857       ExReleaseResourceLite(&IopDriverLoadResource);
2858       KeLeaveCriticalRegion();
2859 
2860       /* Driver is loaded and initialized at this point */
2861       if (NT_SUCCESS(Status))
2862       {
2863           /* Initialize the device, including all filters */
2864           Status = PipCallDriverAddDevice(DeviceNode, FALSE, DriverObject);
2865 
2866           /* Remove the extra reference */
2867           ObDereferenceObject(DriverObject);
2868       }
2869       else
2870       {
2871          /*
2872           * Don't disable when trying to load only boot drivers
2873           */
2874          if (!BootDrivers)
2875          {
2876             IopDeviceNodeSetFlag(DeviceNode, DNF_DISABLED);
2877          }
2878       }
2879    }
2880 
2881    return STATUS_SUCCESS;
2882 }
2883 
2884 /*
2885  * IopInitializePnpServices
2886  *
2887  * Initialize services for discovered children
2888  *
2889  * Parameters
2890  *    DeviceNode
2891  *       Top device node to start initializing services.
2892  *
2893  * Return Value
2894  *    Status
2895  */
2896 NTSTATUS
2897 IopInitializePnpServices(IN PDEVICE_NODE DeviceNode)
2898 {
2899    DEVICETREE_TRAVERSE_CONTEXT Context;
2900 
2901    DPRINT("IopInitializePnpServices(%p)\n", DeviceNode);
2902 
2903    IopInitDeviceTreeTraverseContext(
2904       &Context,
2905       DeviceNode,
2906       IopActionInitChildServices,
2907       DeviceNode);
2908 
2909    return IopTraverseDeviceTree(&Context);
2910 }
2911 
2912 static NTSTATUS INIT_FUNCTION
2913 IopEnumerateDetectedDevices(
2914    IN HANDLE hBaseKey,
2915    IN PUNICODE_STRING RelativePath OPTIONAL,
2916    IN HANDLE hRootKey,
2917    IN BOOLEAN EnumerateSubKeys,
2918    IN PCM_FULL_RESOURCE_DESCRIPTOR ParentBootResources,
2919    IN ULONG ParentBootResourcesLength)
2920 {
2921    UNICODE_STRING IdentifierU = RTL_CONSTANT_STRING(L"Identifier");
2922    UNICODE_STRING HardwareIDU = RTL_CONSTANT_STRING(L"HardwareID");
2923    UNICODE_STRING ConfigurationDataU = RTL_CONSTANT_STRING(L"Configuration Data");
2924    UNICODE_STRING BootConfigU = RTL_CONSTANT_STRING(L"BootConfig");
2925    UNICODE_STRING LogConfU = RTL_CONSTANT_STRING(L"LogConf");
2926    OBJECT_ATTRIBUTES ObjectAttributes;
2927    HANDLE hDevicesKey = NULL;
2928    HANDLE hDeviceKey = NULL;
2929    HANDLE hLevel1Key, hLevel2Key = NULL, hLogConf;
2930    UNICODE_STRING Level2NameU;
2931    WCHAR Level2Name[5];
2932    ULONG IndexDevice = 0;
2933    ULONG IndexSubKey;
2934    PKEY_BASIC_INFORMATION pDeviceInformation = NULL;
2935    ULONG DeviceInfoLength = sizeof(KEY_BASIC_INFORMATION) + 50 * sizeof(WCHAR);
2936    PKEY_VALUE_PARTIAL_INFORMATION pValueInformation = NULL;
2937    ULONG ValueInfoLength = sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 50 * sizeof(WCHAR);
2938    UNICODE_STRING DeviceName, ValueName;
2939    ULONG RequiredSize;
2940    PCM_FULL_RESOURCE_DESCRIPTOR BootResources = NULL;
2941    ULONG BootResourcesLength;
2942    NTSTATUS Status;
2943 
2944    const UNICODE_STRING IdentifierSerial = RTL_CONSTANT_STRING(L"SerialController");
2945    UNICODE_STRING HardwareIdSerial = RTL_CONSTANT_STRING(L"*PNP0501\0");
2946    static ULONG DeviceIndexSerial = 0;
2947    const UNICODE_STRING IdentifierKeyboard = RTL_CONSTANT_STRING(L"KeyboardController");
2948    UNICODE_STRING HardwareIdKeyboard = RTL_CONSTANT_STRING(L"*PNP0303\0");
2949    static ULONG DeviceIndexKeyboard = 0;
2950    const UNICODE_STRING IdentifierMouse = RTL_CONSTANT_STRING(L"PointerController");
2951    UNICODE_STRING HardwareIdMouse = RTL_CONSTANT_STRING(L"*PNP0F13\0");
2952    static ULONG DeviceIndexMouse = 0;
2953    const UNICODE_STRING IdentifierParallel = RTL_CONSTANT_STRING(L"ParallelController");
2954    UNICODE_STRING HardwareIdParallel = RTL_CONSTANT_STRING(L"*PNP0400\0");
2955    static ULONG DeviceIndexParallel = 0;
2956    const UNICODE_STRING IdentifierFloppy = RTL_CONSTANT_STRING(L"FloppyDiskPeripheral");
2957    UNICODE_STRING HardwareIdFloppy = RTL_CONSTANT_STRING(L"*PNP0700\0");
2958    static ULONG DeviceIndexFloppy = 0;
2959    UNICODE_STRING HardwareIdKey;
2960    PUNICODE_STRING pHardwareId;
2961    ULONG DeviceIndex = 0;
2962    PUCHAR CmResourceList;
2963    ULONG ListCount;
2964 
2965     if (RelativePath)
2966     {
2967         Status = IopOpenRegistryKeyEx(&hDevicesKey, hBaseKey, RelativePath, KEY_ENUMERATE_SUB_KEYS);
2968         if (!NT_SUCCESS(Status))
2969         {
2970             DPRINT("ZwOpenKey() failed with status 0x%08lx\n", Status);
2971             goto cleanup;
2972         }
2973     }
2974     else
2975         hDevicesKey = hBaseKey;
2976 
2977    pDeviceInformation = ExAllocatePool(PagedPool, DeviceInfoLength);
2978    if (!pDeviceInformation)
2979    {
2980       DPRINT("ExAllocatePool() failed\n");
2981       Status = STATUS_NO_MEMORY;
2982       goto cleanup;
2983    }
2984 
2985    pValueInformation = ExAllocatePool(PagedPool, ValueInfoLength);
2986    if (!pValueInformation)
2987    {
2988       DPRINT("ExAllocatePool() failed\n");
2989       Status = STATUS_NO_MEMORY;
2990       goto cleanup;
2991    }
2992 
2993    while (TRUE)
2994    {
2995       Status = ZwEnumerateKey(hDevicesKey, IndexDevice, KeyBasicInformation, pDeviceInformation, DeviceInfoLength, &RequiredSize);
2996       if (Status == STATUS_NO_MORE_ENTRIES)
2997          break;
2998       else if (Status == STATUS_BUFFER_OVERFLOW || Status == STATUS_BUFFER_TOO_SMALL)
2999       {
3000          ExFreePool(pDeviceInformation);
3001          DeviceInfoLength = RequiredSize;
3002          pDeviceInformation = ExAllocatePool(PagedPool, DeviceInfoLength);
3003          if (!pDeviceInformation)
3004          {
3005             DPRINT("ExAllocatePool() failed\n");
3006             Status = STATUS_NO_MEMORY;
3007             goto cleanup;
3008          }
3009          Status = ZwEnumerateKey(hDevicesKey, IndexDevice, KeyBasicInformation, pDeviceInformation, DeviceInfoLength, &RequiredSize);
3010       }
3011       if (!NT_SUCCESS(Status))
3012       {
3013          DPRINT("ZwEnumerateKey() failed with status 0x%08lx\n", Status);
3014          goto cleanup;
3015       }
3016       IndexDevice++;
3017 
3018       /* Open device key */
3019       DeviceName.Length = DeviceName.MaximumLength = (USHORT)pDeviceInformation->NameLength;
3020       DeviceName.Buffer = pDeviceInformation->Name;
3021 
3022       Status = IopOpenRegistryKeyEx(&hDeviceKey, hDevicesKey, &DeviceName,
3023           KEY_QUERY_VALUE + (EnumerateSubKeys ? KEY_ENUMERATE_SUB_KEYS : 0));
3024       if (!NT_SUCCESS(Status))
3025       {
3026          DPRINT("ZwOpenKey() failed with status 0x%08lx\n", Status);
3027          goto cleanup;
3028       }
3029 
3030       /* Read boot resources, and add then to parent ones */
3031       Status = ZwQueryValueKey(hDeviceKey, &ConfigurationDataU, KeyValuePartialInformation, pValueInformation, ValueInfoLength, &RequiredSize);
3032       if (Status == STATUS_BUFFER_OVERFLOW || Status == STATUS_BUFFER_TOO_SMALL)
3033       {
3034          ExFreePool(pValueInformation);
3035          ValueInfoLength = RequiredSize;
3036          pValueInformation = ExAllocatePool(PagedPool, ValueInfoLength);
3037          if (!pValueInformation)
3038          {
3039             DPRINT("ExAllocatePool() failed\n");
3040             ZwDeleteKey(hLevel2Key);
3041             Status = STATUS_NO_MEMORY;
3042             goto cleanup;
3043          }
3044          Status = ZwQueryValueKey(hDeviceKey, &ConfigurationDataU, KeyValuePartialInformation, pValueInformation, ValueInfoLength, &RequiredSize);
3045       }
3046       if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
3047       {
3048          BootResources = ParentBootResources;
3049          BootResourcesLength = ParentBootResourcesLength;
3050       }
3051       else if (!NT_SUCCESS(Status))
3052       {
3053          DPRINT("ZwQueryValueKey() failed with status 0x%08lx\n", Status);
3054          goto nextdevice;
3055       }
3056       else if (pValueInformation->Type != REG_FULL_RESOURCE_DESCRIPTOR)
3057       {
3058          DPRINT("Wrong registry type: got 0x%lx, expected 0x%lx\n", pValueInformation->Type, REG_FULL_RESOURCE_DESCRIPTOR);
3059          goto nextdevice;
3060       }
3061       else
3062       {
3063          static const ULONG Header = FIELD_OFFSET(CM_FULL_RESOURCE_DESCRIPTOR, PartialResourceList.PartialDescriptors);
3064 
3065          /* Concatenate current resources and parent ones */
3066          if (ParentBootResourcesLength == 0)
3067             BootResourcesLength = pValueInformation->DataLength;
3068          else
3069             BootResourcesLength = ParentBootResourcesLength
3070             + pValueInformation->DataLength
3071                - Header;
3072          BootResources = ExAllocatePool(PagedPool, BootResourcesLength);
3073          if (!BootResources)
3074          {
3075             DPRINT("ExAllocatePool() failed\n");
3076             goto nextdevice;
3077          }
3078          if (ParentBootResourcesLength < sizeof(CM_FULL_RESOURCE_DESCRIPTOR))
3079          {
3080             RtlCopyMemory(BootResources, pValueInformation->Data, pValueInformation->DataLength);
3081          }
3082          else if (ParentBootResources->PartialResourceList.PartialDescriptors[ParentBootResources->PartialResourceList.Count - 1].Type == CmResourceTypeDeviceSpecific)
3083          {
3084             RtlCopyMemory(BootResources, pValueInformation->Data, pValueInformation->DataLength);
3085             RtlCopyMemory(
3086                (PVOID)((ULONG_PTR)BootResources + pValueInformation->DataLength),
3087                (PVOID)((ULONG_PTR)ParentBootResources + Header),
3088                ParentBootResourcesLength - Header);
3089             BootResources->PartialResourceList.Count += ParentBootResources->PartialResourceList.Count;
3090          }
3091          else
3092          {
3093             RtlCopyMemory(BootResources, pValueInformation->Data, Header);
3094             RtlCopyMemory(
3095                (PVOID)((ULONG_PTR)BootResources + Header),
3096                (PVOID)((ULONG_PTR)ParentBootResources + Header),
3097                ParentBootResourcesLength - Header);
3098             RtlCopyMemory(
3099                (PVOID)((ULONG_PTR)BootResources + ParentBootResourcesLength),
3100                pValueInformation->Data + Header,
3101                pValueInformation->DataLength - Header);
3102             BootResources->PartialResourceList.Count += ParentBootResources->PartialResourceList.Count;
3103          }
3104       }
3105 
3106       if (EnumerateSubKeys)
3107       {
3108          IndexSubKey = 0;
3109          while (TRUE)
3110          {
3111             Status = ZwEnumerateKey(hDeviceKey, IndexSubKey, KeyBasicInformation, pDeviceInformation, DeviceInfoLength, &RequiredSize);
3112             if (Status == STATUS_NO_MORE_ENTRIES)
3113                break;
3114             else if (Status == STATUS_BUFFER_OVERFLOW || Status == STATUS_BUFFER_TOO_SMALL)
3115             {
3116                ExFreePool(pDeviceInformation);
3117                DeviceInfoLength = RequiredSize;
3118                pDeviceInformation = ExAllocatePool(PagedPool, DeviceInfoLength);
3119                if (!pDeviceInformation)
3120                {
3121                   DPRINT("ExAllocatePool() failed\n");
3122                   Status = STATUS_NO_MEMORY;
3123                   goto cleanup;
3124                }
3125                Status = ZwEnumerateKey(hDeviceKey, IndexSubKey, KeyBasicInformation, pDeviceInformation, DeviceInfoLength, &RequiredSize);
3126             }
3127             if (!NT_SUCCESS(Status))
3128             {
3129                DPRINT("ZwEnumerateKey() failed with status 0x%08lx\n", Status);
3130                goto cleanup;
3131             }
3132             IndexSubKey++;
3133             DeviceName.Length = DeviceName.MaximumLength = (USHORT)pDeviceInformation->NameLength;
3134             DeviceName.Buffer = pDeviceInformation->Name;
3135 
3136             Status = IopEnumerateDetectedDevices(
3137                hDeviceKey,
3138                &DeviceName,
3139                hRootKey,
3140                TRUE,
3141                BootResources,
3142                BootResourcesLength);
3143             if (!NT_SUCCESS(Status))
3144                goto cleanup;
3145          }
3146       }
3147 
3148       /* Read identifier */
3149       Status = ZwQueryValueKey(hDeviceKey, &IdentifierU, KeyValuePartialInformation, pValueInformation, ValueInfoLength, &RequiredSize);
3150       if (Status == STATUS_BUFFER_OVERFLOW || Status == STATUS_BUFFER_TOO_SMALL)
3151       {
3152          ExFreePool(pValueInformation);
3153          ValueInfoLength = RequiredSize;
3154          pValueInformation = ExAllocatePool(PagedPool, ValueInfoLength);
3155          if (!pValueInformation)
3156          {
3157             DPRINT("ExAllocatePool() failed\n");
3158             Status = STATUS_NO_MEMORY;
3159             goto cleanup;
3160          }
3161          Status = ZwQueryValueKey(hDeviceKey, &IdentifierU, KeyValuePartialInformation, pValueInformation, ValueInfoLength, &RequiredSize);
3162       }
3163       if (!NT_SUCCESS(Status))
3164       {
3165          if (Status != STATUS_OBJECT_NAME_NOT_FOUND)
3166          {
3167             DPRINT("ZwQueryValueKey() failed with status 0x%08lx\n", Status);
3168             goto nextdevice;
3169          }
3170          ValueName.Length = ValueName.MaximumLength = 0;
3171       }
3172       else if (pValueInformation->Type != REG_SZ)
3173       {
3174          DPRINT("Wrong registry type: got 0x%lx, expected 0x%lx\n", pValueInformation->Type, REG_SZ);
3175          goto nextdevice;
3176       }
3177       else
3178       {
3179          /* Assign hardware id to this device */
3180          ValueName.Length = ValueName.MaximumLength = (USHORT)pValueInformation->DataLength;
3181          ValueName.Buffer = (PWCHAR)pValueInformation->Data;
3182          if (ValueName.Length >= sizeof(WCHAR) && ValueName.Buffer[ValueName.Length / sizeof(WCHAR) - 1] == UNICODE_NULL)
3183             ValueName.Length -= sizeof(WCHAR);
3184       }
3185 
3186       if (RelativePath && RtlCompareUnicodeString(RelativePath, &IdentifierSerial, FALSE) == 0)
3187       {
3188          pHardwareId = &HardwareIdSerial;
3189          DeviceIndex = DeviceIndexSerial++;
3190       }
3191       else if (RelativePath && RtlCompareUnicodeString(RelativePath, &IdentifierKeyboard, FALSE) == 0)
3192       {
3193          pHardwareId = &HardwareIdKeyboard;
3194          DeviceIndex = DeviceIndexKeyboard++;
3195       }
3196       else if (RelativePath && RtlCompareUnicodeString(RelativePath, &IdentifierMouse, FALSE) == 0)
3197       {
3198          pHardwareId = &HardwareIdMouse;
3199          DeviceIndex = DeviceIndexMouse++;
3200       }
3201       else if (RelativePath && RtlCompareUnicodeString(RelativePath, &IdentifierParallel, FALSE) == 0)
3202       {
3203          pHardwareId = &HardwareIdParallel;
3204          DeviceIndex = DeviceIndexParallel++;
3205       }
3206       else if (RelativePath && RtlCompareUnicodeString(RelativePath, &IdentifierFloppy, FALSE) == 0)
3207       {
3208          pHardwareId = &HardwareIdFloppy;
3209          DeviceIndex = DeviceIndexFloppy++;
3210       }
3211       else
3212       {
3213          /* Unknown key path */
3214          DPRINT("Unknown key path '%wZ'\n", RelativePath);
3215          goto nextdevice;
3216       }
3217 
3218       /* Prepare hardware id key (hardware id value without final \0) */
3219       HardwareIdKey = *pHardwareId;
3220       HardwareIdKey.Length -= sizeof(UNICODE_NULL);
3221 
3222       /* Add the detected device to Root key */
3223       InitializeObjectAttributes(&ObjectAttributes, &HardwareIdKey, OBJ_KERNEL_HANDLE, hRootKey, NULL);
3224       Status = ZwCreateKey(
3225          &hLevel1Key,
3226          KEY_CREATE_SUB_KEY,
3227          &ObjectAttributes,
3228          0,
3229          NULL,
3230          ExpInTextModeSetup ? REG_OPTION_VOLATILE : 0,
3231          NULL);
3232       if (!NT_SUCCESS(Status))
3233       {
3234          DPRINT("ZwCreateKey() failed with status 0x%08lx\n", Status);
3235          goto nextdevice;
3236       }
3237       swprintf(Level2Name, L"%04lu", DeviceIndex);
3238       RtlInitUnicodeString(&Level2NameU, Level2Name);
3239       InitializeObjectAttributes(&ObjectAttributes, &Level2NameU, OBJ_KERNEL_HANDLE, hLevel1Key, NULL);
3240       Status = ZwCreateKey(
3241          &hLevel2Key,
3242          KEY_SET_VALUE | KEY_CREATE_SUB_KEY,
3243          &ObjectAttributes,
3244          0,
3245          NULL,
3246          ExpInTextModeSetup ? REG_OPTION_VOLATILE : 0,
3247          NULL);
3248       ZwClose(hLevel1Key);
3249       if (!NT_SUCCESS(Status))
3250       {
3251          DPRINT("ZwCreateKey() failed with status 0x%08lx\n", Status);
3252          goto nextdevice;
3253       }
3254       DPRINT("Found %wZ #%lu (%wZ)\n", &ValueName, DeviceIndex, &HardwareIdKey);
3255       Status = ZwSetValueKey(hLevel2Key, &HardwareIDU, 0, REG_MULTI_SZ, pHardwareId->Buffer, pHardwareId->MaximumLength);
3256       if (!NT_SUCCESS(Status))
3257       {
3258          DPRINT("ZwSetValueKey() failed with status 0x%08lx\n", Status);
3259          ZwDeleteKey(hLevel2Key);
3260          goto nextdevice;
3261       }
3262       /* Create 'LogConf' subkey */
3263       InitializeObjectAttributes(&ObjectAttributes, &LogConfU, OBJ_KERNEL_HANDLE, hLevel2Key, NULL);
3264       Status = ZwCreateKey(
3265          &hLogConf,
3266          KEY_SET_VALUE,
3267          &ObjectAttributes,
3268          0,
3269          NULL,
3270          REG_OPTION_VOLATILE,
3271          NULL);
3272       if (!NT_SUCCESS(Status))
3273       {
3274          DPRINT("ZwCreateKey() failed with status 0x%08lx\n", Status);
3275          ZwDeleteKey(hLevel2Key);
3276          goto nextdevice;
3277       }
3278       if (BootResourcesLength >= sizeof(CM_FULL_RESOURCE_DESCRIPTOR))
3279       {
3280          CmResourceList = ExAllocatePool(PagedPool, BootResourcesLength + sizeof(ULONG));
3281          if (!CmResourceList)
3282          {
3283             ZwClose(hLogConf);
3284             ZwDeleteKey(hLevel2Key);
3285             goto nextdevice;
3286          }
3287 
3288          /* Add the list count (1st member of CM_RESOURCE_LIST) */
3289          ListCount = 1;
3290          RtlCopyMemory(CmResourceList,
3291                        &ListCount,
3292                        sizeof(ULONG));
3293 
3294          /* Now add the actual list (2nd member of CM_RESOURCE_LIST) */
3295          RtlCopyMemory(CmResourceList + sizeof(ULONG),
3296                        BootResources,
3297                        BootResourcesLength);
3298 
3299          /* Save boot resources to 'LogConf\BootConfig' */
3300          Status = ZwSetValueKey(hLogConf, &BootConfigU, 0, REG_RESOURCE_LIST, CmResourceList, BootResourcesLength + sizeof(ULONG));
3301          if (!NT_SUCCESS(Status))
3302          {
3303             DPRINT("ZwSetValueKey() failed with status 0x%08lx\n", Status);
3304             ZwClose(hLogConf);
3305             ZwDeleteKey(hLevel2Key);
3306             goto nextdevice;
3307          }
3308       }
3309       ZwClose(hLogConf);
3310 
3311 nextdevice:
3312       if (BootResources && BootResources != ParentBootResources)
3313       {
3314          ExFreePool(BootResources);
3315          BootResources = NULL;
3316       }
3317       if (hLevel2Key)
3318       {
3319          ZwClose(hLevel2Key);
3320          hLevel2Key = NULL;
3321       }
3322       if (hDeviceKey)
3323       {
3324          ZwClose(hDeviceKey);
3325          hDeviceKey = NULL;
3326       }
3327    }
3328 
3329    Status = STATUS_SUCCESS;
3330 
3331 cleanup:
3332    if (hDevicesKey && hDevicesKey != hBaseKey)
3333       ZwClose(hDevicesKey);
3334    if (hDeviceKey)
3335       ZwClose(hDeviceKey);
3336    if (pDeviceInformation)
3337       ExFreePool(pDeviceInformation);
3338    if (pValueInformation)
3339       ExFreePool(pValueInformation);
3340    return Status;
3341 }
3342 
3343 static BOOLEAN INIT_FUNCTION
3344 IopIsFirmwareMapperDisabled(VOID)
3345 {
3346    UNICODE_STRING KeyPathU = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\SYSTEM\\CURRENTCONTROLSET\\Control\\Pnp");
3347    UNICODE_STRING KeyNameU = RTL_CONSTANT_STRING(L"DisableFirmwareMapper");
3348    OBJECT_ATTRIBUTES ObjectAttributes;
3349    HANDLE hPnpKey;
3350    PKEY_VALUE_PARTIAL_INFORMATION KeyInformation;
3351    ULONG DesiredLength, Length;
3352    ULONG KeyValue = 0;
3353    NTSTATUS Status;
3354 
3355    InitializeObjectAttributes(&ObjectAttributes, &KeyPathU, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL);
3356    Status = ZwOpenKey(&hPnpKey, KEY_QUERY_VALUE, &ObjectAttributes);
3357    if (NT_SUCCESS(Status))
3358    {
3359        Status = ZwQueryValueKey(hPnpKey,
3360                                 &KeyNameU,
3361                                 KeyValuePartialInformation,
3362                                 NULL,
3363                                 0,
3364                                 &DesiredLength);
3365        if ((Status == STATUS_BUFFER_TOO_SMALL) ||
3366            (Status == STATUS_BUFFER_OVERFLOW))
3367        {
3368            Length = DesiredLength;
3369            KeyInformation = ExAllocatePool(PagedPool, Length);
3370            if (KeyInformation)
3371            {
3372                Status = ZwQueryValueKey(hPnpKey,
3373                                         &KeyNameU,
3374                                         KeyValuePartialInformation,
3375                                         KeyInformation,
3376                                         Length,
3377                                         &DesiredLength);
3378                if (NT_SUCCESS(Status) && KeyInformation->DataLength == sizeof(ULONG))
3379                {
3380                    KeyValue = (ULONG)(*KeyInformation->Data);
3381                }
3382                else
3383                {
3384                    DPRINT1("ZwQueryValueKey(%wZ%wZ) failed\n", &KeyPathU, &KeyNameU);
3385                }
3386 
3387                ExFreePool(KeyInformation);
3388            }
3389            else
3390            {
3391                DPRINT1("Failed to allocate memory for registry query\n");
3392            }
3393        }
3394        else
3395        {
3396            DPRINT1("ZwQueryValueKey(%wZ%wZ) failed with status 0x%08lx\n", &KeyPathU, &KeyNameU, Status);
3397        }
3398 
3399        ZwClose(hPnpKey);
3400    }
3401    else
3402    {
3403        DPRINT1("ZwOpenKey(%wZ) failed with status 0x%08lx\n", &KeyPathU, Status);
3404    }
3405 
3406    DPRINT("Firmware mapper is %s\n", KeyValue != 0 ? "disabled" : "enabled");
3407 
3408    return (KeyValue != 0) ? TRUE : FALSE;
3409 }
3410 
3411 NTSTATUS
3412 NTAPI
3413 INIT_FUNCTION
3414 IopUpdateRootKey(VOID)
3415 {
3416    UNICODE_STRING EnumU = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Enum");
3417    UNICODE_STRING RootPathU = RTL_CONSTANT_STRING(L"Root");
3418    UNICODE_STRING MultiKeyPathU = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\HARDWARE\\DESCRIPTION\\System\\MultifunctionAdapter");
3419    OBJECT_ATTRIBUTES ObjectAttributes;
3420    HANDLE hEnum, hRoot;
3421    NTSTATUS Status;
3422 
3423    InitializeObjectAttributes(&ObjectAttributes, &EnumU, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL);
3424    Status = ZwCreateKey(&hEnum, KEY_CREATE_SUB_KEY, &ObjectAttributes, 0, NULL, 0, NULL);
3425    if (!NT_SUCCESS(Status))
3426    {
3427       DPRINT1("ZwCreateKey() failed with status 0x%08lx\n", Status);
3428       return Status;
3429    }
3430 
3431    InitializeObjectAttributes(&ObjectAttributes, &RootPathU, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, hEnum, NULL);
3432    Status = ZwCreateKey(&hRoot, KEY_CREATE_SUB_KEY, &ObjectAttributes, 0, NULL, 0, NULL);
3433    ZwClose(hEnum);
3434    if (!NT_SUCCESS(Status))
3435    {
3436       DPRINT1("ZwOpenKey() failed with status 0x%08lx\n", Status);
3437       return Status;
3438    }
3439 
3440    if (!IopIsFirmwareMapperDisabled())
3441    {
3442         Status = IopOpenRegistryKeyEx(&hEnum, NULL, &MultiKeyPathU, KEY_ENUMERATE_SUB_KEYS);
3443         if (!NT_SUCCESS(Status))
3444         {
3445             /* Nothing to do, don't return with an error status */
3446             DPRINT("ZwOpenKey() failed with status 0x%08lx\n", Status);
3447             ZwClose(hRoot);
3448             return STATUS_SUCCESS;
3449         }
3450         Status = IopEnumerateDetectedDevices(
3451             hEnum,
3452             NULL,
3453             hRoot,
3454             TRUE,
3455             NULL,
3456             0);
3457         ZwClose(hEnum);
3458    }
3459    else
3460    {
3461         /* Enumeration is disabled */
3462         Status = STATUS_SUCCESS;
3463    }
3464 
3465    ZwClose(hRoot);
3466 
3467    return Status;
3468 }
3469 
3470 NTSTATUS
3471 NTAPI
3472 IopOpenRegistryKeyEx(PHANDLE KeyHandle,
3473                      HANDLE ParentKey,
3474                      PUNICODE_STRING Name,
3475                      ACCESS_MASK DesiredAccess)
3476 {
3477     OBJECT_ATTRIBUTES ObjectAttributes;
3478     NTSTATUS Status;
3479 
3480     PAGED_CODE();
3481 
3482     *KeyHandle = NULL;
3483 
3484     InitializeObjectAttributes(&ObjectAttributes,
3485         Name,
3486         OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
3487         ParentKey,
3488         NULL);
3489 
3490     Status = ZwOpenKey(KeyHandle, DesiredAccess, &ObjectAttributes);
3491 
3492     return Status;
3493 }
3494 
3495 NTSTATUS
3496 NTAPI
3497 IopCreateRegistryKeyEx(OUT PHANDLE Handle,
3498                        IN HANDLE RootHandle OPTIONAL,
3499                        IN PUNICODE_STRING KeyName,
3500                        IN ACCESS_MASK DesiredAccess,
3501                        IN ULONG CreateOptions,
3502                        OUT PULONG Disposition OPTIONAL)
3503 {
3504     OBJECT_ATTRIBUTES ObjectAttributes;
3505     ULONG KeyDisposition, RootHandleIndex = 0, i = 1, NestedCloseLevel = 0;
3506     USHORT Length;
3507     HANDLE HandleArray[2];
3508     BOOLEAN Recursing = TRUE;
3509     PWCHAR pp, p, p1;
3510     UNICODE_STRING KeyString;
3511     NTSTATUS Status = STATUS_SUCCESS;
3512     PAGED_CODE();
3513 
3514     /* P1 is start, pp is end */
3515     p1 = KeyName->Buffer;
3516     pp = (PVOID)((ULONG_PTR)p1 + KeyName->Length);
3517 
3518     /* Create the target key */
3519     InitializeObjectAttributes(&ObjectAttributes,
3520                                KeyName,
3521                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
3522                                RootHandle,
3523                                NULL);
3524     Status = ZwCreateKey(&HandleArray[i],
3525                          DesiredAccess,
3526                          &ObjectAttributes,
3527                          0,
3528                          NULL,
3529                          CreateOptions,
3530                          &KeyDisposition);
3531 
3532     /* Now we check if this failed */
3533     if ((Status == STATUS_OBJECT_NAME_NOT_FOUND) && (RootHandle))
3534     {
3535         /* Target key failed, so we'll need to create its parent. Setup array */
3536         HandleArray[0] = NULL;
3537         HandleArray[1] = RootHandle;
3538 
3539         /* Keep recursing for each missing parent */
3540         while (Recursing)
3541         {
3542             /* And if we're deep enough, close the last handle */
3543             if (NestedCloseLevel > 1) ZwClose(HandleArray[RootHandleIndex]);
3544 
3545             /* We're setup to ping-pong between the two handle array entries */
3546             RootHandleIndex = i;
3547             i = (i + 1) & 1;
3548 
3549             /* Clear the one we're attempting to open now */
3550             HandleArray[i] = NULL;
3551 
3552             /* Process the parent key name */
3553             for (p = p1; ((p < pp) && (*p != OBJ_NAME_PATH_SEPARATOR)); p++);
3554             Length = (USHORT)(p - p1) * sizeof(WCHAR);
3555 
3556             /* Is there a parent name? */
3557             if (Length)
3558             {
3559                 /* Build the unicode string for it */
3560                 KeyString.Buffer = p1;
3561                 KeyString.Length = KeyString.MaximumLength = Length;
3562 
3563                 /* Now try opening the parent */
3564                 InitializeObjectAttributes(&ObjectAttributes,
3565                                            &KeyString,
3566                                            OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
3567                                            HandleArray[RootHandleIndex],
3568                                            NULL);
3569                 Status = ZwCreateKey(&HandleArray[i],
3570                                      DesiredAccess,
3571                                      &ObjectAttributes,
3572                                      0,
3573                                      NULL,
3574                                      CreateOptions,
3575                                      &KeyDisposition);
3576                 if (NT_SUCCESS(Status))
3577                 {
3578                     /* It worked, we have one more handle */
3579                     NestedCloseLevel++;
3580                 }
3581                 else
3582                 {
3583                     /* Parent key creation failed, abandon loop */
3584                     Recursing = FALSE;
3585                     continue;
3586                 }
3587             }
3588             else
3589             {
3590                 /* We don't have a parent name, probably corrupted key name */
3591                 Status = STATUS_INVALID_PARAMETER;
3592                 Recursing = FALSE;
3593                 continue;
3594             }
3595 
3596             /* Now see if there's more parents to create */
3597             p1 = p + 1;
3598             if ((p == pp) || (p1 == pp))
3599             {
3600                 /* We're done, hopefully successfully, so stop */
3601                 Recursing = FALSE;
3602             }
3603         }
3604 
3605         /* Outer loop check for handle nesting that requires closing the top handle */
3606         if (NestedCloseLevel > 1) ZwClose(HandleArray[RootHandleIndex]);
3607     }
3608 
3609     /* Check if we broke out of the loop due to success */
3610     if (NT_SUCCESS(Status))
3611     {
3612         /* Return the target handle (we closed all the parent ones) and disposition */
3613         *Handle = HandleArray[i];
3614         if (Disposition) *Disposition = KeyDisposition;
3615     }
3616 
3617     /* Return the success state */
3618     return Status;
3619 }
3620 
3621 NTSTATUS
3622 NTAPI
3623 IopGetRegistryValue(IN HANDLE Handle,
3624                     IN PWSTR ValueName,
3625                     OUT PKEY_VALUE_FULL_INFORMATION *Information)
3626 {
3627     UNICODE_STRING ValueString;
3628     NTSTATUS Status;
3629     PKEY_VALUE_FULL_INFORMATION FullInformation;
3630     ULONG Size;
3631     PAGED_CODE();
3632 
3633     RtlInitUnicodeString(&ValueString, ValueName);
3634 
3635     Status = ZwQueryValueKey(Handle,
3636                              &ValueString,
3637                              KeyValueFullInformation,
3638                              NULL,
3639                              0,
3640                              &Size);
3641     if ((Status != STATUS_BUFFER_OVERFLOW) &&
3642         (Status != STATUS_BUFFER_TOO_SMALL))
3643     {
3644         return Status;
3645     }
3646 
3647     FullInformation = ExAllocatePool(NonPagedPool, Size);
3648     if (!FullInformation) return STATUS_INSUFFICIENT_RESOURCES;
3649 
3650     Status = ZwQueryValueKey(Handle,
3651                              &ValueString,
3652                              KeyValueFullInformation,
3653                              FullInformation,
3654                              Size,
3655                              &Size);
3656     if (!NT_SUCCESS(Status))
3657     {
3658         ExFreePool(FullInformation);
3659         return Status;
3660     }
3661 
3662     *Information = FullInformation;
3663     return STATUS_SUCCESS;
3664 }
3665 
3666 RTL_GENERIC_COMPARE_RESULTS
3667 NTAPI
3668 PiCompareInstancePath(IN PRTL_AVL_TABLE Table,
3669                       IN PVOID FirstStruct,
3670                       IN PVOID SecondStruct)
3671 {
3672     /* FIXME: TODO */
3673     ASSERT(FALSE);
3674     return 0;
3675 }
3676 
3677 //
3678 //  The allocation function is called by the generic table package whenever
3679 //  it needs to allocate memory for the table.
3680 //
3681 
3682 PVOID
3683 NTAPI
3684 PiAllocateGenericTableEntry(IN PRTL_AVL_TABLE Table,
3685                             IN CLONG ByteSize)
3686 {
3687     /* FIXME: TODO */
3688     ASSERT(FALSE);
3689     return NULL;
3690 }
3691 
3692 VOID
3693 NTAPI
3694 PiFreeGenericTableEntry(IN PRTL_AVL_TABLE Table,
3695                         IN PVOID Buffer)
3696 {
3697     /* FIXME: TODO */
3698     ASSERT(FALSE);
3699 }
3700 
3701 VOID
3702 NTAPI
3703 PpInitializeDeviceReferenceTable(VOID)
3704 {
3705     /* Setup the guarded mutex and AVL table */
3706     KeInitializeGuardedMutex(&PpDeviceReferenceTableLock);
3707     RtlInitializeGenericTableAvl(
3708         &PpDeviceReferenceTable,
3709         (PRTL_AVL_COMPARE_ROUTINE)PiCompareInstancePath,
3710         (PRTL_AVL_ALLOCATE_ROUTINE)PiAllocateGenericTableEntry,
3711         (PRTL_AVL_FREE_ROUTINE)PiFreeGenericTableEntry,
3712         NULL);
3713 }
3714 
3715 BOOLEAN
3716 NTAPI
3717 PiInitPhase0(VOID)
3718 {
3719     /* Initialize the resource when accessing device registry data */
3720     ExInitializeResourceLite(&PpRegistryDeviceResource);
3721 
3722     /* Setup the device reference AVL table */
3723     PpInitializeDeviceReferenceTable();
3724     return TRUE;
3725 }
3726 
3727 BOOLEAN
3728 NTAPI
3729 PpInitSystem(VOID)
3730 {
3731     /* Check the initialization phase */
3732     switch (ExpInitializationPhase)
3733     {
3734     case 0:
3735 
3736         /* Do Phase 0 */
3737         return PiInitPhase0();
3738 
3739     case 1:
3740 
3741         /* Do Phase 1 */
3742         return TRUE;
3743         //return PiInitPhase1();
3744 
3745     default:
3746 
3747         /* Don't know any other phase! Bugcheck! */
3748         KeBugCheck(UNEXPECTED_INITIALIZATION_CALL);
3749         return FALSE;
3750     }
3751 }
3752 
3753 LONG IopNumberDeviceNodes;
3754 
3755 PDEVICE_NODE
3756 NTAPI
3757 PipAllocateDeviceNode(IN PDEVICE_OBJECT PhysicalDeviceObject)
3758 {
3759     PDEVICE_NODE DeviceNode;
3760     PAGED_CODE();
3761 
3762     /* Allocate it */
3763     DeviceNode = ExAllocatePoolWithTag(NonPagedPool, sizeof(DEVICE_NODE), TAG_IO_DEVNODE);
3764     if (!DeviceNode) return DeviceNode;
3765 
3766     /* Statistics */
3767     InterlockedIncrement(&IopNumberDeviceNodes);
3768 
3769     /* Set it up */
3770     RtlZeroMemory(DeviceNode, sizeof(DEVICE_NODE));
3771     DeviceNode->InterfaceType = InterfaceTypeUndefined;
3772     DeviceNode->BusNumber = -1;
3773     DeviceNode->ChildInterfaceType = InterfaceTypeUndefined;
3774     DeviceNode->ChildBusNumber = -1;
3775     DeviceNode->ChildBusTypeIndex = -1;
3776 //    KeInitializeEvent(&DeviceNode->EnumerationMutex, SynchronizationEvent, TRUE);
3777     InitializeListHead(&DeviceNode->DeviceArbiterList);
3778     InitializeListHead(&DeviceNode->DeviceTranslatorList);
3779     InitializeListHead(&DeviceNode->TargetDeviceNotify);
3780     InitializeListHead(&DeviceNode->DockInfo.ListEntry);
3781     InitializeListHead(&DeviceNode->PendedSetInterfaceState);
3782 
3783     /* Check if there is a PDO */
3784     if (PhysicalDeviceObject)
3785     {
3786         /* Link it and remove the init flag */
3787         DeviceNode->PhysicalDeviceObject = PhysicalDeviceObject;
3788         ((PEXTENDED_DEVOBJ_EXTENSION)PhysicalDeviceObject->DeviceObjectExtension)->DeviceNode = DeviceNode;
3789         PhysicalDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
3790     }
3791 
3792     /* Return the node */
3793     return DeviceNode;
3794 }
3795 
3796 /* PUBLIC FUNCTIONS **********************************************************/
3797 
3798 NTSTATUS
3799 NTAPI
3800 PnpBusTypeGuidGet(IN USHORT Index,
3801                   IN LPGUID BusTypeGuid)
3802 {
3803     NTSTATUS Status = STATUS_SUCCESS;
3804 
3805     /* Acquire the lock */
3806     ExAcquireFastMutex(&PnpBusTypeGuidList->Lock);
3807 
3808     /* Validate size */
3809     if (Index < PnpBusTypeGuidList->GuidCount)
3810     {
3811         /* Copy the data */
3812         RtlCopyMemory(BusTypeGuid, &PnpBusTypeGuidList->Guids[Index], sizeof(GUID));
3813     }
3814     else
3815     {
3816         /* Failure path */
3817         Status = STATUS_OBJECT_NAME_NOT_FOUND;
3818     }
3819 
3820     /* Release lock and return status */
3821     ExReleaseFastMutex(&PnpBusTypeGuidList->Lock);
3822     return Status;
3823 }
3824 
3825 NTSTATUS
3826 NTAPI
3827 PnpDeviceObjectToDeviceInstance(IN PDEVICE_OBJECT DeviceObject,
3828                                 IN PHANDLE DeviceInstanceHandle,
3829                                 IN ACCESS_MASK DesiredAccess)
3830 {
3831     NTSTATUS Status;
3832     HANDLE KeyHandle;
3833     PDEVICE_NODE DeviceNode;
3834     UNICODE_STRING KeyName = RTL_CONSTANT_STRING(L"\\REGISTRY\\MACHINE\\SYSTEM\\CURRENTCONTROLSET\\ENUM");
3835     PAGED_CODE();
3836 
3837     /* Open the enum key */
3838     Status = IopOpenRegistryKeyEx(&KeyHandle,
3839                                   NULL,
3840                                   &KeyName,
3841                                   KEY_READ);
3842     if (!NT_SUCCESS(Status)) return Status;
3843 
3844     /* Make sure we have an instance path */
3845     DeviceNode = IopGetDeviceNode(DeviceObject);
3846     if ((DeviceNode) && (DeviceNode->InstancePath.Length))
3847     {
3848         /* Get the instance key */
3849         Status = IopOpenRegistryKeyEx(DeviceInstanceHandle,
3850                                       KeyHandle,
3851                                       &DeviceNode->InstancePath,
3852                                       DesiredAccess);
3853     }
3854     else
3855     {
3856         /* Fail */
3857         Status = STATUS_INVALID_DEVICE_REQUEST;
3858     }
3859 
3860     /* Close the handle and return status */
3861     ZwClose(KeyHandle);
3862     return Status;
3863 }
3864 
3865 ULONG
3866 NTAPI
3867 PnpDetermineResourceListSize(IN PCM_RESOURCE_LIST ResourceList)
3868 {
3869     ULONG FinalSize, PartialSize, EntrySize, i, j;
3870     PCM_FULL_RESOURCE_DESCRIPTOR FullDescriptor;
3871     PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptor;
3872 
3873     /* If we don't have one, that's easy */
3874     if (!ResourceList) return 0;
3875 
3876     /* Start with the minimum size possible */
3877     FinalSize = FIELD_OFFSET(CM_RESOURCE_LIST, List);
3878 
3879     /* Loop each full descriptor */
3880     FullDescriptor = ResourceList->List;
3881     for (i = 0; i < ResourceList->Count; i++)
3882     {
3883         /* Start with the minimum size possible */
3884         PartialSize = FIELD_OFFSET(CM_FULL_RESOURCE_DESCRIPTOR, PartialResourceList) +
3885         FIELD_OFFSET(CM_PARTIAL_RESOURCE_LIST, PartialDescriptors);
3886 
3887         /* Loop each partial descriptor */
3888         PartialDescriptor = FullDescriptor->PartialResourceList.PartialDescriptors;
3889         for (j = 0; j < FullDescriptor->PartialResourceList.Count; j++)
3890         {
3891             /* Start with the minimum size possible */
3892             EntrySize = sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR);
3893 
3894             /* Check if there is extra data */
3895             if (PartialDescriptor->Type == CmResourceTypeDeviceSpecific)
3896             {
3897                 /* Add that data */
3898                 EntrySize += PartialDescriptor->u.DeviceSpecificData.DataSize;
3899             }
3900 
3901             /* The size of partial descriptors is bigger */
3902             PartialSize += EntrySize;
3903 
3904             /* Go to the next partial descriptor */
3905             PartialDescriptor = (PVOID)((ULONG_PTR)PartialDescriptor + EntrySize);
3906         }
3907 
3908         /* The size of full descriptors is bigger */
3909         FinalSize += PartialSize;
3910 
3911         /* Go to the next full descriptor */
3912         FullDescriptor = (PVOID)((ULONG_PTR)FullDescriptor + PartialSize);
3913     }
3914 
3915     /* Return the final size */
3916     return FinalSize;
3917 }
3918 
3919 NTSTATUS
3920 NTAPI
3921 PiGetDeviceRegistryProperty(IN PDEVICE_OBJECT DeviceObject,
3922                             IN ULONG ValueType,
3923                             IN PWSTR ValueName,
3924                             IN PWSTR KeyName,
3925                             OUT PVOID Buffer,
3926                             IN PULONG BufferLength)
3927 {
3928     NTSTATUS Status;
3929     HANDLE KeyHandle, SubHandle;
3930     UNICODE_STRING KeyString;
3931     PKEY_VALUE_FULL_INFORMATION KeyValueInfo = NULL;
3932     ULONG Length;
3933     PAGED_CODE();
3934 
3935     /* Find the instance key */
3936     Status = PnpDeviceObjectToDeviceInstance(DeviceObject, &KeyHandle, KEY_READ);
3937     if (NT_SUCCESS(Status))
3938     {
3939         /* Check for name given by caller */
3940         if (KeyName)
3941         {
3942             /* Open this key */
3943             RtlInitUnicodeString(&KeyString, KeyName);
3944             Status = IopOpenRegistryKeyEx(&SubHandle,
3945                                           KeyHandle,
3946                                           &KeyString,
3947                                           KEY_READ);
3948             if (NT_SUCCESS(Status))
3949             {
3950                 /* And use this handle instead */
3951                 ZwClose(KeyHandle);
3952                 KeyHandle = SubHandle;
3953             }
3954         }
3955 
3956         /* Check if sub-key handle succeeded (or no-op if no key name given) */
3957         if (NT_SUCCESS(Status))
3958         {
3959             /* Now get the size of the property */
3960             Status = IopGetRegistryValue(KeyHandle,
3961                                          ValueName,
3962                                          &KeyValueInfo);
3963         }
3964 
3965         /* Close the key */
3966         ZwClose(KeyHandle);
3967     }
3968 
3969     /* Fail if any of the registry operations failed */
3970     if (!NT_SUCCESS(Status)) return Status;
3971 
3972     /* Check how much data we have to copy */
3973     Length = KeyValueInfo->DataLength;
3974     if (*BufferLength >= Length)
3975     {
3976         /* Check for a match in the value type */
3977         if (KeyValueInfo->Type == ValueType)
3978         {
3979             /* Copy the data */
3980             RtlCopyMemory(Buffer,
3981                           (PVOID)((ULONG_PTR)KeyValueInfo +
3982                           KeyValueInfo->DataOffset),
3983                           Length);
3984         }
3985         else
3986         {
3987             /* Invalid registry property type, fail */
3988            Status = STATUS_INVALID_PARAMETER_2;
3989         }
3990     }
3991     else
3992     {
3993         /* Buffer is too small to hold data */
3994         Status = STATUS_BUFFER_TOO_SMALL;
3995     }
3996 
3997     /* Return the required buffer length, free the buffer, and return status */
3998     *BufferLength = Length;
3999     ExFreePool(KeyValueInfo);
4000     return Status;
4001 }
4002 
4003 #define PIP_RETURN_DATA(x, y)   {ReturnLength = x; Data = y; Status = STATUS_SUCCESS; break;}
4004 #define PIP_REGISTRY_DATA(x, y) {ValueName = x; ValueType = y; break;}
4005 #define PIP_UNIMPLEMENTED()     {UNIMPLEMENTED_DBGBREAK(); break;}
4006 
4007 /*
4008  * @implemented
4009  */
4010 NTSTATUS
4011 NTAPI
4012 IoGetDeviceProperty(IN PDEVICE_OBJECT DeviceObject,
4013                     IN DEVICE_REGISTRY_PROPERTY DeviceProperty,
4014                     IN ULONG BufferLength,
4015                     OUT PVOID PropertyBuffer,
4016                     OUT PULONG ResultLength)
4017 {
4018     PDEVICE_NODE DeviceNode = IopGetDeviceNode(DeviceObject);
4019     DEVICE_CAPABILITIES DeviceCaps;
4020     ULONG ReturnLength = 0, Length = 0, ValueType;
4021     PWCHAR ValueName = NULL, EnumeratorNameEnd, DeviceInstanceName;
4022     PVOID Data = NULL;
4023     NTSTATUS Status = STATUS_BUFFER_TOO_SMALL;
4024     GUID BusTypeGuid;
4025     POBJECT_NAME_INFORMATION ObjectNameInfo = NULL;
4026     BOOLEAN NullTerminate = FALSE;
4027 
4028     DPRINT("IoGetDeviceProperty(0x%p %d)\n", DeviceObject, DeviceProperty);
4029 
4030     /* Assume failure */
4031     *ResultLength = 0;
4032 
4033     /* Only PDOs can call this */
4034     if (!DeviceNode) return STATUS_INVALID_DEVICE_REQUEST;
4035 
4036     /* Handle all properties */
4037     switch (DeviceProperty)
4038     {
4039         case DevicePropertyBusTypeGuid:
4040 
4041             /* Get the GUID from the internal cache */
4042             Status = PnpBusTypeGuidGet(DeviceNode->ChildBusTypeIndex, &BusTypeGuid);
4043             if (!NT_SUCCESS(Status)) return Status;
4044 
4045             /* This is the format of the returned data */
4046             PIP_RETURN_DATA(sizeof(GUID), &BusTypeGuid);
4047 
4048         case DevicePropertyLegacyBusType:
4049 
4050             /* Validate correct interface type */
4051             if (DeviceNode->ChildInterfaceType == InterfaceTypeUndefined)
4052                 return STATUS_OBJECT_NAME_NOT_FOUND;
4053 
4054             /* This is the format of the returned data */
4055             PIP_RETURN_DATA(sizeof(INTERFACE_TYPE), &DeviceNode->ChildInterfaceType);
4056 
4057         case DevicePropertyBusNumber:
4058 
4059             /* Validate correct bus number */
4060             if ((DeviceNode->ChildBusNumber & 0x80000000) == 0x80000000)
4061                 return STATUS_OBJECT_NAME_NOT_FOUND;
4062 
4063             /* This is the format of the returned data */
4064             PIP_RETURN_DATA(sizeof(ULONG), &DeviceNode->ChildBusNumber);
4065 
4066         case DevicePropertyEnumeratorName:
4067 
4068             /* Get the instance path */
4069             DeviceInstanceName = DeviceNode->InstancePath.Buffer;
4070 
4071             /* Sanity checks */
4072             ASSERT((BufferLength & 1) == 0);
4073             ASSERT(DeviceInstanceName != NULL);
4074 
4075             /* Get the name from the path */
4076             EnumeratorNameEnd = wcschr(DeviceInstanceName, OBJ_NAME_PATH_SEPARATOR);
4077             ASSERT(EnumeratorNameEnd);
4078 
4079             /* This string needs to be NULL-terminated */
4080             NullTerminate = TRUE;
4081 
4082             /* This is the format of the returned data */
4083             PIP_RETURN_DATA((ULONG)(EnumeratorNameEnd - DeviceInstanceName) * sizeof(WCHAR),
4084                             DeviceInstanceName);
4085 
4086         case DevicePropertyAddress:
4087 
4088             /* Query the device caps */
4089             Status = IopQueryDeviceCapabilities(DeviceNode, &DeviceCaps);
4090             if (!NT_SUCCESS(Status) || (DeviceCaps.Address == MAXULONG))
4091                 return STATUS_OBJECT_NAME_NOT_FOUND;
4092 
4093             /* This is the format of the returned data */
4094             PIP_RETURN_DATA(sizeof(ULONG), &DeviceCaps.Address);
4095 
4096         case DevicePropertyBootConfigurationTranslated:
4097 
4098             /* Validate we have resources */
4099             if (!DeviceNode->BootResources)
4100 //            if (!DeviceNode->BootResourcesTranslated) // FIXFIX: Need this field
4101             {
4102                 /* No resources will still fake success, but with 0 bytes */
4103                 *ResultLength = 0;
4104                 return STATUS_SUCCESS;
4105             }
4106 
4107             /* This is the format of the returned data */
4108             PIP_RETURN_DATA(PnpDetermineResourceListSize(DeviceNode->BootResources), // FIXFIX: Should use BootResourcesTranslated
4109                             DeviceNode->BootResources); // FIXFIX: Should use BootResourcesTranslated
4110 
4111         case DevicePropertyPhysicalDeviceObjectName:
4112 
4113             /* Sanity check for Unicode-sized string */
4114             ASSERT((BufferLength & 1) == 0);
4115 
4116             /* Allocate name buffer */
4117             Length = BufferLength + sizeof(OBJECT_NAME_INFORMATION);
4118             ObjectNameInfo = ExAllocatePool(PagedPool, Length);
4119             if (!ObjectNameInfo) return STATUS_INSUFFICIENT_RESOURCES;
4120 
4121             /* Query the PDO name */
4122             Status = ObQueryNameString(DeviceObject,
4123                                        ObjectNameInfo,
4124                                        Length,
4125                                        ResultLength);
4126             if (Status == STATUS_INFO_LENGTH_MISMATCH)
4127             {
4128                 /* It's up to the caller to try again */
4129                 Status = STATUS_BUFFER_TOO_SMALL;
4130             }
4131 
4132             /* This string needs to be NULL-terminated */
4133             NullTerminate = TRUE;
4134 
4135             /* Return if successful */
4136             if (NT_SUCCESS(Status)) PIP_RETURN_DATA(ObjectNameInfo->Name.Length,
4137                                                     ObjectNameInfo->Name.Buffer);
4138 
4139             /* Let the caller know how big the name is */
4140             *ResultLength -= sizeof(OBJECT_NAME_INFORMATION);
4141             break;
4142 
4143         /* Handle the registry-based properties */
4144         case DevicePropertyUINumber:
4145             PIP_REGISTRY_DATA(REGSTR_VAL_UI_NUMBER, REG_DWORD);
4146         case DevicePropertyLocationInformation:
4147             PIP_REGISTRY_DATA(REGSTR_VAL_LOCATION_INFORMATION, REG_SZ);
4148         case DevicePropertyDeviceDescription:
4149             PIP_REGISTRY_DATA(REGSTR_VAL_DEVDESC, REG_SZ);
4150         case DevicePropertyHardwareID:
4151             PIP_REGISTRY_DATA(REGSTR_VAL_HARDWAREID, REG_MULTI_SZ);
4152         case DevicePropertyCompatibleIDs:
4153             PIP_REGISTRY_DATA(REGSTR_VAL_COMPATIBLEIDS, REG_MULTI_SZ);
4154         case DevicePropertyBootConfiguration:
4155             PIP_REGISTRY_DATA(REGSTR_VAL_BOOTCONFIG, REG_RESOURCE_LIST);
4156         case DevicePropertyClassName:
4157             PIP_REGISTRY_DATA(REGSTR_VAL_CLASS, REG_SZ);
4158         case DevicePropertyClassGuid:
4159             PIP_REGISTRY_DATA(REGSTR_VAL_CLASSGUID, REG_SZ);
4160         case DevicePropertyDriverKeyName:
4161             PIP_REGISTRY_DATA(REGSTR_VAL_DRIVER, REG_SZ);
4162         case DevicePropertyManufacturer:
4163             PIP_REGISTRY_DATA(REGSTR_VAL_MFG, REG_SZ);
4164         case DevicePropertyFriendlyName:
4165             PIP_REGISTRY_DATA(REGSTR_VAL_FRIENDLYNAME, REG_SZ);
4166         case DevicePropertyContainerID:
4167             //PIP_REGISTRY_DATA(REGSTR_VAL_CONTAINERID, REG_SZ); // Win7
4168             PIP_UNIMPLEMENTED();
4169         case DevicePropertyRemovalPolicy:
4170             PIP_UNIMPLEMENTED();
4171             break;
4172         case DevicePropertyInstallState:
4173             PIP_REGISTRY_DATA(REGSTR_VAL_CONFIGFLAGS, REG_DWORD);
4174             break;
4175         case DevicePropertyResourceRequirements:
4176             PIP_UNIMPLEMENTED();
4177         case DevicePropertyAllocatedResources:
4178             PIP_UNIMPLEMENTED();
4179         default:
4180             return STATUS_INVALID_PARAMETER_2;
4181     }
4182 
4183     /* Having a registry value name implies registry data */
4184     if (ValueName)
4185     {
4186         /* We know up-front how much data to expect */
4187         *ResultLength = BufferLength;
4188 
4189         /* Go get the data, use the LogConf subkey if necessary */
4190         Status = PiGetDeviceRegistryProperty(DeviceObject,
4191                                              ValueType,
4192                                              ValueName,
4193                                              (DeviceProperty ==
4194                                               DevicePropertyBootConfiguration) ?
4195                                              L"LogConf":  NULL,
4196                                              PropertyBuffer,
4197                                              ResultLength);
4198     }
4199     else if (NT_SUCCESS(Status))
4200     {
4201         /* We know up-front how much data to expect, check the caller's buffer */
4202         *ResultLength = ReturnLength + (NullTerminate ? sizeof(UNICODE_NULL) : 0);
4203         if (*ResultLength <= BufferLength)
4204         {
4205             /* Buffer is all good, copy the data */
4206             RtlCopyMemory(PropertyBuffer, Data, ReturnLength);
4207 
4208             /* Check if we need to NULL-terminate the string */
4209             if (NullTerminate)
4210             {
4211                 /* Terminate the string */
4212                 ((PWCHAR)PropertyBuffer)[ReturnLength / sizeof(WCHAR)] = UNICODE_NULL;
4213             }
4214 
4215             /* This is the success path */
4216             Status = STATUS_SUCCESS;
4217         }
4218         else
4219         {
4220             /* Failure path */
4221             Status = STATUS_BUFFER_TOO_SMALL;
4222         }
4223     }
4224 
4225     /* Free any allocation we may have made, and return the status code */
4226     if (ObjectNameInfo) ExFreePool(ObjectNameInfo);
4227     return Status;
4228 }
4229 
4230 /*
4231  * @implemented
4232  */
4233 VOID
4234 NTAPI
4235 IoInvalidateDeviceState(IN PDEVICE_OBJECT PhysicalDeviceObject)
4236 {
4237     PDEVICE_NODE DeviceNode = IopGetDeviceNode(PhysicalDeviceObject);
4238     IO_STACK_LOCATION Stack;
4239     ULONG PnPFlags;
4240     NTSTATUS Status;
4241     IO_STATUS_BLOCK IoStatusBlock;
4242 
4243     RtlZeroMemory(&Stack, sizeof(IO_STACK_LOCATION));
4244     Stack.MajorFunction = IRP_MJ_PNP;
4245     Stack.MinorFunction = IRP_MN_QUERY_PNP_DEVICE_STATE;
4246 
4247     Status = IopSynchronousCall(PhysicalDeviceObject, &Stack, (PVOID*)&PnPFlags);
4248     if (!NT_SUCCESS(Status))
4249     {
4250         if (Status != STATUS_NOT_SUPPORTED)
4251         {
4252             DPRINT1("IRP_MN_QUERY_PNP_DEVICE_STATE failed with status 0x%lx\n", Status);
4253         }
4254         return;
4255     }
4256 
4257     if (PnPFlags & PNP_DEVICE_NOT_DISABLEABLE)
4258         DeviceNode->UserFlags |= DNUF_NOT_DISABLEABLE;
4259     else
4260         DeviceNode->UserFlags &= ~DNUF_NOT_DISABLEABLE;
4261 
4262     if (PnPFlags & PNP_DEVICE_DONT_DISPLAY_IN_UI)
4263         DeviceNode->UserFlags |= DNUF_DONT_SHOW_IN_UI;
4264     else
4265         DeviceNode->UserFlags &= ~DNUF_DONT_SHOW_IN_UI;
4266 
4267     if ((PnPFlags & PNP_DEVICE_REMOVED) ||
4268         ((PnPFlags & PNP_DEVICE_FAILED) && !(PnPFlags & PNP_DEVICE_RESOURCE_REQUIREMENTS_CHANGED)))
4269     {
4270         /* Flag it if it's failed */
4271         if (PnPFlags & PNP_DEVICE_FAILED) DeviceNode->Problem = CM_PROB_FAILED_POST_START;
4272 
4273         /* Send removal IRPs to all of its children */
4274         IopPrepareDeviceForRemoval(PhysicalDeviceObject, TRUE);
4275 
4276         /* Send surprise removal */
4277         IopSendSurpriseRemoval(PhysicalDeviceObject);
4278 
4279         /* Tell the user-mode PnP manager that a device was removed */
4280         IopQueueTargetDeviceEvent(&GUID_DEVICE_SURPRISE_REMOVAL,
4281                                   &DeviceNode->InstancePath);
4282 
4283         IopSendRemoveDevice(PhysicalDeviceObject);
4284     }
4285     else if ((PnPFlags & PNP_DEVICE_FAILED) && (PnPFlags & PNP_DEVICE_RESOURCE_REQUIREMENTS_CHANGED))
4286     {
4287         /* Stop for resource rebalance */
4288         Status = IopStopDevice(DeviceNode);
4289         if (!NT_SUCCESS(Status))
4290         {
4291             DPRINT1("Failed to stop device for rebalancing\n");
4292 
4293             /* Stop failed so don't rebalance */
4294             PnPFlags &= ~PNP_DEVICE_RESOURCE_REQUIREMENTS_CHANGED;
4295         }
4296     }
4297 
4298     /* Resource rebalance */
4299     if (PnPFlags & PNP_DEVICE_RESOURCE_REQUIREMENTS_CHANGED)
4300     {
4301         DPRINT("Sending IRP_MN_QUERY_RESOURCES to device stack\n");
4302 
4303         Status = IopInitiatePnpIrp(PhysicalDeviceObject,
4304                                    &IoStatusBlock,
4305                                    IRP_MN_QUERY_RESOURCES,
4306                                    NULL);
4307         if (NT_SUCCESS(Status) && IoStatusBlock.Information)
4308         {
4309             DeviceNode->BootResources =
4310             (PCM_RESOURCE_LIST)IoStatusBlock.Information;
4311             IopDeviceNodeSetFlag(DeviceNode, DNF_HAS_BOOT_CONFIG);
4312         }
4313         else
4314         {
4315             DPRINT("IopInitiatePnpIrp() failed (Status %x) or IoStatusBlock.Information=NULL\n", Status);
4316             DeviceNode->BootResources = NULL;
4317         }
4318 
4319         DPRINT("Sending IRP_MN_QUERY_RESOURCE_REQUIREMENTS to device stack\n");
4320 
4321         Status = IopInitiatePnpIrp(PhysicalDeviceObject,
4322                                    &IoStatusBlock,
4323                                    IRP_MN_QUERY_RESOURCE_REQUIREMENTS,
4324                                    NULL);
4325         if (NT_SUCCESS(Status))
4326         {
4327             DeviceNode->ResourceRequirements =
4328             (PIO_RESOURCE_REQUIREMENTS_LIST)IoStatusBlock.Information;
4329         }
4330         else
4331         {
4332             DPRINT("IopInitiatePnpIrp() failed (Status %08lx)\n", Status);
4333             DeviceNode->ResourceRequirements = NULL;
4334         }
4335 
4336         /* IRP_MN_FILTER_RESOURCE_REQUIREMENTS is called indirectly by IopStartDevice */
4337         if (IopStartDevice(DeviceNode) != STATUS_SUCCESS)
4338         {
4339             DPRINT1("Restart after resource rebalance failed\n");
4340 
4341             DeviceNode->Flags &= ~(DNF_STARTED | DNF_START_REQUEST_PENDING);
4342             DeviceNode->Flags |= DNF_START_FAILED;
4343 
4344             IopRemoveDevice(DeviceNode);
4345         }
4346     }
4347 }
4348 
4349 /**
4350  * @name IoOpenDeviceRegistryKey
4351  *
4352  * Open a registry key unique for a specified driver or device instance.
4353  *
4354  * @param DeviceObject   Device to get the registry key for.
4355  * @param DevInstKeyType Type of the key to return.
4356  * @param DesiredAccess  Access mask (eg. KEY_READ | KEY_WRITE).
4357  * @param DevInstRegKey  Handle to the opened registry key on
4358  *                       successful return.
4359  *
4360  * @return Status.
4361  *
4362  * @implemented
4363  */
4364 NTSTATUS
4365 NTAPI
4366 IoOpenDeviceRegistryKey(IN PDEVICE_OBJECT DeviceObject,
4367                         IN ULONG DevInstKeyType,
4368                         IN ACCESS_MASK DesiredAccess,
4369                         OUT PHANDLE DevInstRegKey)
4370 {
4371    static WCHAR RootKeyName[] =
4372       L"\\Registry\\Machine\\System\\CurrentControlSet\\";
4373    static WCHAR ProfileKeyName[] =
4374       L"Hardware Profiles\\Current\\System\\CurrentControlSet\\";
4375    static WCHAR ClassKeyName[] = L"Control\\Class\\";
4376    static WCHAR EnumKeyName[] = L"Enum\\";
4377    static WCHAR DeviceParametersKeyName[] = L"Device Parameters";
4378    ULONG KeyNameLength;
4379    PWSTR KeyNameBuffer;
4380    UNICODE_STRING KeyName;
4381    ULONG DriverKeyLength;
4382    OBJECT_ATTRIBUTES ObjectAttributes;
4383    PDEVICE_NODE DeviceNode = NULL;
4384    NTSTATUS Status;
4385 
4386    DPRINT("IoOpenDeviceRegistryKey() called\n");
4387 
4388    if ((DevInstKeyType & (PLUGPLAY_REGKEY_DEVICE | PLUGPLAY_REGKEY_DRIVER)) == 0)
4389    {
4390        DPRINT1("IoOpenDeviceRegistryKey(): got wrong params, exiting... \n");
4391        return STATUS_INVALID_PARAMETER;
4392    }
4393 
4394    if (!IopIsValidPhysicalDeviceObject(DeviceObject))
4395        return STATUS_INVALID_DEVICE_REQUEST;
4396    DeviceNode = IopGetDeviceNode(DeviceObject);
4397 
4398    /*
4399     * Calculate the length of the base key name. This is the full
4400     * name for driver key or the name excluding "Device Parameters"
4401     * subkey for device key.
4402     */
4403 
4404    KeyNameLength = sizeof(RootKeyName);
4405    if (DevInstKeyType & PLUGPLAY_REGKEY_CURRENT_HWPROFILE)
4406       KeyNameLength += sizeof(ProfileKeyName) - sizeof(UNICODE_NULL);
4407    if (DevInstKeyType & PLUGPLAY_REGKEY_DRIVER)
4408    {
4409       KeyNameLength += sizeof(ClassKeyName) - sizeof(UNICODE_NULL);
4410       Status = IoGetDeviceProperty(DeviceObject, DevicePropertyDriverKeyName,
4411                                    0, NULL, &DriverKeyLength);
4412       if (Status != STATUS_BUFFER_TOO_SMALL)
4413          return Status;
4414       KeyNameLength += DriverKeyLength;
4415    }
4416    else
4417    {
4418       KeyNameLength += sizeof(EnumKeyName) - sizeof(UNICODE_NULL) +
4419                        DeviceNode->InstancePath.Length;
4420    }
4421 
4422    /*
4423     * Now allocate the buffer for the key name...
4424     */
4425 
4426    KeyNameBuffer = ExAllocatePool(PagedPool, KeyNameLength);
4427    if (KeyNameBuffer == NULL)
4428       return STATUS_INSUFFICIENT_RESOURCES;
4429 
4430    KeyName.Length = 0;
4431    KeyName.MaximumLength = (USHORT)KeyNameLength;
4432    KeyName.Buffer = KeyNameBuffer;
4433 
4434    /*
4435     * ...and build the key name.
4436     */
4437 
4438    KeyName.Length += sizeof(RootKeyName) - sizeof(UNICODE_NULL);
4439    RtlCopyMemory(KeyNameBuffer, RootKeyName, KeyName.Length);
4440 
4441    if (DevInstKeyType & PLUGPLAY_REGKEY_CURRENT_HWPROFILE)
4442       RtlAppendUnicodeToString(&KeyName, ProfileKeyName);
4443 
4444    if (DevInstKeyType & PLUGPLAY_REGKEY_DRIVER)
4445    {
4446       RtlAppendUnicodeToString(&KeyName, ClassKeyName);
4447       Status = IoGetDeviceProperty(DeviceObject, DevicePropertyDriverKeyName,
4448                                    DriverKeyLength, KeyNameBuffer +
4449                                    (KeyName.Length / sizeof(WCHAR)),
4450                                    &DriverKeyLength);
4451       if (!NT_SUCCESS(Status))
4452       {
4453          DPRINT1("Call to IoGetDeviceProperty() failed with Status 0x%08lx\n", Status);
4454          ExFreePool(KeyNameBuffer);
4455          return Status;
4456       }
4457       KeyName.Length += (USHORT)DriverKeyLength - sizeof(UNICODE_NULL);
4458    }
4459    else
4460    {
4461       RtlAppendUnicodeToString(&KeyName, EnumKeyName);
4462       Status = RtlAppendUnicodeStringToString(&KeyName, &DeviceNode->InstancePath);
4463       if (DeviceNode->InstancePath.Length == 0)
4464       {
4465          ExFreePool(KeyNameBuffer);
4466          return Status;
4467       }
4468    }
4469 
4470    /*
4471     * Open the base key.
4472     */
4473    Status = IopOpenRegistryKeyEx(DevInstRegKey, NULL, &KeyName, DesiredAccess);
4474    if (!NT_SUCCESS(Status))
4475    {
4476       DPRINT1("IoOpenDeviceRegistryKey(%wZ): Base key doesn't exist, exiting... (Status 0x%08lx)\n", &KeyName, Status);
4477       ExFreePool(KeyNameBuffer);
4478       return Status;
4479    }
4480    ExFreePool(KeyNameBuffer);
4481 
4482    /*
4483     * For driver key we're done now.
4484     */
4485 
4486    if (DevInstKeyType & PLUGPLAY_REGKEY_DRIVER)
4487       return Status;
4488 
4489    /*
4490     * Let's go further. For device key we must open "Device Parameters"
4491     * subkey and create it if it doesn't exist yet.
4492     */
4493 
4494    RtlInitUnicodeString(&KeyName, DeviceParametersKeyName);
4495    InitializeObjectAttributes(&ObjectAttributes,
4496                               &KeyName,
4497                               OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
4498                               *DevInstRegKey,
4499                               NULL);
4500    Status = ZwCreateKey(DevInstRegKey, DesiredAccess, &ObjectAttributes,
4501                         0, NULL, ExpInTextModeSetup ? REG_OPTION_VOLATILE : 0, NULL);
4502    ZwClose(ObjectAttributes.RootDirectory);
4503 
4504    return Status;
4505 }
4506 
4507 static
4508 NTSTATUS
4509 IopQueryRemoveChildDevices(PDEVICE_NODE ParentDeviceNode, BOOLEAN Force)
4510 {
4511     PDEVICE_NODE ChildDeviceNode, NextDeviceNode, FailedRemoveDevice;
4512     NTSTATUS Status;
4513     KIRQL OldIrql;
4514 
4515     KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql);
4516     ChildDeviceNode = ParentDeviceNode->Child;
4517     while (ChildDeviceNode != NULL)
4518     {
4519         NextDeviceNode = ChildDeviceNode->Sibling;
4520         KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql);
4521 
4522         Status = IopPrepareDeviceForRemoval(ChildDeviceNode->PhysicalDeviceObject, Force);
4523         if (!NT_SUCCESS(Status))
4524         {
4525             FailedRemoveDevice = ChildDeviceNode;
4526             goto cleanup;
4527         }
4528 
4529         KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql);
4530         ChildDeviceNode = NextDeviceNode;
4531     }
4532     KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql);
4533 
4534     return STATUS_SUCCESS;
4535 
4536 cleanup:
4537     KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql);
4538     ChildDeviceNode = ParentDeviceNode->Child;
4539     while (ChildDeviceNode != NULL)
4540     {
4541         NextDeviceNode = ChildDeviceNode->Sibling;
4542         KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql);
4543 
4544         IopCancelPrepareDeviceForRemoval(ChildDeviceNode->PhysicalDeviceObject);
4545 
4546         /* IRP_MN_CANCEL_REMOVE_DEVICE is also sent to the device
4547          * that failed the IRP_MN_QUERY_REMOVE_DEVICE request */
4548         if (ChildDeviceNode == FailedRemoveDevice)
4549             return Status;
4550 
4551         ChildDeviceNode = NextDeviceNode;
4552 
4553         KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql);
4554     }
4555     KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql);
4556 
4557     return Status;
4558 }
4559 
4560 static
4561 VOID
4562 IopSendRemoveChildDevices(PDEVICE_NODE ParentDeviceNode)
4563 {
4564     PDEVICE_NODE ChildDeviceNode, NextDeviceNode;
4565     KIRQL OldIrql;
4566 
4567     KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql);
4568     ChildDeviceNode = ParentDeviceNode->Child;
4569     while (ChildDeviceNode != NULL)
4570     {
4571         NextDeviceNode = ChildDeviceNode->Sibling;
4572         KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql);
4573 
4574         IopSendRemoveDevice(ChildDeviceNode->PhysicalDeviceObject);
4575 
4576         ChildDeviceNode = NextDeviceNode;
4577 
4578         KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql);
4579     }
4580     KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql);
4581 }
4582 
4583 static
4584 VOID
4585 IopCancelRemoveChildDevices(PDEVICE_NODE ParentDeviceNode)
4586 {
4587     PDEVICE_NODE ChildDeviceNode, NextDeviceNode;
4588     KIRQL OldIrql;
4589 
4590     KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql);
4591     ChildDeviceNode = ParentDeviceNode->Child;
4592     while (ChildDeviceNode != NULL)
4593     {
4594         NextDeviceNode = ChildDeviceNode->Sibling;
4595         KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql);
4596 
4597         IopCancelPrepareDeviceForRemoval(ChildDeviceNode->PhysicalDeviceObject);
4598 
4599         ChildDeviceNode = NextDeviceNode;
4600 
4601         KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql);
4602     }
4603     KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql);
4604 }
4605 
4606 static
4607 NTSTATUS
4608 IopQueryRemoveDeviceRelations(PDEVICE_RELATIONS DeviceRelations, BOOLEAN Force)
4609 {
4610     /* This function DOES NOT dereference the device objects on SUCCESS
4611      * but it DOES dereference device objects on FAILURE */
4612 
4613     ULONG i, j;
4614     NTSTATUS Status;
4615 
4616     for (i = 0; i < DeviceRelations->Count; i++)
4617     {
4618         Status = IopPrepareDeviceForRemoval(DeviceRelations->Objects[i], Force);
4619         if (!NT_SUCCESS(Status))
4620         {
4621             j = i;
4622             goto cleanup;
4623         }
4624     }
4625 
4626     return STATUS_SUCCESS;
4627 
4628 cleanup:
4629     /* IRP_MN_CANCEL_REMOVE_DEVICE is also sent to the device
4630      * that failed the IRP_MN_QUERY_REMOVE_DEVICE request */
4631     for (i = 0; i <= j; i++)
4632     {
4633         IopCancelPrepareDeviceForRemoval(DeviceRelations->Objects[i]);
4634         ObDereferenceObject(DeviceRelations->Objects[i]);
4635         DeviceRelations->Objects[i] = NULL;
4636     }
4637     for (; i < DeviceRelations->Count; i++)
4638     {
4639         ObDereferenceObject(DeviceRelations->Objects[i]);
4640         DeviceRelations->Objects[i] = NULL;
4641     }
4642     ExFreePool(DeviceRelations);
4643 
4644     return Status;
4645 }
4646 
4647 static
4648 VOID
4649 IopSendRemoveDeviceRelations(PDEVICE_RELATIONS DeviceRelations)
4650 {
4651     /* This function DOES dereference the device objects in all cases */
4652 
4653     ULONG i;
4654 
4655     for (i = 0; i < DeviceRelations->Count; i++)
4656     {
4657         IopSendRemoveDevice(DeviceRelations->Objects[i]);
4658         DeviceRelations->Objects[i] = NULL;
4659     }
4660 
4661     ExFreePool(DeviceRelations);
4662 }
4663 
4664 static
4665 VOID
4666 IopCancelRemoveDeviceRelations(PDEVICE_RELATIONS DeviceRelations)
4667 {
4668     /* This function DOES dereference the device objects in all cases */
4669 
4670     ULONG i;
4671 
4672     for (i = 0; i < DeviceRelations->Count; i++)
4673     {
4674         IopCancelPrepareDeviceForRemoval(DeviceRelations->Objects[i]);
4675         ObDereferenceObject(DeviceRelations->Objects[i]);
4676         DeviceRelations->Objects[i] = NULL;
4677     }
4678 
4679     ExFreePool(DeviceRelations);
4680 }
4681 
4682 VOID
4683 IopCancelPrepareDeviceForRemoval(PDEVICE_OBJECT DeviceObject)
4684 {
4685     IO_STACK_LOCATION Stack;
4686     IO_STATUS_BLOCK IoStatusBlock;
4687     PDEVICE_RELATIONS DeviceRelations;
4688     NTSTATUS Status;
4689 
4690     IopCancelRemoveDevice(DeviceObject);
4691 
4692     Stack.Parameters.QueryDeviceRelations.Type = RemovalRelations;
4693 
4694     Status = IopInitiatePnpIrp(DeviceObject,
4695                                &IoStatusBlock,
4696                                IRP_MN_QUERY_DEVICE_RELATIONS,
4697                                &Stack);
4698     if (!NT_SUCCESS(Status))
4699     {
4700         DPRINT("IopInitiatePnpIrp() failed with status 0x%08lx\n", Status);
4701         DeviceRelations = NULL;
4702     }
4703     else
4704     {
4705         DeviceRelations = (PDEVICE_RELATIONS)IoStatusBlock.Information;
4706     }
4707 
4708     if (DeviceRelations)
4709         IopCancelRemoveDeviceRelations(DeviceRelations);
4710 }
4711 
4712 NTSTATUS
4713 IopPrepareDeviceForRemoval(IN PDEVICE_OBJECT DeviceObject, BOOLEAN Force)
4714 {
4715     PDEVICE_NODE DeviceNode = IopGetDeviceNode(DeviceObject);
4716     IO_STACK_LOCATION Stack;
4717     IO_STATUS_BLOCK IoStatusBlock;
4718     PDEVICE_RELATIONS DeviceRelations;
4719     NTSTATUS Status;
4720 
4721     if ((DeviceNode->UserFlags & DNUF_NOT_DISABLEABLE) && !Force)
4722     {
4723         DPRINT1("Removal not allowed for %wZ\n", &DeviceNode->InstancePath);
4724         return STATUS_UNSUCCESSFUL;
4725     }
4726 
4727     if (!Force && IopQueryRemoveDevice(DeviceObject) != STATUS_SUCCESS)
4728     {
4729         DPRINT1("Removal vetoed by failing the query remove request\n");
4730 
4731         IopCancelRemoveDevice(DeviceObject);
4732 
4733         return STATUS_UNSUCCESSFUL;
4734     }
4735 
4736     Stack.Parameters.QueryDeviceRelations.Type = RemovalRelations;
4737 
4738     Status = IopInitiatePnpIrp(DeviceObject,
4739                                &IoStatusBlock,
4740                                IRP_MN_QUERY_DEVICE_RELATIONS,
4741                                &Stack);
4742     if (!NT_SUCCESS(Status))
4743     {
4744         DPRINT("IopInitiatePnpIrp() failed with status 0x%08lx\n", Status);
4745         DeviceRelations = NULL;
4746     }
4747     else
4748     {
4749         DeviceRelations = (PDEVICE_RELATIONS)IoStatusBlock.Information;
4750     }
4751 
4752     if (DeviceRelations)
4753     {
4754         Status = IopQueryRemoveDeviceRelations(DeviceRelations, Force);
4755         if (!NT_SUCCESS(Status))
4756             return Status;
4757     }
4758 
4759     Status = IopQueryRemoveChildDevices(DeviceNode, Force);
4760     if (!NT_SUCCESS(Status))
4761     {
4762         if (DeviceRelations)
4763             IopCancelRemoveDeviceRelations(DeviceRelations);
4764         return Status;
4765     }
4766 
4767     if (DeviceRelations)
4768         IopSendRemoveDeviceRelations(DeviceRelations);
4769     IopSendRemoveChildDevices(DeviceNode);
4770 
4771     return STATUS_SUCCESS;
4772 }
4773 
4774 NTSTATUS
4775 IopRemoveDevice(PDEVICE_NODE DeviceNode)
4776 {
4777     NTSTATUS Status;
4778 
4779     DPRINT("Removing device: %wZ\n", &DeviceNode->InstancePath);
4780 
4781     Status = IopPrepareDeviceForRemoval(DeviceNode->PhysicalDeviceObject, FALSE);
4782     if (NT_SUCCESS(Status))
4783     {
4784         IopSendRemoveDevice(DeviceNode->PhysicalDeviceObject);
4785         IopQueueTargetDeviceEvent(&GUID_DEVICE_SAFE_REMOVAL,
4786                                   &DeviceNode->InstancePath);
4787         return STATUS_SUCCESS;
4788     }
4789 
4790     return Status;
4791 }
4792 
4793 /*
4794  * @implemented
4795  */
4796 VOID
4797 NTAPI
4798 IoRequestDeviceEject(IN PDEVICE_OBJECT PhysicalDeviceObject)
4799 {
4800     PDEVICE_NODE DeviceNode = IopGetDeviceNode(PhysicalDeviceObject);
4801     PDEVICE_RELATIONS DeviceRelations;
4802     IO_STATUS_BLOCK IoStatusBlock;
4803     IO_STACK_LOCATION Stack;
4804     DEVICE_CAPABILITIES Capabilities;
4805     NTSTATUS Status;
4806 
4807     IopQueueTargetDeviceEvent(&GUID_DEVICE_KERNEL_INITIATED_EJECT,
4808                               &DeviceNode->InstancePath);
4809 
4810     if (IopQueryDeviceCapabilities(DeviceNode, &Capabilities) != STATUS_SUCCESS)
4811     {
4812         goto cleanup;
4813     }
4814 
4815     Stack.Parameters.QueryDeviceRelations.Type = EjectionRelations;
4816 
4817     Status = IopInitiatePnpIrp(PhysicalDeviceObject,
4818                                &IoStatusBlock,
4819                                IRP_MN_QUERY_DEVICE_RELATIONS,
4820                                &Stack);
4821     if (!NT_SUCCESS(Status))
4822     {
4823         DPRINT("IopInitiatePnpIrp() failed with status 0x%08lx\n", Status);
4824         DeviceRelations = NULL;
4825     }
4826     else
4827     {
4828         DeviceRelations = (PDEVICE_RELATIONS)IoStatusBlock.Information;
4829     }
4830 
4831     if (DeviceRelations)
4832     {
4833         Status = IopQueryRemoveDeviceRelations(DeviceRelations, FALSE);
4834         if (!NT_SUCCESS(Status))
4835             goto cleanup;
4836     }
4837 
4838     Status = IopQueryRemoveChildDevices(DeviceNode, FALSE);
4839     if (!NT_SUCCESS(Status))
4840     {
4841         if (DeviceRelations)
4842             IopCancelRemoveDeviceRelations(DeviceRelations);
4843         goto cleanup;
4844     }
4845 
4846     if (IopPrepareDeviceForRemoval(PhysicalDeviceObject, FALSE) != STATUS_SUCCESS)
4847     {
4848         if (DeviceRelations)
4849             IopCancelRemoveDeviceRelations(DeviceRelations);
4850         IopCancelRemoveChildDevices(DeviceNode);
4851         goto cleanup;
4852     }
4853 
4854     if (DeviceRelations)
4855         IopSendRemoveDeviceRelations(DeviceRelations);
4856     IopSendRemoveChildDevices(DeviceNode);
4857 
4858     DeviceNode->Problem = CM_PROB_HELD_FOR_EJECT;
4859     if (Capabilities.EjectSupported)
4860     {
4861         if (IopSendEject(PhysicalDeviceObject) != STATUS_SUCCESS)
4862         {
4863             goto cleanup;
4864         }
4865     }
4866     else
4867     {
4868         DeviceNode->Flags |= DNF_DISABLED;
4869     }
4870 
4871     IopQueueTargetDeviceEvent(&GUID_DEVICE_EJECT,
4872                               &DeviceNode->InstancePath);
4873 
4874     return;
4875 
4876 cleanup:
4877     IopQueueTargetDeviceEvent(&GUID_DEVICE_EJECT_VETOED,
4878                               &DeviceNode->InstancePath);
4879 }
4880 
4881 /*
4882  * @implemented
4883  */
4884 VOID
4885 NTAPI
4886 IoInvalidateDeviceRelations(
4887     IN PDEVICE_OBJECT DeviceObject,
4888     IN DEVICE_RELATION_TYPE Type)
4889 {
4890     PINVALIDATE_DEVICE_RELATION_DATA Data;
4891     KIRQL OldIrql;
4892 
4893     Data = ExAllocatePool(NonPagedPool, sizeof(INVALIDATE_DEVICE_RELATION_DATA));
4894     if (!Data)
4895         return;
4896 
4897     ObReferenceObject(DeviceObject);
4898     Data->DeviceObject = DeviceObject;
4899     Data->Type = Type;
4900 
4901     KeAcquireSpinLock(&IopDeviceRelationsSpinLock, &OldIrql);
4902     InsertTailList(&IopDeviceRelationsRequestList, &Data->RequestListEntry);
4903     if (IopDeviceRelationsRequestInProgress)
4904     {
4905         KeReleaseSpinLock(&IopDeviceRelationsSpinLock, OldIrql);
4906         return;
4907     }
4908     IopDeviceRelationsRequestInProgress = TRUE;
4909     KeReleaseSpinLock(&IopDeviceRelationsSpinLock, OldIrql);
4910 
4911     ExInitializeWorkItem(&IopDeviceRelationsWorkItem,
4912                          IopDeviceRelationsWorker,
4913                          NULL);
4914     ExQueueWorkItem(&IopDeviceRelationsWorkItem,
4915                     DelayedWorkQueue);
4916 }
4917 
4918 /*
4919  * @implemented
4920  */
4921 NTSTATUS
4922 NTAPI
4923 IoSynchronousInvalidateDeviceRelations(
4924     IN PDEVICE_OBJECT DeviceObject,
4925     IN DEVICE_RELATION_TYPE Type)
4926 {
4927     PAGED_CODE();
4928 
4929     switch (Type)
4930     {
4931         case BusRelations:
4932             /* Enumerate the device */
4933             return IopEnumerateDevice(DeviceObject);
4934         case PowerRelations:
4935              /* Not handled yet */
4936              return STATUS_NOT_IMPLEMENTED;
4937         case TargetDeviceRelation:
4938             /* Nothing to do */
4939             return STATUS_SUCCESS;
4940         default:
4941             /* Ejection relations are not supported */
4942             return STATUS_NOT_SUPPORTED;
4943     }
4944 }
4945 
4946 /*
4947  * @implemented
4948  */
4949 BOOLEAN
4950 NTAPI
4951 IoTranslateBusAddress(IN INTERFACE_TYPE InterfaceType,
4952                       IN ULONG BusNumber,
4953                       IN PHYSICAL_ADDRESS BusAddress,
4954                       IN OUT PULONG AddressSpace,
4955                       OUT PPHYSICAL_ADDRESS TranslatedAddress)
4956 {
4957     /* FIXME: Notify the resource arbiter */
4958 
4959     return HalTranslateBusAddress(InterfaceType,
4960                                   BusNumber,
4961                                   BusAddress,
4962                                   AddressSpace,
4963                                   TranslatedAddress);
4964 }
4965