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.

955 lines
26 KiB

  1. title "Interval Clock Interrupt"
  2. ;++
  3. ;
  4. ; Copyright (c) 1989 Microsoft Corporation
  5. ;
  6. ; Module Name:
  7. ;
  8. ; ixclock.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.
  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. ; Landy Wang (corollary!landy) 04-Dec-92
  51. ; Move much code into separate modules for easy inclusion by various
  52. ; HAL builds.
  53. ;
  54. ;--
  55. .386p
  56. .xlist
  57. include hal386.inc
  58. include callconv.inc ; calling convention macros
  59. include i386\ix8259.inc
  60. include i386\kimacro.inc
  61. include mac386.inc
  62. include i386\ixcmos.inc
  63. .list
  64. EXTRNP _KeUpdateSystemTime,0
  65. EXTRNP Kei386EoiHelper,0,IMPORT
  66. EXTRNP _KeSetTimeIncrement,2,IMPORT
  67. EXTRNP _HalEndSystemInterrupt,2
  68. EXTRNP _HalBeginSystemInterrupt,3
  69. EXTRNP _HalpReleaseCmosSpinLock ,0
  70. EXTRNP _HalpMcaQueueDpc, 0
  71. extrn _KdEnteredDebugger:DWORD
  72. extrn _HalpTimerWatchdogEnabled:DWORD
  73. extrn _HalpTimerWatchdogStorage:DWORD
  74. extrn _HalpTimerWatchdogCurFrame:DWORD
  75. extrn _HalpTimerWatchdogLastFrame:DWORD
  76. extrn _HalpTimerWatchdogStorageOverflow:DWORD
  77. ;
  78. ; Constants used to initialize timer 0
  79. ;
  80. TIMER1_DATA_PORT0 EQU 40H ; Timer1, channel 0 data port
  81. TIMER1_CONTROL_PORT0 EQU 43H ; Timer1, channel 0 control port
  82. TIMER2_DATA_PORT0 EQU 48H ; Timer1, channel 0 data port
  83. TIMER2_CONTROL_PORT0 EQU 4BH ; Timer1, channel 0 control port
  84. TIMER1_IRQ EQU 0 ; Irq 0 for timer1 interrupt
  85. COMMAND_8254_COUNTER0 EQU 00H ; Select count 0
  86. COMMAND_8254_RW_16BIT EQU 30H ; Read/Write LSB firt then MSB
  87. COMMAND_8254_MODE2 EQU 4 ; Use mode 2
  88. COMMAND_8254_BCD EQU 0 ; Binary count down
  89. COMMAND_8254_LATCH_READ EQU 0 ; Latch read command
  90. PERFORMANCE_FREQUENCY EQU 1193182
  91. COUNTER_TICKS_AVG_SHIFT EQU 4
  92. COUNTER_TICKS_FOR_AVG EQU 16
  93. PAGE_SIZE EQU 1000H
  94. FRAME_COPY_SIZE EQU 64
  95. ;
  96. ; ==== Values used for System Clock ====
  97. ;
  98. _DATA SEGMENT DWORD PUBLIC 'DATA'
  99. ;
  100. ; The following array stores the per microsecond loop count for each
  101. ; central processor.
  102. ;
  103. ;
  104. ; 8254 performance counter.
  105. ;
  106. public HalpPerfCounterLow, HalpPerfCounterHigh
  107. public HalpLastPerfCounterLow, HalpLastPerfCounterHigh
  108. HalpPerfCounterLow dd 0
  109. HalpPerfCounterHigh dd 0
  110. HalpLastPerfCounterLow dd 0
  111. HalpLastPerfCounterHigh dd 0
  112. public HalpCurrentRollOver, HalpCurrentTimeIncrement
  113. HalpCurrentRollOver dd 0
  114. HalpCurrentTimeIncrement dd 0
  115. public _HalpClockWork, _HalpClockSetMSRate, _HalpClockMcaQueueDpc
  116. _HalpClockWork label dword
  117. _HalpClockSetMSRate db 0
  118. _HalpClockMcaQueueDpc db 0
  119. _bReserved1 db 0
  120. _bReserved2 db 0
  121. ;
  122. ; timer latency watchdog variables
  123. ;
  124. public _HalpWatchdogAvgCounter, _HalpWatchdogCountLow, _HalpWatchdogCountHigh
  125. public _HalpWatchdogTscLow, _HalpWatchdogTscHigh
  126. _HalpWatchdogAvgCounter dd 0
  127. _HalpWatchdogCountLow dd 0
  128. _HalpWatchdogCountHigh dd 0
  129. _HalpWatchdogTscLow dd 0
  130. _HalpWatchdogTscHigh dd 0
  131. _DATA ends
  132. _TEXT SEGMENT DWORD PUBLIC 'DATA'
  133. ;
  134. ; Convert the interval to rollover count for 8254 Timer1 device.
  135. ; Timer1 counts down a 16 bit value at a rate of 1.193181667M counts-per-sec.
  136. ; (The main crystal freq is 14.31818, and this is a divide by 12)
  137. ;
  138. ; The best fit value closest to 10ms is 10.0144012689ms:
  139. ; ROLLOVER_COUNT 11949
  140. ; TIME_INCREMENT 100144
  141. ; Calculated error is -.0109472 s/day
  142. ;
  143. ;
  144. ; The following table contains 8254 values timer values to use at
  145. ; any given ms setting from 1ms - 15ms. All values work out to the
  146. ; same error per day (-.0109472 s/day).
  147. ;
  148. public HalpRollOverTable
  149. ; RollOver Time
  150. ; Count Increment MS
  151. HalpRollOverTable dd 1197, 10032 ; 1 ms
  152. dd 2394, 20064 ; 2 ms
  153. dd 3591, 30096 ; 3 ms
  154. dd 4767, 39952 ; 4 ms
  155. dd 5964, 49984 ; 5 ms
  156. dd 7161, 60016 ; 6 ms
  157. dd 8358, 70048 ; 7 ms
  158. dd 9555, 80080 ; 8 ms
  159. dd 10731, 89936 ; 9 ms
  160. dd 11949, 100144 ; 10 ms
  161. dd 13125, 110000 ; 11 ms
  162. dd 14322, 120032 ; 12 ms
  163. dd 15519, 130064 ; 13 ms
  164. dd 16695, 139920 ; 14 ms
  165. dd 17892, 149952 ; 15 ms
  166. TimeIncr equ 4
  167. RollOver equ 0
  168. _TEXT ends
  169. _DATA SEGMENT DWORD PUBLIC 'DATA'
  170. public HalpLargestClockMS, HalpNextMSRate, HalpPendingMSRate
  171. HalpLargestClockMS dd 15 ; Table goes to 15MS
  172. HalpNextMSRate dd 0
  173. HalpPendingMSRate dd 0
  174. _DATA ends
  175. PAGELK SEGMENT DWORD PUBLIC 'CODE'
  176. ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
  177. page ,132
  178. subttl "Initialize Clock"
  179. ;++
  180. ;
  181. ; VOID
  182. ; HalpInitializeClock (
  183. ; )
  184. ;
  185. ; Routine Description:
  186. ;
  187. ; This routine initialize system time clock using 8254 timer1 counter 0
  188. ; to generate an interrupt at every 15ms interval at 8259 irq0.
  189. ;
  190. ; See the definitions of TIME_INCREMENT and ROLLOVER_COUNT if clock rate
  191. ; needs to be changed.
  192. ;
  193. ; Arguments:
  194. ;
  195. ; None
  196. ;
  197. ; Return Value:
  198. ;
  199. ; None.
  200. ;
  201. ;--
  202. cPublicProc _HalpInitializeClock ,0
  203. mov eax, PCR[PcPrcb]
  204. cmp byte ptr [eax].PbCpuType, 4 ; 486 or better?
  205. jc short @f ; no, skip
  206. mov HalpLargestClockMS, 10 ; Limit 486's to 10MS
  207. @@:
  208. mov eax, HalpLargestClockMS
  209. mov ecx, HalpRollOverTable.TimeIncr
  210. mov edx, HalpRollOverTable[eax*8-8].TimeIncr
  211. mov eax, HalpRollOverTable[eax*8-8].RollOver
  212. mov HalpCurrentTimeIncrement, edx
  213. ;
  214. ; (ecx) = Min time_incr
  215. ; (edx) = Max time_incr
  216. ; (eax) = max roll over count
  217. ;
  218. push eax
  219. stdCall _KeSetTimeIncrement, <edx, ecx>
  220. pop ecx
  221. ;
  222. ; timer latency watchdog initialization
  223. ;
  224. cmp _HalpTimerWatchdogEnabled, 0
  225. jz short @f
  226. .586p
  227. rdtsc
  228. .386p
  229. mov _HalpWatchdogAvgCounter, COUNTER_TICKS_FOR_AVG
  230. mov _HalpWatchdogTscLow, eax
  231. mov _HalpWatchdogTscHigh, edx
  232. xor eax, eax
  233. mov _HalpWatchdogCountLow, eax
  234. mov _HalpWatchdogCountHigh, eax
  235. @@:
  236. pushfd ; save caller's eflag
  237. cli ; make sure interrupts are disabled
  238. ;
  239. ; Set clock rate
  240. ; (ecx) = RollOverCount
  241. ;
  242. mov al,COMMAND_8254_COUNTER0+COMMAND_8254_RW_16BIT+COMMAND_8254_MODE2
  243. out TIMER1_CONTROL_PORT0, al ;program count mode of timer 0
  244. IoDelay
  245. mov al, cl
  246. out TIMER1_DATA_PORT0, al ; program timer 0 LSB count
  247. IoDelay
  248. mov al,ch
  249. out TIMER1_DATA_PORT0, al ; program timer 0 MSB count
  250. popfd ; restore caller's eflag
  251. mov HalpCurrentRollOver, ecx ; Set RollOverCount & initialized
  252. stdRET _HalpInitializeClock
  253. stdENDP _HalpInitializeClock
  254. PAGELK ends
  255. _TEXT$03 SEGMENT DWORD PUBLIC 'CODE'
  256. page ,132
  257. subttl "Query Performance Counter"
  258. ;++
  259. ;
  260. ; LARGE_INTEGER
  261. ; KeQueryPerformanceCounter (
  262. ; OUT PLARGE_INTEGER PerformanceFrequency OPTIONAL
  263. ; )
  264. ;
  265. ; Routine Description:
  266. ;
  267. ; This routine returns current 64-bit performance counter and,
  268. ; optionally, the Performance Frequency.
  269. ;
  270. ; Note this routine can NOT be called at Profiling interrupt
  271. ; service routine. Because this routine depends on IRR0 to determine
  272. ; the actual count.
  273. ;
  274. ; Also note that the performace counter returned by this routine
  275. ; is not necessary the value when this routine is just entered.
  276. ; The value returned is actually the counter value at any point
  277. ; between the routine is entered and is exited.
  278. ;
  279. ; Arguments:
  280. ;
  281. ; PerformanceFrequency [TOS+4] - optionally, supplies the address
  282. ; of a variable to receive the performance counter frequency.
  283. ;
  284. ; Return Value:
  285. ;
  286. ; Current value of the performance counter will be returned.
  287. ;
  288. ;--
  289. ;
  290. ; Parameter definitions
  291. ;
  292. KqpcFrequency EQU [esp+12] ; User supplied Performance Frequence
  293. cPublicProc _KeQueryPerformanceCounter ,1
  294. ;
  295. ; First check to see if the performance counter has been initialized yet.
  296. ; Since the kernel debugger calls KeQueryPerformanceCounter to support the
  297. ; !timer command, we need to return something reasonable before 8254
  298. ; initialization has occured. Reading garbage off the 8254 is not reasonable.
  299. ;
  300. cmp HalpCurrentRollOver, 0
  301. je Kqpc50
  302. push ebx
  303. push esi
  304. Kqpc01: pushfd
  305. cli
  306. Kqpc20:
  307. ;
  308. ; Fetch the base value. Note that interrupts are off.
  309. ;
  310. mov ebx, HalpPerfCounterLow
  311. mov esi, HalpPerfCounterHigh ; [esi:ebx] = Performance counter
  312. ;
  313. ; Fetch the current counter value from the hardware
  314. ;
  315. mov al, COMMAND_8254_LATCH_READ+COMMAND_8254_COUNTER0
  316. ;Latch PIT Ctr 0 command.
  317. out TIMER1_CONTROL_PORT0, al
  318. IODelay
  319. in al, TIMER1_DATA_PORT0 ;Read PIT Ctr 0, LSByte.
  320. IODelay
  321. movzx ecx,al ;Zero upper bytes of (ECX).
  322. in al, TIMER1_DATA_PORT0 ;Read PIT Ctr 0, MSByte.
  323. mov ch, al ;(CX) = PIT Ctr 0 count.
  324. ;
  325. ; Now enable interrupts such that if timer interrupt is pending, it can
  326. ; be serviced and update the PerformanceCounter. Note that there could
  327. ; be a long time between the sti and cli because ANY interrupt could come
  328. ; in in between.
  329. ;
  330. popfd ; don't re-enable interrupts if
  331. nop ; the caller had them off!
  332. ; (kernel debugger calls this function
  333. ; with interrupts disabled)
  334. jmp $+2 ; allow interrupt in case counter
  335. ; has wrapped
  336. pushfd
  337. cli
  338. ;
  339. ; Fetch the base value again.
  340. ;
  341. ; Note: it's possible that the counter wrapped before we read the value
  342. ; and that the timer tick interrupt did not occur during while interrupts
  343. ; where enabled. (ie, there's a delay between when the device raises the
  344. ; interrupt and when the processor see it).
  345. ;
  346. ;
  347. ; note *2 -
  348. mov eax, HalpPerfCounterLow
  349. mov edx, HalpPerfCounterHigh ; [edx:eax] = new counter value
  350. ;
  351. ; Compare the two reads of Performance counter. If they are different,
  352. ; start over
  353. ;
  354. cmp eax, ebx
  355. jne short Kqpc20
  356. cmp edx, esi
  357. jne short Kqpc20
  358. neg ecx ; PIT counts down from 0h
  359. add ecx, HalpCurrentRollOver
  360. jnc short Kqpc60
  361. Kqpc30:
  362. add eax, ecx
  363. adc edx, 0 ; [edx:eax] = Final result
  364. cmp edx, HalpLastPerfCounterHigh
  365. jc short Kqpc70 ; jmp if edx < lastperfcounterhigh
  366. jne short Kqpc35 ; jmp if edx > lastperfcounterhigh
  367. cmp eax, HalpLastPerfCounterLow
  368. jc short Kqpc70 ; jmp if eax < lastperfcounterlow
  369. Kqpc35:
  370. mov HalpLastPerfCounterLow, eax
  371. mov HalpLastPerfCounterHigh, edx
  372. popfd ; restore interrupt flag
  373. ;
  374. ; Return the freq. if caller wants it.
  375. ;
  376. cmp dword ptr KqpcFrequency, 0 ; is it a NULL variable?
  377. jz short Kqpc40 ; if z, yes, go exit
  378. mov ecx, KqpcFrequency ; (ecx)-> Frequency variable
  379. mov DWORD PTR [ecx], PERFORMANCE_FREQUENCY ; Set frequency
  380. mov DWORD PTR [ecx+4], 0
  381. Kqpc40:
  382. pop esi ; restore esi and ebx
  383. pop ebx
  384. stdRET _KeQueryPerformanceCounter
  385. Kqpc50:
  386. ; Initialization hasn't occured yet, so just return zeroes.
  387. mov eax, 0
  388. mov edx, 0
  389. stdRET _KeQueryPerformanceCounter
  390. Kqpc60:
  391. ;
  392. ; The current count is larger then the HalpCurrentRollOver. The only way
  393. ; that could happen is if there is an interrupt in route to the processor
  394. ; but it was not processed while interrupts were enabled.
  395. ;
  396. mov esi, [esp] ; (esi) = flags
  397. mov ecx, HalpCurrentRollOver ; (ecx) = max possible value
  398. popfd ; restore flags
  399. test esi, EFLAGS_INTERRUPT_MASK
  400. jnz Kqpc01 ; ints are enabled, problem should go away
  401. pushfd ; fix stack
  402. jmp short Kqpc30 ; ints are disabled, use max count (ecx)
  403. Kqpc70:
  404. ;
  405. ; The current count is smaller then the last returned count. The only way
  406. ; this should occur is if there is an interrupt in route to the processor
  407. ; which was not been processed.
  408. ;
  409. mov ebx, HalpLastPerfCounterLow
  410. mov esi, HalpLastPerfCounterHigh
  411. mov ecx, ebx
  412. or ecx, esi ; is last returned value 0?
  413. jz short Kqpc35 ; Yes, then just return what we have
  414. ; sanity check - make sure count is not off by bogus amount
  415. sub ebx, eax
  416. sbb esi, edx
  417. jnz short Kqpc75 ; off by bogus amount
  418. cmp ebx, HalpCurrentRollOver
  419. jg short Kqpc75 ; off by bogus amount
  420. sub eax, ebx
  421. sbb edx, esi ; (edx:eax) = last returned count
  422. mov ecx, [esp] ; (ecx) = flags
  423. popfd
  424. test ecx, EFLAGS_INTERRUPT_MASK
  425. jnz Kqpc01 ; ints enabled, problem should go away
  426. pushfd ; fix stack
  427. jmp Kqpc35 ; ints disabled, just return last count
  428. Kqpc75:
  429. popfd
  430. xor eax, eax ; reset bogus values
  431. mov HalpLastPerfCounterLow, eax
  432. mov HalpLastPerfCounterHigh, eax
  433. jmp Kqpc01 ; go try again
  434. stdENDP _KeQueryPerformanceCounter
  435. ;++
  436. ;
  437. ; VOID
  438. ; HalCalibratePerformanceCounter (
  439. ; IN LONG volatile *Number,
  440. ; IN ULONGLONG NewCount
  441. ; )
  442. ;
  443. ; /*++
  444. ;
  445. ; Routine Description:
  446. ;
  447. ; This routine resets the performance counter value for the current
  448. ; processor to zero. The reset is done such that the resulting value
  449. ; is closely synchronized with other processors in the configuration.
  450. ;
  451. ; Arguments:
  452. ;
  453. ; Number - Supplies a pointer to count of the number of processors in
  454. ; the configuration.
  455. ;
  456. ; NewCount - Supplies the value to synchronize the counter too
  457. ;
  458. ; Note: this hal does not currently set the counter
  459. ;
  460. ; Return Value:
  461. ;
  462. ; None.
  463. ;--
  464. cPublicProc _HalCalibratePerformanceCounter,3
  465. mov eax, [esp+4] ; ponter to Number
  466. pushfd ; save previous interrupt state
  467. cli ; disable interrupts (go to high_level)
  468. lock dec dword ptr [eax] ; count down
  469. @@: YIELD
  470. cmp dword ptr [eax], 0 ; wait for all processors to signal
  471. jnz short @b
  472. ;
  473. ; Nothing to calibrate on a UP machine...
  474. ;
  475. popfd ; restore interrupt flag
  476. stdRET _HalCalibratePerformanceCounter
  477. stdENDP _HalCalibratePerformanceCounter
  478. page ,132
  479. subttl "System Clock Interrupt"
  480. ;++
  481. ;
  482. ; Routine Description:
  483. ;
  484. ; This routine is entered as the result of an interrupt generated by CLOCK.
  485. ; Its function is to dismiss the interrupt, raise system Irql to
  486. ; CLOCK2_LEVEL, update performance counter and transfer control to the
  487. ; standard system routine to update the system time and the execution
  488. ; time of the current thread
  489. ; and process.
  490. ;
  491. ; Arguments:
  492. ;
  493. ; None
  494. ; Interrupt is disabled
  495. ;
  496. ; Return Value:
  497. ;
  498. ; Does not return, jumps directly to KeUpdateSystemTime, which returns
  499. ;
  500. ; Sets Irql = CLOCK2_LEVEL and dismisses the interrupt
  501. ;
  502. ;--
  503. ENTER_DR_ASSIST Hci_a, Hci_t
  504. cPublicProc _HalpClockInterrupt ,0
  505. ;
  506. ; Save machine state in trap frame
  507. ;
  508. ENTER_INTERRUPT Hci_a, Hci_t
  509. ;
  510. ; (esp) - base of trap frame
  511. ;
  512. ;
  513. ; Dismiss interrupt and raise irq level to clock2 level
  514. ;
  515. Hci10:
  516. push CLOCK_VECTOR
  517. sub esp, 4 ; allocate space to save OldIrql
  518. stdCall _HalBeginSystemInterrupt, <CLOCK2_LEVEL, CLOCK_VECTOR, esp>
  519. or al,al ; check for spurious interrupt
  520. jz Hci100
  521. ;
  522. ; Update performance counter
  523. ;
  524. xor ebx, ebx
  525. mov eax, HalpCurrentRollOver
  526. add HalpPerfCounterLow, eax ; update performace counter
  527. adc HalpPerfCounterHigh, ebx
  528. ;
  529. ; Timer latency watchdog
  530. ;
  531. cmp _HalpTimerWatchdogEnabled, 0
  532. jz Hci14
  533. .586p
  534. rdtsc
  535. .386p
  536. ;
  537. ; Compare difference to watchdog count, while storing a copy of the
  538. ; current counter.
  539. ;
  540. push eax
  541. push edx
  542. sub eax, _HalpWatchdogTscLow
  543. sbb edx, _HalpWatchdogTscHigh
  544. pop _HalpWatchdogTscHigh
  545. pop _HalpWatchdogTscLow
  546. js Hci115 ; Was this a bogus counter?
  547. ; (e.g, negative delta)
  548. push eax
  549. mov ecx, dword ptr _KdEnteredDebugger
  550. xor eax, eax
  551. xchg eax, [ecx]
  552. or al, al
  553. pop eax
  554. jnz Hci14
  555. cmp HalpPendingMSRate, ebx ; Was a new rate set during last
  556. jnz Hci14 ; tick? Yes, skip this compare
  557. ;
  558. ; If we need to compute the average of the time-stamp counter for
  559. ; the current period, add the delta to the counter.
  560. ;
  561. cmp _HalpWatchdogAvgCounter, ebx
  562. jnz Hci12
  563. cmp edx, _HalpWatchdogCountHigh
  564. ja short Hci11
  565. jb Hci14
  566. cmp eax, _HalpWatchdogCountLow
  567. jbe Hci14
  568. Hci11:
  569. cmp dword ptr [_HalpTimerWatchdogStorageOverflow], 0
  570. jne short Hci115
  571. ;
  572. ; copy FRAME_COPY_SIZE dwords from the stack, or to next page boundary,
  573. ; whichever is less
  574. ;
  575. push esi
  576. push edi
  577. lea esi, [esp+8]
  578. lea ecx, [esi + PAGE_SIZE - 1]
  579. and ecx, NOT(PAGE_SIZE - 1)
  580. sub ecx, esi
  581. shr ecx, 2
  582. cmp ecx, FRAME_COPY_SIZE
  583. jbe short Hci111
  584. mov ecx, FRAME_COPY_SIZE
  585. Hci111:
  586. mov edi, dword ptr _HalpTimerWatchdogCurFrame
  587. rep movsd
  588. add _HalpTimerWatchdogCurFrame, (FRAME_COPY_SIZE*4)
  589. ;
  590. ; If we didn't copy an entire FRAME_COPY_SIZE dwords, zero fill.
  591. ;
  592. mov ecx, dword ptr _HalpTimerWatchdogCurFrame
  593. sub ecx, edi
  594. shr ecx, 2
  595. xor eax, eax
  596. rep stosd
  597. cmp edi, dword ptr _HalpTimerWatchdogLastFrame
  598. jbe short Hci112
  599. mov dword ptr [_HalpTimerWatchdogStorageOverflow], 1
  600. Hci112:
  601. pop edi
  602. pop esi
  603. Hci115:
  604. ;
  605. ; reset last time so that we're accurate after the trap
  606. ;
  607. .586p
  608. rdtsc
  609. .386p
  610. mov _HalpWatchdogTscHigh, edx
  611. mov _HalpWatchdogTscLow, eax
  612. jmp short Hci14
  613. Hci12:
  614. ;
  615. ; Increment the total counter, perform average when the count is reached
  616. ;
  617. add _HalpWatchdogCountLow, eax
  618. adc _HalpWatchdogCountHigh, edx
  619. dec _HalpWatchdogAvgCounter
  620. jnz short Hci14
  621. mov edx, _HalpWatchdogCountHigh
  622. mov eax, _HalpWatchdogCountLow
  623. ;
  624. ; compute the average * 2, this measures when we have missed
  625. ; an interrupt at this rate.
  626. ;
  627. mov ecx, COUNTER_TICKS_AVG_SHIFT - 1
  628. Hci13:
  629. shr edx, 1
  630. rcr eax, 1
  631. loop short Hci13
  632. mov _HalpWatchdogCountLow, eax
  633. mov _HalpWatchdogCountHigh, edx
  634. Hci14:
  635. ;
  636. ; Check for any more work
  637. ;
  638. mov eax, HalpCurrentTimeIncrement
  639. cmp _HalpClockWork, ebx ; Any clock interrupt work desired?
  640. jz _KeUpdateSystemTime@0 ; No, process tick
  641. cmp _HalpClockMcaQueueDpc, bl
  642. je short Hci20
  643. mov _HalpClockMcaQueueDpc, bl
  644. ;
  645. ; Queue MCA Dpc
  646. ;
  647. push eax
  648. stdCall _HalpMcaQueueDpc ; Queue MCA Dpc
  649. pop eax
  650. Hci20:
  651. ;
  652. ; (esp) = OldIrql
  653. ; (esp+4) = Vector
  654. ; (esp+8) = base of trap frame
  655. ; ebp = trap frame
  656. ; eax = time increment
  657. ; ebx = 0
  658. ;
  659. cmp _HalpClockSetMSRate, bl ; New clock rate desired?
  660. jz _KeUpdateSystemTime@0 ; No, process tick
  661. ;
  662. ; Time of clock frequency is being changed. See if the 8254 was
  663. ; was reprogrammed for a new rate during last tick
  664. ;
  665. cmp HalpPendingMSRate, ebx ; Was a new rate set durning last
  666. jnz short Hci50 ; tick? Yes, go update globals
  667. Hci40:
  668. ; (eax) = time increment for current tick
  669. ;
  670. ; A new clock rate needs to be set. Setting the rate here will
  671. ; cause the tick after the next tick to be at the new rate.
  672. ; (the next tick is already in progress by the 8254 and will occur
  673. ; at the same rate as this tick)
  674. ;
  675. mov ebx, HalpNextMSRate
  676. mov HalpPendingMSRate, ebx ; pending rate
  677. mov ecx, HalpRollOverTable[ebx*8-8].RollOver
  678. ;
  679. ; Set clock rate
  680. ; (ecx) = RollOverCount
  681. ;
  682. push eax ; save current tick's rate
  683. mov al,COMMAND_8254_COUNTER0+COMMAND_8254_RW_16BIT+COMMAND_8254_MODE2
  684. out TIMER1_CONTROL_PORT0, al ;program count mode of timer 0
  685. IoDelay
  686. mov al, cl
  687. out TIMER1_DATA_PORT0, al ; program timer 0 LSB count
  688. IoDelay
  689. mov al,ch
  690. out TIMER1_DATA_PORT0, al ; program timer 0 MSB count
  691. pop eax
  692. ;
  693. ; (esp) = OldIrql
  694. ; (esp+4) = Vector
  695. ; (esp+8) = base of trap frame
  696. ; ebp = trap frame
  697. ; eax = time increment
  698. ;
  699. jmp _KeUpdateSystemTime@0 ; dispatch this tick
  700. Hci50:
  701. ;
  702. ; The next tick will occur at the rate which was programmed during the last
  703. ; tick. Update globals for new rate which starts with the next tick.
  704. ;
  705. ; (eax) = time increment for current tick
  706. ;
  707. mov ebx, HalpPendingMSRate
  708. mov ecx, HalpRollOverTable[ebx*8-8].RollOver
  709. mov edx, HalpRollOverTable[ebx*8-8].TimeIncr
  710. mov HalpCurrentRollOver, ecx
  711. mov HalpCurrentTimeIncrement, edx ; next tick rate
  712. mov HalpPendingMSRate, 0 ; no longer pending, clear it
  713. cmp _HalpTimerWatchdogEnabled, 0
  714. jz short @f
  715. ;
  716. ; Schedule to recalibrate watchdog counter
  717. ;
  718. push eax
  719. .586p
  720. rdtsc
  721. .386p
  722. mov _HalpWatchdogAvgCounter, COUNTER_TICKS_FOR_AVG
  723. mov _HalpWatchdogTscLow, eax
  724. mov _HalpWatchdogTscHigh, edx
  725. xor eax,eax
  726. mov _HalpWatchdogCountHigh, eax
  727. mov _HalpWatchdogCountLow, eax
  728. pop eax
  729. @@:
  730. cmp ebx, HalpNextMSRate ; new rate == NextRate?
  731. jne Hci40 ; no, go set new pending rate
  732. mov _HalpClockSetMSRate, 0 ; all done setting new rate
  733. jmp _KeUpdateSystemTime@0 ; dispatch this tick
  734. Hci100:
  735. add esp, 8 ; spurious, no EndOfInterrupt
  736. SPURIOUS_INTERRUPT_EXIT ; exit interrupt without eoi
  737. stdENDP _HalpClockInterrupt
  738. ;++
  739. ;
  740. ; ULONG
  741. ; HalSetTimeIncrement (
  742. ; IN ULONG DesiredIncrement
  743. ; )
  744. ;
  745. ; /*++
  746. ;
  747. ; Routine Description:
  748. ;
  749. ; This routine initialize system time clock to generate an
  750. ; interrupt at every DesiredIncrement interval.
  751. ;
  752. ; Arguments:
  753. ;
  754. ; DesiredIncrement - desired interval between every timer tick (in
  755. ; 100ns unit.)
  756. ;
  757. ; Return Value:
  758. ;
  759. ; The *REAL* time increment set.
  760. ;--
  761. cPublicProc _HalSetTimeIncrement,1
  762. mov eax, [esp+4] ; desired setting
  763. xor edx, edx
  764. mov ecx, 10000
  765. div ecx ; round to MS
  766. cmp eax, HalpLargestClockMS ; MS > max?
  767. jc short @f
  768. mov eax, HalpLargestClockMS ; yes, use max
  769. @@:
  770. or eax, eax ; MS < min?
  771. jnz short @f
  772. inc eax ; yes, use min
  773. @@:
  774. mov HalpNextMSRate, eax
  775. mov _HalpClockSetMSRate, 1 ; New clock rate desired.
  776. mov eax, HalpRollOverTable[eax*8-8].TimeIncr
  777. stdRET _HalSetTimeIncrement
  778. stdENDP _HalSetTimeIncrement
  779. _TEXT$03 ends
  780. end