You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
6635 lines
206 KiB
6635 lines
206 KiB
/*++
|
|
|
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
|
|
Module Name:
|
|
|
|
pnpsubs.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the plug-and-play subroutines for the
|
|
I/O system.
|
|
|
|
|
|
Author:
|
|
|
|
Shie-Lin Tzong (shielint) 3-Jan-1995
|
|
|
|
Environment:
|
|
|
|
Kernel mode
|
|
|
|
Revision History:
|
|
|
|
|
|
--*/
|
|
|
|
#include "pnpmgrp.h"
|
|
#pragma hdrstop
|
|
|
|
//
|
|
// Data structure for each entry in the device reference table.
|
|
//
|
|
typedef struct _DEVICE_REFERENCE {
|
|
PDEVICE_OBJECT DeviceObject; // PDO
|
|
PUNICODE_STRING DeviceInstance; // Pointer to instance path for the devnode for the PDO
|
|
} DEVICE_REFERENCE, *PDEVICE_REFERENCE;
|
|
|
|
#ifdef POOL_TAGGING
|
|
#undef ExAllocatePool
|
|
#define ExAllocatePool(a,b) ExAllocatePoolWithTag(a,b,'uspP')
|
|
#endif
|
|
|
|
//
|
|
// Regular data segment
|
|
//
|
|
|
|
#ifdef ALLOC_DATA_PRAGMA
|
|
#pragma data_seg()
|
|
#endif
|
|
|
|
//
|
|
// Table to map InstancePath to DO.
|
|
//
|
|
RTL_GENERIC_TABLE PpDeviceReferenceTable;
|
|
|
|
//
|
|
// Lock to synchronize access to the table.
|
|
//
|
|
KGUARDED_MUTEX PpDeviceReferenceTableLock;
|
|
|
|
//
|
|
// Table of BusType GUIDs
|
|
//
|
|
GUID *PpBusTypeGuidArray;
|
|
|
|
//
|
|
// Number of entries in the BusTypeGuid table.
|
|
//
|
|
ULONG PpBusTypeGuidCount;
|
|
|
|
//
|
|
// Maximum number of entries in the BusTypeGuid table.
|
|
//
|
|
ULONG PpBusTypeGuidCountMax;
|
|
|
|
//
|
|
// Lock used to synchronize access to the BusTypeGuid table.
|
|
//
|
|
KGUARDED_MUTEX PpBusTypeGuidLock;
|
|
|
|
//
|
|
// Prototype of internal functions
|
|
//
|
|
|
|
VOID
|
|
IopDisableDevice(
|
|
IN PDEVICE_NODE DeviceNode
|
|
);
|
|
|
|
BOOLEAN
|
|
IopDeleteKeyRecursiveCallback(
|
|
IN HANDLE KeyHandle,
|
|
IN PUNICODE_STRING KeyName,
|
|
IN OUT PVOID Context
|
|
);
|
|
|
|
NTSTATUS
|
|
PipGenerateMadeupNodeName (
|
|
IN PUNICODE_STRING ServiceKeyName,
|
|
OUT PUNICODE_STRING MadeupNodeName
|
|
);
|
|
|
|
RTL_GENERIC_COMPARE_RESULTS
|
|
NTAPI
|
|
PiCompareInstancePath (
|
|
PRTL_GENERIC_TABLE Table,
|
|
PVOID FirstStruct,
|
|
PVOID SecondStruct
|
|
);
|
|
|
|
ULONG
|
|
PiFixupID(
|
|
IN PWCHAR ID,
|
|
IN ULONG MaxIDLength,
|
|
IN BOOLEAN Multi,
|
|
IN ULONG AllowedSeparators,
|
|
IN PUNICODE_STRING LogString OPTIONAL
|
|
);
|
|
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(INIT, PpInitializeDeviceReferenceTable)
|
|
#pragma alloc_text(INIT, PipRegMultiSzToUnicodeStrings)
|
|
#pragma alloc_text(INIT, PipFreeUnicodeStringList)
|
|
#pragma alloc_text(INIT, PpBusTypeGuidInitialize)
|
|
|
|
#pragma alloc_text(PAGE, PipApplyFunctionToServiceInstances)
|
|
#pragma alloc_text(PAGE, PipApplyFunctionToSubKeys)
|
|
#pragma alloc_text(PAGE, IopCleanupDeviceRegistryValues)
|
|
#pragma alloc_text(PAGE, IopCmResourcesToIoResources)
|
|
#pragma alloc_text(PAGE, PipConcatenateUnicodeStrings)
|
|
#pragma alloc_text(PAGE, PipCreateMadeupNode)
|
|
#pragma alloc_text(PAGE, PipGenerateMadeupNodeName)
|
|
#pragma alloc_text(PAGE, IopCreateRegistryKeyEx)
|
|
#pragma alloc_text(PAGE, IopDeleteKeyRecursive)
|
|
#pragma alloc_text(PAGE, IopDeleteKeyRecursiveCallback)
|
|
#pragma alloc_text(PAGE, IopDeleteLegacyKey)
|
|
#pragma alloc_text(PAGE, IopDetermineResourceListSize)
|
|
#pragma alloc_text(PAGE, PpSaveDeviceCapabilities)
|
|
#pragma alloc_text(PAGE, IopQueryAndSaveDeviceNodeCapabilities)
|
|
#pragma alloc_text(PAGE, IopDeviceObjectFromDeviceInstance)
|
|
#pragma alloc_text(PAGE, IopDeviceObjectToDeviceInstance)
|
|
#pragma alloc_text(PAGE, IopDisableDevice)
|
|
#pragma alloc_text(PAGE, IopDriverLoadingFailed)
|
|
#pragma alloc_text(PAGE, IopFilterResourceRequirementsList)
|
|
#pragma alloc_text(PAGE, IopGetDeviceInstanceCsConfigFlags)
|
|
#pragma alloc_text(PAGE, IopGetDeviceResourcesFromRegistry)
|
|
#pragma alloc_text(PAGE, PipGetServiceInstanceCsConfigFlags)
|
|
#pragma alloc_text(PAGE, IopIsAnyDeviceInstanceEnabled)
|
|
#pragma alloc_text(PAGE, IopIsDeviceInstanceEnabled)
|
|
#pragma alloc_text(PAGE, PipIsDuplicatedDevices)
|
|
#pragma alloc_text(PAGE, IopIsLegacyDriver)
|
|
#pragma alloc_text(PAGE, IopMergeCmResourceLists)
|
|
#pragma alloc_text(PAGE, IopMergeFilteredResourceRequirementsList)
|
|
#pragma alloc_text(PAGE, IopOpenCurrentHwProfileDeviceInstanceKey)
|
|
#pragma alloc_text(PAGE, IopOpenRegistryKeyEx)
|
|
#pragma alloc_text(PAGE, PipOpenServiceEnumKeys)
|
|
#pragma alloc_text(PAGE, IopPrepareDriverLoading)
|
|
#pragma alloc_text(PAGE, PipReadDeviceConfiguration)
|
|
#pragma alloc_text(PAGE, IopRestartDeviceNode)
|
|
#pragma alloc_text(PAGE, PipServiceInstanceToDeviceInstance)
|
|
#pragma alloc_text(PAGE, IopMapDeviceObjectToDeviceInstance)
|
|
#pragma alloc_text(PAGE, PiRegSzToString)
|
|
#pragma alloc_text(PAGE, PiCompareInstancePath)
|
|
#pragma alloc_text(PAGE, PiAllocateGenericTableEntry)
|
|
#pragma alloc_text(PAGE, PiFreeGenericTableEntry)
|
|
#pragma alloc_text(PAGE, PpSystemHiveLimitCallback)
|
|
#pragma alloc_text(PAGE, PpLogEvent)
|
|
#pragma alloc_text(PAGE, PiFixupID)
|
|
#pragma alloc_text(PAGE, PpQueryID)
|
|
#pragma alloc_text(PAGE, PpQueryDeviceID)
|
|
#pragma alloc_text(PAGE, PpQueryBusInformation)
|
|
#pragma alloc_text(PAGE, PpBusTypeGuidGetIndex)
|
|
#pragma alloc_text(PAGE, PpBusTypeGuidGet)
|
|
|
|
#if DBG
|
|
|
|
#pragma alloc_text(PAGE, IopDebugPrint)
|
|
|
|
#endif
|
|
#endif
|
|
|
|
NTSTATUS
|
|
PipCreateMadeupNode(
|
|
IN PUNICODE_STRING ServiceKeyName,
|
|
OUT PHANDLE ReturnedHandle,
|
|
OUT PUNICODE_STRING KeyName,
|
|
OUT PULONG InstanceNumber,
|
|
IN BOOLEAN ResourceOwned
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine creates a new instance node under System\Enum\Root\LEGACY_<ServiceKeyName>
|
|
key and all the required default value entries. Also a value entry under
|
|
Service\<ServiceKeyName>\Enum is created to point to the newly created madeup
|
|
entry. A handle and the keyname of the new key are returned to caller.
|
|
Caller must free the unicode string when he is done with it.
|
|
|
|
Parameters:
|
|
|
|
ServiceKeyName - Supplies a pointer to the name of the subkey in the
|
|
system service list (HKEY_LOCAL_MACHINE\CurrentControlSet\Services)
|
|
that caused the driver to load. This is the RegistryPath parameter
|
|
to the DriverEntry routine.
|
|
|
|
ReturnedHandle - Supplies a variable to receive the handle of the
|
|
newly created key.
|
|
|
|
KeyName - Supplies a variable to receive the name of the newly created
|
|
key.
|
|
|
|
InstanceNumber - supplies a variable to receive the InstanceNumber value
|
|
entry created under service\name\enum subkey.
|
|
|
|
ResourceOwned - supplies a BOOLEAN variable to indicate if caller owns
|
|
the registry resource shared.
|
|
|
|
ADRIAO N.B. 08/25/2000 - All users of this function pass in TRUE...
|
|
|
|
Return Value:
|
|
|
|
Status code that indicates whether or not the function was successful.
|
|
|
|
--*/
|
|
|
|
{
|
|
PKEY_VALUE_FULL_INFORMATION keyValueInformation;
|
|
UNICODE_STRING tmpKeyName, unicodeInstanceName, unicodeString;
|
|
UNICODE_STRING rootKeyName, unicodeValueName, unicodeKeyName;
|
|
HANDLE handle, enumRootHandle;
|
|
ULONG instance;
|
|
UCHAR unicodeBuffer[20];
|
|
ULONG tmpValue, disposition;
|
|
NTSTATUS status;
|
|
PWSTR p;
|
|
BOOLEAN releaseResource;
|
|
|
|
PAGED_CODE();
|
|
|
|
disposition = 0;
|
|
releaseResource = FALSE;
|
|
if (!ResourceOwned) {
|
|
|
|
PiLockPnpRegistry(FALSE);
|
|
releaseResource = TRUE;
|
|
}
|
|
//
|
|
// Open LocalMachine\System\CurrentControlSet\Enum\Root
|
|
//
|
|
status = IopOpenRegistryKeyEx( &enumRootHandle,
|
|
NULL,
|
|
&CmRegistryMachineSystemCurrentControlSetEnumRootName,
|
|
KEY_ALL_ACCESS
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
goto local_exit0;
|
|
}
|
|
//
|
|
// Generate the LEGACY_<ServiceKeyName> device id name from the ServiceKeyName.
|
|
//
|
|
status = PipGenerateMadeupNodeName( ServiceKeyName,
|
|
&unicodeKeyName);
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
ZwClose(enumRootHandle);
|
|
goto local_exit0;
|
|
}
|
|
//
|
|
// Open, and create if not already exist, System\Enum\Root\LEGACY_<ServiceKeyName>
|
|
//
|
|
status = IopCreateRegistryKeyEx( &handle,
|
|
enumRootHandle,
|
|
&unicodeKeyName,
|
|
KEY_ALL_ACCESS,
|
|
REG_OPTION_NON_VOLATILE,
|
|
NULL);
|
|
ZwClose(enumRootHandle);
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
RtlFreeUnicodeString(&unicodeKeyName);
|
|
goto local_exit0;
|
|
}
|
|
instance = 1;
|
|
PiWstrToUnicodeString(&unicodeValueName, REGSTR_VALUE_NEXT_INSTANCE);
|
|
status = ZwSetValueKey( handle,
|
|
&unicodeValueName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_DWORD,
|
|
&instance,
|
|
sizeof(instance));
|
|
instance--;
|
|
*InstanceNumber = instance;
|
|
PiUlongToInstanceKeyUnicodeString(&unicodeInstanceName,
|
|
unicodeBuffer + sizeof(WCHAR), // reserve first WCHAR space
|
|
20 - sizeof(WCHAR),
|
|
instance);
|
|
status = IopCreateRegistryKeyEx( ReturnedHandle,
|
|
handle,
|
|
&unicodeInstanceName,
|
|
KEY_ALL_ACCESS,
|
|
REG_OPTION_NON_VOLATILE,
|
|
&disposition);
|
|
ZwClose(handle);
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
RtlFreeUnicodeString(&unicodeKeyName);
|
|
goto local_exit0;
|
|
}
|
|
//
|
|
// Prepare newly created registry key name for returning to caller
|
|
//
|
|
*(PWSTR)unicodeBuffer = OBJ_NAME_PATH_SEPARATOR;
|
|
unicodeInstanceName.Buffer = (PWSTR)unicodeBuffer;
|
|
unicodeInstanceName.Length += sizeof(WCHAR);
|
|
unicodeInstanceName.MaximumLength += sizeof(WCHAR);
|
|
PiWstrToUnicodeString(&rootKeyName, REGSTR_KEY_ROOTENUM);
|
|
PiWstrToUnicodeString(&tmpKeyName, L"\\");
|
|
status = PipConcatenateUnicodeStrings(&unicodeString, &tmpKeyName, &unicodeKeyName);
|
|
RtlFreeUnicodeString(&unicodeKeyName);
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
goto local_exit0;
|
|
}
|
|
status = PipConcatenateUnicodeStrings(&tmpKeyName, &rootKeyName, &unicodeString);
|
|
RtlFreeUnicodeString(&unicodeString);
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
goto local_exit0;
|
|
}
|
|
status = PipConcatenateUnicodeStrings(KeyName, &tmpKeyName, &unicodeInstanceName);
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
RtlFreeUnicodeString(&tmpKeyName);
|
|
goto local_exit0;
|
|
}
|
|
|
|
if (disposition == REG_CREATED_NEW_KEY) {
|
|
//
|
|
// Create all the default value entry for the newly created key.
|
|
// Service = ServiceKeyName
|
|
// FoundAtEnum = 1
|
|
// Class = "LegacyDriver"
|
|
// ClassGUID = GUID for legacy driver class
|
|
// ConfigFlags = 0
|
|
//
|
|
// Create "Control" subkey with "NewlyCreated" value key
|
|
//
|
|
PiWstrToUnicodeString(&unicodeValueName, REGSTR_KEY_CONTROL);
|
|
status = IopCreateRegistryKeyEx( &handle,
|
|
*ReturnedHandle,
|
|
&unicodeValueName,
|
|
KEY_ALL_ACCESS,
|
|
REG_OPTION_VOLATILE,
|
|
NULL);
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
PiWstrToUnicodeString(&unicodeValueName, REGSTR_VALUE_NEWLY_CREATED);
|
|
tmpValue = 0;
|
|
ZwSetValueKey(handle,
|
|
&unicodeValueName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_DWORD,
|
|
&tmpValue,
|
|
sizeof(tmpValue));
|
|
ZwClose(handle);
|
|
}
|
|
|
|
handle = *ReturnedHandle;
|
|
|
|
PiWstrToUnicodeString(&unicodeValueName, REGSTR_VALUE_SERVICE);
|
|
p = (PWSTR)ExAllocatePool(PagedPool,
|
|
ServiceKeyName->Length + sizeof(UNICODE_NULL));
|
|
if(p) {
|
|
|
|
RtlCopyMemory(p, ServiceKeyName->Buffer, ServiceKeyName->Length);
|
|
p[ServiceKeyName->Length / sizeof (WCHAR)] = UNICODE_NULL;
|
|
ZwSetValueKey(
|
|
handle,
|
|
&unicodeValueName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_SZ,
|
|
p,
|
|
ServiceKeyName->Length + sizeof(UNICODE_NULL)
|
|
);
|
|
//
|
|
// We'll keep the null-terminated service name buffer around for a while,
|
|
// because we may need it later on for the DeviceDesc in case the service
|
|
// has no DisplayName.
|
|
//
|
|
}
|
|
|
|
PiWstrToUnicodeString(&unicodeValueName, REGSTR_VALUE_LEGACY);
|
|
tmpValue = 1;
|
|
ZwSetValueKey(
|
|
handle,
|
|
&unicodeValueName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_DWORD,
|
|
&tmpValue,
|
|
sizeof(tmpValue)
|
|
);
|
|
|
|
PiWstrToUnicodeString(&unicodeValueName, REGSTR_VALUE_CONFIG_FLAGS);
|
|
tmpValue = 0;
|
|
ZwSetValueKey(
|
|
handle,
|
|
&unicodeValueName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_DWORD,
|
|
&tmpValue,
|
|
sizeof(tmpValue)
|
|
);
|
|
|
|
PiWstrToUnicodeString(&unicodeValueName, REGSTR_VALUE_CLASS);
|
|
ZwSetValueKey(
|
|
handle,
|
|
&unicodeValueName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_SZ,
|
|
REGSTR_VALUE_LEGACY_DRIVER,
|
|
sizeof(REGSTR_VALUE_LEGACY_DRIVER)
|
|
);
|
|
|
|
PiWstrToUnicodeString(&unicodeValueName, REGSTR_VALUE_CLASSGUID);
|
|
ZwSetValueKey(
|
|
handle,
|
|
&unicodeValueName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_SZ,
|
|
(PVOID)®STR_VALUE_LEGACY_DRIVER_CLASS_GUID,
|
|
sizeof(REGSTR_VALUE_LEGACY_DRIVER_CLASS_GUID));
|
|
//
|
|
// Initialize DeviceDesc= value entry. If the service key has a "DisplayName"
|
|
// value entry, it is used as the DeviceDesc value. Otherwise, the service key
|
|
// name is used.
|
|
//
|
|
status = PipOpenServiceEnumKeys(ServiceKeyName,
|
|
KEY_READ,
|
|
&handle,
|
|
NULL,
|
|
FALSE);
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
keyValueInformation = NULL;
|
|
unicodeString.Length = 0;
|
|
status = IopGetRegistryValue(handle,
|
|
REGSTR_VALUE_DISPLAY_NAME,
|
|
&keyValueInformation
|
|
);
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
if (keyValueInformation->Type == REG_SZ) {
|
|
|
|
if (keyValueInformation->DataLength > sizeof(UNICODE_NULL)) {
|
|
|
|
IopRegistryDataToUnicodeString(&unicodeString,
|
|
(PWSTR)KEY_VALUE_DATA(keyValueInformation),
|
|
keyValueInformation->DataLength
|
|
);
|
|
}
|
|
}
|
|
}
|
|
if ((unicodeString.Length == 0) && p) {
|
|
//
|
|
// No DisplayName--use the service key name.
|
|
//
|
|
unicodeString.Length = ServiceKeyName->Length;
|
|
unicodeString.MaximumLength = ServiceKeyName->Length + sizeof(UNICODE_NULL);
|
|
unicodeString.Buffer = p;
|
|
}
|
|
|
|
if(unicodeString.Length) {
|
|
|
|
PiWstrToUnicodeString(&unicodeValueName, REGSTR_VALUE_DEVICE_DESC);
|
|
ZwSetValueKey(*ReturnedHandle,
|
|
&unicodeValueName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_SZ,
|
|
unicodeString.Buffer,
|
|
unicodeString.Length + sizeof(UNICODE_NULL)
|
|
);
|
|
}
|
|
if (keyValueInformation) {
|
|
|
|
ExFreePool(keyValueInformation);
|
|
}
|
|
ZwClose(handle);
|
|
}
|
|
|
|
if(p) {
|
|
|
|
ExFreePool(p);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Create new value entry under ServiceKeyName\Enum to reflect the newly
|
|
// added made-up device instance node.
|
|
//
|
|
|
|
PiUnlockPnpRegistry();
|
|
releaseResource = FALSE;
|
|
|
|
status = PpDeviceRegistration(KeyName, TRUE, NULL);
|
|
|
|
if (ResourceOwned) {
|
|
|
|
PiLockPnpRegistry(FALSE);
|
|
}
|
|
RtlFreeUnicodeString(&tmpKeyName);
|
|
if (!NT_SUCCESS(status)) {
|
|
//
|
|
// There is no registry key for the ServiceKeyName information.
|
|
//
|
|
ZwClose(*ReturnedHandle);
|
|
RtlFreeUnicodeString(KeyName);
|
|
}
|
|
|
|
local_exit0:
|
|
|
|
if (releaseResource) {
|
|
|
|
PiUnlockPnpRegistry();
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
PipGenerateMadeupNodeName (
|
|
IN PUNICODE_STRING ServiceKeyName,
|
|
OUT PUNICODE_STRING MadeupNodeName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine parses the ServiceKeyName string and replaces any space
|
|
characters with an underscore character, and any invalid characters (not
|
|
allowed in a "device instance") with their hexadecimal character
|
|
representation.
|
|
|
|
Invalid characters are:
|
|
c < 0x20 (' ')
|
|
c > 0x7F
|
|
c == 0x2C (',')
|
|
|
|
The resulting modified ServiceKeyName string is used to create a valid
|
|
device id. Paged pool space is allocated for the destination string.
|
|
Caller must release the space once done with it.
|
|
|
|
Arguments:
|
|
|
|
ServiceKeyName - Supplies a pointer to the name of the subkey in the
|
|
system service list (HKEY_LOCAL_MACHINE\CurrentControlSet\Services)
|
|
that caused the driver to load. This is the RegistryPath parameter
|
|
to the DriverEntry routine.
|
|
|
|
MadeupNodeName - Supplies a variable to receive the name of madeup device
|
|
id. If successful, the caller is responsible for freeing the allocated
|
|
buffer.
|
|
|
|
Return Value:
|
|
|
|
Status code that indicates whether or not the function was successful.
|
|
|
|
--*/
|
|
{
|
|
PWCHAR BufferEnd, p, q;
|
|
ULONG length;
|
|
PWSTR buffer;
|
|
|
|
//
|
|
// We'll need at least as much room as the size of the unicode service key
|
|
// name, plus the LEGACY_ prefix and terminating NULL char..
|
|
//
|
|
length = sizeof(REGSTR_KEY_MADEUP) + ServiceKeyName->Length;
|
|
|
|
p = ServiceKeyName->Buffer;
|
|
BufferEnd = (PWCHAR)((PUCHAR)p + ServiceKeyName->Length);
|
|
while(p != BufferEnd) {
|
|
if ((*p < L' ') || (*p > (WCHAR)0x7F) || (*p == L',')) {
|
|
//
|
|
// Each "invalid" character will be replaced with a '*' character
|
|
// (size already accounted for in calculated length), plus one
|
|
// character for each nibble of each byte in the invalid character.
|
|
//
|
|
length += 2*sizeof(WCHAR)*sizeof(WCHAR);
|
|
}
|
|
p++;
|
|
}
|
|
|
|
//
|
|
// Allocate a buffer large enough to hold the converted
|
|
// LEGACY_<ServiceKeyName> string.
|
|
//
|
|
buffer = (PWSTR)ExAllocatePool(PagedPool, length);
|
|
if (!buffer) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
MadeupNodeName->Buffer = buffer;
|
|
MadeupNodeName->Length = (USHORT)(length - sizeof(UNICODE_NULL));
|
|
MadeupNodeName->MaximumLength = (USHORT)length;
|
|
|
|
RtlCopyMemory(buffer, REGSTR_KEY_MADEUP, sizeof(REGSTR_KEY_MADEUP));
|
|
|
|
q = buffer + (sizeof(REGSTR_KEY_MADEUP) - sizeof(UNICODE_NULL))/sizeof(WCHAR);
|
|
|
|
p = ServiceKeyName->Buffer;
|
|
BufferEnd = (PWCHAR)((PUCHAR)p + ServiceKeyName->Length);
|
|
while(p != BufferEnd) {
|
|
if (*p == L' ') {
|
|
//
|
|
// replace ' ' with '_'
|
|
//
|
|
*q = L'_';
|
|
q++;
|
|
|
|
} else if ((*p < L' ') || (*p > (WCHAR)0x7F) || (*p == L',')) {
|
|
//
|
|
// replace invalid characters with '*' plus a character string
|
|
// representation of the hexadecimal digits.
|
|
//
|
|
int i, nibble;
|
|
|
|
*q = L'*';
|
|
q++;
|
|
|
|
for (i = 1; i <= 2*sizeof(WCHAR); i++) {
|
|
nibble = ((USHORT)((*p) >> (0x10 - 4*i)) & 0xF);
|
|
*q = nibble > 9 ? (WCHAR)(nibble - 10 + L'A') : (WCHAR)(nibble + L'0');
|
|
q++;
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// copy the existing character.
|
|
//
|
|
*q = *p;
|
|
q++;
|
|
}
|
|
p++;
|
|
}
|
|
|
|
*q = UNICODE_NULL;
|
|
|
|
//
|
|
// Upcase the resulting device id.
|
|
//
|
|
|
|
RtlUpcaseUnicodeString(MadeupNodeName, MadeupNodeName, FALSE);
|
|
|
|
//
|
|
// Sanity check to make sure that the device id we generated is valid. At
|
|
// this point, there should be absolutely no reason that it wouldn't be.
|
|
//
|
|
|
|
if (!PiFixupID(MadeupNodeName->Buffer, MAX_DEVICE_ID_LEN, FALSE, 0, NULL)) {
|
|
ASSERT(0);
|
|
RtlFreeUnicodeString(MadeupNodeName);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
PipConcatenateUnicodeStrings (
|
|
OUT PUNICODE_STRING Destination,
|
|
IN PUNICODE_STRING String1,
|
|
IN PUNICODE_STRING String2 OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine returns a buffer containing the concatenation of the
|
|
two specified strings. Since String2 is optional, this function may
|
|
also be used to make a copy of a unicode string. Paged pool space
|
|
is allocated for the destination string. Caller must release the
|
|
space once done with it.
|
|
|
|
Parameters:
|
|
|
|
Destination - Supplies a variable to receive the concatenated
|
|
UNICODE_STRING.
|
|
|
|
String1 - Supplies a pointer to the frist UNICODE_STRING.
|
|
|
|
String2 - Supplies an optional pointer to the second UNICODE_STRING.
|
|
|
|
Return Value:
|
|
|
|
Status code that indicates whether or not the function was successful.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
ULONG length;
|
|
|
|
PAGED_CODE();
|
|
|
|
length = String1->Length;
|
|
if (ARGUMENT_PRESENT(String2)) {
|
|
|
|
length += String2->Length;
|
|
}
|
|
status = IopAllocateUnicodeString(Destination,
|
|
(USHORT)length
|
|
);
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
RtlCopyUnicodeString(Destination, String1);
|
|
if (ARGUMENT_PRESENT(String2)) {
|
|
|
|
RtlAppendUnicodeStringToString(Destination, String2);
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
IopPrepareDriverLoading (
|
|
IN PUNICODE_STRING KeyName,
|
|
IN HANDLE KeyHandle,
|
|
IN PVOID ImageBase,
|
|
IN BOOLEAN IsFilter
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine first checks if the driver is loadable. If its a
|
|
PnP driver, it will always be loaded (we trust it to do the right
|
|
things.) If it is a legacy driver, we need to check if its device
|
|
has been disabled. Once we decide to load the driver, the Enum
|
|
subkey of the service node will be checked for duplicates, if any.
|
|
|
|
Parameters:
|
|
|
|
KeyName - Supplies a pointer to the driver's service key unicode string
|
|
|
|
KeyHandle - Supplies a handle to the driver service node in the registry
|
|
that describes the driver to be loaded.
|
|
|
|
Return Value:
|
|
|
|
The function value is the final status of the load operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
PKEY_VALUE_FULL_INFORMATION keyValueInformation;
|
|
ULONG tmp, count;
|
|
HANDLE serviceEnumHandle = NULL, sysEnumXxxHandle, controlHandle;
|
|
UNICODE_STRING unicodeKeyName, unicodeValueName;
|
|
BOOLEAN IsPlugPlayDriver;
|
|
PIMAGE_NT_HEADERS header;
|
|
GUID blockedDriverGuid;
|
|
|
|
header = RtlImageNtHeader(ImageBase);
|
|
status = STATUS_SUCCESS;
|
|
IsPlugPlayDriver = (header &&
|
|
(header->OptionalHeader.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_WDM_DRIVER))? TRUE : FALSE;
|
|
|
|
if (!IopIsAnyDeviceInstanceEnabled(KeyName, KeyHandle, (BOOLEAN)(IsPlugPlayDriver ? FALSE : TRUE))) {
|
|
|
|
if (!IsPlugPlayDriver) {
|
|
|
|
PiLockPnpRegistry(FALSE);
|
|
|
|
//
|
|
// First open registry ServiceKeyName\Enum branch
|
|
//
|
|
|
|
PiWstrToUnicodeString(&unicodeKeyName, REGSTR_KEY_ENUM);
|
|
status = IopCreateRegistryKeyEx( &serviceEnumHandle,
|
|
KeyHandle,
|
|
&unicodeKeyName,
|
|
KEY_ALL_ACCESS,
|
|
REG_OPTION_VOLATILE,
|
|
NULL
|
|
);
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Find out how many device instances listed in the ServiceName's
|
|
// Enum key.
|
|
//
|
|
|
|
count = 0;
|
|
status = IopGetRegistryValue ( serviceEnumHandle,
|
|
REGSTR_VALUE_COUNT,
|
|
&keyValueInformation);
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
if ( keyValueInformation->Type == REG_DWORD &&
|
|
keyValueInformation->DataLength >= sizeof(ULONG)) {
|
|
|
|
count = *(PULONG)KEY_VALUE_DATA(keyValueInformation);
|
|
|
|
}
|
|
|
|
ExFreePool(keyValueInformation);
|
|
|
|
}
|
|
if ( NT_SUCCESS(status) ||
|
|
status == STATUS_OBJECT_PATH_NOT_FOUND ||
|
|
status == STATUS_OBJECT_NAME_NOT_FOUND) {
|
|
|
|
if (count) {
|
|
|
|
status = STATUS_PLUGPLAY_NO_DEVICE;
|
|
|
|
} else {
|
|
|
|
//
|
|
// If there is no Enum key or instance under Enum for the
|
|
// legacy driver we will create a madeup node for it.
|
|
//
|
|
|
|
status = PipCreateMadeupNode( KeyName,
|
|
&sysEnumXxxHandle,
|
|
&unicodeKeyName,
|
|
&tmp,
|
|
TRUE);
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
RtlFreeUnicodeString(&unicodeKeyName);
|
|
|
|
//
|
|
// Create and set Control\ActiveService value
|
|
//
|
|
|
|
PiWstrToUnicodeString(&unicodeValueName, REGSTR_KEY_CONTROL);
|
|
status = IopCreateRegistryKeyEx( &controlHandle,
|
|
sysEnumXxxHandle,
|
|
&unicodeValueName,
|
|
KEY_ALL_ACCESS,
|
|
REG_OPTION_VOLATILE,
|
|
NULL
|
|
);
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
PiWstrToUnicodeString(&unicodeValueName, REGSTR_VAL_ACTIVESERVICE);
|
|
ZwSetValueKey( controlHandle,
|
|
&unicodeValueName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_SZ,
|
|
KeyName->Buffer,
|
|
KeyName->Length + sizeof(UNICODE_NULL));
|
|
ZwClose(controlHandle);
|
|
|
|
}
|
|
count++;
|
|
//
|
|
// Don't forget to update the "Count=" and "NextInstance=" value entries
|
|
//
|
|
|
|
PiWstrToUnicodeString(&unicodeValueName, REGSTR_VALUE_COUNT);
|
|
ZwSetValueKey( serviceEnumHandle,
|
|
&unicodeValueName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_DWORD,
|
|
&count,
|
|
sizeof(count));
|
|
|
|
PiWstrToUnicodeString(&unicodeValueName, REGSTR_VALUE_NEXT_INSTANCE);
|
|
ZwSetValueKey( serviceEnumHandle,
|
|
&unicodeValueName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_DWORD,
|
|
&count,
|
|
sizeof(count));
|
|
|
|
ZwClose(sysEnumXxxHandle);
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
|
|
ZwClose(serviceEnumHandle);
|
|
}
|
|
|
|
PiUnlockPnpRegistry();
|
|
}
|
|
}
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
RtlZeroMemory(&blockedDriverGuid, sizeof(GUID));
|
|
|
|
status = PpCheckInDriverDatabase(
|
|
KeyName,
|
|
KeyHandle,
|
|
ImageBase,
|
|
header->OptionalHeader.SizeOfImage,
|
|
IsFilter,
|
|
&blockedDriverGuid);
|
|
|
|
if (status == STATUS_DRIVER_BLOCKED ||
|
|
status == STATUS_DRIVER_BLOCKED_CRITICAL) {
|
|
//
|
|
// Notify the user-mode Plug and Play manager that a driver was just
|
|
// blocked.
|
|
//
|
|
PpSetBlockedDriverEvent(&blockedDriverGuid);
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
PipServiceInstanceToDeviceInstance (
|
|
IN HANDLE ServiceKeyHandle OPTIONAL,
|
|
IN PUNICODE_STRING ServiceKeyName OPTIONAL,
|
|
IN ULONG ServiceInstanceOrdinal,
|
|
OUT PUNICODE_STRING DeviceInstanceRegistryPath OPTIONAL,
|
|
OUT PHANDLE DeviceInstanceHandle OPTIONAL,
|
|
IN ACCESS_MASK DesiredAccess
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine reads the service node enum entry to find the desired device instance
|
|
under the System\Enum tree. It then optionally returns the registry path of the
|
|
specified device instance (relative to HKLM\System\Enum) and an open handle
|
|
to that registry key.
|
|
|
|
It is the caller's responsibility to close the handle returned if
|
|
DeviceInstanceHandle is supplied, and also to free the (PagedPool) memory
|
|
allocated for the unicode string buffer of DeviceInstanceRegistryPath, if
|
|
supplied.
|
|
|
|
Parameters:
|
|
|
|
ServiceKeyHandle - Optionally, supplies a handle to the driver service node in the
|
|
registry that controls this device instance. If this argument is not specified,
|
|
then ServiceKeyName is used to specify the service entry.
|
|
|
|
ServiceKeyName - Optionally supplies the name of the service entry that controls
|
|
the device instance. This must be specified if ServiceKeyHandle isn't given.
|
|
|
|
ServiceInstanceOrdinal - Supplies the instance value under the service entry's
|
|
volatile Enum subkey that references the desired device instance.
|
|
|
|
DeviceInstanceRegistryPath - Optionally, supplies a pointer to a unicode string
|
|
that will be initialized with the registry path (relative to HKLM\System\Enum)
|
|
to the device instance key.
|
|
|
|
DeviceInstanceHandle - Optionally, supplies a pointer to a variable that will
|
|
receive a handle to the opened device instance registry key.
|
|
|
|
DesiredAccess - If DeviceInstanceHandle is specified (i.e., the device instance
|
|
key is to be opened), then this variable specifies the access that is needed
|
|
to this key.
|
|
|
|
Return Value:
|
|
|
|
NT status code indicating whether the function was successful.
|
|
|
|
--*/
|
|
|
|
{
|
|
WCHAR unicodeBuffer[20];
|
|
UNICODE_STRING unicodeKeyName;
|
|
NTSTATUS status;
|
|
HANDLE handle;
|
|
PKEY_VALUE_FULL_INFORMATION keyValueInformation;
|
|
|
|
//
|
|
// Open registry ServiceKeyName\Enum branch
|
|
//
|
|
if(ARGUMENT_PRESENT(ServiceKeyHandle)) {
|
|
|
|
PiWstrToUnicodeString(&unicodeKeyName, REGSTR_KEY_ENUM);
|
|
status = IopOpenRegistryKeyEx( &handle,
|
|
ServiceKeyHandle,
|
|
&unicodeKeyName,
|
|
KEY_READ
|
|
);
|
|
} else {
|
|
|
|
status = PipOpenServiceEnumKeys(ServiceKeyName,
|
|
KEY_READ,
|
|
NULL,
|
|
&handle,
|
|
FALSE
|
|
);
|
|
}
|
|
|
|
if (!NT_SUCCESS( status )) {
|
|
|
|
//
|
|
// There is no registry key for the ServiceKeyName\Enum information.
|
|
//
|
|
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Read a path to System\Enum hardware tree branch specified by the service
|
|
// instance ordinal
|
|
//
|
|
|
|
StringCbPrintfW(unicodeBuffer, sizeof(unicodeBuffer), REGSTR_VALUE_STANDARD_ULONG_FORMAT, ServiceInstanceOrdinal);
|
|
status = IopGetRegistryValue ( handle,
|
|
unicodeBuffer,
|
|
&keyValueInformation
|
|
);
|
|
|
|
ZwClose(handle);
|
|
if (!NT_SUCCESS( status )) {
|
|
return status;
|
|
} else {
|
|
if(keyValueInformation->Type == REG_SZ) {
|
|
IopRegistryDataToUnicodeString(&unicodeKeyName,
|
|
(PWSTR)KEY_VALUE_DATA(keyValueInformation),
|
|
keyValueInformation->DataLength
|
|
);
|
|
if(!unicodeKeyName.Length) {
|
|
status = STATUS_OBJECT_PATH_NOT_FOUND;
|
|
}
|
|
} else {
|
|
status = STATUS_INVALID_PLUGPLAY_DEVICE_PATH;
|
|
}
|
|
|
|
if(!NT_SUCCESS(status)) {
|
|
goto PrepareForReturn;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the DeviceInstanceHandle argument was specified, open the device instance
|
|
// key under HKLM\System\CurrentControlSet\Enum
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(DeviceInstanceHandle)) {
|
|
|
|
status = IopOpenRegistryKeyEx( &handle,
|
|
NULL,
|
|
&CmRegistryMachineSystemCurrentControlSetEnumName,
|
|
KEY_READ
|
|
);
|
|
|
|
if (NT_SUCCESS( status )) {
|
|
|
|
status = IopOpenRegistryKeyEx( DeviceInstanceHandle,
|
|
handle,
|
|
&unicodeKeyName,
|
|
DesiredAccess
|
|
);
|
|
ZwClose(handle);
|
|
}
|
|
|
|
if (!NT_SUCCESS( status )) {
|
|
goto PrepareForReturn;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the DeviceInstanceRegistryPath argument was specified, then store a
|
|
// copy of the device instance path in the supplied unicode string variable.
|
|
//
|
|
if (ARGUMENT_PRESENT(DeviceInstanceRegistryPath)) {
|
|
|
|
status = PipConcatenateUnicodeStrings( DeviceInstanceRegistryPath,
|
|
&unicodeKeyName,
|
|
NULL);
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
if(ARGUMENT_PRESENT(DeviceInstanceHandle)) {
|
|
ZwClose(*DeviceInstanceHandle);
|
|
}
|
|
}
|
|
}
|
|
|
|
PrepareForReturn:
|
|
|
|
ExFreePool(keyValueInformation);
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
IopOpenRegistryKeyEx(
|
|
OUT PHANDLE Handle,
|
|
IN HANDLE BaseHandle OPTIONAL,
|
|
IN PUNICODE_STRING KeyName,
|
|
IN ACCESS_MASK DesiredAccess
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Opens a registry key using the name passed in based at the BaseHandle node.
|
|
This name may specify a key that is actually a registry path.
|
|
|
|
Arguments:
|
|
|
|
Handle - Pointer to the handle which will contain the registry key that
|
|
was opened.
|
|
|
|
BaseHandle - Optional handle to the base path from which the key must be
|
|
opened. If this parameter is specified, then KeyName must be a relative
|
|
path.
|
|
|
|
KeyName - Name of the Key that must be opened/created (possibly a registry path)
|
|
|
|
DesiredAccess - Specifies the desired access that the caller needs to
|
|
the key.
|
|
|
|
Return Value:
|
|
|
|
The function value is the final status of the operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
|
|
PAGED_CODE();
|
|
|
|
*Handle = NULL;
|
|
|
|
InitializeObjectAttributes( &objectAttributes,
|
|
KeyName,
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
|
BaseHandle,
|
|
(PSECURITY_DESCRIPTOR) NULL
|
|
);
|
|
//
|
|
// Simply attempt to open the path, as specified.
|
|
//
|
|
return ZwOpenKey( Handle, DesiredAccess, &objectAttributes );
|
|
}
|
|
|
|
NTSTATUS
|
|
IopCreateRegistryKeyEx(
|
|
OUT PHANDLE Handle,
|
|
IN HANDLE BaseHandle OPTIONAL,
|
|
IN PUNICODE_STRING KeyName,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN ULONG CreateOptions,
|
|
OUT PULONG Disposition OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Opens or creates a registry key using the name
|
|
passed in based at the BaseHandle node. This name may specify a key
|
|
that is actually a registry path, in which case each intermediate subkey
|
|
will be created (if Create is TRUE).
|
|
|
|
NOTE: Creating a registry path (i.e., more than one of the keys in the path
|
|
do not presently exist) requires that a BaseHandle be specified.
|
|
|
|
Arguments:
|
|
|
|
Handle - Pointer to the handle which will contain the registry key that
|
|
was opened.
|
|
|
|
BaseHandle - Optional handle to the base path from which the key must be opened.
|
|
If KeyName specifies a registry path that must be created, then this parameter
|
|
must be specified, and KeyName must be a relative path.
|
|
|
|
KeyName - Name of the Key that must be opened/created (possibly a registry path)
|
|
|
|
DesiredAccess - Specifies the desired access that the caller needs to
|
|
the key.
|
|
|
|
CreateOptions - Options passed to ZwCreateKey.
|
|
|
|
Disposition - If Create is TRUE, this optional pointer receives a ULONG indicating
|
|
whether the key was newly created:
|
|
|
|
REG_CREATED_NEW_KEY - A new Registry Key was created
|
|
REG_OPENED_EXISTING_KEY - An existing Registry Key was opened
|
|
|
|
Return Value:
|
|
|
|
The function value is the final status of the operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
ULONG disposition, baseHandleIndex = 0, keyHandleIndex = 1, closeBaseHandle;
|
|
HANDLE handles[2];
|
|
BOOLEAN continueParsing;
|
|
PWCHAR pathEndPtr, pathCurPtr, pathBeginPtr;
|
|
ULONG pathComponentLength;
|
|
UNICODE_STRING unicodeString;
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
*Handle= NULL;
|
|
|
|
InitializeObjectAttributes( &objectAttributes,
|
|
KeyName,
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
|
BaseHandle,
|
|
(PSECURITY_DESCRIPTOR) NULL
|
|
);
|
|
//
|
|
// Attempt to create the path as specified. We have to try it this
|
|
// way first, because it allows us to create a key without a BaseHandle
|
|
// (if only the last component of the registry path is not present).
|
|
//
|
|
status = ZwCreateKey(&(handles[keyHandleIndex]),
|
|
DesiredAccess,
|
|
&objectAttributes,
|
|
0,
|
|
(PUNICODE_STRING) NULL,
|
|
CreateOptions,
|
|
&disposition
|
|
);
|
|
|
|
if (status == STATUS_OBJECT_NAME_NOT_FOUND && ARGUMENT_PRESENT(BaseHandle)) {
|
|
//
|
|
// If we get to here, then there must be more than one element of the
|
|
// registry path that does not currently exist. We will now parse the
|
|
// specified path, extracting each component and doing a ZwCreateKey on it.
|
|
//
|
|
handles[baseHandleIndex] = NULL;
|
|
handles[keyHandleIndex] = BaseHandle;
|
|
closeBaseHandle = 0;
|
|
continueParsing = TRUE;
|
|
pathBeginPtr = KeyName->Buffer;
|
|
pathEndPtr = (PWCHAR)((PCHAR)pathBeginPtr + KeyName->Length);
|
|
status = STATUS_SUCCESS;
|
|
|
|
while(continueParsing) {
|
|
//
|
|
// There's more to do, so close the previous base handle (if necessary),
|
|
// and replace it with the current key handle.
|
|
//
|
|
if(closeBaseHandle > 1) {
|
|
ZwClose(handles[baseHandleIndex]);
|
|
}
|
|
baseHandleIndex = keyHandleIndex;
|
|
keyHandleIndex = (keyHandleIndex + 1) & 1; // toggle between 0 and 1.
|
|
handles[keyHandleIndex] = NULL;
|
|
|
|
//
|
|
// Extract next component out of the specified registry path.
|
|
//
|
|
for (pathCurPtr = pathBeginPtr;
|
|
((pathCurPtr < pathEndPtr) && (*pathCurPtr != OBJ_NAME_PATH_SEPARATOR));
|
|
pathCurPtr++);
|
|
|
|
pathComponentLength = (ULONG)((PCHAR)pathCurPtr - (PCHAR)pathBeginPtr);
|
|
if (pathComponentLength != 0) {
|
|
//
|
|
// Then we have a non-empty path component (key name). Attempt
|
|
// to create this key.
|
|
//
|
|
unicodeString.Buffer = pathBeginPtr;
|
|
unicodeString.Length = unicodeString.MaximumLength = (USHORT)pathComponentLength;
|
|
|
|
InitializeObjectAttributes(&objectAttributes,
|
|
&unicodeString,
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
|
handles[baseHandleIndex],
|
|
(PSECURITY_DESCRIPTOR) NULL
|
|
);
|
|
status = ZwCreateKey(&(handles[keyHandleIndex]),
|
|
DesiredAccess,
|
|
&objectAttributes,
|
|
0,
|
|
(PUNICODE_STRING) NULL,
|
|
CreateOptions,
|
|
&disposition
|
|
);
|
|
if(NT_SUCCESS(status)) {
|
|
//
|
|
// Increment the closeBaseHandle value, which basically tells us whether
|
|
// the BaseHandle passed in has been 'shifted out' of our way, so that
|
|
// we should start closing our base handles when we're finished with them.
|
|
//
|
|
closeBaseHandle++;
|
|
} else {
|
|
continueParsing = FALSE;
|
|
continue;
|
|
}
|
|
} else {
|
|
//
|
|
// Either a path separator ('\') was included at the beginning of
|
|
// the path, or we hit 2 consecutive separators.
|
|
//
|
|
status = STATUS_INVALID_PARAMETER;
|
|
continueParsing = FALSE;
|
|
continue;
|
|
}
|
|
|
|
if((pathCurPtr == pathEndPtr) ||
|
|
((pathBeginPtr = pathCurPtr + 1) == pathEndPtr)) {
|
|
//
|
|
// Then we've reached the end of the path
|
|
//
|
|
continueParsing = FALSE;
|
|
}
|
|
}
|
|
|
|
if(closeBaseHandle > 1) {
|
|
ZwClose(handles[baseHandleIndex]);
|
|
}
|
|
}
|
|
|
|
if(NT_SUCCESS(status)) {
|
|
*Handle = handles[keyHandleIndex];
|
|
|
|
if(ARGUMENT_PRESENT(Disposition)) {
|
|
*Disposition = disposition;
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
PipOpenServiceEnumKeys (
|
|
IN PUNICODE_STRING ServiceKeyName,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
OUT PHANDLE ServiceHandle OPTIONAL,
|
|
OUT PHANDLE ServiceEnumHandle OPTIONAL,
|
|
IN BOOLEAN CreateEnum
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine opens the HKEY_LOCAL_MACHINE\CurrentControlSet\Services\
|
|
ServiceKeyName and its Enum subkey and returns handles for both key.
|
|
It is caller's responsibility to close the returned handles.
|
|
|
|
Arguments:
|
|
|
|
ServiceKeyName - Supplies a pointer to the name of the subkey in the
|
|
system service list (HKEY_LOCAL_MACHINE\CurrentControlSet\Services)
|
|
that caused the driver to load. This is the RegistryPath parameter
|
|
to the DriverEntry routine.
|
|
|
|
DesiredAccess - Specifies the desired access to the keys.
|
|
|
|
ServiceHandle - Supplies a variable to receive a handle to ServiceKeyName.
|
|
A NULL ServiceHandle indicates caller does not want need the handle to
|
|
the ServiceKeyName.
|
|
|
|
ServiceEnumHandle - Supplies a variable to receive a handle to ServiceKeyName\Enum.
|
|
A NULL ServiceEnumHandle indicates caller does not need the handle to
|
|
the ServiceKeyName\Enum.
|
|
|
|
CreateEnum - Supplies a BOOLEAN variable to indicate should the Enum subkey be
|
|
created if not present.
|
|
|
|
Return Value:
|
|
|
|
status
|
|
|
|
--*/
|
|
|
|
{
|
|
HANDLE handle, serviceHandle, enumHandle;
|
|
UNICODE_STRING enumName;
|
|
NTSTATUS status;
|
|
|
|
//
|
|
// Open System\CurrentControlSet\Services
|
|
//
|
|
|
|
status = IopOpenRegistryKeyEx( &handle,
|
|
NULL,
|
|
&CmRegistryMachineSystemCurrentControlSetServices,
|
|
DesiredAccess
|
|
);
|
|
|
|
if (!NT_SUCCESS( status )) {
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Open the registry ServiceKeyName key.
|
|
//
|
|
|
|
status = IopOpenRegistryKeyEx( &serviceHandle,
|
|
handle,
|
|
ServiceKeyName,
|
|
DesiredAccess
|
|
);
|
|
|
|
ZwClose(handle);
|
|
if (!NT_SUCCESS( status )) {
|
|
|
|
//
|
|
// There is no registry key for the ServiceKeyName information.
|
|
//
|
|
|
|
return status;
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT(ServiceEnumHandle) || CreateEnum) {
|
|
|
|
//
|
|
// Open registry ServiceKeyName\Enum branch if caller wants
|
|
// the handle or wants to create it.
|
|
//
|
|
|
|
PiWstrToUnicodeString(&enumName, REGSTR_KEY_ENUM);
|
|
|
|
if (CreateEnum) {
|
|
status = IopCreateRegistryKeyEx( &enumHandle,
|
|
serviceHandle,
|
|
&enumName,
|
|
DesiredAccess,
|
|
REG_OPTION_VOLATILE,
|
|
NULL
|
|
);
|
|
} else {
|
|
status = IopOpenRegistryKeyEx( &enumHandle,
|
|
serviceHandle,
|
|
&enumName,
|
|
DesiredAccess
|
|
);
|
|
|
|
}
|
|
|
|
if (!NT_SUCCESS( status )) {
|
|
|
|
//
|
|
// There is no registry key for the ServiceKeyName\Enum information.
|
|
//
|
|
|
|
ZwClose(serviceHandle);
|
|
return status;
|
|
}
|
|
if (ARGUMENT_PRESENT(ServiceEnumHandle)) {
|
|
*ServiceEnumHandle = enumHandle;
|
|
} else {
|
|
ZwClose(enumHandle);
|
|
}
|
|
}
|
|
|
|
//
|
|
// if caller wants to have the ServiceKey handle, we return it. Otherwise
|
|
// we close it.
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(ServiceHandle)) {
|
|
*ServiceHandle = serviceHandle;
|
|
} else {
|
|
ZwClose(serviceHandle);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
IopGetDeviceInstanceCsConfigFlags(
|
|
IN PUNICODE_STRING DeviceInstance,
|
|
OUT PULONG CsConfigFlags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine retrieves the csconfig flags for the specified device.
|
|
|
|
Arguments:
|
|
|
|
DeviceInstance - Supplies a pointer to the devnode's instance path
|
|
|
|
CsConfigFlags - Supplies a variable to receive the device's CsConfigFlags
|
|
|
|
Return Value:
|
|
|
|
status
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
HANDLE handle1, handle2;
|
|
UNICODE_STRING tempUnicodeString;
|
|
PKEY_VALUE_FULL_INFORMATION keyValueInformation;
|
|
|
|
PAGED_CODE();
|
|
|
|
*CsConfigFlags = 0;
|
|
|
|
status = IopOpenRegistryKeyEx( &handle1,
|
|
NULL,
|
|
&CmRegistryMachineSystemCurrentControlSetHardwareProfilesCurrent,
|
|
KEY_READ
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Now, we must open the System\CCS\Enum key under this.
|
|
//
|
|
//
|
|
// Open system\CurrentControlSet under current hardware profile key
|
|
//
|
|
|
|
PiWstrToUnicodeString(&tempUnicodeString, REGSTR_PATH_CURRENTCONTROLSET);
|
|
status = IopOpenRegistryKeyEx( &handle2,
|
|
handle1,
|
|
&tempUnicodeString,
|
|
KEY_READ
|
|
);
|
|
ZwClose(handle1);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
return status;
|
|
}
|
|
|
|
PiWstrToUnicodeString(&tempUnicodeString, REGSTR_KEY_ENUM);
|
|
|
|
status = IopOpenRegistryKeyEx( &handle1,
|
|
handle2,
|
|
&tempUnicodeString,
|
|
KEY_READ
|
|
);
|
|
|
|
ZwClose(handle2);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
status = IopOpenRegistryKeyEx( &handle2,
|
|
handle1,
|
|
DeviceInstance,
|
|
KEY_READ
|
|
);
|
|
|
|
ZwClose(handle1);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
status = IopGetRegistryValue( handle2,
|
|
REGSTR_VALUE_CSCONFIG_FLAGS,
|
|
&keyValueInformation
|
|
);
|
|
|
|
ZwClose(handle2);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
if ((keyValueInformation->Type == REG_DWORD) &&
|
|
(keyValueInformation->DataLength >= sizeof(ULONG))) {
|
|
|
|
*CsConfigFlags = *(PULONG)KEY_VALUE_DATA(keyValueInformation);
|
|
}
|
|
ExFreePool(keyValueInformation);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
PipGetServiceInstanceCsConfigFlags(
|
|
IN PUNICODE_STRING ServiceKeyName,
|
|
IN ULONG Instance,
|
|
OUT PULONG CsConfigFlags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine retrieves the csconfig flags for the specified device
|
|
which is specified by the instance number under ServiceKeyName\Enum.
|
|
|
|
Arguments:
|
|
|
|
ServiceKeyName - Supplies a pointer to the name of the subkey in the
|
|
system service list (HKEY_LOCAL_MACHINE\CurrentControlSet\Services)
|
|
that caused the driver to load.
|
|
|
|
Instance - Supplies the instance value under ServiceKeyName\Enum key
|
|
|
|
CsConfigFlags - Supplies a variable to receive the device's CsConfigFlags
|
|
|
|
Return Value:
|
|
|
|
status
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
HANDLE handle;
|
|
PKEY_VALUE_FULL_INFORMATION keyValueInformation;
|
|
|
|
PAGED_CODE();
|
|
|
|
*CsConfigFlags = 0;
|
|
|
|
status = IopOpenCurrentHwProfileDeviceInstanceKey(&handle,
|
|
ServiceKeyName,
|
|
Instance,
|
|
KEY_READ,
|
|
FALSE
|
|
);
|
|
if(NT_SUCCESS(status)) {
|
|
status = IopGetRegistryValue(handle,
|
|
REGSTR_VALUE_CSCONFIG_FLAGS,
|
|
&keyValueInformation
|
|
);
|
|
if(NT_SUCCESS(status)) {
|
|
if((keyValueInformation->Type == REG_DWORD) &&
|
|
(keyValueInformation->DataLength >= sizeof(ULONG))) {
|
|
*CsConfigFlags = *(PULONG)KEY_VALUE_DATA(keyValueInformation);
|
|
}
|
|
ExFreePool(keyValueInformation);
|
|
}
|
|
ZwClose(handle);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
IopOpenCurrentHwProfileDeviceInstanceKey(
|
|
OUT PHANDLE Handle,
|
|
IN PUNICODE_STRING ServiceKeyName,
|
|
IN ULONG Instance,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN BOOLEAN Create
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine sets the csconfig flags for the specified device
|
|
which is specified by the instance number under ServiceKeyName\Enum.
|
|
|
|
Arguments:
|
|
|
|
ServiceKeyName - Supplies a pointer to the name of the subkey in the
|
|
system service list (HKEY_LOCAL_MACHINE\CurrentControlSet\Services)
|
|
that caused the driver to load. This is the RegistryPath parameter
|
|
to the DriverEntry routine.
|
|
|
|
Instance - Supplies the instance value under ServiceKeyName\Enum key
|
|
|
|
DesiredAccess - Specifies the desired access that the caller needs to
|
|
the key.
|
|
|
|
Create - Determines if the key is to be created if it does not exist.
|
|
|
|
Return Value:
|
|
|
|
status
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
UNICODE_STRING tempUnicodeString;
|
|
HANDLE profileHandle, profileEnumHandle, tmpHandle;
|
|
|
|
//
|
|
// See if we can open current hardware profile
|
|
//
|
|
|
|
if (Create) {
|
|
status = IopCreateRegistryKeyEx( &profileHandle,
|
|
NULL,
|
|
&CmRegistryMachineSystemCurrentControlSetHardwareProfilesCurrent,
|
|
KEY_READ,
|
|
REG_OPTION_NON_VOLATILE,
|
|
NULL
|
|
);
|
|
} else {
|
|
status = IopOpenRegistryKeyEx( &profileHandle,
|
|
NULL,
|
|
&CmRegistryMachineSystemCurrentControlSetHardwareProfilesCurrent,
|
|
KEY_READ
|
|
);
|
|
}
|
|
|
|
if(NT_SUCCESS(status)) {
|
|
//
|
|
// Now, we must open the System\CCS\Enum key under this.
|
|
//
|
|
//
|
|
// Open system\CurrentControlSet under current hardware profile key
|
|
//
|
|
|
|
PiWstrToUnicodeString(&tempUnicodeString, REGSTR_PATH_CURRENTCONTROLSET);
|
|
status = IopOpenRegistryKeyEx( &tmpHandle,
|
|
profileHandle,
|
|
&tempUnicodeString,
|
|
DesiredAccess
|
|
);
|
|
ZwClose(profileHandle);
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
PiWstrToUnicodeString(&tempUnicodeString, REGSTR_KEY_ENUM);
|
|
|
|
if (Create) {
|
|
status = IopCreateRegistryKeyEx( &profileEnumHandle,
|
|
tmpHandle,
|
|
&tempUnicodeString,
|
|
KEY_READ,
|
|
REG_OPTION_NON_VOLATILE,
|
|
NULL
|
|
);
|
|
} else {
|
|
status = IopOpenRegistryKeyEx( &profileEnumHandle,
|
|
tmpHandle,
|
|
&tempUnicodeString,
|
|
KEY_READ
|
|
);
|
|
}
|
|
|
|
ZwClose(tmpHandle);
|
|
if(NT_SUCCESS(status)) {
|
|
|
|
status = PipServiceInstanceToDeviceInstance(NULL,
|
|
ServiceKeyName,
|
|
Instance,
|
|
&tempUnicodeString,
|
|
NULL,
|
|
0
|
|
);
|
|
if (NT_SUCCESS(status)) {
|
|
if (Create) {
|
|
status = IopCreateRegistryKeyEx( Handle,
|
|
profileEnumHandle,
|
|
&tempUnicodeString,
|
|
DesiredAccess,
|
|
REG_OPTION_NON_VOLATILE,
|
|
NULL
|
|
);
|
|
} else {
|
|
status = IopOpenRegistryKeyEx( Handle,
|
|
profileEnumHandle,
|
|
&tempUnicodeString,
|
|
DesiredAccess
|
|
);
|
|
}
|
|
RtlFreeUnicodeString(&tempUnicodeString);
|
|
}
|
|
ZwClose(profileEnumHandle);
|
|
}
|
|
}
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
PipApplyFunctionToSubKeys(
|
|
IN HANDLE BaseHandle OPTIONAL,
|
|
IN PUNICODE_STRING KeyName OPTIONAL,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN ULONG Flags,
|
|
IN PIOP_SUBKEY_CALLBACK_ROUTINE SubKeyCallbackRoutine,
|
|
IN OUT PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine enumerates all subkeys under the specified key, and calls
|
|
the specified callback routine for each subkey.
|
|
|
|
Arguments:
|
|
|
|
BaseHandle - Optional handle to the base registry path. If KeyName is also
|
|
specified, then KeyName represents a subkey under this path. If KeyName
|
|
is not specified, the subkeys are enumerated under this handle. If this
|
|
parameter is not specified, then the full path to the base key must be
|
|
given in KeyName.
|
|
|
|
KeyName - Optional name of the key whose subkeys are to be enumerated.
|
|
|
|
DesiredAccess - Specifies the desired access that the callback routine
|
|
needs to the subkeys. If no desired access is specified (i.e.,
|
|
DesiredAccess is zero), then no handle will be opened for the
|
|
subkeys, and the callback will be passed a NULL for its SubKeyHandle
|
|
parameter.
|
|
|
|
Flags - Controls the behavior of subkey enumeration. Currently, the
|
|
following flags are defined:
|
|
|
|
FUNCTIONSUBKEY_FLAG_IGNORE_NON_CRITICAL_ERRORS - Specifies whether this
|
|
function should immediately terminate on all errors, or only on
|
|
critical ones. An example of a non-critical error is when an
|
|
enumerated subkey cannot be opened for the desired access.
|
|
|
|
FUNCTION_SUBKEY_DELETE_SUBKEYS - Specifies that each subkey should be
|
|
deleted after the specified SubKeyCallBackRoutine has been performed
|
|
on it. Note that this is NOT a recursive delete on each of the
|
|
subkeys, just an attempt to delete the subkey itself. It the subkey
|
|
contains children, this will fail.
|
|
|
|
SubKeyCallbackRoutine - Supplies a pointer to a function that will
|
|
be called for each subkey found under the
|
|
specified key. The prototype of the function
|
|
is as follows:
|
|
|
|
typedef BOOLEAN (*PIOP_SUBKEY_CALLBACK_ROUTINE) (
|
|
IN HANDLE SubKeyHandle,
|
|
IN PUNICODE_STRING SubKeyName,
|
|
IN OUT PVOID Context
|
|
);
|
|
|
|
where SubKeyHandle is the handle to an enumerated subkey under the
|
|
specified key, SubKeyName is its name, and Context is a pointer to
|
|
user-defined data.
|
|
|
|
This function should return TRUE to continue enumeration, or
|
|
FALSE to terminate it.
|
|
|
|
Context - Supplies a pointer to user-defined data that will be passed
|
|
in to the callback routine at each subkey invocation.
|
|
|
|
Return Value:
|
|
|
|
NT status code indicating whether the subkeys were successfully
|
|
enumerated. Note that this does not provide information on the
|
|
success or failure of the callback routine--if desired, this
|
|
information should be stored in the Context structure.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
BOOLEAN CloseHandle = FALSE, ContinueEnumeration;
|
|
HANDLE Handle, SubKeyHandle;
|
|
ULONG i, RequiredBufferLength;
|
|
PKEY_BASIC_INFORMATION KeyInformation = NULL;
|
|
// Use an initial key name buffer size large enough for a 20-character key
|
|
// (+ terminating NULL)
|
|
ULONG KeyInformationLength = sizeof(KEY_BASIC_INFORMATION) + (20 * sizeof(WCHAR));
|
|
UNICODE_STRING SubKeyName;
|
|
|
|
if(ARGUMENT_PRESENT(KeyName)) {
|
|
|
|
Status = IopOpenRegistryKeyEx( &Handle,
|
|
BaseHandle,
|
|
KeyName,
|
|
KEY_READ
|
|
);
|
|
if(!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
} else {
|
|
CloseHandle = TRUE;
|
|
}
|
|
|
|
} else {
|
|
|
|
Handle = BaseHandle;
|
|
}
|
|
|
|
//
|
|
// Enumerate the subkeys until we run out of them.
|
|
//
|
|
i = 0;
|
|
SubKeyHandle = NULL;
|
|
|
|
for ( ; ; ) {
|
|
|
|
if (!KeyInformation) {
|
|
|
|
KeyInformation = (PKEY_BASIC_INFORMATION)ExAllocatePool(PagedPool,
|
|
KeyInformationLength
|
|
);
|
|
if (!KeyInformation) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
break;
|
|
}
|
|
}
|
|
|
|
Status = ZwEnumerateKey(Handle,
|
|
i,
|
|
KeyBasicInformation,
|
|
KeyInformation,
|
|
KeyInformationLength,
|
|
&RequiredBufferLength
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
if (Status == STATUS_BUFFER_OVERFLOW ||
|
|
Status == STATUS_BUFFER_TOO_SMALL) {
|
|
//
|
|
// Try again with larger buffer.
|
|
//
|
|
ExFreePool(KeyInformation);
|
|
KeyInformation = NULL;
|
|
KeyInformationLength = RequiredBufferLength;
|
|
continue;
|
|
|
|
} else {
|
|
|
|
if (Status == STATUS_NO_MORE_ENTRIES) {
|
|
//
|
|
// No more subkeys.
|
|
//
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
//
|
|
// break out of loop
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Initialize a unicode string with this key name. Note that this string
|
|
// WILL NOT be NULL-terminated.
|
|
//
|
|
SubKeyName.Length = SubKeyName.MaximumLength = (USHORT)KeyInformation->NameLength;
|
|
SubKeyName.Buffer = KeyInformation->Name;
|
|
|
|
//
|
|
// If DesiredAccess is non-zero, open a handle to this subkey.
|
|
//
|
|
if (DesiredAccess) {
|
|
Status = IopOpenRegistryKeyEx( &SubKeyHandle,
|
|
Handle,
|
|
&SubKeyName,
|
|
DesiredAccess
|
|
);
|
|
if (!NT_SUCCESS(Status)) {
|
|
//
|
|
// This is a non-critical error.
|
|
//
|
|
if(Flags & FUNCTIONSUBKEY_FLAG_IGNORE_NON_CRITICAL_ERRORS) {
|
|
goto ContinueWithNextSubKey;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Invoke the supplied callback function for this subkey.
|
|
//
|
|
ContinueEnumeration = SubKeyCallbackRoutine(SubKeyHandle, &SubKeyName, Context);
|
|
|
|
if (DesiredAccess) {
|
|
if (ContinueEnumeration &&
|
|
(Flags & FUNCTIONSUBKEY_FLAG_DELETE_SUBKEYS)) {
|
|
//
|
|
// Delete the key when asked to, only if the callback routine
|
|
// was successful, otherwise we may not be able to.
|
|
//
|
|
Status = ZwDeleteKey(SubKeyHandle);
|
|
}
|
|
ZwClose(SubKeyHandle);
|
|
}
|
|
|
|
if(!ContinueEnumeration) {
|
|
//
|
|
// Enumeration has been aborted.
|
|
//
|
|
Status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
}
|
|
|
|
ContinueWithNextSubKey:
|
|
if (!(Flags & FUNCTIONSUBKEY_FLAG_DELETE_SUBKEYS)) {
|
|
//
|
|
// Only increment the enumeration index for non-deleted subkeys
|
|
//
|
|
i++;
|
|
}
|
|
}
|
|
|
|
if(KeyInformation) {
|
|
ExFreePool(KeyInformation);
|
|
}
|
|
|
|
if(CloseHandle) {
|
|
ZwClose(Handle);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
PipRegMultiSzToUnicodeStrings(
|
|
IN PKEY_VALUE_FULL_INFORMATION KeyValueInformation,
|
|
OUT PUNICODE_STRING *UnicodeStringList,
|
|
OUT PULONG UnicodeStringCount
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine takes a KEY_VALUE_FULL_INFORMATION structure containing
|
|
a REG_MULTI_SZ value, and allocates an array of UNICODE_STRINGs,
|
|
initializing each one to a copy of one of the strings in the value entry.
|
|
All the resulting UNICODE_STRINGs will be NULL terminated
|
|
(MaximumLength = Length + sizeof(UNICODE_NULL)).
|
|
|
|
It is the responsibility of the caller to free the buffers for each
|
|
unicode string, as well as the buffer containing the UNICODE_STRING
|
|
array. This may be done by calling PipFreeUnicodeStringList.
|
|
|
|
Arguments:
|
|
|
|
KeyValueInformation - Supplies the buffer containing the REG_MULTI_SZ
|
|
value entry data.
|
|
|
|
UnicodeStringList - Receives a pointer to an array of UNICODE_STRINGs, each
|
|
initialized with a copy of one of the strings in the REG_MULTI_SZ.
|
|
|
|
UnicodeStringCount - Receives the number of strings in the
|
|
UnicodeStringList.
|
|
|
|
Returns:
|
|
|
|
NT status code indicating whether the function was successful.
|
|
|
|
NOTE: This function is only available during INIT time!
|
|
|
|
--*/
|
|
|
|
{
|
|
PWCHAR p, BufferEnd, StringStart;
|
|
ULONG StringCount, i, StringLength;
|
|
|
|
//
|
|
// First, make sure this is really a REG_MULTI_SZ value.
|
|
//
|
|
if(KeyValueInformation->Type != REG_MULTI_SZ) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Make a preliminary pass through the buffer to count the number of strings
|
|
// There will always be at least one string returned (possibly empty).
|
|
//
|
|
// FUTURE: Make this robust against odd length buffers.
|
|
//
|
|
StringCount = 0;
|
|
p = (PWCHAR)KEY_VALUE_DATA(KeyValueInformation);
|
|
BufferEnd = (PWCHAR)((PUCHAR)p + KeyValueInformation->DataLength);
|
|
while(p != BufferEnd) {
|
|
if(!*p) {
|
|
StringCount++;
|
|
if(((p + 1) == BufferEnd) || !*(p + 1)) {
|
|
break;
|
|
}
|
|
}
|
|
p++;
|
|
}
|
|
if(p == BufferEnd) {
|
|
StringCount++;
|
|
}
|
|
|
|
*UnicodeStringList = ExAllocatePool(PagedPool, sizeof(UNICODE_STRING) * StringCount);
|
|
if(!(*UnicodeStringList)) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Now, make a second pass through the buffer making copies of each string.
|
|
//
|
|
i = 0;
|
|
StringStart = p = (PWCHAR)KEY_VALUE_DATA(KeyValueInformation);
|
|
while(p != BufferEnd) {
|
|
if(!*p) {
|
|
StringLength = (ULONG)((PUCHAR)p - (PUCHAR)StringStart) + sizeof(UNICODE_NULL);
|
|
(*UnicodeStringList)[i].Buffer = ExAllocatePool(PagedPool, StringLength);
|
|
|
|
if(!((*UnicodeStringList)[i].Buffer)) {
|
|
PipFreeUnicodeStringList(*UnicodeStringList, i);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlCopyMemory((*UnicodeStringList)[i].Buffer, StringStart, StringLength);
|
|
|
|
(*UnicodeStringList)[i].Length =
|
|
((*UnicodeStringList)[i].MaximumLength = (USHORT)StringLength)
|
|
- sizeof(UNICODE_NULL);
|
|
|
|
i++;
|
|
|
|
if(((p + 1) == BufferEnd) || !*(p + 1)) {
|
|
break;
|
|
} else {
|
|
StringStart = p + 1;
|
|
}
|
|
}
|
|
p++;
|
|
}
|
|
if(p == BufferEnd) {
|
|
StringLength = (ULONG)((PUCHAR)p - (PUCHAR)StringStart);
|
|
(*UnicodeStringList)[i].Buffer = ExAllocatePool(PagedPool,
|
|
StringLength + sizeof(UNICODE_NULL)
|
|
);
|
|
if(!((*UnicodeStringList)[i].Buffer)) {
|
|
PipFreeUnicodeStringList(*UnicodeStringList, i);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
if(StringLength) {
|
|
RtlCopyMemory((*UnicodeStringList)[i].Buffer, StringStart, StringLength);
|
|
}
|
|
(*UnicodeStringList)[i].Buffer[CB_TO_CWC(StringLength)] = UNICODE_NULL;
|
|
|
|
(*UnicodeStringList)[i].MaximumLength =
|
|
((*UnicodeStringList)[i].Length = (USHORT)StringLength)
|
|
+ sizeof(UNICODE_NULL);
|
|
}
|
|
|
|
*UnicodeStringCount = StringCount;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
PipApplyFunctionToServiceInstances(
|
|
IN HANDLE ServiceKeyHandle OPTIONAL,
|
|
IN PUNICODE_STRING ServiceKeyName OPTIONAL,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN BOOLEAN IgnoreNonCriticalErrors,
|
|
IN PIOP_SUBKEY_CALLBACK_ROUTINE DevInstCallbackRoutine,
|
|
IN OUT PVOID Context,
|
|
OUT PULONG ServiceInstanceOrdinal OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine enumerates all device instances referenced by the instance
|
|
ordinal entries under a service's volatile Enum key, and calls
|
|
the specified callback routine for each instance's corresponding subkey
|
|
under HKLM\System\Enum.
|
|
|
|
Arguments:
|
|
|
|
ServiceKeyHandle - Optional handle to the service entry. If this parameter
|
|
is not specified, then the service key name must be given in
|
|
ServiceKeyName (if both parameters are specified, then ServiceKeyHandle
|
|
is used, and ServiceKeyName is ignored).
|
|
|
|
ServiceKeyName - Optional name of the service entry key (under
|
|
HKLM\CurrentControlSet\Services). If this parameter is not specified,
|
|
then ServiceKeyHandle must contain a handle to the desired service key.
|
|
|
|
DesiredAccess - Specifies the desired access that the callback routine
|
|
needs to the enumerated device instance keys. If no desired access is
|
|
specified (i.e., DesiredAccess is zero), then no handle will be opened
|
|
for the device instance keys, and the callback will be passed a NULL for
|
|
its DeviceInstanceHandle parameter.
|
|
|
|
IgnoreNonCriticalErrors - Specifies whether this function should
|
|
immediately terminate on all errors, or only on critical ones.
|
|
An example of a non-critical error is when an enumerated device instance
|
|
key cannot be opened for the desired access.
|
|
|
|
DevInstCallbackRoutine - Supplies a pointer to a function that will
|
|
be called for each device instance key referenced by a service instance
|
|
entry under the service's volatile Enum subkey. The prototype of the
|
|
function is as follows:
|
|
|
|
typedef BOOLEAN (*PIOP_SUBKEY_CALLBACK_ROUTINE) (
|
|
IN HANDLE DeviceInstanceHandle,
|
|
IN PUNICODE_STRING DeviceInstancePath,
|
|
IN OUT PVOID Context
|
|
);
|
|
|
|
where DeviceInstanceHandle is the handle to an enumerated device instance
|
|
key, DeviceInstancePath is the registry path (relative to
|
|
HKLM\System\Enum) to this device instance, and Context is a pointer to
|
|
user-defined data.
|
|
|
|
This function should return TRUE to continue enumeration, or
|
|
FALSE to terminate it.
|
|
|
|
Context - Supplies a pointer to user-defined data that will be passed
|
|
in to the callback routine at each device instance key invocation.
|
|
|
|
ServiceInstanceOrdinal - Optionally, receives the service instance ordinal (1 based)
|
|
that terminated the enumeration, or the total number of instances enumerated
|
|
if the enumeration completed without being aborted.
|
|
|
|
Return Value:
|
|
|
|
NT status code indicating whether the device instance keys were successfully
|
|
enumerated. Note that this does not provide information on the success or
|
|
failure of the callback routine--if desired, this information should be
|
|
stored in the Context structure.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
HANDLE ServiceEnumHandle, SystemEnumHandle, DeviceInstanceHandle;
|
|
UNICODE_STRING TempUnicodeString;
|
|
ULONG ServiceInstanceCount, i, junk;
|
|
PKEY_VALUE_FULL_INFORMATION KeyValueInformation;
|
|
BOOLEAN ContinueEnumeration;
|
|
|
|
//
|
|
// First, open up the volatile Enum subkey under the specified service entry.
|
|
//
|
|
|
|
if(ARGUMENT_PRESENT(ServiceKeyHandle)) {
|
|
PiWstrToUnicodeString(&TempUnicodeString, REGSTR_KEY_ENUM);
|
|
Status = IopOpenRegistryKeyEx( &ServiceEnumHandle,
|
|
ServiceKeyHandle,
|
|
&TempUnicodeString,
|
|
KEY_READ
|
|
);
|
|
} else {
|
|
Status = PipOpenServiceEnumKeys(ServiceKeyName,
|
|
KEY_READ,
|
|
NULL,
|
|
&ServiceEnumHandle,
|
|
FALSE
|
|
);
|
|
}
|
|
if(!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Find out how many instances are referenced in the service's Enum key.
|
|
//
|
|
|
|
ServiceInstanceCount = 0; // assume none.
|
|
|
|
Status = IopGetRegistryValue(ServiceEnumHandle,
|
|
REGSTR_VALUE_COUNT,
|
|
&KeyValueInformation
|
|
);
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
if((KeyValueInformation->Type == REG_DWORD) &&
|
|
(KeyValueInformation->DataLength >= sizeof(ULONG))) {
|
|
|
|
ServiceInstanceCount = *(PULONG)KEY_VALUE_DATA(KeyValueInformation);
|
|
|
|
}
|
|
ExFreePool(KeyValueInformation);
|
|
|
|
} else if (Status != STATUS_OBJECT_NAME_NOT_FOUND) {
|
|
goto PrepareForReturn;
|
|
} else {
|
|
//
|
|
// If 'Count' value entry not found, consider this to mean there are simply
|
|
// no device instance controlled by this service.
|
|
//
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Now, enumerate each service instance, and call the specified callback function
|
|
// for the corresponding device instance.
|
|
//
|
|
|
|
if (ServiceInstanceCount) {
|
|
|
|
//
|
|
// Set DeviceInstanceHandle to NULL (assume we won't be opening up the
|
|
// device instance keys).
|
|
//
|
|
|
|
DeviceInstanceHandle = NULL;
|
|
SystemEnumHandle = NULL;
|
|
|
|
if (DesiredAccess) {
|
|
Status = IopOpenRegistryKeyEx( &SystemEnumHandle,
|
|
NULL,
|
|
&CmRegistryMachineSystemCurrentControlSetEnumName,
|
|
KEY_READ
|
|
);
|
|
if(!NT_SUCCESS(Status)) {
|
|
goto PrepareForReturn;
|
|
}
|
|
}
|
|
|
|
KeyValueInformation = (PKEY_VALUE_FULL_INFORMATION)ExAllocatePool(
|
|
PagedPool,
|
|
PNP_SCRATCH_BUFFER_SIZE);
|
|
if (!KeyValueInformation) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto PrepareForReturn;
|
|
}
|
|
|
|
for (i = 0; ; i++) {
|
|
|
|
Status = ZwEnumerateValueKey(
|
|
ServiceEnumHandle,
|
|
i,
|
|
KeyValueFullInformation,
|
|
KeyValueInformation,
|
|
PNP_SCRATCH_BUFFER_SIZE,
|
|
&junk
|
|
);
|
|
|
|
if (!NT_SUCCESS (Status)) {
|
|
if (Status == STATUS_NO_MORE_ENTRIES) {
|
|
Status = STATUS_SUCCESS;
|
|
break;
|
|
} else if (IgnoreNonCriticalErrors) {
|
|
continue;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (KeyValueInformation->Type != REG_SZ) {
|
|
continue;
|
|
}
|
|
|
|
ContinueEnumeration = TRUE;
|
|
TempUnicodeString.Length = 0;
|
|
IopRegistryDataToUnicodeString(&TempUnicodeString,
|
|
(PWSTR)KEY_VALUE_DATA(KeyValueInformation),
|
|
KeyValueInformation->DataLength
|
|
);
|
|
if (TempUnicodeString.Length) {
|
|
|
|
//
|
|
// We have retrieved a (non-empty) string for this service instance.
|
|
// If the user specified a non-zero value for the DesiredAccess
|
|
// parameter, we will attempt to open up the corresponding device
|
|
// instance key under HKLM\System\Enum.
|
|
//
|
|
if (DesiredAccess) {
|
|
Status = IopOpenRegistryKeyEx( &DeviceInstanceHandle,
|
|
SystemEnumHandle,
|
|
&TempUnicodeString,
|
|
DesiredAccess
|
|
);
|
|
}
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
//
|
|
// Invoke the specified callback routine for this device instance.
|
|
//
|
|
ContinueEnumeration = DevInstCallbackRoutine(DeviceInstanceHandle,
|
|
&TempUnicodeString,
|
|
Context
|
|
);
|
|
if (DesiredAccess) {
|
|
ZwClose(DeviceInstanceHandle);
|
|
}
|
|
} else if (IgnoreNonCriticalErrors) {
|
|
continue;
|
|
} else {
|
|
break;
|
|
}
|
|
} else {
|
|
continue;
|
|
}
|
|
if (!ContinueEnumeration) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT(ServiceInstanceOrdinal)) {
|
|
*ServiceInstanceOrdinal = i;
|
|
}
|
|
|
|
if (DesiredAccess) {
|
|
ZwClose(SystemEnumHandle);
|
|
}
|
|
ExFreePool(KeyValueInformation);
|
|
}
|
|
|
|
|
|
PrepareForReturn:
|
|
|
|
ZwClose(ServiceEnumHandle);
|
|
|
|
return Status;
|
|
}
|
|
|
|
BOOLEAN
|
|
PipIsDuplicatedDevices(
|
|
IN PCM_RESOURCE_LIST Configuration1,
|
|
IN PCM_RESOURCE_LIST Configuration2,
|
|
IN PHAL_BUS_INFORMATION BusInfo1 OPTIONAL,
|
|
IN PHAL_BUS_INFORMATION BusInfo2 OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine compares two set of configurations and bus information to
|
|
determine if the resources indicate the same device. If BusInfo1 and
|
|
BusInfo2 both are absent, it means caller wants to compare the raw
|
|
resources.
|
|
|
|
Arguments:
|
|
|
|
Configuration1 - Supplies a pointer to the first set of resource.
|
|
|
|
Configuration2 - Supplies a pointer to the second set of resource.
|
|
|
|
BusInfo1 - Supplies a pointer to the first set of bus information.
|
|
|
|
BusInfo2 - Supplies a pointer to the second set of bus information.
|
|
|
|
Return Value:
|
|
|
|
returns TRUE if the two set of resources indicate the same device;
|
|
otherwise a value of FALSE is returned.
|
|
|
|
--*/
|
|
|
|
{
|
|
PCM_PARTIAL_RESOURCE_LIST list1, list2;
|
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR descriptor1, descriptor2;
|
|
|
|
ULONG i, j;
|
|
ULONG pass = 0;
|
|
|
|
//
|
|
// The BusInfo for both resources must be both present or not present.
|
|
//
|
|
|
|
if ((ARGUMENT_PRESENT(BusInfo1) && !ARGUMENT_PRESENT(BusInfo2)) ||
|
|
(!ARGUMENT_PRESENT(BusInfo1) && ARGUMENT_PRESENT(BusInfo2))) {
|
|
|
|
//
|
|
// Unable to determine.
|
|
//
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Next check resources used by the two devices.
|
|
// Currently, we *only* check the Io ports.
|
|
//
|
|
|
|
if (Configuration1->Count == 0 || Configuration2->Count == 0) {
|
|
|
|
//
|
|
// If any one of the configuration data is empty, we assume
|
|
// the devices are not duplicates.
|
|
//
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
RedoScan:
|
|
|
|
list1 = &(Configuration1->List[0].PartialResourceList);
|
|
list2 = &(Configuration2->List[0].PartialResourceList);
|
|
|
|
for(i = 0, descriptor1 = list1->PartialDescriptors;
|
|
i < list1->Count;
|
|
i++, descriptor1++) {
|
|
|
|
//
|
|
// If this is an i/o port or a memory range then look for a match
|
|
// in the other list.
|
|
//
|
|
|
|
if((descriptor1->Type == CmResourceTypePort) ||
|
|
(descriptor1->Type == CmResourceTypeMemory)) {
|
|
|
|
for(j = 0, descriptor2 = list2->PartialDescriptors;
|
|
j < list2->Count;
|
|
j++, descriptor2++) {
|
|
|
|
//
|
|
// If the types match then check to see if both addresses
|
|
// match as well. If bus info was provided then go ahead
|
|
// and translate the ranges first.
|
|
//
|
|
|
|
if(descriptor1->Type == descriptor2->Type) {
|
|
|
|
PHYSICAL_ADDRESS range1, range1Translated;
|
|
PHYSICAL_ADDRESS range2, range2Translated;
|
|
ULONG range1IoSpace, range2IoSpace;
|
|
|
|
range1 = descriptor1->u.Generic.Start;
|
|
range2 = descriptor2->u.Generic.Start;
|
|
|
|
if((range1.QuadPart == 0) ||
|
|
(BusInfo1 == NULL) ||
|
|
(HalTranslateBusAddress(
|
|
BusInfo1->BusType,
|
|
BusInfo1->BusNumber,
|
|
range1,
|
|
&range1IoSpace,
|
|
&range1Translated) == FALSE)) {
|
|
|
|
range1Translated = range1;
|
|
range1IoSpace =
|
|
(descriptor1->Type == CmResourceTypePort) ? TRUE :
|
|
FALSE;
|
|
}
|
|
|
|
if((range2.QuadPart == 0) ||
|
|
(BusInfo2 == NULL) ||
|
|
(HalTranslateBusAddress(
|
|
BusInfo2->BusType,
|
|
BusInfo2->BusNumber,
|
|
range2,
|
|
&range2IoSpace,
|
|
&range2Translated) == FALSE)) {
|
|
|
|
range2Translated = range2;
|
|
range2IoSpace =
|
|
(descriptor2->Type == CmResourceTypePort) ? TRUE :
|
|
FALSE;
|
|
}
|
|
|
|
//
|
|
// If the ranges are in the same space and start at the
|
|
// same location then break out and go on to the next
|
|
// range
|
|
//
|
|
|
|
if((range1Translated.QuadPart == range2Translated.QuadPart) &&
|
|
(range1IoSpace == range2IoSpace)) {
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we made it all the way through the resource list without
|
|
// finding a match then these are not duplicates.
|
|
//
|
|
|
|
if(j == list2->Count) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// If every resource in list 1 exists in list 2 then we also need to make
|
|
// sure that every resource in list 2 exists in list 1.
|
|
//
|
|
|
|
if(pass == 0) {
|
|
|
|
PVOID tmp ;
|
|
|
|
tmp = Configuration2;
|
|
Configuration2 = Configuration1;
|
|
Configuration1 = tmp;
|
|
|
|
tmp = BusInfo2;
|
|
BusInfo2 = BusInfo1;
|
|
BusInfo1 = tmp;
|
|
|
|
pass = 1;
|
|
|
|
goto RedoScan;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
VOID
|
|
PipFreeUnicodeStringList(
|
|
IN PUNICODE_STRING UnicodeStringList,
|
|
IN ULONG StringCount
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine frees the buffer for each UNICODE_STRING in the specified list
|
|
(there are StringCount of them), and then frees the memory used for the
|
|
string list itself.
|
|
|
|
Arguments:
|
|
|
|
UnicodeStringList - Supplies a pointer to an array of UNICODE_STRINGs.
|
|
|
|
StringCount - Supplies the number of strings in the UnicodeStringList array.
|
|
|
|
Returns:
|
|
|
|
None.
|
|
|
|
NOTE: This function is only available during INIT time!
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG i;
|
|
|
|
if(UnicodeStringList) {
|
|
|
|
for(i = 0; i < StringCount; i++) {
|
|
|
|
if(UnicodeStringList[i].Buffer) {
|
|
|
|
ExFreePool(UnicodeStringList[i].Buffer);
|
|
}
|
|
}
|
|
ExFreePool(UnicodeStringList);
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
IopDriverLoadingFailed(
|
|
IN HANDLE ServiceHandle OPTIONAL,
|
|
IN PUNICODE_STRING ServiceName OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is invoked when driver failed to start. All the device
|
|
instances controlled by this driver/service are marked as failing to
|
|
start.
|
|
|
|
Arguments:
|
|
|
|
ServiceKeyHandle - Optionally, supplies a handle to the driver service node in the
|
|
registry that controls this device instance. If this argument is not specified,
|
|
then ServiceKeyName is used to specify the service entry.
|
|
|
|
ServiceKeyName - Optionally supplies the name of the service entry that controls
|
|
the device instance. This must be specified if ServiceKeyHandle isn't given.
|
|
|
|
Returns:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
PKEY_VALUE_FULL_INFORMATION keyValueInformation;
|
|
BOOLEAN closeHandle = FALSE, deletePdo;
|
|
HANDLE handle, serviceEnumHandle, controlHandle, devInstHandle;
|
|
HANDLE sysEnumHandle = NULL;
|
|
ULONG deviceFlags, count, newCount, i, j;
|
|
UNICODE_STRING unicodeValueName, deviceInstanceName;
|
|
WCHAR unicodeBuffer[20];
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Open registry ServiceKeyName\Enum branch
|
|
//
|
|
|
|
if (!ARGUMENT_PRESENT(ServiceHandle)) {
|
|
status = PipOpenServiceEnumKeys(ServiceName,
|
|
KEY_READ,
|
|
&ServiceHandle,
|
|
&serviceEnumHandle,
|
|
FALSE
|
|
);
|
|
closeHandle = TRUE;
|
|
} else {
|
|
PiWstrToUnicodeString(&unicodeValueName, REGSTR_KEY_ENUM);
|
|
status = IopOpenRegistryKeyEx( &serviceEnumHandle,
|
|
ServiceHandle,
|
|
&unicodeValueName,
|
|
KEY_READ
|
|
);
|
|
}
|
|
if (!NT_SUCCESS( status )) {
|
|
|
|
//
|
|
// No Service Enum key? no device instance. Return FALSE.
|
|
//
|
|
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Set "STARTFAILED" flags. So, we won't load it again.
|
|
//
|
|
|
|
PiWstrToUnicodeString(&unicodeValueName, L"INITSTARTFAILED");
|
|
deviceFlags = 1;
|
|
ZwSetValueKey(
|
|
serviceEnumHandle,
|
|
&unicodeValueName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_DWORD,
|
|
&deviceFlags,
|
|
sizeof(deviceFlags)
|
|
);
|
|
|
|
//
|
|
// Find out how many device instances listed in the ServiceName's
|
|
// Enum key.
|
|
//
|
|
|
|
status = IopGetRegistryValue ( serviceEnumHandle,
|
|
REGSTR_VALUE_COUNT,
|
|
&keyValueInformation
|
|
);
|
|
count = 0;
|
|
if (NT_SUCCESS(status)) {
|
|
if ((keyValueInformation->Type == REG_DWORD) &&
|
|
(keyValueInformation->DataLength >= sizeof(ULONG))) {
|
|
|
|
count = *(PULONG)KEY_VALUE_DATA(keyValueInformation);
|
|
}
|
|
ExFreePool(keyValueInformation);
|
|
}
|
|
if (count == 0) {
|
|
ZwClose(serviceEnumHandle);
|
|
if (closeHandle) {
|
|
ZwClose(ServiceHandle);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Open HTREE\ROOT\0 key so later we can remove device instance key
|
|
// from its AttachedComponents value name.
|
|
//
|
|
|
|
status = IopOpenRegistryKeyEx( &sysEnumHandle,
|
|
NULL,
|
|
&CmRegistryMachineSystemCurrentControlSetEnumName,
|
|
KEY_ALL_ACCESS
|
|
);
|
|
|
|
//
|
|
// Walk through each registered device instance to mark its Problem and
|
|
// StatusFlags as fail to start and reset its ActiveService
|
|
//
|
|
|
|
newCount = count;
|
|
for (i = 0; i < count; i++) {
|
|
deletePdo = FALSE;
|
|
status = PipServiceInstanceToDeviceInstance (
|
|
ServiceHandle,
|
|
ServiceName,
|
|
i,
|
|
&deviceInstanceName,
|
|
&handle,
|
|
KEY_ALL_ACCESS
|
|
);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
PDEVICE_OBJECT deviceObject;
|
|
PDEVICE_NODE deviceNode;
|
|
|
|
//
|
|
// If the device instance is a detected device reported during driver's
|
|
// DriverEntry we need to clean it up.
|
|
//
|
|
|
|
deviceObject = IopDeviceObjectFromDeviceInstance(&deviceInstanceName);
|
|
if (deviceObject) {
|
|
deviceNode = (PDEVICE_NODE)deviceObject->DeviceObjectExtension->DeviceNode;
|
|
if (deviceNode) {
|
|
|
|
IopReleaseDeviceResources(deviceNode, TRUE);
|
|
|
|
if ((deviceNode->Flags & DNF_MADEUP) &&
|
|
((deviceNode->State == DeviceNodeStarted) ||
|
|
(deviceNode->State == DeviceNodeStartPostWork))) {
|
|
|
|
//
|
|
// Now mark this one deleted.
|
|
//
|
|
PipSetDevNodeState(deviceNode, DeviceNodeRemoved, NULL);
|
|
|
|
PipSetDevNodeProblem(deviceNode, CM_PROB_DEVICE_NOT_THERE);
|
|
|
|
deletePdo = TRUE;
|
|
}
|
|
}
|
|
ObDereferenceObject(deviceObject); // added via IopDeviceObjectFromDeviceInstance
|
|
}
|
|
|
|
PiLockPnpRegistry(FALSE);
|
|
|
|
PiWstrToUnicodeString(&unicodeValueName, REGSTR_KEY_CONTROL);
|
|
controlHandle = NULL;
|
|
status = IopOpenRegistryKeyEx( &controlHandle,
|
|
handle,
|
|
&unicodeValueName,
|
|
KEY_ALL_ACCESS
|
|
);
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
status = IopGetRegistryValue(controlHandle,
|
|
REGSTR_VALUE_NEWLY_CREATED,
|
|
&keyValueInformation);
|
|
if (NT_SUCCESS(status)) {
|
|
ExFreePool(keyValueInformation);
|
|
}
|
|
if ((status != STATUS_OBJECT_NAME_NOT_FOUND) &&
|
|
(status != STATUS_OBJECT_PATH_NOT_FOUND)) {
|
|
|
|
//
|
|
// Remove the instance value name from service enum key
|
|
//
|
|
|
|
PiUlongToUnicodeString(&unicodeValueName, unicodeBuffer, 20, i);
|
|
status = ZwDeleteValueKey (serviceEnumHandle, &unicodeValueName);
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// If we can successfaully remove the instance value entry
|
|
// from service enum key, we then remove the device instance key
|
|
// Otherwise, we go thru normal path to mark driver loading failed
|
|
// in the device instance key.
|
|
//
|
|
|
|
newCount--;
|
|
|
|
ZwDeleteKey(controlHandle);
|
|
ZwDeleteKey(handle);
|
|
|
|
|
|
//
|
|
// We also want to delete the ROOT\LEGACY_<driver> key
|
|
//
|
|
|
|
if (sysEnumHandle) {
|
|
deviceInstanceName.Length -= 5 * sizeof(WCHAR);
|
|
deviceInstanceName.Buffer[deviceInstanceName.Length / sizeof(WCHAR)] =
|
|
UNICODE_NULL;
|
|
status = IopOpenRegistryKeyEx( &devInstHandle,
|
|
sysEnumHandle,
|
|
&deviceInstanceName,
|
|
KEY_ALL_ACCESS
|
|
);
|
|
deviceInstanceName.Buffer[deviceInstanceName.Length / sizeof(WCHAR)] =
|
|
OBJ_NAME_PATH_SEPARATOR;
|
|
deviceInstanceName.Length += 5 * sizeof(WCHAR);
|
|
if (NT_SUCCESS(status)) {
|
|
ZwDeleteKey(devInstHandle);
|
|
ZwClose(devInstHandle);
|
|
}
|
|
}
|
|
|
|
//
|
|
// If there is a PDO for this device, remove it
|
|
//
|
|
|
|
if (deletePdo) {
|
|
IoDeleteDevice(deviceObject);
|
|
}
|
|
|
|
ZwClose(controlHandle);
|
|
ZwClose(handle);
|
|
IopCleanupDeviceRegistryValues(&deviceInstanceName);
|
|
|
|
ExFreePool(deviceInstanceName.Buffer);
|
|
PiUnlockPnpRegistry();
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Reset Control\ActiveService value name.
|
|
//
|
|
|
|
if (controlHandle) {
|
|
PiWstrToUnicodeString(&unicodeValueName, REGSTR_VAL_ACTIVESERVICE);
|
|
ZwDeleteValueKey(controlHandle, &unicodeValueName);
|
|
ZwClose(controlHandle);
|
|
}
|
|
|
|
ZwClose(handle);
|
|
ExFreePool(deviceInstanceName.Buffer);
|
|
|
|
PiUnlockPnpRegistry();
|
|
}
|
|
}
|
|
|
|
//
|
|
// If some instance value entry is deleted, we need to update the count of instance
|
|
// value entries and rearrange the instance value entries under service enum key.
|
|
//
|
|
|
|
if (newCount != count) {
|
|
|
|
PiLockPnpRegistry(FALSE);
|
|
|
|
if (newCount != 0) {
|
|
j = 0;
|
|
i = 0;
|
|
while (i < count) {
|
|
PiUlongToUnicodeString(&unicodeValueName, unicodeBuffer, 20, i);
|
|
status = IopGetRegistryValue(serviceEnumHandle,
|
|
unicodeValueName.Buffer,
|
|
&keyValueInformation
|
|
);
|
|
if (NT_SUCCESS(status)) {
|
|
if (i != j) {
|
|
|
|
//
|
|
// Need to change the instance i to instance j
|
|
//
|
|
|
|
ZwDeleteValueKey(serviceEnumHandle, &unicodeValueName);
|
|
|
|
PiUlongToUnicodeString(&unicodeValueName, unicodeBuffer, 20, j);
|
|
ZwSetValueKey (serviceEnumHandle,
|
|
&unicodeValueName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_SZ,
|
|
(PVOID)KEY_VALUE_DATA(keyValueInformation),
|
|
keyValueInformation->DataLength
|
|
);
|
|
}
|
|
ExFreePool(keyValueInformation);
|
|
j++;
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Don't forget to update the "Count=" and "NextInstance=" value entries
|
|
//
|
|
|
|
PiWstrToUnicodeString( &unicodeValueName, REGSTR_VALUE_COUNT);
|
|
|
|
ZwSetValueKey(serviceEnumHandle,
|
|
&unicodeValueName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_DWORD,
|
|
&newCount,
|
|
sizeof (newCount)
|
|
);
|
|
PiWstrToUnicodeString( &unicodeValueName, REGSTR_VALUE_NEXT_INSTANCE);
|
|
|
|
ZwSetValueKey(serviceEnumHandle,
|
|
&unicodeValueName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_DWORD,
|
|
&newCount,
|
|
sizeof (newCount)
|
|
);
|
|
|
|
PiUnlockPnpRegistry();
|
|
}
|
|
ZwClose(serviceEnumHandle);
|
|
if (closeHandle) {
|
|
ZwClose(ServiceHandle);
|
|
}
|
|
if (sysEnumHandle) {
|
|
ZwClose(sysEnumHandle);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
IopDisableDevice(
|
|
IN PDEVICE_NODE DeviceNode
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine tries to ask a bus driver stopping decoding resources
|
|
|
|
Arguments:
|
|
|
|
DeviceNode - Specifies the device to be disabled.
|
|
|
|
Handle - specifies the device instance handle.
|
|
|
|
Returns:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// If the device has boot config, we will query-remove and remove the device to free
|
|
// the boot config if possible.
|
|
//
|
|
status = IopRemoveDevice (DeviceNode->PhysicalDeviceObject, IRP_MN_QUERY_REMOVE_DEVICE);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
status = IopRemoveDevice (DeviceNode->PhysicalDeviceObject, IRP_MN_REMOVE_DEVICE);
|
|
ASSERT(NT_SUCCESS(status));
|
|
IopReleaseDeviceResources(DeviceNode, TRUE);
|
|
|
|
} else {
|
|
|
|
IopRemoveDevice (DeviceNode->PhysicalDeviceObject, IRP_MN_CANCEL_REMOVE_DEVICE);
|
|
}
|
|
|
|
if (PipDoesDevNodeHaveProblem(DeviceNode)) {
|
|
|
|
ASSERT(PipIsDevNodeProblem(DeviceNode, CM_PROB_NOT_CONFIGURED) ||
|
|
PipIsDevNodeProblem(DeviceNode, CM_PROB_FAILED_INSTALL) ||
|
|
PipIsDevNodeProblem(DeviceNode, CM_PROB_REINSTALL));
|
|
|
|
PipClearDevNodeProblem(DeviceNode);
|
|
}
|
|
|
|
PipSetDevNodeProblem(DeviceNode, CM_PROB_DISABLED);
|
|
}
|
|
|
|
BOOLEAN
|
|
IopIsAnyDeviceInstanceEnabled(
|
|
IN PUNICODE_STRING ServiceKeyName,
|
|
IN HANDLE ServiceHandle OPTIONAL,
|
|
IN BOOLEAN LegacyIncluded
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine checks if any of the devices instances is turned on for the
|
|
specified service. This routine is used for Pnp Driver only and is temporary
|
|
function to support SUR.
|
|
|
|
Arguments:
|
|
|
|
ServiceKeyName - Specifies the service key unicode name
|
|
|
|
ServiceHandle - Optionally supplies a handle to the service key to be
|
|
checked.
|
|
|
|
LegacyIncluded - TRUE, a legacy device instance key is counted as a device
|
|
instance.
|
|
FALSE, a legacy device instance key is not counted.
|
|
|
|
Returns:
|
|
|
|
A BOOLEAN value.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
PKEY_VALUE_FULL_INFORMATION keyValueInformation;
|
|
HANDLE serviceEnumHandle, handle, controlHandle;
|
|
ULONG i, count, legacy;
|
|
UNICODE_STRING unicodeName, instancePath;
|
|
BOOLEAN enabled, closeHandle, instanceEnabled;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Initialize for proper cleanup.
|
|
//
|
|
closeHandle = FALSE;
|
|
|
|
//
|
|
// Initialize for all instances disabled.
|
|
//
|
|
enabled = FALSE;
|
|
|
|
//
|
|
// Open registry ServiceKeyName\Enum branch
|
|
//
|
|
if (!ARGUMENT_PRESENT(ServiceHandle)) {
|
|
|
|
status = PipOpenServiceEnumKeys(ServiceKeyName,
|
|
KEY_READ,
|
|
&ServiceHandle,
|
|
&serviceEnumHandle,
|
|
FALSE
|
|
);
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
closeHandle = TRUE;
|
|
}
|
|
|
|
} else {
|
|
|
|
PiWstrToUnicodeString(&unicodeName, REGSTR_KEY_ENUM);
|
|
status = IopOpenRegistryKeyEx(&serviceEnumHandle,
|
|
ServiceHandle,
|
|
&unicodeName,
|
|
KEY_READ
|
|
);
|
|
}
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// No Service Enum key? no device instance. Return FALSE.
|
|
//
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Find out how many device instances listed in the ServiceName's
|
|
// Enum key.
|
|
//
|
|
count = 0;
|
|
status = IopGetRegistryValue(serviceEnumHandle,
|
|
REGSTR_VALUE_COUNT,
|
|
&keyValueInformation
|
|
);
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
if ((keyValueInformation->Type == REG_DWORD) &&
|
|
(keyValueInformation->DataLength >= sizeof(ULONG))) {
|
|
|
|
count = *(PULONG)KEY_VALUE_DATA(keyValueInformation);
|
|
}
|
|
|
|
ExFreePool(keyValueInformation);
|
|
}
|
|
ZwClose(serviceEnumHandle);
|
|
|
|
if (count == 0) {
|
|
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Walk through each registered device instance to check if it is enabled.
|
|
//
|
|
for (i = 0; i < count; i++) {
|
|
|
|
//
|
|
// Get device instance handle. If it fails, we will skip this device
|
|
// instance.
|
|
//
|
|
status = PipServiceInstanceToDeviceInstance(ServiceHandle,
|
|
NULL,
|
|
i,
|
|
&instancePath,
|
|
&handle,
|
|
KEY_ALL_ACCESS
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
continue;
|
|
}
|
|
|
|
instanceEnabled = IopIsDeviceInstanceEnabled(NULL, &instancePath, TRUE);
|
|
ExFreePool(instancePath.Buffer);
|
|
|
|
if (instanceEnabled) {
|
|
|
|
legacy = 0;
|
|
if (LegacyIncluded == FALSE) {
|
|
|
|
//
|
|
// Get the legacy count.
|
|
//
|
|
status = IopGetRegistryValue(handle,
|
|
REGSTR_VALUE_LEGACY,
|
|
&keyValueInformation
|
|
);
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
if ( keyValueInformation->Type == REG_DWORD &&
|
|
keyValueInformation->DataLength == sizeof(ULONG)) {
|
|
|
|
legacy = *(PULONG)KEY_VALUE_DATA(keyValueInformation);
|
|
}
|
|
|
|
ExFreePool(keyValueInformation);
|
|
}
|
|
}
|
|
if (legacy == 0) {
|
|
|
|
//
|
|
// Mark that the driver has at least one device instance to work
|
|
// with.
|
|
//
|
|
PiWstrToUnicodeString(&unicodeName, REGSTR_KEY_CONTROL);
|
|
status = IopCreateRegistryKeyEx(&controlHandle,
|
|
handle,
|
|
&unicodeName,
|
|
KEY_ALL_ACCESS,
|
|
REG_OPTION_VOLATILE,
|
|
NULL
|
|
);
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
PiWstrToUnicodeString(&unicodeName, REGSTR_VAL_ACTIVESERVICE);
|
|
ZwSetValueKey(controlHandle,
|
|
&unicodeName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_SZ,
|
|
ServiceKeyName->Buffer,
|
|
ServiceKeyName->Length + sizeof(UNICODE_NULL)
|
|
);
|
|
|
|
ZwClose(controlHandle);
|
|
}
|
|
|
|
//
|
|
// At least one instance is enabled.
|
|
//
|
|
enabled = TRUE;
|
|
}
|
|
}
|
|
|
|
ZwClose(handle);
|
|
}
|
|
|
|
exit:
|
|
|
|
if (closeHandle) {
|
|
|
|
ZwClose(ServiceHandle);
|
|
}
|
|
|
|
return enabled;
|
|
}
|
|
|
|
BOOLEAN
|
|
IopIsDeviceInstanceEnabled(
|
|
IN HANDLE DeviceInstanceHandle OPTIONAL,
|
|
IN PUNICODE_STRING DeviceInstance,
|
|
IN BOOLEAN Disable
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine checks if the specified devices instances is enabled.
|
|
|
|
Arguments:
|
|
|
|
DeviceInstanceHandle - Optionally supplies a handle to the device instance
|
|
key to be checked.
|
|
|
|
DeviceInstance - Specifies the device instance key unicode name. Caller
|
|
must at least specified DeviceInstanceHandle or DeviceInstance.
|
|
|
|
Disable - If this flag is set, and the device should be disabled
|
|
but is currently disabled, then the device is disabled.
|
|
|
|
Returns:
|
|
|
|
A BOOLEAN value.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
PKEY_VALUE_FULL_INFORMATION keyValueInformation;
|
|
HANDLE handle, controlHandle;
|
|
ULONG deviceFlags, disableCount;
|
|
BOOLEAN enabled, closeHandle;
|
|
PDEVICE_OBJECT deviceObject;
|
|
PDEVICE_NODE deviceNode;
|
|
UNICODE_STRING unicodeString;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Initialize for proper cleanup.
|
|
//
|
|
deviceObject = NULL;
|
|
closeHandle = FALSE;
|
|
|
|
//
|
|
// Assume device is enabled.
|
|
//
|
|
enabled = TRUE;
|
|
|
|
//
|
|
// First check if the device node is already disabled.
|
|
//
|
|
deviceObject = IopDeviceObjectFromDeviceInstance(DeviceInstance);
|
|
deviceNode = PP_DO_TO_DN(deviceObject);
|
|
if (deviceNode) {
|
|
|
|
if ( PipIsDevNodeProblem(deviceNode, CM_PROB_DISABLED) ||
|
|
PipIsDevNodeProblem(deviceNode, CM_PROB_HARDWARE_DISABLED)) {
|
|
|
|
enabled = FALSE;
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Open the device instance key if not specified.
|
|
//
|
|
if (!ARGUMENT_PRESENT(DeviceInstanceHandle)) {
|
|
|
|
status = IopOpenRegistryKeyEx(
|
|
&handle,
|
|
NULL,
|
|
&CmRegistryMachineSystemCurrentControlSetEnumName,
|
|
KEY_READ);
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
status = IopOpenRegistryKeyEx(
|
|
&DeviceInstanceHandle,
|
|
handle,
|
|
DeviceInstance,
|
|
KEY_READ);
|
|
|
|
ZwClose(handle);
|
|
}
|
|
|
|
//
|
|
// If we cannot open the device instance key
|
|
//
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
enabled = FALSE;
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Remember to close the key since we opened it.
|
|
//
|
|
closeHandle = TRUE;
|
|
}
|
|
|
|
//
|
|
// First check if the device has been disabled by the global CONFIGFLAG.
|
|
//
|
|
deviceFlags = 0;
|
|
status = IopGetRegistryValue(DeviceInstanceHandle,
|
|
REGSTR_VALUE_CONFIG_FLAGS,
|
|
&keyValueInformation);
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
if ( keyValueInformation->Type == REG_DWORD &&
|
|
keyValueInformation->DataLength == sizeof(ULONG)) {
|
|
|
|
deviceFlags = *(PULONG)KEY_VALUE_DATA(keyValueInformation);
|
|
}
|
|
|
|
ExFreePool(keyValueInformation);
|
|
}
|
|
|
|
if (deviceFlags & CONFIGFLAG_DISABLED) {
|
|
|
|
deviceFlags = CSCONFIGFLAG_DISABLED;
|
|
} else {
|
|
|
|
//
|
|
// Get the configflags for this device in the current profile.
|
|
//
|
|
IopGetDeviceInstanceCsConfigFlags(DeviceInstance, &deviceFlags);
|
|
}
|
|
|
|
//
|
|
// Determine if the device should be disabled based on flags.
|
|
//
|
|
if ( (deviceFlags & CSCONFIGFLAG_DISABLED) ||
|
|
(deviceFlags & CSCONFIGFLAG_DO_NOT_CREATE) ||
|
|
(deviceFlags & CSCONFIGFLAG_DO_NOT_START)) {
|
|
|
|
enabled = FALSE;
|
|
}
|
|
|
|
if (enabled) {
|
|
|
|
//
|
|
// Get the disable count on this device.
|
|
//
|
|
disableCount = 0;
|
|
PiWstrToUnicodeString(&unicodeString, REGSTR_KEY_CONTROL);
|
|
status = IopOpenRegistryKeyEx(&controlHandle,
|
|
DeviceInstanceHandle,
|
|
&unicodeString,
|
|
KEY_READ
|
|
);
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
status = IopGetRegistryValue(
|
|
controlHandle,
|
|
REGSTR_VALUE_DISABLECOUNT,
|
|
&keyValueInformation);
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
if ( keyValueInformation->Type == REG_DWORD &&
|
|
keyValueInformation->DataLength == sizeof(ULONG)) {
|
|
|
|
disableCount = *(PULONG)KEY_VALUE_DATA(keyValueInformation);
|
|
}
|
|
|
|
ExFreePool(keyValueInformation);
|
|
}
|
|
|
|
ZwClose(controlHandle);
|
|
}
|
|
|
|
//
|
|
// Device should be disabled if there is a non-zero DisableCount on it.
|
|
//
|
|
if (disableCount) {
|
|
|
|
enabled = FALSE;
|
|
}
|
|
|
|
}
|
|
|
|
if (enabled == FALSE) {
|
|
|
|
//
|
|
// Device should be disabled. If there is a devnode, disable if
|
|
// specified.
|
|
//
|
|
if (Disable && deviceNode) {
|
|
|
|
IopDisableDevice(deviceNode);
|
|
}
|
|
}
|
|
|
|
exit:
|
|
|
|
//
|
|
// Cleanup.
|
|
//
|
|
if (deviceObject) {
|
|
|
|
ObDereferenceObject(deviceObject);
|
|
}
|
|
if (closeHandle) {
|
|
|
|
ZwClose(DeviceInstanceHandle);
|
|
}
|
|
|
|
return enabled;
|
|
}
|
|
|
|
ULONG
|
|
IopDetermineResourceListSize(
|
|
IN PCM_RESOURCE_LIST ResourceList
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines size of the passed in ResourceList
|
|
structure.
|
|
|
|
Arguments:
|
|
|
|
Configuration1 - Supplies a pointer to the resource list.
|
|
|
|
Return Value:
|
|
|
|
size of the resource list structure.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG totalSize, listSize, descriptorSize, i, j;
|
|
PCM_FULL_RESOURCE_DESCRIPTOR fullResourceDesc;
|
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR partialDescriptor;
|
|
|
|
if (!ResourceList) {
|
|
totalSize = 0;
|
|
} else {
|
|
totalSize = FIELD_OFFSET(CM_RESOURCE_LIST, List);
|
|
fullResourceDesc = &ResourceList->List[0];
|
|
for (i = 0; i < ResourceList->Count; i++) {
|
|
listSize = FIELD_OFFSET(CM_FULL_RESOURCE_DESCRIPTOR,
|
|
PartialResourceList) +
|
|
FIELD_OFFSET(CM_PARTIAL_RESOURCE_LIST,
|
|
PartialDescriptors);
|
|
partialDescriptor = &fullResourceDesc->PartialResourceList.PartialDescriptors[0];
|
|
for (j = 0; j < fullResourceDesc->PartialResourceList.Count; j++) {
|
|
descriptorSize = sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR);
|
|
if (partialDescriptor->Type == CmResourceTypeDeviceSpecific) {
|
|
descriptorSize += partialDescriptor->u.DeviceSpecificData.DataSize;
|
|
}
|
|
listSize += descriptorSize;
|
|
partialDescriptor = (PCM_PARTIAL_RESOURCE_DESCRIPTOR)
|
|
((PUCHAR)partialDescriptor + descriptorSize);
|
|
}
|
|
totalSize += listSize;
|
|
fullResourceDesc = (PCM_FULL_RESOURCE_DESCRIPTOR)
|
|
((PUCHAR)fullResourceDesc + listSize);
|
|
}
|
|
}
|
|
return totalSize;
|
|
}
|
|
|
|
VOID
|
|
PpInitializeDeviceReferenceTable(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initializes data structures associated with the device
|
|
reference table.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
KeInitializeGuardedMutex(&PpDeviceReferenceTableLock);
|
|
RtlInitializeGenericTable( &PpDeviceReferenceTable,
|
|
PiCompareInstancePath,
|
|
PiAllocateGenericTableEntry,
|
|
PiFreeGenericTableEntry,
|
|
NULL);
|
|
}
|
|
|
|
RTL_GENERIC_COMPARE_RESULTS
|
|
NTAPI
|
|
PiCompareInstancePath(
|
|
IN PRTL_GENERIC_TABLE Table,
|
|
IN PVOID FirstStruct,
|
|
IN PVOID SecondStruct
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the callback for the generic table routines.
|
|
|
|
Arguments:
|
|
|
|
Table - Table for which this is invoked.
|
|
|
|
FirstStruct - An element in the table to compare.
|
|
|
|
SecondStruct - Another element in the table to compare.
|
|
|
|
Return Value:
|
|
|
|
RTL_GENERIC_COMPARE_RESULTS.
|
|
|
|
--*/
|
|
|
|
{
|
|
PUNICODE_STRING lhs = ((PDEVICE_REFERENCE)FirstStruct)->DeviceInstance;
|
|
PUNICODE_STRING rhs = ((PDEVICE_REFERENCE)SecondStruct)->DeviceInstance;
|
|
LONG result;
|
|
|
|
PAGED_CODE();
|
|
|
|
UNREFERENCED_PARAMETER (Table);
|
|
|
|
result = RtlCompareUnicodeString(lhs, rhs, TRUE);
|
|
if (result < 0) {
|
|
|
|
return GenericLessThan;
|
|
} else if (result > 0) {
|
|
|
|
return GenericGreaterThan;
|
|
}
|
|
return GenericEqual;
|
|
}
|
|
|
|
PVOID
|
|
NTAPI
|
|
PiAllocateGenericTableEntry(
|
|
IN PRTL_GENERIC_TABLE Table,
|
|
IN CLONG ByteSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the callback for allocation for entries in the generic table.
|
|
|
|
Arguments:
|
|
|
|
Table - Table for which this is invoked.
|
|
|
|
ByteSize - Amount of memory to allocate.
|
|
|
|
Return Value:
|
|
|
|
Pointer to allocated memory if successful, else NULL.
|
|
|
|
--*/
|
|
|
|
{
|
|
PAGED_CODE();
|
|
|
|
UNREFERENCED_PARAMETER (Table);
|
|
|
|
return ExAllocatePool(PagedPool | POOL_COLD_ALLOCATION, ByteSize);
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
PiFreeGenericTableEntry(
|
|
IN PRTL_GENERIC_TABLE Table,
|
|
IN PVOID Buffer
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the callback for releasing memory for entries in the generic
|
|
table.
|
|
|
|
Arguments:
|
|
|
|
Table - Table for which this is invoked.
|
|
|
|
Buffer - Buffer to free.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PAGED_CODE();
|
|
|
|
UNREFERENCED_PARAMETER (Table);
|
|
|
|
ExFreePool(Buffer);
|
|
}
|
|
|
|
NTSTATUS
|
|
IopMapDeviceObjectToDeviceInstance(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PUNICODE_STRING DeviceInstance
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine adds a reference for the specified device to the
|
|
PpDeviceReferenceTable lookup table.
|
|
|
|
Note, caller must own the PpRegistryDeviceResource before calling the
|
|
function.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - supplies a pointer to a physical device object.
|
|
|
|
DeviceInstance - supplies a UNICODE_STRING to specify the device instance path.
|
|
|
|
Return Value:
|
|
|
|
Status code that indicates whether or not the function was successful.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
HANDLE hEnum, hInstance, hControl;
|
|
UNICODE_STRING unicodeKeyName;
|
|
DEVICE_REFERENCE deviceReference;
|
|
#if DBG
|
|
PDEVICE_OBJECT oldDeviceObject;
|
|
#endif
|
|
|
|
PAGED_CODE();
|
|
|
|
#if DBG
|
|
oldDeviceObject = IopDeviceObjectFromDeviceInstance(DeviceInstance);
|
|
ASSERT(!oldDeviceObject);
|
|
if (oldDeviceObject) {
|
|
|
|
ObDereferenceObject(oldDeviceObject);
|
|
}
|
|
#endif
|
|
|
|
deviceReference.DeviceObject = DeviceObject;
|
|
deviceReference.DeviceInstance = DeviceInstance;
|
|
KeAcquireGuardedMutex(&PpDeviceReferenceTableLock);
|
|
if (RtlInsertElementGenericTable(&PpDeviceReferenceTable,
|
|
(PVOID)&deviceReference,
|
|
(CLONG)sizeof(DEVICE_REFERENCE),
|
|
NULL)) {
|
|
status = STATUS_SUCCESS;
|
|
} else {
|
|
|
|
status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
KeReleaseGuardedMutex(&PpDeviceReferenceTableLock);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
//
|
|
// Create the volatile Control subkey for this device instance,
|
|
// since user-mode depends on it to be present for non-phantom
|
|
// devices.
|
|
//
|
|
// NTRAID #174944-2000/08/30-jamesca:
|
|
// Remove dependence on the presence of volatile Control subkey
|
|
// for present devices.
|
|
//
|
|
status = IopOpenRegistryKeyEx(&hEnum,
|
|
NULL,
|
|
&CmRegistryMachineSystemCurrentControlSetEnumName,
|
|
KEY_READ);
|
|
if (NT_SUCCESS(status)) {
|
|
status = IopOpenRegistryKeyEx(&hInstance,
|
|
hEnum,
|
|
DeviceInstance,
|
|
KEY_ALL_ACCESS);
|
|
if (NT_SUCCESS(status)) {
|
|
PiWstrToUnicodeString(&unicodeKeyName, REGSTR_KEY_CONTROL);
|
|
status = IopCreateRegistryKeyEx(&hControl,
|
|
hInstance,
|
|
&unicodeKeyName,
|
|
KEY_ALL_ACCESS,
|
|
REG_OPTION_VOLATILE,
|
|
NULL);
|
|
if (NT_SUCCESS(status)) {
|
|
ZwClose(hControl);
|
|
}
|
|
ZwClose(hInstance);
|
|
}
|
|
ZwClose(hEnum);
|
|
}
|
|
|
|
//
|
|
// The attempt to create the volatile Control subkey should always
|
|
// succeed, but just in case it didn't, make sure to always return
|
|
// STATUS_SUCCESS when the device reference is successfully added to
|
|
// the table.
|
|
//
|
|
ASSERT(NT_SUCCESS(status));
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
PDEVICE_OBJECT
|
|
IopDeviceObjectFromDeviceInstance(
|
|
IN PUNICODE_STRING DeviceInstance
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine receives a DeviceInstance path (or DeviceInstance handle) and
|
|
returns a reference to a bus device object for the DeviceInstance.
|
|
|
|
Note, caller must owner the PpRegistryDeviceResource before calling the function,
|
|
|
|
Arguments:
|
|
|
|
DeviceInstance - supplies a UNICODE_STRING to specify the device instance path.
|
|
|
|
Returns:
|
|
|
|
A reference to the desired bus device object.
|
|
|
|
--*/
|
|
|
|
{
|
|
DEVICE_REFERENCE key;
|
|
PDEVICE_REFERENCE deviceReference;
|
|
PDEVICE_OBJECT deviceObject;
|
|
PDEVICE_NODE deviceNode;
|
|
|
|
PAGED_CODE();
|
|
//
|
|
// Look-up the DO in our table.
|
|
//
|
|
deviceObject = NULL;
|
|
key.DeviceObject = NULL;
|
|
key.DeviceInstance = DeviceInstance;
|
|
KeAcquireGuardedMutex(&PpDeviceReferenceTableLock);
|
|
|
|
deviceReference = RtlLookupElementGenericTable(&PpDeviceReferenceTable, (PVOID)&key);
|
|
if (deviceReference) {
|
|
|
|
deviceObject = deviceReference->DeviceObject;
|
|
ASSERT(deviceObject);
|
|
if (deviceObject) {
|
|
|
|
ASSERT(deviceObject->Type == IO_TYPE_DEVICE);
|
|
if (deviceObject->Type != IO_TYPE_DEVICE) {
|
|
|
|
deviceObject = NULL;
|
|
} else {
|
|
|
|
deviceNode = (PDEVICE_NODE)deviceObject->DeviceObjectExtension->DeviceNode;
|
|
ASSERT(deviceNode && (deviceNode->PhysicalDeviceObject == deviceObject));
|
|
if (!deviceNode || deviceNode->PhysicalDeviceObject != deviceObject) {
|
|
|
|
deviceObject = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//
|
|
// Take a reference if we found the device object.
|
|
//
|
|
if (deviceObject) {
|
|
|
|
ObReferenceObject(deviceObject);
|
|
}
|
|
|
|
KeReleaseGuardedMutex(&PpDeviceReferenceTableLock);
|
|
|
|
return deviceObject;
|
|
}
|
|
|
|
NTSTATUS
|
|
IopDeviceObjectToDeviceInstance (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PHANDLE DeviceInstanceHandle,
|
|
IN ACCESS_MASK DesiredAccess
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine receives a DeviceObject pointer and returns a handle to the device
|
|
instance path under registry System\ENUM key.
|
|
|
|
Note, caller must owner the PpRegistryDeviceResource before calling the function,
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - supplies a pointer to a physical device object.
|
|
|
|
DeviceInstanceHandle - Supplies a variable to receive the handle to the registry
|
|
device instance key.
|
|
|
|
DesiredAccess - specifies the access that is needed to this key.
|
|
|
|
Returns:
|
|
|
|
NTSTATUS code to indicate success or failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
HANDLE handle;
|
|
PDEVICE_NODE deviceNode;
|
|
|
|
PAGED_CODE();
|
|
|
|
status = IopOpenRegistryKeyEx( &handle,
|
|
NULL,
|
|
&CmRegistryMachineSystemCurrentControlSetEnumName,
|
|
KEY_READ
|
|
);
|
|
|
|
if (!NT_SUCCESS( status )) {
|
|
return status;
|
|
}
|
|
|
|
deviceNode = (PDEVICE_NODE) DeviceObject->DeviceObjectExtension->DeviceNode;
|
|
if (deviceNode && (deviceNode->InstancePath.Length != 0)) {
|
|
status = IopOpenRegistryKeyEx( DeviceInstanceHandle,
|
|
handle,
|
|
&deviceNode->InstancePath,
|
|
DesiredAccess
|
|
);
|
|
} else {
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
ZwClose(handle);
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
IopCleanupDeviceRegistryValues (
|
|
IN PUNICODE_STRING InstancePath
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine cleans up a device instance key when the device is no
|
|
longer present/enumerated. If the device is registered to a Service
|
|
the Service's enum key will also been cleaned up.
|
|
|
|
Note the caller must lock the RegistryDeviceResource
|
|
|
|
Arguments:
|
|
|
|
InstancePath - supplies a pointer to the name of the device instance key.
|
|
|
|
Return Value:
|
|
|
|
status
|
|
|
|
--*/
|
|
|
|
{
|
|
DEVICE_REFERENCE key;
|
|
NTSTATUS status;
|
|
#if DBG
|
|
PDEVICE_OBJECT deviceObject;
|
|
#endif
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Delete the mapping between this instance path and corresponding DO.
|
|
//
|
|
key.DeviceObject = NULL;
|
|
key.DeviceInstance = InstancePath;
|
|
|
|
KeAcquireGuardedMutex(&PpDeviceReferenceTableLock);
|
|
RtlDeleteElementGenericTable(&PpDeviceReferenceTable, (PVOID)&key);
|
|
KeReleaseGuardedMutex(&PpDeviceReferenceTableLock);
|
|
#if DBG
|
|
deviceObject = IopDeviceObjectFromDeviceInstance(InstancePath);
|
|
ASSERT(!deviceObject);
|
|
if (deviceObject) {
|
|
|
|
ObDereferenceObject(deviceObject);
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Deregister the device from its controlling service's service enum key
|
|
//
|
|
|
|
status = PiDeviceRegistration( InstancePath, FALSE, NULL );
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
IopGetDeviceResourcesFromRegistry (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN ULONG ResourceType,
|
|
IN ULONG Preference,
|
|
OUT PVOID *Resource,
|
|
OUT PULONG Length
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines the resources decoded by the device specified.
|
|
If the device object is a madeup device, we will try to read the resources
|
|
from registry. Otherwise, we need to traverse the internal assigned resource
|
|
list to compose the resource list.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - supplies a pointer to a device object whose registry
|
|
values are to be cleaned up.
|
|
|
|
ResourceType - 0 for CM_RESOURCE_LIST and 1 for IO_RESOURCE_REQUIREMENTS_LIS
|
|
|
|
Flags - specify the preference.
|
|
|
|
Resource - Specified a variable to receive the required resources.
|
|
|
|
Length - Specified a variable to receive the length of the resource structure.
|
|
|
|
Return Value:
|
|
|
|
status
|
|
|
|
--*/
|
|
|
|
{
|
|
HANDLE handle, handlex;
|
|
NTSTATUS status;
|
|
PKEY_VALUE_FULL_INFORMATION keyValueInformation;
|
|
UNICODE_STRING unicodeName;
|
|
PWCHAR valueName = NULL;
|
|
|
|
PAGED_CODE();
|
|
|
|
*Resource = NULL;
|
|
*Length = 0;
|
|
|
|
//
|
|
// Open the LogConfig key of the device instance.
|
|
//
|
|
|
|
status = IopDeviceObjectToDeviceInstance(DeviceObject, &handlex, KEY_READ);
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
if (ResourceType == QUERY_RESOURCE_LIST) {
|
|
|
|
//
|
|
// Caller is asking for CM_RESOURCE_LIST
|
|
//
|
|
|
|
if (Preference & REGISTRY_ALLOC_CONFIG) {
|
|
|
|
//
|
|
// Try alloc config first
|
|
//
|
|
|
|
PiWstrToUnicodeString(&unicodeName, REGSTR_KEY_CONTROL);
|
|
status = IopOpenRegistryKeyEx( &handle,
|
|
handlex,
|
|
&unicodeName,
|
|
KEY_READ
|
|
);
|
|
if (NT_SUCCESS(status)) {
|
|
status = PipReadDeviceConfiguration (handle, REGISTRY_ALLOC_CONFIG, (PCM_RESOURCE_LIST *)Resource, Length);
|
|
ZwClose(handle);
|
|
if (NT_SUCCESS(status)) {
|
|
ZwClose(handlex);
|
|
return status;
|
|
}
|
|
}
|
|
}
|
|
|
|
handle = NULL;
|
|
if (Preference & REGISTRY_FORCED_CONFIG) {
|
|
|
|
PiWstrToUnicodeString(&unicodeName, REGSTR_KEY_LOG_CONF);
|
|
status = IopOpenRegistryKeyEx( &handle,
|
|
handlex,
|
|
&unicodeName,
|
|
KEY_READ
|
|
);
|
|
if (NT_SUCCESS(status)) {
|
|
status = PipReadDeviceConfiguration (handle, REGISTRY_FORCED_CONFIG, (PCM_RESOURCE_LIST *)Resource, Length);
|
|
if (NT_SUCCESS(status)) {
|
|
ZwClose(handle);
|
|
ZwClose(handlex);
|
|
return status;
|
|
}
|
|
} else {
|
|
ZwClose(handlex);
|
|
return status;
|
|
}
|
|
}
|
|
if (Preference & REGISTRY_BOOT_CONFIG) {
|
|
|
|
//
|
|
// Try alloc config first
|
|
//
|
|
|
|
if (handle == NULL) {
|
|
PiWstrToUnicodeString(&unicodeName, REGSTR_KEY_LOG_CONF);
|
|
status = IopOpenRegistryKeyEx( &handle,
|
|
handlex,
|
|
&unicodeName,
|
|
KEY_READ
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
ZwClose(handlex);
|
|
return status;
|
|
}
|
|
}
|
|
status = PipReadDeviceConfiguration( handle,
|
|
REGISTRY_BOOT_CONFIG,
|
|
(PCM_RESOURCE_LIST *)Resource,
|
|
Length);
|
|
}
|
|
if (handle) {
|
|
ZwClose(handle);
|
|
}
|
|
} else {
|
|
|
|
PiWstrToUnicodeString(&unicodeName, REGSTR_KEY_LOG_CONF);
|
|
status = IopOpenRegistryKeyEx( &handle,
|
|
handlex,
|
|
&unicodeName,
|
|
KEY_READ
|
|
);
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
if (Preference & REGISTRY_OVERRIDE_CONFIGVECTOR) {
|
|
valueName = REGSTR_VALUE_OVERRIDE_CONFIG_VECTOR;
|
|
} else if (Preference & REGISTRY_BASIC_CONFIGVECTOR) {
|
|
valueName = REGSTR_VALUE_BASIC_CONFIG_VECTOR;
|
|
}
|
|
if (valueName) {
|
|
|
|
//
|
|
// Try to read device's configuration vector
|
|
//
|
|
|
|
status = IopGetRegistryValue (handle,
|
|
valueName,
|
|
&keyValueInformation);
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Try to read what caller wants.
|
|
//
|
|
|
|
if ((keyValueInformation->Type == REG_RESOURCE_REQUIREMENTS_LIST) &&
|
|
(keyValueInformation->DataLength != 0)) {
|
|
|
|
*Resource = ExAllocatePool(PagedPool,
|
|
keyValueInformation->DataLength);
|
|
if (*Resource) {
|
|
PIO_RESOURCE_REQUIREMENTS_LIST ioResource;
|
|
|
|
*Length = keyValueInformation->DataLength;
|
|
RtlCopyMemory(*Resource,
|
|
KEY_VALUE_DATA(keyValueInformation),
|
|
keyValueInformation->DataLength);
|
|
|
|
//
|
|
// Process the io resource requirements list to change undefined
|
|
// interface type to our default type.
|
|
//
|
|
|
|
ioResource = *Resource;
|
|
if (ioResource->InterfaceType == InterfaceTypeUndefined) {
|
|
ioResource->BusNumber = 0;
|
|
ioResource->InterfaceType = PnpDefaultInterfaceType;
|
|
}
|
|
} else {
|
|
status = STATUS_INVALID_PARAMETER_2;
|
|
}
|
|
}
|
|
ExFreePool(keyValueInformation);
|
|
}
|
|
}
|
|
ZwClose(handle);
|
|
}
|
|
}
|
|
ZwClose(handlex);
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
PipReadDeviceConfiguration (
|
|
IN HANDLE Handle,
|
|
IN ULONG Flags,
|
|
OUT PCM_RESOURCE_LIST *CmResource,
|
|
OUT PULONG Length
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine read the specified ALLOC config or ForcedConfig or Boot config.
|
|
|
|
Arguments:
|
|
|
|
Hanle - supplies a handle to the registry key to read resources.
|
|
|
|
Return Value:
|
|
|
|
status
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
PKEY_VALUE_FULL_INFORMATION keyValueInformation;
|
|
PWCHAR valueName;
|
|
PCM_RESOURCE_LIST resourceList;
|
|
PCM_FULL_RESOURCE_DESCRIPTOR cmFullDesc;
|
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR cmPartDesc;
|
|
ULONG j, k, size;
|
|
|
|
PAGED_CODE();
|
|
|
|
*CmResource = NULL;
|
|
*Length = 0;
|
|
|
|
switch (Flags) {
|
|
|
|
case REGISTRY_ALLOC_CONFIG:
|
|
valueName = REGSTR_VALUE_ALLOC_CONFIG;
|
|
break;
|
|
|
|
case REGISTRY_FORCED_CONFIG:
|
|
valueName = REGSTR_VALUE_FORCED_CONFIG;
|
|
break;
|
|
|
|
case REGISTRY_BOOT_CONFIG:
|
|
valueName = REGSTR_VALUE_BOOT_CONFIG;
|
|
break;
|
|
|
|
default:
|
|
return STATUS_INVALID_PARAMETER_2;
|
|
}
|
|
|
|
//
|
|
// Read the registry value of the desired value name
|
|
//
|
|
status = IopGetRegistryValue(Handle,
|
|
valueName,
|
|
&keyValueInformation
|
|
);
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
if ( keyValueInformation->Type == REG_RESOURCE_LIST &&
|
|
keyValueInformation->DataLength != 0) {
|
|
|
|
*CmResource = ExAllocatePool(PagedPool,
|
|
keyValueInformation->DataLength
|
|
);
|
|
if (*CmResource) {
|
|
|
|
*Length = keyValueInformation->DataLength;
|
|
RtlCopyMemory(*CmResource,
|
|
KEY_VALUE_DATA(keyValueInformation),
|
|
keyValueInformation->DataLength
|
|
);
|
|
//
|
|
// Process the resource list read from Registry to change undefined
|
|
// interface type to our default interface type.
|
|
//
|
|
resourceList = *CmResource;
|
|
cmFullDesc = &resourceList->List[0];
|
|
for (j = 0; j < resourceList->Count; j++) {
|
|
|
|
if (cmFullDesc->InterfaceType == InterfaceTypeUndefined) {
|
|
|
|
cmFullDesc->BusNumber = 0;
|
|
cmFullDesc->InterfaceType = PnpDefaultInterfaceType;
|
|
}
|
|
|
|
cmPartDesc = &cmFullDesc->PartialResourceList.PartialDescriptors[0];
|
|
for (k = 0; k < cmFullDesc->PartialResourceList.Count; k++) {
|
|
|
|
size = 0;
|
|
switch (cmPartDesc->Type) {
|
|
case CmResourceTypeDeviceSpecific:
|
|
size = cmPartDesc->u.DeviceSpecificData.DataSize;
|
|
break;
|
|
}
|
|
cmPartDesc++;
|
|
cmPartDesc = (PCM_PARTIAL_RESOURCE_DESCRIPTOR) ((PUCHAR)cmPartDesc + size);
|
|
}
|
|
cmFullDesc = (PCM_FULL_RESOURCE_DESCRIPTOR)cmPartDesc;
|
|
}
|
|
} else {
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
} else if (keyValueInformation->Type != REG_RESOURCE_LIST) {
|
|
|
|
status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
ExFreePool(keyValueInformation);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
PIO_RESOURCE_REQUIREMENTS_LIST
|
|
IopCmResourcesToIoResources(
|
|
IN ULONG SlotNumber,
|
|
IN PCM_RESOURCE_LIST CmResourceList,
|
|
IN ULONG Priority
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routines converts the input CmResourceList to IO_RESOURCE_REQUIREMENTS_LIST.
|
|
|
|
Arguments:
|
|
|
|
SlotNumber - supplies the SlotNumber the resources refer to.
|
|
|
|
CmResourceList - the cm resource list to convert.
|
|
|
|
Priority - specifies the priority of the logconfig
|
|
|
|
Return Value:
|
|
|
|
returns a IO_RESOURCE_REQUIREMENTS_LISTST if succeeds. Otherwise a NULL value is
|
|
returned.
|
|
|
|
--*/
|
|
{
|
|
PIO_RESOURCE_REQUIREMENTS_LIST ioResReqList;
|
|
ULONG count = 0, size, i, j;
|
|
PCM_FULL_RESOURCE_DESCRIPTOR cmFullDesc;
|
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR cmPartDesc;
|
|
PIO_RESOURCE_DESCRIPTOR ioDesc;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// First determine number of descriptors required.
|
|
//
|
|
cmFullDesc = &CmResourceList->List[0];
|
|
for (i = 0; i < CmResourceList->Count; i++) {
|
|
count += cmFullDesc->PartialResourceList.Count;
|
|
cmPartDesc = &cmFullDesc->PartialResourceList.PartialDescriptors[0];
|
|
for (j = 0; j < cmFullDesc->PartialResourceList.Count; j++) {
|
|
size = 0;
|
|
switch (cmPartDesc->Type) {
|
|
case CmResourceTypeDeviceSpecific:
|
|
size = cmPartDesc->u.DeviceSpecificData.DataSize;
|
|
count--;
|
|
break;
|
|
}
|
|
cmPartDesc++;
|
|
cmPartDesc = (PCM_PARTIAL_RESOURCE_DESCRIPTOR) ((PUCHAR)cmPartDesc + size);
|
|
}
|
|
cmFullDesc = (PCM_FULL_RESOURCE_DESCRIPTOR)cmPartDesc;
|
|
}
|
|
|
|
if (count == 0) {
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Count the extra descriptors for InterfaceType and BusNumber information.
|
|
//
|
|
|
|
count += CmResourceList->Count - 1;
|
|
|
|
//
|
|
// Allocate heap space for IO RESOURCE REQUIREMENTS LIST
|
|
//
|
|
|
|
count++; // add one for CmResourceTypeConfigData
|
|
ioResReqList = (PIO_RESOURCE_REQUIREMENTS_LIST)ExAllocatePool(
|
|
PagedPool,
|
|
sizeof(IO_RESOURCE_REQUIREMENTS_LIST) +
|
|
count * sizeof(IO_RESOURCE_DESCRIPTOR)
|
|
);
|
|
if (!ioResReqList) {
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Parse the cm resource descriptor and build its corresponding IO resource descriptor
|
|
//
|
|
|
|
ioResReqList->InterfaceType = CmResourceList->List[0].InterfaceType;
|
|
ioResReqList->BusNumber = CmResourceList->List[0].BusNumber;
|
|
ioResReqList->SlotNumber = SlotNumber;
|
|
ioResReqList->Reserved[0] = 0;
|
|
ioResReqList->Reserved[1] = 0;
|
|
ioResReqList->Reserved[2] = 0;
|
|
ioResReqList->AlternativeLists = 1;
|
|
ioResReqList->List[0].Version = 1;
|
|
ioResReqList->List[0].Revision = 1;
|
|
ioResReqList->List[0].Count = count;
|
|
|
|
//
|
|
// Generate a CmResourceTypeConfigData descriptor
|
|
//
|
|
|
|
ioDesc = &ioResReqList->List[0].Descriptors[0];
|
|
ioDesc->Option = IO_RESOURCE_PREFERRED;
|
|
ioDesc->Type = CmResourceTypeConfigData;
|
|
ioDesc->ShareDisposition = CmResourceShareShared;
|
|
ioDesc->Flags = 0;
|
|
ioDesc->Spare1 = 0;
|
|
ioDesc->Spare2 = 0;
|
|
ioDesc->u.ConfigData.Priority = Priority;
|
|
ioDesc++;
|
|
|
|
cmFullDesc = &CmResourceList->List[0];
|
|
for (i = 0; i < CmResourceList->Count; i++) {
|
|
if (i != 0) {
|
|
|
|
//
|
|
// Set up descriptor to remember the InterfaceType and BusNumber.
|
|
//
|
|
|
|
ioDesc->Option = IO_RESOURCE_PREFERRED;
|
|
ioDesc->Type = CmResourceTypeReserved;
|
|
ioDesc->ShareDisposition = CmResourceShareUndetermined;
|
|
ioDesc->Flags = 0;
|
|
ioDesc->Spare1 = 0;
|
|
ioDesc->Spare2 = 0;
|
|
if (cmFullDesc->InterfaceType == InterfaceTypeUndefined) {
|
|
ioDesc->u.DevicePrivate.Data[0] = PnpDefaultInterfaceType;
|
|
} else {
|
|
ioDesc->u.DevicePrivate.Data[0] = cmFullDesc->InterfaceType;
|
|
}
|
|
ioDesc->u.DevicePrivate.Data[1] = cmFullDesc->BusNumber;
|
|
ioDesc->u.DevicePrivate.Data[2] = 0;
|
|
ioDesc++;
|
|
}
|
|
cmPartDesc = &cmFullDesc->PartialResourceList.PartialDescriptors[0];
|
|
for (j = 0; j < cmFullDesc->PartialResourceList.Count; j++) {
|
|
ioDesc->Option = IO_RESOURCE_PREFERRED;
|
|
ioDesc->Type = cmPartDesc->Type;
|
|
ioDesc->ShareDisposition = cmPartDesc->ShareDisposition;
|
|
ioDesc->Flags = cmPartDesc->Flags;
|
|
ioDesc->Spare1 = 0;
|
|
ioDesc->Spare2 = 0;
|
|
|
|
size = 0;
|
|
switch (cmPartDesc->Type) {
|
|
case CmResourceTypePort:
|
|
ioDesc->u.Port.MinimumAddress = cmPartDesc->u.Port.Start;
|
|
ioDesc->u.Port.MaximumAddress.QuadPart = cmPartDesc->u.Port.Start.QuadPart +
|
|
cmPartDesc->u.Port.Length - 1;
|
|
ioDesc->u.Port.Alignment = 1;
|
|
ioDesc->u.Port.Length = cmPartDesc->u.Port.Length;
|
|
ioDesc++;
|
|
break;
|
|
case CmResourceTypeInterrupt:
|
|
#if defined(_X86_)
|
|
ioDesc->u.Interrupt.MinimumVector = ioDesc->u.Interrupt.MaximumVector =
|
|
cmPartDesc->u.Interrupt.Level;
|
|
#else
|
|
ioDesc->u.Interrupt.MinimumVector = ioDesc->u.Interrupt.MaximumVector =
|
|
cmPartDesc->u.Interrupt.Vector;
|
|
#endif
|
|
ioDesc++;
|
|
break;
|
|
case CmResourceTypeMemory:
|
|
ioDesc->u.Memory.MinimumAddress = cmPartDesc->u.Memory.Start;
|
|
ioDesc->u.Memory.MaximumAddress.QuadPart = cmPartDesc->u.Memory.Start.QuadPart +
|
|
cmPartDesc->u.Memory.Length - 1;
|
|
ioDesc->u.Memory.Alignment = 1;
|
|
ioDesc->u.Memory.Length = cmPartDesc->u.Memory.Length;
|
|
ioDesc++;
|
|
break;
|
|
case CmResourceTypeDma:
|
|
ioDesc->u.Dma.MinimumChannel = cmPartDesc->u.Dma.Channel;
|
|
ioDesc->u.Dma.MaximumChannel = cmPartDesc->u.Dma.Channel;
|
|
ioDesc++;
|
|
break;
|
|
case CmResourceTypeDeviceSpecific:
|
|
size = cmPartDesc->u.DeviceSpecificData.DataSize;
|
|
break;
|
|
case CmResourceTypeBusNumber:
|
|
ioDesc->u.BusNumber.MinBusNumber = cmPartDesc->u.BusNumber.Start;
|
|
ioDesc->u.BusNumber.MaxBusNumber = cmPartDesc->u.BusNumber.Start +
|
|
cmPartDesc->u.BusNumber.Length - 1;
|
|
ioDesc->u.BusNumber.Length = cmPartDesc->u.BusNumber.Length;
|
|
ioDesc++;
|
|
break;
|
|
default:
|
|
ioDesc->u.DevicePrivate.Data[0] = cmPartDesc->u.DevicePrivate.Data[0];
|
|
ioDesc->u.DevicePrivate.Data[1] = cmPartDesc->u.DevicePrivate.Data[1];
|
|
ioDesc->u.DevicePrivate.Data[2] = cmPartDesc->u.DevicePrivate.Data[2];
|
|
ioDesc++;
|
|
break;
|
|
}
|
|
cmPartDesc++;
|
|
cmPartDesc = (PCM_PARTIAL_RESOURCE_DESCRIPTOR) ((PUCHAR)cmPartDesc + size);
|
|
}
|
|
cmFullDesc = (PCM_FULL_RESOURCE_DESCRIPTOR)cmPartDesc;
|
|
}
|
|
ioResReqList->ListSize = (ULONG)((ULONG_PTR)ioDesc - (ULONG_PTR)ioResReqList);
|
|
return ioResReqList;
|
|
}
|
|
|
|
NTSTATUS
|
|
IopFilterResourceRequirementsList(
|
|
IN PIO_RESOURCE_REQUIREMENTS_LIST IoList,
|
|
IN PCM_RESOURCE_LIST CmList,
|
|
IN OUT PIO_RESOURCE_REQUIREMENTS_LIST *FilteredList,
|
|
OUT PBOOLEAN ExactMatch
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routines adjusts the input IoList based on input BootConfig.
|
|
|
|
|
|
Arguments:
|
|
|
|
IoList - supplies the pointer to an IoResourceRequirementsList
|
|
|
|
CmList - supplies the pointer to a BootConfig.
|
|
|
|
FilteredList - Supplies a variable to receive the filtered resource
|
|
requirements list.
|
|
|
|
Return Value:
|
|
|
|
A NTSTATUS code to indicate the result of the function.
|
|
|
|
--*/
|
|
{
|
|
PIO_RESOURCE_REQUIREMENTS_LIST ioList, newList;
|
|
PIO_RESOURCE_LIST ioResourceList, newIoResourceList, selectedResourceList = NULL;
|
|
PIO_RESOURCE_DESCRIPTOR ioResourceDescriptor, ioResourceDescriptorEnd;
|
|
PIO_RESOURCE_DESCRIPTOR newIoResourceDescriptor, configDataDescriptor;
|
|
LONG ioResourceDescriptorCount = 0;
|
|
USHORT version;
|
|
PCM_FULL_RESOURCE_DESCRIPTOR cmFullDesc;
|
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR cmDescriptor;
|
|
ULONG cmDescriptorCount = 0;
|
|
ULONG size, i, j, oldCount, phase;
|
|
LONG k, alternativeLists;
|
|
BOOLEAN exactMatch;
|
|
|
|
PAGED_CODE();
|
|
|
|
*FilteredList = NULL;
|
|
*ExactMatch = FALSE;
|
|
|
|
//
|
|
// Make sure there is some resource requirements to be filtered.
|
|
// If no, we will convert CmList/BootConfig to an IoResourceRequirementsList
|
|
//
|
|
|
|
if (IoList == NULL || IoList->AlternativeLists == 0) {
|
|
if (CmList && CmList->Count != 0) {
|
|
*FilteredList = IopCmResourcesToIoResources (0, CmList, LCPRI_BOOTCONFIG);
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Make a copy of the Io Resource Requirements List
|
|
//
|
|
|
|
ioList = (PIO_RESOURCE_REQUIREMENTS_LIST) ExAllocatePool(PagedPool, IoList->ListSize);
|
|
if (ioList == NULL) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlCopyMemory(ioList, IoList, IoList->ListSize);
|
|
|
|
//
|
|
// If there is no BootConfig, simply return the copy of the input Io list.
|
|
//
|
|
|
|
if (CmList == NULL || CmList->Count == 0) {
|
|
*FilteredList = ioList;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// First determine minimum number of descriptors required.
|
|
//
|
|
|
|
cmFullDesc = &CmList->List[0];
|
|
for (i = 0; i < CmList->Count; i++) {
|
|
cmDescriptorCount += cmFullDesc->PartialResourceList.Count;
|
|
cmDescriptor = &cmFullDesc->PartialResourceList.PartialDescriptors[0];
|
|
for (j = 0; j < cmFullDesc->PartialResourceList.Count; j++) {
|
|
size = 0;
|
|
switch (cmDescriptor->Type) {
|
|
case CmResourceTypeConfigData:
|
|
case CmResourceTypeDevicePrivate:
|
|
cmDescriptorCount--;
|
|
break;
|
|
case CmResourceTypeDeviceSpecific:
|
|
size = cmDescriptor->u.DeviceSpecificData.DataSize;
|
|
cmDescriptorCount--;
|
|
break;
|
|
default:
|
|
|
|
//
|
|
// Invalid cmresource list. Ignore it and use io resources
|
|
//
|
|
|
|
if (cmDescriptor->Type == CmResourceTypeNull ||
|
|
cmDescriptor->Type >= CmResourceTypeMaximum) {
|
|
cmDescriptorCount--;
|
|
}
|
|
}
|
|
cmDescriptor++;
|
|
cmDescriptor = (PCM_PARTIAL_RESOURCE_DESCRIPTOR) ((PUCHAR)cmDescriptor + size);
|
|
}
|
|
cmFullDesc = (PCM_FULL_RESOURCE_DESCRIPTOR)cmDescriptor;
|
|
}
|
|
|
|
if (cmDescriptorCount == 0) {
|
|
*FilteredList = ioList;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// cmDescriptorCount is the number of BootConfig Descriptors needs.
|
|
//
|
|
// For each IO list Alternative ...
|
|
//
|
|
|
|
ioResourceList = ioList->List;
|
|
k = ioList->AlternativeLists;
|
|
while (--k >= 0) {
|
|
ioResourceDescriptor = ioResourceList->Descriptors;
|
|
ioResourceDescriptorEnd = ioResourceDescriptor + ioResourceList->Count;
|
|
while (ioResourceDescriptor < ioResourceDescriptorEnd) {
|
|
ioResourceDescriptor->Spare1 = 0;
|
|
ioResourceDescriptor++;
|
|
}
|
|
ioResourceList = (PIO_RESOURCE_LIST) ioResourceDescriptorEnd;
|
|
}
|
|
|
|
ioResourceList = ioList->List;
|
|
k = alternativeLists = ioList->AlternativeLists;
|
|
while (--k >= 0) {
|
|
version = ioResourceList->Version;
|
|
if (version == 0xffff) { // Convert bogus version to valid number
|
|
version = 1;
|
|
}
|
|
|
|
//
|
|
// We use Version field to store number of BootConfig found.
|
|
// Count field to store new number of descriptor in the alternative list.
|
|
//
|
|
|
|
ioResourceList->Version = 0;
|
|
oldCount = ioResourceList->Count;
|
|
|
|
ioResourceDescriptor = ioResourceList->Descriptors;
|
|
ioResourceDescriptorEnd = ioResourceDescriptor + ioResourceList->Count;
|
|
|
|
if (ioResourceDescriptor == ioResourceDescriptorEnd) {
|
|
|
|
//
|
|
// An alternative list with zero descriptor count
|
|
//
|
|
|
|
ioResourceList->Version = 0xffff; // Mark it as invalid
|
|
ioList->AlternativeLists--;
|
|
continue;
|
|
}
|
|
|
|
exactMatch = TRUE;
|
|
|
|
//
|
|
// For each Cm Resource descriptor ... except DevicePrivate and
|
|
// DeviceSpecific...
|
|
//
|
|
|
|
cmFullDesc = &CmList->List[0];
|
|
for (i = 0; i < CmList->Count; i++) {
|
|
cmDescriptor = &cmFullDesc->PartialResourceList.PartialDescriptors[0];
|
|
for (j = 0; j < cmFullDesc->PartialResourceList.Count; j++) {
|
|
size = 0;
|
|
switch (cmDescriptor->Type) {
|
|
case CmResourceTypeDevicePrivate:
|
|
break;
|
|
case CmResourceTypeDeviceSpecific:
|
|
size = cmDescriptor->u.DeviceSpecificData.DataSize;
|
|
break;
|
|
default:
|
|
if (cmDescriptor->Type == CmResourceTypeNull ||
|
|
cmDescriptor->Type >= CmResourceTypeMaximum) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Check CmDescriptor against current Io Alternative list
|
|
//
|
|
|
|
for (phase = 0; phase < 2; phase++) {
|
|
ioResourceDescriptor = ioResourceList->Descriptors;
|
|
while (ioResourceDescriptor < ioResourceDescriptorEnd) {
|
|
if ((ioResourceDescriptor->Type == cmDescriptor->Type) &&
|
|
(ioResourceDescriptor->Spare1 == 0)) {
|
|
ULONGLONG min1, max1, min2, max2;
|
|
ULONG len1 = 1, len2 = 1, align1, align2;
|
|
UCHAR share1, share2;
|
|
|
|
share2 = ioResourceDescriptor->ShareDisposition;
|
|
share1 = cmDescriptor->ShareDisposition;
|
|
if ((share1 == CmResourceShareUndetermined) ||
|
|
(share1 > CmResourceShareShared)) {
|
|
share1 = share2;
|
|
}
|
|
if ((share2 == CmResourceShareUndetermined) ||
|
|
(share2 > CmResourceShareShared)) {
|
|
share2 = share1;
|
|
}
|
|
align1 = align2 = 1;
|
|
|
|
switch (cmDescriptor->Type) {
|
|
case CmResourceTypePort:
|
|
case CmResourceTypeMemory:
|
|
min1 = cmDescriptor->u.Port.Start.QuadPart;
|
|
max1 = cmDescriptor->u.Port.Start.QuadPart + cmDescriptor->u.Port.Length - 1;
|
|
len1 = cmDescriptor->u.Port.Length;
|
|
min2 = ioResourceDescriptor->u.Port.MinimumAddress.QuadPart;
|
|
max2 = ioResourceDescriptor->u.Port.MaximumAddress.QuadPart;
|
|
len2 = ioResourceDescriptor->u.Port.Length;
|
|
align2 = ioResourceDescriptor->u.Port.Alignment;
|
|
break;
|
|
case CmResourceTypeInterrupt:
|
|
max1 = min1 = cmDescriptor->u.Interrupt.Vector;
|
|
min2 = ioResourceDescriptor->u.Interrupt.MinimumVector;
|
|
max2 = ioResourceDescriptor->u.Interrupt.MaximumVector;
|
|
break;
|
|
case CmResourceTypeDma:
|
|
min1 = max1 =cmDescriptor->u.Dma.Channel;
|
|
min2 = ioResourceDescriptor->u.Dma.MinimumChannel;
|
|
max2 = ioResourceDescriptor->u.Dma.MaximumChannel;
|
|
break;
|
|
case CmResourceTypeBusNumber:
|
|
min1 = cmDescriptor->u.BusNumber.Start;
|
|
max1 = cmDescriptor->u.BusNumber.Start + cmDescriptor->u.BusNumber.Length - 1;
|
|
len1 = cmDescriptor->u.BusNumber.Length;
|
|
min2 = ioResourceDescriptor->u.BusNumber.MinBusNumber;
|
|
max2 = ioResourceDescriptor->u.BusNumber.MaxBusNumber;
|
|
len2 = ioResourceDescriptor->u.BusNumber.Length;
|
|
break;
|
|
default:
|
|
ASSERT(0);
|
|
max1 = max2 = min1 = min2 = 0;
|
|
break;
|
|
}
|
|
if (phase == 0) {
|
|
if (share1 == share2 && min2 == min1 && max2 >= max1 && len2 >= len1) {
|
|
|
|
//
|
|
// For phase 0 match, we want near exact match...
|
|
//
|
|
|
|
if (max2 != max1) {
|
|
exactMatch = FALSE;
|
|
}
|
|
ioResourceList->Version++;
|
|
ioResourceDescriptor->Spare1 = 0x80;
|
|
if (ioResourceDescriptor->Option & IO_RESOURCE_ALTERNATIVE) {
|
|
PIO_RESOURCE_DESCRIPTOR ioDesc;
|
|
|
|
ioDesc = ioResourceDescriptor;
|
|
ioDesc--;
|
|
while (ioDesc >= ioResourceList->Descriptors) {
|
|
ioDesc->Type = CmResourceTypeNull;
|
|
ioResourceList->Count--;
|
|
if (ioDesc->Option == IO_RESOURCE_ALTERNATIVE) {
|
|
ioDesc--;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
ioResourceDescriptor->Option = IO_RESOURCE_PREFERRED;
|
|
ioResourceDescriptor->Flags = cmDescriptor->Flags;
|
|
if (ioResourceDescriptor->Type == CmResourceTypePort ||
|
|
ioResourceDescriptor->Type == CmResourceTypeMemory) {
|
|
ioResourceDescriptor->u.Port.MinimumAddress.QuadPart = min1;
|
|
ioResourceDescriptor->u.Port.MaximumAddress.QuadPart = min1 + len2 - 1;
|
|
ioResourceDescriptor->u.Port.Alignment = 1;
|
|
} else if (ioResourceDescriptor->Type == CmResourceTypeBusNumber) {
|
|
ioResourceDescriptor->u.BusNumber.MinBusNumber = (ULONG)min1;
|
|
ioResourceDescriptor->u.BusNumber.MaxBusNumber = (ULONG)(min1 + len2 - 1);
|
|
}
|
|
ioResourceDescriptor++;
|
|
while (ioResourceDescriptor < ioResourceDescriptorEnd) {
|
|
if (ioResourceDescriptor->Option & IO_RESOURCE_ALTERNATIVE) {
|
|
ioResourceDescriptor->Type = CmResourceTypeNull;
|
|
ioResourceDescriptor++;
|
|
ioResourceList->Count--;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
phase = 1; // skip phase 1
|
|
break;
|
|
} else {
|
|
ioResourceDescriptor++;
|
|
}
|
|
} else {
|
|
exactMatch = FALSE;
|
|
if (share1 == share2 && min2 <= min1 && max2 >= max1 && len2 >= len1 &&
|
|
(min1 & (align2 - 1)) == 0) {
|
|
|
|
//
|
|
// Io range covers Cm range ... Change the Io range to what is specified
|
|
// in BootConfig.
|
|
//
|
|
//
|
|
|
|
switch (cmDescriptor->Type) {
|
|
case CmResourceTypePort:
|
|
case CmResourceTypeMemory:
|
|
ioResourceDescriptor->u.Port.MinimumAddress.QuadPart = min1;
|
|
ioResourceDescriptor->u.Port.MaximumAddress.QuadPart = min1 + len2 - 1;
|
|
break;
|
|
case CmResourceTypeInterrupt:
|
|
case CmResourceTypeDma:
|
|
ioResourceDescriptor->u.Interrupt.MinimumVector = (ULONG)min1;
|
|
ioResourceDescriptor->u.Interrupt.MaximumVector = (ULONG)max1;
|
|
break;
|
|
case CmResourceTypeBusNumber:
|
|
ioResourceDescriptor->u.BusNumber.MinBusNumber = (ULONG)min1;
|
|
ioResourceDescriptor->u.BusNumber.MaxBusNumber = (ULONG)(min1 + len2 - 1);
|
|
break;
|
|
}
|
|
ioResourceList->Version++;
|
|
ioResourceDescriptor->Spare1 = 0x80;
|
|
ioResourceDescriptor->Flags = cmDescriptor->Flags;
|
|
if (ioResourceDescriptor->Option & IO_RESOURCE_ALTERNATIVE) {
|
|
PIO_RESOURCE_DESCRIPTOR ioDesc;
|
|
|
|
ioDesc = ioResourceDescriptor;
|
|
ioDesc--;
|
|
while (ioDesc >= ioResourceList->Descriptors) {
|
|
ioDesc->Type = CmResourceTypeNull;
|
|
ioResourceList->Count--;
|
|
if (ioDesc->Option == IO_RESOURCE_ALTERNATIVE) {
|
|
ioDesc--;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
ioResourceDescriptor->Option = IO_RESOURCE_PREFERRED;
|
|
ioResourceDescriptor++;
|
|
while (ioResourceDescriptor < ioResourceDescriptorEnd) {
|
|
if (ioResourceDescriptor->Option & IO_RESOURCE_ALTERNATIVE) {
|
|
ioResourceDescriptor->Type = CmResourceTypeNull;
|
|
ioResourceList->Count--;
|
|
ioResourceDescriptor++;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
} else {
|
|
ioResourceDescriptor++;
|
|
}
|
|
}
|
|
} else {
|
|
ioResourceDescriptor++;
|
|
}
|
|
} // Don't add any instruction after this ...
|
|
} // phase
|
|
} // switch
|
|
|
|
//
|
|
// Move to next Cm Descriptor
|
|
//
|
|
|
|
cmDescriptor++;
|
|
cmDescriptor = (PCM_PARTIAL_RESOURCE_DESCRIPTOR) ((PUCHAR)cmDescriptor + size);
|
|
}
|
|
|
|
//
|
|
// Move to next Cm List
|
|
//
|
|
|
|
cmFullDesc = (PCM_FULL_RESOURCE_DESCRIPTOR)cmDescriptor;
|
|
}
|
|
|
|
if (ioResourceList->Version != (USHORT)cmDescriptorCount) {
|
|
|
|
//
|
|
// If the current alternative list does not cover all the boot config
|
|
// descriptors, make it as invalid.
|
|
//
|
|
|
|
ioResourceList->Version = 0xffff;
|
|
ioList->AlternativeLists--;
|
|
} else {
|
|
if ((ioResourceList->Count == cmDescriptorCount) ||
|
|
(ioResourceList->Count == (cmDescriptorCount + 1) &&
|
|
ioResourceList->Descriptors[0].Type == CmResourceTypeConfigData)) {
|
|
if (selectedResourceList) {
|
|
ioResourceList->Version = 0xffff;
|
|
ioList->AlternativeLists--;
|
|
} else {
|
|
selectedResourceList = ioResourceList;
|
|
ioResourceDescriptorCount += ioResourceList->Count;
|
|
ioResourceList->Version = version;
|
|
if (exactMatch) {
|
|
*ExactMatch = TRUE;
|
|
}
|
|
}
|
|
} else {
|
|
ioResourceDescriptorCount += ioResourceList->Count;
|
|
ioResourceList->Version = version;
|
|
}
|
|
}
|
|
ioResourceList->Count = oldCount;
|
|
|
|
//
|
|
// Move to next Io alternative list.
|
|
//
|
|
|
|
ioResourceList = (PIO_RESOURCE_LIST) ioResourceDescriptorEnd;
|
|
}
|
|
|
|
//
|
|
// If there is not any valid alternative, convert CmList to Io list.
|
|
//
|
|
|
|
if (ioList->AlternativeLists == 0) {
|
|
*FilteredList = IopCmResourcesToIoResources (0, CmList, LCPRI_BOOTCONFIG);
|
|
ExFreePool(ioList);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// we have finished filtering the resource requirements list. Now allocate memory
|
|
// and rebuild a new list.
|
|
//
|
|
|
|
size = sizeof(IO_RESOURCE_REQUIREMENTS_LIST) +
|
|
sizeof(IO_RESOURCE_LIST) * (ioList->AlternativeLists - 1) +
|
|
sizeof(IO_RESOURCE_DESCRIPTOR) * (ioResourceDescriptorCount);
|
|
newList = (PIO_RESOURCE_REQUIREMENTS_LIST) ExAllocatePool(PagedPool, size);
|
|
if (newList == NULL) {
|
|
ExFreePool(ioList);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Walk through the io resource requirements list and pick up any valid descriptor.
|
|
//
|
|
|
|
newList->ListSize = size;
|
|
newList->InterfaceType = CmList->List->InterfaceType;
|
|
newList->BusNumber = CmList->List->BusNumber;
|
|
newList->SlotNumber = ioList->SlotNumber;
|
|
if (ioList->AlternativeLists > 1) {
|
|
*ExactMatch = FALSE;
|
|
}
|
|
newList->AlternativeLists = ioList->AlternativeLists;
|
|
ioResourceList = ioList->List;
|
|
newIoResourceList = newList->List;
|
|
while (--alternativeLists >= 0) {
|
|
ioResourceDescriptor = ioResourceList->Descriptors;
|
|
ioResourceDescriptorEnd = ioResourceDescriptor + ioResourceList->Count;
|
|
if (ioResourceList->Version == 0xffff) {
|
|
ioResourceList = (PIO_RESOURCE_LIST)ioResourceDescriptorEnd;
|
|
continue;
|
|
}
|
|
newIoResourceList->Version = ioResourceList->Version;
|
|
newIoResourceList->Revision = ioResourceList->Revision;
|
|
|
|
newIoResourceDescriptor = newIoResourceList->Descriptors;
|
|
if (ioResourceDescriptor->Type != CmResourceTypeConfigData) {
|
|
newIoResourceDescriptor->Option = IO_RESOURCE_PREFERRED;
|
|
newIoResourceDescriptor->Type = CmResourceTypeConfigData;
|
|
newIoResourceDescriptor->ShareDisposition = CmResourceShareShared;
|
|
newIoResourceDescriptor->Flags = 0;
|
|
newIoResourceDescriptor->Spare1 = 0;
|
|
newIoResourceDescriptor->Spare2 = 0;
|
|
newIoResourceDescriptor->u.ConfigData.Priority = LCPRI_BOOTCONFIG;
|
|
configDataDescriptor = newIoResourceDescriptor;
|
|
newIoResourceDescriptor++;
|
|
} else {
|
|
newList->ListSize -= sizeof(IO_RESOURCE_DESCRIPTOR);
|
|
configDataDescriptor = newIoResourceDescriptor;
|
|
}
|
|
|
|
while (ioResourceDescriptor < ioResourceDescriptorEnd) {
|
|
if (ioResourceDescriptor->Type != CmResourceTypeNull) {
|
|
*newIoResourceDescriptor = *ioResourceDescriptor;
|
|
newIoResourceDescriptor++;
|
|
}
|
|
ioResourceDescriptor++;
|
|
}
|
|
newIoResourceList->Count = (ULONG)(newIoResourceDescriptor - newIoResourceList->Descriptors);
|
|
|
|
//if (newIoResourceList->Count == (cmDescriptorCount + 1)) {
|
|
configDataDescriptor->u.ConfigData.Priority = LCPRI_BOOTCONFIG;
|
|
//}
|
|
|
|
//
|
|
// Move to next Io alternative list.
|
|
//
|
|
|
|
newIoResourceList = (PIO_RESOURCE_LIST) newIoResourceDescriptor;
|
|
ioResourceList = (PIO_RESOURCE_LIST) ioResourceDescriptorEnd;
|
|
}
|
|
ASSERT((PUCHAR)newIoResourceList == ((PUCHAR)newList + newList->ListSize));
|
|
|
|
*FilteredList = newList;
|
|
ExFreePool(ioList);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
IopMergeFilteredResourceRequirementsList (
|
|
IN PIO_RESOURCE_REQUIREMENTS_LIST IoList1,
|
|
IN PIO_RESOURCE_REQUIREMENTS_LIST IoList2,
|
|
IN OUT PIO_RESOURCE_REQUIREMENTS_LIST *MergedList
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routines merges two IoLists into one.
|
|
|
|
|
|
Arguments:
|
|
|
|
IoList1 - supplies the pointer to the first IoResourceRequirementsList
|
|
|
|
IoList2 - supplies the pointer to the second IoResourceRequirementsList
|
|
|
|
MergedList - Supplies a variable to receive the merged resource
|
|
requirements list.
|
|
|
|
Return Value:
|
|
|
|
A NTSTATUS code to indicate the result of the function.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PIO_RESOURCE_REQUIREMENTS_LIST ioList, newList;
|
|
ULONG size;
|
|
PUCHAR p;
|
|
|
|
PAGED_CODE();
|
|
|
|
*MergedList = NULL;
|
|
|
|
//
|
|
// First handle the easy cases that both IO Lists are empty or any one of
|
|
// them is empty.
|
|
//
|
|
|
|
if ((IoList1 == NULL || IoList1->AlternativeLists == 0) &&
|
|
(IoList2 == NULL || IoList2->AlternativeLists == 0)) {
|
|
return status;
|
|
}
|
|
ioList = NULL;
|
|
if (IoList1 == NULL || IoList1->AlternativeLists == 0) {
|
|
ioList = IoList2;
|
|
} else if (IoList2 == NULL || IoList2->AlternativeLists == 0) {
|
|
ioList = IoList1;
|
|
}
|
|
if (ioList) {
|
|
newList = (PIO_RESOURCE_REQUIREMENTS_LIST) ExAllocatePool(PagedPool, ioList->ListSize);
|
|
if (newList == NULL) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
RtlCopyMemory(newList, ioList, ioList->ListSize);
|
|
*MergedList = newList;
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Do real work...
|
|
//
|
|
|
|
size = IoList1->ListSize + IoList2->ListSize - FIELD_OFFSET(IO_RESOURCE_REQUIREMENTS_LIST, List);
|
|
newList = (PIO_RESOURCE_REQUIREMENTS_LIST) ExAllocatePool(
|
|
PagedPool,
|
|
size
|
|
);
|
|
if (newList == NULL) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
p = (PUCHAR)newList;
|
|
RtlCopyMemory(p, IoList1, IoList1->ListSize);
|
|
p += IoList1->ListSize;
|
|
RtlCopyMemory(p,
|
|
&IoList2->List[0],
|
|
size - IoList1->ListSize
|
|
);
|
|
newList->ListSize = size;
|
|
newList->AlternativeLists += IoList2->AlternativeLists;
|
|
*MergedList = newList;
|
|
return status;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
IopMergeCmResourceLists (
|
|
IN PCM_RESOURCE_LIST List1,
|
|
IN PCM_RESOURCE_LIST List2,
|
|
IN OUT PCM_RESOURCE_LIST *MergedList
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routines merges two IoLists into one.
|
|
|
|
|
|
Arguments:
|
|
|
|
IoList1 - supplies the pointer to the first CmResourceList
|
|
|
|
IoList2 - supplies the pointer to the second CmResourceList
|
|
|
|
MergedList - Supplies a variable to receive the merged resource
|
|
list.
|
|
|
|
Return Value:
|
|
|
|
A NTSTATUS code to indicate the result of the function.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PCM_RESOURCE_LIST cmList, newList;
|
|
ULONG size, size1, size2;
|
|
PUCHAR p;
|
|
|
|
PAGED_CODE();
|
|
|
|
*MergedList = NULL;
|
|
|
|
//
|
|
// First handle the easy cases that both IO Lists are empty or any one of
|
|
// them is empty.
|
|
//
|
|
|
|
if ((List1 == NULL || List1->Count == 0) &&
|
|
(List2 == NULL || List2->Count == 0)) {
|
|
return status;
|
|
}
|
|
|
|
cmList = NULL;
|
|
if (List1 == NULL || List1->Count == 0) {
|
|
cmList = List2;
|
|
} else if (List2 == NULL || List2->Count == 0) {
|
|
cmList = List1;
|
|
}
|
|
if (cmList) {
|
|
size = IopDetermineResourceListSize(cmList);
|
|
newList = (PCM_RESOURCE_LIST) ExAllocatePool(PagedPool, size);
|
|
if (newList == NULL) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
RtlCopyMemory(newList, cmList, size);
|
|
*MergedList = newList;
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Do real work...
|
|
//
|
|
|
|
size1 = IopDetermineResourceListSize(List1);
|
|
size2 = IopDetermineResourceListSize(List2);
|
|
size = size1 + size2;
|
|
newList = (PCM_RESOURCE_LIST) ExAllocatePool(
|
|
PagedPool,
|
|
size
|
|
);
|
|
if (newList == NULL) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
p = (PUCHAR)newList;
|
|
RtlCopyMemory(p, List1, size1);
|
|
p += size1;
|
|
RtlCopyMemory(p,
|
|
&List2->List[0],
|
|
size2 - FIELD_OFFSET(CM_RESOURCE_LIST, List)
|
|
);
|
|
newList->Count = List1->Count + List2->Count;
|
|
*MergedList = newList;
|
|
return status;
|
|
|
|
}
|
|
|
|
BOOLEAN
|
|
IopIsLegacyDriver (
|
|
IN PDRIVER_OBJECT DriverObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine checks if the driver object specifies a legacy driver.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - supplies a pointer to the driver object to be checked.
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// If AddDevice entry is not empty it is a wdm driver
|
|
//
|
|
if (DriverObject->DriverExtension->AddDevice) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Else if LEGACY flag is set in the driver object, it's a legacy driver.
|
|
//
|
|
if (DriverObject->Flags & DRVO_LEGACY_DRIVER) {
|
|
|
|
return TRUE;
|
|
} else {
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
IopDeleteLegacyKey(
|
|
IN PDRIVER_OBJECT DriverObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine checks if the Legacy= value of the driver's legacy_xxx key
|
|
is one. If yes, it deletes the Legacy key.
|
|
|
|
Parameters:
|
|
|
|
DriverObject - supplies a pointer to the driver object.
|
|
|
|
Return Value:
|
|
|
|
None. If anything fails in this routine, the legacy key stays.
|
|
|
|
--*/
|
|
|
|
{
|
|
WCHAR buffer[MAX_DEVICE_ID_LEN], *end;
|
|
NTSTATUS status;
|
|
UNICODE_STRING deviceName, instanceName, unicodeName, *serviceName;
|
|
ULONG length;
|
|
HANDLE handle, handle1, handlex, enumHandle;
|
|
ULONG legacy;
|
|
PKEY_VALUE_FULL_INFORMATION keyValueInformation;
|
|
PDEVICE_OBJECT deviceObject;
|
|
PDEVICE_NODE deviceNode, devNodex, devNodey;
|
|
BOOLEAN deletedPDO;
|
|
|
|
//
|
|
// Initialize for proper cleanup.
|
|
//
|
|
enumHandle = NULL;
|
|
handle1 = NULL;
|
|
handle = NULL;
|
|
|
|
serviceName = &DriverObject->DriverExtension->ServiceKeyName;
|
|
|
|
PiLockPnpRegistry(FALSE);
|
|
|
|
status = IopOpenRegistryKeyEx(&enumHandle,
|
|
NULL,
|
|
&CmRegistryMachineSystemCurrentControlSetEnumName,
|
|
KEY_ALL_ACCESS
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
goto exit;
|
|
}
|
|
|
|
status = PipGenerateMadeupNodeName(serviceName,
|
|
&deviceName
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
goto exit;
|
|
}
|
|
|
|
StringCchPrintfExW(buffer,
|
|
sizeof(buffer) / sizeof(WCHAR),
|
|
&end,
|
|
NULL,
|
|
0,
|
|
L"%s\\%s",
|
|
REGSTR_KEY_ROOTENUM,
|
|
deviceName.Buffer
|
|
);
|
|
length = (ULONG)(end - buffer);
|
|
|
|
RtlFreeUnicodeString(&deviceName);
|
|
|
|
deviceName.MaximumLength = sizeof(buffer);
|
|
ASSERT(length <= sizeof(buffer) - 10);
|
|
deviceName.Length = (USHORT)(length * sizeof(WCHAR));
|
|
deviceName.Buffer = buffer;
|
|
|
|
RtlUpcaseUnicodeString(&deviceName, &deviceName, FALSE);
|
|
|
|
status = IopOpenRegistryKeyEx(&handle1,
|
|
enumHandle,
|
|
&deviceName,
|
|
KEY_ALL_ACCESS
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
goto exit;
|
|
}
|
|
|
|
deviceName.Buffer[deviceName.Length / sizeof(WCHAR)] =
|
|
OBJ_NAME_PATH_SEPARATOR;
|
|
deviceName.Length += sizeof(WCHAR);
|
|
PiUlongToInstanceKeyUnicodeString(
|
|
&instanceName,
|
|
buffer + deviceName.Length / sizeof(WCHAR),
|
|
sizeof(buffer) - deviceName.Length,
|
|
0
|
|
);
|
|
deviceName.Length = (USHORT)(deviceName.Length + instanceName.Length);
|
|
|
|
//
|
|
// deviceName is now the full InstancePath (ROOT\LEGACY_service\0000)
|
|
// and instancePath points to the instance ID (0000)
|
|
//
|
|
status = IopOpenRegistryKeyEx(&handle,
|
|
handle1,
|
|
&instanceName,
|
|
KEY_ALL_ACCESS
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
goto exit;
|
|
}
|
|
|
|
legacy = 1;
|
|
status = IopGetRegistryValue(handle,
|
|
REGSTR_VALUE_LEGACY,
|
|
&keyValueInformation
|
|
);
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
if ( keyValueInformation->Type == REG_DWORD &&
|
|
keyValueInformation->DataLength >= sizeof(ULONG)) {
|
|
|
|
legacy = *(PULONG)KEY_VALUE_DATA(keyValueInformation);
|
|
}
|
|
|
|
ExFreePool(keyValueInformation);
|
|
}
|
|
|
|
if (legacy == 0) {
|
|
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// We also want to delete the madeup device node
|
|
//
|
|
deletedPDO = FALSE;
|
|
deviceObject = IopDeviceObjectFromDeviceInstance(&deviceName);
|
|
if (deviceObject) {
|
|
|
|
deviceNode = PP_DO_TO_DN(deviceObject);
|
|
if (deviceNode != NULL && (deviceNode->Flags & DNF_MADEUP)) {
|
|
|
|
//
|
|
// Now mark this one deleted.
|
|
//
|
|
if (!PipDoesDevNodeHaveProblem(deviceNode)) {
|
|
|
|
PipSetDevNodeState(deviceNode, DeviceNodeRemoved, NULL);
|
|
PipSetDevNodeProblem(deviceNode, CM_PROB_DEVICE_NOT_THERE);
|
|
}
|
|
|
|
IopReleaseDeviceResources(deviceNode, FALSE);
|
|
|
|
devNodex = deviceNode;
|
|
while (devNodex) {
|
|
|
|
devNodey = devNodex;
|
|
devNodex = (PDEVICE_NODE)devNodey->OverUsed2.NextResourceDeviceNode;
|
|
devNodey->OverUsed2.NextResourceDeviceNode = NULL;
|
|
devNodey->OverUsed1.LegacyDeviceNode = NULL;
|
|
}
|
|
deviceNode->Flags &= ~DNF_MADEUP;
|
|
|
|
IoDeleteDevice(deviceObject);
|
|
deletedPDO = TRUE;
|
|
}
|
|
ObDereferenceObject(deviceObject);
|
|
}
|
|
|
|
PiWstrToUnicodeString(&unicodeName, REGSTR_KEY_CONTROL);
|
|
status = IopOpenRegistryKeyEx(&handlex,
|
|
handle,
|
|
&unicodeName,
|
|
KEY_ALL_ACCESS
|
|
);
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
ZwDeleteKey(handlex);
|
|
ZwClose(handlex);
|
|
}
|
|
|
|
PiWstrToUnicodeString(&unicodeName, REGSTR_KEY_LOG_CONF);
|
|
status = IopOpenRegistryKeyEx(&handlex,
|
|
handle,
|
|
&unicodeName,
|
|
KEY_ALL_ACCESS
|
|
);
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
ZwDeleteKey(handlex);
|
|
ZwClose(handlex);
|
|
}
|
|
|
|
//
|
|
// We need to call IopCleanupDeviceRegistryValue even we are going to
|
|
// delete it. Because, it also cleans up related value names in other
|
|
// keys.
|
|
//
|
|
if (deletedPDO) {
|
|
|
|
IopCleanupDeviceRegistryValues(&deviceName);
|
|
}
|
|
|
|
ZwDeleteKey(handle);
|
|
ZwDeleteKey(handle1);
|
|
|
|
exit:
|
|
|
|
PiUnlockPnpRegistry();
|
|
|
|
if (handle) {
|
|
|
|
ZwClose(handle);
|
|
}
|
|
if (handle1) {
|
|
|
|
ZwClose(handle1);
|
|
}
|
|
|
|
if (enumHandle) {
|
|
|
|
ZwClose(enumHandle);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
NTSTATUS
|
|
IopQueryAndSaveDeviceNodeCapabilities (
|
|
IN PDEVICE_NODE DeviceNode
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called after start to refresh Capability flags
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - supplies a pointer to a device object whose registry
|
|
values are to be updated.
|
|
|
|
Return Value:
|
|
|
|
status
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
DEVICE_CAPABILITIES capabilities;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(DeviceNode != NULL);
|
|
|
|
//
|
|
// Open the device instance key
|
|
//
|
|
|
|
status = PpIrpQueryCapabilities(DeviceNode->PhysicalDeviceObject, &capabilities);
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
return status;
|
|
}
|
|
|
|
return PpSaveDeviceCapabilities(DeviceNode,&capabilities);
|
|
}
|
|
|
|
NTSTATUS
|
|
PpSaveDeviceCapabilities (
|
|
IN PDEVICE_NODE DeviceNode,
|
|
IN PDEVICE_CAPABILITIES Capabilities
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine updates device capabilities, must be called after a valid device instance key has been created
|
|
Called directly from IopProcessNewDeviceNode, and indirecly via IopQueryAndSaveDeviceNodeCapabilities
|
|
after device is started.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - supplies a pointer to a device object whose registry
|
|
values are to be updated.
|
|
|
|
Return Value:
|
|
|
|
status
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
UNICODE_STRING unicodeName;
|
|
ULONG value;
|
|
HANDLE handle;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(DeviceNode != NULL);
|
|
ASSERT(Capabilities != NULL);
|
|
|
|
//
|
|
// Open the device instance key
|
|
//
|
|
status = IopDeviceObjectToDeviceInstance(DeviceNode->PhysicalDeviceObject, &handle, KEY_ALL_ACCESS);
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
if (DeviceNode->Flags & DNF_HAS_BOOT_CONFIG) {
|
|
|
|
Capabilities->SurpriseRemovalOK = 0;
|
|
}
|
|
//
|
|
// Assert the bit fields are completely contained in a ULONG. This is a
|
|
// public structure, so it shouldn't ever change, but paranoia is a good
|
|
// thing...
|
|
//
|
|
ASSERT((FIELD_OFFSET(DEVICE_CAPABILITIES, Address) -
|
|
FIELD_OFFSET(DEVICE_CAPABILITIES, Version) -
|
|
FIELD_SIZE (DEVICE_CAPABILITIES, Version)) == sizeof(ULONG));
|
|
|
|
DeviceNode->CapabilityFlags =
|
|
*((PULONG) (((PUCHAR) Capabilities) +
|
|
FIELD_OFFSET(DEVICE_CAPABILITIES, Version) +
|
|
FIELD_SIZE(DEVICE_CAPABILITIES, Version)));
|
|
|
|
value = (Capabilities->LockSupported) |
|
|
(Capabilities->EjectSupported << 1) |
|
|
(Capabilities->WarmEjectSupported<< 1) |
|
|
(Capabilities->Removable << 2) |
|
|
(Capabilities->DockDevice << 3) |
|
|
(Capabilities->UniqueID << 4) |
|
|
(Capabilities->SilentInstall << 5) |
|
|
(Capabilities->RawDeviceOK << 6) |
|
|
(Capabilities->SurpriseRemovalOK << 7) |
|
|
(Capabilities->HardwareDisabled << 8) |
|
|
(Capabilities->NonDynamic << 9);
|
|
|
|
PiWstrToUnicodeString(&unicodeName, REGSTR_VALUE_CAPABILITIES);
|
|
ZwSetValueKey(
|
|
handle,
|
|
&unicodeName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_DWORD,
|
|
&value,
|
|
sizeof(value));
|
|
|
|
PiWstrToUnicodeString(&unicodeName, REGSTR_VALUE_UI_NUMBER);
|
|
value = Capabilities->UINumber;
|
|
if(value != (ULONG)-1) {
|
|
|
|
ZwSetValueKey(
|
|
handle,
|
|
&unicodeName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_DWORD,
|
|
&value,
|
|
sizeof(value));
|
|
} else {
|
|
|
|
ZwDeleteValueKey(handle, &unicodeName);
|
|
}
|
|
|
|
ZwClose(handle);
|
|
}
|
|
|
|
ASSERT(NT_SUCCESS(status));
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
IopRestartDeviceNode(
|
|
IN PDEVICE_NODE DeviceNode
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
PpDevNodeLockTree(PPL_TREEOP_BLOCK_READS_FROM_ALLOW);
|
|
|
|
ASSERT(DeviceNode->State == DeviceNodeRemoved ||
|
|
DeviceNode->State == DeviceNodeInitialized );
|
|
|
|
ASSERT(!PipDoesDevNodeHaveProblem(DeviceNode));
|
|
|
|
ASSERT(DeviceNode->Flags & DNF_ENUMERATED);
|
|
|
|
if (!(DeviceNode->Flags & DNF_ENUMERATED)) {
|
|
|
|
PpDevNodeUnlockTree(PPL_TREEOP_BLOCK_READS_FROM_ALLOW);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
DeviceNode->UserFlags &= ~DNUF_NEED_RESTART;
|
|
DeviceNode->Flags &= ~(DNF_DRIVER_BLOCKED | DNF_HARDWARE_VERIFICATION);
|
|
|
|
#if DBG_SCOPE
|
|
DeviceNode->FailureStatus = 0;
|
|
if (DeviceNode->PreviousResourceList) {
|
|
ExFreePool(DeviceNode->PreviousResourceList);
|
|
DeviceNode->PreviousResourceList = NULL;
|
|
}
|
|
if (DeviceNode->PreviousResourceRequirements) {
|
|
ExFreePool(DeviceNode->PreviousResourceRequirements);
|
|
DeviceNode->PreviousResourceRequirements = NULL;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Prepare to set the device state back to DeviceNodeUninitialized. To
|
|
// do this we free any existing devnode strings so we can recreate them
|
|
// during enumeration.
|
|
//
|
|
// ADRIAO N.B. 8/19/2000 -
|
|
// We don't restore the state to DeviceNodeInitialized to maintain Win2K
|
|
// behavior. We have no idea if anyone actually depends on this. In theory
|
|
// this would let a bus driver get away with changing a child's IDs after a
|
|
// remove.
|
|
//
|
|
|
|
if (DeviceNode->State != DeviceNodeUninitialized) {
|
|
|
|
DeviceNode->Flags &= ~(DNF_NO_RESOURCE_REQUIRED |
|
|
DNF_RESOURCE_REQUIREMENTS_CHANGED);
|
|
|
|
if (DeviceNode->ServiceName.Length != 0) {
|
|
ExFreePool(DeviceNode->ServiceName.Buffer);
|
|
PiWstrToUnicodeString(&DeviceNode->ServiceName, NULL);
|
|
}
|
|
|
|
if (DeviceNode->ResourceRequirements != NULL) {
|
|
ExFreePool(DeviceNode->ResourceRequirements);
|
|
DeviceNode->ResourceRequirements = NULL;
|
|
DeviceNode->Flags &= ~DNF_RESOURCE_REQUIREMENTS_NEED_FILTERED;
|
|
}
|
|
}
|
|
|
|
ASSERT(DeviceNode->ServiceName.Length == 0 &&
|
|
DeviceNode->ServiceName.MaximumLength == 0 &&
|
|
DeviceNode->ServiceName.Buffer == NULL);
|
|
|
|
ASSERT(!(DeviceNode->Flags &
|
|
~(DNF_MADEUP | DNF_ENUMERATED | DNF_HAS_BOOT_CONFIG | DNF_IDS_QUERIED |
|
|
DNF_BOOT_CONFIG_RESERVED | DNF_NO_RESOURCE_REQUIRED)));
|
|
|
|
PipSetDevNodeState(DeviceNode, DeviceNodeUninitialized, NULL);
|
|
|
|
PpDevNodeUnlockTree(PPL_TREEOP_BLOCK_READS_FROM_ALLOW);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
BOOLEAN
|
|
IopDeleteKeyRecursiveCallback(
|
|
IN HANDLE KeyHandle,
|
|
IN PUNICODE_STRING KeyName,
|
|
IN OUT PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is a callback routine to PipApplyFunctionToSubKeys, that gets called
|
|
through IopDeleteKeyRecursive. This routine prepares a given key for
|
|
deletion by deleting all of its subkeys. This is done, using
|
|
PipApplyFunctionToSubKeys, with instructions to delete all enumerated
|
|
subkeys, and calling this routine as a callback routine, if necessary, until
|
|
no subkeys remain. KeyHandle can then be successfully deleted by the
|
|
caller.
|
|
|
|
Arguments:
|
|
|
|
KeyHandle - Handle to a subkey that has been enumerated by
|
|
PipApplyFunctionToSubKeys.
|
|
|
|
KeyName - Name of the subkey whose handle is specified by KeyHandle.
|
|
|
|
Context - Supplies a pointer to user-defined data that will be passed
|
|
in to the callback routine at each subkey invocation.
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN that returns whether or not the given key can be safely deleted.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
PAGED_CODE();
|
|
|
|
UNREFERENCED_PARAMETER(KeyName);
|
|
|
|
//
|
|
// delete any subkeys, recursively if necessary
|
|
//
|
|
status = PipApplyFunctionToSubKeys(
|
|
KeyHandle,
|
|
NULL,
|
|
KEY_ALL_ACCESS,
|
|
FUNCTIONSUBKEY_FLAG_IGNORE_NON_CRITICAL_ERRORS |
|
|
FUNCTIONSUBKEY_FLAG_DELETE_SUBKEYS,
|
|
IopDeleteKeyRecursiveCallback,
|
|
Context
|
|
);
|
|
|
|
*((NTSTATUS *)Context) = status;
|
|
|
|
return (BOOLEAN)NT_SUCCESS(status);
|
|
}
|
|
|
|
NTSTATUS
|
|
IopDeleteKeyRecursive(
|
|
IN HANDLE ParentKey OPTIONAL,
|
|
IN PWCHAR KeyName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Recursively deletes all subkeys of KeyName, then deletes KeyName.
|
|
|
|
Arguments:
|
|
|
|
ParentKey - Handle to the parent key of KeyName. If NULL then KeyName is
|
|
expected to start with \Registry.
|
|
|
|
KeyName - Name of subkey to delete, as a NULL terminated UNICODE string.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS if no errors, otherwise the appropriate error.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
BOOLEAN result;
|
|
HANDLE hKey;
|
|
UNICODE_STRING unicodeKeyName;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Attempt to open the key name we were given
|
|
//
|
|
RtlInitUnicodeString(&unicodeKeyName, KeyName);
|
|
status = IopOpenRegistryKeyEx(&hKey,
|
|
ParentKey,
|
|
&unicodeKeyName,
|
|
KEY_ALL_ACCESS
|
|
);
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Recusively delete all subkeys
|
|
//
|
|
result = IopDeleteKeyRecursiveCallback(hKey,
|
|
&unicodeKeyName,
|
|
(PVOID)&status
|
|
);
|
|
if (result) {
|
|
|
|
//
|
|
// It is safe to delete this key
|
|
//
|
|
status = ZwDeleteKey(hKey);
|
|
}
|
|
ZwClose(hKey);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
BOOLEAN
|
|
PiRegSzToString(
|
|
IN PWCHAR RegSzData,
|
|
IN ULONG RegSzLength,
|
|
OUT PULONG StringLength OPTIONAL,
|
|
OUT PWSTR *CopiedString OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine takes as input a REG_SZ data buffer (as returned in the DataOffset area
|
|
of the buffer in a KEY_VALUE_FULL_INFORMATION structure), as well as the length
|
|
of the buffer, in bytes (as specified by the DataLength field in the above mentioned
|
|
struct). It optionally returns the length of the contained string (in bytes), not
|
|
including the terminating NULL, as well as an optional copy of the string itself
|
|
(properly NULL-terminated).
|
|
|
|
It is the responsibility of the caller to free the (PagedPool) buffer allocated
|
|
for the string copy.
|
|
|
|
Arguments:
|
|
|
|
RegSzData - Supplies a pointer to the REG_SZ data buffer.
|
|
|
|
RegSzLength - Supplies the length of the RegSzData buffer, in bytes.
|
|
|
|
StringLength - Optionally supplies a pointer to a variable that will receive
|
|
the length, in bytes, of the string (excluding terminating NULL).
|
|
|
|
CopiedString - Optionally supplies a pointer to a wide character pointer
|
|
that will recieve a (properly NULL-terminated) copy of the specified
|
|
string. If this paramater is NULL, no copy will be made.
|
|
|
|
Return Value:
|
|
|
|
If success, returns TRUE
|
|
|
|
If failure (not able to allocate memory for string copy), returns FALSE
|
|
|
|
--*/
|
|
|
|
{
|
|
PWCHAR curPos, endOfRegSzData;
|
|
ULONG actualStringLength;
|
|
|
|
//
|
|
// Since we're converting a byte count to a wide-character count (and the
|
|
// compiler is converting it back when adding it to a PWCHAR), we are
|
|
// ensuring that endOfRegSzData is not on an odd-byte boundary, even if
|
|
// the RegSzLength passed in was odd. This takes care of the case where
|
|
// the REG_SZ buffer retrieved from the registry is bogus (e.g., you have
|
|
// a 5-byte buffer, the 1st unicode character of which is a UNICODE_NULL).
|
|
//
|
|
endOfRegSzData = (curPos = RegSzData) + CB_TO_CWC(RegSzLength);
|
|
|
|
while ((curPos < endOfRegSzData) && *curPos) {
|
|
|
|
curPos++;
|
|
}
|
|
|
|
actualStringLength = (ULONG)((PUCHAR)curPos - (PUCHAR)RegSzData);
|
|
|
|
if (ARGUMENT_PRESENT(StringLength)) {
|
|
|
|
*StringLength = (ULONG)((PUCHAR)curPos - (PUCHAR)RegSzData);
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT(CopiedString)) {
|
|
|
|
//
|
|
// Allocate memory for the string (+ terminating NULL)
|
|
//
|
|
*CopiedString = (PWSTR)ExAllocatePool(PagedPool,
|
|
actualStringLength +
|
|
sizeof(UNICODE_NULL));
|
|
if (*CopiedString == NULL) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Copy the string and NULL-terminate it.
|
|
//
|
|
if (actualStringLength) {
|
|
|
|
RtlCopyMemory(*CopiedString, RegSzData, actualStringLength);
|
|
}
|
|
|
|
*(PWCHAR)((PUCHAR)(*CopiedString) + actualStringLength) = UNICODE_NULL;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
ULONG
|
|
IopDebugPrint (
|
|
IN ULONG Level,
|
|
IN PCHAR Format,
|
|
...
|
|
)
|
|
{
|
|
va_list ap;
|
|
|
|
va_start(ap, Format);
|
|
|
|
vDbgPrintExWithPrefix("", DPFLTR_NTOSPNP_ID, Level, Format, ap);
|
|
|
|
va_end(ap);
|
|
|
|
return Level;
|
|
}
|
|
|
|
VOID
|
|
PpSystemHiveLimitCallback(
|
|
PSYSTEM_HIVE_LIMITS HiveLimits,
|
|
ULONG Level
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
if (Level >= HiveLimits->High) {
|
|
|
|
PpSystemHiveTooLarge = TRUE;
|
|
} else {
|
|
|
|
ASSERT(Level <= HiveLimits->Low);
|
|
|
|
PpSystemHiveTooLarge = FALSE;
|
|
|
|
PpResetProblemDevices(IopRootDeviceNode, CM_PROB_REGISTRY_TOO_LARGE);
|
|
|
|
PipRequestDeviceAction(IopRootDeviceNode->PhysicalDeviceObject,
|
|
RestartEnumeration,
|
|
FALSE,
|
|
0,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
}
|
|
}
|
|
|
|
VOID
|
|
PpLogEvent(
|
|
IN PUNICODE_STRING InsertionString1,
|
|
IN PUNICODE_STRING InsertionString2,
|
|
IN NTSTATUS Status,
|
|
IN PVOID DumpData,
|
|
IN ULONG DumpDataSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine logs the driver block event.
|
|
|
|
Arguments:
|
|
|
|
InsertionString1 - First insertion string for event log entry.
|
|
|
|
InsertionString2 - Second insertion string for event log entry.
|
|
|
|
Status - Status code to be logged.
|
|
|
|
DumpData - Data to be logged with the event.
|
|
|
|
DumpDataSize - Size of the data to be logged in bytes.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
SIZE_T size, stringLength1, stringLength2, stringOffset;
|
|
PIO_ERROR_LOG_PACKET errorLogEntry;
|
|
PUCHAR stringPtr;
|
|
|
|
PAGED_CODE();
|
|
|
|
stringLength1 = stringLength2 = 0;
|
|
|
|
if (InsertionString1) {
|
|
|
|
stringLength1 = InsertionString1->Length + sizeof(UNICODE_NULL);
|
|
}
|
|
|
|
if (InsertionString2) {
|
|
|
|
stringLength2 = InsertionString2->Length + sizeof(UNICODE_NULL);
|
|
}
|
|
|
|
//
|
|
// Calculate the size of the the error packet
|
|
//
|
|
size = FIELD_OFFSET(IO_ERROR_LOG_PACKET, DumpData) + DumpDataSize;
|
|
|
|
//
|
|
// Determine the string offset and size, adjusting for alignment.
|
|
//
|
|
stringOffset = ALIGN_UP_ULONG(size, 2);
|
|
|
|
size = stringOffset + stringLength1 + stringLength2;
|
|
|
|
if (size <= ERROR_LOG_MAXIMUM_SIZE) {
|
|
|
|
//
|
|
// Allocate an error log packet. Note that Io takes care of initializing
|
|
// the header and zeroing all fields (such as NumberOfStrings).
|
|
//
|
|
errorLogEntry = IoAllocateGenericErrorLogEntry((UCHAR)size);
|
|
|
|
if (errorLogEntry) {
|
|
|
|
errorLogEntry->ErrorCode = Status;
|
|
errorLogEntry->FinalStatus = Status;
|
|
errorLogEntry->DumpDataSize = (USHORT)DumpDataSize;
|
|
errorLogEntry->StringOffset = (USHORT)stringOffset;
|
|
stringPtr = ((PUCHAR)errorLogEntry) + stringOffset;
|
|
|
|
if (DumpDataSize) {
|
|
|
|
RtlCopyMemory(&errorLogEntry->DumpData[0], DumpData, DumpDataSize);
|
|
}
|
|
|
|
if (InsertionString1) {
|
|
|
|
errorLogEntry->NumberOfStrings = 1;
|
|
RtlCopyMemory(stringPtr, InsertionString1->Buffer, InsertionString1->Length);
|
|
stringPtr += InsertionString1->Length;
|
|
*(PWCHAR)stringPtr = UNICODE_NULL;
|
|
stringPtr += sizeof(UNICODE_NULL);
|
|
}
|
|
|
|
if (InsertionString2) {
|
|
|
|
errorLogEntry->NumberOfStrings += 1;
|
|
RtlCopyMemory(stringPtr, InsertionString2->Buffer, InsertionString2->Length);
|
|
stringPtr += InsertionString2->Length;
|
|
*(PWCHAR)stringPtr = UNICODE_NULL;
|
|
}
|
|
|
|
IoWriteErrorLogEntry(errorLogEntry);
|
|
}
|
|
} else {
|
|
|
|
ASSERT(size <= ERROR_LOG_MAXIMUM_SIZE);
|
|
}
|
|
}
|
|
|
|
ULONG
|
|
PiFixupID(
|
|
IN PWCHAR ID,
|
|
IN ULONG MaxIDLength,
|
|
IN BOOLEAN Multi,
|
|
IN ULONG AllowedSeparators,
|
|
IN PUNICODE_STRING LogString OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine parses the device instance string and replaces any invalid
|
|
characters (not allowed in a "device instance") with an underscore
|
|
character.
|
|
|
|
Invalid characters are:
|
|
c <= 0x20 (' ')
|
|
c > 0x7F
|
|
c == 0x2C (',')
|
|
|
|
Arguments:
|
|
|
|
ID - ID to be fixed up.
|
|
|
|
MaxIDLength - Maximum allowed size of ID.
|
|
|
|
Multi - Specifies if the ID is MULTI_SZ or not.
|
|
|
|
AllowedSeparators - Number of separators allowed in the ID.
|
|
|
|
Return Value:
|
|
|
|
ID length in number of characters.
|
|
|
|
--*/
|
|
|
|
{
|
|
PWCHAR p, pMax, lastNull;
|
|
ULONG separators;
|
|
UNICODE_STRING reason;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// BUGBUG - do we need to uppercase these!?
|
|
//
|
|
separators = 0;
|
|
lastNull = NULL;
|
|
for(p = ID, pMax = p + MaxIDLength; p < pMax; p++) {
|
|
|
|
if(*p == UNICODE_NULL) {
|
|
|
|
if(Multi == FALSE || (lastNull && p == lastNull + 1)) {
|
|
|
|
break;
|
|
}
|
|
pMax += MaxIDLength;
|
|
lastNull = p;
|
|
continue;
|
|
}
|
|
if (*p == L' ') {
|
|
|
|
*p = L'_';
|
|
} else if ((*p < L' ') || (*p > (WCHAR)0x7F) || (*p == L',')) {
|
|
|
|
IopDbgPrint((IOP_ENUMERATION_ERROR_LEVEL,
|
|
"PiFixupID: ID at %p has invalid character %02X\n",
|
|
ID,
|
|
*p));
|
|
|
|
if(LogString) {
|
|
|
|
PiWstrToUnicodeString(&reason, L"invalid character");
|
|
PpLogEvent(LogString, &reason, STATUS_PNP_INVALID_ID, p, sizeof(WCHAR));
|
|
}
|
|
|
|
return 0;
|
|
} else if ((*p == OBJ_NAME_PATH_SEPARATOR && ++separators > AllowedSeparators)) {
|
|
|
|
IopDbgPrint((IOP_ENUMERATION_ERROR_LEVEL,
|
|
"PiFixupID: ID at %p has too many (%d) separators\n",
|
|
ID,
|
|
separators));
|
|
if(LogString) {
|
|
|
|
PiWstrToUnicodeString(&reason, L"too many separators");
|
|
PpLogEvent(LogString,
|
|
&reason,
|
|
STATUS_PNP_INVALID_ID,
|
|
&separators,
|
|
sizeof(ULONG)
|
|
);
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
if( p >= pMax ||
|
|
(AllowedSeparators != (ULONG)-1 &&
|
|
separators != AllowedSeparators)) {
|
|
|
|
IopDbgPrint((IOP_ENUMERATION_ERROR_LEVEL,
|
|
"PiFixupID: ID at %p not terminated, or too long or has invalid number (%d) of separators\n",
|
|
ID,
|
|
separators));
|
|
if(LogString) {
|
|
|
|
PiWstrToUnicodeString(&reason,
|
|
L"not terminated, too long or invalid number of separators"
|
|
);
|
|
PpLogEvent(LogString, &reason, STATUS_PNP_INVALID_ID, NULL, 0);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
return (ULONG)(ULONG_PTR)(p - ID) + 1;
|
|
}
|
|
|
|
NTSTATUS
|
|
PpQueryID(
|
|
IN PDEVICE_NODE DeviceNode,
|
|
IN BUS_QUERY_ID_TYPE IDType,
|
|
OUT PWCHAR *ID,
|
|
OUT PULONG IDLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine queries the specified ID and fixes it up. If this
|
|
routine fails, ID will be set to NULL.
|
|
|
|
Arguments:
|
|
|
|
DeviceNode - The devnode whose IDs need to be queried.
|
|
|
|
IDType - Type of ID to be queried.
|
|
|
|
ID - Receives the ID returned by the driver if any. The caller
|
|
is expected to free the storage for ID on success.
|
|
|
|
IDLength - Receives the length of the ID (including terminating NULL) in
|
|
bytes.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
UNICODE_STRING reason;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(IDType == BusQueryDeviceID || IDType == BusQueryInstanceID ||
|
|
IDType == BusQueryHardwareIDs || IDType == BusQueryCompatibleIDs);
|
|
|
|
*IDLength = 0;
|
|
status = PpIrpQueryID(DeviceNode->PhysicalDeviceObject, IDType, ID);
|
|
if(NT_SUCCESS(status)) {
|
|
|
|
switch(IDType) {
|
|
|
|
case BusQueryDeviceID:
|
|
|
|
*IDLength = PiFixupID(*ID,
|
|
MAX_DEVICE_ID_LEN,
|
|
FALSE,
|
|
1,
|
|
&DeviceNode->Parent->ServiceName
|
|
);
|
|
break;
|
|
|
|
case BusQueryInstanceID:
|
|
|
|
*IDLength = PiFixupID(*ID,
|
|
MAX_DEVICE_ID_LEN,
|
|
FALSE,
|
|
0,
|
|
&DeviceNode->Parent->ServiceName
|
|
);
|
|
break;
|
|
|
|
case BusQueryHardwareIDs:
|
|
case BusQueryCompatibleIDs:
|
|
|
|
*IDLength = PiFixupID(*ID,
|
|
MAX_DEVICE_ID_LEN,
|
|
TRUE,
|
|
(ULONG)-1,
|
|
&DeviceNode->Parent->ServiceName
|
|
);
|
|
break;
|
|
|
|
default:
|
|
|
|
*IDLength = 0;
|
|
break;
|
|
}
|
|
(*IDLength) *= sizeof(WCHAR);
|
|
if(*IDLength == 0) {
|
|
|
|
status = STATUS_PNP_INVALID_ID;
|
|
}
|
|
}
|
|
|
|
if(!NT_SUCCESS(status)) {
|
|
|
|
if (status == STATUS_PNP_INVALID_ID || IDType == BusQueryDeviceID) {
|
|
|
|
PipSetDevNodeProblem(DeviceNode, CM_PROB_INVALID_DATA);
|
|
if ((DeviceNode->Parent->Flags & DNF_CHILD_WITH_INVALID_ID) == 0) {
|
|
|
|
DeviceNode->Parent->Flags |= DNF_CHILD_WITH_INVALID_ID;
|
|
PpSetInvalidIDEvent(&DeviceNode->Parent->InstancePath);
|
|
}
|
|
}
|
|
if (status == STATUS_PNP_INVALID_ID) {
|
|
|
|
IopDbgPrint((IOP_ENUMERATION_ERROR_LEVEL,
|
|
"PpQueryID: Bogus ID returned by %wZ\n",
|
|
&DeviceNode->Parent->ServiceName));
|
|
ASSERT(status != STATUS_PNP_INVALID_ID);
|
|
|
|
} else if ( IDType == BusQueryDeviceID &&
|
|
status != STATUS_INSUFFICIENT_RESOURCES) {
|
|
//
|
|
// DeviceID is not optional.
|
|
//
|
|
PiWstrToUnicodeString(&reason, L"failed IRP_MN_QUERY_ID-BusQueryDeviceID");
|
|
PpLogEvent(
|
|
&DeviceNode->Parent->ServiceName,
|
|
&reason,
|
|
status,
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
IopDbgPrint((IOP_ENUMERATION_ERROR_LEVEL,
|
|
"PpIrpQueryID: Failed by %wZ, status = %x\n",
|
|
&DeviceNode->Parent->ServiceName, status));
|
|
ASSERT(IDType != BusQueryDeviceID);
|
|
}
|
|
|
|
if(*ID) {
|
|
|
|
ExFreePool(*ID);
|
|
*ID = NULL;
|
|
*IDLength = 0;
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
PpQueryDeviceID(
|
|
IN PDEVICE_NODE DeviceNode,
|
|
OUT PWCHAR *BusID,
|
|
OUT PWCHAR *DeviceID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine queries the Device ID and fixes it up. It also parses the
|
|
DeviceID and returns the pointers to BusID and DeviceID parts. If this
|
|
routine fails, BusID and DeviceID will be set to NULL.
|
|
|
|
Arguments:
|
|
|
|
DeviceNode - The devnode whose DeviceID needs to be queried.
|
|
|
|
BusID - Recieves the pointer to the bus part of DeviceID.
|
|
|
|
DeviceID - Recieves the pointer to the device part of DeviceID.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
PWCHAR id, separator;
|
|
ULONG idLength;
|
|
|
|
PAGED_CODE();
|
|
|
|
*BusID = NULL;
|
|
*DeviceID= NULL;
|
|
|
|
status = PpQueryID(DeviceNode, BusQueryDeviceID, &id, &idLength);
|
|
if(NT_SUCCESS(status)) {
|
|
|
|
ASSERT(id && idLength);
|
|
|
|
*BusID = id;
|
|
separator = wcschr(id, OBJ_NAME_PATH_SEPARATOR);
|
|
|
|
ASSERT(separator);
|
|
|
|
*separator = UNICODE_NULL;
|
|
*DeviceID = separator + 1;
|
|
|
|
} else {
|
|
|
|
ASSERT(id == NULL && idLength == 0);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
PpQueryBusInformation(
|
|
IN PDEVICE_NODE DeviceNode
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine queries the bus information.
|
|
|
|
Arguments:
|
|
|
|
DeviceNode - The devnode whose BusInormation needs to be queried.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
PPNP_BUS_INFORMATION busInfo;
|
|
|
|
PAGED_CODE();
|
|
|
|
status = PpIrpQueryBusInformation(
|
|
DeviceNode->PhysicalDeviceObject,
|
|
&busInfo
|
|
);
|
|
if(NT_SUCCESS(status)) {
|
|
|
|
ASSERT(busInfo);
|
|
|
|
DeviceNode->ChildBusTypeIndex = PpBusTypeGuidGetIndex(
|
|
&busInfo->BusTypeGuid
|
|
);
|
|
DeviceNode->ChildInterfaceType = busInfo->LegacyBusType;
|
|
DeviceNode->ChildBusNumber = busInfo->BusNumber;
|
|
|
|
ExFreePool(busInfo);
|
|
|
|
} else {
|
|
|
|
ASSERT(busInfo == NULL);
|
|
|
|
DeviceNode->ChildBusTypeIndex = 0xffff;
|
|
DeviceNode->ChildInterfaceType = InterfaceTypeUndefined;
|
|
DeviceNode->ChildBusNumber = 0xfffffff0;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
PpBusTypeGuidInitialize(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine opens the specified subkey.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS.
|
|
|
|
--*/
|
|
|
|
{
|
|
PAGED_CODE();
|
|
|
|
PpBusTypeGuidCountMax = 16;
|
|
PpBusTypeGuidArray = ExAllocatePool(PagedPool,
|
|
sizeof(GUID) * PpBusTypeGuidCountMax);
|
|
if (PpBusTypeGuidArray == NULL) {
|
|
|
|
PpBusTypeGuidCountMax = 0;
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
PpBusTypeGuidCount = 0;
|
|
|
|
KeInitializeGuardedMutex(&PpBusTypeGuidLock);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
USHORT
|
|
PpBusTypeGuidGetIndex(
|
|
IN LPGUID BusTypeGuid
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine looks up the BusTypeGuid and returns its index into the table.
|
|
|
|
Arguments:
|
|
|
|
BusTypeGuid - GUID to lookup.
|
|
|
|
Return Value:
|
|
|
|
Index into the table iff successful, else 0xFFFF.
|
|
|
|
--*/
|
|
|
|
{
|
|
LPGUID p;
|
|
ULONG i;
|
|
|
|
PAGED_CODE();
|
|
|
|
KeAcquireGuardedMutex(&PpBusTypeGuidLock);
|
|
//
|
|
// First look it up.
|
|
//
|
|
for (i = 0; i < PpBusTypeGuidCount; i++) {
|
|
|
|
if (IopCompareGuid(BusTypeGuid, &PpBusTypeGuidArray[i])) {
|
|
|
|
break;
|
|
}
|
|
}
|
|
//
|
|
// If the GUID is not in the table, add it.
|
|
//
|
|
if (i == PpBusTypeGuidCount) {
|
|
//
|
|
// Grow the table if needed.
|
|
//
|
|
if (i == PpBusTypeGuidCountMax) {
|
|
//
|
|
// We grow the table one entry at a time. This should not be a
|
|
// problem since this should not happen often.
|
|
//
|
|
p = ExAllocatePool(PagedPool, (i + 1) * sizeof(GUID));
|
|
if (p) {
|
|
//
|
|
// Copy the old table.
|
|
//
|
|
RtlCopyMemory(p,
|
|
PpBusTypeGuidArray,
|
|
PpBusTypeGuidCount * sizeof(GUID)
|
|
);
|
|
//
|
|
// Update global data.
|
|
//
|
|
PpBusTypeGuidCountMax++;
|
|
if (PpBusTypeGuidArray) {
|
|
|
|
ExFreePool(PpBusTypeGuidArray);
|
|
}
|
|
PpBusTypeGuidArray = p;
|
|
|
|
} else {
|
|
//
|
|
// Return invalid index on failure.
|
|
//
|
|
i = (ULONG)-1;
|
|
}
|
|
}
|
|
//
|
|
// Copy the new entry on success.
|
|
//
|
|
if (i != (ULONG)-1) {
|
|
//
|
|
// Copy the new entry.
|
|
//
|
|
RtlCopyMemory(&PpBusTypeGuidArray[PpBusTypeGuidCount],
|
|
BusTypeGuid,
|
|
sizeof(GUID)
|
|
);
|
|
//
|
|
// Update global data.
|
|
//
|
|
PpBusTypeGuidCount++;
|
|
}
|
|
}
|
|
|
|
KeReleaseGuardedMutex(&PpBusTypeGuidLock);
|
|
|
|
return (USHORT)i;
|
|
}
|
|
|
|
NTSTATUS
|
|
PpBusTypeGuidGet(
|
|
IN USHORT Index,
|
|
IN OUT LPGUID BusTypeGuid
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine return the BusTypeGuid in the table at the specified index.
|
|
|
|
Arguments:
|
|
|
|
Index - BusTypeGuid index.
|
|
|
|
BusTypeGuid - Recieves the GUID.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
KeAcquireGuardedMutex(&PpBusTypeGuidLock);
|
|
|
|
if (Index < PpBusTypeGuidCount) {
|
|
|
|
RtlCopyMemory(BusTypeGuid, &PpBusTypeGuidArray[Index], sizeof(GUID));
|
|
status = STATUS_SUCCESS;
|
|
} else {
|
|
|
|
status = STATUS_OBJECT_NAME_NOT_FOUND;
|
|
}
|
|
|
|
KeReleaseGuardedMutex(&PpBusTypeGuidLock);
|
|
|
|
return status;
|
|
}
|