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.

587 lines
12 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. mpprofil.c
  5. Abstract:
  6. This module implements the HAL profiling functions.
  7. Author:
  8. Shie-Lin Tzong (shielint) 12-Jan-1990
  9. Environment:
  10. Kernel mode only.
  11. Revision History:
  12. bryanwi 20-Sep-90
  13. Forrest Foltz (forrestf) 28-Oct-2000
  14. Ported from ixprofil.asm to ixprofil.c
  15. --*/
  16. #include "halcmn.h"
  17. #include "mpprofil.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. //
  24. // Platform specifc interface functions
  25. //
  26. PPROFILE_INTERFACE HalpProfileInterface;
  27. #define InitializeProfiling HalpProfileInterface->InitializeProfiling
  28. #define EnableMonitoring HalpProfileInterface->EnableMonitoring
  29. #define DisableMonitoring HalpProfileInterface->DisableMonitoring
  30. #define SetInterval HalpProfileInterface->SetInterval
  31. #define QueryInformation HalpProfileInterface->QueryInformation
  32. #define CheckOverflowStatus HalpProfileInterface->CheckOverflowStatus
  33. ULONG HalpProfileRunning = FALSE;
  34. VOID (*HalpPerfInterruptHandler)(PKTRAP_FRAME);
  35. #pragma alloc_text(PAGE, HalpQueryProfileInformation)
  36. #pragma alloc_text(INIT, HalpInitializeProfiling)
  37. VOID
  38. HalpInitializeProfiling (
  39. ULONG Number
  40. )
  41. /*++
  42. Routine Description:
  43. This routine is called at phase 1 initialization to initialize profiling
  44. for each processor in the system.
  45. Arguments:
  46. Number - Supplies the processor number.
  47. Return Value:
  48. None.
  49. --*/
  50. {
  51. if (Number == 0) {
  52. //
  53. // Setup profile interface functions of Amd64
  54. //
  55. HalpProfileInterface = &Amd64PriofileInterface;
  56. HalpPerfInterruptHandler = NULL;
  57. }
  58. InitializeProfiling();
  59. }
  60. VOID
  61. HalStartProfileInterrupt(
  62. KPROFILE_SOURCE ProfileSource
  63. )
  64. /*++
  65. Routine Description:
  66. If ProfileSource is ProfileTime, this routine enables local APIC
  67. Timer interrupt. Otherwise it calls the platform specific interface
  68. functions to enable the monitoring of the specifed performance event
  69. and set up MSRs to generate performance monitor interrupt (PMI) when
  70. counters overflow.
  71. This function is called at PROFILE_LEVEL on every processor.
  72. Arguments:
  73. ProfileSource - Supplies the Profile Source.
  74. Return Value:
  75. None.
  76. --*/
  77. {
  78. ULONG initialCount;
  79. if (ProfileSource == ProfileTime) {
  80. initialCount = HalpGetCurrentHalPcr()->ProfileCountDown;
  81. LOCAL_APIC(LU_INITIAL_COUNT) = initialCount;
  82. HalpProfileRunning = TRUE;
  83. //
  84. // Set the Local APIC Timer to interrupt periodically at
  85. // APIC_PROFILE_VECTOR
  86. //
  87. LOCAL_APIC(LU_TIMER_VECTOR) = APIC_TIMER_ENABLED;
  88. }
  89. else {
  90. EnableMonitoring(ProfileSource);
  91. }
  92. }
  93. VOID
  94. HalStopProfileInterrupt (
  95. IN ULONG ProfileSource
  96. )
  97. /*++
  98. Routine Description:
  99. If ProfileSource is ProfileTime, this routine disables local APIC
  100. Timer interrupt. Otherwise if calls the platform specific interface
  101. functions to disable the monitoring of specified performance event
  102. and its interrupt.
  103. This function is called at PROFILE_LEVEL on every processor.
  104. Arguments:
  105. ProfileSource - Supplies the Profile Source.
  106. Return Value:
  107. None.
  108. --*/
  109. {
  110. if (ProfileSource == ProfileTime) {
  111. HalpProfileRunning = FALSE;
  112. LOCAL_APIC(LU_TIMER_VECTOR) = APIC_TIMER_DISABLED;
  113. }
  114. else {
  115. DisableMonitoring(ProfileSource);
  116. }
  117. }
  118. ULONG_PTR
  119. HalSetProfileInterval (
  120. ULONG_PTR Interval
  121. )
  122. /*++
  123. Routine Description:
  124. This procedure sets the interrupt rate (and thus the sampling
  125. interval) of the ProfileTime.
  126. Arguments:
  127. Interval - Supplies the desired profile interrupt interval which is
  128. specified in 100ns units (MINIMUM is 1221 or 122.1 uS,
  129. see ke\profobj.c )
  130. Return Value:
  131. Interval actually used
  132. --*/
  133. {
  134. ULONG64 period;
  135. ULONG apicFreqency;
  136. PHALPCR halPcr;
  137. ULONG countDown;
  138. halPcr = HalpGetCurrentHalPcr();
  139. //
  140. // Call SetInterval to validate the input value and update
  141. // internal structure
  142. //
  143. period = Interval;
  144. SetInterval(ProfileTime, &period);
  145. //
  146. // Compute the countdown value corresponding to the desired period.
  147. // The calculation is done with 64-bit intermediate values.
  148. //
  149. countDown =
  150. (ULONG)((period * halPcr->ApicClockFreqHz) / TIME_UNITS_PER_SECOND);
  151. halPcr->ProfileCountDown = countDown;
  152. LOCAL_APIC(LU_INITIAL_COUNT) = countDown;
  153. return period;
  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. // We're going to be polling the CMOS_STATUS_A register. Program
  172. // that register address here, outside of the loop.
  173. //
  174. WRITE_PORT_UCHAR(CMOS_ADDRESS_PORT,CMOS_STATUS_A);
  175. //
  176. // Wait for the status bit to be clear
  177. //
  178. do {
  179. value = READ_PORT_UCHAR(CMOS_DATA_PORT);
  180. } while ((value & CMOS_STATUS_BUSY) != 0);
  181. //
  182. // Now wait for the rising edge of the status bit
  183. //
  184. do {
  185. value = READ_PORT_UCHAR(CMOS_DATA_PORT);
  186. } while ((value & CMOS_STATUS_BUSY) == 0);
  187. }
  188. ULONG
  189. HalpScaleTimers (
  190. VOID
  191. )
  192. /*++
  193. Routine Description:
  194. Determines the frequency of the APIC timer. This routine is run
  195. during initialization
  196. Arguments:
  197. None.
  198. Return Value:
  199. None.
  200. --*/
  201. {
  202. ULONG flags;
  203. ULONG passCount;
  204. ULONG64 cpuFreq;
  205. ULONG apicFreq;
  206. UCHAR value;
  207. PHALPCR halPcr;
  208. HalpAcquireCmosSpinLock();
  209. flags = HalpDisableInterrupts();
  210. LOCAL_APIC(LU_TIMER_VECTOR) = APIC_TIMER_DISABLED;
  211. LOCAL_APIC(LU_DIVIDER_CONFIG) = LU_DIVIDE_BY_1;
  212. passCount = 2;
  213. while (passCount > 0) {
  214. //
  215. // Make sure the write has occured
  216. //
  217. LOCAL_APIC(LU_TIMER_VECTOR);
  218. //
  219. // Wait for the rising edge of the UIP bit, this is the start of the
  220. // cycle.
  221. //
  222. HalpWaitForCmosRisingEdge();
  223. //
  224. // At this point the UIP bit has just changed to the set state.
  225. // Clear the time stamp counter and start the APIC counting down
  226. // from it's maximum value.
  227. //
  228. PROCESSOR_FENCE;
  229. LOCAL_APIC(LU_INITIAL_COUNT) = 0xFFFFFFFF;
  230. cpuFreq = ReadTimeStampCounter();
  231. //
  232. // Wait for the next rising edge, this marks the end of the CMOS
  233. // clock update cycle.
  234. //
  235. HalpWaitForCmosRisingEdge();
  236. PROCESSOR_FENCE;
  237. apicFreq = 0xFFFFFFFF - LOCAL_APIC(LU_CURRENT_COUNT);
  238. cpuFreq = ReadTimeStampCounter() - cpuFreq;
  239. passCount -= 1;
  240. }
  241. halPcr = HalpGetCurrentHalPcr();
  242. //
  243. // cpuFreq is elapsed timestamp in one second. Round to nearest
  244. // 10Khz and store.
  245. //
  246. halPcr->TSCHz = TIMER_ROUND(cpuFreq);
  247. //
  248. // Calculate the apic frequency, rounding to the nearest 10Khz
  249. //
  250. apicFreq = TIMER_ROUND(apicFreq);
  251. halPcr->ApicClockFreqHz = apicFreq;
  252. //
  253. // Store microsecond representation of TSC frequency.
  254. //
  255. halPcr->StallScaleFactor = (ULONG)(halPcr->TSCHz / 1000000);
  256. if ((halPcr->TSCHz % 1000000) != 0) {
  257. halPcr->StallScaleFactor += 1;
  258. }
  259. HalpReleaseCmosSpinLock();
  260. halPcr->ProfileCountDown = apicFreq;
  261. //
  262. // Set the interrupt rate in the chip and return the apic frequency
  263. //
  264. LOCAL_APIC(LU_INITIAL_COUNT) = apicFreq;
  265. HalpRestoreInterrupts(flags);
  266. return halPcr->ApicClockFreqHz;
  267. }
  268. BOOLEAN
  269. HalpProfileInterrupt (
  270. IN PKINTERRUPT Interrupt,
  271. IN PVOID ServiceContext
  272. )
  273. /*++
  274. Routine Description:
  275. Interrupt handler of APIC_PROFILE_VECTOR
  276. This routine is entered as the result of local APIC Timer interrupt.
  277. Its function is to update the profile information of ProfileTime.
  278. Arguments:
  279. Interrupt - Supplies a pointer to the kernel interrupt object
  280. ServiceContext - Supplies the service context
  281. Return Value:
  282. TRUE
  283. --*/
  284. {
  285. UNREFERENCED_PARAMETER(ServiceContext);
  286. if (HalpProfileRunning != FALSE) {
  287. KeProfileInterruptWithSource(Interrupt->TrapFrame, ProfileTime);
  288. }
  289. return TRUE;
  290. }
  291. BOOLEAN
  292. HalpPerfInterrupt (
  293. IN PKINTERRUPT Interrupt,
  294. IN PVOID ServiceContext
  295. )
  296. /*++
  297. Routine Description:
  298. Interrupt handler of APIC_PERF_VECTOR
  299. This routine is entered as the result of an overflow interrupt
  300. from performance-monitoring counters. Its function is to find
  301. out the counters overflowed, reset them and update the related
  302. profile information for the profile objects.
  303. Arguments:
  304. Interrupt - Supplies a pointer to the kernel interrupt object
  305. ServiceContext - Supplies the service context
  306. Return Value:
  307. TRUE
  308. --*/
  309. {
  310. ULONG_PTR InterVal = 0;
  311. OVERFLOW_STATUS OverflowStatus;
  312. ULONG i;
  313. KPROFILE_SOURCE *p;
  314. UNREFERENCED_PARAMETER(Interrupt);
  315. if (HalpPerfInterruptHandler != NULL) {
  316. HalpPerfInterruptHandler(Interrupt->TrapFrame);
  317. return TRUE;
  318. }
  319. CheckOverflowStatus(&OverflowStatus);
  320. i = OverflowStatus.Number;
  321. p = OverflowStatus.pSource;
  322. //
  323. // ASSERT if we reached here but no counter overflowed
  324. //
  325. ASSERT(i);
  326. while (i--) {
  327. DisableMonitoring(*p);
  328. KeProfileInterruptWithSource(Interrupt->TrapFrame, *p);
  329. EnableMonitoring(*p);
  330. p++;
  331. }
  332. return TRUE;
  333. }
  334. NTSTATUS
  335. HalpSetProfileSourceInterval(
  336. IN KPROFILE_SOURCE ProfileSource,
  337. IN OUT ULONG_PTR *Interval
  338. )
  339. /*++
  340. Routine Description:
  341. This function sets the interrupt interval of given profile source.
  342. Arguments:
  343. ProfileSource - Supplies the profile source.
  344. Interval - Supplies the interval value and returns the actual interval.
  345. Return Value:
  346. STATUS_SUCCESS - If the profile interval is successfully updated.
  347. STATUS_NOT_SUPPORTED - If the specified profile source is not supported.
  348. --*/
  349. {
  350. if (ProfileSource == ProfileTime) {
  351. *Interval = HalSetProfileInterval(*Interval);
  352. return STATUS_SUCCESS;
  353. }
  354. return SetInterval(ProfileSource, Interval);
  355. }
  356. NTSTATUS
  357. HalpQueryProfileInformation(
  358. IN HAL_QUERY_INFORMATION_CLASS InformationClass,
  359. IN ULONG BufferSize,
  360. OUT PVOID Buffer,
  361. OUT PULONG ReturnedLength
  362. )
  363. /*++
  364. Routine Description:
  365. This function retrieves the information of profile sources.
  366. Arguments:
  367. InformationClass - Constant that describes the type of information .
  368. BufferSize - Size of the memory pointed to by Buffer.
  369. Buffer - Requested information described by InformationClass.
  370. ReturnedLength - The actual bytes returned or needed for the requested
  371. information.
  372. Return Value:
  373. STATUS_SUCCESS - If the requested information is retrieved successfully.
  374. STATUS_INFO_LENGTH_MISMATCH - If the incoming BufferSize is too small.
  375. STATUS_NOT_SUPPORTED - If the specified information class or profile
  376. source is not supported.
  377. --*/
  378. {
  379. PHAL_PROFILE_SOURCE_INFORMATION_EX ProfileInformation;
  380. return QueryInformation (InformationClass,
  381. BufferSize,
  382. Buffer,
  383. ReturnedLength);
  384. }