Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

749 lines
17 KiB

  1. /*++
  2. Copyright (c) 1994-2000 Microsoft Corporation
  3. Module Name:
  4. pentnt.c
  5. Abstract:
  6. This module contains a simple program to detect the Pentium FPU
  7. FDIV precision error, and offers to force floating point emulation
  8. on if the bug is present.
  9. Author:
  10. Bryan M. Willman (bryanwi) 7-Dec-1994
  11. Revision History:
  12. --*/
  13. #define UNICODE
  14. #include <stdio.h>
  15. #include <time.h>
  16. #include <stdlib.h>
  17. #include <string.h>
  18. #include <fcntl.h>
  19. #include <io.h>
  20. #include <windows.h>
  21. #include <ctype.h>
  22. #include <assert.h>
  23. #include <locale.h>
  24. #include <stdarg.h>
  25. #include "pbmsg.h"
  26. void SetForceNpxEmulation(ULONG setting);
  27. void TestForDivideError();
  28. void ScanArgs(int argc, char **argv);
  29. void GetSystemState();
  30. void printmessage (DWORD messageID, ...);
  31. int ms_p5_test_fdiv(void);
  32. //
  33. // Core control state vars
  34. //
  35. BOOLEAN NeedHelp;
  36. BOOLEAN Force;
  37. ULONG ForceValue;
  38. BOOLEAN FDivError;
  39. BOOLEAN NTOK;
  40. ULONG CurrentForceValue;
  41. ULONG FloatHardware;
  42. //
  43. // ForceValue and CurrentForceValue
  44. //
  45. #define FORCE_OFF 0 // User wants emulation turned off
  46. #define FORCE_CONDITIONAL 1 // User wants emulation iff we detect bad pentium
  47. #define FORCE_ALWAYS 2 // User wants emulation regardless
  48. //
  49. // hardware fp status
  50. //
  51. #define FLOAT_NONE 0 // No fp hardware
  52. #define FLOAT_ON 1 // Fp hardware is present and active
  53. #define FLOAT_OFF 2 // Fp hardware is present and disabled
  54. void
  55. __cdecl
  56. main(
  57. int argc,
  58. char **argv
  59. )
  60. /*++
  61. Routine Description:
  62. Main procedure for pentnt.
  63. First, we call a series of routines that build a state vector
  64. in some booleans.
  65. We'll then act on these control variables:
  66. NeedHelp - User has asked for help, or made a command error
  67. Force - True if user has asked to change a force setting
  68. ForceValue - Has no meaning if Force is FALSE. Else says
  69. what the user wants us to do.
  70. FloatHardware - Indicates if there is any and whether it's on
  71. NTOK - Indicates if first OS version with fix is what
  72. we are running
  73. FdivError - if TRUE, FP gives WRONG answer, else, gives right answer
  74. CurrentForceValue - what the current force setting is
  75. All of these will be set before we do any work.
  76. Arguments:
  77. argc - count of arguments, including the name of our proggram
  78. argv - argument list - see command line syntax above
  79. Return Value:
  80. Exit(0) - nothing changed, and current state is OK
  81. Exit(1) - either a state change was requested, or just help,
  82. or the current state may have a problem.
  83. Exit(2) - we hit something really weird....
  84. --*/
  85. {
  86. CHAR lBuf[16];
  87. DWORD dwCodePage;
  88. LANGID LangId;
  89. //
  90. // build up state vector in global booleans
  91. //
  92. ScanArgs(argc, argv);
  93. GetSystemState();
  94. TestForDivideError();
  95. /*
  96. printf("NeedHelp = %d Force = %d ForceValue = %d\n",
  97. NeedHelp, Force, ForceValue);
  98. printf("FDivError = %d NTOK = %d CurrentForceValue = %d FloatHardware = %d\n",
  99. FDivError, NTOK, CurrentForceValue, FloatHardware);
  100. */
  101. // Since FormatMessage checks the current TEB's locale, and the Locale for
  102. // CHCP is initialized when the message class is initialized, the TEB has to
  103. // be updated after the code page is changed successfully.
  104. // Why are we doing this, you ask. Well, the FE guys have plans to add
  105. // more than one set of language resources to this module, but not all
  106. // the possible resources. So this limited set is what they plan for.
  107. // If FormatMessage can't find the right language, it falls back to
  108. // something hopefully useful.
  109. // Set the console output CP to OEMCP.
  110. SetConsoleOutputCP(GetOEMCP());
  111. dwCodePage = GetConsoleOutputCP();
  112. sprintf(lBuf, ".%d", dwCodePage);
  113. switch( dwCodePage )
  114. {
  115. case 932:
  116. LangId = MAKELANGID( LANG_JAPANESE, SUBLANG_DEFAULT );
  117. break;
  118. case 949:
  119. LangId = MAKELANGID( LANG_KOREAN, SUBLANG_KOREAN );
  120. break;
  121. case 936:
  122. LangId = MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED );
  123. break;
  124. case 950:
  125. LangId = MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL );
  126. break;
  127. default:
  128. LangId = PRIMARYLANGID(LANGIDFROMLCID( GetUserDefaultLCID() ));
  129. if (LangId == LANG_JAPANESE ||
  130. LangId == LANG_KOREAN ||
  131. LangId == LANG_CHINESE ) {
  132. LangId = MAKELANGID( LANG_ENGLISH, SUBLANG_ENGLISH_US );
  133. }
  134. else {
  135. LangId = MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT );
  136. }
  137. break;
  138. }
  139. SetThreadLocale( MAKELCID(LangId, SORT_DEFAULT) );
  140. setlocale(LC_ALL, lBuf);
  141. //
  142. // ok, we know the state of the command and the machine, do work
  143. //
  144. //
  145. // if they asked for help, or did something that indicates they don't
  146. // understand how the program works, print help and exit.
  147. //
  148. if (NeedHelp) {
  149. printmessage(MSG_PENTBUG_HELP);
  150. exit(1);
  151. }
  152. //
  153. // never do anything if there's no floating point hardware in the box
  154. //
  155. if (FloatHardware == FLOAT_NONE) {
  156. printmessage(MSG_PENTBUG_NO_FLOAT_HARDWARE);
  157. exit(0);
  158. }
  159. //
  160. // never do anything if it's the wrong version of NT.
  161. //
  162. if (!NTOK) {
  163. printmessage(MSG_PENTBUG_NEED_NTOK);
  164. exit(1);
  165. }
  166. assert(NTOK == TRUE);
  167. assert(NeedHelp == FALSE);
  168. assert((FloatHardware == FLOAT_ON) || (FloatHardware == FLOAT_OFF));
  169. if (Force) {
  170. switch (ForceValue) {
  171. case FORCE_OFF:
  172. if (CurrentForceValue == FORCE_OFF) {
  173. if (FloatHardware == FLOAT_ON) {
  174. //
  175. // user wants fp on, fp is on, fp set to be on
  176. // all is as it should be
  177. //
  178. printmessage(MSG_PENTBUG_IS_OFF_OK);
  179. exit(FDivError);
  180. }
  181. if (FloatHardware == FLOAT_OFF) {
  182. //
  183. // user need to reboot to finish turning emulation off
  184. //
  185. printmessage(MSG_PENTBUG_IS_OFF_REBOOT);
  186. exit(1);
  187. }
  188. } else {
  189. //
  190. // they want it off, it's not off, so turn it off
  191. // remind them to reboot
  192. //
  193. SetForceNpxEmulation(FORCE_OFF);
  194. printmessage(MSG_PENTBUG_TURNED_OFF);
  195. printmessage(MSG_PENTBUG_REBOOT);
  196. exit(1);
  197. }
  198. break;
  199. case FORCE_CONDITIONAL:
  200. if (CurrentForceValue == FORCE_CONDITIONAL) {
  201. if (FDivError) {
  202. //
  203. // tell them to reboot
  204. //
  205. printmessage(MSG_PENTBUG_IS_ON_COND_REBOOT);
  206. exit(1);
  207. } else {
  208. //
  209. // tell them to be happy
  210. //
  211. printmessage(MSG_PENTBUG_IS_ON_COND_OK);
  212. exit(0);
  213. }
  214. } else {
  215. //
  216. // set it to what they want and tell them to reboot
  217. //
  218. SetForceNpxEmulation(ForceValue);
  219. printmessage(MSG_PENTBUG_TURNED_ON_CONDITIONAL);
  220. printmessage(MSG_PENTBUG_REBOOT);
  221. exit(1);
  222. }
  223. break;
  224. case FORCE_ALWAYS:
  225. if (CurrentForceValue == FORCE_ALWAYS) {
  226. if (FloatHardware == FLOAT_OFF) {
  227. //
  228. // tell them to be happy
  229. //
  230. printmessage(MSG_PENTBUG_IS_ON_ALWAYS_OK);
  231. exit(0);
  232. } else {
  233. //
  234. // tell them to reboot to finish
  235. //
  236. printmessage(MSG_PENTBUG_IS_ON_ALWAYS_REBOOT);
  237. exit(1);
  238. }
  239. } else {
  240. SetForceNpxEmulation(ForceValue);
  241. printmessage(MSG_PENTBUG_TURNED_ON_ALWAYS);
  242. printmessage(MSG_PENTBUG_REBOOT);
  243. exit(1);
  244. }
  245. break;
  246. default:
  247. printf("pentnt: INTERNAL ERROR\n");
  248. exit(2);
  249. } // switch
  250. }
  251. //
  252. // no action requested, just report state and give advice
  253. //
  254. assert(Force == FALSE);
  255. if (!FDivError) {
  256. if (FloatHardware == FLOAT_ON) {
  257. printmessage(MSG_PENTBUG_FLOAT_WORKS);
  258. } else {
  259. printmessage(MSG_PENTBUG_EMULATION_ON_AND_WORKS);
  260. }
  261. exit(0);
  262. }
  263. //
  264. // since we're here, we have an fdiv error, tell user what to do about it
  265. //
  266. assert(FDivError);
  267. printmessage(MSG_PENTBUG_FDIV_ERROR);
  268. if ((CurrentForceValue == FORCE_CONDITIONAL) ||
  269. (CurrentForceValue == FORCE_ALWAYS))
  270. {
  271. printmessage(MSG_PENTBUG_IS_ON_REBOOT);
  272. exit(1);
  273. }
  274. printmessage(MSG_PENTBUG_CRITICAL_WORK);
  275. exit(1);
  276. assert((TRUE==FALSE));
  277. }
  278. VOID
  279. SetForceNpxEmulation(
  280. ULONG Setting
  281. )
  282. /*++
  283. Routine Description:
  284. SetForceNpxEmulation will simply set the ForceNpxEmulation value
  285. entry under the Session Manager key to the value passed in.
  286. 0 = off
  287. 1 = conditional
  288. 2 = always
  289. If the set attempt fails, exit with a message.
  290. --*/
  291. {
  292. HKEY hkey;
  293. LONG rc;
  294. rc = RegOpenKeyEx(
  295. HKEY_LOCAL_MACHINE,
  296. TEXT("System\\CurrentControlSet\\Control\\Session Manager"),
  297. 0,
  298. KEY_WRITE,
  299. &hkey
  300. );
  301. if (rc != ERROR_SUCCESS) {
  302. printmessage(MSG_PENTBUG_SET_FAILED, rc);
  303. exit(2);
  304. }
  305. rc = RegSetValueEx(
  306. hkey,
  307. TEXT("ForceNpxEmulation"),
  308. 0,
  309. REG_DWORD,
  310. (unsigned char *)&Setting,
  311. sizeof(ULONG)
  312. );
  313. if (rc != ERROR_SUCCESS) {
  314. printmessage(MSG_PENTBUG_SET_FAILED, rc);
  315. exit(2);
  316. }
  317. return;
  318. }
  319. VOID
  320. ScanArgs(
  321. int argc,
  322. char **argv
  323. )
  324. /*++
  325. Routine Description:
  326. ScanArgs - parse command line arguments, and set control flags
  327. to reflect what we find.
  328. Sets NeedHelp, Force, ForceValue.
  329. Arguments:
  330. argc - count of command line args
  331. argv - argument vector
  332. Return Value:
  333. --*/
  334. {
  335. int i;
  336. Force = FALSE;
  337. NeedHelp = FALSE;
  338. for (i = 1; i < argc; i++) {
  339. if ( ! ((argv[i][0] == '-') ||
  340. (argv[i][0] == '/')) )
  341. {
  342. NeedHelp = TRUE;
  343. goto done;
  344. }
  345. switch (argv[i][1]) {
  346. case '?':
  347. case 'h':
  348. case 'H':
  349. NeedHelp = TRUE;
  350. break;
  351. case 'c':
  352. case 'C':
  353. if (Force) {
  354. NeedHelp = TRUE;
  355. } else {
  356. Force = TRUE;
  357. ForceValue = FORCE_CONDITIONAL;
  358. }
  359. break;
  360. case 'f':
  361. case 'F':
  362. if (Force) {
  363. NeedHelp = TRUE;
  364. } else {
  365. Force = TRUE;
  366. ForceValue = FORCE_ALWAYS;
  367. }
  368. break;
  369. case 'o':
  370. case 'O':
  371. if (Force) {
  372. NeedHelp = TRUE;
  373. } else {
  374. Force = TRUE;
  375. ForceValue = FORCE_OFF;
  376. }
  377. break;
  378. default:
  379. NeedHelp = TRUE;
  380. goto done;
  381. }
  382. }
  383. done:
  384. if (NeedHelp) {
  385. Force = FALSE;
  386. }
  387. return;
  388. }
  389. VOID
  390. GetSystemState(
  391. )
  392. /*++
  393. Routine Description:
  394. GetSystemState - get the system version, whether the computer
  395. has FP hardware or not, and whether the force
  396. emulation switch is already set or not.
  397. Sets FloatHardware, NTOK, CurrentForceValue
  398. Arguments:
  399. Return Value:
  400. --*/
  401. {
  402. HKEY hkey;
  403. TCHAR Buffer[32];
  404. DWORD BufferSize = 32;
  405. DWORD Type;
  406. int major;
  407. int minor;
  408. LONG rc;
  409. PULONG p;
  410. OSVERSIONINFO OsVersionInfo;
  411. NTOK = FALSE;
  412. //
  413. // decide if the system version is OK.
  414. //
  415. OsVersionInfo.dwOSVersionInfoSize = sizeof(OsVersionInfo);
  416. GetVersionEx(&OsVersionInfo);
  417. if (OsVersionInfo.dwPlatformId != VER_PLATFORM_WIN32_NT) {
  418. printmessage(MSG_PENTBUG_NOT_NT);
  419. exit(2);
  420. }
  421. if ( (OsVersionInfo.dwMajorVersion > 3) ||
  422. ( (OsVersionInfo.dwMajorVersion == 3) &&
  423. (OsVersionInfo.dwMinorVersion >= 51) ))
  424. {
  425. //
  426. // build 3.51 or greater, it has the fix
  427. //
  428. NTOK = TRUE;
  429. } else if ( (OsVersionInfo.dwMajorVersion == 3) &&
  430. (OsVersionInfo.dwMinorVersion == 50))
  431. {
  432. if (OsVersionInfo.szCSDVersion[0] != (TCHAR)'\0') {
  433. //
  434. // we have a service pack for 3.5, since pack 1 and
  435. // later have the fix, it's OK
  436. //
  437. NTOK = TRUE;
  438. }
  439. }
  440. /*
  441. printf("debug NTOK forced true for testing\n\n\n");
  442. NTOK = TRUE;
  443. */
  444. //
  445. // determine if float hardware is present
  446. //
  447. rc = RegOpenKeyEx(
  448. HKEY_LOCAL_MACHINE,
  449. TEXT("Hardware\\Description\\System\\FloatingPointProcessor"),
  450. 0,
  451. KEY_READ,
  452. &hkey
  453. );
  454. if (rc == ERROR_SUCCESS) {
  455. FloatHardware = FLOAT_ON;
  456. RegCloseKey(hkey);
  457. } else {
  458. rc = RegOpenKeyEx(
  459. HKEY_LOCAL_MACHINE,
  460. TEXT("Hardware\\Description\\System\\DisabledFloatingPointProcessor"),
  461. 0,
  462. KEY_READ,
  463. &hkey
  464. );
  465. if (rc == ERROR_SUCCESS) {
  466. FloatHardware = FLOAT_OFF;
  467. RegCloseKey(hkey);
  468. } else {
  469. FloatHardware = FLOAT_NONE;
  470. }
  471. }
  472. //
  473. // determine if emulation has been forced on
  474. //
  475. rc = RegOpenKeyEx(
  476. HKEY_LOCAL_MACHINE,
  477. TEXT("System\\CurrentControlSet\\Control\\Session Manager"),
  478. 0,
  479. KEY_READ,
  480. &hkey
  481. );
  482. if (rc != ERROR_SUCCESS) {
  483. return;
  484. }
  485. BufferSize = 32;
  486. rc = RegQueryValueEx(
  487. hkey,
  488. TEXT("ForceNpxEmulation"),
  489. 0,
  490. &Type,
  491. (unsigned char *)Buffer,
  492. &BufferSize
  493. );
  494. if ( (rc == ERROR_SUCCESS) &&
  495. (Type == REG_DWORD) )
  496. {
  497. p = (PULONG)Buffer;
  498. CurrentForceValue = *p;
  499. }
  500. return;
  501. }
  502. //
  503. // these must be globals to make the compiler do the right thing
  504. //
  505. VOID
  506. TestForDivideError(
  507. )
  508. /*++
  509. Routine Description:
  510. Do a divide with a known divident/divisor pair, followed by
  511. a multiply to see if we get the right answer back.
  512. FDivError = TRUE if we get the WRONG answer, FALSE.
  513. Arguments:
  514. Return Value:
  515. --*/
  516. {
  517. DWORD pick;
  518. HANDLE ph;
  519. DWORD processmask;
  520. DWORD systemmask;
  521. ULONG i;
  522. //
  523. // fetch the affinity mask, which is also effectively a list
  524. // of processors
  525. //
  526. ph = GetCurrentProcess();
  527. GetProcessAffinityMask(ph, &processmask, &systemmask);
  528. //
  529. // step through the mask, testing each cpu.
  530. // if any is bad, we treat them all as bad
  531. //
  532. FDivError = FALSE;
  533. for (i = 0; i < 32; i++) {
  534. pick = 1 << i;
  535. if ((systemmask & pick) != 0) {
  536. //*//printf("pick = %08lx\n", pick);
  537. SetThreadAffinityMask(GetCurrentThread(), pick);
  538. //
  539. // call the official test function
  540. //
  541. if (ms_p5_test_fdiv()) {
  542. //
  543. // do NOT just assign func to FDivError, that will reset
  544. // it if a second cpu is good. must be one way flag
  545. //
  546. FDivError = TRUE;
  547. }
  548. } // IF
  549. } // for
  550. return;
  551. }
  552. /***
  553. * testfdiv.c - routine to test for correct operation of x86 FDIV instruction.
  554. *
  555. *Purpose:
  556. * Detects early steppings of Pentium with incorrect FDIV tables using
  557. * 'official' Intel test values. Returns 1 if flawed Pentium is detected,
  558. * 0 otherwise.
  559. *
  560. */
  561. int ms_p5_test_fdiv(void)
  562. {
  563. double dTestDivisor = 3145727.0;
  564. double dTestDividend = 4195835.0;
  565. double dRslt;
  566. _asm {
  567. fld qword ptr [dTestDividend]
  568. fdiv qword ptr [dTestDivisor]
  569. fmul qword ptr [dTestDivisor]
  570. fsubr qword ptr [dTestDividend]
  571. fstp qword ptr [dRslt]
  572. }
  573. return (dRslt > 1.0);
  574. }
  575. //
  576. // Call FormatMessage and dump the result. All messages to Stdout
  577. //
  578. void printmessage (DWORD messageID, ...)
  579. {
  580. unsigned short messagebuffer[4096];
  581. va_list ap;
  582. va_start(ap, messageID);
  583. FormatMessage(FORMAT_MESSAGE_FROM_HMODULE, NULL, messageID, 0,
  584. messagebuffer, 4095, &ap);
  585. wprintf(messagebuffer);
  586. va_end(ap);
  587. }