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.

1095 lines
27 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name:
  4. pmtimerc.c
  5. Abstract:
  6. Implements workarounds for PIIX4 bugs. The
  7. nature of the ACPI timer in PIIX4 is that it
  8. occasionally returns completely bogus data.
  9. Intel claims that this happens about 0.02% of
  10. the time when it is polled continuously. As NT
  11. almost never polls it continuously, we don't
  12. really know what the real behavior is.
  13. The workaround is something like this: On
  14. every clock tick, we read the timer. Using this
  15. value, we compute an upper bound for what the
  16. timer may read by the next clock tick. We also
  17. record the minimum value ever returned. If, at
  18. any time, we read the timer and it does not fall
  19. within the minimum and upper bound, then we read
  20. it again. If it either falls within the bounds
  21. or it is very close to the last read, we use it.
  22. If not, we read it again.
  23. This behavior allows us to read the timer only
  24. once almost all the time that we need a time
  25. stamp. Exiting the debugger is almost guaranteed
  26. to cause the read-twice behavior.
  27. Author:
  28. Jake Oshins (jakeo) 30-Oct-1998
  29. Environment:
  30. Kernel mode only.
  31. Revision History:
  32. --*/
  33. #include "halp.h"
  34. #ifdef APIC_HAL
  35. #include "apic.inc"
  36. #include "ntapic.inc"
  37. #endif
  38. BOOLEAN
  39. HalpPmTimerSpecialStall(
  40. IN ULONG Ticks
  41. );
  42. BOOLEAN
  43. HalpPmTimerScaleTimers(
  44. VOID
  45. );
  46. LARGE_INTEGER
  47. HalpPmTimerQueryPerfCount(
  48. OUT PLARGE_INTEGER PerformanceFrequency OPTIONAL
  49. );
  50. VOID
  51. HalpAdjustUpperBoundTable2X(
  52. VOID
  53. );
  54. #ifdef ALLOC_PRAGMA
  55. #pragma alloc_text(INIT, HalpPmTimerScaleTimers)
  56. #pragma alloc_text(INIT, HalpPmTimerSpecialStall)
  57. #pragma alloc_text(INIT, HalpAdjustUpperBoundTable2X)
  58. #endif
  59. typedef struct {
  60. ULONG CurrentTimePort;
  61. volatile ULONG TimeLow;
  62. volatile ULONG TimeHigh2;
  63. volatile ULONG TimeHigh1;
  64. ULONG MsbMask;
  65. ULONG BiasLow;
  66. ULONG BiasHigh;
  67. volatile ULONG UpperBoundLow;
  68. volatile ULONG UpperBoundHigh2;
  69. volatile ULONG UpperBoundHigh1;
  70. } TIMER_INFO, *PTIMER_INFO;
  71. typedef struct {
  72. ULONG RawRead[2];
  73. ULONG AdjustedLow[2];
  74. ULONG AdjustedHigh[2];
  75. ULONG TITL;
  76. ULONG TITH;
  77. ULONG UBL;
  78. ULONG UBH;
  79. ULONG ReturnedLow;
  80. ULONG ReturnedHigh;
  81. ULONG ReadCount;
  82. ULONG TickMin;
  83. ULONG TickCount;
  84. ULONG TickNewUB;
  85. //UCHAR padding[4];
  86. } TIMER_PERF_INDEX ,*PTIMER_PERF_INDEX;
  87. extern TIMER_INFO TimerInfo;
  88. #if DBG
  89. ULONG LastKQPCValue[3] = {0};
  90. static ULARGE_INTEGER LastClockSkew = { 0, 0 };
  91. #endif
  92. #ifndef NO_PM_KEQPC
  93. extern ULONG HalpCurrentMSRateTableIndex;
  94. extern UCHAR HalpBrokenAcpiTimer;
  95. extern UCHAR HalpPiix4;
  96. extern PVOID QueryTimer;
  97. #if DBG
  98. extern TIMER_PERF_INDEX TimerPerf[];
  99. extern ULONG TimerPerfIndex;
  100. #endif
  101. //
  102. // The UpperBoundTable contains the values which should be added
  103. // to the current counter value to ensure that the upper bound is
  104. // reasonable. Values listed here are for all the 15 possible
  105. // timer tick lengths. The unit is "PM Timer Ticks" and the
  106. // value corresponds to the number of ticks that will pass in
  107. // roughly two timer ticks at this rate.
  108. //
  109. #define UPPER_BOUND_TABLE_SIZE 15
  110. static ULONG HalpPiix4UpperBoundTable[] = {
  111. #if 0
  112. 20000 , // 1 ms
  113. 35000 , // 2 ms
  114. 50000 , // 3 ms
  115. 65000 , // 4 ms
  116. 85000 , // 5 ms
  117. 100000, // 6 ms
  118. 115000, // 7 ms
  119. 130000, // 8 ms
  120. 150000, // 9 ms
  121. 165000, // 10 ms
  122. 180000, // 11 ms
  123. 195000, // 12 ms
  124. 211000, // 13 ms
  125. 230000, // 14 ms
  126. 250000 // 15 ms
  127. #endif
  128. 14318,
  129. 28636,
  130. 42954,
  131. 57272,
  132. 71590,
  133. 85908,
  134. 100226,
  135. 114544,
  136. 128862,
  137. 143180,
  138. 157498,
  139. 171818,
  140. 186136,
  141. 200454,
  142. 214772
  143. };
  144. VOID
  145. HalpAdjustUpperBoundTable2X(
  146. VOID
  147. )
  148. /*++
  149. Routine Description:
  150. This adjusts the upper bound table for PM timer running at 2X
  151. Arguments:
  152. None
  153. Return Value:
  154. None
  155. --*/
  156. {
  157. ULONG Looper;
  158. for (Looper = 0; Looper < UPPER_BOUND_TABLE_SIZE; Looper++) {
  159. HalpPiix4UpperBoundTable[Looper] *= 2;
  160. }
  161. }
  162. ULONG
  163. HalpQuery8254Counter(
  164. VOID
  165. );
  166. static ULARGE_INTEGER ClockSkew = { 0, 0 };
  167. static BOOLEAN PositiveSkew = TRUE;
  168. #ifdef TIMER_DBG
  169. static BOOLEAN DoItOnce = TRUE;
  170. static BOOLEAN PacketLog = TRUE;
  171. static BOOLEAN TimerTick = FALSE;
  172. static ULONG NegativeGlitches = 0;
  173. static ULONG PositiveGlitches = 0;
  174. static ULONG PacketLogCount = 5;
  175. typedef struct _TIMER_PACKET {
  176. ULONG Hardware;
  177. ULARGE_INTEGER CurrentRead0;
  178. ULARGE_INTEGER TimeStamp;
  179. ULARGE_INTEGER Minimum;
  180. ULARGE_INTEGER Maximum;
  181. BOOLEAN PositiveSkew;
  182. BOOLEAN TimerTick;
  183. UCHAR Reserved[2];
  184. ULARGE_INTEGER Skew;
  185. ULARGE_INTEGER CurrentRead1;
  186. } TIMER_PACKET, *PTIMER_PACKET;
  187. #define MAX_TIMER_PACKETS 10
  188. static ULONG PacketIndex = 0;
  189. static TIMER_PACKET TimerLog[MAX_TIMER_PACKETS];
  190. #endif // TIMER_DBG
  191. #define A_FEW_TICKS 3
  192. ULARGE_INTEGER
  193. FASTCALL
  194. HalpQueryBrokenPiix4(
  195. VOID
  196. )
  197. {
  198. ULARGE_INTEGER lastRead;
  199. ULARGE_INTEGER currentRead;
  200. ULARGE_INTEGER currentRead0;
  201. ULARGE_INTEGER minTime;
  202. ULARGE_INTEGER upperBound;
  203. ULONG hardwareVal;
  204. ULONG bitsInHardware;
  205. ULONG flags;
  206. ULONG ClockBits;
  207. ULARGE_INTEGER RollOver;
  208. ULARGE_INTEGER SkewedTime;
  209. #ifndef NT_UP
  210. KIRQL Irql;
  211. #endif
  212. #ifdef TIMER_DBG
  213. ULONG Index;
  214. #endif
  215. #if DBG
  216. ULONG readCount = 0;
  217. PTIMER_PERF_INDEX timerPerfRecord =
  218. &(TimerPerf[TimerPerfIndex]);
  219. RtlZeroMemory(timerPerfRecord, sizeof(TIMER_PERF_INDEX));
  220. #endif
  221. //
  222. // N.B. This is, of course, not MP safe. But none
  223. // of the PIIX4 workaround code is. MP machines don't
  224. // use PIIX4 code.
  225. //
  226. _asm {
  227. pushfd
  228. pop eax
  229. mov flags, eax
  230. cli
  231. }
  232. lastRead.QuadPart = 0;
  233. bitsInHardware = (TimerInfo.MsbMask << 1) - 1;
  234. //
  235. // Get current minimum reported time.
  236. //
  237. minTime.HighPart = TimerInfo.TimeHigh2;
  238. minTime.LowPart = TimerInfo.TimeLow;
  239. #if DBG
  240. timerPerfRecord->TITL = TimerInfo.TimeLow;
  241. timerPerfRecord->TITH = TimerInfo.TimeHigh1;
  242. #endif
  243. //
  244. // Loop until we get a time that we can believe.
  245. //
  246. RollOver.QuadPart = 0;
  247. while (TRUE) {
  248. //
  249. // Read the hardware.
  250. //
  251. hardwareVal = READ_PORT_ULONG((PULONG)TimerInfo.CurrentTimePort);
  252. #ifdef TIMER_DBG
  253. if (DoItOnce) {
  254. RtlZeroMemory(&TimerLog[0], sizeof(TIMER_PACKET) *
  255. MAX_TIMER_PACKETS);
  256. DoItOnce = FALSE;
  257. }
  258. if (FALSE) { //((hardwareVal & 0xFFFF8000) == 0xFFFF8000) {
  259. PacketLog = TRUE;
  260. PacketLogCount = 5;
  261. }
  262. if (PacketLog) {
  263. if (PacketLogCount == 0) {
  264. PacketLog = FALSE;
  265. }
  266. if (PacketLogCount > 0) {
  267. Index = PacketIndex++ % MAX_TIMER_PACKETS;
  268. RtlZeroMemory(&TimerLog[Index], sizeof(TIMER_PACKET));
  269. TimerLog[Index].Hardware = hardwareVal;
  270. TimerLog[Index].TimerTick = TimerTick;
  271. {
  272. ULONG TSCounterHigh;
  273. ULONG TSCounterLow;
  274. _asm { rdtsc
  275. mov TSCounterLow, eax
  276. mov TSCounterHigh, edx };
  277. TimerLog[Index].TimeStamp.HighPart = TSCounterHigh;
  278. TimerLog[Index].TimeStamp.LowPart = TSCounterLow;
  279. }
  280. TimerLog[Index].Minimum = minTime;
  281. TimerLog[Index].PositiveSkew = PositiveSkew;
  282. TimerLog[Index].Skew = ClockSkew;
  283. if ((PacketLogCount < 4) && (PacketLogCount > 0)) {
  284. PacketLogCount--;
  285. }
  286. }
  287. }
  288. #endif // TIMER_DBG
  289. currentRead.HighPart = minTime.HighPart;
  290. currentRead.LowPart = (minTime.LowPart & (~bitsInHardware)) |
  291. hardwareVal;
  292. currentRead0 = currentRead;
  293. //
  294. // Check for rollover, since this function is called during each
  295. // system clock interrupt, if the HW has really rolled over, then it
  296. // should be within upper bound ticks since that is approximately
  297. // twice the number of ticks we expect during each system clock
  298. // interrupt, however, some broken timers occasionally tick backward
  299. // a few ticks, and if this happens, we may accidentally detect
  300. // more than one rollover during this period depending upon how
  301. // frequently applications are calling this API, and how often the
  302. // HW glitches, and this can cause applications to jerk like mad,
  303. // but we cannot apply heuristics to try to throw out any of these
  304. // detected rolls during this interval because we could accidentally
  305. // throw out the one and only legitimate rollover
  306. //
  307. if (RollOver.QuadPart > 0) {
  308. currentRead.QuadPart += RollOver.QuadPart;
  309. } else {
  310. SkewedTime = minTime;
  311. //
  312. // If time is skewed, we need to remove the skew to accurately
  313. // assess whether the timer has wrapped
  314. //
  315. if (ClockSkew.QuadPart > 0) {
  316. if (PositiveSkew) {
  317. SkewedTime.QuadPart -= ClockSkew.QuadPart;
  318. } else {
  319. SkewedTime.QuadPart += ClockSkew.QuadPart;
  320. }
  321. }
  322. if (((ULONG)(SkewedTime.LowPart & bitsInHardware) > hardwareVal) &&
  323. (hardwareVal < (HalpPiix4UpperBoundTable[HalpCurrentMSRateTableIndex] / 2))) {
  324. RollOver.QuadPart = (UINT64)(TimerInfo.MsbMask) << 1;
  325. currentRead.QuadPart += RollOver.QuadPart;
  326. }
  327. }
  328. #ifdef TIMER_DBG
  329. if (PacketLog) {
  330. TimerLog[Index].CurrentRead0 = currentRead;
  331. }
  332. #endif
  333. #if DBG
  334. readCount = timerPerfRecord->ReadCount;
  335. readCount &= 1;
  336. timerPerfRecord->RawRead[readCount] = hardwareVal;
  337. timerPerfRecord->AdjustedLow[readCount] = currentRead.LowPart;
  338. timerPerfRecord->AdjustedHigh[readCount] = currentRead.HighPart;
  339. timerPerfRecord->ReadCount++;
  340. #endif
  341. //
  342. // Get the current upper bound.
  343. //
  344. upperBound.HighPart = TimerInfo.UpperBoundHigh2;
  345. upperBound.LowPart = TimerInfo.UpperBoundLow;
  346. #ifdef TIMER_DBG
  347. if (PacketLog) {
  348. TimerLog[Index].Maximum = upperBound;
  349. }
  350. #endif
  351. if ((minTime.QuadPart <= currentRead.QuadPart) &&
  352. (currentRead.QuadPart <= upperBound.QuadPart)) {
  353. //
  354. // This value from the counter is within the boundaries
  355. // that we expect.
  356. //
  357. //
  358. // If there was previously a skew, unskew
  359. //
  360. ClockSkew.QuadPart = 0;
  361. break;
  362. }
  363. if (ClockSkew.QuadPart > 0) {
  364. SkewedTime = currentRead;
  365. if (PositiveSkew) {
  366. SkewedTime.QuadPart += ClockSkew.QuadPart;
  367. } else {
  368. SkewedTime.QuadPart -= ClockSkew.QuadPart;
  369. }
  370. if ((minTime.QuadPart <= SkewedTime.QuadPart) &&
  371. (SkewedTime.QuadPart <= upperBound.QuadPart)) {
  372. //
  373. // This value from the counter is within the boundaries
  374. // that we accept
  375. //
  376. currentRead = SkewedTime;
  377. break;
  378. }
  379. }
  380. //
  381. // We are guaranteed to break out of this as soon as we read
  382. // two consectutive non-decreasing values from the timer whose
  383. // difference is less than or equal to 0xfff-- is this
  384. // too much to ask?
  385. //
  386. if ((currentRead.QuadPart - lastRead.QuadPart) > 0xfff) {
  387. lastRead = currentRead;
  388. continue;
  389. }
  390. #ifdef TIMER_DBG
  391. if (PacketLog) {
  392. if (PacketLogCount > 0) {
  393. PacketLogCount--;
  394. }
  395. }
  396. #endif
  397. //
  398. // Now we are really screwed-- we are consistently reading values
  399. // from the timer, that are not within the boundaries we expect
  400. //
  401. // We are going to record/apply a skew that will get us back on
  402. // track
  403. //
  404. if (currentRead.QuadPart < minTime.QuadPart) {
  405. //
  406. // Time jumped backward a small fraction, just add a few ticks
  407. //
  408. if ((minTime.QuadPart - currentRead.QuadPart) < 0x40) {
  409. SkewedTime.QuadPart = minTime.QuadPart + A_FEW_TICKS;
  410. //
  411. // Time jumped backward quite a bit, add half a system clock
  412. // interrupt worth of ticks since we know this routine
  413. // gets called every clock interrupt
  414. //
  415. } else {
  416. SkewedTime.QuadPart = minTime.QuadPart +
  417. (HalpPiix4UpperBoundTable[HalpCurrentMSRateTableIndex] /
  418. 8);
  419. }
  420. #ifdef TIMER_DBG
  421. PositiveGlitches++;
  422. if (PacketLog) {
  423. TimerLog[Index].PositiveSkew = TRUE;
  424. TimerLog[Index].Skew.QuadPart =
  425. SkewedTime.QuadPart - currentRead.QuadPart;
  426. }
  427. #endif // TIMER_DBG
  428. PositiveSkew = TRUE;
  429. ClockSkew.QuadPart = SkewedTime.QuadPart - currentRead.QuadPart;
  430. //
  431. // currentRead > upperBound
  432. //
  433. } else {
  434. //
  435. // Time jumped forward more than a system clock, interrupts
  436. // may have been disabled by some unruly driver, or maybe
  437. // we were hung up in the debugger, at any rate, let's add
  438. // a full system clock interrupt worth of ticks
  439. //
  440. SkewedTime.QuadPart = minTime.QuadPart +
  441. (HalpPiix4UpperBoundTable[HalpCurrentMSRateTableIndex] /
  442. 4);
  443. #ifdef TIMER_DBG
  444. NegativeGlitches++;
  445. if (PacketLog) {
  446. TimerLog[Index].PositiveSkew = FALSE;
  447. TimerLog[Index].Skew.QuadPart =
  448. currentRead.QuadPart - SkewedTime.QuadPart;
  449. }
  450. #endif // TIMER_DBG
  451. PositiveSkew = FALSE;
  452. ClockSkew.QuadPart = currentRead.QuadPart - SkewedTime.QuadPart;
  453. }
  454. currentRead = SkewedTime;
  455. break;
  456. }
  457. #ifdef TIMER_DBG
  458. if (PacketLog) {
  459. TimerLog[Index].CurrentRead1 = currentRead;
  460. }
  461. #endif
  462. //
  463. // If we detected a rollover, and there is negative skew, then we
  464. // should recalculate the skew as positive skew to avoid making
  465. // an erroneous correction on the next read
  466. //
  467. if ((ClockSkew.QuadPart > 0) && (RollOver.QuadPart > 0) &&
  468. (PositiveSkew == FALSE) && (ClockSkew.QuadPart > hardwareVal)) {
  469. //
  470. // I still want to study this case, even though it is handled
  471. // by the if statement below, it may very well be that when we
  472. // hit this case, we are double-wrapping the timer by mistake
  473. //
  474. ASSERT(currentRead.QuadPart >= currentRead0.QuadPart);
  475. if (currentRead.QuadPart >= currentRead0.QuadPart) {
  476. ClockSkew.QuadPart = currentRead.QuadPart - currentRead0.QuadPart;
  477. PositiveSkew = TRUE;
  478. }
  479. #if TIMER_DBG
  480. else {
  481. if ((PacketLog) && (PacketLogCount > 3)) {
  482. PacketLogCount = 3;
  483. }
  484. }
  485. #endif
  486. }
  487. //
  488. // Similarly, if there is no rollover, but positive skew is causing
  489. // the timer to rollover, then we need to readjust the skew also to
  490. // avoid the possibility of making an erroneous correction on the next
  491. // read
  492. //
  493. if ((ClockSkew.QuadPart > 0) && (RollOver.QuadPart == 0) &&
  494. (PositiveSkew == TRUE) && ((currentRead.QuadPart & ~bitsInHardware) >
  495. (minTime.QuadPart & ~bitsInHardware))) {
  496. //
  497. // I'm not sure what this means, or how it can happen, but I will
  498. // endeavor to decipher the condition if and when it occurs
  499. //
  500. ASSERT(currentRead0.QuadPart + bitsInHardware + 1 >
  501. currentRead.QuadPart);
  502. if (currentRead0.QuadPart + bitsInHardware + 1 >
  503. currentRead.QuadPart) {
  504. ClockSkew.QuadPart = currentRead0.QuadPart + bitsInHardware + 1 -
  505. currentRead.QuadPart;
  506. PositiveSkew = FALSE;
  507. }
  508. #if TIMER_DBG
  509. else {
  510. if ((PacketLog) && (PacketLogCount > 3)) {
  511. PacketLogCount = 3;
  512. }
  513. }
  514. #endif
  515. }
  516. //
  517. // Compute new upper bound.
  518. //
  519. upperBound.QuadPart = currentRead.QuadPart +
  520. HalpPiix4UpperBoundTable[HalpCurrentMSRateTableIndex];
  521. //
  522. // Update upper and lower bounds.
  523. //
  524. TimerInfo.TimeHigh1 = currentRead.HighPart;
  525. TimerInfo.TimeLow = currentRead.LowPart;
  526. TimerInfo.TimeHigh2 = currentRead.HighPart;
  527. TimerInfo.UpperBoundHigh1 = upperBound.HighPart;
  528. TimerInfo.UpperBoundLow = upperBound.LowPart;
  529. TimerInfo.UpperBoundHigh2 = upperBound.HighPart;
  530. #if DBG
  531. LastClockSkew = ClockSkew;
  532. #endif
  533. _asm {
  534. mov eax, flags
  535. push eax
  536. popfd
  537. }
  538. #if DBG
  539. timerPerfRecord->ReturnedLow = currentRead.LowPart;
  540. timerPerfRecord->ReturnedHigh = currentRead.HighPart;
  541. timerPerfRecord->UBL = upperBound.LowPart;
  542. timerPerfRecord->UBH = upperBound.HighPart;
  543. TimerPerfIndex = (TimerPerfIndex + 1) % (4096 / sizeof(TIMER_PERF_INDEX));
  544. #endif
  545. return currentRead;
  546. }
  547. VOID
  548. HalpBrokenPiix4TimerTick(
  549. VOID
  550. )
  551. {
  552. ULARGE_INTEGER currentCount;
  553. ULARGE_INTEGER upperBound;
  554. #if DBG
  555. PTIMER_PERF_INDEX timerPerfRecord;
  556. #endif
  557. #ifdef TIMER_DBG
  558. TimerTick = TRUE;
  559. #endif
  560. currentCount =
  561. HalpQueryBrokenPiix4();
  562. #ifdef TIMER_DBG
  563. TimerTick = FALSE;
  564. #endif
  565. #if DBG
  566. timerPerfRecord = &(TimerPerf[TimerPerfIndex]);
  567. timerPerfRecord->TickMin = currentCount.LowPart;
  568. timerPerfRecord->TickNewUB = TimerInfo.UpperBoundLow;
  569. timerPerfRecord->TickCount++;
  570. #endif
  571. }
  572. #endif // NO_PM_KEQPC
  573. VOID
  574. HalaAcpiTimerInit(
  575. IN ULONG TimerPort,
  576. IN BOOLEAN TimerValExt
  577. )
  578. {
  579. TimerInfo.CurrentTimePort = TimerPort;
  580. if (TimerValExt) {
  581. TimerInfo.MsbMask = 0x80000000;
  582. }
  583. #ifndef NO_PM_KEQPC
  584. if (HalpBrokenAcpiTimer) {
  585. QueryTimer = HalpQueryBrokenPiix4;
  586. #if DBG
  587. {
  588. KIRQL oldIrql;
  589. KeRaiseIrql(HIGH_LEVEL, &oldIrql);
  590. LastKQPCValue[0] = 0;
  591. LastKQPCValue[1] = 0;
  592. LastKQPCValue[2] = 0;
  593. KeLowerIrql(oldIrql);
  594. }
  595. #endif
  596. }
  597. #endif // NO_PM_KEQPC
  598. }
  599. #define PIT_FREQUENCY 1193182
  600. #define PM_TMR_FREQ 3579545
  601. #define EIGHTH_SECOND_PM_TICKS 447443
  602. ULONG PMTimerFreq = PM_TMR_FREQ;
  603. #ifdef SPEEDY_BOOT
  604. static ULONG HalpFoundPrime = 0;
  605. VOID
  606. HalpPrimeSearch(
  607. IN ULONG Primer,
  608. IN ULONG BitMask
  609. )
  610. /*++
  611. Routine Description:
  612. The objective of this routine is to waste as many CPU cycles as possible
  613. by searching for prime numbers. To be fairly consistent in the amount
  614. of time it wastes, it is severly less than optimal-- we force Primer
  615. to be odd by or-ing in 15, then we and it with the BitMask, and or
  616. in BitMask+1, and finally we continue testing after we discover the
  617. Primer's not prime, until out test factor squared is greater than, or
  618. equal to the Primer.
  619. Arguments:
  620. Primer - The number to search (seed)
  621. BitMask - How many bits of primer to use in search, controls amount
  622. of time wasted
  623. Return Value:
  624. None
  625. --*/
  626. {
  627. ULONG Index;
  628. BOOLEAN FoundPrime;
  629. Primer |= 0xF;
  630. BitMask |= 0xF;
  631. Primer &= BitMask;
  632. Primer |= (BitMask + 1);
  633. FoundPrime = TRUE;
  634. for (Index = 3; (Index * Index) < Primer; Index += 2) {
  635. if ((Primer % Index) == 0) {
  636. FoundPrime = FALSE;
  637. // Do not break-- we're trying to waste time, remember?
  638. }
  639. }
  640. //
  641. // Stuff prime(s) in global so sneaky optimizing compiler
  642. // doesn't optimize out this B.S.
  643. //
  644. if (FoundPrime) {
  645. HalpFoundPrime = Primer;
  646. }
  647. }
  648. BOOLEAN
  649. HalpPmTimerSpecialStall(
  650. IN ULONG Ticks
  651. )
  652. /*++
  653. Routine Description:
  654. Arguments:
  655. Ticks - Number of PM timer ticks to stall
  656. Return Value:
  657. TRUE if we were able to stall for the correct interval,
  658. otherwise FALSE
  659. --*/
  660. {
  661. BOOLEAN TimerWrap;
  662. LARGE_INTEGER TimerWrapBias;
  663. LARGE_INTEGER LastRead;
  664. LARGE_INTEGER InitialTicks;
  665. LARGE_INTEGER TargetTicks;
  666. LARGE_INTEGER CurrentTicks;
  667. ULONG ZeroElapsedTickReads;
  668. InitialTicks = HalpPmTimerQueryPerfCount(NULL);
  669. //
  670. // Let's test the rollover action...
  671. //
  672. CurrentTicks = InitialTicks;
  673. LastRead.QuadPart = InitialTicks.QuadPart;
  674. ZeroElapsedTickReads = 0;
  675. TimerWrapBias.QuadPart = 0;
  676. TimerWrap = FALSE;
  677. TargetTicks.QuadPart = InitialTicks.QuadPart + Ticks;
  678. while (CurrentTicks.QuadPart < TargetTicks.QuadPart) {
  679. //
  680. // Now let's really chew up some cycles and see if we can find
  681. // some prime numbers while we're at it
  682. //
  683. HalpPrimeSearch(CurrentTicks.LowPart, 0x7FFF);
  684. CurrentTicks = HalpPmTimerQueryPerfCount(NULL);
  685. CurrentTicks.QuadPart += TimerWrapBias.QuadPart;
  686. //
  687. // Did the timer wrap, or is it broken?
  688. //
  689. if (CurrentTicks.QuadPart < LastRead.QuadPart) {
  690. //
  691. // The timer can wrap once, otherwise something's amiss
  692. //
  693. if (!TimerWrap) {
  694. TimerWrapBias.QuadPart = (UINT64)(TimerInfo.MsbMask) << 1;
  695. CurrentTicks.QuadPart += TimerWrapBias.QuadPart;
  696. TimerWrap = TRUE;
  697. //
  698. // Something is whack, considering our elaborate stall
  699. // algorithm, this difference is still too significant,
  700. // maybe it's time to upgrade that 200MHz CPU if you
  701. // want fast boot!
  702. //
  703. if ((CurrentTicks.QuadPart - LastRead.QuadPart) > 0x1000) {
  704. return FALSE;
  705. }
  706. //
  707. // We already had one decreasing read, looser!
  708. //
  709. } else {
  710. return FALSE;
  711. }
  712. }
  713. //
  714. // Is the timer really ticking? In practice it is virtually
  715. // impossible to read the timer so quickly that you get the same
  716. // answer twice, but in theory it should be possible, so to avoid
  717. // the possibility of getting stuck in this loop for all eternity
  718. // we will permit this condition to occur one thousand times
  719. // before we give up
  720. //
  721. if (CurrentTicks.QuadPart == LastRead.QuadPart) ZeroElapsedTickReads++;
  722. if (ZeroElapsedTickReads > 1000) {
  723. return FALSE;
  724. }
  725. LastRead = CurrentTicks;
  726. }
  727. return TRUE;
  728. }
  729. static BOOLEAN SpecialStallSuccess = TRUE;
  730. #define TSC 0x10
  731. LONGLONG ReadCycleCounter(VOID) { _asm { rdtsc } }
  732. #define TIMER_ROUNDING 10000
  733. #define __1MHz 1000000
  734. BOOLEAN
  735. HalpPmTimerScaleTimers(
  736. VOID
  737. )
  738. /*++
  739. Routine Description:
  740. Determines the frequency of the APIC timer, this routine is run
  741. during initialization
  742. Arguments:
  743. None
  744. Return Value:
  745. None
  746. --*/
  747. {
  748. ULONG Flags;
  749. ULONG ReadBack;
  750. PHALPCR HalPCR;
  751. PKPCR pPCR;
  752. ULONG ApicHz;
  753. ULONGLONG TscHz;
  754. ULONG RoundApicHz;
  755. ULONGLONG RoundTscHz;
  756. ULONGLONG RoundTscMhz;
  757. //
  758. // If we ever failed before, don't bother wasting any more time
  759. //
  760. if (!SpecialStallSuccess) {
  761. return FALSE;
  762. }
  763. //
  764. // Don't interrupt us!
  765. //
  766. _asm {
  767. pushfd
  768. pop eax
  769. mov Flags, eax
  770. cli
  771. }
  772. pPCR = KeGetPcr();
  773. HalPCR = (PHALPCR)(KeGetPcr()->HalReserved);
  774. //
  775. // Configure APIC timer
  776. //
  777. pLocalApic[LU_TIMER_VECTOR / 4] = INTERRUPT_MASKED |
  778. PERIODIC_TIMER | APIC_PROFILE_VECTOR;
  779. pLocalApic[LU_DIVIDER_CONFIG / 4] = LU_DIVIDE_BY_1;
  780. //
  781. // Make sure the write has happened ???
  782. //
  783. ReadBack = pLocalApic[LU_DIVIDER_CONFIG / 4];
  784. //
  785. // Zero the perf counter
  786. //
  787. HalPCR->PerfCounterLow = 0;
  788. HalPCR->PerfCounterHigh = 0;
  789. //
  790. // Fence ???
  791. //
  792. _asm { xor eax, eax
  793. cpuid }
  794. //
  795. // Reset APIC counter and TSC
  796. //
  797. pLocalApic[LU_INITIAL_COUNT / 4] = (ULONG)-1;
  798. WRMSR(TSC, 0);
  799. //
  800. // Stall for an eigth second
  801. //
  802. SpecialStallSuccess = HalpPmTimerSpecialStall(EIGHTH_SECOND_PM_TICKS);
  803. if (SpecialStallSuccess) {
  804. //
  805. // Read/compute APIC clock and TSC Frequencies (ticks * 8)
  806. //
  807. TscHz = ReadCycleCounter() * 8;
  808. ApicHz = ((ULONG)-1 - pLocalApic[LU_CURRENT_COUNT / 4]) * 8;
  809. //
  810. // Round APIC frequency
  811. //
  812. RoundApicHz = ((ApicHz + (TIMER_ROUNDING / 2)) / TIMER_ROUNDING) *
  813. TIMER_ROUNDING;
  814. HalPCR->ApicClockFreqHz = RoundApicHz;
  815. //
  816. // Round TSC frequency
  817. //
  818. RoundTscHz = ((TscHz + (TIMER_ROUNDING / 2)) / TIMER_ROUNDING) *
  819. TIMER_ROUNDING;
  820. HalPCR->TSCHz = (ULONG)RoundTscHz; // ASSERT(RoundTscHz < __4GHz);
  821. //
  822. // Convert TSC frequency to MHz
  823. //
  824. RoundTscMhz = (RoundTscHz + (__1MHz / 2)) / __1MHz;
  825. pPCR->StallScaleFactor = (ULONG)RoundTscMhz;
  826. HalPCR->ProfileCountDown = RoundApicHz;
  827. pLocalApic[LU_INITIAL_COUNT / 4] = RoundApicHz;
  828. }
  829. //
  830. // Restore interrupt state-- can this be done without _asm ???
  831. //
  832. _asm {
  833. mov eax, Flags
  834. push eax
  835. popfd
  836. }
  837. return SpecialStallSuccess;
  838. }
  839. #endif // SPEEDY_BOOT
  840. #ifndef NO_PM_KEQPC
  841. static ULONG PIT_Ticks = 0xBADCEEDE;
  842. VOID
  843. HalpAcpiTimerPerfCountHack(
  844. VOID
  845. )
  846. /*++
  847. Routine Description:
  848. Some cheezy PIC-based laptops seemed to have wired their ACPI timer to
  849. the wrong frequecy crystal, and their perf counter freq is twice what
  850. it should be. These systems seem to run fine in every other way
  851. except for midi file playback, or anything else that goes by KeQuery-
  852. PerformanceCounter's return frequency value, so we perform a simple
  853. check late during init to see if this clock is twice what we expect,
  854. and if it is we return twice the ACPI frequency in KeQuery...
  855. Arguments:
  856. None
  857. Return Value:
  858. None
  859. --*/
  860. {
  861. ULONG T0_Count = 0;
  862. ULONG T1_Count = 1;
  863. ULONG Retry = 10;
  864. //
  865. // If we happen to hit the rollover just do it again
  866. //
  867. while ((T0_Count < T1_Count) && (Retry--)) {
  868. T0_Count = HalpQuery8254Counter();
  869. KeStallExecutionProcessor(1000);
  870. T1_Count = HalpQuery8254Counter();
  871. }
  872. if (T0_Count < T1_Count) {
  873. return;
  874. }
  875. //
  876. // We should have read ~1200 ticks during this interval, so if we
  877. // recorded between 575 and 725 we can reasonably assume the ACPI
  878. // Timer is running at 2 * spec
  879. //
  880. PIT_Ticks = T0_Count - T1_Count;
  881. if ((PIT_Ticks < 725) && (PIT_Ticks > 575)) {
  882. PMTimerFreq = 2 * PM_TMR_FREQ;
  883. HalpAdjustUpperBoundTable2X();
  884. }
  885. }
  886. #endif // NO_PM_KEQPC