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.

506 lines
9.2 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. mpprofil.c
  5. Abstract:
  6. This module implements the code necessary to initialize, field and
  7. process the profile interrupt.
  8. Author:
  9. Shie-Lin Tzong (shielint) 12-Jan-1990
  10. Environment:
  11. Kernel mode only.
  12. Revision History:
  13. bryanwi 20-Sep-90
  14. Forrest Foltz (forrestf) 28-Oct-2000
  15. Ported from ixprofil.asm to ixprofil.c
  16. --*/
  17. #include "halcmn.h"
  18. #define APIC_TIMER_ENABLED (PERIODIC_TIMER | APIC_PROFILE_VECTOR)
  19. #define APIC_TIMER_DISABLED (INTERRUPT_MASKED | \
  20. PERIODIC_TIMER | \
  21. APIC_PROFILE_VECTOR)
  22. #define TIMER_ROUND(x) ((((x) + 10000 / 2) / 10000) * 10000)
  23. ULONG HalpProfileRunning;
  24. VOID (*HalpPerfInterruptHandler)(PKTRAP_FRAME);
  25. VOID
  26. HalStartProfileInterrupt(
  27. IN ULONG Reserved
  28. )
  29. /*++
  30. Routine Description:
  31. What we do here is set the interrupt rate to the value that's been set
  32. by the KeSetProfileInterval routine. Then enable the APIC Timer interrupt.
  33. This function gets called on every processor so the hal can enable
  34. a profile interrupt on each processor.
  35. Arguments:
  36. Reserved - Not used, must be zero.
  37. Return Value:
  38. None.
  39. --*/
  40. {
  41. ULONG initialCount;
  42. ASSERT(Reserved == 0);
  43. initialCount = HalpGetCurrentHalPcr()->ProfileCountDown;
  44. LOCAL_APIC(LU_INITIAL_COUNT) = initialCount;
  45. HalpProfileRunning = TRUE;
  46. //
  47. // Set the Local APIC Timer to interrupt periodically at
  48. // APIC_PROFILE_VECTOR
  49. //
  50. LOCAL_APIC(LU_TIMER_VECTOR) = APIC_TIMER_ENABLED;
  51. }
  52. VOID
  53. HalStopProfileInterrupt (
  54. IN ULONG Reserved
  55. )
  56. /*++
  57. Routine Description:
  58. Stop the profile interrupt that was started with
  59. HalpStartProfileInterrupt;
  60. This function gets called on every processor so the hal can disable
  61. a profile interrupt on each processor.
  62. Arguments:
  63. Reserved - Not used, must be zero.
  64. Return Value:
  65. None.
  66. --*/
  67. {
  68. ASSERT(Reserved == 0);
  69. HalpProfileRunning = FALSE;
  70. LOCAL_APIC(LU_TIMER_VECTOR) = APIC_TIMER_DISABLED;
  71. }
  72. ULONG_PTR
  73. HalSetProfileInterval (
  74. ULONG_PTR Interval
  75. )
  76. /*++
  77. Routine Description:
  78. This procedure sets the interrupt rate (and thus the sampling
  79. interval) for the profiling interrupt.
  80. Arguments:
  81. Interval - Supplies the desired profile interrupt interval in 100ns
  82. units (MINIMUM is 1221 or 122.1 uS) see ke\profobj.c
  83. Return Value:
  84. Interval actually used
  85. --*/
  86. {
  87. ULONG64 period;
  88. ULONG apicFreqency;
  89. PHAL_PCR halPcr;
  90. ULONG countDown;
  91. halPcr = HalpGetCurrentHalPcr();
  92. //
  93. // Limit the interrupt period to 1 second
  94. //
  95. if (Interval > TIME_UNITS_PER_SECOND) {
  96. period = TIME_UNITS_PER_SECOND;
  97. } else {
  98. period = Interval;
  99. }
  100. //
  101. // Compute the countdown value corresponding to the desired period.
  102. // The calculation is done with 64-bit intermediate values.
  103. //
  104. countDown =
  105. (ULONG)(period * halPcr->ApicClockFreqHz) / TIME_UNITS_PER_SECOND;
  106. halPcr->ProfileCountDown = countDown;
  107. LOCAL_APIC(LU_INITIAL_COUNT) = countDown;
  108. return period;
  109. }
  110. VOID
  111. #if defined(MMTIMER)
  112. HalpAcpiTimerCalibratePerfCount (
  113. #else
  114. HalCalibratePerformanceCounter (
  115. #endif
  116. IN LONG volatile *Number,
  117. IN ULONGLONG NewCount
  118. )
  119. /*++
  120. Routine Description:
  121. This routine calibrates the performance counter value for a
  122. multiprocessor system. The calibration can be done by zeroing
  123. the current performance counter, or by calculating a per-processor
  124. skewing between each processors counter.
  125. Arguments:
  126. Number - Supplies a pointer to count of the number of processors in
  127. the configuration.
  128. NewCount - Supplies the value to synchronize the counter too
  129. Return Value:
  130. None.
  131. --*/
  132. {
  133. ULONG flags;
  134. PHAL_PCR halPcr;
  135. flags = HalpDisableInterrupts();
  136. //
  137. // Store the count in the PCR
  138. //
  139. halPcr = HalpGetCurrentHalPcr();
  140. halPcr->PerfCounter = NewCount;
  141. //
  142. // Wait here until all processors arrive
  143. //
  144. InterlockedDecrement(Number);
  145. while (*Number > 0) {
  146. ;
  147. }
  148. PROCESSOR_FENCE;
  149. //
  150. // Zero the time stamp counter, restore interrupts and return.
  151. //
  152. WriteMSR(MSR_TSC,0);
  153. HalpRestoreInterrupts(flags);
  154. }
  155. VOID
  156. HalpWaitForCmosRisingEdge (
  157. VOID
  158. )
  159. /*++
  160. Routine Description:
  161. Waits until the rising edge of the CMOS_STATUS_BUSY bit is detected.
  162. Note - The CMOS spin lock must be acquired before calling this routine.
  163. Arguments:
  164. None.
  165. Return Value:
  166. None.
  167. --*/
  168. {
  169. UCHAR value;
  170. //
  171. // The simulator takes about a day to go through this, so for right
  172. //
  173. #if !defined(_AMD64_SIMULATOR_)
  174. //
  175. // We're going to be polling the CMOS_STATUS_A register. Program
  176. // that register address here, outside of the loop.
  177. //
  178. WRITE_PORT_UCHAR(CMOS_ADDRESS_PORT,CMOS_STATUS_A);
  179. //
  180. // Wait for the status bit to be clear
  181. //
  182. do {
  183. value = READ_PORT_UCHAR(CMOS_DATA_PORT);
  184. } while ((value & CMOS_STATUS_BUSY) != 0);
  185. //
  186. // Now wait for the rising edge of the status bit
  187. //
  188. do {
  189. value = READ_PORT_UCHAR(CMOS_DATA_PORT);
  190. } while ((value & CMOS_STATUS_BUSY) == 0);
  191. #endif
  192. }
  193. ULONG
  194. HalpScaleTimers (
  195. VOID
  196. )
  197. /*++
  198. Routine Description:
  199. Determines the frequency of the APIC timer. This routine is run
  200. during initialization
  201. Arguments:
  202. None.
  203. Return Value:
  204. None.
  205. --*/
  206. {
  207. ULONG flags;
  208. ULONG passCount;
  209. ULONG64 cpuFreq;
  210. ULONG apicFreq;
  211. UCHAR value;
  212. PHAL_PCR halPcr;
  213. HalpAcquireCmosSpinLock();
  214. flags = HalpDisableInterrupts();
  215. LOCAL_APIC(LU_TIMER_VECTOR) = APIC_TIMER_DISABLED;
  216. LOCAL_APIC(LU_DIVIDER_CONFIG) = LU_DIVIDE_BY_1;
  217. passCount = 2;
  218. while (passCount > 0) {
  219. //
  220. // Make sure the write has occured
  221. //
  222. LOCAL_APIC(LU_TIMER_VECTOR);
  223. //
  224. // Wait for the rising edge of the UIP bit, this is the start of the
  225. // cycle.
  226. //
  227. HalpWaitForCmosRisingEdge();
  228. //
  229. // At this point the UIP bit has just changed to the set state.
  230. // Clear the time stamp counter and start the APIC counting down
  231. // from it's maximum value.
  232. //
  233. PROCESSOR_FENCE;
  234. LOCAL_APIC(LU_INITIAL_COUNT) = 0xFFFFFFFF;
  235. cpuFreq = ReadTimeStampCounter();
  236. //
  237. // Wait for the next rising edge, this marks the end of the CMOS
  238. // clock update cycle.
  239. //
  240. HalpWaitForCmosRisingEdge();
  241. PROCESSOR_FENCE;
  242. apicFreq = 0xFFFFFFFF - LOCAL_APIC(LU_CURRENT_COUNT);
  243. cpuFreq = ReadTimeStampCounter() - cpuFreq;
  244. passCount -= 1;
  245. }
  246. halPcr = HalpGetCurrentHalPcr();
  247. //
  248. // cpuFreq is elapsed timestamp in one second. Round to nearest
  249. // 10Khz and store.
  250. //
  251. halPcr->TSCHz = TIMER_ROUND(cpuFreq);
  252. //
  253. // Calculate the apic frequency, rounding to the nearest 10Khz
  254. //
  255. apicFreq = TIMER_ROUND(apicFreq);
  256. halPcr->ApicClockFreqHz = apicFreq;
  257. //
  258. // Store microsecond representation of TSC frequency.
  259. //
  260. halPcr->StallScaleFactor = (ULONG)(halPcr->TSCHz / 1000000);
  261. if ((halPcr->TSCHz % 1000000) != 0) {
  262. halPcr->StallScaleFactor += 1;
  263. }
  264. HalpReleaseCmosSpinLock();
  265. halPcr->ProfileCountDown = apicFreq;
  266. //
  267. // Set the interrupt rate in the chip and return the apic frequency
  268. //
  269. LOCAL_APIC(LU_INITIAL_COUNT) = apicFreq;
  270. HalpRestoreInterrupts(flags);
  271. return halPcr->ApicClockFreqHz;
  272. }
  273. BOOLEAN
  274. HalpProfileInterrupt (
  275. IN PKINTERRUPT Interrupt,
  276. IN PVOID ServiceContext
  277. )
  278. /*++
  279. Routine Description:
  280. This routine is entered as the result of a profile interrupt. Its
  281. function is to dismiss the interrupt, raise system Irql to
  282. HAL_PROFILE_LEVEL and transfer control to the standard system routine
  283. to process any active profiles.
  284. Arguments:
  285. Interrupt - Supplies a pointer to the kernel interrupt object
  286. ServiceContext - Supplies the service context
  287. Return Value:
  288. TRUE
  289. --*/
  290. {
  291. UNREFERENCED_PARAMETER(ServiceContext);
  292. if (HalpProfileRunning != FALSE) {
  293. //
  294. // BUGBUG what do we use for source?
  295. //
  296. KeProfileInterruptWithSource(Interrupt->TrapFrame,0);
  297. }
  298. return TRUE;
  299. }
  300. BOOLEAN
  301. HalpPerfInterrupt (
  302. IN PKINTERRUPT Interrupt,
  303. IN PVOID ServiceContext
  304. )
  305. /*++
  306. Routine Description:
  307. This routine is entered as the result of a perf interrupt. Its
  308. function is to dismiss the interrupt, raise system Irql to
  309. HAL_PROFILE_LEVEL and transfer control to the standard system routine
  310. to process any active profiles.
  311. Arguments:
  312. Interrupt - Supplies a pointer to the kernel interrupt object
  313. ServiceContext - Supplies the service context
  314. Return Value:
  315. TRUE
  316. --*/
  317. {
  318. UNREFERENCED_PARAMETER(Interrupt);
  319. if (HalpPerfInterruptHandler != NULL) {
  320. HalpPerfInterruptHandler(Interrupt->TrapFrame);
  321. } else {
  322. ASSERT(FALSE);
  323. }
  324. //
  325. // Starting with the Willamette processor, the perf interrupt gets masked
  326. // on interrupting. Need to clear the mask before leaving the interrupt
  327. // handler.
  328. //
  329. LOCAL_APIC(LU_PERF_VECTOR), ~INTERRUPT_MASKED;
  330. return TRUE;
  331. }