3553 lines
110 KiB
C
3553 lines
110 KiB
C
/*++
|
||
|
||
Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
||
Module Name:
|
||
|
||
pnpioapi.c
|
||
|
||
Abstract:
|
||
|
||
This module contains the plug-and-play IO system APIs.
|
||
|
||
Author:
|
||
|
||
Shie-Lin Tzong (shielint) 3-Jan-1995
|
||
Andrew Thornton (andrewth) 5-Sept-1996
|
||
Paula Tomlinson (paulat) 1-May-1997
|
||
|
||
Environment:
|
||
|
||
Kernel mode
|
||
|
||
Revision History:
|
||
|
||
|
||
--*/
|
||
|
||
#include "pnpmgrp.h"
|
||
#pragma hdrstop
|
||
#include <stddef.h>
|
||
#include <wdmguid.h>
|
||
|
||
#ifdef POOL_TAGGING
|
||
#undef ExAllocatePool
|
||
#define ExAllocatePool(a,b) ExAllocatePoolWithTag(a,b,'oipP')
|
||
#endif
|
||
|
||
|
||
//
|
||
// Define device state work item.
|
||
//
|
||
|
||
typedef struct _DEVICE_WORK_ITEM {
|
||
WORK_QUEUE_ITEM WorkItem;
|
||
PDEVICE_OBJECT DeviceObject;
|
||
PVOID Context;
|
||
} DEVICE_WORK_ITEM, *PDEVICE_WORK_ITEM;
|
||
|
||
NTSTATUS
|
||
IopQueueDeviceWorkItem(
|
||
IN PDEVICE_OBJECT PhysicalDeviceObject,
|
||
IN VOID (*WorkerRoutine)(PVOID),
|
||
IN PVOID Context
|
||
);
|
||
|
||
VOID
|
||
IopRequestDeviceEjectWorker(
|
||
PVOID Context
|
||
);
|
||
|
||
BOOLEAN
|
||
IopIsReportedAlready(
|
||
IN HANDLE Handle,
|
||
IN PUNICODE_STRING ServiceName,
|
||
IN PCM_RESOURCE_LIST ResourceList,
|
||
OUT PBOOLEAN MatchingKey
|
||
);
|
||
|
||
//
|
||
// Definitions for IoOpenDeviceRegistryKey
|
||
//
|
||
|
||
#define PATH_CURRENTCONTROLSET_HW_PROFILE_CURRENT TEXT("\\Registry\\Machine\\System\\CurrentControlSet\\Hardware Profiles\\Current\\System\\CurrentControlSet")
|
||
#define PATH_CURRENTCONTROLSET TEXT("\\Registry\\Machine\\System\\CurrentControlSet")
|
||
#define PATH_ENUM TEXT("Enum\\")
|
||
#define PATH_CONTROL_CLASS TEXT("Control\\Class\\")
|
||
#define PATH_CCS_CONTROL_CLASS PATH_CURRENTCONTROLSET TEXT("\\") REGSTR_KEY_CONTROL TEXT("\\") REGSTR_KEY_CLASS
|
||
#define MAX_RESTPATH_BUF_LEN 512
|
||
|
||
//
|
||
// Definitions for PpCreateLegacyDeviceIds
|
||
//
|
||
|
||
#define LEGACY_COMPATIBLE_ID_BASE TEXT("DETECTED")
|
||
|
||
NTSTATUS
|
||
PpCreateLegacyDeviceIds(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PUNICODE_STRING DriverName,
|
||
IN PCM_RESOURCE_LIST Resources
|
||
);
|
||
|
||
//
|
||
// An IO_GET_LEGACY_VETO_LIST_CONTEXT structure.
|
||
//
|
||
|
||
typedef struct {
|
||
PWSTR * VetoList;
|
||
ULONG VetoListLength;
|
||
PPNP_VETO_TYPE VetoType;
|
||
NTSTATUS * Status;
|
||
} IO_GET_LEGACY_VETO_LIST_CONTEXT, *PIO_GET_LEGACY_VETO_LIST_CONTEXT;
|
||
|
||
BOOLEAN
|
||
IopAppendLegacyVeto(
|
||
IN PIO_GET_LEGACY_VETO_LIST_CONTEXT Context,
|
||
IN PUNICODE_STRING VetoName
|
||
);
|
||
BOOLEAN
|
||
IopGetLegacyVetoListDevice(
|
||
IN PDEVICE_NODE DeviceNode,
|
||
IN PIO_GET_LEGACY_VETO_LIST_CONTEXT Context
|
||
);
|
||
BOOLEAN
|
||
IopGetLegacyVetoListDeviceNode(
|
||
IN PDEVICE_NODE DeviceNode,
|
||
IN PIO_GET_LEGACY_VETO_LIST_CONTEXT Context
|
||
);
|
||
VOID
|
||
IopGetLegacyVetoListDrivers(
|
||
IN PIO_GET_LEGACY_VETO_LIST_CONTEXT Context
|
||
);
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE, IoForwardAndCatchIrp)
|
||
#pragma alloc_text(PAGE, IoGetDeviceProperty)
|
||
#pragma alloc_text(PAGE, IoGetDmaAdapter)
|
||
#pragma alloc_text(PAGE, IoGetLegacyVetoList)
|
||
#pragma alloc_text(PAGE, IoIsWdmVersionAvailable)
|
||
#pragma alloc_text(PAGE, IoOpenDeviceRegistryKey)
|
||
#pragma alloc_text(PAGE, IoReportDetectedDevice)
|
||
#pragma alloc_text(PAGE, IoSynchronousInvalidateDeviceRelations)
|
||
#pragma alloc_text(PAGE, PpCreateLegacyDeviceIds)
|
||
#pragma alloc_text(PAGE, IopAppendLegacyVeto)
|
||
#pragma alloc_text(PAGE, IopGetLegacyVetoListDevice)
|
||
#pragma alloc_text(PAGE, IopGetLegacyVetoListDeviceNode)
|
||
#pragma alloc_text(PAGE, IopGetLegacyVetoListDrivers)
|
||
#pragma alloc_text(PAGE, IopIsReportedAlready)
|
||
#pragma alloc_text(PAGE, IopOpenDeviceParametersSubkey)
|
||
#pragma alloc_text(PAGE, IopRequestDeviceEjectWorker)
|
||
#pragma alloc_text(PAGE, IopResourceRequirementsChanged)
|
||
#pragma alloc_text(PAGE, PiGetDeviceRegistryProperty)
|
||
#endif // ALLOC_PRAGMA
|
||
|
||
NTSTATUS
|
||
IoGetDeviceProperty(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN DEVICE_REGISTRY_PROPERTY DeviceProperty,
|
||
IN ULONG BufferLength,
|
||
OUT PVOID PropertyBuffer,
|
||
OUT PULONG ResultLength
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine lets drivers query the registry properties associated with the
|
||
specified device.
|
||
|
||
Parameters:
|
||
|
||
DeviceObject - Supplies the device object whoes registry property is to be
|
||
returned. This device object should be the one created by
|
||
a bus driver.
|
||
|
||
DeviceProperty - Specifies what device property to get.
|
||
|
||
BufferLength - Specifies the length, in byte, of the PropertyBuffer.
|
||
|
||
PropertyBuffer - Supplies a pointer to a buffer to receive property data.
|
||
|
||
ResultLength - Supplies a pointer to a variable to receive the size of the
|
||
property data returned.
|
||
|
||
ReturnValue:
|
||
|
||
Status code that indicates whether or not the function was successful. If
|
||
PropertyBuffer is not big enough to hold requested data, STATUS_BUFFER_TOO_SMALL
|
||
will be returned and ResultLength will be set to the number of bytes actually
|
||
required.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
PDEVICE_NODE deviceNode;
|
||
DEVICE_CAPABILITIES capabilities;
|
||
PWSTR valueName, keyName = NULL;
|
||
ULONG valueType, length, instance, configFlags;
|
||
DEVICE_INSTALL_STATE deviceInstallState;
|
||
POBJECT_NAME_INFORMATION deviceObjectName;
|
||
PWSTR deviceInstanceName;
|
||
PWCHAR enumeratorNameEnd, ids;
|
||
UNICODE_STRING unicodeString, guidString;
|
||
HANDLE classHandle, classGuidHandle;
|
||
PKEY_VALUE_FULL_INFORMATION keyValueInformation;
|
||
WCHAR classGuid[GUID_STRING_LEN];
|
||
GUID busTypeGuid;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Initialize out parameters
|
||
//
|
||
*ResultLength = 0;
|
||
|
||
if (!IS_PDO(DeviceObject)) {
|
||
|
||
if ((DeviceProperty != DevicePropertyInstallState) &&
|
||
((DeviceProperty != DevicePropertyEnumeratorName) ||
|
||
(NULL == DeviceObject->DeviceObjectExtension->DeviceNode))) {
|
||
|
||
//
|
||
// We'll use the verifier to fail anyone who passes in something
|
||
// that is not a PDO *except* for the DevicePropertyInstallState.
|
||
// This is because our check for if something is a PDO really means
|
||
// is this a PDO that PNP knows about. For the most part these are
|
||
// the same, but the DevicePropertyInstallState will get called by
|
||
// classpnp, for device objects that *it* thinks it reported as
|
||
// PDOs, but PartMgr actually swallowed. This is a gross exception
|
||
// to make, so PartMgr really should be fixed.
|
||
//
|
||
// The arbiters attempt to retrieve the Enumerator Name property
|
||
// in determining whether "driver shared" resource allocations may
|
||
// be accommodated. The PDO used may be of the "legacy resource
|
||
// devnode" placeholder variety. The IS_PDO() macro explicitly
|
||
// disallows these devnodes, so we must special-case this as well,
|
||
// in order to avoid a verifier failure. Note that our behavior
|
||
// here is correct--we want the get-property call to fail for these
|
||
// legacy resource devnodes.
|
||
//
|
||
PpvUtilFailDriver(
|
||
PPVERROR_DDI_REQUIRES_PDO,
|
||
(PVOID) _ReturnAddress(),
|
||
DeviceObject,
|
||
NULL
|
||
);
|
||
}
|
||
|
||
return STATUS_INVALID_DEVICE_REQUEST;
|
||
}
|
||
|
||
deviceNode = (PDEVICE_NODE) DeviceObject->DeviceObjectExtension->DeviceNode;
|
||
|
||
//
|
||
// Map Device Property to registry value name and value type.
|
||
//
|
||
switch(DeviceProperty) {
|
||
|
||
case DevicePropertyPhysicalDeviceObjectName:
|
||
|
||
ASSERT (0 == (1 & BufferLength)); // had better be an even length
|
||
//
|
||
// Create a buffer for the Obj manager.
|
||
//
|
||
length = BufferLength + sizeof (OBJECT_NAME_INFORMATION);
|
||
deviceObjectName = (POBJECT_NAME_INFORMATION)ExAllocatePool(
|
||
PagedPool,
|
||
length);
|
||
if (NULL == deviceObjectName) {
|
||
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
status = ObQueryNameString (DeviceObject,
|
||
deviceObjectName,
|
||
length,
|
||
ResultLength);
|
||
if (STATUS_INFO_LENGTH_MISMATCH == status) {
|
||
|
||
status = STATUS_BUFFER_TOO_SMALL;
|
||
}
|
||
if (NT_SUCCESS (status)) {
|
||
|
||
if (deviceObjectName->Name.Length == 0) {
|
||
//
|
||
// PDO has no NAME, probably it's been deleted
|
||
//
|
||
*ResultLength = 0;
|
||
} else {
|
||
|
||
*ResultLength = deviceObjectName->Name.Length + sizeof(UNICODE_NULL);
|
||
if (*ResultLength > BufferLength) {
|
||
status = STATUS_BUFFER_TOO_SMALL;
|
||
} else {
|
||
|
||
RtlCopyMemory(PropertyBuffer,
|
||
deviceObjectName->Name.Buffer,
|
||
deviceObjectName->Name.Length);
|
||
//
|
||
// NULL terminate.
|
||
//
|
||
*(PWCHAR)(((PUCHAR)PropertyBuffer) + deviceObjectName->Name.Length) = L'\0';
|
||
}
|
||
}
|
||
} else {
|
||
|
||
*ResultLength -= sizeof(OBJECT_NAME_INFORMATION);
|
||
}
|
||
|
||
ExFreePool (deviceObjectName);
|
||
return status;
|
||
|
||
case DevicePropertyBusTypeGuid:
|
||
|
||
status = PpBusTypeGuidGet(deviceNode->ChildBusTypeIndex, &busTypeGuid);
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
*ResultLength = sizeof(GUID);
|
||
if(*ResultLength <= BufferLength) {
|
||
|
||
RtlCopyMemory(PropertyBuffer,
|
||
&busTypeGuid,
|
||
sizeof(GUID));
|
||
} else {
|
||
|
||
status = STATUS_BUFFER_TOO_SMALL;
|
||
}
|
||
}
|
||
|
||
return status;
|
||
|
||
case DevicePropertyLegacyBusType:
|
||
|
||
if (deviceNode->ChildInterfaceType != InterfaceTypeUndefined) {
|
||
|
||
*ResultLength = sizeof(INTERFACE_TYPE);
|
||
if(*ResultLength <= BufferLength) {
|
||
|
||
*(PINTERFACE_TYPE)PropertyBuffer = deviceNode->ChildInterfaceType;
|
||
status = STATUS_SUCCESS;
|
||
} else {
|
||
|
||
status = STATUS_BUFFER_TOO_SMALL;
|
||
}
|
||
} else {
|
||
|
||
status = STATUS_OBJECT_NAME_NOT_FOUND;
|
||
}
|
||
|
||
return status;
|
||
|
||
case DevicePropertyBusNumber:
|
||
//
|
||
// Retrieve the property from the parent's devnode field.
|
||
//
|
||
if ((deviceNode->ChildBusNumber & 0x80000000) != 0x80000000) {
|
||
|
||
*ResultLength = sizeof(ULONG);
|
||
if(*ResultLength <= BufferLength) {
|
||
|
||
*(PULONG)PropertyBuffer = deviceNode->ChildBusNumber;
|
||
status = STATUS_SUCCESS;
|
||
} else {
|
||
|
||
status = STATUS_BUFFER_TOO_SMALL;
|
||
}
|
||
} else {
|
||
|
||
status = STATUS_OBJECT_NAME_NOT_FOUND;
|
||
}
|
||
|
||
return status;
|
||
|
||
case DevicePropertyEnumeratorName:
|
||
|
||
ASSERT (0 == (1 & BufferLength)); // had better be an even length
|
||
deviceInstanceName = deviceNode->InstancePath.Buffer;
|
||
//
|
||
// There should always be a string here, except for (possibly)
|
||
// HTREE\Root\0, but no one should ever be calling us with that PDO
|
||
// anyway.
|
||
//
|
||
ASSERT (deviceInstanceName);
|
||
//
|
||
// We know we're going to find a separator character (\) in the string,
|
||
// so the fact that unicode strings may not be null-terminated isn't
|
||
// a problem.
|
||
//
|
||
enumeratorNameEnd = wcschr(deviceInstanceName, OBJ_NAME_PATH_SEPARATOR);
|
||
ASSERT (enumeratorNameEnd);
|
||
//
|
||
// Compute required length, minus null terminating character.
|
||
//
|
||
length = (ULONG)((PUCHAR)enumeratorNameEnd - (PUCHAR)deviceInstanceName);
|
||
//
|
||
// Store required length in caller-supplied OUT parameter.
|
||
//
|
||
*ResultLength = length + sizeof(UNICODE_NULL);
|
||
if(*ResultLength > BufferLength) {
|
||
|
||
status = STATUS_BUFFER_TOO_SMALL;
|
||
} else {
|
||
|
||
memcpy((PUCHAR)PropertyBuffer, (PUCHAR)deviceInstanceName, length);
|
||
*(PWCHAR)((PUCHAR)PropertyBuffer + length) = UNICODE_NULL;
|
||
status = STATUS_SUCCESS;
|
||
}
|
||
|
||
return status;
|
||
|
||
case DevicePropertyAddress:
|
||
|
||
status = PipQueryDeviceCapabilities(deviceNode, &capabilities);
|
||
if (NT_SUCCESS(status) && (capabilities.Address != 0xFFFFFFFF)) {
|
||
|
||
*ResultLength = sizeof(ULONG);
|
||
if(*ResultLength <= BufferLength) {
|
||
|
||
*(PULONG)PropertyBuffer = capabilities.Address;
|
||
status = STATUS_SUCCESS;
|
||
} else {
|
||
|
||
status = STATUS_BUFFER_TOO_SMALL;
|
||
}
|
||
} else {
|
||
|
||
status = STATUS_OBJECT_NAME_NOT_FOUND;
|
||
}
|
||
|
||
return status;
|
||
|
||
case DevicePropertyRemovalPolicy:
|
||
|
||
*ResultLength = sizeof(ULONG);
|
||
if(*ResultLength <= BufferLength) {
|
||
|
||
PpHotSwapGetDevnodeRemovalPolicy(
|
||
deviceNode,
|
||
TRUE, // Include Registry Override
|
||
(PDEVICE_REMOVAL_POLICY) PropertyBuffer
|
||
);
|
||
status = STATUS_SUCCESS;
|
||
} else {
|
||
|
||
status = STATUS_BUFFER_TOO_SMALL;
|
||
}
|
||
|
||
return status;
|
||
|
||
case DevicePropertyUINumber:
|
||
|
||
valueName = REGSTR_VALUE_UI_NUMBER;
|
||
valueType = REG_DWORD;
|
||
break;
|
||
|
||
case DevicePropertyLocationInformation:
|
||
|
||
valueName = REGSTR_VALUE_LOCATION_INFORMATION;
|
||
valueType = REG_SZ;
|
||
break;
|
||
|
||
case DevicePropertyDeviceDescription:
|
||
|
||
valueName = REGSTR_VALUE_DEVICE_DESC;
|
||
valueType = REG_SZ;
|
||
break;
|
||
|
||
case DevicePropertyHardwareID:
|
||
|
||
valueName = REGSTR_VALUE_HARDWAREID;
|
||
valueType = REG_MULTI_SZ;
|
||
break;
|
||
|
||
case DevicePropertyCompatibleIDs:
|
||
|
||
valueName = REGSTR_VALUE_COMPATIBLEIDS;
|
||
valueType = REG_MULTI_SZ;
|
||
break;
|
||
|
||
case DevicePropertyBootConfiguration:
|
||
|
||
keyName = REGSTR_KEY_LOG_CONF;
|
||
valueName = REGSTR_VAL_BOOTCONFIG;
|
||
valueType = REG_RESOURCE_LIST;
|
||
break;
|
||
|
||
case DevicePropertyBootConfigurationTranslated:
|
||
|
||
return STATUS_NOT_SUPPORTED;
|
||
break;
|
||
|
||
case DevicePropertyClassName:
|
||
|
||
valueName = REGSTR_VALUE_CLASS;
|
||
valueType = REG_SZ;
|
||
break;
|
||
|
||
case DevicePropertyClassGuid:
|
||
valueName = REGSTR_VALUE_CLASSGUID;
|
||
valueType = REG_SZ;
|
||
break;
|
||
|
||
case DevicePropertyDriverKeyName:
|
||
|
||
valueName = REGSTR_VALUE_DRIVER;
|
||
valueType = REG_SZ;
|
||
break;
|
||
|
||
case DevicePropertyManufacturer:
|
||
|
||
valueName = REGSTR_VAL_MFG;
|
||
valueType = REG_SZ;
|
||
break;
|
||
|
||
case DevicePropertyFriendlyName:
|
||
|
||
valueName = REGSTR_VALUE_FRIENDLYNAME;
|
||
valueType = REG_SZ;
|
||
break;
|
||
|
||
case DevicePropertyInstallState:
|
||
|
||
if (deviceNode == IopRootDeviceNode) {
|
||
//
|
||
// The root devnode is always installed, by definition. We
|
||
// specifically set it's InstallState here because the
|
||
// CONFIGFLAG_REINSTALL flag will wunfortunately still exist on the
|
||
// root devnode reg key on a running system (we should fix that
|
||
// later).
|
||
//
|
||
deviceInstallState = InstallStateInstalled;
|
||
status = STATUS_SUCCESS;
|
||
|
||
} else {
|
||
//
|
||
// For all other devnodes, walk up the devnode tree, retrieving the
|
||
// install state of all ancestors up to (but not including) the root
|
||
// devnode. We'll stop when we've reached the top of the tree, or
|
||
// when some intermediate device has an "uninstalled" install state.
|
||
//
|
||
|
||
valueName = REGSTR_VALUE_CONFIG_FLAGS;
|
||
valueType = REG_DWORD;
|
||
|
||
do {
|
||
//
|
||
// Get the ConfigFlags registry value.
|
||
//
|
||
length = sizeof(ULONG);
|
||
status = PiGetDeviceRegistryProperty(
|
||
deviceNode->PhysicalDeviceObject,
|
||
valueType,
|
||
valueName,
|
||
keyName,
|
||
(PVOID)&configFlags,
|
||
&length
|
||
);
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
//
|
||
// The install state is just a subset of the device's ConfigFlags
|
||
//
|
||
if (configFlags & CONFIGFLAG_REINSTALL) {
|
||
|
||
deviceInstallState = InstallStateNeedsReinstall;
|
||
|
||
} else if (configFlags & CONFIGFLAG_FAILEDINSTALL) {
|
||
|
||
deviceInstallState = InstallStateFailedInstall;
|
||
|
||
} else if (configFlags & CONFIGFLAG_FINISH_INSTALL) {
|
||
|
||
deviceInstallState = InstallStateFinishInstall;
|
||
} else {
|
||
|
||
deviceInstallState = InstallStateInstalled;
|
||
}
|
||
}
|
||
|
||
deviceNode = deviceNode->Parent;
|
||
|
||
} while ((NT_SUCCESS(status)) &&
|
||
(deviceInstallState == InstallStateInstalled) &&
|
||
(deviceNode != IopRootDeviceNode));
|
||
}
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
*ResultLength = sizeof(ULONG);
|
||
if(*ResultLength <= BufferLength) {
|
||
*(PDEVICE_INSTALL_STATE)PropertyBuffer = deviceInstallState;
|
||
} else {
|
||
status = STATUS_BUFFER_TOO_SMALL;
|
||
}
|
||
}
|
||
|
||
return status;
|
||
|
||
default:
|
||
|
||
return STATUS_INVALID_PARAMETER_2;
|
||
}
|
||
//
|
||
// Get the registry value.
|
||
//
|
||
*ResultLength = BufferLength;
|
||
status = PiGetDeviceRegistryProperty(
|
||
DeviceObject,
|
||
valueType,
|
||
valueName,
|
||
keyName,
|
||
PropertyBuffer,
|
||
ResultLength
|
||
);
|
||
|
||
return status;
|
||
}
|
||
|
||
NTSTATUS
|
||
PiGetDeviceRegistryProperty(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN ULONG ValueType,
|
||
IN PWSTR ValueName,
|
||
IN PWSTR KeyName,
|
||
OUT PVOID Buffer,
|
||
IN OUT PULONG BufferLength
|
||
)
|
||
{
|
||
NTSTATUS status;
|
||
HANDLE handle, subKeyHandle;
|
||
UNICODE_STRING unicodeKey;
|
||
PKEY_VALUE_FULL_INFORMATION keyValueInformation;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Enter critical section and acquire a lock on the registry. Both these
|
||
// mechanisms are required to prevent deadlock in the case where an APC
|
||
// routine calls this routine after the registry resource has been claimed
|
||
// in this case it would wait blocking this thread so the registry would
|
||
// never be released -> deadlock. Critical sectioning the registry manipulatio
|
||
// portion solves this problem
|
||
//
|
||
PiLockPnpRegistry(TRUE);
|
||
//
|
||
// Based on the PDO specified by caller, find the handle of its device
|
||
// instance registry key.
|
||
//
|
||
status = IopDeviceObjectToDeviceInstance(DeviceObject, &handle, KEY_READ);
|
||
if (NT_SUCCESS(status)) {
|
||
//
|
||
// If the data is stored in a subkey then open this key and close the old one
|
||
//
|
||
if (KeyName) {
|
||
|
||
RtlInitUnicodeString(&unicodeKey, KeyName);
|
||
status = IopOpenRegistryKeyEx( &subKeyHandle,
|
||
handle,
|
||
&unicodeKey,
|
||
KEY_READ
|
||
);
|
||
if(NT_SUCCESS(status)){
|
||
|
||
ZwClose(handle);
|
||
handle = subKeyHandle;
|
||
}
|
||
}
|
||
if (NT_SUCCESS(status)) {
|
||
//
|
||
// Read the registry value of the desired value name
|
||
//
|
||
status = IopGetRegistryValue (handle,
|
||
ValueName,
|
||
&keyValueInformation);
|
||
}
|
||
//
|
||
// We have finished using the registry so clean up and release our resources
|
||
//
|
||
ZwClose(handle);
|
||
}
|
||
PiUnlockPnpRegistry();
|
||
//
|
||
// If we have been sucessfull in finding the info hand it back to the caller
|
||
//
|
||
if (NT_SUCCESS(status)) {
|
||
//
|
||
// Check that the buffer we have been given is big enough and that the value returned is
|
||
// of the correct registry type
|
||
//
|
||
if (*BufferLength >= keyValueInformation->DataLength) {
|
||
|
||
if (keyValueInformation->Type == ValueType) {
|
||
|
||
RtlCopyMemory( Buffer,
|
||
KEY_VALUE_DATA(keyValueInformation),
|
||
keyValueInformation->DataLength);
|
||
} else {
|
||
|
||
status = STATUS_INVALID_PARAMETER_2;
|
||
}
|
||
} else {
|
||
|
||
status = STATUS_BUFFER_TOO_SMALL;
|
||
}
|
||
*BufferLength = keyValueInformation->DataLength;
|
||
ExFreePool(keyValueInformation);
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
NTSTATUS
|
||
IoOpenDeviceRegistryKey(
|
||
IN PDEVICE_OBJECT PhysicalDeviceObject,
|
||
IN ULONG DevInstKeyType,
|
||
IN ACCESS_MASK DesiredAccess,
|
||
OUT PHANDLE DevInstRegKey
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine returns a handle to an opened registry key that the driver
|
||
may use to store/retrieve configuration information specific to a particular
|
||
device instance.
|
||
|
||
The driver must call ZwClose to close the handle returned from this api
|
||
when access is no longer required.
|
||
|
||
Parameters:
|
||
|
||
DeviceObject - Supples the device object of the physical device instance to
|
||
open a registry storage key for. Normally it is a device object
|
||
created by the hal bus extender.
|
||
|
||
DevInstKeyType - Supplies flags specifying which storage key associated with
|
||
the device instance is to be opened. May be a combination of
|
||
the following value:
|
||
|
||
PLUGPLAY_REGKEY_DEVICE - Open a key for storing device specific
|
||
(driver-independent) information relating to the device instance.
|
||
The flag may not be specified with PLUGPLAY_REGKEY_DRIVER.
|
||
|
||
PLUGPLAY_REGKEY_DRIVER - Open a key for storing driver-specific
|
||
information relating to the device instance, This flag may
|
||
not be specified with PLUGPLAY_REGKEY_DEVICE.
|
||
|
||
PLUGPLAY_REGKEY_CURRENT_HWPROFILE - If this flag is specified,
|
||
then a key in the current hardware profile branch will be
|
||
opened for the specified storage type. This allows the driver
|
||
to access configuration information that is hardware profile
|
||
specific.
|
||
|
||
DesiredAccess - Specifies the access mask for the key to be opened.
|
||
|
||
DevInstRegKey - Supplies the address of a variable that receives a handle to the
|
||
opened key for the specified registry storage location.
|
||
|
||
Return Value:
|
||
|
||
Status code that indicates whether or not the function was successful.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
NTSTATUS status, appendStatus;
|
||
HANDLE hBasePath;
|
||
UNICODE_STRING unicodeBasePath, unicodeRestPath;
|
||
WCHAR drvInst[GUID_STRING_LEN + 5];
|
||
ULONG drvInstLength;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Until SCSIPORT stops passing non PDOs allow the system to boot.
|
||
//
|
||
// ASSERT_PDO(PhysicalDeviceObject);
|
||
//
|
||
|
||
//
|
||
// Initialise out parameters
|
||
//
|
||
|
||
*DevInstRegKey = NULL;
|
||
|
||
//
|
||
// Allocate a buffer to build the RestPath string in
|
||
//
|
||
|
||
unicodeRestPath.Buffer = ExAllocatePool(PagedPool | POOL_COLD_ALLOCATION, MAX_RESTPATH_BUF_LEN);
|
||
|
||
if (unicodeRestPath.Buffer == NULL) {
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto clean0;
|
||
}
|
||
|
||
unicodeRestPath.Length=0;
|
||
unicodeRestPath.MaximumLength=MAX_RESTPATH_BUF_LEN;
|
||
|
||
//
|
||
// Select the base path to the CurrentControlSet based on if we are dealing with
|
||
// a hardware profile or not
|
||
//
|
||
|
||
if (DevInstKeyType & PLUGPLAY_REGKEY_CURRENT_HWPROFILE) {
|
||
PiWstrToUnicodeString(&unicodeBasePath, PATH_CURRENTCONTROLSET_HW_PROFILE_CURRENT);
|
||
|
||
} else {
|
||
PiWstrToUnicodeString(&unicodeBasePath, PATH_CURRENTCONTROLSET);
|
||
}
|
||
|
||
//
|
||
// Enter critical section and acquire a lock on the registry. Both these
|
||
// mechanisms are required to prevent deadlock in the case where an APC
|
||
// routine calls this routine after the registry resource has been claimed
|
||
// in this case it would wait blocking this thread so the registry would
|
||
// never be released -> deadlock. Critical sectioning the registry manipulation
|
||
// portion solves this problem
|
||
//
|
||
PiLockPnpRegistry(TRUE);
|
||
|
||
//
|
||
// Open the base registry key
|
||
//
|
||
|
||
status = IopOpenRegistryKeyEx( &hBasePath,
|
||
NULL,
|
||
&unicodeBasePath,
|
||
KEY_READ
|
||
);
|
||
|
||
if(!NT_SUCCESS(status)) {
|
||
goto clean1;
|
||
}
|
||
|
||
//
|
||
// Build the RestPath string
|
||
//
|
||
|
||
switch (DevInstKeyType) {
|
||
|
||
case PLUGPLAY_REGKEY_DEVICE:
|
||
case PLUGPLAY_REGKEY_DEVICE + PLUGPLAY_REGKEY_CURRENT_HWPROFILE:
|
||
{
|
||
PDEVICE_NODE pDeviceNode;
|
||
|
||
//
|
||
// Initialise the rest path with Enum\
|
||
//
|
||
|
||
appendStatus = RtlAppendUnicodeToString(&unicodeRestPath, PATH_ENUM);
|
||
ASSERT(NT_SUCCESS( appendStatus ));
|
||
//
|
||
// Get the Enumerator\DeviceID\InstanceID path from the DeviceNode
|
||
//
|
||
|
||
pDeviceNode = (PDEVICE_NODE) PhysicalDeviceObject->DeviceObjectExtension->DeviceNode;
|
||
|
||
//
|
||
// Ensure this is a PDO and not an FDO (only PDO's have a DeviceNode)
|
||
//
|
||
|
||
if (pDeviceNode) {
|
||
appendStatus = RtlAppendUnicodeStringToString(&unicodeRestPath, &(pDeviceNode->InstancePath));
|
||
ASSERT(NT_SUCCESS( appendStatus ));
|
||
} else {
|
||
status = STATUS_INVALID_DEVICE_REQUEST;
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
case PLUGPLAY_REGKEY_DRIVER:
|
||
case PLUGPLAY_REGKEY_DRIVER + PLUGPLAY_REGKEY_CURRENT_HWPROFILE:
|
||
{
|
||
|
||
HANDLE hDeviceKey;
|
||
|
||
//
|
||
// Initialise the rest path with Control\Class\
|
||
//
|
||
|
||
appendStatus = RtlAppendUnicodeToString(&unicodeRestPath, PATH_CONTROL_CLASS);
|
||
ASSERT(NT_SUCCESS( appendStatus ));
|
||
|
||
//
|
||
// Open the device instance key for this device
|
||
//
|
||
|
||
status = IopDeviceObjectToDeviceInstance(PhysicalDeviceObject, &hDeviceKey, KEY_READ);
|
||
|
||
if(!NT_SUCCESS(status)){
|
||
goto clean1;
|
||
}
|
||
|
||
//
|
||
// See if we have a driver value
|
||
//
|
||
|
||
status = IoGetDeviceProperty(PhysicalDeviceObject, DevicePropertyDriverKeyName, sizeof(drvInst), drvInst, &drvInstLength);
|
||
if(NT_SUCCESS(status)){
|
||
//
|
||
// Append <DevInstClass>\<ClassInstanceOrdinal>
|
||
//
|
||
appendStatus = RtlAppendUnicodeToString(&unicodeRestPath, drvInst);
|
||
ASSERT(NT_SUCCESS( appendStatus ));
|
||
}
|
||
|
||
ZwClose(hDeviceKey);
|
||
|
||
break;
|
||
}
|
||
default:
|
||
|
||
//
|
||
// ISSUE 2001/02/08 ADRIAO - This is parameter #2, not parameter #3!
|
||
//
|
||
status = STATUS_INVALID_PARAMETER_3;
|
||
goto clean2;
|
||
}
|
||
|
||
|
||
//
|
||
// If we succeeded in building the rest path then open the key and hand it back to the caller
|
||
//
|
||
|
||
if (NT_SUCCESS(status)){
|
||
if (DevInstKeyType == PLUGPLAY_REGKEY_DEVICE) {
|
||
|
||
status = IopOpenDeviceParametersSubkey(DevInstRegKey,
|
||
hBasePath,
|
||
&unicodeRestPath,
|
||
DesiredAccess);
|
||
} else {
|
||
|
||
status = IopCreateRegistryKeyEx( DevInstRegKey,
|
||
hBasePath,
|
||
&unicodeRestPath,
|
||
DesiredAccess,
|
||
REG_OPTION_NON_VOLATILE,
|
||
NULL
|
||
);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Free up resources
|
||
//
|
||
|
||
clean2:
|
||
ZwClose(hBasePath);
|
||
clean1:
|
||
PiUnlockPnpRegistry();
|
||
ExFreePool(unicodeRestPath.Buffer);
|
||
clean0:
|
||
return status;
|
||
|
||
}
|
||
|
||
NTSTATUS
|
||
IoSynchronousInvalidateDeviceRelations(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN DEVICE_RELATION_TYPE Type
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This API notifies the system that changes have occurred in the device
|
||
relations of the specified type for the supplied DeviceObject. All
|
||
cached information concerning the relationships must be invalidated,
|
||
and if needed re-obtained via IRP_MN_QUERY_DEVICE_RELATIONS.
|
||
|
||
This routine performs device enumeration synchronously.
|
||
Note, A driver can NOT call this IO api while processing pnp irps AND
|
||
A driver can NOT call this api from any system thread except the system
|
||
threads created by the driver itself.
|
||
|
||
Parameters:
|
||
|
||
DeviceObject - the PDEVICE_OBJECT for which the specified relation type
|
||
information has been invalidated. This pointer is valid
|
||
for the duration of the call.
|
||
|
||
Type - specifies the type of the relation being invalidated.
|
||
|
||
ReturnValue:
|
||
|
||
Status code that indicates whether or not the function was successful.
|
||
|
||
--*/
|
||
|
||
{
|
||
PDEVICE_NODE deviceNode;
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
KEVENT completionEvent;
|
||
|
||
PAGED_CODE();
|
||
|
||
ASSERT_PDO(DeviceObject);
|
||
|
||
switch (Type) {
|
||
case BusRelations:
|
||
|
||
if (PnPInitialized) {
|
||
|
||
deviceNode = (PDEVICE_NODE)DeviceObject->DeviceObjectExtension->DeviceNode;
|
||
|
||
if (deviceNode->State == DeviceNodeStarted) {
|
||
|
||
KeInitializeEvent( &completionEvent, NotificationEvent, FALSE );
|
||
|
||
status = PipRequestDeviceAction( DeviceObject,
|
||
ReenumerateDeviceTree,
|
||
FALSE,
|
||
0,
|
||
&completionEvent,
|
||
NULL );
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
status = KeWaitForSingleObject( &completionEvent,
|
||
Executive,
|
||
KernelMode,
|
||
FALSE,
|
||
NULL);
|
||
}
|
||
} else {
|
||
status = STATUS_UNSUCCESSFUL;
|
||
}
|
||
} else {
|
||
status = STATUS_UNSUCCESSFUL;
|
||
}
|
||
break;
|
||
|
||
case EjectionRelations:
|
||
|
||
//
|
||
// For Ejection relation change, we will ignore it. We don't keep track
|
||
// the Ejection relation. We will query the Ejection relation only when
|
||
// we are requested to eject a device.
|
||
//
|
||
|
||
status = STATUS_NOT_SUPPORTED;
|
||
break;
|
||
|
||
case PowerRelations:
|
||
|
||
|
||
//
|
||
// Call off to Po code, which will do the right thing
|
||
//
|
||
PoInvalidateDevicePowerRelations(DeviceObject);
|
||
break;
|
||
}
|
||
return status;
|
||
}
|
||
|
||
VOID
|
||
IoInvalidateDeviceRelations(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN DEVICE_RELATION_TYPE Type
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This API notifies the system that changes have occurred in the device
|
||
relations of the specified type for the supplied DeviceObject. All
|
||
cached information concerning the relationships must be invalidated,
|
||
and if needed re-obtained via IRP_MN_QUERY_DEVICE_RELATIONS.
|
||
|
||
Parameters:
|
||
|
||
DeviceObject - the PDEVICE_OBJECT for which the specified relation type
|
||
information has been invalidated. This pointer is valid
|
||
for the duration of the call.
|
||
|
||
Type - specifies the type of the relation being invalidated.
|
||
|
||
ReturnValue:
|
||
|
||
none.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PDEVICE_NODE deviceNode;
|
||
KIRQL oldIrql;
|
||
|
||
ASSERT_PDO(DeviceObject);
|
||
|
||
switch (Type) {
|
||
case BusRelations:
|
||
case SingleBusRelations:
|
||
|
||
//
|
||
// If the call was made before PnP completes device enumeration
|
||
// we can safely ignore it. PnP manager will do it without
|
||
// driver's request.
|
||
//
|
||
|
||
deviceNode = (PDEVICE_NODE) DeviceObject->DeviceObjectExtension->DeviceNode;
|
||
if (deviceNode) {
|
||
|
||
PipRequestDeviceAction( DeviceObject,
|
||
Type == BusRelations ?
|
||
ReenumerateDeviceTree : ReenumerateDeviceOnly,
|
||
FALSE,
|
||
0,
|
||
NULL,
|
||
NULL );
|
||
}
|
||
break;
|
||
|
||
case EjectionRelations:
|
||
|
||
//
|
||
// For Ejection relation change, we will ignore it. We don't keep track
|
||
// the Ejection relation. We will query the Ejection relation only when
|
||
// we are requested to eject a device.
|
||
|
||
break;
|
||
|
||
case PowerRelations:
|
||
|
||
//
|
||
// Call off to Po code, which will do the right thing
|
||
//
|
||
PoInvalidateDevicePowerRelations(DeviceObject);
|
||
break;
|
||
}
|
||
}
|
||
|
||
VOID
|
||
IoRequestDeviceEject(
|
||
IN PDEVICE_OBJECT DeviceObject
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This API notifies that the device eject button has been pressed. This API must
|
||
be called at IRQL <= DISPATCH_LEVEL.
|
||
|
||
This API informs PnP that a device eject has been requested, the device will
|
||
not necessarily be ejected as a result of this API. The device will only be
|
||
ejected if the drivers associated with it agree to stop and the device is
|
||
successfully powered down. Note that eject in this context refers to device
|
||
eject, not to media (floppies, cds, tapes) eject. For example, eject of a
|
||
cd-rom disk drive, not ejection of a cd-rom disk.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - the PDEVICE_OBJECT for the device whose eject button has
|
||
been pressed. This pointer is valid for the duration of
|
||
the call, if the API wants to keep a copy of it, it
|
||
should obtain its own reference to the object
|
||
(ObReferenceObject).
|
||
|
||
ReturnValue:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
ASSERT_PDO(DeviceObject);
|
||
|
||
IopQueueDeviceWorkItem(DeviceObject, IopRequestDeviceEjectWorker, NULL);
|
||
}
|
||
|
||
VOID
|
||
IopRequestDeviceEjectWorker(
|
||
IN PVOID Context
|
||
)
|
||
{
|
||
PDEVICE_WORK_ITEM deviceWorkItem = (PDEVICE_WORK_ITEM)Context;
|
||
PDEVICE_OBJECT deviceObject = deviceWorkItem->DeviceObject;
|
||
|
||
ExFreePool(deviceWorkItem);
|
||
|
||
//
|
||
// Queue the event, we'll return immediately after it's queued.
|
||
//
|
||
|
||
PpSetTargetDeviceRemove( deviceObject,
|
||
TRUE,
|
||
TRUE,
|
||
TRUE,
|
||
CM_PROB_HELD_FOR_EJECT,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
NULL);
|
||
|
||
ObDereferenceObject(deviceObject);
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
IoReportDetectedDevice(
|
||
IN PDRIVER_OBJECT DriverObject,
|
||
IN INTERFACE_TYPE LegacyBusType,
|
||
IN ULONG BusNumber,
|
||
IN ULONG SlotNumber,
|
||
IN PCM_RESOURCE_LIST ResourceList,
|
||
IN PIO_RESOURCE_REQUIREMENTS_LIST ResourceRequirements OPTIONAL,
|
||
IN BOOLEAN ResourceAssigned,
|
||
IN OUT PDEVICE_OBJECT *DeviceObject
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
PnP device drivers call this API to report any device detected. This routine
|
||
creates a Physical Device object, reference the Physical Device object and
|
||
returns back to the callers. Once the detected device is reported, the Pnp manager
|
||
considers the device has been fully controlled by the reporting drivers. Thus it
|
||
will not invoke AddDevice entry and send StartDevice irp to the driver.
|
||
|
||
The driver needs to report the resources it used to detect this device such that
|
||
pnp manager can perform duplicates detection on this device.
|
||
|
||
The caller must dereference the DeviceObject once it no longer needs it.
|
||
|
||
Parameters:
|
||
|
||
DriverObject - Supplies the driver object of the driver who detected
|
||
this device.
|
||
|
||
ResourceList - Supplies a pointer to the resource list which the driver used
|
||
to detect the device.
|
||
|
||
ResourceRequirements - supplies a pointer to the resource requirements list
|
||
for the detected device. This is optional.
|
||
|
||
ResourceAssigned - if TRUE, the driver already called IoReportResourceUsage or
|
||
IoAssignResource to get the ownership of the resources. Otherwise,
|
||
the PnP manager will call IoReportResourceUsage to allocate the
|
||
resources for the driver.
|
||
|
||
DeviceObject - if NULL, this routine will create a PDO and return it thru this variable.
|
||
Otherwise, a PDO is already created and this routine will simply use the supplied
|
||
PDO.
|
||
|
||
Return Value:
|
||
|
||
Status code that indicates whether or not the function was successful.
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
WCHAR buffer[MAX_DEVICE_ID_LEN];
|
||
NTSTATUS status;
|
||
UNICODE_STRING deviceName, instanceName, unicodeName, *serviceName, driverName;
|
||
PDEVICE_NODE deviceNode;
|
||
ULONG length, i = 0, disposition, tmpValue, listSize = 0;
|
||
HANDLE handle = NULL, handle1, logConfHandle = NULL, controlHandle = NULL, enumHandle;
|
||
PCM_RESOURCE_LIST cmResource;
|
||
PWSTR p;
|
||
LARGE_INTEGER tickCount;
|
||
PDEVICE_OBJECT deviceObject;
|
||
BOOLEAN newlyCreated = FALSE;
|
||
|
||
PAGED_CODE();
|
||
|
||
if (*DeviceObject) {
|
||
|
||
deviceObject = *DeviceObject;
|
||
|
||
//
|
||
// The PDO is already known. simply handle the resourcelist and resreq list.
|
||
// This is a hack for NDIS drivers.
|
||
//
|
||
deviceNode = (PDEVICE_NODE)(*DeviceObject)->DeviceObjectExtension->DeviceNode;
|
||
if (!deviceNode) {
|
||
return STATUS_NO_SUCH_DEVICE;
|
||
}
|
||
PiLockPnpRegistry(FALSE);
|
||
|
||
//
|
||
// Write ResourceList and ResReq list to the device instance
|
||
//
|
||
|
||
status = IopDeviceObjectToDeviceInstance (*DeviceObject,
|
||
&handle,
|
||
KEY_ALL_ACCESS
|
||
);
|
||
if (!NT_SUCCESS(status)) {
|
||
PiUnlockPnpRegistry();
|
||
return status;
|
||
}
|
||
if (ResourceAssigned) {
|
||
PiWstrToUnicodeString(&unicodeName, REGSTR_VALUE_NO_RESOURCE_AT_INIT);
|
||
tmpValue = 1;
|
||
ZwSetValueKey(handle,
|
||
&unicodeName,
|
||
TITLE_INDEX_VALUE,
|
||
REG_DWORD,
|
||
&tmpValue,
|
||
sizeof(tmpValue)
|
||
);
|
||
}
|
||
PiWstrToUnicodeString(&unicodeName, REGSTR_KEY_LOG_CONF);
|
||
status = IopCreateRegistryKeyEx( &logConfHandle,
|
||
handle,
|
||
&unicodeName,
|
||
KEY_ALL_ACCESS,
|
||
REG_OPTION_NON_VOLATILE,
|
||
NULL
|
||
);
|
||
ZwClose(handle);
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
//
|
||
// Write the ResourceList and and ResourceRequirements to the logconf key under
|
||
// device instance key.
|
||
//
|
||
|
||
if (ResourceList) {
|
||
PiWstrToUnicodeString(&unicodeName, REGSTR_VAL_BOOTCONFIG);
|
||
ZwSetValueKey(
|
||
logConfHandle,
|
||
&unicodeName,
|
||
TITLE_INDEX_VALUE,
|
||
REG_RESOURCE_LIST,
|
||
ResourceList,
|
||
listSize = IopDetermineResourceListSize(ResourceList)
|
||
);
|
||
}
|
||
if (ResourceRequirements) {
|
||
PiWstrToUnicodeString(&unicodeName, REGSTR_VALUE_BASIC_CONFIG_VECTOR);
|
||
ZwSetValueKey(
|
||
logConfHandle,
|
||
&unicodeName,
|
||
TITLE_INDEX_VALUE,
|
||
REG_RESOURCE_REQUIREMENTS_LIST,
|
||
ResourceRequirements,
|
||
ResourceRequirements->ListSize
|
||
);
|
||
}
|
||
ZwClose(logConfHandle);
|
||
}
|
||
PiUnlockPnpRegistry();
|
||
if (NT_SUCCESS(status)) {
|
||
goto checkResource;
|
||
} else {
|
||
return status;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Normal case: *DeviceObject is NULL
|
||
//
|
||
|
||
*DeviceObject = NULL;
|
||
serviceName = &DriverObject->DriverExtension->ServiceKeyName;
|
||
|
||
//
|
||
// Special handling for driver object created thru IoCreateDriver.
|
||
// When a builtin driver calls IoReportDetectedDevice, the ServiceKeyName of
|
||
// the driver object is set to \Driver\DriverName. To create a detected device
|
||
// instance key, we will take only the DriverName.
|
||
//
|
||
|
||
if (DriverObject->Flags & DRVO_BUILTIN_DRIVER) {
|
||
p = serviceName->Buffer + (serviceName->Length / sizeof(WCHAR)) - 1;
|
||
driverName.Length = 0;
|
||
while (*p != '\\' && (p != serviceName->Buffer)) {
|
||
p--;
|
||
driverName.Length += sizeof(WCHAR);
|
||
}
|
||
if (p == serviceName->Buffer) {
|
||
return STATUS_UNSUCCESSFUL;
|
||
} else {
|
||
p++;
|
||
driverName.Buffer = p;
|
||
driverName.MaximumLength = driverName.Length + sizeof(WCHAR);
|
||
}
|
||
} else {
|
||
|
||
//
|
||
// Before doing anything first perform duplicate detection
|
||
//
|
||
|
||
status = IopDuplicateDetection( LegacyBusType,
|
||
BusNumber,
|
||
SlotNumber,
|
||
&deviceNode
|
||
);
|
||
|
||
if (NT_SUCCESS(status) && deviceNode) {
|
||
|
||
deviceObject = deviceNode->PhysicalDeviceObject;
|
||
|
||
if (PipAreDriversLoaded(deviceNode) ||
|
||
(PipDoesDevNodeHaveProblem(deviceNode) &&
|
||
deviceNode->Problem != CM_PROB_NOT_CONFIGURED &&
|
||
deviceNode->Problem != CM_PROB_REINSTALL &&
|
||
deviceNode->Problem != CM_PROB_FAILED_INSTALL)) {
|
||
|
||
ObDereferenceObject(deviceObject);
|
||
|
||
return STATUS_NO_SUCH_DEVICE;
|
||
}
|
||
|
||
deviceNode->Flags &= ~DNF_HAS_PROBLEM;
|
||
deviceNode->Problem = 0;
|
||
|
||
IopDeleteLegacyKey(DriverObject);
|
||
goto checkResource;
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Create a PDO
|
||
//
|
||
|
||
status = IoCreateDevice( IoPnpDriverObject,
|
||
sizeof(IOPNP_DEVICE_EXTENSION),
|
||
NULL,
|
||
FILE_DEVICE_CONTROLLER,
|
||
FILE_AUTOGENERATED_DEVICE_NAME,
|
||
FALSE,
|
||
&deviceObject );
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
deviceObject->Flags |= DO_BUS_ENUMERATED_DEVICE; // Mark this is a PDO
|
||
status = PipAllocateDeviceNode(deviceObject, &deviceNode);
|
||
if (status != STATUS_SYSTEM_HIVE_TOO_LARGE && deviceNode) {
|
||
|
||
//
|
||
// First delete the Legacy_DriverName key and subkeys from Enum\Root, if exits.
|
||
//
|
||
|
||
if (!(DriverObject->Flags & DRVO_BUILTIN_DRIVER)) {
|
||
IopDeleteLegacyKey(DriverObject);
|
||
}
|
||
|
||
//
|
||
// Create the compatible id list we'll use for this made-up device.
|
||
//
|
||
|
||
status = PpCreateLegacyDeviceIds(
|
||
deviceObject,
|
||
((DriverObject->Flags & DRVO_BUILTIN_DRIVER) ?
|
||
&driverName : serviceName),
|
||
ResourceList);
|
||
|
||
PiLockPnpRegistry(FALSE);
|
||
if(!NT_SUCCESS(status)) {
|
||
goto exit;
|
||
}
|
||
|
||
//
|
||
// Create/Open a registry key for the device instance and
|
||
// write the addr of the device object to registry
|
||
//
|
||
|
||
if (DriverObject->Flags & DRVO_BUILTIN_DRIVER) {
|
||
length = _snwprintf(buffer, sizeof(buffer) / sizeof(WCHAR), L"ROOT\\%s", driverName.Buffer);
|
||
} else {
|
||
length = _snwprintf(buffer, sizeof(buffer) / sizeof(WCHAR), L"ROOT\\%s", serviceName->Buffer);
|
||
}
|
||
length *= sizeof(WCHAR);
|
||
deviceName.MaximumLength = sizeof(buffer);
|
||
ASSERT(length <= sizeof(buffer) - 10);
|
||
deviceName.Length = (USHORT)length;
|
||
deviceName.Buffer = buffer;
|
||
|
||
status = IopOpenRegistryKeyEx( &enumHandle,
|
||
NULL,
|
||
&CmRegistryMachineSystemCurrentControlSetEnumName,
|
||
KEY_ALL_ACCESS
|
||
);
|
||
if (!NT_SUCCESS(status)) {
|
||
goto exit;
|
||
}
|
||
|
||
status = IopCreateRegistryKeyEx( &handle1,
|
||
enumHandle,
|
||
&deviceName,
|
||
KEY_ALL_ACCESS,
|
||
REG_OPTION_NON_VOLATILE,
|
||
&disposition
|
||
);
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
deviceName.Buffer[deviceName.Length / sizeof(WCHAR)] =
|
||
OBJ_NAME_PATH_SEPARATOR;
|
||
deviceName.Length += sizeof(UNICODE_NULL);
|
||
length += sizeof(UNICODE_NULL);
|
||
if (disposition != REG_CREATED_NEW_KEY) {
|
||
|
||
for ( ; ; ) {
|
||
|
||
deviceName.Length = (USHORT)length;
|
||
PiUlongToInstanceKeyUnicodeString(&instanceName,
|
||
buffer + deviceName.Length / sizeof(WCHAR),
|
||
sizeof(buffer) - deviceName.Length,
|
||
i
|
||
);
|
||
deviceName.Length += instanceName.Length;
|
||
status = IopCreateRegistryKeyEx( &handle,
|
||
handle1,
|
||
&instanceName,
|
||
KEY_ALL_ACCESS,
|
||
REG_OPTION_NON_VOLATILE,
|
||
&disposition
|
||
);
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
if (disposition == REG_CREATED_NEW_KEY) {
|
||
ZwClose(handle1);
|
||
break;
|
||
} else {
|
||
PKEY_VALUE_FULL_INFORMATION keyValueInformation = NULL;
|
||
BOOLEAN migratedKey = FALSE, matchingKey = FALSE;
|
||
|
||
//
|
||
// Check if the key exists because it was
|
||
// explicitly migrated during textmode setup.
|
||
//
|
||
status = IopGetRegistryValue(handle,
|
||
REGSTR_VALUE_MIGRATED,
|
||
&keyValueInformation);
|
||
if (NT_SUCCESS(status)) {
|
||
if ((keyValueInformation->Type == REG_DWORD) &&
|
||
(keyValueInformation->DataLength == sizeof(ULONG)) &&
|
||
((*(PULONG)KEY_VALUE_DATA(keyValueInformation)) != 0)) {
|
||
migratedKey = TRUE;
|
||
}
|
||
ExFreePool(keyValueInformation);
|
||
PiWstrToUnicodeString(&unicodeName, REGSTR_VALUE_MIGRATED);
|
||
ZwDeleteValueKey(handle, &unicodeName);
|
||
}
|
||
|
||
if (IopIsReportedAlready(handle, serviceName, ResourceList, &matchingKey)) {
|
||
|
||
ASSERT(matchingKey);
|
||
|
||
//
|
||
// Write the reported resources to registry in case the irq changed
|
||
//
|
||
|
||
PiWstrToUnicodeString(&unicodeName, REGSTR_KEY_LOG_CONF);
|
||
status = IopCreateRegistryKeyEx( &logConfHandle,
|
||
handle,
|
||
&unicodeName,
|
||
KEY_ALL_ACCESS,
|
||
REG_OPTION_NON_VOLATILE,
|
||
NULL
|
||
);
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
//
|
||
// Write the ResourceList and and ResourceRequirements to the device instance key
|
||
//
|
||
|
||
if (ResourceList) {
|
||
PiWstrToUnicodeString(&unicodeName, REGSTR_VAL_BOOTCONFIG);
|
||
ZwSetValueKey(
|
||
logConfHandle,
|
||
&unicodeName,
|
||
TITLE_INDEX_VALUE,
|
||
REG_RESOURCE_LIST,
|
||
ResourceList,
|
||
listSize = IopDetermineResourceListSize(ResourceList)
|
||
);
|
||
}
|
||
if (ResourceRequirements) {
|
||
PiWstrToUnicodeString(&unicodeName, REGSTR_VALUE_BASIC_CONFIG_VECTOR);
|
||
ZwSetValueKey(
|
||
logConfHandle,
|
||
&unicodeName,
|
||
TITLE_INDEX_VALUE,
|
||
REG_RESOURCE_REQUIREMENTS_LIST,
|
||
ResourceRequirements,
|
||
ResourceRequirements->ListSize
|
||
);
|
||
}
|
||
ZwClose(logConfHandle);
|
||
}
|
||
|
||
PiUnlockPnpRegistry();
|
||
IoDeleteDevice(deviceObject);
|
||
ZwClose(handle1);
|
||
deviceObject = IopDeviceObjectFromDeviceInstance(&deviceName); // Add a reference
|
||
ZwClose(handle);
|
||
ZwClose(enumHandle);
|
||
ASSERT(deviceObject);
|
||
if (deviceObject == NULL) {
|
||
status = STATUS_UNSUCCESSFUL;
|
||
return status;
|
||
}
|
||
deviceNode = (PDEVICE_NODE)
|
||
deviceObject->DeviceObjectExtension->DeviceNode;
|
||
|
||
if (PipAreDriversLoaded(deviceNode) ||
|
||
(PipDoesDevNodeHaveProblem(deviceNode) &&
|
||
deviceNode->Problem != CM_PROB_NOT_CONFIGURED &&
|
||
deviceNode->Problem != CM_PROB_REINSTALL &&
|
||
deviceNode->Problem != CM_PROB_FAILED_INSTALL)) {
|
||
|
||
ObDereferenceObject(deviceObject);
|
||
|
||
return STATUS_NO_SUCH_DEVICE;
|
||
}
|
||
goto checkResource;
|
||
|
||
} else if (matchingKey && migratedKey) {
|
||
//
|
||
// We opened an existing key whose Service
|
||
// and Resources match those being reported
|
||
// for this device. No device is yet
|
||
// reported as using this instance, so we'll
|
||
// use it, and treat is as a new key.
|
||
//
|
||
disposition = REG_CREATED_NEW_KEY;
|
||
ZwClose(handle1);
|
||
break;
|
||
|
||
} else {
|
||
i++;
|
||
ZwClose(handle);
|
||
continue;
|
||
}
|
||
}
|
||
} else {
|
||
ZwClose(handle1);
|
||
ZwClose(enumHandle);
|
||
goto exit;
|
||
}
|
||
}
|
||
} else {
|
||
|
||
//
|
||
// This is a new device key. So, instance is 0. Create it.
|
||
//
|
||
|
||
PiUlongToInstanceKeyUnicodeString(&instanceName,
|
||
buffer + deviceName.Length / sizeof(WCHAR),
|
||
sizeof(buffer) - deviceName.Length,
|
||
i
|
||
);
|
||
deviceName.Length += instanceName.Length;
|
||
status = IopCreateRegistryKeyEx( &handle,
|
||
handle1,
|
||
&instanceName,
|
||
KEY_ALL_ACCESS,
|
||
REG_OPTION_NON_VOLATILE,
|
||
&disposition
|
||
);
|
||
ZwClose(handle1);
|
||
if (!NT_SUCCESS(status)) {
|
||
ZwClose(enumHandle);
|
||
goto exit;
|
||
}
|
||
ASSERT(disposition == REG_CREATED_NEW_KEY);
|
||
}
|
||
} else {
|
||
ZwClose(enumHandle);
|
||
goto exit;
|
||
}
|
||
|
||
ASSERT(disposition == REG_CREATED_NEW_KEY);
|
||
newlyCreated = TRUE;
|
||
|
||
//
|
||
// Initialize new device instance registry key
|
||
//
|
||
|
||
if (ResourceAssigned) {
|
||
PiWstrToUnicodeString(&unicodeName, REGSTR_VALUE_NO_RESOURCE_AT_INIT);
|
||
tmpValue = 1;
|
||
ZwSetValueKey(handle,
|
||
&unicodeName,
|
||
TITLE_INDEX_VALUE,
|
||
REG_DWORD,
|
||
&tmpValue,
|
||
sizeof(tmpValue)
|
||
);
|
||
}
|
||
PiWstrToUnicodeString(&unicodeName, REGSTR_KEY_LOG_CONF);
|
||
logConfHandle = NULL;
|
||
status = IopCreateRegistryKeyEx( &logConfHandle,
|
||
handle,
|
||
&unicodeName,
|
||
KEY_ALL_ACCESS,
|
||
REG_OPTION_NON_VOLATILE,
|
||
NULL
|
||
);
|
||
|
||
ASSERT(status == STATUS_SUCCESS);
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
//
|
||
// Write the ResourceList and and ResourceRequirements to the logconf key under
|
||
// device instance key.
|
||
//
|
||
|
||
if (ResourceList) {
|
||
PiWstrToUnicodeString(&unicodeName, REGSTR_VAL_BOOTCONFIG);
|
||
ZwSetValueKey(
|
||
logConfHandle,
|
||
&unicodeName,
|
||
TITLE_INDEX_VALUE,
|
||
REG_RESOURCE_LIST,
|
||
ResourceList,
|
||
listSize = IopDetermineResourceListSize(ResourceList)
|
||
);
|
||
}
|
||
if (ResourceRequirements) {
|
||
PiWstrToUnicodeString(&unicodeName, REGSTR_VALUE_BASIC_CONFIG_VECTOR);
|
||
ZwSetValueKey(
|
||
logConfHandle,
|
||
&unicodeName,
|
||
TITLE_INDEX_VALUE,
|
||
REG_RESOURCE_REQUIREMENTS_LIST,
|
||
ResourceRequirements,
|
||
ResourceRequirements->ListSize
|
||
);
|
||
}
|
||
//ZwClose(logConfHandle);
|
||
}
|
||
|
||
PiWstrToUnicodeString(&unicodeName, REGSTR_VALUE_CONFIG_FLAGS);
|
||
tmpValue = CONFIGFLAG_FINISH_INSTALL;
|
||
ZwSetValueKey(handle,
|
||
&unicodeName,
|
||
TITLE_INDEX_VALUE,
|
||
REG_DWORD,
|
||
&tmpValue,
|
||
sizeof(tmpValue)
|
||
);
|
||
|
||
PiWstrToUnicodeString(&unicodeName, REGSTR_VALUE_LEGACY);
|
||
tmpValue = 0;
|
||
ZwSetValueKey(
|
||
handle,
|
||
&unicodeName,
|
||
TITLE_INDEX_VALUE,
|
||
REG_DWORD,
|
||
&tmpValue,
|
||
sizeof(ULONG)
|
||
);
|
||
|
||
PiWstrToUnicodeString(&unicodeName, REGSTR_KEY_CONTROL);
|
||
controlHandle = NULL;
|
||
IopCreateRegistryKeyEx( &controlHandle,
|
||
handle,
|
||
&unicodeName,
|
||
KEY_ALL_ACCESS,
|
||
REG_OPTION_VOLATILE,
|
||
NULL
|
||
);
|
||
|
||
ASSERT(status == STATUS_SUCCESS);
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
PiWstrToUnicodeString(&unicodeName, REGSTR_VALUE_DEVICE_REPORTED);
|
||
tmpValue = 1;
|
||
status = ZwSetValueKey(controlHandle,
|
||
&unicodeName,
|
||
TITLE_INDEX_VALUE,
|
||
REG_DWORD,
|
||
&tmpValue,
|
||
sizeof(ULONG)
|
||
);
|
||
status = ZwSetValueKey(handle,
|
||
&unicodeName,
|
||
TITLE_INDEX_VALUE,
|
||
REG_DWORD,
|
||
&tmpValue,
|
||
sizeof(ULONG)
|
||
);
|
||
|
||
//ZwClose(controlHandle);
|
||
}
|
||
|
||
ZwClose(enumHandle);
|
||
|
||
//
|
||
// Create Service value name and set it to the calling driver's service
|
||
// key name.
|
||
//
|
||
|
||
PiWstrToUnicodeString(&unicodeName, REGSTR_VALUE_SERVICE);
|
||
p = (PWSTR)ExAllocatePool(PagedPool, serviceName->Length + sizeof(UNICODE_NULL));
|
||
if (!p) {
|
||
PiUnlockPnpRegistry();
|
||
goto CleanupRegistry;
|
||
}
|
||
RtlCopyMemory(p, serviceName->Buffer, serviceName->Length);
|
||
p[serviceName->Length / sizeof (WCHAR)] = UNICODE_NULL;
|
||
ZwSetValueKey(
|
||
handle,
|
||
&unicodeName,
|
||
TITLE_INDEX_VALUE,
|
||
REG_SZ,
|
||
p,
|
||
serviceName->Length + sizeof(UNICODE_NULL)
|
||
);
|
||
if (DriverObject->Flags & DRVO_BUILTIN_DRIVER) {
|
||
deviceNode->ServiceName = *serviceName;
|
||
} else {
|
||
ExFreePool(p);
|
||
}
|
||
|
||
PiUnlockPnpRegistry();
|
||
//ZwClose(logConfHandle);
|
||
//ZwClose(controlHandle);
|
||
//ZwClose(handle);
|
||
|
||
//
|
||
// Register the device for the driver and save the device
|
||
// instance path in device node.
|
||
//
|
||
|
||
if (!(DriverObject->Flags & DRVO_BUILTIN_DRIVER)) {
|
||
PpDeviceRegistration( &deviceName,
|
||
TRUE,
|
||
&deviceNode->ServiceName
|
||
);
|
||
}
|
||
|
||
if (PipConcatenateUnicodeStrings(&deviceNode->InstancePath, &deviceName, NULL)) {
|
||
|
||
deviceNode->Flags = DNF_MADEUP | DNF_ENUMERATED;
|
||
|
||
PipSetDevNodeState(deviceNode, DeviceNodeInitialized, NULL);
|
||
|
||
PpDevNodeInsertIntoTree(IopRootDeviceNode, deviceNode);
|
||
|
||
//
|
||
// Add an entry into the table to set up a mapping between the DO
|
||
// and the instance path.
|
||
//
|
||
|
||
status = IopMapDeviceObjectToDeviceInstance(deviceObject, &deviceNode->InstancePath);
|
||
ASSERT(NT_SUCCESS(status));
|
||
|
||
//
|
||
// Add a reference to the DeviceObject for ourself
|
||
//
|
||
|
||
ObReferenceObject(deviceObject);
|
||
|
||
IopNotifySetupDeviceArrival(deviceObject, NULL, FALSE);
|
||
|
||
goto checkResource;
|
||
}
|
||
}
|
||
IoDeleteDevice(deviceObject);
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
return status;
|
||
checkResource:
|
||
|
||
|
||
//
|
||
// At this point the *DeviceObject is established. Check if we need to report resources for
|
||
// the detected device. If we failed to
|
||
//
|
||
|
||
if (ResourceAssigned) {
|
||
//ASSERT(deviceNode->ResourceList == NULL); // make sure we have not reported resources yet.
|
||
|
||
//
|
||
// If the driver specifies it already has acquired the resource. We will put a flag
|
||
// in the device instance path to not to allocate resources at boot time. The Driver
|
||
// may do detection and report it again.
|
||
//
|
||
|
||
deviceNode->Flags |= DNF_NO_RESOURCE_REQUIRED; // do not need resources for this boot.
|
||
if (ResourceList) {
|
||
|
||
//
|
||
// Write the resource list to the reported device instance key.
|
||
//
|
||
|
||
listSize = IopDetermineResourceListSize(ResourceList);
|
||
IopWriteAllocatedResourcesToRegistry (deviceNode, ResourceList, listSize);
|
||
}
|
||
} else {
|
||
BOOLEAN conflict;
|
||
|
||
if (ResourceList && ResourceList->Count && ResourceList->List[0].PartialResourceList.Count) {
|
||
if (listSize == 0) {
|
||
listSize = IopDetermineResourceListSize(ResourceList);
|
||
}
|
||
cmResource = (PCM_RESOURCE_LIST) ExAllocatePool(PagedPool, listSize);
|
||
if (cmResource) {
|
||
RtlCopyMemory(cmResource, ResourceList, listSize);
|
||
PiWstrToUnicodeString(&unicodeName, PNPMGR_STR_PNP_MANAGER);
|
||
status = IoReportResourceUsageInternal(
|
||
ArbiterRequestLegacyReported,
|
||
&unicodeName, // DriverClassName OPTIONAL,
|
||
deviceObject->DriverObject, // DriverObject,
|
||
NULL, // DriverList OPTIONAL,
|
||
0, // DriverListSize OPTIONAL,
|
||
deviceNode->PhysicalDeviceObject,
|
||
// DeviceObject OPTIONAL,
|
||
cmResource, // DeviceList OPTIONAL,
|
||
listSize, // DeviceListSize OPTIONAL,
|
||
FALSE, // OverrideConflict,
|
||
&conflict // ConflictDetected
|
||
);
|
||
ExFreePool(cmResource);
|
||
if (!NT_SUCCESS(status) || conflict) {
|
||
status = STATUS_CONFLICTING_ADDRESSES;
|
||
PipSetDevNodeProblem(deviceNode, CM_PROB_NORMAL_CONFLICT);
|
||
}
|
||
} else {
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
PipSetDevNodeProblem(deviceNode, CM_PROB_OUT_OF_MEMORY);
|
||
}
|
||
} else {
|
||
ASSERT(ResourceRequirements == NULL);
|
||
deviceNode->Flags |= DNF_NO_RESOURCE_REQUIRED; // do not need resources for this boot.
|
||
}
|
||
}
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
IopDoDeferredSetInterfaceState(deviceNode);
|
||
|
||
PipSetDevNodeState(deviceNode, DeviceNodeStartPostWork, NULL);
|
||
|
||
*DeviceObject = deviceObject;
|
||
if (newlyCreated) {
|
||
if (controlHandle) {
|
||
ZwClose(controlHandle);
|
||
}
|
||
if (logConfHandle) {
|
||
ZwClose(logConfHandle);
|
||
}
|
||
ZwClose(handle);
|
||
}
|
||
|
||
//
|
||
// Make sure we enumerate and process this device's children.
|
||
//
|
||
|
||
PipRequestDeviceAction(deviceObject, ReenumerateDeviceOnly, FALSE, 0, NULL, NULL);
|
||
|
||
return status;
|
||
|
||
}
|
||
CleanupRegistry:
|
||
IopReleaseDeviceResources(deviceNode, FALSE);
|
||
if (newlyCreated) {
|
||
IoDeleteDevice(deviceObject);
|
||
if (controlHandle) {
|
||
ZwDeleteKey(controlHandle);
|
||
}
|
||
if (logConfHandle) {
|
||
ZwDeleteKey(logConfHandle);
|
||
}
|
||
if (handle) {
|
||
ZwDeleteKey(handle);
|
||
}
|
||
}
|
||
return status;
|
||
exit:
|
||
PiUnlockPnpRegistry();
|
||
IoDeleteDevice(*DeviceObject);
|
||
return status;
|
||
}
|
||
|
||
BOOLEAN
|
||
IopIsReportedAlready(
|
||
IN HANDLE Handle,
|
||
IN PUNICODE_STRING ServiceName,
|
||
IN PCM_RESOURCE_LIST ResourceList,
|
||
IN PBOOLEAN MatchingKey
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine determines if the reported device instance is already reported
|
||
or not.
|
||
|
||
Parameters:
|
||
|
||
Handle - Supplies a handle to the reported device instance key.
|
||
|
||
ServiceName - supplies a pointer to the unicode service key name.
|
||
|
||
ResourceList - supplies a pointer to the reported Resource list.
|
||
|
||
MatchingKey - supplies a pointer to a variable to receive whether the
|
||
ServiceName and ResourceList properties for this key match those
|
||
reported.
|
||
|
||
Return Value:
|
||
|
||
Status code that indicates whether or not the function was successful.
|
||
|
||
--*/
|
||
|
||
{
|
||
PKEY_VALUE_FULL_INFORMATION keyValueInfo1 = NULL, keyValueInfo2 = NULL;
|
||
NTSTATUS status;
|
||
UNICODE_STRING unicodeName;
|
||
HANDLE logConfHandle, controlHandle = NULL;
|
||
BOOLEAN returnValue = FALSE;
|
||
PCM_RESOURCE_LIST cmResource = NULL;
|
||
ULONG tmpValue;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Assume no match unless we determine otherwise.
|
||
//
|
||
*MatchingKey = FALSE;
|
||
|
||
//
|
||
// Check if "Service" value matches what the caller passed in.
|
||
//
|
||
|
||
status = IopGetRegistryValue(Handle, REGSTR_VALUE_SERVICE, &keyValueInfo1);
|
||
if (NT_SUCCESS(status)) {
|
||
if ((keyValueInfo1->Type == REG_SZ) &&
|
||
(keyValueInfo1->DataLength != 0)) {
|
||
unicodeName.Buffer = (PWSTR)KEY_VALUE_DATA(keyValueInfo1);
|
||
unicodeName.MaximumLength = unicodeName.Length = (USHORT)keyValueInfo1->DataLength;
|
||
if (unicodeName.Buffer[(keyValueInfo1->DataLength / sizeof(WCHAR)) - 1] == UNICODE_NULL) {
|
||
unicodeName.Length -= sizeof(WCHAR);
|
||
}
|
||
if (RtlEqualUnicodeString(ServiceName, &unicodeName, TRUE)) {
|
||
|
||
//
|
||
// Next check if resources are the same
|
||
//
|
||
|
||
PiWstrToUnicodeString(&unicodeName, REGSTR_KEY_LOG_CONF);
|
||
status = IopOpenRegistryKeyEx( &logConfHandle,
|
||
Handle,
|
||
&unicodeName,
|
||
KEY_READ
|
||
);
|
||
if (NT_SUCCESS(status)) {
|
||
status = IopGetRegistryValue(logConfHandle,
|
||
REGSTR_VAL_BOOTCONFIG,
|
||
&keyValueInfo2);
|
||
ZwClose(logConfHandle);
|
||
if (NT_SUCCESS(status)) {
|
||
if ((keyValueInfo2->Type == REG_RESOURCE_LIST) &&
|
||
(keyValueInfo2->DataLength != 0)) {
|
||
cmResource = (PCM_RESOURCE_LIST)KEY_VALUE_DATA(keyValueInfo2);
|
||
if (ResourceList && cmResource &&
|
||
PipIsDuplicatedDevices(ResourceList, cmResource, NULL, NULL)) {
|
||
*MatchingKey = TRUE;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if (!ResourceList && !cmResource) {
|
||
*MatchingKey = TRUE;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// If this registry key is for a device reported during the same boot
|
||
// this is not a duplicate.
|
||
//
|
||
|
||
PiWstrToUnicodeString(&unicodeName, REGSTR_KEY_CONTROL);
|
||
status = IopOpenRegistryKeyEx( &controlHandle,
|
||
Handle,
|
||
&unicodeName,
|
||
KEY_ALL_ACCESS
|
||
);
|
||
if (NT_SUCCESS(status)) {
|
||
status = IopGetRegistryValue(controlHandle,
|
||
REGSTR_VALUE_DEVICE_REPORTED,
|
||
&keyValueInfo1);
|
||
if (NT_SUCCESS(status)) {
|
||
goto exit;
|
||
}
|
||
|
||
if (*MatchingKey == TRUE) {
|
||
|
||
returnValue = TRUE;
|
||
|
||
//
|
||
// Mark this key has been used.
|
||
//
|
||
|
||
PiWstrToUnicodeString(&unicodeName, REGSTR_VALUE_DEVICE_REPORTED);
|
||
tmpValue = 1;
|
||
status = ZwSetValueKey(controlHandle,
|
||
&unicodeName,
|
||
TITLE_INDEX_VALUE,
|
||
REG_DWORD,
|
||
&tmpValue,
|
||
sizeof(ULONG)
|
||
);
|
||
if (!NT_SUCCESS(status)) {
|
||
returnValue = FALSE;
|
||
}
|
||
}
|
||
}
|
||
|
||
exit:
|
||
if (controlHandle) {
|
||
ZwClose(controlHandle);
|
||
}
|
||
|
||
if (keyValueInfo1) {
|
||
ExFreePool(keyValueInfo1);
|
||
}
|
||
if (keyValueInfo2) {
|
||
ExFreePool(keyValueInfo2);
|
||
}
|
||
return returnValue;
|
||
}
|
||
|
||
|
||
|
||
|
||
VOID
|
||
IoInvalidateDeviceState(
|
||
IN PDEVICE_OBJECT PhysicalDeviceObject
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This API will cause the PnP manager to send the specified PDO an IRP_MN_QUERY_PNP_DEVICE_STATE
|
||
IRP.
|
||
|
||
Parameters:
|
||
|
||
PhysicalDeviceObject - Provides a pointer to the PDO who's state is to be invalidated.
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
--*/
|
||
{
|
||
PDEVICE_NODE deviceNode;
|
||
|
||
ASSERT_PDO(PhysicalDeviceObject);
|
||
|
||
//
|
||
// If the call was made before PnP completes device enumeration
|
||
// we can safely ignore it. PnP manager will do it without
|
||
// driver's request. If the device was already removed or surprised
|
||
// removed then ignore it as well since this is only valid for started
|
||
// devices.
|
||
//
|
||
|
||
deviceNode = (PDEVICE_NODE)PhysicalDeviceObject->DeviceObjectExtension->DeviceNode;
|
||
|
||
if (deviceNode->State != DeviceNodeStarted) {
|
||
return;
|
||
}
|
||
|
||
PipRequestDeviceAction( PhysicalDeviceObject,
|
||
RequeryDeviceState,
|
||
FALSE,
|
||
0,
|
||
NULL,
|
||
NULL);
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
IopQueueDeviceWorkItem(
|
||
IN PDEVICE_OBJECT PhysicalDeviceObject,
|
||
IN VOID (*WorkerRoutine)(PVOID),
|
||
IN PVOID Context
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This API will cause the PnP manager to send the specified PDO an
|
||
IRP_MN_QUERY_PNP_DEVICE_STATE IRP.
|
||
|
||
Parameters:
|
||
|
||
PhysicalDeviceObject - Provides a pointer to the PDO who's state is to be
|
||
invalidated.
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
--*/
|
||
|
||
{
|
||
PDEVICE_WORK_ITEM deviceWorkItem;
|
||
|
||
//
|
||
// Since this routine can be called at DPC level we need to queue
|
||
// a work item and process it when the irql drops.
|
||
//
|
||
|
||
deviceWorkItem = ExAllocatePool(NonPagedPool, sizeof(DEVICE_WORK_ITEM));
|
||
if (deviceWorkItem == NULL) {
|
||
|
||
//
|
||
// Failed to allocate memory for work item. Nothing we can do ...
|
||
//
|
||
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
ObReferenceObject(PhysicalDeviceObject);
|
||
deviceWorkItem->DeviceObject = PhysicalDeviceObject;
|
||
deviceWorkItem->Context = Context;
|
||
|
||
ExInitializeWorkItem( &deviceWorkItem->WorkItem,
|
||
WorkerRoutine,
|
||
deviceWorkItem);
|
||
|
||
//
|
||
// Queue a work item to do the enumeration
|
||
//
|
||
|
||
ExQueueWorkItem( &deviceWorkItem->WorkItem, DelayedWorkQueue );
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// Private routines
|
||
//
|
||
VOID
|
||
IopResourceRequirementsChanged(
|
||
IN PDEVICE_OBJECT PhysicalDeviceObject,
|
||
IN BOOLEAN StopRequired
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine handles request of device resource requirements list change.
|
||
|
||
Parameters:
|
||
|
||
PhysicalDeviceObject - Provides a pointer to the PDO who's state is to be invalidated.
|
||
|
||
StopRequired - Supplies a BOOLEAN value to indicate if the resources reallocation needs
|
||
to be done after device stopped.
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
--*/
|
||
|
||
{
|
||
PDEVICE_NODE deviceNode;
|
||
PDEVICE_OBJECT device = NULL;
|
||
|
||
PAGED_CODE();
|
||
|
||
PipRequestDeviceAction( PhysicalDeviceObject,
|
||
ResourceRequirementsChanged,
|
||
FALSE,
|
||
StopRequired,
|
||
NULL,
|
||
NULL );
|
||
}
|
||
|
||
BOOLEAN
|
||
IoIsWdmVersionAvailable(
|
||
IN UCHAR MajorVersion,
|
||
IN UCHAR MinorVersion
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine reports whether WDM functionality is available that
|
||
is greater than or equal to the specified major and minor version.
|
||
|
||
Parameters:
|
||
|
||
MajorVersion - Supplies the WDM major version that is required.
|
||
|
||
MinorVersion - Supplies the WDM minor version that is required.
|
||
|
||
Return Value:
|
||
|
||
If WDM support is available at _at least_ the requested level, the
|
||
return value is TRUE, otherwise it is FALSE.
|
||
|
||
--*/
|
||
|
||
{
|
||
return ((MajorVersion < WDM_MAJORVERSION) ||
|
||
((MajorVersion == WDM_MAJORVERSION) && (MinorVersion <= WDM_MINORVERSION)));
|
||
}
|
||
|
||
NTKERNELAPI
|
||
PDMA_ADAPTER
|
||
IoGetDmaAdapter(
|
||
IN PDEVICE_OBJECT PhysicalDeviceObject OPTIONAL,
|
||
IN PDEVICE_DESCRIPTION DeviceDescription,
|
||
IN OUT PULONG NumberOfMapRegisters
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function returns the appropriate DMA adapter object for the device
|
||
defined in the device description structure. This code is a wrapper
|
||
which queries the bus interface standard and then calls the returned
|
||
get DMA adapter function. If an adapter object was not retrieved then
|
||
a legecy function is attempted.
|
||
|
||
Arguments:
|
||
|
||
PhysicalDeviceObject - Optionally, supplies the PDO for the device
|
||
requesting the DMA adapter. If not supplied, this routine performs the
|
||
function of the non-PnP HalGetDmaAdapter routine.
|
||
|
||
DeviceDescriptor - Supplies a description of the deivce.
|
||
|
||
NumberOfMapRegisters - Returns the maximum number of map registers which
|
||
may be allocated by the device driver.
|
||
|
||
Return Value:
|
||
|
||
A pointer to the requested adapter object or NULL if an adapter could not
|
||
be created.
|
||
|
||
--*/
|
||
|
||
{
|
||
KEVENT event;
|
||
NTSTATUS status;
|
||
PIRP irp;
|
||
IO_STATUS_BLOCK ioStatusBlock;
|
||
PIO_STACK_LOCATION irpStack;
|
||
BUS_INTERFACE_STANDARD busInterface;
|
||
PDMA_ADAPTER dmaAdapter = NULL;
|
||
PDEVICE_DESCRIPTION deviceDescriptionToUse;
|
||
DEVICE_DESCRIPTION privateDeviceDescription;
|
||
ULONG resultLength;
|
||
PDEVICE_OBJECT targetDevice;
|
||
|
||
PAGED_CODE();
|
||
|
||
if (PhysicalDeviceObject != NULL) {
|
||
|
||
ASSERT_PDO(PhysicalDeviceObject);
|
||
|
||
//
|
||
// First off, determine whether or not the caller has requested that we
|
||
// automatically fill in the proper InterfaceType value into the
|
||
// DEVICE_DESCRIPTION structure used in retrieving the DMA adapter object.
|
||
// If so, then retrieve that interface type value into our own copy of
|
||
// the DEVICE_DESCRIPTION buffer.
|
||
//
|
||
if ((DeviceDescription->InterfaceType == InterfaceTypeUndefined) ||
|
||
(DeviceDescription->InterfaceType == PNPBus)) {
|
||
//
|
||
// Make a copy of the caller-supplied device description, so
|
||
// we can modify it to fill in the correct interface type.
|
||
//
|
||
RtlCopyMemory(&privateDeviceDescription,
|
||
DeviceDescription,
|
||
sizeof(DEVICE_DESCRIPTION)
|
||
);
|
||
|
||
status = IoGetDeviceProperty(PhysicalDeviceObject,
|
||
DevicePropertyLegacyBusType,
|
||
sizeof(privateDeviceDescription.InterfaceType),
|
||
(PVOID)&(privateDeviceDescription.InterfaceType),
|
||
&resultLength
|
||
);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
ASSERT(status == STATUS_OBJECT_NAME_NOT_FOUND);
|
||
|
||
//
|
||
// Since the enumerator didn't tell us what interface type to
|
||
// use for this PDO, we'll fall back to our default. This is
|
||
// ISA for machines where the legacy bus is ISA or EISA, and it
|
||
// is MCA for machines whose legacy bus is MicroChannel.
|
||
//
|
||
privateDeviceDescription.InterfaceType = PnpDefaultInterfaceType;
|
||
}
|
||
|
||
//
|
||
// Use our private device description buffer from now on.
|
||
//
|
||
deviceDescriptionToUse = &privateDeviceDescription;
|
||
|
||
} else {
|
||
//
|
||
// Use the caller-supplied device description.
|
||
//
|
||
deviceDescriptionToUse = DeviceDescription;
|
||
}
|
||
|
||
//
|
||
// Now, query for the BUS_INTERFACE_STANDARD interface from the PDO.
|
||
//
|
||
KeInitializeEvent( &event, NotificationEvent, FALSE );
|
||
|
||
targetDevice = IoGetAttachedDeviceReference(PhysicalDeviceObject);
|
||
|
||
irp = IoBuildSynchronousFsdRequest( IRP_MJ_PNP,
|
||
targetDevice,
|
||
NULL,
|
||
0,
|
||
NULL,
|
||
&event,
|
||
&ioStatusBlock );
|
||
|
||
if (irp == NULL) {
|
||
return NULL;
|
||
}
|
||
|
||
RtlZeroMemory( &busInterface, sizeof( BUS_INTERFACE_STANDARD ));
|
||
|
||
irpStack = IoGetNextIrpStackLocation( irp );
|
||
irpStack->MinorFunction = IRP_MN_QUERY_INTERFACE;
|
||
irpStack->Parameters.QueryInterface.InterfaceType = (LPGUID) &GUID_BUS_INTERFACE_STANDARD;
|
||
irpStack->Parameters.QueryInterface.Size = sizeof( BUS_INTERFACE_STANDARD );
|
||
irpStack->Parameters.QueryInterface.Version = 1;
|
||
irpStack->Parameters.QueryInterface.Interface = (PINTERFACE) &busInterface;
|
||
irpStack->Parameters.QueryInterface.InterfaceSpecificData = NULL;
|
||
|
||
//
|
||
// Initialize the status to error in case the ACPI driver decides not to
|
||
// set it correctly.
|
||
//
|
||
|
||
irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
|
||
|
||
status = IoCallDriver(targetDevice, irp);
|
||
|
||
if (status == STATUS_PENDING) {
|
||
|
||
KeWaitForSingleObject( &event, Executive, KernelMode, FALSE, NULL );
|
||
status = ioStatusBlock.Status;
|
||
|
||
}
|
||
|
||
ObDereferenceObject(targetDevice);
|
||
|
||
if (NT_SUCCESS( status)) {
|
||
|
||
if (busInterface.GetDmaAdapter != NULL) {
|
||
|
||
|
||
dmaAdapter = busInterface.GetDmaAdapter( busInterface.Context,
|
||
deviceDescriptionToUse,
|
||
NumberOfMapRegisters );
|
||
|
||
}
|
||
|
||
//
|
||
// Dereference the interface
|
||
//
|
||
|
||
busInterface.InterfaceDereference( busInterface.Context );
|
||
}
|
||
|
||
} else {
|
||
//
|
||
// The caller didn't specify the PDO, so we'll just use the device
|
||
// description exactly as they specified it (i.e., we can't attempt to
|
||
// make our own determination of what interface type to use).
|
||
//
|
||
deviceDescriptionToUse = DeviceDescription;
|
||
}
|
||
|
||
//
|
||
// If there is no DMA adapter, try the legacy mode code.
|
||
//
|
||
|
||
#if !defined(NO_LEGACY_DRIVERS)
|
||
|
||
if (dmaAdapter == NULL) {
|
||
|
||
dmaAdapter = HalGetDmaAdapter( PhysicalDeviceObject,
|
||
deviceDescriptionToUse,
|
||
NumberOfMapRegisters );
|
||
|
||
}
|
||
|
||
#endif // NO_LEGACY_DRIVERS
|
||
|
||
return( dmaAdapter );
|
||
}
|
||
|
||
NTSTATUS
|
||
IopOpenDeviceParametersSubkey(
|
||
OUT HANDLE *ParamKeyHandle,
|
||
IN HANDLE ParentKeyHandle,
|
||
IN PUNICODE_STRING SubKeyString,
|
||
IN ACCESS_MASK DesiredAccess
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine reports whether WDM functionality is available that
|
||
is greater than or equal to the specified major and minor version.
|
||
|
||
Parameters:
|
||
|
||
MajorVersion - Supplies the WDM major version that is required.
|
||
|
||
MinorVersion - Supplies the WDM minor version that is required.
|
||
|
||
Return Value:
|
||
|
||
If WDM support is available at _at least_ the requested level, the
|
||
return value is TRUE, otherwise it is FALSE.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
ULONG disposition;
|
||
ULONG lengthSD;
|
||
PSECURITY_DESCRIPTOR oldSD = NULL;
|
||
SECURITY_DESCRIPTOR newSD;
|
||
ACL_SIZE_INFORMATION aclSizeInfo;
|
||
PACL oldDacl;
|
||
PACL newDacl = NULL;
|
||
ULONG sizeDacl;
|
||
BOOLEAN daclPresent, daclDefaulted;
|
||
PACCESS_ALLOWED_ACE ace;
|
||
ULONG aceIndex;
|
||
HANDLE deviceKeyHandle;
|
||
UNICODE_STRING deviceParamString;
|
||
|
||
//
|
||
// First try and open the device key
|
||
//
|
||
status = IopOpenRegistryKeyEx( &deviceKeyHandle,
|
||
ParentKeyHandle,
|
||
SubKeyString,
|
||
KEY_WRITE
|
||
);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
return status;
|
||
}
|
||
|
||
PiWstrToUnicodeString(&deviceParamString, REGSTR_KEY_DEVICEPARAMETERS);
|
||
|
||
status = IopCreateRegistryKeyEx( ParamKeyHandle,
|
||
deviceKeyHandle,
|
||
&deviceParamString,
|
||
DesiredAccess | READ_CONTROL | WRITE_DAC,
|
||
REG_OPTION_NON_VOLATILE,
|
||
&disposition
|
||
);
|
||
|
||
ZwClose(deviceKeyHandle);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
IopDbgPrint(( IOP_IOAPI_WARNING_LEVEL,
|
||
"IopOpenDeviceParametersSubkey: IopCreateRegistryKeyEx failed, status = %8.8X\n", status));
|
||
return status;
|
||
}
|
||
|
||
if (disposition == REG_CREATED_NEW_KEY) {
|
||
|
||
//
|
||
// Need to set an ACL on the key if it was created
|
||
//
|
||
//
|
||
// Get the security descriptor from the key so we can add the
|
||
// administrator.
|
||
//
|
||
status = ZwQuerySecurityObject(*ParamKeyHandle,
|
||
DACL_SECURITY_INFORMATION,
|
||
NULL,
|
||
0,
|
||
&lengthSD);
|
||
|
||
if (status == STATUS_BUFFER_TOO_SMALL) {
|
||
oldSD = ExAllocatePool( PagedPool, lengthSD );
|
||
|
||
if (oldSD != NULL) {
|
||
|
||
status = ZwQuerySecurityObject(*ParamKeyHandle,
|
||
DACL_SECURITY_INFORMATION,
|
||
oldSD,
|
||
lengthSD,
|
||
&lengthSD);
|
||
if (!NT_SUCCESS(status)) {
|
||
IopDbgPrint(( IOP_IOAPI_WARNING_LEVEL,
|
||
"IopOpenDeviceParametersSubkey: ZwQuerySecurityObject failed, status = %8.8X\n", status));
|
||
goto Cleanup0;
|
||
}
|
||
} else {
|
||
|
||
IopDbgPrint(( IOP_IOAPI_WARNING_LEVEL,
|
||
"IopOpenDeviceParametersSubkey: Failed to allocate memory, status = %8.8X\n", status));
|
||
status = STATUS_NO_MEMORY;
|
||
goto Cleanup0;
|
||
}
|
||
} else {
|
||
IopDbgPrint(( IOP_IOAPI_WARNING_LEVEL,
|
||
"IopOpenDeviceParametersSubkey: ZwQuerySecurityObject failed %8.8X\n",status));
|
||
status = STATUS_UNSUCCESSFUL;
|
||
goto Cleanup0;
|
||
}
|
||
|
||
status = RtlCreateSecurityDescriptor( (PSECURITY_DESCRIPTOR) &newSD,
|
||
SECURITY_DESCRIPTOR_REVISION );
|
||
ASSERT( NT_SUCCESS( status ) );
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
IopDbgPrint(( IOP_IOAPI_WARNING_LEVEL,
|
||
"IopOpenDeviceParametersSubkey: RtlCreateSecurityDescriptor failed, status = %8.8X\n", status));
|
||
goto Cleanup0;
|
||
}
|
||
//
|
||
// get the current DACL
|
||
//
|
||
status = RtlGetDaclSecurityDescriptor(oldSD, &daclPresent, &oldDacl, &daclDefaulted);
|
||
|
||
ASSERT( NT_SUCCESS( status ) );
|
||
|
||
//
|
||
// calculate the size of the new DACL
|
||
//
|
||
|
||
if (daclPresent) {
|
||
|
||
status = RtlQueryInformationAcl( oldDacl,
|
||
&aclSizeInfo,
|
||
sizeof(ACL_SIZE_INFORMATION),
|
||
AclSizeInformation);
|
||
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
IopDbgPrint(( IOP_IOAPI_WARNING_LEVEL,
|
||
"IopOpenDeviceParametersSubkey: RtlQueryInformationAcl failed, status = %8.8X\n", status));
|
||
goto Cleanup0;
|
||
}
|
||
|
||
sizeDacl = aclSizeInfo.AclBytesInUse;
|
||
} else {
|
||
sizeDacl = sizeof(ACL);
|
||
}
|
||
|
||
sizeDacl += sizeof(ACCESS_ALLOWED_ACE) + RtlLengthSid(SeAliasAdminsSid) - sizeof(ULONG);
|
||
|
||
//
|
||
// create and initialize the new DACL
|
||
//
|
||
newDacl = ExAllocatePool(PagedPool, sizeDacl);
|
||
|
||
if (newDacl == NULL) {
|
||
|
||
IopDbgPrint(( IOP_IOAPI_WARNING_LEVEL,
|
||
"IopOpenDeviceParametersSubkey: ExAllocatePool failed\n"));
|
||
goto Cleanup0;
|
||
}
|
||
|
||
status = RtlCreateAcl(newDacl, sizeDacl, ACL_REVISION);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
IopDbgPrint(( IOP_IOAPI_WARNING_LEVEL,
|
||
"IopOpenDeviceParametersSubkey: RtlCreateAcl failed, status = %8.8X\n", status));
|
||
goto Cleanup0;
|
||
}
|
||
|
||
//
|
||
// copy the current (original) DACL into this new one
|
||
//
|
||
if (daclPresent) {
|
||
|
||
for (aceIndex = 0; aceIndex < aclSizeInfo.AceCount; aceIndex++) {
|
||
|
||
status = RtlGetAce(oldDacl, aceIndex, (PVOID *)&ace);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
IopDbgPrint(( IOP_IOAPI_WARNING_LEVEL,
|
||
"IopOpenDeviceParametersSubkey: RtlGetAce failed, status = %8.8X\n", status));
|
||
goto Cleanup0;
|
||
}
|
||
|
||
//
|
||
// We need to skip copying any ACEs which refer to the Administrator
|
||
// to ensure that our full control ACE is the one and only.
|
||
//
|
||
if ((ace->Header.AceType != ACCESS_ALLOWED_ACE_TYPE &&
|
||
ace->Header.AceType != ACCESS_DENIED_ACE_TYPE) ||
|
||
!RtlEqualSid((PSID)&ace->SidStart, SeAliasAdminsSid)) {
|
||
|
||
status = RtlAddAce( newDacl,
|
||
ACL_REVISION,
|
||
~0U,
|
||
ace,
|
||
ace->Header.AceSize
|
||
);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
IopDbgPrint(( IOP_IOAPI_WARNING_LEVEL,
|
||
"IopOpenDeviceParametersSubkey: RtlAddAce failed, status = %8.8X\n", status));
|
||
goto Cleanup0;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// and my new admin-full ace to this new DACL
|
||
//
|
||
status = RtlAddAccessAllowedAceEx( newDacl,
|
||
ACL_REVISION,
|
||
CONTAINER_INHERIT_ACE,
|
||
KEY_ALL_ACCESS,
|
||
SeAliasAdminsSid
|
||
);
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
IopDbgPrint(( IOP_IOAPI_WARNING_LEVEL,
|
||
"IopOpenDeviceParametersSubkey: RtlAddAccessAllowedAceEx failed, status = %8.8X\n", status));
|
||
goto Cleanup0;
|
||
}
|
||
|
||
//
|
||
// Set the new DACL in the absolute security descriptor
|
||
//
|
||
status = RtlSetDaclSecurityDescriptor( (PSECURITY_DESCRIPTOR) &newSD,
|
||
TRUE,
|
||
newDacl,
|
||
FALSE
|
||
);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
IopDbgPrint(( IOP_IOAPI_WARNING_LEVEL,
|
||
"IopOpenDeviceParametersSubkey: RtlSetDaclSecurityDescriptor failed, status = %8.8X\n", status));
|
||
goto Cleanup0;
|
||
}
|
||
|
||
//
|
||
// validate the new security descriptor
|
||
//
|
||
status = RtlValidSecurityDescriptor(&newSD);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
IopDbgPrint(( IOP_IOAPI_WARNING_LEVEL,
|
||
"IopOpenDeviceParametersSubkey: RtlValidSecurityDescriptor failed, status = %8.8X\n", status));
|
||
goto Cleanup0;
|
||
}
|
||
|
||
|
||
status = ZwSetSecurityObject( *ParamKeyHandle,
|
||
DACL_SECURITY_INFORMATION,
|
||
&newSD
|
||
);
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
IopDbgPrint(( IOP_IOAPI_WARNING_LEVEL,
|
||
"IopOpenDeviceParametersSubkey: ZwSetSecurityObject failed, status = %8.8X\n", status));
|
||
goto Cleanup0;
|
||
}
|
||
}
|
||
|
||
//
|
||
// If we encounter an error updating the DACL we still return success.
|
||
//
|
||
|
||
Cleanup0:
|
||
|
||
if (oldSD != NULL) {
|
||
ExFreePool(oldSD);
|
||
}
|
||
|
||
if (newDacl != NULL) {
|
||
ExFreePool(newDacl);
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
PpCreateLegacyDeviceIds(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PUNICODE_STRING DriverName,
|
||
IN PCM_RESOURCE_LIST Resources
|
||
)
|
||
{
|
||
PIOPNP_DEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
||
PWCHAR buffer;
|
||
|
||
ULONG length = 0;
|
||
|
||
INTERFACE_TYPE interface;
|
||
static const WCHAR* interfaceNames[] ={L"",
|
||
L"Internal",
|
||
L"Isa",
|
||
L"Eisa",
|
||
L"MicroChannel",
|
||
L"TurboChannel",
|
||
L"PCIBus",
|
||
L"VMEBus",
|
||
L"NuBus",
|
||
L"PCMCIABus",
|
||
L"CBus",
|
||
L"MPIBus",
|
||
L"MPSABus",
|
||
L"ProcessorInternal",
|
||
L"InternalPowerBus",
|
||
L"PNPISABus",
|
||
L"PNPBus",
|
||
L"Other",
|
||
L"Root"};
|
||
|
||
|
||
PAGED_CODE();
|
||
|
||
if(Resources != NULL) {
|
||
|
||
interface = Resources->List[0].InterfaceType;
|
||
|
||
if((interface > MaximumInterfaceType) ||
|
||
(interface < InterfaceTypeUndefined)) {
|
||
interface = MaximumInterfaceType;
|
||
}
|
||
} else {
|
||
interface = Internal;
|
||
}
|
||
|
||
interface++;
|
||
|
||
//
|
||
// The compatible ID generated will be
|
||
// DETECTED<InterfaceName>\<Driver Name>
|
||
//
|
||
|
||
length = (ULONG)(wcslen(LEGACY_COMPATIBLE_ID_BASE) * sizeof(WCHAR));
|
||
length += (ULONG)(wcslen(interfaceNames[interface]) * sizeof(WCHAR));
|
||
length += sizeof(L'\\');
|
||
length += DriverName->Length;
|
||
length += sizeof(UNICODE_NULL);
|
||
|
||
length += (ULONG)(wcslen(LEGACY_COMPATIBLE_ID_BASE) * sizeof(WCHAR));
|
||
length += sizeof(L'\\');
|
||
length += DriverName->Length;
|
||
length += sizeof(UNICODE_NULL) * 2;
|
||
|
||
buffer = ExAllocatePool(PagedPool, length);
|
||
deviceExtension->CompatibleIdList = buffer;
|
||
|
||
if(buffer == NULL) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
RtlZeroMemory(buffer, length);
|
||
|
||
swprintf(buffer, L"%ws%ws\\%wZ", LEGACY_COMPATIBLE_ID_BASE,
|
||
interfaceNames[interface],
|
||
DriverName);
|
||
|
||
//
|
||
// Adjust the buffer to point to the end and generate the second
|
||
// compatible id string.
|
||
//
|
||
|
||
buffer += wcslen(buffer) + 1;
|
||
|
||
swprintf(buffer, L"%ws\\%wZ", LEGACY_COMPATIBLE_ID_BASE, DriverName);
|
||
|
||
deviceExtension->CompatibleIdListSize = length;
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
IopAppendLegacyVeto(
|
||
IN PIO_GET_LEGACY_VETO_LIST_CONTEXT Context,
|
||
IN PUNICODE_STRING VetoName
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine appends a veto (driver name or device instance path) to the
|
||
veto list.
|
||
|
||
Parameters:
|
||
|
||
Context - An IO_GET_LEGACY_VETO_LIST_CONTEXT pointer.
|
||
|
||
VetoName - The name of the driver/device to append to the veto list.
|
||
|
||
ReturnValue:
|
||
|
||
A BOOLEAN which indicates whether the append operation was successful.
|
||
|
||
--*/
|
||
{
|
||
ULONG Length;
|
||
PWSTR Buffer;
|
||
|
||
//
|
||
// Compute the length of the (new) veto list. This is the length of
|
||
// the old veto list + the size of the new veto + the size of the
|
||
// terminating '\0'.
|
||
//
|
||
|
||
Length = Context->VetoListLength + VetoName->Length + sizeof (WCHAR);
|
||
|
||
//
|
||
// Allocate the new veto list.
|
||
//
|
||
|
||
Buffer = ExAllocatePool(
|
||
NonPagedPool,
|
||
Length
|
||
);
|
||
|
||
//
|
||
// If we succeeded in allocating the new veto list, copy the old
|
||
// veto list to the new list, append the new veto, and finally,
|
||
// append a terminating '\0'. Otherwise, update the status to
|
||
// indicate an error; IopGetLegacyVetoList will free the veto list
|
||
// before it returns.
|
||
//
|
||
|
||
if (Buffer != NULL) {
|
||
|
||
if (*Context->VetoList != NULL) {
|
||
|
||
RtlCopyMemory(
|
||
Buffer,
|
||
*Context->VetoList,
|
||
Context->VetoListLength
|
||
);
|
||
|
||
ExFreePool(*Context->VetoList);
|
||
|
||
}
|
||
|
||
RtlCopyMemory(
|
||
&Buffer[Context->VetoListLength / sizeof (WCHAR)],
|
||
VetoName->Buffer,
|
||
VetoName->Length
|
||
);
|
||
|
||
Buffer[Length / sizeof (WCHAR) - 1] = L'\0';
|
||
|
||
*Context->VetoList = Buffer;
|
||
Context->VetoListLength = Length;
|
||
|
||
return TRUE;
|
||
|
||
} else {
|
||
|
||
*Context->Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
return FALSE;
|
||
|
||
}
|
||
}
|
||
|
||
BOOLEAN
|
||
IopGetLegacyVetoListDevice(
|
||
IN PDEVICE_NODE DeviceNode,
|
||
IN PIO_GET_LEGACY_VETO_LIST_CONTEXT Context
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine determines whether the specified device node should be added to
|
||
the veto list, and if so, calls IopAppendLegacyVeto to add it.
|
||
|
||
Parameters:
|
||
|
||
DeviceNode - The device node to be added.
|
||
|
||
Context - An IO_GET_LEGACY_VETO_LIST_CONTEXT pointer.
|
||
|
||
ReturnValue:
|
||
|
||
A BOOLEAN value which indicates whether the device node enumeration
|
||
process should be terminated or not.
|
||
|
||
--*/
|
||
{
|
||
PDEVICE_CAPABILITIES DeviceCapabilities;
|
||
|
||
//
|
||
// A device node should be added added to the veto list, if it has the
|
||
// NonDynamic capability.
|
||
//
|
||
|
||
DeviceCapabilities = IopDeviceNodeFlagsToCapabilities(DeviceNode);
|
||
|
||
if (DeviceCapabilities->NonDynamic) {
|
||
|
||
//
|
||
// Update the veto type. If an error occurrs while adding the device
|
||
// node to the veto list, or the caller did not provide a veto list
|
||
// pointer, terminate the enumeration process now.
|
||
//
|
||
|
||
*Context->VetoType = PNP_VetoLegacyDevice;
|
||
|
||
if (Context->VetoList != NULL) {
|
||
|
||
if (!IopAppendLegacyVeto(Context, &DeviceNode->InstancePath)) {
|
||
return FALSE;
|
||
}
|
||
|
||
} else {
|
||
|
||
return FALSE;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
BOOLEAN
|
||
IopGetLegacyVetoListDeviceNode(
|
||
IN PDEVICE_NODE DeviceNode,
|
||
IN PIO_GET_LEGACY_VETO_LIST_CONTEXT Context
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine recusively walks the device tree, invoking
|
||
IopGetLegacyVetoListDevice to add device nodes to the veto list
|
||
(as appropriate).
|
||
|
||
Parameters:
|
||
|
||
DeviceNode - The device node.
|
||
|
||
Context - An IO_GET_LEGACY_VETO_LIST_CONTEXT pointer.
|
||
|
||
|
||
ReturnValue:
|
||
|
||
A BOOLEAN value which indicates whether the device tree enumeration
|
||
process should be terminated or not.
|
||
|
||
--*/
|
||
{
|
||
PDEVICE_NODE Child;
|
||
|
||
//
|
||
// Determine whether the device node should be added to the veto
|
||
// list and add it. If an operation is unsuccessful or we determine
|
||
// the veto type but the caller doesn't need the veto list, then we
|
||
// terminate our search now.
|
||
//
|
||
|
||
if (!IopGetLegacyVetoListDevice(DeviceNode, Context)) {
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Call ourselves recursively to enumerate our children. If while
|
||
// enumerating our children we determine we can terminate the search
|
||
// prematurely, do so.
|
||
//
|
||
|
||
for (Child = DeviceNode->Child;
|
||
Child != NULL;
|
||
Child = Child->Sibling) {
|
||
|
||
if (!IopGetLegacyVetoListDeviceNode(Child, Context)) {
|
||
return FALSE;
|
||
}
|
||
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
VOID
|
||
IopGetLegacyVetoListDrivers(
|
||
IN PIO_GET_LEGACY_VETO_LIST_CONTEXT Context
|
||
)
|
||
{
|
||
PDRIVER_OBJECT driverObject;
|
||
OBJECT_ATTRIBUTES attributes;
|
||
UNICODE_STRING driverString;
|
||
POBJECT_DIRECTORY_INFORMATION dirInfo;
|
||
HANDLE directoryHandle;
|
||
ULONG dirInfoLength, neededLength, dirContext;
|
||
NTSTATUS status;
|
||
BOOLEAN restartScan;
|
||
|
||
dirInfoLength = 0;
|
||
dirInfo = NULL;
|
||
restartScan = TRUE;
|
||
|
||
//
|
||
// Get handle to \\Driver directory
|
||
//
|
||
|
||
PiWstrToUnicodeString(&driverString, L"\\Driver");
|
||
|
||
InitializeObjectAttributes(&attributes,
|
||
&driverString,
|
||
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
status = ZwOpenDirectoryObject(&directoryHandle,
|
||
DIRECTORY_QUERY,
|
||
&attributes
|
||
);
|
||
if (!NT_SUCCESS(status)) {
|
||
*Context->Status = status;
|
||
return;
|
||
}
|
||
|
||
for (;;) {
|
||
|
||
//
|
||
// Get info on next object in directory. If the buffer is too
|
||
// small, reallocate it and try again. Otherwise, any failure
|
||
// including STATUS_NO_MORE_ENTRIES breaks us out.
|
||
//
|
||
|
||
status = ZwQueryDirectoryObject(directoryHandle,
|
||
dirInfo,
|
||
dirInfoLength,
|
||
TRUE, // force one at a time
|
||
restartScan,
|
||
&dirContext,
|
||
&neededLength);
|
||
if (status == STATUS_BUFFER_TOO_SMALL) {
|
||
dirInfoLength = neededLength;
|
||
if (dirInfo != NULL) {
|
||
ExFreePool(dirInfo);
|
||
}
|
||
dirInfo = ExAllocatePool(PagedPool | POOL_COLD_ALLOCATION, dirInfoLength);
|
||
if (dirInfo == NULL) {
|
||
*Context->Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
break;
|
||
}
|
||
status = ZwQueryDirectoryObject(directoryHandle,
|
||
dirInfo,
|
||
dirInfoLength,
|
||
TRUE, // force one at a time
|
||
restartScan,
|
||
&dirContext,
|
||
&neededLength);
|
||
}
|
||
restartScan = FALSE;
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Have name of object. Create object path and use
|
||
// ObReferenceObjectByName() to get DriverObject. This may
|
||
// fail non-fatally if DriverObject has gone away in the interim.
|
||
//
|
||
|
||
driverString.MaximumLength = sizeof(L"\\Driver\\") +
|
||
dirInfo->Name.Length;
|
||
driverString.Length = driverString.MaximumLength - sizeof(WCHAR);
|
||
driverString.Buffer = ExAllocatePool(PagedPool | POOL_COLD_ALLOCATION,
|
||
driverString.MaximumLength);
|
||
if (driverString.Buffer == NULL) {
|
||
*Context->Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
break;
|
||
}
|
||
|
||
swprintf(driverString.Buffer, L"\\Driver\\%ws", dirInfo->Name.Buffer);
|
||
status = ObReferenceObjectByName(&driverString,
|
||
OBJ_CASE_INSENSITIVE,
|
||
NULL, // access state
|
||
0, // access mask
|
||
IoDriverObjectType,
|
||
KernelMode,
|
||
NULL, // parse context
|
||
&driverObject);
|
||
|
||
ExFreePool(driverString.Buffer);
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
ASSERT(driverObject->Type == IO_TYPE_DRIVER);
|
||
if (driverObject->Flags & DRVO_LEGACY_RESOURCES) {
|
||
//
|
||
// Update the veto type. If the caller provided a
|
||
// veto list pointer, add the driver to the veto list.
|
||
// If an error occurs while adding the driver to the
|
||
// veto list, or the caller did not provide a veto
|
||
// list pointer, terminate the driver enumeration now.
|
||
//
|
||
// NOTE: Driver may be loaded but not running,
|
||
// distinction is not made here.
|
||
|
||
|
||
*Context->VetoType = PNP_VetoLegacyDriver;
|
||
|
||
if (Context->VetoList != NULL) {
|
||
IopAppendLegacyVeto(Context, &dirInfo->Name);
|
||
}
|
||
}
|
||
ObDereferenceObject(driverObject);
|
||
|
||
//
|
||
// Early out if we have a veto and the caller didn't want a list or
|
||
// we hit some error already
|
||
//
|
||
if (((*Context->VetoType == PNP_VetoLegacyDriver) &&
|
||
(Context->VetoList == NULL)) ||
|
||
!NT_SUCCESS(*Context->Status)) {
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
if (dirInfo != NULL) {
|
||
ExFreePool(dirInfo);
|
||
}
|
||
|
||
ZwClose(directoryHandle);
|
||
}
|
||
|
||
NTSTATUS
|
||
IoGetLegacyVetoList(
|
||
OUT PWSTR *VetoList OPTIONAL,
|
||
OUT PPNP_VETO_TYPE VetoType
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is used by PNP and PO to determine whether legacy drivers and
|
||
devices are installed in the system. This routine is conceptually a
|
||
QUERY_REMOVE_DEVICE and QUERY_POWER-like interface for legacy drivers
|
||
and devices.
|
||
|
||
Parameters:
|
||
|
||
VetoList - A pointer to a PWSTR. (Optional) If specified,
|
||
IoGetLegacyVetoList will allocate a veto list, and return a
|
||
pointer to the veto list in VetoList.
|
||
|
||
VetoType - A pointer to a PNP_VETO_TYPE. If no legacy drivers
|
||
or devices are found in the system, VetoType is assigned
|
||
PNP_VetoTypeUnknown. If one or more legacy drivers are installed,
|
||
VetoType is assigned PNP_VetoLegacyDriver. If one or more
|
||
legacy devices are installed, VetoType is assigned
|
||
PNP_VetoLegacyDevice. VetoType is assigned independent of
|
||
whether a VetoList is created.
|
||
|
||
ReturnValue:
|
||
|
||
An NTSTATUS value indicating whether the IoGetLegacyVetoList() operation
|
||
was successful.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status;
|
||
IO_GET_LEGACY_VETO_LIST_CONTEXT Context;
|
||
UNICODE_STRING UnicodeString;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Initialize the veto list.
|
||
//
|
||
|
||
if (VetoList != NULL) {
|
||
*VetoList = NULL;
|
||
}
|
||
|
||
//
|
||
// Initialize the veto type.
|
||
//
|
||
|
||
ASSERT(VetoType != NULL);
|
||
|
||
*VetoType = PNP_VetoTypeUnknown;
|
||
|
||
//
|
||
// Initialize the status.
|
||
//
|
||
|
||
Status = STATUS_SUCCESS;
|
||
|
||
if (PnPInitialized == FALSE) {
|
||
|
||
//
|
||
// Can't touch anything, but nothing is really started either.
|
||
//
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// Initialize our local context.
|
||
//
|
||
|
||
Context.VetoList = VetoList;
|
||
Context.VetoListLength = 0;
|
||
Context.VetoType = VetoType;
|
||
Context.Status = &Status;
|
||
|
||
//
|
||
// Enumerate all driver objects. This process can: (1) modify
|
||
// the veto list, (2) modify the veto type and/or (3) modify the
|
||
// status.
|
||
//
|
||
|
||
IopGetLegacyVetoListDrivers(&Context);
|
||
|
||
//
|
||
// If the driver enumeration process was successful and no legacy
|
||
// drivers were detected, enumerate all device nodes. The same
|
||
// context values as above may be modified during device enumeration.
|
||
//
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
|
||
if (*VetoType == PNP_VetoTypeUnknown) {
|
||
|
||
PpDevNodeLockTree(PPL_SIMPLE_READ);
|
||
|
||
IopGetLegacyVetoListDeviceNode(
|
||
IopRootDeviceNode,
|
||
&Context
|
||
);
|
||
|
||
PpDevNodeUnlockTree(PPL_SIMPLE_READ);
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// If the previous operation(s) was/were successful, and the caller
|
||
// provided a veto list pointer and we have constructed a veto
|
||
// list, terminate the veto list with an empty string, i.e. MULTI_SZ.
|
||
//
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
|
||
if (*VetoType != PNP_VetoTypeUnknown) {
|
||
|
||
if (VetoList != NULL) {
|
||
|
||
PiWstrToUnicodeString(
|
||
&UnicodeString,
|
||
L""
|
||
);
|
||
|
||
IopAppendLegacyVeto(
|
||
&Context,
|
||
&UnicodeString
|
||
);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// If a previous operation was unsuccessful, free any veto list we may have
|
||
// allocated along the way.
|
||
//
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
|
||
if (VetoList != NULL && *VetoList != NULL) {
|
||
ExFreePool(*VetoList);
|
||
*VetoList = NULL;
|
||
}
|
||
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
NTSTATUS
|
||
PnpCompletionRoutine(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN PKEVENT Event
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is used to stop further processing on an Irp which has been
|
||
passed to IoForwardAndCatchIrp. It signals a event which has been passed
|
||
in the context parameter to indicate that the Irp processing is complete.
|
||
It then returns STATUS_MORE_PROCESSING_REQUIRED in order to stop processing
|
||
on this Irp.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject -
|
||
Contains the device which set up this completion routine.
|
||
|
||
Irp -
|
||
Contains the Irp which is being stopped.
|
||
|
||
Event -
|
||
Contains the event which is used to signal that this Irp has been
|
||
completed.
|
||
|
||
Return Value:
|
||
|
||
Returns STATUS_MORE_PROCESSING_REQUIRED in order to stop processing on the
|
||
Irp.
|
||
|
||
--*/
|
||
{
|
||
UNREFERENCED_PARAMETER( DeviceObject );
|
||
UNREFERENCED_PARAMETER( Irp );
|
||
|
||
//
|
||
// This will allow the ForwardAndCatchIrp call to continue on its way.
|
||
//
|
||
KeSetEvent(Event, IO_NO_INCREMENT, FALSE);
|
||
//
|
||
// This will ensure that nothing else touches the Irp, since the original
|
||
// caller has now continued, and the Irp may not exist anymore.
|
||
//
|
||
return STATUS_MORE_PROCESSING_REQUIRED;
|
||
}
|
||
|
||
NTKERNELAPI
|
||
BOOLEAN
|
||
IoForwardAndCatchIrp(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is used with devices which may be stacked, and may not use
|
||
file objects to communicate.
|
||
|
||
Forwards an IRP to the specified driver after initializing the next
|
||
stack location, and regains control of the Irp on completion from that
|
||
driver.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject -
|
||
Contains the device to forward the Irp to.
|
||
|
||
Irp -
|
||
Contains the Irp which is being forwarded to the specified driver.
|
||
|
||
Return Value:
|
||
|
||
Returns TRUE if the IRP was forwarded, else FALSE if no stack space
|
||
was available.
|
||
|
||
--*/
|
||
{
|
||
KEVENT Event;
|
||
|
||
PAGED_CODE();
|
||
//
|
||
// Ensure that there is another stack location before copying parameters.
|
||
//
|
||
ASSERT(Irp->CurrentLocation > 1);
|
||
if (Irp->CurrentLocation == 1) {
|
||
return FALSE;
|
||
}
|
||
IoCopyCurrentIrpStackLocationToNext(Irp);
|
||
//
|
||
// Set up a completion routine so that the Irp is not actually
|
||
// completed. Thus the caller can get control of the Irp back after
|
||
// this next driver is done with it.
|
||
//
|
||
KeInitializeEvent(&Event, NotificationEvent, FALSE);
|
||
IoSetCompletionRoutine(Irp, PnpCompletionRoutine, &Event, TRUE, TRUE, TRUE);
|
||
if (IoCallDriver(DeviceObject, Irp) == STATUS_PENDING) {
|
||
//
|
||
// Wait for completion which will occur when the CompletionRoutine
|
||
// signals this event. Wait in KernelMode so that the current stack
|
||
// is not paged out, since there is an event object on this stack.
|
||
//
|
||
KeWaitForSingleObject(
|
||
&Event,
|
||
Suspended,
|
||
KernelMode,
|
||
FALSE,
|
||
NULL);
|
||
}
|
||
return TRUE;
|
||
}
|
||
|
||
NTSTATUS
|
||
IoGetDeviceInstanceName(
|
||
IN PDEVICE_OBJECT PhysicalDeviceObject,
|
||
OUT PUNICODE_STRING InstanceName
|
||
)
|
||
{
|
||
PDEVICE_NODE deviceNode;
|
||
|
||
ASSERT_PDO(PhysicalDeviceObject);
|
||
|
||
deviceNode = PhysicalDeviceObject->DeviceObjectExtension->DeviceNode;
|
||
|
||
if (PipConcatenateUnicodeStrings( InstanceName,
|
||
&deviceNode->InstancePath,
|
||
NULL)) {
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
} else {
|
||
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
}
|
||
|
||
|
||
|