|
|
/*++
Copyright (c) 1994-2000 Microsoft Corporation
Module Name:
pentnt.c
Abstract:
This module contains a simple program to detect the Pentium FPU FDIV precision error, and offers to force floating point emulation on if the bug is present.
Author:
Bryan M. Willman (bryanwi) 7-Dec-1994
Revision History:
--*/
#define UNICODE
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <io.h>
#include <windows.h>
#include <ctype.h>
#include <assert.h>
#include <locale.h>
#include <stdarg.h>
#include "pbmsg.h"
void SetForceNpxEmulation(ULONG setting); void TestForDivideError(); void ScanArgs(int argc, char **argv); void GetSystemState(); void printmessage (DWORD messageID, ...); int ms_p5_test_fdiv(void);
//
// Core control state vars
//
BOOLEAN NeedHelp;
BOOLEAN Force; ULONG ForceValue;
BOOLEAN FDivError;
BOOLEAN NTOK;
ULONG CurrentForceValue;
ULONG FloatHardware;
//
// ForceValue and CurrentForceValue
//
#define FORCE_OFF 0 // User wants emulation turned off
#define FORCE_CONDITIONAL 1 // User wants emulation iff we detect bad pentium
#define FORCE_ALWAYS 2 // User wants emulation regardless
//
// hardware fp status
//
#define FLOAT_NONE 0 // No fp hardware
#define FLOAT_ON 1 // Fp hardware is present and active
#define FLOAT_OFF 2 // Fp hardware is present and disabled
void __cdecl main( int argc, char **argv ) /*++
Routine Description:
Main procedure for pentnt.
First, we call a series of routines that build a state vector in some booleans.
We'll then act on these control variables:
NeedHelp - User has asked for help, or made a command error
Force - True if user has asked to change a force setting ForceValue - Has no meaning if Force is FALSE. Else says what the user wants us to do.
FloatHardware - Indicates if there is any and whether it's on
NTOK - Indicates if first OS version with fix is what we are running
FdivError - if TRUE, FP gives WRONG answer, else, gives right answer CurrentForceValue - what the current force setting is
All of these will be set before we do any work.
Arguments:
argc - count of arguments, including the name of our proggram
argv - argument list - see command line syntax above
Return Value:
Exit(0) - nothing changed, and current state is OK
Exit(1) - either a state change was requested, or just help, or the current state may have a problem.
Exit(2) - we hit something really weird....
--*/ { CHAR lBuf[16]; DWORD dwCodePage; LANGID LangId;
//
// build up state vector in global booleans
//
ScanArgs(argc, argv); GetSystemState(); TestForDivideError();
/*
printf("NeedHelp = %d Force = %d ForceValue = %d\n", NeedHelp, Force, ForceValue); printf("FDivError = %d NTOK = %d CurrentForceValue = %d FloatHardware = %d\n", FDivError, NTOK, CurrentForceValue, FloatHardware); */
// Since FormatMessage checks the current TEB's locale, and the Locale for
// CHCP is initialized when the message class is initialized, the TEB has to
// be updated after the code page is changed successfully.
// Why are we doing this, you ask. Well, the FE guys have plans to add
// more than one set of language resources to this module, but not all
// the possible resources. So this limited set is what they plan for.
// If FormatMessage can't find the right language, it falls back to
// something hopefully useful.
// Set the console output CP to OEMCP.
SetConsoleOutputCP(GetOEMCP());
dwCodePage = GetConsoleOutputCP();
sprintf(lBuf, ".%d", dwCodePage);
switch( dwCodePage ) { case 932: LangId = MAKELANGID( LANG_JAPANESE, SUBLANG_DEFAULT ); break; case 949: LangId = MAKELANGID( LANG_KOREAN, SUBLANG_KOREAN ); break; case 936: LangId = MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED ); break; case 950: LangId = MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL ); break; default: LangId = PRIMARYLANGID(LANGIDFROMLCID( GetUserDefaultLCID() )); if (LangId == LANG_JAPANESE || LangId == LANG_KOREAN || LangId == LANG_CHINESE ) { LangId = MAKELANGID( LANG_ENGLISH, SUBLANG_ENGLISH_US ); } else { LangId = MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ); } break; }
SetThreadLocale( MAKELCID(LangId, SORT_DEFAULT) ); setlocale(LC_ALL, lBuf);
//
// ok, we know the state of the command and the machine, do work
//
//
// if they asked for help, or did something that indicates they don't
// understand how the program works, print help and exit.
//
if (NeedHelp) { printmessage(MSG_PENTBUG_HELP); exit(1); }
//
// never do anything if there's no floating point hardware in the box
//
if (FloatHardware == FLOAT_NONE) { printmessage(MSG_PENTBUG_NO_FLOAT_HARDWARE); exit(0); }
//
// never do anything if it's the wrong version of NT.
//
if (!NTOK) { printmessage(MSG_PENTBUG_NEED_NTOK); exit(1); }
assert(NTOK == TRUE); assert(NeedHelp == FALSE); assert((FloatHardware == FLOAT_ON) || (FloatHardware == FLOAT_OFF));
if (Force) {
switch (ForceValue) {
case FORCE_OFF:
if (CurrentForceValue == FORCE_OFF) {
if (FloatHardware == FLOAT_ON) { //
// user wants fp on, fp is on, fp set to be on
// all is as it should be
//
printmessage(MSG_PENTBUG_IS_OFF_OK); exit(FDivError); }
if (FloatHardware == FLOAT_OFF) { //
// user need to reboot to finish turning emulation off
//
printmessage(MSG_PENTBUG_IS_OFF_REBOOT); exit(1); }
} else { //
// they want it off, it's not off, so turn it off
// remind them to reboot
//
SetForceNpxEmulation(FORCE_OFF); printmessage(MSG_PENTBUG_TURNED_OFF); printmessage(MSG_PENTBUG_REBOOT); exit(1); } break;
case FORCE_CONDITIONAL:
if (CurrentForceValue == FORCE_CONDITIONAL) {
if (FDivError) { //
// tell them to reboot
//
printmessage(MSG_PENTBUG_IS_ON_COND_REBOOT); exit(1); } else { //
// tell them to be happy
//
printmessage(MSG_PENTBUG_IS_ON_COND_OK); exit(0); }
} else { //
// set it to what they want and tell them to reboot
//
SetForceNpxEmulation(ForceValue); printmessage(MSG_PENTBUG_TURNED_ON_CONDITIONAL); printmessage(MSG_PENTBUG_REBOOT); exit(1); } break;
case FORCE_ALWAYS:
if (CurrentForceValue == FORCE_ALWAYS) {
if (FloatHardware == FLOAT_OFF) { //
// tell them to be happy
//
printmessage(MSG_PENTBUG_IS_ON_ALWAYS_OK); exit(0); } else { //
// tell them to reboot to finish
//
printmessage(MSG_PENTBUG_IS_ON_ALWAYS_REBOOT); exit(1); }
} else { SetForceNpxEmulation(ForceValue); printmessage(MSG_PENTBUG_TURNED_ON_ALWAYS); printmessage(MSG_PENTBUG_REBOOT); exit(1); } break;
default: printf("pentnt: INTERNAL ERROR\n"); exit(2);
} // switch
}
//
// no action requested, just report state and give advice
//
assert(Force == FALSE);
if (!FDivError) {
if (FloatHardware == FLOAT_ON) { printmessage(MSG_PENTBUG_FLOAT_WORKS); } else { printmessage(MSG_PENTBUG_EMULATION_ON_AND_WORKS); } exit(0); }
//
// since we're here, we have an fdiv error, tell user what to do about it
//
assert(FDivError);
printmessage(MSG_PENTBUG_FDIV_ERROR);
if ((CurrentForceValue == FORCE_CONDITIONAL) || (CurrentForceValue == FORCE_ALWAYS)) { printmessage(MSG_PENTBUG_IS_ON_REBOOT); exit(1); }
printmessage(MSG_PENTBUG_CRITICAL_WORK); exit(1);
assert((TRUE==FALSE)); }
VOID SetForceNpxEmulation( ULONG Setting ) /*++
Routine Description:
SetForceNpxEmulation will simply set the ForceNpxEmulation value entry under the Session Manager key to the value passed in. 0 = off 1 = conditional 2 = always
If the set attempt fails, exit with a message.
--*/ { HKEY hkey; LONG rc;
rc = RegOpenKeyEx( HKEY_LOCAL_MACHINE, TEXT("System\\CurrentControlSet\\Control\\Session Manager"), 0, KEY_WRITE, &hkey );
if (rc != ERROR_SUCCESS) { printmessage(MSG_PENTBUG_SET_FAILED, rc); exit(2); }
rc = RegSetValueEx( hkey, TEXT("ForceNpxEmulation"), 0, REG_DWORD, (unsigned char *)&Setting, sizeof(ULONG) );
if (rc != ERROR_SUCCESS) { printmessage(MSG_PENTBUG_SET_FAILED, rc); exit(2); }
return; }
VOID ScanArgs( int argc, char **argv ) /*++
Routine Description:
ScanArgs - parse command line arguments, and set control flags to reflect what we find.
Sets NeedHelp, Force, ForceValue.
Arguments:
argc - count of command line args
argv - argument vector
Return Value:
--*/ { int i;
Force = FALSE; NeedHelp = FALSE;
for (i = 1; i < argc; i++) { if ( ! ((argv[i][0] == '-') || (argv[i][0] == '/')) ) { NeedHelp = TRUE; goto done; }
switch (argv[i][1]) {
case '?': case 'h': case 'H': NeedHelp = TRUE; break;
case 'c': case 'C': if (Force) { NeedHelp = TRUE; } else { Force = TRUE; ForceValue = FORCE_CONDITIONAL; } break;
case 'f': case 'F': if (Force) { NeedHelp = TRUE; } else { Force = TRUE; ForceValue = FORCE_ALWAYS; } break;
case 'o': case 'O': if (Force) { NeedHelp = TRUE; } else { Force = TRUE; ForceValue = FORCE_OFF; } break;
default: NeedHelp = TRUE; goto done; } }
done: if (NeedHelp) { Force = FALSE; } return; }
VOID GetSystemState( ) /*++
Routine Description:
GetSystemState - get the system version, whether the computer has FP hardware or not, and whether the force emulation switch is already set or not.
Sets FloatHardware, NTOK, CurrentForceValue
Arguments:
Return Value:
--*/ { HKEY hkey; TCHAR Buffer[32]; DWORD BufferSize = 32; DWORD Type; int major; int minor; LONG rc; PULONG p; OSVERSIONINFO OsVersionInfo;
NTOK = FALSE;
//
// decide if the system version is OK.
//
OsVersionInfo.dwOSVersionInfoSize = sizeof(OsVersionInfo); GetVersionEx(&OsVersionInfo);
if (OsVersionInfo.dwPlatformId != VER_PLATFORM_WIN32_NT) { printmessage(MSG_PENTBUG_NOT_NT); exit(2); }
if ( (OsVersionInfo.dwMajorVersion > 3) || ( (OsVersionInfo.dwMajorVersion == 3) && (OsVersionInfo.dwMinorVersion >= 51) )) { //
// build 3.51 or greater, it has the fix
//
NTOK = TRUE;
} else if ( (OsVersionInfo.dwMajorVersion == 3) && (OsVersionInfo.dwMinorVersion == 50)) { if (OsVersionInfo.szCSDVersion[0] != (TCHAR)'\0') { //
// we have a service pack for 3.5, since pack 1 and
// later have the fix, it's OK
//
NTOK = TRUE; } } /*
printf("debug NTOK forced true for testing\n\n\n"); NTOK = TRUE; */
//
// determine if float hardware is present
//
rc = RegOpenKeyEx( HKEY_LOCAL_MACHINE, TEXT("Hardware\\Description\\System\\FloatingPointProcessor"), 0, KEY_READ, &hkey );
if (rc == ERROR_SUCCESS) {
FloatHardware = FLOAT_ON; RegCloseKey(hkey);
} else {
rc = RegOpenKeyEx( HKEY_LOCAL_MACHINE, TEXT("Hardware\\Description\\System\\DisabledFloatingPointProcessor"), 0, KEY_READ, &hkey );
if (rc == ERROR_SUCCESS) {
FloatHardware = FLOAT_OFF; RegCloseKey(hkey);
} else {
FloatHardware = FLOAT_NONE;
} }
//
// determine if emulation has been forced on
//
rc = RegOpenKeyEx( HKEY_LOCAL_MACHINE, TEXT("System\\CurrentControlSet\\Control\\Session Manager"), 0, KEY_READ, &hkey );
if (rc != ERROR_SUCCESS) { return; }
BufferSize = 32; rc = RegQueryValueEx( hkey, TEXT("ForceNpxEmulation"), 0, &Type, (unsigned char *)Buffer, &BufferSize );
if ( (rc == ERROR_SUCCESS) && (Type == REG_DWORD) ) { p = (PULONG)Buffer; CurrentForceValue = *p; }
return; }
//
// these must be globals to make the compiler do the right thing
//
VOID TestForDivideError( ) /*++
Routine Description:
Do a divide with a known divident/divisor pair, followed by a multiply to see if we get the right answer back.
FDivError = TRUE if we get the WRONG answer, FALSE. Arguments:
Return Value:
--*/ { DWORD pick; HANDLE ph; DWORD processmask; DWORD systemmask; ULONG i;
//
// fetch the affinity mask, which is also effectively a list
// of processors
//
ph = GetCurrentProcess(); GetProcessAffinityMask(ph, &processmask, &systemmask);
//
// step through the mask, testing each cpu.
// if any is bad, we treat them all as bad
//
FDivError = FALSE; for (i = 0; i < 32; i++) { pick = 1 << i;
if ((systemmask & pick) != 0) {
//*//printf("pick = %08lx\n", pick);
SetThreadAffinityMask(GetCurrentThread(), pick);
//
// call the official test function
//
if (ms_p5_test_fdiv()) { //
// do NOT just assign func to FDivError, that will reset
// it if a second cpu is good. must be one way flag
//
FDivError = TRUE; }
} // IF
} // for
return; }
/***
* testfdiv.c - routine to test for correct operation of x86 FDIV instruction. * *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); }
//
// Call FormatMessage and dump the result. All messages to Stdout
//
void printmessage (DWORD messageID, ...) { unsigned short messagebuffer[4096]; va_list ap;
va_start(ap, messageID);
FormatMessage(FORMAT_MESSAGE_FROM_HMODULE, NULL, messageID, 0, messagebuffer, 4095, &ap);
wprintf(messagebuffer);
va_end(ap); }
|