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