/*++ Copyright (c) 1998 Microsoft Corporation Module Name: rules.c Abstract: This modules contains routines to implement rules to describe a machine. This is based on the detection code from W9x. Author: Santosh Jodh (santoshj) 08-Aug-1998 Environment: Kernel mode. --*/ #include "cmp.h" #include "stdlib.h" #include "parseini.h" #include "geninst.h" #include "rules.h" #include "acpitabl.h" #include "ntacpi.h" #define TABLE_ENTRIES_FROM_RSDT_POINTER(p) (((p)->Header.Length-min((p)->Header.Length, sizeof(DESCRIPTION_HEADER))) / 4) // Size of the ROM BIOS segment. #define SYSTEM_BIOS_LENGTH 0x10000 // PnP BIOS structure signature. #define PNPBIOS_SIGNATURE 'PnP$' typedef BOOLEAN (* PFN_RULE)(IN PVOID InfHandle, IN PCHAR Description, IN ULONG RuleIndex); typedef struct _PNP_BIOS_TABLE PNP_BIOS_TABLE, *PPNP_BIOS_TABLE; #pragma pack(push, 1) struct _PNP_BIOS_TABLE{ ULONG Signature; UCHAR Version; UCHAR Length; USHORT ControlField; UCHAR CheckSum; ULONG EventNotification; USHORT RMOffset; USHORT RMSegment; USHORT PMOffset; ULONG PMSegment; ULONG Oem; USHORT RMData; ULONG PMData; }; #pragma pack(pop) ULONG CmpComputeChecksum(IN PCHAR Address, IN ULONG Size); NTSTATUS CmpFindRSDTTable(OUT PACPI_BIOS_MULTI_NODE *Rsdt); NTSTATUS CmpGetRegistryValue(IN HANDLE KeyName, IN PWSTR ValueName, OUT PKEY_VALUE_PARTIAL_INFORMATION *Information); PDESCRIPTION_HEADER CmpFindACPITable(IN ULONG Signature, IN OUT PULONG Length); BOOLEAN CmpCheckOperator(IN PCHAR Operator, IN ULONG Lhs, IN ULONG Rhs); PVOID CmpMapPhysicalAddress(IN OUT PVOID *BaseAddress, IN ULONG Address, IN ULONG Size); BOOLEAN CmpGetInfData(IN PVOID InfHandle, IN PCHAR Section, IN ULONG KeyIndex, IN ULONG LineIndex, IN OUT PCHAR Buffer, IN OUT PULONG BufferSize); PVOID CmpFindPattern(IN PCHAR Buffer, IN ULONG BufSize, IN PCHAR Pattern, IN ULONG PatSize, IN BOOLEAN IgnoreCase, IN ULONG Step); ULONG CmpGetPnPBIOSTableAddress(VOID); BOOLEAN CmpMatchDescription(IN PVOID InfHandle, IN PCHAR Description); BOOLEAN CmpMatchDateRule(IN PVOID InfHandle, IN PCHAR Description, IN ULONG RuleIndex); BOOLEAN CmpMatchMemoryRule(IN PVOID InfHandle, IN PCHAR Description, IN ULONG RuleIndex); BOOLEAN CmpMatchSearchRule(IN PVOID InfHandle, IN PCHAR Description, IN ULONG RuleIndex); BOOLEAN CmpMatchNextMatchRule(IN PVOID InfHandle, IN PCHAR Description, IN ULONG RuleIndex); BOOLEAN CmpMatchPointerRule(IN PVOID InfHandle, IN PCHAR Description, IN ULONG RuleIndex); BOOLEAN CmpMatchOemIdRule(IN PVOID InfHandle, IN PCHAR Description, IN ULONG RuleIndex); BOOLEAN CmpMatchPModeRule(IN PVOID InfHandle, IN PCHAR Description, IN ULONG RuleIndex); BOOLEAN CmpMatchRmPmSameRule(IN PVOID InfHandle, IN PCHAR Description, IN ULONG RuleIndex); BOOLEAN CmpMatchInstallRule(IN PVOID InfHandle, IN PCHAR Description, IN ULONG RuleIndex); BOOLEAN CmpMatchAcpiOemIdRule(IN PVOID InfHandle, IN PCHAR Description, IN ULONG RuleIndex); BOOLEAN CmpMatchAcpiOemTableIdRule(IN PVOID InfHandle, IN PCHAR Description, IN ULONG RuleIndex); BOOLEAN CmpMatchAcpiOemRevisionRule(IN PVOID InfHandle, IN PCHAR Description, IN ULONG RuleIndex); BOOLEAN CmpMatchAcpiRevisionRule(IN PVOID InfHandle, IN PCHAR Description, IN ULONG RuleIndex); BOOLEAN CmpMatchAcpiCreatorRevisionRule(IN PVOID InfHandle, IN PCHAR Description, IN ULONG RuleIndex); // Number of rules currently implemented. #define NUM_OF_RULES 14 // Rule table. struct { PCHAR Name; PFN_RULE Action; } gRuleTable[NUM_OF_RULES] = { {"Date", CmpMatchDateRule}, {"Memory", CmpMatchMemoryRule}, {"Search", CmpMatchSearchRule}, {"NextMatch", CmpMatchNextMatchRule}, {"Pointer", CmpMatchPointerRule}, {"OemId", CmpMatchOemIdRule}, {"PMode", CmpMatchPModeRule}, {"RmPmSame", CmpMatchRmPmSameRule}, {"Install", CmpMatchInstallRule}, {"ACPIOemId", CmpMatchAcpiOemIdRule}, {"ACPIOemTableId", CmpMatchAcpiOemTableIdRule}, {"ACPIOemRevision", CmpMatchAcpiOemRevisionRule}, {"ACPIRevision", CmpMatchAcpiRevisionRule}, {"ACPICreatorRevision", CmpMatchAcpiCreatorRevisionRule} }; PVOID gSearchAddress = NULL; static WCHAR rgzMultiFunctionAdapter[] = L"\\Registry\\Machine\\Hardware\\Description\\System\\MultifunctionAdapter"; static WCHAR rgzAcpiConfigurationData[] = L"Configuration Data"; static WCHAR rgzAcpiIdentifier[] = L"Identifier"; static WCHAR rgzBIOSIdentifier[] = L"ACPI BIOS"; #ifdef ALLOC_PRAGMA #pragma alloc_text(INIT,CmpFindACPITable) #pragma alloc_text(INIT,CmpFindRSDTTable) #pragma alloc_text(INIT,CmpComputeChecksum) #pragma alloc_text(INIT,CmpCheckOperator) #pragma alloc_text(INIT,CmpMapPhysicalAddress) #pragma alloc_text(INIT,CmpGetInfData) #pragma alloc_text(INIT,CmpFindPattern) #pragma alloc_text(INIT,CmpGetPnPBIOSTableAddress) #pragma alloc_text(INIT,CmpMatchInfList) #pragma alloc_text(INIT,CmpMatchDescription) #pragma alloc_text(INIT,CmpMatchDateRule) #pragma alloc_text(INIT,CmpMatchMemoryRule) #pragma alloc_text(INIT,CmpMatchSearchRule) #pragma alloc_text(INIT,CmpMatchNextMatchRule) #pragma alloc_text(INIT,CmpMatchPointerRule) #pragma alloc_text(INIT,CmpMatchOemIdRule) #pragma alloc_text(INIT,CmpMatchPModeRule) #pragma alloc_text(INIT,CmpMatchRmPmSameRule) #pragma alloc_text(INIT,CmpMatchInstallRule) #pragma alloc_text(INIT,CmpMatchAcpiOemIdRule) #pragma alloc_text(INIT,CmpMatchAcpiOemTableIdRule) #pragma alloc_text(INIT,CmpMatchAcpiOemRevisionRule) #pragma alloc_text(INIT,CmpMatchAcpiRevisionRule) #pragma alloc_text(INIT,CmpMatchAcpiCreatorRevisionRule) #endif BOOLEAN CmpMatchInfList( IN PVOID InfImage, IN ULONG ImageSize, IN PCHAR Section ) /*++ Routine Description: Input Parameters: InfImage - Pointer to the inf image in memory. ImageSize - Size of the inf image. Section - Section name containing the descriptions. Description - Return Value: TRUE if the machine matches any one of the descriptions in the inf. --*/ { PCHAR computerName; ULONG i = 0; PVOID infHandle; BOOLEAN result = FALSE; infHandle = CmpOpenInfFile(InfImage, ImageSize); if (infHandle) { // Do any clean-up specified in the inf. CmpGenInstall(infHandle, "Cleanup"); // Go through each description in this section and try to match // this machine to it. while ((computerName = CmpGetSectionLineIndex(infHandle, Section, i++, 0))) { // Reset search result from previous description. gSearchAddress = NULL; // If this description matches, we are done. // NOTE: IF MORE THAN ONE DESCRIPTION MATCHES, // WE STOP AT THE FIRST ONE. if (CmpMatchDescription(infHandle, computerName)) { DbgPrint("CmpMatchInfList: Machine matches %s description!\n", computerName); result = TRUE; } } CmpCloseInfFile(infHandle); } // None of the descriptions match. return (result); } BOOLEAN CmpMatchDescription( IN PVOID InfHandle, IN PCHAR Description ) /*++ Routine Description: This routine processes all the rules in the specified description. Input Parameters: InfHandle - Handle to the inf containing the description. Description - Section name containing the rules. Return Value: TRUE iff all the rules in the description succeed. --*/ { ULONG ruleNumber; ULONG i; PCHAR ruleName; // Proceed only if the section does exist. if (CmpSearchInfSection(InfHandle, Description)) { // Go through all the rules in the description and try to match // each of them. ruleNumber = 0; while ((ruleName = CmpGetKeyName(InfHandle, Description, ruleNumber))) { // Search for the rule in our table. for ( i = 0; i < NUM_OF_RULES && _stricmp(ruleName, gRuleTable[i].Name); i++); // If we did not find the rule or the rule failed, // return failure. if ( i >= NUM_OF_RULES || !(*gRuleTable[i].Action)(InfHandle, Description, ruleNumber++)) { return (FALSE); } } // Description matches if we found at least one rule and all rules // succeeded. if (ruleNumber) { return (TRUE); } } // Description did not match. return (FALSE); } BOOLEAN CmpMatchDateRule( IN PVOID InfHandle, IN PCHAR Description, IN ULONG RuleIndex ) /*++ Routine Description: This routine checks if the machine satisfies the DATE rule. The BIOS date is stored in a standard location in the BIOS ROM at FFFF:5. Syntax - DATE=operator,month,day,year where operator [=, ==, !=, <>, <, <=, =<, >, >=, =>] Examples - date="<=",2,1,95 is TRUE if the BIOS date on this machine is less than or equal to 02/01/95. Input Parameters: InfHandle - Handle to the inf to be read. Description - Name of the section containing the rule info. RuleIndex - Line number for the rule in the description section. Return Value: TRUE if the BIOS on this machine has the specified relation with the date specified in the rule. --*/ { PCHAR op; PCHAR month; PCHAR day; PCHAR year; ULONG infDate; ULONG yr; ULONG biosDate; CHAR temp[3]; PVOID baseAddress; PCHAR address; op = CmpGetSectionLineIndex(InfHandle, Description, RuleIndex, 0); month = CmpGetSectionLineIndex(InfHandle, Description, RuleIndex, 1); day = CmpGetSectionLineIndex(InfHandle, Description, RuleIndex, 2); year = CmpGetSectionLineIndex(InfHandle, Description, RuleIndex, 3); if (op && month && day && year) { yr = strtoul(year, NULL, 16); infDate = ((yr < 0x80) ? 0x20000000 : 0x19000000) + (yr << 16) + (strtoul(month, NULL, 16) << 8) + (strtoul(day, NULL, 16)); address = CmpMapPhysicalAddress(&baseAddress, 0xFFFF5, 8); if (address) { temp[2] = '\0'; RtlCopyBytes(temp, address + 6, 2); yr = strtoul(temp, NULL, 16); biosDate = ((yr < 0x80) ? 0x20000000 : 0x19000000) + (yr << 16); RtlCopyBytes(temp, address, 2); biosDate |= (strtoul(temp, NULL, 16) << 8); RtlCopyBytes(temp, address + 3, 2); biosDate |= strtoul(temp, NULL, 16); ZwUnmapViewOfSection(NtCurrentProcess(), baseAddress); if (CmpCheckOperator(op, biosDate, infDate)) { return (TRUE); } } } return (FALSE); } BOOLEAN CmpMatchMemoryRule( IN PVOID InfHandle, IN PCHAR Description, IN ULONG RuleIndex ) /*++ Routine Description: This routine checks if the machine satisfies the MEMORY rule. Syntax - MEMORY=segment,offset,type,data where type ["S", "B"] Examples - memory=f000,e000,S,"TOSHIBA" is TRUE if the memory in this machine at physical address f000:e000 has the string "TOSHIBA". memory=ffff,5,B,01,02 is TRUE if the memory in this machine at physical memory ffff:5 has the bytes 0x01 and 0x02. Input Parameters: InfHandle - Handle to the inf to be read. Description - Name of the section containing the rule info. RuleIndex - Line number for the rule in the description section. Return Value: TRUE iff the MEMORY in this machine at the specified address contains the specified data. --*/ { BOOLEAN match = FALSE; PCHAR segment; PCHAR offset; CHAR data[MAX_DESCRIPTION_LEN + 1]; ULONG cbData; PVOID baseAddress; PCHAR address; ULONG memory; // Read in the segment and offset of the address specified. segment = CmpGetSectionLineIndex(InfHandle, Description, RuleIndex, 0); offset = CmpGetSectionLineIndex(InfHandle, Description, RuleIndex, 1); if (segment && offset) { // Get the data specified in the inf. cbData = sizeof(data); if (CmpGetInfData(InfHandle, Description, RuleIndex, 2, data, &cbData)) { memory = (strtoul(segment, NULL, 16) << 4) + strtoul(offset, NULL, 16); // Map in the physical address. address = CmpMapPhysicalAddress(&baseAddress, memory, cbData); if (address) { // Check if the inf data matches data in memory. match = (RtlCompareMemory(address, data, cbData) == cbData); // Unmap the physical address. ZwUnmapViewOfSection(NtCurrentProcess(), baseAddress); } } } return (match); } BOOLEAN CmpMatchSearchRule( IN PVOID InfHandle, IN PCHAR Description, IN ULONG RuleIndex ) /*++ Routine Description: This routine checks to see if the machine matches the SEARCH rule. Syntax - SEARCH=segment,offset,length,type,data where type ["S", "B"] Examples - search=f000,e000,7f,S,"SurePath" is TRUE if the string "SurePath" is somewhere in memory range F000:E000 to F000:E07F (inclusive). Input Parameters: InfHandle - Handle to the inf to be read. Description - Name of the section containing the rule info. RuleIndex - Line number for the rule in the description section. Return Value: TRUE iff the specified pattern is found within the specified address range. --*/ { BOOLEAN match = FALSE; PCHAR segment; PCHAR offset; PCHAR size; CHAR data[MAX_DESCRIPTION_LEN + 1]; ULONG cbData; ULONG memory; ULONG length; PVOID baseAddress; PCHAR address; segment = CmpGetSectionLineIndex(InfHandle, Description, RuleIndex, 0); offset = CmpGetSectionLineIndex(InfHandle, Description, RuleIndex, 1); size = CmpGetSectionLineIndex(InfHandle, Description, RuleIndex, 2); if (segment && offset && size) { // Get the data specified in the inf. cbData = sizeof(data); if (CmpGetInfData(InfHandle, Description, RuleIndex, 3, data, &cbData)) { memory = (strtoul(segment, NULL, 16) << 4) + strtoul(offset, NULL, 16); // Map in the physical address. length = strtoul(size, NULL, 16); address = CmpMapPhysicalAddress(&baseAddress, memory, length); if (address) { gSearchAddress = CmpFindPattern(address, length, data, cbData, FALSE, 0); if (gSearchAddress) { // If we found the pattern, compute the actual address for it. (PCHAR)gSearchAddress -= (ULONG)address; (PCHAR)gSearchAddress += memory; match = TRUE; } // Unmap the physical address. ZwUnmapViewOfSection(NtCurrentProcess(), baseAddress); } } } return (match); } BOOLEAN CmpMatchNextMatchRule( IN PVOID InfHandle, IN PCHAR Description, IN ULONG RuleIndex ) /*++ Routine Description: This routine checks to see if the machine matches the NEXTMATCH rule. Syntax - NEXTMATCH=offset,type,data where type ["S", "B"] Examples - nextmatch=f0,S,"Atlanta" is TRUE if the string "Atlanta" is at offset 0xF0 from the previous successful SEARCH or NEXTMATCH rule. Input Parameters: InfHandle - Handle to the inf to be read. Description - Name of the section containing the rule info. RuleIndex - Line number for the rule in the description section. Return Value: TRUE iff the specified pattern is found at the specified offset from the previous successful SEARCH or NEXTMATCH. --*/ { BOOLEAN match = FALSE; PCHAR offset; CHAR data[MAX_DESCRIPTION_LEN + 1]; ULONG cbData; PVOID baseAddress; PCHAR address; if (gSearchAddress) { offset = CmpGetSectionLineIndex(InfHandle, Description, RuleIndex, 0); if (offset) { // Get the data specified in the inf. cbData = sizeof(data); if (CmpGetInfData(InfHandle, Description, RuleIndex, 1, data, &cbData)) { (PCHAR)gSearchAddress += strtoul(offset, NULL, 16); // Map in the physical address. address = CmpMapPhysicalAddress(&baseAddress, (ULONG)gSearchAddress, cbData); if (address) { // Check if the inf data matches data in memory. match = (RtlCompareMemory(address, data, cbData) == cbData); // Unmap the physical address. ZwUnmapViewOfSection(NtCurrentProcess(), baseAddress); } } } } return (match); } BOOLEAN CmpMatchPointerRule( IN PVOID InfHandle, IN PCHAR Description, IN ULONG RuleIndex ) { BOOLEAN match = FALSE; PCHAR segment1; PCHAR offset1; PCHAR segment2; PCHAR offset2; PCHAR index; PCHAR op; CHAR data[MAX_DESCRIPTION_LEN + 1]; ULONG cbData; ULONG memory; ULONG pointer; PVOID baseAddress; PCHAR address; segment1 = CmpGetSectionLineIndex(InfHandle, Description, RuleIndex, 0); offset1 = CmpGetSectionLineIndex(InfHandle, Description, RuleIndex, 1); segment2 = CmpGetSectionLineIndex(InfHandle, Description, RuleIndex, 2); offset2 = CmpGetSectionLineIndex(InfHandle, Description, RuleIndex, 3); index = CmpGetSectionLineIndex(InfHandle, Description, RuleIndex, 4); op = CmpGetSectionLineIndex(InfHandle, Description, RuleIndex, 5); if ( segment1 && offset1 && segment2 && offset2 && index && op) { // Get the data specified in the inf. cbData = sizeof(data); if (CmpGetInfData(InfHandle, Description, RuleIndex, 6, data, &cbData)) { if (strlen(offset2) == 0) { memory = strtoul(segment2, NULL, 16) << 4; } else { memory = (strtoul(segment2, NULL, 16) << 4) + strtoul(offset2, NULL, 16); } address = CmpMapPhysicalAddress(&baseAddress, memory, 4); if (address) { pointer = *((PUSHORT)address); // Unmap the physical address. ZwUnmapViewOfSection(NtCurrentProcess(), baseAddress); if (strlen(offset1) == 0) { memory = (strtoul(segment1, NULL, 16) << 4) + pointer; } else { memory = (strtoul(segment1, NULL, 16) << 4) + strtoul(offset1, NULL, 16); address = CmpMapPhysicalAddress(&baseAddress, memory, 2); if (address) { memory = ((*(PUSHORT)address) << 4) + pointer; // Unmap the physical address. ZwUnmapViewOfSection(NtCurrentProcess(), baseAddress); } } memory += strtoul(index, NULL, 16); // Map in the physical address. address = CmpMapPhysicalAddress(&baseAddress, memory, cbData); if (address) { match = CmpCheckOperator(op, RtlCompareMemory(address, data, cbData), cbData); // Unmap the physical address. ZwUnmapViewOfSection(NtCurrentProcess(), baseAddress); } } } } return (match); } BOOLEAN CmpMatchOemIdRule( IN PVOID InfHandle, IN PCHAR Description, IN ULONG RuleIndex ) { BOOLEAN match = FALSE; ULONG address; PCHAR op; PCHAR oemIdStr; ULONG oemId; PCHAR baseAddress; PPNP_BIOS_TABLE biosTable; // Search for the PnPBIOS structure in the BIOS ROM. address = CmpGetPnPBIOSTableAddress(); // Proceed if we found the PnP BIOS structure. if (address) { op = CmpGetSectionLineIndex(InfHandle, Description, RuleIndex, 0); oemIdStr = CmpGetSectionLineIndex(InfHandle, Description, RuleIndex, 1); if (op && oemIdStr) { if ( strlen(oemIdStr) == 7 && isalpha(oemIdStr[0]) && isalpha(oemIdStr[1]) && isalpha(oemIdStr[2]) && isxdigit(oemIdStr[3]) && isxdigit(oemIdStr[4]) && isxdigit(oemIdStr[5]) && isxdigit(oemIdStr[6])) { biosTable = (PPNP_BIOS_TABLE)CmpMapPhysicalAddress(&baseAddress, address, sizeof(PNP_BIOS_TABLE)); if (biosTable) { oemId = ((ULONG)(oemIdStr[0] & 0x1F) << 26) + ((ULONG)(oemIdStr[1] & 0x1F) << 21) + ((ULONG)(oemIdStr[2] & 0x1F) << 16) + strtoul(&oemIdStr[3], NULL, 16); // We only support EQUAL and NOT EQUAL operators. if (strcmp(op, "=") == 0 || strcmp(op, "==") == 0) { match = (oemId == biosTable->Oem); } else if( strcmp(op, "<>") == 0 || strcmp(op, "!=") == 0 || strcmp(op, "=!") == 0) { match = (oemId != biosTable->Oem); } // Unmap the physical address. ZwUnmapViewOfSection(NtCurrentProcess(), baseAddress); } } } } return (match); } BOOLEAN CmpMatchPModeRule( IN PVOID InfHandle, IN PCHAR Description, IN ULONG RuleIndex ) { BOOLEAN match = FALSE; ULONG address; CHAR data[MAX_DESCRIPTION_LEN + 1]; ULONG cbData; PVOID baseAddress; PPNP_BIOS_TABLE biosTable; ULONG pmAddress; PCHAR pmodeEntry; // Search for the PnPBIOS structure in the BIOS ROM. address = CmpGetPnPBIOSTableAddress(); // Proceed if we found the PnP BIOS structure. if (address) { // Get the data specified in the inf. cbData = sizeof(data); if (CmpGetInfData(InfHandle, Description, RuleIndex, 0, data, &cbData)) { biosTable = (PPNP_BIOS_TABLE)CmpMapPhysicalAddress(&baseAddress, address, sizeof(PNP_BIOS_TABLE)); if (biosTable) { pmAddress = (biosTable->PMSegment << 4) + biosTable->PMOffset; // Unmap the physical address. ZwUnmapViewOfSection(NtCurrentProcess(), baseAddress); pmodeEntry = CmpMapPhysicalAddress(&baseAddress, pmAddress, SYSTEM_BIOS_LENGTH); if (pmodeEntry) { if (*pmodeEntry == 0xE9) { pmodeEntry += (3 + (*((PUSHORT)&pmodeEntry[1]))); } match = (RtlCompareMemory(pmodeEntry, data, cbData) == cbData); // Unmap the physical address. ZwUnmapViewOfSection(NtCurrentProcess(), baseAddress); } } } } return (match); } BOOLEAN CmpMatchRmPmSameRule( IN PVOID InfHandle, IN PCHAR Description, IN ULONG RuleIndex ) { BOOLEAN match = FALSE; ULONG address; PCHAR baseAddress; PPNP_BIOS_TABLE biosTable; // Search for the PnPBIOS structure in the BIOS ROM. address = CmpGetPnPBIOSTableAddress(); // Proceed if we found the PnP BIOS structure. if (address) { biosTable = CmpMapPhysicalAddress(&baseAddress, address, sizeof(PNP_BIOS_TABLE)); if (biosTable) { match = ( biosTable->RMSegment == biosTable->PMSegment && biosTable->RMOffset == biosTable->PMOffset); // Unmap the physical address. ZwUnmapViewOfSection(NtCurrentProcess(), baseAddress); } } return (match); } BOOLEAN CmpMatchInstallRule( IN PVOID InfHandle, IN PCHAR Description, IN ULONG RuleIndex ) { BOOLEAN match = FALSE; PCHAR install; install = CmpGetSectionLineIndex(InfHandle, Description, RuleIndex, 0); if (install) { if (CmpGenInstall(InfHandle, install)) { // Successfully installed the specified section. match = TRUE; } } return (match); } BOOLEAN CmpMatchAcpiOemIdRule( IN PVOID InfHandle, IN PCHAR Description, IN ULONG RuleIndex ) /*++ Routine Description: This function processes a ACPI OEM ID rule from an INF file Examples: AcpiOemId="RSDT", "123456" is true if the RSDT has the OEM ID of 123456. AcpiOemId="DSDT", "768000" is true if the DSDT has the OEM ID of 768000. Arguments: InfHandle - Handle of the inf containing the rule. Description - Specifies the section name the rule is in RuleIndex - Specifies the index of the rule in the section Return Value: TRUE - the computer has the specified ACPI OEM ID. FALSE - the computer does not have the specified ACPI OEM ID. --*/ { BOOLEAN anyCase = FALSE; BOOLEAN match = FALSE; PCHAR tableName; PCHAR oemId; PCHAR optionalArgs; ULONG length; PDESCRIPTION_HEADER header; CHAR tableOemId[7]; STRING acpiString; STRING tableString; tableName = CmpGetSectionLineIndex( InfHandle, Description, RuleIndex, 0 ); oemId = CmpGetSectionLineIndex( InfHandle, Description, RuleIndex, 1 ); if (tableName && oemId) { // See if we have to do a case insensitive match optionalArgs = CmpGetSectionLineIndex( InfHandle, Description, RuleIndex, 2 ); if (optionalArgs) { if (_stricmp(optionalArgs,"any") == 0) { anyCase = TRUE; } } // Find the specified table in the BIOS ROM. header = CmpFindACPITable(*(PULONG)tableName, &length); if (header) { // Build the OEM id from the table RtlZeroMemory(tableOemId, sizeof(tableOemId)); RtlCopyMemory(tableOemId, header->OEMID, sizeof(header->OEMID)); RtlInitString( &tableString, tableOemId ); // And one from the string in the file RtlInitString( &acpiString, oemId ); // Now see if they are equal match = RtlEqualString( &acpiString, &tableString, anyCase ); // Unmap the table MmUnmapIoSpace(header, length ); } } return (match); } BOOLEAN CmpMatchAcpiOemTableIdRule( IN PVOID InfHandle, IN PCHAR Description, IN ULONG RuleIndex ) /*++ Routine Description: This function processes a ACPI OEM Table ID rule from an INF file. Examples: AcpiOemTableId="RSDT", "12345678" is true if the RSDT has the Oem Table ID of 12345678. AcpiOemTableId="DSDT", "87654321" is true if the DSDT has the Oem Table ID of 87654321. Arguments: InfHandle - Handle of the inf containing the rule. Description - Specifies the section name the rule is in RuleIndex - Specifies the index of the rule in the section Return Value: TRUE - the computer has the specified ACPI OEM Table ID. FALSE - the computer does not have the specified ACPI OEM Table ID. --*/ { BOOLEAN match = FALSE; PCHAR tableName; PCHAR oemTableId; ULONG length; PDESCRIPTION_HEADER header; ULONG idLength; CHAR acpiOemTableId[8]; tableName = CmpGetSectionLineIndex( InfHandle, Description, RuleIndex, 0 ); oemTableId = CmpGetSectionLineIndex( InfHandle, Description, RuleIndex, 1 ); if (tableName && oemTableId) { // Find the specified table in the BIOS ROM. header = CmpFindACPITable(*(PULONG)tableName, &length); if (header) { RtlZeroMemory(acpiOemTableId, sizeof(acpiOemTableId)); idLength = strlen(oemTableId); if (idLength > sizeof(acpiOemTableId)) { idLength = sizeof(acpiOemTableId); } RtlCopyMemory(acpiOemTableId, oemTableId, idLength); match = RtlEqualMemory(acpiOemTableId, header->OEMTableID, sizeof(header->OEMTableID)); MmUnmapIoSpace( header, length ); } } return (match); } BOOLEAN CmpMatchAcpiOemRevisionRule( IN PVOID InfHandle, IN PCHAR Description, IN ULONG RuleIndex ) /*++ Routine Description: This function processes a ACPI Oem Revision rule from an INF file. Examples: AcpiOemRevision="=","RSDT", 1234 is true if the RSDT has the Oem Revision EQUAL to 1234. AcpiOemRevision=">","DSDT", 4321 is true if the DSDT has the Oem Revision GREATER than 4321. Arguments: InfHandle - Handle of the inf containing the rule. Description - Specifies the section name the rule is in RuleIndex - Specifies the index of the rule in the section Return Value: TRUE - the computer has the specified ACPI Oem Revision. FALSE - the computer does not have the specified ACPI Oem Revision. --*/ { BOOLEAN match = FALSE; PCHAR op; PCHAR tableName; PCHAR oemRevisionStr; ULONG oemRevision; ULONG length; PDESCRIPTION_HEADER header; op = CmpGetSectionLineIndex( InfHandle, Description, RuleIndex, 0 ); tableName = CmpGetSectionLineIndex( InfHandle, Description, RuleIndex, 1 ); oemRevisionStr = CmpGetSectionLineIndex( InfHandle, Description, RuleIndex, 2 ); if (op && tableName && oemRevisionStr) { // Find the specified table. header = CmpFindACPITable(*(PULONG)tableName, &length); if (header) { RtlCharToInteger(oemRevisionStr, 16, &oemRevision); match = CmpCheckOperator(op, header->OEMRevision, oemRevision); MmUnmapIoSpace(header, length); } } return(match); } BOOLEAN CmpMatchAcpiRevisionRule( IN PVOID InfHandle, IN PCHAR Description, IN ULONG RuleIndex ) /*++ Routine Description: This function processes a ACPI Revision rule from an INF file. Examples: AcpiRevision="=", "RSDT", 1234 is true if the RSDT ACPI Revision is EQUAL to 1234. AcpiRevision=">", "DSDT", 4321 is true if the DSDT ACPI Revision is GREATER than 4321. Arguments: InfHandle - Handle of the inf containing the rule. Description - Specifies the section name the rule is in RuleIndex - Specifies the index of the rule in the section Return Value: TRUE - the computer has the specified ACPI Revision. FALSE - the computer does not have the specified ACPI Revision. --*/ { BOOLEAN match = FALSE; PCHAR op; PCHAR tableName; PCHAR revisionStr; ULONG revision; ULONG length; PDESCRIPTION_HEADER header; op = CmpGetSectionLineIndex( InfHandle, Description, RuleIndex, 0 ); tableName = CmpGetSectionLineIndex( InfHandle, Description, RuleIndex, 1 ); revisionStr = CmpGetSectionLineIndex( InfHandle, Description, RuleIndex, 2 ); if (op && tableName && revisionStr){ // Find the specified table. header = CmpFindACPITable(*(PULONG)tableName, &length); if (header) { RtlCharToInteger(revisionStr, 16, &revision); match = CmpCheckOperator(op, header->Revision, revision); MmUnmapIoSpace(header, length); } } return(match); } BOOLEAN CmpMatchAcpiCreatorRevisionRule( IN PVOID InfHandle, IN PCHAR Description, IN ULONG RuleIndex ) /*++ Routine Description: This function processes a ACPI Creator Revision rule from an INF file. Examples: AcpiCreatorRevision="=", "RSDT", 1234 is true if the RSDT ACPI Creator Revision is EQUAL to 1234. AcpiCreatorRevision=">", "DSDT", 4321 is true if the DSDT ACPI Creator Revision is GREATER than 4321. Arguments: InfHandle - Handle of the inf containing the rule. Description - Specifies the section name the rule is in RuleIndex - Specifies the index of the rule in the section Return Value: TRUE - the computer has the specified ACPI Creator Revision. FALSE - the computer does not have the specified ACPI Creator Revision. --*/ { BOOLEAN match = FALSE; PCHAR op; PCHAR tableName; PCHAR creatorRevisionStr; ULONG creatorRevision; ULONG length; PDESCRIPTION_HEADER header; op = CmpGetSectionLineIndex( InfHandle, Description, RuleIndex, 0 ); tableName = CmpGetSectionLineIndex( InfHandle, Description, RuleIndex, 1 ); creatorRevisionStr = CmpGetSectionLineIndex( InfHandle, Description, RuleIndex, 2 ); if (op && tableName && creatorRevisionStr) { // Find the specified table. header = CmpFindACPITable(*(PULONG)tableName, &length); if (header){ RtlCharToInteger(creatorRevisionStr, 16, &creatorRevision); match = CmpCheckOperator(op, header->CreatorRev, creatorRevision); MmUnmapIoSpace( header, length ); } } return(match); } BOOLEAN CmpMatchAcpiCreatorIdRule( IN PVOID InfHandle, IN PCHAR Description, IN ULONG RuleIndex ) /*++ Routine Description: This function processes a ACPI Creator ID rule from an INF file. Examples: AcpiCreatorId="RSDT", "MSFT" is true if the RSDT has the Creator ID of MSFT. Arguments: InfHandle - Handle of the inf containing the rule. Description - Specifies the section name the rule is in RuleIndex - Specifies the index of the rule in the section Return Value: TRUE - the computer has the specified ACPI Creator ID. FALSE - the computer does not have the specified ACPI Creator ID. --*/ { BOOLEAN match = FALSE; PCHAR tableName; PCHAR creatorId; ULONG length; PDESCRIPTION_HEADER header; ULONG idLength; CHAR acpiCreatorId[6]; tableName = CmpGetSectionLineIndex( InfHandle, Description, RuleIndex, 0 ); creatorId = CmpGetSectionLineIndex( InfHandle, Description, RuleIndex, 1 ); if (tableName && creatorId) { // Find the specified table. header = CmpFindACPITable(*(PULONG)tableName, &length); if (header) { RtlZeroMemory(acpiCreatorId, sizeof(acpiCreatorId)); idLength = strlen(creatorId); if (idLength > sizeof(acpiCreatorId)) { idLength = sizeof(acpiCreatorId); } RtlCopyMemory(acpiCreatorId, creatorId, idLength); match = RtlEqualMemory(acpiCreatorId, header->CreatorID, sizeof(header->CreatorID)); MmUnmapIoSpace( header, length ); } } return(match); } BOOLEAN CmpGetInfData( IN PVOID InfHandle, IN PCHAR Section, IN ULONG LineIndex, IN ULONG ValueIndex, IN OUT PCHAR Buffer, IN OUT PULONG BufferSize ) /*++ Routine Description: This routine reads and parses data from the inf. It understands two kinds of data 1. String 2. Binary. Examples- B,02 - byte 0x02 B,72,0D,FF,0F - sequence of bytes 0x72 0x0D 0xFF 0x0F or the DWORD 0x0FFF0D72 S,COMPAQ - ASCII string "COMPAQ" Input Parameters: InfHandle - Handle to the inf to be read. Section - Section name to be read. LineIndex - Index of the line in the Section to be read. ValueIndex - First value to be read on the LineIndex. Buffer - Parsed data gets returned in this buffer. BufferSize - On entry, contains the size of Buffer. The number of bytes parsed in gets returned in this variable. Return Value: TRUE iff data was parsed in successfully. Else FALSE. --*/ { BOOLEAN result = FALSE; ULONG cbData; PCHAR data; ULONG remainingBytes; // Validate input parameters. if (Buffer && BufferSize && *BufferSize) { // Read in the data type "S" or "B". PCHAR type = CmpGetSectionLineIndex(InfHandle, Section, LineIndex, ValueIndex++); if (type) { // Initialize local data. remainingBytes = *BufferSize; // Process Binary data. if (_stricmp(type, "B") == 0) { // Parse data as long as there is more data and the buffer is not full. for (result = TRUE; result == TRUE && remainingBytes; remainingBytes--) { CHAR value; // Read in the data. data = CmpGetSectionLineIndex(InfHandle, Section, LineIndex, ValueIndex++); if (data) { // Convert the data read in and validate that is indeed a HEX value. value = (CHAR)strtoul(data, NULL, 16); if (value == 0 && strcmp(data, "00") && strcmp(data, "0")) { result = FALSE; } else { *Buffer++ = value; } } else { break; } } // Return the number of bytes parsed in. *BufferSize -= remainingBytes; } // Process String data. else if(_stricmp(type, "S") == 0) { // Read in the string. data = CmpGetSectionLineIndex(InfHandle, Section, LineIndex, ValueIndex); // Only copy as much data as the buffer can hold. cbData = min(remainingBytes, strlen(data)); RtlCopyBytes(Buffer, data, cbData); // Return the number of bytes actually copied. *BufferSize = cbData; result = TRUE; } } } return (result); } PVOID CmpMapPhysicalAddress(IN OUT PVOID *BaseAddress, IN ULONG Address, IN ULONG Size) /*++ Routine Description: This routine maps the specified physical segment into the process virtual memory. Input Parameters: Segment - Segment to be mapped. Size - Segment size to be mapped. Return Value: Virtual address for the mapped segment. --*/ { UNICODE_STRING sectionName; OBJECT_ATTRIBUTES objectAttributes; HANDLE sectionHandle; NTSTATUS status; PVOID baseAddress; ULONG viewSize; LARGE_INTEGER viewBase; PVOID ptr = NULL; *BaseAddress = NULL; RtlInitUnicodeString(§ionName, L"\\Device\\PhysicalMemory"); InitializeObjectAttributes( &objectAttributes, §ionName, OBJ_CASE_INSENSITIVE, (HANDLE)NULL, (PSECURITY_DESCRIPTOR)NULL); status = ZwOpenSection( §ionHandle, SECTION_MAP_READ, &objectAttributes); if (NT_SUCCESS(status)) { baseAddress = NULL; viewSize = Size; viewBase.LowPart = Address & ~(0xFFF); viewBase.HighPart = 0; status = ZwMapViewOfSection(sectionHandle, NtCurrentProcess(), &baseAddress, 0, viewSize, &viewBase, &viewSize, ViewUnmap, MEM_DOS_LIM, PAGE_READWRITE); if (NT_SUCCESS(status)) { ptr = (PVOID)((PCHAR)baseAddress + (Address & 0xFFF)); *BaseAddress = baseAddress; } } return (ptr); } BOOLEAN CmpCheckOperator( IN PCHAR Operator, IN ULONG Lhs, IN ULONG Rhs ) /*++ Routine Description: This routine tests condition specified by the operator by applying it to the specified LHS and RHS arguments. Input Parameters: Operator - Is the operator to be tested. Lhs - Left Hand Side argument for the Operator. Rhs - Right Hand Side argument for the Operator. Return Value: True iff the condition Lhs Operator Rhs is satisfied. --*/ { BOOLEAN result = FALSE; // We are pretty lenient about which operators we support. // "=" or "==" for EQUAL. if (strcmp(Operator, "=") == 0 || strcmp(Operator, "==") == 0) { result = (Lhs == Rhs); } // "!=" or "=!" or "<>" for NOT EQUAL. else if( strcmp(Operator, "!=") == 0 || strcmp(Operator, "<>") == 0 || strcmp(Operator, "=!") == 0) { result = (Lhs != Rhs); } // "<" for LESS THAN. else if(strcmp(Operator, "<") == 0) { result = (Lhs < Rhs); } // "<=" or "=<" for LESS THAN or EQUAL. else if(strcmp(Operator, "<=") == 0 || strcmp(Operator, "=<") == 0) { result = (Lhs <= Rhs); } // ">" for GREATER THAN. else if(strcmp(Operator, ">") == 0) { result = (Lhs > Rhs); } // ">=" or "=>" for GREATER THAN or EQUAL. else if(strcmp(Operator, ">=") == 0 || strcmp(Operator, "=>") == 0) { result = (Lhs >= Rhs); } else { DbgPrint("Invalid operator %s used!\n", Operator); } return (result); } PVOID CmpFindPattern( IN PCHAR Buffer, IN ULONG BufSize, IN PCHAR Pattern, IN ULONG PatSize, IN BOOLEAN IgnoreCase, IN ULONG Step ) /*++ Routine Description: This routine searches the buffer for the specified pattern of data. Input Parameters: Buffer - Buffer to be searched. BufSize - Size of this buffer. Pattern - Pattern to be searched. PatSize - Size of the pattern. IgnoreCase - TRUE if the search is to be case insensitive. Return Value: Returns the pointer into the buffer where the pattern is first found. --*/ { PCHAR bufEnd; if (PatSize > BufSize) { return (NULL); } if (PatSize == 0) { PatSize = strlen(Pattern); } if (Step == 0) { Step = 1; } for ( bufEnd = Buffer + BufSize; Buffer + PatSize < bufEnd; Buffer += Step) { if (IgnoreCase) { if (_strnicmp(Buffer, Pattern, PatSize) == 0) { return (Buffer); } } else { if (strncmp(Buffer, Pattern, PatSize) == 0) { return (Buffer); } } } return (NULL); } ULONG CmpGetPnPBIOSTableAddress( VOID ) /*++ Routine Description: This routine searches the BIOS ROM for the PnP BIOS installation structure. Input Parameters: None. Return Value: Returns the physical address in the ROM BIOS where the PnP BIOS structure is located. --*/ { static ULONG tableAddress = (ULONG)-1; PVOID baseAddress; PPNP_BIOS_TABLE address; PPNP_BIOS_TABLE lastAddress; ULONG i; ULONG checksum; if (tableAddress == (ULONG)-1) { // Search for the PnPBIOS structure in the BIOS ROM. address = (PPNP_BIOS_TABLE)CmpMapPhysicalAddress(&baseAddress, 0xF0000, SYSTEM_BIOS_LENGTH); if (address) { for ( lastAddress = (PPNP_BIOS_TABLE)((PCHAR)address + SYSTEM_BIOS_LENGTH - 0x10); address < lastAddress; (PCHAR)address += 0x10) { if (address->Signature == PNPBIOS_SIGNATURE) { for ( i = 0, checksum = 0; i < address->Length; i++) { checksum += ((PUCHAR)address)[i]; } if ( (checksum & 0xFF) == 0 && address->Length >= 0x21) { tableAddress = 0xF0000 + (SYSTEM_BIOS_LENGTH - 10) - ((PCHAR)lastAddress - (PCHAR)address); break; } } } // Unmap the physical address. ZwUnmapViewOfSection(NtCurrentProcess(), baseAddress); } } return (tableAddress); } PDESCRIPTION_HEADER CmpFindACPITable( IN ULONG Signature, IN OUT PULONG Length ) { PDESCRIPTION_HEADER header = NULL; PDESCRIPTION_HEADER tempHeader = NULL; static PHYSICAL_ADDRESS rsdtAddress = { -1, -1 }; ULONG length = 0; // Use the cached location of RSDT address if available. if (rsdtAddress.QuadPart == -1) { NTSTATUS status; PACPI_BIOS_MULTI_NODE rsdpMulti; rsdtAddress.QuadPart = 0; // Get the multinode status = CmpFindRSDTTable( &rsdpMulti ); if (!NT_SUCCESS(status)) { return NULL; } // Map the address rsdtAddress.LowPart = rsdpMulti->RsdtAddress.LowPart; rsdtAddress.HighPart = rsdpMulti->RsdtAddress.HighPart; // Done with the multinode ExFreePool( rsdpMulti ); } // If we have an address if (rsdtAddress.QuadPart) { // Map in the the rsdt table tempHeader = MmMapIoSpace( rsdtAddress, sizeof(DESCRIPTION_HEADER), MmCached ); if (tempHeader == NULL) { DbgPrint("CmpFindACPITable: Cannot map RSDT at %I64x\n", rsdtAddress.QuadPart); return NULL; } // If what we are looking for is the RSDT, then we are done if (Signature == RSDT_SIGNATURE) { header = tempHeader; length = sizeof(DESCRIPTION_HEADER); } else if (Signature == DSDT_SIGNATURE) { PFADT fadt; PHYSICAL_ADDRESS dsdtAddress; ULONG tempLength; fadt = (PFADT) CmpFindACPITable( FADT_SIGNATURE, &length ); if (fadt) { dsdtAddress.HighPart = 0; dsdtAddress.LowPart = fadt->dsdt; // Done with the FADT MmUnmapIoSpace( fadt, length ); // Map in the dsdt table header = MmMapIoSpace( dsdtAddress, sizeof(DESCRIPTION_HEADER), MmCached ); if (header == NULL) { DbgPrint( "CmpFindACPITable: Cannot map DSDT at %I64x\n", dsdtAddress.QuadPart ); MmUnmapIoSpace( tempHeader, sizeof(DESCRIPTION_HEADER) ); return NULL; } length = sizeof(DESCRIPTION_HEADER); } else { DbgPrint("CmpFindACPITable: Cannot find FADT\n"); MmUnmapIoSpace( tempHeader, sizeof(DESCRIPTION_HEADER) ); return NULL; } } else { PHYSICAL_ADDRESS tableAddress; PRSDT rsdt; ULONG i; ULONG num; ULONG rsdtLength; // Map in the entire RSDT rsdtLength = tempHeader->Length; rsdt = (PRSDT) MmMapIoSpace( rsdtAddress, rsdtLength, MmCached ); if (rsdt == NULL) { DbgPrint( "CmpFindACPITable: Cannot map RSDT at %I64x\n", rsdtAddress.QuadPart ); MmUnmapIoSpace( tempHeader, sizeof(DESCRIPTION_HEADER) ); return NULL; } // Done with the temp header MmUnmapIoSpace( tempHeader, sizeof(DESCRIPTION_HEADER) ); // Look at all the table entries for the header that we care about num = TABLE_ENTRIES_FROM_RSDT_POINTER( rsdt ); for (i = 0; i < num ; i ++) { // Get the address of the table tableAddress.HighPart = 0; tableAddress.LowPart = rsdt->Tables[i]; // Map in the header tempHeader = MmMapIoSpace( tableAddress, sizeof(DESCRIPTION_HEADER), MmCached ); if (!tempHeader) { DbgPrint( "CmpFindACPITable: Cannot map header at %I64x\n", tableAddress.QuadPart ); MmUnmapIoSpace( rsdt, rsdtLength ); return NULL; } // Signature check if (tempHeader->Signature != Signature) { MmUnmapIoSpace( tempHeader, sizeof(DESCRIPTION_HEADER) ); continue; } // Are we looking at the FADT? if (Signature == FADT_SIGNATURE) { // Map the entire table for this one length = tempHeader->Length; header = MmMapIoSpace( tableAddress, length, MmCached ); // Unmap the old table MmUnmapIoSpace( tempHeader, sizeof(DESCRIPTION_HEADER) ); // Did we successfully map the header? if (header == NULL ) { DbgPrint( "CmpFindACPITable: Cannot map FADT at %I64x\n", tableAddress.QuadPart ); MmUnmapIoSpace( rsdt, rsdtLength ); return NULL; } } else { // Remember where the table and length are stored length = sizeof(DESCRIPTION_HEADER); header = tempHeader; } } // for // Done with the rsdt MmUnmapIoSpace( rsdt, rsdtLength ); } // If we found the table, return its length. if (Length) { if (header) { *Length = length; } else { *Length = 0; } } } return (header); } NTSTATUS CmpFindRSDTTable( OUT PACPI_BIOS_MULTI_NODE *Rsdt ) /*++ Routine Description: This function looks into the registry to find the ACPI RSDT, which was stored there by ntdetect.com Arguments: RsdtPtr - Pointer to a buffer that contains the ACPI Root System Description Pointer Structure. The caller is responsible for freeing this buffer. Note: This is returned in non-paged pool. Return Value: A NTSTATUS code to indicate the result of the initialization. --*/ { BOOLEAN same; HANDLE hMFunc; HANDLE hBus; NTSTATUS status; OBJECT_ATTRIBUTES objectAttributes; PACPI_BIOS_MULTI_NODE multiNode; PCM_PARTIAL_RESOURCE_DESCRIPTOR prd; PCM_PARTIAL_RESOURCE_LIST prl; PKEY_VALUE_PARTIAL_INFORMATION valueInfo; PWSTR p; ULONG i; ULONG length; ULONG multiNodeSize; UNICODE_STRING unicodeString; UNICODE_STRING unicodeValueName; UNICODE_STRING biosId; WCHAR wbuffer[10]; PAGED_CODE(); // Look in the registry for the "ACPI BIOS bus" data RtlInitUnicodeString( &unicodeString, rgzMultiFunctionAdapter ); InitializeObjectAttributes( &objectAttributes, &unicodeString, OBJ_CASE_INSENSITIVE, NULL, // handle NULL ); status = ZwOpenKey( &hMFunc, KEY_READ, &objectAttributes ); if (!NT_SUCCESS(status)) { DbgPrint("CmpFindRSDTTable: Cannot open MultifunctionAdapter registry key.\n"); return status; } // We will need to make a unicode string that we can use to enumerate // the subkeys of the MFA key unicodeString.Buffer = wbuffer; unicodeString.MaximumLength = sizeof(wbuffer); RtlInitUnicodeString( &biosId, rgzBIOSIdentifier ); // Loop over all subkeys for (i = 0; TRUE; i++) { // Turn the number into a key name RtlIntegerToUnicodeString( i, 10, &unicodeString); InitializeObjectAttributes( &objectAttributes, &unicodeString, OBJ_CASE_INSENSITIVE, hMFunc, NULL ); // Open the named subkey status = ZwOpenKey( &hBus, KEY_READ, &objectAttributes ); if (!NT_SUCCESS(status)) { // Out of Multifunction adapter entries... DbgPrint("CmpFindRSDTTable: ACPI BIOS MultifunctionAdapter registry key not found.\n"); ZwClose (hMFunc); return STATUS_UNSUCCESSFUL; } // Check the Indentifier to see if this is an ACPI BIOS entry status = CmpGetRegistryValue( hBus, rgzAcpiIdentifier, &valueInfo ); if (!NT_SUCCESS (status)) { ZwClose( hBus ); continue; } p = (PWSTR) ((PUCHAR) valueInfo->Data); unicodeValueName.Buffer = p; unicodeValueName.MaximumLength = (USHORT)valueInfo->DataLength; length = valueInfo->DataLength; // Determine the real length of the ID string while (length) { if (p[length / sizeof(WCHAR) - 1] == UNICODE_NULL) { length -= 2; } else { break; } } // Do we have a match the "ACPI BIOS" identifier? unicodeValueName.Length = (USHORT)length; same = RtlEqualUnicodeString( &biosId, &unicodeValueName, TRUE ); ExFreePool( valueInfo ); if (!same) { ZwClose( hBus ); continue; } // We do, so get the configuration data status = CmpGetRegistryValue( hBus, rgzAcpiConfigurationData, &valueInfo ); ZwClose( hBus ); if (!NT_SUCCESS(status)) { continue ; } // The data that we want is at the end of the PARTIAL_RESOURCE_LIST // descriptor prl = (PCM_PARTIAL_RESOURCE_LIST)(valueInfo->Data); prd = &prl->PartialDescriptors[0]; multiNode = (PACPI_BIOS_MULTI_NODE) ( (PCHAR) prd + sizeof(CM_PARTIAL_RESOURCE_LIST) ); break; } // Calculate the size of the data so that we can make a copy multiNodeSize = sizeof(ACPI_BIOS_MULTI_NODE) + ( (ULONG)(multiNode->Count - 1) * sizeof(ACPI_E820_ENTRY) ); *Rsdt = (PACPI_BIOS_MULTI_NODE) ExAllocatePoolWithTag(NonPagedPool, multiNodeSize, 'IPCA'); if (*Rsdt == NULL) { ExFreePool( valueInfo ); return STATUS_INSUFFICIENT_RESOURCES; } RtlCopyMemory(*Rsdt, multiNode, multiNodeSize); // Done with the key memory ExFreePool(valueInfo); // Done return STATUS_SUCCESS; } NTSTATUS CmpGetRegistryValue( IN HANDLE KeyHandle, IN PWSTR ValueName, OUT PKEY_VALUE_PARTIAL_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. --*/ { NTSTATUS status; PKEY_VALUE_PARTIAL_INFORMATION infoBuffer; ULONG keyValueLength; UNICODE_STRING unicodeString; PAGED_CODE(); 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, KeyValuePartialInformation, (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, 'IPCA'); if (!infoBuffer) { return STATUS_INSUFFICIENT_RESOURCES; } // Query the data for the key value. status = ZwQueryValueKey(KeyHandle, &unicodeString, KeyValuePartialInformation, 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; }