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
FilterResourceRequirements(__in IO_RESOURCE_REQUIREMENTS_LIST ** IoList)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
AllocateDmaEnablerList(VOID)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
AddDmaEnabler(__in FxDmaEnabler * Enabler)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
RemoveDmaEnabler(__in FxDmaEnabler * Enabler)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
WriteStateToRegistry(__in HANDLE RegKey,__in PUNICODE_STRING ValueName,__in ULONG Value)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 __REACTOS__
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
ReadRegistryS0Idle(__in PCUNICODE_STRING ValueName,__out BOOLEAN * Enabled)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 __REACTOS__
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
ReadRegistrySxWake(__in PCUNICODE_STRING ValueName,__out BOOLEAN * Enabled)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
PnpPassThroughQIWorker(__in MxDeviceObject * Device,__inout FxIrp * Irp,__inout FxIrp * ForwardIrp)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
RevokeDmaEnablerResources(__in FxDmaEnabler * DmaEnabler)529 FxPkgPnp::RevokeDmaEnablerResources(
530     __in FxDmaEnabler *DmaEnabler
531     )
532 {
533     // DmaEnabler->RevokeResources();
534     ROSWDFNOTIMPLEMENTED;
535 }
536 
537 VOID
QueryForD3ColdInterface(VOID)538 FxPkgPnp::QueryForD3ColdInterface(
539     VOID
540     )
541 {
542     MxDeviceObject deviceObject;
543     PDEVICE_OBJECT topOfStack;
544     PDEVICE_OBJECT pdo;
545     FxAutoIrp irp;
546     NTSTATUS status;
547 
548     //
549     // This function can be invoked multiple times, particularly if filters
550     // send IRP_MN_QUERY_CAPABILITIES.  So bail out if the interface has already
551     // been acquired.
552     //
553 
554     if ((m_D3ColdInterface.InterfaceDereference != NULL) ||
555         (m_D3ColdInterface.GetIdleWakeInfo != NULL) ||
556         (m_D3ColdInterface.SetD3ColdSupport != NULL)) {
557         return;
558     }
559 
560     pdo = m_Device->GetPhysicalDevice();
561 
562     if (pdo == NULL) {
563         return;
564     }
565 
566     //
567     // Get the top of stack device object, even though normal filters and the
568     // FDO may not have been added to the stack yet to ensure that this
569     // query-interface is seen by bus filters.  Specifically, in a PCI device
570     // which is soldered to the motherboard, ACPI will be on the stack and it
571     // needs to see this IRP.
572     //
573     topOfStack = IoGetAttachedDeviceReference(pdo);
574     deviceObject.SetObject(topOfStack);
575     if (deviceObject.GetObject() != NULL) {
576         irp.SetIrp(FxIrp::AllocateIrp(deviceObject.GetStackSize()));
577         if (irp.GetIrp() == NULL) {
578 
579             DoTraceLevelMessage(
580                 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
581                 "Failed to allocate IRP to get D3COLD_SUPPORT_INTERFACE from !devobj %p",
582                 pdo);
583         } else {
584 
585             //
586             // Initialize the Irp
587             //
588             irp.SetStatus(STATUS_NOT_SUPPORTED);
589 
590             irp.ClearNextStack();
591             irp.SetMajorFunction(IRP_MJ_PNP);
592             irp.SetMinorFunction(IRP_MN_QUERY_INTERFACE);
593             irp.SetParameterQueryInterfaceType(&GUID_D3COLD_SUPPORT_INTERFACE);
594             irp.SetParameterQueryInterfaceVersion(D3COLD_SUPPORT_INTERFACE_VERSION);
595             irp.SetParameterQueryInterfaceSize(sizeof(m_D3ColdInterface));
596             irp.SetParameterQueryInterfaceInterfaceSpecificData(NULL);
597             irp.SetParameterQueryInterfaceInterface((PINTERFACE)&m_D3ColdInterface);
598 
599             status = irp.SendIrpSynchronously(deviceObject.GetObject());
600 
601             if (!NT_SUCCESS(status)) {
602                 DoTraceLevelMessage(
603                     GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
604                     "!devobj %p declined to supply D3COLD_SUPPORT_INTERFACE",
605                     pdo);
606 
607                 RtlZeroMemory(&m_D3ColdInterface, sizeof(m_D3ColdInterface));
608             }
609         }
610     }
611     ObDereferenceObject(topOfStack);
612 }
613 
614 VOID
DropD3ColdInterface(VOID)615 FxPkgPnp::DropD3ColdInterface(
616     VOID
617     )
618 {
619     if (m_D3ColdInterface.InterfaceDereference != NULL) {
620         m_D3ColdInterface.InterfaceDereference(m_D3ColdInterface.Context);
621     }
622 
623     RtlZeroMemory(&m_D3ColdInterface, sizeof(m_D3ColdInterface));
624 }
625 
626