#include "setupp.h" #pragma hdrstop // // TRUE if we detected a flawed pentium chip. // BOOL FlawedPentium; // // TRUE if NPX emulation is forced on. // Flag indicating what user wants to do. // BOOL CurrentNpxSetting; BOOL UserNpxSetting; // // Name of value in HKLM\System\CurrentControlSet\Control\Session Manager // controlling npx emulation. // PCWSTR NpxEmulationKey = L"System\\CurrentControlSet\\Control\\Session Manager"; PCWSTR NpxEmulationValue = L"ForceNpxEmulation"; BOOL TestForDivideError( VOID ); int ms_p5_test_fdiv( void ); VOID CheckPentium( VOID ) /*++ Routine Description: Check all processor(s) for the Pentium floating-point devide errata. Arguments: None. Return Value: None. Global variables FlawedPentium, CurrentNpxSetting, and UserNpxSetting will be filled in. --*/ { LONG rc; HKEY hKey; DWORD DataType; DWORD ForcedOn; DWORD DataSize; static LONG CheckedPentium = -1; // // If we didn't already check it CheckedPentium will become 0 // with this increment. If we already checked it then CheckedPentium // will become something greater than 0. // if(InterlockedIncrement(&CheckedPentium)) { return; } // // Perform division test to see whether pentium is flawed. // if(FlawedPentium = TestForDivideError()) { SetuplogError( LogSevInformation, SETUPLOG_USE_MESSAGEID, MSG_LOG_FLAWED_PENTIUM, 0,0); } // // Check registry to see whether npx is currently forced on. Assume not. // CurrentNpxSetting = 0; rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE,NpxEmulationKey,0,KEY_QUERY_VALUE,&hKey); if(rc == NO_ERROR) { DataSize = sizeof(DWORD); rc = RegQueryValueEx( hKey, NpxEmulationValue, 0, &DataType, (PBYTE)&ForcedOn, &DataSize ); // // If the value isn't present then assume emulation // is not currently forced on. Otherwise the value tells us // whether emulation is forced on. // CurrentNpxSetting = (rc == NO_ERROR) ? ForcedOn : 0; if(rc == ERROR_FILE_NOT_FOUND) { rc = NO_ERROR; // prevent bogus warning from being logged. } RegCloseKey(hKey); } if(rc != NO_ERROR) { SetuplogError( LogSevWarning, SETUPLOG_USE_MESSAGEID, MSG_LOG_UNABLE_TO_CHECK_NPX_SETTING, rc, 0,0); } // // For now set user's choice to the current setting. // UserNpxSetting = CurrentNpxSetting; } BOOL SetNpxEmulationState( VOID ) /*++ Routine Description: Set state of NPX emulation based on current state of global variables CurrentNpxSetting and UserNpxSetting. Arguments: None. Return Value: Boolean value indicating outcome. --*/ { LONG rc; HKEY hKey; DWORD DataType; DWORD ForcedOn; DWORD DataSize; // // Nothing to to if the setting has not changed. // if(CurrentNpxSetting == UserNpxSetting) { return(TRUE); } rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE,NpxEmulationKey,0,KEY_SET_VALUE,&hKey); if(rc == NO_ERROR) { rc = RegSetValueEx( hKey, NpxEmulationValue, 0, REG_DWORD, (PBYTE)&UserNpxSetting, sizeof(DWORD) ); if(rc == NO_ERROR) { CurrentNpxSetting = UserNpxSetting; } RegCloseKey(hKey); } if(rc != NO_ERROR) { SetuplogError( LogSevWarning, SETUPLOG_USE_MESSAGEID, MSG_LOG_UNABLE_TO_SET_NPX_SETTING, rc, 0,0); } return(rc == NO_ERROR); } BOOL TestForDivideError( VOID ) /*++ Routine Description: Do a divide with a known divident/divisor pair, followed by a multiply to see if we get the right answer back. Arguments: None. Return Value: Boolean value indicating whether the computer exhibits the pentium fpu bug. --*/ { DWORD pick; DWORD processmask; DWORD systemmask; DWORD i; BOOL rc; // // Assume no fpu bug. // rc = FALSE; // // Fetch the affinity mask, which is also effectively a list // of processors // GetProcessAffinityMask(GetCurrentProcess(),&processmask,&systemmask); // // Step through the mask, testing each cpu. // if any is bad, we treat them all as bad // for(i = 0; i < 32; i++) { pick = 1 << i; if(systemmask & pick) { SetThreadAffinityMask(GetCurrentThread(), pick); // // Call the critical test function // if(ms_p5_test_fdiv()) { rc = TRUE; break; } } } // // Reset affinity for this thread before returning. // SetThreadAffinityMask(GetCurrentThread(), processmask); return(rc); } /*** * testfdiv.c - routine to test for correct operation of x86 FDIV instruction. * * Copyright (c) 1994, Microsoft Corporation. All rights reserved. * *Purpose: * Detects early steppings of Pentium with incorrect FDIV tables using * 'official' Intel test values. Returns 1 if flawed Pentium is detected, * 0 otherwise. * */ int ms_p5_test_fdiv(void) { double dTestDivisor = 3145727.0; double dTestDividend = 4195835.0; double dRslt; _asm { fld qword ptr [dTestDividend] fdiv qword ptr [dTestDivisor] fmul qword ptr [dTestDivisor] fsubr qword ptr [dTestDividend] fstp qword ptr [dRslt] } return (dRslt > 1.0); } BOOL CALLBACK PentiumDlgProc( IN HWND hdlg, IN UINT msg, IN WPARAM wParam, IN LPARAM lParam ) { NMHDR *NotifyParams; switch(msg) { case WM_INITDIALOG: // // Check the pentium. // CheckPentium(); // // Set up default. If user setting is non-0, then some kind // of emulation is turned on (there are 2 possibilities). // CheckRadioButton( hdlg, IDC_RADIO_1, IDC_RADIO_2, UserNpxSetting ? IDC_RADIO_2 : IDC_RADIO_1 ); break; case WM_SIMULATENEXT: PropSheet_PressButton( GetParent(hdlg), PSBTN_NEXT); break; case WM_NOTIFY: NotifyParams = (NMHDR *)lParam; switch(NotifyParams->code) { case PSN_SETACTIVE: TESTHOOK(522); SetWizardButtons(hdlg,WizPagePentiumErrata); if (FlawedPentium || UiTest) { if(Unattended) { // // This call makes the dialog activate, meaning // we end up going through the PSN_WIZNEXT code below. // if (!UnattendSetActiveDlg(hdlg, IDD_PENTIUM)) { break; } // Page becomes active, make page visible. SendMessage(GetParent(hdlg), WMX_BBTEXT, (WPARAM)FALSE, 0); } else { SetWindowLong(hdlg,DWL_MSGRESULT, 0); // Page becomes active, make page visible. SendMessage(GetParent(hdlg), WMX_BBTEXT, (WPARAM)FALSE, 0); } } else { SetWindowLong(hdlg,DWL_MSGRESULT,-1); } break; case PSN_WIZNEXT: case PSN_WIZFINISH: // // Fetch emulation state. If user wants emulation and emulation // was already turned on preserve the current emulation setting. // Otherwise use setting 1. // if(IsDlgButtonChecked(hdlg,IDC_RADIO_2)) { if(!UserNpxSetting) { UserNpxSetting = 1; } } else { UserNpxSetting = 0; } break; default: break; } break; default: return(FALSE); } return(TRUE); }