/*++ Copyright (c) 1995 Microsoft Corporation Module Name: pnpsubs.c Abstract: This module contains the plug-and-play initialization subroutines for the I/O system. Author: Shie-Lin Tzong (shielint) 30-Jan-1995 Environment: Kernel mode Revision History: --*/ #include "iop.h" #if _PNP_POWER_ extern ULONG IopPeripheralCount[]; BOOLEAN IopIsDuplicatedResourceLists( IN PCM_RESOURCE_LIST Configuration1, IN PCM_RESOURCE_LIST Configuration2 ); NTSTATUS IopInitializeHardwareConfiguration( IN PLOADER_PARAMETER_BLOCK LoaderBlock ); NTSTATUS IopSetupConfigurationTree( IN PCONFIGURATION_COMPONENT_DATA CurrentEntry, IN HANDLE Handle, IN PUNICODE_STRING ParentName, IN INTERFACE_TYPE InterfaceType, IN BUS_DATA_TYPE BusDataType, IN ULONG BusNumber ); NTSTATUS IopInitializeRegistryNode( IN PCONFIGURATION_COMPONENT_DATA CurrentEntry, IN HANDLE EnumHandle, IN PUNICODE_STRING ParentKeyName, IN PUNICODE_STRING KeyName, IN ULONG Instance, IN INTERFACE_TYPE InterfaceType, IN BUS_DATA_TYPE BusDataType, IN ULONG BusNumber ); #endif // _PNP_POWER_ NTSTATUS IopInitServiceEnumList ( VOID ); #if 0 BOOLEAN IopInitializeBusKey( IN HANDLE KeyHandle, IN PUNICODE_STRING KeyName, IN OUT PVOID WorkName ); #endif BOOLEAN IopInitializeDeviceKey( IN HANDLE KeyHandle, IN PUNICODE_STRING KeyName, IN OUT PVOID WorkName ); BOOLEAN IopInitializeDeviceInstanceKey( IN HANDLE KeyHandle, IN PUNICODE_STRING KeyName, IN OUT PVOID WorkName ); #ifdef ALLOC_PRAGMA #if _PNP_POWER_ #pragma alloc_text(INIT, IopIsDuplicatedResourceLists) #pragma alloc_text(INIT, IopInitializeHardwareConfiguration) #pragma alloc_text(INIT, IopSetupConfigurationTree) #pragma alloc_text(INIT, IopInitializeRegistryNode) #endif // _PNP_POWER_ #pragma alloc_text(INIT, IopInitServiceEnumList) #if 0 #pragma alloc_text(INIT, IopInitializeBusKey) #endif #pragma alloc_text(INIT, IopInitializeDeviceKey) #pragma alloc_text(INIT, IopInitializeDeviceInstanceKey) #pragma alloc_text(INIT, IopInitializePlugPlayServices) #endif #if _PNP_POWER_ BOOLEAN IopIsDuplicatedResourceLists( IN PCM_RESOURCE_LIST Configuration1, IN PCM_RESOURCE_LIST Configuration2 ) /*++ Routine Description: This routine compares two set of resource lists to determine if they are exactly the same. Arguments: Configuration1 - Supplies a pointer to the first set of resource. Configuration2 - Supplies a pointer to the second set of resource. Return Value: returns TRUE if the two set of resources are the same; otherwise a value of FALSE is returned. --*/ { ULONG resource1Size, resource2Size; BOOLEAN sameResource = FALSE; resource1Size = IopDetermineResourceListSize(Configuration1); resource2Size = IopDetermineResourceListSize(Configuration2); if (resource1Size == resource2Size) { if (!memcmp(Configuration1, Configuration2, resource1Size)) { sameResource = TRUE; } } return sameResource; } NTSTATUS IopInitializeHardwareConfiguration( IN PLOADER_PARAMETER_BLOCK LoaderBlock ) /*++ Routine Description: This routine creates \\Registry\Machine\Sysem\Enum\Root node in the registry and calls worker routine to put the hardware information detected by arc firmware/ntdetect to the Enum\Root branch. This routine and its worker routines use both pnp scratch buffer1 and scratch buffer2. Arguments: LoaderBlock - supplies a pointer to the LoaderBlock passed in from the OS Loader. Returns: NTSTATUS code for sucess or reason of failure. --*/ { NTSTATUS status; HANDLE baseHandle; UNICODE_STRING unicodeName, rootName; PCONFIGURATION_COMPONENT_DATA currentEntry; PCONFIGURATION_COMPONENT component; INTERFACE_TYPE interfaceType; BUS_DATA_TYPE busDataType; ULONG busNumber, i; unicodeName.Length = 0; unicodeName.MaximumLength = PNP_LARGE_SCRATCH_BUFFER_SIZE; unicodeName.Buffer = IopPnpScratchBuffer1; PiWstrToUnicodeString(&rootName, REGSTR_KEY_ROOTENUM); RtlAppendStringToString((PSTRING)&unicodeName, (PSTRING)&rootName); currentEntry = (PCONFIGURATION_COMPONENT_DATA)LoaderBlock->ConfigurationRoot; if (currentEntry) { // // Open\Create \\Registry\Machine\System\CurrentControlSet\Enum\Root and use the // returned handle as the BaseHandle to build the Arc keys. // status = IopOpenRegistryKey(&baseHandle, NULL, &CmRegistryMachineSystemCurrentControlSetEnumRootName, KEY_ALL_ACCESS, FALSE ); if (!NT_SUCCESS(status)) { return status; } currentEntry = currentEntry->Child; while (currentEntry != NULL) { component = ¤tEntry->ComponentEntry; // // We are only interested in isa, or internal bus component. // For other busses, they will be picked up by bus enumerators. // if (component->Class == AdapterClass) { if (component->Type == MultiFunctionAdapter) { _strupr(component->Identifier); if (!strstr(component->Identifier, "PNP")) { if (!_stricmp(component->Identifier, "ISA")) { interfaceType = Isa; busNumber = 0; busDataType = MaximumBusDataType; } else if (!_stricmp(component->Identifier, "INTERNAL")) { interfaceType = Internal; busNumber = 0; busDataType = MaximumBusDataType; #if defined(_X86_) } else if (!_stricmp(component->Identifier, "MCA")) { interfaceType = MicroChannel; busNumber = 0; busDataType = Pos; #endif } else { currentEntry = currentEntry->Sibling; continue; } } } #if defined(_X86_) else if (component->Type == EisaAdapter) { interfaceType = Eisa; busNumber = 0; busDataType = EisaConfiguration; } else { currentEntry = currentEntry->Sibling; continue; } #endif #if 0 // // Reset peripheral count before processing a bus. // for (i = 0; i <= MaximumType; i++) { IopPeripheralCount[i] = 0; } #endif status = IopSetupConfigurationTree(currentEntry->Child, baseHandle, &unicodeName, interfaceType, busDataType, busNumber ); } currentEntry = currentEntry->Sibling; } NtClose(baseHandle); return(status); } else { return STATUS_SUCCESS; } } NTSTATUS IopSetupConfigurationTree( IN PCONFIGURATION_COMPONENT_DATA CurrentEntry, IN HANDLE Handle, IN PUNICODE_STRING WorkName, IN INTERFACE_TYPE InterfaceType, IN BUS_DATA_TYPE BusDataType, IN ULONG BusNumber ) /*++ Routine Description: This routine traverses loader configuration tree and register desired hardware information to System\Enuk\Root registry data base. Arguments: CurrentEntry - Supplies a pointer to a loader configuration tree or subtree. Handle - Supplies the handle to the registry where we can create new key. WorkName - Supplies a pointer to a unicode string to specify the parent key name of current entry. InterfaceType - Specify the Interface type of the bus that the CurrentEntry component resides. BusDataType - Specify the data/configuration type of the bus that the CurrentEntry component resides. BusNumber - Specify the Bus Number of the bus that the CurrentEntry component resides. If Bus number is -1, it means InterfaceType and BusNumber are meaningless for this component. Returns: NTSTATUS --*/ { NTSTATUS status; PCONFIGURATION_COMPONENT component; UNICODE_STRING keyName; UNICODE_STRING unicodeName; static ULONG peripheralCount = 0, controllerCount = 0; BOOLEAN freeKeyName = FALSE, nameMapped; // BUGBUG (shielint): initialize pnpId for now to avoid compiler warning. PWSTR pnpId = NULL; STRING stringName; USHORT namePosition; // // Process current entry first // if (CurrentEntry) { component = &CurrentEntry->ComponentEntry; nameMapped = FALSE; #if 0 switch (component->Type) { case DiskController: pnpId = L"DiskController"; break; case TapeController: pnpId = L"PNP0100"; break; case CdromController: pnpId = L"PNP0200"; break; case WormController: pnpId = L"PNP0300"; break; case SerialController: pnpId = L"PNP0400"; break; case NetworkController: pnpId = L"PNP0500"; break; case DisplayController: pnpId = L"PNP0600"; break; case ParallelController: pnpId = L"PNP0700"; break; case PointerController: pnpId = L"PNP0800"; break; case KeyboardController: pnpId = L"PNP0900"; break; case AudioController: pnpId = L"PNP1000"; break; case OtherController: pnpId = L"PNP1100"; break; case DiskPeripheral: pnpId = L"PNP1200"; break; case FloppyDiskPeripheral: pnpId = L"PNP1300"; break; case TapePeripheral: pnpId = L"PNP1400"; break; case ModemPeripheral: pnpId = L"PNP1500"; break; case MonitorPeripheral: pnpId = L"PNP1600"; break; case PrinterPeripheral: pnpId = L"PNP1700"; break; case PointerPeripheral: pnpId = L"PNP1800"; break; case KeyboardPeripheral: pnpId = L"PNP1900"; break; case TerminalPeripheral: pnpId = L"PNP2000"; break; case OtherPeripheral: pnpId = L"PNP2100"; break; case LinePeripheral: pnpId = L"PNP2200"; break; case NetworkPeripheral: pnpId = L"PNP2300"; break; } #endif // // if we did NOT successfully mapped a PNP id for the component, we // will create a special key name and the key will be processed by // user mode inf file later. // if (nameMapped) { RtlCreateUnicodeString(&keyName, pnpId); } else { RtlInitUnicodeString(&unicodeName, L"Arc"); IopConcatenateUnicodeStrings(&keyName, &unicodeName, &CmTypeName[component->Type]); freeKeyName = TRUE; } // // Initialize and copy current component to Enum\Root if it is // the one we're insterested in. // namePosition = WorkName->Length; status = IopInitializeRegistryNode( CurrentEntry, Handle, WorkName, &keyName, IopPeripheralCount[component->Type]++, InterfaceType, BusDataType, BusNumber ); if (freeKeyName) { RtlFreeUnicodeString(&keyName); } if (!NT_SUCCESS(status)) { return status; } // // Process the child entry of current entry // status = IopSetupConfigurationTree(CurrentEntry->Child, Handle, WorkName, InterfaceType, BusDataType, BusNumber ); WorkName->Length = namePosition; if (!NT_SUCCESS(status)) { return status; } // // Process all the Siblings of current entry // status = IopSetupConfigurationTree(CurrentEntry->Sibling, Handle, WorkName, InterfaceType, BusDataType, BusNumber ); return(status); } else { return(STATUS_SUCCESS); } } NTSTATUS IopInitializeRegistryNode( IN PCONFIGURATION_COMPONENT_DATA CurrentEntry, IN HANDLE EnumRootHandle, IN PUNICODE_STRING WorkName, IN PUNICODE_STRING KeyName, IN ULONG Instance, IN INTERFACE_TYPE InterfaceType, IN BUS_DATA_TYPE BusDataType, IN ULONG BusNumber ) /*++ Routine Description: This routine creates a node for the current firmware component and puts component data to the data part of the node. Arguments: CurrentEntry - Supplies a pointer to a configuration component. EnumRootHandle - Supplies the handle of Enum key under which we will build our new key. WorkName - Supplies a point to a unicode string which is the name of its parent node. KeyName - Suppiles a pointer to a UNICODE string which will be the name of the new key. Instance - Supplies an instance number of KeyName. InterfaceType - Specify the Interface type of the bus that the CurrentEntry component resides. (See BusNumber also) BusDataType - Specifies the configuration type of the bus. BusNumber - Specify the Bus Number of the bus that the CurrentEntry component resides on. If Bus number is -1, it means InterfaceType and BusNumber are meaningless for this component. Returns: None. --*/ { NTSTATUS status; HANDLE handle, keyHandle; UNICODE_STRING unicodeName, unicodeValueName; PCONFIGURATION_COMPONENT component; PKEY_VALUE_FULL_INFORMATION keyValueInformation, serviceInfo = NULL; PWSTR service = (PWSTR)NULL, p; ULONG disposition, foundAlready, dataLength = 0; ULONG serviceLength = 0, tmpValue, emptyResource = 0; BOOLEAN newKey = FALSE; PCM_RESOURCE_LIST dataArea, configuration1; CHAR unicodeBuffer[20]; PUCHAR resourceBuffer = IopPnpScratchBuffer2; BOOLEAN freeDataArea = FALSE, isDuplicated; component = &CurrentEntry->ComponentEntry; // // Open/Create a key under Enum/Root bransh. If fails, // exit (nothing we can do.) // status = IopOpenRegistryKeyPersist ( &keyHandle, EnumRootHandle, KeyName, KEY_ALL_ACCESS, TRUE, &disposition ); if (!NT_SUCCESS(status)) { return status; } #if 0 // not sure if we need it // // If the key is newly created, set its NewDevice value to TRUE so that // user mode Pnp mgr can initiate a device installation. The NewDevice // value will be reset by user mode Pnp mgr. SO , we don't touch it here. // if (disposition == REG_CREATED_NEW_KEY) { newKey = TRUE; PiWstrToUnicodeString(&unicodeValueName, REGSTR_VALUE_NEWDEVICE); tmpValue = 1; NtSetValueKey( keyHandle, &unicodeValueName, TITLE_INDEX_VALUE, REG_DWORD, &tmpValue, sizeof (tmpValue) ); PiWstrToUnicodeString(&unicodeValueName, REGSTR_VALUE_STATIC); NtSetValueKey( keyHandle, &unicodeValueName, TITLE_INDEX_VALUE, REG_DWORD, &tmpValue, sizeof(tmpValue) ); } #endif if (component->Type > OtherController) { // // The current component is a peripheral. // // // Create a new instance key under KeyName // PiUlongToInstanceKeyUnicodeString(&unicodeName, unicodeBuffer, 20, Instance); status = IopOpenRegistryKeyPersist ( &handle, keyHandle, &unicodeName, KEY_ALL_ACCESS, TRUE, &disposition ); NtClose(keyHandle); if (!NT_SUCCESS(status)) { goto init_Exit; } // // // Create all the default value entry for the newly created key. // Service = (do NOT create) // BaseDevicePath = WorkName // FoundAtEnum = 1 // InterfaceType = InterfaceType (only for bus device) // SystemBusNumber = BusNumber (only for bus device) // BusDataType = BusDataType (only for bus device) // PiWstrToUnicodeString(&unicodeValueName, REGSTR_VALUE_BASEDEVICEPATH); p = WorkName->Buffer; p += WorkName->Length / sizeof(WCHAR); *p = UNICODE_NULL; NtSetValueKey( handle, &unicodeValueName, TITLE_INDEX_VALUE, REG_SZ, WorkName->Buffer, WorkName->Length + sizeof (UNICODE_NULL) ); PiWstrToUnicodeString(&unicodeValueName, REGSTR_VALUE_FOUNDATENUM); tmpValue = 1; NtSetValueKey( handle, &unicodeValueName, TITLE_INDEX_VALUE, REG_DWORD, &tmpValue, sizeof(tmpValue) ); PiWstrToUnicodeString(&unicodeValueName, REGSTR_VALUE_SYSTEMBUSNUMBER); tmpValue = BusNumber; NtSetValueKey( handle, &unicodeValueName, TITLE_INDEX_VALUE, REG_DWORD, &tmpValue, sizeof(tmpValue) ); PiWstrToUnicodeString(&unicodeValueName, REGSTR_VALUE_INTERFACETYPE); tmpValue = InterfaceType; NtSetValueKey( handle, &unicodeValueName, TITLE_INDEX_VALUE, REG_DWORD, &tmpValue, sizeof(tmpValue) ); PiWstrToUnicodeString(&unicodeValueName, REGSTR_VALUE_BUSDATATYPE); tmpValue = BusDataType; NtSetValueKey( handle, &unicodeValueName, TITLE_INDEX_VALUE, REG_DWORD, &tmpValue, sizeof(tmpValue) ); NtClose(handle); // // Append keyanme and instance key name to workname // (In fact, we don't need to do this because there // is nothing under peripheral component.) // // // Append KeyName to workname // p = WorkName->Buffer; p += WorkName->Length / sizeof(WCHAR); *p = OBJ_NAME_PATH_SEPARATOR; WorkName->Length += sizeof (WCHAR); RtlAppendStringToString((PSTRING)WorkName, (PSTRING)KeyName); } else { // // Current component is a controller // // // Append KeyName to workname // p = WorkName->Buffer; p += WorkName->Length / sizeof(WCHAR); *p = OBJ_NAME_PATH_SEPARATOR; WorkName->Length += sizeof (WCHAR); RtlAppendStringToString((PSTRING)WorkName, (PSTRING)KeyName); // // We need to convert the h/w tree configuration data format from // CM_PARTIAL_RESOURCE_DESCRIPTIOR to CM_RESOURCE_LIST. // if (CurrentEntry->ConfigurationData) { // // This component has configuration data, we copy the data // to our work area, add some more data items and copy the new // configuration data to the registry. // dataLength = component->ConfigurationDataLength + FIELD_OFFSET(CM_FULL_RESOURCE_DESCRIPTOR, PartialResourceList) + FIELD_OFFSET(CM_RESOURCE_LIST, List); dataArea = (PCM_RESOURCE_LIST)resourceBuffer; // // Make sure our reserved area is big enough to hold the data. // if (dataLength > PNP_LARGE_SCRATCH_BUFFER_SIZE) { // // If reserved area is not big enough, we resize our reserved // area. If, unfortunately, the reallocation fails, we simply // loss the configuration data of this particular component. // dataArea = (PCM_RESOURCE_LIST)ExAllocatePool( PagedPool, dataLength ); if (dataArea) { freeDataArea = TRUE; } } if (dataArea) { RtlMoveMemory((PUCHAR)&dataArea->List->PartialResourceList.Version, CurrentEntry->ConfigurationData, component->ConfigurationDataLength ); dataArea->Count = 1; dataArea->List[0].InterfaceType = InterfaceType; dataArea->List[0].BusNumber = BusNumber; } } if (CurrentEntry->ConfigurationData == NULL || !dataArea) { // // This component has NO configuration data (or we can't resize // our reserved area to hold the data), we simple add whatever // is required to set up a CM_FULL_RESOURCE_LIST. // dataArea = (PCM_RESOURCE_LIST)&emptyResource; dataLength = FIELD_OFFSET(CM_RESOURCE_LIST, List); } if (!newKey) { // // If the key exists already, we need to check if current entry // already being converted (most likely the answer is yes.). If it already // converted, we simply set "FoundAtEnum=" to TRUE. Otherwise, we will // create it. // tmpValue = 0; PiUlongToInstanceKeyUnicodeString(&unicodeName, unicodeBuffer, 20, tmpValue); status = IopOpenRegistryKey (&handle, keyHandle, &unicodeName, KEY_ALL_ACCESS, FALSE ); while (NT_SUCCESS(status)) { // // if the current key has been Found/Enum'ed already, we need // to skip it. // foundAlready = 0; status = IopGetRegistryValue (handle, REGSTR_VALUE_FOUNDATENUM, &keyValueInformation); if (NT_SUCCESS(status)) { if (keyValueInformation->DataLength != 0) { foundAlready = *(PULONG)KEY_VALUE_DATA(keyValueInformation); } ExFreePool(keyValueInformation); } if (!foundAlready) { keyValueInformation = NULL; status = IopGetRegistryValue (handle, REGSTR_VALUE_DETECTSIGNATURE, &keyValueInformation); if (NT_SUCCESS(status) && keyValueInformation->DataLength != 0) { configuration1 = (PCM_RESOURCE_LIST)KEY_VALUE_DATA(keyValueInformation); } else if (status == STATUS_OBJECT_NAME_NOT_FOUND || keyValueInformation->DataLength == 0) { // // If no "DetectSignature =" value entry, we set up an empty // CM_RESOURCE_LIST. // configuration1 = (PCM_RESOURCE_LIST)&emptyResource; } // // To detect ARC duplicated components, we should be able // to simply compare the RAW resource list. If they are the // same *most likely* they are duplicates. This includes // the case that both resource list are empty. (if they // are empty, we should be able to simply pick up the // key and use it.) // isDuplicated = IopIsDuplicatedResourceLists( configuration1, dataArea); if (!isDuplicated) { // // BUGBUG We should also check for bus info. // isDuplicated = IopIsDuplicatedDevices( configuration1, dataArea, NULL, NULL ); } if (keyValueInformation) { ExFreePool(keyValueInformation); } if (isDuplicated) { PiWstrToUnicodeString( &unicodeValueName, REGSTR_VALUE_FOUNDATENUM); tmpValue = 1; status = NtSetValueKey(handle, &unicodeValueName, TITLE_INDEX_VALUE, REG_DWORD, &tmpValue, sizeof(tmpValue) ); NtClose(handle); NtClose(keyHandle); goto init_Exit0; } } NtClose(handle); tmpValue++; PiUlongToInstanceKeyUnicodeString(&unicodeName, unicodeBuffer, 20, tmpValue); status = IopOpenRegistryKey (&handle, keyHandle, &unicodeName, KEY_ALL_ACCESS, FALSE ); } Instance = tmpValue; } // // We need to create the new instance key if we can come here... // PiUlongToInstanceKeyUnicodeString(&unicodeName, unicodeBuffer, 20, Instance); status = IopOpenRegistryKeyPersist ( &handle, keyHandle, &unicodeName, KEY_ALL_ACCESS, TRUE, NULL ); NtClose(keyHandle); PNP_ASSERT(NT_SUCCESS(status), "IopInitRegistryNode: Fail to create new key."); if (!NT_SUCCESS(status)) { goto init_Exit; } // // Newly created key -- // // Create all the default value entry for the newly created key. // Service = #if 0 // NewInstance = 1 #endif // FoundAtEnum = 1 // Configuration = // InterfaceType = InterfaceType // SystemBusNumber = BusNumber // BusDataType = BusDataType // #if 0 PiWstrToUnicodeString(&unicodeValueName, REGSTR_VALUE_NEWINSTANCE); tmpValue = 1; NtSetValueKey( handle, &unicodeValueName, TITLE_INDEX_VALUE, REG_DWORD, &tmpValue, sizeof(tmpValue) ); #endif PiWstrToUnicodeString(&unicodeValueName, REGSTR_VALUE_FOUNDATENUM); tmpValue = 1; NtSetValueKey( handle, &unicodeValueName, TITLE_INDEX_VALUE, REG_DWORD, &tmpValue, sizeof(tmpValue) ); #if 0 // // SystemBusNumber, InterfaceType and BusDataType are for Bus // devices only. For ntdetect/arc detected devices, we don't set // up bus devices. // PiWstrToUnicodeString(&unicodeValueName, REGSTR_VALUE_SYSTEMBUSNUMBER); tmpValue = BusNumber; NtSetValueKey( handle, &unicodeValueName, TITLE_INDEX_VALUE, REG_DWORD, &tmpValue, sizeof(tmpValue) ); PiWstrToUnicodeString(&unicodeValueName, REGSTR_VALUE_INTERFACETYPE); tmpValue = InterfaceType; NtSetValueKey( handle, &unicodeValueName, TITLE_INDEX_VALUE, REG_DWORD, &tmpValue, sizeof(tmpValue) ); PiWstrToUnicodeString(&unicodeValueName, REGSTR_VALUE_BUSDATATYPE); tmpValue = BusDataType; NtSetValueKey( handle, &unicodeValueName, TITLE_INDEX_VALUE, REG_DWORD, &tmpValue, sizeof(tmpValue) ); #endif PiWstrToUnicodeString(&unicodeValueName, REGSTR_VALUE_CONFIGURATION); NtSetValueKey( handle, &unicodeValueName, TITLE_INDEX_VALUE, REG_RESOURCE_LIST, dataArea, dataLength ); PiWstrToUnicodeString(&unicodeValueName, REGSTR_VALUE_DETECTSIGNATURE); NtSetValueKey( handle, &unicodeValueName, TITLE_INDEX_VALUE, REG_RESOURCE_LIST, dataArea, dataLength ); NtClose(handle); } status = STATUS_SUCCESS; init_Exit0: p = WorkName->Buffer; p += WorkName->Length / sizeof(WCHAR); *p = OBJ_NAME_PATH_SEPARATOR; WorkName->Length += sizeof (WCHAR); RtlAppendStringToString((PSTRING)WorkName, (PSTRING)&unicodeName); init_Exit: if (freeDataArea) { ExFreePool(dataArea); } if (serviceInfo) { ExFreePool(serviceInfo); } return(status); } #endif // _PNP_POWER_ NTSTATUS IopInitServiceEnumList ( VOID ) /*++ Routine Description: This routine scans through System\Enum\Root subtree and assigns device instances to their corresponding Service\name\Enum\Root entries. Basically, this routine establishes the Enum branches of service list. Arguments: None. Return Value: The function value is the final status of the operation. --*/ { NTSTATUS status; HANDLE baseHandle; UNICODE_STRING workName, tmpName; // // Open System\CurrentControlSet\Enum key and call worker routine to recursively // scan through the subkeys. // status = IopOpenRegistryKeyPersist(&baseHandle, NULL, &CmRegistryMachineSystemCurrentControlSetEnumRootName, KEY_READ, TRUE, NULL ); if (NT_SUCCESS(status)) { workName.Buffer = (PWSTR)IopPnpScratchBuffer1; RtlFillMemory((PUCHAR)IopPnpScratchBuffer1, PNP_LARGE_SCRATCH_BUFFER_SIZE, 0); workName.MaximumLength = PNP_LARGE_SCRATCH_BUFFER_SIZE; workName.Length = 0; #if 1 // only look at ROOT key PiWstrToUnicodeString(&tmpName, REGSTR_KEY_ROOTENUM); RtlAppendStringToString((PSTRING)&workName, (PSTRING)&tmpName); #endif // // Enumerate all subkeys under the System\CCS\Enum\Root. // status = IopApplyFunctionToSubKeys(baseHandle, NULL, KEY_ALL_ACCESS, TRUE, #if 0 IopInitializeBusKey, #else IopInitializeDeviceKey, #endif &workName ); NtClose(baseHandle); } return status; } #if 0 BOOLEAN IopInitializeBusKey( IN HANDLE KeyHandle, IN PUNICODE_STRING KeyName, IN OUT PVOID WorkName ) /*++ Routine Description: This routine is a callback function for IopApplyFunctionToSubKeys. It is called for each subkey under HKLM\System\Enum. Arguments: KeyHandle - Supplies a handle to this key. KeyName - Supplies the name of this key. WorkName - points to the unicodestring which describes the path up to this key. Returns: TRUE to continue the enumeration. FALSE to abort it. --*/ { USHORT length; PWSTR p; PUNICODE_STRING unicodeName = WorkName; NTSTATUS status; length = unicodeName->Length; p = unicodeName->Buffer; if ( unicodeName->Length / sizeof(WCHAR) != 0) { p += unicodeName->Length / sizeof(WCHAR); *p = OBJ_NAME_PATH_SEPARATOR; unicodeName->Length += sizeof (WCHAR); } RtlAppendStringToString((PSTRING)unicodeName, (PSTRING)KeyName); // // Enumerate all subkeys under the System\Enum. // status = IopApplyFunctionToSubKeys(KeyHandle, NULL, KEY_ALL_ACCESS, TRUE, IopInitializeDeviceKey, WorkName ); unicodeName->Length = length; // Should be zero return TRUE; } #endif // 0 BOOLEAN IopInitializeDeviceKey( IN HANDLE KeyHandle, IN PUNICODE_STRING KeyName, IN OUT PVOID WorkName ) /*++ Routine Description: This routine is a callback function for IopApplyFunctionToSubKeys. It is called for each subkey under HKLM\System\CCS\Enum\BusKey. Arguments: KeyHandle - Supplies a handle to this key. KeyName - Supplies the name of this key. WorkName - points to the unicodestring which describes the path up to this key. Returns: TRUE to continue the enumeration. FALSE to abort it. --*/ { USHORT length; PWSTR p; PUNICODE_STRING unicodeName = WorkName; length = unicodeName->Length; p = unicodeName->Buffer; if ( unicodeName->Length / sizeof(WCHAR) != 0) { p += unicodeName->Length / sizeof(WCHAR); *p = OBJ_NAME_PATH_SEPARATOR; unicodeName->Length += sizeof (WCHAR); } RtlAppendStringToString((PSTRING)unicodeName, (PSTRING)KeyName); // // Enumerate all subkeys under the current device key. // IopApplyFunctionToSubKeys(KeyHandle, NULL, KEY_ALL_ACCESS, TRUE, IopInitializeDeviceInstanceKey, WorkName ); unicodeName->Length = length; return TRUE; } BOOLEAN IopInitializeDeviceInstanceKey( IN HANDLE KeyHandle, IN PUNICODE_STRING KeyName, IN OUT PVOID WorkName ) /*++ Routine Description: This routine is a callback function for IopApplyFunctionToSubKeys. It is called for each subkey under HKLM\System\Enum\BusKey\DeviceKey. Arguments: KeyHandle - Supplies a handle to this key. KeyName - Supplies the name of this key. WorkName - points to the unicodestring which describes the path up to this key. Returns: TRUE to continue the enumeration. FALSE to abort it. --*/ { UNICODE_STRING unicodeName, serviceName; PKEY_VALUE_FULL_INFORMATION keyValueInformation; NTSTATUS status; BOOLEAN duplicate = FALSE; ULONG foundAtEnum, deviceFlags, instance, tmpValue1, tmpValue2; USHORT length; PUNICODE_STRING pUnicode; // // Get the "Problem" value entry to determine what we need to do with // the device instance key. // deviceFlags = 0; status = IopGetRegistryValue ( KeyHandle, REGSTR_VALUE_PROBLEM, &keyValueInformation ); if (NT_SUCCESS(status)) { if ((keyValueInformation->Type == REG_DWORD) && (keyValueInformation->DataLength >= sizeof(ULONG))) { deviceFlags = *(PULONG)KEY_VALUE_DATA(keyValueInformation); } ExFreePool(keyValueInformation); } if (deviceFlags == CM_PROB_MOVED) { // // If the device instance was moved, we simply delete the key. // The key will be deleted once the caller close the open handle. // NtDeleteKey(KeyHandle); return TRUE; } // // The device instance key exists. We need to propagate the ConfigFlag // to problem and StatusFlags // deviceFlags = 0; status = IopGetRegistryValue(KeyHandle, REGSTR_VALUE_CONFIG_FLAGS, &keyValueInformation); if (NT_SUCCESS(status)) { if ((keyValueInformation->Type == REG_DWORD) && (keyValueInformation->DataLength >= sizeof(ULONG))) { deviceFlags = *(PULONG)KEY_VALUE_DATA(keyValueInformation); } ExFreePool(keyValueInformation); } if (deviceFlags & CONFIGFLAG_REINSTALL) { tmpValue1 = CM_PROB_REINSTALL; // Problem tmpValue2 = DN_HAS_PROBLEM; // StatusFlags } else { tmpValue1 = tmpValue2 = 0; } PiWstrToUnicodeString(&unicodeName, REGSTR_VALUE_PROBLEM); NtSetValueKey(KeyHandle, &unicodeName, TITLE_INDEX_VALUE, REG_DWORD, &tmpValue1, sizeof(tmpValue1) ); PiWstrToUnicodeString(&unicodeName, REGSTR_VALUE_STATUSFLAGS); NtSetValueKey(KeyHandle, &unicodeName, TITLE_INDEX_VALUE, REG_DWORD, &tmpValue2, sizeof(tmpValue2) ); // // Get the "DuplicateOf" value entry to determine if the device instance // should be registered. If the device instance is duplicate, We don't // add it to its service key's enum branch. // status = IopGetRegistryValue ( KeyHandle, REGSTR_VALUE_DUPLICATEOF, &keyValueInformation ); if (NT_SUCCESS(status)) { if ((keyValueInformation->Type == REG_SZ) && (keyValueInformation->DataLength > 0)) { duplicate = TRUE; } ExFreePool(keyValueInformation); } if (!duplicate) { // // Combine WorkName and KeyName to form device instance path // and register this device instance by // constructing new value entry for ServiceKeyName\Enum key. // i.e., = // pUnicode = (PUNICODE_STRING)WorkName; length = pUnicode->Length; // Save WorkName if (pUnicode->Buffer[pUnicode->Length / sizeof(WCHAR) - 1] != OBJ_NAME_PATH_SEPARATOR) { pUnicode->Buffer[pUnicode->Length / sizeof(WCHAR)] = OBJ_NAME_PATH_SEPARATOR; pUnicode->Length += 2; } RtlAppendStringToString((PSTRING)pUnicode, (PSTRING)KeyName); PpDeviceRegistration(pUnicode, TRUE); pUnicode->Length = length; // Restore WorkName } // // Get the "Service=" value entry from KeyHandle // keyValueInformation = NULL; serviceName.Length = 0; status = IopGetRegistryValue ( KeyHandle, REGSTR_VALUE_SERVICE, &keyValueInformation ); if (NT_SUCCESS(status)) { // // Append the new instance to its corresponding // Service\Name\Enum. // if ((keyValueInformation->Type == REG_SZ) && (keyValueInformation->DataLength != 0)) { // // Set up ServiceKeyName unicode string // IopRegistryDataToUnicodeString( &serviceName, (PWSTR)KEY_VALUE_DATA(keyValueInformation), keyValueInformation->DataLength ); } // // Do not Free keyValueInformation // } // // The Pnp mgr set FoundAtEnum to 0 for everything under system\ccs\enum. // For the stuff under Root we need to set them to 1 except if their // CsConfigFlags was set to CSCONFIGFLAG_DO_NOT_CREATE. // foundAtEnum = 1; status = RtlUnicodeStringToInteger(KeyName, 10, &instance); if (NT_SUCCESS(status)) { if (serviceName.Length != 0) { status = IopGetDeviceInstanceCsConfigFlags( &serviceName, instance, &deviceFlags ); if (NT_SUCCESS(status) && (deviceFlags & CSCONFIGFLAG_DO_NOT_CREATE)) { foundAtEnum = 0; } } } if (keyValueInformation) { ExFreePool(keyValueInformation); } PiWstrToUnicodeString(&unicodeName, REGSTR_VALUE_FOUNDATENUM); NtSetValueKey(KeyHandle, &unicodeName, TITLE_INDEX_VALUE, REG_DWORD, &foundAtEnum, sizeof(foundAtEnum) ); // // Clean up "NtLogicalDevicePaths=" and "NtPhysicalDevicePaths=" of this key // PiWstrToUnicodeString(&unicodeName, REGSTR_VALUE_NT_PHYSICAL_DEVICE_PATHS); NtDeleteValueKey(KeyHandle, &unicodeName); PiWstrToUnicodeString(&unicodeName, REGSTR_VALUE_NT_LOGICAL_DEVICE_PATHS); NtDeleteValueKey(KeyHandle, &unicodeName); return TRUE; } NTSTATUS IopInitializePlugPlayServices( IN PLOADER_PARAMETER_BLOCK LoaderBlock ) /*++ Routine Description: This routine initializes kernel mode Plug and Play services. Arguments: LoaderBlock - supplies a pointer to the LoaderBlock passed in from the OS Loader. Returns: NTSTATUS code for sucess or reason of failure. --*/ { NTSTATUS status; HANDLE hTreeHandle, parentHandle, handle; UNICODE_STRING unicodeName; ULONG foundAtEnum; // // Allocate two one-page scratch buffers to be used by our // initialization code. This avoids constant pool allocations. // IopPnpScratchBuffer1 = ExAllocatePool(PagedPool, PNP_LARGE_SCRATCH_BUFFER_SIZE); if (!IopPnpScratchBuffer1) { return STATUS_INSUFFICIENT_RESOURCES; } IopPnpScratchBuffer2 = ExAllocatePool(PagedPool, PNP_LARGE_SCRATCH_BUFFER_SIZE); if (!IopPnpScratchBuffer2) { ExFreePool(IopPnpScratchBuffer1); return STATUS_INSUFFICIENT_RESOURCES; } // // Next open/create System\CurrentControlSet\Enum\Root key. // status = IopOpenRegistryKey ( &parentHandle, NULL, &CmRegistryMachineSystemCurrentControlSet, KEY_ALL_ACCESS, FALSE ); if (!NT_SUCCESS(status)) { goto init_Exit; } PiWstrToUnicodeString(&unicodeName, REGSTR_KEY_ENUM); status = IopOpenRegistryKeyPersist ( &handle, parentHandle, &unicodeName, KEY_ALL_ACCESS, TRUE, NULL ); NtClose(parentHandle); if (!NT_SUCCESS(status)) { goto init_Exit; } parentHandle = handle; PiWstrToUnicodeString(&unicodeName, REGSTR_KEY_ROOTENUM); status = IopOpenRegistryKeyPersist ( &handle, parentHandle, &unicodeName, KEY_ALL_ACCESS, TRUE, NULL ); NtClose(parentHandle); if (!NT_SUCCESS(status)) { goto init_Exit; } NtClose(handle); #if _PNP_POWER_ // // Convert Hardware/Firmware tree to Pnp required format. // status = IopInitializeHardwareConfiguration(LoaderBlock); if (!NT_SUCCESS(status)) { goto init_Exit; } #endif // _PNP_POWER_ // // Initialize the FoundAtEnum= value entry to 1 for HTREE\ROOT\0 // status = IopOpenRegistryKey(&handle, NULL, &CmRegistryMachineSystemCurrentControlSetEnumName, KEY_ALL_ACCESS, FALSE ); if (NT_SUCCESS(status)) { PiWstrToUnicodeString(&unicodeName, REGSTR_VALUE_HTREE_ROOT_0); status = IopOpenRegistryKeyPersist(&hTreeHandle, handle, &unicodeName, KEY_ALL_ACCESS, TRUE, NULL ); NtClose(handle); if (NT_SUCCESS(status)) { PiWstrToUnicodeString(&unicodeName, REGSTR_VALUE_FOUNDATENUM); foundAtEnum = 1; NtSetValueKey(hTreeHandle, &unicodeName, TITLE_INDEX_VALUE, REG_DWORD, &foundAtEnum, sizeof(foundAtEnum) ); NtClose(hTreeHandle); } } // // Set up Enum subkey for service list. // status = IopInitServiceEnumList(); init_Exit: // // Free our scratch buffers and exit. // ExFreePool(IopPnpScratchBuffer1); ExFreePool(IopPnpScratchBuffer2); return status; }