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