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.

860 lines
20 KiB

  1. /*++
  2. Module Name:
  3. mpclockc.c
  4. Abstract:
  5. Author:
  6. Ron Mosgrove - Intel
  7. Environment:
  8. Kernel mode
  9. Revision History:
  10. --*/
  11. #include "halp.h"
  12. //
  13. // Define global data used to communicate new clock rates to the clock
  14. // interrupt service routine.
  15. //
  16. struct RtcTimeIncStruc {
  17. ULONG RTCRegisterA; // The RTC register A value for this rate
  18. ULONG RateIn100ns; // This rate in multiples of 100ns
  19. ULONG RateAdjustmentNs; // Error Correction (in ns)
  20. ULONG RateAdjustmentCnt; // Error Correction (as a fraction of 256)
  21. ULONG IpiRate; // IPI Rate Count (as a fraction of 256)
  22. };
  23. //
  24. // The adjustment is expressed in terms of a fraction of 256 so that
  25. // the ISR can easily determine when a 100ns slice needs to be subtracted
  26. // from the count passed to the kernel without any expensive operations
  27. //
  28. // Using 256 as a base means that anytime the count becomes greater
  29. // than 256 the time slice must be incremented, the overflow can then
  30. // be cleared by AND'ing the value with 0xff
  31. //
  32. #define AVAILABLE_INCREMENTS 5
  33. struct RtcTimeIncStruc HalpRtcTimeIncrements[AVAILABLE_INCREMENTS] = {
  34. {0x026, 9766, 38, 96, /* 3/8 of 256 */ 16},
  35. {0x027, 19532, 75, 192, /* 3/4 of 256 */ 32},
  36. {0x028, 39063, 50, 128, /* 1/2 of 256 */ 64},
  37. {0x029, 78125, 0, 0, 128},
  38. {0x02a, 156250, 0, 0, 256}
  39. };
  40. ULONG HalpInitialClockRateIndex = AVAILABLE_INCREMENTS-1;
  41. extern ULONG HalpCurrentRTCRegisterA;
  42. extern ULONG HalpCurrentClockRateIn100ns;
  43. extern ULONG HalpCurrentClockRateAdjustment;
  44. extern ULONG HalpCurrentIpiRate;
  45. extern ULONG HalpNextMSRate;
  46. #if !defined(_WIN64)
  47. extern ULONG HalpClockWork;
  48. extern BOOLEAN HalpClockSetMSRate;
  49. #endif
  50. VOID
  51. HalpSetInitialClockRate (
  52. VOID
  53. );
  54. VOID
  55. HalpInitializeTimerResolution (
  56. ULONG Rate
  57. );
  58. #ifdef ALLOC_PRAGMA
  59. #pragma alloc_text(PAGELK, HalpSetInitialClockRate)
  60. #pragma alloc_text(INIT, HalpInitializeTimerResolution)
  61. #endif
  62. VOID
  63. HalpInitializeTimerResolution (
  64. ULONG Rate
  65. )
  66. /*++
  67. Routine Description:
  68. This function is called to initialize the timer resolution to be
  69. something other then the default. The rate is set to the closest
  70. supported setting below the requested rate.
  71. Arguments:
  72. Rate - in 100ns units
  73. Return Value:
  74. None
  75. --*/
  76. {
  77. ULONG i, s;
  78. //
  79. // Find the table index of the rate to use
  80. //
  81. for (i=1; i < AVAILABLE_INCREMENTS; i++) {
  82. if (HalpRtcTimeIncrements[i].RateIn100ns > Rate) {
  83. break;
  84. }
  85. }
  86. HalpInitialClockRateIndex = i - 1;
  87. //
  88. // Scale IpiRate according to max TimeIncr rate which can be used
  89. //
  90. s = AVAILABLE_INCREMENTS - HalpInitialClockRateIndex - 1;
  91. for (i=0; i < AVAILABLE_INCREMENTS; i++) {
  92. HalpRtcTimeIncrements[i].IpiRate <<= s;
  93. }
  94. }
  95. VOID
  96. HalpSetInitialClockRate (
  97. VOID
  98. )
  99. /*++
  100. Routine Description:
  101. This function is called to set the initial clock interrupt rate
  102. Arguments:
  103. None
  104. Return Value:
  105. None
  106. --*/
  107. {
  108. extern ULONG HalpNextMSRate;
  109. //
  110. // On ACPI timer machines, we need to init an index into the
  111. // milisecond(s) array used by pmtimerc.c's Piix4 workaround
  112. //
  113. #ifdef ACPI_HAL
  114. #ifdef NT_UP
  115. extern ULONG HalpCurrentMSRateTableIndex;
  116. HalpCurrentMSRateTableIndex = (1 << HalpInitialClockRateIndex) - 1;
  117. //
  118. // The Piix4 upper bound table ends at 15ms (index 14), so we'll have
  119. // to map our 15.6ms entry to it as a special case
  120. //
  121. if (HalpCurrentMSRateTableIndex == 0xF) {
  122. HalpCurrentMSRateTableIndex--;
  123. }
  124. #endif
  125. #endif
  126. HalpNextMSRate = HalpInitialClockRateIndex;
  127. HalpCurrentClockRateIn100ns =
  128. HalpRtcTimeIncrements[HalpNextMSRate].RateIn100ns;
  129. HalpCurrentClockRateAdjustment =
  130. HalpRtcTimeIncrements[HalpNextMSRate].RateAdjustmentCnt;
  131. HalpCurrentRTCRegisterA =
  132. HalpRtcTimeIncrements[HalpNextMSRate].RTCRegisterA;
  133. HalpCurrentIpiRate =
  134. HalpRtcTimeIncrements[HalpNextMSRate].IpiRate;
  135. HalpClockWork = 0;
  136. KeSetTimeIncrement (
  137. HalpRtcTimeIncrements[HalpNextMSRate].RateIn100ns,
  138. HalpRtcTimeIncrements[0].RateIn100ns
  139. );
  140. }
  141. #ifdef MMTIMER
  142. ULONG
  143. HalpAcpiTimerSetTimeIncrement(
  144. IN ULONG DesiredIncrement
  145. )
  146. #else
  147. ULONG
  148. HalSetTimeIncrement (
  149. IN ULONG DesiredIncrement
  150. )
  151. #endif
  152. /*++
  153. Routine Description:
  154. This function is called to set the clock interrupt rate to the frequency
  155. required by the specified time increment value.
  156. Arguments:
  157. DesiredIncrement - Supplies desired number of 100ns units between clock
  158. interrupts.
  159. Return Value:
  160. The actual time increment in 100ns units.
  161. --*/
  162. {
  163. ULONG i;
  164. KIRQL OldIrql;
  165. //
  166. // Set the new clock interrupt parameters, return the new time increment value.
  167. //
  168. for (i=1; i <= HalpInitialClockRateIndex; i++) {
  169. if (HalpRtcTimeIncrements[i].RateIn100ns > DesiredIncrement) {
  170. break;
  171. }
  172. }
  173. i = i - 1;
  174. KeRaiseIrql(HIGH_LEVEL,&OldIrql);
  175. HalpNextMSRate = i + 1;
  176. HalpClockSetMSRate = TRUE;
  177. KeLowerIrql (OldIrql);
  178. return (HalpRtcTimeIncrements[i].RateIn100ns);
  179. }
  180. #if defined(_WIN64)
  181. #include "..\amd64\halcmn.h"
  182. ULONG HalpCurrentMSRateTableIndex;
  183. //
  184. // Forward declared functions
  185. //
  186. VOID
  187. HalpUpdateTimerWatchdog (
  188. VOID
  189. );
  190. VOID
  191. HalpClockInterruptWork (
  192. VOID
  193. );
  194. VOID
  195. HalpMcaQueueDpc (
  196. VOID
  197. );
  198. extern PBOOLEAN KdEnteredDebugger;
  199. extern PVOID HalpTimerWatchdogCurFrame;
  200. extern PVOID HalpTimerWatchdogLastFrame;
  201. extern ULONG HalpTimerWatchdogStorageOverflow;
  202. extern ULONG HalpTimerWatchdogEnabled;
  203. //
  204. // Local constants
  205. //
  206. #define COUNTER_TICKS_FOR_AVG 16
  207. #define FRAME_COPY_SIZE (64 * sizeof(ULONG))
  208. #define APIC_ICR_CLOCK (DELIVER_FIXED | ICR_ALL_EXCL_SELF | APIC_CLOCK_VECTOR)
  209. //
  210. // Flags to tell clock routine when P0 can Ipi other processors
  211. //
  212. ULONG HalpIpiClock = 0;
  213. //
  214. // Timer latency watchdog variables
  215. //
  216. ULONG HalpWatchdogAvgCounter;
  217. ULONG64 HalpWatchdogCount;
  218. ULONG64 HalpWatchdogTsc;
  219. HALP_CLOCKWORK_UNION HalpClockWorkUnion;
  220. //
  221. // Clock rate adjustment counter. This counter is used to keep a tally of
  222. // adjustments needed to be applied to the RTC rate as passed to the kernel.
  223. //
  224. ULONG HalpCurrentRTCRegisterA;
  225. ULONG HalpCurrentClockRateIn100ns = 0;
  226. ULONG HalpCurrentClockRateAdjustment = 0;
  227. ULONG HalpCurrentIpiRate = 0;
  228. ULONG HalpNextMSRate = 0;
  229. ULONG HalpPendingRate = 0;
  230. //
  231. // Other
  232. //
  233. BOOLEAN HalpUse8254 = FALSE;
  234. UCHAR HalpSample8254 = 0;
  235. UCHAR HalpRateAdjustment = 0;
  236. ULONG HalpIpiRateCounter = 0;
  237. VOID
  238. HalpInitializeClock (
  239. VOID
  240. )
  241. /*++
  242. Routine Description:
  243. This routine initialize system time clock using RTC to generate an
  244. interrupt at every 15.6250 ms interval at APIC_CLOCK_VECTOR
  245. It also initializes the 8254 if the 8254 is to be used for performance
  246. counters.
  247. See the definition of RegisterAClockValue if clock rate needs to be
  248. changed.
  249. This routine assumes it runs during Phase 0 on P0.
  250. Arguments:
  251. None
  252. Return Value:
  253. None.
  254. --*/
  255. {
  256. ULONG flags;
  257. UCHAR regB;
  258. if (HalpTimerWatchdogEnabled != 0) {
  259. HalpWatchdogAvgCounter = COUNTER_TICKS_FOR_AVG;
  260. HalpWatchdogTsc = ReadTimeStampCounter();
  261. HalpWatchdogCount = 0;
  262. }
  263. flags = HalpDisableInterrupts();
  264. HalpSetInitialClockRate();
  265. //
  266. // Set the interrupt rate to what is actually needed
  267. //
  268. HalpAcquireCmosSpinLock();
  269. CMOS_WRITE(CMOS_STATUS_A,(UCHAR)HalpCurrentRTCRegisterA);
  270. //
  271. // Don't clobber the Daylight Savings Time bit in register B, because we
  272. // stash the LastKnownGood "environment variable" there.
  273. //
  274. regB = CMOS_READ(CMOS_STATUS_B);
  275. regB &= 1;
  276. regB |= REGISTER_B_ENABLE_PERIODIC_INTERRUPT;
  277. //
  278. // Write the register B value, then read C and D to initialize
  279. //
  280. CMOS_WRITE(CMOS_STATUS_B,regB);
  281. CMOS_READ(CMOS_STATUS_C);
  282. CMOS_READ(CMOS_STATUS_D);
  283. HalpReleaseCmosSpinLock();
  284. if (HalpUse8254 != FALSE) {
  285. HalpAcquireSystemHardwareSpinLock();
  286. //
  287. // Program the 8254 to count down the maximum interval (8254 access
  288. // is guarded with the system hardware spin lock).
  289. //
  290. // First program the count mode of the timer, then program the
  291. // interval.
  292. //
  293. WRITE_PORT_UCHAR(TIMER1_CONTROL_PORT,
  294. TIMER_COMMAND_COUNTER0 |
  295. TIMER_COMMAND_RW_16BIT |
  296. TIMER_COMMAND_MODE2);
  297. IO_DELAY();
  298. WRITE_PORT_USHORT_PAIR(TIMER1_DATA_PORT0,
  299. TIMER1_DATA_PORT0,
  300. PERFORMANCE_INTERVAL);
  301. IO_DELAY();
  302. HalpUse8254 |= PERF_8254_INITIALIZED;
  303. HalpReleaseSystemHardwareSpinLock();
  304. }
  305. HalpRestoreInterrupts(flags);
  306. }
  307. BOOLEAN
  308. HalpClockInterruptStub (
  309. IN PKINTERRUPT Interrupt,
  310. IN PVOID ServiceContext,
  311. IN PKTRAP_FRAME TrapFrame
  312. )
  313. /*++
  314. Routine Description:
  315. This routine is entered as the result of an interrupt generated by
  316. CLOCK2. Its function is to dismiss the interrupt and return.
  317. This routine is executed on P0 during phase 0.
  318. Arguments:
  319. Interrupt - Supplies a pointer to the kernel interrupt object
  320. ServiceContext - Supplies the service context
  321. TrapFrame - Supplise a pointer to the trap frame
  322. Return Value:
  323. TRUE
  324. --*/
  325. {
  326. UCHAR status;
  327. UNREFERENCED_PARAMETER(Interrupt);
  328. UNREFERENCED_PARAMETER(ServiceContext);
  329. UNREFERENCED_PARAMETER(TrapFrame);
  330. //
  331. // Clear the interrupt flag on the RTC by banging on the CMOS. On some
  332. // systems this doesn't work the first time we do it, so we do it twice.
  333. // It is rumored that some machines require more than this, but that
  334. // hasn't been observed with NT.
  335. //
  336. CMOS_READ(CMOS_STATUS_C);
  337. do {
  338. status = CMOS_READ(CMOS_STATUS_C);
  339. } while ((status & 0x80) != 0);
  340. return TRUE;
  341. }
  342. BOOLEAN
  343. HalpClockInterrupt (
  344. IN PKINTERRUPT Interrupt,
  345. IN PVOID ServiceContext
  346. )
  347. /*++
  348. Routine Description:
  349. This routine is entered as the result of an interrupt generated by
  350. CLOCK2. Its function is to dismiss the interrupt, raise system Irql to
  351. CLOCK2_LEVEL, update performance the counter and transfer control to
  352. the standard system routine to update the system time and the execution
  353. time of the current thread and process.
  354. Thie routine is executed only on P0
  355. Arguments:
  356. Interrupt - Supplies a pointer to the kernel interrupt object
  357. ServiceContext - Supplies the service context
  358. Return Value:
  359. TRUE
  360. --*/
  361. {
  362. ULONG timeIncrement;
  363. ULONG flags;
  364. ULONG64 timeStamp;
  365. LONG64 timeStampDelta;
  366. ULONG rateCounter;
  367. if (HalpUse8254 != FALSE) {
  368. if (HalpSample8254 == 0) {
  369. //
  370. // Call KeQueryPerformanceCounter() so that wrap-around of 8254
  371. // is detected and the base value for performance counters
  372. // updated. Ignore returned value and reset HalpSample8254.
  373. //
  374. // WARNING - change reset value above if maximum RTC time
  375. // increment is increased to be more than the current maximum
  376. // value of 15.625 ms. Currently the call will be made every
  377. // 3rd timer tick.
  378. //
  379. HalpSample8254 = 2;
  380. KeQueryPerformanceCounter(0);
  381. } else {
  382. HalpSample8254 -= 1;
  383. }
  384. }
  385. //
  386. // This is the RTC interrupt, so we have to clear the flag on the RTC.
  387. //
  388. HalpAcquireCmosSpinLock();
  389. //
  390. // Clear the interrupt flag on the RTC by banging on the CMOS. On some
  391. // systems this doesn't work the first time we do it, so we do it twice.
  392. // It is rumored that some machines require more than this, but that
  393. // hasn't been observed with NT.
  394. //
  395. CMOS_READ(CMOS_STATUS_C);
  396. CMOS_READ(CMOS_STATUS_C);
  397. HalpReleaseCmosSpinLock();
  398. //
  399. // Adjust the tick count as needed.
  400. //
  401. timeIncrement = HalpCurrentClockRateIn100ns;
  402. HalpRateAdjustment += (UCHAR)HalpCurrentClockRateAdjustment;
  403. if (HalpRateAdjustment < HalpCurrentClockRateAdjustment) {
  404. timeIncrement--;
  405. }
  406. //
  407. // With an APIC based system we will force a clock interrupt to all other
  408. // processors. This is not really an IPI in the NT sense of the word,
  409. // it uses the Local Apic to generate interrupts to other CPUs.
  410. //
  411. #if !defined(NT_UP)
  412. //
  413. // See if we need to IPI anyone. This happens only at the lowest
  414. // supported frequency (i.e. the value KeSetTimeIncrement is called
  415. // with). We have an IPI Rate based on the current clock relative
  416. // to the lowest clock rate.
  417. //
  418. rateCounter = HalpIpiRateCounter + HalpCurrentIpiRate;
  419. HalpIpiRateCounter = rateCounter & 0xFF;
  420. if (HalpIpiRateCounter != rateCounter &&
  421. HalpIpiClock != 0) {
  422. //
  423. // Time to send an Ipi and at least one other processor is alive.
  424. //
  425. flags = HalpDisableInterrupts();
  426. HalpStallWhileApicBusy();
  427. LOCAL_APIC(LU_INT_CMD_LOW) = APIC_ICR_CLOCK;
  428. HalpRestoreInterrupts(flags);
  429. }
  430. #endif // NT_UP
  431. if (HalpTimerWatchdogEnabled != 0) {
  432. HalpUpdateTimerWatchdog();
  433. }
  434. if (HalpClockWork != 0) {
  435. HalpClockInterruptWork();
  436. }
  437. KeUpdateSystemTime(Interrupt->TrapFrame,timeIncrement);
  438. return TRUE;
  439. }
  440. BOOLEAN
  441. HalpClockInterruptPn (
  442. IN PKINTERRUPT Interrupt,
  443. IN PVOID ServiceContext
  444. )
  445. /*++
  446. Routine Description:
  447. This routine is entered as the result of an interrupt generated by
  448. CLOCK2. Its function is to dismiss the interrupt, raise system Irql to
  449. CLOCK2_LEVEL, update performance the counter and transfer control to
  450. the standard system routine to update the system time and the execution
  451. time of the current thread and process.
  452. This routine is executed on all processors other than P0.
  453. Arguments:
  454. Interrupt - Supplies a pointer to the kernel interrupt object
  455. ServiceContext - Supplies the service context
  456. Return Value:
  457. TRUE
  458. --*/
  459. {
  460. UNREFERENCED_PARAMETER(ServiceContext);
  461. HalpEnableInterrupts();
  462. KeUpdateRunTime(Interrupt->TrapFrame);
  463. return TRUE;
  464. }
  465. VOID
  466. HalpClockInterruptWork (
  467. VOID
  468. )
  469. {
  470. struct RtcTimeIncStruc *timeIncStruc;
  471. BOOLEAN changeRate;
  472. //
  473. // There is more clock interrupt work to do
  474. //
  475. if (HalpClockMcaQueueDpc != FALSE) {
  476. //
  477. // Queue an MCA dpc
  478. //
  479. HalpClockMcaQueueDpc = FALSE;
  480. HalpMcaQueueDpc();
  481. }
  482. if (HalpClockSetMSRate != FALSE) {
  483. //
  484. // The clock frequency is being changed. See if we have changed
  485. // rates since the last tick.
  486. //
  487. if (HalpPendingRate != 0) {
  488. //
  489. // A new rate was set during the last tick, so update
  490. // globals.
  491. //
  492. // The next tick will occur at the rate which was programmed
  493. // during the last tick. Update globals for the new rate
  494. // which starts with the next tick.
  495. //
  496. // We will get here if there is a request for a rate change.
  497. // There could have been two requests, that is why we are
  498. // comparing the Pending with the NextRate.
  499. //
  500. timeIncStruc = &HalpRtcTimeIncrements[HalpPendingRate - 1];
  501. HalpCurrentClockRateIn100ns = timeIncStruc->RateIn100ns;
  502. HalpCurrentClockRateAdjustment = timeIncStruc->RateAdjustmentCnt;
  503. HalpCurrentIpiRate = timeIncStruc->IpiRate;
  504. if (HalpPendingRate != HalpNextMSRate) {
  505. changeRate = TRUE;
  506. } else {
  507. changeRate = FALSE;
  508. }
  509. HalpPendingRate = 0;
  510. if (changeRate != FALSE) {
  511. //
  512. // A new clock rate needs to be set. Setting the rate here
  513. // will cause the tick after the next tick to be at the new
  514. // rate.
  515. //
  516. // (The next tick is already in progress and will occur at
  517. // the same rate as this tick.)
  518. //
  519. HalpAcquireCmosSpinLock();
  520. HalpPendingRate = HalpNextMSRate;
  521. timeIncStruc = &HalpRtcTimeIncrements[HalpPendingRate - 1];
  522. HalpCurrentRTCRegisterA = timeIncStruc->RTCRegisterA;
  523. CMOS_WRITE(CMOS_STATUS_A,(UCHAR)HalpCurrentRTCRegisterA);
  524. if (HalpTimerWatchdogEnabled != FALSE) {
  525. HalpWatchdogTsc = ReadTimeStampCounter();
  526. HalpWatchdogCount = 0;
  527. HalpWatchdogAvgCounter = COUNTER_TICKS_FOR_AVG;
  528. }
  529. HalpReleaseCmosSpinLock();
  530. }
  531. }
  532. }
  533. }
  534. VOID
  535. HalpUpdateTimerWatchdog (
  536. VOID
  537. )
  538. {
  539. ULONG stackStart;
  540. ULONG64 timeStamp;
  541. ULONG64 timeStampDelta;
  542. timeStamp = ReadTimeStampCounter();
  543. timeStampDelta = timeStamp - HalpWatchdogTsc;
  544. HalpWatchdogTsc = timeStamp;
  545. if ((LONG64)timeStampDelta < 0) {
  546. //
  547. // Bogus (negative) timestamp count.
  548. //
  549. return;
  550. }
  551. if (*KdEnteredDebugger != FALSE) {
  552. //
  553. // Skip if we're in the debugger.
  554. //
  555. return;
  556. }
  557. if (HalpPendingRate != 0) {
  558. //
  559. // A new rate was set during the last tick, discontinue
  560. // processing
  561. //
  562. return;
  563. }
  564. if (HalpWatchdogAvgCounter != 0) {
  565. //
  566. // Increment the total counter, perform average when the count is
  567. // reached.
  568. //
  569. HalpWatchdogCount += timeStampDelta;
  570. HalpWatchdogAvgCounter -= 1;
  571. if (HalpWatchdogAvgCounter == 0) {
  572. HalpWatchdogCount /= COUNTER_TICKS_FOR_AVG;
  573. }
  574. return;
  575. }
  576. if (timeStampDelta <= HalpWatchdogCount) {
  577. return;
  578. }
  579. if (HalpTimerWatchdogCurFrame != NULL &&
  580. HalpTimerWatchdogStorageOverflow == FALSE) {
  581. PVOID pSrc;
  582. ULONG copyBytes;
  583. //
  584. // Copy FRAME_COPY_SIZE dwords from the stack, or to next
  585. // page boundary, whichever is less.
  586. //
  587. pSrc = &stackStart;
  588. copyBytes = (ULONG)(PAGE_SIZE - ((ULONG_PTR)pSrc & (PAGE_SIZE-1)));
  589. if (copyBytes > FRAME_COPY_SIZE) {
  590. copyBytes = FRAME_COPY_SIZE;
  591. }
  592. RtlCopyMemory(HalpTimerWatchdogCurFrame, pSrc, copyBytes);
  593. (ULONG_PTR)HalpTimerWatchdogCurFrame += copyBytes;
  594. //
  595. // If we didn't copy an entire FRAME_COPY_SIZE buffer, zero
  596. // fill.
  597. //
  598. copyBytes = FRAME_COPY_SIZE - copyBytes;
  599. if (copyBytes > 0) {
  600. RtlZeroMemory(HalpTimerWatchdogCurFrame,copyBytes);
  601. (ULONG_PTR)HalpTimerWatchdogCurFrame += copyBytes;
  602. }
  603. if (HalpTimerWatchdogCurFrame >= HalpTimerWatchdogLastFrame) {
  604. HalpTimerWatchdogStorageOverflow = TRUE;
  605. }
  606. }
  607. }
  608. #endif // _WIN64