1 /*++
2 
3 Copyright (c) Microsoft Corporation
4 
5 Module Name:
6 
7     FxResourceCollection.cpp
8 
9 Abstract:
10 
11     This module implements a base object for derived collection classes and
12     the derived collection classes.
13 
14 Author:
15 
16 
17 
18 Environment:
19 
20     Both kernel and user mode
21 
22 Revision History:
23 
24 --*/
25 
26 #include "fxsupportpch.hpp"
27 
28 extern "C" {
29 #if defined(EVENT_TRACING)
30 #include "FxResourceCollection.tmh"
31 #endif
32 }
33 
34 BOOLEAN
RemoveAndDelete(__in ULONG Index)35 FxResourceCollection::RemoveAndDelete(
36     __in ULONG Index
37     )
38 /*++
39 
40 Routine Description:
41     Removes an entry from the collection and then deletes it if found.  The
42     caller must have removal permissions to perform this action.
43 
44 Arguments:
45     Index - zero based index into the collection at which to perform the removal
46 
47 Return Value:
48     TRUE if the item was found and deleted, FALSE otherwise
49 
50   --*/
51 {
52     FxObject* pObject;
53     FxCollectionEntry* pEntry;
54     KIRQL irql;
55 
56     if (IsRemoveAllowed() == FALSE) {
57         DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
58                             "Removes not allowed on handle %p, remove at index %d"
59                             "failed", GetObjectHandle(), Index);
60 
61         FxVerifierDbgBreakPoint(GetDriverGlobals());
62         return FALSE;
63     }
64 
65     pObject = NULL;
66 
67     Lock(&irql);
68 
69     pEntry = FindEntry(Index);
70     if (pEntry != NULL) {
71 
72         //
73         // Mark the list as changed so when we go to create a WDM resource list we
74         // know if a new list is needed.
75         //
76         MarkChanged();
77 
78         pObject = pEntry->m_Object;
79 
80         //
81         // Remove the entry
82         //
83         RemoveEntry(pEntry);
84     }
85     Unlock(irql);
86 
87     if (pObject != NULL) {
88         //
89         // Delete the object since we created it
90         //
91         pObject->DeleteObject();
92         pObject = NULL;
93 
94         return TRUE;
95     }
96     else {
97         return FALSE;
98     }
99 }
100 
101 _Must_inspect_result_
102 NTSTATUS
AddAt(__in ULONG Index,__in FxObject * Object)103 FxResourceCollection::AddAt(
104     __in ULONG Index,
105     __in FxObject* Object
106     )
107 /*++
108 
109 Routine Description:
110     Adds an object into the collection at the specified index.
111 
112 Arguments:
113     Index - zero baesd index in which to insert into the list.   WDF_INSERT_AT_END
114             is a special value which indicates that the insertion is an append.
115 
116     Object - object to add
117 
118 Return Value:
119     NTSTATUS
120 
121   --*/
122 {
123     FxCollectionEntry *pNew;
124     PLIST_ENTRY ple;
125     NTSTATUS status;
126     KIRQL irql;
127 
128     if (IsAddAllowed() == FALSE) {
129         DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
130                             "Adds not allowed on handle %p, add at index %d"
131                             "failed", GetObjectHandle(), Index);
132 
133         FxVerifierDbgBreakPoint(GetDriverGlobals());
134 
135         return STATUS_ACCESS_DENIED;
136     }
137 
138     Lock(&irql);
139 
140     ple = NULL;
141     status = STATUS_SUCCESS;
142 
143     pNew = AllocateEntry(GetDriverGlobals());
144 
145     if (pNew != NULL) {
146         //
147         // Inserting at the current count (i.e. one past the end) is the same
148         // as append.
149         //
150         if (Index == WDF_INSERT_AT_END || Index == Count()) {
151             ple = &m_ListHead;
152         }
153         else {
154             FxCollectionEntry* cur, *end;
155             ULONG i;
156 
157             for (cur = Start(), end = End(), i = 0;
158                  cur != end;
159                  cur = cur->Next(), i++) {
160                 if (i == Index) {
161                     ple = &cur->m_ListEntry;
162                     break;
163                 }
164             }
165 
166             if (ple == NULL) {
167                 delete pNew;
168                 status = STATUS_ARRAY_BOUNDS_EXCEEDED;
169             }
170         }
171     }
172     else {
173         status = STATUS_INSUFFICIENT_RESOURCES;
174     }
175 
176     if (NT_SUCCESS(status)) {
177         PLIST_ENTRY blink;
178 
179         // ple now points to the list entry which we will insert our node
180         // *before*
181 
182         blink = ple->Blink;
183 
184         // Link the previous with the new entry
185         blink->Flink = &pNew->m_ListEntry;
186         pNew->m_ListEntry.Blink = blink;
187 
188         // Link the current with the new entry
189         pNew->m_ListEntry.Flink = ple;
190         ple->Blink = &pNew->m_ListEntry;
191 
192         AddEntry(pNew, Object);
193 
194         //
195         // Mark the list as changed so when we go to create a WDM resource list
196         // we know if a new list is needed.
197         //
198         MarkChanged();
199     }
200 
201     Unlock(irql);
202 
203     if (!NT_SUCCESS(status)) {
204         Object->DeleteFromFailedCreate();
205     }
206 
207     return status;
208 }
209 
210 _Must_inspect_result_
211 NTSTATUS
BuildFromWdmList(__deref_in PIO_RESOURCE_LIST * WdmResourceList)212 FxIoResList::BuildFromWdmList(
213     __deref_in PIO_RESOURCE_LIST* WdmResourceList
214     )
215 /*++
216 
217 Routine Description:
218     Builds up the collection with FxResourceIo objects based on the passed in
219     WDM io resource list
220 
221 Arguments:
222     WdmResourceList - list which specifies the io resource objects to create
223 
224 Return Value:
225     NTSTATUS
226 
227   --*/
228 {
229     PIO_RESOURCE_DESCRIPTOR pWdmDescriptor;
230     ULONG i, count;
231     NTSTATUS status;
232 
233     pWdmDescriptor = &(*WdmResourceList)->Descriptors[0];
234     count = (*WdmResourceList)->Count;
235     status = STATUS_SUCCESS;
236 
237     for (i = 0; i < count; i++) {
238         //
239         // Now create a new resource object for each resource
240         // in our list.
241         //
242         FxResourceIo *pResource;
243 
244         pResource = new(GetDriverGlobals())
245             FxResourceIo(GetDriverGlobals(), pWdmDescriptor);
246 
247         if (pResource == NULL) {
248             //
249             // We failed, clean up, and exit.  Since we are only
250             // keeping references on the master collection, if
251             // we free this, everything else will go away too.
252             //
253             status = STATUS_INSUFFICIENT_RESOURCES;
254         }
255 
256         if (NT_SUCCESS(status)) {
257             status = pResource->AssignParentObject(this);
258 
259             //
260             // See notes in previous AssignParentObject as to why
261             // we are asserting.
262             //
263             ASSERT(NT_SUCCESS(status));
264             UNREFERENCED_PARAMETER(status);
265 
266             status = Add(pResource) ? STATUS_SUCCESS : STATUS_INSUFFICIENT_RESOURCES;
267         }
268 
269         if (!NT_SUCCESS(status)) {
270             break;
271         }
272 
273         pWdmDescriptor++;
274     }
275 
276     if (NT_SUCCESS(status)) {
277         status = m_OwningList->Add(this) ? STATUS_SUCCESS : STATUS_INSUFFICIENT_RESOURCES;
278     }
279 
280     if (NT_SUCCESS(status)) {
281         *WdmResourceList = (PIO_RESOURCE_LIST) pWdmDescriptor;
282     }
283 
284     return status;
285 }
286 
287 _Must_inspect_result_
288 NTSTATUS
BuildFromWdmList(__in PCM_RESOURCE_LIST WdmResourceList,__in UCHAR AccessFlags)289 FxCmResList::BuildFromWdmList(
290     __in PCM_RESOURCE_LIST WdmResourceList,
291     __in UCHAR AccessFlags
292     )
293 /*++
294 
295 Routine Description:
296     Builds up the collection with FxResourceCm objects based on the passed in
297     WDM io resource list.
298 
299 Arguments:
300     WdmResourceList - list which specifies the io resource objects to create
301 
302     AccessFlags - permissions to be associated with the list
303 
304 Return Value:
305     NTSTATUS
306 
307   --*/
308 {
309     NTSTATUS status;
310 
311     //
312     // Predispose to success
313     //
314     status = STATUS_SUCCESS;
315 
316     Clear();
317 
318     m_AccessFlags = AccessFlags;
319 
320     if (WdmResourceList != NULL) {
321         PCM_PARTIAL_RESOURCE_DESCRIPTOR pDescriptor;
322         ULONG count, i;
323 
324         //
325         // We only expect to see one full resource descriptor.
326         //
327         ASSERT(WdmResourceList->Count == 1);
328 
329         count = WdmResourceList->List[0].PartialResourceList.Count;
330         pDescriptor = WdmResourceList->List[0].PartialResourceList.PartialDescriptors;
331 
332         for(i = 0; i < count; i++, pDescriptor++) {
333             FxResourceCm *pResource;
334 
335             pResource = new(GetDriverGlobals())
336                 FxResourceCm(GetDriverGlobals(), pDescriptor);
337 
338             if (pResource == NULL) {
339                 status = STATUS_INSUFFICIENT_RESOURCES;
340             }
341 
342             if (NT_SUCCESS(status)) {
343                 status = pResource->AssignParentObject(this);
344 
345                 //
346                 // Since we control our own lifetime here, the assign should
347                 // always work.
348                 //
349                 ASSERT(NT_SUCCESS(status));
350 
351                 status = Add(pResource) ? STATUS_SUCCESS : STATUS_INSUFFICIENT_RESOURCES;
352             }
353 
354             if (!NT_SUCCESS(status)) {
355                 Clear();
356                 break;
357             }
358         }
359     }
360 
361     return status;
362 }
363 
364 _Must_inspect_result_
365 PCM_RESOURCE_LIST
CreateWdmList(__in __drv_strictTypeMatch (__drv_typeExpr)POOL_TYPE PoolType)366 FxCmResList::CreateWdmList(
367     __in __drv_strictTypeMatch(__drv_typeExpr) POOL_TYPE PoolType
368     )
369 /*++
370 
371 Routine Description:
372     Allocates and initializes a WDM CM resource list based off of the current
373     contents of this collection.
374 
375 Arguments:
376     PoolType - the pool type from which to allocate the resource list
377 
378 Return Value:
379     a new resource list upon success, NULL upon failure
380 
381   --*/
382 {
383     PCM_RESOURCE_LIST pWdmResourceList;
384     ULONG size;
385     PFX_DRIVER_GLOBALS pFxDriverGlobals;
386 
387     pWdmResourceList = NULL;
388     pFxDriverGlobals = GetDriverGlobals();
389 
390     if (Count()) {
391         //
392         // NOTE: This function assumes all resources are on the same bus
393         // and therefore there is only one FULL_RESOURCE_DESCRIPTOR.
394         //
395         size = sizeof(CM_RESOURCE_LIST) +
396                (sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR) * (Count() - 1));
397 
398         pWdmResourceList = (PCM_RESOURCE_LIST)
399             MxMemory::MxAllocatePoolWithTag(PoolType, size, pFxDriverGlobals->Tag);
400 
401         if (pWdmResourceList != NULL) {
402             PCM_PARTIAL_RESOURCE_DESCRIPTOR pDescriptor;
403             FxCollectionEntry *cur, *end;
404 
405             RtlZeroMemory(pWdmResourceList, size);
406 
407             pWdmResourceList->Count = 1;  // We only return one full descriptor
408 
409             pWdmResourceList->List[0].PartialResourceList.Version  = 1;
410             pWdmResourceList->List[0].PartialResourceList.Revision = 1;
411             pWdmResourceList->List[0].PartialResourceList.Count = Count();
412 
413             pDescriptor =
414                 pWdmResourceList->List[0].PartialResourceList.PartialDescriptors;
415 
416             end = End();
417             for (cur = Start(); cur != end; cur = cur->Next()) {
418                 FxResourceCm *pResource;
419 
420                 pResource = (FxResourceCm*) cur->m_Object;
421 
422                 RtlCopyMemory(pDescriptor,
423                               &pResource->m_Descriptor,
424                               sizeof(pResource->m_Descriptor));
425                 pDescriptor++;
426             }
427         }
428     }
429 
430     return pWdmResourceList;
431 }
432 
433 ULONG
GetCount(VOID)434 FxCmResList::GetCount(
435     VOID
436     )
437 {
438     ULONG count;
439     KIRQL irql;
440 
441     Lock(&irql);
442     count = Count();
443     Unlock(irql);
444 
445     return count;
446 }
447 
448 PCM_PARTIAL_RESOURCE_DESCRIPTOR
GetDescriptor(__in ULONG Index)449 FxCmResList::GetDescriptor(
450     __in ULONG Index
451     )
452 {
453     FxResourceCm* pObject;
454     KIRQL irql;
455 
456     Lock(&irql);
457     pObject = (FxResourceCm*) GetItem(Index);
458     Unlock(irql);
459 
460     if (pObject == NULL) {
461         return NULL;
462     }
463     else {
464         //
465         // Copy the current descriptor to the clone and return it
466         //
467         RtlCopyMemory(&pObject->m_DescriptorClone,
468                       &pObject->m_Descriptor,
469                       sizeof(pObject->m_Descriptor));
470 
471         return &pObject->m_DescriptorClone;
472     }
473 }
474 
475 _Must_inspect_result_
476 FxIoResReqList*
_CreateFromWdmList(__in PFX_DRIVER_GLOBALS FxDriverGlobals,__in PIO_RESOURCE_REQUIREMENTS_LIST WdmRequirementsList,__in UCHAR AccessFlags)477 FxIoResReqList::_CreateFromWdmList(
478     __in PFX_DRIVER_GLOBALS FxDriverGlobals,
479     __in PIO_RESOURCE_REQUIREMENTS_LIST WdmRequirementsList,
480     __in UCHAR AccessFlags
481     )
482 /*++
483 
484 Routine Description:
485     Allocates and populates an FxIoResReqList based on the WDM resource
486     requirements list.
487 
488 Arguments:
489     WdmRequirementsList - a list of IO_RESOURCE_LISTs which will indicate how
490                           to  fill in the returned collection object
491 
492     AccessFlags - permissions to associate with the newly created object
493 
494 Return Value:
495     a new object upon success, NULL upon failure
496 
497   --*/
498 
499 {
500     FxIoResReqList* pIoResReqList;
501     ULONG i;
502 
503     pIoResReqList = new(FxDriverGlobals, WDF_NO_OBJECT_ATTRIBUTES)
504         FxIoResReqList(FxDriverGlobals, AccessFlags);
505 
506     if (pIoResReqList != NULL) {
507         PIO_RESOURCE_LIST pWdmResourceList;
508         NTSTATUS status;
509 
510         if (WdmRequirementsList == NULL) {
511             return pIoResReqList;
512         }
513 
514         status = STATUS_SUCCESS;
515         pWdmResourceList = &WdmRequirementsList->List[0];
516 
517         pIoResReqList->m_InterfaceType = WdmRequirementsList->InterfaceType;
518         pIoResReqList->m_SlotNumber = WdmRequirementsList->SlotNumber;
519 
520         for (i = 0; i < WdmRequirementsList->AlternativeLists; i++) {
521             FxIoResList *pResList;
522 
523             pResList = new(FxDriverGlobals, WDF_NO_OBJECT_ATTRIBUTES)
524                 FxIoResList(FxDriverGlobals, pIoResReqList);
525 
526             if (pResList != NULL) {
527                 status = pResList->AssignParentObject(pIoResReqList);
528 
529                 //
530                 // Since we control our own lifetime, assigning the parent should
531                 // never fail.
532                 //
533                 ASSERT(NT_SUCCESS(status));
534 
535                 status = pResList->BuildFromWdmList(&pWdmResourceList);
536             }
537             else {
538                 //
539                 // We failed to allocate a child collection.  Clean up
540                 // and break out of the loop.
541                 //
542                 status = STATUS_INSUFFICIENT_RESOURCES;
543             }
544 
545             if (!NT_SUCCESS(status)) {
546                 break;
547             }
548         }
549 
550         if (!NT_SUCCESS(status)) {
551             //
552             // Cleanup and return a NULL object
553             //
554             pIoResReqList->DeleteObject();
555             pIoResReqList = NULL;
556         }
557     }
558 
559     return pIoResReqList;
560 }
561 
562 _Must_inspect_result_
563 PIO_RESOURCE_REQUIREMENTS_LIST
CreateWdmList(VOID)564 FxIoResReqList::CreateWdmList(
565     VOID
566     )
567 /*++
568 
569 Routine Description:
570     Creates a WDM io resource requirements list based off of the current
571     contents of the collection
572 
573 Arguments:
574     None
575 
576 Return Value:
577     new WDM io resource requirements list allocated out of paged pool upon success,
578     NULL upon failure or an empty list
579 
580   --*/
581 {
582     PIO_RESOURCE_REQUIREMENTS_LIST pRequirementsList;
583     FxCollectionEntry *cur, *end;
584     NTSTATUS status;
585     ULONG totalDescriptors;
586     ULONG size;
587     ULONG count;
588     ULONG tmp;
589     PFX_DRIVER_GLOBALS pFxDriverGlobals;
590 
591     totalDescriptors = 0;
592     pRequirementsList = NULL;
593 
594     count = Count();
595     pFxDriverGlobals = GetDriverGlobals();
596 
597     if (count > 0) {
598         //
599         // The collection object should contain a set of child collections
600         // with each of the various requirement lists.  Use the number of
601         // these collections to determine the size of our requirements
602         // list.
603         //
604         end = End();
605         for (cur = Start(); cur != end; cur = cur->Next()) {
606             status = RtlULongAdd(totalDescriptors,
607                                  ((FxIoResList *) cur->m_Object)->Count(),
608                                  &totalDescriptors);
609 
610             if (!NT_SUCCESS(status)) {
611                 goto Overflow;
612             }
613         }
614 
615         //
616         // We now have enough information to determine how much memory we
617         // need to allocate for our requirements list.
618         //
619         // size = sizeof(IO_RESOURCE_REQUIREMENTS_LIST) +
620         //        (sizeof(IO_RESOURCE_LIST) * (count - 1)) +
621         //        (sizeof(IO_RESOURCE_DESCRIPTOR) * totalDescriptors) -
622         //         (sizeof(IO_RESOURCE_DESCRIPTOR) * count);
623         //
624         // sizeof(IO_RESOURCE_DESCRIPTOR) * count is subtracted off because
625         // each IO_RESOURCE_LIST has an embedded IO_RESOURCE_DESCRIPTOR in it
626         // and we don't want to overallocated.
627         //
628 
629         //
630         // To handle overflow each mathematical operation is split out into an
631         // overflow safe call.
632         //
633         size = sizeof(IO_RESOURCE_REQUIREMENTS_LIST);
634 
635         // sizeof(IO_RESOURCE_LIST) * (count - 1)
636         status = RtlULongMult(sizeof(IO_RESOURCE_LIST), count - 1, &tmp);
637         if (!NT_SUCCESS(status)) {
638             goto Overflow;
639         }
640 
641         status = RtlULongAdd(size, tmp, &size);
642         if (!NT_SUCCESS(status)) {
643             goto Overflow;
644         }
645 
646         // (sizeof(IO_RESOURCE_DESCRIPTOR) * totalDescriptors)
647         status = RtlULongMult(sizeof(IO_RESOURCE_DESCRIPTOR),
648                               totalDescriptors,
649                               &tmp);
650         if (!NT_SUCCESS(status)) {
651             goto Overflow;
652         }
653 
654         status = RtlULongAdd(size, tmp, &size);
655         if (!NT_SUCCESS(status)) {
656             goto Overflow;
657         }
658 
659         //  - sizeof(IO_RESOURCE_DESCRIPTOR) * Count() (note the subtraction!)
660         status = RtlULongMult(sizeof(IO_RESOURCE_DESCRIPTOR), count, &tmp);
661         if (!NT_SUCCESS(status)) {
662             goto Overflow;
663         }
664 
665         // Sub, not Add!
666         status = RtlULongSub(size, tmp, &size);
667         if (!NT_SUCCESS(status)) {
668             goto Overflow;
669         }
670 
671         pRequirementsList = (PIO_RESOURCE_REQUIREMENTS_LIST)
672             MxMemory::MxAllocatePoolWithTag(PagedPool, size, pFxDriverGlobals->Tag);
673 
674         if (pRequirementsList != NULL) {
675             PIO_RESOURCE_LIST pList;
676             FxResourceIo *pResource;
677 
678             pList = pRequirementsList->List;
679 
680             //
681             // Start by zero initializing our structure
682             //
683             RtlZeroMemory(pRequirementsList, size);
684 
685             //
686             // InterfaceType and BusNumber are unused for WDM, but InterfaceType
687             // is used by the arbiters.
688             //
689             pRequirementsList->InterfaceType = m_InterfaceType;
690 
691             pRequirementsList->SlotNumber = m_SlotNumber;
692 
693             //
694             // Now populate the requirements list with the resources from
695             // our collections.
696             //
697             pRequirementsList->ListSize = size;
698             pRequirementsList->AlternativeLists = Count();
699 
700             end = End();
701             for (cur = Start(); cur != end; cur = cur->Next()) {
702                 FxIoResList* pIoResList;
703                 PIO_RESOURCE_DESCRIPTOR pDescriptor;
704                 FxCollectionEntry *pIoResCur, *pIoResEnd;
705 
706                 pIoResList = (FxIoResList*) cur->m_Object;
707 
708                 pList->Version  = 1;
709                 pList->Revision = 1;
710                 pList->Count = pIoResList->Count();
711 
712                 pDescriptor = pList->Descriptors;
713 
714                 pIoResEnd = pIoResList->End();
715                 for (pIoResCur = pIoResList->Start();
716                      pIoResCur != pIoResEnd;
717                      pIoResCur = pIoResCur->Next()) {
718 
719                     pResource = (FxResourceIo *) pIoResCur->m_Object;
720                     RtlCopyMemory(pDescriptor,
721                                   &pResource->m_Descriptor,
722                                   sizeof(pResource->m_Descriptor));
723                     pDescriptor++;
724                 }
725 
726                 pList = (PIO_RESOURCE_LIST) pDescriptor;
727             }
728         }
729     }
730 
731     return pRequirementsList;
732 
733 Overflow:
734     DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
735                         "Integer overflow occured when computing size of "
736                         "IO_RESOURCE_REQUIREMENTS_LIST");
737 
738     return NULL;
739 }
740