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