xref: /reactos/ntoskrnl/io/pnpmgr/devaction.c (revision 6b82ed57)
1 /*
2  * PROJECT:     ReactOS Kernel
3  * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:     PnP manager device manipulation functions
5  * COPYRIGHT:   Casper S. Hornstrup (chorns@users.sourceforge.net)
6  *              2007 Hervé Poussineau (hpoussin@reactos.org)
7  *              2014-2017 Thomas Faber (thomas.faber@reactos.org)
8  *              2020 Victor Perevertkin (victor.perevertkin@reactos.org)
9  */
10 
11 /* Device tree is a resource shared among all system services: hal, kernel, drivers etc.
12  * Thus all code which interacts with the tree needs to be synchronized.
13  * Here it's done via a list of DEVICE_ACTION_REQUEST structures, which represents
14  * the device action queue. It is being processed exclusively by the PipDeviceActionWorker.
15  *
16  * Operation queuing can be done with the PiQueueDeviceAction function or with
17  * the PiPerfomSyncDeviceAction for synchronous operations.
18  * All device manipulation like starting, removing, enumeration (see DEVICE_ACTION enum)
19  * have to be done with the PiQueueDeviceAction in order to avoid race conditions.
20  *
21  * Note: there is one special operation here - PiActionEnumRootDevices. It is meant to be done
22  * during initialization process (and be the first device tree operation executed) and
23  * is always executed synchronously.
24  */
25 
26 /* INCLUDES ******************************************************************/
27 
28 #include <ntoskrnl.h>
29 #define NDEBUG
30 #include <debug.h>
31 
32 /* GLOBALS *******************************************************************/
33 
34 extern ERESOURCE IopDriverLoadResource;
35 extern BOOLEAN PnpSystemInit;
36 extern PDEVICE_NODE IopRootDeviceNode;
37 extern BOOLEAN PnPBootDriversLoaded;
38 extern BOOLEAN PnPBootDriversInitialized;
39 
40 #define MAX_DEVICE_ID_LEN          200
41 #define MAX_SEPARATORS_INSTANCEID  0
42 #define MAX_SEPARATORS_DEVICEID    1
43 
44 /* DATA **********************************************************************/
45 
46 LIST_ENTRY IopDeviceActionRequestList;
47 WORK_QUEUE_ITEM IopDeviceActionWorkItem;
48 BOOLEAN IopDeviceActionInProgress;
49 KSPIN_LOCK IopDeviceActionLock;
50 KEVENT PiEnumerationFinished;
51 static const WCHAR ServicesKeyName[] = L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\";
52 
53 /* TYPES *********************************************************************/
54 
55 typedef struct _DEVICE_ACTION_REQUEST
56 {
57     LIST_ENTRY RequestListEntry;
58     PDEVICE_OBJECT DeviceObject;
59     PKEVENT CompletionEvent;
60     NTSTATUS *CompletionStatus;
61     DEVICE_ACTION Action;
62 } DEVICE_ACTION_REQUEST, *PDEVICE_ACTION_REQUEST;
63 
64 typedef enum _ADD_DEV_DRIVER_TYPE
65 {
66     LowerFilter,
67     LowerClassFilter,
68     DeviceDriver,
69     UpperFilter,
70     UpperClassFilter
71 } ADD_DEV_DRIVER_TYPE;
72 
73 typedef struct _ADD_DEV_DRIVERS_LIST
74 {
75     LIST_ENTRY ListEntry;
76     PDRIVER_OBJECT DriverObject;
77     ADD_DEV_DRIVER_TYPE DriverType;
78 } ADD_DEV_DRIVERS_LIST, *PADD_DEV_DRIVERS_LIST;
79 
80 typedef struct _ATTACH_FILTER_DRIVERS_CONTEXT
81 {
82     ADD_DEV_DRIVER_TYPE DriverType;
83     PDEVICE_NODE DeviceNode;
84     PLIST_ENTRY DriversListHead;
85 } ATTACH_FILTER_DRIVERS_CONTEXT, *PATTACH_FILTER_DRIVERS_CONTEXT;
86 
87 /* FUNCTIONS *****************************************************************/
88 
89 PDEVICE_OBJECT
90 IopGetDeviceObjectFromDeviceInstance(PUNICODE_STRING DeviceInstance);
91 
92 NTSTATUS
93 IopGetParentIdPrefix(PDEVICE_NODE DeviceNode, PUNICODE_STRING ParentIdPrefix);
94 
95 USHORT
96 NTAPI
97 IopGetBusTypeGuidIndex(LPGUID BusTypeGuid);
98 
99 NTSTATUS
100 IopSetDeviceInstanceData(HANDLE InstanceKey, PDEVICE_NODE DeviceNode);
101 
102 VOID
103 NTAPI
104 IopInstallCriticalDevice(PDEVICE_NODE DeviceNode);
105 
106 static
107 VOID
108 IopCancelPrepareDeviceForRemoval(PDEVICE_OBJECT DeviceObject);
109 
110 static
111 NTSTATUS
112 IopPrepareDeviceForRemoval(PDEVICE_OBJECT DeviceObject, BOOLEAN Force);
113 
114 static
115 NTSTATUS
116 IopSetServiceEnumData(
117     _In_ PDEVICE_NODE DeviceNode,
118     _In_ HANDLE InstanceHandle);
119 
120 static
121 BOOLEAN
122 IopValidateID(
123     _In_ PWCHAR Id,
124     _In_ BUS_QUERY_ID_TYPE QueryType)
125 {
126     PWCHAR PtrChar;
127     PWCHAR StringEnd;
128     WCHAR Char;
129     ULONG SeparatorsCount = 0;
130     PWCHAR PtrPrevChar = NULL;
131     ULONG MaxSeparators;
132     BOOLEAN IsMultiSz;
133 
134     PAGED_CODE();
135 
136     switch (QueryType)
137     {
138         case BusQueryDeviceID:
139             MaxSeparators = MAX_SEPARATORS_DEVICEID;
140             IsMultiSz = FALSE;
141             break;
142         case BusQueryInstanceID:
143             MaxSeparators = MAX_SEPARATORS_INSTANCEID;
144             IsMultiSz = FALSE;
145             break;
146 
147         case BusQueryHardwareIDs:
148         case BusQueryCompatibleIDs:
149             MaxSeparators = MAX_SEPARATORS_DEVICEID;
150             IsMultiSz = TRUE;
151             break;
152 
153         default:
154             DPRINT1("IopValidateID: Not handled QueryType - %x\n", QueryType);
155             return FALSE;
156     }
157 
158     StringEnd = Id + MAX_DEVICE_ID_LEN;
159 
160     for (PtrChar = Id; PtrChar < StringEnd; PtrChar++)
161     {
162         Char = *PtrChar;
163 
164         if (Char == UNICODE_NULL)
165         {
166             if (!IsMultiSz || (PtrPrevChar && PtrChar == PtrPrevChar + 1))
167             {
168                 if (MaxSeparators == SeparatorsCount || IsMultiSz)
169                 {
170                     return TRUE;
171                 }
172 
173                 DPRINT1("IopValidateID: SeparatorsCount - %lu, MaxSeparators - %lu\n",
174                         SeparatorsCount, MaxSeparators);
175                 goto ErrorExit;
176             }
177 
178             StringEnd = PtrChar + MAX_DEVICE_ID_LEN + 1;
179             PtrPrevChar = PtrChar;
180             SeparatorsCount = 0;
181         }
182         else if (Char < ' ' || Char > 0x7F || Char == ',')
183         {
184             DPRINT1("IopValidateID: Invalid character - %04X\n", Char);
185             goto ErrorExit;
186         }
187         else if (Char == ' ')
188         {
189             *PtrChar = '_';
190         }
191         else if (Char == '\\')
192         {
193             SeparatorsCount++;
194 
195             if (SeparatorsCount > MaxSeparators)
196             {
197                 DPRINT1("IopValidateID: SeparatorsCount - %lu, MaxSeparators - %lu\n",
198                         SeparatorsCount, MaxSeparators);
199                 goto ErrorExit;
200             }
201         }
202     }
203 
204     DPRINT1("IopValidateID: Not terminated ID\n");
205 
206 ErrorExit:
207     // FIXME logging
208     return FALSE;
209 }
210 
211 static
212 NTSTATUS
213 IopCreateDeviceInstancePath(
214     _In_ PDEVICE_NODE DeviceNode,
215     _Out_ PUNICODE_STRING InstancePath)
216 {
217     IO_STATUS_BLOCK IoStatusBlock;
218     UNICODE_STRING DeviceId;
219     UNICODE_STRING InstanceId;
220     IO_STACK_LOCATION Stack;
221     NTSTATUS Status;
222     UNICODE_STRING ParentIdPrefix = { 0, 0, NULL };
223     DEVICE_CAPABILITIES DeviceCapabilities;
224     BOOLEAN IsValidID;
225 
226     DPRINT("Sending IRP_MN_QUERY_ID.BusQueryDeviceID to device stack\n");
227 
228     Stack.Parameters.QueryId.IdType = BusQueryDeviceID;
229     Status = IopInitiatePnpIrp(DeviceNode->PhysicalDeviceObject,
230                                &IoStatusBlock,
231                                IRP_MN_QUERY_ID,
232                                &Stack);
233     if (!NT_SUCCESS(Status))
234     {
235         DPRINT1("IopInitiatePnpIrp(BusQueryDeviceID) failed (Status %x)\n", Status);
236         return Status;
237     }
238 
239     IsValidID = IopValidateID((PWCHAR)IoStatusBlock.Information, BusQueryDeviceID);
240 
241     if (!IsValidID)
242     {
243         DPRINT1("Invalid DeviceID. DeviceNode - %p\n", DeviceNode);
244     }
245 
246     /* Save the device id string */
247     RtlInitUnicodeString(&DeviceId, (PWSTR)IoStatusBlock.Information);
248 
249     DPRINT("Sending IRP_MN_QUERY_CAPABILITIES to device stack (after enumeration)\n");
250 
251     Status = IopQueryDeviceCapabilities(DeviceNode, &DeviceCapabilities);
252     if (!NT_SUCCESS(Status))
253     {
254         if (Status != STATUS_NOT_SUPPORTED)
255         {
256             DPRINT1("IopQueryDeviceCapabilities() failed (Status 0x%08lx)\n", Status);
257         }
258         RtlFreeUnicodeString(&DeviceId);
259         return Status;
260     }
261 
262     /* This bit is only check after enumeration */
263     if (DeviceCapabilities.HardwareDisabled)
264     {
265         /* FIXME: Cleanup device */
266         RtlFreeUnicodeString(&DeviceId);
267         return STATUS_PLUGPLAY_NO_DEVICE;
268     }
269 
270     if (!DeviceCapabilities.UniqueID)
271     {
272         /* Device has not a unique ID. We need to prepend parent bus unique identifier */
273         DPRINT("Instance ID is not unique\n");
274         Status = IopGetParentIdPrefix(DeviceNode, &ParentIdPrefix);
275         if (!NT_SUCCESS(Status))
276         {
277             DPRINT1("IopGetParentIdPrefix() failed (Status 0x%08lx)\n", Status);
278             RtlFreeUnicodeString(&DeviceId);
279             return Status;
280         }
281     }
282 
283     DPRINT("Sending IRP_MN_QUERY_ID.BusQueryInstanceID to device stack\n");
284 
285     Stack.Parameters.QueryId.IdType = BusQueryInstanceID;
286     Status = IopInitiatePnpIrp(DeviceNode->PhysicalDeviceObject,
287                                &IoStatusBlock,
288                                IRP_MN_QUERY_ID,
289                                &Stack);
290     if (!NT_SUCCESS(Status))
291     {
292         DPRINT("IopInitiatePnpIrp(BusQueryInstanceID) failed (Status %lx)\n", Status);
293         ASSERT(IoStatusBlock.Information == 0);
294     }
295 
296     if (IoStatusBlock.Information)
297     {
298         IsValidID = IopValidateID((PWCHAR)IoStatusBlock.Information, BusQueryInstanceID);
299 
300         if (!IsValidID)
301         {
302             DPRINT1("Invalid InstanceID. DeviceNode - %p\n", DeviceNode);
303         }
304     }
305 
306     RtlInitUnicodeString(&InstanceId,
307                          (PWSTR)IoStatusBlock.Information);
308 
309     InstancePath->Length = 0;
310     InstancePath->MaximumLength = DeviceId.Length + sizeof(WCHAR) +
311                                   ParentIdPrefix.Length +
312                                   InstanceId.Length +
313                                   sizeof(UNICODE_NULL);
314     if (ParentIdPrefix.Length && InstanceId.Length)
315     {
316         InstancePath->MaximumLength += sizeof(WCHAR);
317     }
318 
319     InstancePath->Buffer = ExAllocatePoolWithTag(PagedPool,
320                                                  InstancePath->MaximumLength,
321                                                  TAG_IO);
322     if (!InstancePath->Buffer)
323     {
324         RtlFreeUnicodeString(&InstanceId);
325         RtlFreeUnicodeString(&ParentIdPrefix);
326         RtlFreeUnicodeString(&DeviceId);
327         return STATUS_INSUFFICIENT_RESOURCES;
328     }
329 
330     /* Start with the device id */
331     RtlCopyUnicodeString(InstancePath, &DeviceId);
332     RtlAppendUnicodeToString(InstancePath, L"\\");
333 
334     /* Add information from parent bus device to InstancePath */
335     RtlAppendUnicodeStringToString(InstancePath, &ParentIdPrefix);
336     if (ParentIdPrefix.Length && InstanceId.Length)
337     {
338         RtlAppendUnicodeToString(InstancePath, L"&");
339     }
340 
341     /* Finally, add the id returned by the driver stack */
342     RtlAppendUnicodeStringToString(InstancePath, &InstanceId);
343 
344     /*
345      * FIXME: Check for valid characters, if there is invalid characters
346      * then bugcheck
347      */
348 
349     RtlFreeUnicodeString(&InstanceId);
350     RtlFreeUnicodeString(&DeviceId);
351     RtlFreeUnicodeString(&ParentIdPrefix);
352 
353     return STATUS_SUCCESS;
354 }
355 
356 /**
357  * @brief      Loads and/or returns the driver associated with the registry entry if the driver
358  *             is enabled. In case of an error, sets up a corresponding Problem to the DeviceNode
359  */
360 static
361 NTSTATUS
362 NTAPI
363 PiAttachFilterDriversCallback(
364     PWSTR ValueName,
365     ULONG ValueType,
366     PVOID ValueData,
367     ULONG ValueLength,
368     PVOID Ctx,
369     PVOID EntryContext)
370 {
371     PATTACH_FILTER_DRIVERS_CONTEXT context = Ctx;
372     PDRIVER_OBJECT DriverObject;
373     NTSTATUS Status;
374     BOOLEAN loadDrivers = (BOOLEAN)(ULONG_PTR)EntryContext;
375 
376     PAGED_CODE();
377 
378     // No filter value present
379     if (ValueType != REG_SZ)
380         return STATUS_SUCCESS;
381 
382     if (ValueLength <= sizeof(WCHAR))
383         return STATUS_OBJECT_NAME_NOT_FOUND;
384 
385     // open the service registry key
386     UNICODE_STRING serviceName = { .Length = 0 }, servicesKeyName;
387     RtlInitUnicodeString(&serviceName, ValueData);
388     RtlInitUnicodeString(&servicesKeyName, ServicesKeyName);
389 
390     HANDLE ccsServicesHandle, serviceHandle = NULL;
391 
392     Status = IopOpenRegistryKeyEx(&ccsServicesHandle, NULL, &servicesKeyName, KEY_READ);
393     if (!NT_SUCCESS(Status))
394     {
395         DPRINT1("Failed to open a registry key for \"%wZ\" (status %x)\n", &serviceName, Status);
396         return Status;
397     }
398 
399     Status = IopOpenRegistryKeyEx(&serviceHandle, ccsServicesHandle, &serviceName, KEY_READ);
400     ZwClose(ccsServicesHandle);
401     if (!NT_SUCCESS(Status))
402     {
403         DPRINT1("Failed to open a registry key for \"%wZ\" (status %x)\n", &serviceName, Status);
404         return Status;
405     }
406 
407     PADD_DEV_DRIVERS_LIST driverEntry = ExAllocatePoolWithTag(PagedPool,
408                                                               sizeof(*driverEntry),
409                                                               TAG_PNP_DEVACTION);
410 
411     if (!driverEntry)
412     {
413         DPRINT1("Failed to allocate driverEntry for \"%wZ\"\n", &serviceName);
414         ZwClose(serviceHandle);
415         return STATUS_INSUFFICIENT_RESOURCES;
416     }
417 
418     // check if the driver is disabled
419     PKEY_VALUE_FULL_INFORMATION kvInfo;
420     SERVICE_LOAD_TYPE startType = DisableLoad;
421 
422     Status = IopGetRegistryValue(serviceHandle, L"Start", &kvInfo);
423     if (NT_SUCCESS(Status))
424     {
425         if (kvInfo->Type == REG_DWORD)
426         {
427             RtlMoveMemory(&startType,
428                           (PVOID)((ULONG_PTR)kvInfo + kvInfo->DataOffset),
429                           sizeof(startType));
430         }
431 
432         ExFreePool(kvInfo);
433     }
434 
435     // TODO: take into account other start types (like SERVICE_DEMAND_START)
436     if (startType >= DisableLoad)
437     {
438         if (!(context->DeviceNode->Flags & DNF_HAS_PROBLEM))
439         {
440             PiSetDevNodeProblem(context->DeviceNode, CM_PROB_DISABLED_SERVICE);
441         }
442 
443         DPRINT("Service \"%wZ\" is disabled (start type %u)\n", &serviceName, startType);
444         Status = STATUS_UNSUCCESSFUL;
445         goto Cleanup;
446     }
447 
448     // check if the driver is already loaded
449     UNICODE_STRING driverName;
450     Status = IopGetDriverNames(serviceHandle, &driverName, NULL);
451     if (!NT_SUCCESS(Status))
452     {
453         DPRINT1("Unable to obtain the driver name for \"%wZ\"\n", &serviceName);
454         goto Cleanup;
455     }
456 
457     // try to open it
458     Status = ObReferenceObjectByName(&driverName,
459                                      OBJ_OPENIF | OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
460                                      NULL, /* PassedAccessState */
461                                      0, /* DesiredAccess */
462                                      IoDriverObjectType,
463                                      KernelMode,
464                                      NULL, /* ParseContext */
465                                      (PVOID*)&DriverObject);
466     RtlFreeUnicodeString(&driverName);
467 
468     // the driver was not probably loaded, try to load
469     if (!NT_SUCCESS(Status))
470     {
471         if (loadDrivers)
472         {
473             Status = IopLoadDriver(serviceHandle, &DriverObject);
474         }
475         else
476         {
477             DPRINT("Service \"%wZ\" will not be loaded now\n", &serviceName);
478             // return failure, the driver will be loaded later (in a subsequent call)
479             Status = STATUS_UNSUCCESSFUL;
480             goto Cleanup;
481         }
482     }
483 
484     if (NT_SUCCESS(Status))
485     {
486         driverEntry->DriverObject = DriverObject;
487         driverEntry->DriverType = context->DriverType;
488         InsertTailList(context->DriversListHead, &driverEntry->ListEntry);
489         ZwClose(serviceHandle);
490         return STATUS_SUCCESS;
491     }
492     else
493     {
494         if (!(context->DeviceNode->Flags & DNF_HAS_PROBLEM))
495         {
496             switch (Status)
497             {
498                 case STATUS_INSUFFICIENT_RESOURCES:
499                     PiSetDevNodeProblem(context->DeviceNode, CM_PROB_OUT_OF_MEMORY);
500                     break;
501                 case STATUS_FAILED_DRIVER_ENTRY:
502                     PiSetDevNodeProblem(context->DeviceNode, CM_PROB_FAILED_DRIVER_ENTRY);
503                     break;
504                 case STATUS_ILL_FORMED_SERVICE_ENTRY:
505                     PiSetDevNodeProblem(context->DeviceNode, CM_PROB_DRIVER_SERVICE_KEY_INVALID);
506                     break;
507                 default:
508                     PiSetDevNodeProblem(context->DeviceNode, CM_PROB_DRIVER_FAILED_LOAD);
509                     break;
510             }
511         }
512 
513         DPRINT1("Failed to load driver \"%wZ\" for %wZ (status %x)\n",
514             &serviceName, &context->DeviceNode->InstancePath, Status);
515     }
516 
517 Cleanup:
518     ExFreePoolWithTag(driverEntry, TAG_PNP_DEVACTION);
519     if (serviceHandle)
520     {
521         ZwClose(serviceHandle);
522     }
523     return Status;
524 }
525 
526 
527 /**
528  * @brief      Calls PiAttachFilterDriversCallback for filter drivers (if any)
529  */
530 static
531 NTSTATUS
532 PiAttachFilterDrivers(
533     PLIST_ENTRY DriversListHead,
534     PDEVICE_NODE DeviceNode,
535     HANDLE EnumSubKey,
536     HANDLE ClassKey,
537     BOOLEAN Lower,
538     BOOLEAN LoadDrivers)
539 {
540     RTL_QUERY_REGISTRY_TABLE QueryTable[2] = { { NULL, 0, NULL, NULL, 0, NULL, 0 }, };
541     ATTACH_FILTER_DRIVERS_CONTEXT routineContext;
542     NTSTATUS Status;
543 
544     PAGED_CODE();
545 
546     routineContext.DriversListHead = DriversListHead;
547     routineContext.DeviceNode = DeviceNode;
548 
549     // First add device filters
550     routineContext.DriverType = Lower ? LowerFilter : UpperFilter;
551     QueryTable[0] = (RTL_QUERY_REGISTRY_TABLE){
552         .QueryRoutine = PiAttachFilterDriversCallback,
553         .Name = Lower ? L"LowerFilters" : L"UpperFilters",
554         .DefaultType = REG_NONE,
555         .EntryContext = (PVOID)(ULONG_PTR)LoadDrivers
556     };
557 
558     Status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
559                                     (PWSTR)EnumSubKey,
560                                     QueryTable,
561                                     &routineContext,
562                                     NULL);
563     if (ClassKey == NULL)
564     {
565         return Status;
566     }
567 
568     // Then add device class filters
569     routineContext.DriverType = Lower ? LowerClassFilter : UpperClassFilter;
570     QueryTable[0] = (RTL_QUERY_REGISTRY_TABLE){
571         .QueryRoutine = PiAttachFilterDriversCallback,
572         .Name = Lower ? L"LowerFilters" : L"UpperFilters",
573         .DefaultType = REG_NONE,
574         .EntryContext = (PVOID)(ULONG_PTR)LoadDrivers
575     };
576 
577     Status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
578                                     (PWSTR)ClassKey,
579                                     QueryTable,
580                                     &routineContext,
581                                     NULL);
582     return Status;
583 }
584 
585 /**
586  * @brief      Loads all drivers for a device node (actual service and filters)
587  *             and calls their AddDevice routine
588  *
589  * @param[in]  DeviceNode   The device node
590  * @param[in]  LoadDrivers  Whether to load drivers if they are not loaded yet
591  *                          (used when storage subsystem is not yet initialized)
592  */
593 static
594 NTSTATUS
595 PiCallDriverAddDevice(
596     _In_ PDEVICE_NODE DeviceNode,
597     _In_ BOOLEAN LoadDrivers)
598 {
599     NTSTATUS Status;
600     HANDLE EnumRootKey, SubKey;
601     HANDLE ClassKey = NULL;
602     UNICODE_STRING EnumRoot = RTL_CONSTANT_STRING(ENUM_ROOT);
603     static UNICODE_STRING ccsControlClass =
604     RTL_CONSTANT_STRING(L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Class");
605     PKEY_VALUE_FULL_INFORMATION kvInfo = NULL;
606 
607     PAGED_CODE();
608 
609     // open the enumeration root key
610     Status = IopOpenRegistryKeyEx(&EnumRootKey, NULL, &EnumRoot, KEY_READ);
611     if (!NT_SUCCESS(Status))
612     {
613         DPRINT1("IopOpenRegistryKeyEx() failed for \"%wZ\" (status %x)\n", &EnumRoot, Status);
614         return Status;
615     }
616 
617     // open an instance subkey
618     Status = IopOpenRegistryKeyEx(&SubKey, EnumRootKey, &DeviceNode->InstancePath, KEY_READ);
619     ZwClose(EnumRootKey);
620     if (!NT_SUCCESS(Status))
621     {
622         DPRINT1("Failed to open a devnode instance key for \"%wZ\" (status %x)\n",
623                 &DeviceNode->InstancePath, Status);
624         return Status;
625     }
626 
627     // try to get the class GUID of an instance and its registry key
628     Status = IopGetRegistryValue(SubKey, REGSTR_VAL_CLASSGUID, &kvInfo);
629     if (NT_SUCCESS(Status))
630     {
631         if (kvInfo->Type == REG_SZ && kvInfo->DataLength > sizeof(WCHAR))
632         {
633             UNICODE_STRING classGUID = {
634                 .MaximumLength = kvInfo->DataLength,
635                 .Length = kvInfo->DataLength - sizeof(UNICODE_NULL),
636                 .Buffer = (PVOID)((ULONG_PTR)kvInfo + kvInfo->DataOffset)
637             };
638             HANDLE ccsControlHandle;
639 
640             Status = IopOpenRegistryKeyEx(&ccsControlHandle, NULL, &ccsControlClass, KEY_READ);
641             if (!NT_SUCCESS(Status))
642             {
643                 DPRINT1("IopOpenRegistryKeyEx() failed for \"%wZ\" (status %x)\n",
644                         &ccsControlClass, Status);
645             }
646             else
647             {
648                 // open the CCS\Control\Class\<ClassGUID> key
649                 Status = IopOpenRegistryKeyEx(&ClassKey, ccsControlHandle, &classGUID, KEY_READ);
650                 ZwClose(ccsControlHandle);
651                 if (!NT_SUCCESS(Status))
652                 {
653                     DPRINT1("Failed to open class key \"%wZ\" (status %x)\n", &classGUID, Status);
654                 }
655             }
656 
657             if (ClassKey)
658             {
659                 // Check the Properties key of a class too
660                 // Windows fills some device properties from this key (which is protected)
661                 // TODO: add the device properties from this key
662 
663                 UNICODE_STRING properties = RTL_CONSTANT_STRING(REGSTR_KEY_DEVICE_PROPERTIES);
664                 HANDLE propertiesHandle;
665 
666                 Status = IopOpenRegistryKeyEx(&propertiesHandle, ClassKey, &properties, KEY_READ);
667                 if (!NT_SUCCESS(Status))
668                 {
669                     DPRINT("Properties key failed to open for \"%wZ\" (status %x)\n",
670                            &classGUID, Status);
671                 }
672                 else
673                 {
674                     ZwClose(propertiesHandle);
675                 }
676             }
677         }
678 
679         ExFreePool(kvInfo);
680     }
681 
682     // the driver loading order:
683     // 1. LowerFilters
684     // 2. LowerClassFilters
685     // 3. Device driver (only one service!)
686     // 4. UpperFilters
687     // 5. UpperClassFilters
688 
689     LIST_ENTRY drvListHead;
690     InitializeListHead(&drvListHead);
691 
692     // lower (class) filters
693     Status = PiAttachFilterDrivers(&drvListHead, DeviceNode, SubKey, ClassKey, TRUE, LoadDrivers);
694     if (!NT_SUCCESS(Status))
695     {
696         goto Cleanup;
697     }
698 
699     ATTACH_FILTER_DRIVERS_CONTEXT routineContext = {
700         .DriversListHead = &drvListHead,
701         .DriverType = DeviceDriver,
702         .DeviceNode = DeviceNode
703     };
704 
705     RTL_QUERY_REGISTRY_TABLE queryTable[2] = {{
706         .QueryRoutine = PiAttachFilterDriversCallback,
707         .Name = L"Service",
708         .Flags = RTL_QUERY_REGISTRY_REQUIRED,
709         .DefaultType = REG_SZ, // REG_MULTI_SZ is not allowed here
710         .DefaultData = L"",
711         .EntryContext = (PVOID)(ULONG_PTR)LoadDrivers
712     },};
713 
714     // device driver
715     Status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
716                                     (PWSTR)SubKey,
717                                     queryTable,
718                                     &routineContext,
719                                     NULL);
720     if (NT_SUCCESS(Status))
721     {
722         // do nothing
723     }
724     // if a driver is not found, but a device allows raw access -> proceed
725     else if (Status == STATUS_OBJECT_NAME_NOT_FOUND &&
726              (DeviceNode->CapabilityFlags & 0x00000040)) // CM_DEVCAP_RAWDEVICEOK
727     {
728         // add a dummy entry to the drivers list (need for later processing)
729         PADD_DEV_DRIVERS_LIST driverEntry = ExAllocatePoolZero(PagedPool,
730                                                                sizeof(*driverEntry),
731                                                                TAG_PNP_DEVACTION);
732         driverEntry->DriverType = DeviceDriver;
733         InsertTailList(&drvListHead, &driverEntry->ListEntry);
734         DPRINT("No service for \"%wZ\" (RawDeviceOK)\n", &DeviceNode->InstancePath);
735     }
736     else
737     {
738         if (Status == STATUS_OBJECT_TYPE_MISMATCH && !(DeviceNode->Flags & DNF_HAS_PROBLEM))
739         {
740             PiSetDevNodeProblem(DeviceNode, CM_PROB_REGISTRY);
741         }
742         DPRINT("No service for \"%wZ\" (loadDrv: %u)\n", &DeviceNode->InstancePath, LoadDrivers);
743         goto Cleanup;
744     }
745 
746     // upper (class) filters
747     Status = PiAttachFilterDrivers(&drvListHead, DeviceNode, SubKey, ClassKey, FALSE, LoadDrivers);
748     if (!NT_SUCCESS(Status))
749     {
750         goto Cleanup;
751     }
752 
753     // finally loop through the stack and call AddDevice for every driver
754     for (PLIST_ENTRY listEntry = drvListHead.Flink;
755          listEntry != &drvListHead;
756          listEntry = listEntry->Flink)
757     {
758         PADD_DEV_DRIVERS_LIST driverEntry;
759         driverEntry = CONTAINING_RECORD(listEntry, ADD_DEV_DRIVERS_LIST, ListEntry);
760         PDRIVER_OBJECT driverObject = driverEntry->DriverObject;
761 
762         // FIXME: ReactOS is not quite ready for this assert
763         // (legacy drivers should not have AddDevice routine)
764         // ASSERT(!(DriverObject->Flags & DRVO_LEGACY_DRIVER));
765 
766         if (driverObject && driverObject->DriverExtension->AddDevice)
767         {
768             Status = driverObject->DriverExtension->AddDevice(driverEntry->DriverObject,
769                                                               DeviceNode->PhysicalDeviceObject);
770         }
771         else if (driverObject == NULL)
772         {
773             // valid only for DeviceDriver
774             ASSERT(driverEntry->DriverType == DeviceDriver);
775             ASSERT(DeviceNode->CapabilityFlags & 0x00000040); // CM_DEVCAP_RAWDEVICEOK
776             Status = STATUS_SUCCESS;
777         }
778         else
779         {
780             // HACK: the driver doesn't have a AddDevice routine. We shouldn't be here,
781             // but ReactOS' PnP stack is not that correct yet
782             DeviceNode->Flags |= DNF_LEGACY_DRIVER;
783             Status = STATUS_UNSUCCESSFUL;
784         }
785 
786         // for filter drivers we don't care about the AddDevice result
787         if (driverEntry->DriverType == DeviceDriver)
788         {
789             if (NT_SUCCESS(Status))
790             {
791                 PDEVICE_OBJECT fdo = IoGetAttachedDeviceReference(DeviceNode->PhysicalDeviceObject);
792 
793                 // HACK: Check if we have a ACPI device (needed for power management)
794                 if (fdo->DeviceType == FILE_DEVICE_ACPI)
795                 {
796                     static BOOLEAN SystemPowerDeviceNodeCreated = FALSE;
797 
798                     // There can be only one system power device
799                     if (!SystemPowerDeviceNodeCreated)
800                     {
801                         PopSystemPowerDeviceNode = DeviceNode;
802                         ObReferenceObject(PopSystemPowerDeviceNode->PhysicalDeviceObject);
803                         SystemPowerDeviceNodeCreated = TRUE;
804                     }
805                 }
806 
807                 ObDereferenceObject(fdo);
808                 PiSetDevNodeState(DeviceNode, DeviceNodeDriversAdded);
809             }
810             else
811             {
812                 // lower filters (if already started) will be removed upon this request
813                 PiSetDevNodeProblem(DeviceNode, CM_PROB_FAILED_ADD);
814                 PiSetDevNodeState(DeviceNode, DeviceNodeAwaitingQueuedRemoval);
815                 break;
816             }
817         }
818 
819 #if DBG
820         PDEVICE_OBJECT attachedDO = IoGetAttachedDevice(DeviceNode->PhysicalDeviceObject);
821         if (attachedDO->Flags & DO_DEVICE_INITIALIZING)
822         {
823             DPRINT1("DO_DEVICE_INITIALIZING is not cleared on a device 0x%p!\n", attachedDO);
824         }
825 #endif
826     }
827 
828 Cleanup:
829     while (!IsListEmpty(&drvListHead))
830     {
831         PLIST_ENTRY listEntry = RemoveHeadList(&drvListHead);
832         PADD_DEV_DRIVERS_LIST driverEntry;
833         driverEntry = CONTAINING_RECORD(listEntry, ADD_DEV_DRIVERS_LIST, ListEntry);
834 
835         // drivers which don't have any devices (in case of failure) will be cleaned up
836         if (driverEntry->DriverObject)
837         {
838             ObDereferenceObject(driverEntry->DriverObject);
839         }
840         ExFreePoolWithTag(driverEntry, TAG_PNP_DEVACTION);
841     }
842 
843     ZwClose(SubKey);
844     if (ClassKey != NULL)
845     {
846         ZwClose(ClassKey);
847     }
848 
849     return Status;
850 }
851 
852 NTSTATUS
853 NTAPI
854 IopQueryDeviceCapabilities(PDEVICE_NODE DeviceNode,
855                            PDEVICE_CAPABILITIES DeviceCaps)
856 {
857     IO_STATUS_BLOCK StatusBlock;
858     IO_STACK_LOCATION Stack;
859     NTSTATUS Status;
860     HANDLE InstanceKey;
861     UNICODE_STRING ValueName;
862 
863     /* Set up the Header */
864     RtlZeroMemory(DeviceCaps, sizeof(DEVICE_CAPABILITIES));
865     DeviceCaps->Size = sizeof(DEVICE_CAPABILITIES);
866     DeviceCaps->Version = 1;
867     DeviceCaps->Address = -1;
868     DeviceCaps->UINumber = -1;
869 
870     /* Set up the Stack */
871     RtlZeroMemory(&Stack, sizeof(IO_STACK_LOCATION));
872     Stack.Parameters.DeviceCapabilities.Capabilities = DeviceCaps;
873 
874     /* Send the IRP */
875     Status = IopInitiatePnpIrp(DeviceNode->PhysicalDeviceObject,
876                                &StatusBlock,
877                                IRP_MN_QUERY_CAPABILITIES,
878                                &Stack);
879     if (!NT_SUCCESS(Status))
880     {
881         if (Status != STATUS_NOT_SUPPORTED)
882         {
883             DPRINT1("IRP_MN_QUERY_CAPABILITIES failed with status 0x%lx\n", Status);
884         }
885         return Status;
886     }
887 
888     /* Map device capabilities to capability flags */
889     DeviceNode->CapabilityFlags = 0;
890     if (DeviceCaps->LockSupported)
891         DeviceNode->CapabilityFlags |= 0x00000001;    // CM_DEVCAP_LOCKSUPPORTED
892 
893     if (DeviceCaps->EjectSupported)
894         DeviceNode->CapabilityFlags |= 0x00000002;    // CM_DEVCAP_EJECTSUPPORTED
895 
896     if (DeviceCaps->Removable)
897         DeviceNode->CapabilityFlags |= 0x00000004;    // CM_DEVCAP_REMOVABLE
898 
899     if (DeviceCaps->DockDevice)
900         DeviceNode->CapabilityFlags |= 0x00000008;    // CM_DEVCAP_DOCKDEVICE
901 
902     if (DeviceCaps->UniqueID)
903         DeviceNode->CapabilityFlags |= 0x00000010;    // CM_DEVCAP_UNIQUEID
904 
905     if (DeviceCaps->SilentInstall)
906         DeviceNode->CapabilityFlags |= 0x00000020;    // CM_DEVCAP_SILENTINSTALL
907 
908     if (DeviceCaps->RawDeviceOK)
909         DeviceNode->CapabilityFlags |= 0x00000040;    // CM_DEVCAP_RAWDEVICEOK
910 
911     if (DeviceCaps->SurpriseRemovalOK)
912         DeviceNode->CapabilityFlags |= 0x00000080;    // CM_DEVCAP_SURPRISEREMOVALOK
913 
914     if (DeviceCaps->HardwareDisabled)
915         DeviceNode->CapabilityFlags |= 0x00000100;    // CM_DEVCAP_HARDWAREDISABLED
916 
917     if (DeviceCaps->NonDynamic)
918         DeviceNode->CapabilityFlags |= 0x00000200;    // CM_DEVCAP_NONDYNAMIC
919 
920     if (DeviceCaps->NoDisplayInUI)
921         DeviceNode->UserFlags |= DNUF_DONT_SHOW_IN_UI;
922     else
923         DeviceNode->UserFlags &= ~DNUF_DONT_SHOW_IN_UI;
924 
925     Status = IopCreateDeviceKeyPath(&DeviceNode->InstancePath, REG_OPTION_NON_VOLATILE, &InstanceKey);
926     if (NT_SUCCESS(Status))
927     {
928         /* Set 'Capabilities' value */
929         RtlInitUnicodeString(&ValueName, L"Capabilities");
930         Status = ZwSetValueKey(InstanceKey,
931                                &ValueName,
932                                0,
933                                REG_DWORD,
934                                &DeviceNode->CapabilityFlags,
935                                sizeof(ULONG));
936 
937         /* Set 'UINumber' value */
938         if (DeviceCaps->UINumber != MAXULONG)
939         {
940             RtlInitUnicodeString(&ValueName, L"UINumber");
941             Status = ZwSetValueKey(InstanceKey,
942                                    &ValueName,
943                                    0,
944                                    REG_DWORD,
945                                    &DeviceCaps->UINumber,
946                                    sizeof(ULONG));
947         }
948 
949         ZwClose(InstanceKey);
950     }
951 
952     return Status;
953 }
954 
955 static
956 NTSTATUS
957 IopQueryHardwareIds(PDEVICE_NODE DeviceNode,
958                     HANDLE InstanceKey)
959 {
960     IO_STACK_LOCATION Stack;
961     IO_STATUS_BLOCK IoStatusBlock;
962     PWSTR Ptr;
963     UNICODE_STRING ValueName;
964     NTSTATUS Status;
965     ULONG Length, TotalLength;
966     BOOLEAN IsValidID;
967 
968     DPRINT("Sending IRP_MN_QUERY_ID.BusQueryHardwareIDs to device stack\n");
969 
970     RtlZeroMemory(&Stack, sizeof(Stack));
971     Stack.Parameters.QueryId.IdType = BusQueryHardwareIDs;
972     Status = IopInitiatePnpIrp(DeviceNode->PhysicalDeviceObject,
973                                &IoStatusBlock,
974                                IRP_MN_QUERY_ID,
975                                &Stack);
976     if (NT_SUCCESS(Status))
977     {
978         IsValidID = IopValidateID((PWCHAR)IoStatusBlock.Information, BusQueryHardwareIDs);
979 
980         if (!IsValidID)
981         {
982             DPRINT1("Invalid HardwareIDs. DeviceNode - %p\n", DeviceNode);
983         }
984 
985         TotalLength = 0;
986 
987         Ptr = (PWSTR)IoStatusBlock.Information;
988         DPRINT("Hardware IDs:\n");
989         while (*Ptr)
990         {
991             DPRINT("  %S\n", Ptr);
992             Length = (ULONG)wcslen(Ptr) + 1;
993 
994             Ptr += Length;
995             TotalLength += Length;
996         }
997         DPRINT("TotalLength: %hu\n", TotalLength);
998         DPRINT("\n");
999 
1000         RtlInitUnicodeString(&ValueName, L"HardwareID");
1001         Status = ZwSetValueKey(InstanceKey,
1002                                &ValueName,
1003                                0,
1004                                REG_MULTI_SZ,
1005                                (PVOID)IoStatusBlock.Information,
1006                                (TotalLength + 1) * sizeof(WCHAR));
1007         if (!NT_SUCCESS(Status))
1008         {
1009             DPRINT1("ZwSetValueKey() failed (Status %lx)\n", Status);
1010         }
1011     }
1012     else
1013     {
1014         DPRINT("IopInitiatePnpIrp() failed (Status %x)\n", Status);
1015     }
1016 
1017     return Status;
1018 }
1019 
1020 static
1021 NTSTATUS
1022 IopQueryCompatibleIds(PDEVICE_NODE DeviceNode,
1023                       HANDLE InstanceKey)
1024 {
1025     IO_STACK_LOCATION Stack;
1026     IO_STATUS_BLOCK IoStatusBlock;
1027     PWSTR Ptr;
1028     UNICODE_STRING ValueName;
1029     NTSTATUS Status;
1030     ULONG Length, TotalLength;
1031     BOOLEAN IsValidID;
1032 
1033     DPRINT("Sending IRP_MN_QUERY_ID.BusQueryCompatibleIDs to device stack\n");
1034 
1035     RtlZeroMemory(&Stack, sizeof(Stack));
1036     Stack.Parameters.QueryId.IdType = BusQueryCompatibleIDs;
1037     Status = IopInitiatePnpIrp(DeviceNode->PhysicalDeviceObject,
1038                                &IoStatusBlock,
1039                                IRP_MN_QUERY_ID,
1040                                &Stack);
1041     if (NT_SUCCESS(Status) && IoStatusBlock.Information)
1042     {
1043         IsValidID = IopValidateID((PWCHAR)IoStatusBlock.Information, BusQueryCompatibleIDs);
1044 
1045         if (!IsValidID)
1046         {
1047             DPRINT1("Invalid CompatibleIDs. DeviceNode - %p\n", DeviceNode);
1048         }
1049 
1050         TotalLength = 0;
1051 
1052         Ptr = (PWSTR)IoStatusBlock.Information;
1053         DPRINT("Compatible IDs:\n");
1054         while (*Ptr)
1055         {
1056             DPRINT("  %S\n", Ptr);
1057             Length = (ULONG)wcslen(Ptr) + 1;
1058 
1059             Ptr += Length;
1060             TotalLength += Length;
1061         }
1062         DPRINT("TotalLength: %hu\n", TotalLength);
1063         DPRINT("\n");
1064 
1065         RtlInitUnicodeString(&ValueName, L"CompatibleIDs");
1066         Status = ZwSetValueKey(InstanceKey,
1067                                &ValueName,
1068                                0,
1069                                REG_MULTI_SZ,
1070                                (PVOID)IoStatusBlock.Information,
1071                                (TotalLength + 1) * sizeof(WCHAR));
1072         if (!NT_SUCCESS(Status))
1073         {
1074             DPRINT1("ZwSetValueKey() failed (Status %lx) or no Compatible ID returned\n", Status);
1075         }
1076     }
1077     else
1078     {
1079         DPRINT("IopInitiatePnpIrp() failed (Status %x)\n", Status);
1080     }
1081 
1082     return Status;
1083 }
1084 
1085 /**
1086  * @brief      Sets the DeviceNode's DeviceDesc and LocationInformation registry values
1087  */
1088 VOID
1089 PiSetDevNodeText(
1090     _In_ PDEVICE_NODE DeviceNode,
1091     _In_ HANDLE InstanceKey)
1092 {
1093     PAGED_CODE();
1094 
1095     LCID localeId;
1096 
1097     // Get the Locale ID
1098     NTSTATUS status = ZwQueryDefaultLocale(FALSE, &localeId);
1099     if (!NT_SUCCESS(status))
1100     {
1101         DPRINT1("ZwQueryDefaultLocale() failed with status %x\n", status);
1102         return;
1103     }
1104 
1105     // Step 1: Write the DeviceDesc value if does not exist
1106 
1107     UNICODE_STRING valDeviceDesc = RTL_CONSTANT_STRING(L"DeviceDesc");
1108     ULONG len;
1109 
1110     status = ZwQueryValueKey(InstanceKey, &valDeviceDesc, KeyValueBasicInformation, NULL, 0, &len);
1111     if (status == STATUS_OBJECT_NAME_NOT_FOUND)
1112     {
1113         PWSTR deviceDesc = NULL;
1114         status = PiIrpQueryDeviceText(DeviceNode, localeId, DeviceTextDescription, &deviceDesc);
1115 
1116         if (deviceDesc && deviceDesc[0] != UNICODE_NULL)
1117         {
1118             status = ZwSetValueKey(InstanceKey,
1119                                    &valDeviceDesc,
1120                                    0,
1121                                    REG_SZ,
1122                                    deviceDesc,
1123                                    ((ULONG)wcslen(deviceDesc) + 1) * sizeof(WCHAR));
1124 
1125             if (!NT_SUCCESS(status))
1126             {
1127                 DPRINT1("ZwSetValueKey() failed (Status %x)\n", status);
1128             }
1129         }
1130         else
1131         {
1132             // This key is mandatory, so even if the Irp fails, we still write it
1133             UNICODE_STRING unknownDeviceDesc = RTL_CONSTANT_STRING(L"Unknown device");
1134             DPRINT("Driver didn't return DeviceDesc (status %x)\n", status);
1135 
1136             status = ZwSetValueKey(InstanceKey,
1137                                    &valDeviceDesc,
1138                                    0,
1139                                    REG_SZ,
1140                                    unknownDeviceDesc.Buffer,
1141                                    unknownDeviceDesc.MaximumLength);
1142             if (!NT_SUCCESS(status))
1143             {
1144                 DPRINT1("ZwSetValueKey() failed (Status %x)\n", status);
1145             }
1146         }
1147 
1148         if (deviceDesc)
1149         {
1150             ExFreePoolWithTag(deviceDesc, 0);
1151         }
1152     }
1153 
1154     // Step 2: LocaltionInformation is overwritten unconditionally
1155 
1156     PWSTR deviceLocationInfo = NULL;
1157     status = PiIrpQueryDeviceText(DeviceNode,
1158                                   localeId,
1159                                   DeviceTextLocationInformation,
1160                                   &deviceLocationInfo);
1161 
1162     if (deviceLocationInfo && deviceLocationInfo[0] != UNICODE_NULL)
1163     {
1164         UNICODE_STRING valLocationInfo = RTL_CONSTANT_STRING(L"LocationInformation");
1165 
1166         status = ZwSetValueKey(InstanceKey,
1167                                &valLocationInfo,
1168                                0,
1169                                REG_SZ,
1170                                deviceLocationInfo,
1171                                ((ULONG)wcslen(deviceLocationInfo) + 1) * sizeof(WCHAR));
1172         if (!NT_SUCCESS(status))
1173         {
1174             DPRINT1("ZwSetValueKey() failed (Status %x)\n", status);
1175         }
1176     }
1177 
1178     if (deviceLocationInfo)
1179     {
1180         ExFreePoolWithTag(deviceLocationInfo, 0);
1181     }
1182     else
1183     {
1184         DPRINT("Driver didn't return LocationInformation (status %x)\n", status);
1185     }
1186 }
1187 
1188 static
1189 NTSTATUS
1190 PiInitializeDevNode(
1191     _In_ PDEVICE_NODE DeviceNode)
1192 {
1193     IO_STATUS_BLOCK IoStatusBlock;
1194     NTSTATUS Status;
1195     HANDLE InstanceKey = NULL;
1196     UNICODE_STRING InstancePathU;
1197     PDEVICE_OBJECT OldDeviceObject;
1198 
1199     DPRINT("PiProcessNewDevNode(%p)\n", DeviceNode);
1200     DPRINT("PDO 0x%p\n", DeviceNode->PhysicalDeviceObject);
1201 
1202     /*
1203      * FIXME: For critical errors, cleanup and disable device, but always
1204      * return STATUS_SUCCESS.
1205      */
1206 
1207     Status = IopCreateDeviceInstancePath(DeviceNode, &InstancePathU);
1208     if (!NT_SUCCESS(Status))
1209     {
1210         if (Status != STATUS_PLUGPLAY_NO_DEVICE)
1211         {
1212             DPRINT1("IopCreateDeviceInstancePath() failed with status 0x%lx\n", Status);
1213         }
1214         return Status;
1215     }
1216 
1217     /* Verify that this is not a duplicate */
1218     OldDeviceObject = IopGetDeviceObjectFromDeviceInstance(&InstancePathU);
1219     if (OldDeviceObject != NULL)
1220     {
1221         PDEVICE_NODE OldDeviceNode = IopGetDeviceNode(OldDeviceObject);
1222 
1223         DPRINT1("Duplicate device instance '%wZ'\n", &InstancePathU);
1224         DPRINT1("Current instance parent: '%wZ'\n", &DeviceNode->Parent->InstancePath);
1225         DPRINT1("Old instance parent: '%wZ'\n", &OldDeviceNode->Parent->InstancePath);
1226 
1227         KeBugCheckEx(PNP_DETECTED_FATAL_ERROR,
1228                      0x01,
1229                      (ULONG_PTR)DeviceNode->PhysicalDeviceObject,
1230                      (ULONG_PTR)OldDeviceObject,
1231                      0);
1232     }
1233 
1234     DeviceNode->InstancePath = InstancePathU;
1235 
1236     DPRINT("InstancePath is %S\n", DeviceNode->InstancePath.Buffer);
1237 
1238     /*
1239      * Create registry key for the instance id, if it doesn't exist yet
1240      */
1241     Status = IopCreateDeviceKeyPath(&DeviceNode->InstancePath, REG_OPTION_NON_VOLATILE, &InstanceKey);
1242     if (!NT_SUCCESS(Status))
1243     {
1244         DPRINT1("Failed to create the instance key! (Status %lx)\n", Status);
1245 
1246         /* We have to return success otherwise we abort the traverse operation */
1247         return STATUS_SUCCESS;
1248     }
1249 
1250     IopQueryHardwareIds(DeviceNode, InstanceKey);
1251 
1252     IopQueryCompatibleIds(DeviceNode, InstanceKey);
1253 
1254     DeviceNode->Flags |= DNF_IDS_QUERIED;
1255 
1256     // Set the device's DeviceDesc and LocationInformation fields
1257     PiSetDevNodeText(DeviceNode, InstanceKey);
1258 
1259     DPRINT("Sending IRP_MN_QUERY_BUS_INFORMATION to device stack\n");
1260 
1261     Status = IopInitiatePnpIrp(DeviceNode->PhysicalDeviceObject,
1262                                &IoStatusBlock,
1263                                IRP_MN_QUERY_BUS_INFORMATION,
1264                                NULL);
1265     if (NT_SUCCESS(Status) && IoStatusBlock.Information)
1266     {
1267         PPNP_BUS_INFORMATION BusInformation = (PPNP_BUS_INFORMATION)IoStatusBlock.Information;
1268 
1269         DeviceNode->ChildBusNumber = BusInformation->BusNumber;
1270         DeviceNode->ChildInterfaceType = BusInformation->LegacyBusType;
1271         DeviceNode->ChildBusTypeIndex = IopGetBusTypeGuidIndex(&BusInformation->BusTypeGuid);
1272         ExFreePoolWithTag(BusInformation, 0);
1273     }
1274     else
1275     {
1276         DPRINT("IopInitiatePnpIrp() failed (Status %x) or IoStatusBlock.Information=NULL\n", Status);
1277 
1278         DeviceNode->ChildBusNumber = 0xFFFFFFF0;
1279         DeviceNode->ChildInterfaceType = InterfaceTypeUndefined;
1280         DeviceNode->ChildBusTypeIndex = -1;
1281     }
1282 
1283     DPRINT("Sending IRP_MN_QUERY_RESOURCES to device stack\n");
1284 
1285     Status = IopInitiatePnpIrp(DeviceNode->PhysicalDeviceObject,
1286                                &IoStatusBlock,
1287                                IRP_MN_QUERY_RESOURCES,
1288                                NULL);
1289     if (NT_SUCCESS(Status) && IoStatusBlock.Information)
1290     {
1291         DeviceNode->BootResources = (PCM_RESOURCE_LIST)IoStatusBlock.Information;
1292         IopDeviceNodeSetFlag(DeviceNode, DNF_HAS_BOOT_CONFIG);
1293     }
1294     else
1295     {
1296         DPRINT("IopInitiatePnpIrp() failed (Status %x) or IoStatusBlock.Information=NULL\n", Status);
1297         DeviceNode->BootResources = NULL;
1298     }
1299 
1300     DPRINT("Sending IRP_MN_QUERY_RESOURCE_REQUIREMENTS to device stack\n");
1301 
1302     Status = IopInitiatePnpIrp(DeviceNode->PhysicalDeviceObject,
1303                                &IoStatusBlock,
1304                                IRP_MN_QUERY_RESOURCE_REQUIREMENTS,
1305                                NULL);
1306     if (NT_SUCCESS(Status))
1307     {
1308         DeviceNode->ResourceRequirements = (PIO_RESOURCE_REQUIREMENTS_LIST)IoStatusBlock.Information;
1309     }
1310     else
1311     {
1312         DPRINT("IopInitiatePnpIrp() failed (Status %08lx)\n", Status);
1313         DeviceNode->ResourceRequirements = NULL;
1314     }
1315 
1316     if (InstanceKey != NULL)
1317     {
1318         IopSetDeviceInstanceData(InstanceKey, DeviceNode);
1319     }
1320 
1321     // Try installing a critical device, so its Service key is populated
1322     // then call IopSetServiceEnumData to populate service's Enum key.
1323     // That allows us to start devices during an early boot
1324     IopInstallCriticalDevice(DeviceNode);
1325     IopSetServiceEnumData(DeviceNode, InstanceKey);
1326 
1327     ZwClose(InstanceKey);
1328 
1329     PiSetDevNodeState(DeviceNode, DeviceNodeInitialized);
1330 
1331     if (!IopDeviceNodeHasFlag(DeviceNode, DNF_LEGACY_DRIVER))
1332     {
1333         /* Report the device to the user-mode pnp manager */
1334         IopQueueDeviceInstallEvent(&GUID_DEVICE_ENUMERATED,
1335                                    &DeviceNode->InstancePath);
1336     }
1337 
1338     return STATUS_SUCCESS;
1339 }
1340 
1341 static
1342 NTSTATUS
1343 IopSetServiceEnumData(
1344     _In_ PDEVICE_NODE DeviceNode,
1345     _In_ HANDLE InstanceHandle)
1346 {
1347     UNICODE_STRING ServicesKeyPath = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\");
1348     UNICODE_STRING ServiceKeyName;
1349     UNICODE_STRING EnumKeyName;
1350     UNICODE_STRING ValueName;
1351     UNICODE_STRING ServiceName;
1352     PKEY_VALUE_FULL_INFORMATION KeyValueInformation, kvInfo2;
1353     HANDLE ServiceKey = NULL, ServiceEnumKey = NULL;
1354     ULONG Disposition;
1355     ULONG Count = 0, NextInstance = 0;
1356     WCHAR ValueBuffer[6];
1357     NTSTATUS Status = STATUS_SUCCESS;
1358 
1359     // obtain the device node's ServiceName
1360     Status = IopGetRegistryValue(InstanceHandle, L"Service", &kvInfo2);
1361     if (!NT_SUCCESS(Status))
1362     {
1363         return Status;
1364     }
1365 
1366     if (kvInfo2->Type != REG_SZ || kvInfo2->DataLength <= sizeof(WCHAR))
1367     {
1368         ExFreePool(kvInfo2);
1369         return STATUS_UNSUCCESSFUL;
1370     }
1371 
1372     ServiceName.MaximumLength = kvInfo2->DataLength;
1373     ServiceName.Length = kvInfo2->DataLength - sizeof(UNICODE_NULL);
1374     ServiceName.Buffer = (PVOID)((ULONG_PTR)kvInfo2 + kvInfo2->DataOffset);
1375 
1376     DPRINT("IopSetServiceEnumData(%p)\n", DeviceNode);
1377     DPRINT("Instance: %wZ\n", &DeviceNode->InstancePath);
1378     DPRINT("Service: %wZ\n", &ServiceName);
1379 
1380     ServiceKeyName.MaximumLength = ServicesKeyPath.Length + ServiceName.Length + sizeof(UNICODE_NULL);
1381     ServiceKeyName.Length = 0;
1382     ServiceKeyName.Buffer = ExAllocatePool(PagedPool, ServiceKeyName.MaximumLength);
1383     if (ServiceKeyName.Buffer == NULL)
1384     {
1385         DPRINT1("No ServiceKeyName.Buffer!\n");
1386         return STATUS_INSUFFICIENT_RESOURCES;
1387     }
1388 
1389     RtlAppendUnicodeStringToString(&ServiceKeyName, &ServicesKeyPath);
1390     RtlAppendUnicodeStringToString(&ServiceKeyName, &ServiceName);
1391 
1392     DPRINT("ServiceKeyName: %wZ\n", &ServiceKeyName);
1393 
1394     Status = IopOpenRegistryKeyEx(&ServiceKey, NULL, &ServiceKeyName, KEY_CREATE_SUB_KEY);
1395     if (!NT_SUCCESS(Status))
1396     {
1397         goto done;
1398     }
1399 
1400     Status = RtlDuplicateUnicodeString(RTL_DUPLICATE_UNICODE_STRING_ALLOCATE_NULL_STRING,
1401                                        &ServiceName,
1402                                        &DeviceNode->ServiceName);
1403     if (!NT_SUCCESS(Status))
1404     {
1405         goto done;
1406     }
1407 
1408     RtlInitUnicodeString(&EnumKeyName, L"Enum");
1409     Status = IopCreateRegistryKeyEx(&ServiceEnumKey,
1410                                     ServiceKey,
1411                                     &EnumKeyName,
1412                                     KEY_SET_VALUE,
1413                                     REG_OPTION_VOLATILE,
1414                                     &Disposition);
1415     if (NT_SUCCESS(Status))
1416     {
1417         if (Disposition == REG_OPENED_EXISTING_KEY)
1418         {
1419             /* Read the NextInstance value */
1420             Status = IopGetRegistryValue(ServiceEnumKey,
1421                                          L"Count",
1422                                          &KeyValueInformation);
1423             if (!NT_SUCCESS(Status))
1424                 goto done;
1425 
1426             if ((KeyValueInformation->Type == REG_DWORD) &&
1427                 (KeyValueInformation->DataLength))
1428             {
1429                 /* Read it */
1430                 Count = *(PULONG)((ULONG_PTR)KeyValueInformation +
1431                                   KeyValueInformation->DataOffset);
1432             }
1433 
1434             ExFreePool(KeyValueInformation);
1435             KeyValueInformation = NULL;
1436 
1437             /* Read the NextInstance value */
1438             Status = IopGetRegistryValue(ServiceEnumKey,
1439                                          L"NextInstance",
1440                                          &KeyValueInformation);
1441             if (!NT_SUCCESS(Status))
1442                 goto done;
1443 
1444             if ((KeyValueInformation->Type == REG_DWORD) &&
1445                 (KeyValueInformation->DataLength))
1446             {
1447                 NextInstance = *(PULONG)((ULONG_PTR)KeyValueInformation +
1448                                          KeyValueInformation->DataOffset);
1449             }
1450 
1451             ExFreePool(KeyValueInformation);
1452             KeyValueInformation = NULL;
1453         }
1454 
1455         /* Set the instance path */
1456         swprintf(ValueBuffer, L"%lu", NextInstance);
1457         RtlInitUnicodeString(&ValueName, ValueBuffer);
1458         Status = ZwSetValueKey(ServiceEnumKey,
1459                                &ValueName,
1460                                0,
1461                                REG_SZ,
1462                                DeviceNode->InstancePath.Buffer,
1463                                DeviceNode->InstancePath.MaximumLength);
1464         if (!NT_SUCCESS(Status))
1465             goto done;
1466 
1467         /* Increment Count and NextInstance */
1468         Count++;
1469         NextInstance++;
1470 
1471         /* Set the new Count value */
1472         RtlInitUnicodeString(&ValueName, L"Count");
1473         Status = ZwSetValueKey(ServiceEnumKey,
1474                                &ValueName,
1475                                0,
1476                                REG_DWORD,
1477                                &Count,
1478                                sizeof(Count));
1479         if (!NT_SUCCESS(Status))
1480             goto done;
1481 
1482         /* Set the new NextInstance value */
1483         RtlInitUnicodeString(&ValueName, L"NextInstance");
1484         Status = ZwSetValueKey(ServiceEnumKey,
1485                                &ValueName,
1486                                0,
1487                                REG_DWORD,
1488                                &NextInstance,
1489                                sizeof(NextInstance));
1490     }
1491 
1492 done:
1493     if (ServiceEnumKey != NULL)
1494         ZwClose(ServiceEnumKey);
1495 
1496     if (ServiceKey != NULL)
1497         ZwClose(ServiceKey);
1498 
1499     ExFreePool(ServiceKeyName.Buffer);
1500     ExFreePool(kvInfo2);
1501 
1502     return Status;
1503 }
1504 
1505 /**
1506  * @brief      Processes the IoInvalidateDeviceState request
1507  *
1508  * Sends IRP_MN_QUERY_PNP_DEVICE_STATE request and sets device node's flags
1509  * according to the result.
1510  * Tree reenumeration should be started upon a successful return of the function.
1511  *
1512  * @todo       Do not return STATUS_SUCCESS if nothing is changed.
1513  */
1514 static
1515 NTSTATUS
1516 PiUpdateDeviceState(
1517     _In_ PDEVICE_NODE DeviceNode)
1518 {
1519     PNP_DEVICE_STATE PnPFlags;
1520     NTSTATUS Status;
1521 
1522     Status = PiIrpQueryPnPDeviceState(DeviceNode, &PnPFlags);
1523     if (!NT_SUCCESS(Status))
1524     {
1525         return Status;
1526     }
1527 
1528     if (PnPFlags & PNP_DEVICE_NOT_DISABLEABLE)
1529         DeviceNode->UserFlags |= DNUF_NOT_DISABLEABLE;
1530     else
1531         DeviceNode->UserFlags &= ~DNUF_NOT_DISABLEABLE;
1532 
1533     if (PnPFlags & PNP_DEVICE_DONT_DISPLAY_IN_UI)
1534         DeviceNode->UserFlags |= DNUF_DONT_SHOW_IN_UI;
1535     else
1536         DeviceNode->UserFlags &= ~DNUF_DONT_SHOW_IN_UI;
1537 
1538     if (PnPFlags & PNP_DEVICE_REMOVED || PnPFlags & PNP_DEVICE_DISABLED)
1539     {
1540         PiSetDevNodeProblem(DeviceNode,
1541                             PnPFlags & PNP_DEVICE_DISABLED
1542                             ? CM_PROB_HARDWARE_DISABLED
1543                             : CM_PROB_DEVICE_NOT_THERE);
1544 
1545         PiSetDevNodeState(DeviceNode, DeviceNodeAwaitingQueuedRemoval);
1546     }
1547     else if (PnPFlags & PNP_DEVICE_RESOURCE_REQUIREMENTS_CHANGED)
1548     {
1549         // Query resource rebalance
1550 
1551         if (PnPFlags & PNP_DEVICE_FAILED)
1552             DeviceNode->Flags &= DNF_NON_STOPPED_REBALANCE;
1553         else
1554             DeviceNode->Flags |= DNF_NON_STOPPED_REBALANCE;
1555 
1556         // Clear DNF_NO_RESOURCE_REQUIRED just in case (will be set back if needed)
1557         DeviceNode->Flags &= ~DNF_NO_RESOURCE_REQUIRED;
1558 
1559         // This will be caught up later by enumeration
1560         DeviceNode->Flags |= DNF_RESOURCE_REQUIREMENTS_CHANGED;
1561     }
1562     else if (PnPFlags & PNP_DEVICE_FAILED)
1563     {
1564         PiSetDevNodeProblem(DeviceNode, CM_PROB_FAILED_POST_START);
1565         PiSetDevNodeState(DeviceNode, DeviceNodeAwaitingQueuedRemoval);
1566     }
1567 
1568     return STATUS_SUCCESS;
1569 }
1570 
1571 static
1572 NTSTATUS
1573 PiStartDeviceFinal(
1574     _In_ PDEVICE_NODE DeviceNode)
1575 {
1576     DEVICE_CAPABILITIES DeviceCapabilities;
1577     NTSTATUS Status;
1578 
1579     if (!(DeviceNode->Flags & DNF_IDS_QUERIED))
1580     {
1581         // query ids (for reported devices)
1582         UNICODE_STRING enumRoot = RTL_CONSTANT_STRING(ENUM_ROOT);
1583         HANDLE enumRootHandle, instanceHandle;
1584 
1585         // open the enumeration root key
1586         Status = IopOpenRegistryKeyEx(&enumRootHandle, NULL, &enumRoot, KEY_READ);
1587         if (!NT_SUCCESS(Status))
1588         {
1589             DPRINT1("IopOpenRegistryKeyEx() failed for \"%wZ\" (status %x)\n", &enumRoot, Status);
1590             return Status;
1591         }
1592 
1593         // open an instance subkey
1594         Status = IopOpenRegistryKeyEx(&instanceHandle, enumRootHandle, &DeviceNode->InstancePath, KEY_READ);
1595         ZwClose(enumRootHandle);
1596         if (!NT_SUCCESS(Status))
1597         {
1598             DPRINT1("Failed to open a devnode instance key for \"%wZ\" (status %x)\n",
1599                     &DeviceNode->InstancePath, Status);
1600             return Status;
1601         }
1602 
1603         IopQueryHardwareIds(DeviceNode, instanceHandle);
1604         IopQueryCompatibleIds(DeviceNode, instanceHandle);
1605 
1606         DeviceNode->Flags |= DNF_IDS_QUERIED;
1607         ZwClose(instanceHandle);
1608     }
1609 
1610     // we're about to start - needs enumeration
1611     DeviceNode->Flags |= DNF_REENUMERATE;
1612 
1613     DPRINT("Sending IRP_MN_QUERY_CAPABILITIES to device stack (after start)\n");
1614 
1615     Status = IopQueryDeviceCapabilities(DeviceNode, &DeviceCapabilities);
1616     if (!NT_SUCCESS(Status))
1617     {
1618         DPRINT("IopInitiatePnpIrp() failed (Status 0x%08lx)\n", Status);
1619     }
1620 
1621     // Query the device state (IRP_MN_QUERY_PNP_DEVICE_STATE)
1622     PiUpdateDeviceState(DeviceNode);
1623 
1624     DPRINT("Sending GUID_DEVICE_ARRIVAL %wZ\n", &DeviceNode->InstancePath);
1625     IopQueueTargetDeviceEvent(&GUID_DEVICE_ARRIVAL, &DeviceNode->InstancePath);
1626 
1627     PiSetDevNodeState(DeviceNode, DeviceNodeStarted);
1628 
1629     return STATUS_SUCCESS;
1630 }
1631 
1632 /* PUBLIC FUNCTIONS **********************************************************/
1633 
1634 /**
1635  * @brief      Sends one of the remove IRPs to the device stack
1636  *
1637  * If there is a mounted VPB attached to a one of the stack devices, the IRP
1638  * should be send to a VPB's DeviceObject first (which belongs to a FS driver).
1639  * FS driver will then forward it down to the volume device.
1640  * While walking the device stack, the function sets (or unsets) VPB_REMOVE_PENDING flag
1641  * thus blocking all further mounts on a soon-to-be-removed devices
1642  */
1643 static
1644 NTSTATUS
1645 PiIrpSendRemoveCheckVpb(
1646     _In_ PDEVICE_OBJECT DeviceObject,
1647     _In_ UCHAR MinorFunction)
1648 {
1649     KIRQL oldIrql;
1650 
1651     ASSERT(MinorFunction == IRP_MN_QUERY_REMOVE_DEVICE ||
1652            MinorFunction == IRP_MN_CANCEL_REMOVE_DEVICE ||
1653            MinorFunction == IRP_MN_SURPRISE_REMOVAL ||
1654            MinorFunction == IRP_MN_REMOVE_DEVICE);
1655 
1656     PDEVICE_OBJECT vpbDevObj = DeviceObject, targetDevice = DeviceObject;
1657 
1658     // walk the device stack down, stop on a first mounted device
1659     do
1660     {
1661         if (vpbDevObj->Vpb)
1662         {
1663             // two locks are needed here
1664             KeWaitForSingleObject(&vpbDevObj->DeviceLock, Executive, KernelMode, FALSE, NULL);
1665             IoAcquireVpbSpinLock(&oldIrql);
1666 
1667             if (MinorFunction == IRP_MN_CANCEL_REMOVE_DEVICE)
1668             {
1669                 vpbDevObj->Vpb->Flags &= ~VPB_REMOVE_PENDING;
1670             }
1671             else
1672             {
1673                 vpbDevObj->Vpb->Flags |= VPB_REMOVE_PENDING;
1674             }
1675 
1676             BOOLEAN isMounted = (_Bool)(vpbDevObj->Vpb->Flags & VPB_MOUNTED);
1677 
1678             if (isMounted)
1679             {
1680                 targetDevice = vpbDevObj->Vpb->DeviceObject;
1681             }
1682 
1683             IoReleaseVpbSpinLock(oldIrql);
1684             KeSetEvent(&vpbDevObj->DeviceLock, IO_NO_INCREMENT, FALSE);
1685 
1686             if (isMounted)
1687             {
1688                 break;
1689             }
1690         }
1691 
1692         oldIrql = KeAcquireQueuedSpinLock(LockQueueIoDatabaseLock);
1693         vpbDevObj = vpbDevObj->AttachedDevice;
1694         KeReleaseQueuedSpinLock(LockQueueIoDatabaseLock, oldIrql);
1695     } while (vpbDevObj);
1696 
1697     ASSERT(targetDevice);
1698 
1699     PVOID info;
1700     IO_STACK_LOCATION stack = {.MajorFunction = IRP_MJ_PNP, .MinorFunction = MinorFunction};
1701 
1702     return IopSynchronousCall(targetDevice, &stack, &info);
1703 }
1704 
1705 NTSTATUS
1706 IopUpdateResourceMapForPnPDevice(
1707     IN PDEVICE_NODE DeviceNode);
1708 
1709 static
1710 VOID
1711 NTAPI
1712 IopSendRemoveDevice(IN PDEVICE_OBJECT DeviceObject)
1713 {
1714     PDEVICE_NODE DeviceNode = IopGetDeviceNode(DeviceObject);
1715 
1716     ASSERT(DeviceNode->State == DeviceNodeAwaitingQueuedRemoval);
1717 
1718     /* Drivers should never fail a IRP_MN_REMOVE_DEVICE request */
1719     PiIrpSendRemoveCheckVpb(DeviceObject, IRP_MN_REMOVE_DEVICE);
1720 
1721     /* Start of HACK: update resources stored in registry, so IopDetectResourceConflict works */
1722     if (DeviceNode->ResourceList)
1723     {
1724         ASSERT(DeviceNode->ResourceListTranslated);
1725         DeviceNode->ResourceList->Count = 0;
1726         DeviceNode->ResourceListTranslated->Count = 0;
1727         IopUpdateResourceMapForPnPDevice(DeviceNode);
1728     }
1729     /* End of HACK */
1730 
1731     PiSetDevNodeState(DeviceNode, DeviceNodeRemoved);
1732     PiNotifyTargetDeviceChange(&GUID_TARGET_DEVICE_REMOVE_COMPLETE, DeviceObject, NULL);
1733     LONG_PTR refCount = ObDereferenceObject(DeviceObject);
1734     if (refCount != 0)
1735     {
1736         DPRINT1("Leaking device %wZ, refCount = %d\n", &DeviceNode->InstancePath, (INT32)refCount);
1737     }
1738 }
1739 
1740 static
1741 VOID
1742 IopSendRemoveDeviceRelations(PDEVICE_RELATIONS DeviceRelations)
1743 {
1744     /* This function DOES dereference the device objects in all cases */
1745 
1746     ULONG i;
1747 
1748     for (i = 0; i < DeviceRelations->Count; i++)
1749     {
1750         IopSendRemoveDevice(DeviceRelations->Objects[i]);
1751         DeviceRelations->Objects[i] = NULL;
1752     }
1753 
1754     ExFreePool(DeviceRelations);
1755 }
1756 
1757 static
1758 VOID
1759 IopSendRemoveChildDevices(PDEVICE_NODE ParentDeviceNode)
1760 {
1761     PDEVICE_NODE ChildDeviceNode, NextDeviceNode;
1762     KIRQL OldIrql;
1763 
1764     KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql);
1765     ChildDeviceNode = ParentDeviceNode->Child;
1766     while (ChildDeviceNode != NULL)
1767     {
1768         NextDeviceNode = ChildDeviceNode->Sibling;
1769         KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql);
1770 
1771         IopSendRemoveDevice(ChildDeviceNode->PhysicalDeviceObject);
1772 
1773         ChildDeviceNode = NextDeviceNode;
1774 
1775         KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql);
1776     }
1777     KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql);
1778 }
1779 
1780 static
1781 VOID
1782 NTAPI
1783 IopSendSurpriseRemoval(IN PDEVICE_OBJECT DeviceObject)
1784 {
1785     ASSERT(IopGetDeviceNode(DeviceObject)->State == DeviceNodeAwaitingQueuedRemoval);
1786     /* Drivers should never fail a IRP_MN_SURPRISE_REMOVAL request */
1787     PiIrpSendRemoveCheckVpb(DeviceObject, IRP_MN_SURPRISE_REMOVAL);
1788 }
1789 
1790 static
1791 VOID
1792 NTAPI
1793 IopCancelRemoveDevice(IN PDEVICE_OBJECT DeviceObject)
1794 {
1795     /* Drivers should never fail a IRP_MN_CANCEL_REMOVE_DEVICE request */
1796     PiIrpSendRemoveCheckVpb(DeviceObject, IRP_MN_CANCEL_REMOVE_DEVICE);
1797 
1798     PiNotifyTargetDeviceChange(&GUID_TARGET_DEVICE_REMOVE_CANCELLED, DeviceObject, NULL);
1799 }
1800 
1801 static
1802 VOID
1803 IopCancelRemoveChildDevices(PDEVICE_NODE ParentDeviceNode)
1804 {
1805     PDEVICE_NODE ChildDeviceNode, NextDeviceNode;
1806     KIRQL OldIrql;
1807 
1808     KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql);
1809     ChildDeviceNode = ParentDeviceNode->Child;
1810     while (ChildDeviceNode != NULL)
1811     {
1812         NextDeviceNode = ChildDeviceNode->Sibling;
1813         KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql);
1814 
1815         IopCancelPrepareDeviceForRemoval(ChildDeviceNode->PhysicalDeviceObject);
1816 
1817         ChildDeviceNode = NextDeviceNode;
1818 
1819         KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql);
1820     }
1821     KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql);
1822 }
1823 
1824 static
1825 VOID
1826 IopCancelRemoveDeviceRelations(PDEVICE_RELATIONS DeviceRelations)
1827 {
1828     /* This function DOES dereference the device objects in all cases */
1829 
1830     ULONG i;
1831 
1832     for (i = 0; i < DeviceRelations->Count; i++)
1833     {
1834         IopCancelPrepareDeviceForRemoval(DeviceRelations->Objects[i]);
1835         ObDereferenceObject(DeviceRelations->Objects[i]);
1836         DeviceRelations->Objects[i] = NULL;
1837     }
1838 
1839     ExFreePool(DeviceRelations);
1840 }
1841 
1842 static
1843 VOID
1844 IopCancelPrepareDeviceForRemoval(PDEVICE_OBJECT DeviceObject)
1845 {
1846     IO_STACK_LOCATION Stack;
1847     IO_STATUS_BLOCK IoStatusBlock;
1848     PDEVICE_RELATIONS DeviceRelations;
1849     NTSTATUS Status;
1850 
1851     IopCancelRemoveDevice(DeviceObject);
1852 
1853     Stack.Parameters.QueryDeviceRelations.Type = RemovalRelations;
1854 
1855     Status = IopInitiatePnpIrp(DeviceObject,
1856                                &IoStatusBlock,
1857                                IRP_MN_QUERY_DEVICE_RELATIONS,
1858                                &Stack);
1859     if (!NT_SUCCESS(Status))
1860     {
1861         DPRINT("IopInitiatePnpIrp() failed with status 0x%08lx\n", Status);
1862         DeviceRelations = NULL;
1863     }
1864     else
1865     {
1866         DeviceRelations = (PDEVICE_RELATIONS)IoStatusBlock.Information;
1867     }
1868 
1869     if (DeviceRelations)
1870         IopCancelRemoveDeviceRelations(DeviceRelations);
1871 }
1872 
1873 static
1874 NTSTATUS
1875 NTAPI
1876 IopQueryRemoveDevice(IN PDEVICE_OBJECT DeviceObject)
1877 {
1878     PDEVICE_NODE DeviceNode = IopGetDeviceNode(DeviceObject);
1879     NTSTATUS Status;
1880 
1881     ASSERT(DeviceNode);
1882 
1883     IopQueueTargetDeviceEvent(&GUID_DEVICE_REMOVE_PENDING,
1884                               &DeviceNode->InstancePath);
1885 
1886     Status = PiIrpSendRemoveCheckVpb(DeviceObject, IRP_MN_QUERY_REMOVE_DEVICE);
1887 
1888     PiNotifyTargetDeviceChange(&GUID_TARGET_DEVICE_QUERY_REMOVE, DeviceObject, NULL);
1889 
1890     if (!NT_SUCCESS(Status))
1891     {
1892         DPRINT1("Removal vetoed by %wZ\n", &DeviceNode->InstancePath);
1893         IopQueueTargetDeviceEvent(&GUID_DEVICE_REMOVAL_VETOED,
1894                                   &DeviceNode->InstancePath);
1895     }
1896 
1897     return Status;
1898 }
1899 
1900 static
1901 NTSTATUS
1902 IopQueryRemoveChildDevices(PDEVICE_NODE ParentDeviceNode, BOOLEAN Force)
1903 {
1904     PDEVICE_NODE ChildDeviceNode, NextDeviceNode, FailedRemoveDevice;
1905     NTSTATUS Status;
1906     KIRQL OldIrql;
1907 
1908     KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql);
1909     ChildDeviceNode = ParentDeviceNode->Child;
1910     while (ChildDeviceNode != NULL)
1911     {
1912         NextDeviceNode = ChildDeviceNode->Sibling;
1913         KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql);
1914         PiSetDevNodeState(ChildDeviceNode, DeviceNodeAwaitingQueuedRemoval);
1915 
1916         Status = IopPrepareDeviceForRemoval(ChildDeviceNode->PhysicalDeviceObject, Force);
1917         if (!NT_SUCCESS(Status))
1918         {
1919             FailedRemoveDevice = ChildDeviceNode;
1920             goto cleanup;
1921         }
1922 
1923         KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql);
1924         ChildDeviceNode = NextDeviceNode;
1925     }
1926     KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql);
1927 
1928     return STATUS_SUCCESS;
1929 
1930 cleanup:
1931     KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql);
1932     ChildDeviceNode = ParentDeviceNode->Child;
1933     while (ChildDeviceNode != NULL)
1934     {
1935         NextDeviceNode = ChildDeviceNode->Sibling;
1936         KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql);
1937 
1938         IopCancelPrepareDeviceForRemoval(ChildDeviceNode->PhysicalDeviceObject);
1939 
1940         /* IRP_MN_CANCEL_REMOVE_DEVICE is also sent to the device
1941          * that failed the IRP_MN_QUERY_REMOVE_DEVICE request */
1942         if (ChildDeviceNode == FailedRemoveDevice)
1943             return Status;
1944 
1945         ChildDeviceNode = NextDeviceNode;
1946 
1947         KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql);
1948     }
1949     KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql);
1950 
1951     return Status;
1952 }
1953 
1954 static
1955 NTSTATUS
1956 IopQueryRemoveDeviceRelations(PDEVICE_RELATIONS DeviceRelations, BOOLEAN Force)
1957 {
1958     /* This function DOES NOT dereference the device objects on SUCCESS
1959      * but it DOES dereference device objects on FAILURE */
1960 
1961     ULONG i, j;
1962     NTSTATUS Status;
1963 
1964     for (i = 0; i < DeviceRelations->Count; i++)
1965     {
1966         Status = IopPrepareDeviceForRemoval(DeviceRelations->Objects[i], Force);
1967         if (!NT_SUCCESS(Status))
1968         {
1969             j = i;
1970             goto cleanup;
1971         }
1972     }
1973 
1974     return STATUS_SUCCESS;
1975 
1976 cleanup:
1977     /* IRP_MN_CANCEL_REMOVE_DEVICE is also sent to the device
1978      * that failed the IRP_MN_QUERY_REMOVE_DEVICE request */
1979     for (i = 0; i <= j; i++)
1980     {
1981         IopCancelPrepareDeviceForRemoval(DeviceRelations->Objects[i]);
1982         ObDereferenceObject(DeviceRelations->Objects[i]);
1983         DeviceRelations->Objects[i] = NULL;
1984     }
1985     for (; i < DeviceRelations->Count; i++)
1986     {
1987         ObDereferenceObject(DeviceRelations->Objects[i]);
1988         DeviceRelations->Objects[i] = NULL;
1989     }
1990     ExFreePool(DeviceRelations);
1991 
1992     return Status;
1993 }
1994 
1995 static
1996 NTSTATUS
1997 IopPrepareDeviceForRemoval(IN PDEVICE_OBJECT DeviceObject, BOOLEAN Force)
1998 {
1999     PDEVICE_NODE DeviceNode = IopGetDeviceNode(DeviceObject);
2000     IO_STACK_LOCATION Stack;
2001     IO_STATUS_BLOCK IoStatusBlock;
2002     PDEVICE_RELATIONS DeviceRelations;
2003     NTSTATUS Status;
2004 
2005     if ((DeviceNode->UserFlags & DNUF_NOT_DISABLEABLE) && !Force)
2006     {
2007         DPRINT1("Removal not allowed for %wZ\n", &DeviceNode->InstancePath);
2008         return STATUS_UNSUCCESSFUL;
2009     }
2010 
2011     if (!Force && IopQueryRemoveDevice(DeviceObject) != STATUS_SUCCESS)
2012     {
2013         DPRINT1("Removal vetoed by failing the query remove request\n");
2014 
2015         IopCancelRemoveDevice(DeviceObject);
2016 
2017         return STATUS_UNSUCCESSFUL;
2018     }
2019 
2020     Stack.Parameters.QueryDeviceRelations.Type = RemovalRelations;
2021 
2022     Status = IopInitiatePnpIrp(DeviceObject,
2023                                &IoStatusBlock,
2024                                IRP_MN_QUERY_DEVICE_RELATIONS,
2025                                &Stack);
2026     if (!NT_SUCCESS(Status))
2027     {
2028         DPRINT("IopInitiatePnpIrp() failed with status 0x%08lx\n", Status);
2029         DeviceRelations = NULL;
2030     }
2031     else
2032     {
2033         DeviceRelations = (PDEVICE_RELATIONS)IoStatusBlock.Information;
2034     }
2035 
2036     if (DeviceRelations)
2037     {
2038         Status = IopQueryRemoveDeviceRelations(DeviceRelations, Force);
2039         if (!NT_SUCCESS(Status))
2040             return Status;
2041     }
2042 
2043     Status = IopQueryRemoveChildDevices(DeviceNode, Force);
2044     if (!NT_SUCCESS(Status))
2045     {
2046         if (DeviceRelations)
2047             IopCancelRemoveDeviceRelations(DeviceRelations);
2048         return Status;
2049     }
2050 
2051     if (DeviceRelations)
2052         IopSendRemoveDeviceRelations(DeviceRelations);
2053     IopSendRemoveChildDevices(DeviceNode);
2054 
2055     return STATUS_SUCCESS;
2056 }
2057 
2058 static
2059 NTSTATUS
2060 IopRemoveDevice(PDEVICE_NODE DeviceNode)
2061 {
2062     NTSTATUS Status;
2063 
2064     // This function removes the device subtree, with the root in DeviceNode
2065     // atm everyting is in fact done inside this function, which is completely wrong.
2066     // The right implementation should have a separate removal worker thread and
2067     // properly do device node state transitions
2068 
2069     DPRINT("Removing device: %wZ\n", &DeviceNode->InstancePath);
2070 
2071     BOOLEAN surpriseRemoval = (_Bool)(DeviceNode->Flags & DNF_DEVICE_GONE);
2072 
2073     Status = IopPrepareDeviceForRemoval(DeviceNode->PhysicalDeviceObject, surpriseRemoval);
2074 
2075     if (surpriseRemoval)
2076     {
2077         IopSendSurpriseRemoval(DeviceNode->PhysicalDeviceObject);
2078         IopQueueTargetDeviceEvent(&GUID_DEVICE_SURPRISE_REMOVAL, &DeviceNode->InstancePath);
2079     }
2080 
2081     if (NT_SUCCESS(Status))
2082     {
2083         IopSendRemoveDevice(DeviceNode->PhysicalDeviceObject);
2084         if (surpriseRemoval)
2085         {
2086             IopQueueTargetDeviceEvent(&GUID_DEVICE_SAFE_REMOVAL, &DeviceNode->InstancePath);
2087         }
2088         return STATUS_SUCCESS;
2089     }
2090 
2091     return Status;
2092 }
2093 
2094 static
2095 NTSTATUS
2096 PiEnumerateDevice(
2097     _In_ PDEVICE_NODE DeviceNode)
2098 {
2099     PDEVICE_OBJECT ChildDeviceObject;
2100     PDEVICE_NODE ChildDeviceNode;
2101     ULONG i;
2102 
2103     // bus relations are already obtained for this device node
2104 
2105     if (!NT_SUCCESS(DeviceNode->CompletionStatus))
2106     {
2107         DPRINT("QDR request failed for %wZ, status %x\n",
2108             &DeviceNode->InstancePath, DeviceNode->CompletionStatus);
2109         // treat as if there are no child objects
2110     }
2111 
2112     PDEVICE_RELATIONS DeviceRelations = DeviceNode->OverUsed1.PendingDeviceRelations;
2113     DeviceNode->OverUsed1.PendingDeviceRelations = NULL;
2114 
2115     // it's acceptable not to have PDOs
2116     if (!DeviceRelations)
2117     {
2118         PiSetDevNodeState(DeviceNode, DeviceNodeStarted);
2119         DPRINT("No PDOs\n");
2120         return STATUS_SUCCESS;
2121     }
2122 
2123     // mark children nodes as non-present (those not returned in DR request will be removed)
2124     for (PDEVICE_NODE child = DeviceNode->Child; child != NULL; child = child->Sibling)
2125     {
2126         child->Flags &= ~DNF_ENUMERATED;
2127     }
2128 
2129     DPRINT("PiEnumerateDevice: enumerating %u children\n", DeviceRelations->Count);
2130 
2131     // create device nodes for all new children and set DNF_ENUMERATED back for old ones
2132     for (i = 0; i < DeviceRelations->Count; i++)
2133     {
2134         ChildDeviceObject = DeviceRelations->Objects[i];
2135         ASSERT((ChildDeviceObject->Flags & DO_DEVICE_INITIALIZING) == 0);
2136 
2137         ChildDeviceNode = IopGetDeviceNode(ChildDeviceObject);
2138         if (!ChildDeviceNode)
2139         {
2140             /* One doesn't exist, create it */
2141             ChildDeviceNode = PipAllocateDeviceNode(ChildDeviceObject);
2142             if (ChildDeviceNode)
2143             {
2144                 PiInsertDevNode(ChildDeviceNode, DeviceNode);
2145 
2146                 /* Mark the node as enumerated */
2147                 ChildDeviceNode->Flags |= DNF_ENUMERATED;
2148 
2149                 /* Mark the DO as bus enumerated */
2150                 ChildDeviceObject->Flags |= DO_BUS_ENUMERATED_DEVICE;
2151             }
2152             else
2153             {
2154                 /* Ignore this DO */
2155                 DPRINT1("PipAllocateDeviceNode() failed. Skipping PDO %u\n", i);
2156                 ObDereferenceObject(ChildDeviceObject);
2157             }
2158         }
2159         else
2160         {
2161             /* Mark it as enumerated */
2162             ChildDeviceNode->Flags |= DNF_ENUMERATED;
2163             ObDereferenceObject(ChildDeviceObject);
2164         }
2165     }
2166     ExFreePool(DeviceRelations);
2167 
2168     // time to remove non-reported devices
2169     for (PDEVICE_NODE child = DeviceNode->Child; child != NULL; child = child->Sibling)
2170     {
2171         if (!(child->Flags & (DNF_ENUMERATED|DNF_DEVICE_GONE)))
2172         {
2173             // this flag indicates that this is a surprise removal
2174             child->Flags |= DNF_DEVICE_GONE;
2175             PiSetDevNodeState(child, DeviceNodeAwaitingQueuedRemoval);
2176         }
2177     }
2178 
2179     PiSetDevNodeState(DeviceNode, DeviceNodeStarted);
2180     return STATUS_SUCCESS;
2181 }
2182 
2183 static
2184 NTSTATUS
2185 NTAPI
2186 IopSendEject(IN PDEVICE_OBJECT DeviceObject)
2187 {
2188     IO_STACK_LOCATION Stack;
2189     PVOID Dummy;
2190 
2191     RtlZeroMemory(&Stack, sizeof(IO_STACK_LOCATION));
2192     Stack.MajorFunction = IRP_MJ_PNP;
2193     Stack.MinorFunction = IRP_MN_EJECT;
2194 
2195     return IopSynchronousCall(DeviceObject, &Stack, &Dummy);
2196 }
2197 
2198 /*
2199  * @implemented
2200  */
2201 VOID
2202 NTAPI
2203 IoRequestDeviceEject(IN PDEVICE_OBJECT PhysicalDeviceObject)
2204 {
2205     PDEVICE_NODE DeviceNode = IopGetDeviceNode(PhysicalDeviceObject);
2206     PDEVICE_RELATIONS DeviceRelations;
2207     IO_STATUS_BLOCK IoStatusBlock;
2208     IO_STACK_LOCATION Stack;
2209     DEVICE_CAPABILITIES Capabilities;
2210     NTSTATUS Status;
2211 
2212     IopQueueTargetDeviceEvent(&GUID_DEVICE_KERNEL_INITIATED_EJECT,
2213                               &DeviceNode->InstancePath);
2214 
2215     if (IopQueryDeviceCapabilities(DeviceNode, &Capabilities) != STATUS_SUCCESS)
2216     {
2217         goto cleanup;
2218     }
2219 
2220     Stack.Parameters.QueryDeviceRelations.Type = EjectionRelations;
2221 
2222     Status = IopInitiatePnpIrp(PhysicalDeviceObject,
2223                                &IoStatusBlock,
2224                                IRP_MN_QUERY_DEVICE_RELATIONS,
2225                                &Stack);
2226     if (!NT_SUCCESS(Status))
2227     {
2228         DPRINT("IopInitiatePnpIrp() failed with status 0x%08lx\n", Status);
2229         DeviceRelations = NULL;
2230     }
2231     else
2232     {
2233         DeviceRelations = (PDEVICE_RELATIONS)IoStatusBlock.Information;
2234     }
2235 
2236     if (DeviceRelations)
2237     {
2238         Status = IopQueryRemoveDeviceRelations(DeviceRelations, FALSE);
2239         if (!NT_SUCCESS(Status))
2240             goto cleanup;
2241     }
2242 
2243     Status = IopQueryRemoveChildDevices(DeviceNode, FALSE);
2244     if (!NT_SUCCESS(Status))
2245     {
2246         if (DeviceRelations)
2247             IopCancelRemoveDeviceRelations(DeviceRelations);
2248         goto cleanup;
2249     }
2250 
2251     if (IopPrepareDeviceForRemoval(PhysicalDeviceObject, FALSE) != STATUS_SUCCESS)
2252     {
2253         if (DeviceRelations)
2254             IopCancelRemoveDeviceRelations(DeviceRelations);
2255         IopCancelRemoveChildDevices(DeviceNode);
2256         goto cleanup;
2257     }
2258 
2259     if (DeviceRelations)
2260         IopSendRemoveDeviceRelations(DeviceRelations);
2261     IopSendRemoveChildDevices(DeviceNode);
2262 
2263     DeviceNode->Problem = CM_PROB_HELD_FOR_EJECT;
2264     if (Capabilities.EjectSupported)
2265     {
2266         if (IopSendEject(PhysicalDeviceObject) != STATUS_SUCCESS)
2267         {
2268             goto cleanup;
2269         }
2270     }
2271     else
2272     {
2273         // DeviceNode->Flags |= DNF_DISABLED;
2274     }
2275 
2276     IopQueueTargetDeviceEvent(&GUID_DEVICE_EJECT,
2277                               &DeviceNode->InstancePath);
2278 
2279     return;
2280 
2281 cleanup:
2282     IopQueueTargetDeviceEvent(&GUID_DEVICE_EJECT_VETOED,
2283                               &DeviceNode->InstancePath);
2284 }
2285 
2286 static
2287 VOID
2288 PiFakeResourceRebalance(
2289     _In_ PDEVICE_NODE DeviceNode)
2290 {
2291     ASSERT(DeviceNode->Flags & DNF_RESOURCE_REQUIREMENTS_CHANGED);
2292 
2293     PCM_RESOURCE_LIST bootConfig = NULL;
2294     PIO_RESOURCE_REQUIREMENTS_LIST resourceRequirements = NULL;
2295 
2296     PiIrpQueryResources(DeviceNode, &bootConfig);
2297     PiIrpQueryResourceRequirements(DeviceNode, &resourceRequirements);
2298 
2299     DeviceNode->BootResources = bootConfig;
2300     DeviceNode->ResourceRequirements = resourceRequirements;
2301 
2302     if (bootConfig)
2303     {
2304         DeviceNode->Flags |= DNF_HAS_BOOT_CONFIG;
2305     }
2306 
2307     DeviceNode->Flags &= ~DNF_RESOURCE_REQUIREMENTS_CHANGED;
2308 }
2309 
2310 static
2311 VOID
2312 PiDevNodeStateMachine(
2313     _In_ PDEVICE_NODE RootNode)
2314 {
2315     NTSTATUS status;
2316     BOOLEAN doProcessAgain;
2317     PDEVICE_NODE currentNode = RootNode;
2318     PDEVICE_OBJECT referencedObject;
2319 
2320     do
2321     {
2322         doProcessAgain = FALSE;
2323 
2324         // The device can be removed during processing, but we still need its Parent and Sibling
2325         // links to continue the tree traversal. So keep the link till the and of a cycle
2326         referencedObject = currentNode->PhysicalDeviceObject;
2327         ObReferenceObject(referencedObject);
2328 
2329         // Devices with problems are skipped (unless they are not being removed)
2330         if (currentNode->Flags & DNF_HAS_PROBLEM &&
2331             currentNode->State != DeviceNodeAwaitingQueuedRemoval)
2332         {
2333             goto skipEnum;
2334         }
2335 
2336         switch (currentNode->State)
2337         {
2338             case DeviceNodeUnspecified: // this state is not used
2339                 break;
2340             case DeviceNodeUninitialized:
2341                 DPRINT("DeviceNodeUninitialized %wZ\n", &currentNode->InstancePath);
2342                 status = PiInitializeDevNode(currentNode);
2343                 doProcessAgain = NT_SUCCESS(status);
2344                 break;
2345             case DeviceNodeInitialized:
2346                 DPRINT("DeviceNodeInitialized %wZ\n", &currentNode->InstancePath);
2347                 status = PiCallDriverAddDevice(currentNode, PnPBootDriversInitialized);
2348                 doProcessAgain = NT_SUCCESS(status);
2349                 break;
2350             case DeviceNodeDriversAdded:
2351                 DPRINT("DeviceNodeDriversAdded %wZ\n", &currentNode->InstancePath);
2352                 status = IopAssignDeviceResources(currentNode);
2353                 doProcessAgain = NT_SUCCESS(status);
2354                 break;
2355             case DeviceNodeResourcesAssigned:
2356                 DPRINT("DeviceNodeResourcesAssigned %wZ\n", &currentNode->InstancePath);
2357                 // send IRP_MN_START_DEVICE
2358                 PiIrpStartDevice(currentNode);
2359 
2360                 // skip DeviceNodeStartPending, it is probably used for an async IRP_MN_START_DEVICE
2361                 PiSetDevNodeState(currentNode, DeviceNodeStartCompletion);
2362                 doProcessAgain = TRUE;
2363                 break;
2364             case DeviceNodeStartPending: // skipped on XP/2003
2365                 break;
2366             case DeviceNodeStartCompletion:
2367                 DPRINT("DeviceNodeStartCompletion %wZ\n", &currentNode->InstancePath);
2368                 status = currentNode->CompletionStatus;
2369                 doProcessAgain = TRUE;
2370                 if (!NT_SUCCESS(status))
2371                 {
2372                     UINT32 problem = (status == STATUS_PNP_REBOOT_REQUIRED)
2373                                      ? CM_PROB_NEED_RESTART
2374                                      : CM_PROB_FAILED_START;
2375 
2376                     PiSetDevNodeProblem(currentNode, problem);
2377                     PiSetDevNodeState(currentNode, DeviceNodeAwaitingQueuedRemoval);
2378                 }
2379                 else
2380                 {
2381                     // TODO: IopDoDeferredSetInterfaceState and IopAllocateLegacyBootResources
2382                     // are called here too
2383 
2384                     PiSetDevNodeState(currentNode, DeviceNodeStartPostWork);
2385                 }
2386                 break;
2387             case DeviceNodeStartPostWork:
2388                 DPRINT("DeviceNodeStartPostWork %wZ\n", &currentNode->InstancePath);
2389                 // TODO: inspect the status
2390                 status = PiStartDeviceFinal(currentNode);
2391                 doProcessAgain = TRUE;
2392                 break;
2393             case DeviceNodeStarted:
2394                 if (currentNode->Flags & DNF_REENUMERATE)
2395                 {
2396                     DPRINT("DeviceNodeStarted REENUMERATE %wZ\n", &currentNode->InstancePath);
2397                     currentNode->Flags &= ~DNF_REENUMERATE;
2398                     status = PiIrpQueryDeviceRelations(currentNode, BusRelations);
2399 
2400                     // again, skip DeviceNodeEnumeratePending as with the starting sequence
2401                     PiSetDevNodeState(currentNode, DeviceNodeEnumerateCompletion);
2402                     doProcessAgain = TRUE;
2403                 }
2404                 else if (currentNode->Flags & DNF_RESOURCE_REQUIREMENTS_CHANGED)
2405                 {
2406                     if (currentNode->Flags & DNF_NON_STOPPED_REBALANCE)
2407                     {
2408                         PiFakeResourceRebalance(currentNode);
2409                         currentNode->Flags &= ~DNF_NON_STOPPED_REBALANCE;
2410                     }
2411                     else
2412                     {
2413                         PiIrpQueryStopDevice(currentNode);
2414                         PiSetDevNodeState(currentNode, DeviceNodeQueryStopped);
2415                     }
2416 
2417                     doProcessAgain = TRUE;
2418                 }
2419                 break;
2420             case DeviceNodeQueryStopped:
2421                 // we're here after sending IRP_MN_QUERY_STOP_DEVICE
2422                 status = currentNode->CompletionStatus;
2423                 if (NT_SUCCESS(status))
2424                 {
2425                     PiIrpStopDevice(currentNode);
2426                     PiSetDevNodeState(currentNode, DeviceNodeStopped);
2427                 }
2428                 else
2429                 {
2430                     PiIrpCancelStopDevice(currentNode);
2431                     PiSetDevNodeState(currentNode, DeviceNodeStarted);
2432                 }
2433                 doProcessAgain = TRUE;
2434                 break;
2435             case DeviceNodeStopped:
2436                 // TODO: do resource rebalance (not implemented)
2437                 PiFakeResourceRebalance(currentNode);
2438 
2439                 PiSetDevNodeState(currentNode, DeviceNodeDriversAdded);
2440                 doProcessAgain = TRUE;
2441                 break;
2442             case DeviceNodeRestartCompletion:
2443                 break;
2444             case DeviceNodeEnumeratePending: // skipped on XP/2003
2445                 break;
2446             case DeviceNodeEnumerateCompletion:
2447                 DPRINT("DeviceNodeEnumerateCompletion %wZ\n", &currentNode->InstancePath);
2448                 status = PiEnumerateDevice(currentNode);
2449                 doProcessAgain = TRUE;
2450                 break;
2451             case DeviceNodeAwaitingQueuedDeletion:
2452                 break;
2453             case DeviceNodeAwaitingQueuedRemoval:
2454                 DPRINT("DeviceNodeAwaitingQueuedRemoval %wZ\n", &currentNode->InstancePath);
2455                 status = IopRemoveDevice(currentNode);
2456                 break;
2457             case DeviceNodeQueryRemoved:
2458                 break;
2459             case DeviceNodeRemovePendingCloses:
2460                 break;
2461             case DeviceNodeRemoved:
2462                 break;
2463             case DeviceNodeDeletePendingCloses:
2464                 break;
2465             case DeviceNodeDeleted:
2466                 break;
2467             default:
2468                 break;
2469         }
2470 
2471 skipEnum:
2472         if (!doProcessAgain)
2473         {
2474             KIRQL OldIrql;
2475             KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql);
2476             /* If we have a child, simply go down the tree */
2477             if (currentNode->State != DeviceNodeRemoved && currentNode->Child != NULL)
2478             {
2479                 ASSERT(currentNode->Child->Parent == currentNode);
2480                 currentNode = currentNode->Child;
2481             }
2482             else
2483             {
2484                 while (currentNode != RootNode)
2485                 {
2486                     /* All children processed -- go sideways */
2487                     if (currentNode->Sibling != NULL)
2488                     {
2489                         ASSERT(currentNode->Sibling->Parent == currentNode->Parent);
2490                         currentNode = currentNode->Sibling;
2491                         break;
2492                     }
2493                     else
2494                     {
2495                         /* We're the last sibling -- go back up */
2496                         ASSERT(currentNode->Parent->LastChild == currentNode);
2497                         currentNode = currentNode->Parent;
2498                     }
2499                     /* We already visited the parent and all its children, so keep looking */
2500                 }
2501             }
2502             KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql);
2503         }
2504         ObDereferenceObject(referencedObject);
2505     } while (doProcessAgain || currentNode != RootNode);
2506 }
2507 
2508 #ifdef DBG
2509 static
2510 PCSTR
2511 ActionToStr(
2512     _In_ DEVICE_ACTION Action)
2513 {
2514     switch (Action)
2515     {
2516         case PiActionEnumDeviceTree:
2517             return "PiActionEnumDeviceTree";
2518         case PiActionEnumRootDevices:
2519             return "PiActionEnumRootDevices";
2520         case PiActionResetDevice:
2521             return "PiActionResetDevice";
2522         case PiActionAddBootDevices:
2523             return "PiActionAddBootDevices";
2524         case PiActionStartDevice:
2525             return "PiActionStartDevice";
2526         case PiActionQueryState:
2527             return "PiActionQueryState";
2528         default:
2529             return "(request unknown)";
2530     }
2531 }
2532 #endif
2533 
2534 static
2535 VOID
2536 NTAPI
2537 PipDeviceActionWorker(
2538     _In_opt_ PVOID Context)
2539 {
2540     PLIST_ENTRY ListEntry;
2541     PDEVICE_ACTION_REQUEST Request;
2542     KIRQL OldIrql;
2543     PDEVICE_NODE deviceNode;
2544     NTSTATUS status;
2545 
2546     KeAcquireSpinLock(&IopDeviceActionLock, &OldIrql);
2547     while (!IsListEmpty(&IopDeviceActionRequestList))
2548     {
2549         ListEntry = RemoveHeadList(&IopDeviceActionRequestList);
2550         KeReleaseSpinLock(&IopDeviceActionLock, OldIrql);
2551         Request = CONTAINING_RECORD(ListEntry, DEVICE_ACTION_REQUEST, RequestListEntry);
2552 
2553         ASSERT(Request->DeviceObject);
2554 
2555         deviceNode = IopGetDeviceNode(Request->DeviceObject);
2556         ASSERT(deviceNode);
2557 
2558         status = STATUS_SUCCESS;
2559 
2560         DPRINT("Processing PnP request %p: DeviceObject - %p, Action - %s\n",
2561                Request, Request->DeviceObject, ActionToStr(Request->Action));
2562 
2563         switch (Request->Action)
2564         {
2565             case PiActionAddBootDevices:
2566             {
2567                 if (deviceNode->State == DeviceNodeInitialized &&
2568                     !(deviceNode->Flags & DNF_HAS_PROBLEM))
2569                 {
2570                     status = PiCallDriverAddDevice(deviceNode, PnPBootDriversInitialized);
2571                 }
2572                 break;
2573             }
2574             case PiActionEnumRootDevices:
2575             case PiActionEnumDeviceTree:
2576                 deviceNode->Flags |= DNF_REENUMERATE;
2577                 PiDevNodeStateMachine(deviceNode);
2578                 break;
2579 
2580             case PiActionResetDevice:
2581                 // TODO: the operation is a no-op for everything except removed nodes
2582                 // for removed nodes, it returns them back to DeviceNodeUninitialized
2583                 if (deviceNode->State == DeviceNodeRemoved)
2584                 {
2585                     deviceNode->State = DeviceNodeUninitialized;
2586                 }
2587                 status = STATUS_SUCCESS;
2588                 break;
2589 
2590             case PiActionStartDevice:
2591                 // This action is triggered from usermode, when a driver is installed
2592                 // for a non-critical PDO
2593                 if (deviceNode->State == DeviceNodeInitialized &&
2594                     !(deviceNode->Flags & DNF_HAS_PROBLEM))
2595                 {
2596                     PiDevNodeStateMachine(deviceNode);
2597                 }
2598                 else
2599                 {
2600                     DPRINT1("NOTE: attempt to start an already started/uninitialized device %wZ\n",
2601                             &deviceNode->InstancePath);
2602                     status = STATUS_UNSUCCESSFUL;
2603                 }
2604                 break;
2605 
2606             case PiActionQueryState:
2607                 // This action is only valid for started devices. If the device is not yet
2608                 // started, the PnP manager issues IRP_MN_QUERY_PNP_DEVICE_STATE by itself.
2609                 if (deviceNode->State == DeviceNodeStarted)
2610                 {
2611                     // Issue a IRP_MN_QUERY_PNP_DEVICE_STATE request: it will update node's flags
2612                     // and then do enumeration if something has changed
2613                     status = PiUpdateDeviceState(deviceNode);
2614                     if (NT_SUCCESS(status))
2615                     {
2616                         PiDevNodeStateMachine(deviceNode);
2617                     }
2618                 }
2619                 // TODO: Windows may return STATUS_DELETE_PENDING here
2620                 status = STATUS_SUCCESS;
2621                 break;
2622 
2623             default:
2624                 DPRINT1("Unimplemented device action %u\n", Request->Action);
2625                 status = STATUS_NOT_IMPLEMENTED;
2626                 break;
2627         }
2628 
2629         if (Request->CompletionStatus)
2630         {
2631             *Request->CompletionStatus = status;
2632         }
2633 
2634         if (Request->CompletionEvent)
2635         {
2636             KeSetEvent(Request->CompletionEvent, IO_NO_INCREMENT, FALSE);
2637         }
2638 
2639         DPRINT("Finished processing PnP request %p\n", Request);
2640         ObDereferenceObject(Request->DeviceObject);
2641         ExFreePoolWithTag(Request, TAG_IO);
2642         KeAcquireSpinLock(&IopDeviceActionLock, &OldIrql);
2643     }
2644     IopDeviceActionInProgress = FALSE;
2645     KeSetEvent(&PiEnumerationFinished, IO_NO_INCREMENT, FALSE);
2646     KeReleaseSpinLock(&IopDeviceActionLock, OldIrql);
2647 }
2648 
2649 /**
2650  * @brief      Queue a device operation to a worker thread.
2651  *
2652  * @param[in]  DeviceObject      The device object
2653  * @param[in]  Action            The action
2654  * @param[in]  CompletionEvent   The completion event object (optional)
2655  * @param[out] CompletionStatus  Status returned be the action will be written here
2656  */
2657 
2658 VOID
2659 PiQueueDeviceAction(
2660     _In_ PDEVICE_OBJECT DeviceObject,
2661     _In_ DEVICE_ACTION Action,
2662     _In_opt_ PKEVENT CompletionEvent,
2663     _Out_opt_ NTSTATUS *CompletionStatus)
2664 {
2665     PDEVICE_ACTION_REQUEST Request;
2666     KIRQL OldIrql;
2667 
2668     Request = ExAllocatePoolWithTag(NonPagedPoolMustSucceed, sizeof(*Request), TAG_IO);
2669 
2670     DPRINT("PiQueueDeviceAction: DeviceObject - %p, Request - %p, Action - %s\n",
2671         DeviceObject, Request, ActionToStr(Action));
2672 
2673     ObReferenceObject(DeviceObject);
2674 
2675     Request->DeviceObject = DeviceObject;
2676     Request->Action = Action;
2677     Request->CompletionEvent = CompletionEvent;
2678     Request->CompletionStatus = CompletionStatus;
2679 
2680     KeAcquireSpinLock(&IopDeviceActionLock, &OldIrql);
2681     InsertTailList(&IopDeviceActionRequestList, &Request->RequestListEntry);
2682 
2683     if (Action == PiActionEnumRootDevices || Action == PiActionAddBootDevices)
2684     {
2685         ASSERT(!IopDeviceActionInProgress);
2686 
2687         IopDeviceActionInProgress = TRUE;
2688         KeClearEvent(&PiEnumerationFinished);
2689         KeReleaseSpinLock(&IopDeviceActionLock, OldIrql);
2690 
2691         PipDeviceActionWorker(NULL);
2692         return;
2693     }
2694 
2695     if (IopDeviceActionInProgress || !PnPBootDriversLoaded)
2696     {
2697         KeReleaseSpinLock(&IopDeviceActionLock, OldIrql);
2698         return;
2699     }
2700     IopDeviceActionInProgress = TRUE;
2701     KeClearEvent(&PiEnumerationFinished);
2702     KeReleaseSpinLock(&IopDeviceActionLock, OldIrql);
2703 
2704     ExInitializeWorkItem(&IopDeviceActionWorkItem, PipDeviceActionWorker, NULL);
2705     ExQueueWorkItem(&IopDeviceActionWorkItem, DelayedWorkQueue);
2706 }
2707 
2708 /**
2709  * @brief      Perfom a device operation synchronously via PiQueueDeviceAction
2710  *
2711  * @param[in]  DeviceObject  The device object
2712  * @param[in]  Action        The action
2713  *
2714  * @return     Status of the operation
2715  */
2716 
2717 NTSTATUS
2718 PiPerformSyncDeviceAction(
2719     _In_ PDEVICE_OBJECT DeviceObject,
2720     _In_ DEVICE_ACTION Action)
2721 {
2722     KEVENT opFinished;
2723     NTSTATUS status;
2724 
2725     KeInitializeEvent(&opFinished, SynchronizationEvent, FALSE);
2726     PiQueueDeviceAction(DeviceObject, Action, &opFinished, &status);
2727     KeWaitForSingleObject(&opFinished, Executive, KernelMode, FALSE, NULL);
2728 
2729     return status;
2730 }
2731