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