|
|
#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); }
|