/*++ Copyright (c) Microsoft Corporation Module Name: FxResourceCollection.cpp Abstract: This module implements a base object for derived collection classes and the derived collection classes. Author: Environment: Both kernel and user mode Revision History: --*/ #include "fxsupportpch.hpp" extern "C" { #if defined(EVENT_TRACING) #include "FxResourceCollection.tmh" #endif } BOOLEAN FxResourceCollection::RemoveAndDelete( __in ULONG Index ) /*++ Routine Description: Removes an entry from the collection and then deletes it if found. The caller must have removal permissions to perform this action. Arguments: Index - zero based index into the collection at which to perform the removal Return Value: TRUE if the item was found and deleted, FALSE otherwise --*/ { FxObject* pObject; FxCollectionEntry* pEntry; KIRQL irql; if (IsRemoveAllowed() == FALSE) { DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP, "Removes not allowed on handle %p, remove at index %d" "failed", GetObjectHandle(), Index); FxVerifierDbgBreakPoint(GetDriverGlobals()); return FALSE; } pObject = NULL; Lock(&irql); pEntry = FindEntry(Index); if (pEntry != NULL) { // // Mark the list as changed so when we go to create a WDM resource list we // know if a new list is needed. // MarkChanged(); pObject = pEntry->m_Object; // // Remove the entry // RemoveEntry(pEntry); } Unlock(irql); if (pObject != NULL) { // // Delete the object since we created it // pObject->DeleteObject(); pObject = NULL; return TRUE; } else { return FALSE; } } _Must_inspect_result_ NTSTATUS FxResourceCollection::AddAt( __in ULONG Index, __in FxObject* Object ) /*++ Routine Description: Adds an object into the collection at the specified index. Arguments: Index - zero baesd index in which to insert into the list. WDF_INSERT_AT_END is a special value which indicates that the insertion is an append. Object - object to add Return Value: NTSTATUS --*/ { FxCollectionEntry *pNew; PLIST_ENTRY ple; NTSTATUS status; KIRQL irql; if (IsAddAllowed() == FALSE) { DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP, "Adds not allowed on handle %p, add at index %d" "failed", GetObjectHandle(), Index); FxVerifierDbgBreakPoint(GetDriverGlobals()); return STATUS_ACCESS_DENIED; } Lock(&irql); ple = NULL; status = STATUS_SUCCESS; pNew = AllocateEntry(GetDriverGlobals()); if (pNew != NULL) { // // Inserting at the current count (i.e. one past the end) is the same // as append. // if (Index == WDF_INSERT_AT_END || Index == Count()) { ple = &m_ListHead; } else { FxCollectionEntry* cur, *end; ULONG i; for (cur = Start(), end = End(), i = 0; cur != end; cur = cur->Next(), i++) { if (i == Index) { ple = &cur->m_ListEntry; break; } } if (ple == NULL) { delete pNew; status = STATUS_ARRAY_BOUNDS_EXCEEDED; } } } else { status = STATUS_INSUFFICIENT_RESOURCES; } if (NT_SUCCESS(status)) { PLIST_ENTRY blink; // ple now points to the list entry which we will insert our node // *before* blink = ple->Blink; // Link the previous with the new entry blink->Flink = &pNew->m_ListEntry; pNew->m_ListEntry.Blink = blink; // Link the current with the new entry pNew->m_ListEntry.Flink = ple; ple->Blink = &pNew->m_ListEntry; AddEntry(pNew, Object); // // Mark the list as changed so when we go to create a WDM resource list // we know if a new list is needed. // MarkChanged(); } Unlock(irql); if (!NT_SUCCESS(status)) { Object->DeleteFromFailedCreate(); } return status; } _Must_inspect_result_ NTSTATUS FxIoResList::BuildFromWdmList( __deref_in PIO_RESOURCE_LIST* WdmResourceList ) /*++ Routine Description: Builds up the collection with FxResourceIo objects based on the passed in WDM io resource list Arguments: WdmResourceList - list which specifies the io resource objects to create Return Value: NTSTATUS --*/ { PIO_RESOURCE_DESCRIPTOR pWdmDescriptor; ULONG i, count; NTSTATUS status; pWdmDescriptor = &(*WdmResourceList)->Descriptors[0]; count = (*WdmResourceList)->Count; status = STATUS_SUCCESS; for (i = 0; i < count; i++) { // // Now create a new resource object for each resource // in our list. // FxResourceIo *pResource; pResource = new(GetDriverGlobals()) FxResourceIo(GetDriverGlobals(), pWdmDescriptor); if (pResource == NULL) { // // We failed, clean up, and exit. Since we are only // keeping references on the master collection, if // we free this, everything else will go away too. // status = STATUS_INSUFFICIENT_RESOURCES; } if (NT_SUCCESS(status)) { status = pResource->AssignParentObject(this); // // See notes in previous AssignParentObject as to why // we are asserting. // ASSERT(NT_SUCCESS(status)); UNREFERENCED_PARAMETER(status); status = Add(pResource) ? STATUS_SUCCESS : STATUS_INSUFFICIENT_RESOURCES; } if (!NT_SUCCESS(status)) { break; } pWdmDescriptor++; } if (NT_SUCCESS(status)) { status = m_OwningList->Add(this) ? STATUS_SUCCESS : STATUS_INSUFFICIENT_RESOURCES; } if (NT_SUCCESS(status)) { *WdmResourceList = (PIO_RESOURCE_LIST) pWdmDescriptor; } return status; } _Must_inspect_result_ NTSTATUS FxCmResList::BuildFromWdmList( __in PCM_RESOURCE_LIST WdmResourceList, __in UCHAR AccessFlags ) /*++ Routine Description: Builds up the collection with FxResourceCm objects based on the passed in WDM io resource list. Arguments: WdmResourceList - list which specifies the io resource objects to create AccessFlags - permissions to be associated with the list Return Value: NTSTATUS --*/ { NTSTATUS status; // // Predispose to success // status = STATUS_SUCCESS; Clear(); m_AccessFlags = AccessFlags; if (WdmResourceList != NULL) { PCM_PARTIAL_RESOURCE_DESCRIPTOR pDescriptor; ULONG count, i; // // We only expect to see one full resource descriptor. // ASSERT(WdmResourceList->Count == 1); count = WdmResourceList->List[0].PartialResourceList.Count; pDescriptor = WdmResourceList->List[0].PartialResourceList.PartialDescriptors; for(i = 0; i < count; i++, pDescriptor++) { FxResourceCm *pResource; pResource = new(GetDriverGlobals()) FxResourceCm(GetDriverGlobals(), pDescriptor); if (pResource == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; } if (NT_SUCCESS(status)) { status = pResource->AssignParentObject(this); // // Since we control our own lifetime here, the assign should // always work. // ASSERT(NT_SUCCESS(status)); status = Add(pResource) ? STATUS_SUCCESS : STATUS_INSUFFICIENT_RESOURCES; } if (!NT_SUCCESS(status)) { Clear(); break; } } } return status; } _Must_inspect_result_ PCM_RESOURCE_LIST FxCmResList::CreateWdmList( __in __drv_strictTypeMatch(__drv_typeExpr) POOL_TYPE PoolType ) /*++ Routine Description: Allocates and initializes a WDM CM resource list based off of the current contents of this collection. Arguments: PoolType - the pool type from which to allocate the resource list Return Value: a new resource list upon success, NULL upon failure --*/ { PCM_RESOURCE_LIST pWdmResourceList; ULONG size; PFX_DRIVER_GLOBALS pFxDriverGlobals; pWdmResourceList = NULL; pFxDriverGlobals = GetDriverGlobals(); if (Count()) { // // NOTE: This function assumes all resources are on the same bus // and therefore there is only one FULL_RESOURCE_DESCRIPTOR. // size = sizeof(CM_RESOURCE_LIST) + (sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR) * (Count() - 1)); pWdmResourceList = (PCM_RESOURCE_LIST) MxMemory::MxAllocatePoolWithTag(PoolType, size, pFxDriverGlobals->Tag); if (pWdmResourceList != NULL) { PCM_PARTIAL_RESOURCE_DESCRIPTOR pDescriptor; FxCollectionEntry *cur, *end; RtlZeroMemory(pWdmResourceList, size); pWdmResourceList->Count = 1; // We only return one full descriptor pWdmResourceList->List[0].PartialResourceList.Version = 1; pWdmResourceList->List[0].PartialResourceList.Revision = 1; pWdmResourceList->List[0].PartialResourceList.Count = Count(); pDescriptor = pWdmResourceList->List[0].PartialResourceList.PartialDescriptors; end = End(); for (cur = Start(); cur != end; cur = cur->Next()) { FxResourceCm *pResource; pResource = (FxResourceCm*) cur->m_Object; RtlCopyMemory(pDescriptor, &pResource->m_Descriptor, sizeof(pResource->m_Descriptor)); pDescriptor++; } } } return pWdmResourceList; } ULONG FxCmResList::GetCount( VOID ) { ULONG count; KIRQL irql; Lock(&irql); count = Count(); Unlock(irql); return count; } PCM_PARTIAL_RESOURCE_DESCRIPTOR FxCmResList::GetDescriptor( __in ULONG Index ) { FxResourceCm* pObject; KIRQL irql; Lock(&irql); pObject = (FxResourceCm*) GetItem(Index); Unlock(irql); if (pObject == NULL) { return NULL; } else { // // Copy the current descriptor to the clone and return it // RtlCopyMemory(&pObject->m_DescriptorClone, &pObject->m_Descriptor, sizeof(pObject->m_Descriptor)); return &pObject->m_DescriptorClone; } } _Must_inspect_result_ FxIoResReqList* FxIoResReqList::_CreateFromWdmList( __in PFX_DRIVER_GLOBALS FxDriverGlobals, __in PIO_RESOURCE_REQUIREMENTS_LIST WdmRequirementsList, __in UCHAR AccessFlags ) /*++ Routine Description: Allocates and populates an FxIoResReqList based on the WDM resource requirements list. Arguments: WdmRequirementsList - a list of IO_RESOURCE_LISTs which will indicate how to fill in the returned collection object AccessFlags - permissions to associate with the newly created object Return Value: a new object upon success, NULL upon failure --*/ { FxIoResReqList* pIoResReqList; ULONG i; pIoResReqList = new(FxDriverGlobals, WDF_NO_OBJECT_ATTRIBUTES) FxIoResReqList(FxDriverGlobals, AccessFlags); if (pIoResReqList != NULL) { PIO_RESOURCE_LIST pWdmResourceList; NTSTATUS status; if (WdmRequirementsList == NULL) { return pIoResReqList; } status = STATUS_SUCCESS; pWdmResourceList = &WdmRequirementsList->List[0]; pIoResReqList->m_InterfaceType = WdmRequirementsList->InterfaceType; pIoResReqList->m_SlotNumber = WdmRequirementsList->SlotNumber; for (i = 0; i < WdmRequirementsList->AlternativeLists; i++) { FxIoResList *pResList; pResList = new(FxDriverGlobals, WDF_NO_OBJECT_ATTRIBUTES) FxIoResList(FxDriverGlobals, pIoResReqList); if (pResList != NULL) { status = pResList->AssignParentObject(pIoResReqList); // // Since we control our own lifetime, assigning the parent should // never fail. // ASSERT(NT_SUCCESS(status)); status = pResList->BuildFromWdmList(&pWdmResourceList); } else { // // We failed to allocate a child collection. Clean up // and break out of the loop. // status = STATUS_INSUFFICIENT_RESOURCES; } if (!NT_SUCCESS(status)) { break; } } if (!NT_SUCCESS(status)) { // // Cleanup and return a NULL object // pIoResReqList->DeleteObject(); pIoResReqList = NULL; } } return pIoResReqList; } _Must_inspect_result_ PIO_RESOURCE_REQUIREMENTS_LIST FxIoResReqList::CreateWdmList( VOID ) /*++ Routine Description: Creates a WDM io resource requirements list based off of the current contents of the collection Arguments: None Return Value: new WDM io resource requirements list allocated out of paged pool upon success, NULL upon failure or an empty list --*/ { PIO_RESOURCE_REQUIREMENTS_LIST pRequirementsList; FxCollectionEntry *cur, *end; NTSTATUS status; ULONG totalDescriptors; ULONG size; ULONG count; ULONG tmp; PFX_DRIVER_GLOBALS pFxDriverGlobals; totalDescriptors = 0; pRequirementsList = NULL; count = Count(); pFxDriverGlobals = GetDriverGlobals(); if (count > 0) { // // The collection object should contain a set of child collections // with each of the various requirement lists. Use the number of // these collections to determine the size of our requirements // list. // end = End(); for (cur = Start(); cur != end; cur = cur->Next()) { status = RtlULongAdd(totalDescriptors, ((FxIoResList *) cur->m_Object)->Count(), &totalDescriptors); if (!NT_SUCCESS(status)) { goto Overflow; } } // // We now have enough information to determine how much memory we // need to allocate for our requirements list. // // size = sizeof(IO_RESOURCE_REQUIREMENTS_LIST) + // (sizeof(IO_RESOURCE_LIST) * (count - 1)) + // (sizeof(IO_RESOURCE_DESCRIPTOR) * totalDescriptors) - // (sizeof(IO_RESOURCE_DESCRIPTOR) * count); // // sizeof(IO_RESOURCE_DESCRIPTOR) * count is subtracted off because // each IO_RESOURCE_LIST has an embedded IO_RESOURCE_DESCRIPTOR in it // and we don't want to overallocated. // // // To handle overflow each mathematical operation is split out into an // overflow safe call. // size = sizeof(IO_RESOURCE_REQUIREMENTS_LIST); // sizeof(IO_RESOURCE_LIST) * (count - 1) status = RtlULongMult(sizeof(IO_RESOURCE_LIST), count - 1, &tmp); if (!NT_SUCCESS(status)) { goto Overflow; } status = RtlULongAdd(size, tmp, &size); if (!NT_SUCCESS(status)) { goto Overflow; } // (sizeof(IO_RESOURCE_DESCRIPTOR) * totalDescriptors) status = RtlULongMult(sizeof(IO_RESOURCE_DESCRIPTOR), totalDescriptors, &tmp); if (!NT_SUCCESS(status)) { goto Overflow; } status = RtlULongAdd(size, tmp, &size); if (!NT_SUCCESS(status)) { goto Overflow; } // - sizeof(IO_RESOURCE_DESCRIPTOR) * Count() (note the subtraction!) status = RtlULongMult(sizeof(IO_RESOURCE_DESCRIPTOR), count, &tmp); if (!NT_SUCCESS(status)) { goto Overflow; } // Sub, not Add! status = RtlULongSub(size, tmp, &size); if (!NT_SUCCESS(status)) { goto Overflow; } pRequirementsList = (PIO_RESOURCE_REQUIREMENTS_LIST) MxMemory::MxAllocatePoolWithTag(PagedPool, size, pFxDriverGlobals->Tag); if (pRequirementsList != NULL) { PIO_RESOURCE_LIST pList; FxResourceIo *pResource; pList = pRequirementsList->List; // // Start by zero initializing our structure // RtlZeroMemory(pRequirementsList, size); // // InterfaceType and BusNumber are unused for WDM, but InterfaceType // is used by the arbiters. // pRequirementsList->InterfaceType = m_InterfaceType; pRequirementsList->SlotNumber = m_SlotNumber; // // Now populate the requirements list with the resources from // our collections. // pRequirementsList->ListSize = size; pRequirementsList->AlternativeLists = Count(); end = End(); for (cur = Start(); cur != end; cur = cur->Next()) { FxIoResList* pIoResList; PIO_RESOURCE_DESCRIPTOR pDescriptor; FxCollectionEntry *pIoResCur, *pIoResEnd; pIoResList = (FxIoResList*) cur->m_Object; pList->Version = 1; pList->Revision = 1; pList->Count = pIoResList->Count(); pDescriptor = pList->Descriptors; pIoResEnd = pIoResList->End(); for (pIoResCur = pIoResList->Start(); pIoResCur != pIoResEnd; pIoResCur = pIoResCur->Next()) { pResource = (FxResourceIo *) pIoResCur->m_Object; RtlCopyMemory(pDescriptor, &pResource->m_Descriptor, sizeof(pResource->m_Descriptor)); pDescriptor++; } pList = (PIO_RESOURCE_LIST) pDescriptor; } } } return pRequirementsList; Overflow: DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP, "Integer overflow occured when computing size of " "IO_RESOURCE_REQUIREMENTS_LIST"); return NULL; }