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