1 /*++
2 
3 Copyright (c) Microsoft Corporation
4 
5 Module Name:
6 
7     FxWmiProvider.cpp
8 
9 Abstract:
10 
11     This module implements the FxWmiProvider object
12 
13 Author:
14 
15 
16 
17 Revision History:
18 
19 
20 --*/
21 
22 #include "fxwmipch.hpp"
23 
24 extern "C" {
25 // #include "FxWmiProvider.tmh"
26 }
27 
FxWmiProvider(__in PFX_DRIVER_GLOBALS FxDriverGlobals,__in PWDF_WMI_PROVIDER_CONFIG Config,__in FxDevice * Device)28 FxWmiProvider::FxWmiProvider(
29     __in PFX_DRIVER_GLOBALS FxDriverGlobals,
30     __in PWDF_WMI_PROVIDER_CONFIG Config,
31     __in FxDevice* Device
32     ) :
33     FxNonPagedObject(FX_TYPE_WMI_PROVIDER,
34                      sizeof(FxWmiProvider),
35                      FxDriverGlobals),
36     m_FunctionControl(FxDriverGlobals)
37 {
38     InitializeListHead(&m_ListEntry);
39     InitializeListHead(&m_InstanceListHead);
40     m_NumInstances = 0;
41 
42     m_Parent = Device->m_PkgWmi;
43 
44     m_EventControlEnabled = FALSE;
45     m_DataBlockControlEnabled = FALSE;
46     m_RemoveGuid = FALSE;
47 
48     m_TracingHandle = 0;
49 
50     m_Flags = Config->Flags;
51     m_MinInstanceBufferSize = Config->MinInstanceBufferSize;
52     RtlCopyMemory(&m_Guid, &Config->Guid, sizeof(GUID));
53 
54     if (Config->EvtWmiProviderFunctionControl != NULL) {
55         m_FunctionControl.m_Method = Config->EvtWmiProviderFunctionControl;
56     }
57 
58     //
59     // Driver cannot call WdfObjectDelete on this handle
60     //
61     MarkNoDeleteDDI();
62 
63     MarkDisposeOverride(ObjectDoNotLock);
64 }
65 
~FxWmiProvider()66 FxWmiProvider::~FxWmiProvider()
67 {
68     ASSERT(IsListEmpty(&m_ListEntry));
69 }
70 
71 BOOLEAN
Dispose(VOID)72 FxWmiProvider::Dispose(
73     VOID
74     )
75 {
76     //
77     // Object is being deleted, remove this object from the irp handler's list
78     // of providers.  If we don't do this, the irp handler will have a list
79     // which contains entries which have been freed.
80     //
81     m_Parent->RemoveProvider(this);
82 
83     return FxNonPagedObject::Dispose(); // __super call
84 }
85 
86 _Must_inspect_result_
87 NTSTATUS
_Create(__in PFX_DRIVER_GLOBALS CallersGlobals,__in WDFDEVICE Device,__in_opt PWDF_OBJECT_ATTRIBUTES ProviderAttributes,__in PWDF_WMI_PROVIDER_CONFIG WmiProviderConfig,__out WDFWMIPROVIDER * WmiProvider,__out FxWmiProvider ** Provider)88 FxWmiProvider::_Create(
89     __in PFX_DRIVER_GLOBALS CallersGlobals,
90     __in WDFDEVICE Device,
91     __in_opt PWDF_OBJECT_ATTRIBUTES ProviderAttributes,
92     __in PWDF_WMI_PROVIDER_CONFIG WmiProviderConfig,
93     __out WDFWMIPROVIDER* WmiProvider,
94     __out FxWmiProvider** Provider
95     )
96 {
97     PFX_DRIVER_GLOBALS pFxDriverGlobals;
98     FxDevice* pDevice;
99     FxWmiProvider* pProvider;
100     NTSTATUS status;
101     WDFOBJECT hProvider;
102     GUID zeroGuid;
103     BOOLEAN update;
104 
105     FxObjectHandleGetPtrAndGlobals(CallersGlobals,
106                                    Device,
107                                    FX_TYPE_DEVICE,
108                                    (PVOID*) &pDevice,
109                                    &pFxDriverGlobals);
110 
111     *Provider = NULL;
112     update = FALSE;
113 
114     *WmiProvider = NULL;
115 
116     status = FxValidateObjectAttributes(pFxDriverGlobals,
117                                         ProviderAttributes,
118                                         FX_VALIDATE_OPTION_PARENT_NOT_ALLOWED);
119     if (!NT_SUCCESS(status)) {
120         return status;
121     }
122 
123     if (WmiProviderConfig->Size != sizeof(WDF_WMI_PROVIDER_CONFIG)) {
124         status = STATUS_INFO_LENGTH_MISMATCH;
125 
126         DoTraceLevelMessage(
127             pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
128             "WmiProviderConfig Size 0x%x, expected size 0x%x, %!STATUS!",
129             WmiProviderConfig->Size, sizeof(WDF_WMI_PROVIDER_CONFIG),
130             status);
131 
132         return status;
133     }
134 
135     if ((WmiProviderConfig->Flags & ~WdfWmiProviderValidFlags) != 0) {
136         status = STATUS_INVALID_PARAMETER;
137         DoTraceLevelMessage(
138             pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
139             "Invalid flag(s) set, Flags 0x%x, valid mask 0x%x, %!STATUS!",
140             WmiProviderConfig->Flags, WdfWmiProviderValidFlags,
141             status);
142         return status;
143     }
144 
145     if ((WmiProviderConfig->Flags & WdfWmiProviderTracing) &&
146         (WmiProviderConfig->Flags & ~WdfWmiProviderTracing)) {
147         status = STATUS_INVALID_PARAMETER;
148 
149         DoTraceLevelMessage(
150             pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
151             "WdfWmiProviderTracing must be the only flag set, %!STATUS!",
152             status);
153 
154         return status;
155     }
156 
157     //
158     // Function control makes sense if it is expensive.  Otherwise you will
159     // not be called back on it.
160     //
161     // The opposite, marking yourself as expensive but providing no callback is
162     // OK b/c the provider marks the enabled state and the driver can retrieve
163     // it at runtime.
164     //
165     // Function control also applies to tracing GUIDs since the tracing subsystem
166     // will call enable/disable events to start/stop tracing.
167     //
168     if (WmiProviderConfig->EvtWmiProviderFunctionControl != NULL &&
169         ((WmiProviderConfig->Flags & (WdfWmiProviderTracing | WdfWmiProviderExpensive)) == 0)) {
170         status = STATUS_INVALID_PARAMETER;
171 
172         DoTraceLevelMessage(
173             pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
174             "EvtWmiProviderFunctionControl can only be set if Flags 0x%x has "
175             "WdfWmiProviderTracing (%d) or WdfWmiProviderExpensive (%d) bit "
176             "values set, %!STATUS!",
177             WmiProviderConfig->Flags, WdfWmiProviderTracing,
178             WdfWmiProviderExpensive, status);
179 
180         return status;
181     }
182 
183     RtlZeroMemory(&zeroGuid, sizeof(zeroGuid));
184 
185     if (RtlCompareMemory(&WmiProviderConfig->Guid,
186                          &zeroGuid,
187                          sizeof(GUID)) == sizeof(GUID)) {
188         status = STATUS_INVALID_PARAMETER;
189 
190         DoTraceLevelMessage(
191             pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
192             "WmiProvider Guid filed is all zeros, %!STATUS!",
193             status);
194 
195         return status;
196     }
197 
198     pProvider = NULL;
199 
200     pProvider = new(pFxDriverGlobals, ProviderAttributes)
201         FxWmiProvider(pFxDriverGlobals, WmiProviderConfig, pDevice);
202 
203     if (pProvider == NULL) {
204         status = STATUS_INSUFFICIENT_RESOURCES;
205 
206         DoTraceLevelMessage(
207             pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
208             "Could not allocate memory for a WDFWMIPROVIDER, %!STATUS!",
209             status);
210 
211         return status;
212     }
213 
214     status = pDevice->m_PkgWmi->AddProvider(pProvider, &update);
215 
216     if (NT_SUCCESS(status)) {
217         status = pProvider->Commit(ProviderAttributes, &hProvider, pDevice);
218 
219         if (!NT_SUCCESS(status)) {
220             pDevice->m_PkgWmi->RemoveProvider(pProvider);
221         }
222         else {
223             //
224             // NT_SUCCES(status) case
225             //
226             *WmiProvider = (WDFWMIPROVIDER) hProvider;
227         }
228     }
229 
230     if (NT_SUCCESS(status)) {
231         *Provider = pProvider;
232 
233         if (update) {
234             pDevice->m_PkgWmi->UpdateGuids();
235         }
236     }
237     else {
238         //
239         // AddProvider incremented update count on success however since we
240         // are not going to update registration due to this failure, decrement
241         // the count.
242         //
243         if (update) {
244             pDevice->m_PkgWmi->DecrementUpdateCount();
245         }
246 
247         pProvider->DeleteFromFailedCreate();
248     }
249 
250     return status;
251 }
252 
253 _Must_inspect_result_
254 NTSTATUS
AddInstanceLocked(__in FxWmiInstance * Instance,__in BOOLEAN NoErrorIfPresent,__out PBOOLEAN Update,__in AddInstanceAction Action)255 FxWmiProvider::AddInstanceLocked(
256     __in  FxWmiInstance* Instance,
257     __in  BOOLEAN NoErrorIfPresent,
258     __out PBOOLEAN Update,
259     __in  AddInstanceAction Action
260     )
261 {
262     NTSTATUS status;
263 
264     *Update = FALSE;
265 
266     if (!IsListEmpty(&Instance->m_ListEntry)) {
267         if (NoErrorIfPresent) {
268             return STATUS_SUCCESS;
269         }
270         else {
271             //
272             // Entry is already on a list, bad caller!
273             //
274             status = STATUS_INVALID_DEVICE_REQUEST;
275 
276             DoTraceLevelMessage(
277                 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGDEVICE,
278                 "WDFWMIINSTANCE %p already added, %!STATUS!",
279                 Instance->GetHandle(), status);
280 
281             return status;
282         }
283     }
284 
285     //
286     // Check to see if we are in the process of
287     //
288     switch (m_Parent->m_RegisteredState) {
289     case FxWmiIrpHandler::WmiUnregistered:
290         //
291         // The GUID will be reported when we do the initial registration
292         //
293         break;
294 
295     case FxWmiIrpHandler::WmiDeregistered:
296         //
297         // Either the GUID will be reported when we do the re-registration or
298         // we will clean it up when we goto the cleanup state.
299         //
300         break;
301 
302     case FxWmiIrpHandler::WmiRegistered:
303         //
304         // Since we already registered we need to tell WMI the change in the
305         // number of instances on this provider.
306         //
307         *Update = TRUE;
308         break;
309 
310     case FxWmiIrpHandler::WmiCleanedUp:
311         //
312         // Device is going away, registration is not allowed for the device
313         // anymore.
314         //
315         status = STATUS_INVALID_DEVICE_STATE;
316 
317         DoTraceLevelMessage(
318             GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGDEVICE,
319             "WMI is being cleanedup, WDFWMIINSTANCE %p add failing, %!STATUS!",
320             Instance->GetHandle(), status);
321 
322         return status;
323 
324     default:
325         ASSERT(FALSE);
326         break;
327     }
328 
329     if (Action == AddInstanceToTail) {
330         InsertTailList(&m_InstanceListHead, &Instance->m_ListEntry);
331     }
332     else {
333         InsertHeadList(&m_InstanceListHead, &Instance->m_ListEntry);
334     }
335 
336     //
337     // Since the count is increasing to at least one, we are not going to
338     // need to report this GUID as missing.  We could check the
339     // m_Parent->m_RegisteredState and only set it when we are registered, but
340     // it does us no harm to always clear this value regardless of state.
341     //
342     m_RemoveGuid = FALSE;
343 
344     m_NumInstances++;
345     status = STATUS_SUCCESS;
346 
347     return status;
348 }
349 
350 _Must_inspect_result_
351 NTSTATUS
AddInstance(__in FxWmiInstance * Instance,__in BOOLEAN NoErrorIfPresent)352 FxWmiProvider::AddInstance(
353      __in FxWmiInstance* Instance,
354      __in BOOLEAN NoErrorIfPresent
355      )
356 {
357     NTSTATUS status;
358     KIRQL irql;
359     BOOLEAN update;
360 
361     if (m_Flags & WdfWmiProviderTracing) {
362         status = STATUS_INVALID_DEVICE_REQUEST;
363 
364         DoTraceLevelMessage(
365             GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGDEVICE,
366             "WDFWMIINSTANCE %p cannot be added to tracing WDFWMIPROVIDER %p, "
367             "%!STATUS!", Instance->GetHandle(), GetHandle(), status);
368 
369         return status;
370     }
371 
372     m_Parent->Lock(&irql);
373     status = AddInstanceLocked(Instance, NoErrorIfPresent, &update);
374 
375     if (update) {
376         update = m_Parent->DeferUpdateLocked(irql);
377     }
378     m_Parent->Unlock(irql);
379 
380     if (update) {
381         m_Parent->UpdateGuids();
382     }
383 
384     return status;
385 }
386 
387 VOID
RemoveInstance(__in FxWmiInstance * Instance)388 FxWmiProvider::RemoveInstance(
389     __in FxWmiInstance* Instance
390     )
391 {
392     KIRQL irql;
393     BOOLEAN update;
394 
395     update = FALSE;
396 
397     m_Parent->Lock(&irql);
398 
399     if (!IsListEmpty(&Instance->m_ListEntry)) {
400         //
401         // Instance is in the list of instances on this provider.  Remove it.
402         //
403         RemoveEntryList(&Instance->m_ListEntry);
404         InitializeListHead(&Instance->m_ListEntry);
405         m_NumInstances--;
406 
407         if (m_Parent->m_RegisteredState == FxWmiIrpHandler::WmiRegistered) {
408             update = TRUE;
409 
410             //
411             // When the count goes to zero, inform WMI that the GUID should be
412             // removed when we get requeried.  We only need to do this once we have
413             // been registered.  In all other states, we ignore this value.
414             //
415             if (m_NumInstances == 0 &&
416                 (m_Flags & WdfWmiProviderExpensive) == 0) {
417                 m_RemoveGuid = TRUE;
418             }
419         }
420     }
421     else {
422         //
423         // The instance was explicitly removed and now the instance is trying
424         // to remove itself during dispose.  Nothing to do.
425         //
426         DO_NOTHING();
427     }
428 
429     if (update) {
430         update = m_Parent->DeferUpdateLocked(irql);
431     }
432 
433     m_Parent->Unlock(irql);
434 
435     if (update) {
436         m_Parent->UpdateGuids();
437     }
438 }
439 
440 ULONG
GetInstanceIndex(__in FxWmiInstance * Instance)441 FxWmiProvider::GetInstanceIndex(
442     __in FxWmiInstance* Instance
443     )
444 {
445     PLIST_ENTRY ple;
446     ULONG index;
447     KIRQL irql;
448 
449     m_Parent->Lock(&irql);
450     for (index = 0, ple = m_InstanceListHead.Flink;
451          index < m_NumInstances;
452          index++, ple = ple->Flink) {
453         if (CONTAINING_RECORD(ple, FxWmiInstance, m_ListEntry) == Instance) {
454             break;
455         }
456     }
457     m_Parent->Unlock(irql);
458 
459     return index;
460 }
461 
462 _Must_inspect_result_
463 FxWmiInstance*
GetInstanceReferenced(__in ULONG Index,__in PVOID Tag)464 FxWmiProvider::GetInstanceReferenced(
465     __in ULONG Index,
466     __in PVOID Tag
467     )
468 {
469     FxWmiInstance* pInstance;
470     KIRQL irql;
471 
472     m_Parent->Lock(&irql);
473     pInstance = GetInstanceReferencedLocked(Index, Tag);
474     m_Parent->Unlock(irql);
475 
476     return pInstance;
477 }
478 
479 _Must_inspect_result_
480 FxWmiInstance*
GetInstanceReferencedLocked(__in ULONG Index,__in PVOID Tag)481 FxWmiProvider::GetInstanceReferencedLocked(
482     __in ULONG Index,
483     __in PVOID Tag
484     )
485 {
486     FxWmiInstance* pFound;
487     PLIST_ENTRY ple;
488     ULONG i;
489 
490     pFound = NULL;
491 
492     for (i = 0, ple = m_InstanceListHead.Flink;
493          i < m_NumInstances;
494          ple = ple->Flink, i++) {
495 
496         if (i == Index) {
497             pFound = CONTAINING_RECORD(ple, FxWmiInstance, m_ListEntry);
498             pFound->ADDREF(Tag);
499             break;
500         }
501     }
502 
503     return pFound;
504 }
505 
506 _Must_inspect_result_
507 NTSTATUS
FunctionControl(__in WDF_WMI_PROVIDER_CONTROL Control,__in BOOLEAN Enable)508 FxWmiProvider::FunctionControl(
509     __in WDF_WMI_PROVIDER_CONTROL Control,
510     __in BOOLEAN Enable
511     )
512 {
513     return m_FunctionControl.Invoke(m_Parent->GetDevice()->GetHandle(),
514                                     GetHandle(),
515                                     Control,
516                                     Enable);
517 }
518 
519 ULONG
GetRegistrationFlagsLocked(VOID)520 FxWmiProvider::GetRegistrationFlagsLocked(
521     VOID
522     )
523 {
524     ULONG flags;
525 
526     if (m_Flags & WdfWmiProviderTracing) {
527         flags = WMIREG_FLAG_TRACED_GUID | WMIREG_FLAG_TRACE_CONTROL_GUID;
528 
529         //
530         // Once tracing GUID is registered, we do not allow it to be unregistered
531         //
532         ASSERT(m_RemoveGuid == FALSE);
533     }
534     else {
535         flags = WMIREG_FLAG_INSTANCE_PDO;
536 
537         if (m_Flags & WdfWmiProviderExpensive) {
538             flags |= WMIREG_FLAG_EXPENSIVE;
539         }
540 
541         if (m_Flags & WdfWmiProviderEventOnly) {
542             flags |= WMIREG_FLAG_EVENT_ONLY_GUID;
543         }
544     }
545 
546     if (m_RemoveGuid) {
547         //
548         // We have gone down to zero instances of this provider, report it as
549         // gone to WMI.
550         //
551         ASSERT(m_NumInstances == 0);
552         flags |= WMIREG_FLAG_REMOVE_GUID;
553 
554         //
555         // Once reported as removed, we do not have not have to report ourselves
556         // as removed again.
557         //
558         m_RemoveGuid = FALSE;
559     }
560 
561     return flags;
562 }
563 
564