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