5863 lines
185 KiB
C
5863 lines
185 KiB
C
/*++
|
|
Copyright (c) 1995 Microsoft Corporation
|
|
|
|
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
|
|
--*/
|
|
|
|
#include "iop.h"
|
|
#pragma hdrstop
|
|
|
|
#ifdef POOL_TAGGING
|
|
#undef ExAllocatePool
|
|
#define ExAllocatePool(a,b) ExAllocatePoolWithTag(a,b,'uspP')
|
|
#endif
|
|
|
|
// Prototype of internal functions
|
|
|
|
VOID IopDisableDevice(IN PDEVICE_NODE DeviceNode, IN HANDLE Handle);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, IopCreateMadeupNode)
|
|
#pragma alloc_text(PAGE, IopRemoveStringFromValueKey)
|
|
#pragma alloc_text(PAGE, IopAppendStringToValueKey)
|
|
#pragma alloc_text(PAGE, IopConcatenateUnicodeStrings)
|
|
#pragma alloc_text(PAGE, IopPrepareDriverLoading)
|
|
#pragma alloc_text(PAGE, IopServiceInstanceToDeviceInstance)
|
|
#pragma alloc_text(PAGE, IopCreateRegistryKeyEx)
|
|
#pragma alloc_text(PAGE, IopOpenRegistryKeyEx)
|
|
#pragma alloc_text(PAGE, IopOpenServiceEnumKeys)
|
|
#pragma alloc_text(PAGE, IopOpenCurrentHwProfileDeviceInstanceKey)
|
|
#pragma alloc_text(PAGE, IopGetDeviceInstanceCsConfigFlags)
|
|
#pragma alloc_text(PAGE, IopGetServiceInstanceCsConfigFlags)
|
|
#pragma alloc_text(PAGE, IopSetServiceInstanceCsConfigFlags)
|
|
#pragma alloc_text(PAGE, IopApplyFunctionToSubKeys)
|
|
#pragma alloc_text(PAGE, IopRegMultiSzToUnicodeStrings)
|
|
#pragma alloc_text(PAGE, IopApplyFunctionToServiceInstances)
|
|
#pragma alloc_text(PAGE, IopIsDuplicatedDevices)
|
|
#pragma alloc_text(PAGE, IopMarkDuplicateDevice)
|
|
#pragma alloc_text(PAGE, IopFreeUnicodeStringList)
|
|
#pragma alloc_text(PAGE, IopDriverLoadingFailed)
|
|
#pragma alloc_text(PAGE, IopIsAnyDeviceInstanceEnabled)
|
|
#pragma alloc_text(PAGE, IopIsDeviceInstanceEnabled)
|
|
#pragma alloc_text(PAGE, IopDetermineResourceListSize)
|
|
#pragma alloc_text(PAGE, IopReferenceDriverObjectByName)
|
|
#pragma alloc_text(PAGE, IopDeviceObjectFromDeviceInstance)
|
|
#pragma alloc_text(PAGE, IopDeviceObjectToDeviceInstance)
|
|
#pragma alloc_text(PAGE, IopCleanupDeviceRegistryValues)
|
|
#pragma alloc_text(PAGE, IopGetDeviceResourcesFromRegistry)
|
|
#pragma alloc_text(PAGE, IopCmResourcesToIoResources)
|
|
#pragma alloc_text(PAGE, IopReadDeviceConfiguration)
|
|
#pragma alloc_text(PAGE, IopGetGroupOrderIndex)
|
|
#pragma alloc_text(PAGE, IopDeleteLegacyKey)
|
|
#pragma alloc_text(PAGE, IopIsLegacyDriver)
|
|
#pragma alloc_text(PAGE, IopFilterResourceRequirementsList)
|
|
#pragma alloc_text(PAGE, IopMergeFilteredResourceRequirementsList)
|
|
#pragma alloc_text(PAGE, IopDisableDevice)
|
|
#pragma alloc_text(PAGE, IopMergeCmResourceLists)
|
|
#pragma alloc_text(PAGE, IopDeviceCapabilitiesToRegistry)
|
|
#pragma alloc_text(PAGE, IopDeviceNodeCapabilitiesToRegistry)
|
|
#pragma alloc_text(PAGE, IopRestartDeviceNode)
|
|
#pragma alloc_text(PAGE, IopDeleteKeyRecursive)
|
|
#endif
|
|
|
|
NTSTATUS
|
|
IopCreateMadeupNode(
|
|
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\*Madeup<Name>
|
|
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 exclusively.
|
|
|
|
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, hTreeHandle;
|
|
ULONG instance;
|
|
UCHAR unicodeBuffer[20];
|
|
ULONG tmpValue, disposition = 0;
|
|
NTSTATUS status;
|
|
PWSTR p;
|
|
BOOLEAN releaseResource = FALSE;
|
|
BOOLEAN successful;
|
|
|
|
if (!ResourceOwned) {
|
|
KeEnterCriticalRegion();
|
|
ExAcquireResourceShared(&PpRegistryDeviceResource, TRUE);
|
|
releaseResource = TRUE;
|
|
}
|
|
|
|
|
|
// Open LocalMachine\System\CurrentControlSet\Enum\Root
|
|
|
|
|
|
status = IopOpenRegistryKeyEx( &enumRootHandle,
|
|
NULL,
|
|
&CmRegistryMachineSystemCurrentControlSetEnumRootName,
|
|
KEY_ALL_ACCESS
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto local_exit0;
|
|
}
|
|
|
|
|
|
// Open, and create if not already exist, System\Enum\Root\LEGACY_<ServiceName>
|
|
// First, try to find the ServiceName by extracting it from user supplied
|
|
// ServiceKeyName.
|
|
|
|
|
|
PiWstrToUnicodeString(&tmpKeyName, REGSTR_KEY_MADEUP);
|
|
successful = IopConcatenateUnicodeStrings( &unicodeKeyName, &tmpKeyName, ServiceKeyName);
|
|
if (!successful) {
|
|
ZwClose(enumRootHandle);
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto local_exit0;
|
|
}
|
|
|
|
RtlUpcaseUnicodeString(&unicodeKeyName, &unicodeKeyName, FALSE);
|
|
if (!IopFixupDeviceId(unicodeKeyName.Buffer)) {
|
|
status = STATUS_INVALID_PARAMETER;
|
|
RtlFreeUnicodeString(&unicodeKeyName);
|
|
goto local_exit0;
|
|
}
|
|
|
|
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);
|
|
RtlInitUnicodeString(&tmpKeyName, L"\\");
|
|
IopConcatenateUnicodeStrings(&unicodeString, &tmpKeyName, &unicodeKeyName);
|
|
RtlFreeUnicodeString(&unicodeKeyName);
|
|
IopConcatenateUnicodeStrings(&tmpKeyName, &rootKeyName, &unicodeString);
|
|
RtlFreeUnicodeString(&unicodeString);
|
|
IopConcatenateUnicodeStrings(KeyName, &tmpKeyName, &unicodeInstanceName);
|
|
|
|
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) {
|
|
RtlMoveMemory(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.
|
|
|
|
// ExFreePool(p);
|
|
}
|
|
|
|
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,
|
|
REGSTR_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 = IopOpenServiceEnumKeys(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.
|
|
|
|
|
|
ExReleaseResource(&PpRegistryDeviceResource);
|
|
KeLeaveCriticalRegion();
|
|
releaseResource = FALSE;
|
|
|
|
status = PpDeviceRegistration( KeyName, TRUE, NULL );
|
|
|
|
if (ResourceOwned) {
|
|
KeEnterCriticalRegion();
|
|
ExAcquireResourceShared(&PpRegistryDeviceResource, TRUE);
|
|
}
|
|
RtlFreeUnicodeString(&tmpKeyName);
|
|
if (!NT_SUCCESS( status )) {
|
|
|
|
|
|
// There is no registry key for the ServiceKeyName information.
|
|
|
|
|
|
ZwClose(*ReturnedHandle);
|
|
RtlFreeUnicodeString(KeyName);
|
|
}
|
|
local_exit0:
|
|
if (releaseResource) {
|
|
ExReleaseResource(&PpRegistryDeviceResource);
|
|
KeLeaveCriticalRegion();
|
|
}
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
IopRemoveStringFromValueKey (
|
|
IN HANDLE Handle,
|
|
IN PWSTR ValueName,
|
|
IN PUNICODE_STRING String
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine remove a string from a value entry specified by ValueName
|
|
under an already opened registry handle. Note, this routine will not
|
|
delete the ValueName entry even it becomes empty after the removal.
|
|
|
|
Parameters:
|
|
|
|
Handle - Supplies the handle to a registry key whose value entry will
|
|
be modified.
|
|
|
|
ValueName - Supplies a unicode string to specify the value entry.
|
|
|
|
String - Supplies a unicode string to remove from value entry.
|
|
|
|
Return Value:
|
|
|
|
Status code that indicates whether or not the function was successful.
|
|
|
|
--*/
|
|
|
|
{
|
|
PKEY_VALUE_FULL_INFORMATION keyValueInformation;
|
|
UNICODE_STRING unicodeString;
|
|
PWSTR nextString, currentString;
|
|
ULONG length, leftLength;
|
|
NTSTATUS status;
|
|
BOOLEAN found = FALSE;
|
|
|
|
if (String == NULL || String->Length / sizeof(WCHAR) == 0) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
// Read registry value entry data
|
|
|
|
|
|
status = IopGetRegistryValue(Handle, ValueName, &keyValueInformation);
|
|
|
|
if (!NT_SUCCESS( status )) {
|
|
return status;
|
|
} else if ((keyValueInformation->Type != REG_MULTI_SZ) ||
|
|
(keyValueInformation->DataLength == 0)) {
|
|
|
|
status = (keyValueInformation->Type == REG_MULTI_SZ) ? STATUS_SUCCESS
|
|
: STATUS_INVALID_PARAMETER;
|
|
ExFreePool(keyValueInformation);
|
|
return status;
|
|
}
|
|
|
|
|
|
// Scan through the multi_sz string to find the matching string
|
|
// and remove it.
|
|
|
|
|
|
status = STATUS_SUCCESS;
|
|
currentString = (PWSTR)KEY_VALUE_DATA(keyValueInformation);
|
|
leftLength = keyValueInformation->DataLength;
|
|
while (!found && leftLength >= String->Length + sizeof(WCHAR)) {
|
|
unicodeString.Buffer = currentString;
|
|
length = wcslen( currentString ) * sizeof( WCHAR );
|
|
unicodeString.Length = (USHORT)length;
|
|
length += sizeof(UNICODE_NULL);
|
|
unicodeString.MaximumLength = (USHORT)length;
|
|
nextString = currentString + length / sizeof(WCHAR);
|
|
leftLength -= length;
|
|
|
|
if (RtlEqualUnicodeString(&unicodeString, String, TRUE)) {
|
|
found = TRUE;
|
|
RtlMoveMemory(currentString, nextString, leftLength);
|
|
RtlInitUnicodeString(&unicodeString, ValueName);
|
|
status = ZwSetValueKey(
|
|
Handle,
|
|
&unicodeString,
|
|
TITLE_INDEX_VALUE,
|
|
REG_MULTI_SZ,
|
|
KEY_VALUE_DATA(keyValueInformation),
|
|
keyValueInformation->DataLength - length
|
|
);
|
|
break;
|
|
} else {
|
|
currentString = nextString;
|
|
}
|
|
}
|
|
ExFreePool(keyValueInformation);
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
IopAppendStringToValueKey (
|
|
IN HANDLE Handle,
|
|
IN PWSTR ValueName,
|
|
IN PUNICODE_STRING String,
|
|
IN BOOLEAN Create
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine appends a string to a value entry specified by ValueName
|
|
under an already opened registry handle. If the ValueName is not present
|
|
and Create is TRUE, a new value entry will be created using the name
|
|
ValueName.
|
|
|
|
Parameters:
|
|
|
|
Handle - Supplies the handle to a registry key whose value entry will
|
|
be modified.
|
|
|
|
ValueName - Supplies a pointer to a string to specify the value entry.
|
|
|
|
String - Supplies a unicode string to append to the value entry.
|
|
|
|
Create - Supplies a BOOLEAN variable to indicate if the ValueName
|
|
value entry should be created if it is not present.
|
|
|
|
Return Value:
|
|
|
|
Status code that indicates whether or not the function was successful.
|
|
|
|
--*/
|
|
|
|
{
|
|
PKEY_VALUE_FULL_INFORMATION keyValueInformation;
|
|
PWSTR destinationString, p;
|
|
UNICODE_STRING unicodeValueName;
|
|
ULONG size;
|
|
NTSTATUS status;
|
|
|
|
if ( !String || (String->Length < sizeof(WCHAR)) ) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
// Read registry value entry data
|
|
|
|
|
|
status = IopGetRegistryValue(Handle, ValueName, &keyValueInformation);
|
|
|
|
if(!NT_SUCCESS( status )) {
|
|
if (status == STATUS_OBJECT_NAME_NOT_FOUND && Create) {
|
|
|
|
|
|
// if no valid entry exists and user said ok to create one
|
|
|
|
|
|
keyValueInformation = NULL;
|
|
} else {
|
|
return status;
|
|
}
|
|
} else if(keyValueInformation->Type != REG_MULTI_SZ) {
|
|
|
|
ExFreePool(keyValueInformation);
|
|
|
|
if(Create) {
|
|
keyValueInformation = NULL;
|
|
} else {
|
|
return STATUS_INVALID_PARAMETER_2;
|
|
}
|
|
|
|
} else if(keyValueInformation->DataLength < sizeof(WCHAR) ||
|
|
*(PWCHAR)KEY_VALUE_DATA(keyValueInformation) == UNICODE_NULL) { // empty or one NULL WCHAR
|
|
|
|
ExFreePool(keyValueInformation);
|
|
keyValueInformation = NULL;
|
|
}
|
|
|
|
|
|
// Allocate a buffer to hold new data for the specified key value entry
|
|
// Make sure the buffer is at least an empty MULTI_SZ big.
|
|
|
|
|
|
if (keyValueInformation) {
|
|
size = keyValueInformation->DataLength + String->Length + sizeof (UNICODE_NULL);
|
|
} else {
|
|
size = String->Length + 2 * sizeof(UNICODE_NULL);
|
|
}
|
|
|
|
destinationString = p = (PWSTR)ExAllocatePool(PagedPool, size);
|
|
if (destinationString == NULL) {
|
|
if (keyValueInformation) {
|
|
ExFreePool(keyValueInformation);
|
|
}
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
|
|
// Copy the existing data to our newly allocated buffer, if any
|
|
|
|
|
|
if (keyValueInformation) {
|
|
|
|
|
|
// Note we need to remove a UNICODE_NULL because the
|
|
// MULTI_SZ has two terminating UNICODE_NULL.
|
|
|
|
|
|
RtlMoveMemory(p,
|
|
KEY_VALUE_DATA(keyValueInformation),
|
|
keyValueInformation->DataLength - sizeof(WCHAR)
|
|
);
|
|
p += keyValueInformation->DataLength / sizeof(WCHAR) - 1;
|
|
|
|
ExFreePool(keyValueInformation);
|
|
}
|
|
|
|
|
|
// Append the user specified unicode string to our buffer
|
|
|
|
RtlMoveMemory(p,
|
|
String->Buffer,
|
|
String->Length
|
|
);
|
|
p += String->Length / sizeof(WCHAR);
|
|
*p = UNICODE_NULL;
|
|
p++;
|
|
*p = UNICODE_NULL;
|
|
|
|
|
|
// Finally write the data to the specified registy value entry
|
|
|
|
|
|
RtlInitUnicodeString(&unicodeValueName, ValueName);
|
|
status = ZwSetValueKey(
|
|
Handle,
|
|
&unicodeValueName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_MULTI_SZ,
|
|
destinationString,
|
|
size
|
|
);
|
|
|
|
ExFreePool(destinationString);
|
|
return status;
|
|
}
|
|
|
|
BOOLEAN
|
|
IopConcatenateUnicodeStrings (
|
|
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 handle of the
|
|
newly created key.
|
|
|
|
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.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG length;
|
|
PWSTR buffer;
|
|
|
|
length = String1->Length + sizeof(UNICODE_NULL);
|
|
if (ARGUMENT_PRESENT(String2)) {
|
|
length += String2->Length;
|
|
}
|
|
buffer = (PWSTR)ExAllocatePool(PagedPool, length);
|
|
if (!buffer) {
|
|
return FALSE;
|
|
}
|
|
Destination->Buffer = buffer;
|
|
Destination->Length = (USHORT)length - sizeof(UNICODE_NULL);
|
|
Destination->MaximumLength = (USHORT)length;
|
|
RtlMoveMemory (Destination->Buffer, String1->Buffer, String1->Length);
|
|
if(ARGUMENT_PRESENT(String2)) {
|
|
RtlMoveMemory((PUCHAR)Destination->Buffer + String1->Length,
|
|
String2->Buffer,
|
|
String2->Length
|
|
);
|
|
}
|
|
buffer[length / sizeof(WCHAR) - 1] = UNICODE_NULL;
|
|
return TRUE;
|
|
}
|
|
|
|
NTSTATUS
|
|
IopPrepareDriverLoading (
|
|
IN PUNICODE_STRING KeyName,
|
|
IN HANDLE KeyHandle,
|
|
IN PIMAGE_NT_HEADERS Header
|
|
)
|
|
|
|
/*++
|
|
|
|
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;
|
|
|
|
status = STATUS_SUCCESS;
|
|
IsPlugPlayDriver = (Header &&
|
|
(Header->OptionalHeader.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_WDM_DRIVER))? TRUE : FALSE;
|
|
|
|
if (!IopIsAnyDeviceInstanceEnabled(KeyName, KeyHandle, (BOOLEAN)(IsPlugPlayDriver ? FALSE : TRUE))) {
|
|
|
|
if (IsPlugPlayDriver) {
|
|
|
|
if (!PnPDetectionEnabled) {
|
|
|
|
status = STATUS_PLUGPLAY_NO_DEVICE;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
KeEnterCriticalRegion();
|
|
ExAcquireResourceShared(&PpRegistryDeviceResource, TRUE);
|
|
|
|
|
|
// 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 = IopCreateMadeupNode( 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);
|
|
}
|
|
|
|
ExReleaseResource(&PpRegistryDeviceResource);
|
|
KeLeaveCriticalRegion();
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
IopServiceInstanceToDeviceInstance (
|
|
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 = IopOpenServiceEnumKeys(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
|
|
|
|
|
|
swprintf(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)) {
|
|
|
|
if (!IopConcatenateUnicodeStrings(DeviceInstanceRegistryPath,
|
|
&unicodeKeyName,
|
|
NULL)) {
|
|
|
|
if(ARGUMENT_PRESENT(DeviceInstanceHandle)) {
|
|
ZwClose(*DeviceInstanceHandle);
|
|
}
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
|
|
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 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.
|
|
|
|
Return Value:
|
|
|
|
The function value is the final status of the operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
|
|
PAGED_CODE();
|
|
|
|
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();
|
|
|
|
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++);
|
|
|
|
if((pathComponentLength = (ULONG)((PCHAR)pathCurPtr - (PCHAR)pathBeginPtr))) {
|
|
|
|
// 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
|
|
IopOpenServiceEnumKeys (
|
|
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
|
|
IopGetServiceInstanceCsConfigFlags(
|
|
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
|
|
IopSetServiceInstanceCsConfigFlags(
|
|
IN PUNICODE_STRING ServiceKeyName,
|
|
IN ULONG Instance,
|
|
IN ULONG CsConfigFlags
|
|
)
|
|
|
|
/*++
|
|
|
|
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
|
|
|
|
CsConfigFlags - Supplies the device instance's new CsConfigFlags
|
|
|
|
Return Value:
|
|
|
|
status
|
|
|
|
--*/
|
|
|
|
{
|
|
HANDLE handle;
|
|
NTSTATUS status;
|
|
UNICODE_STRING tempUnicodeString;
|
|
|
|
PAGED_CODE();
|
|
|
|
status = IopOpenCurrentHwProfileDeviceInstanceKey(&handle,
|
|
ServiceKeyName,
|
|
Instance,
|
|
KEY_READ | KEY_WRITE,
|
|
TRUE
|
|
);
|
|
if(NT_SUCCESS(status)) {
|
|
|
|
PiWstrToUnicodeString(&tempUnicodeString, REGSTR_VALUE_CSCONFIG_FLAGS);
|
|
status = ZwSetValueKey(handle,
|
|
&tempUnicodeString,
|
|
TITLE_INDEX_VALUE,
|
|
REG_DWORD,
|
|
&CsConfigFlags,
|
|
sizeof(CsConfigFlags)
|
|
);
|
|
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 = IopServiceInstanceToDeviceInstance(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
|
|
IopApplyFunctionToSubKeys(
|
|
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;
|
|
|
|
while(TRUE) {
|
|
|
|
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) {
|
|
|
|
// Try again with larger buffer.
|
|
|
|
ExFreePool(KeyInformation);
|
|
KeyInformation = NULL;
|
|
KeyInformationLength = RequiredBufferLength;
|
|
continue;
|
|
|
|
} else if(Status == STATUS_NO_MORE_ENTRIES) {
|
|
|
|
// break out of loop
|
|
|
|
Status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
} else {
|
|
|
|
// This is a non-critical error.
|
|
|
|
if(Flags & FUNCTIONSUBKEY_FLAG_IGNORE_NON_CRITICAL_ERRORS) {
|
|
goto ContinueWithNextSubKey;
|
|
} else {
|
|
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
|
|
IopRegMultiSzToUnicodeStrings(
|
|
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 IopFreeUnicodeStringList.
|
|
|
|
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.
|
|
|
|
--*/
|
|
|
|
{
|
|
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).
|
|
|
|
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)) {
|
|
IopFreeUnicodeStringList(*UnicodeStringList, i);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlMoveMemory((*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)) {
|
|
IopFreeUnicodeStringList(*UnicodeStringList, i);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
if(StringLength) {
|
|
RtlMoveMemory((*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
|
|
IopApplyFunctionToServiceInstances(
|
|
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;
|
|
WCHAR ValueNameString[20];
|
|
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 = IopOpenServiceEnumKeys(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) {
|
|
|
|
if (DesiredAccess) {
|
|
Status = IopOpenRegistryKeyEx( &SystemEnumHandle,
|
|
NULL,
|
|
&CmRegistryMachineSystemCurrentControlSetEnumName,
|
|
KEY_READ
|
|
);
|
|
if(!NT_SUCCESS(Status)) {
|
|
goto PrepareForReturn;
|
|
}
|
|
} else {
|
|
|
|
// Set DeviceInstanceHandle to NULL, since we won't be opening up the
|
|
// device instance keys.
|
|
|
|
DeviceInstanceHandle = NULL;
|
|
}
|
|
KeyValueInformation = (PKEY_VALUE_FULL_INFORMATION)ExAllocatePool(
|
|
PagedPool,
|
|
PNP_SCRATCH_BUFFER_SIZE);
|
|
if (!KeyValueInformation) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto PrepareForReturn;
|
|
}
|
|
i = 0;
|
|
while (TRUE) {
|
|
Status = ZwEnumerateValueKey ( // i was initialized to zero above.
|
|
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(DesiredAccess) {
|
|
ZwClose(SystemEnumHandle);
|
|
}
|
|
ExFreePool(KeyValueInformation);
|
|
}
|
|
|
|
if(ARGUMENT_PRESENT(ServiceInstanceOrdinal)) {
|
|
*ServiceInstanceOrdinal = i;
|
|
}
|
|
|
|
PrepareForReturn:
|
|
|
|
ZwClose(ServiceEnumHandle);
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
IopMarkDuplicateDevice (
|
|
IN PUNICODE_STRING TargetKeyName,
|
|
IN ULONG TargetInstance,
|
|
IN PUNICODE_STRING SourceKeyName,
|
|
IN ULONG SourceInstance
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine marks the device instance specified by TargetKeyName and TargetInstance
|
|
as DuplicateOf the device specified by SourceKeyName and SourceInstance.
|
|
|
|
Arguments:
|
|
|
|
TargetKeyName - supplies a pointer to the name of service key which will be marked
|
|
as duplicate.
|
|
|
|
TargetInstance - the instance number of the target device.
|
|
|
|
SourceKeyName - supplies a pointer to the name of service key.
|
|
|
|
SourceInstance - the instance number of the source device.
|
|
|
|
|
|
Returns:
|
|
|
|
NTSTATUS code.
|
|
|
|
--*/
|
|
|
|
{
|
|
HANDLE handle;
|
|
NTSTATUS status;
|
|
UNICODE_STRING sourceDeviceString, unicodeValueName;
|
|
|
|
|
|
// Open the handle of the target device instance.
|
|
|
|
|
|
status = IopServiceInstanceToDeviceInstance(
|
|
NULL,
|
|
TargetKeyName,
|
|
TargetInstance,
|
|
NULL,
|
|
&handle,
|
|
0
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
|
|
// Get the name of the source device instance
|
|
|
|
|
|
status = IopServiceInstanceToDeviceInstance(
|
|
NULL,
|
|
SourceKeyName,
|
|
SourceInstance,
|
|
&sourceDeviceString,
|
|
NULL,
|
|
0
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
|
|
// Write the name of the source device to the DuplicateOf value entry of
|
|
// target device key.
|
|
|
|
|
|
PiWstrToUnicodeString(&unicodeValueName, REGSTR_VALUE_DUPLICATEOF);
|
|
status = ZwSetValueKey(
|
|
handle,
|
|
&unicodeValueName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_SZ,
|
|
&sourceDeviceString,
|
|
sourceDeviceString.Length + sizeof(WCHAR)
|
|
);
|
|
return status;
|
|
}
|
|
|
|
BOOLEAN
|
|
IopIsDuplicatedDevices(
|
|
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
|
|
IopFreeUnicodeStringList(
|
|
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.
|
|
|
|
--*/
|
|
|
|
{
|
|
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;
|
|
HANDLE sysEnumHandle = NULL;
|
|
ULONG deviceFlags, count, newCount, i, j;
|
|
UNICODE_STRING unicodeValueName, deviceInstanceName;
|
|
WCHAR unicodeBuffer[20];
|
|
|
|
|
|
// Open registry ServiceKeyName\Enum branch
|
|
|
|
|
|
if (!ARGUMENT_PRESENT(ServiceHandle)) {
|
|
status = IopOpenServiceEnumKeys(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.
|
|
|
|
|
|
RtlInitUnicodeString(&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 = IopServiceInstanceToDeviceInstance (
|
|
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(handle, NULL);
|
|
if (deviceObject) {
|
|
deviceNode = (PDEVICE_NODE)deviceObject->DeviceObjectExtension->DeviceNode;
|
|
if (deviceNode) {
|
|
|
|
IopReleaseDeviceResources(deviceNode, TRUE);
|
|
|
|
if ((deviceNode->Flags & DNF_MADEUP) && (deviceNode->Flags & DNF_STARTED)) {
|
|
|
|
|
|
// Now mark this one deleted.
|
|
|
|
deviceNode->Flags &= ~DNF_STARTED;
|
|
|
|
IopSetDevNodeProblem(deviceNode, CM_PROB_DEVICE_NOT_THERE);
|
|
deletePdo = TRUE;
|
|
}
|
|
}
|
|
ObDereferenceObject(deviceObject); // added via IopDeviceObjectFromDeviceInstance
|
|
}
|
|
|
|
KeEnterCriticalRegion();
|
|
ExAcquireResourceShared(&PpRegistryDeviceResource, TRUE);
|
|
|
|
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( &handle,
|
|
sysEnumHandle,
|
|
&deviceInstanceName,
|
|
KEY_ALL_ACCESS
|
|
);
|
|
if (NT_SUCCESS(status)) {
|
|
ZwDeleteKey(handle);
|
|
}
|
|
}
|
|
|
|
ExFreePool(deviceInstanceName.Buffer);
|
|
|
|
|
|
// If there is a PDO for this device, remove it
|
|
|
|
|
|
if (deletePdo) {
|
|
IoDeleteDevice(deviceObject);
|
|
}
|
|
|
|
ExReleaseResource(&PpRegistryDeviceResource);
|
|
KeLeaveCriticalRegion();
|
|
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Reset Control\ActiveService value name.
|
|
|
|
|
|
if (controlHandle) {
|
|
PiWstrToUnicodeString(&unicodeValueName, REGSTR_VAL_ACTIVESERVICE);
|
|
ZwDeleteValueKey(controlHandle, &unicodeValueName);
|
|
ZwClose(controlHandle);
|
|
}
|
|
|
|
ZwClose(handle);
|
|
ExFreePool(deviceInstanceName.Buffer);
|
|
|
|
ExReleaseResource(&PpRegistryDeviceResource);
|
|
KeLeaveCriticalRegion();
|
|
|
|
}
|
|
}
|
|
|
|
|
|
// 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) {
|
|
|
|
KeEnterCriticalRegion();
|
|
ExAcquireResourceShared(&PpRegistryDeviceResource, TRUE);
|
|
|
|
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)
|
|
);
|
|
|
|
ExReleaseResource(&PpRegistryDeviceResource);
|
|
KeLeaveCriticalRegion();
|
|
}
|
|
ZwClose(serviceEnumHandle);
|
|
if (closeHandle) {
|
|
ZwClose(ServiceHandle);
|
|
}
|
|
if (sysEnumHandle) {
|
|
ZwClose(sysEnumHandle);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
IopDisableDevice(
|
|
IN PDEVICE_NODE DeviceNode,
|
|
IN HANDLE Handle
|
|
)
|
|
|
|
/*++
|
|
|
|
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;
|
|
|
|
|
|
// 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 (IopDoesDevNodeHaveProblem(DeviceNode)) {
|
|
ASSERT(IopIsDevNodeProblem(DeviceNode, CM_PROB_NOT_CONFIGURED) ||
|
|
IopIsDevNodeProblem(DeviceNode, CM_PROB_FAILED_INSTALL) ||
|
|
IopIsDevNodeProblem(DeviceNode, CM_PROB_REINSTALL));
|
|
|
|
IopClearDevNodeProblem(DeviceNode);
|
|
}
|
|
|
|
IopSetDevNodeProblem(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, deviceFlags;
|
|
UNICODE_STRING unicodeName;
|
|
BOOLEAN enabled, setProblem, closeHandle = FALSE;
|
|
PDEVICE_OBJECT physicalDeviceObject;
|
|
PDEVICE_NODE deviceNode;
|
|
|
|
|
|
// Open registry ServiceKeyName\Enum branch
|
|
|
|
|
|
if (!ARGUMENT_PRESENT(ServiceHandle)) {
|
|
status = IopOpenServiceEnumKeys(ServiceKeyName,
|
|
KEY_READ,
|
|
&ServiceHandle,
|
|
&serviceEnumHandle,
|
|
FALSE
|
|
);
|
|
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.
|
|
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
// Find out how many device instances listed in the ServiceName's
|
|
// Enum key.
|
|
|
|
|
|
status = IopGetRegistryValue ( serviceEnumHandle,
|
|
REGSTR_VALUE_COUNT,
|
|
&keyValueInformation
|
|
);
|
|
ZwClose(serviceEnumHandle);
|
|
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) {
|
|
if (closeHandle) {
|
|
ZwClose(ServiceHandle);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
// Walk through each registered device instance to check it is enabled.
|
|
|
|
|
|
enabled = FALSE;
|
|
for (i = 0; i < count; i++) {
|
|
|
|
|
|
// Get device instance handle. If it fails, we will skip this device
|
|
// instance.
|
|
|
|
|
|
status = IopServiceInstanceToDeviceInstance (
|
|
ServiceHandle,
|
|
NULL,
|
|
i,
|
|
NULL,
|
|
&handle,
|
|
KEY_ALL_ACCESS
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
continue;
|
|
}
|
|
|
|
physicalDeviceObject = IopDeviceObjectFromDeviceInstance(handle, NULL);
|
|
if (physicalDeviceObject) {
|
|
deviceNode = (PDEVICE_NODE)physicalDeviceObject->DeviceObjectExtension->DeviceNode;
|
|
if (deviceNode && (IopIsDevNodeProblem(deviceNode, CM_PROB_DISABLED) || IopIsDevNodeProblem(deviceNode, CM_PROB_HARDWARE_DISABLED))) {
|
|
ZwClose(handle);
|
|
ObDereferenceObject(physicalDeviceObject);
|
|
continue;
|
|
}
|
|
} else {
|
|
deviceNode = NULL;
|
|
}
|
|
|
|
|
|
// Check if the device instance has been disabled.
|
|
// First check global flag: CONFIGFLAG and then CSCONFIGFLAG.
|
|
|
|
|
|
deviceFlags = 0;
|
|
status = IopGetRegistryValue(handle,
|
|
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) {
|
|
|
|
|
|
// Convert this flag into the hardware profile-specific version, so it'll
|
|
// look the same as the CsConfigFlags we retrieve below.
|
|
|
|
|
|
deviceFlags = CSCONFIGFLAG_DISABLED;
|
|
|
|
} else {
|
|
|
|
status = IopGetServiceInstanceCsConfigFlags( ServiceKeyName,
|
|
i,
|
|
&deviceFlags
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
deviceFlags = 0;
|
|
}
|
|
}
|
|
|
|
|
|
// If the device is disabled (either globally, or specifically for this
|
|
// hardware profile), then mark the devnode as DNF_DISABLED.
|
|
|
|
|
|
if ((deviceFlags & CSCONFIGFLAG_DISABLED) || (deviceFlags & CSCONFIGFLAG_DO_NOT_START)) {
|
|
|
|
if (deviceNode) {
|
|
IopDisableDevice(deviceNode, handle);
|
|
}
|
|
}
|
|
|
|
if (physicalDeviceObject) {
|
|
ObDereferenceObject(physicalDeviceObject);
|
|
}
|
|
|
|
|
|
// Finally, we need to set the STATUSFLAGS of the device instance to
|
|
// indicate if the driver is successfully started.
|
|
|
|
|
|
if (!(deviceFlags & (CSCONFIGFLAG_DISABLED | CSCONFIGFLAG_DO_NOT_CREATE | CSCONFIGFLAG_DO_NOT_START))) {
|
|
|
|
ULONG legacy;
|
|
|
|
|
|
// Check should legacy instance key be counted as an enabled device
|
|
|
|
|
|
if (LegacyIncluded == FALSE) {
|
|
|
|
|
|
// The legacy variable must be initialized to zero. Because the device
|
|
// instance key may be an enumerated device. In this case, there is no
|
|
// legacy value name.
|
|
|
|
|
|
legacy = 0;
|
|
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);
|
|
}
|
|
} else {
|
|
legacy = 0;
|
|
}
|
|
|
|
if (legacy == 0) {
|
|
|
|
|
|
// Mark that the driver has at least a 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);
|
|
}
|
|
enabled = TRUE;
|
|
}
|
|
}
|
|
ZwClose(handle);
|
|
}
|
|
|
|
if (closeHandle) {
|
|
ZwClose(ServiceHandle);
|
|
}
|
|
return enabled;
|
|
}
|
|
|
|
BOOLEAN
|
|
IopIsDeviceInstanceEnabled(
|
|
IN HANDLE DeviceInstanceHandle OPTIONAL,
|
|
IN PUNICODE_STRING DeviceInstance,
|
|
IN BOOLEAN DisableIfEnabled
|
|
)
|
|
|
|
/*++
|
|
|
|
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.
|
|
|
|
DisableIfEnabled - 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, handle1;
|
|
ULONG deviceFlags;
|
|
BOOLEAN enabled, closeHandle = FALSE;
|
|
UNICODE_STRING unicodeString;
|
|
PDEVICE_OBJECT deviceObject = NULL;
|
|
PDEVICE_NODE deviceNode = NULL;
|
|
|
|
|
|
// Open registry ServiceKeyName\Enum branch
|
|
|
|
|
|
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 (!NT_SUCCESS( status )) {
|
|
return FALSE;
|
|
}
|
|
closeHandle = TRUE;
|
|
}
|
|
|
|
enabled = TRUE;
|
|
|
|
|
|
// First check the device node
|
|
|
|
|
|
deviceObject = IopDeviceObjectFromDeviceInstance(DeviceInstanceHandle, NULL);
|
|
if (deviceObject) {
|
|
deviceNode = (PDEVICE_NODE)deviceObject->DeviceObjectExtension->DeviceNode;
|
|
if (deviceNode && (IopIsDevNodeProblem(deviceNode, CM_PROB_DISABLED) || IopIsDevNodeProblem(deviceNode, CM_PROB_DISABLED))) {
|
|
enabled = FALSE;
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
|
|
// Check if the device instance has been disabled.
|
|
// First check global flag: CONFIGFLAG and then CSCONFIGFLAG.
|
|
|
|
|
|
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)) {
|
|
enabled = TRUE;
|
|
|
|
|
|
// See if we can open current hardware profile
|
|
|
|
status = IopOpenRegistryKeyEx( &handle1,
|
|
NULL,
|
|
&CmRegistryMachineSystemCurrentControlSetHardwareProfilesCurrent,
|
|
KEY_READ
|
|
);
|
|
|
|
if (NT_SUCCESS(status) && DeviceInstance != NULL) {
|
|
|
|
|
|
// Now, we must open the System\CCS\Enum key under this.
|
|
|
|
|
|
// Open system\CurrentControlSet under current hardware profile key
|
|
|
|
|
|
PiWstrToUnicodeString(&unicodeString, REGSTR_PATH_CURRENTCONTROLSET);
|
|
status = IopOpenRegistryKeyEx( &handle,
|
|
handle1,
|
|
&unicodeString,
|
|
KEY_READ
|
|
);
|
|
ZwClose(handle1);
|
|
if (NT_SUCCESS(status)) {
|
|
PiWstrToUnicodeString(&unicodeString, REGSTR_KEY_ENUM);
|
|
status = IopOpenRegistryKeyEx( &handle1,
|
|
handle,
|
|
&unicodeString,
|
|
KEY_READ
|
|
);
|
|
ZwClose(handle);
|
|
if (NT_SUCCESS(status)) {
|
|
status = IopOpenRegistryKeyEx( &handle,
|
|
handle1,
|
|
DeviceInstance,
|
|
KEY_READ
|
|
);
|
|
ZwClose(handle1);
|
|
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))) {
|
|
deviceFlags = *(PULONG)KEY_VALUE_DATA(keyValueInformation);
|
|
}
|
|
ExFreePool(keyValueInformation);
|
|
}
|
|
ZwClose(handle);
|
|
if (NT_SUCCESS(status)) {
|
|
if ((deviceFlags & CSCONFIGFLAG_DISABLED) ||
|
|
(deviceFlags & CSCONFIGFLAG_DO_NOT_CREATE) ||
|
|
(deviceFlags & CSCONFIGFLAG_DO_NOT_START)) {
|
|
enabled = FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
enabled = FALSE;
|
|
}
|
|
|
|
|
|
// If the device is disabled and has device node associated with it.
|
|
// disable the device.
|
|
|
|
|
|
if (enabled == FALSE && deviceNode && DisableIfEnabled) {
|
|
IopDisableDevice(deviceNode, DeviceInstanceHandle);
|
|
}
|
|
exit:
|
|
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;
|
|
}
|
|
|
|
PDRIVER_OBJECT
|
|
IopReferenceDriverObjectByName (
|
|
IN PUNICODE_STRING DriverName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine references a driver object by a given driver name.
|
|
|
|
Arguments:
|
|
|
|
DriverName - supplies a pointer to the name of the driver whose driver object is
|
|
to be referenced.
|
|
|
|
Returns:
|
|
|
|
A pointer to a DRIVER_OBJECT if succeeds. Otherwise, a NULL value.
|
|
|
|
--*/
|
|
|
|
{
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
HANDLE driverHandle;
|
|
NTSTATUS status;
|
|
PDRIVER_OBJECT driverObject;
|
|
|
|
|
|
// Make sure the driver name is valid.
|
|
if (DriverName->Length == 0) {
|
|
return NULL;
|
|
}
|
|
|
|
InitializeObjectAttributes(&objectAttributes, DriverName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
|
|
status = ObOpenObjectByName(&objectAttributes,
|
|
IoDriverObjectType,
|
|
KernelMode,
|
|
NULL,
|
|
FILE_READ_ATTRIBUTES,
|
|
NULL,
|
|
&driverHandle);
|
|
if (NT_SUCCESS(status)) {
|
|
// Now reference the driver object.
|
|
status = ObReferenceObjectByHandle(driverHandle, 0, IoDriverObjectType, KernelMode, &driverObject, NULL);
|
|
NtClose(driverHandle);
|
|
}
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
return driverObject;
|
|
} else {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
PDEVICE_OBJECT
|
|
IopDeviceObjectFromDeviceInstance(
|
|
IN HANDLE DeviceInstanceHandle OPTIONAL,
|
|
IN PUNICODE_STRING DeviceInstance OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
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:
|
|
|
|
DeviceInstanceHandle - Supplies a handle to the registry Device Instance key.
|
|
If this parameter is NULL, DeviceInstance must specify the registry path.
|
|
|
|
DeviceInstance - supplies a UNICODE_STRING to specify the device instance path.
|
|
if this parameter is NULL, DeviceInstanceHandle must specify the handle
|
|
to its registry key.
|
|
|
|
Returns:
|
|
|
|
A reference to the desired bus device object.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
HANDLE handle, deviceHandle = NULL;
|
|
PDEVICE_OBJECT deviceReference = NULL;
|
|
UNICODE_STRING unicodeName;
|
|
PKEY_VALUE_FULL_INFORMATION keyValueInformation;
|
|
PDEVICE_NODE deviceNode;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (!ARGUMENT_PRESENT(DeviceInstanceHandle)) {
|
|
|
|
|
|
// Treat HTREE\ROOT\0 specially
|
|
|
|
PiWstrToUnicodeString(&unicodeName, REGSTR_VAL_ROOT_DEVNODE);
|
|
|
|
if (RtlEqualUnicodeString(&unicodeName, DeviceInstance, TRUE)) {
|
|
|
|
deviceReference = IopRootDeviceNode->PhysicalDeviceObject;
|
|
ASSERT(deviceReference);
|
|
ObReferenceObject(deviceReference);
|
|
|
|
return deviceReference;
|
|
}
|
|
|
|
|
|
// first open System\CCS\Enum key and then the device instance key.
|
|
|
|
|
|
status = IopOpenRegistryKeyEx( &handle,
|
|
NULL,
|
|
&CmRegistryMachineSystemCurrentControlSetEnumName,
|
|
KEY_READ
|
|
);
|
|
|
|
if (!NT_SUCCESS( status )) {
|
|
return deviceReference;
|
|
}
|
|
|
|
ASSERT(DeviceInstance);
|
|
status = IopOpenRegistryKeyEx( &deviceHandle,
|
|
handle,
|
|
DeviceInstance,
|
|
KEY_READ
|
|
);
|
|
ZwClose(handle);
|
|
if (!NT_SUCCESS(status)) {
|
|
return deviceReference;
|
|
}
|
|
DeviceInstanceHandle = deviceHandle;
|
|
}
|
|
|
|
|
|
// Read the address of device object
|
|
|
|
|
|
PiWstrToUnicodeString(&unicodeName, REGSTR_KEY_CONTROL);
|
|
status = IopOpenRegistryKeyEx( &handle,
|
|
DeviceInstanceHandle,
|
|
&unicodeName,
|
|
KEY_READ
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
goto exit;
|
|
}
|
|
|
|
status = IopGetRegistryValue (handle,
|
|
REGSTR_VALUE_DEVICE_REFERENCE,
|
|
&keyValueInformation);
|
|
ZwClose(handle);
|
|
if (NT_SUCCESS(status)) {
|
|
if ((keyValueInformation->Type == REG_DWORD) &&
|
|
(keyValueInformation->DataLength == sizeof(ULONG_PTR))) {
|
|
|
|
deviceReference = *(PDEVICE_OBJECT *)KEY_VALUE_DATA(keyValueInformation);
|
|
}
|
|
ExFreePool(keyValueInformation);
|
|
}
|
|
if (!deviceReference) {
|
|
goto exit;
|
|
}
|
|
|
|
|
|
// Make sure the address of the device object is not clobbered,
|
|
// unfortunately if the address is truly bogus we will bugcheck since
|
|
// try/except doesn't work for kernel addresses.
|
|
|
|
|
|
if (deviceReference->Type != IO_TYPE_DEVICE) {
|
|
deviceReference = NULL;
|
|
} else {
|
|
deviceNode = (PDEVICE_NODE)deviceReference->DeviceObjectExtension->DeviceNode;
|
|
if (deviceNode && (deviceNode->PhysicalDeviceObject == deviceReference)) {
|
|
ObReferenceObject(deviceReference);
|
|
} else {
|
|
deviceReference = NULL;
|
|
}
|
|
}
|
|
|
|
exit:
|
|
if (deviceHandle) {
|
|
ZwClose(deviceHandle);
|
|
}
|
|
return deviceReference;
|
|
|
|
}
|
|
|
|
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,
|
|
IN BOOLEAN KeepReference
|
|
)
|
|
|
|
/*++
|
|
|
|
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 RegistryDeciceResource
|
|
|
|
Arguments:
|
|
|
|
InstancePath - supplies a pointer to the name of the device instance key.
|
|
|
|
KeepReference - supplies a boolean value to indicate if we should remove the
|
|
device instance path to device object mapping.
|
|
|
|
Return Value:
|
|
|
|
status
|
|
|
|
--*/
|
|
|
|
{
|
|
HANDLE handle, instanceHandle;
|
|
UNICODE_STRING unicodeValueName;
|
|
NTSTATUS status;
|
|
ULONG count = 0;
|
|
PKEY_VALUE_FULL_INFORMATION keyValueInformation;
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
// Open System\CurrentControlSet\Enum
|
|
|
|
|
|
status = IopOpenRegistryKeyEx( &handle,
|
|
NULL,
|
|
&CmRegistryMachineSystemCurrentControlSetEnumName,
|
|
KEY_ALL_ACCESS
|
|
);
|
|
|
|
if (!NT_SUCCESS( status )) {
|
|
return status;
|
|
}
|
|
|
|
|
|
// Open the device instance key
|
|
|
|
|
|
status = IopOpenRegistryKeyEx( &instanceHandle,
|
|
handle,
|
|
InstancePath,
|
|
KEY_ALL_ACCESS
|
|
);
|
|
|
|
ZwClose(handle);
|
|
if (!NT_SUCCESS( status )) {
|
|
|
|
|
|
// There is no registry key for the ServiceKeyName information.
|
|
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
// Delete the DeviceReference value name under Control key
|
|
|
|
|
|
if (!KeepReference) {
|
|
|
|
|
|
// BUGBUG (lonnym)--verify that removal of the following code fragment is valid.
|
|
|
|
#if 0
|
|
|
|
// Set the 'FoundAtEnum' value to false.
|
|
|
|
|
|
PiWstrToUnicodeString(&unicodeValueName, REGSTR_VALUE_FOUNDATENUM);
|
|
tmpValue = 0;
|
|
ZwSetValueKey(instanceHandle,
|
|
&unicodeValueName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_DWORD,
|
|
&tmpValue,
|
|
sizeof(tmpValue)
|
|
);
|
|
#endif // (lonnym, verify removal to here)
|
|
|
|
PiWstrToUnicodeString(&unicodeValueName, REGSTR_KEY_CONTROL);
|
|
status = IopOpenRegistryKeyEx( &handle,
|
|
instanceHandle,
|
|
&unicodeValueName,
|
|
KEY_ALL_ACCESS
|
|
);
|
|
if (NT_SUCCESS( status )) {
|
|
PiWstrToUnicodeString(&unicodeValueName, REGSTR_VALUE_DEVICE_REFERENCE);
|
|
ZwDeleteValueKey(handle, &unicodeValueName);
|
|
ZwClose(handle);
|
|
}
|
|
|
|
|
|
// Deregister the device from its controlling service's service enum key
|
|
|
|
|
|
status = PiDeviceRegistration( InstancePath, FALSE, NULL );
|
|
}
|
|
|
|
ZwClose(instanceHandle);
|
|
|
|
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
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_NODE deviceNode = (PDEVICE_NODE)DeviceObject->DeviceObjectExtension->DeviceNode;
|
|
HANDLE handle, handlex;
|
|
NTSTATUS status;
|
|
PKEY_VALUE_FULL_INFORMATION keyValueInformation;
|
|
PCM_RESOURCE_LIST cmResource;
|
|
PIO_RESOURCE_REQUIREMENTS_LIST ioResource;
|
|
ULONG length;
|
|
UNICODE_STRING unicodeName;
|
|
PWCHAR valueName = NULL;
|
|
|
|
*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 = IopReadDeviceConfiguration (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 = IopReadDeviceConfiguration (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 = IopReadDeviceConfiguration( 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;
|
|
RtlMoveMemory(*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
|
|
IopReadDeviceConfiguration (
|
|
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;
|
|
|
|
*CmResource = NULL;
|
|
*Length = 0;
|
|
|
|
if (Flags == REGISTRY_ALLOC_CONFIG) {
|
|
valueName = REGSTR_VALUE_ALLOC_CONFIG;
|
|
} else if (Flags == REGISTRY_FORCED_CONFIG) {
|
|
valueName = REGSTR_VALUE_FORCED_CONFIG;
|
|
} else if (Flags == REGISTRY_BOOT_CONFIG) {
|
|
valueName = REGSTR_VALUE_BOOT_CONFIG;
|
|
} else {
|
|
return STATUS_INVALID_PARAMETER_2;
|
|
}
|
|
|
|
|
|
// Read the registry value of the desired value name
|
|
|
|
|
|
status = IopGetRegistryValue (Handle,
|
|
valueName,
|
|
&keyValueInformation);
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
|
|
// Try to read what caller wants.
|
|
|
|
|
|
if ((keyValueInformation->Type == REG_RESOURCE_LIST) &&
|
|
(keyValueInformation->DataLength != 0)) {
|
|
*CmResource = ExAllocatePool(PagedPool,
|
|
keyValueInformation->DataLength);
|
|
if (*CmResource) {
|
|
if (*CmResource) {
|
|
*Length = keyValueInformation->DataLength;
|
|
RtlMoveMemory(*CmResource,
|
|
KEY_VALUE_DATA(keyValueInformation),
|
|
keyValueInformation->DataLength);
|
|
} else {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
ExFreePool(keyValueInformation);
|
|
if (*CmResource) {
|
|
PCM_RESOURCE_LIST resourceList;
|
|
PCM_FULL_RESOURCE_DESCRIPTOR cmFullDesc;
|
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR cmPartDesc;
|
|
ULONG j, k, size, count;
|
|
|
|
|
|
// 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 if (keyValueInformation->Type != REG_RESOURCE_LIST) {
|
|
status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
}
|
|
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;
|
|
|
|
|
|
// 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.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
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;
|
|
}
|
|
|
|
RtlMoveMemory(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);
|
|
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;
|
|
}
|
|
RtlMoveMemory(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;
|
|
RtlMoveMemory(p, IoList1, IoList1->ListSize);
|
|
p += IoList1->ListSize;
|
|
RtlMoveMemory(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;
|
|
}
|
|
RtlMoveMemory(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;
|
|
RtlMoveMemory(p, List1, size1);
|
|
p += size1;
|
|
RtlMoveMemory(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;
|
|
}
|
|
}
|
|
|
|
|
|
USHORT IopGetGroupOrderIndex (IN HANDLE ServiceHandle)
|
|
/*++
|
|
Routine Description:
|
|
This routine reads the Group value of the service key, finds its position in the ServiceOrderList.
|
|
If ServiceHandle is NULL or unrecognized group value, it returns a value with max group order + 1.
|
|
if any.
|
|
Parameters:
|
|
ServiceHandle - supplies a handle to the service key.
|
|
Return Value:
|
|
group order index.
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
PKEY_VALUE_FULL_INFORMATION keyValueInformation;
|
|
UNICODE_STRING *groupTable, group;
|
|
HANDLE handle;
|
|
ULONG count, index;
|
|
|
|
PAGED_CODE();
|
|
|
|
// Open System\CurrentControlSet\Control\ServiceOrderList
|
|
PiWstrToUnicodeString(&group, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\ServiceGroupOrder");
|
|
status = IopOpenRegistryKeyEx( &handle, NULL, &group, KEY_READ);
|
|
if (!NT_SUCCESS( status )) {
|
|
return NO_MORE_GROUP;
|
|
}
|
|
|
|
// Read and build a unicode string array containing all the group names.
|
|
status = IopGetRegistryValue (handle, L"List", &keyValueInformation);
|
|
ZwClose(handle);
|
|
if (NT_SUCCESS(status)) {
|
|
if ((keyValueInformation->Type == REG_MULTI_SZ) && (keyValueInformation->DataLength != 0)) {
|
|
status = IopRegMultiSzToUnicodeStrings(keyValueInformation, &groupTable, &count);
|
|
} else {
|
|
status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
ExFreePool(keyValueInformation);
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
return NO_MORE_GROUP;
|
|
}
|
|
|
|
if (ServiceHandle == NULL) {
|
|
IopFreeUnicodeStringList(groupTable, count);
|
|
return (USHORT)(count + 1);
|
|
}
|
|
|
|
// Read service key's Group value
|
|
status = IopGetRegistryValue (ServiceHandle, L"Group", &keyValueInformation);
|
|
if (NT_SUCCESS(status)) {
|
|
// Try to read what caller wants.
|
|
if ((keyValueInformation->Type == REG_SZ) && (keyValueInformation->DataLength != 0)) {
|
|
IopRegistryDataToUnicodeString(&group, (PWSTR)KEY_VALUE_DATA(keyValueInformation), keyValueInformation->DataLength);
|
|
}
|
|
} else {
|
|
// If we failed to read the Group value, or no Group value...
|
|
IopFreeUnicodeStringList(groupTable, count);
|
|
return (USHORT)count;
|
|
}
|
|
|
|
index = 0;
|
|
for (index = 0; index < count; index++) {
|
|
if (RtlEqualUnicodeString(&group, &groupTable[index], TRUE)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
ExFreePool(keyValueInformation);
|
|
IopFreeUnicodeStringList(groupTable, count);
|
|
return (USHORT)index;
|
|
}
|
|
|
|
|
|
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[100];
|
|
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;
|
|
|
|
serviceName = &DriverObject->DriverExtension->ServiceKeyName;
|
|
|
|
KeEnterCriticalRegion();
|
|
ExAcquireResourceShared(&PpRegistryDeviceResource, TRUE);
|
|
|
|
status = IopOpenRegistryKeyEx( &enumHandle,
|
|
NULL,
|
|
&CmRegistryMachineSystemCurrentControlSetEnumName,
|
|
KEY_ALL_ACCESS
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto exit;
|
|
}
|
|
|
|
length = _snwprintf(buffer, sizeof(buffer) / sizeof(WCHAR), L"ROOT\\LEGACY_%s", serviceName->Buffer);
|
|
deviceName.MaximumLength = sizeof(buffer);
|
|
ASSERT(length <= sizeof(buffer) - 10);
|
|
deviceName.Length = (USHORT)(length * sizeof(WCHAR));
|
|
deviceName.Buffer = buffer;
|
|
|
|
status = IopOpenRegistryKeyEx( &handle1,
|
|
enumHandle,
|
|
&deviceName,
|
|
KEY_ALL_ACCESS
|
|
);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
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 += instanceName.Length;
|
|
|
|
|
|
status = IopOpenRegistryKeyEx( &handle,
|
|
handle1,
|
|
&instanceName,
|
|
KEY_ALL_ACCESS
|
|
);
|
|
if (NT_SUCCESS(status)) {
|
|
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) {
|
|
|
|
|
|
// We also want to delete the madeup device node
|
|
|
|
|
|
deviceObject = IopDeviceObjectFromDeviceInstance(handle, NULL);
|
|
if (deviceObject) {
|
|
|
|
PDEVICE_NODE devNodex, devNodey;
|
|
|
|
deviceNode = (PDEVICE_NODE)deviceObject->DeviceObjectExtension->DeviceNode;
|
|
if (deviceNode != NULL && (deviceNode->Flags & DNF_MADEUP)) {
|
|
|
|
|
|
// Now mark this one deleted.
|
|
|
|
if (!IopDoesDevNodeHaveProblem(deviceNode)) {
|
|
deviceNode->Flags &= ~DNF_STARTED;
|
|
IopSetDevNodeProblem(deviceNode, CM_PROB_DEVICE_NOT_THERE);
|
|
}
|
|
|
|
|
|
// This is actually doing nothing because DeviceNode->ResourceList is NULL.
|
|
|
|
|
|
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; // remove its boot config if any
|
|
IoDeleteDevice(deviceObject);
|
|
}
|
|
ObDereferenceObject(deviceObject); // added via IopDeviceObjectFromDeviceInstance
|
|
}
|
|
|
|
PiWstrToUnicodeString(&unicodeName, REGSTR_KEY_CONTROL);
|
|
status = IopOpenRegistryKeyEx( &handlex,
|
|
handle,
|
|
&unicodeName,
|
|
KEY_ALL_ACCESS
|
|
);
|
|
if (NT_SUCCESS(status)) {
|
|
ZwDeleteKey(handlex);
|
|
}
|
|
PiWstrToUnicodeString(&unicodeName, REGSTR_KEY_LOG_CONF);
|
|
status = IopOpenRegistryKeyEx( &handlex,
|
|
handle,
|
|
&unicodeName,
|
|
KEY_ALL_ACCESS
|
|
);
|
|
if (NT_SUCCESS(status)) {
|
|
ZwDeleteKey(handlex);
|
|
}
|
|
|
|
ZwClose(enumHandle);
|
|
|
|
|
|
// We need to call IopCleanupDeviceRegistryValue even we are going to
|
|
// delete it. Because, it also cleans up related value names in other
|
|
// keys.
|
|
|
|
|
|
IopCleanupDeviceRegistryValues(&deviceName, FALSE);
|
|
ZwDeleteKey(handle);
|
|
ZwDeleteKey(handle1);
|
|
} else {
|
|
ZwClose(handle);
|
|
ZwClose(handle1);
|
|
ZwClose(enumHandle);
|
|
}
|
|
} else {
|
|
ZwClose(handle1);
|
|
ZwClose(enumHandle);
|
|
}
|
|
} else {
|
|
ZwClose(enumHandle);
|
|
}
|
|
exit:
|
|
ExReleaseResource(&PpRegistryDeviceResource);
|
|
KeLeaveCriticalRegion();
|
|
return;
|
|
}
|
|
|
|
NTSTATUS
|
|
IopDeviceNodeCapabilitiesToRegistry (
|
|
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 = IopQueryDeviceCapabilities(DeviceNode, &capabilities);
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
return IopDeviceCapabilitiesToRegistry(DeviceNode,&capabilities);
|
|
}
|
|
|
|
NTSTATUS
|
|
IopDeviceCapabilitiesToRegistry (
|
|
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 IopDeviceNodeCapabilitiesToRegistry
|
|
after device is started.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - supplies a pointer to a device object whose registry
|
|
values are to be updated.
|
|
|
|
Return Value:
|
|
|
|
status
|
|
|
|
--*/
|
|
|
|
{
|
|
HANDLE handle;
|
|
NTSTATUS status;
|
|
UNICODE_STRING unicodeName;
|
|
ULONG tmpValue;
|
|
|
|
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)) {
|
|
return 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)));
|
|
|
|
PiWstrToUnicodeString(&unicodeName, REGSTR_VAL_CAPABILITIES);
|
|
tmpValue = (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);
|
|
|
|
status = ZwSetValueKey(
|
|
handle,
|
|
&unicodeName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_DWORD,
|
|
&tmpValue,
|
|
sizeof(tmpValue)
|
|
);
|
|
|
|
PiWstrToUnicodeString(&unicodeName, REGSTR_VAL_UI_NUMBER);
|
|
tmpValue = Capabilities->UINumber;
|
|
if(tmpValue != (ULONG)-1) {
|
|
ZwSetValueKey(handle,
|
|
&unicodeName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_DWORD,
|
|
&tmpValue,
|
|
sizeof(tmpValue)
|
|
);
|
|
} else {
|
|
ZwDeleteValueKey(handle, &unicodeName);
|
|
}
|
|
|
|
ZwClose(handle);
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
IopRestartDeviceNode(
|
|
IN PDEVICE_NODE DeviceNode
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
ASSERT(!(IopDoesDevNodeHaveProblem(DeviceNode) ||
|
|
(DeviceNode->Flags & (DNF_STARTED |
|
|
DNF_ADDED |
|
|
DNF_RESOURCE_ASSIGNED |
|
|
DNF_RESOURCE_REPORTED)) ||
|
|
(DeviceNode->UserFlags & DNUF_WILL_BE_REMOVED)));
|
|
|
|
ASSERT(DeviceNode->Flags & DNF_ENUMERATED);
|
|
|
|
if (!(DeviceNode->Flags & DNF_ENUMERATED)) {
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
DeviceNode->UserFlags &= ~DNUF_NEED_RESTART;
|
|
|
|
#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
|
|
|
|
|
|
// Free any existing devnode strings so we can recreate them
|
|
// during enumeration.
|
|
|
|
|
|
if (DeviceNode->Flags & DNF_PROCESSED) {
|
|
|
|
DeviceNode->Flags &= ~(DNF_PROCESSED |
|
|
DNF_ENUMERATION_REQUEST_QUEUED |
|
|
DNF_NO_RESOURCE_REQUIRED |
|
|
DNF_RESOURCE_REQUIREMENTS_CHANGED);
|
|
|
|
if (DeviceNode->ServiceName.Length != 0) {
|
|
ExFreePool(DeviceNode->ServiceName.Buffer);
|
|
RtlInitUnicodeString(&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_BOOT_CONFIG_RESERVED | DNF_NO_RESOURCE_REQUIRED)));
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
BOOLEAN
|
|
IopDeleteKeyRecursiveCallback(
|
|
IN HANDLE KeyHandle,
|
|
IN PUNICODE_STRING KeyName,
|
|
IN OUT PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is a callback routine to IopApplyFunctionToSubkeys, that gets called
|
|
through IopDeleteKeyRecursive. This routine prepares a given key for
|
|
deletion by deleting all of its subkeys. This is done, using
|
|
IopApplyFunctionToSubkeys, 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
|
|
IopApplyFunctionToSubkeys.
|
|
|
|
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 = IopApplyFunctionToSubKeys(
|
|
KeyHandle,
|
|
NULL,
|
|
KEY_ALL_ACCESS,
|
|
FUNCTIONSUBKEY_FLAG_IGNORE_NON_CRITICAL_ERRORS |
|
|
FUNCTIONSUBKEY_FLAG_DELETE_SUBKEYS,
|
|
IopDeleteKeyRecursiveCallback,
|
|
Context);
|
|
|
|
*((NTSTATUS *)Context) = status;
|
|
return (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;
|
|
} |