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