/*++ Copyright (c) 1991 Microsoft Corporation Module Name: misc.c Abstract: This file contains pnp isa bus extender support routines. Author: Shie-Lin Tzong (shielint) 27-Jusly-1995 Environment: Kernel mode only. Revision History: --*/ #include "busp.h" #include "pnpisa.h" #ifdef ALLOC_PRAGMA #pragma alloc_text(INIT,PipDecompressEisaId) #pragma alloc_text(INIT,PipOpenRegistryKey) #pragma alloc_text(INIT,PipOpenRegistryKeyPersist) #pragma alloc_text(INIT,PipGetRegistryValue) #pragma alloc_text(INIT,PipOpenCurrentHwProfileDeviceInstanceKey) #pragma alloc_text(INIT,PipGetDeviceInstanceCsConfigFlags) #pragma alloc_text(INIT,PipRemoveStringFromValueKey) #pragma alloc_text(INIT,PipAppendStringToValueKey) #pragma alloc_text(INIT,PipServiceInstanceToDeviceInstance) #pragma alloc_text(INIT,PipLogError) #pragma alloc_text(INIT,PipApplyFunctionToSubKeys) #if DBG #pragma alloc_text(INIT,PipDebugPrint) #pragma alloc_text(INIT,PipDumpIoResourceDescriptor) #pragma alloc_text(INIT,PipDumpIoResourceList) #pragma alloc_text(INIT,PipDumpCmResourceDescriptor) #pragma alloc_text(INIT,PipDumpCmResourceList) #endif #endif VOID PipDecompressEisaId( IN ULONG CompressedId, IN PUCHAR EisaId ) /*++ Routine Description: This routine decompressed compressed Eisa Id and returns the Id to caller specified character buffer. Arguments: CompressedId - supplies the compressed Eisa Id. EisaId - supplies a 8-char buffer to receive the decompressed Eisa Id. Return Value: None. --*/ { USHORT c1, c2; LONG i; PAGED_CODE(); CompressedId &= 0xffffff7f; // remove the reserved bit (bit 7 of byte 0) c1 = c2 = (USHORT)CompressedId; c1 = (c1 & 0xff) << 8; c2 = (c2 & 0xff00) >> 8; c1 |= c2; for (i = 2; i >= 0; i--) { *(EisaId + i) = (UCHAR)(c1 & 0x1f) + 0x40; c1 >>= 5; } EisaId += 3; c1 = c2 = (USHORT)(CompressedId >> 16); c1 = (c1 & 0xff) << 8; c2 = (c2 & 0xff00) >> 8; c1 |= c2; sprintf (EisaId, "%04x", c1); } NTSTATUS PipOpenRegistryKey( OUT PHANDLE Handle, IN HANDLE BaseHandle OPTIONAL, IN PUNICODE_STRING KeyName, IN ACCESS_MASK DesiredAccess, IN BOOLEAN Create ) /*++ Routine Description: Opens or creates a VOLATILE registry key using the name passed in based at the BaseHandle node. Arguments: Handle - Pointer to the handle which will contain the registry key that was opened. BaseHandle - Handle to the base path from which the key must be opened. KeyName - Name of the Key that must be opened/created. 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: The function value is the final status of the operation. --*/ { OBJECT_ATTRIBUTES objectAttributes; ULONG disposition; PAGED_CODE(); // // Initialize the object for the key. // InitializeObjectAttributes( &objectAttributes, KeyName, OBJ_CASE_INSENSITIVE, BaseHandle, (PSECURITY_DESCRIPTOR) NULL ); // // Create the key or open it, as appropriate based on the caller's // wishes. // if (Create) { return ZwCreateKey( Handle, DesiredAccess, &objectAttributes, 0, (PUNICODE_STRING) NULL, REG_OPTION_VOLATILE, &disposition ); } else { return ZwOpenKey( Handle, DesiredAccess, &objectAttributes ); } } NTSTATUS PipOpenRegistryKeyPersist( OUT PHANDLE Handle, IN HANDLE BaseHandle OPTIONAL, IN PUNICODE_STRING KeyName, IN ACCESS_MASK DesiredAccess, IN BOOLEAN Create, OUT PULONG Disposition OPTIONAL ) /*++ Routine Description: Opens or creates a PERSIST (non-volatile) 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. Create - Determines if the key is to be created if it does not exist. 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, BaseHandle, (PSECURITY_DESCRIPTOR) NULL ); if(Create) { // // 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, REG_OPTION_NON_VOLATILE, &disposition ); if(!((status == STATUS_OBJECT_NAME_NOT_FOUND) && ARGUMENT_PRESENT(BaseHandle))) { // // Then either we succeeded, or failed, but there's nothing we can do // about it. In either case, prepare to return. // goto PrepareForReturn; } } else { // // Simply attempt to open the path, as specified. // return ZwOpenKey(Handle, DesiredAccess, &objectAttributes ); } // // 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 = (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, handles[baseHandleIndex], (PSECURITY_DESCRIPTOR) NULL ); status = ZwCreateKey(&(handles[keyHandleIndex]), DesiredAccess, &objectAttributes, 0, (PUNICODE_STRING) NULL, REG_OPTION_NON_VOLATILE, &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]); } PrepareForReturn: if (NT_SUCCESS(status)) { *Handle = handles[keyHandleIndex]; if(ARGUMENT_PRESENT(Disposition)) { *Disposition = disposition; } } return status; } NTSTATUS PipGetRegistryValue( IN HANDLE KeyHandle, IN PWSTR ValueName, OUT PKEY_VALUE_FULL_INFORMATION *Information ) /*++ Routine Description: This routine is invoked to retrieve the data for a registry key's value. This is done by querying the value of the key with a zero-length buffer to determine the size of the value, and then allocating a buffer and actually querying the value into the buffer. It is the responsibility of the caller to free the buffer. Arguments: KeyHandle - Supplies the key handle whose value is to be queried ValueName - Supplies the null-terminated Unicode name of the value. Information - Returns a pointer to the allocated data buffer. Return Value: The function value is the final status of the query operation. --*/ { UNICODE_STRING unicodeString; NTSTATUS status; PKEY_VALUE_FULL_INFORMATION infoBuffer; ULONG keyValueLength; PAGED_CODE(); *Information = NULL; RtlInitUnicodeString( &unicodeString, ValueName ); // // Figure out how big the data value is so that a buffer of the // appropriate size can be allocated. // status = ZwQueryValueKey( KeyHandle, &unicodeString, KeyValueFullInformation, (PVOID) NULL, 0, &keyValueLength ); if (status != STATUS_BUFFER_OVERFLOW && status != STATUS_BUFFER_TOO_SMALL) { return status; } // // Allocate a buffer large enough to contain the entire key data value. // infoBuffer = ExAllocatePool( NonPagedPool, keyValueLength ); if (!infoBuffer) { return STATUS_INSUFFICIENT_RESOURCES; } // // Query the data for the key value. // status = ZwQueryValueKey( KeyHandle, &unicodeString, KeyValueFullInformation, infoBuffer, keyValueLength, &keyValueLength ); if (!NT_SUCCESS( status )) { ExFreePool( infoBuffer ); return status; } // // Everything worked, so simply return the address of the allocated // buffer to the caller, who is now responsible for freeing it. // *Information = infoBuffer; return STATUS_SUCCESS; } NTSTATUS PipOpenCurrentHwProfileDeviceInstanceKey( OUT PHANDLE Handle, IN PUNICODE_STRING DeviceInstanceName, IN ACCESS_MASK DesiredAccess ) /*++ 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 unicodeString; HANDLE profileEnumHandle; // // See if we can open the device instance key of current hardware profile // RtlInitUnicodeString ( &unicodeString, L"\\REGISTRY\\MACHINE\\SYSTEM\\CURRENTCONTROLSET\\HARDWARE PROFILES\\CURRENT\\SYSTEM\\CURRENTCONTROLSET\\ENUM" ); status = PipOpenRegistryKey(&profileEnumHandle, NULL, &unicodeString, KEY_READ, FALSE ); if (NT_SUCCESS(status)) { status = PipOpenRegistryKey(Handle, profileEnumHandle, DeviceInstanceName, DesiredAccess, FALSE ); ZwClose(profileEnumHandle); } return status; } NTSTATUS PipGetDeviceInstanceCsConfigFlags( IN PUNICODE_STRING DeviceInstance, 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; *CsConfigFlags = 0; status = PipOpenCurrentHwProfileDeviceInstanceKey(&handle, DeviceInstance, KEY_READ ); if(NT_SUCCESS(status)) { status = PipGetRegistryValue(handle, L"CsConfigFlags", &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 PipRemoveStringFromValueKey ( 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 = PipGetRegistryValue(Handle, ValueName, &keyValueInformation); if (!NT_SUCCESS( status )) { return status; } else if ((keyValueInformation->Type != REG_MULTI_SZ) || (keyValueInformation->DataLength == 0)) { ExFreePool(keyValueInformation); return (keyValueInformation->Type == REG_MULTI_SZ) ? STATUS_SUCCESS : STATUS_INVALID_PARAMETER; } // // 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 PipAppendStringToValueKey ( 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 = PipGetRegistryValue(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)) { 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; } NTSTATUS PipServiceInstanceToDeviceInstance ( IN PUNICODE_STRING RegistryPath, 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: RegistryPath - Supplies the name of the service entry that controls the device instance. This is the registry path passed to DriverEntry. 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, unicodeString; NTSTATUS status; HANDLE handle, handlex; PKEY_VALUE_FULL_INFORMATION keyValueInformation; PWSTR buffer; // // Open registry ServiceKeyName\Enum branch // status = PipOpenRegistryKey(&handle, NULL, RegistryPath, KEY_ALL_ACCESS, FALSE ); if (!NT_SUCCESS(status)) { DebugPrint((DEBUG_MESSAGE, "PnPIsa: Unable to open Service RegistryPath\n")); return status; } RtlInitUnicodeString(&unicodeKeyName, L"ENUM"); status = PipOpenRegistryKey(&handlex, handle, &unicodeKeyName, KEY_READ, FALSE ); ZwClose(handle); 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, L"%u", ServiceInstanceOrdinal); status = PipGetRegistryValue ( handlex, unicodeBuffer, &keyValueInformation ); ZwClose(handlex); if (!NT_SUCCESS( status )) { return status; } else { if (keyValueInformation->Type == REG_SZ) { unicodeKeyName.Buffer = (PWSTR)KEY_VALUE_DATA(keyValueInformation); unicodeKeyName.MaximumLength = (USHORT)keyValueInformation->DataLength; unicodeKeyName.Length = unicodeKeyName.MaximumLength - sizeof(UNICODE_NULL); 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)) { RtlInitUnicodeString(&unicodeString, L"\\REGISTRY\\MACHINE\\SYSTEM\\CURRENTCONTROLSET\\ENUM"); status = PipOpenRegistryKey(&handle, NULL, &unicodeString, KEY_READ, FALSE ); if (NT_SUCCESS( status )) { status = PipOpenRegistryKey (DeviceInstanceHandle, handle, &unicodeKeyName, DesiredAccess, FALSE ); 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)) { buffer = (PWSTR)ExAllocatePool(PagedPool, unicodeKeyName.MaximumLength); if (!buffer) { if(ARGUMENT_PRESENT(DeviceInstanceHandle)) { ZwClose(*DeviceInstanceHandle); } status = STATUS_INSUFFICIENT_RESOURCES; } else { RtlMoveMemory(buffer, unicodeKeyName.Buffer, unicodeKeyName.MaximumLength); DeviceInstanceRegistryPath->Buffer = buffer; DeviceInstanceRegistryPath->Length = unicodeKeyName.Length; DeviceInstanceRegistryPath->MaximumLength = unicodeKeyName.MaximumLength; } } PrepareForReturn: ExFreePool(keyValueInformation); return status; } VOID PipLogError( IN NTSTATUS ErrorCode, IN ULONG UniqueErrorValue, IN NTSTATUS FinalStatus, IN PULONG DumpData, IN ULONG DumpCount, IN USHORT StringLength, IN PWCHAR String ) /*++ Routine Description: This routine contains common code to write an error log entry. It is called from other routines to avoid duplication of code. This routine only allows caller to supply one insertion string to the error log. Arguments: ErrorCode - The error code for the error log packet. UniqueErrorValue - The unique error value for the error log packet. FinalStatus - The final status of the operation for the error log packet. DumpData - Pointer to an array of dump data for the error log packet. DumpCount - The number of entries in the dump data array. StringLength - The length of insertion string *NOT* including the NULL terminater. String - The pointer to the insertion string Return Value: None. --*/ { PIO_ERROR_LOG_PACKET errorLogEntry; ULONG i, size; PUCHAR p; size = sizeof(IO_ERROR_LOG_PACKET) + DumpCount * sizeof(ULONG) + StringLength + sizeof(UNICODE_NULL) - sizeof(ULONG); errorLogEntry = (PIO_ERROR_LOG_PACKET) IoAllocateErrorLogEntry( PipDriverObject, (UCHAR) size ); if (errorLogEntry != NULL) { RtlZeroMemory(errorLogEntry, size); errorLogEntry->ErrorCode = ErrorCode; errorLogEntry->DumpDataSize = (USHORT) (DumpCount * sizeof(ULONG)); errorLogEntry->UniqueErrorValue = UniqueErrorValue; errorLogEntry->FinalStatus = FinalStatus; for (i = 0; i < DumpCount; i++) errorLogEntry->DumpData[i] = DumpData[i]; if (String) { errorLogEntry->NumberOfStrings = 1; errorLogEntry->StringOffset = sizeof(IO_ERROR_LOG_PACKET) + DumpCount * sizeof(ULONG) - sizeof(ULONG); p= (PUCHAR)errorLogEntry + errorLogEntry->StringOffset; RtlMoveMemory(p, String, StringLength); } IoWriteErrorLogEntry(errorLogEntry); } } NTSTATUS PipApplyFunctionToSubKeys( IN HANDLE BaseHandle OPTIONAL, IN PUNICODE_STRING KeyName OPTIONAL, IN ACCESS_MASK DesiredAccess, IN BOOLEAN IgnoreNonCriticalErrors, IN PPIP_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. 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 subkey cannot be opened for the desired access. 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 indicate the Subkey has been deleted or FALSE. 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, keyDeleted; 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 = PipOpenRegistryKey(&Handle, BaseHandle, KeyName, KEY_READ, FALSE ); 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) { keyDeleted = FALSE; 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(IgnoreNonCriticalErrors) { 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 = PipOpenRegistryKey(&SubKeyHandle, Handle, &SubKeyName, DesiredAccess, FALSE ); if(!NT_SUCCESS(Status)) { // // This is a non-critical error. // if(IgnoreNonCriticalErrors) { goto ContinueWithNextSubKey; } else { break; } } } // // Invoke the supplied callback function for this subkey. // keyDeleted = SubKeyCallbackRoutine(SubKeyHandle, &SubKeyName, Context); if(DesiredAccess) { ZwClose(SubKeyHandle); } ContinueWithNextSubKey: if (!keyDeleted) { i++; } } if(KeyInformation) { ExFreePool(KeyInformation); } if(CloseHandle) { ZwClose(Handle); } return Status; } #if DBG VOID PipDebugPrint ( ULONG Level, PCCHAR DebugMessage, ... ) /*++ Routine Description: This routine displays debugging message or causes a break. Arguments: Level - supplies debugging levelcode. DEBUG_MESSAGE - displays message only. DEBUG_BREAK - displays message and break. DebugMessage - supplies a pointer to the debugging message. Return Value: None. --*/ { UCHAR Buffer[256]; va_list ap; va_start(ap, DebugMessage); vsprintf(Buffer, DebugMessage, ap); DbgPrint(Buffer); if (Level == DEBUG_BREAK) { DbgBreakPoint(); } va_end(ap); } VOID PipDumpIoResourceDescriptor ( IN PUCHAR Indent, IN PIO_RESOURCE_DESCRIPTOR Desc ) /*++ Routine Description: This routine processes a IO_RESOURCE_DESCRIPTOR and displays it. Arguments: Indent - # char of indentation. Desc - supplies a pointer to the IO_RESOURCE_DESCRIPTOR to be displayed. Return Value: None. --*/ { UCHAR c = ' '; if (Desc->Option == IO_RESOURCE_ALTERNATIVE) { c = 'A'; } else if (Desc->Option == IO_RESOURCE_PREFERRED) { c = 'P'; } switch (Desc->Type) { case CmResourceTypePort: DbgPrint ("%sIO %c Min: %x:%08x, Max: %x:%08x, Algn: %x, Len %x\n", Indent, c, Desc->u.Port.MinimumAddress.HighPart, Desc->u.Port.MinimumAddress.LowPart, Desc->u.Port.MaximumAddress.HighPart, Desc->u.Port.MaximumAddress.LowPart, Desc->u.Port.Alignment, Desc->u.Port.Length ); break; case CmResourceTypeMemory: DbgPrint ("%sMEM %c Min: %x:%08x, Max: %x:%08x, Algn: %x, Len %x\n", Indent, c, Desc->u.Memory.MinimumAddress.HighPart, Desc->u.Memory.MinimumAddress.LowPart, Desc->u.Memory.MaximumAddress.HighPart, Desc->u.Memory.MaximumAddress.LowPart, Desc->u.Memory.Alignment, Desc->u.Memory.Length ); break; case CmResourceTypeInterrupt: DbgPrint ("%sINT %c Min: %x, Max: %x\n", Indent, c, Desc->u.Interrupt.MinimumVector, Desc->u.Interrupt.MaximumVector ); break; case CmResourceTypeDma: DbgPrint ("%sDMA %c Min: %x, Max: %x\n", Indent, c, Desc->u.Dma.MinimumChannel, Desc->u.Dma.MaximumChannel ); break; } } VOID PipDumpIoResourceList ( IN PIO_RESOURCE_REQUIREMENTS_LIST IoList ) /*++ Routine Description: This routine displays Io resource requirements list. Arguments: IoList - supplies a pointer to the Io resource requirements list to be displayed. Return Value: None. --*/ { PIO_RESOURCE_LIST resList; PIO_RESOURCE_DESCRIPTOR resDesc; ULONG listCount, count, i, j; if (IoList == NULL) { return; } DbgPrint("Pnp Bios IO Resource Requirements List for Slot %x -\n", IoList->SlotNumber); DbgPrint(" List Count = %x, Bus Number = %x\n", IoList->AlternativeLists, IoList->BusNumber); listCount = IoList->AlternativeLists; resList = &IoList->List[0]; for (i = 0; i < listCount; i++) { DbgPrint(" Version = %x, Revision = %x, Desc count = %x\n", resList->Version, resList->Revision, resList->Count); resDesc = &resList->Descriptors[0]; count = resList->Count; for (j = 0; j < count; j++) { PipDumpIoResourceDescriptor(" ", resDesc); resDesc++; } resList = (PIO_RESOURCE_LIST) resDesc; } } VOID PipDumpCmResourceDescriptor ( IN PUCHAR Indent, IN PCM_PARTIAL_RESOURCE_DESCRIPTOR Desc ) /*++ Routine Description: This routine processes a IO_RESOURCE_DESCRIPTOR and displays it. Arguments: Indent - # char of indentation. Desc - supplies a pointer to the IO_RESOURCE_DESCRIPTOR to be displayed. Return Value: None. --*/ { switch (Desc->Type) { case CmResourceTypePort: DbgPrint ("%sIO Start: %x:%08x, Length: %x\n", Indent, Desc->u.Port.Start.HighPart, Desc->u.Port.Start.LowPart, Desc->u.Port.Length ); break; case CmResourceTypeMemory: DbgPrint ("%sMEM Start: %x:%08x, Length: %x\n", Indent, Desc->u.Memory.Start.HighPart, Desc->u.Memory.Start.LowPart, Desc->u.Memory.Length ); break; case CmResourceTypeInterrupt: DbgPrint ("%sINT Level: %x, Vector: %x, Affinity: %x\n", Indent, Desc->u.Interrupt.Level, Desc->u.Interrupt.Vector, Desc->u.Interrupt.Affinity ); break; case CmResourceTypeDma: DbgPrint ("%sDMA Channel: %x, Port: %x\n", Indent, Desc->u.Dma.Channel, Desc->u.Dma.Port ); break; } } VOID PipDumpCmResourceList ( IN PCM_RESOURCE_LIST CmList ) /*++ Routine Description: This routine displays CM resource list. Arguments: CmList - supplies a pointer to CM resource list Return Value: None. --*/ { PCM_FULL_RESOURCE_DESCRIPTOR fullDesc; PCM_PARTIAL_RESOURCE_LIST partialDesc; PCM_PARTIAL_RESOURCE_DESCRIPTOR desc; ULONG count, i; if (CmList) { fullDesc = &CmList->List[0]; DbgPrint("Pnp Bios Cm Resource List -\n"); DbgPrint(" List Count = %x, Bus Number = %x\n", CmList->Count, fullDesc->BusNumber); partialDesc = &fullDesc->PartialResourceList; DbgPrint(" Version = %x, Revision = %x, Desc count = %x\n", partialDesc->Version, partialDesc->Revision, partialDesc->Count); count = partialDesc->Count; desc = &partialDesc->PartialDescriptors[0]; for (i = 0; i < count; i++) { PipDumpCmResourceDescriptor(" ", desc); desc++; } } } #endif