xref: /reactos/ntoskrnl/io/pnpmgr/plugplay.c (revision d8ba5920)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * COPYRIGHT:       GPL - See COPYING in the top level directory
4  * FILE:            ntoskrnl/io/pnpmgr/plugplay.c
5  * PURPOSE:         Plug-and-play interface routines
6  * PROGRAMMERS:     Eric Kohl <eric.kohl@reactos.org>
7  */
8 
9 /* INCLUDES *****************************************************************/
10 
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14 
15 typedef struct _PNP_EVENT_ENTRY
16 {
17     LIST_ENTRY ListEntry;
18     PLUGPLAY_EVENT_BLOCK Event;
19 } PNP_EVENT_ENTRY, *PPNP_EVENT_ENTRY;
20 
21 typedef struct _IOP_FIND_DEVICE_INSTANCE_TRAVERSE_CONTEXT
22 {
23     PCUNICODE_STRING InstancePath;
24     PDEVICE_OBJECT DeviceObject;
25 } IOP_FIND_DEVICE_INSTANCE_TRAVERSE_CONTEXT, *PIOP_FIND_DEVICE_INSTANCE_TRAVERSE_CONTEXT;
26 
27 
28 /* GLOBALS *******************************************************************/
29 
30 static LIST_ENTRY IopPnpEventQueueHead;
31 static KEVENT IopPnpNotifyEvent;
32 
33 /* FUNCTIONS *****************************************************************/
34 
35 NTSTATUS
36 IopSetDeviceInstanceData(HANDLE InstanceKey, PDEVICE_NODE DeviceNode);
37 
38 CODE_SEG("INIT")
39 NTSTATUS
IopInitPlugPlayEvents(VOID)40 IopInitPlugPlayEvents(VOID)
41 {
42     InitializeListHead(&IopPnpEventQueueHead);
43 
44     KeInitializeEvent(&IopPnpNotifyEvent,
45                       SynchronizationEvent,
46                       FALSE);
47 
48     return STATUS_SUCCESS;
49 }
50 
51 NTSTATUS
IopQueueDeviceChangeEvent(_In_ const GUID * EventGuid,_In_ const GUID * InterfaceClassGuid,_In_ PUNICODE_STRING SymbolicLinkName)52 IopQueueDeviceChangeEvent(
53     _In_ const GUID *EventGuid,
54     _In_ const GUID *InterfaceClassGuid,
55     _In_ PUNICODE_STRING SymbolicLinkName)
56 {
57     PPNP_EVENT_ENTRY EventEntry;
58     UNICODE_STRING Copy;
59     ULONG TotalSize;
60 
61     /* Allocate a big enough buffer */
62     Copy.Length = 0;
63     Copy.MaximumLength = SymbolicLinkName->Length + sizeof(UNICODE_NULL);
64     TotalSize =
65         FIELD_OFFSET(PLUGPLAY_EVENT_BLOCK, DeviceClass.SymbolicLinkName) +
66         Copy.MaximumLength;
67 
68     EventEntry = ExAllocatePool(NonPagedPool,
69                                 TotalSize + FIELD_OFFSET(PNP_EVENT_ENTRY, Event));
70     if (!EventEntry)
71         return STATUS_INSUFFICIENT_RESOURCES;
72     RtlZeroMemory(EventEntry, TotalSize + FIELD_OFFSET(PNP_EVENT_ENTRY, Event));
73 
74     /* Fill the buffer with the event GUID */
75     RtlCopyMemory(&EventEntry->Event.EventGuid, EventGuid, sizeof(GUID));
76     EventEntry->Event.EventCategory = DeviceClassChangeEvent;
77     EventEntry->Event.TotalSize = TotalSize;
78 
79     /* Fill the interface class GUID */
80     RtlCopyMemory(&EventEntry->Event.DeviceClass.ClassGuid, InterfaceClassGuid, sizeof(GUID));
81 
82     /* Fill the symbolic link name */
83     RtlCopyMemory(&EventEntry->Event.DeviceClass.SymbolicLinkName,
84                   SymbolicLinkName->Buffer, SymbolicLinkName->Length);
85     EventEntry->Event.DeviceClass.SymbolicLinkName[SymbolicLinkName->Length / sizeof(WCHAR)] = UNICODE_NULL;
86 
87     InsertHeadList(&IopPnpEventQueueHead,
88                    &EventEntry->ListEntry);
89     KeSetEvent(&IopPnpNotifyEvent,
90                0,
91                FALSE);
92 
93     return STATUS_SUCCESS;
94 }
95 
96 NTSTATUS
IopQueueDeviceInstallEvent(_In_ const GUID * EventGuid,_In_ PUNICODE_STRING DeviceId)97 IopQueueDeviceInstallEvent(
98     _In_ const GUID *EventGuid,
99     _In_ PUNICODE_STRING DeviceId)
100 {
101     PPNP_EVENT_ENTRY EventEntry;
102     UNICODE_STRING Copy;
103     ULONG TotalSize;
104 
105     /* Allocate a big enough buffer */
106     Copy.Length = 0;
107     Copy.MaximumLength = DeviceId->Length + sizeof(UNICODE_NULL);
108     TotalSize =
109         FIELD_OFFSET(PLUGPLAY_EVENT_BLOCK, InstallDevice.DeviceId) +
110         Copy.MaximumLength;
111 
112     EventEntry = ExAllocatePool(NonPagedPool,
113                                 TotalSize + FIELD_OFFSET(PNP_EVENT_ENTRY, Event));
114     if (!EventEntry)
115         return STATUS_INSUFFICIENT_RESOURCES;
116     RtlZeroMemory(EventEntry, TotalSize + FIELD_OFFSET(PNP_EVENT_ENTRY, Event));
117 
118     /* Fill the buffer with the event GUID */
119     RtlCopyMemory(&EventEntry->Event.EventGuid, EventGuid, sizeof(GUID));
120     EventEntry->Event.EventCategory = DeviceInstallEvent;
121     EventEntry->Event.TotalSize = TotalSize;
122 
123     /* Fill the symbolic link name */
124     RtlCopyMemory(&EventEntry->Event.InstallDevice.DeviceId,
125                   DeviceId->Buffer, DeviceId->Length);
126     EventEntry->Event.InstallDevice.DeviceId[DeviceId->Length / sizeof(WCHAR)] = UNICODE_NULL;
127 
128     InsertHeadList(&IopPnpEventQueueHead, &EventEntry->ListEntry);
129 
130     KeSetEvent(&IopPnpNotifyEvent, 0, FALSE);
131 
132     return STATUS_SUCCESS;
133 }
134 
135 
136 NTSTATUS
IopQueueTargetDeviceEvent(const GUID * Guid,PUNICODE_STRING DeviceIds)137 IopQueueTargetDeviceEvent(const GUID *Guid,
138                           PUNICODE_STRING DeviceIds)
139 {
140     PPNP_EVENT_ENTRY EventEntry;
141     UNICODE_STRING Copy;
142     ULONG TotalSize;
143     NTSTATUS Status;
144 
145     ASSERT(DeviceIds);
146 
147     /* Allocate a big enough buffer */
148     Copy.Length = 0;
149     Copy.MaximumLength = DeviceIds->Length + sizeof(UNICODE_NULL);
150     TotalSize =
151         FIELD_OFFSET(PLUGPLAY_EVENT_BLOCK, TargetDevice.DeviceIds) +
152         Copy.MaximumLength;
153 
154     EventEntry = ExAllocatePool(NonPagedPool,
155                                 TotalSize + FIELD_OFFSET(PNP_EVENT_ENTRY, Event));
156     if (!EventEntry)
157         return STATUS_INSUFFICIENT_RESOURCES;
158     RtlZeroMemory(EventEntry, TotalSize + FIELD_OFFSET(PNP_EVENT_ENTRY, Event));
159 
160     /* Fill the buffer with the event GUID */
161     RtlCopyMemory(&EventEntry->Event.EventGuid,
162                   Guid,
163                   sizeof(GUID));
164     EventEntry->Event.EventCategory = TargetDeviceChangeEvent;
165     EventEntry->Event.TotalSize = TotalSize;
166 
167     /* Fill the device id */
168     Copy.Buffer = EventEntry->Event.TargetDevice.DeviceIds;
169     Status = RtlAppendUnicodeStringToString(&Copy, DeviceIds);
170     if (!NT_SUCCESS(Status))
171     {
172         ExFreePool(EventEntry);
173         return Status;
174     }
175 
176     InsertHeadList(&IopPnpEventQueueHead,
177                    &EventEntry->ListEntry);
178     KeSetEvent(&IopPnpNotifyEvent,
179                0,
180                FALSE);
181 
182     return STATUS_SUCCESS;
183 }
184 
185 NTSTATUS
IopFindDeviceInstanceTraverse(_In_ PDEVICE_NODE DeviceNode,_Inout_ PVOID Context)186 IopFindDeviceInstanceTraverse(
187     _In_ PDEVICE_NODE DeviceNode,
188     _Inout_ PVOID Context)
189 {
190     PIOP_FIND_DEVICE_INSTANCE_TRAVERSE_CONTEXT DeviceInstanceContext = Context;
191 
192     if (RtlEqualUnicodeString(&DeviceNode->InstancePath,
193                               DeviceInstanceContext->InstancePath, TRUE))
194     {
195         ObReferenceObject(DeviceNode->PhysicalDeviceObject);
196         DeviceInstanceContext->DeviceObject = DeviceNode->PhysicalDeviceObject;
197 
198         /* Stop enumeration */
199         return STATUS_UNSUCCESSFUL;
200     }
201 
202     return STATUS_SUCCESS;
203 }
204 
205 PDEVICE_OBJECT
IopGetDeviceObjectFromDeviceInstance(PUNICODE_STRING DeviceInstance)206 IopGetDeviceObjectFromDeviceInstance(PUNICODE_STRING DeviceInstance)
207 {
208     DEVICETREE_TRAVERSE_CONTEXT Context;
209     IOP_FIND_DEVICE_INSTANCE_TRAVERSE_CONTEXT DeviceInstanceContext;
210 
211     if (IopRootDeviceNode == NULL)
212         return NULL;
213 
214     if (DeviceInstance == NULL ||
215         DeviceInstance->Length == 0)
216     {
217         if (IopRootDeviceNode->PhysicalDeviceObject)
218         {
219             ObReferenceObject(IopRootDeviceNode->PhysicalDeviceObject);
220             return IopRootDeviceNode->PhysicalDeviceObject;
221         }
222         else
223             return NULL;
224     }
225 
226     /* Traverse the device tree to find the matching device node */
227     DeviceInstanceContext.InstancePath = DeviceInstance;
228     DeviceInstanceContext.DeviceObject = NULL;
229     IopInitDeviceTreeTraverseContext(&Context,
230                                      IopRootDeviceNode,
231                                      IopFindDeviceInstanceTraverse,
232                                      &DeviceInstanceContext);
233     (void)IopTraverseDeviceTree(&Context);
234 
235     /* In case of error or instance not found, this will still be NULL from above. */
236     return DeviceInstanceContext.DeviceObject;
237 }
238 
239 static NTSTATUS
IopCaptureUnicodeString(PUNICODE_STRING DstName,PUNICODE_STRING SrcName)240 IopCaptureUnicodeString(PUNICODE_STRING DstName, PUNICODE_STRING SrcName)
241 {
242     NTSTATUS Status = STATUS_SUCCESS;
243     volatile UNICODE_STRING Name;
244 
245     Name.Buffer = NULL;
246     _SEH2_TRY
247     {
248         Name.Length = SrcName->Length;
249         Name.MaximumLength = SrcName->MaximumLength;
250         if (Name.Length > Name.MaximumLength)
251         {
252             Status = STATUS_INVALID_PARAMETER;
253             _SEH2_LEAVE;
254         }
255 
256         if (Name.MaximumLength)
257         {
258             ProbeForRead(SrcName->Buffer,
259                          Name.MaximumLength,
260                          sizeof(WCHAR));
261             Name.Buffer = ExAllocatePool(NonPagedPool, Name.MaximumLength);
262             if (Name.Buffer == NULL)
263             {
264                 Status = STATUS_INSUFFICIENT_RESOURCES;
265                 _SEH2_LEAVE;
266             }
267 
268             memcpy(Name.Buffer, SrcName->Buffer, Name.MaximumLength);
269         }
270 
271         *DstName = Name;
272     }
273     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
274     {
275         if (Name.Buffer)
276         {
277             ExFreePool(Name.Buffer);
278         }
279         Status = _SEH2_GetExceptionCode();
280     }
281     _SEH2_END;
282 
283     return Status;
284 }
285 
286 
287 static
288 NTSTATUS
PiControlInitializeDevice(_In_ PPLUGPLAY_CONTROL_DEVICE_CONTROL_DATA ControlData)289 PiControlInitializeDevice(
290     _In_ PPLUGPLAY_CONTROL_DEVICE_CONTROL_DATA ControlData)
291 {
292     UNICODE_STRING DeviceInstance;
293     PDEVICE_OBJECT DeviceObject;
294     PDEVICE_NODE DeviceNode;
295     NTSTATUS Status = STATUS_SUCCESS;
296     HANDLE InstanceKey;
297 
298     DPRINT("PiControlInitializeDevice(%p)\n", ControlData);
299 
300     Status = IopCaptureUnicodeString(&DeviceInstance, &ControlData->DeviceInstance);
301     if (!NT_SUCCESS(Status))
302     {
303         return Status;
304     }
305 
306     DPRINT("Device: %wZ\n", &DeviceInstance);
307 
308     /* Leave, if the device already exists */
309     DeviceObject = IopGetDeviceObjectFromDeviceInstance(&DeviceInstance);
310     if (DeviceObject != NULL)
311     {
312         DPRINT1("Device %wZ already exists!\n", &DeviceInstance);
313         ObDereferenceObject(DeviceObject);
314         Status = STATUS_SUCCESS;
315         goto done;
316     }
317 
318     DPRINT("Device %wZ does not exist!\n", &DeviceInstance);
319 
320     /* Create a device node for the device instance */
321     Status = PnpRootCreateDeviceObject(&DeviceObject);
322     if (!NT_SUCCESS(Status))
323     {
324         DPRINT1("IoCreateDevice() failed (Status 0x%08lx)\n", Status);
325         goto done;
326     }
327 
328     /* Allocate a new device node */
329     DeviceNode = PipAllocateDeviceNode(DeviceObject);
330     if (DeviceNode == NULL)
331     {
332         DPRINT1("Failed to allocate a device node!\n");
333         IoDeleteDevice(DeviceObject);
334         Status = STATUS_INSUFFICIENT_RESOURCES;
335         goto done;
336     }
337 
338     // Set the device instance of the device node
339     // NOTE: a NULL-terminated string is required for PnpRootRegisterDevice
340     Status = RtlDuplicateUnicodeString(
341                 RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE,
342                 &DeviceInstance,
343                 &DeviceNode->InstancePath);
344     if (!NT_SUCCESS(Status))
345     {
346         DPRINT1("RtlDuplicateUnicodeString() failed (Status 0x%08lx)\n", Status);
347         IopFreeDeviceNode(DeviceNode);
348         IoDeleteDevice(DeviceObject);
349         goto done;
350     }
351 
352     DeviceObject->Flags |= DO_BUS_ENUMERATED_DEVICE;
353     DeviceNode->Flags |= DNF_MADEUP | DNF_IDS_QUERIED | DNF_ENUMERATED;
354     PiSetDevNodeState(DeviceNode, DeviceNodeInitialized);
355 
356     Status = IopCreateDeviceKeyPath(&DeviceInstance, REG_OPTION_NON_VOLATILE, &InstanceKey);
357     if (!NT_SUCCESS(Status))
358     {
359         DPRINT1("Failed to create the instance key! (Status %lx)\n", Status);
360         IopFreeDeviceNode(DeviceNode);
361         IoDeleteDevice(DeviceObject);
362         goto done;
363     }
364 
365     /* Write the resource information to the registry */
366     IopSetDeviceInstanceData(InstanceKey, DeviceNode);
367 
368     // Finish the root device registration
369     PnpRootRegisterDevice(DeviceObject);
370 
371     /* Insert as a root enumerated device node */
372     PiInsertDevNode(DeviceNode, IopRootDeviceNode);
373 
374     /* Report the device to the user-mode pnp manager */
375     IopQueueDeviceInstallEvent(&GUID_DEVICE_ENUMERATED, &DeviceNode->InstancePath);
376 
377     ZwClose(InstanceKey);
378 done:
379     ExFreePool(DeviceInstance.Buffer);
380 
381     return Status;
382 }
383 
384 
385 /*
386  * Remove the current PnP event from the tail of the event queue
387  * and signal IopPnpNotifyEvent if there is yet another event in the queue.
388  */
389 static
390 NTSTATUS
IopRemovePlugPlayEvent(_In_ PPLUGPLAY_CONTROL_USER_RESPONSE_DATA ResponseData)391 IopRemovePlugPlayEvent(
392     _In_ PPLUGPLAY_CONTROL_USER_RESPONSE_DATA ResponseData)
393 {
394     /* Remove a pnp event entry from the tail of the queue */
395     if (!IsListEmpty(&IopPnpEventQueueHead))
396     {
397         ExFreePool(CONTAINING_RECORD(RemoveTailList(&IopPnpEventQueueHead), PNP_EVENT_ENTRY, ListEntry));
398     }
399 
400     /* Signal the next pnp event in the queue */
401     if (!IsListEmpty(&IopPnpEventQueueHead))
402     {
403         KeSetEvent(&IopPnpNotifyEvent,
404                    0,
405                    FALSE);
406     }
407 
408     return STATUS_SUCCESS;
409 }
410 
411 
412 static NTSTATUS
IopGetInterfaceDeviceList(PPLUGPLAY_CONTROL_INTERFACE_DEVICE_LIST_DATA DeviceList)413 IopGetInterfaceDeviceList(PPLUGPLAY_CONTROL_INTERFACE_DEVICE_LIST_DATA DeviceList)
414 {
415     NTSTATUS Status;
416     PLUGPLAY_CONTROL_INTERFACE_DEVICE_LIST_DATA StackList;
417     UNICODE_STRING DeviceInstance;
418     PDEVICE_OBJECT DeviceObject = NULL;
419     GUID FilterGuid;
420     PZZWSTR SymbolicLinkList = NULL, LinkList;
421     SIZE_T TotalLength;
422 
423     _SEH2_TRY
424     {
425         RtlCopyMemory(&StackList, DeviceList, sizeof(PLUGPLAY_CONTROL_INTERFACE_DEVICE_LIST_DATA));
426 
427         ProbeForRead(StackList.FilterGuid, sizeof(GUID), sizeof(UCHAR));
428         RtlCopyMemory(&FilterGuid, StackList.FilterGuid, sizeof(GUID));
429 
430         if (StackList.Buffer != NULL && StackList.BufferSize != 0)
431         {
432             ProbeForWrite(StackList.Buffer, StackList.BufferSize, sizeof(UCHAR));
433         }
434     }
435     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
436     {
437         _SEH2_YIELD(return _SEH2_GetExceptionCode());
438     }
439     _SEH2_END;
440 
441     Status = IopCaptureUnicodeString(&DeviceInstance, &StackList.DeviceInstance);
442     if (NT_SUCCESS(Status))
443     {
444         /* Get the device object */
445         DeviceObject = IopGetDeviceObjectFromDeviceInstance(&DeviceInstance);
446         if (DeviceInstance.Buffer != NULL)
447         {
448             ExFreePool(DeviceInstance.Buffer);
449         }
450     }
451 
452     Status = IoGetDeviceInterfaces(&FilterGuid, DeviceObject, StackList.Flags, &SymbolicLinkList);
453     ObDereferenceObject(DeviceObject);
454 
455     if (!NT_SUCCESS(Status))
456     {
457         /* failed */
458         return Status;
459     }
460 
461     LinkList = SymbolicLinkList;
462     while (*SymbolicLinkList != UNICODE_NULL)
463     {
464         SymbolicLinkList += wcslen(SymbolicLinkList) + (sizeof(UNICODE_NULL) / sizeof(WCHAR));
465     }
466     TotalLength = ((SymbolicLinkList - LinkList + 1) * sizeof(WCHAR));
467 
468     _SEH2_TRY
469     {
470         if (StackList.Buffer != NULL &&
471             StackList.BufferSize >= TotalLength)
472         {
473             // We've already probed the buffer for writing above.
474             RtlCopyMemory(StackList.Buffer, LinkList, TotalLength);
475         }
476 
477         DeviceList->BufferSize = TotalLength;
478     }
479     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
480     {
481         ExFreePool(LinkList);
482         _SEH2_YIELD(return _SEH2_GetExceptionCode());
483     }
484     _SEH2_END;
485 
486     ExFreePool(LinkList);
487     return STATUS_SUCCESS;
488 }
489 
490 static NTSTATUS
IopGetDeviceProperty(PPLUGPLAY_CONTROL_PROPERTY_DATA PropertyData)491 IopGetDeviceProperty(PPLUGPLAY_CONTROL_PROPERTY_DATA PropertyData)
492 {
493     PDEVICE_OBJECT DeviceObject = NULL;
494     PDEVICE_NODE DeviceNode;
495     UNICODE_STRING DeviceInstance;
496     ULONG BufferSize;
497     ULONG Property;
498     DEVICE_REGISTRY_PROPERTY DeviceProperty;
499     PVOID Buffer;
500     NTSTATUS Status;
501 
502     DPRINT("IopGetDeviceProperty() called\n");
503     DPRINT("Device name: %wZ\n", &PropertyData->DeviceInstance);
504 
505     Status = IopCaptureUnicodeString(&DeviceInstance, &PropertyData->DeviceInstance);
506     if (!NT_SUCCESS(Status))
507     {
508         return Status;
509     }
510 
511     _SEH2_TRY
512     {
513         Property = PropertyData->Property;
514         BufferSize = PropertyData->BufferSize;
515         ProbeForWrite(PropertyData->Buffer,
516                       BufferSize,
517                       sizeof(UCHAR));
518     }
519     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
520     {
521         if (DeviceInstance.Buffer != NULL)
522         {
523             ExFreePool(DeviceInstance.Buffer);
524         }
525         _SEH2_YIELD(return _SEH2_GetExceptionCode());
526     }
527     _SEH2_END;
528 
529     /* Get the device object */
530     DeviceObject = IopGetDeviceObjectFromDeviceInstance(&DeviceInstance);
531     if (DeviceInstance.Buffer != NULL)
532     {
533         ExFreePool(DeviceInstance.Buffer);
534     }
535     if (DeviceObject == NULL)
536     {
537         return STATUS_NO_SUCH_DEVICE;
538     }
539 
540     Buffer = ExAllocatePool(NonPagedPool, BufferSize);
541     if (Buffer == NULL)
542     {
543         ObDereferenceObject(DeviceObject);
544         return STATUS_INSUFFICIENT_RESOURCES;
545     }
546 
547 
548     DeviceNode = ((PEXTENDED_DEVOBJ_EXTENSION)DeviceObject->DeviceObjectExtension)->DeviceNode;
549 
550     if (Property == PNP_PROPERTY_POWER_DATA)
551     {
552         if (BufferSize < sizeof(CM_POWER_DATA))
553         {
554             BufferSize = 0;
555             Status = STATUS_BUFFER_TOO_SMALL;
556         }
557         else
558         {
559             DEVICE_CAPABILITIES DeviceCapabilities;
560             PCM_POWER_DATA PowerData;
561             IO_STACK_LOCATION Stack;
562             IO_STATUS_BLOCK IoStatusBlock;
563 
564             PowerData = (PCM_POWER_DATA)Buffer;
565             RtlZeroMemory(PowerData, sizeof(CM_POWER_DATA));
566             PowerData->PD_Size = sizeof(CM_POWER_DATA);
567 
568             RtlZeroMemory(&DeviceCapabilities, sizeof(DEVICE_CAPABILITIES));
569             DeviceCapabilities.Size = sizeof(DEVICE_CAPABILITIES);
570             DeviceCapabilities.Version = 1;
571             DeviceCapabilities.Address = -1;
572             DeviceCapabilities.UINumber = -1;
573 
574             Stack.Parameters.DeviceCapabilities.Capabilities = &DeviceCapabilities;
575 
576             Status = IopInitiatePnpIrp(DeviceObject,
577                                        &IoStatusBlock,
578                                        IRP_MN_QUERY_CAPABILITIES,
579                                        &Stack);
580             if (NT_SUCCESS(Status))
581             {
582                 DPRINT("Got device capabiliities\n");
583 
584                 PowerData->PD_MostRecentPowerState = PowerDeviceD0; // FIXME
585                 if (DeviceCapabilities.DeviceD1)
586                     PowerData->PD_Capabilities |= PDCAP_D1_SUPPORTED;
587                 if (DeviceCapabilities.DeviceD2)
588                     PowerData->PD_Capabilities |= PDCAP_D2_SUPPORTED;
589                 if (DeviceCapabilities.WakeFromD0)
590                     PowerData->PD_Capabilities |= PDCAP_WAKE_FROM_D0_SUPPORTED;
591                 if (DeviceCapabilities.WakeFromD1)
592                     PowerData->PD_Capabilities |= PDCAP_WAKE_FROM_D1_SUPPORTED;
593                 if (DeviceCapabilities.WakeFromD2)
594                     PowerData->PD_Capabilities |= PDCAP_WAKE_FROM_D2_SUPPORTED;
595                 if (DeviceCapabilities.WakeFromD3)
596                     PowerData->PD_Capabilities |= PDCAP_WAKE_FROM_D3_SUPPORTED;
597                 if (DeviceCapabilities.WarmEjectSupported)
598                     PowerData->PD_Capabilities |= PDCAP_WARM_EJECT_SUPPORTED;
599                 PowerData->PD_D1Latency = DeviceCapabilities.D1Latency;
600                 PowerData->PD_D2Latency = DeviceCapabilities.D2Latency;
601                 PowerData->PD_D3Latency = DeviceCapabilities.D3Latency;
602                 RtlCopyMemory(&PowerData->PD_PowerStateMapping,
603                               &DeviceCapabilities.DeviceState,
604                               sizeof(DeviceCapabilities.DeviceState));
605                 PowerData->PD_DeepestSystemWake = DeviceCapabilities.SystemWake;
606             }
607             else
608             {
609                 DPRINT("IRP_MN_QUERY_CAPABILITIES failed (Status 0x%08lx)\n", Status);
610 
611                 PowerData->PD_Capabilities = PDCAP_D0_SUPPORTED | PDCAP_D3_SUPPORTED;
612                 PowerData->PD_MostRecentPowerState = PowerDeviceD0;
613             }
614         }
615     }
616     else if (Property == PNP_PROPERTY_REMOVAL_POLICY_OVERRIDE)
617     {
618         UNIMPLEMENTED;
619         BufferSize = 0;
620         Status = STATUS_NOT_IMPLEMENTED;
621     }
622     else if (Property == PNP_PROPERTY_REMOVAL_POLICY_HARDWARE_DEFAULT)
623     {
624         if (BufferSize < sizeof(DeviceNode->HardwareRemovalPolicy))
625         {
626             BufferSize = 0;
627             Status = STATUS_BUFFER_TOO_SMALL;
628         }
629         else
630         {
631             BufferSize = sizeof(DeviceNode->HardwareRemovalPolicy);
632             RtlCopyMemory(Buffer,
633                           &DeviceNode->HardwareRemovalPolicy,
634                           BufferSize);
635         }
636     }
637     else
638     {
639         switch (Property)
640         {
641             case PNP_PROPERTY_UI_NUMBER:
642                 DeviceProperty = DevicePropertyUINumber;
643                 break;
644 
645             case PNP_PROPERTY_PHYSICAL_DEVICE_OBJECT_NAME:
646                 DeviceProperty = DevicePropertyPhysicalDeviceObjectName;
647                 break;
648 
649             case PNP_PROPERTY_BUSTYPEGUID:
650                 DeviceProperty = DevicePropertyBusTypeGuid;
651                 break;
652 
653             case PNP_PROPERTY_LEGACYBUSTYPE:
654                 DeviceProperty = DevicePropertyLegacyBusType;
655                 break;
656 
657             case PNP_PROPERTY_BUSNUMBER:
658                 DeviceProperty = DevicePropertyBusNumber;
659                 break;
660 
661             case PNP_PROPERTY_REMOVAL_POLICY:
662                 DeviceProperty = DevicePropertyRemovalPolicy;
663                 break;
664 
665             case PNP_PROPERTY_ADDRESS:
666                 DeviceProperty = DevicePropertyAddress;
667                 break;
668 
669             case PNP_PROPERTY_ENUMERATOR_NAME:
670                 DeviceProperty = DevicePropertyEnumeratorName;
671                 break;
672 
673             case PNP_PROPERTY_INSTALL_STATE:
674                 DeviceProperty = DevicePropertyInstallState;
675                 break;
676 
677 #if (WINVER >= _WIN32_WINNT_WS03)
678             case PNP_PROPERTY_LOCATION_PATHS:
679                 UNIMPLEMENTED;
680                 BufferSize = 0;
681                 Status = STATUS_NOT_IMPLEMENTED;
682                 break;
683 #endif
684 
685 #if (WINVER >= _WIN32_WINNT_WIN7)
686             case PNP_PROPERTY_CONTAINERID:
687                 DeviceProperty = DevicePropertyContainerID;
688                 break;
689 #endif
690 
691             default:
692                 BufferSize = 0;
693                 Status = STATUS_INVALID_PARAMETER;
694                 break;
695         }
696 
697         if (Status == STATUS_SUCCESS)
698         {
699             Status = IoGetDeviceProperty(DeviceObject,
700                                          DeviceProperty,
701                                          BufferSize,
702                                          Buffer,
703                                          &BufferSize);
704         }
705     }
706 
707     ObDereferenceObject(DeviceObject);
708 
709     if (NT_SUCCESS(Status))
710     {
711         _SEH2_TRY
712         {
713             RtlCopyMemory(PropertyData->Buffer, Buffer, BufferSize);
714             PropertyData->BufferSize = BufferSize;
715         }
716         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
717         {
718             Status = _SEH2_GetExceptionCode();
719         }
720         _SEH2_END;
721     }
722 
723     ExFreePool(Buffer);
724     return Status;
725 }
726 
727 
728 static NTSTATUS
IopGetRelatedDevice(PPLUGPLAY_CONTROL_RELATED_DEVICE_DATA RelatedDeviceData)729 IopGetRelatedDevice(PPLUGPLAY_CONTROL_RELATED_DEVICE_DATA RelatedDeviceData)
730 {
731     UNICODE_STRING RootDeviceName;
732     PDEVICE_OBJECT DeviceObject = NULL;
733     PDEVICE_NODE DeviceNode = NULL;
734     PDEVICE_NODE RelatedDeviceNode;
735     UNICODE_STRING TargetDeviceInstance;
736     NTSTATUS Status = STATUS_SUCCESS;
737     ULONG Relation = 0;
738     ULONG MaximumLength = 0;
739 
740     DPRINT("IopGetRelatedDevice() called\n");
741     DPRINT("Device name: %wZ\n", &RelatedDeviceData->TargetDeviceInstance);
742 
743     Status = IopCaptureUnicodeString(&TargetDeviceInstance, &RelatedDeviceData->TargetDeviceInstance);
744     if (!NT_SUCCESS(Status))
745     {
746         return Status;
747     }
748 
749     _SEH2_TRY
750     {
751         Relation = RelatedDeviceData->Relation;
752         MaximumLength = RelatedDeviceData->RelatedDeviceInstanceLength;
753         ProbeForWrite(RelatedDeviceData->RelatedDeviceInstance,
754                       MaximumLength,
755                       sizeof(WCHAR));
756     }
757     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
758     {
759         if (TargetDeviceInstance.Buffer != NULL)
760         {
761             ExFreePool(TargetDeviceInstance.Buffer);
762         }
763         _SEH2_YIELD(return _SEH2_GetExceptionCode());
764     }
765     _SEH2_END;
766 
767     RtlInitUnicodeString(&RootDeviceName,
768                          L"HTREE\\ROOT\\0");
769     if (RtlEqualUnicodeString(&TargetDeviceInstance,
770                               &RootDeviceName,
771                               TRUE))
772     {
773         DeviceNode = IopRootDeviceNode;
774         if (TargetDeviceInstance.Buffer != NULL)
775         {
776             ExFreePool(TargetDeviceInstance.Buffer);
777         }
778     }
779     else
780     {
781         /* Get the device object */
782         DeviceObject = IopGetDeviceObjectFromDeviceInstance(&TargetDeviceInstance);
783         if (TargetDeviceInstance.Buffer != NULL)
784         {
785             ExFreePool(TargetDeviceInstance.Buffer);
786         }
787         if (DeviceObject == NULL)
788             return STATUS_NO_SUCH_DEVICE;
789 
790         DeviceNode = ((PEXTENDED_DEVOBJ_EXTENSION)DeviceObject->DeviceObjectExtension)->DeviceNode;
791     }
792 
793     switch (Relation)
794     {
795         case PNP_GET_PARENT_DEVICE:
796             RelatedDeviceNode = DeviceNode->Parent;
797             break;
798 
799         case PNP_GET_CHILD_DEVICE:
800             RelatedDeviceNode = DeviceNode->Child;
801             break;
802 
803         case PNP_GET_SIBLING_DEVICE:
804             RelatedDeviceNode = DeviceNode->Sibling;
805             break;
806 
807         default:
808             if (DeviceObject != NULL)
809             {
810                 ObDereferenceObject(DeviceObject);
811             }
812 
813             return STATUS_INVALID_PARAMETER;
814     }
815 
816     if (RelatedDeviceNode == NULL)
817     {
818         if (DeviceObject)
819         {
820             ObDereferenceObject(DeviceObject);
821         }
822 
823         return STATUS_NO_SUCH_DEVICE;
824     }
825 
826     if (RelatedDeviceNode->InstancePath.Length > MaximumLength)
827     {
828         if (DeviceObject)
829         {
830             ObDereferenceObject(DeviceObject);
831         }
832 
833         return STATUS_BUFFER_TOO_SMALL;
834     }
835 
836     /* Copy related device instance name */
837     _SEH2_TRY
838     {
839         RtlCopyMemory(RelatedDeviceData->RelatedDeviceInstance,
840                       RelatedDeviceNode->InstancePath.Buffer,
841                       RelatedDeviceNode->InstancePath.Length);
842         RelatedDeviceData->RelatedDeviceInstanceLength = RelatedDeviceNode->InstancePath.Length;
843     }
844     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
845     {
846         Status = _SEH2_GetExceptionCode();
847     }
848     _SEH2_END;
849 
850     if (DeviceObject != NULL)
851     {
852         ObDereferenceObject(DeviceObject);
853     }
854 
855     DPRINT("IopGetRelatedDevice() done\n");
856 
857     return Status;
858 }
859 
860 static
861 BOOLEAN
PiIsDevNodeStarted(_In_ PDEVICE_NODE DeviceNode)862 PiIsDevNodeStarted(
863     _In_ PDEVICE_NODE DeviceNode)
864 {
865     return (DeviceNode->State == DeviceNodeStartPending ||
866             DeviceNode->State == DeviceNodeStartCompletion ||
867             DeviceNode->State == DeviceNodeStartPostWork ||
868             DeviceNode->State == DeviceNodeStarted ||
869             DeviceNode->State == DeviceNodeQueryStopped ||
870             DeviceNode->State == DeviceNodeEnumeratePending ||
871             DeviceNode->State == DeviceNodeEnumerateCompletion ||
872             DeviceNode->State == DeviceNodeStopped ||
873             DeviceNode->State == DeviceNodeRestartCompletion);
874 }
875 
876 static ULONG
IopGetDeviceNodeStatus(PDEVICE_NODE DeviceNode)877 IopGetDeviceNodeStatus(PDEVICE_NODE DeviceNode)
878 {
879     ULONG Output = DN_NT_ENUMERATOR | DN_NT_DRIVER;
880 
881     if (DeviceNode->Parent == IopRootDeviceNode)
882         Output |= DN_ROOT_ENUMERATED;
883 
884     // FIXME: review for deleted and removed states
885     if (DeviceNode->State >= DeviceNodeDriversAdded)
886         Output |= DN_DRIVER_LOADED;
887 
888     if (PiIsDevNodeStarted(DeviceNode))
889         Output |= DN_STARTED;
890 
891     if (DeviceNode->UserFlags & DNUF_WILL_BE_REMOVED)
892         Output |= DN_WILL_BE_REMOVED;
893 
894     if (DeviceNode->Flags & DNF_HAS_PROBLEM)
895         Output |= DN_HAS_PROBLEM;
896 
897     if (DeviceNode->Flags & DNF_HAS_PRIVATE_PROBLEM)
898         Output |= DN_PRIVATE_PROBLEM;
899 
900     if (DeviceNode->Flags & DNF_DRIVER_BLOCKED)
901         Output |= DN_DRIVER_BLOCKED;
902 
903     if (DeviceNode->Flags & DNF_CHILD_WITH_INVALID_ID)
904         Output |= DN_CHILD_WITH_INVALID_ID;
905 
906     if (DeviceNode->Flags & DNF_HAS_PRIVATE_PROBLEM)
907         Output |= DN_PRIVATE_PROBLEM;
908 
909     if (DeviceNode->Flags & DNF_LEGACY_DRIVER)
910         Output |= DN_LEGACY_DRIVER;
911 
912     if (DeviceNode->UserFlags & DNUF_DONT_SHOW_IN_UI)
913         Output |= DN_NO_SHOW_IN_DM;
914 
915     if (!(DeviceNode->UserFlags & DNUF_NOT_DISABLEABLE))
916         Output |= DN_DISABLEABLE;
917 
918     return Output;
919 }
920 
921 static NTSTATUS
IopDeviceStatus(PPLUGPLAY_CONTROL_STATUS_DATA StatusData)922 IopDeviceStatus(PPLUGPLAY_CONTROL_STATUS_DATA StatusData)
923 {
924     PDEVICE_OBJECT DeviceObject;
925     PDEVICE_NODE DeviceNode;
926     ULONG Operation = 0;
927     ULONG DeviceStatus = 0;
928     ULONG DeviceProblem = 0;
929     UNICODE_STRING DeviceInstance;
930     NTSTATUS Status;
931 
932     DPRINT("IopDeviceStatus() called\n");
933 
934     Status = IopCaptureUnicodeString(&DeviceInstance, &StatusData->DeviceInstance);
935     if (!NT_SUCCESS(Status))
936     {
937         return Status;
938     }
939 
940     DPRINT("Device name: '%wZ'\n", &DeviceInstance);
941 
942     _SEH2_TRY
943     {
944         Operation = StatusData->Operation;
945         if (Operation == PNP_SET_DEVICE_STATUS)
946         {
947             DeviceStatus = StatusData->DeviceStatus;
948             DeviceProblem = StatusData->DeviceProblem;
949         }
950     }
951     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
952     {
953         if (DeviceInstance.Buffer != NULL)
954         {
955             ExFreePool(DeviceInstance.Buffer);
956         }
957         _SEH2_YIELD(return _SEH2_GetExceptionCode());
958     }
959     _SEH2_END;
960 
961     /* Get the device object */
962     DeviceObject = IopGetDeviceObjectFromDeviceInstance(&DeviceInstance);
963     if (DeviceInstance.Buffer != NULL)
964     {
965         ExFreePool(DeviceInstance.Buffer);
966     }
967     if (DeviceObject == NULL)
968     {
969         return STATUS_NO_SUCH_DEVICE;
970     }
971 
972     DeviceNode = IopGetDeviceNode(DeviceObject);
973 
974     switch (Operation)
975     {
976         case PNP_GET_DEVICE_STATUS:
977             DPRINT("Get status data\n");
978             DeviceStatus = IopGetDeviceNodeStatus(DeviceNode);
979             DeviceProblem = DeviceNode->Problem;
980             break;
981 
982         case PNP_SET_DEVICE_STATUS:
983             DPRINT1("Set status data is NOT SUPPORTED\n");
984             break;
985 
986         case PNP_CLEAR_DEVICE_STATUS:
987             DPRINT1("FIXME: Clear status data!\n");
988             break;
989     }
990 
991     ObDereferenceObject(DeviceObject);
992 
993     if (Operation == PNP_GET_DEVICE_STATUS)
994     {
995         _SEH2_TRY
996         {
997             StatusData->DeviceStatus = DeviceStatus;
998             StatusData->DeviceProblem = DeviceProblem;
999         }
1000         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1001         {
1002             Status = _SEH2_GetExceptionCode();
1003         }
1004         _SEH2_END;
1005     }
1006 
1007     return Status;
1008 }
1009 
1010 static
1011 NTSTATUS
IopGetDeviceRelations(PPLUGPLAY_CONTROL_DEVICE_RELATIONS_DATA RelationsData)1012 IopGetDeviceRelations(PPLUGPLAY_CONTROL_DEVICE_RELATIONS_DATA RelationsData)
1013 {
1014     UNICODE_STRING DeviceInstance;
1015     PDEVICE_OBJECT DeviceObject = NULL;
1016     IO_STACK_LOCATION Stack;
1017     IO_STATUS_BLOCK IoStatusBlock;
1018     PDEVICE_RELATIONS DeviceRelations = NULL;
1019     PDEVICE_OBJECT ChildDeviceObject;
1020     PDEVICE_NODE ChildDeviceNode;
1021     ULONG i;
1022     ULONG Relations;
1023     ULONG BufferSize, RequiredSize;
1024     ULONG BufferLeft;
1025     PWCHAR Buffer, Ptr;
1026     NTSTATUS Status = STATUS_SUCCESS;
1027 
1028     DPRINT("IopGetDeviceRelations() called\n");
1029     DPRINT("Device name: %wZ\n", &RelationsData->DeviceInstance);
1030     DPRINT("Relations: %lu\n", RelationsData->Relations);
1031     DPRINT("BufferSize: %lu\n", RelationsData->BufferSize);
1032     DPRINT("Buffer: %p\n", RelationsData->Buffer);
1033 
1034     _SEH2_TRY
1035     {
1036         Relations = RelationsData->Relations;
1037         BufferSize = RelationsData->BufferSize;
1038         Buffer = RelationsData->Buffer;
1039 
1040         ProbeForWrite(Buffer, BufferSize, sizeof(CHAR));
1041     }
1042     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1043     {
1044         _SEH2_YIELD(return _SEH2_GetExceptionCode());
1045     }
1046     _SEH2_END;
1047 
1048     Status = IopCaptureUnicodeString(&DeviceInstance, &RelationsData->DeviceInstance);
1049     if (!NT_SUCCESS(Status))
1050     {
1051         DPRINT1("IopCaptureUnicodeString() failed (Status 0x%08lx)\n", Status);
1052         return Status;
1053     }
1054 
1055     /* Get the device object */
1056     DeviceObject = IopGetDeviceObjectFromDeviceInstance(&DeviceInstance);
1057     if (DeviceObject == NULL)
1058     {
1059         DPRINT1("IopGetDeviceObjectFromDeviceInstance() returned NULL\n");
1060         Status = STATUS_NO_SUCH_DEVICE;
1061         goto done;
1062     }
1063 
1064     switch (Relations)
1065     {
1066         case PNP_EJECT_RELATIONS:
1067             Stack.Parameters.QueryDeviceRelations.Type = EjectionRelations;
1068             break;
1069 
1070         case PNP_REMOVAL_RELATIONS:
1071             Stack.Parameters.QueryDeviceRelations.Type = RemovalRelations;
1072             break;
1073 
1074         case PNP_POWER_RELATIONS:
1075             Stack.Parameters.QueryDeviceRelations.Type = PowerRelations;
1076             break;
1077 
1078         case PNP_BUS_RELATIONS:
1079             Stack.Parameters.QueryDeviceRelations.Type = BusRelations;
1080             break;
1081 
1082         default:
1083             Status = STATUS_INVALID_PARAMETER;
1084             goto done;
1085     }
1086 
1087     Status = IopInitiatePnpIrp(DeviceObject,
1088                                &IoStatusBlock,
1089                                IRP_MN_QUERY_DEVICE_RELATIONS,
1090                                &Stack);
1091     if (!NT_SUCCESS(Status))
1092     {
1093         DPRINT1("IopInitiatePnpIrp() failed (Status 0x%08lx)\n", Status);
1094         goto done;
1095     }
1096 
1097     DeviceRelations = (PDEVICE_RELATIONS)IoStatusBlock.Information;
1098 
1099     DPRINT("Found %d device relations\n", DeviceRelations->Count);
1100 
1101     _SEH2_TRY
1102     {
1103         RequiredSize = 0;
1104         BufferLeft = BufferSize;
1105         Ptr = Buffer;
1106 
1107         for (i = 0; i < DeviceRelations->Count; i++)
1108         {
1109             ChildDeviceObject = DeviceRelations->Objects[i];
1110 
1111             ChildDeviceNode = IopGetDeviceNode(ChildDeviceObject);
1112             if (ChildDeviceNode)
1113             {
1114                 DPRINT("Device instance: %wZ\n", &ChildDeviceNode->InstancePath);
1115                 DPRINT("RequiredSize: %hu\n", ChildDeviceNode->InstancePath.Length + sizeof(WCHAR));
1116 
1117                 if (Ptr != NULL)
1118                 {
1119                     if (BufferLeft < ChildDeviceNode->InstancePath.Length + 2 * sizeof(WCHAR))
1120                     {
1121                         Status = STATUS_BUFFER_TOO_SMALL;
1122                         break;
1123                     }
1124 
1125                     RtlCopyMemory(Ptr,
1126                                   ChildDeviceNode->InstancePath.Buffer,
1127                                   ChildDeviceNode->InstancePath.Length);
1128                     Ptr = (PWCHAR)((ULONG_PTR)Ptr + ChildDeviceNode->InstancePath.Length);
1129                     *Ptr = UNICODE_NULL;
1130                     Ptr = (PWCHAR)((ULONG_PTR)Ptr + sizeof(WCHAR));
1131 
1132                     BufferLeft -= (ChildDeviceNode->InstancePath.Length + sizeof(WCHAR));
1133                 }
1134 
1135                 RequiredSize += (ChildDeviceNode->InstancePath.Length + sizeof(WCHAR));
1136             }
1137         }
1138 
1139         if (Ptr != NULL && BufferLeft >= sizeof(WCHAR))
1140             *Ptr = UNICODE_NULL;
1141 
1142         if (RequiredSize > 0)
1143             RequiredSize += sizeof(WCHAR);
1144 
1145         DPRINT("BufferSize: %lu  RequiredSize: %lu\n", RelationsData->BufferSize, RequiredSize);
1146 
1147         RelationsData->BufferSize = RequiredSize;
1148     }
1149     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1150     {
1151         Status = _SEH2_GetExceptionCode();
1152     }
1153     _SEH2_END;
1154 
1155 done:
1156     if (DeviceRelations != NULL)
1157         ExFreePool(DeviceRelations);
1158 
1159     if (DeviceObject != NULL)
1160         ObDereferenceObject(DeviceObject);
1161 
1162     if (DeviceInstance.Buffer != NULL)
1163         ExFreePool(DeviceInstance.Buffer);
1164 
1165     return Status;
1166 }
1167 
1168 static NTSTATUS
IopGetDeviceDepth(PPLUGPLAY_CONTROL_DEPTH_DATA DepthData)1169 IopGetDeviceDepth(PPLUGPLAY_CONTROL_DEPTH_DATA DepthData)
1170 {
1171     PDEVICE_OBJECT DeviceObject;
1172     PDEVICE_NODE DeviceNode;
1173     UNICODE_STRING DeviceInstance;
1174     NTSTATUS Status = STATUS_SUCCESS;
1175 
1176     DPRINT("IopGetDeviceDepth() called\n");
1177     DPRINT("Device name: %wZ\n", &DepthData->DeviceInstance);
1178 
1179     Status = IopCaptureUnicodeString(&DeviceInstance, &DepthData->DeviceInstance);
1180     if (!NT_SUCCESS(Status))
1181     {
1182         return Status;
1183     }
1184 
1185     /* Get the device object */
1186     DeviceObject = IopGetDeviceObjectFromDeviceInstance(&DeviceInstance);
1187     if (DeviceInstance.Buffer != NULL)
1188     {
1189         ExFreePool(DeviceInstance.Buffer);
1190     }
1191     if (DeviceObject == NULL)
1192     {
1193         return STATUS_NO_SUCH_DEVICE;
1194     }
1195 
1196     DeviceNode = IopGetDeviceNode(DeviceObject);
1197 
1198     _SEH2_TRY
1199     {
1200         DepthData->Depth = DeviceNode->Level;
1201     }
1202     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1203     {
1204         Status = _SEH2_GetExceptionCode();
1205     }
1206     _SEH2_END;
1207 
1208     ObDereferenceObject(DeviceObject);
1209 
1210     return Status;
1211 }
1212 
1213 static
1214 NTSTATUS
PiControlSyncDeviceAction(_In_ PPLUGPLAY_CONTROL_DEVICE_CONTROL_DATA DeviceData,_In_ PLUGPLAY_CONTROL_CLASS ControlClass)1215 PiControlSyncDeviceAction(
1216     _In_ PPLUGPLAY_CONTROL_DEVICE_CONTROL_DATA DeviceData,
1217     _In_ PLUGPLAY_CONTROL_CLASS ControlClass)
1218 {
1219     PDEVICE_OBJECT DeviceObject;
1220     NTSTATUS Status;
1221     UNICODE_STRING DeviceInstance;
1222 
1223     ASSERT(ControlClass == PlugPlayControlEnumerateDevice ||
1224            ControlClass == PlugPlayControlStartDevice ||
1225            ControlClass == PlugPlayControlResetDevice);
1226 
1227     Status = IopCaptureUnicodeString(&DeviceInstance, &DeviceData->DeviceInstance);
1228     if (!NT_SUCCESS(Status))
1229     {
1230         return Status;
1231     }
1232 
1233     DeviceObject = IopGetDeviceObjectFromDeviceInstance(&DeviceInstance);
1234     if (DeviceInstance.Buffer != NULL)
1235     {
1236         ExFreePool(DeviceInstance.Buffer);
1237     }
1238     if (DeviceObject == NULL)
1239     {
1240         return STATUS_NO_SUCH_DEVICE;
1241     }
1242 
1243     DEVICE_ACTION Action;
1244 
1245     switch (ControlClass)
1246     {
1247         case PlugPlayControlEnumerateDevice:
1248             Action = PiActionEnumDeviceTree;
1249             break;
1250         case PlugPlayControlStartDevice:
1251             Action = PiActionStartDevice;
1252             break;
1253         case PlugPlayControlResetDevice:
1254             Action = PiActionResetDevice;
1255             break;
1256         default:
1257             UNREACHABLE;
1258             break;
1259     }
1260 
1261     Status = PiPerformSyncDeviceAction(DeviceObject, Action);
1262 
1263     ObDereferenceObject(DeviceObject);
1264 
1265     return Status;
1266 }
1267 
1268 static
1269 NTSTATUS
PiControlQueryRemoveDevice(_In_ PPLUGPLAY_CONTROL_QUERY_REMOVE_DATA ControlData)1270 PiControlQueryRemoveDevice(
1271     _In_ PPLUGPLAY_CONTROL_QUERY_REMOVE_DATA ControlData)
1272 {
1273     PDEVICE_OBJECT DeviceObject;
1274     NTSTATUS Status;
1275     UNICODE_STRING DeviceInstance;
1276 
1277     Status = IopCaptureUnicodeString(&DeviceInstance, &ControlData->DeviceInstance);
1278     if (!NT_SUCCESS(Status))
1279     {
1280         return Status;
1281     }
1282 
1283     DeviceObject = IopGetDeviceObjectFromDeviceInstance(&DeviceInstance);
1284     if (DeviceInstance.Buffer != NULL)
1285     {
1286         ExFreePool(DeviceInstance.Buffer);
1287     }
1288     if (DeviceObject == NULL)
1289     {
1290         return STATUS_NO_SUCH_DEVICE;
1291     }
1292 
1293     UNIMPLEMENTED;
1294     Status = STATUS_NOT_IMPLEMENTED;
1295 
1296     ObDereferenceObject(DeviceObject);
1297 
1298     return Status;
1299 }
1300 
1301 /* PUBLIC FUNCTIONS **********************************************************/
1302 
1303 /*
1304  * Plug and Play event structure used by NtGetPlugPlayEvent.
1305  *
1306  * EventGuid
1307  *    Can be one of the following values:
1308  *       GUID_HWPROFILE_QUERY_CHANGE
1309  *       GUID_HWPROFILE_CHANGE_CANCELLED
1310  *       GUID_HWPROFILE_CHANGE_COMPLETE
1311  *       GUID_TARGET_DEVICE_QUERY_REMOVE
1312  *       GUID_TARGET_DEVICE_REMOVE_CANCELLED
1313  *       GUID_TARGET_DEVICE_REMOVE_COMPLETE
1314  *       GUID_PNP_CUSTOM_NOTIFICATION
1315  *       GUID_PNP_POWER_NOTIFICATION
1316  *       GUID_DEVICE_* (see above)
1317  *
1318  * EventCategory
1319  *    Type of the event that happened.
1320  *
1321  * Result
1322  *    ?
1323  *
1324  * Flags
1325  *    ?
1326  *
1327  * TotalSize
1328  *    Size of the event block including the device IDs and other
1329  *    per category specific fields.
1330  */
1331 
1332 /*
1333  * NtGetPlugPlayEvent
1334  *
1335  * Returns one Plug & Play event from a global queue.
1336  *
1337  * Parameters
1338  *    Reserved1
1339  *    Reserved2
1340  *       Always set to zero.
1341  *
1342  *    Buffer
1343  *       The buffer that will be filled with the event information on
1344  *       successful return from the function.
1345  *
1346  *    BufferSize
1347  *       Size of the buffer pointed by the Buffer parameter. If the
1348  *       buffer size is not large enough to hold the whole event
1349  *       information, error STATUS_BUFFER_TOO_SMALL is returned and
1350  *       the buffer remains untouched.
1351  *
1352  * Return Values
1353  *    STATUS_PRIVILEGE_NOT_HELD
1354  *    STATUS_BUFFER_TOO_SMALL
1355  *    STATUS_SUCCESS
1356  *
1357  * Remarks
1358  *    This function isn't multi-thread safe!
1359  *
1360  * @implemented
1361  */
1362 NTSTATUS
1363 NTAPI
NtGetPlugPlayEvent(IN ULONG Reserved1,IN ULONG Reserved2,OUT PPLUGPLAY_EVENT_BLOCK Buffer,IN ULONG BufferSize)1364 NtGetPlugPlayEvent(IN ULONG Reserved1,
1365                    IN ULONG Reserved2,
1366                    OUT PPLUGPLAY_EVENT_BLOCK Buffer,
1367                    IN ULONG BufferSize)
1368 {
1369     PPNP_EVENT_ENTRY Entry;
1370     NTSTATUS Status;
1371 
1372     DPRINT("NtGetPlugPlayEvent() called\n");
1373 
1374     /* Function can only be called from user-mode */
1375     if (KeGetPreviousMode() == KernelMode)
1376     {
1377         DPRINT1("NtGetPlugPlayEvent cannot be called from kernel mode!\n");
1378         return STATUS_ACCESS_DENIED;
1379     }
1380 
1381     /* Check for Tcb privilege */
1382     if (!SeSinglePrivilegeCheck(SeTcbPrivilege,
1383                                 UserMode))
1384     {
1385         DPRINT1("NtGetPlugPlayEvent: Caller does not hold the SeTcbPrivilege privilege!\n");
1386         return STATUS_PRIVILEGE_NOT_HELD;
1387     }
1388 
1389     /* Wait for a PnP event */
1390     DPRINT("Waiting for pnp notification event\n");
1391     Status = KeWaitForSingleObject(&IopPnpNotifyEvent,
1392                                    UserRequest,
1393                                    UserMode,
1394                                    FALSE,
1395                                    NULL);
1396     if (!NT_SUCCESS(Status) || Status == STATUS_USER_APC)
1397     {
1398         DPRINT("KeWaitForSingleObject() failed (Status %lx)\n", Status);
1399         ASSERT(Status == STATUS_USER_APC);
1400         return Status;
1401     }
1402 
1403     /* Get entry from the tail of the queue */
1404     Entry = CONTAINING_RECORD(IopPnpEventQueueHead.Blink,
1405                               PNP_EVENT_ENTRY,
1406                               ListEntry);
1407 
1408     /* Check the buffer size */
1409     if (BufferSize < Entry->Event.TotalSize)
1410     {
1411         DPRINT1("Buffer is too small for the pnp-event\n");
1412         return STATUS_BUFFER_TOO_SMALL;
1413     }
1414 
1415     /* Copy event data to the user buffer */
1416     _SEH2_TRY
1417     {
1418         ProbeForWrite(Buffer,
1419                       Entry->Event.TotalSize,
1420                       sizeof(UCHAR));
1421         RtlCopyMemory(Buffer,
1422                       &Entry->Event,
1423                       Entry->Event.TotalSize);
1424     }
1425     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1426     {
1427         _SEH2_YIELD(return _SEH2_GetExceptionCode());
1428     }
1429     _SEH2_END;
1430 
1431     DPRINT("NtGetPlugPlayEvent() done\n");
1432 
1433     return STATUS_SUCCESS;
1434 }
1435 
1436 /*
1437  * NtPlugPlayControl
1438  *
1439  * A function for doing various Plug & Play operations from user mode.
1440  *
1441  * Parameters
1442  *    PlugPlayControlClass
1443  *       0x00   Reenumerate device tree
1444  *
1445  *              Buffer points to UNICODE_STRING decribing the instance
1446  *              path (like "HTREE\ROOT\0" or "Root\ACPI_HAL\0000"). For
1447  *              more information about instance paths see !devnode command
1448  *              in kernel debugger or look at "Inside Windows 2000" book,
1449  *              chapter "Driver Loading, Initialization, and Installation".
1450  *
1451  *       0x01   Register new device
1452  *       0x02   Deregister device
1453  *       0x03   Initialize device
1454  *       0x04   Start device
1455  *       0x06   Query and remove device
1456  *       0x07   User response
1457  *
1458  *              Called after processing the message from NtGetPlugPlayEvent.
1459  *
1460  *       0x08   Generate legacy device
1461  *       0x09   Get interface device list
1462  *       0x0A   Get property data
1463  *       0x0B   Device class association (Registration)
1464  *       0x0C   Get related device
1465  *       0x0D   Get device interface alias
1466  *       0x0E   Get/set/clear device status
1467  *       0x0F   Get device depth
1468  *       0x10   Query device relations
1469  *       0x11   Query target device relation
1470  *       0x12   Query conflict list
1471  *       0x13   Retrieve dock data
1472  *       0x14   Reset device
1473  *       0x15   Halt device
1474  *       0x16   Get blocked driver data
1475  *
1476  *    Buffer
1477  *       The buffer contains information that is specific to each control
1478  *       code. The buffer is read-only.
1479  *
1480  *    BufferSize
1481  *       Size of the buffer pointed by the Buffer parameter. If the
1482  *       buffer size specifies incorrect value for specified control
1483  *       code, error ??? is returned.
1484  *
1485  * Return Values
1486  *    STATUS_PRIVILEGE_NOT_HELD
1487  *    STATUS_SUCCESS
1488  *    ...
1489  *
1490  * @unimplemented
1491  */
1492 NTSTATUS
1493 NTAPI
NtPlugPlayControl(IN PLUGPLAY_CONTROL_CLASS PlugPlayControlClass,IN OUT PVOID Buffer,IN ULONG BufferLength)1494 NtPlugPlayControl(IN PLUGPLAY_CONTROL_CLASS PlugPlayControlClass,
1495                   IN OUT PVOID Buffer,
1496                   IN ULONG BufferLength)
1497 {
1498     DPRINT("NtPlugPlayControl(%d %p %lu) called\n",
1499            PlugPlayControlClass, Buffer, BufferLength);
1500 
1501     /* Function can only be called from user-mode */
1502     if (KeGetPreviousMode() == KernelMode)
1503     {
1504         DPRINT1("NtGetPlugPlayEvent cannot be called from kernel mode!\n");
1505         return STATUS_ACCESS_DENIED;
1506     }
1507 
1508     /* Check for Tcb privilege */
1509     if (!SeSinglePrivilegeCheck(SeTcbPrivilege,
1510                                 UserMode))
1511     {
1512         DPRINT1("NtGetPlugPlayEvent: Caller does not hold the SeTcbPrivilege privilege!\n");
1513         return STATUS_PRIVILEGE_NOT_HELD;
1514     }
1515 
1516     /* Probe the buffer */
1517     _SEH2_TRY
1518     {
1519         ProbeForWrite(Buffer,
1520                       BufferLength,
1521                       sizeof(ULONG));
1522     }
1523     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1524     {
1525         _SEH2_YIELD(return _SEH2_GetExceptionCode());
1526     }
1527     _SEH2_END;
1528 
1529     switch (PlugPlayControlClass)
1530     {
1531         case PlugPlayControlEnumerateDevice:
1532             if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_ENUMERATE_DEVICE_DATA))
1533                 return STATUS_INVALID_PARAMETER;
1534             // the Flags field is not used anyway
1535             return PiControlSyncDeviceAction((PPLUGPLAY_CONTROL_DEVICE_CONTROL_DATA)Buffer,
1536                                              PlugPlayControlClass);
1537 
1538 //        case PlugPlayControlRegisterNewDevice:
1539 //        case PlugPlayControlDeregisterDevice:
1540 
1541         case PlugPlayControlInitializeDevice:
1542             if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA))
1543                 return STATUS_INVALID_PARAMETER;
1544             return PiControlInitializeDevice((PPLUGPLAY_CONTROL_DEVICE_CONTROL_DATA)Buffer);
1545 
1546         case PlugPlayControlStartDevice:
1547         case PlugPlayControlResetDevice:
1548             if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA))
1549                 return STATUS_INVALID_PARAMETER;
1550             return PiControlSyncDeviceAction((PPLUGPLAY_CONTROL_DEVICE_CONTROL_DATA)Buffer,
1551                                              PlugPlayControlClass);
1552 
1553 //        case PlugPlayControlUnlockDevice:
1554         case PlugPlayControlQueryAndRemoveDevice:
1555               if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_QUERY_REMOVE_DATA))
1556                   return STATUS_INVALID_PARAMETER;
1557               return PiControlQueryRemoveDevice((PPLUGPLAY_CONTROL_QUERY_REMOVE_DATA)Buffer);
1558 
1559         case PlugPlayControlUserResponse:
1560             if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_USER_RESPONSE_DATA))
1561                 return STATUS_INVALID_PARAMETER;
1562             return IopRemovePlugPlayEvent((PPLUGPLAY_CONTROL_USER_RESPONSE_DATA)Buffer);
1563 
1564 //        case PlugPlayControlGenerateLegacyDevice:
1565 
1566         case PlugPlayControlGetInterfaceDeviceList:
1567             if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_INTERFACE_DEVICE_LIST_DATA))
1568                 return STATUS_INVALID_PARAMETER;
1569             return IopGetInterfaceDeviceList((PPLUGPLAY_CONTROL_INTERFACE_DEVICE_LIST_DATA)Buffer);
1570 
1571         case PlugPlayControlProperty:
1572             if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_PROPERTY_DATA))
1573                 return STATUS_INVALID_PARAMETER;
1574             return IopGetDeviceProperty((PPLUGPLAY_CONTROL_PROPERTY_DATA)Buffer);
1575 
1576 //        case PlugPlayControlDeviceClassAssociation:
1577 
1578         case PlugPlayControlGetRelatedDevice:
1579             if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_RELATED_DEVICE_DATA))
1580                 return STATUS_INVALID_PARAMETER;
1581             return IopGetRelatedDevice((PPLUGPLAY_CONTROL_RELATED_DEVICE_DATA)Buffer);
1582 
1583 //        case PlugPlayControlGetInterfaceDeviceAlias:
1584 
1585         case PlugPlayControlDeviceStatus:
1586             if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_STATUS_DATA))
1587                 return STATUS_INVALID_PARAMETER;
1588             return IopDeviceStatus((PPLUGPLAY_CONTROL_STATUS_DATA)Buffer);
1589 
1590         case PlugPlayControlGetDeviceDepth:
1591             if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_DEPTH_DATA))
1592                 return STATUS_INVALID_PARAMETER;
1593             return IopGetDeviceDepth((PPLUGPLAY_CONTROL_DEPTH_DATA)Buffer);
1594 
1595         case PlugPlayControlQueryDeviceRelations:
1596             if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_DEVICE_RELATIONS_DATA))
1597                 return STATUS_INVALID_PARAMETER;
1598             return IopGetDeviceRelations((PPLUGPLAY_CONTROL_DEVICE_RELATIONS_DATA)Buffer);
1599 
1600 //        case PlugPlayControlTargetDeviceRelation:
1601 //        case PlugPlayControlQueryConflictList:
1602 //        case PlugPlayControlRetrieveDock:
1603 //        case PlugPlayControlHaltDevice:
1604 //        case PlugPlayControlGetBlockedDriverList:
1605 
1606         default:
1607             return STATUS_NOT_IMPLEMENTED;
1608     }
1609 
1610     return STATUS_NOT_IMPLEMENTED;
1611 }
1612