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