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