Leaked source code of windows server 2003
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.

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