/*++ Copyright (c) 1990, 1991 Microsoft Corporation Module Name: init386.c Abstract: This module is responsible to build any x86 specific entries in the hardware tree of registry. Author: Ken Reneris (kenr) 04-Aug-1992 Environment: Kernel mode. Revision History: shielint - add BIOS date and version detection. --*/ #include "cmp.h" #include "stdio.h" // // Title Index is set to 0. // (from ..\cmconfig.c) // #define TITLE_INDEX_VALUE 0 extern PCHAR SearchStrings[]; extern PCHAR BiosBegin; extern PCHAR Start; extern PCHAR End; extern UCHAR CmpID1[]; extern UCHAR CmpID2[]; extern WCHAR CmpVendorID[]; extern WCHAR CmpFeatureBits[]; extern WCHAR CmpMHz[]; extern WCHAR CmpUpdateSignature[]; extern UCHAR CmpCyrixID[]; // // Bios date and version definitions // #define BIOS_DATE_LENGTH 9 #define MAXIMUM_BIOS_VERSION_LENGTH 128 #define SYSTEM_BIOS_START 0xF0000 #define SYSTEM_BIOS_LENGTH 0x10000 #define INT10_VECTOR 0x10 #define VIDEO_BIOS_START 0xC0000 #define VIDEO_BIOS_LENGTH 0x8000 #define VERSION_DATA_LENGTH PAGE_SIZE extern ULONG CmpConfigurationAreaSize; extern PCM_FULL_RESOURCE_DESCRIPTOR CmpConfigurationData; BOOLEAN CmpGetBiosVersion ( PCHAR SearchArea, ULONG SearchLength, PCHAR VersionString ); BOOLEAN CmpGetBiosDate ( PCHAR SearchArea, ULONG SearchLength, PCHAR DateString ); ULONG Ke386CyrixId ( VOID ); #ifdef ALLOC_PRAGMA #pragma alloc_text(INIT,CmpGetBiosDate) #pragma alloc_text(INIT,CmpGetBiosVersion) #pragma alloc_text(INIT,CmpInitializeMachineDependentConfiguration) #endif BOOLEAN CmpGetBiosDate ( PCHAR SearchArea, ULONG SearchLength, PCHAR DateString ) /*++ Routine Description: This routine finds the most recent date in the computer/video card's ROM. When GetRomDate encounters a datae, it checks the previously found date to see if the new date is more recent. Arguments: SearchArea - the area to search for a date. SearchLength - Length of search. DateString - Supplies a pointer to a fixed length memory to receive the date string. Return Value: NT_SUCCESS if a date is found. --*/ { BOOLEAN FoundFlag = TRUE; // Set to TRUE if the item was found CHAR PrevDate[BIOS_DATE_LENGTH]; // Date currently being examined CHAR CurrDate[BIOS_DATE_LENGTH]; // Date currently being examined PCHAR String; USHORT i; // Looping variable USHORT Length; // Number of characters to move PCHAR Start = SearchArea + 2; PCHAR End = SearchArea + SearchLength - 5; // // Clear out the previous date // RtlZeroMemory(PrevDate, BIOS_DATE_LENGTH); while (FoundFlag) { String = NULL; // // Search for '/' with a digit on either side and another // '/' 3 character away. // while (Start < End) { if (*Start == '/' && *(Start+3) == '/' && (*(Start+1) <= '9' && *(Start+1) >= '0') && (*(Start-1) <= '9' && *(Start-1) >= '0') && (*(Start+5) <= '9' && *(Start+5) >= '0') && (*(Start+4) <= '9' && *(Start+4) >= '0') && (*(Start+2) <= '9' && *(Start+2) >= '0')) { String = Start; break; } else { Start++; } } if (String) { Start = String + 3; String -= 2; // Move String to the beginning of // date. // // Copy the year into CurrDate // CurrDate[0] = String[6]; CurrDate[1] = String[7]; CurrDate[2] = '/'; // The 1st "/" for YY/MM/DD // // Copy the month & day into CurrDate // (Process properly if this is a one digit month) // if (*String > '9' || *String < '0') { CurrDate[3] = '0'; String++; i = 4; Length = 4; } else { i = 3; Length = 5; } RtlMoveMemory(&CurrDate[i], String, Length); // // Compare the dates, to see which is more recent // if (memcmp (PrevDate, CurrDate, BIOS_DATE_LENGTH - 1) < 0) { RtlMoveMemory(PrevDate, CurrDate, BIOS_DATE_LENGTH - 1); } } else { FoundFlag = FALSE; } } // // If we did not find a date // if (PrevDate[0] == '\0') { DateString[0] = '\0'; return (FALSE); } // // Put the date from chPrevDate's YY/MM/DD format // into pchDateString's MM/DD/YY format DateString[5] = '/'; DateString[6] = PrevDate[0]; DateString[7] = PrevDate[1]; RtlMoveMemory(DateString, &PrevDate[3], 5); DateString[8] = '\0'; return (TRUE); } BOOLEAN CmpGetBiosVersion ( PCHAR SearchArea, ULONG SearchLength, PCHAR VersionString ) /*++ Routine Description: This routine finds the version number stored in ROM, if any. Arguments: SearchArea - the area to search for the version. SearchLength - Length of search VersionString - Supplies a pointer to a fixed length memory to receive the version string. Return Value: TRUE if a version number is found. Else a value of FALSE is returned. --*/ { PCHAR String; USHORT Length; USHORT i; CHAR Buffer[MAXIMUM_BIOS_VERSION_LENGTH]; PCHAR BufferPointer; if (SearchArea != NULL) { // // If caller does not specify the search area, we will search // the area left from previous search. // BiosBegin = SearchArea; Start = SearchArea + 1; End = SearchArea + SearchLength - 2; } while (1) { // // Search for a period with a digit on either side // String = NULL; while (Start <= End) { if (*Start == '.' && *(Start+1) >= '0' && *(Start+1) <= '9' && *(Start-1) >= '0' && *(Start-1) <= '9') { String = Start; break; } else { Start++; } } if (Start > End) { return(FALSE); } else { Start += 2; } Length = 0; Buffer[MAXIMUM_BIOS_VERSION_LENGTH - 1] = '\0'; BufferPointer = &Buffer[MAXIMUM_BIOS_VERSION_LENGTH - 1]; // // Search for the beginning of the string // String--; while (Length < MAXIMUM_BIOS_VERSION_LENGTH - 8 && String >= BiosBegin && *String >= ' ' && *String <= 127 && *String != '$') { --BufferPointer; *BufferPointer = *String; --String, ++Length; } ++String; // // Can one of the search strings be found // for (i = 0; SearchStrings[i]; i++) { if (strstr(BufferPointer, SearchStrings[i])) { goto Found; } } } Found: // // Skip leading white space // for (; *String == ' '; ++String) ; // // Copy the string to user supplied buffer // for (i = 0; i < MAXIMUM_BIOS_VERSION_LENGTH - 1 && String <= (End + 1) && *String >= ' ' && *String <= 127 && *String != '$'; ++i, ++String) { VersionString[i] = *String; } VersionString[i] = '\0'; return (TRUE); } NTSTATUS CmpInitializeMachineDependentConfiguration( IN PLOADER_PARAMETER_BLOCK LoaderBlock ) /*++ Routine Description: This routine creates x86 specific entries in the registry. Arguments: LoaderBlock - supplies a pointer to the LoaderBlock passed in from the OS Loader. Returns: NTSTATUS code for sucess or reason of failure. --*/ { NTSTATUS Status; ULONG VideoBiosStart; UNICODE_STRING KeyName; UNICODE_STRING ValueName; UNICODE_STRING ValueData; ANSI_STRING AnsiString; OBJECT_ATTRIBUTES ObjectAttributes; ULONG Disposition; HANDLE ParentHandle; HANDLE BaseHandle, NpxHandle; HANDLE CurrentControlSet; CONFIGURATION_COMPONENT_DATA CurrentEntry; PUCHAR VendorID; UCHAR Buffer[MAXIMUM_BIOS_VERSION_LENGTH]; PKPRCB Prcb; ULONG i, Junk; ULONG VersionsLength = 0, Length; PCHAR VersionStrings, VersionPointer; UNICODE_STRING SectionName; ULONG ViewSize; LARGE_INTEGER ViewBase; PVOID BaseAddress; HANDLE SectionHandle; UCHAR DeviceIndexTable[NUMBER_TYPES]; for (i = 0; i < NUMBER_TYPES; i++) { DeviceIndexTable[i] = 0; } InitializeObjectAttributes( &ObjectAttributes, &CmRegistryMachineHardwareDescriptionSystemName, OBJ_CASE_INSENSITIVE, NULL, NULL ); Status = NtOpenKey( &ParentHandle, KEY_READ, &ObjectAttributes ); if (!NT_SUCCESS(Status)) { // Something is really wrong... return Status; } // // On an ARC machine the processor(s) are included in the hardware // configuration passed in from bootup. Since there's no standard // way to get all the ARC information for each processor in an MP // machine via pc-ROMs the information will be added here (if it's // not already present). // RtlInitUnicodeString( &KeyName, L"CentralProcessor" ); InitializeObjectAttributes( &ObjectAttributes, &KeyName, 0, ParentHandle, NULL ); ObjectAttributes.Attributes |= OBJ_CASE_INSENSITIVE; Status = NtCreateKey( &BaseHandle, KEY_READ | KEY_WRITE, &ObjectAttributes, TITLE_INDEX_VALUE, &CmClassName[ProcessorClass], 0, &Disposition ); NtClose (BaseHandle); if (Disposition == REG_CREATED_NEW_KEY) { // // The ARC rom didn't add the processor(s) into the registry. // Do it now. // CmpConfigurationData = (PCM_FULL_RESOURCE_DESCRIPTOR)ExAllocatePool( PagedPool, CmpConfigurationAreaSize ); for (i=0; i < (ULONG)KeNumberProcessors; i++) { Prcb = KiProcessorBlock[i]; RtlZeroMemory (&CurrentEntry, sizeof CurrentEntry); CurrentEntry.ComponentEntry.Class = ProcessorClass; CurrentEntry.ComponentEntry.Type = CentralProcessor; CurrentEntry.ComponentEntry.Key = i; CurrentEntry.ComponentEntry.AffinityMask = 1 << i; CurrentEntry.ComponentEntry.Identifier = Buffer; if (Prcb->CpuID == 0) { // // Old style stepping format // sprintf (Buffer, CmpID1, Prcb->CpuType, (Prcb->CpuStep >> 8) + 'A', Prcb->CpuStep & 0xff ); } else { // // New style stepping format // sprintf (Buffer, CmpID2, Prcb->CpuType, (Prcb->CpuStep >> 8), Prcb->CpuStep & 0xff ); } CurrentEntry.ComponentEntry.IdentifierLength = strlen (Buffer) + 1; Status = CmpInitializeRegistryNode( &CurrentEntry, ParentHandle, &BaseHandle, -1, (ULONG)-1, DeviceIndexTable ); if (!NT_SUCCESS(Status)) { return(Status); } if (KeI386NpxPresent) { RtlZeroMemory (&CurrentEntry, sizeof CurrentEntry); CurrentEntry.ComponentEntry.Class = ProcessorClass; CurrentEntry.ComponentEntry.Type = FloatingPointProcessor; CurrentEntry.ComponentEntry.Key = i; CurrentEntry.ComponentEntry.AffinityMask = 1 << i; CurrentEntry.ComponentEntry.Identifier = Buffer; if (Prcb->CpuType == 3) { // // 386 processors have 387's installed, else // use processor identifier as the NPX identifier // strcpy (Buffer, "80387"); } CurrentEntry.ComponentEntry.IdentifierLength = strlen (Buffer) + 1; Status = CmpInitializeRegistryNode( &CurrentEntry, ParentHandle, &NpxHandle, -1, (ULONG)-1, DeviceIndexTable ); if (!NT_SUCCESS(Status)) { NtClose(BaseHandle); return(Status); } NtClose(NpxHandle); } // // If processor supports Cpu Indentification then // go obtain that information for the registry // VendorID = Prcb->CpuID ? Prcb->VendorString : NULL; // // Move to target processor and get other related // processor information for the registery // KeSetSystemAffinityThread(Prcb->SetMember); if (!Prcb->CpuID) { // // Test for Cyrix processor // if (Ke386CyrixId ()) { VendorID = CmpCyrixID; } } // // Restore thread's affinity to all processors // KeRevertToUserAffinityThread(); if (VendorID) { // // Add Vendor Indentifier to the registery // RtlInitUnicodeString( &ValueName, CmpVendorID ); RtlInitAnsiString( &AnsiString, VendorID ); RtlAnsiStringToUnicodeString( &ValueData, &AnsiString, TRUE ); Status = NtSetValueKey( BaseHandle, &ValueName, TITLE_INDEX_VALUE, REG_SZ, ValueData.Buffer, ValueData.Length + sizeof( UNICODE_NULL ) ); RtlFreeUnicodeString(&ValueData); } if (Prcb->FeatureBits) { // // Add processor feature bits to the registery // RtlInitUnicodeString( &ValueName, CmpFeatureBits ); Status = NtSetValueKey( BaseHandle, &ValueName, TITLE_INDEX_VALUE, REG_DWORD, &Prcb->FeatureBits, sizeof (Prcb->FeatureBits) ); } if (Prcb->MHz) { // // Add processor MHz to the registery // RtlInitUnicodeString( &ValueName, CmpMHz ); Status = NtSetValueKey( BaseHandle, &ValueName, TITLE_INDEX_VALUE, REG_DWORD, &Prcb->MHz, sizeof (Prcb->MHz) ); } if (Prcb->UpdateSignature.QuadPart) { // // Add processor MHz to the registery // RtlInitUnicodeString( &ValueName, CmpUpdateSignature ); Status = NtSetValueKey( BaseHandle, &ValueName, TITLE_INDEX_VALUE, REG_BINARY, &Prcb->UpdateSignature, sizeof (Prcb->UpdateSignature) ); } NtClose(BaseHandle); } ExFreePool((PVOID)CmpConfigurationData); } // // Next we try to collect System BIOS date and version strings. // BUGBUG This code should be moved to ntdetect.com after product 1. // // // Open a physical memory section to map in physical memory. // RtlInitUnicodeString( &SectionName, L"\\Device\\PhysicalMemory" ); InitializeObjectAttributes( &ObjectAttributes, &SectionName, OBJ_CASE_INSENSITIVE, (HANDLE) NULL, (PSECURITY_DESCRIPTOR) NULL ); Status = ZwOpenSection( &SectionHandle, SECTION_ALL_ACCESS, &ObjectAttributes ); if (!NT_SUCCESS(Status)) { // // If fail, forget the bios data and version // goto AllDone; } // // Examine the first page of physical memory for int 10 segment // address. // BaseAddress = 0; ViewSize = 0x1000; ViewBase.LowPart = 0; ViewBase.HighPart = 0; Status =ZwMapViewOfSection( SectionHandle, NtCurrentProcess(), &BaseAddress, 0, ViewSize, &ViewBase, &ViewSize, ViewUnmap, MEM_DOS_LIM, PAGE_READWRITE ); if (!NT_SUCCESS(Status)) { VideoBiosStart = VIDEO_BIOS_START; } else { VideoBiosStart = (*((PULONG)BaseAddress + INT10_VECTOR) & 0xFFFF0000) >> 12; VideoBiosStart += (*((PULONG)BaseAddress + INT10_VECTOR) & 0x0000FFFF); VideoBiosStart &= 0xffff8000; if (VideoBiosStart < VIDEO_BIOS_START) { VideoBiosStart = VIDEO_BIOS_START; } Status = ZwUnmapViewOfSection( NtCurrentProcess(), BaseAddress ); } VersionStrings = ExAllocatePool(PagedPool, VERSION_DATA_LENGTH); BaseAddress = 0; ViewSize = SYSTEM_BIOS_LENGTH; ViewBase.LowPart = SYSTEM_BIOS_START; ViewBase.HighPart = 0; Status =ZwMapViewOfSection( SectionHandle, NtCurrentProcess(), &BaseAddress, 0, ViewSize, &ViewBase, &ViewSize, ViewUnmap, MEM_DOS_LIM, PAGE_READWRITE ); if (NT_SUCCESS(Status)) { if (CmpGetBiosDate(BaseAddress, SYSTEM_BIOS_LENGTH, Buffer)) { // // Convert ascii date string to unicode string and // store it in registry. // RtlInitUnicodeString( &ValueName, L"SystemBiosDate" ); RtlInitAnsiString( &AnsiString, Buffer ); RtlAnsiStringToUnicodeString( &ValueData, &AnsiString, TRUE ); Status = NtSetValueKey( ParentHandle, &ValueName, TITLE_INDEX_VALUE, REG_SZ, ValueData.Buffer, ValueData.Length + sizeof( UNICODE_NULL ) ); RtlFreeUnicodeString(&ValueData); } if (VersionStrings && CmpGetBiosVersion(BaseAddress, SYSTEM_BIOS_LENGTH, Buffer)) { VersionPointer = VersionStrings; do { // // Try to detect ALL the possible BIOS version strings. // Convert them to unicode strings and copy them to our // VersionStrings buffer. // RtlInitAnsiString( &AnsiString, Buffer ); RtlAnsiStringToUnicodeString( &ValueData, &AnsiString, TRUE ); Length = ValueData.Length + sizeof(UNICODE_NULL); RtlMoveMemory(VersionPointer, ValueData.Buffer, Length ); VersionsLength += Length; RtlFreeUnicodeString(&ValueData); if (VersionsLength + (MAXIMUM_BIOS_VERSION_LENGTH + sizeof(UNICODE_NULL)) * 2 > PAGE_SIZE) { break; } VersionPointer += Length; } while (CmpGetBiosVersion(NULL, 0, Buffer)); if (VersionsLength != 0) { // // Append a UNICODE_NULL to the end of VersionStrings // *(PWSTR)VersionPointer = UNICODE_NULL; VersionsLength += sizeof(UNICODE_NULL); // // If any version string is found, we set up a ValueName and // initialize its value to the string(s) we found. // RtlInitUnicodeString( &ValueName, L"SystemBiosVersion" ); Status = NtSetValueKey( ParentHandle, &ValueName, TITLE_INDEX_VALUE, REG_MULTI_SZ, VersionStrings, VersionsLength ); } } ZwUnmapViewOfSection(NtCurrentProcess(), BaseAddress); } // // Next we try to collect Video BIOS date and version strings. // BaseAddress = 0; ViewSize = VIDEO_BIOS_LENGTH; ViewBase.LowPart = VideoBiosStart; ViewBase.HighPart = 0; Status =ZwMapViewOfSection( SectionHandle, NtCurrentProcess(), &BaseAddress, 0, ViewSize, &ViewBase, &ViewSize, ViewUnmap, MEM_DOS_LIM, PAGE_READWRITE ); if (NT_SUCCESS(Status)) { if (CmpGetBiosDate(BaseAddress, VIDEO_BIOS_LENGTH, Buffer)) { RtlInitUnicodeString( &ValueName, L"VideoBiosDate" ); RtlInitAnsiString( &AnsiString, Buffer ); RtlAnsiStringToUnicodeString( &ValueData, &AnsiString, TRUE ); Status = NtSetValueKey( ParentHandle, &ValueName, TITLE_INDEX_VALUE, REG_SZ, ValueData.Buffer, ValueData.Length + sizeof( UNICODE_NULL ) ); RtlFreeUnicodeString(&ValueData); } if (VersionStrings && CmpGetBiosVersion(BaseAddress, VIDEO_BIOS_LENGTH, Buffer)) { VersionPointer = VersionStrings; do { // // Try to detect ALL the possible BIOS version strings. // Convert them to unicode strings and copy them to our // VersionStrings buffer. // RtlInitAnsiString( &AnsiString, Buffer ); RtlAnsiStringToUnicodeString( &ValueData, &AnsiString, TRUE ); Length = ValueData.Length + sizeof(UNICODE_NULL); RtlMoveMemory(VersionPointer, ValueData.Buffer, Length ); VersionsLength += Length; RtlFreeUnicodeString(&ValueData); if (VersionsLength + (MAXIMUM_BIOS_VERSION_LENGTH + sizeof(UNICODE_NULL)) * 2 > PAGE_SIZE) { break; } VersionPointer += Length; } while (CmpGetBiosVersion(NULL, 0, Buffer)); if (VersionsLength != 0) { // // Append a UNICODE_NULL to the end of VersionStrings // *(PWSTR)VersionPointer = UNICODE_NULL; VersionsLength += sizeof(UNICODE_NULL); RtlInitUnicodeString( &ValueName, L"VideoBiosVersion" ); Status = NtSetValueKey( ParentHandle, &ValueName, TITLE_INDEX_VALUE, REG_MULTI_SZ, VersionStrings, VersionsLength ); } } ZwUnmapViewOfSection(NtCurrentProcess(), BaseAddress); } ZwClose(SectionHandle); if (VersionStrings) { ExFreePool((PVOID)VersionStrings); } AllDone: NtClose (ParentHandle); // // Add any other x86 specific code here... // return STATUS_SUCCESS; }