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.

1091 lines
28 KiB

  1. title "Interval Clock Interrupt"
  2. ;++
  3. ;
  4. ; Copyright (c) 1989 Microsoft Corporation
  5. ;
  6. ; Module Name:
  7. ;
  8. ; spclock.asm
  9. ;
  10. ; Abstract:
  11. ;
  12. ; This module implements the code necessary to field and process the
  13. ; interval clock 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. ; Add KiSetProfileInterval, KiStartProfileInterrupt,
  28. ; KiStopProfileInterrupt procedures.
  29. ; KiProfileInterrupt ISR.
  30. ; KiProfileList, KiProfileLock are delcared here.
  31. ;
  32. ; shielint 10-Dec-90
  33. ; Add performance counter support.
  34. ; Move system clock to irq8, ie we now use RTC to generate system
  35. ; clock. Performance count and Profile use timer 1 counter 0.
  36. ; The interval of the irq0 interrupt can be changed by
  37. ; KiSetProfileInterval. Performance counter does not care about the
  38. ; interval of the interrupt as long as it knows the rollover count.
  39. ; Note: Currently I implemented 1 performance counter for the whole
  40. ; i386 NT. It works on UP and SystemPro.
  41. ;
  42. ; John Vert (jvert) 11-Jul-1991
  43. ; Moved from ke\i386 to hal\i386. Removed non-HAL stuff
  44. ;
  45. ; shie-lin tzong (shielint) 13-March-92
  46. ; Move System clock back to irq0 and use RTC (irq8) to generate
  47. ; profile interrupt. Performance counter and system clock use time1
  48. ; counter 0 of 8254.
  49. ;
  50. ;
  51. ;--
  52. .386p
  53. .xlist
  54. include callconv.inc
  55. include hal386.inc
  56. include i386\ix8259.inc
  57. include i386\ixcmos.inc
  58. include i386\kimacro.inc
  59. include mac386.inc
  60. include i386\spmp.inc
  61. .list
  62. EXTRNP _DbgBreakPoint,0,IMPORT
  63. extrn KiI8259MaskTable:DWORD
  64. EXTRNP _KeUpdateSystemTime,0
  65. EXTRNP _KeUpdateRunTime,1,IMPORT
  66. EXTRNP Kei386EoiHelper,0,IMPORT
  67. EXTRNP _HalEndSystemInterrupt,2
  68. EXTRNP _HalBeginSystemInterrupt,3
  69. EXTRNP _HalRequestIpi,1
  70. EXTRNP _HalpAcquireCmosSpinLock ,0
  71. EXTRNP _HalpReleaseCmosSpinLock ,0
  72. EXTRNP _KeStallExecutionProcessor, 1
  73. extrn _HalpProcessorPCR:DWORD
  74. extrn _HalpSystemHardwareLock:DWORD
  75. extrn _HalpFindFirstSetRight:BYTE
  76. extrn _Sp8259PerProcessorMode:BYTE
  77. EXTRNP _KeSetTimeIncrement,2,IMPORT
  78. EXTRNP _HalpMcaQueueDpc, 0
  79. extrn _SpType:BYTE
  80. ;
  81. ; Constants used to initialize timer 0
  82. ;
  83. TIMER1_DATA_PORT0 EQU 40H ; Timer1, channel 0 data port
  84. TIMER1_CONTROL_PORT0 EQU 43H ; Timer1, channel 0 control port
  85. TIMER1_IRQ EQU 0 ; Irq 0 for timer1 interrupt
  86. COMMAND_8254_COUNTER0 EQU 00H ; Select count 0
  87. COMMAND_8254_RW_16BIT EQU 30H ; Read/Write LSB firt then MSB
  88. COMMAND_8254_MODE2 EQU 4 ; Use mode 2
  89. COMMAND_8254_BCD EQU 0 ; Binary count down
  90. COMMAND_8254_LATCH_READ EQU 0 ; Latch read command
  91. PERFORMANCE_FREQUENCY EQU 1193182
  92. ;
  93. ; ==== Values used for System Clock ====
  94. ;
  95. ;
  96. ; Convert the interval to rollover count for 8254 Timer1 device.
  97. ; Timer1 counts down a 16 bit value at a rate of 1.193181667M counts-per-sec.
  98. ;
  99. ;
  100. ; The best fit value closest to 10ms (but not below) is 10.0144012689ms:
  101. ; ROLLOVER_COUNT 11949
  102. ; TIME_INCREMENT 100144
  103. ; Calculated error is -.0109472 s/day
  104. ;
  105. ; The best fit value closest to 15ms (but not above) is 14.9952019ms:
  106. ; ROLLOVER_COUNT 17892
  107. ; TIME_INCREMENT 149952
  108. ; Calculated error is -.0109472 s/day
  109. ;
  110. ; On 486 class machines or better we use a 10ms tick, on 386
  111. ; class machines we use a 15ms tick
  112. ;
  113. ROLLOVER_COUNT_10MS EQU 11949
  114. TIME_INCREMENT_10MS EQU 100144
  115. ;
  116. ; Value for KeQueryPerf retries.
  117. ;
  118. MAX_PERF_RETRY equ 3 ; Odly enough 3 is plenty.
  119. _DATA SEGMENT DWORD PUBLIC 'DATA'
  120. ;
  121. ; The following array stores the per microsecond loop count for each
  122. ; central processor.
  123. ;
  124. public _HalpIpiClock
  125. _HalpIpiClock dd 0 ; Processors to IPI clock pulse to
  126. public HalpPerfCounterLow
  127. public HalpPerfCounterHigh
  128. HalpPerfCounterLow dd 0
  129. HalpPerfCounterHigh dd 0
  130. HalpPerfP0Value dd 0
  131. HalpCalibrateFlag db 0
  132. db 0
  133. dw 0
  134. HalpRollOverCount dd 0
  135. public _HalpClockWork, _HalpClockSetMSRate, _HalpClockMcaQueueDpc
  136. _HalpClockWork label dword
  137. _HalpClockSetMSRate db 0
  138. _HalpClockMcaQueueDpc db 0
  139. _bReserved1 db 0
  140. _bReserved2 db 0
  141. ;
  142. ; Storage for variable to ensure that queries are always
  143. ; greater than the last.
  144. ;
  145. HalpLastQueryLowValue dd 0
  146. HalpLastQueryHighValue dd 0
  147. HalpForceDataLock dd 0
  148. ; endmod
  149. _DATA ends
  150. _TEXT SEGMENT DWORD PUBLIC 'CODE'
  151. ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
  152. page ,132
  153. subttl "Initialize Clock"
  154. ;++
  155. ;
  156. ; VOID
  157. ; HalpInitializeClock (
  158. ; )
  159. ;
  160. ; Routine Description:
  161. ;
  162. ; This routine initialize system time clock using 8254 timer1 counter 0
  163. ; to generate an interrupt at every 15ms interval at 8259 irq0
  164. ;
  165. ; See the definition of TIME_INCREMENT and ROLLOVER_COUNT if clock rate
  166. ; needs to be changed.
  167. ;
  168. ; Arguments:
  169. ;
  170. ; None
  171. ;
  172. ; Return Value:
  173. ;
  174. ; None.
  175. ;
  176. ;--
  177. cPublicProc _HalpInitializeClock ,0
  178. ;
  179. ; Use 15ms or 10ms clock tick?
  180. ;
  181. mov edx, TIME_INCREMENT_10MS ; yes, use 10ms clock
  182. mov ecx, ROLLOVER_COUNT_10MS
  183. ;
  184. ; Fill in PCR value with TIME_INCREMENT
  185. ; (edx) = TIME_INCREMENT
  186. ; (ecx) = ROLLOVER_COUNT
  187. ;
  188. cmp byte ptr PCR[PcHal.PcrNumber], 0
  189. jne short icl_10
  190. push ecx
  191. stdCall _KeSetTimeIncrement, <edx, edx>
  192. pop ecx
  193. pushfd ; save caller's eflag
  194. cli ; make sure interrupts are disabled
  195. ;
  196. ; Set clock rate
  197. ; (ecx) = RollOverCount
  198. ;
  199. mov al,COMMAND_8254_COUNTER0+COMMAND_8254_RW_16BIT+COMMAND_8254_MODE2
  200. out TIMER1_CONTROL_PORT0, al ;program count mode of timer 0
  201. IoDelay
  202. mov al, cl
  203. out TIMER1_DATA_PORT0, al ; program timer 0 LSB count
  204. IoDelay
  205. mov al,ch
  206. out TIMER1_DATA_PORT0, al ; program timer 0 MSB count
  207. popfd ; restore caller's eflag
  208. mov HalpRollOverCount, ecx ; Set RollOverCount & initialized
  209. stdRET _HalpInitializeClock
  210. icl_10:
  211. pushfd ; save caller's eflag
  212. cli ; make sure interrupts are disabled
  213. ;
  214. ; initialize clock, non-p0
  215. ; (ecx) = ROLLOVER_COUNT
  216. ;
  217. mov al,COMMAND_8254_COUNTER0+COMMAND_8254_RW_16BIT+COMMAND_8254_MODE2
  218. out TIMER1_CONTROL_PORT0, al ;program count mode of timer 0
  219. IoDelay
  220. mov al, cl
  221. out TIMER1_DATA_PORT0, al ; program timer 0 LSB count
  222. IoDelay
  223. mov al,ch
  224. out TIMER1_DATA_PORT0, al ; program timer 0 MSB count
  225. popfd ; restore caller's eflag
  226. stdRET _HalpInitializeClock
  227. stdENDP _HalpInitializeClock
  228. ;++
  229. ;
  230. ; VOID
  231. ; HalCalibratePerformanceCounter (
  232. ; IN LONG volatile *Number,
  233. ; IN ULONGLONG NewCount
  234. ; )
  235. ;
  236. ; /*++
  237. ;
  238. ; Routine Description:
  239. ;
  240. ; This routine calibrates the performance counter value for a
  241. ; multiprocessor system. The calibration can be done by zeroing
  242. ; the current performance counter, or by calculating a per-processor
  243. ; skewing between each processors counter.
  244. ;
  245. ; Arguments:
  246. ;
  247. ; Number - Supplies a pointer to count of the number of processors in
  248. ; the configuration.
  249. ;
  250. ; NewCount - Supplies the value to synchronize the counter too
  251. ;
  252. ; Return Value:
  253. ;
  254. ; None.
  255. ;--
  256. cPublicProc _HalCalibratePerformanceCounter,3
  257. mov eax, [esp+4] ; ponter to Number
  258. pushfd ; save previous interrupt state
  259. cli ; disable interrupts (go to high_level)
  260. lock dec dword ptr [eax] ; count down
  261. @@: cmp dword ptr [eax], 0 ; wait for all processors to signal
  262. jnz short @b
  263. test _Sp8259PerProcessorMode, SP_SMPCLOCK
  264. jz short cal_exit ; 8254 per processor?
  265. xor ecx, ecx
  266. mov al, COMMAND_8254_LATCH_READ+COMMAND_8254_COUNTER0
  267. ; Latch PIT Ctr 0 command.
  268. out TIMER1_CONTROL_PORT0, al
  269. IODelay
  270. in al, TIMER1_DATA_PORT0 ; Read PIT Ctr 0, LSByte.
  271. IODelay
  272. movzx ecx, al
  273. in al, TIMER1_DATA_PORT0 ; Read PIT Ctr 0, MSByte.
  274. mov ch, al ; (CX) = PIT Ctr 0 count.
  275. cmp byte ptr PCR[PcHal.PcrNumber], 0 ; is this the processor
  276. jz short cal_p0 ; which updates HalpPerfCounter?
  277. @@: cmp HalpCalibrateFlag, 0 ; wait for P0 to post it's counter
  278. jz short @b
  279. sub ecx, HalpPerfP0Value ; compute difference
  280. neg ecx
  281. mov PCR[PcHal.PcrPerfSkew], ecx
  282. cal_exit:
  283. popfd
  284. stdRET _HalCalibratePerformanceCounter
  285. cal_p0: mov HalpPerfP0Value, ecx ; post our timer value
  286. mov HalpCalibrateFlag, 1 ; signal we are done
  287. jmp short cal_exit
  288. stdENDP _HalCalibratePerformanceCounter
  289. page ,132
  290. subttl "Query Performance Counter"
  291. ;++
  292. ;
  293. ; LARGE_INTEGER
  294. ; KeQueryPerformanceCounter (
  295. ; OUT PLARGE_INTEGER PerformanceFrequency OPTIONAL
  296. ; )
  297. ;
  298. ; Routine Description:
  299. ;
  300. ; This routine returns current 64-bit performance counter and,
  301. ; optionally, the Performance Frequency.
  302. ;
  303. ; Note this routine can NOT be called at Profiling interrupt
  304. ; service routine. Because this routine depends on IRR0 to determine
  305. ; the actual count.
  306. ;
  307. ; Also note that the performace counter returned by this routine
  308. ; is not necessary the value when this routine is just entered.
  309. ; The value returned is actually the counter value at any point
  310. ; between the routine is entered and is exited.
  311. ;
  312. ; Arguments:
  313. ;
  314. ; PerformanceFrequency [TOS+4] - optionally, supplies the address
  315. ; of a variable to receive the performance counter frequency.
  316. ;
  317. ; Return Value:
  318. ;
  319. ; Current value of the performance counter will be returned.
  320. ;
  321. ;--
  322. ;
  323. ; Parameter definitions
  324. ;
  325. KqpcFrequency EQU [esp+20] ; User supplied Performance Frequence
  326. RetryPerfCount EQU [esp] ; Local retry variable
  327. cPublicProc _KeQueryPerformanceCounter ,1
  328. push ebx
  329. push esi
  330. push edi
  331. push 0 ; make space for RetryPerfCount
  332. ;
  333. ; First check to see if the performance counter has been initialized yet.
  334. ; Since the kernel debugger calls KeQueryPerformanceCounter to support the
  335. ; !timer command, we need to return something reasonable before 8254
  336. ; initialization has occured. Reading garbage off the 8254 is not reasonable.
  337. ;
  338. cmp HalpRollOverCount, 0
  339. jne short Kqpc11 ; ok, perf counter has been initialized
  340. ;
  341. ; Initialization hasn't occured yet, so just return zeroes.
  342. ;
  343. mov eax, 0
  344. mov edx, 0
  345. jmp Kqpc50
  346. Kqpc11: pushfd
  347. cli
  348. Kqpc20:
  349. lea eax, _HalpSystemHardwareLock
  350. ACQUIRE_SPINLOCK eax, Kqpc198
  351. ;
  352. ; Fetch the base value. Note that interrupts are off.
  353. ;
  354. ; NOTE:
  355. ; Need to watch for Px reading the 'CounterLow', P0 updates both
  356. ; then Px finishes reading 'CounterHigh' [getting the wrong value].
  357. ; After reading both, make sure that 'CounterLow' didn't change.
  358. ; If it did, read it again. This way, we won't have to use a spinlock.
  359. @@:
  360. mov ebx, HalpPerfCounterLow
  361. mov esi, HalpPerfCounterHigh ; [esi:ebx] = Performance counter
  362. cmp ebx, HalpPerfCounterLow ;
  363. jne short @b
  364. ;
  365. ; Fetch the current counter value from the hardware
  366. ;
  367. ;
  368. ; Background: Belize style systems have an 8254 per Processor.
  369. ;
  370. ; In short the original implementation kinda assumes that each
  371. ; timer on each processor will be in perfect sycnh with each other.
  372. ; This is a bad assumption, and the reason why we have attempted
  373. ; to use only the timer on P0.
  374. ;
  375. ; There is an existing window where the return value may not be accurate.
  376. ; The window will occur when multiple queries are made back to back
  377. ; in an MP environment, and there are a lot of IPIs going on. Intuitive,
  378. ; right. The problem is that this routine may return a value with the
  379. ; the hardware system timer on P0 that has already generated an interrupt
  380. ; and reset its rollover, but the software has yet to process the interrupt
  381. ; to update the performance counter value. When this occurs, the second
  382. ; querry will seem to have a lower value than the first.
  383. ;
  384. ; So, why don't I just fix it. Well the cause of the problem is the
  385. ; overhead associated with handling the interrupt, and the fact that
  386. ; the IPI has a higher IRQL. In addition, a busy system could be
  387. ; issueing multiple IPIs back to back, which could extend this window
  388. ; even further.
  389. ;
  390. ; I have managed to close the window most of the way for most normal
  391. ; conditions. It takes several minutes on a busy system, with
  392. ; multiple applications running with back to back queries to get
  393. ; an invalid value. It can happen though.
  394. ;
  395. ; A retry implementation has been instrumented on top off the
  396. ; Indexed IO implementation to finally close the window.
  397. ; It seems to work OK.
  398. ;
  399. ; In reality, I think the fix is sufficient. The performance counter
  400. ; is not designed propperly (via only software) to yield very accurate
  401. ; values on sub timer tic (10-15msec) ranges on multiprocessor systems.
  402. ;
  403. ; Problems with this design:
  404. ;
  405. ; On an idle system threads executing from P0 will always
  406. ; use less overhead than threads executing on P1.
  407. ; On a ProLiant 2000 with 2 P5-66s the difference in 2
  408. ; consecutive KeQueryPerformanceCounter calls from P0
  409. ; is about 14, while from P1 is about 22. Unfortunately
  410. ; on a busy system P0 performs about the same, but P1
  411. ; is much slower due to the overhead involved in performing
  412. ; an Indexed_IO. This means the busyier your system gets
  413. ; the less accurate your performance values will become.
  414. ;
  415. ; The solution:
  416. ;
  417. ; A system wide hardware timer needs to be used. This is about the
  418. ; only way to get accurate performance numbers from multiple
  419. ; processors without causing unnecessary software overhead.
  420. ;
  421. ; Supposedly there is a 48 bit counter that we may be able to use
  422. ; with SystemPro XL, and ProLiant systems, unfortunately it does
  423. ; not appear that any OS is currently using this feature, so
  424. ; its dependability may be suspect.
  425. ;
  426. ; JSL
  427. ;
  428. ;
  429. ; Essentially all we are doing is always using the timer value on P0.
  430. ; The indexed_io is a mechanism for one processor to access IOSPACE
  431. ; on another processor's IOSPACE. I suspect this will have a greater
  432. ; impact on performance than just reading the timer locally.
  433. ; By using the indexed_io you are gauranteed of going out on the bus.
  434. ;
  435. ; But, hey if the user understands anything about performance, they
  436. ; know that there will be some amount of overhead each time you make
  437. ; this KeQueryPerformanceCounter call.
  438. ;
  439. ;
  440. ; Increment the Retry counter now for convenience
  441. ;
  442. inc dword ptr RetryPerfCount+4
  443. ;
  444. ; This is Belize specific.
  445. ;
  446. cmp _SpType, SMP_SYSPRO2
  447. jne timer_p0
  448. ;
  449. ; Only use Indexed_IO on a nonP0 processor
  450. ;
  451. cmp byte ptr PCR[PcHal.PcrNumber], 0 ; is this the processor
  452. je timer_p0 ; which updates HalpPerfCounter?
  453. ;
  454. ; So read the timer of P0.
  455. ;
  456. push ebx
  457. mov bl, 0
  458. mov al, COMMAND_8254_LATCH_READ+COMMAND_8254_COUNTER0
  459. ; Latch PIT Ctr 0 command.
  460. INDEXED_IO_WRITE bl,TIMER1_CONTROL_PORT0,al
  461. IODelay
  462. INDEXED_IO_READ bl,TIMER1_DATA_PORT0 ; Read PIT Ctr 0, LSByte.
  463. movzx ecx, al
  464. INDEXED_IO_READ bl,TIMER1_DATA_PORT0 ; Read PIT Ctr 0, MSByte.
  465. IODelay
  466. mov ch,al ; (CX) = PIT Ctr 0 count.
  467. pop ebx
  468. lea eax, _HalpSystemHardwareLock
  469. RELEASE_SPINLOCK eax
  470. jmp short TimerValDone
  471. timer_p0:
  472. mov al, COMMAND_8254_LATCH_READ+COMMAND_8254_COUNTER0
  473. ;Latch PIT Ctr 0 command.
  474. out TIMER1_CONTROL_PORT0, al
  475. IODelay
  476. in al, TIMER1_DATA_PORT0 ;Read PIT Ctr 0, LSByte.
  477. IODelay
  478. movzx ecx,al ;Zero upper bytes of (ECX).
  479. in al, TIMER1_DATA_PORT0 ;Read PIT Ctr 0, MSByte.
  480. mov ch, al ;(CX) = PIT Ctr 0 count.
  481. lea eax, _HalpSystemHardwareLock
  482. RELEASE_SPINLOCK eax
  483. TimerValDone:
  484. mov al, PCR[PcHal.PcrNumber] ; get current processor #
  485. ;
  486. ; This is Belize specific.
  487. ;
  488. cmp _SpType, SMP_SYSPRO2
  489. je NoCPU0Update
  490. ;
  491. ; If not on P0 then make sure P0 isn't in the process of
  492. ; of updating its timer. Do this by checking the status
  493. ; of the PIC using indexed_io.
  494. ; Make sure that only one thread at time reads P0 PIC.
  495. ;
  496. cmp al, 0 ; Are we p0
  497. je NoCPU0Update
  498. ;
  499. ; Check IRQL at PO before going any further
  500. ;
  501. push edx
  502. mov edx, _HalpProcessorPCR[0] ; PCR of processor 0
  503. cmp byte ptr ds:[edx].PcIrql,CLOCK2_LEVEL
  504. pop edx
  505. jb short NoCPU0Update
  506. push ebx
  507. Kqpc11p:
  508. ;
  509. ; Check P0 PIC and confirm Timer Interrupt status.
  510. ; Perform Spin Lock before reading P0 PIC.
  511. ;
  512. pushfd
  513. cli
  514. lea ebx, _HalpSystemHardwareLock
  515. ACQUIRE_SPINLOCK ebx, Kqpc198p ; Spin if another thread is here
  516. INDEXED_IO_READ 0,PIC1_PORT1 ; read CPU 0 port 21 for masks
  517. RELEASE_SPINLOCK ebx
  518. popfd
  519. pop ebx
  520. test al, 1h ; check for IRQ 0 masked off
  521. mov al, PCR[PcHal.PcrNumber] ; get current processor #
  522. jz short NoCPU0Update
  523. ;
  524. ; Try ReadAgain if below retry count.
  525. ;
  526. cmp RetryPerfCount+4, MAX_PERF_RETRY
  527. ja short NoCPU0Update
  528. ReadAgain:
  529. ;
  530. ; This readagain is only executed when P0 is
  531. ; at CLOCK2_LEVEL or greater.
  532. ; AND when Timer IRQ is active (ie interrupt in progress).
  533. ; This is done to close the window of an interrupt
  534. ; occuring and the irql hasn't been raised yet.
  535. ;
  536. popfd
  537. jmp Kqpc11 ; go back and read again
  538. NoCPU0Update:
  539. ;
  540. ; Now enable interrupts such that if timer interrupt is pending, it can
  541. ; be serviced and update the PerformanceCounter. Note that there could
  542. ; be a long time between the sti and cli because ANY interrupt could come
  543. ; in in between.
  544. ;
  545. popfd ; don't re-enable interrupts if
  546. nop ; the caller had them off!
  547. jmp $+2 ; allow interrupt in case counter
  548. ; has wrapped
  549. pushfd
  550. cli
  551. ;
  552. ; In Belize mode we do not care about this since we use the P0 clock.
  553. ;
  554. cmp _SpType, SMP_SYSPRO2
  555. je short Kqpc35
  556. ;
  557. ; If we moved processors while interrupts were enabled, start over
  558. ;
  559. cmp al, PCR[PcHal.PcrNumber]
  560. jne Kqpc20
  561. Kqpc35:
  562. ;
  563. ; Fetch the base value again.
  564. ;
  565. @@: mov eax, HalpPerfCounterLow
  566. mov edx, HalpPerfCounterHigh ; [edx:eax] = new counter value
  567. cmp eax, HalpPerfCounterLow ; did it move?
  568. jne short @b ; re-read
  569. ;
  570. ; Compare the two reads of Performance counter. If they are different,
  571. ; start over
  572. ;
  573. cmp eax, ebx
  574. jne Kqpc20
  575. cmp edx, esi
  576. jne Kqpc20
  577. neg ecx ; PIT counts down from 0h
  578. add ecx, HalpRollOverCount
  579. ;
  580. ; In Belize mode we do not care about this since we use the P0 clock.
  581. ;
  582. cmp _SpType, SMP_SYSPRO2
  583. je short Kqpc37
  584. add ecx, PCR[PcHal.PcrPerfSkew]
  585. Kqpc37:
  586. popfd ; restore interrupt flag
  587. xchg ecx, eax
  588. mov ebx, edx
  589. cdq
  590. add eax, ecx
  591. adc edx, ebx ; [edx:eax] = Final result
  592. ;
  593. ; We only want to execute this code In Belize mode.
  594. ;
  595. cmp _SpType, SMP_SYSPRO2
  596. jne Kqpc50
  597. ;
  598. ; Ok compare this result with the last result.
  599. ; We will force the value to be greater than the last value,
  600. ; after we have used up all of our retry counts.
  601. ;
  602. ; This should slam shut that annoying Window that causes
  603. ; applications to recieve a 2nd query less then the first.
  604. ;
  605. ; This is not an most elegant solution, but fortunately
  606. ; this situation is hit only on a rare occasions.
  607. ;
  608. ; Yeah, I know that this value can roll over
  609. ; if someone runs some perf tests, and comes back in a
  610. ; few weeks and wants to run some more. In this situation
  611. ; the the very first call to this function will yield an
  612. ; invalid value. This is the price of the fix.
  613. ;
  614. ;
  615. ; Protect the global data with a spinlock
  616. ;
  617. push ebx
  618. Kqpc42: pushfd
  619. cli
  620. lea ebx, HalpForceDataLock
  621. ACQUIRE_SPINLOCK ebx, Kqpc199 ; Spin if another thread is here
  622. ;
  623. ; Compare this value to the last value, if less then
  624. ; fix it up.
  625. ;
  626. cmp edx, HalpLastQueryHighValue
  627. ja short Kqpc44
  628. cmp eax, HalpLastQueryLowValue
  629. ja short Kqpc44
  630. ;
  631. ; Release the spinlock.
  632. ;
  633. RELEASE_SPINLOCK ebx
  634. popfd
  635. pop ebx
  636. ;
  637. ; Try Again if below count.
  638. ;
  639. cmp RetryPerfCount, MAX_PERF_RETRY
  640. jbe Kqpc11 ; go back and read again
  641. ;
  642. ; Exhausted retry count so Fix up the values and leave.
  643. ;
  644. mov eax, HalpLastQueryLowValue
  645. inc eax
  646. mov edx, HalpLastQueryHighValue
  647. jmp short Kqpc50
  648. Kqpc44:
  649. ;
  650. ; Save off the perf values for next time.
  651. ;
  652. mov HalpLastQueryLowValue, eax
  653. mov HalpLastQueryHighValue, edx
  654. ;
  655. ; Release the spinlock.
  656. ;
  657. RELEASE_SPINLOCK ebx
  658. popfd
  659. pop ebx
  660. ;
  661. ; Return the counter
  662. ;
  663. Kqpc50:
  664. ; return value is in edx:eax
  665. ;
  666. ; Return the freq. if caller wants it.
  667. ;
  668. or dword ptr KqpcFrequency, 0 ; is it a NULL variable?
  669. jz short Kqpc99 ; if z, yes, go exit
  670. mov ecx, KqpcFrequency ; (ecx)-> Frequency variable
  671. mov DWORD PTR [ecx], PERFORMANCE_FREQUENCY ; Set frequency
  672. mov DWORD PTR [ecx+4], 0
  673. Kqpc99:
  674. pop edi ; remove locals
  675. pop edi ; restore regs
  676. pop esi
  677. pop ebx
  678. stdRET _KeQueryPerformanceCounter
  679. Kqpc198: popfd
  680. SPIN_ON_SPINLOCK eax,<Kqpc11>
  681. ;
  682. ; This is just where we are spinning while we are waiting to read the PIC
  683. ;
  684. Kqpc198p: popfd
  685. SPIN_ON_SPINLOCK ebx,<Kqpc11p>
  686. ;
  687. ; This is just where we are spinning while waiting global last perf data
  688. ;
  689. Kqpc199: popfd
  690. SPIN_ON_SPINLOCK ebx,<Kqpc42>
  691. stdENDP _KeQueryPerformanceCounter
  692. ; endmod
  693. page ,132
  694. subttl "System Clock Interrupt"
  695. ;++
  696. ;
  697. ; Routine Description:
  698. ;
  699. ;
  700. ; This routine is entered as the result of an interrupt generated by CLOCK2.
  701. ; Its function is to dismiss the interrupt, raise system Irql to
  702. ; CLOCK2_LEVEL, update performance counter and transfer control to the
  703. ; standard system routine to update the system time and the execution
  704. ; time of the current thread
  705. ; and process.
  706. ;
  707. ;
  708. ; Arguments:
  709. ;
  710. ; None
  711. ; Interrupt is disabled
  712. ;
  713. ; Return Value:
  714. ;
  715. ; Does not return, jumps directly to KeUpdateSystemTime, which returns
  716. ;
  717. ; Sets Irql = CLOCK2_LEVEL and dismisses the interrupt
  718. ;
  719. ;--
  720. ENTER_DR_ASSIST Hci_a, Hci_t
  721. cPublicProc _HalpClockInterrupt ,0
  722. ;
  723. ; Save machine state in trap frame
  724. ;
  725. ENTER_INTERRUPT Hci_a, Hci_t
  726. ;
  727. ; (esp) - base of trap frame
  728. ;
  729. ;
  730. ; dismiss interrupt and raise Irql
  731. ;
  732. Hci10:
  733. push CLOCK_VECTOR
  734. sub esp, 4 ; allocate space to save OldIrql
  735. stdCall _HalBeginSystemInterrupt, <CLOCK2_LEVEL,CLOCK_VECTOR,esp>
  736. or al,al ; check for spurious interrupt
  737. jz Hci100
  738. ;
  739. ; Update performance counter
  740. ;
  741. mov eax, HalpRollOverCount
  742. xor ebx, ebx
  743. add HalpPerfCounterLow, eax ; update performace counter
  744. adc HalpPerfCounterHigh, ebx
  745. cmp _HalpClockWork, ebx
  746. jz short Hci20
  747. cmp _HalpClockMcaQueueDpc, bl
  748. jz short Hci20
  749. mov _HalpClockMcaQueueDpc, bl
  750. ;
  751. ; Queue MCA Dpc
  752. ;
  753. stdCall _HalpMcaQueueDpc
  754. Hci20:
  755. ;
  756. ; (esp) = OldIrql
  757. ; (esp+4) = Vector
  758. ; (esp+8) = base of trap frame
  759. ; (ebp) = address of trap frame
  760. ; (eax) = time increment
  761. ;
  762. mov eax, TIME_INCREMENT_10MS
  763. mov ebx, _HalpIpiClock ; Emulate clock ticks to any processors?
  764. or ebx, ebx
  765. jz _KeUpdateSystemTime@0
  766. ;
  767. ; On the SystemPro we know the processor which needs an emulated clock tick.
  768. ; Just set that processors bit and IPI him
  769. ;
  770. @@:
  771. movzx ecx, _HalpFindFirstSetRight[ebx] ; lookup first processor
  772. btr ebx, ecx
  773. mov ecx, _HalpProcessorPCR[ecx*4] ; PCR of processor
  774. mov [ecx].PcHal.PcrIpiClockTick, 1 ; Set internal IPI event
  775. or ebx, ebx ; any other processors?
  776. jnz short @b ; yes, loop
  777. stdCall _HalRequestIpi, <_HalpIpiClock> ; IPI the processor(s)
  778. mov eax, TIME_INCREMENT_10MS
  779. jmp _KeUpdateSystemTime@0
  780. Hci100:
  781. add esp, 8
  782. SPURIOUS_INTERRUPT_EXIT
  783. stdENDP _HalpClockInterrupt
  784. page ,132
  785. subttl "NonPrimaryClockTick"
  786. ;++
  787. ;
  788. ; VOID
  789. ; HalpNonPrimaryClockInterrupt (
  790. ; );
  791. ;
  792. ; Routine Description:
  793. ; ISR for clock interrupts for every processor except one.
  794. ;
  795. ; Arguments:
  796. ;
  797. ; None.
  798. ; Interrupt is dismissed
  799. ;
  800. ; Return Value:
  801. ;
  802. ; None.
  803. ;
  804. ;--
  805. ENTER_DR_ASSIST Hni_a, Hni_t
  806. cPublicProc _HalpNonPrimaryClockInterrupt ,0
  807. ENTER_INTERRUPT Hni_a, Hni_t
  808. ; Dismiss interrupt and raise irql
  809. push CLOCK_VECTOR
  810. sub esp, 4 ; allocate space to save OldIrql
  811. stdCall _HalBeginSystemInterrupt, <CLOCK2_LEVEL,CLOCK_VECTOR,esp>
  812. or al,al ; check for spurious interrupt
  813. jz Hni100
  814. ; TOS const PreviousIrql
  815. stdCall _KeUpdateRunTime,<dword ptr [esp]>
  816. INTERRUPT_EXIT ; will do an iret
  817. Hni100:
  818. add esp, 8
  819. SPURIOUS_INTERRUPT_EXIT
  820. stdENDP _HalpNonPrimaryClockInterrupt
  821. page ,132
  822. subttl "Emulate NonPrimaryClockTick"
  823. ;++
  824. ;
  825. ; VOID
  826. ; HalpSWNonPrimaryClockTick (
  827. ; );
  828. ;
  829. ; Routine Description:
  830. ; On the SystemPro the second processor does not get it's own clock
  831. ; ticks. The HAL emulates them by sending an IPI which sets an overloaded
  832. ; software interrupt level of SWCLOCK_LEVEL. When the processor attempts
  833. ; to lower it's irql level below SWCLOCK_LEVEL the soft interrupt code
  834. ; lands us here as if an interrupt occured.
  835. ;
  836. ; Arguments:
  837. ;
  838. ; None.
  839. ; Interrupt is dismissed
  840. ;
  841. ; Return Value:
  842. ;
  843. ; None.
  844. ;
  845. ENTER_DR_ASSIST Hsi_a, Hsi_t
  846. public _HalpSWNonPrimaryClockTick
  847. _HalpSWNonPrimaryClockTick proc
  848. ;
  849. ; Create IRET frame on stack
  850. ;
  851. pop eax
  852. pushfd
  853. push cs
  854. push eax
  855. ;
  856. ; Save machine state in trap frame
  857. ;
  858. ENTER_INTERRUPT Hsi_a, Hsi_t
  859. public _HalpSWNonPrimaryClockTick2ndEntry
  860. _HalpSWNonPrimaryClockTick2ndEntry:
  861. ; Save previous IRQL and set new priority level
  862. push fs:PcIrql ; save previous IRQL
  863. mov byte ptr fs:PcIrql, SWCLOCK_LEVEL ; set new irql
  864. btr dword ptr fs:PcIRR, SWCLOCK_LEVEL ; clear the pending bit in IRR
  865. sti
  866. ; TOS const PreviousIrql
  867. stdCall _KeUpdateRunTime,<dword ptr [esp]>
  868. SOFT_INTERRUPT_EXIT ; will do an iret
  869. _HalpSWNonPrimaryClockTick endp
  870. ;++
  871. ;
  872. ; ULONG
  873. ; HalSetTimeIncrement (
  874. ; IN ULONG DesiredIncrement
  875. ; )
  876. ;
  877. ; /*++
  878. ;
  879. ; Routine Description:
  880. ;
  881. ; This routine initialize system time clock to generate an
  882. ; interrupt at every DesiredIncrement interval.
  883. ;
  884. ; Arguments:
  885. ;
  886. ; DesiredIncrement - desired interval between every timer tick (in
  887. ; 100ns unit.)
  888. ;
  889. ; Return Value:
  890. ;
  891. ; The *REAL* time increment set.
  892. ;--
  893. cPublicProc _HalSetTimeIncrement,1
  894. mov eax, TIME_INCREMENT_10MS ; yes, use 10ms clock
  895. stdRET _HalSetTimeIncrement
  896. stdENDP _HalSetTimeIncrement
  897. _TEXT ends
  898. end
  899.