windows-nt/Source/XPSP1/NT/base/ntsetup/syssetup/i386/fpu.c
2020-09-26 16:20:57 +08:00

398 lines
8.1 KiB
C

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