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.

1098 lines
28 KiB

  1. title "Interval Clock Interrupt"
  2. ;++
  3. ;
  4. ; Copyright (c) 1989 Microsoft Corporation
  5. ; Copyright (c) 1992 Intel Corporation
  6. ; All rights reserved
  7. ;
  8. ; INTEL CORPORATION PROPRIETARY INFORMATION
  9. ;
  10. ; This software is supplied to Microsoft under the terms
  11. ; of a license agreement with Intel Corporation and may not be
  12. ; copied nor disclosed except in accordance with the terms
  13. ; of that agreement.
  14. ;
  15. ;
  16. ; Module Name:
  17. ;
  18. ; mpclock.asm
  19. ;
  20. ; Abstract:
  21. ;
  22. ; This module implements the code necessary to field and process the
  23. ; interval clock interrupt.
  24. ;
  25. ; Author:
  26. ;
  27. ; Shie-Lin Tzong (shielint) 12-Jan-1990
  28. ;
  29. ; Environment:
  30. ;
  31. ; Kernel mode only.
  32. ;
  33. ; Revision History:
  34. ;
  35. ; Ron Mosgrove (Intel) Aug 1993
  36. ; Modified to support PC+MP Systems
  37. ;--
  38. .586p
  39. .xlist
  40. include hal386.inc
  41. include i386\ix8259.inc
  42. include i386\ixcmos.inc
  43. include callconv.inc
  44. include i386\kimacro.inc
  45. include mac386.inc
  46. include apic.inc
  47. include ntapic.inc
  48. include i386\mp8254.inc
  49. .list
  50. EXTRNP _KeUpdateSystemTime,0
  51. EXTRNP _KeUpdateRunTime,1,IMPORT
  52. EXTRNP Kei386EoiHelper,0,IMPORT
  53. EXTRNP _HalEndSystemInterrupt,2
  54. EXTRNP _HalBeginSystemInterrupt,3
  55. EXTRNP _HalpAcquireCmosSpinLock ,0
  56. EXTRNP _HalpReleaseCmosSpinLock ,0
  57. EXTRNP _HalpAcquireSystemHardwareSpinLock ,0
  58. EXTRNP _HalpReleaseSystemHardwareSpinLock ,0
  59. EXTRNP _HalpSetInitialClockRate,0
  60. EXTRNP _HalpMcaQueueDpc, 0
  61. EXTRNP _KeQueryPerformanceCounter, 1
  62. extrn _HalpRtcTimeIncrements:DWORD
  63. extrn _KdEnteredDebugger:DWORD
  64. extrn _HalpTimerWatchdogEnabled:DWORD
  65. extrn _HalpTimerWatchdogStorage:DWORD
  66. extrn _HalpTimerWatchdogCurFrame:DWORD
  67. extrn _HalpTimerWatchdogLastFrame:DWORD
  68. extrn _HalpTimerWatchdogStorageOverflow:DWORD
  69. ifdef ACPI_HAL
  70. ifdef NT_UP
  71. EXTRNP _HalpBrokenPiix4TimerTick, 0
  72. extrn _HalpBrokenAcpiTimer:byte
  73. endif
  74. endif
  75. ifdef MMTIMER
  76. EXTRNP _HalpmmTimerClockInterrupt, 0
  77. MMT_VECTOR EQU 0D3h
  78. endif
  79. ;
  80. ; Constants used to initialize CMOS/Real Time Clock
  81. ;
  82. CMOS_CONTROL_PORT EQU 70h ; command port for cmos
  83. CMOS_DATA_PORT EQU 71h ; cmos data port
  84. CMOS_STATUS_BUSY EQU 80H ; Time update in progress
  85. D_INT032 EQU 8E00h ; access word for 386 ring 0 interrupt gate
  86. REGISTER_B_ENABLE_PERIODIC_INTERRUPT EQU 01000010B
  87. ; RT/CMOS Register 'B' Init byte
  88. ; Values for byte shown are
  89. ; Bit 7 = Update inhibit
  90. ; Bit 6 = Periodic interrupt enable
  91. ; Bit 5 = Alarm interrupt disable
  92. ; Bit 4 = Update interrupt disable
  93. ; Bit 3 = Square wave disable
  94. ; Bit 2 = BCD data format
  95. ; Bit 1 = 24 hour time mode
  96. ; Bit 0 = Daylight Savings disable
  97. REGISTER_B_DISABLE_PERIODIC_INTERRUPT EQU 00000010B
  98. ;
  99. ; RegisterAInitByte sets 8Hz clock rate, used during init to set up
  100. ; KeStallExecutionProcessor, etc. (See RegASystemClockByte below.)
  101. ;
  102. RegisterAInitByte EQU 00101101B ; RT/CMOS Register 'A' init byte
  103. ; 32.768KHz Base divider rate
  104. ; 8Hz int rate, period = 125.0ms
  105. PeriodInMicroSecond EQU 125000 ;
  106. COUNTER_TICKS_AVG_SHIFT EQU 4
  107. COUNTER_TICKS_FOR_AVG EQU 16
  108. PAGE_SIZE EQU 1000H
  109. FRAME_COPY_SIZE EQU 64
  110. _DATA SEGMENT DWORD PUBLIC 'DATA'
  111. ;
  112. ; There is a "C" version of this structure in MPCLOCKC.C
  113. ;
  114. TimeStrucSize EQU 20
  115. RtcTimeIncStruc struc
  116. RTCRegisterA dd 0 ;The RTC register A value for this rate
  117. RateIn100ns dd 0 ;This rate in multiples of 100ns
  118. RateAdjustmentNs dd 0 ;Error Correction (in ns)
  119. RateAdjustmentCnt dd 0 ;Error Correction (as a fraction of 256)
  120. IpiRate dd 0 ;IPI Rate Count (as a fraction of 256)
  121. RtcTimeIncStruc ends
  122. ifdef DBGSSF
  123. DebugSSFStruc struc
  124. SSFCount1 dd 0
  125. SSFCount2 dd 0
  126. SSFRdtsc1 dd 0
  127. SSFRdtsc2 dd 0
  128. SSFRdtsc3 dd 0
  129. SSFRna1 dd 0
  130. SSFRna2 dd 0
  131. SSFRna3 dd 0
  132. DebugSSFStruc ends
  133. public HalpDbgSSF
  134. HalpDbgSSF db (size DebugSSFStruc) * 10 dup (0)
  135. endif
  136. ALIGN dword
  137. public RTCClockFreq
  138. public RegisterAClockValue
  139. RTCClockFreq dd 156250
  140. RegisterAClockValue dd 00101010B ; default interval = 15.6250 ms
  141. MINIMUM_STALL_FACTOR EQU 10H ; Reasonable Minimum
  142. public HalpP0StallFactor
  143. HalpP0StallFactor dd MINIMUM_STALL_FACTOR
  144. public HalpInitStallComputedCount
  145. HalpInitStallComputedCount dd 0
  146. public HalpInitStallLoopCount
  147. HalpInitStallLoopCount dd 0
  148. ALIGN dword
  149. ;
  150. ; Clock Rate Adjustment Counter. This counter is used to keep a tally of
  151. ; adjustments needed to be applied to the RTC rate as passed to the
  152. ; kernel.
  153. ;
  154. public _HalpCurrentRTCRegisterA, _HalpCurrentClockRateIn100ns
  155. public _HalpCurrentClockRateAdjustment, _HalpCurrentIpiRate
  156. public _HalpIpiRateCounter, _HalpNextMSRate, _HalpPendingRate
  157. public _HalpRateAdjustment
  158. _HalpCurrentRTCRegisterA dd 0
  159. _HalpCurrentClockRateIn100ns dd 0
  160. _HalpCurrentClockRateAdjustment dd 0
  161. _HalpCurrentIpiRate dd 0
  162. _HalpIpiRateCounter dd 0
  163. _HalpNextMSRate dd 0
  164. _HalpPendingRate dd 0
  165. _HalpRateAdjustment dd 0
  166. ifdef ACPI_HAL
  167. public _HalpCurrentMSRateTableIndex
  168. _HalpCurrentMSRateTableIndex dd 0
  169. endif
  170. ;
  171. ; HalpUse8254 - flag to indicate 8254 should be used
  172. ; HalpSample8254 - count to sample 8254
  173. ;
  174. ; N.B. access to the 8254 is gaurded with the Cmos lock
  175. ;
  176. public _HalpUse8254
  177. _HalpUse8254 db 0
  178. _HalpSample8254 db 0
  179. _b8254Reserved dw 0
  180. ;
  181. ; Flag to tell clock routine when P0 can Ipi Other processors
  182. ;
  183. public _HalpIpiClock
  184. _HalpIpiClock dd 0
  185. public _HalpClockWork, _HalpClockSetMSRate, _HalpClockMcaQueueDpc
  186. _HalpClockWork label dword
  187. _HalpClockSetMSRate db 0
  188. _HalpClockMcaQueueDpc db 0
  189. _bReserved1 db 0
  190. _bReserved2 db 0
  191. ;
  192. ; timer latency watchdog variables
  193. ;
  194. public _HalpWatchdogAvgCounter, _HalpWatchdogCountLow, _HalpWatchdogCountHigh
  195. public _HalpWatchdogTscLow, _HalpWatchdogTscHigh
  196. _HalpWatchdogAvgCounter dd 0
  197. _HalpWatchdogCountLow dd 0
  198. _HalpWatchdogCountHigh dd 0
  199. _HalpWatchdogTscLow dd 0
  200. _HalpWatchdogTscHigh dd 0
  201. _DATA ends
  202. PAGELK SEGMENT PARA PUBLIC 'CODE'
  203. ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
  204. page ,132
  205. subttl "Initialize Clock"
  206. ;++
  207. ;
  208. ; VOID
  209. ; HalpInitializeClock (
  210. ; )
  211. ;
  212. ; Routine Description:
  213. ;
  214. ; This routine initialize system time clock using RTC to generate an
  215. ; interrupt at every 15.6250 ms interval at APIC_CLOCK_VECTOR
  216. ;
  217. ; It also initializes the 8254 if the 8254 is to be used for performance
  218. ; counters.
  219. ;
  220. ; See the definition of RegisterAClockValue if clock rate needs to be
  221. ; changed.
  222. ;
  223. ; This routine assumes it runs during Phase 0 on P0.
  224. ;
  225. ; Arguments:
  226. ;
  227. ; None
  228. ;
  229. ; Return Value:
  230. ;
  231. ; None.
  232. ;
  233. ;--
  234. cPublicProc _HalpInitializeClock ,0
  235. ;
  236. ; timer latency watchdog initialization
  237. ;
  238. cmp _HalpTimerWatchdogEnabled, 0
  239. jz short @f
  240. rdtsc
  241. mov _HalpWatchdogAvgCounter, COUNTER_TICKS_FOR_AVG
  242. mov _HalpWatchdogTscLow, eax
  243. mov _HalpWatchdogTscHigh, edx
  244. xor eax, eax
  245. mov _HalpWatchdogCountLow, eax
  246. mov _HalpWatchdogCountHigh, eax
  247. @@:
  248. pushfd ; save caller's eflag
  249. cli ; make sure interrupts are disabled
  250. stdCall _HalpSetInitialClockRate
  251. ;
  252. ; Set the interrupt rate to what is actually needed
  253. ;
  254. stdCall _HalpAcquireCmosSpinLock ; intr disabled
  255. mov eax, _HalpCurrentRTCRegisterA
  256. shl ax, 8
  257. mov al, 0AH ; Register A
  258. CMOS_WRITE ; Initialize it
  259. ;
  260. ; Don't clobber the Daylight Savings Time bit in register B, because we
  261. ; stash the LastKnownGood "environment variable" there.
  262. ;
  263. mov ax, 0bh
  264. CMOS_READ
  265. and al, 1
  266. mov ah, al
  267. or ah, REGISTER_B_ENABLE_PERIODIC_INTERRUPT
  268. mov al, 0bh
  269. CMOS_WRITE ; Initialize it
  270. mov al,0CH ; Register C
  271. CMOS_READ ; Read to initialize
  272. mov al,0DH ; Register D
  273. CMOS_READ ; Read to initialize
  274. stdCall _HalpReleaseCmosSpinLock
  275. ;
  276. ; For HALAACPI (free), init the 8254 so we can use it to
  277. ; verify the ACPI timer frequency
  278. ;
  279. ifdef ACPI_HAL
  280. ifdef NT_UP
  281. jmp short Hic50
  282. endif
  283. endif
  284. cmp _HalpUse8254, 0
  285. jz short Hic90
  286. Hic50:
  287. stdCall _HalpAcquireSystemHardwareSpinLock ; intr disabled
  288. ; Program 8254 to count down the maximum interval
  289. ; (8254 access is gaurded with CmosSpinLock)
  290. mov eax, PERFORMANCE_INTERVAL
  291. mov ecx, eax
  292. ; set up counter 0 for periodic, binary count-down from max value
  293. mov al,COMMAND_8254_COUNTER0+COMMAND_8254_RW_16BIT+COMMAND_8254_MODE2
  294. out TIMER1_CONTROL_PORT0, al ; program count mode of timer 0
  295. IoDelay
  296. mov al, cl
  297. out TIMER1_DATA_PORT0, al ; program counter 0 LSB count
  298. IoDelay
  299. mov al,ch
  300. out TIMER1_DATA_PORT0, al ; program counter 0 MSB count
  301. or _HalpUse8254, PERF_8254_INITIALIZED
  302. stdCall _HalpReleaseSystemHardwareSpinLock
  303. Hic90:
  304. popfd ; restore caller's eflag
  305. stdRET _HalpInitializeClock
  306. stdENDP _HalpInitializeClock
  307. PAGELK ends
  308. _TEXT SEGMENT PARA PUBLIC 'CODE'
  309. ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
  310. page ,132
  311. subttl "System Clock Interrupt"
  312. ;++
  313. ;
  314. ; Routine Description:
  315. ;
  316. ;
  317. ; This routine is entered as the result of an interrupt generated by CLOCK2.
  318. ; Its function is to dismiss the interrupt, raise system Irql to
  319. ; CLOCK2_LEVEL, update performance counter and transfer control to the
  320. ; standard system routine to update the system time and the execution
  321. ; time of the current thread
  322. ; and process.
  323. ;
  324. ;
  325. ; Arguments:
  326. ;
  327. ; None
  328. ; Interrupt is disabled
  329. ;
  330. ; Return Value:
  331. ;
  332. ; Does not return, jumps directly to KeUpdateSystemTime, which returns
  333. ;
  334. ; Sets Irql = CLOCK2_LEVEL and dismisses the interrupt
  335. ;
  336. ;--
  337. APIC_ICR_CLOCK equ (DELIVER_FIXED OR ICR_ALL_EXCL_SELF OR APIC_CLOCK_VECTOR)
  338. ENTER_DR_ASSIST Hci_a, Hci_t
  339. cPublicProc _HalpClockInterrupt ,0
  340. ;
  341. ; Save machine state in trap frame
  342. ;
  343. ENTER_INTERRUPT Hci_a, Hci_t
  344. ;
  345. ; (esp) - base of trap frame
  346. ;
  347. ; dismiss interrupt and raise Irql
  348. ;
  349. push APIC_CLOCK_VECTOR
  350. sub esp, 4 ; allocate space to save OldIrql
  351. stdCall _HalBeginSystemInterrupt, <CLOCK2_LEVEL,APIC_CLOCK_VECTOR,esp>
  352. ifdef NT_UP
  353. ifdef ACPI_HAL
  354. ;
  355. ; Check to see if we need to fix up a broken PIIX4
  356. ;
  357. .if (_HalpBrokenAcpiTimer)
  358. stdCall _HalpBrokenPiix4TimerTick
  359. .endif
  360. endif
  361. endif
  362. mov al, _HalpUse8254
  363. or al, al
  364. jz short Hci90
  365. add _HalpSample8254, 56h
  366. jnc short Hci90
  367. ; Call KeQueryPerformanceCounter() so that wrap-around of 8254 is
  368. ; detected and the base value for performance counters updated.
  369. ; Ignore returned value and reset HalpSample8254.
  370. ;
  371. ; WARNING - change increment value above if maximum RTC time increment
  372. ; is increased to be more than current maximum value of 15.625 ms.
  373. ; Currently the call will be made every 3rd timer tick.
  374. xor eax, eax
  375. mov _HalpSample8254, al
  376. stdCall _KeQueryPerformanceCounter, <eax>
  377. Hci90:
  378. ;
  379. ; This is the RTC interrupt, so we have to clear the
  380. ; interrupt flag on the RTC.
  381. ;
  382. stdCall _HalpAcquireCmosSpinLock
  383. ;
  384. ; clear interrupt flag on RTC by banging on the CMOS. On some systems this
  385. ; doesn't work the first time we do it, so we do it twice. It is rumored that
  386. ; some machines require more than this, but that hasn't been observed with NT.
  387. ;
  388. mov al,0CH ; Register C
  389. CMOS_READ ; Read to initialize
  390. mov al,0CH ; Register C
  391. CMOS_READ ; Read to initialize
  392. stdCall _HalpReleaseCmosSpinLock
  393. mov eax, _HalpCurrentClockRateIn100ns
  394. xor ebx, ebx
  395. ;
  396. ; Adjust the tick count as needed
  397. ;
  398. mov ecx, _HalpCurrentClockRateAdjustment
  399. add byte ptr _HalpRateAdjustment, cl
  400. sbb eax, ebx
  401. ;
  402. ; (esp) = OldIrql
  403. ; (esp+4) = Vector
  404. ; (esp+8) = base of trap frame
  405. ; eax = time increment of this tick
  406. ; ebx = 0
  407. ;
  408. ;
  409. ; With an APIC Based System we will force a clock interrupt to all other
  410. ; processors. This is not really an IPI in the NT sense of the word, it
  411. ; uses the Local Apic to generate interrupts to other CPU's.
  412. ;
  413. ifdef NT_UP
  414. ; UP implemention, we don't care about IPI's here
  415. else ; ! NT_UP
  416. ;
  417. ; See if we need to IPI anyone, this happens only at the
  418. ; Lowest supported frequency (ie the value KeSetTimeIncrement
  419. ; is called with. We have a IPI Rate based upon the current
  420. ; clock rate relative to the lowest clock rate.
  421. ;
  422. mov ecx, _HalpIpiRateCounter
  423. add ecx, _HalpCurrentIpiRate
  424. cmp ch, bl
  425. mov byte ptr _HalpIpiRateCounter, cl
  426. jz short HalpDontSendClockIpi ; No, Skip it
  427. ;
  428. ; Don't send vectors onto the APIC bus until at least one other
  429. ; processor comes on line. Vectors placed on the bus will hang
  430. ; around until someone picks them up.
  431. ;
  432. cmp _HalpIpiClock, ebx
  433. je short HalpDontSendClockIpi
  434. ;
  435. ; At least one other processor is alive, send clock pulse to all
  436. ; other processors
  437. ;
  438. ; We use a destination shorthand and therefore only needs to
  439. ; write the lower 32 bits of the ICR.
  440. pushfd
  441. cli
  442. ;
  443. ; Now issue the Clock IPI Command by writing to the Memory Mapped Register
  444. ;
  445. STALL_WHILE_APIC_BUSY
  446. mov dword ptr APIC[LU_INT_CMD_LOW], APIC_ICR_CLOCK
  447. popfd
  448. HalpDontSendClockIpi:
  449. endif ; NT_UP
  450. cmp dword ptr _HalpTimerWatchdogEnabled, 0
  451. jz Hci15
  452. push eax
  453. ;
  454. ; Timer latency watchdog code
  455. ;
  456. rdtsc
  457. ;
  458. ; Compare difference to watchdog count, while storing a copy of the
  459. ; current counter.
  460. ;
  461. push eax
  462. push edx
  463. sub eax, _HalpWatchdogTscLow
  464. sbb edx, _HalpWatchdogTscHigh
  465. pop _HalpWatchdogTscHigh
  466. pop _HalpWatchdogTscLow
  467. js Hci115 ; Was this a bogus counter?
  468. ; (e.g, negative delta)
  469. push eax
  470. push edx
  471. mov ecx, dword ptr _KdEnteredDebugger
  472. mov eax, [ecx] ; eax =
  473. xor edx, edx ; InterlockedExchange(
  474. @@: cmpxchg [ecx], edx ; &KdEnteredDebugger,
  475. jnz short @b ; TRUE );
  476. or al, al
  477. pop edx
  478. pop eax
  479. jnz Hci14 ; In the debugger? Yes, skip it.
  480. cmp _HalpPendingRate, ebx ; Was a new rate set during last
  481. jnz Hci14 ; tick? Yes, skip this compare
  482. ;
  483. ; If we need to compute the average of the time-stamp counter for
  484. ; the current period, add the delta to the counter.
  485. ;
  486. cmp _HalpWatchdogAvgCounter, ebx
  487. jnz Hci12
  488. cmp edx, _HalpWatchdogCountHigh
  489. ja short Hci11
  490. jb Hci14
  491. cmp eax, _HalpWatchdogCountLow
  492. jbe Hci14
  493. Hci11:
  494. cmp dword ptr [_HalpTimerWatchdogCurFrame], 0
  495. je short Hci115
  496. cmp dword ptr [_HalpTimerWatchdogStorageOverflow], 0
  497. jne short Hci115
  498. ;
  499. ; copy FRAME_COPY_SIZE dwords from the stack, or to next page boundary,
  500. ; whichever is less
  501. ;
  502. push esi
  503. push edi
  504. lea esi, [esp+8]
  505. lea ecx, [esi + PAGE_SIZE - 1]
  506. and ecx, NOT(PAGE_SIZE - 1)
  507. sub ecx, esi
  508. shr ecx, 2
  509. cmp ecx, FRAME_COPY_SIZE
  510. jbe short Hci112
  511. mov ecx, FRAME_COPY_SIZE
  512. Hci111:
  513. mov edi, dword ptr _HalpTimerWatchdogCurFrame
  514. rep movsd
  515. add _HalpTimerWatchdogCurFrame, (FRAME_COPY_SIZE*4)
  516. ;
  517. ; If we didn't copy an entire FRAME_COPY_SIZE dwords, zero fill.
  518. ;
  519. mov ecx, dword ptr _HalpTimerWatchdogCurFrame
  520. sub ecx, edi
  521. shr ecx, 2
  522. xor eax, eax
  523. rep stosd
  524. cmp edi, dword ptr _HalpTimerWatchdogLastFrame
  525. jbe short Hci112
  526. mov dword ptr [_HalpTimerWatchdogStorageOverflow], 1
  527. Hci112:
  528. pop edi
  529. pop esi
  530. Hci115:
  531. ;
  532. ; Reset last time so that we're accurate after the trap
  533. ;
  534. rdtsc
  535. mov _HalpWatchdogTscHigh, edx
  536. mov _HalpWatchdogTscLow, eax
  537. jmp short Hci14
  538. Hci12:
  539. ;
  540. ; Increment the total counter, perform average when the count is reached
  541. ;
  542. add _HalpWatchdogCountLow, eax
  543. adc _HalpWatchdogCountHigh, edx
  544. dec _HalpWatchdogAvgCounter
  545. jnz short Hci14
  546. mov edx, _HalpWatchdogCountHigh
  547. mov eax, _HalpWatchdogCountLow
  548. ;
  549. ; compute the average * 2, this measures when we have missed
  550. ; an interrupt at this rate.
  551. ;
  552. mov ecx, COUNTER_TICKS_AVG_SHIFT - 1
  553. Hci13:
  554. shr edx, 1
  555. rcr eax, 1
  556. loop short Hci13
  557. mov _HalpWatchdogCountLow, eax
  558. mov _HalpWatchdogCountHigh, edx
  559. Hci14:
  560. pop eax
  561. Hci15:
  562. ;
  563. ; Check for any more work
  564. ;
  565. cmp _HalpClockWork, ebx ; Any clock interrupt work desired?
  566. jz _KeUpdateSystemTime@0 ; No, process tick
  567. cmp _HalpClockMcaQueueDpc, bl
  568. je short CheckTimerRate
  569. mov _HalpClockMcaQueueDpc, bl
  570. ;
  571. ; Queue MCA Dpc
  572. ;
  573. push eax
  574. stdCall _HalpMcaQueueDpc ; Queue MCA Dpc
  575. pop eax
  576. CheckTimerRate:
  577. ;
  578. ; (esp) = OldIrql
  579. ; (esp+4) = Vector
  580. ; (esp+8) = base of trap frame
  581. ; ebp = trap frame
  582. ; eax = time increment of this tick
  583. ; ebx = 0
  584. ;
  585. cmp _HalpClockSetMSRate, bl ; New clock rate desired?
  586. jz _KeUpdateSystemTime@0 ; No, process tick
  587. ;
  588. ; Time of clock frequency is being changed. See if we have changed rates
  589. ; since the last tick
  590. ;
  591. cmp _HalpPendingRate, ebx ; Was a new rate set durning last
  592. jnz SetUpForNextTick ; tick? Yes, go update globals
  593. ProgramTimerRate:
  594. ; (eax) = time increment for current tick
  595. ;
  596. ; A new clock rate needs to be set. Setting the rate here will
  597. ; cause the tick after the next tick to be at the new rate.
  598. ; (the next tick is already in progress and will occur at the same
  599. ; rate as this tick)
  600. ;
  601. push eax
  602. stdCall _HalpAcquireCmosSpinLock
  603. mov eax, _HalpNextMSRate
  604. mov _HalpPendingRate, eax ; pending rate
  605. dec eax
  606. mov ecx, TimeStrucSize
  607. xor edx, edx
  608. mul ecx
  609. mov eax, _HalpRtcTimeIncrements[eax].RTCRegisterA
  610. mov _HalpCurrentRTCRegisterA, eax
  611. shl ax, 8 ; (ah) = (al)
  612. mov al, 0AH ; Register A
  613. CMOS_WRITE ; Set it
  614. cmp _HalpTimerWatchdogEnabled, 0
  615. jz short @f
  616. ;
  617. ; Timer latency watchdog: schedule to recalibrate TSC delta
  618. ;
  619. rdtsc
  620. mov _HalpWatchdogAvgCounter, COUNTER_TICKS_FOR_AVG
  621. mov _HalpWatchdogTscLow, eax
  622. mov _HalpWatchdogTscHigh, edx
  623. xor eax,eax
  624. mov _HalpWatchdogCountHigh, eax
  625. mov _HalpWatchdogCountLow, eax
  626. @@:
  627. stdCall _HalpReleaseCmosSpinLock
  628. pop eax
  629. jmp _KeUpdateSystemTime@0 ; dispatch this tick
  630. SetUpForNextTick:
  631. ;
  632. ; The next tick will occur at the rate which was programmed during the last
  633. ; tick. Update globals for new rate which starts with the next tick.
  634. ;
  635. ; We will get here if there is a request for a rate change. There could
  636. ; been two requests. That is why we are conmparing the Pending with the
  637. ; NextRate.
  638. ;
  639. ; (eax) = time increment for current tick
  640. ;
  641. push eax
  642. mov eax, _HalpPendingRate
  643. dec eax
  644. ifdef ACPI_HAL
  645. ifdef NT_UP
  646. ;
  647. ; Update the index used by Piix4 workaround; this maps RTC system clock
  648. ; milisecond indices into PM Timer (PMT) milisecond indices
  649. ;
  650. ; RTC { 0=1ms, 1=2ms, 2=4ms, 3=8ms, 4=15.6ms }
  651. ;
  652. ; PMT { 0=1ms, 1=2ms, 2=3ms, ..., 14=15ms }
  653. ;
  654. ; So to convert from RTC index to PMT: PMT = (1 << RTC) - 1
  655. ;
  656. ; NOTE: Since the PM timer array only goes to 15ms, we map our last RTC
  657. ; index (4=15.6) to PMT index 14 (15ms) as a special case
  658. ;
  659. mov edx, 1
  660. mov cl, al
  661. shl edx, cl
  662. dec edx
  663. cmp edx, 0fh ; Check for special case RTC 15.6ms -> PMT 15ms
  664. jne short @f
  665. dec edx
  666. @@:
  667. mov _HalpCurrentMSRateTableIndex, edx
  668. endif
  669. endif
  670. mov ecx, TimeStrucSize
  671. xor edx, edx
  672. mul ecx
  673. mov ebx, _HalpRtcTimeIncrements[eax].RateIn100ns
  674. mov ecx, _HalpRtcTimeIncrements[eax].RateAdjustmentCnt
  675. mov edx, _HalpRtcTimeIncrements[eax].IpiRate
  676. mov _HalpCurrentClockRateIn100ns, ebx
  677. mov _HalpCurrentClockRateAdjustment, ecx
  678. mov _HalpCurrentIpiRate, edx
  679. mov ebx, _HalpPendingRate
  680. mov _HalpPendingRate, 0 ; no longer pending, clear it
  681. pop eax
  682. cmp ebx, _HalpNextMSRate ; new rate == NextRate?
  683. jne ProgramTimerRate ; no, go set new pending rate
  684. mov _HalpClockSetMSRate, 0 ; all done setting new rate
  685. jmp _KeUpdateSystemTime@0 ; dispatch this tick
  686. stdENDP _HalpClockInterrupt
  687. page ,132
  688. subttl "System Clock Interrupt - Non BSP"
  689. ;++
  690. ;
  691. ; Routine Description:
  692. ;
  693. ;
  694. ; This routine is entered as the result of an interrupt generated by
  695. ; CLOCK2. Its function is to dismiss the interrupt, raise system Irql
  696. ; to CLOCK2_LEVEL, transfer control to the standard system routine to
  697. ; the execution time of the current thread and process.
  698. ;
  699. ; This routine is executed on all processors other than P0
  700. ;
  701. ;
  702. ; Arguments:
  703. ;
  704. ; None
  705. ; Interrupt is disabled
  706. ;
  707. ; Return Value:
  708. ;
  709. ; Does not return, jumps directly to KeUpdateSystemTime, which returns
  710. ;
  711. ; Sets Irql = CLOCK2_LEVEL and dismisses the interrupt
  712. ;
  713. ;--
  714. ENTER_DR_ASSIST HPn_a, HPn_t
  715. cPublicProc _HalpClockInterruptPn ,0
  716. ;
  717. ; Save machine state in trap frame
  718. ;
  719. ENTER_INTERRUPT HPn_a, HPn_t
  720. ;
  721. ; (esp) - base of trap frame
  722. ;
  723. ; dismiss interrupt and raise Irql
  724. ;
  725. push APIC_CLOCK_VECTOR
  726. sub esp, 4 ; allocate space to save OldIrql
  727. stdCall _HalBeginSystemInterrupt, <CLOCK2_LEVEL,APIC_CLOCK_VECTOR,esp>
  728. ;
  729. ; All processors will update RunTime for current thread
  730. ;
  731. sti
  732. ; TOS const PreviousIrql
  733. stdCall _KeUpdateRunTime,<dword ptr [esp]>
  734. INTERRUPT_EXIT ; lower irql to old value, iret
  735. ;
  736. ; We don't return here
  737. ;
  738. stdENDP _HalpClockInterruptPn
  739. page ,132
  740. subttl "System Clock Interrupt - Stub"
  741. ;++
  742. ;
  743. ; Routine Description:
  744. ;
  745. ;
  746. ; This routine is entered as the result of an interrupt generated by
  747. ; CLOCK2. Its function is to interrupt and return.
  748. ;
  749. ; This routine is executed on P0 During Phase 0
  750. ;
  751. ;
  752. ; Arguments:
  753. ;
  754. ; None
  755. ; Interrupt is disabled
  756. ;
  757. ; Return Value:
  758. ;
  759. ;--
  760. APIC_ICR_CLOCK equ (DELIVER_FIXED OR ICR_ALL_EXCL_SELF OR APIC_CLOCK_VECTOR)
  761. ENTER_DR_ASSIST HStub_a, HStub_t
  762. cPublicProc _HalpClockInterruptStub ,0
  763. ;
  764. ; Save machine state in trap frame
  765. ;
  766. ENTER_INTERRUPT HStub_a, HStub_t
  767. ;
  768. ; (esp) - base of trap frame
  769. ;
  770. ;
  771. ; clear interrupt flag on RTC by banging on the CMOS. On some systems this
  772. ; doesn't work the first time we do it, so we do it twice. It is rumored that
  773. ; some machines require more than this, but that hasn't been observed with NT.
  774. ;
  775. mov al,0CH ; Register C
  776. CMOS_READ ; Read to initialize
  777. mov al,0CH ; Register C
  778. CMOS_READ ; Read to initialize
  779. Hpi10: test al, 80h
  780. jz short Hpi15
  781. mov al,0CH ; Register C
  782. CMOS_READ ; Read to initialize
  783. jmp short Hpi10
  784. Hpi15:
  785. mov dword ptr APIC[LU_EOI], 0 ; send EOI to APIC local unit
  786. ;
  787. ; Do interrupt exit processing without EOI
  788. ;
  789. SPURIOUS_INTERRUPT_EXIT
  790. ;
  791. ; We don't return here
  792. ;
  793. stdENDP _HalpClockInterruptStub
  794. ifdef MMTIMER
  795. page ,132
  796. subttl "Multi Media Event Timer System Clock Interrupt Stub"
  797. ;++
  798. ;
  799. ; Routine Description:
  800. ;
  801. ;
  802. ; This routine is entered as the result of an interrupt generated by
  803. ; CLOCK2, its function is to interrupt, call HalpmmTimerClockInterrupt
  804. ; to update performance counters and adjust the system clock frequency
  805. ; if necessary, to IPI other processors, and update system time
  806. ;
  807. ; This routine is executed on P0
  808. ;
  809. ; Arguments:
  810. ;
  811. ; None - Interrupt is disabled
  812. ;
  813. ; Return Value:
  814. ;
  815. ;--
  816. ENTER_DR_ASSIST Hmmt_a, Hmmt_t
  817. cPublicProc _HalpmmTimerClockInterruptStub
  818. ;
  819. ; Save machine state in trap frame
  820. ;
  821. ENTER_INTERRUPT Hmmt_a, Hmmt_t
  822. ;
  823. ; (esp) - base of trap frame
  824. ;
  825. ; dismiss interrupt and raise Irql
  826. ;
  827. push MMT_VECTOR
  828. sub esp, 4 ; allocate space to save OldIrql
  829. stdCall _HalBeginSystemInterrupt, <CLOCK2_LEVEL,MMT_VECTOR,esp>
  830. ;
  831. ; Update performace counter and adjust clock frequency if necessary
  832. ;
  833. stdCall _HalpmmTimerClockInterrupt
  834. INTERRUPT_EXIT ; lower irql to old value, iret
  835. ;
  836. ; We don't return here
  837. ;
  838. stdENDP _HalpmmTimerClockInterruptStub
  839. endif
  840. ifdef ACPI_HAL
  841. page ,132
  842. subttl "Query 8254 Counter"
  843. ;++
  844. ;
  845. ; ULONG
  846. ; HalpQuery8254Counter(
  847. ; VOID
  848. ; )
  849. ;
  850. ; Routine Description:
  851. ;
  852. ; This routine returns the current value of the 8254 counter
  853. ;
  854. ; Arguments:
  855. ;
  856. ; None
  857. ;
  858. ; Return Value:
  859. ;
  860. ; Current value of the 8254 counter is returned
  861. ;
  862. ;--
  863. cPublicProc _HalpQuery8254Counter, 0
  864. stdCall _HalpAcquireSystemHardwareSpinLock ; intr disabled
  865. ;
  866. ; Fetch the current counter value from the hardware
  867. ;
  868. mov al, COMMAND_8254_LATCH_READ + COMMAND_8254_COUNTER0
  869. ; Latch PIT Ctr 0 command.
  870. out TIMER1_CONTROL_PORT0, al
  871. IODelay
  872. in al, TIMER1_DATA_PORT0 ; Read PIT Ctr 0, LSByte.
  873. IODelay
  874. movzx ecx, al ; Zero upper bytes of (ECX).
  875. in al, TIMER1_DATA_PORT0 ; Read PIT Ctr 0, MSByte.
  876. mov ch, al ; (CX) = PIT Ctr 0 count.
  877. mov eax, ecx
  878. stdCall _HalpReleaseSystemHardwareSpinLock
  879. stdRET _HalpQuery8254Counter
  880. stdENDP _HalpQuery8254Counter
  881. endif
  882. _TEXT ends
  883. end