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.

672 lines
15 KiB

  1. /*++
  2. Module Name:
  3. i64clock.c
  4. Abstract:
  5. Author:
  6. Bernard Lint, M. Jayakumar
  7. Ron Mosgrove - Intel
  8. Environment:
  9. Kernel mode
  10. Revision History:
  11. Based on halmps/mpclockc.c
  12. --*/
  13. #include "halp.h"
  14. extern ULONG HalpNextRate;
  15. extern BOOLEAN HalpClockSetMSRate;
  16. int _fltused;
  17. BOOLEAN ReadPerfMonCounter = FALSE;
  18. ULONG HalpCPUMHz;
  19. #if defined(INSTRUMENT_CLOCK_DRIFT)
  20. ULONGLONG HalpITMSkew;
  21. ULONG HalpCountBadITMValues;
  22. ULONGLONG HalpMinITMMissed;
  23. ULONGLONG HalpMaxITMMissed;
  24. ULONG HalpBreakMissedTickMin;
  25. ULONG HalpBreakMissedTickMax = ~0U;
  26. BOOLEAN HalpResetITMDebug;
  27. #endif
  28. #if DBG
  29. // Thierry - until compiler supports the generation of enum types in pdbs...
  30. // At that time, we will be able the use the enums instead of globals.
  31. unsigned int HalpDpfltrMaxMask = HALIA64_DPFLTR_MAXMASK;
  32. #endif // DBG
  33. ULONGLONG HalpITCFrequency = 500000000; // 500MHz for default real hardware power on.
  34. ULONGLONG HalpProcessorFrequency = 500000000; // 500MHz CPU
  35. ULONG HalpMissedTickCount[HAL_MAXIMUM_PROCESSOR];
  36. //
  37. // Ticks per 100ns used to compute ITM update count
  38. //
  39. double HalpITCTicksPer100ns;
  40. ULONGLONG HalpClockCount;
  41. //
  42. // HalpSetNextClockInterrupt():
  43. // move to cr.itm latency (= 40 cycles) + 2 cycles from the itc read.
  44. //
  45. ULONGLONG HalpITMUpdateLatency = 42;
  46. //
  47. // All of these are in 100ns units
  48. //
  49. ULONGLONG HalpCurrentTimeIncrement = DEFAULT_CLOCK_INTERVAL;
  50. ULONGLONG HalpNextTimeIncrement = DEFAULT_CLOCK_INTERVAL;
  51. ULONGLONG HalpNewTimeIncrement = DEFAULT_CLOCK_INTERVAL;
  52. ULONG HalpProfileInterval = DEFAULT_PROFILE_INTERVAL;
  53. BOOLEAN HalpProfileStopped = TRUE;
  54. ULONGLONG // = (current ITC - previous ITM)
  55. HalpSetNextClockInterrupt(
  56. VOID
  57. );
  58. #ifdef ALLOC_PRAGMA
  59. #pragma alloc_text(INIT,HalpSetInitialClockRate)
  60. #pragma alloc_text(INIT,HalpInitializeTimerResolution)
  61. #endif
  62. VOID
  63. HalpClearClock (
  64. )
  65. /*++
  66. Routine Description:
  67. Set clock to zero.
  68. Return Value:
  69. None.
  70. --*/
  71. {
  72. HalpWriteITC(0);
  73. HalpWriteITM(0);
  74. return;
  75. }
  76. VOID
  77. HalpInitializeClock (
  78. VOID
  79. )
  80. /*++
  81. Routine Description:
  82. Initialize system time clock (ITC and ITM) to generate an interrupt
  83. at every 10 ms interval at ITC_CLOCK_VECTOR.
  84. Previously this routine initialize system time clock using 8254 timer1
  85. counter 0 to generate an interrupt at every 15ms interval at 8259 irq0.
  86. See the definitions of TIME_INCREMENT and ROLLOVER_COUNT if clock rate
  87. needs to be changed.
  88. Arguments:
  89. None
  90. Return Value:
  91. None.
  92. --*/
  93. {
  94. HalpSetInitialClockRate();
  95. HalpClearClock();
  96. HalpWriteITM(PCR->HalReserved[CURRENT_ITM_VALUE_INDEX]);
  97. return;
  98. }
  99. VOID
  100. HalpInitializeClockPn (
  101. VOID
  102. )
  103. /*++
  104. Routine Description:
  105. Assumes that only non-BSP processors call this routine.
  106. Initializes system time clock (ITC and ITM) to generate an interrupt
  107. at every 10 ms interval at ITC_CLOCK_VECTOR.
  108. Previously this routine initialize system time clock using 8254 timer1
  109. counter 0 to generate an interrupt at every 15ms interval at 8259 irq0.
  110. See the definitions of TIME_INCREMENT and ROLLOVER_COUNT if clock rate
  111. needs to be changed.
  112. Arguments:
  113. None
  114. Return Value:
  115. None.
  116. --*/
  117. {
  118. ULONGLONG itmValue;
  119. itmValue = (ULONGLONG)(HalpITCTicksPer100ns * MAXIMUM_CLOCK_INTERVAL);
  120. PCR->HalReserved[CURRENT_ITM_VALUE_INDEX] = itmValue;
  121. HalpClearClock();
  122. HalpWriteITM( itmValue );
  123. return;
  124. }
  125. VOID
  126. HalpSetInitialClockRate (
  127. VOID
  128. )
  129. /*++
  130. Routine Description:
  131. This function is called to set the initial clock interrupt rate
  132. Assumes that only the BSP processor calls this routine.
  133. Arguments:
  134. None
  135. Return Value:
  136. None
  137. --*/
  138. {
  139. //
  140. // CPU Frequency in MHz = ticks per second / 10 ** 6
  141. //
  142. HalpCPUMHz = (ULONG)((HalpProcessorFrequency + 500000) / 1000000);
  143. //
  144. // Ticks per 100ns = ticks per second / 10 ** 7
  145. //
  146. HalpITCTicksPer100ns = (double) HalpITCFrequency / (10000000.);
  147. if (HalpITCTicksPer100ns < 1) {
  148. HalpITCTicksPer100ns = 1;
  149. }
  150. HalpClockCount = (ULONGLONG)(HalpITCTicksPer100ns * MAXIMUM_CLOCK_INTERVAL);
  151. PCR->HalReserved[CURRENT_ITM_VALUE_INDEX] = HalpClockCount;
  152. KeSetTimeIncrement(MAXIMUM_CLOCK_INTERVAL, MINIMUM_CLOCK_INTERVAL);
  153. }
  154. VOID
  155. HalpClockInterrupt (
  156. IN PKINTERRUPT_ROUTINE Interrupt,
  157. IN PKTRAP_FRAME TrapFrame
  158. )
  159. /*++
  160. Routine Description:
  161. System Clock Interrupt Handler, for P0 processor only.
  162. N.B. Assumptions: Comes with IRQL set to CLOCK_LEVEL to disable
  163. interrupts.
  164. Arguments:
  165. TrapFrame - Trap frame address.
  166. Return Value:
  167. None.
  168. --*/
  169. {
  170. ULONGLONG CurrentITC;
  171. ULONGLONG currentITCDelta;
  172. ULONGLONG elapsedTimeIn100ns, numberOfTicksElapsed;
  173. #if defined(INSTRUMENT_CLOCK_DRIFT)
  174. ULONGLONG originalITM, currentITM, excessITM;
  175. #endif
  176. LONG mcaNotification;
  177. //
  178. // Check to see if a clock interrupt is generated in a small latency window
  179. // in HalpSetNextClockInterrupt.
  180. //
  181. CurrentITC = HalpReadITC();
  182. #ifndef DISABLE_ITC_WORKAROUND
  183. while ((CurrentITC & 0xFFFFFFFF) == 0xFFFFFFFF) {
  184. CurrentITC = HalpReadITC();
  185. }
  186. #endif
  187. if ((LONGLONG) (PCR->HalReserved[CURRENT_ITM_VALUE_INDEX] - CurrentITC) > 0)
  188. {
  189. return;
  190. }
  191. //
  192. // PerfMon: Per P0 clock interrupt PMD4 collection.
  193. //
  194. if (ReadPerfMonCounter) {
  195. ULONGLONG currentPerfMonValue;
  196. currentPerfMonValue = HalpReadPerfMonDataReg4();
  197. HalDebugPrint(( HAL_INFO, "\nHAL: HalpClockInterrupt - PMD4=%I64x\n", currentPerfMonValue ));
  198. }
  199. #if defined(INSTRUMENT_CLOCK_DRIFT)
  200. //
  201. // Time this interrupt was scheduled to occur
  202. //
  203. originalITM = (ULONGLONG)PCR->HalReserved[CURRENT_ITM_VALUE_INDEX];
  204. #endif
  205. //
  206. // Set next clock interrupt, based on ITC and
  207. // increment ITM, accounting for interrupt latency.
  208. //
  209. currentITCDelta = HalpSetNextClockInterrupt();
  210. #if defined(INSTRUMENT_CLOCK_DRIFT)
  211. //
  212. // Time the next interrupt is scheduled to occur
  213. //
  214. currentITM = (ULONGLONG)PCR->HalReserved[CURRENT_ITM_VALUE_INDEX];
  215. excessITM = (currentITM - originalITM) / HalpClockCount - 1;
  216. if (excessITM != 0) {
  217. if (HalpMinITMMissed == 0 || excessITM < HalpMinITMMissed) {
  218. HalpMinITMMissed = excessITM;
  219. }
  220. if (excessITM > HalpMaxITMMissed) {
  221. HalpMaxITMMissed = excessITM;
  222. }
  223. HalpCountBadITMValues++;
  224. HalpITMSkew += excessITM;
  225. if (HalpBreakMissedTickMin != 0 &&
  226. HalpBreakMissedTickMin <= excessITM &&
  227. HalpBreakMissedTickMax >= excessITM &&
  228. !HalpResetITMDebug) {
  229. DbgBreakPoint();
  230. }
  231. }
  232. if (HalpResetITMDebug) {
  233. HalpResetITMDebug = FALSE;
  234. HalpCountBadITMValues = 0;
  235. HalpITMSkew = 0;
  236. HalpMinITMMissed = 0;
  237. HalpMaxITMMissed = 0;
  238. }
  239. #endif
  240. //
  241. // Call the kernel to update system time.
  242. // P0 updates System time and Run Time.
  243. //
  244. elapsedTimeIn100ns = (ULONGLONG) (currentITCDelta/HalpITCTicksPer100ns);
  245. elapsedTimeIn100ns += HalpCurrentTimeIncrement;
  246. if (elapsedTimeIn100ns >= MAXIMUM_CLOCK_INTERVAL) {
  247. KeUpdateSystemTime( TrapFrame, MAXIMUM_CLOCK_INTERVAL );
  248. } else {
  249. numberOfTicksElapsed = elapsedTimeIn100ns / HalpCurrentTimeIncrement;
  250. KeUpdateSystemTime( TrapFrame,
  251. (ULONG)(numberOfTicksElapsed * HalpCurrentTimeIncrement)
  252. );
  253. }
  254. HalpCurrentTimeIncrement = HalpNextTimeIncrement;
  255. HalpNextTimeIncrement = HalpNewTimeIncrement;
  256. //
  257. // If MCA notification was requested by MCA Handler, execute it.
  258. //
  259. mcaNotification = InterlockedExchange( &HalpMcaInfo.DpcNotification, 0 );
  260. if ( mcaNotification ) {
  261. if ( HalpMcaInfo.KernelDelivery ) {
  262. if ( !HalpMcaInfo.KernelDelivery( HalpMcaInfo.KernelToken, NULL ) ) {
  263. InterlockedIncrement( &HalpMcaInfo.Stats.KernelDeliveryFails );
  264. }
  265. }
  266. if ( HalpMcaInfo.DriverInfo.DpcCallback ) {
  267. if ( !KeInsertQueueDpc( &HalpMcaInfo.DriverDpc, NULL, NULL ) ) {
  268. InterlockedIncrement( &HalpMcaInfo.Stats.DriverDpcQueueFails );
  269. }
  270. }
  271. }
  272. //
  273. // Poll for debugger breakin if enabled.
  274. //
  275. if ( KdDebuggerEnabled && KdPollBreakIn() ) {
  276. KeBreakinBreakpoint();
  277. }
  278. } // HalpClockInterrupt()
  279. VOID
  280. HalpClockInterruptPn (
  281. IN PKINTERRUPT_ROUTINE Interrupt,
  282. IN PKTRAP_FRAME TrapFrame
  283. )
  284. /*++
  285. Routine Description:
  286. System Clock Interrupt Handler, for processors other than P0.
  287. N.B. Assumptions: Comes with IRQL set to CLOCK_LEVEL to disable
  288. interrupts.
  289. Arguments:
  290. TrapFrame - Trap frame address.
  291. Return Value:
  292. None.
  293. --*/
  294. {
  295. //
  296. // Set next clock interrupt, based on ITC and
  297. // increment ITM, accounting for interrupt latency.
  298. //
  299. (void)HalpSetNextClockInterrupt();
  300. //
  301. // Call the kernel to update run time.
  302. // Pn updates only Run time.
  303. //
  304. KeUpdateRunTime(TrapFrame);
  305. // IA64 MCA Notification - 09/18/2000 - WARNING
  306. // If faster MCA notification was required, the BSP MCA notification checking
  307. // should be placed here.
  308. //
  309. } // HalpClockInterruptPn()
  310. VOID
  311. HalpInitializeClockInterrupts (
  312. VOID
  313. )
  314. {
  315. PKPRCB Prcb;
  316. UCHAR InterruptVector;
  317. ULONGLONG ITVData;
  318. Prcb = PCR->Prcb;
  319. InterruptVector = CLOCK_LEVEL << VECTOR_IRQL_SHIFT;
  320. if (Prcb -> Number == 0) {
  321. HalpSetHandlerAddressToVector(InterruptVector,HalpClockInterrupt);
  322. } else {
  323. //
  324. // Non-BSP processor
  325. //
  326. HalpSetHandlerAddressToVector(InterruptVector, HalpClockInterruptPn);
  327. }
  328. ITVData = (ULONGLONG) InterruptVector;
  329. HalpWriteITVector(ITVData);
  330. return;
  331. }
  332. ULONG
  333. HalSetTimeIncrement (
  334. IN ULONG DesiredIncrement
  335. )
  336. /*++
  337. Routine Description:
  338. This function is called to set the clock interrupt rate to the frequency
  339. required by the specified time increment value.
  340. N.B. This function is only executed on the processor that keeps the
  341. system time. Previously this was called HalpSetTimeIncrement. We
  342. have renamed it HalSetTimeIncrement.
  343. Arguments:
  344. DesiredIncrement - Supplies desired number of 100ns units between clock
  345. interrupts.
  346. Return Value:
  347. The actual time increment in 100ns units.
  348. --*/
  349. {
  350. ULONGLONG NextIntervalCount;
  351. KIRQL OldIrql;
  352. //
  353. // DesiredIncrement must map within the acceptable range.
  354. //
  355. if (DesiredIncrement < MINIMUM_CLOCK_INTERVAL)
  356. DesiredIncrement = MINIMUM_CLOCK_INTERVAL;
  357. else if (DesiredIncrement > MAXIMUM_CLOCK_INTERVAL)
  358. DesiredIncrement = MAXIMUM_CLOCK_INTERVAL;
  359. //
  360. // Raise IRQL to the highest level, set the new clock interrupt
  361. // parameters, lower IRQl, and return the new time increment value.
  362. //
  363. KeRaiseIrql(HIGH_LEVEL, &OldIrql);
  364. //
  365. // Calculate the actual 64 bit time value which forms the target interval.
  366. // The resulting value is added to the ITC to form the new ITM value.
  367. // HalpITCTicksPer100ns is the calibrated value for the ITC whose value
  368. // works out to be 100ns (or as close as we can come).
  369. // Previously HalpITCTicksPer100ns was called as HalpPerformanceFrequency
  370. //
  371. NextIntervalCount = (ULONGLONG)(HalpITCTicksPer100ns * DesiredIncrement);
  372. //
  373. // Calculate the number of 100ns units to report to the kernel every
  374. // time the ITM matches the ITC with this new period. Note, for small
  375. // values of DesiredIncrement (min being 10000, ie 1ms), truncation
  376. // in the above may result in a small decrement in the 5th decimal
  377. // place. As we are effectively dealing with a 4 digit number, eg
  378. // 10000 becomes 9999.something, we really can't do any better than
  379. // the following.
  380. //
  381. HalpClockCount = NextIntervalCount;
  382. HalpNewTimeIncrement = DesiredIncrement;
  383. //
  384. // HalpClockSetMSRate = TRUE;
  385. //
  386. KeLowerIrql(OldIrql);
  387. return DesiredIncrement;
  388. }
  389. NTSTATUS
  390. HalpQueryFrequency(
  391. PULONGLONG ITCFrequency,
  392. PULONGLONG ProcessorFrequency
  393. )
  394. /*++
  395. Routine Description:
  396. This function is called to provide the ITC update rate.
  397. This value is computed by first getting the platform base frequency
  398. from the SAL_FREQ_BASE call. Then applying on the return value, the
  399. ITC ratios obtained from the PAL_FREQ_RATIOS call.
  400. Arguments:
  401. None.
  402. Return Value:
  403. ULONGLONG ITCFrequency - number of ITC updates per seconds
  404. --*/
  405. {
  406. ULONG ITCRatioDenominator = 0;
  407. ULONG ITCRatioNumerator = 0;
  408. ULONG ProcessorRatioDenominator = 0;
  409. ULONG ProcessorRatioNumerator = 0;
  410. SAL_PAL_RETURN_VALUES SalReturn = {0};
  411. SAL_PAL_RETURN_VALUES PalReturn = {0};
  412. SAL_STATUS SalStatus;
  413. PAL_STATUS PalStatus;
  414. SalStatus = HalpSalCall(SAL_FREQ_BASE,
  415. 0 /* Platform base clock frequency is the clock input to the processor */,
  416. 0,
  417. 0,
  418. 0,
  419. 0,
  420. 0,
  421. 0,
  422. &SalReturn);
  423. if (SalStatus != 0) {
  424. HalDebugPrint(( HAL_ERROR,
  425. "HAL: HalInitSystem - Phase1 SAL_FREQ_BASE is returning error # %d\n",
  426. SalStatus ));
  427. return STATUS_UNSUCCESSFUL;
  428. }
  429. HalDebugPrint(( HAL_INFO, "HAL: HalInitSystem - Platform base clock Frequency is %I64u\n",SalReturn.ReturnValues[1] ));
  430. PalStatus = HalpPalCall( PAL_FREQ_RATIOS,
  431. 0,
  432. 0,
  433. 0,
  434. &PalReturn);
  435. if (PalStatus != 0) {
  436. HalDebugPrint(( HAL_ERROR,
  437. "HAL: HalInitSystem - Phase1 PAL_FREQ_RATIOS is returning error # %d\n",
  438. PalStatus ));
  439. return STATUS_UNSUCCESSFUL;
  440. }
  441. ProcessorRatioNumerator = (ULONG)((PalReturn.ReturnValues[1]) >> 32);
  442. ProcessorRatioDenominator = (ULONG)( PalReturn.ReturnValues[1]);
  443. HalDebugPrint(( HAL_INFO,
  444. "HAL: HalInitSystem - PAL returns Processor to Platform clock Frequency as %lu : %lu\n",
  445. ProcessorRatioNumerator,
  446. ProcessorRatioDenominator));
  447. *ProcessorFrequency = SalReturn.ReturnValues[1] * ProcessorRatioNumerator / ProcessorRatioDenominator;
  448. HalDebugPrint(( HAL_INFO,
  449. "HAL: HalInitSystem - Processor clock Frequency is %I64u \n",
  450. ProcessorFrequency ));
  451. ITCRatioNumerator = (ULONG)((PalReturn.ReturnValues[3]) >> 32);
  452. ITCRatioDenominator = (ULONG)( PalReturn.ReturnValues[3]);
  453. HalDebugPrint(( HAL_INFO,
  454. "HAL: HalInitSystem - PAL returns ITC to Platform clock Frequency as %lu : %lu\n",
  455. ITCRatioNumerator,
  456. ITCRatioDenominator));
  457. *ITCFrequency = SalReturn.ReturnValues[1] * ITCRatioNumerator / ITCRatioDenominator;
  458. HalDebugPrint(( HAL_INFO,
  459. "HAL: HalInitSystem - ITC clock Frequency is %I64u \n",
  460. ITCFrequency ));
  461. return STATUS_SUCCESS;
  462. }