/*++ Copyright (c) Microsoft Corporation. All rights reserved. Module Name: rules.c Abstract: This module contains routines to implement rules used to describe a machine. This is based on the detection code from W9x. Author: Santosh Jodh (santoshj) 08-Aug-1998 Environment: Kernel mode. Revision History: --*/ #include "cmp.h" #include "stdlib.h" #include "parseini.h" #include "geninst.h" #include "acpitabl.h" #include "ntacpi.h" #include "rules.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 LOGICAL (* 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 ); LOGICAL CmpCheckOperator( IN PCHAR Operator, IN ULONG Lhs, IN ULONG Rhs ); PVOID CmpMapPhysicalAddress( IN OUT PVOID *BaseAddress, IN ULONG_PTR 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 ); LOGICAL CmpMatchDateRule( IN PVOID InfHandle, IN PCHAR Description, IN ULONG RuleIndex ); LOGICAL CmpMatchMemoryRule( IN PVOID InfHandle, IN PCHAR Description, IN ULONG RuleIndex ); LOGICAL CmpMatchSearchRule( IN PVOID InfHandle, IN PCHAR Description, IN ULONG RuleIndex ); LOGICAL CmpMatchNextMatchRule( IN PVOID InfHandle, IN PCHAR Description, IN ULONG RuleIndex ); LOGICAL CmpMatchPointerRule( IN PVOID InfHandle, IN PCHAR Description, IN ULONG RuleIndex ); LOGICAL CmpMatchOemIdRule( IN PVOID InfHandle, IN PCHAR Description, IN ULONG RuleIndex ); LOGICAL CmpMatchPModeRule( IN PVOID InfHandle, IN PCHAR Description, IN ULONG RuleIndex ); LOGICAL CmpMatchRmPmSameRule( IN PVOID InfHandle, IN PCHAR Description, IN ULONG RuleIndex ); LOGICAL CmpMatchInstallRule( IN PVOID InfHandle, IN PCHAR Description, IN ULONG RuleIndex ); LOGICAL CmpMatchAcpiOemIdRule( IN PVOID InfHandle, IN PCHAR Description, IN ULONG RuleIndex ); LOGICAL CmpMatchAcpiOemTableIdRule( IN PVOID InfHandle, IN PCHAR Description, IN ULONG RuleIndex ); LOGICAL CmpMatchAcpiOemRevisionRule( IN PVOID InfHandle, IN PCHAR Description, IN ULONG RuleIndex ); LOGICAL CmpMatchAcpiRevisionRule( IN PVOID InfHandle, IN PCHAR Description, IN ULONG RuleIndex ); LOGICAL CmpMatchAcpiCreatorRevisionRule( IN PVOID InfHandle, IN PCHAR Description, IN ULONG RuleIndex ); // // Number of rules currently implemented. // #define NUM_OF_RULES 14 // // Rule table. // #ifdef ALLOC_DATA_PRAGMA #pragma data_seg("INITDATA") #pragma const_seg("INITCONST") #endif struct { PCHAR Name; PFN_RULE Action; } const 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 const WCHAR rgzMultiFunctionAdapter[] = L"\\Registry\\Machine\\Hardware\\Description\\System\\MultifunctionAdapter"; static const WCHAR rgzAcpiConfigurationData[] = L"Configuration Data"; static const WCHAR rgzAcpiIdentifier[] = L"Identifier"; static const WCHAR rgzBIOSIdentifier[] = L"ACPI BIOS"; #ifdef ALLOC_PRAGMA #pragma alloc_text(INIT,CmpGetRegistryValue) #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; // // We will process ALL sections even if one or more match. // if (CmpMatchDescription(infHandle, computerName)) { CmKdPrintEx((DPFLTR_SYSTEM_ID, DPFLTR_WARNING_LEVEL, "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); } LOGICAL 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); } LOGICAL 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. --*/ { LOGICAL 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); } LOGICAL 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. --*/ { LOGICAL 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. // gSearchAddress = (PVOID)((PCHAR)gSearchAddress - address); gSearchAddress = (PVOID)((PCHAR)gSearchAddress + memory); match = TRUE; } // // Unmap the physical address. // ZwUnmapViewOfSection(NtCurrentProcess(), baseAddress); } } } return (match); } LOGICAL 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. --*/ { LOGICAL 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)) { gSearchAddress = (PVOID)((PCHAR)gSearchAddress + strtoul(offset, NULL, 16)); // // Map in the physical address. // address = CmpMapPhysicalAddress(&baseAddress, (ULONG_PTR)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); } LOGICAL CmpMatchPointerRule( IN PVOID InfHandle, IN PCHAR Description, IN ULONG RuleIndex ) { LOGICAL 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, (ULONG)RtlCompareMemory(address, data, cbData), cbData); // // Unmap the physical address. // ZwUnmapViewOfSection(NtCurrentProcess(), baseAddress); } } } } return (match); } LOGICAL CmpMatchOemIdRule( IN PVOID InfHandle, IN PCHAR Description, IN ULONG RuleIndex ) { LOGICAL 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); } LOGICAL CmpMatchPModeRule( IN PVOID InfHandle, IN PCHAR Description, IN ULONG RuleIndex ) { LOGICAL 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); } LOGICAL CmpMatchRmPmSameRule( IN PVOID InfHandle, IN PCHAR Description, IN ULONG RuleIndex ) { LOGICAL match = FALSE; ULONG address; PCHAR baseAddress; PPNP_BIOS_TABLE biosTable; UNREFERENCED_PARAMETER (InfHandle); UNREFERENCED_PARAMETER (Description); UNREFERENCED_PARAMETER (RuleIndex); // // 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); } LOGICAL 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); } LOGICAL 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); } LOGICAL 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. --*/ { LOGICAL match = FALSE; PCHAR tableName; PCHAR oemTableId; ULONG length; PDESCRIPTION_HEADER header; SIZE_T 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); } LOGICAL 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. --*/ { LOGICAL 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); } LOGICAL 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. --*/ { LOGICAL 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); } LOGICAL 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. --*/ { LOGICAL 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); } LOGICAL 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. --*/ { LOGICAL match = FALSE; PCHAR tableName; PCHAR creatorId; ULONG length; PDESCRIPTION_HEADER header; SIZE_T 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 = (ULONG)strlen(data); cbData = (ULONG) min(remainingBytes, cbData); 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_PTR 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; SIZE_T 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.QuadPart = Address & ~(0xFFF); 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); } LOGICAL 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. --*/ { LOGICAL 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 { #ifndef _CM_LDR_ DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"Invalid operator %s used!\n", Operator); #endif //_CM_LDR_ } 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 = (ULONG) 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; address = (PPNP_BIOS_TABLE)((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) - (ULONG)((PCHAR)lastAddress - (PCHAR)address); break; } } } // // Unmap the physical address. // ZwUnmapViewOfSection(NtCurrentProcess(), baseAddress); } } return (tableAddress); } PDESCRIPTION_HEADER CmpFindACPITable( IN ULONG Signature, IN OUT PULONG Length ) { static PHYSICAL_ADDRESS rsdtAddress = { (ULONG)-1, (ULONG)-1 }; PDESCRIPTION_HEADER header; PDESCRIPTION_HEADER rsdtHeader; ULONG length; NTSTATUS status; PACPI_BIOS_MULTI_NODE rsdpMulti; PFADT fadt; PHYSICAL_ADDRESS dsdtAddress; PHYSICAL_ADDRESS tableAddress; PRSDT rsdt; ULONG rsdtLength; ULONG i; ULONG num; length = 0; header = NULL; rsdtHeader = NULL; // // Use the cached location of RSDT address if available. // if (rsdtAddress.QuadPart == -1) { rsdtAddress.QuadPart = 0; // // Get the multinode // status = CmpFindRSDTTable(&rsdpMulti); if (!NT_SUCCESS(status)) { goto exit; } // // Map the address // rsdtAddress.LowPart = rsdpMulti->RsdtAddress.LowPart; rsdtAddress.HighPart = rsdpMulti->RsdtAddress.HighPart; // // Done with the multinode // ExFreePool(rsdpMulti); } // // If we dont have an address, system probably does not have an ACPI BIOS. // if (rsdtAddress.QuadPart == 0) { goto exit; } // // Map in the the rsdt table // rsdtHeader = MmMapIoSpace( rsdtAddress, sizeof(DESCRIPTION_HEADER), MmNonCached ); if (rsdtHeader == NULL) { #ifndef _CM_LDR_ DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"CmpFindACPITable: Cannot map RSDT at %I64x\n", rsdtAddress.QuadPart); #endif //_CM_LDR_ goto exit; } // // If what we are looking for is the RSDT, then we are done // if (Signature == RSDT_SIGNATURE) { header = rsdtHeader; length = sizeof(DESCRIPTION_HEADER); // // We want to leave this mapped, caller wil unmap. // rsdtHeader = NULL; goto exit; } if (Signature == DSDT_SIGNATURE) { fadt = (PFADT)CmpFindACPITable(FADT_SIGNATURE, &length); if (!fadt) { #ifndef _CM_LDR_ DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"CmpFindACPITable: Cannot find FADT\n"); #endif //_CM_LDR_ goto exit; } 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), MmNonCached ); if (header) { length = sizeof(DESCRIPTION_HEADER); } else { #ifndef _CM_LDR_ DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL, "CmpFindACPITable: Cannot map DSDT at %I64x\n", dsdtAddress.QuadPart ); #endif //_CM_LDR_ } goto exit; } // // Map in the entire RSDT // rsdtLength = rsdtHeader->Length; rsdt = (PRSDT)MmMapIoSpace(rsdtAddress, rsdtLength, MmNonCached); if (rsdt == NULL) { #ifndef _CM_LDR_ DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL, "CmpFindACPITable: Cannot map RSDT at %I64x\n", rsdtAddress.QuadPart ); #endif //_CM_LDR_ goto exit; } // // 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 // header = MmMapIoSpace( tableAddress, sizeof(DESCRIPTION_HEADER), MmNonCached ); if (!header) { #ifndef _CM_LDR_ DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL, "CmpFindACPITable: Cannot map header at %I64x\n", tableAddress.QuadPart ); #endif //_CM_LDR_ break; } // // Signature check // if (header->Signature == Signature) { // // Are we looking at the FADT? // if (Signature == FADT_SIGNATURE) { length = header->Length; // // Unmap the old table // MmUnmapIoSpace(header, sizeof(DESCRIPTION_HEADER)); // // Map the entire table for this one // header = MmMapIoSpace(tableAddress, length, MmNonCached); // // Did we successfully map the header? // if (header == NULL ) { #ifndef _CM_LDR_ DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL, "CmpFindACPITable: Cannot map FADT at %I64x\n", tableAddress.QuadPart ); #endif //_CM_LDR_ } } else { // // Remember where the table and length are stored // length = sizeof(DESCRIPTION_HEADER); } break; } MmUnmapIoSpace(header, sizeof(DESCRIPTION_HEADER)); header = NULL; } // for // // Done with the rsdt. // MmUnmapIoSpace(rsdt, rsdtLength); exit: // // Cleanup. // if (rsdtHeader) { MmUnmapIoSpace(rsdtHeader, sizeof(DESCRIPTION_HEADER)); } // // 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)) { #ifndef _CM_LDR_ DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_WARNING_LEVEL,"CmpFindRSDTTable: Cannot open MultifunctionAdapter registry key.\n"); #endif //_CM_LDR_ 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... // #ifndef _CM_LDR_ DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_WARNING_LEVEL,"CmpFindRSDTTable: ACPI BIOS MultifunctionAdapter registry key not found.\n"); #endif //_CM_LDR_ ZwClose (hMFunc); return STATUS_UNSUCCESSFUL; } // // Check the Indentifier to see if this is an ACPI BIOS entry // status = CmpGetRegistryValue( hBus, (PWCHAR)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, (PWCHAR)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( !NT_SUCCESS(status) && (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; }