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.

684 lines
17 KiB

  1. title "Interval Clock Interrupt"
  2. ;++
  3. ;
  4. ; Copyright (c) 1989 Microsoft Corporation
  5. ;
  6. ; Module Name:
  7. ;
  8. ; mpprofile.asm
  9. ;
  10. ; Abstract:
  11. ;
  12. ; This module implements the code necessary to initialize,
  13. ; field and process the profile interrupt.
  14. ;
  15. ; Author:
  16. ;
  17. ; Shie-Lin Tzong (shielint) 12-Jan-1990
  18. ;
  19. ; Environment:
  20. ;
  21. ; Kernel mode only.
  22. ;
  23. ; Revision History:
  24. ;
  25. ; bryanwi 20-Sep-90
  26. ;
  27. ;--
  28. .586p
  29. .xlist
  30. include hal386.inc
  31. include i386\ix8259.inc
  32. include i386\ixcmos.inc
  33. include callconv.inc ; calling convention macros
  34. include i386\kimacro.inc
  35. include mac386.inc
  36. include apic.inc
  37. include ntapic.inc
  38. include i386\mp8254.inc
  39. .list
  40. EXTRNP _DbgBreakPoint,0,IMPORT
  41. EXTRNP _KeProfileInterrupt,1,IMPORT
  42. EXTRNP Kei386EoiHelper,0,IMPORT
  43. EXTRNP _HalEndSystemInterrupt,2
  44. EXTRNP _HalBeginSystemInterrupt,3
  45. EXTRNP _HalpAcquireSystemHardwareSpinLock,0
  46. EXTRNP _HalpReleaseSystemHardwareSpinLock,0
  47. EXTRNP _HalpAcquireCmosSpinLock ,0
  48. EXTRNP _HalpReleaseCmosSpinLock ,0
  49. extrn _HalpUse8254:BYTE
  50. ;
  51. ; APIC Timer Constants
  52. ;
  53. APIC_TIMER_DISABLED equ (INTERRUPT_MASKED OR PERIODIC_TIMER OR APIC_PROFILE_VECTOR)
  54. APIC_TIMER_ENABLED equ (PERIODIC_TIMER OR APIC_PROFILE_VECTOR)
  55. ;
  56. ; number of 100ns intervals in one second
  57. ;
  58. Num100nsIntervalsPerSec equ 10000000
  59. _DATA SEGMENT DWORD PUBLIC 'DATA'
  60. ALIGN dword
  61. public _HalpProfileRunning, _HalpPerfInterruptHandler
  62. _HalpProfileRunning dd 0
  63. _HalpPerfInterruptHandler dd 0
  64. _DATA ends
  65. _TEXT SEGMENT DWORD PUBLIC 'CODE'
  66. ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
  67. ;++
  68. ;
  69. ; HalStartProfileInterrupt(
  70. ; IN ULONG Reserved
  71. ; );
  72. ;
  73. ; Routine Description:
  74. ;
  75. ; What we do here is set the interrupt rate to the value that's been set
  76. ; by the KeSetProfileInterval routine. Then enable the APIC Timer interrupt.
  77. ;
  78. ; This function gets called on every processor so the hal can enable
  79. ; a profile interrupt on each processor.
  80. ;
  81. ;--
  82. cPublicProc _HalStartProfileInterrupt ,1
  83. ;
  84. ; Set the interrupt rate to what is actually needed.
  85. ;
  86. mov eax, PCR[PcHal.ProfileCountDown]
  87. mov dword ptr APIC[LU_INITIAL_COUNT], eax
  88. mov _HalpProfileRunning, 1 ; Indicate profiling
  89. ;
  90. ; Set the Local APIC Timer to interrupt Periodically at APIC_PROFILE_VECTOR
  91. ;
  92. mov dword ptr APIC[LU_TIMER_VECTOR], APIC_TIMER_ENABLED
  93. stdRET _HalStartProfileInterrupt
  94. stdENDP _HalStartProfileInterrupt
  95. ;++
  96. ;
  97. ; HalStopProfileInterrupt(
  98. ; IN ULONG Reserved
  99. ; );
  100. ;
  101. ; Routine Description:
  102. ;
  103. ;--
  104. cPublicProc _HalStopProfileInterrupt ,1
  105. ;
  106. ; Turn off profiling
  107. ;
  108. mov _HalpProfileRunning, 0 ; Indicate profiling is off
  109. mov dword ptr APIC[LU_TIMER_VECTOR], APIC_TIMER_DISABLED
  110. stdRET _HalStopProfileInterrupt
  111. stdENDP _HalStopProfileInterrupt
  112. ;++
  113. ; ULONG
  114. ; HalSetProfileInterval (
  115. ; ULONG Interval
  116. ; );
  117. ;
  118. ; Routine Description:
  119. ;
  120. ; This procedure sets the interrupt rate (and thus the sampling
  121. ; interval) for the profiling interrupt.
  122. ;
  123. ; Arguments:
  124. ;
  125. ; (TOS+4) - Interval in 100ns unit.
  126. ; (MINIMUM is 1221 or 122.1 uS) see ke\profobj.c
  127. ;
  128. ; Return Value:
  129. ;
  130. ; Interval actually used
  131. ;
  132. ;--
  133. cPublicProc _HalSetProfileInterval ,1
  134. mov ecx, [esp+4] ; ecx = interval in 100ns unit
  135. and ecx, 7FFFFFFFh ; Remove sign bit.
  136. ;
  137. ; The only possible error is if we will cause a divide overflow
  138. ; this can happen only if the (frequency * request count) is
  139. ; greater than 2^32* Num100nsIntervalsPerSec.
  140. ;
  141. ; To protect against that we just ensure that the request count
  142. ; is less than (or equal to) Num100nsIntervalsPerSec
  143. ;
  144. cmp ecx, Num100nsIntervalsPerSec
  145. jle @f
  146. mov ecx, Num100nsIntervalsPerSec
  147. @@:
  148. ;
  149. ; Save the interval we're using to return
  150. ;
  151. push ecx
  152. ;
  153. ; Compute the countdown value
  154. ;
  155. ; let
  156. ; R == caller's requested 100ns interval count
  157. ; F == APIC Counter Freguency (hz)
  158. ; N == Number of 100ns Intervals per sec
  159. ;
  160. ; then
  161. ; count = (R*F)/N
  162. ;
  163. ; Get the previously computed APIC counter Freq
  164. ; for this processor
  165. ;
  166. mov eax, PCR[PcHal.ApicClockFreqHz]
  167. ;
  168. ; eax <= F and ecx <= R
  169. ;
  170. ;
  171. ; Compute (request count) * (ApicClockFreqHz) == (R*F)
  172. ;
  173. xor edx, edx
  174. mul ecx
  175. ;
  176. ; edx:eax contains 64Bits of (R*F)
  177. ;
  178. mov ecx, Num100nsIntervalsPerSec
  179. div ecx
  180. ;
  181. ; Compute (R*F) / Num100nsIntervalsPerSec == (R*F)/N
  182. ;
  183. mov PCR[PcHal.ProfileCountDown], eax ; Save the Computed Count Down
  184. mov edx, dword ptr APIC[LU_CURRENT_COUNT]
  185. ;
  186. ; Set the interrupt rate in the chip.
  187. ;
  188. mov dword ptr APIC[LU_INITIAL_COUNT], eax
  189. pop eax ; Return Actual Interval Used
  190. stdRET _HalSetProfileInterval
  191. stdENDP _HalSetProfileInterval
  192. page ,132
  193. subttl "System Profile Interrupt"
  194. ;++
  195. ;
  196. ; Routine Description:
  197. ;
  198. ; This routine is entered as the result of a profile interrupt.
  199. ; Its function is to dismiss the interrupt, raise system Irql to
  200. ; HAL_PROFILE_LEVEL and transfer control to
  201. ; the standard system routine to process any active profiles.
  202. ;
  203. ; Arguments:
  204. ;
  205. ; None
  206. ; Interrupt is disabled
  207. ;
  208. ; Return Value:
  209. ;
  210. ; Does not return, jumps directly to KeProfileInterrupt, which returns
  211. ;
  212. ; Sets Irql = HAL_PROFILE_LEVEL and dismisses the interrupt
  213. ;
  214. ;--
  215. ENTER_DR_ASSIST Hpi_a, Hpi_t
  216. cPublicProc _HalpProfileInterrupt ,0
  217. ;
  218. ; Save machine state in trap frame
  219. ;
  220. ENTER_INTERRUPT Hpi_a, Hpi_t
  221. ;
  222. ; (esp) - base of trap frame
  223. ;
  224. push APIC_PROFILE_VECTOR
  225. sub esp, 4 ; allocate space to save OldIrql
  226. stdCall _HalBeginSystemInterrupt, <HAL_PROFILE_LEVEL,APIC_PROFILE_VECTOR,esp>
  227. cmp _HalpProfileRunning, 0 ; Profiling?
  228. je @f ; if not just exit
  229. stdCall _KeProfileInterrupt,<ebp> ; (ebp) = TrapFrame address
  230. @@:
  231. INTERRUPT_EXIT
  232. stdENDP _HalpProfileInterrupt
  233. subttl "System Perf Interrupt"
  234. ;++
  235. ;
  236. ; Routine Description:
  237. ;
  238. ; This routine is entered as the result of a perf interrupt.
  239. ; Its function is to dismiss the interrupt, raise system Irql to
  240. ; HAL_PROFILE_LEVEL and transfer control to
  241. ; the standard system routine to process any active profiles.
  242. ;
  243. ; Arguments:
  244. ;
  245. ; None
  246. ; Interrupt is disabled
  247. ;
  248. ; Return Value:
  249. ;
  250. ; Does not return, jumps directly to KeProfileInterrupt, which returns
  251. ;
  252. ; Sets Irql = HAL_PROFILE_LEVEL and dismisses the interrupt
  253. ;
  254. ;--
  255. ENTER_DR_ASSIST Hpf_a, Hpf_t
  256. cPublicProc _HalpPerfInterrupt ,0
  257. ;
  258. ; Save machine state in trap frame
  259. ;
  260. ENTER_INTERRUPT Hpf_a, Hpf_t
  261. ;
  262. ; (esp) - base of trap frame
  263. ;
  264. push APIC_PERF_VECTOR
  265. sub esp, 4 ; allocate space to save OldIrql
  266. stdCall _HalBeginSystemInterrupt, <HAL_PROFILE_LEVEL,APIC_PERF_VECTOR,esp>
  267. ;
  268. ; Invoke perf interrupt handler
  269. ;
  270. mov ecx, ebp ; param1 = trap frame
  271. mov eax, _HalpPerfInterruptHandler
  272. or eax, eax
  273. jz short hpf20
  274. call eax
  275. hpf10:
  276. ;
  277. ; Starting with the Willamette processor, the perf interrupt gets masked on
  278. ; interrupting. Needs to clear the mask before leaving the interrupt handler.
  279. ; Do this regardless of whether a valid interrupt handler exists or not.
  280. ;
  281. and dword ptr APIC[LU_PERF_VECTOR], (NOT INTERRUPT_MASKED)
  282. INTERRUPT_EXIT
  283. hpf20:
  284. if DBG
  285. int 3
  286. endif
  287. jmp short hpf10
  288. stdENDP _HalpPerfInterrupt
  289. _TEXT ends
  290. PAGELK SEGMENT PARA PUBLIC 'CODE'
  291. ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
  292. ;++
  293. ;
  294. ; VOID
  295. ; HalCalibratePerformanceCounter (
  296. ; IN LONG volatile *Number,
  297. ; IN ULONGLONG NewCount
  298. ; )
  299. ;
  300. ; /*++
  301. ;
  302. ; Routine Description:
  303. ;
  304. ; This routine calibrates the performance counter value for a
  305. ; multiprocessor system. The calibration can be done by zeroing
  306. ; the current performance counter, or by calculating a per-processor
  307. ; skewing between each processors counter.
  308. ;
  309. ; Arguments:
  310. ;
  311. ; Number - Supplies a pointer to count of the number of processors in
  312. ; the configuration.
  313. ;
  314. ; NewCount - Supplies the value to synchronize the counter too
  315. ;
  316. ; Return Value:
  317. ;
  318. ; None.
  319. ;--
  320. NewCountLow equ [esp + 24]
  321. NewCountHigh equ [esp + 28]
  322. ifdef MMTIMER
  323. cPublicProc _HalpAcpiTimerCalibratePerfCount,3
  324. else
  325. cPublicProc _HalCalibratePerformanceCounter,3
  326. endif
  327. cPublicFpo 3,0
  328. push esi
  329. push edi
  330. push ebx
  331. mov esi, [esp+16] ; pointer to Number
  332. pushfd ; save previous interrupt state
  333. cli ; disable interrupts
  334. cPublicFpo 3,4
  335. xor eax, eax
  336. lock dec dword ptr [esi] ; count down
  337. @@: YIELD
  338. cmp dword ptr [esi], 0 ; wait for all processors to signal
  339. jnz short @b
  340. cpuid ; fence
  341. mov ecx, MsrTSC ; MSR of time stamp counter
  342. mov eax, NewCountLow
  343. mov edx, NewCountHigh
  344. mov PCR[PcHal.PerfCounterLow], eax
  345. mov PCR[PcHal.PerfCounterHigh], edx
  346. xor eax,eax
  347. xor edx,edx
  348. wrmsr ; zero the time stamp counter
  349. popfd ; restore interrupt flag
  350. pop ebx
  351. pop edi
  352. pop esi
  353. ifdef MMTIMER
  354. stdRET _HalpAcpiTimerCalibratePerfCount
  355. stdENDP _HalpAcpiTimerCalibratePerfCount
  356. else
  357. stdRET _HalCalibratePerformanceCounter
  358. stdENDP _HalCalibratePerformanceCounter
  359. endif
  360. PAGELK ends
  361. INIT SEGMENT PARA PUBLIC 'CODE'
  362. ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
  363. page ,132
  364. subttl "Scale Apic Timer"
  365. ;++
  366. ;
  367. ; VOID
  368. ; HalpScaleTimers (
  369. ; IN VOID
  370. ; )
  371. ;
  372. ; Routine Description:
  373. ;
  374. ; Determines the frequency of the APIC timer. This routine is run
  375. ; during initialization
  376. ;
  377. ;
  378. ;--
  379. cPublicProc _HalpScaleTimers ,0
  380. push ebx
  381. push esi
  382. push edi
  383. ;
  384. ; Don't let anyone in until we've finished here
  385. ;
  386. stdCall _HalpAcquireCmosSpinLock
  387. ;
  388. ; Protect us from interrupts
  389. ;
  390. pushfd
  391. cli
  392. ;
  393. ; First set up the Local Apic Counter
  394. ;
  395. ;
  396. ; Configure the APIC timer
  397. ;
  398. APIC_TIMER_DISABLED equ (INTERRUPT_MASKED OR PERIODIC_TIMER OR APIC_PROFILE_VECTOR)
  399. TIMER_ROUNDING equ 10000
  400. mov dword ptr APIC[LU_TIMER_VECTOR], APIC_TIMER_DISABLED
  401. mov dword ptr APIC[LU_DIVIDER_CONFIG], LU_DIVIDE_BY_1
  402. ;
  403. ; We're going to do this twice & take the second results
  404. ;
  405. mov esi, 2
  406. hst10:
  407. ;
  408. ; Make sure the write has occurred
  409. ;
  410. mov eax, dword ptr APIC[LU_DIVIDER_CONFIG]
  411. ;
  412. ; We don't care what the actual time is we are only interested
  413. ; in seeing the UIP transition. We are garenteed a 1 sec interval
  414. ; if we wait for the UIP bit to complete an entire cycle.
  415. ;
  416. ; We also don't much care which direction the transition we use is
  417. ; as long as we wait for the same transition to read the APIC clock.
  418. ; Just because it is most likely that when we begin the UIP bit will
  419. ; be clear, we'll use the transition from !UIP to UIP.
  420. ;
  421. ;
  422. ; Wait for the UIP bit to be cleared, this is our starting state
  423. ;
  424. @@:
  425. mov al, 0Ah ; Specify register A
  426. CMOS_READ ; (al) = CMOS register A
  427. test al, CMOS_STATUS_BUSY ; Is time update in progress?
  428. jnz short @b ; if z, no, wait some more
  429. ;
  430. ; Wait for the UIP bit to get set
  431. ;
  432. @@:
  433. mov al, 0Ah ; Specify register A
  434. CMOS_READ ; (al) = CMOS register A
  435. test al, CMOS_STATUS_BUSY ; Is time update in progress?
  436. jz short @b ; if z, no, wait some more
  437. ;
  438. ; At this point we found the UIP bit set, now set the initial
  439. ; count. Once we write this register its value is copied to the
  440. ; current count register and countdown starts or continues from
  441. ; there
  442. ;
  443. xor eax, eax
  444. mov PCR[PcHal.PerfCounterLow], eax
  445. mov PCR[PcHal.PerfCounterHigh], eax
  446. cpuid ; fence
  447. mov ecx, MsrTSC ; MSR of RDTSC
  448. xor edx, edx
  449. mov eax, edx
  450. mov dword ptr APIC[LU_INITIAL_COUNT], 0FFFFFFFFH
  451. wrmsr ; Clear TSC count
  452. ;
  453. ; Wait for the UIP bit to be cleared again
  454. ;
  455. @@:
  456. mov al, 0Ah ; Specify register A
  457. CMOS_READ ; (al) = CMOS register A
  458. test al, CMOS_STATUS_BUSY ; Is time update in progress?
  459. jnz short @b ; if z, no, wait some more
  460. ;
  461. ; Wait for the UIP bit to get set
  462. ;
  463. @@:
  464. mov al, 0Ah ; Specify register A
  465. CMOS_READ ; (al) = CMOS register A
  466. test al, CMOS_STATUS_BUSY ; Is time update in progress?
  467. jz short @b ; if z, no, wait some more
  468. ;
  469. ; The cycle is complete, we found the UIP bit set. Now read
  470. ; the counters and compute the frequency. The frequency is
  471. ; just the ticks counted which is the initial value minus
  472. ; the current value.
  473. ;
  474. xor eax, eax
  475. cpuid ; fence
  476. rdtsc
  477. mov ecx, dword ptr APIC[LU_CURRENT_COUNT]
  478. dec esi ; if this is the first time
  479. jnz hst10 ; around, go loop
  480. mov dword ptr PCR[PcHal][TSCHz], eax ; Frequency LowPart
  481. mov dword ptr PCR[PcHal][TSCHz+4], edx ; Frequency HighPart
  482. mov eax, 0FFFFFFFFH
  483. sub eax, ecx
  484. ;
  485. ; Round the Apic Timer Freq
  486. ;
  487. xor edx, edx ; (edx:eax) = dividend
  488. mov ecx, TIMER_ROUNDING
  489. div ecx ; now edx has remainder
  490. cmp edx, TIMER_ROUNDING / 2
  491. jle @f ; if less don't round
  492. inc eax ; else round up
  493. @@:
  494. ;
  495. ; Multiply by the Rounding factor to get the rounded Freq
  496. ;
  497. mov ecx, TIMER_ROUNDING
  498. xor edx, edx
  499. mul ecx
  500. mov dword ptr PCR[PcHal.ApicClockFreqHz], eax
  501. ;
  502. ; Round TSC freq
  503. ;
  504. mov eax, dword ptr PCR[PcHal][TSCHz] ; Frequency LowPart
  505. mov edx, dword ptr PCR[PcHal][TSCHz+4] ; Frequency HighPart
  506. mov ecx, TIMER_ROUNDING
  507. div ecx ; now edx has remainder
  508. cmp edx, TIMER_ROUNDING / 2
  509. jle @f ; if less don't round
  510. inc eax ; else round up
  511. @@:
  512. mov ecx, TIMER_ROUNDING
  513. xor edx, edx
  514. mul ecx
  515. mov dword ptr PCR[PcHal][TSCHz], eax ; Frequency LowPart
  516. mov dword ptr PCR[PcHal][TSCHz+4], edx ; Frequency HighPart
  517. ;
  518. ; Convert TSC to microseconds
  519. ;
  520. mov ecx, 1000000
  521. div ecx ; Convert to microseconds
  522. xor ecx, ecx
  523. cmp ecx, edx ; any remainder?
  524. adc eax, ecx ; Yes, add one
  525. mov PCR[PcStallScaleFactor], eax
  526. stdCall _HalpReleaseCmosSpinLock
  527. ;
  528. ; Return Value is the timer frequency
  529. ;
  530. mov eax, dword ptr PCR[PcHal.ApicClockFreqHz]
  531. mov PCR[PcHal.ProfileCountDown], eax
  532. ;
  533. ; Set the interrupt rate in the chip.
  534. ;
  535. mov dword ptr APIC[LU_INITIAL_COUNT], eax
  536. popfd
  537. pop edi
  538. pop esi
  539. pop ebx
  540. stdRET _HalpScaleTimers
  541. stdENDP _HalpScaleTimers
  542. INIT ends
  543. end