1 // 2 // Copyright (C) Microsoft. All rights reserved. 3 // 4 #include "..\pnppriv.hpp" 5 6 #include <initguid.h> 7 #include <wdmguid.h> 8 9 extern "C" { 10 #if defined(EVENT_TRACING) 11 #include "FxPkgPnpKM.tmh" 12 #endif 13 } 14 15 NTSTATUS 16 FxPkgPnp::FilterResourceRequirements( 17 __in IO_RESOURCE_REQUIREMENTS_LIST **IoList 18 ) 19 /*++ 20 21 Routine Description: 22 23 This routine traverses one or more alternate _IO_RESOURCE_LISTs in the input 24 IO_RESOURCE_REQUIREMENTS_LIST looking for interrupt descriptor and applies 25 the policy set by driver in the interrupt object to the resource descriptor. 26 27 LBI - Line based interrupt 28 MSI - Message Signalled interrupt 29 30 Here are the assumptions made about the order of descriptors. 31 32 - An IoRequirementList can have one or more alternate IoResourceList 33 - Each IoResourceList can have one or more resource descriptors 34 - A descriptor can be default (unique), preferred, or alternate descriptors 35 - A preferred descriptor can have zero or more alternate descriptors (P, A, A, A..) 36 - In an IoResourceList, there can be one or more LBI descriptors 37 (non-pci devices)(P,A,P,A) 38 - In an IoResourceList, there can be only one preferred MSI 2.2 39 (single or multi message) descriptor 40 - In an IoResourceList, there cannot be MSI2.2 and MSI-X descriptors 41 - In an IoResourceList, there can be one or more MSI-X descriptor 42 - An alternate descriptor cannot be a very first descriptor in the list 43 44 45 Now with that assumption, this routines parses the list looking for interrupt 46 descriptor. 47 48 - If it finds a LBI, it starts with the very first interrupt object and applies 49 the policy set by the driver to the resource descriptor. 50 - If it's finds an MSI2.2 then it starts with the first interrupt object and applies 51 the policy. If the MSI2.2 is a multi-message one then it loops thru looking for 52 as many interrupt object as there are messages. It doesn't fail the IRP, if the 53 interrupt objects are less than the messages. 54 - If there is an alternate descriptor then it applies the same policy from the 55 interrupt object that it used for the preceding preferred descriptor. 56 - Framework always uses FULLY_SPECIFIED connection type for both LBI and MSI 57 interrupts including MSI-X 58 - Framework will apply the policy on the descriptor set by the driver only 59 if the policy is already not included in the resource descriptor. This is 60 to allow the policy set in the registry to take precedence over the hard 61 coded driver policy. 62 - If the driver registers filter resource requirement and applies the policy 63 on its own (by escaping to WDM) then framework doesn't override that. 64 65 Arguments: 66 67 IoList - Pointer to the list part of an IRP_MN_FILTER_RESOURCE_REQUIREMENTS. 68 69 Return Value: 70 71 NTSTATUS 72 73 --*/ 74 { 75 ULONG altResListIndex; 76 PIO_RESOURCE_REQUIREMENTS_LIST pIoRequirementList; 77 PIO_RESOURCE_LIST pIoResList; 78 79 pIoRequirementList = *IoList; 80 81 if (pIoRequirementList == NULL) { 82 return STATUS_SUCCESS; 83 } 84 85 if (IsListEmpty(&m_InterruptListHead)) { 86 // 87 // No interrupt objects created to filter resource requirements. 88 // 89 return STATUS_SUCCESS; 90 } 91 92 pIoResList = pIoRequirementList->List; 93 94 // 95 // Parse one or more alternative resource lists. 96 // 97 for (altResListIndex = 0; 98 altResListIndex < pIoRequirementList->AlternativeLists; 99 altResListIndex++) { 100 PLIST_ENTRY pIntListEntryForMSI; 101 PLIST_ENTRY pIntListEntryForLBI; 102 BOOLEAN multiMessageMSI22Found; 103 BOOLEAN previousDescMSI; 104 ULONG descIndex; 105 106 multiMessageMSI22Found = FALSE; 107 previousDescMSI = FALSE; 108 109 pIntListEntryForMSI = &m_InterruptListHead; 110 pIntListEntryForLBI = &m_InterruptListHead; 111 112 // 113 // Traverse each _IO_RESOURCE_LISTs looking for interrupt descriptors 114 // and call FilterResourceRequirements method so that it can apply 115 // policy set on the interrupt object into the resource-descriptor. 116 // 117 118 for (descIndex = 0; descIndex < pIoResList->Count; descIndex++) { 119 ULONG messageCount; 120 PIO_RESOURCE_DESCRIPTOR pIoDesc; 121 FxInterrupt* pInterruptInstance; 122 123 pIoDesc = &pIoResList->Descriptors[descIndex]; 124 125 switch (pIoDesc->Type) { 126 case CmResourceTypeInterrupt: 127 128 if (FxInterrupt::_IsMessageInterrupt(pIoDesc->Flags)) { 129 130 previousDescMSI = TRUE; 131 132 // 133 // We will advance to the next interrupt object if the resource 134 // is not an alternate resource descriptor. A resource list can 135 // have a preferred and zero or more alternate resource descriptors 136 // for the same resource. We need to apply the same policy on the 137 // alternate desc that we applied on the preferred one in case one 138 // of the alernate desc is selected for this device. An alternate 139 // resource descriptor can't be the first descriptor in a list. 140 // 141 if ((pIoDesc->Option & IO_RESOURCE_ALTERNATIVE) == 0) { 142 pIntListEntryForMSI = pIntListEntryForMSI->Flink; 143 } 144 145 if (pIntListEntryForMSI == &m_InterruptListHead) { 146 DoTraceLevelMessage( 147 GetDriverGlobals(), TRACE_LEVEL_WARNING, TRACINGPNP, 148 "Not enough interrupt objects created for MSI by WDFDEVICE 0x%p ", 149 m_Device->GetHandle()); 150 break; 151 } 152 153 pInterruptInstance = CONTAINING_RECORD(pIntListEntryForMSI, FxInterrupt, m_PnpList); 154 messageCount = pIoDesc->u.Interrupt.MaximumVector - pIoDesc->u.Interrupt.MinimumVector + 1; 155 156 if (messageCount > 1) { 157 // 158 // PCI spec guarantees that there can be only one preferred/default 159 // MSI 2.2 descriptor in a single list. 160 // 161 if ((pIoDesc->Option & IO_RESOURCE_ALTERNATIVE) == 0) { 162 #if DBG 163 ASSERT(multiMessageMSI22Found == FALSE); 164 #else 165 UNREFERENCED_PARAMETER(multiMessageMSI22Found); 166 #endif 167 multiMessageMSI22Found = TRUE; 168 169 } 170 } 171 else { 172 // 173 // This is either single message MSI 2.2 or MSI-X interrupts 174 // 175 DO_NOTHING(); 176 } 177 178 pInterruptInstance->FilterResourceRequirements(pIoDesc); 179 } 180 else { 181 182 // 183 // We will advance to next interrupt object if the desc is not an alternate 184 // descriptor. For non PCI devices, the first LBI interrupt desc can't be an 185 // alternate descriptor. 186 // 187 if ((pIoDesc->Option & IO_RESOURCE_ALTERNATIVE) == 0) { 188 pIntListEntryForLBI = pIntListEntryForLBI->Flink; 189 } 190 191 // 192 // An LBI can be first alternate resource if there are preceding MSI(X) descriptors 193 // listed in the list. In that case, this descriptor is the alternate interrupt resource 194 // for all of the MSI messages. As a result, we will use the first interrupt object from 195 // the list if this ends up being assigned by the system instead of MSI. 196 // 197 if (previousDescMSI) { 198 ASSERT(pIoDesc->Option & IO_RESOURCE_ALTERNATIVE); 199 pIntListEntryForLBI = m_InterruptListHead.Flink; 200 previousDescMSI = FALSE; 201 } 202 203 // 204 // There can be one or more LBI interrupts and each LBI interrupt 205 // could have zero or more alternate descriptors. 206 // 207 if (pIntListEntryForLBI == &m_InterruptListHead) { 208 DoTraceLevelMessage( 209 GetDriverGlobals(), TRACE_LEVEL_WARNING, TRACINGPNP, 210 "Not enough interrupt objects created for LBI by WDFDEVICE 0x%p ", 211 m_Device->GetHandle()); 212 break; 213 } 214 215 pInterruptInstance = CONTAINING_RECORD(pIntListEntryForLBI, FxInterrupt, m_PnpList); 216 217 pInterruptInstance->FilterResourceRequirements(pIoDesc); 218 } 219 220 break; 221 222 default: 223 break; 224 } 225 } 226 227 // 228 // Since the Descriptors is a variable length list, you cannot get to the next 229 // alternate list by doing pIoRequirementList->List[altResListIndex]. 230 // Descriptors[descIndex] will now point to the end of the descriptor list. 231 // If there is another alternate list, it would be begin there. 232 // 233 pIoResList = (PIO_RESOURCE_LIST) &pIoResList->Descriptors[descIndex]; 234 } 235 236 return STATUS_SUCCESS; 237 } 238 239 _Must_inspect_result_ 240 NTSTATUS 241 FxPkgPnp::AllocateDmaEnablerList( 242 VOID 243 ) 244 { 245 FxSpinLockTransactionedList* pList; 246 NTSTATUS status; 247 KIRQL irql; 248 249 if (m_DmaEnablerList != NULL) { 250 return STATUS_SUCCESS; 251 } 252 253 Lock(&irql); 254 if (m_DmaEnablerList == NULL) { 255 pList = new (GetDriverGlobals()) FxSpinLockTransactionedList(); 256 257 if (pList != NULL) { 258 m_DmaEnablerList = pList; 259 status = STATUS_SUCCESS; 260 } 261 else { 262 status = STATUS_INSUFFICIENT_RESOURCES; 263 } 264 } 265 else { 266 // 267 // Already have a DMA list 268 // 269 status = STATUS_SUCCESS; 270 } 271 Unlock(irql); 272 273 return status; 274 } 275 276 VOID 277 FxPkgPnp::AddDmaEnabler( 278 __in FxDmaEnabler* Enabler 279 ) 280 { 281 DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP, 282 "Adding DmaEnabler %p, WDFDMAENABLER %p", 283 Enabler, Enabler->GetObjectHandle()); 284 285 m_DmaEnablerList->Add(GetDriverGlobals(), &Enabler->m_TransactionLink); 286 } 287 288 VOID 289 FxPkgPnp::RemoveDmaEnabler( 290 __in FxDmaEnabler* Enabler 291 ) 292 { 293 DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP, 294 "Removing DmaEnabler %p, WDFDMAENABLER %p", 295 Enabler, Enabler->GetObjectHandle()); 296 297 m_DmaEnablerList->Remove(GetDriverGlobals(), &Enabler->m_TransactionLink); 298 } 299 300 VOID 301 FxPkgPnp::WriteStateToRegistry( 302 __in HANDLE RegKey, 303 __in PUNICODE_STRING ValueName, 304 __in ULONG Value 305 ) 306 { 307 ZwSetValueKey(RegKey, ValueName, 0, REG_DWORD, &Value, sizeof(Value)); 308 } 309 310 NTSTATUS 311 FxPkgPnp::UpdateWmiInstanceForS0Idle( 312 __in FxWmiInstanceAction Action 313 ) 314 { 315 FxWmiProvider* pProvider; 316 NTSTATUS status; 317 318 switch(Action) { 319 case AddInstance: 320 if (m_PowerPolicyMachine.m_Owner->m_IdleSettings.WmiInstance == NULL) { 321 FxWmiInstanceInternalCallbacks cb; 322 323 cb.SetInstance = _S0IdleSetInstance; 324 cb.QueryInstance = _S0IdleQueryInstance; 325 cb.SetItem = _S0IdleSetItem; 326 327 status = RegisterPowerPolicyWmiInstance( 328 &GUID_POWER_DEVICE_ENABLE, 329 &cb, 330 &m_PowerPolicyMachine.m_Owner->m_IdleSettings.WmiInstance); 331 332 if (!NT_SUCCESS(status)) { 333 return status; 334 } 335 } 336 else { 337 pProvider = m_PowerPolicyMachine.m_Owner->m_IdleSettings. 338 WmiInstance->GetProvider(); 339 340 // 341 // Enable the WMI GUID by adding the instance back to the provider's 342 // list. If there is an error, ignore it. It just means we were 343 // racing with another thread removing or adding the instance. 344 // 345 (void) pProvider->AddInstance( 346 m_PowerPolicyMachine.m_Owner->m_IdleSettings.WmiInstance, 347 TRUE 348 ); 349 } 350 break; 351 352 case RemoveInstance: 353 if (m_PowerPolicyMachine.m_Owner->m_IdleSettings.WmiInstance != NULL) { 354 // 355 // Disable the WMI guid by removing it from the provider's list of 356 // instances. 357 // 358 pProvider = m_PowerPolicyMachine.m_Owner->m_IdleSettings. 359 WmiInstance->GetProvider(); 360 361 pProvider->RemoveInstance( 362 m_PowerPolicyMachine.m_Owner->m_IdleSettings.WmiInstance 363 ); 364 } 365 break; 366 367 default: 368 ASSERT(FALSE); 369 break; 370 } 371 372 return STATUS_SUCCESS;; 373 } 374 375 VOID 376 FxPkgPnp::ReadRegistryS0Idle( 377 __in PCUNICODE_STRING ValueName, 378 __out BOOLEAN *Enabled 379 ) 380 { 381 NTSTATUS status; 382 FxAutoRegKey hKey; 383 384 status = m_Device->OpenSettingsKey(&hKey.m_Key, STANDARD_RIGHTS_READ); 385 386 // 387 // Modify the value of Enabled only if success 388 // 389 if (NT_SUCCESS(status)) { 390 ULONG value; 391 392 status = FxRegKey::_QueryULong( 393 hKey.m_Key, ValueName, &value); 394 395 if (NT_SUCCESS(status)) { 396 // 397 // Normalize the ULONG value into a BOOLEAN 398 // 399 *Enabled = (value == FALSE) ? FALSE : TRUE; 400 } 401 } 402 } 403 404 NTSTATUS 405 FxPkgPnp::UpdateWmiInstanceForSxWake( 406 __in FxWmiInstanceAction Action 407 ) 408 { 409 FxWmiProvider* pProvider; 410 NTSTATUS status; 411 412 switch(Action) { 413 case AddInstance: 414 if (m_PowerPolicyMachine.m_Owner->m_WakeSettings.WmiInstance == NULL) { 415 FxWmiInstanceInternalCallbacks cb; 416 417 cb.SetInstance = _SxWakeSetInstance; 418 cb.QueryInstance = _SxWakeQueryInstance; 419 cb.SetItem = _SxWakeSetItem; 420 421 status = RegisterPowerPolicyWmiInstance( 422 &GUID_POWER_DEVICE_WAKE_ENABLE, 423 &cb, 424 &m_PowerPolicyMachine.m_Owner->m_WakeSettings.WmiInstance); 425 426 if (!NT_SUCCESS(status)) { 427 return status; 428 } 429 } else { 430 pProvider = m_PowerPolicyMachine.m_Owner->m_WakeSettings. 431 WmiInstance->GetProvider(); 432 433 // 434 // Enable the WMI GUID by adding the instance back to the provider's 435 // list. If there is an error, ignore it. It just means we were 436 // racing with another thread removing or adding the instance. 437 // 438 (void) pProvider->AddInstance( 439 m_PowerPolicyMachine.m_Owner->m_WakeSettings.WmiInstance, 440 TRUE 441 ); 442 } 443 break; 444 445 case RemoveInstance: 446 if (m_PowerPolicyMachine.m_Owner->m_WakeSettings.WmiInstance != NULL) { 447 // 448 // Disable the WMI guid by removing it from the provider's list of 449 // instances. 450 // 451 pProvider = m_PowerPolicyMachine.m_Owner->m_WakeSettings. 452 WmiInstance->GetProvider(); 453 454 pProvider->RemoveInstance( 455 m_PowerPolicyMachine.m_Owner->m_WakeSettings.WmiInstance 456 ); 457 } 458 break; 459 460 default: 461 ASSERT(FALSE); 462 break; 463 } 464 465 return STATUS_SUCCESS; 466 } 467 468 VOID 469 FxPkgPnp::ReadRegistrySxWake( 470 __in PCUNICODE_STRING ValueName, 471 __out BOOLEAN *Enabled 472 ) 473 { 474 FxAutoRegKey hKey; 475 NTSTATUS status; 476 477 status = m_Device->OpenSettingsKey(&hKey.m_Key, STANDARD_RIGHTS_READ); 478 479 // 480 // Modify the value of Enabled only if success 481 // 482 if (NT_SUCCESS(status)) { 483 ULONG value; 484 485 status = FxRegKey::_QueryULong( 486 hKey.m_Key, ValueName, &value); 487 488 if (NT_SUCCESS(status)) { 489 // 490 // Normalize the ULONG value into a BOOLEAN 491 // 492 *Enabled = (value == FALSE) ? FALSE : TRUE; 493 } 494 } 495 } 496 497 VOID 498 PnpPassThroughQIWorker( 499 __in MxDeviceObject* Device, 500 __inout FxIrp* Irp, 501 __inout FxIrp* ForwardIrp 502 ) 503 { 504 PIO_STACK_LOCATION pFwdStack, pCurStack; 505 506 pCurStack = Irp->GetCurrentIrpStackLocation(); 507 508 ForwardIrp->SetStatus(STATUS_NOT_SUPPORTED); 509 510 pFwdStack = ForwardIrp->GetNextIrpStackLocation(); 511 pFwdStack->MajorFunction = Irp->GetMajorFunction(); 512 pFwdStack->MinorFunction = Irp->GetMinorFunction(); 513 514 RtlCopyMemory(&pFwdStack->Parameters.QueryInterface, 515 &pCurStack->Parameters.QueryInterface, 516 sizeof(pFwdStack->Parameters.QueryInterface)); 517 518 ForwardIrp->SetInformation(Irp->GetInformation()); 519 ForwardIrp->SendIrpSynchronously(Device->GetObject()); 520 521 pFwdStack = ForwardIrp->GetNextIrpStackLocation(); 522 523 RtlCopyMemory(&pCurStack->Parameters.QueryInterface, 524 &pFwdStack->Parameters.QueryInterface, 525 sizeof(pCurStack->Parameters.QueryInterface)); 526 } 527 528 VOID 529 FxPkgPnp::RevokeDmaEnablerResources( 530 __in FxDmaEnabler *DmaEnabler 531 ) 532 { 533 DmaEnabler->RevokeResources(); 534 } 535 536 VOID 537 FxPkgPnp::QueryForD3ColdInterface( 538 VOID 539 ) 540 { 541 MxDeviceObject deviceObject; 542 PDEVICE_OBJECT topOfStack; 543 PDEVICE_OBJECT pdo; 544 FxAutoIrp irp; 545 NTSTATUS status; 546 547 // 548 // This function can be invoked multiple times, particularly if filters 549 // send IRP_MN_QUERY_CAPABILITIES. So bail out if the interface has already 550 // been acquired. 551 // 552 553 if ((m_D3ColdInterface.InterfaceDereference != NULL) || 554 (m_D3ColdInterface.GetIdleWakeInfo != NULL) || 555 (m_D3ColdInterface.SetD3ColdSupport != NULL)) { 556 return; 557 } 558 559 pdo = m_Device->GetPhysicalDevice(); 560 561 if (pdo == NULL) { 562 return; 563 } 564 565 // 566 // Get the top of stack device object, even though normal filters and the 567 // FDO may not have been added to the stack yet to ensure that this 568 // query-interface is seen by bus filters. Specifically, in a PCI device 569 // which is soldered to the motherboard, ACPI will be on the stack and it 570 // needs to see this IRP. 571 // 572 topOfStack = IoGetAttachedDeviceReference(pdo); 573 deviceObject.SetObject(topOfStack); 574 if (deviceObject.GetObject() != NULL) { 575 irp.SetIrp(FxIrp::AllocateIrp(deviceObject.GetStackSize())); 576 if (irp.GetIrp() == NULL) { 577 578 DoTraceLevelMessage( 579 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP, 580 "Failed to allocate IRP to get D3COLD_SUPPORT_INTERFACE from !devobj %p", 581 pdo); 582 } else { 583 584 // 585 // Initialize the Irp 586 // 587 irp.SetStatus(STATUS_NOT_SUPPORTED); 588 589 irp.ClearNextStack(); 590 irp.SetMajorFunction(IRP_MJ_PNP); 591 irp.SetMinorFunction(IRP_MN_QUERY_INTERFACE); 592 irp.SetParameterQueryInterfaceType(&GUID_D3COLD_SUPPORT_INTERFACE); 593 irp.SetParameterQueryInterfaceVersion(D3COLD_SUPPORT_INTERFACE_VERSION); 594 irp.SetParameterQueryInterfaceSize(sizeof(m_D3ColdInterface)); 595 irp.SetParameterQueryInterfaceInterfaceSpecificData(NULL); 596 irp.SetParameterQueryInterfaceInterface((PINTERFACE)&m_D3ColdInterface); 597 598 status = irp.SendIrpSynchronously(deviceObject.GetObject()); 599 600 if (!NT_SUCCESS(status)) { 601 DoTraceLevelMessage( 602 GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP, 603 "!devobj %p declined to supply D3COLD_SUPPORT_INTERFACE", 604 pdo); 605 606 RtlZeroMemory(&m_D3ColdInterface, sizeof(m_D3ColdInterface)); 607 } 608 } 609 } 610 ObDereferenceObject(topOfStack); 611 } 612 613 VOID 614 FxPkgPnp::DropD3ColdInterface( 615 VOID 616 ) 617 { 618 if (m_D3ColdInterface.InterfaceDereference != NULL) { 619 m_D3ColdInterface.InterfaceDereference(m_D3ColdInterface.Context); 620 } 621 622 RtlZeroMemory(&m_D3ColdInterface, sizeof(m_D3ColdInterface)); 623 } 624 625