/*++ Copyright (c) Microsoft Corporation. All rights reserved. Module Name: pnpenum.c Abstract: This module contains routines to perform device enumeration Author: Shie-Lin Tzong (shielint) Sept. 5, 1996. Revision History: James Cavalaris (t-jcaval) July 29, 1997. Added IopProcessCriticalDeviceRoutine. --*/ #include "pnpmgrp.h" #pragma hdrstop #include #ifdef POOL_TAGGING #undef ExAllocatePool #define ExAllocatePool(a,b) ExAllocatePoolWithTag(a,b,'nepP') #endif #define MAX_REENUMERATION_ATTEMPTS 32 typedef struct _DRIVER_LIST_ENTRY DRIVER_LIST_ENTRY, *PDRIVER_LIST_ENTRY; typedef struct _PI_DEVICE_REQUEST { LIST_ENTRY ListEntry; PDEVICE_OBJECT DeviceObject; DEVICE_REQUEST_TYPE RequestType; BOOLEAN ReorderingBarrier; ULONG_PTR RequestArgument; PKEVENT CompletionEvent; PNTSTATUS CompletionStatus; } PI_DEVICE_REQUEST, *PPI_DEVICE_REQUEST; struct _DRIVER_LIST_ENTRY { PDRIVER_OBJECT DriverObject; PDRIVER_LIST_ENTRY NextEntry; }; typedef enum _ADD_DRIVER_STAGE { LowerDeviceFilters = 0, LowerClassFilters, DeviceService, UpperDeviceFilters, UpperClassFilters, MaximumAddStage } ADD_DRIVER_STAGE; typedef enum _ENUM_TYPE { EnumTypeNone, EnumTypeShallow, EnumTypeDeep } ENUM_TYPE; #define VerifierTypeFromServiceType(service) \ (VF_DEVOBJ_TYPE) (service + 2) typedef struct { PDEVICE_NODE DeviceNode; BOOLEAN LoadDriver; PADD_CONTEXT AddContext; PDRIVER_LIST_ENTRY DriverLists[MaximumAddStage]; } QUERY_CONTEXT, *PQUERY_CONTEXT; // // Hash routine from CNTFS (see cntfs\prefxsup.c) // (used here in the construction of unique ids) // #define HASH_UNICODE_STRING( _pustr, _phash ) { \ PWCHAR _p = (_pustr)->Buffer; \ PWCHAR _ep = _p + ((_pustr)->Length/sizeof(WCHAR)); \ ULONG _chHolder =0; \ \ while( _p < _ep ) { \ _chHolder = 37 * _chHolder + (unsigned int) (*_p++); \ } \ \ *(_phash) = abs(314159269 * _chHolder) % 1000000007; \ } // Parent prefixes are of the form %x&%x&%x #define MAX_PARENT_PREFIX (8 + 8 + 8 + 2) #if 0 #define ASSERT_INITED(x) \ ASSERTMSG("DO_DEVICE_INITIALIZING not cleared on device object", \ ((((x)->Flags) & DO_DEVICE_INITIALIZING) == 0)) #else #if DBG #define ASSERT_INITED(x) \ if (((x)->Flags & DO_DEVICE_INITIALIZING) != 0) \ DbgPrint("DO_DEVICE_INITIALIZING flag not cleared on DO %#08lx\n", x); #else #define ASSERT_INITED(x) /* nothing */ #endif #endif #define PiSetDeviceInstanceSzValue(k, n, v) { \ if (k && *(v)) { \ UNICODE_STRING u; \ PiWstrToUnicodeString(&u, n); \ ZwSetValueKey( \ k, \ &u, \ TITLE_INDEX_VALUE, \ REG_SZ, \ *(v), \ (ULONG)((wcslen(*(v))+1) * sizeof(WCHAR))); \ } \ if (*v) { \ ExFreePool(*v); \ *(v) = NULL; \ } \ } #define PiSetDeviceInstanceMultiSzValue(k, n, v, s) { \ if (k && *(v)) { \ UNICODE_STRING u; \ PiWstrToUnicodeString(&u, n); \ ZwSetValueKey( \ k, \ &u, \ TITLE_INDEX_VALUE, \ REG_MULTI_SZ, \ *(v), \ s); \ } \ if (*(v)) { \ ExFreePool(*v); \ *(v) = NULL; \ } \ } #if DBG VOID PipAssertDevnodesInConsistentState( VOID ); #else #define PipAssertDevnodesInConsistentState() #endif NTSTATUS PipCallDriverAddDevice( IN PDEVICE_NODE DeviceNode, IN BOOLEAN LoadDriver, IN PADD_CONTEXT AddContext ); NTSTATUS PipCallDriverAddDeviceQueryRoutine( IN PWSTR ValueName, IN ULONG ValueType, IN PWCHAR ValueData, IN ULONG ValueLength, IN PQUERY_CONTEXT Context, IN ULONG ServiceType ); NTSTATUS PipChangeDeviceObjectFromRegistryProperties( IN PDEVICE_OBJECT PhysicalDeviceObject, IN HANDLE DeviceClassPropKey, IN HANDLE DevicePropKey, IN BOOLEAN UsePdoCharacteristics ); VOID PipDeviceActionWorker( IN PVOID Context ); NTSTATUS PipEnumerateDevice( IN PDEVICE_NODE DeviceNode, IN BOOLEAN Synchronous ); BOOLEAN PipGetRegistryDwordWithFallback( IN PUNICODE_STRING valueName, IN HANDLE PrimaryKey, IN HANDLE SecondaryKey, IN OUT PULONG Value ); PSECURITY_DESCRIPTOR PipGetRegistrySecurityWithFallback( IN PUNICODE_STRING valueName, IN HANDLE PrimaryKey, IN HANDLE SecondaryKey ); NTSTATUS PipMakeGloballyUniqueId( IN PDEVICE_OBJECT DeviceObject, IN PWCHAR UniqueId, OUT PWCHAR *GloballyUniqueId ); NTSTATUS PipProcessCriticalDeviceRoutine( IN HANDLE hDevInstance, IN PBOOLEAN FoundMatch, IN PUNICODE_STRING ServiceName, IN PUNICODE_STRING ClassGuid, IN PUNICODE_STRING Driver, IN PUNICODE_STRING LowerFilters, IN PUNICODE_STRING UpperFilters ); NTSTATUS PipProcessDevNodeTree( IN PDEVICE_NODE SubtreeRootDeviceNode, IN BOOLEAN LoadDriver, IN BOOLEAN ReallocateResources, IN ENUM_TYPE EnumType, IN BOOLEAN Synchronous, IN BOOLEAN ProcessOnlyIntermediateStates, IN PADD_CONTEXT AddContext, IN PPI_DEVICE_REQUEST Request ); NTSTATUS PipProcessNewDeviceNode( IN OUT PDEVICE_NODE DeviceNode ); NTSTATUS PiProcessQueryDeviceState( IN PDEVICE_OBJECT DeviceObject ); NTSTATUS PipProcessRestartPhase1( IN PDEVICE_NODE DeviceNode, IN BOOLEAN Synchronous ); NTSTATUS PipProcessRestartPhase2( IN PDEVICE_NODE DeviceNode ); NTSTATUS PipProcessStartPhase1( IN PDEVICE_NODE DeviceNode, IN BOOLEAN Synchronous ); NTSTATUS PipProcessStartPhase2( IN PDEVICE_NODE DeviceNode ); NTSTATUS PipProcessStartPhase3( IN PDEVICE_NODE DeviceNode ); NTSTATUS PiRestartDevice( IN PPI_DEVICE_REQUEST Request ); NTSTATUS PiProcessHaltDevice( IN PPI_DEVICE_REQUEST Request ); NTSTATUS PiResetProblemDevicesWorker( IN PDEVICE_NODE DeviceNode, IN PVOID Context ); VOID PiMarkDeviceTreeForReenumeration( IN PDEVICE_NODE DeviceNode, IN BOOLEAN Subtree ); NTSTATUS PiMarkDeviceTreeForReenumerationWorker( IN PDEVICE_NODE DeviceNode, IN PVOID Context ); BOOLEAN PiCollapseEnumRequests( PLIST_ENTRY ListHead ); NTSTATUS PiProcessAddBootDevices( IN PPI_DEVICE_REQUEST Request ); NTSTATUS PiProcessClearDeviceProblem( IN PPI_DEVICE_REQUEST Request ); NTSTATUS PiProcessRequeryDeviceState( IN PPI_DEVICE_REQUEST Request ); NTSTATUS PiProcessResourceRequirementsChanged( IN PPI_DEVICE_REQUEST Request ); NTSTATUS PiProcessReenumeration( IN PPI_DEVICE_REQUEST Request ); NTSTATUS PiProcessSetDeviceProblem( IN PPI_DEVICE_REQUEST Request ); NTSTATUS PiProcessShutdownPnpDevices( IN PDEVICE_NODE DeviceNode ); NTSTATUS PiProcessStartSystemDevices( IN PPI_DEVICE_REQUEST Request ); NTSTATUS PiBuildDeviceNodeInstancePath( IN PDEVICE_NODE DeviceNode, IN PWCHAR BusID, IN PWCHAR DeviceID, IN PWCHAR InstanceID ); NTSTATUS PiCreateDeviceInstanceKey( IN PDEVICE_NODE DeviceNode, OUT PHANDLE InstanceHandle, OUT PULONG Disposition ); NTSTATUS PiQueryAndAllocateBootResources( IN PDEVICE_NODE DeviceNode, IN HANDLE LogConfKey ); NTSTATUS PiQueryResourceRequirements( IN PDEVICE_NODE DeviceNode, IN HANDLE LogConfKey ); #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, IoShutdownPnpDevices) #pragma alloc_text(PAGE, PipCallDriverAddDevice) #pragma alloc_text(PAGE, PipCallDriverAddDeviceQueryRoutine) #pragma alloc_text(PAGE, PipChangeDeviceObjectFromRegistryProperties) #pragma alloc_text(PAGE, PipEnumerateDevice) #pragma alloc_text(PAGE, PipGetRegistryDwordWithFallback) #pragma alloc_text(PAGE, PipGetRegistrySecurityWithFallback) #pragma alloc_text(PAGE, PipMakeGloballyUniqueId) #pragma alloc_text(PAGE, PipProcessCriticalDevice) #pragma alloc_text(PAGE, PipProcessCriticalDeviceRoutine) #pragma alloc_text(PAGE, PipProcessDevNodeTree) #pragma alloc_text(PAGE, PipProcessNewDeviceNode) #pragma alloc_text(PAGE, PiProcessQueryDeviceState) #pragma alloc_text(PAGE, PipQueryDeviceCapabilities) #pragma alloc_text(PAGE, PiProcessHaltDevice) #pragma alloc_text(PAGE, PpResetProblemDevices) #pragma alloc_text(PAGE, PiResetProblemDevicesWorker) #pragma alloc_text(PAGE, PiMarkDeviceTreeForReenumeration) #pragma alloc_text(PAGE, PiMarkDeviceTreeForReenumerationWorker) #pragma alloc_text(PAGE, PiProcessAddBootDevices) #pragma alloc_text(PAGE, PiProcessClearDeviceProblem) #pragma alloc_text(PAGE, PiProcessRequeryDeviceState) #pragma alloc_text(PAGE, PiRestartDevice) #pragma alloc_text(PAGE, PiProcessResourceRequirementsChanged) #pragma alloc_text(PAGE, PiProcessReenumeration) #pragma alloc_text(PAGE, PiProcessSetDeviceProblem) #pragma alloc_text(PAGE, PiProcessShutdownPnpDevices) #pragma alloc_text(PAGE, PiProcessStartSystemDevices) #pragma alloc_text(PAGE, PiBuildDeviceNodeInstancePath) #pragma alloc_text(PAGE, PiCreateDeviceInstanceKey) #pragma alloc_text(PAGE, PiQueryAndAllocateBootResources) #pragma alloc_text(PAGE, PiQueryResourceRequirements) #pragma alloc_text(PAGELK, PiLockDeviceActionQueue) #pragma alloc_text(PAGELK, PiUnlockDeviceActionQueue) //#pragma alloc_text(NONPAGE, PiCollapseEnumRequests) //#pragma alloc_text(NONPAGE, PpRemoveDeviceActionRequests) //#pragma alloc_text(NONPAGE, PiMarkDeviceStackStartPending) #endif // // This flag indicates if the device's InvalidateDeviceRelation is in progress. // To read or write this flag, callers must get IopPnpSpinlock. // BOOLEAN PipEnumerationInProgress; BOOLEAN PipTearDownPnpStacksOnShutdown; WORK_QUEUE_ITEM PipDeviceEnumerationWorkItem; // // Internal constant strings // #define DEVICE_PREFIX_STRING TEXT("\\Device\\") #define DOSDEVICES_PREFIX_STRING TEXT("\\DosDevices\\") VOID PiLockDeviceActionQueue( VOID ) { KIRQL oldIrql; for (;;) { // // Lock the device tree so that power operations dont overlap PnP // operations like rebalance. // PpDevNodeLockTree(PPL_TREEOP_ALLOW_READS); ExAcquireSpinLock(&IopPnPSpinLock, &oldIrql); if (!PipEnumerationInProgress) { // // Device action worker queue is empty. Make it so that new requests // get queued but new device action worker item does not get kicked // off. // PipEnumerationInProgress = TRUE; KeClearEvent(&PiEnumerationLock); ExReleaseSpinLock(&IopPnPSpinLock, oldIrql); break; } ExReleaseSpinLock(&IopPnPSpinLock, oldIrql); // // Unlock the tree so device action worker can finish current processing. // PpDevNodeUnlockTree(PPL_TREEOP_ALLOW_READS); // // Wait for current device action worker item to complete. // KeWaitForSingleObject( &PiEnumerationLock, Executive, KernelMode, FALSE, NULL ); } } VOID PiUnlockDeviceActionQueue( VOID ) { KIRQL oldIrql; // // Check if we need to kick off the enumeration worker here. // ExAcquireSpinLock(&IopPnPSpinLock, &oldIrql); if (!IsListEmpty(&IopPnpEnumerationRequestList)) { ExInitializeWorkItem(&PipDeviceEnumerationWorkItem, PipDeviceActionWorker, NULL); ExQueueWorkItem(&PipDeviceEnumerationWorkItem, DelayedWorkQueue); } else { PipEnumerationInProgress = FALSE; KeSetEvent(&PiEnumerationLock, 0, FALSE); } ExReleaseSpinLock(&IopPnPSpinLock, oldIrql); PpDevNodeUnlockTree(PPL_TREEOP_ALLOW_READS); } NTSTATUS PipRequestDeviceAction( IN PDEVICE_OBJECT DeviceObject OPTIONAL, IN DEVICE_REQUEST_TYPE RequestType, IN BOOLEAN ReorderingBarrier, IN ULONG_PTR RequestArgument, IN PKEVENT CompletionEvent OPTIONAL, IN PNTSTATUS CompletionStatus OPTIONAL ) /*++ Routine Description: This routine queues a work item to enumerate a device. This is for IO internal use only. Arguments: DeviceObject - Supplies a pointer to the device object to be enumerated. if NULL, this is a request to retry resources allocation failed devices. Request - the reason for the enumeration. Return Value: NTSTATUS code. --*/ { PPI_DEVICE_REQUEST request; KIRQL oldIrql; if (PpPnpShuttingDown) { return STATUS_TOO_LATE; } // // If this node is ready for enumeration, enqueue it // request = ExAllocatePool(NonPagedPool, sizeof(PI_DEVICE_REQUEST)); if (request) { // // Put this request onto the pending list // if (DeviceObject == NULL) { DeviceObject = IopRootDeviceNode->PhysicalDeviceObject; } ObReferenceObject(DeviceObject); request->DeviceObject = DeviceObject; request->RequestType = RequestType; request->ReorderingBarrier = ReorderingBarrier; request->RequestArgument = RequestArgument; request->CompletionEvent = CompletionEvent; request->CompletionStatus = CompletionStatus; InitializeListHead(&request->ListEntry); // // Insert the request to the request queue. If the request queue is // not currently being worked on, request a worker thread to start it. // ExAcquireSpinLock(&IopPnPSpinLock, &oldIrql); InsertTailList(&IopPnpEnumerationRequestList, &request->ListEntry); if (RequestType == AddBootDevices || RequestType == ReenumerateBootDevices || RequestType == ReenumerateRootDevices) { ASSERT(!PipEnumerationInProgress); // // This is a special request used when booting the system. Instead // of queuing a work item it synchronously calls the worker routine. // PipEnumerationInProgress = TRUE; KeClearEvent(&PiEnumerationLock); ExReleaseSpinLock(&IopPnPSpinLock, oldIrql); PipDeviceActionWorker(NULL); } else if (PnPBootDriversLoaded && !PipEnumerationInProgress) { PipEnumerationInProgress = TRUE; KeClearEvent(&PiEnumerationLock); ExReleaseSpinLock(&IopPnPSpinLock, oldIrql); // // Queue a work item to do the enumeration // ExInitializeWorkItem(&PipDeviceEnumerationWorkItem, PipDeviceActionWorker, NULL); ExQueueWorkItem(&PipDeviceEnumerationWorkItem, DelayedWorkQueue); } else { ExReleaseSpinLock(&IopPnPSpinLock, oldIrql); } } else { return STATUS_INSUFFICIENT_RESOURCES; } return STATUS_SUCCESS; } VOID PipDeviceActionWorker( IN PVOID Context ) /*++ Routine Description: This function drains items from the "PnP Action queue". The action queue contains a list of operations that must be synchronized wrt to Start & Enum. Parameters: Context - Not used. ReturnValue: None. --*/ { PPI_DEVICE_REQUEST request; PPI_DEVICE_REQUEST collapsedRequest; PLIST_ENTRY entry; BOOLEAN assignResources; BOOLEAN bootProcess; BOOLEAN newDevice; ADD_CONTEXT addContext; KIRQL oldIrql; NTSTATUS status; BOOLEAN dereferenceDevice; UNREFERENCED_PARAMETER(Context); assignResources = FALSE; bootProcess = FALSE; PpDevNodeLockTree(PPL_TREEOP_ALLOW_READS); for ( ; ; ) { status = STATUS_SUCCESS; // // PipProcessDevNodeTree always dereferences passed in device. Set this // to false if PipProcessDevNodeTree is called with the device in the // original request. // dereferenceDevice = TRUE; ExAcquireSpinLock(&IopPnPSpinLock, &oldIrql); entry = RemoveHeadList(&IopPnpEnumerationRequestList); if (entry == &IopPnpEnumerationRequestList) { if (assignResources == FALSE && bootProcess == FALSE) { // // No more processing. // break; } entry = NULL; } ExReleaseSpinLock(&IopPnPSpinLock, oldIrql); if (entry == NULL) { ASSERT(assignResources || bootProcess); if (assignResources || bootProcess) { addContext.DriverStartType = SERVICE_DEMAND_START; ObReferenceObject(IopRootDeviceNode->PhysicalDeviceObject); status = PipProcessDevNodeTree( IopRootDeviceNode, PnPBootDriversInitialized, // LoadDriver assignResources, // ReallocateResources EnumTypeNone, FALSE, FALSE, &addContext, NULL); if (!NT_SUCCESS(status)) { status = STATUS_SUCCESS; } assignResources = FALSE; bootProcess = FALSE; } continue; } // // We have a list of requests to process. Processing depends on the type // of the first one in the list. // ASSERT(entry); request = CONTAINING_RECORD(entry, PI_DEVICE_REQUEST, ListEntry); InitializeListHead(&request->ListEntry); if (PP_DO_TO_DN(request->DeviceObject)->State == DeviceNodeDeleted) { status = STATUS_UNSUCCESSFUL; } else { switch (request->RequestType) { case AddBootDevices: // // Boot driver initialization. // status = PiProcessAddBootDevices(request); break; case AssignResources: // // Resources were freed, we want to try to satisfy any // DNF_INSUFFICIENT_RESOURCES devices. // assignResources = TRUE; break; case ClearDeviceProblem: case ClearEjectProblem: status = PiProcessClearDeviceProblem(request); break; case HaltDevice: status = PiProcessHaltDevice(request); break; case RequeryDeviceState: status = PiProcessRequeryDeviceState(request); break; case ResetDevice: status = PiRestartDevice(request); break; case ResourceRequirementsChanged: status = PiProcessResourceRequirementsChanged(request); if (!NT_SUCCESS(status)) { // // The device wasn't started when IopResourceRequirementsChanged // was called. // assignResources = TRUE; status = STATUS_SUCCESS; } break; case ReenumerateBootDevices: // // Indicate that this is during boot driver initialization phase. // bootProcess = TRUE; break; case RestartEnumeration: // Used after completion of async I/O case ReenumerateRootDevices: case ReenumerateDeviceTree: // // FALL THROUGH... // case ReenumerateDeviceOnly: status = PiProcessReenumeration(request); dereferenceDevice = FALSE; break; case SetDeviceProblem: status = PiProcessSetDeviceProblem(request); break; case ShutdownPnpDevices: status = PiProcessShutdownPnpDevices(IopRootDeviceNode); break; case StartDevice: status = PiRestartDevice(request); break; case StartSystemDevices: status = PiProcessStartSystemDevices(request); dereferenceDevice = FALSE; break; } } // // Free the list. // do { entry = RemoveHeadList(&request->ListEntry); collapsedRequest = CONTAINING_RECORD(entry, PI_DEVICE_REQUEST, ListEntry); // // Done with this enumeration request // if (collapsedRequest->CompletionStatus) { *collapsedRequest->CompletionStatus = status; } if (collapsedRequest->CompletionEvent) { KeSetEvent(collapsedRequest->CompletionEvent, 0, FALSE); } // // Only dereference the original request, the rest get dereferenced // when we collapse. // if ((collapsedRequest == request && dereferenceDevice)) { ObDereferenceObject(collapsedRequest->DeviceObject); } ExFreePool(collapsedRequest); } while (collapsedRequest != request); } PipEnumerationInProgress = FALSE; KeSetEvent(&PiEnumerationLock, 0, FALSE); ExReleaseSpinLock(&IopPnPSpinLock, oldIrql); PpDevNodeUnlockTree(PPL_TREEOP_ALLOW_READS); } NTSTATUS IoShutdownPnpDevices( VOID ) /*++ Routine Description: This function is called by the IO system driver verifier during shutdown. It queues a work item to Query/Remove all the devices in the tree. All the ones supporting removal will be removed and their drivers unloaded if all instances of their devices are removed. This API should only be called once during shutdown, it has no effect on the second and subsequent calls. Parameters: NONE. Return Value: STATUS_SUCCESS if the process was successfully completed. Doesn't mean any devices were actually removed. Otherwise an error code indicating the error. There is no guarantee that no devices have been removed if an error occurs however in the current implementation the only time an error will be reported is if the operation couldn't be queued. --*/ { KEVENT actionEvent; NTSTATUS actionStatus; NTSTATUS status; PAGED_CODE(); KeInitializeEvent(&actionEvent, NotificationEvent, FALSE); status = PipRequestDeviceAction( NULL, ShutdownPnpDevices, FALSE, 0, &actionEvent, &actionStatus); if (NT_SUCCESS(status)) { // // Wait for the event we just queued to finish since synchronous // operation was requested (non alertable wait). // // FUTURE ITEM - Use a timeout here? // status = KeWaitForSingleObject( &actionEvent, Executive, KernelMode, FALSE, NULL); if (NT_SUCCESS(status)) { status = actionStatus; } } return status; } NTSTATUS PipEnumerateDevice( IN PDEVICE_NODE DeviceNode, IN BOOLEAN Synchronous ) /*++ Routine Description: This function assumes that the specified physical device object is a bus and will enumerate all of the children PDOs on the bus. Arguments: DeviceObject - Supplies a pointer to the physical device object to be enumerated. StartContext - supplies a pointer to the START_CONTEXT to control how to add/start new devices. Return Value: NTSTATUS code. --*/ { NTSTATUS status; PDEVICE_OBJECT deviceObject; PAGED_CODE(); DeviceNode->Flags &= ~DNF_REENUMERATE; // // First get a reference to the PDO to make sure it won't go away. // deviceObject = DeviceNode->PhysicalDeviceObject; status = IopQueryDeviceRelations( BusRelations, deviceObject, Synchronous, &DeviceNode->OverUsed1.PendingDeviceRelations); return status; } NTSTATUS PipEnumerateCompleted( IN PDEVICE_NODE DeviceNode ) { PDEVICE_NODE childDeviceNode, nextChildDeviceNode; PDEVICE_OBJECT childDeviceObject; BOOLEAN childRemoved; NTSTATUS status, allocationStatus; ULONG i; if (DeviceNode->OverUsed1.PendingDeviceRelations == NULL) { PipSetDevNodeState(DeviceNode, DeviceNodeStarted, NULL); return STATUS_SUCCESS; } // // Walk all the child device nodes and mark them as not present // childDeviceNode = DeviceNode->Child; while (childDeviceNode) { childDeviceNode->Flags &= ~DNF_ENUMERATED; childDeviceNode = childDeviceNode->Sibling; } // // Check all the PDOs returned see if any new one or any one disappeared. // for (i = 0; i < DeviceNode->OverUsed1.PendingDeviceRelations->Count; i++) { childDeviceObject = DeviceNode->OverUsed1.PendingDeviceRelations->Objects[i]; ASSERT_INITED(childDeviceObject); if (childDeviceObject->DeviceObjectExtension->ExtensionFlags & DOE_DELETE_PENDING) { PP_SAVE_DEVICEOBJECT_TO_TRIAGE_DUMP(childDeviceObject); KeBugCheckEx( PNP_DETECTED_FATAL_ERROR, PNP_ERR_PDO_ENUMERATED_AFTER_DELETION, (ULONG_PTR)childDeviceObject, 0, 0); } // // We've found another physical device, see if there is // already a devnode for it. // childDeviceNode = (PDEVICE_NODE)childDeviceObject->DeviceObjectExtension->DeviceNode; if (childDeviceNode == NULL) { // // Device node doesn't exist, create one. // allocationStatus = PipAllocateDeviceNode( childDeviceObject, &childDeviceNode); if (childDeviceNode) { // // We've found or created a devnode for the PDO that the // bus driver just enumerated. // childDeviceNode->Flags |= DNF_ENUMERATED; // // Mark the device object a bus enumerated device // childDeviceObject->Flags |= DO_BUS_ENUMERATED_DEVICE; // // Put this new device node at the head of the parent's list // of children. // PpDevNodeInsertIntoTree(DeviceNode, childDeviceNode); if (allocationStatus == STATUS_SYSTEM_HIVE_TOO_LARGE) { PipSetDevNodeProblem(childDeviceNode, CM_PROB_REGISTRY_TOO_LARGE); } } else { // // Had a problem creating a devnode. Pretend we've never // seen it. // IopDbgPrint((IOP_ENUMERATION_ERROR_LEVEL, "PipEnumerateDevice: Failed to allocate device node\n")); ObDereferenceObject(childDeviceObject); } } else { // // The device is alreay enumerated. Remark it and release the // device object reference. // childDeviceNode->Flags |= DNF_ENUMERATED; if (childDeviceNode->DockInfo.DockStatus == DOCK_EJECTIRP_COMPLETED) { // // A dock that was listed as departing in an eject relation // didn't actually leave. Remove it from the profile transition // list... // PpProfileCancelTransitioningDock(childDeviceNode, DOCK_DEPARTING); } ASSERT(!(childDeviceNode->Flags & DNF_DEVICE_GONE)); ObDereferenceObject(childDeviceObject); } } ExFreePool(DeviceNode->OverUsed1.PendingDeviceRelations); DeviceNode->OverUsed1.PendingDeviceRelations = NULL; // // If we get here, the enumeration was successful. Process any missing // devnodes. // childRemoved = FALSE; for (childDeviceNode = DeviceNode->Child; childDeviceNode != NULL; childDeviceNode = nextChildDeviceNode) { // // First, we need to remember the 'next child' because the 'child' will be // removed and we won't be able to find the 'next child.' // nextChildDeviceNode = childDeviceNode->Sibling; if (!(childDeviceNode->Flags & DNF_ENUMERATED)) { if (!(childDeviceNode->Flags & DNF_DEVICE_GONE)) { childDeviceNode->Flags |= DNF_DEVICE_GONE; PipRequestDeviceRemoval( childDeviceNode, TRUE, CM_PROB_DEVICE_NOT_THERE ); childRemoved = TRUE; } } } ASSERT(DeviceNode->State == DeviceNodeEnumerateCompletion); PipSetDevNodeState(DeviceNode, DeviceNodeStarted, NULL); // // The root enumerator gets confused if we reenumerate it before we process // newly reported PDOs. Since it can't possibly create the scenario we are // trying to fix, we won't bother waiting for the removes to complete before // processing the new devnodes. // if (childRemoved && DeviceNode != IopRootDeviceNode) { status = STATUS_PNP_RESTART_ENUMERATION; } else { status = STATUS_SUCCESS; } return status; } VOID PiMarkDeviceStackStartPending( IN PDEVICE_OBJECT DeviceObject, IN BOOLEAN Set ) /*++ Routine Description: This function marks the entire device stack with DOE_START_PENDING. Arguments: DeviceObject - PDO for the device stack. Return Value: None. --*/ { PDEVICE_OBJECT attachedDevice; KIRQL irql; irql = KeAcquireQueuedSpinLock( LockQueueIoDatabaseLock ); for (attachedDevice = DeviceObject; attachedDevice != NULL; attachedDevice = attachedDevice->AttachedDevice) { if (Set) { attachedDevice->DeviceObjectExtension->ExtensionFlags |= DOE_START_PENDING; } else { attachedDevice->DeviceObjectExtension->ExtensionFlags &= ~DOE_START_PENDING; } } KeReleaseQueuedSpinLock(LockQueueIoDatabaseLock, irql); } NTSTATUS PiBuildDeviceNodeInstancePath( IN PDEVICE_NODE DeviceNode, IN PWCHAR BusID, IN PWCHAR DeviceID, IN PWCHAR InstanceID ) /*++ Routine Description: This function builds the instance path (BusID\DeviceID\InstanceID). If successful, it will free the storage for any existing instance path and replace with the new one. Arguments: DeviceNode - DeviceNode for which the instance path will be built. BusID - Bus ID. DeviceID - Device ID. InstanceID - Instance ID. Return Value: NTSTATUS. --*/ { ULONG length; PWCHAR instancePath; PAGED_CODE(); if (BusID == NULL || DeviceID == NULL || InstanceID == NULL) { ASSERT( PipIsDevNodeProblem(DeviceNode, CM_PROB_INVALID_DATA) || PipIsDevNodeProblem(DeviceNode, CM_PROB_OUT_OF_MEMORY) || PipIsDevNodeProblem(DeviceNode, CM_PROB_REGISTRY)); return STATUS_UNSUCCESSFUL; } length = (ULONG)((wcslen(BusID) + wcslen(DeviceID) + wcslen(InstanceID) + 2) * sizeof(WCHAR) + sizeof(UNICODE_NULL)); instancePath = (PWCHAR)ExAllocatePool(PagedPool | POOL_COLD_ALLOCATION, length); if (!instancePath) { return STATUS_INSUFFICIENT_RESOURCES; } // // Construct the instance path as \\. This should always be NULL terminated // since we have precomputed the length that we pass into this counted routine. // _snwprintf(instancePath, length / sizeof(WCHAR), L"%s\\%s\\%s", BusID, DeviceID, InstanceID); // // Free old instance path. // if (DeviceNode->InstancePath.Buffer != NULL) { IopCleanupDeviceRegistryValues(&DeviceNode->InstancePath); ExFreePool(DeviceNode->InstancePath.Buffer); } RtlInitUnicodeString(&DeviceNode->InstancePath, instancePath); return STATUS_SUCCESS; } NTSTATUS PiCreateDeviceInstanceKey( IN PDEVICE_NODE DeviceNode, OUT PHANDLE InstanceKey, OUT PULONG Disposition ) /*++ Routine Description: This function will create the device instance key. Arguments: DeviceNode - DeviceNode for which the instance path will be built. InstanceKey - Will recieve the instance key handle. Disposition - Will recieve the disposition whether the key existed or was newly created. Return Value: NTSTATUS. --*/ { NTSTATUS status; HANDLE enumHandle; PKEY_VALUE_FULL_INFORMATION keyValueInformation; UNICODE_STRING unicodeString; PAGED_CODE(); *InstanceKey = NULL; *Disposition = 0; PiLockPnpRegistry(FALSE); status = IopOpenRegistryKeyEx( &enumHandle, NULL, &CmRegistryMachineSystemCurrentControlSetEnumName, KEY_ALL_ACCESS ); if (NT_SUCCESS(status)) { status = IopCreateRegistryKeyEx( InstanceKey, enumHandle, &DeviceNode->InstancePath, KEY_ALL_ACCESS, REG_OPTION_NON_VOLATILE, Disposition ); if (NT_SUCCESS(status)) { // // Keys migrated by textmode setup should be treated as "new". // Migrated keys are identified by the presence of non-zero // REG_DWORD value "Migrated" under the device instance key. // if (*Disposition != REG_CREATED_NEW_KEY) { keyValueInformation = NULL; IopGetRegistryValue( *InstanceKey, REGSTR_VALUE_MIGRATED, &keyValueInformation); if (keyValueInformation) { if ( keyValueInformation->Type == REG_DWORD && keyValueInformation->DataLength == sizeof(ULONG) && *(PULONG)KEY_VALUE_DATA(keyValueInformation) != 0) { *Disposition = REG_CREATED_NEW_KEY; } PiWstrToUnicodeString(&unicodeString, REGSTR_VALUE_MIGRATED); ZwDeleteValueKey(*InstanceKey, &unicodeString); ExFreePool(keyValueInformation); } } } else { IopDbgPrint((IOP_ENUMERATION_ERROR_LEVEL, "PpCreateDeviceInstanceKey: Unable to create %wZ\n", &DeviceNode->InstancePath)); ASSERT(*InstanceKey != NULL); } ZwClose(enumHandle); } else { // // This would be very bad. // IopDbgPrint((IOP_ENUMERATION_ERROR_LEVEL, "PpCreateDeviceInstanceKey: Unable to open %wZ\n", &CmRegistryMachineSystemCurrentControlSetEnumName)); ASSERT(enumHandle != NULL); } PiUnlockPnpRegistry(); return status; } NTSTATUS PiQueryAndAllocateBootResources( IN PDEVICE_NODE DeviceNode, IN HANDLE LogConfKey ) /*++ Routine Description: This function will query the BOOT resources for the device and reserve them from the arbiter. Arguments: DeviceNode - DeviceNode for which the BOOT resources need to be queried. LogConfKey - Handle to the LogConf key under the device instance key. Return Value: NTSTATUS. --*/ { NTSTATUS status; PCM_RESOURCE_LIST cmResource; ULONG cmLength; UNICODE_STRING unicodeString; PAGED_CODE(); status = STATUS_SUCCESS; cmResource = NULL; cmLength = 0; if (DeviceNode->BootResources == NULL) { status = IopQueryDeviceResources( DeviceNode->PhysicalDeviceObject, QUERY_RESOURCE_LIST, &cmResource, &cmLength); if (!NT_SUCCESS(status)) { ASSERT(cmResource == NULL && cmLength == 0); cmResource = NULL; cmLength = 0; } } else { IopDbgPrint(( IOP_ENUMERATION_INFO_LEVEL, "PNPENUM: %ws already has BOOT config in PiQueryAndAllocateBootResources!\n", DeviceNode->InstancePath.Buffer)); } // // Write boot resources to registry // if (LogConfKey && DeviceNode->BootResources == NULL) { PiWstrToUnicodeString(&unicodeString, REGSTR_VAL_BOOTCONFIG); PiLockPnpRegistry(FALSE); if (cmResource) { ZwSetValueKey( LogConfKey, &unicodeString, TITLE_INDEX_VALUE, REG_RESOURCE_LIST, cmResource, cmLength); } else { ZwDeleteValueKey(LogConfKey, &unicodeString); } PiUnlockPnpRegistry(); if (cmResource) { // // This device consumes BOOT resources. Reserve its boot resources // status = (*IopAllocateBootResourcesRoutine)( ArbiterRequestPnpEnumerated, DeviceNode->PhysicalDeviceObject, cmResource); if (NT_SUCCESS(status)) { DeviceNode->Flags |= DNF_HAS_BOOT_CONFIG; } } } if (cmResource) { ExFreePool(cmResource); } return status; } NTSTATUS PiQueryResourceRequirements( IN PDEVICE_NODE DeviceNode, IN HANDLE LogConfKey ) /*++ Routine Description: This function will query the resource requirements for the device. Arguments: DeviceNode - DeviceNode for which the resource requirements need to be queried. LogConfKey - Handle to the LogConf key under the device instance key. Return Value: NTSTATUS. --*/ { NTSTATUS status; PIO_RESOURCE_REQUIREMENTS_LIST ioResource; ULONG ioLength; UNICODE_STRING unicodeString; PAGED_CODE(); // // Query the device's basic config vector. // status = PpIrpQueryResourceRequirements( DeviceNode->PhysicalDeviceObject, &ioResource); if (!NT_SUCCESS(status)) { ASSERT(ioResource == NULL); ioResource = NULL; } if (ioResource) { ioLength = ioResource->ListSize; } else { ioLength = 0; } // // Write resource requirements to registry // if (LogConfKey) { PiWstrToUnicodeString(&unicodeString, REGSTR_VALUE_BASIC_CONFIG_VECTOR); PiLockPnpRegistry(FALSE); if (ioResource) { ZwSetValueKey( LogConfKey, &unicodeString, TITLE_INDEX_VALUE, REG_RESOURCE_REQUIREMENTS_LIST, ioResource, ioLength); DeviceNode->Flags |= DNF_RESOURCE_REQUIREMENTS_NEED_FILTERED; DeviceNode->ResourceRequirements = ioResource; ioResource = NULL; } else { ZwDeleteValueKey(LogConfKey, &unicodeString); } PiUnlockPnpRegistry(); } if (ioResource) { ExFreePool(ioResource); } return status; } NTSTATUS PipProcessNewDeviceNode( IN PDEVICE_NODE DeviceNode /*++ Routine Description: This function will process a new device. Arguments: DeviceNode - New DeviceNode. Return Value: NTSTATUS. --*/ ) { NTSTATUS status, finalStatus; PDEVICE_OBJECT deviceObject, dupeDeviceObject; PWCHAR busID, deviceID, instanceID, description, location, uniqueInstanceID, hwIDs, compatibleIDs; DEVICE_CAPABILITIES capabilities; BOOLEAN globallyUnique, criticalDevice, configuredBySetup, isRemoteBootCard; ULONG instanceIDLength, disposition, configFlags, problem, hwIDLength, compatibleIDLength; HANDLE instanceKey, logConfKey; PDEVICE_NODE dupeDeviceNode; UNICODE_STRING unicodeString; PKEY_VALUE_FULL_INFORMATION keyValueInformation; IO_STACK_LOCATION irpSp; GUID busTypeGuid; PAGED_CODE(); finalStatus = STATUS_SUCCESS; criticalDevice = FALSE; isRemoteBootCard = FALSE; logConfKey = NULL; instanceKey = NULL; disposition = 0; deviceObject = DeviceNode->PhysicalDeviceObject; status = PpQueryDeviceID(DeviceNode, &busID, &deviceID); if (!NT_SUCCESS(status)) { if (status == STATUS_PNP_INVALID_ID) { finalStatus = STATUS_UNSUCCESSFUL; } else { finalStatus = status; } } // // Query the device's capabilities. // status = PipQueryDeviceCapabilities(DeviceNode, &capabilities); // // Process the capabilities before saving them. // DeviceNode->UserFlags &= ~DNUF_DONT_SHOW_IN_UI; globallyUnique = FALSE; if (NT_SUCCESS(status)) { if (capabilities.NoDisplayInUI) { DeviceNode->UserFlags |= DNUF_DONT_SHOW_IN_UI; } if (capabilities.UniqueID) { globallyUnique = TRUE; } } // // Record, is this a dock? // if (capabilities.DockDevice) { if (DeviceNode->DockInfo.DockStatus == DOCK_EJECTIRP_COMPLETED) { ASSERT(DeviceNode->DockInfo.DockStatus != DOCK_EJECTIRP_COMPLETED); PpProfileCancelTransitioningDock(DeviceNode, DOCK_DEPARTING); } DeviceNode->DockInfo.DockStatus = DOCK_QUIESCENT; } else { DeviceNode->DockInfo.DockStatus = DOCK_NOTDOCKDEVICE; } // // Initialize the stack location to pass to IopSynchronousCall() // RtlZeroMemory(&irpSp, sizeof(IO_STACK_LOCATION)); // // Query the device's description. // irpSp.MajorFunction = IRP_MJ_PNP; irpSp.MinorFunction = IRP_MN_QUERY_DEVICE_TEXT; irpSp.Parameters.QueryDeviceText.DeviceTextType = DeviceTextDescription; irpSp.Parameters.QueryDeviceText.LocaleId = PsDefaultSystemLocaleId; status = IopSynchronousCall(deviceObject, &irpSp, (PULONG_PTR)&description); if (!NT_SUCCESS(status)) { description = NULL; } // // Initialize the stack location to pass to IopSynchronousCall() // RtlZeroMemory(&irpSp, sizeof(IO_STACK_LOCATION)); // // Query the device's location information. // irpSp.MajorFunction = IRP_MJ_PNP; irpSp.MinorFunction = IRP_MN_QUERY_DEVICE_TEXT; irpSp.Parameters.QueryDeviceText.DeviceTextType = DeviceTextLocationInformation; irpSp.Parameters.QueryDeviceText.LocaleId = PsDefaultSystemLocaleId; status = IopSynchronousCall(deviceObject, &irpSp, (PULONG_PTR)&location); if (!NT_SUCCESS(status)) { location = NULL; } // // Query the instance ID for the new devnode. // status = PpQueryInstanceID(DeviceNode, &instanceID, &instanceIDLength); if (!globallyUnique) { if ( !PipIsDevNodeProblem(DeviceNode, CM_PROB_INVALID_DATA) && DeviceNode->Parent != IopRootDeviceNode) { uniqueInstanceID = NULL; status = PipMakeGloballyUniqueId(deviceObject, instanceID, &uniqueInstanceID); if (instanceID != NULL) { ExFreePool(instanceID); } instanceID = uniqueInstanceID; if (instanceID) { instanceIDLength = ((ULONG)wcslen(instanceID) + 1) * sizeof(WCHAR); } else { instanceIDLength = 0; ASSERT(!NT_SUCCESS(status)); } } } else if (status == STATUS_NOT_SUPPORTED) { PipSetDevNodeProblem(DeviceNode, CM_PROB_INVALID_DATA); DeviceNode->Parent->Flags |= DNF_CHILD_WITH_INVALID_ID; PpSetInvalidIDEvent(&DeviceNode->Parent->InstancePath); IopDbgPrint((IOP_ENUMERATION_ERROR_LEVEL, "PpQueryID: Bogus ID returned by %wZ\n", &DeviceNode->Parent->ServiceName)); ASSERT(status != STATUS_NOT_SUPPORTED || !globallyUnique); } RetryDuplicateId: if (!NT_SUCCESS(status)) { finalStatus = status; if (!PipIsDevNodeProblem(DeviceNode, CM_PROB_INVALID_DATA)) { if (status == STATUS_INSUFFICIENT_RESOURCES) { PipSetDevNodeProblem(DeviceNode, CM_PROB_OUT_OF_MEMORY); } else { // // Perhaps some other problem code? // PipSetDevNodeProblem(DeviceNode, CM_PROB_REGISTRY); } } } // // Build the device instance path and create the instance key. // status = PiBuildDeviceNodeInstancePath(DeviceNode, busID, deviceID, instanceID); if (NT_SUCCESS(status)) { status = PiCreateDeviceInstanceKey(DeviceNode, &instanceKey, &disposition); } if (!NT_SUCCESS(status)) { finalStatus = status; } // // Mark the devnode as initialized. // PiMarkDeviceStackStartPending(deviceObject, TRUE); // // ISSUE: Should not mark the state if the IDs were invalid. // PipSetDevNodeState(DeviceNode, DeviceNodeInitialized, NULL); if ( !PipIsDevNodeProblem(DeviceNode, CM_PROB_INVALID_DATA) && !PipIsDevNodeProblem(DeviceNode, CM_PROB_OUT_OF_MEMORY) && !PipIsDevNodeProblem(DeviceNode, CM_PROB_REGISTRY)) { // // Check if we are encountering this device for the very first time. // if (disposition == REG_CREATED_NEW_KEY) { // // Save the description only for new devices so we dont clobber // the inf written description for already installed devices. // PiLockPnpRegistry(FALSE); PiSetDeviceInstanceSzValue(instanceKey, REGSTR_VAL_DEVDESC, &description); PiUnlockPnpRegistry(); } else { // // Check if there is another device with the same name. // dupeDeviceObject = IopDeviceObjectFromDeviceInstance(&DeviceNode->InstancePath); if (dupeDeviceObject) { if (dupeDeviceObject != deviceObject) { if (globallyUnique) { globallyUnique = FALSE; PipSetDevNodeProblem(DeviceNode, CM_PROB_DUPLICATE_DEVICE); dupeDeviceNode = dupeDeviceObject->DeviceObjectExtension->DeviceNode; ASSERT(dupeDeviceNode); if (dupeDeviceNode->Parent == DeviceNode->Parent) { // // Definite driver screw up. If the verifier is enabled // we will fail the driver. Otherwise, we will attempt // to uniquify the second device to keep the system // alive. // PpvUtilFailDriver( PPVERROR_DUPLICATE_PDO_ENUMERATED, (PVOID) deviceObject->DriverObject->MajorFunction[IRP_MJ_PNP], deviceObject, (PVOID)dupeDeviceObject); } ObDereferenceObject(dupeDeviceObject); status = PipMakeGloballyUniqueId(deviceObject, instanceID, &uniqueInstanceID); if (instanceID != NULL) { ExFreePool(instanceID); } instanceID = uniqueInstanceID; if (instanceID) { instanceIDLength = ((ULONG)wcslen(instanceID) + 1) * sizeof(WCHAR); } else { instanceIDLength = 0; ASSERT(!NT_SUCCESS(status)); } // // Cleanup and retry. // goto RetryDuplicateId; } // // No need to clean up the ref as we're going to crash the // system. // //ObDereferenceObject(dupCheckDeviceObject); PpvUtilFailDriver( PPVERROR_DUPLICATE_PDO_ENUMERATED, (PVOID) deviceObject->DriverObject->MajorFunction[IRP_MJ_PNP], deviceObject, (PVOID)dupeDeviceObject); PP_SAVE_DEVICEOBJECT_TO_TRIAGE_DUMP(deviceObject); PP_SAVE_DEVICEOBJECT_TO_TRIAGE_DUMP(dupeDeviceObject); KeBugCheckEx( PNP_DETECTED_FATAL_ERROR, PNP_ERR_DUPLICATE_PDO, (ULONG_PTR)deviceObject, (ULONG_PTR)dupeDeviceObject, 0); } ObDereferenceObject(dupeDeviceObject); } } } if ( !PipIsDevNodeProblem(DeviceNode, CM_PROB_INVALID_DATA) && !PipIsDevNodeProblem(DeviceNode, CM_PROB_OUT_OF_MEMORY) && !PipIsDevNodeProblem(DeviceNode, CM_PROB_REGISTRY)) { PiLockPnpRegistry(FALSE); // // Save device location and capabilities. // PiSetDeviceInstanceSzValue(instanceKey, REGSTR_VALUE_LOCATION_INFORMATION, &location); IopSaveDeviceCapabilities(DeviceNode, &capabilities); // // ADRIAO N.B. 2001/05/29 - Raw device issue // processCriticalDevice has no effect on raw devnodes. A raw // devnode with CONFIGFLAG_FAILED_INSTALL or CONFIGFLAG_REINSTALL // should be started anyway if it's in the CDDB (not that NULL CDDB // entries are supported yet), but that doesn't happen today. This // means that boot volumes with CONFIGFLAG_REINSTALL will lead to a // definite 7B. // problem = 0; criticalDevice = (disposition == REG_CREATED_NEW_KEY)? TRUE : FALSE; status = IopGetRegistryValue(instanceKey, REGSTR_VALUE_CONFIG_FLAGS, &keyValueInformation); if (NT_SUCCESS(status)) { configFlags = *(PULONG)KEY_VALUE_DATA(keyValueInformation); if (configFlags & CONFIGFLAG_REINSTALL) { problem = CM_PROB_REINSTALL; criticalDevice = TRUE; } else if (configFlags & CONFIGFLAG_FAILEDINSTALL) { problem = CM_PROB_FAILED_INSTALL; criticalDevice = TRUE; } ExFreePool(keyValueInformation); } else { configFlags = 0; problem = CM_PROB_NOT_CONFIGURED; criticalDevice = TRUE; } if (problem) { if (capabilities.RawDeviceOK) { configFlags |= CONFIGFLAG_FINISH_INSTALL; PiWstrToUnicodeString(&unicodeString, REGSTR_VALUE_CONFIG_FLAGS); ZwSetValueKey( instanceKey, &unicodeString, TITLE_INDEX_VALUE, REG_DWORD, &configFlags, sizeof(configFlags)); } else { PipSetDevNodeProblem(DeviceNode, problem); } } status = IopMapDeviceObjectToDeviceInstance(DeviceNode->PhysicalDeviceObject, &DeviceNode->InstancePath); ASSERT(NT_SUCCESS(status)); if (!NT_SUCCESS(status)) { finalStatus = status; } PiUnlockPnpRegistry(); } PpQueryHardwareIDs( DeviceNode, &hwIDs, &hwIDLength); PpQueryCompatibleIDs( DeviceNode, &compatibleIDs, &compatibleIDLength); PiLockPnpRegistry(FALSE); DeviceNode->Flags |= DNF_IDS_QUERIED; if ( !PipIsDevNodeProblem(DeviceNode, CM_PROB_INVALID_DATA) && !PipIsDevNodeProblem(DeviceNode, CM_PROB_OUT_OF_MEMORY) && !PipIsDevNodeProblem(DeviceNode, CM_PROB_REGISTRY)) { PiWstrToUnicodeString(&unicodeString, REGSTR_KEY_LOG_CONF); IopCreateRegistryKeyEx( &logConfKey, instanceKey, &unicodeString, KEY_ALL_ACCESS, REG_OPTION_NON_VOLATILE, NULL); } PiUnlockPnpRegistry(); PiQueryResourceRequirements(DeviceNode, logConfKey); PiLockPnpRegistry(FALSE); if (IoRemoteBootClient && (IopLoaderBlock != NULL)) { if ( !PipIsDevNodeProblem(DeviceNode, CM_PROB_INVALID_DATA) && !PipIsDevNodeProblem(DeviceNode, CM_PROB_OUT_OF_MEMORY) && !PipIsDevNodeProblem(DeviceNode, CM_PROB_REGISTRY)) { if (hwIDs) { isRemoteBootCard = IopIsRemoteBootCard( DeviceNode->ResourceRequirements, (PLOADER_PARAMETER_BLOCK)IopLoaderBlock, hwIDs); } if (!isRemoteBootCard && compatibleIDs) { isRemoteBootCard = IopIsRemoteBootCard( DeviceNode->ResourceRequirements, (PLOADER_PARAMETER_BLOCK)IopLoaderBlock, compatibleIDs); } } } PiSetDeviceInstanceMultiSzValue(instanceKey, REGSTR_VALUE_HARDWAREID, &hwIDs, hwIDLength); PiSetDeviceInstanceMultiSzValue(instanceKey, REGSTR_VALUE_COMPATIBLEIDS, &compatibleIDs, compatibleIDLength); status = STATUS_SUCCESS; if (isRemoteBootCard) { status = IopSetupRemoteBootCard( (PLOADER_PARAMETER_BLOCK)IopLoaderBlock, instanceKey, &DeviceNode->InstancePath); if (!NT_SUCCESS(status)) { finalStatus = status; } } PiUnlockPnpRegistry(); // // we've pretty much got the PDO information ready, apart from Child bus information // get that now, because class-installer may want it // if (NT_SUCCESS(IopQueryPnpBusInformation( deviceObject, &busTypeGuid, &DeviceNode->ChildInterfaceType, &DeviceNode->ChildBusNumber))) { DeviceNode->ChildBusTypeIndex = PpBusTypeGuidGetIndex(&busTypeGuid); } else { DeviceNode->ChildBusTypeIndex = 0xffff; DeviceNode->ChildInterfaceType = InterfaceTypeUndefined; DeviceNode->ChildBusNumber = 0xfffffff0; } if (NT_SUCCESS(status)) { if (criticalDevice) { // // Process the device as a critical device. // // This will attempt to locate a match for the device in the // CriticalDeviceDatabase using the device's hardware and compatible // ids. If a match is found, critical device settings such as Service, // ClassGUID (to determine Class filters), and device LowerFilters and // UpperFilters will be applied to the device. // // If DevicePath location information matching this device is present // critical device database entry, this routine will also pre-install // the new device with those settings. // if (!capabilities.HardwareDisabled && !PipIsDevNodeProblem(DeviceNode, CM_PROB_NEED_RESTART)) { PipProcessCriticalDevice(DeviceNode); } } ASSERT(!PipDoesDevNodeHaveProblem(DeviceNode) || PipIsDevNodeProblem(DeviceNode, CM_PROB_NOT_CONFIGURED) || PipIsDevNodeProblem(DeviceNode, CM_PROB_REINSTALL) || PipIsDevNodeProblem(DeviceNode, CM_PROB_FAILED_INSTALL) || PipIsDevNodeProblem(DeviceNode, CM_PROB_PARTIAL_LOG_CONF) || PipIsDevNodeProblem(DeviceNode, CM_PROB_HARDWARE_DISABLED) || PipIsDevNodeProblem(DeviceNode, CM_PROB_NEED_RESTART) || PipIsDevNodeProblem(DeviceNode, CM_PROB_DUPLICATE_DEVICE) || PipIsDevNodeProblem(DeviceNode, CM_PROB_INVALID_DATA) || PipIsDevNodeProblem(DeviceNode, CM_PROB_OUT_OF_MEMORY) || PipIsDevNodeProblem(DeviceNode, CM_PROB_REGISTRY)); if (!PipIsDevNodeProblem(DeviceNode, CM_PROB_DISABLED) && !PipIsDevNodeProblem(DeviceNode, CM_PROB_HARDWARE_DISABLED) && !PipIsDevNodeProblem(DeviceNode, CM_PROB_NEED_RESTART) && !PipIsDevNodeProblem(DeviceNode, CM_PROB_INVALID_DATA) && !PipIsDevNodeProblem(DeviceNode, CM_PROB_OUT_OF_MEMORY) && !PipIsDevNodeProblem(DeviceNode, CM_PROB_REGISTRY)) { IopIsDeviceInstanceEnabled(instanceKey, &DeviceNode->InstancePath, TRUE); } } PiQueryAndAllocateBootResources(DeviceNode, logConfKey); if ( !PipIsDevNodeProblem(DeviceNode, CM_PROB_INVALID_DATA) && !PipIsDevNodeProblem(DeviceNode, CM_PROB_OUT_OF_MEMORY) && !PipIsDevNodeProblem(DeviceNode, CM_PROB_REGISTRY)) { PiLockPnpRegistry(FALSE); IopSaveDeviceCapabilities(DeviceNode, &capabilities); PiUnlockPnpRegistry(); PpHotSwapUpdateRemovalPolicy(DeviceNode); // // Create new value entry under ServiceKeyName\Enum to reflect the newly // added made-up device instance node. // status = IopNotifySetupDeviceArrival( deviceObject, instanceKey, TRUE); configuredBySetup = NT_SUCCESS(status) ? TRUE : FALSE; status = PpDeviceRegistration( &DeviceNode->InstancePath, TRUE, &DeviceNode->ServiceName ); if (NT_SUCCESS(status)) { if ( (configuredBySetup || isRemoteBootCard) && PipIsDevNodeProblem(DeviceNode, CM_PROB_NOT_CONFIGURED)) { PipClearDevNodeProblem(DeviceNode); } } // // Add an event so user-mode will attempt to install this device later. // PpSetPlugPlayEvent(&GUID_DEVICE_ENUMERATED, deviceObject); } // // Cleanup. // if (hwIDs) { ExFreePool(hwIDs); } if (compatibleIDs) { ExFreePool(compatibleIDs); } if (logConfKey) { ZwClose(logConfKey); } if (instanceKey) { ZwClose(instanceKey); } if (instanceID) { ExFreePool(instanceID); } if (location) { ExFreePool(location); } if (description) { ExFreePool(description); } if (busID) { ExFreePool(busID); } return finalStatus; } NTSTATUS PipCallDriverAddDevice( IN PDEVICE_NODE DeviceNode, IN BOOLEAN LoadDriver, IN PADD_CONTEXT Context ) /*++ Routine Description: This function checks if the driver for the DeviceNode is present and loads the driver if necessary. Arguments: DeviceNode - Supplies a pointer to the device node to be enumerated. LoadDriver - Supplies a BOOLEAN value to indicate should a driver be loaded to complete enumeration. Context - Supplies a pointer to ADD_CONTEXT to control how the device be added. Return Value: NTSTATUS code. --*/ { HANDLE enumKey, instanceKey, controlKey, classKey = NULL, classPropsKey = NULL; PKEY_VALUE_FULL_INFORMATION keyValueInformation = NULL; RTL_QUERY_REGISTRY_TABLE queryTable[3]; QUERY_CONTEXT queryContext; BOOLEAN deviceRaw = FALSE; BOOLEAN usePdoCharacteristics = TRUE; NTSTATUS status; PDEVICE_OBJECT deviceObject; PDEVICE_OBJECT fdoDeviceObject, topOfPdoStack, topOfLowerFilterStack; BOOLEAN deviceObjectHasBeenAttached = FALSE; UNICODE_STRING unicodeClassGuid; IopDbgPrint(( IOP_ENUMERATION_TRACE_LEVEL, "PipCallDriverAddDevice: Processing devnode %#08lx\n", DeviceNode)); IopDbgPrint(( IOP_ENUMERATION_VERBOSE_LEVEL, "PipCallDriverAddDevice: DevNode flags going in = %#08lx\n", DeviceNode->Flags)); // // The device node may have been started at this point. This is because // some ill-behaved miniport drivers call IopReportedDetectedDevice at // DriverEntry for the devices which we already know about. // ASSERT_INITED(DeviceNode->PhysicalDeviceObject); IopDbgPrint(( IOP_ENUMERATION_TRACE_LEVEL, "PipCallDriverAddDevice:\t%s load driver\n", LoadDriver? "Will" : "Won't")); IopDbgPrint(( IOP_ENUMERATION_VERBOSE_LEVEL, "PipCallDriverAddDevice:\tOpening registry key %wZ\n", &DeviceNode->InstancePath)); // // Open the HKLM\System\CCS\Enum key. // status = IopOpenRegistryKeyEx( &enumKey, NULL, &CmRegistryMachineSystemCurrentControlSetEnumName, KEY_READ ); if (!NT_SUCCESS(status)) { IopDbgPrint(( IOP_ENUMERATION_WARNING_LEVEL, "PipCallDriverAddDevice:\tUnable to open HKLM\\SYSTEM\\CCS\\ENUM\n")); return status; } // // Open the instance key for this devnode // status = IopOpenRegistryKeyEx( &instanceKey, enumKey, &DeviceNode->InstancePath, KEY_READ ); ZwClose(enumKey); if (!NT_SUCCESS(status)) { IopDbgPrint(( IOP_ENUMERATION_WARNING_LEVEL, "PipCallDriverAddDevice:\t\tError %#08lx opening %wZ enum key\n", status, &DeviceNode->InstancePath)); return status; } // // Get the class value to locate the class key for this devnode // status = IopGetRegistryValue(instanceKey, REGSTR_VALUE_CLASSGUID, &keyValueInformation); if(NT_SUCCESS(status)) { if ( keyValueInformation->Type == REG_SZ && keyValueInformation->DataLength) { IopRegistryDataToUnicodeString( &unicodeClassGuid, (PWSTR) KEY_VALUE_DATA(keyValueInformation), keyValueInformation->DataLength); IopDbgPrint(( IOP_ENUMERATION_INFO_LEVEL, "PipCallDriverAddDevice:\t\tClass GUID is %wZ\n", &unicodeClassGuid)); if (InitSafeBootMode) { if (!IopSafebootDriverLoad(&unicodeClassGuid)) { PKEY_VALUE_FULL_INFORMATION ClassValueInformation = NULL; NTSTATUS s; // // don't load the driver // IopDbgPrint((IOP_ENUMERATION_WARNING_LEVEL, "SAFEBOOT: skipping device = %wZ\n", &unicodeClassGuid)); s = IopGetRegistryValue(instanceKey, REGSTR_VAL_DEVDESC, &ClassValueInformation); if (NT_SUCCESS(s)) { UNICODE_STRING ClassString; RtlInitUnicodeString(&ClassString, (PCWSTR) KEY_VALUE_DATA(ClassValueInformation)); IopBootLog(&ClassString, FALSE); } else { IopBootLog(&unicodeClassGuid, FALSE); } ZwClose(instanceKey); return STATUS_UNSUCCESSFUL; } } // // Open the class key // status = IopOpenRegistryKeyEx( &controlKey, NULL, &CmRegistryMachineSystemCurrentControlSetControlClass, KEY_READ ); if (NT_SUCCESS(status)) { status = IopOpenRegistryKeyEx( &classKey, controlKey, &unicodeClassGuid, KEY_READ ); if (!NT_SUCCESS(status)) { IopDbgPrint(( IOP_ENUMERATION_WARNING_LEVEL, "PipCallDriverAddDevice:\tUnable to open GUID key " "%wZ - %#08lx\n", &unicodeClassGuid,status)); } ZwClose(controlKey); } else { IopDbgPrint(( IOP_ENUMERATION_WARNING_LEVEL, "PipCallDriverAddDevice:\tUnable to open " "HKLM\\SYSTEM\\CCS\\CONTROL\\CLASS - %#08lx\n", status)); } if (classKey != NULL) { UNICODE_STRING unicodeProperties; PiWstrToUnicodeString(&unicodeProperties, REGSTR_KEY_DEVICE_PROPERTIES ); status = IopOpenRegistryKeyEx( &classPropsKey, classKey, &unicodeProperties, KEY_READ ); if (!NT_SUCCESS(status)) { IopDbgPrint(( IOP_ENUMERATION_WARNING_LEVEL, "PipCallDriverAddDevice:\tUnable to open GUID\\Properties key " "%wZ - %#08lx\n", &unicodeClassGuid,status)); } } } ExFreePool(keyValueInformation); keyValueInformation = NULL; } // // Check to see if there's a service assigned to this device node. If // there's not then we can bail out without wasting too much time. // RtlZeroMemory(&queryContext, sizeof(queryContext)); queryContext.DeviceNode = DeviceNode; queryContext.LoadDriver = LoadDriver; queryContext.AddContext = Context; RtlZeroMemory(queryTable, sizeof(queryTable)); queryTable[0].QueryRoutine = (PRTL_QUERY_REGISTRY_ROUTINE) PipCallDriverAddDeviceQueryRoutine; queryTable[0].Name = REGSTR_VAL_LOWERFILTERS; queryTable[0].EntryContext = (PVOID) UIntToPtr(LowerDeviceFilters); status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE, (PWSTR) instanceKey, queryTable, &queryContext, NULL); if (NT_SUCCESS(status)) { if (classKey != NULL) { queryTable[0].QueryRoutine = (PRTL_QUERY_REGISTRY_ROUTINE) PipCallDriverAddDeviceQueryRoutine; queryTable[0].Name = REGSTR_VAL_LOWERFILTERS; queryTable[0].EntryContext = (PVOID) UIntToPtr(LowerClassFilters); status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE, (PWSTR) classKey, queryTable, &queryContext, NULL); if (!NT_SUCCESS(status)) { IopDbgPrint(( IOP_ENUMERATION_WARNING_LEVEL, "PipCallDriverAddDevice\t\tError %#08lx reading LowerClassFilters " "value for %wZ\n", status, &DeviceNode->InstancePath)); } } if (NT_SUCCESS(status)) { queryTable[0].QueryRoutine = (PRTL_QUERY_REGISTRY_ROUTINE) PipCallDriverAddDeviceQueryRoutine; queryTable[0].Name = REGSTR_VALUE_SERVICE; queryTable[0].EntryContext = (PVOID) UIntToPtr(DeviceService); queryTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED; status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE, (PWSTR) instanceKey, queryTable, &queryContext, NULL); if (!NT_SUCCESS(status)) { IopDbgPrint(( IOP_ENUMERATION_WARNING_LEVEL, "PipCallDriverAddDevice\t\tError %#08lx reading service " "value for %wZ\n", status, &DeviceNode->InstancePath)); } } } else { IopDbgPrint(( IOP_ENUMERATION_WARNING_LEVEL, "PipCallDriverAddDevice\t\tError %#08lx reading LowerDeviceFilters " "value for %wZ\n", status, &DeviceNode->InstancePath)); } if (DeviceNode->Flags & DNF_LEGACY_DRIVER) { // // One of the services for this device is a legacy driver. Don't try // to add any filters since we'll just mess up the device stack. // status = STATUS_SUCCESS; goto Cleanup; } else if (NT_SUCCESS(status)) { // // Call was successful so we must have been able to reference the // driver object. // ASSERT(queryContext.DriverLists[DeviceService] != NULL); if (queryContext.DriverLists[DeviceService]->NextEntry != NULL) { // // There's more than one service assigned to this device. Configuration // error IopDbgPrint(( IOP_ENUMERATION_ERROR_LEVEL, "PipCallDriverAddDevice: Configuration Error - more " "than one service in driver list\n")); PipSetDevNodeProblem(DeviceNode, CM_PROB_REGISTRY); status = STATUS_UNSUCCESSFUL; goto Cleanup; } // // this is the only case (FDO specified) where we can ignore PDO's characteristics // usePdoCharacteristics = FALSE; } else if (status == STATUS_OBJECT_NAME_NOT_FOUND) { if (!IopDeviceNodeFlagsToCapabilities(DeviceNode)->RawDeviceOK) { // // The device cannot be used raw. Bail out now. // status = STATUS_UNSUCCESSFUL; goto Cleanup; } else { // // Raw device access is okay. // PipClearDevNodeProblem(DeviceNode); usePdoCharacteristics = TRUE; // shouldn't need to do this, but better be safe than sorry deviceRaw = TRUE; status = STATUS_SUCCESS; } } else { // // something else went wrong while parsing the service key. The // query routine will have set the flags appropriately so we can // just bail out. // goto Cleanup; } // // For each type of filter driver we want to build a list of the driver // objects to be loaded. We'll build all the driver lists if we can // and deal with error conditions afterwards. // // // First get all the information we have to out of the instance key and // the device node. // RtlZeroMemory(queryTable, sizeof(queryTable)); queryTable[0].QueryRoutine = (PRTL_QUERY_REGISTRY_ROUTINE) PipCallDriverAddDeviceQueryRoutine; queryTable[0].Name = REGSTR_VAL_UPPERFILTERS; queryTable[0].EntryContext = (PVOID) UIntToPtr(UpperDeviceFilters); status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE, (PWSTR) instanceKey, queryTable, &queryContext, NULL); if (NT_SUCCESS(status) && classKey) { queryTable[0].QueryRoutine = (PRTL_QUERY_REGISTRY_ROUTINE) PipCallDriverAddDeviceQueryRoutine; queryTable[0].Name = REGSTR_VAL_UPPERFILTERS; queryTable[0].EntryContext = (PVOID) UIntToPtr(UpperClassFilters); status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE, (PWSTR) classKey, queryTable, &queryContext, NULL); } if (NT_SUCCESS(status)) { UCHAR serviceType = 0; PDRIVER_LIST_ENTRY listEntry = queryContext.DriverLists[serviceType]; // // Make sure there's no more than one device service. Anything else is // a configuration error. // ASSERT(!(DeviceNode->Flags & DNF_LEGACY_DRIVER)); ASSERTMSG( "Error - Device has no service but cannot be run RAW\n", ((queryContext.DriverLists[DeviceService] != NULL) || (deviceRaw))); // // Do preinit work. // fdoDeviceObject = NULL; topOfLowerFilterStack = NULL; topOfPdoStack = IoGetAttachedDevice(DeviceNode->PhysicalDeviceObject); // // It's okay to try adding all the drivers. // for (serviceType = 0; serviceType < MaximumAddStage; serviceType++) { IopDbgPrint(( IOP_ENUMERATION_INFO_LEVEL, "PipCallDriverAddDevice: Adding Services (type %d)\n", serviceType)); if (serviceType == DeviceService) { topOfLowerFilterStack = IoGetAttachedDevice(DeviceNode->PhysicalDeviceObject); if (deviceRaw && (queryContext.DriverLists[serviceType] == NULL)) { // // Mark the devnode as added, as it has no service. // ASSERT(queryContext.DriverLists[serviceType] == NULL); PipSetDevNodeState(DeviceNode, DeviceNodeDriversAdded, NULL); } else { // // Since we are going to see a service, grab a pointer to // the current top of the stack. While here, assert there // is exactly one service driver to load... // ASSERT(queryContext.DriverLists[serviceType]); ASSERT(!queryContext.DriverLists[serviceType]->NextEntry); } } for (listEntry = queryContext.DriverLists[serviceType]; listEntry != NULL; listEntry = listEntry->NextEntry) { PDRIVER_ADD_DEVICE addDeviceRoutine; IopDbgPrint(( IOP_ENUMERATION_INFO_LEVEL, "PipCallDriverAddDevice:\tAdding driver %#08lx\n", listEntry->DriverObject)); ASSERT(listEntry->DriverObject); ASSERT(listEntry->DriverObject->DriverExtension); ASSERT(listEntry->DriverObject->DriverExtension->AddDevice); // // Invoke the driver's AddDevice() entry point. // addDeviceRoutine = listEntry->DriverObject->DriverExtension->AddDevice; status = PpvUtilCallAddDevice( DeviceNode->PhysicalDeviceObject, listEntry->DriverObject, addDeviceRoutine, VerifierTypeFromServiceType(serviceType) ); IopDbgPrint(( IOP_ENUMERATION_TRACE_LEVEL, "PipCallDriverAddDevice:\t\tRoutine returned " "%#08lx\n", status)); if (NT_SUCCESS(status)) { // // If this is a service, mark the it is legal for a filter to succeed AddDevice // but fail to attach anything to the top of the stack. // if (serviceType == DeviceService) { fdoDeviceObject = topOfLowerFilterStack->AttachedDevice; ASSERT(fdoDeviceObject); } PipSetDevNodeState(DeviceNode, DeviceNodeDriversAdded, NULL); } else if (serviceType == DeviceService) { // // Mark the stack appropriately. // IovUtilMarkStack( DeviceNode->PhysicalDeviceObject, topOfPdoStack->AttachedDevice, fdoDeviceObject, FALSE ); // // If service fails, then add failed. (Alternately, if // filter drivers return failure, we keep going.) // PipRequestDeviceRemoval(DeviceNode, FALSE, CM_PROB_FAILED_ADD); goto Cleanup; } if (IoGetAttachedDevice(DeviceNode->PhysicalDeviceObject)->Flags & DO_DEVICE_INITIALIZING) { IopDbgPrint(( IOP_ENUMERATION_WARNING_LEVEL, "***************** DO_DEVICE_INITIALIZING not cleared on %#08lx\n", IoGetAttachedDevice(DeviceNode->PhysicalDeviceObject))); } ASSERT_INITED(IoGetAttachedDevice(DeviceNode->PhysicalDeviceObject)); } } // // Mark the stack appropriately. We tell the verifier the stack is raw // if the fdo is NULL and we made it this far. // IovUtilMarkStack( DeviceNode->PhysicalDeviceObject, topOfPdoStack->AttachedDevice, fdoDeviceObject, ((fdoDeviceObject == NULL) || deviceRaw) ); // // change PDO and all attached objects // to have properties specified in the registry // PipChangeDeviceObjectFromRegistryProperties(DeviceNode->PhysicalDeviceObject, classPropsKey, instanceKey, usePdoCharacteristics); // // CapabilityFlags are refreshed with call to IopSaveDeviceCapabilities after device is started // } else { IopDbgPrint(( IOP_ENUMERATION_ERROR_LEVEL, "PipCallDriverAddDevice: Error %#08lx while building " "driver load list\n", status)); goto Cleanup; } deviceObject = DeviceNode->PhysicalDeviceObject; status = IopQueryLegacyBusInformation( deviceObject, NULL, &DeviceNode->InterfaceType, &DeviceNode->BusNumber ); if (NT_SUCCESS(status)) { IopInsertLegacyBusDeviceNode(DeviceNode, DeviceNode->InterfaceType, DeviceNode->BusNumber); } else { DeviceNode->InterfaceType = InterfaceTypeUndefined; DeviceNode->BusNumber = 0xfffffff0; } status = STATUS_SUCCESS; ASSERT(DeviceNode->State == DeviceNodeDriversAdded); Cleanup: { UCHAR i; IopDbgPrint(( IOP_ENUMERATION_VERBOSE_LEVEL, "PipCallDriverAddDevice: DevNode flags leaving = %#08lx\n", DeviceNode->Flags)); IopDbgPrint(( IOP_ENUMERATION_VERBOSE_LEVEL, "PipCallDriverAddDevice: Cleaning up\n")); // // Free the entries in the driver load list & release the references on // their driver objects. // for (i = 0; i < MaximumAddStage; i++) { PDRIVER_LIST_ENTRY listHead = queryContext.DriverLists[i]; while(listHead != NULL) { PDRIVER_LIST_ENTRY tmp = listHead; listHead = listHead->NextEntry; ASSERT(tmp->DriverObject != NULL); // // Let the driver unload if it hasn't created any device // objects. We only do this if the paging stack is already // online (the same filter may be needed by more than one card). // IopInitializeBootDrivers will take care of cleaning up any // leftover drivers after boot. // if (PnPBootDriversInitialized) { IopUnloadAttachedDriver(tmp->DriverObject); } ObDereferenceObject(tmp->DriverObject); ExFreePool(tmp); } } } ZwClose(instanceKey); if (classKey != NULL) { ZwClose(classKey); } if (classPropsKey != NULL) { ZwClose(classPropsKey); } IopDbgPrint(( IOP_ENUMERATION_INFO_LEVEL, "PipCallDriverAddDevice: Returning status %#08lx\n", status)); return status; } NTSTATUS PipCallDriverAddDeviceQueryRoutine( IN PWSTR ValueName, IN ULONG ValueType, IN PWCHAR ValueData, IN ULONG ValueLength, IN PQUERY_CONTEXT Context, IN ULONG ServiceType ) /*++ Routine Description: This routine is called to build a list of driver objects which need to be Added to a physical device object. Each time it is called with a service name it will locate a driver object for that device and append it to the proper driver list for the device node. In the event a driver object cannot be located or that it cannot be loaded at this time, this routine will return an error and will set the flags in the device node in the context appropriately. Arguments: ValueName - the name of the value ValueType - the type of the value ValueData - the data in the value (unicode string data) ValueLength - the number of bytes in the value data Context - a structure which contains the device node, the context passed to PipCallDriverAddDevice and the driver lists for the device node. EntryContext - the index of the driver list the routine should append nodes to. Return Value: STATUS_SUCCESS if the driver was located and added to the list successfully or if there was a non-fatal error while handling the driver. an error value indicating why the driver could not be added to the list. --*/ { UNICODE_STRING unicodeServiceName; UNICODE_STRING unicodeDriverName; PKEY_VALUE_FULL_INFORMATION keyValueInformation; ULONG i; ULONG loadType; PWSTR prefixString = L"\\Driver\\"; BOOLEAN madeupService; USHORT groupIndex; PDRIVER_OBJECT driverObject = NULL; NTSTATUS status = STATUS_SUCCESS; NTSTATUS driverEntryStatus; BOOLEAN freeDriverName = FALSE; HANDLE handle, serviceKey; #if DBG PDRIVER_OBJECT tempDrvObj; #endif // // Preinit // serviceKey = NULL; IopDbgPrint(( IOP_ENUMERATION_INFO_LEVEL, "PipCallDriverAddDevice:\t\tValue %ws [Type %d, Len %d] @ " "%#08lx\n", ValueName, ValueType, ValueLength, ValueData)); // // First check and make sure that the value type is okay. An invalid type // is not a fatal error. // if (ValueType != REG_SZ) { IopDbgPrint(( IOP_ENUMERATION_ERROR_LEVEL, "PipCallDriverAddDevice:\t\tValueType %d invalid for " "ServiceType %d\n", ValueType,ServiceType)); return STATUS_SUCCESS; } // // Make sure the string is a reasonable length. // if (ValueLength <= sizeof(WCHAR)) { IopDbgPrint(( IOP_ENUMERATION_ERROR_LEVEL, "PipCallDriverAddDevice:\t\tValueLength %d is too short\n", ValueLength)); return STATUS_SUCCESS; } RtlInitUnicodeString(&unicodeServiceName, ValueData); IopDbgPrint(( IOP_ENUMERATION_INFO_LEVEL, "PipCallDriverAddDevice:\t\t\tService Name %wZ\n", &unicodeServiceName)); // // Check the service name to see if it should be used directly to reference // the driver object. If the string begins with "\Driver", make sure the // madeupService flag is set. // madeupService = TRUE; i = 0; while(*prefixString != L'\0') { if (unicodeServiceName.Buffer[i] != *prefixString) { madeupService = FALSE; break; } i++; prefixString++; } // // Get the driver name from the service key. We need this to figure out // if the driver is already in memory. // if (madeupService) { RtlInitUnicodeString(&unicodeDriverName, unicodeServiceName.Buffer); } else { // // BUGBUG - (RBN) Hack to set the service name in the devnode if it // isn't already set. // // This probably should be done earlier somewhere else after the // INF is run, but if we don't do it now we'll blow up when we // call IopGetDriverLoadType below. // if (Context->DeviceNode->ServiceName.Length == 0) { Context->DeviceNode->ServiceName = unicodeServiceName; Context->DeviceNode->ServiceName.Buffer = ExAllocatePool( NonPagedPool, unicodeServiceName.MaximumLength ); if (Context->DeviceNode->ServiceName.Buffer != NULL) { RtlCopyMemory( Context->DeviceNode->ServiceName.Buffer, unicodeServiceName.Buffer, unicodeServiceName.MaximumLength ); } else { PiWstrToUnicodeString( &Context->DeviceNode->ServiceName, NULL ); IopDbgPrint(( IOP_ENUMERATION_ERROR_LEVEL, "PipCallDriverAddDevice:\t\t\tCannot allocate memory for service name in devnode\n")); status = STATUS_UNSUCCESSFUL; goto Cleanup; } } // // Check in the registry to find the name of the driver object // for this device. // status = PipOpenServiceEnumKeys(&unicodeServiceName, KEY_READ, &serviceKey, NULL, FALSE); if (!NT_SUCCESS(status)) { // // Cannot open the service key for this driver. This is a // fatal error. // IopDbgPrint(( IOP_ENUMERATION_ERROR_LEVEL, "PipCallDriverAddDevice:\t\t\tStatus %#08lx " "opening service key\n", status)); PipSetDevNodeProblem(Context->DeviceNode, CM_PROB_REGISTRY); goto Cleanup; } status = IopGetDriverNameFromKeyNode(serviceKey, &unicodeDriverName); if (!NT_SUCCESS(status)) { // // Can't get the driver name from the service key. This is a // fatal error. // IopDbgPrint(( IOP_ENUMERATION_ERROR_LEVEL, "PipCallDriverAddDevice:\t\t\tStatus %#08lx " "getting driver name\n", status)); PipSetDevNodeProblem(Context->DeviceNode, CM_PROB_REGISTRY); goto Cleanup; } else { freeDriverName = TRUE; } // // Note that we don't close the service key here. We may need it later. // } IopDbgPrint(( IOP_ENUMERATION_INFO_LEVEL, "PipCallDriverAddDevice:\t\t\tDriverName is %wZ\n", &unicodeDriverName)); driverObject = IopReferenceDriverObjectByName(&unicodeDriverName); if (driverObject == NULL) { // // We couldn't find a driver object. It's possible the driver isn't // loaded & initialized so check to see if we can try to load it // now. // if (madeupService) { // // The madeup service's driver doesn't seem to exist yet. // We will fail the request without setting a problem code so // we will try it again later. (Root Enumerated devices...) // IopDbgPrint(( IOP_ENUMERATION_WARNING_LEVEL, "PipCallDriverAddDevice:\t\t\tCannot find driver " "object for madeup service\n")); status = STATUS_UNSUCCESSFUL; goto Cleanup; } // // Get the start type. We always need this in case the service is // disabled. Default to SERVICE_DISABLED if the service's start type // is missing or corrupted. // loadType = SERVICE_DISABLED; status = IopGetRegistryValue(serviceKey, L"Start", &keyValueInformation); if (NT_SUCCESS(status)) { if (keyValueInformation->Type == REG_DWORD) { if (keyValueInformation->DataLength == sizeof(ULONG)) { loadType = *(PULONG)KEY_VALUE_DATA(keyValueInformation); } } ExFreePool(keyValueInformation); } if (ServiceType != DeviceService && !PnPBootDriversInitialized) { // // Get the group index. We need this because PipLoadBootFilterDriver // uses the group index as an index into it's internally sorted // list of loaded boot drivers. // groupIndex = PpInitGetGroupOrderIndex(serviceKey); // // If we are in BootDriverInitialization phase and trying to load a // filter driver // status = PipLoadBootFilterDriver( &unicodeDriverName, groupIndex, &driverObject ); if (NT_SUCCESS(status)) { ASSERT(driverObject); #if DBG tempDrvObj = IopReferenceDriverObjectByName(&unicodeDriverName); ASSERT(tempDrvObj == driverObject); #else ObReferenceObject(driverObject); #endif } else if (status != STATUS_DRIVER_BLOCKED && status != STATUS_DRIVER_BLOCKED_CRITICAL) { goto Cleanup; } } else { if (!Context->LoadDriver) { // // We're not supposed to try and load a driver - most likely our // disk drivers aren't initialized yet. We need to stop the add // process but we can't mark the devnode as failed or we won't // be called again when we can load the drivers. // IopDbgPrint(( IOP_ENUMERATION_VERBOSE_LEVEL, "PipCallDriverAddDevice:\t\t\tNot allowed to load " "drivers yet\n")); status = STATUS_UNSUCCESSFUL; goto Cleanup; } if (loadType > Context->AddContext->DriverStartType) { if (loadType == SERVICE_DISABLED && !PipDoesDevNodeHaveProblem(Context->DeviceNode)) { PipSetDevNodeProblem(Context->DeviceNode, CM_PROB_DISABLED_SERVICE); } // // The service is either disabled or we are not at the right // time to load it. Don't load it, but make sure we can get // called again. If a service is marked as demand start, we // always load it. // IopDbgPrint(( IOP_ENUMERATION_INFO_LEVEL, "PipCallDriverAddDevice:\t\t\tService is disabled or not at right time to load it\n")); status = STATUS_UNSUCCESSFUL; goto Cleanup; } // // Check in the registry to find the name of the driver object // for this device. // status = PipOpenServiceEnumKeys(&unicodeServiceName, KEY_READ, &handle, NULL, FALSE); if (!NT_SUCCESS(status)) { // // Cannot open the service key for this driver. This is a // fatal error. // IopDbgPrint(( IOP_ENUMERATION_ERROR_LEVEL, "PipCallDriverAddDevice:\t\t\tStatus %#08lx " "opening service key\n", status)); // // Convert the status values into something more definite. // if (status != STATUS_INSUFFICIENT_RESOURCES) { status = STATUS_ILL_FORMED_SERVICE_ENTRY; } } else { // // The handle we pass in here will be closed by IopLoadDriver. // Note that IopLoadDriver return success without actually // loading the driver. This happens in the safe mode boot case. // status = IopLoadDriver( handle, FALSE, (ServiceType != DeviceService)? TRUE : FALSE, &driverEntryStatus); // // Convert the status values into something more definite. // if (!NT_SUCCESS(status)) { if (status == STATUS_FAILED_DRIVER_ENTRY) { // // Preserve insufficient resources return by the driver // if (driverEntryStatus == STATUS_INSUFFICIENT_RESOURCES) { status = STATUS_INSUFFICIENT_RESOURCES; } } else if ((status != STATUS_INSUFFICIENT_RESOURCES) && (status != STATUS_PLUGPLAY_NO_DEVICE) && (status != STATUS_DRIVER_FAILED_PRIOR_UNLOAD) && (status != STATUS_DRIVER_BLOCKED) && (status != STATUS_DRIVER_BLOCKED_CRITICAL)) { // // Assume this happened because the driver could not be // loaded. // status = STATUS_DRIVER_UNABLE_TO_LOAD; } } if (PnPInitialized) { IopCallDriverReinitializationRoutines(); } } // // Try and get a pointer to the driver object for the service. // driverObject = IopReferenceDriverObjectByName(&unicodeDriverName); if (driverObject) { if (!NT_SUCCESS(status)) { // // The driver should not be in memory upon failure. // ASSERT(!driverObject); ObDereferenceObject(driverObject); driverObject = NULL; } } else { if (NT_SUCCESS(status)) { // // Driver was probably not loaded because of safe mode. // ASSERT(InitSafeBootMode); status = STATUS_NOT_SAFE_MODE_DRIVER; } } } } // // If we still dont have a driver object, then something failed. // if (driverObject == NULL) { // // Apparently the load didn't work out very well. // ASSERT(!NT_SUCCESS(status)); IopDbgPrint((IOP_ENUMERATION_ERROR_LEVEL, "PipCallDriverAddDevice:\t\t\tUnable to reference " "driver %wZ (%x)\n", &unicodeDriverName, status)); if (!PipDoesDevNodeHaveProblem(Context->DeviceNode)) { switch(status) { case STATUS_ILL_FORMED_SERVICE_ENTRY: PipSetDevNodeProblem(Context->DeviceNode, CM_PROB_DRIVER_SERVICE_KEY_INVALID); break; case STATUS_INSUFFICIENT_RESOURCES: PipSetDevNodeProblem(Context->DeviceNode, CM_PROB_OUT_OF_MEMORY); break; case STATUS_PLUGPLAY_NO_DEVICE: PipSetDevNodeProblem(Context->DeviceNode, CM_PROB_LEGACY_SERVICE_NO_DEVICES); break; case STATUS_DRIVER_FAILED_PRIOR_UNLOAD: PipSetDevNodeProblem(Context->DeviceNode, CM_PROB_DRIVER_FAILED_PRIOR_UNLOAD); break; case STATUS_DRIVER_UNABLE_TO_LOAD: PipSetDevNodeProblem(Context->DeviceNode, CM_PROB_DRIVER_FAILED_LOAD); break; case STATUS_FAILED_DRIVER_ENTRY: PipSetDevNodeProblem(Context->DeviceNode, CM_PROB_FAILED_DRIVER_ENTRY); break; case STATUS_DRIVER_BLOCKED_CRITICAL: PipSetDevNodeProblem(Context->DeviceNode, CM_PROB_DRIVER_BLOCKED); Context->DeviceNode->Flags |= DNF_DRIVER_BLOCKED; break; case STATUS_DRIVER_BLOCKED: Context->DeviceNode->Flags |= DNF_DRIVER_BLOCKED; status = STATUS_SUCCESS; break; default: case STATUS_NOT_SAFE_MODE_DRIVER: ASSERT(0); PipSetDevNodeProblem(Context->DeviceNode, CM_PROB_FAILED_ADD); break; } SAVE_FAILURE_INFO(Context->DeviceNode, status); } else { // // We're very curious - when does this happen? // ASSERT(0); } goto Cleanup; } if (!(driverObject->Flags & DRVO_INITIALIZED)) { ObDereferenceObject(driverObject); status = STATUS_UNSUCCESSFUL; goto Cleanup; } IopDbgPrint(( IOP_ENUMERATION_VERBOSE_LEVEL, "PipCallDriverAddDevice:\t\t\tDriver Reference %#08lx\n", driverObject)); // // Check to see if the driver is a legacy driver rather than a Pnp one. // if (IopIsLegacyDriver(driverObject)) { // // It is. Since the legacy driver may have already obtained a // handle to the device object, we need to assume this device // has been added and started. // IopDbgPrint(( IOP_ENUMERATION_WARNING_LEVEL, "PipCallDriverAddDevice:\t\t\tDriver is a legacy " "driver\n")); if (ServiceType == DeviceService) { Context->DeviceNode->Flags |= DNF_LEGACY_DRIVER; PipSetDevNodeState(Context->DeviceNode, DeviceNodeStarted, NULL); status = STATUS_UNSUCCESSFUL; } else { // // We allow someone to plug in a legacy driver as a filter driver. // In this case, the legacy driver will be loaded but will not be part // of our pnp driver stack. // status = STATUS_SUCCESS; } goto Cleanup; } // // There's a chance the driver detected this PDO during it's driver entry // routine. If it did then just bail out. // if (Context->DeviceNode->State != DeviceNodeInitialized && Context->DeviceNode->State != DeviceNodeDriversAdded) { IopDbgPrint(( IOP_ENUMERATION_WARNING_LEVEL, "PipCallDriverAddDevice\t\t\tDevNode was reported " "as detected during driver entry\n")); status = STATUS_UNSUCCESSFUL; goto Cleanup; } // // Add the driver to the list. // { PDRIVER_LIST_ENTRY listEntry; PDRIVER_LIST_ENTRY *runner = &(Context->DriverLists[ServiceType]); status = STATUS_SUCCESS; // // Allocate a new list entry to queue this driver object for the caller // listEntry = ExAllocatePool(PagedPool, sizeof(DRIVER_LIST_ENTRY)); if (listEntry == NULL) { IopDbgPrint(( IOP_ENUMERATION_WARNING_LEVEL, "PipCallDriverAddDevice:\t\t\tUnable to allocate list " "entry\n")); status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; } listEntry->DriverObject = driverObject; listEntry->NextEntry = NULL; while(*runner != NULL) { runner = &((*runner)->NextEntry); } *runner = listEntry; } Cleanup: if (serviceKey) { ZwClose(serviceKey); } if (freeDriverName) { RtlFreeUnicodeString(&unicodeDriverName); } return status; } NTSTATUS PiRestartDevice( IN PPI_DEVICE_REQUEST Request ) { ADD_CONTEXT addContext; NTSTATUS status; PDEVICE_NODE deviceNode; PAGED_CODE(); ASSERT(Request->DeviceObject != NULL); deviceNode = (PDEVICE_NODE)Request->DeviceObject->DeviceObjectExtension->DeviceNode; if (PipIsDevNodeDeleted(deviceNode)) { return STATUS_DELETE_PENDING; } else if (PipDoesDevNodeHaveProblem(deviceNode)) { return STATUS_UNSUCCESSFUL; } switch(deviceNode->State) { case DeviceNodeStartPending: // // Not wired up today, but if the device is starting then we should // in theory defer completing this request until the IRP is // completed. // ASSERT(0); // // Fall through // case DeviceNodeStarted: case DeviceNodeQueryStopped: case DeviceNodeStopped: case DeviceNodeRestartCompletion: case DeviceNodeEnumeratePending: return STATUS_SUCCESS; case DeviceNodeInitialized: // // ISSUE - 2000/08/23 - AdriaO: Question, // When this happens, isn't it a bug in user mode? // // Anyway, fall on through... // //ASSERT(0); case DeviceNodeRemoved: ASSERT(!(deviceNode->UserFlags & DNUF_WILL_BE_REMOVED)); IopRestartDeviceNode(deviceNode); break; case DeviceNodeUninitialized: case DeviceNodeDriversAdded: case DeviceNodeResourcesAssigned: case DeviceNodeEnumerateCompletion: case DeviceNodeStartCompletion: case DeviceNodeStartPostWork: // // ISSUE - 2000/08/23 - AdriaO: Question, // When this happens, isn't it a bug in user mode? // //ASSERT(0); break; case DeviceNodeAwaitingQueuedDeletion: case DeviceNodeAwaitingQueuedRemoval: case DeviceNodeQueryRemoved: case DeviceNodeRemovePendingCloses: case DeviceNodeDeletePendingCloses: return STATUS_UNSUCCESSFUL; case DeviceNodeDeleted: case DeviceNodeUnspecified: default: ASSERT(0); return STATUS_UNSUCCESSFUL; } if (Request->RequestType == StartDevice) { addContext.DriverStartType = SERVICE_DEMAND_START; ObReferenceObject(deviceNode->PhysicalDeviceObject); status = PipProcessDevNodeTree( deviceNode, PnPBootDriversInitialized, // LoadDriver FALSE, // ReallocateResources EnumTypeNone, Request->CompletionEvent != NULL, // Synchronous FALSE, &addContext, Request); } return STATUS_SUCCESS; } NTSTATUS PipQueryDeviceCapabilities( IN PDEVICE_NODE DeviceNode, OUT PDEVICE_CAPABILITIES Capabilities ) /*++ Routine Description: This routine will issue an irp to the DeviceObject to retrieve the pnp device capabilities. Should only be called twice - first from PipProcessNewDeviceNode, and second from IopQueryAndSaveDeviceNodeCapabilities, called after device is started. If you consider calling this device, see if DeviceNode->CapabilityFlags does what you need instead (accessed via IopDeviceNodeFlagsToCapabilities(...). Arguments: DeviceNode - the device object the request should be sent to. Capabilities - a capabilities structure to be filled in by the driver. Return Value: status --*/ { IO_STACK_LOCATION irpStack; NTSTATUS status; // // Initialize the capabilities structure. // RtlZeroMemory(Capabilities, sizeof(DEVICE_CAPABILITIES)); Capabilities->Size = sizeof(DEVICE_CAPABILITIES); Capabilities->Version = 1; Capabilities->Address = Capabilities->UINumber = (ULONG)-1; // // Initialize the stack location to pass to IopSynchronousCall() // RtlZeroMemory(&irpStack, sizeof(IO_STACK_LOCATION)); // // Query the device's capabilities // irpStack.MajorFunction = IRP_MJ_PNP; irpStack.MinorFunction = IRP_MN_QUERY_CAPABILITIES; irpStack.Parameters.DeviceCapabilities.Capabilities = Capabilities; status = IopSynchronousCall(DeviceNode->PhysicalDeviceObject, &irpStack, NULL); ASSERT(status != STATUS_PENDING); return status; } NTSTATUS PipMakeGloballyUniqueId( IN PDEVICE_OBJECT DeviceObject, IN PWCHAR UniqueId, OUT PWCHAR *GloballyUniqueId ) { NTSTATUS status; ULONG length; PWSTR id, Prefix = NULL; HANDLE enumKey; HANDLE instanceKey; UCHAR keyBuffer[FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data) + sizeof(ULONG)]; PKEY_VALUE_PARTIAL_INFORMATION keyValue, stringValueBuffer = NULL; UNICODE_STRING valueName; ULONG uniqueIdValue, Hash, hashInstance; PDEVICE_NODE parentNode; PAGED_CODE(); id = NULL; // // We need to build an instance id to uniquely identify this // device. We will accomplish this by producing a prefix that will be // prepended to the non-unique device id supplied. // // // To 'unique-ify' the child's instance ID, we will retrieve // the unique "UniqueParentID" number that has been assigned // to the parent and use it to construct a prefix. This is // the legacy mechanism supported here so that existing device // settings are not lost on upgrade. // PiLockPnpRegistry(FALSE); parentNode = ((PDEVICE_NODE)DeviceObject->DeviceObjectExtension->DeviceNode)->Parent; status = IopOpenRegistryKeyEx( &enumKey, NULL, &CmRegistryMachineSystemCurrentControlSetEnumName, KEY_READ | KEY_WRITE ); if (!NT_SUCCESS(status)) { IopDbgPrint(( IOP_ENUMERATION_ERROR_LEVEL, "PipMakeGloballyUniqueId:\tUnable to open HKLM\\SYSTEM\\CCS\\ENUM (status %08lx)\n", status)); goto clean0; } // // Open the instance key for this devnode // status = IopOpenRegistryKeyEx( &instanceKey, enumKey, &parentNode->InstancePath, KEY_READ | KEY_WRITE ); if (!NT_SUCCESS(status)) { IopDbgPrint(( IOP_ENUMERATION_ERROR_LEVEL, "PipMakeGloballyUniqueId:\tUnable to open registry key for %wZ (status %08lx)\n", &parentNode->InstancePath, status)); goto clean1; } // // Attempt to retrieve the "UniqueParentID" value from the device // instance key. // keyValue = (PKEY_VALUE_PARTIAL_INFORMATION)keyBuffer; PiWstrToUnicodeString(&valueName, REGSTR_VALUE_UNIQUE_PARENT_ID); status = ZwQueryValueKey(instanceKey, &valueName, KeyValuePartialInformation, keyValue, sizeof(keyBuffer), &length ); if (NT_SUCCESS(status)) { ASSERT(keyValue->Type == REG_DWORD); ASSERT(keyValue->DataLength == sizeof(ULONG)); if ((keyValue->Type != REG_DWORD) || (keyValue->DataLength != sizeof(ULONG))) { status = STATUS_INVALID_PARAMETER; goto clean2; } uniqueIdValue = *(PULONG)(keyValue->Data); // // OK, we have a unique parent ID number to prefix to the // instance ID. Prefix = (PWSTR)ExAllocatePool(PagedPool, 9 * sizeof(WCHAR)); if (!Prefix) { status = STATUS_INSUFFICIENT_RESOURCES; goto clean2; } swprintf(Prefix, L"%x", uniqueIdValue); } else { // // This is the current mechanism for finding existing // device instance prefixes and calculating new ones if // required. // // // Attempt to retrieve the "ParentIdPrefix" value from the device // instance key. // PiWstrToUnicodeString(&valueName, REGSTR_VALUE_PARENT_ID_PREFIX); length = (MAX_PARENT_PREFIX + 1) * sizeof(WCHAR) + FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data); stringValueBuffer = ExAllocatePool(PagedPool, length); if (stringValueBuffer) { status = ZwQueryValueKey(instanceKey, &valueName, KeyValuePartialInformation, stringValueBuffer, length, &length); } else { status = STATUS_INSUFFICIENT_RESOURCES; goto clean2; } if (NT_SUCCESS(status)) { ASSERT(stringValueBuffer->Type == REG_SZ); if (stringValueBuffer->Type != REG_SZ) { status = STATUS_INVALID_PARAMETER; goto clean2; } // // Parent has already been assigned a "ParentIdPrefix". // Prefix = (PWSTR) ExAllocatePool(PagedPool, stringValueBuffer->DataLength); if (!Prefix) { status = STATUS_INSUFFICIENT_RESOURCES; goto clean2; } wcscpy(Prefix, (PWSTR) stringValueBuffer->Data); } else { // // Parent has not been assigned a "ParentIdPrefix". // Compute the prefix: // * Compute Hash // * Look for value of the form: // NextParentId..:REG_DWORD: // under CCS\Enum. If not present, create it. // * Assign the new "ParentIdPrefix" which will be of // of the form: // && // // Allocate a buffer once for the NextParentId... value // and for the prefix. length = (ULONG)(max(wcslen(REGSTR_VALUE_NEXT_PARENT_ID) + 2 + 8 + 8, MAX_PARENT_PREFIX) + 1); // Device instances are case in-sensitive. Upcase before // performing hash to ensure that the hash is case-insensitve. status = RtlUpcaseUnicodeString(&valueName, &parentNode->InstancePath, TRUE); if (!NT_SUCCESS(status)) { goto clean2; } HASH_UNICODE_STRING(&valueName, &Hash); RtlFreeUnicodeString(&valueName); Prefix = (PWSTR) ExAllocatePool(PagedPool, length * sizeof(WCHAR)); if (!Prefix) { status = STATUS_INSUFFICIENT_RESOURCES; goto clean2; } // Check for existence of "NextParentId...." value and update. swprintf(Prefix, L"%s.%x.%x", REGSTR_VALUE_NEXT_PARENT_ID, Hash, parentNode->Level); RtlInitUnicodeString(&valueName, Prefix); keyValue = (PKEY_VALUE_PARTIAL_INFORMATION)keyBuffer; status = ZwQueryValueKey(enumKey, &valueName, KeyValuePartialInformation, keyValue, sizeof(keyBuffer), &length ); if (NT_SUCCESS(status) && (keyValue->Type == REG_DWORD) && (keyValue->DataLength == sizeof(ULONG))) { hashInstance = *(PULONG)(keyValue->Data); } else { hashInstance = 0; } hashInstance++; status = ZwSetValueKey(enumKey, &valueName, TITLE_INDEX_VALUE, REG_DWORD, &hashInstance, sizeof(hashInstance) ); if (!NT_SUCCESS(status)) { goto clean2; } hashInstance--; // Create actual ParentIdPrefix string PiWstrToUnicodeString(&valueName, REGSTR_VALUE_PARENT_ID_PREFIX); length = swprintf(Prefix, L"%x&%x&%x", parentNode->Level, Hash, hashInstance) + 1; status = ZwSetValueKey(instanceKey, &valueName, TITLE_INDEX_VALUE, REG_SZ, Prefix, length * sizeof(WCHAR) ); if (!NT_SUCCESS(status)) { goto clean2; } } } // Construct the instance id from the non-unique id (if any) // provided by the child and the prefix we've constructed. length = (ULONG)(wcslen(Prefix) + (UniqueId ? wcslen(UniqueId) : 0) + 2); id = (PWSTR)ExAllocatePool(PagedPool, length * sizeof(WCHAR)); if (!id) { status = STATUS_INSUFFICIENT_RESOURCES; } else if (UniqueId) { swprintf(id, L"%s&%s", Prefix, UniqueId); } else { wcscpy(id, Prefix); } clean2: ZwClose(instanceKey); clean1: ZwClose(enumKey); clean0: PiUnlockPnpRegistry(); if (stringValueBuffer) { ExFreePool(stringValueBuffer); } if (Prefix) { ExFreePool(Prefix); } *GloballyUniqueId = id; return status; } BOOLEAN PipProcessCriticalDevice( IN PDEVICE_NODE DeviceNode ) /*++ Routine Description: This routine will check whether the device is a "critical" one (see the top of this file for a description of critical). If the device is critical then it will be assigned a service based on the contents of IopCriticalDeviceList. Arguments: DeviceNode - the device node to process Return Value: TRUE if the device is critical FALSE otherwise --*/ { HANDLE enumKey; HANDLE instanceKey; UNICODE_STRING service, classGuid, driver, lowerFilters, upperFilters; UNICODE_STRING serviceValue, guidValue, driverValue, lowerFiltersValue, upperFiltersValue; BOOLEAN foundMatch = FALSE; NTSTATUS status; #if DBG_SCOPE PWCHAR str; ULONG length; #endif IopDbgPrint(( IOP_ENUMERATION_INFO_LEVEL, "PipIsCriticalPnpDevice called for devnode %#08lx\n", DeviceNode)); // // Open the HKLM\System\CCS\Enum key. // status = IopOpenRegistryKeyEx( &enumKey, NULL, &CmRegistryMachineSystemCurrentControlSetEnumName, KEY_READ ); if (!NT_SUCCESS(status)) { IopDbgPrint(( IOP_ENUMERATION_ERROR_LEVEL, "PipIsCriticalPnpDevice: couldn't open enum key %#08lx\n", status)); return FALSE; } // // Open the instance key for this devnode // status = IopOpenRegistryKeyEx( &instanceKey, enumKey, &DeviceNode->InstancePath, KEY_ALL_ACCESS ); ZwClose(enumKey); if (!NT_SUCCESS(status)) { IopDbgPrint(( IOP_ENUMERATION_ERROR_LEVEL, "PipIsCriticalPnpDevice: couldn't open instance path key %wZ [%#08lx]\n", &(DeviceNode->InstancePath), status)); return FALSE; } // // Call IopProcessCriticalDeviceRoutine to // enumerate entries in the CriticalDeviceDatabase // and compare with HardwareId and CompatibleIds // value data from instanceKey. // PiWstrToUnicodeString(&service, NULL); PiWstrToUnicodeString(&classGuid, NULL); PiWstrToUnicodeString(&driver, NULL); PiWstrToUnicodeString(&lowerFilters, NULL); PiWstrToUnicodeString(&upperFilters, NULL); status = PipProcessCriticalDeviceRoutine(instanceKey, &foundMatch, &service, &classGuid, &driver, &lowerFilters, &upperFilters); if (!NT_SUCCESS(status) || !foundMatch) { IopDbgPrint(( IOP_ENUMERATION_VERBOSE_LEVEL, "PipProcessCriticalDevice: No match found for devnode %wZ [%#08lx]\n", &DeviceNode->InstancePath, status)); ZwClose(instanceKey); return FALSE; } // // If we get here then this is a "critical" device and we know the service // to setup for it. Set the service value in the registry. // IopDbgPrint(( IOP_ENUMERATION_WARNING_LEVEL, "PipProcessCriticalDevice: Setting up critical service\n")); PiWstrToUnicodeString(&serviceValue, REGSTR_VALUE_SERVICE); PiWstrToUnicodeString(&guidValue, REGSTR_VALUE_CLASSGUID); PiWstrToUnicodeString(&driverValue, REGSTR_VALUE_DRIVER); PiWstrToUnicodeString(&lowerFiltersValue, REGSTR_VALUE_LOWERFILTERS); PiWstrToUnicodeString(&upperFiltersValue, REGSTR_VALUE_UPPERFILTERS); if (classGuid.Buffer) { IopDbgPrint(( IOP_ENUMERATION_INFO_LEVEL, "PipProcessCriticalDevice: classGuid is %wZ\n", &classGuid)); status = ZwSetValueKey(instanceKey, &guidValue, 0L, REG_SZ, classGuid.Buffer, classGuid.Length + sizeof(UNICODE_NULL)); } if (driver.Buffer) { IopDbgPrint(( IOP_ENUMERATION_INFO_LEVEL, "PipProcessCriticalDevice: driver is %wZ\n", &driver)); status = ZwSetValueKey(instanceKey, &driverValue, 0L, REG_SZ, driver.Buffer, driver.Length + sizeof(UNICODE_NULL)); } if (lowerFilters.Buffer) { #if DBG_SCOPE str = lowerFilters.Buffer; while ((length = (ULONG)wcslen(str)) != 0) { IopDbgPrint(( IOP_ENUMERATION_INFO_LEVEL, "PipProcessCriticalDevice: lower filter is %ws\n", str)); str += (length + 1); } #endif status = ZwSetValueKey(instanceKey, &lowerFiltersValue, 0L, REG_MULTI_SZ, lowerFilters.Buffer, lowerFilters.Length); // + sizeof(UNICODE_NULL)); } if (upperFilters.Buffer) { #if DBG_SCOPE str = upperFilters.Buffer; while ((length = (ULONG)wcslen(str)) != 0) { IopDbgPrint(( IOP_ENUMERATION_INFO_LEVEL, "PipProcessCriticalDevice: upper filter is %ws\n", str)); str += (length + 1); } #endif status = ZwSetValueKey(instanceKey, &upperFiltersValue, 0L, REG_MULTI_SZ, upperFilters.Buffer, upperFilters.Length); // + sizeof(UNICODE_NULL)); } IopDbgPrint(( IOP_ENUMERATION_INFO_LEVEL, "PipProcessCriticalDevice: service is %wZ\n", &service)); status = ZwSetValueKey(instanceKey, &serviceValue, 0L, REG_SZ, service.Buffer, service.Length + sizeof(UNICODE_NULL)); // // If the service was set properly set the CONFIGFLAG_FINISH_INSTALL so // we will still get a new hw found popup and go through the class // installer. // if (NT_SUCCESS(status)) { UCHAR buffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(ULONG)]; UNICODE_STRING valueName; PKEY_VALUE_PARTIAL_INFORMATION keyInfo = (PKEY_VALUE_PARTIAL_INFORMATION) buffer; ULONG flags = 0; ULONG valueLength; NTSTATUS tmpStatus; PiWstrToUnicodeString(&valueName, REGSTR_VALUE_CONFIG_FLAGS); tmpStatus = ZwQueryValueKey(instanceKey, &valueName, KeyValuePartialInformation, keyInfo, sizeof(buffer), &valueLength); if (NT_SUCCESS(tmpStatus) && (keyInfo->Type == REG_DWORD)) { flags = *(PULONG)keyInfo->Data; } flags &= ~(CONFIGFLAG_REINSTALL | CONFIGFLAG_FAILEDINSTALL); flags |= CONFIGFLAG_FINISH_INSTALL; ZwSetValueKey(instanceKey, &valueName, 0L, REG_DWORD, &flags, sizeof(ULONG)); ASSERT(!PipDoesDevNodeHaveProblem(DeviceNode) || PipIsDevNodeProblem(DeviceNode, CM_PROB_NOT_CONFIGURED) || PipIsDevNodeProblem(DeviceNode, CM_PROB_FAILED_INSTALL) || PipIsDevNodeProblem(DeviceNode, CM_PROB_REINSTALL)); PipClearDevNodeProblem(DeviceNode); } ZwClose(instanceKey); RtlFreeUnicodeString(&service); RtlFreeUnicodeString(&classGuid); RtlFreeUnicodeString(&driver); RtlFreeUnicodeString(&lowerFilters); RtlFreeUnicodeString(&upperFilters); return (BOOLEAN)NT_SUCCESS(status); } NTSTATUS PipProcessCriticalDeviceRoutine( IN HANDLE HDevInstance, IN PBOOLEAN FoundMatch, IN PUNICODE_STRING ServiceName, IN PUNICODE_STRING ClassGuid, IN PUNICODE_STRING Driver, IN PUNICODE_STRING LowerFilters, IN PUNICODE_STRING UpperFilters ) /*++ Routine Description: This routine will enumerate all values of the CriticalDeviceDatabase registry key, and compare each with entries within the HardwareId and CompatibleIds values of key associated with the current device instance. Arguments: HDevInstance - HANDLE to device instance. FoundMatch - receives TRUE if a match was found. ServiceName - receives name of service to be assigned to the device instance pointed to by HDevInstance. ClassGuid - receives name of class GUID to be assigned to the device instance pointed to by HDevInstance. Driver - receives name of "class GUID\InstanceID" to be assigned to the device instance pointed to by HDevInstance. LowerFilters - receives name of lower filters to be assigned to the device instance pointed to by HDevInstance. UpperFilters - receives name of upper filters to be assigned to the device instance pointed to by HDevInstance. Return Value: NTSTATUS code --*/ { NTSTATUS status; HANDLE hRegistryMachine, hCriticalDeviceKey, hCriticalEntry; PWSTR keyValueInfoTag[2]; PKEY_VALUE_FULL_INFORMATION keyValueInfo[2]; BUFFER_INFO infoBuffer; ULONG enumIndex, idIndex, resultSize, stringLength; UNICODE_STRING tmpUnicodeString, unicodeCriticalEntry, unicodeCriticalDeviceKeyName; PWCHAR stringStart, bufferEnd, ptr, ids; PRTL_QUERY_REGISTRY_TABLE parameters = NULL; #define INITIAL_INFOBUFFER_SIZE sizeof(KEY_VALUE_FULL_INFORMATION) + 8*sizeof(WCHAR) + 255*sizeof(WCHAR) infoBuffer.Buffer = NULL; keyValueInfo[0] = NULL; keyValueInfo[1] = NULL; ids = NULL; // // Get handle to \REGISTRY\MACHINE registry key. // status = IopOpenRegistryKeyEx( &hRegistryMachine, NULL, &CmRegistryMachineName, KEY_READ ); if (!NT_SUCCESS(status)) { return status; } // // Open CriticalDeviceDatabase registry key to enumerate through. // // This key contains hardware id's for so called "critical" devices. These // are devices for which, for one reason or another, we cannot wait for // config manager to bring them on line. These are primarily devices which // are necessary in order to bring the system up into user mode so that // config manager can be run (disks, keyboards, video, etc...) // PiWstrToUnicodeString(&unicodeCriticalDeviceKeyName, REGSTR_PATH_CRITICALDEVICEDATABASE); status = IopOpenRegistryKeyEx( &hCriticalDeviceKey, hRegistryMachine, &unicodeCriticalDeviceKeyName, KEY_READ ); // // Close handle to \REGISTRY\MACHINE. // ZwClose(hRegistryMachine); // // Check success in opening CriticalDeviceDatabase key. // if (!NT_SUCCESS(status)) { IopDbgPrint(( IOP_ENUMERATION_WARNING_LEVEL, "PipProcessCriticalDeviceRoutine: Unable to open %wZ key, status = %#08lx\n", &unicodeCriticalDeviceKeyName, status)); return status; } // // Allocate a buffer to store KeyValueFullInformation // of values from CriticalDeviceDatabase key. // status = IopAllocateBuffer( &infoBuffer, INITIAL_INFOBUFFER_SIZE ); if (!NT_SUCCESS(status)) { IopDbgPrint(( IOP_ENUMERATION_WARNING_LEVEL, "PipProcessCriticalDeviceRoutine: Unable to allocate buffer to hold key values, status = %\n")); goto cleanup; } // // Retrieve the HardwareId and CompatibleIds device instance registry key // values. // keyValueInfoTag[0] = REGSTR_VALUE_HARDWAREID; keyValueInfoTag[1] = REGSTR_VALUE_COMPATIBLEIDS; for (idIndex=0; idIndex < 2; idIndex++) { IopDbgPrint(( IOP_ENUMERATION_VERBOSE_LEVEL, "PipProcessCriticalDeviceRoutine: Processing %ws entries\n", keyValueInfoTag[idIndex])); // // Read Key Value Information from HardwareId and CompatibleIds // status = IopGetRegistryValue(HDevInstance, keyValueInfoTag[idIndex], &keyValueInfo[idIndex]); if (!NT_SUCCESS(status)) { // // Error retrieving the registry value, skip it and move on. // IopDbgPrint(( IOP_ENUMERATION_WARNING_LEVEL, "PipProcessCriticalDeviceRoutine: Error retrieving %ws value, status = %#08lx\n", keyValueInfoTag[idIndex], status)); status = STATUS_SUCCESS; continue; } else if ((keyValueInfo[idIndex]->Type != REG_MULTI_SZ) || (keyValueInfo[idIndex]->DataLength == 0)) { // // The registry value is not valid, skip it and move on. // ExFreePool(keyValueInfo[idIndex]); keyValueInfo[idIndex] = NULL; IopDbgPrint(( IOP_ENUMERATION_WARNING_LEVEL, "PipProcessCriticalDeviceRoutine: Invalid %ws registry value, skipping.\n", keyValueInfoTag[idIndex])); continue; } else { ASSERT(keyValueInfo[idIndex]); ids = (PWCHAR)KEY_VALUE_DATA(keyValueInfo[idIndex]); // // Make sure all of the IDs match '\' replacement policy. // tmpUnicodeString.Buffer = ids; tmpUnicodeString.Length = (USHORT)keyValueInfo[idIndex]->DataLength; tmpUnicodeString.MaximumLength = tmpUnicodeString.Length; IopReplaceSeperatorWithPound(&tmpUnicodeString, &tmpUnicodeString); // // Find start and end of this REG_MULTI_SZ // ptr = ids; stringStart = ptr; bufferEnd = (PWCHAR)((PUCHAR)ptr + keyValueInfo[idIndex]->DataLength); while(ptr != bufferEnd) { if (!*ptr) { // // Found null-terminated end of a single SZ within the MULTI_SZ. // stringLength = (ULONG)((PUCHAR)ptr - (PUCHAR)stringStart); tmpUnicodeString.Buffer = stringStart; tmpUnicodeString.Length = (USHORT)stringLength; tmpUnicodeString.MaximumLength = (USHORT)stringLength + sizeof(UNICODE_NULL); IopDbgPrint(( IOP_ENUMERATION_VERBOSE_LEVEL, "PipProcessCriticalDeviceRoutine: Searching for %ws entry: %wZ\n", keyValueInfoTag[idIndex], &tmpUnicodeString)); // // Enumerate through Critical Device entries. // // For each CriticalDeviceDatabase value entry compare with all entries of HardwareId // and CompatibleIds REG_MULTI_SZ for a match // enumIndex = 0; while (((status = ZwEnumerateKey( hCriticalDeviceKey, enumIndex, KeyBasicInformation, (PVOID) infoBuffer.Buffer, infoBuffer.MaxSize, &resultSize)) != STATUS_NO_MORE_ENTRIES)) { if (status == STATUS_BUFFER_OVERFLOW) { // // Buffer allocated to hold value was too small; // resize to specified length, and try again. // IopDbgPrint(( IOP_ENUMERATION_VERBOSE_LEVEL, "PipProcessCriticalDeviceRoutine: Resizing buffer...\n")); status = IopResizeBuffer( &infoBuffer, resultSize, FALSE ); if (!NT_SUCCESS(status)) { // // If we can't resize the buffer to the required // size, we can't do much more. // IopDbgPrint(( IOP_ENUMERATION_WARNING_LEVEL, "PipProcessCriticalDeviceRoutine: Error resizing buffer, status = %#08lx\n", status)); goto cleanup; } continue; } else if (!NT_SUCCESS(status)) { // // ZwEnumerateKey returned failure status other than // STATUS_NO_MORE_ENTRIES or STATUS_BUFFER_OVERFLOW. // IopDbgPrint(( IOP_ENUMERATION_WARNING_LEVEL, "PipProcessCriticalDeviceRoutine: Failed to enumerate critical device, status = %#08lx\n", status)); goto cleanup; } // // Store CriticalDeviceDatabase entry in a unicode string to do // case-insensitive comparisons with HardwareId/CompatibleIds entries // from the new device's instance key. // unicodeCriticalEntry.Buffer = ((PKEY_BASIC_INFORMATION)(infoBuffer.Buffer))->Name; unicodeCriticalEntry.Length = (USHORT) ((PKEY_BASIC_INFORMATION)(infoBuffer.Buffer))->NameLength; unicodeCriticalEntry.MaximumLength = unicodeCriticalEntry.Length; IopDbgPrint(( IOP_ENUMERATION_VERBOSE_LEVEL, "PipProcessCriticalDeviceRoutine: \t key (%u) enumerated: %wZ\n", enumIndex, &unicodeCriticalEntry)); // // Check for a case-insenitive unicode string match. // if (RtlEqualUnicodeString(&tmpUnicodeString, &unicodeCriticalEntry, TRUE)) { IopDbgPrint(( IOP_ENUMERATION_INFO_LEVEL, "PipProcessCriticalDeviceRoutine: ***** Critical Device %wZ: Matched to Device %wZ.\n", &tmpUnicodeString, &unicodeCriticalEntry)); // // Query registry values of the critical device match. // #define NUM_QUERIES 5 status = IopOpenRegistryKeyEx( &hCriticalEntry, hCriticalDeviceKey, &unicodeCriticalEntry, KEY_READ ); if (!NT_SUCCESS(status)) { goto cleanup; } parameters = (PRTL_QUERY_REGISTRY_TABLE) ExAllocatePool(NonPagedPool, sizeof(RTL_QUERY_REGISTRY_TABLE)*(NUM_QUERIES+1)); if (!parameters) { ZwClose(hCriticalEntry); status = STATUS_INSUFFICIENT_RESOURCES; goto cleanup; } // // RTL_QUERY_REGISTRY_DIRECT uses system provided QueryRoutine. // Look at the DDK documentation for more details on this flag. // RtlZeroMemory(parameters, sizeof(RTL_QUERY_REGISTRY_TABLE) * (NUM_QUERIES + 1)); parameters[0].Flags = RTL_QUERY_REGISTRY_DIRECT; parameters[0].Name = REGSTR_VALUE_SERVICE; parameters[0].EntryContext = ServiceName; parameters[0].DefaultType = REG_SZ; parameters[0].DefaultData = L""; parameters[0].DefaultLength = 0; parameters[1].Flags = RTL_QUERY_REGISTRY_DIRECT; parameters[1].Name = REGSTR_VALUE_CLASSGUID; parameters[1].EntryContext = ClassGuid; parameters[1].DefaultType = REG_SZ; parameters[1].DefaultData = L""; parameters[1].DefaultLength = 0; parameters[2].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_NOEXPAND; parameters[2].Name = REGSTR_VALUE_LOWERFILTERS; parameters[2].EntryContext = LowerFilters; parameters[2].DefaultType = REG_MULTI_SZ; parameters[2].DefaultData = L""; parameters[2].DefaultLength = 0; parameters[3].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_NOEXPAND; parameters[3].Name = REGSTR_VALUE_UPPERFILTERS; parameters[3].EntryContext = UpperFilters; parameters[3].DefaultType = REG_MULTI_SZ; parameters[3].DefaultData = L""; parameters[3].DefaultLength = 0; parameters[4].Flags = RTL_QUERY_REGISTRY_DIRECT; parameters[4].Name = REGSTR_VALUE_DRIVER; parameters[4].EntryContext = Driver; parameters[4].DefaultType = REG_SZ; parameters[4].DefaultData = L""; parameters[4].DefaultLength = 0; status = RtlQueryRegistryValues( RTL_REGISTRY_HANDLE | RTL_REGISTRY_OPTIONAL, (PWSTR) hCriticalEntry, parameters, NULL, NULL ); ExFreePool(parameters); ZwClose(hCriticalEntry); if (NT_SUCCESS(status)) { // // Sanity check all of the values... // 1) There is a service name // 2) If there is a class guid, it is of the proper length // if (ServiceName->Buffer && ServiceName->Length && ((ClassGuid->Length == 0) || (ClassGuid->Length >= 38*sizeof(WCHAR))) && ((Driver->Length == 0) || (Driver->Length >= 38*sizeof(WCHAR)))) { // // Caller expects XxxFilters->Buffer == NULL, so make // the default case look like that // if (UpperFilters->Length <= 2 && UpperFilters->Buffer) { RtlFreeUnicodeString(UpperFilters); } if (LowerFilters->Length <= 2 && LowerFilters->Buffer) { RtlFreeUnicodeString(LowerFilters); } if (ClassGuid->Length == 0 && ClassGuid->Buffer) { RtlFreeUnicodeString(ClassGuid); } if (Driver->Length == 0 && Driver->Buffer) { RtlFreeUnicodeString(Driver); } IopDbgPrint(( IOP_ENUMERATION_INFO_LEVEL, "PipProcessCriticalDeviceRoutine: ***** Using ServiceName %wZ.\n", ServiceName)); if (ClassGuid->Buffer) { IopDbgPrint(( IOP_ENUMERATION_INFO_LEVEL, "PipProcessCriticalDeviceRoutine: ***** Using ClassGuid %wZ.\n", ClassGuid)); } if (Driver->Buffer) { IopDbgPrint(( IOP_ENUMERATION_INFO_LEVEL, "PipProcessCriticalDeviceRoutine: ***** Using Driver %wZ.\n", Driver)); } // // We have a ServiceName match from the // CriticalDeviceDatabase, so we're done. // *FoundMatch = TRUE; goto cleanup; } } else { // // Just continue searching the database. // status = STATUS_SUCCESS; } // // Free any strings that may have been allocated by // RtlQueryRegistryValues // RtlFreeUnicodeString(ServiceName); RtlFreeUnicodeString(ClassGuid); RtlFreeUnicodeString(Driver); RtlFreeUnicodeString(LowerFilters); RtlFreeUnicodeString(UpperFilters); IopDbgPrint(( IOP_ENUMERATION_VERBOSE_LEVEL, "PipProcessCriticalDeviceRoutine: Found no ServiceName for %wZ\n", &unicodeCriticalEntry)); } // // enumerate next key value. // enumIndex++; } // // See if we're at the end of the MULTI_SZ // if (((ptr + 1) == bufferEnd) || !*(ptr + 1)) { break; } else { stringStart = ptr + 1; } } // // advance to next character. // ptr++; } ids = NULL; } } // // No match with a ServiceName was found. // IopDbgPrint(( IOP_ENUMERATION_INFO_LEVEL, "PipProcessCriticalDeviceRoutine: No match found for this device.\n")); *FoundMatch = FALSE; status = STATUS_SUCCESS; cleanup: if (infoBuffer.Buffer) { IopFreeBuffer(&infoBuffer); } if (hCriticalDeviceKey) { ZwClose(hCriticalDeviceKey); } if (keyValueInfo[0]) { ExFreePool(keyValueInfo[0]); } if (keyValueInfo[1]) { ExFreePool(keyValueInfo[1]); } return status; } BOOLEAN PipGetRegistryDwordWithFallback( IN PUNICODE_STRING valueName, IN HANDLE PrimaryKey, IN HANDLE SecondaryKey, IN OUT PULONG Value ) /*++ Routine Description: If (1) Primary key has a value named "ValueName" that is REG_DWORD, return it Else If (2) Secondary key has a value named "ValueName" that is REG_DWORD, return it Else (3) Leave Value untouched and return error Arguments: ValueName - Unicode name of value to query PrimaryKey - If non-null, check this first SecondaryKey - If non-null, check this second Value - IN = default value, OUT = actual value Return Value: TRUE if value found --*/ { PKEY_VALUE_FULL_INFORMATION info; PUCHAR data; NTSTATUS status; HANDLE Keys[3]; int count = 0; int index; BOOLEAN set = FALSE; if (PrimaryKey != NULL) { Keys[count++] = PrimaryKey; } if (SecondaryKey != NULL) { Keys[count++] = SecondaryKey; } Keys[count] = NULL; for (index = 0; index < count && !set; index ++) { info = NULL; try { status = IopGetRegistryValue(Keys[index], valueName->Buffer, &info); if (NT_SUCCESS(status) && info->Type == REG_DWORD) { data = ((PUCHAR) info) + info->DataOffset; *Value = *((PULONG) data); set = TRUE; } } except(EXCEPTION_EXECUTE_HANDLER) { // // do nothing // } if (info) { ExFreePool(info); } } return set; } PSECURITY_DESCRIPTOR PipGetRegistrySecurityWithFallback( IN PUNICODE_STRING valueName, IN HANDLE PrimaryKey, IN HANDLE SecondaryKey ) /*++ Routine Description: If (1) Primary key has a binary value named "ValueName" that is REG_BINARY and appears to be a valid security descriptor, return it Else (2) do same for Secondary key Else (3) Return NULL Arguments: ValueName - Unicode name of value to query PrimaryKey - If non-null, check this first SecondaryKey - If non-null, check this second Return Value: Security Descriptor if found, else NULL --*/ { PKEY_VALUE_FULL_INFORMATION info; PUCHAR data; NTSTATUS status; HANDLE Keys[3]; int count = 0; int index; BOOLEAN set = FALSE; PSECURITY_DESCRIPTOR secDesc = NULL; PSECURITY_DESCRIPTOR allocDesc = NULL; if (PrimaryKey != NULL) { Keys[count++] = PrimaryKey; } if (SecondaryKey != NULL) { Keys[count++] = SecondaryKey; } Keys[count] = NULL; for (index = 0; index < count && !set; index ++) { info = NULL; try { status = IopGetRegistryValue(Keys[index], valueName->Buffer, &info); if (NT_SUCCESS(status) && info->Type == REG_BINARY) { data = ((PUCHAR) info) + info->DataOffset; secDesc = (PSECURITY_DESCRIPTOR)data; /*if (SeValidSecurityDescriptor( SECURITY_DESCRIPTOR_MIN_LENGTH, secDesc)) {*/ status = SeCaptureSecurityDescriptor(secDesc, KernelMode, PagedPool, TRUE, &allocDesc); if (NT_SUCCESS(status)) { set = TRUE; } /*} else { // // Perhaps this happened due to a corrupted registry entry? // IopDbgPrint(( IOP_ENUMERATION_ERROR_LEVEL, "PipChangeDeviceObjectFromRegistryProperties: Security descriptor not valid!\n")); ASSERT(0); }*/ } } except(EXCEPTION_EXECUTE_HANDLER) { // // do nothing // } if (info) { ExFreePool(info); } } if (set) { return allocDesc; } return NULL; } NTSTATUS PipChangeDeviceObjectFromRegistryProperties( IN PDEVICE_OBJECT PhysicalDeviceObject, IN HANDLE DeviceClassPropKey, IN HANDLE DevicePropKey, IN BOOLEAN UsePdoCharacteristics ) /*++ Routine Description: This routine will obtain settings from either (1) DevNode settings (via DevicePropKey) or (2) Class settings (via DeviceClassPropKey) applying to PDO and all attached device objects Properties set/ changed are: * DeviceType - the I/O system type for the device object * DeviceCharacteristics - the I/O system characteristic flags to be set for the device object * Exclusive - the device can only be accessed exclusively * Security - security for the device The routine will then use the DeviceType and DeviceCharacteristics specified to determine whether a VPB should be allocated as well as to set default security if none is specified in the registry. Arguments: PhysicalDeviceObject - the PDO we are to configure DeviceClassPropKey - a handle to Control\\Properties protected key DevicePropKey - a handle to Enum\ protected key Return Value: status --*/ { UNICODE_STRING valueName; NTSTATUS status; BOOLEAN deviceTypeSpec = FALSE; BOOLEAN characteristicsSpec = FALSE; BOOLEAN exclusiveSpec = FALSE; BOOLEAN securityForce = FALSE; UCHAR buffer[SECURITY_DESCRIPTOR_MIN_LENGTH]; SECURITY_INFORMATION securityInformation = 0; PSECURITY_DESCRIPTOR securityDescriptor = NULL; PACL allocatedAcl = NULL; ULONG deviceType = 0; ULONG characteristics = 0; ULONG exclusive = 0; ULONG prevCharacteristics = 0; PDEVICE_OBJECT StackIterator = NULL; PDEVICE_NODE deviceNode = NULL; ASSERT(PhysicalDeviceObject); deviceNode = PhysicalDeviceObject->DeviceObjectExtension->DeviceNode; ASSERT(deviceNode); IopDbgPrint(( IOP_ENUMERATION_VERBOSE_LEVEL, "PipChangeDeviceObjectFromRegistryProperties: Modifying device stack for PDO: %08x\n",PhysicalDeviceObject)); // // Iterate through all device objects to get our starting settings (OR everyone together) // generally, a PDO should take on the characteristics of whoever is above the PDO, and not used in the equation // the exception being if it's being used RAW // we detect this by absense of service name, or it's the only Device Object. // StackIterator = PhysicalDeviceObject; if (UsePdoCharacteristics || StackIterator->AttachedDevice == NULL) { IopDbgPrint(( IOP_ENUMERATION_WARNING_LEVEL, "PipChangeDeviceObjectFromRegistryProperties: Assuming PDO is being used RAW\n")); } else { StackIterator = StackIterator->AttachedDevice; IopDbgPrint(( IOP_ENUMERATION_VERBOSE_LEVEL, "PipChangeDeviceObjectFromRegistryProperties: Ignoring PDO's settings\n")); } // // we can't propagate DO_EXCLUSIVE, since it happens to break some devices (eg, Serial) // but we do propagage certain characteristics flags // for ( ; StackIterator != NULL; StackIterator = StackIterator->AttachedDevice) { prevCharacteristics |= StackIterator->Characteristics; } // // 1) Get Device type, DevicePropKey preferred over DeviceClassPropKey // PiWstrToUnicodeString(&valueName, REGSTR_VAL_DEVICE_TYPE); deviceTypeSpec = PipGetRegistryDwordWithFallback(&valueName,DevicePropKey,DeviceClassPropKey,&deviceType); PiWstrToUnicodeString(&valueName, REGSTR_VAL_DEVICE_CHARACTERISTICS); characteristicsSpec = PipGetRegistryDwordWithFallback(&valueName,DevicePropKey,DeviceClassPropKey,&characteristics); PiWstrToUnicodeString(&valueName, REGSTR_VAL_DEVICE_EXCLUSIVE); exclusiveSpec = PipGetRegistryDwordWithFallback(&valueName,DevicePropKey,DeviceClassPropKey,&exclusive); if (!characteristicsSpec) { characteristics = 0; } characteristics = (characteristics | prevCharacteristics) & FILE_CHARACTERISTICS_PROPAGATED; // mask only applicable characteristics PiWstrToUnicodeString(&valueName, REGSTR_VAL_DEVICE_SECURITY_DESCRIPTOR); securityDescriptor = PipGetRegistrySecurityWithFallback(&valueName,DevicePropKey,DeviceClassPropKey); if (securityDescriptor == NULL) { // // determine if we should create internal default // if (deviceTypeSpec) { BOOLEAN hasName = (PhysicalDeviceObject->Flags & DO_DEVICE_HAS_NAME) ? TRUE : FALSE; securityDescriptor = IopCreateDefaultDeviceSecurityDescriptor( (DEVICE_TYPE)deviceType, characteristics, hasName, &buffer[0], &allocatedAcl, &securityInformation ); if (securityDescriptor) { securityForce = TRUE; // forced default security descriptor } else { IopDbgPrint(( IOP_ENUMERATION_WARNING_LEVEL, "PipChangeDeviceObjectFromRegistryProperties: Was not able to get default security descriptor\n")); } } } else { // // further process the security information we're given to set "securityInformation" // PSID sid; PACL acl; BOOLEAN present, tmp; securityInformation = 0; // // See what information is in the captured descriptor so we can build // up a securityInformation block to go with it. // status = RtlGetOwnerSecurityDescriptor(securityDescriptor, &sid, &tmp); if (NT_SUCCESS(status) && (sid != NULL)) { securityInformation |= OWNER_SECURITY_INFORMATION; } status = RtlGetGroupSecurityDescriptor(securityDescriptor, &sid, &tmp); if (NT_SUCCESS(status) && (sid != NULL)) { securityInformation |= GROUP_SECURITY_INFORMATION; } status = RtlGetSaclSecurityDescriptor(securityDescriptor, &present, &acl, &tmp); if (NT_SUCCESS(status) && (present)) { securityInformation |= SACL_SECURITY_INFORMATION; } status = RtlGetDaclSecurityDescriptor(securityDescriptor, &present, &acl, &tmp); if (NT_SUCCESS(status) && (present)) { securityInformation |= DACL_SECURITY_INFORMATION; } } #if DBG if (deviceTypeSpec == FALSE && characteristicsSpec == FALSE && exclusiveSpec == FALSE && securityDescriptor == NULL) { IopDbgPrint(( IOP_ENUMERATION_INFO_LEVEL, "PipChangeDeviceObjectFromRegistryProperties: No property changes\n")); } else { if (deviceTypeSpec) { IopDbgPrint(( IOP_ENUMERATION_INFO_LEVEL, "PipChangeDeviceObjectFromRegistryProperties: Overide DeviceType=%08x\n", deviceType)); } if (characteristicsSpec) { IopDbgPrint(( IOP_ENUMERATION_INFO_LEVEL, "PipChangeDeviceObjectFromRegistryProperties: Overide DeviceCharacteristics=%08x\n", characteristics)); } if (exclusiveSpec) { IopDbgPrint(( IOP_ENUMERATION_INFO_LEVEL, "PipChangeDeviceObjectFromRegistryProperties: Overide Exclusive=%d\n",(exclusive?1:0))); } if (securityForce) { IopDbgPrint(( IOP_ENUMERATION_INFO_LEVEL, "PipChangeDeviceObjectFromRegistryProperties: Overide Security based on DeviceType & DeviceCharacteristics\n")); } if (securityDescriptor == NULL) { IopDbgPrint(( IOP_ENUMERATION_INFO_LEVEL, "PipChangeDeviceObjectFromRegistryProperties: Overide Security\n")); } } #endif // // modify apropriate characteristics of PDO to be the same as those of rest of stack // eg, PDO may be initialized as Raw-Capable Secure Open, but then be modified to be more lax // PhysicalDeviceObject->Characteristics = (PhysicalDeviceObject->Characteristics & ~FILE_CHARACTERISTICS_PROPAGATED) | characteristics; ASSERT((PhysicalDeviceObject->Characteristics & FILE_CHARACTERISTICS_PROPAGATED) == characteristics); // sanity (checks bit bounds) // // exclusivity flag applies only to PDO // if someone is relying on this flag, they better not name the FDO or any filter DO's // otherwise the device can be opened via two handles. // if (exclusiveSpec && exclusive) { PhysicalDeviceObject->Flags |= DO_EXCLUSIVE; } // // iterate through rest of objects // these flags were used to create characteristics & deviceType, so // we will only end up setting flags, not clearing them // for (StackIterator = PhysicalDeviceObject->AttachedDevice; StackIterator != NULL; StackIterator = StackIterator->AttachedDevice) { // // modify characteristics (set only) // StackIterator->Characteristics |= characteristics; ASSERT((StackIterator->Characteristics & FILE_CHARACTERISTICS_PROPAGATED) == characteristics); // sanity (checks we only needed to set) } if (deviceTypeSpec) { // // modify device type - PDO only // PhysicalDeviceObject->DeviceType = deviceType; } if (securityDescriptor != NULL) { // // modify security (applied to whole stack) // status = ObSetSecurityObjectByPointer(PhysicalDeviceObject, securityInformation, securityDescriptor); if (NT_SUCCESS(status) == FALSE) { IopDbgPrint(( IOP_ENUMERATION_ERROR_LEVEL, "PipChangeDeviceObjectFromRegistryProperties: Set security failed (%08x)\n",status)); } } // // cleanup // if ((securityDescriptor != NULL) && !securityForce) { ExFreePool(securityDescriptor); } if (allocatedAcl) { ExFreePool(allocatedAcl); } return STATUS_SUCCESS; } NTSTATUS PipProcessDevNodeTree( IN PDEVICE_NODE SubtreeRootDeviceNode, IN BOOLEAN LoadDriver, IN BOOLEAN ReallocateResources, IN ENUM_TYPE EnumType, IN BOOLEAN Synchronous, IN BOOLEAN ProcessOnlyIntermediateStates, IN PADD_CONTEXT AddContext, IN PPI_DEVICE_REQUEST Request ) /*-- Routine Description: This function is called to handle state transitions related to starting Devnodes. The basic sequence of operations is inheritted from the previous implementation. Resources freed 1) Allocate resources to all candidates in the tree. 2) Traverse the tree searching for a Devnodes ready to be started. 3) Start the Devnode. 4) Enumerate its children. 5) Initialize all the children up to the point of resource allocation. 6) Continue searching for DevNodes to start, if one is found return to step 3. 7) Once the entire tree is processed start over at step 1 until either no children are enumerated or no resources are allocated. A Devnode's resource requirements change If the Devnode wasn't started then treat it the same as the Resources freed case. If it was started then it would have been handled directly by our caller. Start Devnodes during boot 1) Allocate resources to all candidates in the tree (based on IopBootConfigsReserved). 2) Traverse the tree searching for Devnodes ready to be started. 3) Start the Devnode. 4) Enumerate its children. 5) Initialize all the children up to the point of resource allocation. 6) Continue searching for DevNodes to start, if one is found return to step 3. Devnode newly created by user-mode. 1) Reset Devnode to uninitialized state. 2) Process Devnode to DeviceNodeDriversAdded state. 3) Allocate resources to this Devnode. 4) Start the Devnode. 5) Enumerate its children. 6) Initialize any children up to the point of resource allocation. 7) Allocate resources to all candidates in the tree below the initial Devnode. 8) Traverse the tree starting at the initial Devnode searching for a Devnode ready to be started. 9) Start the Devnode. 10) Enumerate its children. 11) Initialize all the children up to the point of resource allocation. 12) Start over at step 7 until either no children are enumerated or no resources are allocated. Device node newly created by IoReportDetectedDevice. 1) Do post start IRP processing 2) Continue from step 5 of the process for Devnodes newly created by user-mode. Reenumeration of a single Devnode (and processing of changes resulting from that enumeration) 1) Enumerate Devnode's children 2) Initialize any children up to the point of resource allocation. 3) Allocate resources to all candidates in the tree below the initial Devnode. 4) Traverse the tree starting at the initial Devnode searching for a Devnode ready to be started. 5) Start the Devnode. 6) Enumerate its children. 7) Initialize all the children up to the point of resource allocation. 8) Start over at step 3 until either no children are enumerated or no resources are allocated. Reenumeration of a subtree. Parameters: SubtreeRootDeviceNode - Root of this tree walk. Depending on the ProcessOnlyIntermediaryStates parameter, the PDO for this devnode may need to be referenced. LoadDriver - Indicates whether drivers should be loaded on this pass (typically TRUE unless boot drivers aren't yet ready) ReallocateResources - TRUE iff resource reallocation should be attempted. EnumType - Specifies type of enumeration. Synchronous - TRUE iff the operation should be performed synchronously (always TRUE currently) ProcessOnlyIntermediateStates - TRUE if only intermediary states should be processed. If FALSE, the caller places a reference on the PDO that this routine will drop. AddContext - Constraints for AddDevice Request - Device action worker that triggered this processing. Return Value: NTSTATUS - Note: Always successful if ProcessOnlyIntermediaryStates is TRUE. ++*/ { PDEVICE_NODE currentNode; PDEVICE_NODE startRoot; PDEVICE_NODE enumeratedBus; PDEVICE_NODE originalSubtree; BOOLEAN processComplete; BOOLEAN newDevice; BOOLEAN rebalancePerformed; NTSTATUS status; ULONG reenumAttempts; enum { SameNode, SiblingNode, ChildNode } nextNode; PAGED_CODE(); originalSubtree = SubtreeRootDeviceNode; // // Collapse enum requests if appropriate. // if (Request && !Request->ReorderingBarrier && EnumType != EnumTypeShallow && !ProcessOnlyIntermediateStates) { if (PiCollapseEnumRequests(&Request->ListEntry)) { SubtreeRootDeviceNode = IopRootDeviceNode; } } reenumAttempts = 0; startRoot = NULL; enumeratedBus = NULL; processComplete = FALSE; newDevice = TRUE; while (newDevice) { newDevice = FALSE; if (!ProcessOnlyIntermediateStates) { // // Process the whole device tree to assign resources to those devices // who have been successfully added to their drivers. // rebalancePerformed = FALSE; newDevice = IopProcessAssignResources( SubtreeRootDeviceNode, ReallocateResources, &rebalancePerformed); if (rebalancePerformed == TRUE) { // // Before we do any other processing, we need to restart // all rebalance participants. // status = PipProcessDevNodeTree( IopRootDeviceNode, LoadDriver, FALSE, EnumType, Synchronous, TRUE, AddContext, Request); ASSERT(NT_SUCCESS(status)); } } if (processComplete && !newDevice) { break; } // // Process the entire subtree. // currentNode = SubtreeRootDeviceNode; processComplete = FALSE; while (!processComplete) { // // Dont process devnodes with problem. // status = STATUS_SUCCESS; nextNode = SiblingNode; if (!PipDoesDevNodeHaveProblem(currentNode)) { switch (currentNode->State) { case DeviceNodeUninitialized: if (!ProcessOnlyIntermediateStates) { if (currentNode->Parent == enumeratedBus && startRoot == NULL) { startRoot = currentNode; } if((!ReallocateResources && EnumType == EnumTypeNone) || startRoot) { status = PipProcessNewDeviceNode(currentNode); if (NT_SUCCESS(status)) { nextNode = SameNode; } } } break; case DeviceNodeInitialized: if (!ProcessOnlyIntermediateStates) { if (!ReallocateResources || startRoot) { status = PipCallDriverAddDevice( currentNode, LoadDriver, AddContext); if (NT_SUCCESS(status)) { nextNode = SameNode; newDevice = TRUE; } else { // // ISSUE - 2000/08/31 - ADRIAO: Not draining // We should really drain the removes here. // We don't because we cannot distinguish // AddDevice's that fail due to non-present // boot drivers from AddDevice's that fail due // to a problem requiring remove. // //status = STATUS_PNP_RESTART_ENUMERATION; } } } break; case DeviceNodeResourcesAssigned: if (!ProcessOnlyIntermediateStates) { if (ReallocateResources && startRoot == NULL) { // // If we assigned resources to this previously // conflicting devnode, remember him so that we will // initial processing on devices in that subtree. // startRoot = currentNode; } status = PipProcessStartPhase1(currentNode, Synchronous); if (NT_SUCCESS(status)) { nextNode = SameNode; } else { // // Cleanup is currently handled in the // DeviceNodeStartCompletion phase, thus // PipProcessStartPhase1 should always succeed. // ASSERT(0); nextNode = SiblingNode; } } else { nextNode = SiblingNode; } break; case DeviceNodeStartCompletion: status = PipProcessStartPhase2(currentNode); if (NT_SUCCESS(status)) { nextNode = SameNode; } else { status = STATUS_PNP_RESTART_ENUMERATION; ASSERT(currentNode->State != DeviceNodeStartCompletion); } break; case DeviceNodeStartPostWork: status = PipProcessStartPhase3(currentNode); if (NT_SUCCESS(status)) { nextNode = SameNode; } else { status = STATUS_PNP_RESTART_ENUMERATION; ASSERT(!ProcessOnlyIntermediateStates); } break; case DeviceNodeStarted: nextNode = ChildNode; if (!ProcessOnlyIntermediateStates) { if ((currentNode->Flags & DNF_REENUMERATE)) { status = PipEnumerateDevice(currentNode, Synchronous); if (NT_SUCCESS(status)) { // // Remember the bus we just enumerated. // enumeratedBus = currentNode; nextNode = SameNode; } else if (status == STATUS_PENDING) { nextNode = SiblingNode; } } } break; case DeviceNodeEnumerateCompletion: status = PipEnumerateCompleted(currentNode); nextNode = ChildNode; break; case DeviceNodeStopped: status = PipProcessRestartPhase1(currentNode, Synchronous); if (NT_SUCCESS(status)) { nextNode = SameNode; } else { // // Cleanup is currently handled in the // DeviceNodeStartCompletion phase, thus // PipProcessRestartPhase1 should always succeed. // ASSERT(0); nextNode = SiblingNode; } break; case DeviceNodeRestartCompletion: status = PipProcessRestartPhase2(currentNode); if (NT_SUCCESS(status)) { nextNode = SameNode; } else { status = STATUS_PNP_RESTART_ENUMERATION; ASSERT(currentNode->State != DeviceNodeRestartCompletion); } break; case DeviceNodeDriversAdded: case DeviceNodeAwaitingQueuedDeletion: case DeviceNodeAwaitingQueuedRemoval: case DeviceNodeRemovePendingCloses: case DeviceNodeRemoved: nextNode = SiblingNode; break; case DeviceNodeStartPending: case DeviceNodeEnumeratePending: case DeviceNodeQueryStopped: case DeviceNodeQueryRemoved: case DeviceNodeDeletePendingCloses: case DeviceNodeDeleted: case DeviceNodeUnspecified: default: ASSERT(0); nextNode = SiblingNode; break; } } // // If we need to wait for the queued removals to complete before // we progress,we need to do the following: // 1. capture the instance paths for all the parents of the current // node upto the subtree root where we started // 2. drop the reference to the subtree root allowing it to be // deleted (if required) // 3. drop the tree lock // 4. wait for the removal queue to empty // 5. re-acquire the tree lock // 6. resume processing // if (status == STATUS_PNP_RESTART_ENUMERATION && !ProcessOnlyIntermediateStates) { PDEVICE_OBJECT entryDeviceObject; UNICODE_STRING unicodeName; PWCHAR devnodeList; PWCHAR currentEntry; PWCHAR rootEntry; WCHAR buffer[MAX_INSTANCE_PATH_LENGTH]; status = PipProcessDevNodeTree( IopRootDeviceNode, LoadDriver, ReallocateResources, EnumType, Synchronous, TRUE, AddContext, Request); ASSERT(NT_SUCCESS(status)); PipAssertDevnodesInConsistentState(); if (++reenumAttempts < MAX_REENUMERATION_ATTEMPTS) { devnodeList = ExAllocatePool( PagedPool, (currentNode->Level + 1) * MAX_INSTANCE_PATH_LENGTH * sizeof(WCHAR)); if (devnodeList) { currentEntry = devnodeList; for ( ; ; ) { rootEntry = currentEntry; ASSERT(currentNode->InstancePath.Length < MAX_INSTANCE_PATH_LENGTH); memcpy( currentEntry, currentNode->InstancePath.Buffer, currentNode->InstancePath.Length ); currentEntry += currentNode->InstancePath.Length / sizeof(WCHAR); *currentEntry++ = UNICODE_NULL; if (currentNode == SubtreeRootDeviceNode) { break; } currentNode = currentNode->Parent; } } else { ASSERT(SubtreeRootDeviceNode->InstancePath.Length < MAX_INSTANCE_PATH_LENGTH); memcpy( buffer, SubtreeRootDeviceNode->InstancePath.Buffer, SubtreeRootDeviceNode->InstancePath.Length ); rootEntry = buffer; } } else { rootEntry = NULL; devnodeList = NULL; } ObDereferenceObject(originalSubtree->PhysicalDeviceObject); PpDevNodeUnlockTree(PPL_TREEOP_ALLOW_READS); PpSynchronizeDeviceEventQueue(); PpDevNodeLockTree(PPL_TREEOP_ALLOW_READS); if (reenumAttempts >= MAX_REENUMERATION_ATTEMPTS) { IopDbgPrint((IOP_ENUMERATION_ERROR_LEVEL, "Restarted reenumeration %d times, giving up!\n", reenumAttempts)); ASSERT(reenumAttempts < MAX_REENUMERATION_ATTEMPTS); return STATUS_UNSUCCESSFUL; } RtlInitUnicodeString(&unicodeName, rootEntry); entryDeviceObject = IopDeviceObjectFromDeviceInstance(&unicodeName); if (entryDeviceObject == NULL) { if (devnodeList) { ExFreePool(devnodeList); } return STATUS_UNSUCCESSFUL; } SubtreeRootDeviceNode = entryDeviceObject->DeviceObjectExtension->DeviceNode; originalSubtree = currentNode = SubtreeRootDeviceNode; // // Try to start processing where we left off. // if (devnodeList) { for(currentEntry = devnodeList; currentEntry != rootEntry; currentEntry += ((unicodeName.Length / sizeof(WCHAR))+1)) { RtlInitUnicodeString(&unicodeName, currentEntry); entryDeviceObject = IopDeviceObjectFromDeviceInstance(&unicodeName); if (entryDeviceObject != NULL) { currentNode = entryDeviceObject->DeviceObjectExtension->DeviceNode; ObDereferenceObject(entryDeviceObject); break; } } ExFreePool(devnodeList); } nextNode = SameNode; } // // This code advances the current node based on nextNode. // switch (nextNode) { case SameNode: break; case ChildNode: if (currentNode->Child != NULL) { currentNode = currentNode->Child; break; } // FALLTHRU - No more children so advance to sibling case SiblingNode: while (currentNode != SubtreeRootDeviceNode) { if (currentNode == startRoot) { // // We completed processing of the new subtree. // if (EnumType != EnumTypeNone) { enumeratedBus = startRoot->Parent; } startRoot = NULL; } else if (currentNode == enumeratedBus) { enumeratedBus = enumeratedBus->Parent; } if (currentNode->Sibling != NULL) { currentNode = currentNode->Sibling; break; } if (currentNode->Parent != NULL) { currentNode = currentNode->Parent; } } if (currentNode == SubtreeRootDeviceNode) { processComplete = TRUE; } break; } } } if (!ProcessOnlyIntermediateStates) { PipAssertDevnodesInConsistentState(); ObDereferenceObject(originalSubtree->PhysicalDeviceObject); } return STATUS_SUCCESS; } NTSTATUS PipProcessStartPhase1( IN PDEVICE_NODE DeviceNode, IN BOOLEAN Synchronous ) { PDEVICE_OBJECT deviceObject; NTSTATUS status = STATUS_SUCCESS; PNP_VETO_TYPE vetoType; PAGED_CODE(); ASSERT(DeviceNode->State == DeviceNodeResourcesAssigned); deviceObject = DeviceNode->PhysicalDeviceObject; IopUncacheInterfaceInformation(deviceObject); if (DeviceNode->DockInfo.DockStatus != DOCK_NOTDOCKDEVICE) { // // This is a dock so we a little bit of work before starting it. // Take the profile change semaphore. We do this whenever a dock // is in our list, even if no query is going to occur. // PpProfileBeginHardwareProfileTransition(FALSE); // // Tell the profile code what dock device object may be bringing the // new hardware profile online. // PpProfileIncludeInHardwareProfileTransition(DeviceNode, DOCK_ARRIVING); // // Ask everyone if this is really a good idea right now. // status = PpProfileQueryHardwareProfileChange( FALSE, PROFILE_PERHAPS_IN_PNPEVENT, &vetoType, NULL ); } if (NT_SUCCESS(status)) { status = IopStartDevice(deviceObject); } // // Failure cleanup is handled in PipProcessStartPhase2, thus we write away // the failure code and always succeed. // PipSetDevNodeState(DeviceNode, DeviceNodeStartCompletion, NULL); DeviceNode->CompletionStatus = status; return STATUS_SUCCESS; } NTSTATUS PipProcessStartPhase2( IN PDEVICE_NODE DeviceNode ) { ULONG problem = CM_PROB_FAILED_START; NTSTATUS status; PAGED_CODE(); status = DeviceNode->CompletionStatus; if (DeviceNode->DockInfo.DockStatus != DOCK_NOTDOCKDEVICE) { if (NT_SUCCESS(status)) { // // Commit the current Hardware Profile as necessary. // PpProfileCommitTransitioningDock(DeviceNode, DOCK_ARRIVING); } else { PpProfileCancelHardwareProfileTransition(); } } if (!NT_SUCCESS(status)) { SAVE_FAILURE_INFO(DeviceNode, DeviceNode->CompletionStatus); // // Handle certain problems determined by the status code // switch(status) { case STATUS_PNP_REBOOT_REQUIRED: problem = CM_PROB_NEED_RESTART; break; default: problem = CM_PROB_FAILED_START; break; } PipRequestDeviceRemoval(DeviceNode, FALSE, problem); if (DeviceNode->DockInfo.DockStatus != DOCK_NOTDOCKDEVICE) { ASSERT(DeviceNode->DockInfo.DockStatus == DOCK_QUIESCENT); IoRequestDeviceEject(DeviceNode->PhysicalDeviceObject); } } else { IopDoDeferredSetInterfaceState(DeviceNode); // // Reserve legacy resources for the legacy interface and bus number. // if (!IopBootConfigsReserved && DeviceNode->InterfaceType != InterfaceTypeUndefined) { // // ISA = EISA. // if (DeviceNode->InterfaceType == Isa) { IopAllocateLegacyBootResources(Eisa, DeviceNode->BusNumber); } IopAllocateLegacyBootResources(DeviceNode->InterfaceType, DeviceNode->BusNumber); } // // This code path currently doesn't expect any of the above functions // to fail. If they do, a removal should be queued and failure should // be returned. // ASSERT(DeviceNode->State == DeviceNodeStartCompletion); PipSetDevNodeState(DeviceNode, DeviceNodeStartPostWork, NULL); } return status; } NTSTATUS PipProcessStartPhase3( IN PDEVICE_NODE DeviceNode ) { NTSTATUS status; PDEVICE_OBJECT deviceObject; HANDLE handle; PWCHAR ids; UNICODE_STRING unicodeName; PAGED_CODE(); deviceObject = DeviceNode->PhysicalDeviceObject; if (!(DeviceNode->Flags & DNF_IDS_QUERIED)) { PWCHAR compatibleIds, hwIds; ULONG hwIdLength, compatibleIdLength; // // If the DNF_NEED_QUERY_IDS is set, the device is a reported device. // It should already be started. We need to enumerate its children and ask // the HardwareId and the Compatible ids of the detected device. // status = IopDeviceObjectToDeviceInstance (deviceObject, &handle, KEY_READ ); if (NT_SUCCESS(status)) { PpQueryHardwareIDs( DeviceNode, &hwIds, &hwIdLength); PpQueryCompatibleIDs( DeviceNode, &compatibleIds, &compatibleIdLength); if (hwIds || compatibleIds) { UCHAR buffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(ULONG)]; PKEY_VALUE_PARTIAL_INFORMATION keyInfo = (PKEY_VALUE_PARTIAL_INFORMATION)buffer; PKEY_VALUE_FULL_INFORMATION keyValueInformation; ULONG flags, length; PWCHAR oldID, newID; PiLockPnpRegistry(FALSE); // // Read the current config flags. // PiWstrToUnicodeString (&unicodeName, REGSTR_VALUE_CONFIG_FLAGS); status = ZwQueryValueKey(handle, &unicodeName, KeyValuePartialInformation, keyInfo, sizeof(buffer), &length ); if (NT_SUCCESS(status) && (keyInfo->Type == REG_DWORD)) { flags = *(PULONG)keyInfo->Data; } else { flags = 0; } if (hwIds) { if (!(flags & CONFIGFLAG_FINISH_INSTALL)) { status = IopGetRegistryValue (handle, REGSTR_VALUE_HARDWAREID, &keyValueInformation); if (NT_SUCCESS(status)) { if (keyValueInformation->Type == REG_MULTI_SZ) { ids = (PWCHAR)KEY_VALUE_DATA(keyValueInformation); // // Check if the old and new IDs are identical. // for (oldID = ids, newID = hwIds; *oldID && *newID; oldID += wcslen(oldID) + 1, newID += wcslen(newID) + 1) { if (_wcsicmp(oldID, newID)) { break; } } if (*oldID || *newID) { IopDbgPrint(( IOP_ENUMERATION_WARNING_LEVEL, "IopStartAndEnumerateDevice: Hardware ID has changed for %wZ\n", &DeviceNode->InstancePath)); flags |= CONFIGFLAG_FINISH_INSTALL; } } ExFreePool(keyValueInformation); } } PiWstrToUnicodeString(&unicodeName, REGSTR_VALUE_HARDWAREID); ZwSetValueKey(handle, &unicodeName, TITLE_INDEX_VALUE, REG_MULTI_SZ, hwIds, hwIdLength); ExFreePool(hwIds); } // // create CompatibleId value name. It is a MULTI_SZ, // if (compatibleIds) { if (!(flags & CONFIGFLAG_FINISH_INSTALL)) { status = IopGetRegistryValue (handle, REGSTR_VALUE_COMPATIBLEIDS, &keyValueInformation); if (NT_SUCCESS(status)) { if (keyValueInformation->Type == REG_MULTI_SZ) { ids = (PWCHAR)KEY_VALUE_DATA(keyValueInformation); // // Check if the old and new IDs are identical. // for (oldID = ids, newID = compatibleIds; *oldID && *newID; oldID += wcslen(oldID) + 1, newID += wcslen(newID) + 1) { if (_wcsicmp(oldID, newID)) { break; } } if (*oldID || *newID) { IopDbgPrint(( IOP_ENUMERATION_WARNING_LEVEL, "IopStartAndEnumerateDevice: Compatible ID has changed for %wZ\n", &DeviceNode->InstancePath)); flags |= CONFIGFLAG_FINISH_INSTALL; } } ExFreePool(keyValueInformation); } } PiWstrToUnicodeString(&unicodeName, REGSTR_VALUE_COMPATIBLEIDS); ZwSetValueKey(handle, &unicodeName, TITLE_INDEX_VALUE, REG_MULTI_SZ, compatibleIds, compatibleIdLength); ExFreePool(compatibleIds); } // // If we set the finish install flag, then write out the flags. // if (flags & CONFIGFLAG_FINISH_INSTALL) { PiWstrToUnicodeString (&unicodeName, REGSTR_VALUE_CONFIG_FLAGS); ZwSetValueKey(handle, &unicodeName, TITLE_INDEX_VALUE, REG_DWORD, &flags, sizeof(flags) ); } PiUnlockPnpRegistry(); } ZwClose(handle); DeviceNode->Flags |= DNF_IDS_QUERIED; } } if (PipIsDevNodeProblem(DeviceNode, CM_PROB_INVALID_DATA)) { return STATUS_UNSUCCESSFUL; } DeviceNode->Flags |= DNF_REENUMERATE; IopQueryAndSaveDeviceNodeCapabilities(DeviceNode); status = PiProcessQueryDeviceState(deviceObject); // // The device has been started, attempt to enumerate the device. // PpSetPlugPlayEvent( &GUID_DEVICE_ARRIVAL, DeviceNode->PhysicalDeviceObject); if (!NT_SUCCESS(status)) { return status; } PpvUtilTestStartedPdoStack(deviceObject); PipSetDevNodeState( DeviceNode, DeviceNodeStarted, NULL ); return STATUS_SUCCESS; } NTSTATUS PiProcessQueryDeviceState( IN PDEVICE_OBJECT DeviceObject ) { PDEVICE_NODE deviceNode; PNP_DEVICE_STATE deviceState; NTSTATUS status; ULONG problem; PAGED_CODE(); // // If the device was removed or surprised removed while the work // item was queued then ignore it. // status = IopQueryDeviceState(DeviceObject, &deviceState); // // Now perform the appropriate action based on the returned state // if (!NT_SUCCESS(status)) { return STATUS_SUCCESS; } deviceNode = DeviceObject->DeviceObjectExtension->DeviceNode; if (deviceState & PNP_DEVICE_DONT_DISPLAY_IN_UI) { deviceNode->UserFlags |= DNUF_DONT_SHOW_IN_UI; } else { deviceNode->UserFlags &= ~DNUF_DONT_SHOW_IN_UI; } if (deviceState & PNP_DEVICE_NOT_DISABLEABLE) { if ((deviceNode->UserFlags & DNUF_NOT_DISABLEABLE)==0) { // // this node itself is not disableable // deviceNode->UserFlags |= DNUF_NOT_DISABLEABLE; // // propagate up tree // IopIncDisableableDepends(deviceNode); } } else { if (deviceNode->UserFlags & DNUF_NOT_DISABLEABLE) { // // this node itself is now disableable // // // check tree // IopDecDisableableDepends(deviceNode); deviceNode->UserFlags &= ~DNUF_NOT_DISABLEABLE; } } // // everything here can only be turned on (state set) // if (deviceState & (PNP_DEVICE_DISABLED | PNP_DEVICE_REMOVED)) { problem = (deviceState & PNP_DEVICE_DISABLED) ? CM_PROB_HARDWARE_DISABLED : CM_PROB_DEVICE_NOT_THERE; PipRequestDeviceRemoval(deviceNode, FALSE, problem); status = STATUS_UNSUCCESSFUL; } else if (deviceState & PNP_DEVICE_RESOURCE_REQUIREMENTS_CHANGED) { if (deviceState & PNP_DEVICE_FAILED) { IopResourceRequirementsChanged(DeviceObject, TRUE); } else { IopResourceRequirementsChanged(DeviceObject, FALSE); } } else if (deviceState & PNP_DEVICE_FAILED) { PipRequestDeviceRemoval(deviceNode, FALSE, CM_PROB_FAILED_POST_START); status = STATUS_UNSUCCESSFUL; } return status; } NTSTATUS PipProcessRestartPhase1( IN PDEVICE_NODE DeviceNode, IN BOOLEAN Synchronous ) { NTSTATUS status; PAGED_CODE(); ASSERT(DeviceNode->State == DeviceNodeStopped); status = IopStartDevice(DeviceNode->PhysicalDeviceObject); // // Failure cleanup is handled in PipProcessRestartPhase2, thus we write away // the failure code and always succeed. // DeviceNode->CompletionStatus = status; PipSetDevNodeState(DeviceNode, DeviceNodeRestartCompletion, NULL); return STATUS_SUCCESS; } NTSTATUS PipProcessRestartPhase2( IN PDEVICE_NODE DeviceNode ) { ULONG problem; NTSTATUS status; PAGED_CODE(); status = DeviceNode->CompletionStatus; if (!NT_SUCCESS(status)) { SAVE_FAILURE_INFO(DeviceNode, status); // // Handle certain problems determined by the status code // switch (status) { case STATUS_PNP_REBOOT_REQUIRED: problem = CM_PROB_NEED_RESTART; break; default: problem = CM_PROB_FAILED_START; break; } PipRequestDeviceRemoval(DeviceNode, FALSE, problem); if (DeviceNode->DockInfo.DockStatus != DOCK_NOTDOCKDEVICE) { ASSERT(DeviceNode->DockInfo.DockStatus == DOCK_QUIESCENT); IoRequestDeviceEject(DeviceNode->PhysicalDeviceObject); } } else { PipSetDevNodeState(DeviceNode, DeviceNodeStarted, NULL); } return status; } NTSTATUS PiProcessHaltDevice( IN PPI_DEVICE_REQUEST Request ) /*++ Routine Description: This routine simulates a surprise removal scenario on the passed in device node. Arguments: DeviceNode - DeviceNode to halt Flags - PNP_HALT_ALLOW_NONDISABLEABLE_DEVICES - Allows halt on nodes marked non-disableable. Return Value: NTSTATUS. --*/ { ULONG flags = (ULONG)Request->RequestArgument; PDEVICE_NODE deviceNode; PAGED_CODE(); ASSERT(Request->DeviceObject != NULL); deviceNode = (PDEVICE_NODE)Request->DeviceObject->DeviceObjectExtension->DeviceNode; if (PipIsDevNodeDeleted(deviceNode)) { return STATUS_DELETE_PENDING; } if (flags & (~PNP_HALT_ALLOW_NONDISABLEABLE_DEVICES)) { return STATUS_INVALID_PARAMETER_2; } if (deviceNode->Flags & (DNF_MADEUP | DNF_LEGACY_DRIVER)) { // // Sending surprise removes to legacy devnodes would be a bad idea. // Today, if a legacy devnode fails it is manually taken to the removed // state rather than being put through the engine. // return STATUS_INVALID_DEVICE_REQUEST; } if ((!(deviceNode->Flags & PNP_HALT_ALLOW_NONDISABLEABLE_DEVICES)) && deviceNode->DisableableDepends) { return STATUS_INVALID_DEVICE_REQUEST; } if (deviceNode->State != DeviceNodeStarted) { return STATUS_INVALID_DEVICE_STATE; } PipRequestDeviceRemoval(deviceNode, FALSE, CM_PROB_HALTED); return STATUS_SUCCESS; } VOID PpResetProblemDevices( IN PDEVICE_NODE DeviceNode, IN ULONG Problem ) /*++ Routine Description: This routine resets all non-configured devices *beneath* the passed in devnode so a subsequent enum will kick off new hardware installation on them. Arguments: DeviceNode - DeviceNode to halt Return Value: None. --*/ { PAGED_CODE(); PpDevNodeLockTree(PPL_TREEOP_ALLOW_READS); PipForDeviceNodeSubtree( DeviceNode, PiResetProblemDevicesWorker, (PVOID)(ULONG_PTR)Problem ); PpDevNodeUnlockTree(PPL_TREEOP_ALLOW_READS); } NTSTATUS PiResetProblemDevicesWorker( IN PDEVICE_NODE DeviceNode, IN PVOID Context ) /*++ Routine Description: This is a worker routine for PiResetNonConfiguredDevices. If the devnode has the problem CM_PROB_NOT_CONFIGURED, the devnode is reset so a subsequent reenumeration will bring it back. Arguments: DeviceNode - Device to reset if it has the correct problem. Context - Not used. Return Value: NTSTATUS, non-successful statuses terminate the tree walk. --*/ { PAGED_CODE(); if (PipIsDevNodeProblem(DeviceNode, (ULONG)(ULONG_PTR)Context)) { // // We only need to queue it as an enumeration will drop behind it soon // afterwards... // PipRequestDeviceAction( DeviceNode->PhysicalDeviceObject, ClearDeviceProblem, TRUE, 0, NULL, NULL ); } return STATUS_SUCCESS; } VOID PiMarkDeviceTreeForReenumeration( IN PDEVICE_NODE DeviceNode, IN BOOLEAN Subtree ) /*++ Routine Description: This routine marks the devnode for reenumeration. Arguments: DeviceNode - DeviceNode to mark for re-enumeration Subtree - If TRUE, the entire subtree is marked for re-enumeration. Return Value: None. --*/ { PAGED_CODE(); PPDEVNODE_ASSERT_LOCK_HELD(PPL_TREEOP_ALLOW_READS); PiMarkDeviceTreeForReenumerationWorker(DeviceNode, NULL); if (Subtree) { PipForDeviceNodeSubtree( DeviceNode, PiMarkDeviceTreeForReenumerationWorker, NULL ); } } NTSTATUS PiMarkDeviceTreeForReenumerationWorker( IN PDEVICE_NODE DeviceNode, IN PVOID Context ) /*++ Routine Description: This is a worker routine for PiMarkDeviceTreeForReenumeration. It marks all started devnodes with DNF_REENUMERATE so that the subsequent tree processing will reenumerate the device. Arguments: DeviceNode - Device to mark if started. Context - Not used. Return Value: NTSTATUS, non-successful statuses terminate the tree walk. --*/ { PAGED_CODE(); UNREFERENCED_PARAMETER(Context); if (DeviceNode->State == DeviceNodeStarted) { if (DeviceNode->Flags & DNF_REENUMERATE) { IopDbgPrint((IOP_ENUMERATION_INFO_LEVEL, "PiMarkDeviceTreeForReenumerationWorker: Collapsed enum request on %wZ\n", &DeviceNode->InstancePath)); } else { IopDbgPrint((IOP_ENUMERATION_VERBOSE_LEVEL, "PiMarkDeviceTreeForReenumerationWorker: Reenumerating %wZ\n", &DeviceNode->InstancePath)); } DeviceNode->Flags |= DNF_REENUMERATE; } return STATUS_SUCCESS; } BOOLEAN PiCollapseEnumRequests( PLIST_ENTRY ListHead ) /*++ Routine Description: This function collapses reenumeration requests in the device action queue. Parameters: ListHead - The collapses requests get added to the end of this list. ReturnValue: None. --*/ { KIRQL oldIrql; PPI_DEVICE_REQUEST request; PLIST_ENTRY entry, next, last; PDEVICE_NODE deviceNode; ExAcquireSpinLock(&IopPnPSpinLock, &oldIrql); last = ListHead->Blink; // // Walk the list and build the list of collapsed requests. // for (entry = IopPnpEnumerationRequestList.Flink; entry != &IopPnpEnumerationRequestList; entry = next) { next = entry->Flink; request = CONTAINING_RECORD(entry, PI_DEVICE_REQUEST, ListEntry); if (request->ReorderingBarrier) { break; } switch(request->RequestType) { case ReenumerateRootDevices: case ReenumerateDeviceTree: case RestartEnumeration: // // Add it to our request list and mark the subtree. // RemoveEntryList(entry); InsertTailList(ListHead, entry); break; default: break; } } ExReleaseSpinLock(&IopPnPSpinLock, oldIrql); if (last == ListHead) { entry = ListHead->Flink; } else { entry = last; } while (entry != ListHead) { request = CONTAINING_RECORD(entry, PI_DEVICE_REQUEST, ListEntry); deviceNode = (PDEVICE_NODE)request->DeviceObject->DeviceObjectExtension->DeviceNode; PiMarkDeviceTreeForReenumeration(deviceNode, TRUE); ObDereferenceObject(request->DeviceObject); request->DeviceObject = NULL; entry = entry->Flink; } return (last != ListHead->Blink)? TRUE : FALSE; } NTSTATUS PiProcessAddBootDevices( IN PPI_DEVICE_REQUEST Request ) /*++ Routine Description: This function processes the AddBootDevices device action. Parameters: Request - AddBootDevices device action request. DeviceNode - Devnode on which the action needs to be performed. ReturnValue: STATUS_SUCCESS. --*/ { PDEVICE_NODE deviceNode; ADD_CONTEXT addContext; PAGED_CODE(); ASSERT(Request->DeviceObject != NULL); deviceNode = (PDEVICE_NODE)Request->DeviceObject->DeviceObjectExtension->DeviceNode; // // If the device has been added (or failed) skip it. // // If we know the device is a duplicate of another device which // has been enumerated at this point. we will skip this device. // if (deviceNode->State == DeviceNodeInitialized && !PipDoesDevNodeHaveProblem(deviceNode) && !(deviceNode->Flags & DNF_DUPLICATE) && deviceNode->DuplicatePDO == NULL) { // // Invoke driver's AddDevice Entry for the device. // addContext.DriverStartType = SERVICE_BOOT_START; PipCallDriverAddDevice(deviceNode, PnPBootDriversInitialized, &addContext); } return STATUS_SUCCESS; } NTSTATUS PiProcessClearDeviceProblem( IN PPI_DEVICE_REQUEST Request ) /*++ Routine Description: This function processes the ClearDeviceProblem device action. Parameters: Request - ClearDeviceProblem device action request. DeviceNode - Devnode on which the action needs to be performed. ReturnValue: STATUS_SUCCESS or STATUS_INVALID_PARAMETER_2. --*/ { NTSTATUS status; PDEVICE_NODE deviceNode; PAGED_CODE(); status = STATUS_SUCCESS; ASSERT(Request->DeviceObject != NULL); deviceNode = (PDEVICE_NODE)Request->DeviceObject->DeviceObjectExtension->DeviceNode; if (deviceNode->State == DeviceNodeUninitialized || deviceNode->State == DeviceNodeInitialized || deviceNode->State == DeviceNodeRemoved) { if (PipDoesDevNodeHaveProblem(deviceNode)) { if ((Request->RequestType == ClearDeviceProblem) && (PipIsProblemReadonly(deviceNode->Problem))) { // // ClearDeviceProblem is a user mode request, and we don't let // user mode clear readonly problems! // status = STATUS_INVALID_PARAMETER_2; } else if ((Request->RequestType == ClearEjectProblem) && (!PipIsDevNodeProblem(deviceNode, CM_PROB_HELD_FOR_EJECT))) { // // Clear eject problem means clear CM_PROB_HELD_FOR_EJECT. If // it received another problem, we leave it alone. // status = STATUS_INVALID_DEVICE_REQUEST; } else { deviceNode->Flags &= ~(DNF_HAS_PROBLEM | DNF_HAS_PRIVATE_PROBLEM); deviceNode->Problem = 0; if (deviceNode->State != DeviceNodeUninitialized) { IopRestartDeviceNode(deviceNode); } ASSERT(status == STATUS_SUCCESS); } } } else if (PipIsDevNodeDeleted(deviceNode)) { status = STATUS_DELETE_PENDING; } return status; } NTSTATUS PiProcessRequeryDeviceState( IN PPI_DEVICE_REQUEST Request ) /*++ Routine Description: This function processes the RequeryDeviceState device action. Parameters: Request - RequeryDeviceState device action request. DeviceNode - Devnode on which the action needs to be performed. ReturnValue: STATUS_SUCCESS. --*/ { PDEVICE_NODE deviceNode; NTSTATUS status; PAGED_CODE(); status = STATUS_SUCCESS; ASSERT(Request->DeviceObject != NULL); deviceNode = (PDEVICE_NODE)Request->DeviceObject->DeviceObjectExtension->DeviceNode; if (deviceNode->State == DeviceNodeStarted) { PiProcessQueryDeviceState(Request->DeviceObject); // // PCMCIA driver uses this when switching between Cardbus and R2 cards. // IopUncacheInterfaceInformation(Request->DeviceObject); } else if (PipIsDevNodeDeleted(deviceNode)) { status = STATUS_DELETE_PENDING; } return status; } NTSTATUS PiProcessResourceRequirementsChanged( IN PPI_DEVICE_REQUEST Request ) /*++ Routine Description: This function processes the ResourceRequirementsChanged device action. Parameters: Request - ResourceRequirementsChanged device action request. DeviceNode - Devnode on which the action needs to be performed. ReturnValue: STATUS_SUCCESS or STATUS_UNSUCCESSFUL. --*/ { NTSTATUS status; ADD_CONTEXT addContext; PDEVICE_NODE deviceNode; PAGED_CODE(); ASSERT(Request->DeviceObject != NULL); deviceNode = (PDEVICE_NODE)Request->DeviceObject->DeviceObjectExtension->DeviceNode; if (PipIsDevNodeDeleted(deviceNode)) { return STATUS_DELETE_PENDING; } // // Clear the NO_RESOURCE_REQUIRED flags. // deviceNode->Flags &= ~DNF_NO_RESOURCE_REQUIRED; // // If for some reason this device did not start, we need to clear some flags // such that it can be started later. In this case, we call IopRequestDeviceEnumeration // with NULL device object, so the devices will be handled in non-started case. They will // be assigned resources, started and enumerated. // deviceNode->Flags |= DNF_RESOURCE_REQUIREMENTS_CHANGED; PipClearDevNodeProblem(deviceNode); // // If the device is already started, we call IopRequestDeviceEnumeration with // the device object. // if (deviceNode->State == DeviceNodeStarted) { if (Request->RequestArgument == FALSE) { deviceNode->Flags |= DNF_NON_STOPPED_REBALANCE; } else { // // Explicitly clear it. // deviceNode->Flags &= ~DNF_NON_STOPPED_REBALANCE; } // // Reallocate resources for this devNode. // IopReallocateResources(deviceNode); addContext.DriverStartType = SERVICE_DEMAND_START; status = PipProcessDevNodeTree( IopRootDeviceNode, PnPBootDriversInitialized, // LoadDriver FALSE, // ReallocateResources EnumTypeNone, // ShallowReenumeration Request->CompletionEvent != NULL, // Synchronous TRUE, // ProcessOnlyIntermediateStates &addContext, Request); ASSERT(NT_SUCCESS(status)); if (!NT_SUCCESS(status)) { status = STATUS_SUCCESS; } } else { status = STATUS_UNSUCCESSFUL; } return status; } NTSTATUS PiProcessReenumeration( IN PPI_DEVICE_REQUEST Request ) /*++ Routine Description: This function processes the RestartEnumeration\ReenumerateRootDevices\ ReenumerateDeviceTree\ReenumerateDeviceOnly device action. Parameters: RequestList - List of reenumeration requests. ReturnValue: STATUS_SUCCESS. --*/ { PDEVICE_NODE deviceNode; ADD_CONTEXT addContext; ENUM_TYPE enumType; PAGED_CODE(); ASSERT(Request->DeviceObject != NULL); deviceNode = (PDEVICE_NODE)Request->DeviceObject->DeviceObjectExtension->DeviceNode; if (PipIsDevNodeDeleted(deviceNode)) { return STATUS_DELETE_PENDING; } enumType = (Request->RequestType == ReenumerateDeviceOnly)? EnumTypeShallow : EnumTypeDeep; PiMarkDeviceTreeForReenumeration( deviceNode, enumType != EnumTypeShallow); addContext.DriverStartType = SERVICE_DEMAND_START; PipProcessDevNodeTree( deviceNode, PnPBootDriversInitialized, // LoadDriver FALSE, // ReallocateResources enumType, TRUE, // Synchronous FALSE, &addContext, Request); return STATUS_SUCCESS; } NTSTATUS PiProcessSetDeviceProblem( IN PPI_DEVICE_REQUEST Request ) /*++ Routine Description: This function processes the SetDeviceProblem device action. Parameters: Request - SetDeviceProblem device action request. DeviceNode - Devnode on which the action needs to be performed. ReturnValue: STATUS_SUCCESS or STATUS_INVALID_PARAMETER_2. --*/ { PPLUGPLAY_CONTROL_STATUS_DATA statusData; ULONG flags, userFlags; NTSTATUS status; PDEVICE_NODE deviceNode; PAGED_CODE(); ASSERT(Request->DeviceObject != NULL); deviceNode = (PDEVICE_NODE)Request->DeviceObject->DeviceObjectExtension->DeviceNode; if (PipIsDevNodeDeleted(deviceNode)) { return STATUS_DELETE_PENDING; } status = STATUS_SUCCESS; statusData = (PPLUGPLAY_CONTROL_STATUS_DATA)Request->RequestArgument; userFlags = 0; flags = 0; if (statusData->DeviceStatus & DN_WILL_BE_REMOVED) { userFlags |= DNUF_WILL_BE_REMOVED; } if (statusData->DeviceStatus & DN_NEED_RESTART) { userFlags |= DNUF_NEED_RESTART; } if (statusData->DeviceStatus & DN_PRIVATE_PROBLEM) { flags |= DNF_HAS_PRIVATE_PROBLEM; } if (statusData->DeviceStatus & DN_HAS_PROBLEM) { flags |= DNF_HAS_PROBLEM; } if (statusData->DeviceProblem == CM_PROB_NEED_RESTART) { flags &= ~DNF_HAS_PROBLEM; userFlags |= DNUF_NEED_RESTART; } if (flags & (DNF_HAS_PROBLEM | DNF_HAS_PRIVATE_PROBLEM)) { ASSERT(!PipIsDevNodeDNStarted(deviceNode)); // // ISSUE - 2000/12/07 - ADRIAO: // This set of code allows you to clear read only // problems by first changing it to a resetable problem, // then clearing. This is not intentional. // if ( ((deviceNode->State == DeviceNodeInitialized) || (deviceNode->State == DeviceNodeRemoved)) && !PipIsProblemReadonly(statusData->DeviceProblem)) { deviceNode->Problem = statusData->DeviceProblem; deviceNode->Flags |= flags; deviceNode->UserFlags |= userFlags; } else { status = STATUS_INVALID_PARAMETER_2; } } else { deviceNode->Flags |= flags; deviceNode->UserFlags |= userFlags; } return status; } NTSTATUS PiProcessShutdownPnpDevices( IN OUT PDEVICE_NODE DeviceNode ) /*++ Routine Description: This function processes the ShutdownPnpDevices device action. Walks the tree issuing IRP_MN_QUERY_REMOVE \ IRP_MN_REMOVE_DEVICE to each stack. Parameters: DeviceNode - Root devnode. ReturnValue: STATUS_SUCCESS. --*/ { KEVENT userEvent; ULONG eventResult; WCHAR vetoName[80]; UNICODE_STRING vetoNameString = { 0, sizeof(vetoName), vetoName }; PNP_VETO_TYPE vetoType; NTSTATUS status; PAGED_CODE(); ASSERT(DeviceNode == IopRootDeviceNode); status = STATUS_SUCCESS; if (PipTearDownPnpStacksOnShutdown || (PoCleanShutdownEnabled() & PO_CLEAN_SHUTDOWN_PNP)) { DeviceNode->UserFlags |= DNUF_SHUTDOWN_QUERIED; for ( ; ; ) { // // Acquire the registry lock to prevent in process removals causing // Devnodes to be unlinked from the tree. // PiLockPnpRegistry(FALSE); // // Walk the tree looking for devnodes we haven't QueryRemoved yet. // DeviceNode = DeviceNode->Child; while (DeviceNode != NULL) { if (DeviceNode->UserFlags & DNUF_SHUTDOWN_SUBTREE_DONE) { if (DeviceNode == IopRootDeviceNode) { // // We've processed the entire devnode tree - we're done // DeviceNode = NULL; break; } if (DeviceNode->Sibling == NULL) { DeviceNode = DeviceNode->Parent; DeviceNode->UserFlags |= DNUF_SHUTDOWN_SUBTREE_DONE; } else { DeviceNode = DeviceNode->Sibling; } continue; } if (DeviceNode->UserFlags & DNUF_SHUTDOWN_QUERIED) { if (DeviceNode->Child == NULL) { DeviceNode->UserFlags |= DNUF_SHUTDOWN_SUBTREE_DONE; if (DeviceNode->Sibling == NULL) { DeviceNode = DeviceNode->Parent; DeviceNode->UserFlags |= DNUF_SHUTDOWN_SUBTREE_DONE; } else { DeviceNode = DeviceNode->Sibling; } } else { DeviceNode = DeviceNode->Child; } continue; } break; } if (DeviceNode != NULL) { DeviceNode->UserFlags |= DNUF_SHUTDOWN_QUERIED; // // Queue this device event // KeInitializeEvent(&userEvent, NotificationEvent, FALSE); vetoNameString.Length = 0; // // Queue the event, this call will return immediately. Note that status // is the status of the PpSetTargetDeviceChange while result is the // outcome of the actual event. // status = PpSetTargetDeviceRemove(DeviceNode->PhysicalDeviceObject, FALSE, // KernelInitiated TRUE, // NoRestart FALSE, // DoEject CM_PROB_SYSTEM_SHUTDOWN, &userEvent, &eventResult, &vetoType, &vetoNameString); } else { status = STATUS_UNSUCCESSFUL; } PiUnlockPnpRegistry(); if (DeviceNode == NULL) { // // We've processed the entire tree. // break; } // // Let the removes drain... // PpDevNodeUnlockTree(PPL_TREEOP_ALLOW_READS); if (NT_SUCCESS(status)) { // // Wait for the event we just queued to finish since synchronous // operation was requested (non alertable wait). // // FUTURE ITEM - Use a timeout here? // status = KeWaitForSingleObject( &userEvent, Executive, KernelMode, FALSE, NULL); if (NT_SUCCESS(status)) { status = eventResult; } } // // Require lock, start on the next // PpDevNodeLockTree(PPL_TREEOP_ALLOW_READS); } } // // Prevent any more events or action worker items from being queued // PpPnpShuttingDown = TRUE; // // Drain the event queue // PpDevNodeUnlockTree(PPL_TREEOP_ALLOW_READS); PpSynchronizeDeviceEventQueue(); PpDevNodeLockTree(PPL_TREEOP_ALLOW_READS); return status; } NTSTATUS PiProcessStartSystemDevices( IN PPI_DEVICE_REQUEST Request ) /*++ Routine Description: This function processes the StartSystemDevices device action. Parameters: RequestList - List of reenumeration requests. ReturnValue: STATUS_SUCCESS. --*/ { PDEVICE_NODE deviceNode; ADD_CONTEXT addContext; PAGED_CODE(); deviceNode = (PDEVICE_NODE)Request->DeviceObject->DeviceObjectExtension->DeviceNode; addContext.DriverStartType = SERVICE_DEMAND_START; PipProcessDevNodeTree( deviceNode, PnPBootDriversInitialized, // LoadDriver FALSE, // ReallocateResources EnumTypeNone, Request->CompletionEvent != NULL, // Synchronous FALSE, &addContext, Request); return STATUS_SUCCESS; } VOID PpRemoveDeviceActionRequests( IN PDEVICE_OBJECT DeviceObject ) { KIRQL oldIrql; PPI_DEVICE_REQUEST request; PLIST_ENTRY entry, next; ExAcquireSpinLock(&IopPnPSpinLock, &oldIrql); // // Walk the list and build the list of collapsed requests. // for (entry = IopPnpEnumerationRequestList.Flink; entry != &IopPnpEnumerationRequestList; entry = next) { next = entry->Flink; request = CONTAINING_RECORD(entry, PI_DEVICE_REQUEST, ListEntry); if (request->DeviceObject == DeviceObject) { RemoveEntryList(entry); if (request->CompletionStatus) { *request->CompletionStatus = STATUS_NO_SUCH_DEVICE; } if (request->CompletionEvent) { KeSetEvent(request->CompletionEvent, 0, FALSE); } ObDereferenceObject(request->DeviceObject); ExFreePool(request); } } ExReleaseSpinLock(&IopPnPSpinLock, oldIrql); } #if DBG VOID PipAssertDevnodesInConsistentState( VOID ) { PDEVICE_NODE deviceNode; deviceNode = IopRootDeviceNode; do { ASSERT(deviceNode->State == DeviceNodeUninitialized || deviceNode->State == DeviceNodeInitialized || deviceNode->State == DeviceNodeDriversAdded || deviceNode->State == DeviceNodeResourcesAssigned || deviceNode->State == DeviceNodeStarted || deviceNode->State == DeviceNodeStartPostWork || deviceNode->State == DeviceNodeAwaitingQueuedDeletion || deviceNode->State == DeviceNodeAwaitingQueuedRemoval || deviceNode->State == DeviceNodeRemovePendingCloses || deviceNode->State == DeviceNodeRemoved); if (deviceNode->Child != NULL) { deviceNode = deviceNode->Child; } else { while (deviceNode->Sibling == NULL) { if (deviceNode->Parent != NULL) { deviceNode = deviceNode->Parent; } else { break; } } if (deviceNode->Sibling != NULL) { deviceNode = deviceNode->Sibling; } } } while (deviceNode != IopRootDeviceNode); } #endif