summaryrefslogtreecommitdiffstats
path: root/private/utils/pentbug/pentnt.c
diff options
context:
space:
mode:
Diffstat (limited to 'private/utils/pentbug/pentnt.c')
-rw-r--r--private/utils/pentbug/pentnt.c694
1 files changed, 694 insertions, 0 deletions
diff --git a/private/utils/pentbug/pentnt.c b/private/utils/pentbug/pentnt.c
new file mode 100644
index 000000000..639b2e5ec
--- /dev/null
+++ b/private/utils/pentbug/pentnt.c
@@ -0,0 +1,694 @@
+/*++
+
+Copyright (c) 1994 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 <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
+_CRTAPI1
+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....
+
+
+--*/
+{
+ //
+ // 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);
+ */
+
+ //
+ // 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
+ );
+
+ 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.
+*
+* 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);
+}
+
+
+//
+// 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);
+}
+