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 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 66 FxWmiProvider::~FxWmiProvider() 67 { 68 ASSERT(IsListEmpty(&m_ListEntry)); 69 } 70 71 BOOLEAN 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 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 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 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 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 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* 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* 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 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 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