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