Windows2000/private/ntos/io/pnpinit.c
2020-09-30 17:12:32 +02:00

972 lines
40 KiB
C

/*++
Copyright (c) 1995 Microsoft Corporation
Module Name:
pnpsubs.c
Abstract:
This module contains the plug-and-play initialization subroutines for the I/O system.
Author:
Shie-Lin Tzong (shielint) 30-Jan-1995
Environment:
Kernel mode
--*/
#include "iop.h"
#pragma hdrstop
#ifdef POOL_TAGGING
#undef ExAllocatePool
#define ExAllocatePool(a,b) ExAllocatePoolWithTag(a,b,'nipP')
#endif
// BUGBUG - temporarily allow World Read of the Enum branch
#define ALLOW_WORLD_READ_OF_ENUM 1
typedef struct _ROOT_ENUMERATOR_CONTEXT {
NTSTATUS Status;
PUNICODE_STRING KeyName;
ULONG MaxDeviceCount;
ULONG DeviceCount;
PDEVICE_OBJECT *DeviceList;
} ROOT_ENUMERATOR_CONTEXT, *PROOT_ENUMERATOR_CONTEXT;
NTSTATUS IopPnPDriverEntry(IN PDRIVER_OBJECT DriverObject,IN PUNICODE_STRING RegistryPath);
BOOLEAN IopInitializeDeviceKey(IN HANDLE KeyHandle,IN PUNICODE_STRING KeyName,IN OUT PVOID Context);
BOOLEAN IopInitializeDeviceInstanceKey(IN HANDLE KeyHandle,IN PUNICODE_STRING KeyName,IN OUT PVOID Context);
NTSTATUS IopGetServiceType(IN PUNICODE_STRING KeyName,IN PULONG ServiceType);
INTERFACE_TYPE IopDetermineDefaultInterfaceType (VOID);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, IopPnPDriverEntry)
#pragma alloc_text(INIT, IopInitializePlugPlayServices)
#pragma alloc_text(INIT, IopDetermineDefaultInterfaceType)
#pragma alloc_text(PAGE, IopIsFirmwareMapperDevicePresent)
#pragma alloc_text(PAGE, IopGetRootDevices)
#pragma alloc_text(PAGE, IopInitializeDeviceKey)
#pragma alloc_text(PAGE, IopInitializeDeviceInstanceKey)
#pragma alloc_text(PAGE, IopGetServiceType)
#endif
NTSTATUS IopInitializePlugPlayServices(IN PLOADER_PARAMETER_BLOCK LoaderBlock,IN ULONG Phase)
/*++
Routine Description:
This routine initializes kernel mode Plug and Play services.
Arguments:
LoaderBlock - supplies a pointer to the LoaderBlock passed in from the OS Loader.
Returns:
NTSTATUS code for sucess or reason of failure.
--*/
{
NTSTATUS status;
HANDLE hTreeHandle, parentHandle, handle, hCurrentControlSet = NULL;
UNICODE_STRING unicodeName;
PKEY_VALUE_FULL_INFORMATION detectionInfo;
PDEVICE_OBJECT deviceObject;
ULONG disposition;
if (Phase == 0) {
PnPInitialized = FALSE;
// Allocate two one-page scratch buffers to be used by our initialization code. This avoids constant pool allocations.
IopPnpScratchBuffer1 = ExAllocatePool(PagedPool, PNP_LARGE_SCRATCH_BUFFER_SIZE);
if (!IopPnpScratchBuffer1) {
return STATUS_INSUFFICIENT_RESOURCES;
}
IopPnpScratchBuffer2 = ExAllocatePool(PagedPool, PNP_LARGE_SCRATCH_BUFFER_SIZE);
if (!IopPnpScratchBuffer2) {
ExFreePool(IopPnpScratchBuffer1);
return STATUS_INSUFFICIENT_RESOURCES;
}
IopInitReservedResourceList = NULL;
IopReserveResourcesRoutine = IopReserveBootResources;
// Determine the PnpDefaultInterfaceType.
// For root enumerated devices if the Interface type of their resource list or resource requirements list are undefined.
// We will use the default type instead.
PnpDefaultInterfaceType = IopDetermineDefaultInterfaceType();
// Initialize root arbiters
status = IopPortInitialize();
if (!NT_SUCCESS(status)) {
goto init_Exit0;
}
status = IopMemInitialize();
if (!NT_SUCCESS(status)) {
goto init_Exit0;
}
status = IopDmaInitialize();
if (!NT_SUCCESS(status)) {
goto init_Exit0;
}
status = IopIrqInitialize();
if (!NT_SUCCESS(status)) {
goto init_Exit0;
}
status = IopBusNumberInitialize();
if (!NT_SUCCESS(status)) {
goto init_Exit0;
}
// Next open/create System\CurrentControlSet\Enum\Root key.
status = IopOpenRegistryKeyEx( &hCurrentControlSet,NULL,&CmRegistryMachineSystemCurrentControlSet,KEY_ALL_ACCESS);
if (!NT_SUCCESS(status)) {
hCurrentControlSet = NULL;
goto init_Exit0;
}
PiWstrToUnicodeString(&unicodeName, REGSTR_KEY_ENUM);
status = IopCreateRegistryKeyEx( &handle,hCurrentControlSet,&unicodeName,KEY_ALL_ACCESS,REG_OPTION_NON_VOLATILE,&disposition);
if (!NT_SUCCESS(status)) {
goto init_Exit0;
}
if (disposition == REG_CREATED_NEW_KEY) {
SECURITY_DESCRIPTOR newSD;
PACL newDacl;
ULONG sizeDacl;
status = RtlCreateSecurityDescriptor( &newSD, SECURITY_DESCRIPTOR_REVISION );
ASSERT( NT_SUCCESS( status ) );
// calculate the size of the new DACL
sizeDacl = sizeof(ACL);
sizeDacl += sizeof(ACCESS_ALLOWED_ACE) + RtlLengthSid(SeLocalSystemSid) - sizeof(ULONG);
#if ALLOW_WORLD_READ_OF_ENUM
sizeDacl += sizeof(ACCESS_ALLOWED_ACE) + RtlLengthSid(SeWorldSid) - sizeof(ULONG);
#endif
// create and initialize the new DACL
newDacl = ExAllocatePool(PagedPool, sizeDacl);
if (newDacl != NULL) {
status = RtlCreateAcl(newDacl, sizeDacl, ACL_REVISION);
ASSERT( NT_SUCCESS( status ) );
// Add just the local system full control ace to this new DACL
status = RtlAddAccessAllowedAceEx( newDacl,ACL_REVISION,CONTAINER_INHERIT_ACE,KEY_ALL_ACCESS,SeLocalSystemSid);
ASSERT( NT_SUCCESS( status ) );
#if ALLOW_WORLD_READ_OF_ENUM
// Add just the local system full control ace to this new DACL
status = RtlAddAccessAllowedAceEx( newDacl,ACL_REVISION,CONTAINER_INHERIT_ACE,KEY_READ,SeWorldSid);
ASSERT( NT_SUCCESS( status ) );
#endif
// Set the new DACL in the absolute security descriptor
status = RtlSetDaclSecurityDescriptor( (PSECURITY_DESCRIPTOR) &newSD,TRUE,newDacl,FALSE);
ASSERT( NT_SUCCESS( status ) );
// validate the new security descriptor
status = RtlValidSecurityDescriptor(&newSD);
ASSERT( NT_SUCCESS( status ) );
status = ZwSetSecurityObject( handle,DACL_SECURITY_INFORMATION,&newSD);
if (!NT_SUCCESS(status)) {
KdPrint(("IopInitializePlugPlayServices: ZwSetSecurityObject on Enum key failed, status = %8.8X\n", status));
}
ExFreePool(newDacl);
} else {
KdPrint(("IopInitializePlugPlayServices: ExAllocatePool failed allocating DACL for Enum key\n"));
}
}
parentHandle = handle;
PiWstrToUnicodeString(&unicodeName, REGSTR_KEY_ROOTENUM);
status = IopCreateRegistryKeyEx( &handle,parentHandle,&unicodeName,KEY_ALL_ACCESS,REG_OPTION_NON_VOLATILE,NULL);
NtClose(parentHandle);
if (!NT_SUCCESS(status)) {
goto init_Exit0;
}
NtClose(handle);
// Create the registry entry for the root of the hardware tree (HTREE\ROOT\0).
status = IopOpenRegistryKeyEx( &handle,NULL,&CmRegistryMachineSystemCurrentControlSetEnumName,KEY_ALL_ACCESS);
if (NT_SUCCESS(status)) {
PiWstrToUnicodeString(&unicodeName, REGSTR_VAL_ROOT_DEVNODE);
status = IopCreateRegistryKeyEx( &hTreeHandle,handle,&unicodeName,KEY_ALL_ACCESS,REG_OPTION_NON_VOLATILE,NULL);
NtClose(handle);
if (NT_SUCCESS(status)) {
NtClose(hTreeHandle);
}
}
// Before creating device node tree, we need to initialize the device tree lock.
InitializeListHead(&IopPendingEjects);
InitializeListHead(&IopPendingSurpriseRemovals);
InitializeListHead(&IopPnpEnumerationRequestList);
ExInitializeResource(&IopDeviceTreeLock);
KeInitializeEvent(&PiEventQueueEmpty, NotificationEvent, TRUE );
KeInitializeEvent(&PiEnumerationLock, NotificationEvent, TRUE );
KeInitializeSpinLock(&IopPnPSpinLock);
// Initialize the list of dock devices, and its lock.
InitializeListHead(&IopDockDeviceListHead);
ExInitializeFastMutex(&IopDockDeviceListLock);
IopDockDeviceCount = 0;
// Initialize warm docking variables.
IopWarmEjectPdo = NULL;
KeInitializeEvent(&IopWarmEjectLock, SynchronizationEvent, TRUE );
// Create a PnP manager's driver object to own all the detected PDOs.
RtlInitUnicodeString(&unicodeName, PNPMGR_STR_PNP_DRIVER);
status = IoCreateDriver (&unicodeName, IopPnPDriverEntry);
if (NT_SUCCESS(status)) {
// Create empty device node tree, i.e., only contains only root device node
// (No need to initialize Parent, Child and Sibling links.)
status = IoCreateDevice( IoPnpDriverObject,sizeof(IOPNP_DEVICE_EXTENSION),NULL,FILE_DEVICE_CONTROLLER,0,FALSE,&deviceObject );
if (NT_SUCCESS(status)) {
deviceObject->Flags |= DO_BUS_ENUMERATED_DEVICE;
IopRootDeviceNode = IopAllocateDeviceNode(deviceObject);
if (!IopRootDeviceNode) {
IoDeleteDevice(deviceObject);
IoDeleteDriver(IoPnpDriverObject);
status = STATUS_INSUFFICIENT_RESOURCES;
} else {
IopRootDeviceNode->Flags |= DNF_STARTED + DNF_PROCESSED + DNF_ENUMERATED + DNF_MADEUP + DNF_NO_RESOURCE_REQUIRED + DNF_ADDED;
IopRootDeviceNode->InstancePath.Buffer = ExAllocatePool( PagedPool, sizeof(REGSTR_VAL_ROOT_DEVNODE));
if (IopRootDeviceNode->InstancePath.Buffer != NULL) {
IopRootDeviceNode->InstancePath.MaximumLength = sizeof(REGSTR_VAL_ROOT_DEVNODE);
IopRootDeviceNode->InstancePath.Length = sizeof(REGSTR_VAL_ROOT_DEVNODE) - sizeof(WCHAR);
RtlMoveMemory( IopRootDeviceNode->InstancePath.Buffer,REGSTR_VAL_ROOT_DEVNODE,sizeof(REGSTR_VAL_ROOT_DEVNODE));
} else {
ASSERT(FALSE);// BUGBUG - Need to bugcheck here
}
}
}
}
if (!NT_SUCCESS(status)) {
goto init_Exit0;
}
// Initialize PnPDetectionEnabled flag to determine should Wdm driver be loaded to run detection code.
// This is stored as a REG_DWORD under HKLM\System\CurrentControlSet\Control\Pnp\DetectionEnabled
// if it is not present then PNP_DETECTION_ENABLED_DEFAULT is used
// Open HKLM\System\CurrentControlSet\Control\Pnp
PiWstrToUnicodeString(&unicodeName, REGSTR_PATH_CONTROL_PNP);
status = IopCreateRegistryKeyEx( &handle,hCurrentControlSet,&unicodeName,KEY_ALL_ACCESS,REG_OPTION_NON_VOLATILE,NULL);
if (!NT_SUCCESS(status)) {
goto init_Exit0;
}
// Get the value of DetectionEnabled key if it exisit otherwise use the default
PnPDetectionEnabled = PNP_DETECTION_ENABLED_DEFAULT;
status = IopGetRegistryValue(handle,REGSTR_VALUE_DETECTION_ENABLED,&detectionInfo);
if (NT_SUCCESS(status)) {
if (detectionInfo->Type == REG_DWORD && detectionInfo->DataLength == sizeof(ULONG)) {
PnPDetectionEnabled = (BOOLEAN) *(KEY_VALUE_DATA(detectionInfo));
}
ExFreePool(detectionInfo);
}
NtClose(handle);
// Initialize the kernel mode pnp notification system
status = PpInitializeNotification();
if (!NT_SUCCESS(status)) {
goto init_Exit0;
}
IopInitializePlugPlayNotification();
// Initialize table for holding bus type guid list.
IopBusTypeGuidList = ExAllocatePool(PagedPool, sizeof(BUS_TYPE_GUID_LIST));
if (IopBusTypeGuidList == NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto init_Exit0;
}
RtlZeroMemory( IopBusTypeGuidList, sizeof(BUS_TYPE_GUID_LIST));
ExInitializeFastMutex(&IopBusTypeGuidList->Lock);
IopRequestDeviceAction(IopRootDeviceNode->PhysicalDeviceObject, ReenumerateRootDevices, NULL, NULL);// Enumerate the ROOT bus synchronously.
init_Exit0:
// If we managed to open the Current Control Set close it
if(hCurrentControlSet) {
NtClose(hCurrentControlSet);
}
if (!NT_SUCCESS(status)) {
ExFreePool(IopPnpScratchBuffer1);
ExFreePool(IopPnpScratchBuffer2);
}
} else if (Phase == 1) {
BOOLEAN legacySerialPortMappingOnly = FALSE;
// Next open/create System\CurrentControlSet\Enum\Root key.
status = IopOpenRegistryKeyEx( &hCurrentControlSet,NULL,&CmRegistryMachineSystemCurrentControlSet,KEY_ALL_ACCESS);
if (!NT_SUCCESS(status)) {
hCurrentControlSet = NULL;
goto init_Exit1;
}
// Open HKLM\System\CurrentControlSet\Control\Pnp
PiWstrToUnicodeString(&unicodeName, REGSTR_PATH_CONTROL_PNP);
status = IopCreateRegistryKeyEx( &handle,hCurrentControlSet,&unicodeName,KEY_ALL_ACCESS,REG_OPTION_NON_VOLATILE,NULL);
if (!NT_SUCCESS(status)) {
goto init_Exit1;
}
// Check the "DisableFirmwareMapper" value entry to see whether we should skip mapping ntdetect/firmware reported devices (except for COM ports, which we always map).
status = IopGetRegistryValue(handle,REGSTR_VALUE_DISABLE_FIRMWARE_MAPPER,&detectionInfo);
if (NT_SUCCESS(status)) {
if (detectionInfo->Type == REG_DWORD && detectionInfo->DataLength == sizeof(ULONG)) {
legacySerialPortMappingOnly = (BOOLEAN) *(KEY_VALUE_DATA(detectionInfo));
}
ExFreePool(detectionInfo);
}
NtClose(handle);
MapperProcessFirmwareTree(legacySerialPortMappingOnly);// Collect the necessary firmware tree information.
MapperConstructRootEnumTree(legacySerialPortMappingOnly);// Map this into the root enumerator tree
#if i386
if (!legacySerialPortMappingOnly) {
// Now do the PnP BIOS enumerated devnodes.
extern NTSTATUS PnPBiosMapper(VOID);
status = PnPBiosMapper();
if (NT_SUCCESS (status)) {// If the previous call succeeds, we have a PNPBios, turn any newly created ntdetect COM ports into phantoms
MapperPhantomizeDetectedComPorts();
}
}
EisaBuildEisaDeviceNode();
#endif
MapperFreeList();// We're done with the firmware mapper device list.
IopRequestDeviceAction(IopRootDeviceNode->PhysicalDeviceObject, ReenumerateRootDevices, NULL, NULL);// Enumerate the ROOT bus synchronously.
init_Exit1:
// If we managed to open the Current Control Set close it
if(hCurrentControlSet) {
NtClose(hCurrentControlSet);
}
// Free our scratch buffers and exit.
ExFreePool(IopPnpScratchBuffer1);
ExFreePool(IopPnpScratchBuffer2);
status = STATUS_SUCCESS;
} else {
status = STATUS_INVALID_PARAMETER_1;
}
return status;
}
NTSTATUS IopPnPDriverEntry(IN PDRIVER_OBJECT DriverObject,IN PUNICODE_STRING RegistryPath)
/*++
Routine Description:
This is the callback function when we call IoCreateDriver to create a PnP Driver Object.
In this function, we need to remember the DriverObject.
Arguments:
DriverObject - Pointer to the driver object created by the system.
RegistryPath - is NULL.
Return Value:
STATUS_SUCCESS
--*/
{
// File the pointer to our driver object away
IoPnpDriverObject = DriverObject;
// Fill in the driver object
DriverObject->DriverExtension->AddDevice = (PDRIVER_ADD_DEVICE)IopPnPAddDevice;
DriverObject->MajorFunction[ IRP_MJ_PNP ] = IopPnPDispatch;
DriverObject->MajorFunction[ IRP_MJ_POWER ] = IopPowerDispatch;
return STATUS_SUCCESS;
}
NTSTATUS IopGetRootDevices (PDEVICE_RELATIONS *DeviceRelations)
/*++
Routine Description:
This routine scans through System\Enum\Root subtree to build a device node for each root device.
Arguments:
DeviceRelations - supplies a variable to receive the returned DEVICE_RELATIONS structure.
--*/
{
NTSTATUS status;
HANDLE baseHandle;
UNICODE_STRING workName, tmpName;
PVOID buffer;
ROOT_ENUMERATOR_CONTEXT context;
ULONG i;
PDEVICE_RELATIONS deviceRelations;
PAGED_CODE();
*DeviceRelations = NULL;
buffer = ExAllocatePool(PagedPool, PNP_LARGE_SCRATCH_BUFFER_SIZE);
if (!buffer) {
return STATUS_INSUFFICIENT_RESOURCES;
}
// Allocate a buffer to store the PDOs enumerated.
// Note, the the buffer turns out to be not big enough, it will be reallocated dynamically.
context.DeviceList = (PDEVICE_OBJECT *) ExAllocatePool(PagedPool, PNP_SCRATCH_BUFFER_SIZE * 2);
if (context.DeviceList) {
context.MaxDeviceCount = (PNP_SCRATCH_BUFFER_SIZE * 2) / sizeof(PDEVICE_OBJECT);
context.DeviceCount = 0;
} else {
ExFreePool(buffer);
return STATUS_INSUFFICIENT_RESOURCES;
}
KeEnterCriticalRegion();
ExAcquireResourceExclusive(&PpRegistryDeviceResource, TRUE);
// Open System\CurrentControlSet\Enum\Root key and call worker routine to recursively scan through the subkeys.
status = IopCreateRegistryKeyEx( &baseHandle, NULL, &CmRegistryMachineSystemCurrentControlSetEnumRootName, KEY_READ, REG_OPTION_NON_VOLATILE, NULL);
if (NT_SUCCESS(status)) {
workName.Buffer = (PWSTR)buffer;
RtlFillMemory(buffer, PNP_LARGE_SCRATCH_BUFFER_SIZE, 0);
workName.MaximumLength = PNP_LARGE_SCRATCH_BUFFER_SIZE;
workName.Length = 0;
// only look at ROOT key
PiWstrToUnicodeString(&tmpName, REGSTR_KEY_ROOTENUM);
RtlAppendStringToString((PSTRING)&workName, (PSTRING)&tmpName);
// Enumerate all subkeys under the System\CCS\Enum\Root.
context.Status = STATUS_SUCCESS;
context.KeyName = &workName;
status = IopApplyFunctionToSubKeys(baseHandle, NULL, KEY_ALL_ACCESS, FUNCTIONSUBKEY_FLAG_IGNORE_NON_CRITICAL_ERRORS, IopInitializeDeviceKey, &context);
ZwClose(baseHandle);
// Build returned information from ROOT_ENUMERATOR_CONTEXT.
status = context.Status;
if (NT_SUCCESS(status) && context.DeviceCount != 0) {
deviceRelations = (PDEVICE_RELATIONS) ExAllocatePool(PagedPool,sizeof (DEVICE_RELATIONS) + sizeof(PDEVICE_OBJECT) * context.DeviceCount);
if (deviceRelations == NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
} else {
deviceRelations->Count = context.DeviceCount;
RtlMoveMemory(deviceRelations->Objects,context.DeviceList,sizeof (PDEVICE_OBJECT) * context.DeviceCount);
*DeviceRelations = deviceRelations;
}
}
if (!NT_SUCCESS(status)) {
// If somehow the enumeration failed, we need to derefernece all the device objects.
for (i = 0; i < context.DeviceCount; i++) {
ObDereferenceObject(context.DeviceList[i]);
}
}
}
ExReleaseResource(&PpRegistryDeviceResource);
KeLeaveCriticalRegion();
ExFreePool(buffer);
ExFreePool(context.DeviceList);
return status;
}
BOOLEAN IopInitializeDeviceKey(IN HANDLE KeyHandle,IN PUNICODE_STRING KeyName,IN OUT PVOID Context)
/*++
Routine Description:
This routine is a callback function for IopApplyFunctionToSubKeys.
It is called for each subkey under HKLM\System\CCS\Enum\BusKey.
Arguments:
KeyHandle - Supplies a handle to this key.
KeyName - Supplies the name of this key.
Context - points to the ROOT_ENUMERATOR_CONTEXT structure.
Returns:
TRUE to continue the enumeration.
FALSE to abort it.
--*/
{
USHORT length;
PWSTR p;
PUNICODE_STRING unicodeName = ((PROOT_ENUMERATOR_CONTEXT)Context)->KeyName;
length = unicodeName->Length;
p = unicodeName->Buffer;
if ( unicodeName->Length / sizeof(WCHAR) != 0) {
p += unicodeName->Length / sizeof(WCHAR);
*p = OBJ_NAME_PATH_SEPARATOR;
unicodeName->Length += sizeof (WCHAR);
}
RtlAppendStringToString((PSTRING)unicodeName, (PSTRING)KeyName);
// Enumerate all subkeys under the current device key.
IopApplyFunctionToSubKeys(KeyHandle,NULL,KEY_ALL_ACCESS,FUNCTIONSUBKEY_FLAG_IGNORE_NON_CRITICAL_ERRORS,IopInitializeDeviceInstanceKey,Context);
unicodeName->Length = length;
return NT_SUCCESS(((PROOT_ENUMERATOR_CONTEXT)Context)->Status);
}
BOOLEAN IopInitializeDeviceInstanceKey(IN HANDLE KeyHandle,IN PUNICODE_STRING KeyName,IN OUT PVOID Context)
/*++
Routine Description:
This routine is a callback function for IopApplyFunctionToSubKeys.
It is called for each subkey under HKLM\System\Enum\Root\DeviceKey.
Arguments:
KeyHandle - Supplies a handle to this key.
KeyName - Supplies the name of this key.
Context - points to the ROOT_ENUMERATOR_CONTEXT structure.
Returns:
TRUE to continue the enumeration.
FALSE to abort it.
--*/
{
UNICODE_STRING unicodeName, serviceName;
PKEY_VALUE_FULL_INFORMATION serviceKeyValueInfo;
PKEY_VALUE_FULL_INFORMATION keyValueInformation;
NTSTATUS status;
BOOLEAN isDuplicate = FALSE;
BOOLEAN configuredBySetup;
ULONG deviceFlags, instance, tmpValue1;
ULONG legacy;
USHORT savedLength;
PUNICODE_STRING pUnicode;
HANDLE handle;
PDEVICE_OBJECT deviceObject;
PDEVICE_NODE deviceNode = NULL;
PROOT_ENUMERATOR_CONTEXT enumContext = (PROOT_ENUMERATOR_CONTEXT)Context;
WCHAR buffer[30];
UNICODE_STRING deviceName;
USHORT length;
LARGE_INTEGER tickCount;
// First off, check to see if this is a phantom device instance (i.e., registry key only).
// If so, we want to totally ignore this key and move on to the next one.
status = IopGetRegistryValue(KeyHandle,REGSTR_VAL_PHANTOM,&keyValueInformation);
if (NT_SUCCESS(status)) {
if ((keyValueInformation->Type == REG_DWORD) && (keyValueInformation->DataLength >= sizeof(ULONG))) {
tmpValue1 = *(PULONG)KEY_VALUE_DATA(keyValueInformation);
} else {
tmpValue1 = 0;
}
ExFreePool(keyValueInformation);
if (tmpValue1) {
return TRUE;
}
}
// Since it is highly likely we are going to report another PDO make sure there will be room in the buffer.
if (enumContext->DeviceCount == enumContext->MaxDeviceCount) {
PDEVICE_OBJECT *tmpDeviceObjectList;
ULONG tmpDeviceObjectListSize;
// We need to grow our PDO list buffer.
tmpDeviceObjectListSize = (enumContext->MaxDeviceCount * sizeof(PDEVICE_OBJECT)) + (PNP_SCRATCH_BUFFER_SIZE * 2);
tmpDeviceObjectList = ExAllocatePool(PagedPool, tmpDeviceObjectListSize);
if (tmpDeviceObjectList) {
RtlCopyMemory( tmpDeviceObjectList,enumContext->DeviceList,enumContext->DeviceCount * sizeof(PDEVICE_OBJECT));
ExFreePool(enumContext->DeviceList);
enumContext->DeviceList = tmpDeviceObjectList;
enumContext->MaxDeviceCount = tmpDeviceObjectListSize / sizeof(PDEVICE_OBJECT);
} else {
// We are out of memory. There is no point going any further since we don't have any place to report the PDOs anyways.
enumContext->Status = STATUS_INSUFFICIENT_RESOURCES;
return FALSE;
}
}
// Check if the PDO for the device instance key exists already. If no, see if we need to create it.
deviceObject = IopDeviceObjectFromDeviceInstance(KeyHandle, NULL);
if (deviceObject != NULL) {
enumContext->DeviceList[enumContext->DeviceCount] = deviceObject;
enumContext->DeviceCount++;
return TRUE;
}
// We don't have device object for it.
// First check if this key was created by firmware mapper.
// If yes, make sure the device is still present.
if (!IopIsFirmwareMapperDevicePresent(KeyHandle)) {
return TRUE;
}
// Get the "DuplicateOf" value entry to determine if the device instance should be registered.
// If the device instance is duplicate, We don't add it to its service key's enum branch.
status = IopGetRegistryValue( KeyHandle, REGSTR_VALUE_DUPLICATEOF, &keyValueInformation);
if (NT_SUCCESS(status)) {
if (keyValueInformation->Type == REG_SZ && keyValueInformation->DataLength > 0) {
isDuplicate = TRUE;
}
ExFreePool(keyValueInformation);
}
// Get the "Service=" value entry from KeyHandle
serviceKeyValueInfo = NULL;
RtlInitUnicodeString(&serviceName, NULL);
status = IopGetRegistryValue ( KeyHandle,REGSTR_VALUE_SERVICE,&serviceKeyValueInfo);
if (NT_SUCCESS(status)) {
// Append the new instance to its corresponding
// Service\Name\Enum.
if (serviceKeyValueInfo->Type == REG_SZ && serviceKeyValueInfo->DataLength != 0) {
// Set up ServiceKeyName unicode string
IopRegistryDataToUnicodeString(&serviceName,(PWSTR)KEY_VALUE_DATA(serviceKeyValueInfo),serviceKeyValueInfo->DataLength);
}
// Do not Free serviceKeyValueInfo. It contains Service Name.
}
// Combine Context->KeyName, i.e. the device name and KeyName (device instance name)
// to form device instance path and register this device instance by constructing new value entry for ServiceKeyName\Enum key.
// i.e., <Number> = <PathToSystemEnumBranch>
pUnicode = ((PROOT_ENUMERATOR_CONTEXT)Context)->KeyName;
savedLength = pUnicode->Length; // Save WorkName
if (pUnicode->Buffer[pUnicode->Length / sizeof(WCHAR) - 1] != OBJ_NAME_PATH_SEPARATOR) {
pUnicode->Buffer[pUnicode->Length / sizeof(WCHAR)] = OBJ_NAME_PATH_SEPARATOR;
pUnicode->Length += 2;
}
RtlAppendStringToString((PSTRING)pUnicode, (PSTRING)KeyName);
// For the stuff under Root, we need to expose devnodes for everything
// except those devices whose CsConfigFlags are set to CSCONFIGFLAG_DO_NOT_CREATE.
status = IopGetDeviceInstanceCsConfigFlags( pUnicode, &deviceFlags );
if (NT_SUCCESS(status) && (deviceFlags & CSCONFIGFLAG_DO_NOT_CREATE)) {
ExFreePool(serviceKeyValueInfo);
return TRUE;
}
// Make sure this device instance is really a "device" by checking the "Legacy" value name.
legacy = 0;
status = IopGetRegistryValue( KeyHandle,REGSTR_VALUE_LEGACY,&keyValueInformation);
if (NT_SUCCESS(status)) {
// If "Legacy=" exists ...
if (keyValueInformation->Type == REG_DWORD) {
if (keyValueInformation->DataLength >= sizeof(ULONG)) {
legacy = *(PULONG)KEY_VALUE_DATA(keyValueInformation);
}
}
ExFreePool(keyValueInformation);
}
if (legacy) {
BOOLEAN doCreate = FALSE;
// Check if the the service for the device instance is a kernel mode driver (even though it is a legacy device instance.)
// If yes, we will create a PDO for it.
if (serviceName.Length) {
status = IopGetServiceType(&serviceName, &tmpValue1);
if (NT_SUCCESS(status) && tmpValue1 == SERVICE_KERNEL_DRIVER) {
doCreate = TRUE;
}
}
if (!doCreate) {
// We are not creating PDO for the device instance.
// In this case we need to register the device ourself for legacy compatibility.
// Note we will register this device to its driver even it is a duplicate.
// It will be deregistered when the real enumerated device shows up.
// We need to do this because the driver which controls the device may be a boot driver.
PpDeviceRegistration( pUnicode, TRUE, NULL );
// We did not create a PDO. Release the service and ordinal names.
if (serviceKeyValueInfo) {
ExFreePool(serviceKeyValueInfo);
}
pUnicode->Length = savedLength;// Restore WorkName
return TRUE;
}
}
if (serviceKeyValueInfo) {
ExFreePool(serviceKeyValueInfo);
}
// Create madeup PDO and device node to represent the root device.
KeQueryTickCount(&tickCount);// Madeup a name for the device object.
length = (USHORT) _snwprintf(buffer, sizeof(buffer) / sizeof(WCHAR), L"\\Device\\%04u%x", IopNumberDeviceNodes, tickCount.LowPart);
deviceName.MaximumLength = sizeof(buffer);
deviceName.Length = length * sizeof(WCHAR);
deviceName.Buffer = buffer;
// Create madeup PDO and device node to represent the root device.
status = IoCreateDevice( IoPnpDriverObject,sizeof(IOPNP_DEVICE_EXTENSION),&deviceName,FILE_DEVICE_CONTROLLER,0,FALSE,&deviceObject );
if (NT_SUCCESS(status)) {
deviceObject->Flags |= DO_BUS_ENUMERATED_DEVICE;
deviceObject->DeviceObjectExtension->ExtensionFlags |= DOE_START_PENDING;
deviceNode = IopAllocateDeviceNode(deviceObject);
if (deviceNode) {
PCM_RESOURCE_LIST cmResource;
deviceNode->Flags = DNF_MADEUP + DNF_PROCESSED + DNF_ENUMERATED;
IopInsertTreeDeviceNode(IopRootDeviceNode, deviceNode);
// Make a copy of the device instance path and save it in device node.
status = IopConcatenateUnicodeStrings(&deviceNode->InstancePath,pUnicode,NULL);
if (legacy) {
deviceNode->Flags |= DNF_LEGACY_DRIVER + DNF_ADDED + DNF_STARTED + DNF_NO_RESOURCE_REQUIRED;
} else {
// The device instance key exists. We need to propagate the ConfigFlag to problem and StatusFlags
deviceFlags = 0;
status = IopGetRegistryValue(KeyHandle,REGSTR_VALUE_CONFIG_FLAGS,&keyValueInformation);
if (NT_SUCCESS(status)) {
if ((keyValueInformation->Type == REG_DWORD) && (keyValueInformation->DataLength >= sizeof(ULONG))) {
deviceFlags = *(PULONG)KEY_VALUE_DATA(keyValueInformation);
}
ExFreePool(keyValueInformation);
if (deviceFlags & CONFIGFLAG_REINSTALL) {
IopSetDevNodeProblem(deviceNode, CM_PROB_REINSTALL);
} else if (deviceFlags & CONFIGFLAG_PARTIAL_LOG_CONF) {
IopSetDevNodeProblem(deviceNode, CM_PROB_PARTIAL_LOG_CONF);
} else if (deviceFlags & CONFIGFLAG_FAILEDINSTALL) {
IopSetDevNodeProblem(deviceNode, CM_PROB_FAILED_INSTALL);
}
} else if (status == STATUS_OBJECT_NAME_NOT_FOUND || status == STATUS_OBJECT_PATH_NOT_FOUND) {
IopSetDevNodeProblem(deviceNode, CM_PROB_NOT_CONFIGURED);
}
}
if (isDuplicate) {
deviceNode->Flags |= DNF_DUPLICATE;
}
// If the key say don't assign any resource, honor it...
PiWstrToUnicodeString(&unicodeName, REGSTR_VALUE_NO_RESOURCE_AT_INIT);
status = IopGetRegistryValue( KeyHandle,unicodeName.Buffer,&keyValueInformation);
if (NT_SUCCESS(status)) {
if (keyValueInformation->Type == REG_DWORD) {
if (keyValueInformation->DataLength >= sizeof(ULONG)) {
tmpValue1 = *(PULONG)KEY_VALUE_DATA(keyValueInformation);
if (tmpValue1 != 0) {
deviceNode->Flags |= DNF_NO_RESOURCE_REQUIRED;
}
}
}
ExFreePool(keyValueInformation);
}
// we need to set initial capabilities, like any other device this will also handle hardware-disabled case
IopDeviceNodeCapabilitiesToRegistry(deviceNode);
if (IopDeviceNodeFlagsToCapabilities(deviceNode)->HardwareDisabled && !IopIsDevNodeProblem(deviceNode,CM_PROB_NOT_CONFIGURED)) {
// mark the node as hardware disabled, if no other problems
IopClearDevNodeProblem(deviceNode);
IopSetDevNodeProblem(deviceNode, CM_PROB_HARDWARE_DISABLED);
// Issue a PNP REMOVE_DEVICE Irp so when we query resources we have those required after boot
//status = IopRemoveDevice (deviceNode->PhysicalDeviceObject, IRP_MN_REMOVE_DEVICE);
//ASSERT(NT_SUCCESS(status));
}
// Install service for critical devices.
// however don't do it if we found HardwareDisabled to be set
if (IopDoesDevNodeHaveProblem(deviceNode) && !IopDeviceNodeFlagsToCapabilities(deviceNode)->HardwareDisabled) {
IopProcessCriticalDevice(deviceNode);
}
// Set DNF_DISABLED flag if the device instance is disabled.
ASSERT(!IopDoesDevNodeHaveProblem(deviceNode) ||
IopIsDevNodeProblem(deviceNode, CM_PROB_NOT_CONFIGURED) ||
IopIsDevNodeProblem(deviceNode, CM_PROB_REINSTALL) ||
IopIsDevNodeProblem(deviceNode, CM_PROB_FAILED_INSTALL) ||
IopIsDevNodeProblem(deviceNode, CM_PROB_HARDWARE_DISABLED) ||
IopIsDevNodeProblem(deviceNode, CM_PROB_PARTIAL_LOG_CONF));
if (!IopIsDevNodeProblem(deviceNode, CM_PROB_DISABLED) &&
!IopIsDevNodeProblem(deviceNode, CM_PROB_HARDWARE_DISABLED) &&
!IopIsDeviceInstanceEnabled(KeyHandle, &deviceNode->InstancePath, TRUE)) {
// Normally IopIsDeviceInstanceEnabled would set CM_PROB_DISABLED as a side effect (if necessary).
// But it relies on the DeviceReference already being in the registry.
// We don't write it out till later so just set the problem now.
IopSetDevNodeProblem( deviceNode, CM_PROB_DISABLED );
}
status = IopNotifySetupDeviceArrival( deviceNode->PhysicalDeviceObject, KeyHandle, TRUE);
configuredBySetup = NT_SUCCESS(status);
status = PpDeviceRegistration( &deviceNode->InstancePath, TRUE, &deviceNode->ServiceName);
if (NT_SUCCESS(status) && configuredBySetup && IopIsDevNodeProblem(deviceNode, CM_PROB_NOT_CONFIGURED)) {
IopClearDevNodeProblem(deviceNode);
}
// Write the addr of the device object to registry
PiWstrToUnicodeString(&unicodeName, REGSTR_KEY_CONTROL);
status = IopCreateRegistryKeyEx( &handle,KeyHandle,&unicodeName,KEY_ALL_ACCESS,REG_OPTION_VOLATILE,NULL);
if (NT_SUCCESS(status)) {
PiWstrToUnicodeString(&unicodeName, REGSTR_VALUE_DEVICE_REFERENCE);
ZwSetValueKey( handle,&unicodeName,TITLE_INDEX_VALUE,REG_DWORD,(PULONG_PTR)&deviceObject,sizeof(ULONG_PTR));
ZwClose(handle);
}
ObReferenceObject(deviceObject);// Add a reference for config magr
// Check if this device has BOOT config. If yes, reserve them
cmResource = NULL;
status = IopGetDeviceResourcesFromRegistry (deviceObject,QUERY_RESOURCE_LIST,REGISTRY_BOOT_CONFIG,&cmResource,&tmpValue1);
if (NT_SUCCESS(status) && cmResource) {
// Still reserve boot config, even though the device is disabled.
status = (*IopReserveResourcesRoutine)(ArbiterRequestPnpEnumerated,deviceNode->PhysicalDeviceObject,cmResource);
if (NT_SUCCESS(status)) {
deviceNode->Flags |= DNF_HAS_BOOT_CONFIG;
}
ExFreePool(cmResource);
}
status = STATUS_SUCCESS;
ObReferenceObject(deviceObject);// Add a reference for query device relations
} else {
IoDeleteDevice(deviceObject);
deviceObject = NULL;
status = STATUS_INSUFFICIENT_RESOURCES;
}
}
pUnicode->Length = savedLength;// Restore WorkName
// If we enumerated a root device, add it to the device list
if (NT_SUCCESS(status)) {
ASSERT(deviceObject != NULL);
enumContext->DeviceList[enumContext->DeviceCount] = deviceObject;
enumContext->DeviceCount++;
return TRUE;
} else {
enumContext->Status = status;
return FALSE;
}
}
NTSTATUS IopGetServiceType(IN PUNICODE_STRING KeyName,IN PULONG ServiceType)
/*++
Routine Description:
This routine returns the controlling service's service type of the specified Device instance.
Arguments:
KeyName - supplies a unicode string to specify the device instance.
ServiceType - supplies a pointer to a variable to receive the service type.
--*/
{
NTSTATUS status;
HANDLE handle;
PKEY_VALUE_FULL_INFORMATION keyValueInformation;
PAGED_CODE();
*ServiceType = -1;
status = IopOpenServiceEnumKeys (KeyName,KEY_READ,&handle,NULL,FALSE);
if (NT_SUCCESS(status)) {
status = IopGetRegistryValue(handle, L"Type", &keyValueInformation);
if (NT_SUCCESS(status)) {
if (keyValueInformation->Type == REG_DWORD) {
if (keyValueInformation->DataLength >= sizeof(ULONG)) {
*ServiceType = *(PULONG)KEY_VALUE_DATA(keyValueInformation);
}
}
ExFreePool(keyValueInformation);
}
ZwClose(handle);
}
return status;
}
INTERFACE_TYPE IopDetermineDefaultInterfaceType (VOID)
/*++
Routine Description:
This routine checks if detection flag is set to enable driver detection.
The detection will be enabled if there is no PCI bus in the machine and only on ALPHA machine.
Return Value:
BOOLEAN value to indicate if detection is enabled.
--*/
{
NTSTATUS status;
PVOID p;
PHAL_BUS_INFORMATION pBusInfo;
ULONG length, i;
INTERFACE_TYPE interfaceType = Isa;
pBusInfo = IopPnpScratchBuffer1;
length = PNP_LARGE_SCRATCH_BUFFER_SIZE;
status = HalQuerySystemInformation (HalInstalledBusInformation,length,pBusInfo,&length);
if (!NT_SUCCESS(status)) {
return interfaceType;
}
// Check installed bus information to make sure there is no existing Pnp Isa bus extender.
p = pBusInfo;
for (i = 0; i < length / sizeof(HAL_BUS_INFORMATION); i++, pBusInfo++) {
if (pBusInfo->BusType == Isa || pBusInfo->BusType == Eisa) {
interfaceType = Isa;
break;
} else if (pBusInfo->BusType == MicroChannel) {
interfaceType = MicroChannel;
}
}
return interfaceType;
}
BOOLEAN IopIsFirmwareMapperDevicePresent (IN HANDLE KeyHandle)
/*++
Routine Description:
This routine checks if the registry key is created by FirmwareMapper.
If Yes, it further checks if the device for the key is present in this boot.
Parameters:
KeyHandle - Specifies a handle to the registry key to be checked.
Return Value:
A BOOLEAN vaStatus code that indicates whether or not the function was successful.
--*/
{
NTSTATUS status;
HANDLE handle;
UNICODE_STRING unicodeName;
PKEY_VALUE_FULL_INFORMATION keyValueInformation;
ULONG tmp = 0;
PAGED_CODE();
// First check to see if this device instance key is a firmware-created one
status = IopGetRegistryValue (KeyHandle,REGSTR_VAL_FIRMWAREIDENTIFIED,&keyValueInformation);
if (NT_SUCCESS(status)) {
if ((keyValueInformation->Type == REG_DWORD) && (keyValueInformation->DataLength == sizeof(ULONG))) {
tmp = *(PULONG)KEY_VALUE_DATA(keyValueInformation);
}
ExFreePool(keyValueInformation);
}
if (tmp == 0) {
return TRUE;
}
// Make sure the device is present.
PiWstrToUnicodeString(&unicodeName, REGSTR_KEY_CONTROL);
status = IopOpenRegistryKeyEx( &handle,KeyHandle,&unicodeName,KEY_READ);
if (!NT_SUCCESS(status)) {
return FALSE;
}
status = IopGetRegistryValue (handle,REGSTR_VAL_FIRMWAREMEMBER,&keyValueInformation);
ZwClose(handle);
tmp = 0;
if (NT_SUCCESS(status)) {
if ((keyValueInformation->Type == REG_DWORD) && (keyValueInformation->DataLength == sizeof(ULONG))) {
tmp = *(PULONG)KEY_VALUE_DATA(keyValueInformation);
}
ExFreePool(keyValueInformation);
}
if (!tmp) {
return FALSE;
} else {
return TRUE;
}
}