7181 lines
222 KiB
C
7181 lines
222 KiB
C
|
/*++
|
|||
|
|
|||
|
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 <setupblk.h>
|
|||
|
|
|||
|
#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 <BUS>\<DEVICE>\<INSTANCE>. 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.<level>.<hash>:REG_DWORD: <NextInstance>
|
|||
|
// under CCS\Enum. If not present, create it.
|
|||
|
// * Assign the new "ParentIdPrefix" which will be of
|
|||
|
// of the form:
|
|||
|
// <level>&<hash>&<instance>
|
|||
|
//
|
|||
|
|
|||
|
// 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\<Class>\Properties protected key
|
|||
|
DevicePropKey - a handle to Enum\<Instance> 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
|