/*++ Copyright (c) 1996 Microsoft Corporation Module Name: regcd.c Abstract: This contains all of the registry munging code of the NT-specific side of the ACPI driver Author: Stephane Plante (splante) Environment: Kernel mode only. Revision History: 31-Mar-96 Initial Revision --*/ #include "pch.h" NTSTATUS OSOpenUnicodeHandle( PUNICODE_STRING UnicodeKey, HANDLE ParentHandle, PHANDLE ChildHandle ); #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE,OSCloseHandle) #pragma alloc_text(PAGE,OSCreateHandle) #pragma alloc_text(PAGE,OSGetRegistryValue) #pragma alloc_text(PAGE,OSOpenHandle) #pragma alloc_text(PAGE,OSOpenUnicodeHandle) #pragma alloc_text(PAGE,OSOpenLargestSubkey) #pragma alloc_text(PAGE,OSReadAcpiConfigurationData) #pragma alloc_text(PAGE,OSReadRegValue) #pragma alloc_text(PAGE,OSWriteRegValue) #endif WCHAR rgzAcpiBiosIdentifier[] = L"ACPI BIOS"; WCHAR rgzAcpiConfigurationDataIdentifier[] = L"Configuration Data"; WCHAR rgzAcpiMultiFunctionAdapterIdentifier[] = L"\\Registry\\Machine\\Hardware\\Description\\System\\MultiFunctionAdapter"; WCHAR rgzAcpiRegistryIdentifier[] = L"Identifier"; NTSTATUS OSCloseHandle( HANDLE Key ) { // // Call the function that will close the handle now... // PAGED_CODE(); return ZwClose( Key ); } NTSTATUS OSCreateHandle( PSZ KeyName, HANDLE ParentHandle, PHANDLE ChildHandle ) /*++ Routine Description: Creates a registry key for writting Arguments: KeyName - Name of the key to create ParentHandle - Handle of parent key ChildHandle - Pointer to where the handle is returned Return Value: Status of create/open --*/ { ANSI_STRING ansiKey; NTSTATUS status; OBJECT_ATTRIBUTES objectAttributes; UNICODE_STRING unicodeKey; PAGED_CODE(); ACPIDebugEnter("OSCreateHandle"); // // We need to convert the given narrow character string into unicode // RtlInitAnsiString( &ansiKey, KeyName ); status = RtlAnsiStringToUnicodeString( &unicodeKey, &ansiKey, TRUE ); if (!NT_SUCCESS(status)) { ACPIPrint( ( ACPI_PRINT_CRITICAL, "OSCreateHandle: RtlAnsiStringToUnicodeString = %#08lx\n", status ) ); return status; } // // Initialize the OBJECT Attributes to a known value // RtlZeroMemory( &objectAttributes, sizeof(OBJECT_ATTRIBUTES) ); InitializeObjectAttributes( &objectAttributes, &unicodeKey, OBJ_CASE_INSENSITIVE, ParentHandle, NULL ); // // Create the key here // *ChildHandle = 0; status = ZwCreateKey( ChildHandle, KEY_WRITE, &objectAttributes, 0, NULL, REG_OPTION_NON_VOLATILE, NULL ); // // We no longer care about the Key after this point... // RtlFreeUnicodeString( &unicodeKey ); if (!NT_SUCCESS(status)) { ACPIPrint( ( ACPI_PRINT_REGISTRY, "OSCreateHandle: ZwCreateKey = %#08lx\n", status ) ); } return status; ACPIDebugExit("OSCreateHandle"); } NTSTATUS OSGetRegistryValue( IN HANDLE ParentHandle, IN PWSTR ValueName, OUT PKEY_VALUE_PARTIAL_INFORMATION_ALIGN64 *Information ) { NTSTATUS status; PKEY_VALUE_PARTIAL_INFORMATION_ALIGN64 infoBuffer; ULONG keyValueLength; UNICODE_STRING unicodeString; PAGED_CODE(); ACPIDebugEnter("OSGetRegistryValue"); RtlInitUnicodeString( &unicodeString, ValueName ); // // Figure out how big the data value is so that we can allocate the // proper sized buffer // status = ZwQueryValueKey( ParentHandle, &unicodeString, KeyValuePartialInformationAlign64, (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 = ExAllocatePoolWithTag( NonPagedPool, keyValueLength, ACPI_STRING_POOLTAG ); if (infoBuffer == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } // // Now query the data again and this time it will work // status = ZwQueryValueKey( ParentHandle, &unicodeString, KeyValuePartialInformationAlign64, (PVOID) infoBuffer, keyValueLength, &keyValueLength ); if (!NT_SUCCESS(status)) { ExFreePool( infoBuffer ); return status; } // // Everything worked - so simply return the address of the allocated // structure buffer to the caller, who is now responsible for freeing it // *Information = infoBuffer; return STATUS_SUCCESS; ACPIDebugExit("OSGetRegistryValue"); } NTSTATUS OSOpenHandle( PSZ KeyName, HANDLE ParentHandle, PHANDLE ChildHandle ) { ANSI_STRING ansiKey; NTSTATUS status; UNICODE_STRING unicodeKey; PAGED_CODE(); ACPIDebugEnter("OSOpenHandle"); // // We need to convert the given narrow character string into unicode // RtlInitAnsiString( &ansiKey, KeyName ); status = RtlAnsiStringToUnicodeString( &unicodeKey, &ansiKey, TRUE ); if (!NT_SUCCESS(status)) { ACPIPrint( ( ACPI_PRINT_CRITICAL, "OSOpenHandle: RtlAnsiStringToUnicodeString = %#08lx\n", status ) ); return status; } status = OSOpenUnicodeHandle( &unicodeKey, ParentHandle, ChildHandle ); // // We no longer care about the Key after this point... // RtlFreeUnicodeString( &unicodeKey ); return status; ACPIDebugExit("OSOpenHandle"); } NTSTATUS OSOpenUnicodeHandle( PUNICODE_STRING UnicodeKey, HANDLE ParentHandle, PHANDLE ChildHandle ) { NTSTATUS status; OBJECT_ATTRIBUTES objectAttributes; PAGED_CODE(); // // Initialize the OBJECT Attributes to a known value // RtlZeroMemory( &objectAttributes, sizeof(OBJECT_ATTRIBUTES) ); InitializeObjectAttributes( &objectAttributes, UnicodeKey, OBJ_CASE_INSENSITIVE, ParentHandle, NULL ); // // Open the key here // status = ZwOpenKey( ChildHandle, KEY_READ, &objectAttributes ); if (!NT_SUCCESS(status)) { ACPIPrint( ( ACPI_PRINT_REGISTRY, "OSOpenUnicodeHandle: ZwOpenKey = %#08lx\n", status ) ); } return status; } NTSTATUS OSOpenLargestSubkey( HANDLE ParentHandle, PHANDLE ChildHandle, ULONG RomVersion ) /*++ Routine Description: Open the largest (numerically) subkey under the given parent key. Arguments: ParentHandle - Handle to the parent key ChildHandle - Pointer to where the handle is returned RomVersion - Minimum version number that is acceptable Return Value: Status of open --*/ { NTSTATUS status; UNICODE_STRING unicodeName; PKEY_BASIC_INFORMATION keyInformation; ULONG resultLength; ULONG i; HANDLE workingDir = NULL; HANDLE largestDir = NULL; ULONG largestRev = 0; ULONG thisRev = 0; PAGED_CODE(); ACPIDebugEnter( "OSOpenLargestSubkey" ); keyInformation = ExAllocatePoolWithTag( PagedPool, 512, ACPI_MISC_POOLTAG ); if (keyInformation == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } // // Traverse all subkeys // for (i = 0; ; i++) { // // Get a subkey // status = ZwEnumerateKey( ParentHandle, i, KeyBasicInformation, keyInformation, 512, &resultLength ); if (!NT_SUCCESS(status)) { // Fail when no more subkeys break; } // // Create a UNICODE_STRING using the counted string passed back to // us in the information structure, and convert to an integer. // unicodeName.Length = (USHORT) keyInformation->NameLength; unicodeName.MaximumLength = (USHORT) keyInformation->NameLength; unicodeName.Buffer = keyInformation->Name; RtlUnicodeStringToInteger(&unicodeName, 16, &thisRev); // // Save this one if it is the largest // if ( (workingDir == NULL) || thisRev > largestRev) { // // We'll just open the target rather than save // away the name to open later // status = OSOpenUnicodeHandle( &unicodeName, ParentHandle, &workingDir ); if ( NT_SUCCESS(status) ) { if (largestDir) { OSCloseHandle (largestDir); // Close previous } largestDir = workingDir; // Save handle largestRev = thisRev; // Save version number } } } // // Done with KeyInformation // ExFreePool( keyInformation ); // // No subkey found/opened, this is a problem // if (largestDir == NULL) { return ( NT_SUCCESS(status) ? STATUS_UNSUCCESSFUL : status ); } // // Use the subkey only if it the revision is equal or greater than the // ROM version // if (largestRev < RomVersion) { OSCloseHandle (largestDir); return STATUS_REVISION_MISMATCH; } *ChildHandle = largestDir; // Return handle to subkey return STATUS_SUCCESS; ACPIDebugExit( "OSOpenLargestSubkey" ); } NTSTATUS OSReadAcpiConfigurationData( PKEY_VALUE_PARTIAL_INFORMATION_ALIGN64 *KeyInfo ) /*++ Routine Description: This very specialized routine looks in the Registry and tries to find the information that was written there by ntdetect. It returns a pointer to the keyvalue that will then be processed by the caller to find the pointer to the RSDT and the E820 memory table Arguments: KeyInfo - Where to store the pointer to the information from the registry Return Value: NTSTATUS --*/ { BOOLEAN sameId; HANDLE functionHandle; HANDLE multiHandle; NTSTATUS status; ULONG i; ULONG length; UNICODE_STRING biosId; UNICODE_STRING functionId; UNICODE_STRING registryId; WCHAR wbuffer[4]; ASSERT( KeyInfo != NULL ); if (KeyInfo == NULL) { return STATUS_INVALID_PARAMETER; } *KeyInfo = NULL; // // Open the handle for the MultiFunctionAdapter // RtlInitUnicodeString( &functionId, rgzAcpiMultiFunctionAdapterIdentifier ); status = OSOpenUnicodeHandle( &functionId, NULL, &multiHandle ); if (!NT_SUCCESS(status)) { ACPIPrint( ( ACPI_PRINT_CRITICAL, "OSReadAcpiConfigurationData: Cannot open MFA Handle = %08lx\n", status ) ); ACPIBreakPoint(); return status; } // // Initialize the unicode strings we will need shortly // RtlInitUnicodeString( &biosId, rgzAcpiBiosIdentifier ); functionId.Buffer = wbuffer; functionId.MaximumLength = sizeof(wbuffer); // // Loop until we run out of children in the MFA node // for (i = 0; i < 999; i++) { // // Open the subkey // RtlIntegerToUnicodeString(i, 10, &functionId ); status = OSOpenUnicodeHandle( &functionId, multiHandle, &functionHandle ); if (!NT_SUCCESS(status)) { ACPIPrint( ( ACPI_PRINT_CRITICAL, "OSReadAcpiConfigurationData: Cannot open MFA %ws = %08lx\n", functionId.Buffer, status ) ); ACPIBreakPoint(); OSCloseHandle( multiHandle ); return status; } // // Check the identifier to see if this is an ACPI BIOS entry // status = OSGetRegistryValue( functionHandle, rgzAcpiRegistryIdentifier, KeyInfo ); if (!NT_SUCCESS(status)) { OSCloseHandle( functionHandle ); continue; } // // Convert the key information into a unicode string // registryId.Buffer = (PWSTR) ( (PUCHAR) (*KeyInfo)->Data); registryId.MaximumLength = (USHORT) ( (*KeyInfo)->DataLength ); length = ( (*KeyInfo)->DataLength ) / sizeof(WCHAR); // // Determine the real length of the ID string // while (length) { if (registryId.Buffer[length-1] == UNICODE_NULL) { length--; continue; } break; } registryId.Length = (USHORT) ( length * sizeof(WCHAR) ); // // Compare the bios string and the registry string // sameId = RtlEqualUnicodeString( &biosId, ®istryId, TRUE ); // // We are done with this information at this point // ExFreePool( *KeyInfo ); // // Did the two strings match // if (sameId == FALSE) { OSCloseHandle( functionHandle ); continue; } // // Read the configuration data from the entry // status = OSGetRegistryValue( functionHandle, rgzAcpiConfigurationDataIdentifier, KeyInfo ); // // We are done with the function handle, no matter what // OSCloseHandle( functionHandle ); // // Did we read what we wanted to? // if (!NT_SUCCESS(status)) { continue; } // // At this point, we don't need the bus handle // OSCloseHandle( multiHandle ); return STATUS_SUCCESS; } // // If we got here, then there is nothing to return // ACPIPrint( ( ACPI_PRINT_CRITICAL, "OSReadAcpiConfigurationData - Could not find entry\n" ) ); ACPIBreakPoint(); return STATUS_OBJECT_NAME_NOT_FOUND; } NTSTATUS OSReadRegValue( PSZ ValueName, HANDLE ParentHandle, PUCHAR Buffer, PULONG BufferSize ) /*++ Routine Description: This function is responsible for returning the data in the specified value over to the calling function. Arguments: ValueName - What we are looking for ParentHandle - Our Parent Handle Buffer - Where to store the data BufferSize - Length of the buffer and where to store the # read Return Value: NTSTATUS --*/ { ANSI_STRING ansiValue; HANDLE localHandle = NULL; NTSTATUS status; PKEY_VALUE_PARTIAL_INFORMATION_ALIGN64 data = NULL; ULONG currentLength = 0; ULONG desiredLength = 0; UNICODE_STRING unicodeValue; PAGED_CODE(); ACPIDebugEnter( "OSReadRegValue" ); // // First, try to open a handle to the key // if (ParentHandle == NULL) { status= OSOpenHandle( ACPI_PARAMETERS_REGISTRY_KEY, 0, &localHandle ); if (!NT_SUCCESS(status) || localHandle == NULL) { ACPIPrint( ( ACPI_PRINT_WARNING, "OSReadRegValue: OSOpenHandle = %#08lx\n", status ) ); return (ULONG) status; } } else { localHandle = ParentHandle; } // // Now that we have an open handle, we can convert the value to a // unicode string and query it // RtlInitAnsiString( &ansiValue, ValueName ); status = RtlAnsiStringToUnicodeString( &unicodeValue, &ansiValue, TRUE ); if (!NT_SUCCESS(status)) { ACPIPrint( ( ACPI_PRINT_CRITICAL, "OSReadRegValue: RtlAnsiStringToUnicodeString = %#08lx\n", status ) ); if (ParentHandle == NULL) { OSCloseHandle( localHandle ); } return status; } // // Next, we need to figure out how much memore we need to hold the // entire key // status = ZwQueryValueKey( localHandle, &unicodeValue, KeyValuePartialInformationAlign64, data, currentLength, &desiredLength ); // // We expect this to fail with STATUS_BUFFER_OVERFLOW, so lets make // sure that this is what happened // if (status != STATUS_BUFFER_OVERFLOW && status != STATUS_BUFFER_TOO_SMALL) { ACPIPrint( ( ACPI_PRINT_WARNING, "OSReadRegValue: ZwQueryValueKey = %#08lx\n", status ) ); // // Free resources // RtlFreeUnicodeString( &unicodeValue ); if (ParentHandle == NULL) { OSCloseHandle( localHandle ); } return (NT_SUCCESS(status) ? STATUS_UNSUCCESSFUL : status); } while (status == STATUS_BUFFER_OVERFLOW || status == STATUS_BUFFER_TOO_SMALL) { // // Set the new currentLength // currentLength = desiredLength; // // Allocate a correctly sized buffer // data = ExAllocatePoolWithTag( PagedPool, currentLength, ACPI_MISC_POOLTAG ); if (data == NULL) { ACPIPrint( ( ACPI_PRINT_CRITICAL, "OSReadRegValue: ExAllocatePool(NonPagedPool,%#08lx) failed\n", desiredLength ) ); RtlFreeUnicodeString( &unicodeValue ); if (ParentHandle == NULL) { OSCloseHandle( localHandle ); } return STATUS_INSUFFICIENT_RESOURCES; } // // Actually try to read the entire key now // status = ZwQueryValueKey( localHandle, &unicodeValue, KeyValuePartialInformationAlign64, data, currentLength, &desiredLength ); // // If we don't have enough resources, lets just loop again // if (status == STATUS_BUFFER_OVERFLOW || status == STATUS_BUFFER_TOO_SMALL) { // // Make sure to free the old buffer -- otherwise, we could // have a major memory leak // ExFreePool( data ); continue; } if (!NT_SUCCESS(status)) { ACPIPrint( ( ACPI_PRINT_FAILURE, "OSReadRegValue: ZwQueryValueKey = %#08lx\n", status ) ); RtlFreeUnicodeString( &unicodeValue ); if (ParentHandle == NULL) { OSCloseHandle( localHandle ); } ExFreePool( data ); return status; } // // Done // break; } // while (status == ... // // Free Resources // RtlFreeUnicodeString( &unicodeValue ); if (ParentHandle == NULL) { OSCloseHandle( localHandle ); } // // The value read from the registry is a UNICODE Value, however // we are asked for an ANSI string. So we just work the conversion // backwards // if ( data->Type == REG_SZ || data->Type == REG_MULTI_SZ) { RtlInitUnicodeString( &unicodeValue, (PWSTR) data->Data ); status = RtlUnicodeStringToAnsiString( &ansiValue, &unicodeValue, TRUE); ExFreePool( data ); if (!NT_SUCCESS(status)) { ACPIPrint( ( ACPI_PRINT_CRITICAL, "OSReadRegValue: RtlAnsiStringToUnicodeString = %#08lx\n", status ) ); return (ULONG) status; } // // Is our buffer big enough? // if ( *BufferSize < ansiValue.MaximumLength) { ACPIPrint( ( ACPI_PRINT_WARNING, "OSReadRegValue: %#08lx < %#08lx\n", *BufferSize, ansiValue.MaximumLength ) ); RtlFreeAnsiString( &ansiValue ); return (ULONG) STATUS_BUFFER_OVERFLOW; } else { // // Set the returned size // *BufferSize = ansiValue.MaximumLength; } // // Copy the required information // RtlCopyMemory( Buffer, ansiValue.Buffer, *BufferSize); RtlFreeAnsiString( &ansiValue ); } else if ( *BufferSize >= data->DataLength) { // // Copy the memory // RtlCopyMemory( Buffer, data->Data, data->DataLength ); *BufferSize = data->DataLength; ExFreePool( data ); } else { ExFreePool( data ); return STATUS_BUFFER_OVERFLOW; } // // Done // return STATUS_SUCCESS; ACPIDebugExit( "OSReadRegValue" ); } NTSTATUS OSWriteRegValue( PSZ ValueName, HANDLE Handle, PVOID Data, ULONG DataSize ) /*++ Routine Description: Creates a value item in a registry key, and writes data to it Arguments: ValueName - Name of the value item to create Handle - Handle of the parent key Data - Raw data to be written to the value DataSize - Size of the data to write Return Value: Status of create/write --*/ { ANSI_STRING ansiKey; NTSTATUS status; OBJECT_ATTRIBUTES objectAttributes; UNICODE_STRING unicodeKey; PAGED_CODE(); ACPIDebugEnter("OSWriteRegValue"); // // We need to convert the given narrow character string into unicode // RtlInitAnsiString( &ansiKey, ValueName ); status = RtlAnsiStringToUnicodeString( &unicodeKey, &ansiKey, TRUE ); if (!NT_SUCCESS(status)) { ACPIPrint( ( ACPI_PRINT_CRITICAL, "OSWriteRegValue: RtlAnsiStringToUnicodeString = %#08lx\n", status ) ); return status; } // // Create the value // status = ZwSetValueKey( Handle, &unicodeKey, 0, REG_BINARY, Data, DataSize ); if (!NT_SUCCESS(status)) { ACPIPrint( ( ACPI_PRINT_REGISTRY, "OSRegWriteValue: ZwSetValueKey = %#08lx\n", status ) ); } // // We no longer care about the Key after this point... // RtlFreeUnicodeString( &unicodeKey ); return status; ACPIDebugExit("OSRegWriteValue"); }