1 /*++
2
3 Copyright (c) Microsoft Corporation
4
5 Module Name:
6
7 FxWmiIrpHandler.cpp
8
9 Abstract:
10
11 This module implements the wmi irp handler for the driver frameworks.
12
13 Author:
14
15
16
17 Environment:
18
19 Kernel mode only
20
21 Revision History:
22
23 --*/
24
25 #include "fxwmipch.hpp"
26
27 extern "C" {
28 // #include "FxWmiIrpHandler.tmh"
29 }
30
31 #ifndef WppDebug
32 #define WppDebug(a, b)
33 #endif
34
35 const FxWmiMinorEntry FxWmiIrpHandler::m_WmiDispatchTable[] =
36 {
37 // IRP_MN_QUERY_ALL_DATA
38 { _QueryAllData, FALSE },
39
40 // IRP_MN_QUERY_SINGLE_INSTANCE
41 { _QuerySingleInstance, TRUE },
42
43 // IRP_MN_CHANGE_SINGLE_INSTANCE
44 { _ChangeSingleInstance, TRUE },
45
46 // IRP_MN_CHANGE_SINGLE_ITEM
47 { _ChangeSingleItem, TRUE },
48
49 // IRP_MN_ENABLE_EVENTS
50 { _EnableDisableEventsAndCollection, FALSE },
51
52 // IRP_MN_DISABLE_EVENTS
53 { _EnableDisableEventsAndCollection, FALSE },
54
55 // IRP_MN_ENABLE_COLLECTION
56 { _EnableDisableEventsAndCollection, FALSE },
57
58 // IRP_MN_DISABLE_COLLECTION
59 { _EnableDisableEventsAndCollection, FALSE },
60
61 // IRP_MN_REGINFO
62 { _RegInfo, FALSE },
63
64 // IRP_MN_EXECUTE_METHOD
65 { _ExecuteMethod, TRUE },
66
67 // 0xA is reseverved
68 { NULL, FALSE },
69
70 // IRP_MN_REGINFO_EX
71 { _RegInfo, FALSE },
72 };
73
74 VOID
CheckAssumptions(VOID)75 FxWmiIrpHandler::CheckAssumptions(
76 VOID
77 )
78 {
79 WDFCASSERT(sizeof(m_WmiDispatchTable)/sizeof(m_WmiDispatchTable[0]) ==
80 IRP_MN_REGINFO_EX + 1);
81 }
82
FxWmiIrpHandler(__in PFX_DRIVER_GLOBALS FxDriverGlobals,__in FxDevice * Device,__in WDFTYPE Type)83 FxWmiIrpHandler::FxWmiIrpHandler(
84 __in PFX_DRIVER_GLOBALS FxDriverGlobals,
85 __in FxDevice *Device,
86 __in WDFTYPE Type
87 ) :
88 FxPackage(FxDriverGlobals, Device, Type),
89 m_NumProviders(0), m_RegisteredState(WmiUnregistered),
90 m_WorkItem(NULL), m_WorkItemEvent(NULL), m_WorkItemQueued(FALSE),
91 m_UpdateCount(1) // bias m_UpdateCount to 1, Deregister routine will
92 // decrement this.
93 {
94 InitializeListHead(&m_ProvidersListHead);
95 }
96
~FxWmiIrpHandler()97 FxWmiIrpHandler::~FxWmiIrpHandler()
98 {
99 //
100 // If the device could not get past AddDevice or failed the initial start
101 // device, we will be unregistered. Otherwise we should be cleaned up.
102 //
103 ASSERT(m_RegisteredState != WmiRegistered);
104
105 ASSERT(IsListEmpty(&m_ProvidersListHead));
106
107 if (m_WorkItem != NULL) {
108 IoFreeWorkItem(m_WorkItem);
109 }
110 }
111
112 _Must_inspect_result_
113 NTSTATUS
PostCreateDeviceInitialize(VOID)114 FxWmiIrpHandler::PostCreateDeviceInitialize(
115 VOID
116 )
117 {
118 m_WorkItem = IoAllocateWorkItem(GetDevice()->GetDeviceObject());
119 if (m_WorkItem == NULL) {
120 return STATUS_INSUFFICIENT_RESOURCES;
121 }
122
123 return STATUS_SUCCESS;
124 }
125
126 _Must_inspect_result_
127 NTSTATUS
Register(VOID)128 FxWmiIrpHandler::Register(
129 VOID
130 )
131 {
132 NTSTATUS status;
133 KIRQL irql;
134
135 //
136 // We rely on the PnP state machine to manage our state transitions properly
137 // so that we don't have to do any state checking here.
138 //
139 Lock(&irql);
140 ASSERT(m_RegisteredState == WmiUnregistered ||
141 m_RegisteredState == WmiDeregistered);
142 m_RegisteredState = WmiRegistered;
143 Unlock(irql);
144
145 status = IoWMIRegistrationControl(GetDevice()->GetDeviceObject(),
146 WMIREG_ACTION_REGISTER);
147
148 if (!NT_SUCCESS(status)) {
149 DoTraceLevelMessage(
150 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
151 "could not register WMI with OS, %!STATUS!", status);
152
153 Lock(&irql);
154 m_RegisteredState = WmiUnregistered;
155 Unlock(irql);
156 }
157
158 return status;
159 }
160
161 VOID
Deregister(VOID)162 FxWmiIrpHandler::Deregister(
163 VOID
164 )
165 {
166 FxCREvent event;
167 KIRQL irql;
168 BOOLEAN call;
169
170 call = FALSE;
171
172 Lock(&irql);
173 if (m_RegisteredState == WmiRegistered) {
174 m_RegisteredState = WmiDeregistered;
175
176 if (m_WorkItemQueued) {
177
178
179
180
181
182 m_WorkItemEvent = (PKEVENT)event.GetEvent();
183 }
184
185 call = TRUE;
186 }
187 Unlock(irql);
188
189 if (m_WorkItemEvent != NULL) {
190 event.EnterCRAndWaitAndLeave();
191 }
192
193 if (call) {
194 NTSTATUS status;
195
196 //
197 // Per WMI rules, there should not be any call to update WMI
198 // registration after we have deregistered because that can lead to
199 // deadlock in Pnp, so before we go ahead to deregister, let's ensure
200 // that. This will wait for any pending updates to happen.
201 //
202 DecrementUpdateCountAndWait();
203
204 status = IoWMIRegistrationControl(GetDevice()->GetDeviceObject(),
205 WMIREG_ACTION_DEREGISTER);
206
207 if (!NT_SUCCESS(status)) {
208 DoTraceLevelMessage(
209 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
210 "failure deregistering WMI with OS, %!STATUS!", status);
211 }
212 }
213 }
214
215 VOID
Cleanup(VOID)216 FxWmiIrpHandler::Cleanup(
217 VOID
218 )
219 {
220 KIRQL irql;
221
222 Lock(&irql);
223 m_RegisteredState = WmiCleanedUp;
224 Unlock(irql);
225 }
226
227 VOID
ResetStateForPdoRestart(VOID)228 FxWmiIrpHandler::ResetStateForPdoRestart(
229 VOID
230 )
231 {
232 KIRQL irql;
233
234 Lock(&irql);
235
236 //
237 // We can reach this state in 2 ways:
238 // 1. PDO went through Init->Started->Removed->Started transition
239 // 2. PDO went through Init-><SomeFailedState>->Started state (e.g.
240 // Init->Ejected->EjectFailed->Started) transition.
241 //
242 // So, WMI registration state would either be Deregistered due to #1
243 // or, Unregistered due to #2. Also, update count would be 0 in case of #1
244 // and 1 in case of #2.
245 //
246 ASSERT(m_RegisteredState == WmiUnregistered ||
247 m_RegisteredState == WmiDeregistered);
248 ASSERT(m_UpdateCount == 0 || m_UpdateCount == 1);
249
250 //
251 // Update count is biased to 1 when created so do the same here.
252 //
253 m_UpdateCount = 1;
254 m_UpdateEvent.Initialize();
255 Unlock(irql);
256 }
257
258 _Must_inspect_result_
259 NTSTATUS
AddProviderLocked(__in FxWmiProvider * Provider,__in KIRQL OldIrql,__out_opt PBOOLEAN Update)260 FxWmiIrpHandler::AddProviderLocked(
261 __in FxWmiProvider* Provider,
262 __in KIRQL OldIrql,
263 __out_opt PBOOLEAN Update
264 )
265 {
266 BOOLEAN update;
267
268 update = FALSE;
269
270 switch (m_RegisteredState) {
271 case WmiRegistered:
272 if (Provider->m_Flags & WdfWmiProviderTracing) {
273 update = TRUE;
274 }
275 case WmiUnregistered:
276 break;
277
278 case WmiDeregistered:
279 return STATUS_INVALID_DEVICE_STATE;
280 }
281
282 //
283 // Didn't find it in the list, add it
284 //
285 m_NumProviders++;
286 InsertTailList(&m_ProvidersListHead, &Provider->m_ListEntry);
287
288 if (update) {
289 update = DeferUpdateLocked(OldIrql);
290
291 if (Update != NULL) {
292 *Update = update;
293 }
294 }
295
296 return STATUS_SUCCESS;
297 }
298
299 BOOLEAN
DeferUpdateLocked(__in KIRQL OldIrql)300 FxWmiIrpHandler::DeferUpdateLocked(
301 __in KIRQL OldIrql
302 )
303 {
304 BOOLEAN checkQueue;
305
306 checkQueue = FALSE;
307
308 //
309 // Check to see if the caller is going to return to something > PASSIVE_LEVEL.
310 // If so, then always defer to the workitem.
311 //
312 if (OldIrql > PASSIVE_LEVEL) {
313 checkQueue = TRUE;
314 }
315 else {
316 //
317 // At passive level and updates are allowed, indicate to the caller to
318 // update. The caller will do registration update outside of lock but
319 // update count needs to be incremented under lock, so this is done here.
320 //
321 IncrementUpdateCount();
322 return TRUE;
323 }
324
325 if (checkQueue && m_WorkItemQueued == FALSE) {
326 //
327 // we are going to queue a workitem which will do registration update,
328 // so increment the update count. Note that one work item may correspond
329 // to multiple requests to update (since if the workitem is already
330 // queued, it's not queued again).
331 //
332 IncrementUpdateCount();
333
334 m_WorkItemQueued = TRUE;
335 IoQueueWorkItem(m_WorkItem,
336 _UpdateGuids,
337 DelayedWorkQueue,
338 this);
339 }
340
341 return FALSE;
342 }
343
344 VOID
_UpdateGuids(__in PDEVICE_OBJECT DeviceObject,__in PVOID Context)345 FxWmiIrpHandler::_UpdateGuids(
346 __in PDEVICE_OBJECT DeviceObject,
347 __in PVOID Context
348 )
349 {
350 FxWmiIrpHandler* pThis;
351 KIRQL irql;
352
353 UNREFERENCED_PARAMETER(DeviceObject);
354
355 pThis = (FxWmiIrpHandler*)Context;
356
357 pThis->UpdateGuids();
358
359 pThis->Lock(&irql);
360 pThis->m_WorkItemQueued = FALSE;
361
362 if (pThis->m_WorkItemEvent != NULL) {
363 KeSetEvent(pThis->m_WorkItemEvent, FALSE, IO_NO_INCREMENT);
364 }
365 pThis->Unlock(irql);
366 }
367
368 VOID
UpdateGuids(VOID)369 FxWmiIrpHandler::UpdateGuids(
370 VOID
371 )
372 {
373 NTSTATUS status;
374
375 status = IoWMIRegistrationControl(GetDevice()->GetDeviceObject(),
376 WMIREG_ACTION_UPDATE_GUIDS);
377
378 if (!NT_SUCCESS(status)) {
379 DoTraceLevelMessage(
380 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIO,
381 "IoWMIRegistrationControl DevObj %p, for UpdateGuids failed, %!STATUS!",
382 GetDevice()->GetDeviceObject(), status);
383
384 //
385 // Just drop the error
386 //
387 }
388
389 //
390 // The caller incremented the update count when it decided to do the update.
391 // Decrement the count now that we have completed registration update.
392 //
393 DecrementUpdateCount();
394 }
395
396 _Must_inspect_result_
397 NTSTATUS
AddProvider(__in FxWmiProvider * Provider,__out_opt PBOOLEAN Update)398 FxWmiIrpHandler::AddProvider(
399 __in FxWmiProvider* Provider,
400 __out_opt PBOOLEAN Update
401 )
402 {
403 NTSTATUS status;
404 KIRQL irql;
405
406 Lock(&irql);
407
408 if (IsListEmpty(&Provider->m_ListEntry) == FALSE ||
409 FindProviderLocked(Provider->GetGUID()) != NULL) {
410 status = STATUS_OBJECT_NAME_EXISTS;
411 }
412 else {
413 status = AddProviderLocked(Provider, irql, Update);
414 }
415 Unlock(irql);
416
417 return status;
418 }
419
420 _Must_inspect_result_
421 NTSTATUS
AddPowerPolicyProviderAndInstance(__in PWDF_WMI_PROVIDER_CONFIG ProviderConfig,__in FxWmiInstanceInternalCallbacks * InstanceCallbacks,__inout FxWmiInstanceInternal ** Instance)422 FxWmiIrpHandler::AddPowerPolicyProviderAndInstance(
423 __in PWDF_WMI_PROVIDER_CONFIG ProviderConfig,
424 __in FxWmiInstanceInternalCallbacks* InstanceCallbacks,
425 __inout FxWmiInstanceInternal** Instance
426 )
427 {
428 FxWmiProvider* pProvider;
429 FxWmiInstanceInternal* pInstance;
430 NTSTATUS status;
431 KIRQL irql;
432 BOOLEAN providerAllocated, providerAdded, update;
433
434 providerAllocated = FALSE;
435 providerAdded = FALSE;
436 update = FALSE;
437
438 pInstance = NULL;
439
440 status = STATUS_SUCCESS;
441
442 Lock(&irql);
443
444 pProvider = FindProviderLocked(&ProviderConfig->Guid);
445
446 if (pProvider == NULL) {
447 pProvider = new (GetDriverGlobals(), WDF_NO_OBJECT_ATTRIBUTES)
448 FxWmiProvider(GetDriverGlobals(), ProviderConfig, GetDevice());
449
450 if (pProvider == NULL) {
451 status = STATUS_INSUFFICIENT_RESOURCES;
452 goto Done;
453 }
454
455 providerAllocated = TRUE;
456
457 status = AddProviderLocked(pProvider, irql);
458 if (!NT_SUCCESS(status)) {
459 goto Done;
460 }
461
462 providerAdded = TRUE;
463
464 status = pProvider->AssignParentObject(GetDevice());
465 if (!NT_SUCCESS(status)) {
466 goto Done;
467 }
468 }
469 else if (pProvider->m_NumInstances > 0 &&
470 (FxIsEqualGuid(pProvider->GetGUID(), &GUID_POWER_DEVICE_ENABLE) ||
471 FxIsEqualGuid(pProvider->GetGUID(), &GUID_POWER_DEVICE_WAKE_ENABLE))) {
472
473 DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIO,
474 "WMI Guid already registered by client driver");
475 status = STATUS_WMI_GUID_DISCONNECTED;
476 }
477
478 if (NT_SUCCESS(status)) {
479 pInstance = new (GetDriverGlobals(), WDF_NO_OBJECT_ATTRIBUTES)
480 FxWmiInstanceInternal(GetDriverGlobals(), InstanceCallbacks, pProvider);
481
482 if (pInstance != NULL) {
483 status = pInstance->AssignParentObject(pProvider);
484 }
485 else {
486 status = STATUS_INSUFFICIENT_RESOURCES;
487 }
488
489 if (NT_SUCCESS(status) &&
490 InterlockedCompareExchangePointer((PVOID*) Instance,
491 pInstance,
492 NULL) != NULL) {
493 //
494 // Some other thread got here first and created an instance, just
495 // bail out. the caller will handle this specific return value
496 // gracefully.
497 //
498 status = STATUS_OBJECT_NAME_COLLISION;
499 }
500 else {
501 //
502 // We are the first one to set the value, good for us.
503 //
504 DO_NOTHING();
505 }
506 }
507
508 //
509 // Add the instance only if we are successful to this point
510 //
511 if (NT_SUCCESS(status)) {
512 //
513 // Passing FALSE indicates that this instance cannot be in the list
514 // when added.
515 //
516 status = pProvider->AddInstanceLocked(
517 pInstance,
518 FALSE,
519 &update,
520 FxWmiProvider::AddInstanceToHead
521 );
522 }
523
524 Done:
525 if (NT_SUCCESS(status)) {
526 if (update) {
527 update = DeferUpdateLocked(irql);
528 }
529 }
530 else if (providerAdded) {
531 RemoveProviderLocked(pProvider);
532 }
533
534 Unlock(irql);
535
536 if (NT_SUCCESS(status)) {
537 if (update) {
538 UpdateGuids();
539 }
540 }
541 else {
542 if (pInstance != NULL) {
543 pInstance->DeleteObject();
544 }
545
546 if (providerAllocated) {
547 pProvider->DeleteObject();
548 }
549 }
550
551 return status;
552 }
553
554 VOID
RemoveProvider(__in FxWmiProvider * Provider)555 FxWmiIrpHandler::RemoveProvider(
556 __in FxWmiProvider* Provider
557 )
558 {
559 KIRQL irql;
560
561 //
562 // No need to update via IoWMIRegistrationControl because this is only
563 // called in the error path of creating a provider.
564 //
565 Lock(&irql);
566 RemoveProviderLocked(Provider);
567 Unlock(irql);
568 }
569
570 VOID
RemoveProviderLocked(__in FxWmiProvider * Provider)571 FxWmiIrpHandler::RemoveProviderLocked(
572 __in FxWmiProvider* Provider
573 )
574 {
575 m_NumProviders--;
576 RemoveEntryList(&Provider->m_ListEntry);
577 InitializeListHead(&Provider->m_ListEntry);
578 }
579
580 _Must_inspect_result_
581 FxWmiProvider*
FindProviderLocked(__in LPGUID Guid)582 FxWmiIrpHandler::FindProviderLocked(
583 __in LPGUID Guid
584 )
585 {
586 FxWmiProvider* pFound;
587 PLIST_ENTRY ple;
588
589 pFound = NULL;
590
591 for (ple = m_ProvidersListHead.Flink;
592 ple != &m_ProvidersListHead;
593 ple = ple->Flink) {
594
595 FxWmiProvider* pProvider;
596
597 pProvider = CONTAINING_RECORD(ple, FxWmiProvider, m_ListEntry);
598
599 if (RtlCompareMemory(&pProvider->m_Guid,
600 Guid,
601 sizeof(GUID)) == sizeof(GUID)) {
602 pFound = pProvider;
603 break;
604 }
605 }
606
607 return pFound;
608 }
609
610 _Must_inspect_result_
611 FxWmiProvider*
FindProviderReferenced(__in LPGUID Guid,__in PVOID Tag)612 FxWmiIrpHandler::FindProviderReferenced(
613 __in LPGUID Guid,
614 __in PVOID Tag
615 )
616 {
617 FxWmiProvider* pProvider;
618 KIRQL irql;
619
620 Lock(&irql);
621 pProvider = FindProviderLocked(Guid);
622 if (pProvider != NULL) {
623 pProvider->ADDREF(Tag);
624 }
625 Unlock(irql);
626
627 return pProvider;
628 }
629
630 _Must_inspect_result_
631 NTSTATUS
Dispatch(__in PIRP Irp)632 FxWmiIrpHandler::Dispatch(
633 __in PIRP Irp
634 )
635 {
636 PFX_DRIVER_GLOBALS pFxDriverGlobals;
637 FxWmiProvider* pProvider;
638 FxWmiInstance* pInstance;
639 PIO_STACK_LOCATION stack;
640 PDEVICE_OBJECT pAttached;
641 NTSTATUS status;
642 PVOID pTag;
643 ULONG instanceIndex;
644 KIRQL irql;
645 BOOLEAN handled, completeNow;
646 UCHAR minor;
647
648 pFxDriverGlobals = GetDriverGlobals();
649
650 FX_TRACK_DRIVER(pFxDriverGlobals);
651
652 stack = IoGetCurrentIrpStackLocation(Irp);
653 minor = stack->MinorFunction;
654 pTag = UlongToPtr(minor);
655 status = Irp->IoStatus.Status;
656
657 pProvider = NULL;
658 pInstance = NULL;
659
660 handled = FALSE;
661 completeNow = FALSE;
662
663 DoTraceLevelMessage(
664 GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGIO,
665 "WDFDEVICE 0x%p !devobj 0x%p IRP_MJ_SYSTEM_CONTROL, %!sysctrl! IRP 0x%p",
666 m_Device->GetHandle(), m_Device->GetDeviceObject(), minor, Irp);
667
668 //
669 // Verify the minor code is within range, there is hole in the table at 0xA.
670 // This check works around the hole in the table.
671 //
672 if (minor > IRP_MN_EXECUTE_METHOD && minor != IRP_MN_REGINFO_EX) {
673 goto Done;
674 }
675
676 //
677 // If the irp is not targetted at this device, send it down the stack
678 //
679 if (stack->Parameters.WMI.ProviderId != (UINT_PTR) m_Device->GetDeviceObject()) {
680 goto Done;
681 }
682
683 if (minor == IRP_MN_REGINFO || minor == IRP_MN_REGINFO_EX) {
684 status = STATUS_SUCCESS;
685 }
686 else {
687 Lock(&irql);
688
689 pProvider = FindProviderLocked((LPGUID)stack->Parameters.WMI.DataPath);
690
691 if (pProvider != NULL) {
692 status = STATUS_SUCCESS;
693 }
694 else {
695 //
696 // check for WMI tracing (no pProvider)
697 //
698 status = STATUS_WMI_GUID_NOT_FOUND;
699 }
700
701 if (NT_SUCCESS(status) && m_WmiDispatchTable[minor].CheckInstance) {
702 PWNODE_SINGLE_INSTANCE pSingle;
703
704 pSingle = (PWNODE_SINGLE_INSTANCE) stack->Parameters.WMI.Buffer;
705
706 instanceIndex = pSingle->InstanceIndex;
707
708 //
709 // Also possible bits set in Flags related to instance names
710 // WNODE_FLAG_PDO_INSTANCE_NAMES
711 //
712 if (pSingle->WnodeHeader.Flags & WNODE_FLAG_STATIC_INSTANCE_NAMES) {
713 //
714 // Try to get the instance
715 //
716 pInstance = pProvider->GetInstanceReferencedLocked(
717 instanceIndex, pTag);
718
719 if (pInstance == NULL) {
720 status = STATUS_WMI_INSTANCE_NOT_FOUND;
721 }
722 }
723 else {
724
725
726
727
728
729
730 status = STATUS_WMI_INSTANCE_NOT_FOUND;
731 }
732 }
733
734 if (NT_SUCCESS(status)) {
735 pProvider->ADDREF(pTag);
736 }
737 else {
738 //
739 // NULL out the provider so we don't deref it later. We could not
740 // use if (pProvider != NULL && NT_SUCCESS(status) in the Done: block
741 // because we could have success here, but the dispatch function
742 // returns error.
743 //
744 pProvider = NULL;
745 }
746
747 Unlock(irql);
748
749 if (!NT_SUCCESS(status)) {
750 Irp->IoStatus.Status = status;
751 completeNow = TRUE;
752 }
753 }
754
755 if (NT_SUCCESS(status) && m_WmiDispatchTable[minor].Handler != NULL) {
756 status = m_WmiDispatchTable[minor].Handler(this,
757 Irp,
758 pProvider,
759 pInstance);
760 handled = TRUE;
761 }
762
763 Done:
764 if (pInstance != NULL) {
765 pInstance->RELEASE(pTag);
766 pInstance = NULL;
767 }
768
769 if (pProvider != NULL) {
770 pProvider->RELEASE(pTag);
771 pProvider = NULL;
772 }
773
774 if (handled == FALSE) {
775 pAttached = m_Device->GetAttachedDevice();
776 if (completeNow || pAttached == NULL) {
777 //
778 // Sent to a PDO, error in the FDO handling, or controller devobj
779 // style devobj, complete it here
780 //
781 status = Irp->IoStatus.Status;
782 IoCompleteRequest(Irp, IO_NO_INCREMENT);
783 }
784 else {
785 //
786 // Request sent to PNP device object that is not a PDO, send down
787 // the stack
788 //
789 IoSkipCurrentIrpStackLocation(Irp);
790 status = IoCallDriver(pAttached, Irp);
791 }
792 }
793
794 //
795 // Only release the remove lock *after* we have removed the thread entry
796 // from the list because the list lifetime is tied to the FxDevice lifetime
797 // and the remlock controls the lifetime of FxDevice.
798 //
799 // Since we never pend the wmi request, we can release the remove lock in
800 // this Dispatch routine. If we ever pended the IRPs, this would have to
801 // have some more logic involved.
802 //
803 IoReleaseRemoveLock(
804 &FxDevice::_GetFxWdmExtension(m_Device->GetDeviceObject())->IoRemoveLock,
805 Irp
806 );
807
808 return status;
809 }
810
811 _Must_inspect_result_
812 NTSTATUS
_QueryAllData(__in FxWmiIrpHandler * This,__in PIRP Irp,__in FxWmiProvider * Provider,__in_opt FxWmiInstance * Instance)813 FxWmiIrpHandler::_QueryAllData(
814 __in FxWmiIrpHandler* This,
815 __in PIRP Irp,
816 __in FxWmiProvider* Provider,
817 __in_opt FxWmiInstance* Instance
818 )
819 /*++
820
821 Routine Description:
822 Handles querying all instances for data. In the case where there is not
823 enough space in the buffer, we query each instance for the required amount
824 of buffer space and report that.
825
826 Arguments:
827 This - device instance
828
829 Irp - WMI request for query all data
830
831 Providre - the provider being queried
832
833 Instance - ignored, NULL
834
835 Return Value:
836 STATUS_BUFFER_TOO_SMALL in case the buffer is not large enough
837 STATUS_SUCCESS on a successful query
838 or other values depending on the client driver
839
840 --*/
841 {
842 POFFSETINSTANCEDATAANDLENGTH pOffsets;
843 PWNODE_ALL_DATA pNodeData;
844 PIO_STACK_LOCATION stack;
845 PUCHAR pData;
846 NTSTATUS status, addStatus;
847 ULONG instanceCount, dataBlockOffset, lengthArraySize, i, sizeRemaining,
848 lastAdjustment, bufferUsed, tempOffset;
849 KIRQL irql;
850 BOOLEAN tooSmall;
851
852 UNREFERENCED_PARAMETER(Instance);
853
854 status = STATUS_SUCCESS;
855
856 stack = IoGetCurrentIrpStackLocation(Irp);
857 pNodeData = (PWNODE_ALL_DATA) stack->Parameters.WMI.Buffer;
858
859 lastAdjustment = 0;
860 pData = NULL;
861 dataBlockOffset = 0;
862
863 //
864 // This either the amount of buffer used on successful queries or the
865 // amount of buffer required if the buffer is not big enough.
866 //
867 bufferUsed = 0;
868
869 tooSmall = FALSE;
870
871 if (stack->Parameters.WMI.BufferSize < sizeof(WNODE_ALL_DATA)) {
872 status = STATUS_UNSUCCESSFUL;
873 goto Done;
874 }
875
876
877
878
879
880
881
882 //
883 // All of the provider's access is guarded by the irp handler's lock
884 //
885 This->Lock(&irql);
886 instanceCount = Provider->m_NumInstances;
887 This->Unlock(irql);
888
889
890
891
892
893
894
895
896
897
898
899 if (instanceCount == 0) {
900 status = STATUS_WMI_INSTANCE_NOT_FOUND;
901 DoTraceLevelMessage(
902 This->GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
903 "Failing QueryAllData since no instances found for "
904 "WDFWMIPROVIDER %p, %!STATUS!",
905 Provider->GetHandle(), status);
906 bufferUsed = 0;
907 goto Done;
908 }
909
910 DoTraceLevelMessage(
911 This->GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
912 "WDFWMIPROVIDER %p QueryAllData, num instances %d",
913 Provider->GetHandle(), instanceCount);
914
915 pNodeData->InstanceCount = instanceCount;
916
917
918
919
920
921
922 pNodeData->WnodeHeader.Flags &= ~WNODE_FLAG_FIXED_INSTANCE_SIZE;
923
924 //
925 // Since value of instanceCount is not limited, do overflow-safe addition
926 // and multiplication
927 //
928 // lengthArraySize = instanceCount * sizeof(OFFSETINSTANCEDATAANDLENGTH);
929 status = RtlULongMult(instanceCount,
930 sizeof(OFFSETINSTANCEDATAANDLENGTH),
931 &lengthArraySize);
932
933 if (NT_SUCCESS(status)) {
934 // dataBlockOffset =
935 // FIELD_OFFSET(WNODE_ALL_DATA, OffsetInstanceDataAndLength) + lengthArraySize;
936 status = RtlULongAdd(
937 FIELD_OFFSET(WNODE_ALL_DATA, OffsetInstanceDataAndLength),
938 lengthArraySize,
939 &tempOffset);
940
941 if (NT_SUCCESS(status)) {
942 dataBlockOffset = (ULONG) WDF_ALIGN_SIZE_UP(
943 tempOffset, MEMORY_ALLOCATION_ALIGNMENT);
944
945 if (dataBlockOffset < tempOffset) {
946 status = STATUS_INTEGER_OVERFLOW;
947 }
948 }
949 }
950
951 if (!NT_SUCCESS(status)) {
952 DoTraceLevelMessage(
953 This->GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
954 "Failing QueryAllData since integer overflow occured using"
955 " provider instance count %d for WDFWMIPROVIDER %p, %!STATUS!",
956 instanceCount, Provider->GetHandle(), status);
957 bufferUsed = 0;
958 goto Done;
959 }
960
961 pNodeData->DataBlockOffset = dataBlockOffset;
962
963 if (dataBlockOffset <= stack->Parameters.WMI.BufferSize) {
964 pOffsets = &pNodeData->OffsetInstanceDataAndLength[0];
965 sizeRemaining = stack->Parameters.WMI.BufferSize - dataBlockOffset;
966 pData = (PUCHAR) WDF_PTR_ADD_OFFSET(pNodeData, dataBlockOffset);
967 }
968 else {
969 //
970 // There is not enough room in the WNODE to complete the query, but
971 // we still want to query all the instances to see how big a buffer the
972 // caller should resend.
973 //
974 pOffsets = NULL;
975 pData = NULL;
976 sizeRemaining = 0;
977
978 //
979 // We are in the too small case
980 //
981 tooSmall = TRUE;
982 status = STATUS_BUFFER_TOO_SMALL;
983 }
984
985 if (instanceCount > 0 && Provider->GetMinInstanceBufferSize() != 0) {
986 ULONG size, minSizeAdjusted;
987
988 size = 0;
989
990 minSizeAdjusted = (ULONG) WDF_ALIGN_SIZE_UP(
991 Provider->GetMinInstanceBufferSize(),
992 MEMORY_ALLOCATION_ALIGNMENT
993 );
994
995 //
996 // Quick size check to make sure there is enough buffer for all of the
997 // instances. We round up all but the last instance's minimum size so
998 // that we can make sure we have enough room for an aligned bufer for
999 // each instance.
1000 //
1001 // All but the last instance are using the adjusted size. In the end,
1002 // we will have:
1003 //
1004 // size = minSizeAdjusted * (instanceCount-1) +
1005 // Provider->GetMinInstanceBufferSize();
1006 //
1007 // NOTE: we do not care about instances which do not support query
1008 // data. We don't care b/c by computing the total size of all
1009 // instances we compute the maximum sized buffer needed, and if
1010 // a few instances do no support query data, then the buffer
1011 // will just be too large, never too small.
1012 //
1013 status = RtlULongMult(minSizeAdjusted, instanceCount - 1, &size);
1014 if (!NT_SUCCESS(status)) {
1015 goto Done;
1016 }
1017
1018 status = RtlULongAdd(size, Provider->GetMinInstanceBufferSize(), &size);
1019 if (!NT_SUCCESS(status)) {
1020 goto Done;
1021 }
1022
1023 //
1024 // Since the client indicated that that each block as a minimum instance
1025 // size (which in reality is the fixed size, variable sized instnaces
1026 // should report a minimum instance size of zero), we can compute the
1027 // required buffer size w/out querying each instance for the required
1028 // size.
1029 //
1030 if (sizeRemaining < size) {
1031 //
1032 // bufferUsed in the buffer too small case indicates how many bytes
1033 // we want the caller to allocate.
1034 //
1035 bufferUsed = size;
1036 status = STATUS_BUFFER_TOO_SMALL;
1037 goto Done;
1038 }
1039 }
1040
1041 for (i = 0; i < instanceCount; i++) {
1042 FxWmiInstance* pInstance;
1043
1044 //
1045 // In the case where we have a minimum instance size, we should have
1046 // verified correctly above to have enough buffer.
1047 //
1048 ASSERT(sizeRemaining >= Provider->GetMinInstanceBufferSize());
1049
1050 pInstance = Provider->GetInstanceReferenced(i, Irp);
1051
1052 if (pInstance == NULL) {
1053 break;
1054 }
1055
1056 if (pInstance->IsQueryInstanceSupported()) {
1057 ULONG tmpSize;
1058
1059 tmpSize = 0;
1060
1061 status = pInstance->QueryInstance(sizeRemaining, pData, &tmpSize);
1062
1063 if (NT_SUCCESS(status) || status == STATUS_BUFFER_TOO_SMALL) {
1064 ULONG adjustedSize;
1065
1066 //
1067 // We calculate the size in both cases. In the NT_SUCCESS case,
1068 // it is the amount of buffer used. In the too small case, it is
1069 // the amount of buffer required for a subsequent query.
1070 //
1071 adjustedSize = (ULONG) WDF_ALIGN_SIZE_UP(
1072 tmpSize, MEMORY_ALLOCATION_ALIGNMENT
1073 );
1074
1075 if (adjustedSize < tmpSize) {
1076 //
1077 // Overflow, adjustedSize will be >= tmpSize in the normal
1078 // case.
1079 //
1080 status = STATUS_INTEGER_OVERFLOW;
1081 DoTraceLevelMessage(
1082 This->GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
1083 "WDFWMIINSTNACE %p queried, returned a buffer size of %d,"
1084 "but it could not be rounded up, %!STATUS!",
1085 pInstance->GetHandle(), tmpSize, status);
1086 goto QueryError;
1087 }
1088
1089 //
1090 // Keep track of how much we adjusted up the size on the last
1091 // instance so that when compute the total buffer used, we
1092 // do not include the final size adjustment.
1093 //
1094 lastAdjustment = adjustedSize - tmpSize;
1095
1096 //
1097 // We only write the offset and data if we have not yet
1098 // encountered an instance where the buffer size was too small.
1099 //
1100 if (NT_SUCCESS(status) && tooSmall == FALSE) {
1101 //
1102 // dataBlockOffset is where we are currently at in the buffer
1103 //
1104 pOffsets[i].LengthInstanceData = tmpSize;
1105 pOffsets[i].OffsetInstanceData = dataBlockOffset;
1106
1107 pData += adjustedSize;
1108 }
1109 else {
1110 tooSmall = TRUE;
1111 }
1112
1113 //
1114 // Compute how much buffer we have left and our offset into it.
1115 //
1116 if (adjustedSize <= sizeRemaining) {
1117 sizeRemaining -= adjustedSize;
1118
1119 //
1120 // dataBlockOffset += adjustedSize;
1121 //
1122 addStatus = RtlULongAdd(dataBlockOffset,
1123 adjustedSize,
1124 &dataBlockOffset);
1125 }
1126 else {
1127 //
1128 // dataBlockOffset += sizeRemaining;
1129 //
1130 addStatus = RtlULongAdd(dataBlockOffset,
1131 sizeRemaining,
1132 &dataBlockOffset);
1133 sizeRemaining = 0;
1134 }
1135
1136 if (!NT_SUCCESS(addStatus)) {
1137 status = addStatus;
1138 DoTraceLevelMessage(
1139 This->GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
1140 "WDFWMIPROVIDER %p, arithmetic overflow in computing "
1141 "block offset, %!STATUS!", Provider->GetHandle(),
1142 status);
1143 goto QueryError;
1144 }
1145
1146 //
1147 // Compute how much buffer space we have used or require the
1148 // caller to provide. This is just the count of bytes for the
1149 // data queried, it does not yet include the array size required
1150 // to report offset & lengths.
1151 //
1152 // bufferUsed += adjustedSize;
1153 //
1154 addStatus = RtlULongAdd(bufferUsed, adjustedSize, &bufferUsed);
1155
1156 if (!NT_SUCCESS(addStatus)) {
1157 status = addStatus;
1158 DoTraceLevelMessage(
1159 This->GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
1160 "WDFWMIPROVIDER %p, arithmetic overflow in computing "
1161 "buffer consumed(%d+%d), %!STATUS!",
1162 Provider->GetHandle(), bufferUsed, adjustedSize, status);
1163 goto QueryError;
1164 }
1165 }
1166 }
1167 else if (pOffsets != NULL) {
1168 //
1169 // Indicate no buffer and the current offset. If there any
1170 // other instances after this one, they will share the same
1171 // offset.
1172 //
1173 pOffsets[i].LengthInstanceData = 0;
1174 pOffsets[i].OffsetInstanceData = dataBlockOffset;
1175 }
1176
1177 QueryError:
1178 pInstance->RELEASE(Irp);
1179
1180 //
1181 // We continue on STATUS_BUFFER_TOO_SMALL so that we can ask each
1182 // instance the amount of buffer it needs.
1183 //
1184 if (!NT_SUCCESS(status) && status != STATUS_BUFFER_TOO_SMALL) {
1185 break;
1186 }
1187 }
1188
1189 //
1190 // We can have STATUS_BUFFER_TOO_SMALL if the last instance could not fit
1191 // its data into the buffer or have success if the last instance could write
1192 // its data, but a previous instance could not.
1193 //
1194 if (status == STATUS_BUFFER_TOO_SMALL || (NT_SUCCESS(status) && tooSmall)) {
1195 //
1196 // Since we align up the size of each block to MEMORY_ALLOCATION_ALIGNMENT,
1197 // the total buffer size is possibly adjusted up too high. Subtract the
1198 // last adjustment made for alignment when computing the instance size
1199 // so that our total size is not rounded up.
1200 //
1201 // CompleteWmiQueryAllDataRequest will take care of adding enough space
1202 // to the requested size to account for the array of instance offsets
1203 // and lengths.
1204 //
1205 bufferUsed -=lastAdjustment;
1206
1207 status = STATUS_BUFFER_TOO_SMALL;
1208
1209 DoTraceLevelMessage(
1210 This->GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNP,
1211 "WDFWMIPROVIDER %p QueryAllData returning %!STATUS!, requesting "
1212 "buffer size of 0x%x", Provider->GetHandle(), status, bufferUsed);
1213 }
1214 else if (NT_SUCCESS(status)) {
1215 //
1216 // Compute the total amount of buffer used. dataBlockOffset is the
1217 // offset of the next instance, which is one past the end, so just
1218 // compute the distance from the start.
1219 //
1220 // Since align up the size of each block to MEMORY_ALLOCATION_ALIGNMENT,
1221 // the total buffer size is possibly adjusted up too high. Subtract the
1222 // last adjustment made for alignment when computing the instance size
1223 // so that our total size is not rounded up.
1224 //
1225 ASSERT(dataBlockOffset >= pNodeData->DataBlockOffset);
1226 ASSERT(dataBlockOffset - pNodeData->DataBlockOffset >= lastAdjustment);
1227 bufferUsed = dataBlockOffset - pNodeData->DataBlockOffset - lastAdjustment;
1228 }
1229 else {
1230 DoTraceLevelMessage(
1231 This->GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
1232 "WDFWMIPROVIDER %p QueryAllData returning %!STATUS!",
1233 Provider->GetHandle(), status);
1234
1235 bufferUsed = 0;
1236 }
1237
1238 DoTraceLevelMessage(
1239 This->GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
1240 "WDFWMIPROVIDER %p QueryAllData returning %!STATUS!, buffer used 0x%x",
1241 Provider->GetHandle(), status, bufferUsed);
1242
1243 Done:
1244 status = This->CompleteWmiRequest(Irp, status, bufferUsed);
1245
1246 return status;
1247 }
1248
1249 _Must_inspect_result_
1250 NTSTATUS
_QuerySingleInstance(__in FxWmiIrpHandler * This,__in PIRP Irp,__in FxWmiProvider * Provider,__in FxWmiInstance * Instance)1251 FxWmiIrpHandler::_QuerySingleInstance(
1252 __in FxWmiIrpHandler* This,
1253 __in PIRP Irp,
1254 __in FxWmiProvider* Provider,
1255 __in FxWmiInstance* Instance
1256 )
1257 {
1258 PWNODE_SINGLE_INSTANCE pSingle;
1259 PIO_STACK_LOCATION stack;
1260 NTSTATUS status;
1261 ULONG size, bufferSize;
1262
1263 size = 0;
1264
1265 stack = IoGetCurrentIrpStackLocation(Irp);
1266 pSingle = (PWNODE_SINGLE_INSTANCE) stack->Parameters.WMI.Buffer;
1267 bufferSize = stack->Parameters.WMI.BufferSize - pSingle->DataBlockOffset;
1268
1269 if (Instance->IsQueryInstanceSupported() == FALSE) {
1270 status = STATUS_INVALID_DEVICE_REQUEST;
1271 }
1272 else if (bufferSize < Provider->GetMinInstanceBufferSize()) {
1273 size = Provider->GetMinInstanceBufferSize();
1274 status = STATUS_BUFFER_TOO_SMALL;
1275 }
1276 else {
1277 status = STATUS_SUCCESS;
1278 }
1279
1280 if (NT_SUCCESS(status)) {
1281 status = Instance->QueryInstance(
1282 bufferSize,
1283 WDF_PTR_ADD_OFFSET(pSingle, pSingle->DataBlockOffset),
1284 &size
1285 );
1286
1287 pSingle->SizeDataBlock = size;
1288 }
1289
1290 status = This->CompleteWmiRequest(Irp, status, size);
1291
1292 return status;
1293 }
1294
1295 _Must_inspect_result_
1296 NTSTATUS
_ChangeSingleInstance(__in FxWmiIrpHandler * This,__in PIRP Irp,__in FxWmiProvider * Provider,__in FxWmiInstance * Instance)1297 FxWmiIrpHandler::_ChangeSingleInstance(
1298 __in FxWmiIrpHandler* This,
1299 __in PIRP Irp,
1300 __in FxWmiProvider* Provider,
1301 __in FxWmiInstance* Instance
1302 )
1303 {
1304 PWNODE_SINGLE_INSTANCE pSingle;
1305 PIO_STACK_LOCATION stack;
1306 NTSTATUS status;
1307 ULONG size;
1308
1309 size = 0;
1310
1311 stack = IoGetCurrentIrpStackLocation(Irp);
1312 pSingle = (PWNODE_SINGLE_INSTANCE) stack->Parameters.WMI.Buffer;
1313
1314 if (Instance->IsSetInstanceSupported() == FALSE) {
1315 status = STATUS_WMI_READ_ONLY;
1316 }
1317 else if (pSingle->SizeDataBlock < Provider->m_MinInstanceBufferSize) {
1318 size = Provider->m_MinInstanceBufferSize;
1319 status = STATUS_BUFFER_TOO_SMALL;
1320 }
1321 else {
1322 status = STATUS_SUCCESS;
1323 }
1324
1325 if (NT_SUCCESS(status)) {
1326 size = pSingle->SizeDataBlock;
1327
1328 status = Instance->SetInstance(
1329 pSingle->SizeDataBlock,
1330 size == 0 ?
1331 NULL : WDF_PTR_ADD_OFFSET(pSingle, pSingle->DataBlockOffset)
1332 );
1333
1334 ASSERT(status != STATUS_PENDING);
1335 if (status == STATUS_PENDING) {
1336 status = STATUS_UNSUCCESSFUL;
1337 size = 0;
1338 }
1339 }
1340
1341 status = This->CompleteWmiRequest(Irp, status, size);
1342
1343 return status;
1344 }
1345
1346 _Must_inspect_result_
1347 NTSTATUS
_ChangeSingleItem(__in FxWmiIrpHandler * This,__in PIRP Irp,__in FxWmiProvider * Provider,__in FxWmiInstance * Instance)1348 FxWmiIrpHandler::_ChangeSingleItem(
1349 __in FxWmiIrpHandler* This,
1350 __in PIRP Irp,
1351 __in FxWmiProvider* Provider,
1352 __in FxWmiInstance* Instance
1353 )
1354 {
1355 PWNODE_SINGLE_ITEM pSingle;
1356 NTSTATUS status;
1357 ULONG size;
1358
1359 UNREFERENCED_PARAMETER(Provider);
1360
1361 size = 0;
1362
1363 pSingle = (PWNODE_SINGLE_ITEM) IoGetCurrentIrpStackLocation(Irp)->
1364 Parameters.WMI.Buffer;
1365
1366 if (Instance->IsSetItemSupported() == FALSE) {
1367 status = STATUS_WMI_READ_ONLY;
1368 }
1369 else {
1370 status = STATUS_SUCCESS;
1371 }
1372
1373 if (NT_SUCCESS(status)) {
1374 status = Instance->SetItem(
1375 pSingle->ItemId,
1376 pSingle->SizeDataItem,
1377 pSingle->SizeDataItem == 0 ?
1378 NULL : WDF_PTR_ADD_OFFSET(pSingle, pSingle->DataBlockOffset)
1379 );
1380
1381 ASSERT(status != STATUS_PENDING);
1382 if (status == STATUS_PENDING) {
1383 status = STATUS_UNSUCCESSFUL;
1384 size = 0;
1385 }
1386 }
1387
1388 status = This->CompleteWmiRequest(Irp, status, size);
1389
1390 return status;
1391 }
1392
1393 _Must_inspect_result_
1394 NTSTATUS
_EnableDisableEventsAndCollection(__in FxWmiIrpHandler * This,__in PIRP Irp,__in FxWmiProvider * Provider,__in FxWmiInstance * Instance)1395 FxWmiIrpHandler::_EnableDisableEventsAndCollection(
1396 __in FxWmiIrpHandler* This,
1397 __in PIRP Irp,
1398 __in FxWmiProvider* Provider,
1399 __in FxWmiInstance* Instance
1400 )
1401 {
1402 NTSTATUS status;
1403 BOOLEAN enable;
1404 WDF_WMI_PROVIDER_CONTROL control;
1405 PIO_STACK_LOCATION stack;
1406
1407 UNREFERENCED_PARAMETER(This);
1408 UNREFERENCED_PARAMETER(Instance);
1409
1410 Irp->IoStatus.Information = 0;
1411
1412 stack = IoGetCurrentIrpStackLocation(Irp);
1413
1414 if (stack->Parameters.WMI.BufferSize < sizeof(WNODE_HEADER)) {
1415 status = STATUS_INVALID_PARAMETER;
1416 goto Done;
1417 }
1418
1419 switch (stack->MinorFunction) {
1420 case IRP_MN_ENABLE_EVENTS:
1421 enable = TRUE;
1422 control = WdfWmiEventControl;
1423 break;
1424
1425 case IRP_MN_DISABLE_EVENTS:
1426 enable = FALSE;
1427 control = WdfWmiEventControl;
1428 break;
1429
1430 case IRP_MN_ENABLE_COLLECTION:
1431 enable = TRUE;
1432 control = WdfWmiInstanceControl;
1433 break;
1434
1435 case IRP_MN_DISABLE_COLLECTION:
1436 enable = FALSE;
1437 control = WdfWmiInstanceControl;
1438 break;
1439
1440 default:
1441 status = Irp->IoStatus.Status;
1442 goto Done;
1443 }
1444
1445 if (control == WdfWmiEventControl) {
1446 Provider->m_EventControlEnabled = enable;
1447
1448 //
1449 // Capture the tracing information before making the callback
1450 //
1451 if (Provider->m_Flags & WdfWmiProviderTracing) {
1452 Provider->SetTracingHandle(
1453 ((PWNODE_HEADER) stack->Parameters.WMI.Buffer)->HistoricalContext
1454 );
1455 }
1456 }
1457 else {
1458 Provider->m_DataBlockControlEnabled = enable;
1459 }
1460
1461 if (Provider->IsFunctionControlSupported()) {
1462 status = Provider->FunctionControl(control, enable);
1463 }
1464 else {
1465 status = STATUS_SUCCESS;
1466 }
1467
1468 ASSERT(status != STATUS_PENDING);
1469 if (status == STATUS_PENDING) {
1470 status = STATUS_UNSUCCESSFUL;
1471 }
1472
1473 //
1474 // Undo the previous capture on error
1475 //
1476 if (!NT_SUCCESS(status)) {
1477 if (control == WdfWmiEventControl) {
1478 Provider->m_EventControlEnabled = FALSE;
1479
1480 if (Provider->m_Flags & WdfWmiProviderTracing) {
1481 Provider->SetTracingHandle(NULL);
1482 }
1483 }
1484 else {
1485 Provider->m_DataBlockControlEnabled = FALSE;
1486 }
1487
1488 }
1489
1490 Done:
1491 Irp->IoStatus.Status = status;
1492 IoCompleteRequest(Irp, IO_NO_INCREMENT);
1493
1494 return status;
1495 }
1496
1497 _Must_inspect_result_
1498 NTSTATUS
_ExecuteMethod(__in FxWmiIrpHandler * This,__in PIRP Irp,__in FxWmiProvider * Provider,__in FxWmiInstance * Instance)1499 FxWmiIrpHandler::_ExecuteMethod(
1500 __in FxWmiIrpHandler* This,
1501 __in PIRP Irp,
1502 __in FxWmiProvider* Provider,
1503 __in FxWmiInstance* Instance
1504 )
1505 {
1506 PWNODE_METHOD_ITEM pMethod;
1507 PIO_STACK_LOCATION stack;
1508 NTSTATUS status;
1509 ULONG size, inBufferSize, outBufferSize;
1510
1511 UNREFERENCED_PARAMETER(This);
1512 UNREFERENCED_PARAMETER(Provider);
1513
1514 size = 0;
1515
1516 stack = IoGetCurrentIrpStackLocation(Irp);
1517 pMethod = (PWNODE_METHOD_ITEM) stack->Parameters.WMI.Buffer;
1518 inBufferSize = pMethod->SizeDataBlock;
1519 outBufferSize = stack->Parameters.WMI.BufferSize - pMethod->DataBlockOffset;
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531 if (Instance->IsExecuteMethodSupported() == FALSE) {
1532 //
1533 // WmiLib returns this value when there is no execute method function
1534 // pointer specified to it.
1535 //
1536 status = STATUS_INVALID_DEVICE_REQUEST;
1537 }
1538 else {
1539 status = STATUS_SUCCESS;
1540 }
1541
1542 if (NT_SUCCESS(status)) {
1543 status = Instance->ExecuteMethod(
1544 pMethod->MethodId,
1545 inBufferSize,
1546 outBufferSize,
1547 (inBufferSize == 0 && outBufferSize == 0) ?
1548 NULL : WDF_PTR_ADD_OFFSET(pMethod, pMethod->DataBlockOffset),
1549 &size
1550 );
1551
1552 ASSERT(status != STATUS_PENDING);
1553 if (status == STATUS_PENDING) {
1554 status = STATUS_UNSUCCESSFUL;
1555 size = 0;
1556 }
1557 }
1558
1559 status = This->CompleteWmiRequest(Irp, status, size);
1560
1561 return status;
1562 }
1563
1564 _Must_inspect_result_
1565 NTSTATUS
_RegInfo(__in FxWmiIrpHandler * This,__in PIRP Irp,__in_opt FxWmiProvider * Provider,__in_opt FxWmiInstance * Instance)1566 FxWmiIrpHandler::_RegInfo(
1567 __in FxWmiIrpHandler* This,
1568 __in PIRP Irp,
1569 __in_opt FxWmiProvider* Provider,
1570 __in_opt FxWmiInstance* Instance
1571 )
1572 {
1573 PIO_STACK_LOCATION stack;
1574 PUNICODE_STRING pRegPath;
1575 PWMIREGINFO pWmiRegInfo;
1576 PUCHAR pBuffer;
1577 PUNICODE_STRING pMofString;
1578 FxDevice* pDevice;
1579 NTSTATUS status;
1580 ULONG registryPathOffset, mofResourceOffset, bufferNeeded, information,
1581 bufferSize;
1582 KIRQL irql;
1583
1584 UNREFERENCED_PARAMETER(Provider);
1585 UNREFERENCED_PARAMETER(Instance);
1586
1587 information = 0;
1588
1589 stack = IoGetCurrentIrpStackLocation(Irp);
1590 pBuffer = (PUCHAR) stack->Parameters.WMI.Buffer;
1591 bufferSize = stack->Parameters.WMI.BufferSize;
1592
1593 pDevice = This->m_Device;
1594
1595 This->Lock(&irql);
1596
1597 mofResourceOffset = sizeof(WMIREGINFO) +
1598 This->m_NumProviders * sizeof(WMIREGGUIDW);
1599
1600 pMofString = NULL;
1601
1602 if (pDevice->m_MofResourceName.Buffer != NULL) {
1603 pMofString = &pDevice->m_MofResourceName;
1604 }
1605 else {
1606 //
1607 // Start with the parent and iterate up until we hit a device without
1608 // a parent.
1609 //
1610 pDevice = pDevice->m_ParentDevice;
1611
1612 while (pDevice != NULL) {
1613 if (pDevice->m_MofResourceName.Buffer != NULL) {
1614 pMofString = &pDevice->m_MofResourceName;
1615 break;
1616 }
1617
1618 //
1619 // Advance to the next ancestor
1620 //
1621 pDevice = pDevice->m_ParentDevice;
1622 }
1623
1624 //
1625 // Restore pDevice back to this device
1626 //
1627 pDevice = This->m_Device;
1628 }
1629
1630 pRegPath = pDevice->GetDriver()->GetRegistryPathUnicodeString();
1631
1632 //
1633 // if there is a mof string, add its length. We always need to at least
1634 // add the USHORT to indicate a string of size 0.
1635 //
1636 registryPathOffset = mofResourceOffset + sizeof(USHORT);
1637 if (pMofString != NULL) {
1638 registryPathOffset += pMofString->Length;
1639 }
1640
1641 //
1642 // eventually bufferNeeded = registryPathOffset + pRegPath->Length +
1643 // sizeof(USHORT)
1644 //
1645 status = RtlULongAdd(registryPathOffset, pRegPath->Length, &bufferNeeded);
1646 if (!NT_SUCCESS(status)) {
1647 goto Done;
1648 }
1649
1650 status = RtlULongAdd(bufferNeeded, sizeof(USHORT), &bufferNeeded);
1651 if (!NT_SUCCESS(status)) {
1652 goto Done;
1653 }
1654
1655 if (bufferNeeded <= bufferSize) {
1656 PLIST_ENTRY ple;
1657 ULONG i;
1658 BOOLEAN addref;
1659
1660 information = bufferNeeded;
1661
1662 pWmiRegInfo = (PWMIREGINFO) pBuffer;
1663 pWmiRegInfo->BufferSize = bufferNeeded;
1664 pWmiRegInfo->NextWmiRegInfo = 0;
1665 pWmiRegInfo->MofResourceName = mofResourceOffset;
1666 pWmiRegInfo->RegistryPath = registryPathOffset;
1667 pWmiRegInfo->GuidCount = This->m_NumProviders;
1668
1669 addref = IoGetCurrentIrpStackLocation(Irp)->MinorFunction == IRP_MN_REGINFO_EX
1670 ? TRUE : FALSE;
1671
1672 for (ple = This->m_ProvidersListHead.Flink, i = 0;
1673 i < This->m_NumProviders;
1674 ple = ple->Flink, i++) {
1675
1676 PWMIREGGUIDW pWmiRegGuid;
1677 PDEVICE_OBJECT pdo;
1678 FxWmiProvider* pProvider;
1679
1680 pProvider = CONTAINING_RECORD(ple, FxWmiProvider, m_ListEntry);
1681
1682 pWmiRegGuid = &pWmiRegInfo->WmiRegGuid[i];
1683
1684 RtlCopyMemory(&pWmiRegGuid->Guid,
1685 &pProvider->m_Guid,
1686 sizeof(GUID));
1687
1688 pWmiRegGuid->InstanceCount = pProvider->m_NumInstances;
1689
1690 pWmiRegGuid->Flags = pProvider->GetRegistrationFlagsLocked();
1691
1692 pdo = pDevice->GetPhysicalDevice();
1693 pWmiRegGuid->Pdo = (ULONG_PTR) pdo;
1694
1695 if (addref) {
1696 ObReferenceObject(pdo);
1697 }
1698 }
1699 }
1700 else {
1701 *((PULONG) pBuffer) = bufferNeeded;
1702 information = sizeof(ULONG);
1703 }
1704
1705 This->Unlock(irql);
1706
1707 //
1708 // Must copy the strings outside of any lock since they are in paged pool
1709 //
1710 if (bufferNeeded <= bufferSize) {
1711 PUSHORT pLength;
1712
1713 pLength = WDF_PTR_ADD_OFFSET_TYPE(pBuffer, mofResourceOffset, PUSHORT);
1714
1715 if (pMofString != NULL) {
1716 *pLength = pMofString->Length;
1717
1718 RtlCopyMemory(pLength + 1,
1719 pMofString->Buffer,
1720 pMofString->Length);
1721 }
1722 else {
1723 *pLength = 0;
1724 }
1725
1726 pLength = WDF_PTR_ADD_OFFSET_TYPE(pBuffer, registryPathOffset, PUSHORT);
1727
1728 *pLength = pRegPath->Length;
1729 RtlCopyMemory(pLength + 1,
1730 pRegPath->Buffer,
1731 pRegPath->Length);
1732 }
1733
1734 status = STATUS_SUCCESS;
1735
1736 Done:
1737 if (!NT_SUCCESS(status)) {
1738 This->Unlock(irql);
1739 }
1740
1741 Irp->IoStatus.Status = status;
1742 Irp->IoStatus.Information = information;
1743
1744 IoCompleteRequest(Irp, IO_NO_INCREMENT);
1745
1746 return status;
1747 }
1748
1749 VOID
CompleteWmiQueryAllDataRequest(__in PIRP Irp,__in NTSTATUS Status,__in ULONG BufferUsed)1750 FxWmiIrpHandler::CompleteWmiQueryAllDataRequest(
1751 __in PIRP Irp,
1752 __in NTSTATUS Status,
1753 __in ULONG BufferUsed
1754 )
1755 {
1756 PWNODE_ALL_DATA pNodeAllData;
1757 ULONG bufferNeeded, information;
1758 PIO_STACK_LOCATION stack;
1759
1760 stack = IoGetCurrentIrpStackLocation(Irp);
1761
1762 pNodeAllData = (PWNODE_ALL_DATA) stack->Parameters.WMI.Buffer;
1763 bufferNeeded = pNodeAllData->DataBlockOffset + BufferUsed;
1764
1765 if (NT_SUCCESS(Status) && bufferNeeded > stack->Parameters.WMI.BufferSize) {
1766 Status = STATUS_BUFFER_TOO_SMALL;
1767 }
1768
1769 if (NT_SUCCESS(Status)) {
1770 KeQuerySystemTime(&pNodeAllData->WnodeHeader.TimeStamp);
1771
1772 pNodeAllData->WnodeHeader.BufferSize = bufferNeeded;
1773 information = bufferNeeded;
1774 }
1775 else if (Status == STATUS_BUFFER_TOO_SMALL) {
1776 PWNODE_TOO_SMALL pNodeTooSmall;
1777
1778 pNodeTooSmall = (PWNODE_TOO_SMALL) stack->Parameters.WMI.Buffer;
1779
1780 pNodeTooSmall->WnodeHeader.BufferSize = sizeof(WNODE_TOO_SMALL);
1781 pNodeTooSmall->WnodeHeader.Flags = WNODE_FLAG_TOO_SMALL;
1782 pNodeTooSmall->SizeNeeded = bufferNeeded;
1783
1784 information = sizeof(WNODE_TOO_SMALL);
1785 Status = STATUS_SUCCESS;
1786 }
1787 else {
1788 information = 0;
1789 }
1790
1791 Irp->IoStatus.Information = information;
1792 Irp->IoStatus.Status = Status;
1793 }
1794
1795 VOID
CompleteWmiQuerySingleInstanceRequest(__in PIRP Irp,__in NTSTATUS Status,__in ULONG BufferUsed)1796 FxWmiIrpHandler::CompleteWmiQuerySingleInstanceRequest(
1797 __in PIRP Irp,
1798 __in NTSTATUS Status,
1799 __in ULONG BufferUsed
1800 )
1801 {
1802 PWNODE_SINGLE_INSTANCE pNodeSingle;
1803 ULONG bufferNeeded, information;
1804
1805 pNodeSingle = (PWNODE_SINGLE_INSTANCE)
1806 IoGetCurrentIrpStackLocation(Irp)->Parameters.WMI.Buffer;
1807
1808 bufferNeeded = pNodeSingle->DataBlockOffset + BufferUsed;
1809
1810 if (NT_SUCCESS(Status)) {
1811 pNodeSingle->WnodeHeader.BufferSize = bufferNeeded;
1812 KeQuerySystemTime(&pNodeSingle->WnodeHeader.TimeStamp);
1813
1814 ASSERT(pNodeSingle->SizeDataBlock <= BufferUsed);
1815 information = bufferNeeded;
1816 }
1817 else if (Status == STATUS_BUFFER_TOO_SMALL) {
1818 PWNODE_TOO_SMALL pNodeTooSmall;
1819
1820 pNodeTooSmall = (PWNODE_TOO_SMALL) pNodeSingle;
1821
1822 pNodeTooSmall->WnodeHeader.BufferSize = sizeof(WNODE_TOO_SMALL);
1823 pNodeTooSmall->WnodeHeader.Flags = WNODE_FLAG_TOO_SMALL;
1824 pNodeTooSmall->SizeNeeded = bufferNeeded;
1825
1826 information = sizeof(WNODE_TOO_SMALL);
1827 Status = STATUS_SUCCESS;
1828 }
1829 else {
1830 information = 0;
1831 }
1832
1833 Irp->IoStatus.Information = information;
1834 Irp->IoStatus.Status = Status;
1835 }
1836
1837 VOID
CompleteWmiExecuteMethodRequest(__in PIRP Irp,__in NTSTATUS Status,__in ULONG BufferUsed)1838 FxWmiIrpHandler::CompleteWmiExecuteMethodRequest(
1839 __in PIRP Irp,
1840 __in NTSTATUS Status,
1841 __in ULONG BufferUsed
1842 )
1843 {
1844 PWNODE_METHOD_ITEM pNodeMethod;
1845 ULONG bufferNeeded, information;
1846
1847 pNodeMethod = (PWNODE_METHOD_ITEM)
1848 IoGetCurrentIrpStackLocation(Irp)->Parameters.WMI.Buffer;
1849
1850 bufferNeeded = pNodeMethod->DataBlockOffset + BufferUsed;
1851
1852 if (NT_SUCCESS(Status)) {
1853 pNodeMethod->WnodeHeader.BufferSize = bufferNeeded;
1854 pNodeMethod->SizeDataBlock = BufferUsed;
1855 KeQuerySystemTime(&pNodeMethod->WnodeHeader.TimeStamp);
1856
1857 information = bufferNeeded;
1858 }
1859 else if (Status == STATUS_BUFFER_TOO_SMALL) {
1860 PWNODE_TOO_SMALL pNodeTooSmall;
1861
1862 pNodeTooSmall = (PWNODE_TOO_SMALL) pNodeMethod;
1863
1864 pNodeTooSmall->WnodeHeader.BufferSize = sizeof(WNODE_TOO_SMALL);
1865 pNodeTooSmall->WnodeHeader.Flags = WNODE_FLAG_TOO_SMALL;
1866 pNodeTooSmall->SizeNeeded = bufferNeeded;
1867
1868 information = sizeof(WNODE_TOO_SMALL);
1869 Status = STATUS_SUCCESS;
1870 }
1871 else {
1872 information = 0;
1873 }
1874
1875 Irp->IoStatus.Information = information;
1876 Irp->IoStatus.Status = Status;
1877 }
1878
1879 _Must_inspect_result_
1880 NTSTATUS
CompleteWmiRequest(__in PIRP Irp,__in NTSTATUS Status,__in ULONG BufferUsed)1881 FxWmiIrpHandler::CompleteWmiRequest(
1882 __in PIRP Irp,
1883 __in NTSTATUS Status,
1884 __in ULONG BufferUsed
1885 )
1886 /*++
1887
1888 Routine Description:
1889 This routine will do the work of completing a WMI irp. Depending upon the
1890 the WMI request this routine will fixup the returned WNODE appropriately.
1891
1892 This may be called at DPC level
1893
1894 Arguments:
1895
1896 Irp - Supplies the Irp making the request.
1897
1898 Status has the return status code for the IRP
1899
1900 BufferUsed has the number of bytes needed by the device to return the
1901 data requested in any query. In the case that the buffer passed to
1902 the device is too small this has the number of bytes needed for the
1903 return data. If the buffer passed is large enough then this has the
1904 number of bytes actually used by the device.
1905
1906 Return Value:
1907
1908 status
1909
1910 --*/
1911 {
1912 switch (IoGetCurrentIrpStackLocation(Irp)->MinorFunction) {
1913 case IRP_MN_QUERY_ALL_DATA:
1914 CompleteWmiQueryAllDataRequest(Irp, Status, BufferUsed);
1915 break;
1916
1917 case IRP_MN_QUERY_SINGLE_INSTANCE:
1918 CompleteWmiQuerySingleInstanceRequest(Irp, Status, BufferUsed);
1919 break;
1920
1921 case IRP_MN_EXECUTE_METHOD:
1922 CompleteWmiExecuteMethodRequest(Irp, Status, BufferUsed);
1923 break;
1924
1925 case IRP_MN_CHANGE_SINGLE_INSTANCE:
1926 case IRP_MN_CHANGE_SINGLE_ITEM:
1927 //
1928 // Lot's of drivers return STATUS_BUFFER_TOO_SMALL for an invalid buffer
1929 // size. WMI expects STATUS_WMI_SET_FAILURE in this case. Change the
1930 // Status to the expected value. No way to return any size in
1931 // Information, the buffer is input only.
1932 //
1933 if (Status == STATUS_BUFFER_TOO_SMALL) {
1934 DoTraceLevelMessage(
1935 GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
1936 "Converting %!STATUS! to %!STATUS!",
1937 Status, STATUS_WMI_SET_FAILURE);
1938 Status = STATUS_WMI_SET_FAILURE;
1939 }
1940 // || || Fall through || ||
1941 // \/ \/ \/ \/
1942
1943 default:
1944 //
1945 // All other requests don't return any data
1946 //
1947 Irp->IoStatus.Status = Status;
1948 Irp->IoStatus.Information = 0;
1949 break;
1950 }
1951
1952 //
1953 // One of the complete functions may have morphed the status value
1954 //
1955 Status = Irp->IoStatus.Status;
1956 IoCompleteRequest(Irp, IO_NO_INCREMENT);
1957
1958 return Status;
1959 }
1960
1961