mirror of https://github.com/tongzx/nt5src
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
1098 lines
28 KiB
title "Interval Clock Interrupt"
|
|
;++
|
|
;
|
|
; Copyright (c) 1989 Microsoft Corporation
|
|
; Copyright (c) 1992 Intel Corporation
|
|
; All rights reserved
|
|
;
|
|
; INTEL CORPORATION PROPRIETARY INFORMATION
|
|
;
|
|
; This software is supplied to Microsoft under the terms
|
|
; of a license agreement with Intel Corporation and may not be
|
|
; copied nor disclosed except in accordance with the terms
|
|
; of that agreement.
|
|
;
|
|
;
|
|
; Module Name:
|
|
;
|
|
; mpclock.asm
|
|
;
|
|
; Abstract:
|
|
;
|
|
; This module implements the code necessary to field and process the
|
|
; interval clock interrupt.
|
|
;
|
|
; Author:
|
|
;
|
|
; Shie-Lin Tzong (shielint) 12-Jan-1990
|
|
;
|
|
; Environment:
|
|
;
|
|
; Kernel mode only.
|
|
;
|
|
; Revision History:
|
|
;
|
|
; Ron Mosgrove (Intel) Aug 1993
|
|
; Modified to support PC+MP Systems
|
|
;--
|
|
|
|
.586p
|
|
.xlist
|
|
include hal386.inc
|
|
include i386\ix8259.inc
|
|
include i386\ixcmos.inc
|
|
include callconv.inc
|
|
include i386\kimacro.inc
|
|
include mac386.inc
|
|
include apic.inc
|
|
include ntapic.inc
|
|
include i386\mp8254.inc
|
|
|
|
.list
|
|
|
|
EXTRNP _KeUpdateSystemTime,0
|
|
EXTRNP _KeUpdateRunTime,1,IMPORT
|
|
EXTRNP Kei386EoiHelper,0,IMPORT
|
|
EXTRNP _HalEndSystemInterrupt,2
|
|
EXTRNP _HalBeginSystemInterrupt,3
|
|
EXTRNP _HalpAcquireCmosSpinLock ,0
|
|
EXTRNP _HalpReleaseCmosSpinLock ,0
|
|
EXTRNP _HalpAcquireSystemHardwareSpinLock ,0
|
|
EXTRNP _HalpReleaseSystemHardwareSpinLock ,0
|
|
|
|
EXTRNP _HalpSetInitialClockRate,0
|
|
|
|
EXTRNP _HalpMcaQueueDpc, 0
|
|
EXTRNP _KeQueryPerformanceCounter, 1
|
|
|
|
extrn _HalpRtcTimeIncrements:DWORD
|
|
extrn _KdEnteredDebugger:DWORD
|
|
|
|
extrn _HalpTimerWatchdogEnabled:DWORD
|
|
extrn _HalpTimerWatchdogStorage:DWORD
|
|
extrn _HalpTimerWatchdogCurFrame:DWORD
|
|
extrn _HalpTimerWatchdogLastFrame:DWORD
|
|
extrn _HalpTimerWatchdogStorageOverflow:DWORD
|
|
ifdef ACPI_HAL
|
|
ifdef NT_UP
|
|
EXTRNP _HalpBrokenPiix4TimerTick, 0
|
|
extrn _HalpBrokenAcpiTimer:byte
|
|
endif
|
|
endif
|
|
|
|
ifdef MMTIMER
|
|
EXTRNP _HalpmmTimerClockInterrupt, 0
|
|
|
|
MMT_VECTOR EQU 0D3h
|
|
endif
|
|
|
|
;
|
|
; Constants used to initialize CMOS/Real Time Clock
|
|
;
|
|
|
|
CMOS_CONTROL_PORT EQU 70h ; command port for cmos
|
|
CMOS_DATA_PORT EQU 71h ; cmos data port
|
|
CMOS_STATUS_BUSY EQU 80H ; Time update in progress
|
|
|
|
D_INT032 EQU 8E00h ; access word for 386 ring 0 interrupt gate
|
|
REGISTER_B_ENABLE_PERIODIC_INTERRUPT EQU 01000010B
|
|
; RT/CMOS Register 'B' Init byte
|
|
; Values for byte shown are
|
|
; Bit 7 = Update inhibit
|
|
; Bit 6 = Periodic interrupt enable
|
|
; Bit 5 = Alarm interrupt disable
|
|
; Bit 4 = Update interrupt disable
|
|
; Bit 3 = Square wave disable
|
|
; Bit 2 = BCD data format
|
|
; Bit 1 = 24 hour time mode
|
|
; Bit 0 = Daylight Savings disable
|
|
|
|
REGISTER_B_DISABLE_PERIODIC_INTERRUPT EQU 00000010B
|
|
|
|
;
|
|
; RegisterAInitByte sets 8Hz clock rate, used during init to set up
|
|
; KeStallExecutionProcessor, etc. (See RegASystemClockByte below.)
|
|
;
|
|
|
|
RegisterAInitByte EQU 00101101B ; RT/CMOS Register 'A' init byte
|
|
; 32.768KHz Base divider rate
|
|
; 8Hz int rate, period = 125.0ms
|
|
PeriodInMicroSecond EQU 125000 ;
|
|
|
|
COUNTER_TICKS_AVG_SHIFT EQU 4
|
|
COUNTER_TICKS_FOR_AVG EQU 16
|
|
PAGE_SIZE EQU 1000H
|
|
FRAME_COPY_SIZE EQU 64
|
|
|
|
_DATA SEGMENT DWORD PUBLIC 'DATA'
|
|
|
|
;
|
|
; There is a "C" version of this structure in MPCLOCKC.C
|
|
;
|
|
|
|
TimeStrucSize EQU 20
|
|
|
|
RtcTimeIncStruc struc
|
|
RTCRegisterA dd 0 ;The RTC register A value for this rate
|
|
RateIn100ns dd 0 ;This rate in multiples of 100ns
|
|
RateAdjustmentNs dd 0 ;Error Correction (in ns)
|
|
RateAdjustmentCnt dd 0 ;Error Correction (as a fraction of 256)
|
|
IpiRate dd 0 ;IPI Rate Count (as a fraction of 256)
|
|
RtcTimeIncStruc ends
|
|
|
|
ifdef DBGSSF
|
|
DebugSSFStruc struc
|
|
SSFCount1 dd 0
|
|
SSFCount2 dd 0
|
|
SSFRdtsc1 dd 0
|
|
SSFRdtsc2 dd 0
|
|
SSFRdtsc3 dd 0
|
|
|
|
SSFRna1 dd 0
|
|
SSFRna2 dd 0
|
|
SSFRna3 dd 0
|
|
|
|
DebugSSFStruc ends
|
|
|
|
public HalpDbgSSF
|
|
HalpDbgSSF db (size DebugSSFStruc) * 10 dup (0)
|
|
|
|
endif
|
|
|
|
ALIGN dword
|
|
|
|
public RTCClockFreq
|
|
public RegisterAClockValue
|
|
|
|
RTCClockFreq dd 156250
|
|
RegisterAClockValue dd 00101010B ; default interval = 15.6250 ms
|
|
|
|
MINIMUM_STALL_FACTOR EQU 10H ; Reasonable Minimum
|
|
|
|
public HalpP0StallFactor
|
|
HalpP0StallFactor dd MINIMUM_STALL_FACTOR
|
|
public HalpInitStallComputedCount
|
|
HalpInitStallComputedCount dd 0
|
|
public HalpInitStallLoopCount
|
|
HalpInitStallLoopCount dd 0
|
|
|
|
ALIGN dword
|
|
;
|
|
; Clock Rate Adjustment Counter. This counter is used to keep a tally of
|
|
; adjustments needed to be applied to the RTC rate as passed to the
|
|
; kernel.
|
|
;
|
|
|
|
public _HalpCurrentRTCRegisterA, _HalpCurrentClockRateIn100ns
|
|
public _HalpCurrentClockRateAdjustment, _HalpCurrentIpiRate
|
|
public _HalpIpiRateCounter, _HalpNextMSRate, _HalpPendingRate
|
|
public _HalpRateAdjustment
|
|
_HalpCurrentRTCRegisterA dd 0
|
|
_HalpCurrentClockRateIn100ns dd 0
|
|
_HalpCurrentClockRateAdjustment dd 0
|
|
_HalpCurrentIpiRate dd 0
|
|
_HalpIpiRateCounter dd 0
|
|
_HalpNextMSRate dd 0
|
|
_HalpPendingRate dd 0
|
|
_HalpRateAdjustment dd 0
|
|
|
|
ifdef ACPI_HAL
|
|
public _HalpCurrentMSRateTableIndex
|
|
_HalpCurrentMSRateTableIndex dd 0
|
|
endif
|
|
|
|
;
|
|
; HalpUse8254 - flag to indicate 8254 should be used
|
|
; HalpSample8254 - count to sample 8254
|
|
;
|
|
; N.B. access to the 8254 is gaurded with the Cmos lock
|
|
;
|
|
public _HalpUse8254
|
|
_HalpUse8254 db 0
|
|
_HalpSample8254 db 0
|
|
_b8254Reserved dw 0
|
|
|
|
|
|
;
|
|
; Flag to tell clock routine when P0 can Ipi Other processors
|
|
;
|
|
|
|
public _HalpIpiClock
|
|
_HalpIpiClock dd 0
|
|
|
|
public _HalpClockWork, _HalpClockSetMSRate, _HalpClockMcaQueueDpc
|
|
_HalpClockWork label dword
|
|
_HalpClockSetMSRate db 0
|
|
_HalpClockMcaQueueDpc db 0
|
|
_bReserved1 db 0
|
|
_bReserved2 db 0
|
|
|
|
;
|
|
; timer latency watchdog variables
|
|
;
|
|
|
|
public _HalpWatchdogAvgCounter, _HalpWatchdogCountLow, _HalpWatchdogCountHigh
|
|
public _HalpWatchdogTscLow, _HalpWatchdogTscHigh
|
|
|
|
_HalpWatchdogAvgCounter dd 0
|
|
_HalpWatchdogCountLow dd 0
|
|
_HalpWatchdogCountHigh dd 0
|
|
_HalpWatchdogTscLow dd 0
|
|
_HalpWatchdogTscHigh dd 0
|
|
|
|
_DATA ends
|
|
|
|
|
|
PAGELK SEGMENT PARA PUBLIC 'CODE'
|
|
ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
|
|
|
|
page ,132
|
|
subttl "Initialize Clock"
|
|
;++
|
|
;
|
|
; VOID
|
|
; HalpInitializeClock (
|
|
; )
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; This routine initialize system time clock using RTC to generate an
|
|
; interrupt at every 15.6250 ms interval at APIC_CLOCK_VECTOR
|
|
;
|
|
; It also initializes the 8254 if the 8254 is to be used for performance
|
|
; counters.
|
|
;
|
|
; See the definition of RegisterAClockValue if clock rate needs to be
|
|
; changed.
|
|
;
|
|
; This routine assumes it runs during Phase 0 on P0.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; None
|
|
;
|
|
; Return Value:
|
|
;
|
|
; None.
|
|
;
|
|
;--
|
|
cPublicProc _HalpInitializeClock ,0
|
|
|
|
;
|
|
; timer latency watchdog initialization
|
|
;
|
|
cmp _HalpTimerWatchdogEnabled, 0
|
|
jz short @f
|
|
|
|
rdtsc
|
|
mov _HalpWatchdogAvgCounter, COUNTER_TICKS_FOR_AVG
|
|
mov _HalpWatchdogTscLow, eax
|
|
mov _HalpWatchdogTscHigh, edx
|
|
xor eax, eax
|
|
mov _HalpWatchdogCountLow, eax
|
|
mov _HalpWatchdogCountHigh, eax
|
|
@@:
|
|
|
|
pushfd ; save caller's eflag
|
|
cli ; make sure interrupts are disabled
|
|
|
|
stdCall _HalpSetInitialClockRate
|
|
|
|
;
|
|
; Set the interrupt rate to what is actually needed
|
|
;
|
|
stdCall _HalpAcquireCmosSpinLock ; intr disabled
|
|
|
|
mov eax, _HalpCurrentRTCRegisterA
|
|
shl ax, 8
|
|
mov al, 0AH ; Register A
|
|
CMOS_WRITE ; Initialize it
|
|
;
|
|
; Don't clobber the Daylight Savings Time bit in register B, because we
|
|
; stash the LastKnownGood "environment variable" there.
|
|
;
|
|
mov ax, 0bh
|
|
CMOS_READ
|
|
and al, 1
|
|
mov ah, al
|
|
or ah, REGISTER_B_ENABLE_PERIODIC_INTERRUPT
|
|
mov al, 0bh
|
|
CMOS_WRITE ; Initialize it
|
|
mov al,0CH ; Register C
|
|
CMOS_READ ; Read to initialize
|
|
mov al,0DH ; Register D
|
|
CMOS_READ ; Read to initialize
|
|
|
|
stdCall _HalpReleaseCmosSpinLock
|
|
|
|
;
|
|
; For HALAACPI (free), init the 8254 so we can use it to
|
|
; verify the ACPI timer frequency
|
|
;
|
|
ifdef ACPI_HAL
|
|
ifdef NT_UP
|
|
jmp short Hic50
|
|
endif
|
|
endif
|
|
cmp _HalpUse8254, 0
|
|
jz short Hic90
|
|
|
|
Hic50:
|
|
stdCall _HalpAcquireSystemHardwareSpinLock ; intr disabled
|
|
|
|
; Program 8254 to count down the maximum interval
|
|
; (8254 access is gaurded with CmosSpinLock)
|
|
|
|
mov eax, PERFORMANCE_INTERVAL
|
|
mov ecx, eax
|
|
|
|
; set up counter 0 for periodic, binary count-down from max value
|
|
|
|
mov al,COMMAND_8254_COUNTER0+COMMAND_8254_RW_16BIT+COMMAND_8254_MODE2
|
|
out TIMER1_CONTROL_PORT0, al ; program count mode of timer 0
|
|
IoDelay
|
|
mov al, cl
|
|
out TIMER1_DATA_PORT0, al ; program counter 0 LSB count
|
|
IoDelay
|
|
mov al,ch
|
|
out TIMER1_DATA_PORT0, al ; program counter 0 MSB count
|
|
|
|
|
|
or _HalpUse8254, PERF_8254_INITIALIZED
|
|
stdCall _HalpReleaseSystemHardwareSpinLock
|
|
|
|
Hic90:
|
|
popfd ; restore caller's eflag
|
|
stdRET _HalpInitializeClock
|
|
|
|
stdENDP _HalpInitializeClock
|
|
|
|
PAGELK ends
|
|
|
|
_TEXT SEGMENT PARA PUBLIC 'CODE'
|
|
ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
|
|
|
|
|
|
page ,132
|
|
subttl "System Clock Interrupt"
|
|
;++
|
|
;
|
|
; Routine Description:
|
|
;
|
|
;
|
|
; This routine is entered as the result of an interrupt generated by CLOCK2.
|
|
; Its function is to dismiss the interrupt, raise system Irql to
|
|
; CLOCK2_LEVEL, update performance counter and transfer control to the
|
|
; standard system routine to update the system time and the execution
|
|
; time of the current thread
|
|
; and process.
|
|
;
|
|
;
|
|
; Arguments:
|
|
;
|
|
; None
|
|
; Interrupt is disabled
|
|
;
|
|
; Return Value:
|
|
;
|
|
; Does not return, jumps directly to KeUpdateSystemTime, which returns
|
|
;
|
|
; Sets Irql = CLOCK2_LEVEL and dismisses the interrupt
|
|
;
|
|
;--
|
|
|
|
APIC_ICR_CLOCK equ (DELIVER_FIXED OR ICR_ALL_EXCL_SELF OR APIC_CLOCK_VECTOR)
|
|
|
|
ENTER_DR_ASSIST Hci_a, Hci_t
|
|
|
|
cPublicProc _HalpClockInterrupt ,0
|
|
|
|
;
|
|
; Save machine state in trap frame
|
|
;
|
|
|
|
ENTER_INTERRUPT Hci_a, Hci_t
|
|
|
|
;
|
|
; (esp) - base of trap frame
|
|
;
|
|
; dismiss interrupt and raise Irql
|
|
;
|
|
|
|
push APIC_CLOCK_VECTOR
|
|
sub esp, 4 ; allocate space to save OldIrql
|
|
stdCall _HalBeginSystemInterrupt, <CLOCK2_LEVEL,APIC_CLOCK_VECTOR,esp>
|
|
|
|
ifdef NT_UP
|
|
ifdef ACPI_HAL
|
|
;
|
|
; Check to see if we need to fix up a broken PIIX4
|
|
;
|
|
.if (_HalpBrokenAcpiTimer)
|
|
stdCall _HalpBrokenPiix4TimerTick
|
|
.endif
|
|
endif
|
|
endif
|
|
mov al, _HalpUse8254
|
|
or al, al
|
|
jz short Hci90
|
|
|
|
add _HalpSample8254, 56h
|
|
jnc short Hci90
|
|
|
|
; Call KeQueryPerformanceCounter() so that wrap-around of 8254 is
|
|
; detected and the base value for performance counters updated.
|
|
; Ignore returned value and reset HalpSample8254.
|
|
;
|
|
; WARNING - change increment value above if maximum RTC time increment
|
|
; is increased to be more than current maximum value of 15.625 ms.
|
|
; Currently the call will be made every 3rd timer tick.
|
|
|
|
xor eax, eax
|
|
mov _HalpSample8254, al
|
|
stdCall _KeQueryPerformanceCounter, <eax>
|
|
|
|
Hci90:
|
|
|
|
;
|
|
; This is the RTC interrupt, so we have to clear the
|
|
; interrupt flag on the RTC.
|
|
;
|
|
stdCall _HalpAcquireCmosSpinLock
|
|
|
|
;
|
|
; clear interrupt flag on RTC by banging on the CMOS. On some systems this
|
|
; doesn't work the first time we do it, so we do it twice. It is rumored that
|
|
; some machines require more than this, but that hasn't been observed with NT.
|
|
;
|
|
|
|
mov al,0CH ; Register C
|
|
CMOS_READ ; Read to initialize
|
|
mov al,0CH ; Register C
|
|
CMOS_READ ; Read to initialize
|
|
|
|
stdCall _HalpReleaseCmosSpinLock
|
|
|
|
mov eax, _HalpCurrentClockRateIn100ns
|
|
xor ebx, ebx
|
|
|
|
;
|
|
; Adjust the tick count as needed
|
|
;
|
|
|
|
mov ecx, _HalpCurrentClockRateAdjustment
|
|
add byte ptr _HalpRateAdjustment, cl
|
|
sbb eax, ebx
|
|
|
|
;
|
|
; (esp) = OldIrql
|
|
; (esp+4) = Vector
|
|
; (esp+8) = base of trap frame
|
|
; eax = time increment of this tick
|
|
; ebx = 0
|
|
;
|
|
|
|
;
|
|
; With an APIC Based System we will force a clock interrupt to all other
|
|
; processors. This is not really an IPI in the NT sense of the word, it
|
|
; uses the Local Apic to generate interrupts to other CPU's.
|
|
;
|
|
|
|
ifdef NT_UP
|
|
|
|
; UP implemention, we don't care about IPI's here
|
|
|
|
else ; ! NT_UP
|
|
|
|
;
|
|
; See if we need to IPI anyone, this happens only at the
|
|
; Lowest supported frequency (ie the value KeSetTimeIncrement
|
|
; is called with. We have a IPI Rate based upon the current
|
|
; clock rate relative to the lowest clock rate.
|
|
;
|
|
|
|
mov ecx, _HalpIpiRateCounter
|
|
add ecx, _HalpCurrentIpiRate
|
|
cmp ch, bl
|
|
mov byte ptr _HalpIpiRateCounter, cl
|
|
jz short HalpDontSendClockIpi ; No, Skip it
|
|
|
|
;
|
|
; Don't send vectors onto the APIC bus until at least one other
|
|
; processor comes on line. Vectors placed on the bus will hang
|
|
; around until someone picks them up.
|
|
;
|
|
|
|
cmp _HalpIpiClock, ebx
|
|
je short HalpDontSendClockIpi
|
|
|
|
;
|
|
; At least one other processor is alive, send clock pulse to all
|
|
; other processors
|
|
;
|
|
|
|
; We use a destination shorthand and therefore only needs to
|
|
; write the lower 32 bits of the ICR.
|
|
|
|
|
|
pushfd
|
|
cli
|
|
|
|
;
|
|
; Now issue the Clock IPI Command by writing to the Memory Mapped Register
|
|
;
|
|
|
|
STALL_WHILE_APIC_BUSY
|
|
mov dword ptr APIC[LU_INT_CMD_LOW], APIC_ICR_CLOCK
|
|
|
|
popfd
|
|
|
|
HalpDontSendClockIpi:
|
|
|
|
endif ; NT_UP
|
|
|
|
cmp dword ptr _HalpTimerWatchdogEnabled, 0
|
|
jz Hci15
|
|
push eax
|
|
|
|
|
|
;
|
|
; Timer latency watchdog code
|
|
;
|
|
|
|
rdtsc
|
|
|
|
;
|
|
; Compare difference to watchdog count, while storing a copy of the
|
|
; current counter.
|
|
;
|
|
|
|
push eax
|
|
push edx
|
|
|
|
sub eax, _HalpWatchdogTscLow
|
|
sbb edx, _HalpWatchdogTscHigh
|
|
|
|
pop _HalpWatchdogTscHigh
|
|
pop _HalpWatchdogTscLow
|
|
js Hci115 ; Was this a bogus counter?
|
|
; (e.g, negative delta)
|
|
|
|
push eax
|
|
push edx
|
|
mov ecx, dword ptr _KdEnteredDebugger
|
|
mov eax, [ecx] ; eax =
|
|
xor edx, edx ; InterlockedExchange(
|
|
@@: cmpxchg [ecx], edx ; &KdEnteredDebugger,
|
|
jnz short @b ; TRUE );
|
|
or al, al
|
|
pop edx
|
|
pop eax
|
|
jnz Hci14 ; In the debugger? Yes, skip it.
|
|
|
|
cmp _HalpPendingRate, ebx ; Was a new rate set during last
|
|
jnz Hci14 ; tick? Yes, skip this compare
|
|
|
|
;
|
|
; If we need to compute the average of the time-stamp counter for
|
|
; the current period, add the delta to the counter.
|
|
;
|
|
|
|
cmp _HalpWatchdogAvgCounter, ebx
|
|
jnz Hci12
|
|
|
|
cmp edx, _HalpWatchdogCountHigh
|
|
ja short Hci11
|
|
jb Hci14
|
|
|
|
cmp eax, _HalpWatchdogCountLow
|
|
jbe Hci14
|
|
|
|
Hci11:
|
|
cmp dword ptr [_HalpTimerWatchdogCurFrame], 0
|
|
je short Hci115
|
|
cmp dword ptr [_HalpTimerWatchdogStorageOverflow], 0
|
|
jne short Hci115
|
|
|
|
;
|
|
; copy FRAME_COPY_SIZE dwords from the stack, or to next page boundary,
|
|
; whichever is less
|
|
;
|
|
|
|
push esi
|
|
push edi
|
|
lea esi, [esp+8]
|
|
lea ecx, [esi + PAGE_SIZE - 1]
|
|
and ecx, NOT(PAGE_SIZE - 1)
|
|
sub ecx, esi
|
|
shr ecx, 2
|
|
cmp ecx, FRAME_COPY_SIZE
|
|
jbe short Hci112
|
|
mov ecx, FRAME_COPY_SIZE
|
|
Hci111:
|
|
mov edi, dword ptr _HalpTimerWatchdogCurFrame
|
|
rep movsd
|
|
add _HalpTimerWatchdogCurFrame, (FRAME_COPY_SIZE*4)
|
|
;
|
|
; If we didn't copy an entire FRAME_COPY_SIZE dwords, zero fill.
|
|
;
|
|
mov ecx, dword ptr _HalpTimerWatchdogCurFrame
|
|
sub ecx, edi
|
|
shr ecx, 2
|
|
xor eax, eax
|
|
rep stosd
|
|
cmp edi, dword ptr _HalpTimerWatchdogLastFrame
|
|
jbe short Hci112
|
|
mov dword ptr [_HalpTimerWatchdogStorageOverflow], 1
|
|
Hci112:
|
|
|
|
pop edi
|
|
pop esi
|
|
|
|
Hci115:
|
|
|
|
;
|
|
; Reset last time so that we're accurate after the trap
|
|
;
|
|
rdtsc
|
|
mov _HalpWatchdogTscHigh, edx
|
|
mov _HalpWatchdogTscLow, eax
|
|
|
|
jmp short Hci14
|
|
|
|
Hci12:
|
|
;
|
|
; Increment the total counter, perform average when the count is reached
|
|
;
|
|
|
|
add _HalpWatchdogCountLow, eax
|
|
adc _HalpWatchdogCountHigh, edx
|
|
dec _HalpWatchdogAvgCounter
|
|
jnz short Hci14
|
|
|
|
mov edx, _HalpWatchdogCountHigh
|
|
mov eax, _HalpWatchdogCountLow
|
|
|
|
;
|
|
; compute the average * 2, this measures when we have missed
|
|
; an interrupt at this rate.
|
|
;
|
|
mov ecx, COUNTER_TICKS_AVG_SHIFT - 1
|
|
Hci13:
|
|
shr edx, 1
|
|
rcr eax, 1
|
|
loop short Hci13
|
|
|
|
mov _HalpWatchdogCountLow, eax
|
|
mov _HalpWatchdogCountHigh, edx
|
|
|
|
Hci14:
|
|
pop eax
|
|
|
|
Hci15:
|
|
;
|
|
; Check for any more work
|
|
;
|
|
cmp _HalpClockWork, ebx ; Any clock interrupt work desired?
|
|
jz _KeUpdateSystemTime@0 ; No, process tick
|
|
|
|
cmp _HalpClockMcaQueueDpc, bl
|
|
je short CheckTimerRate
|
|
|
|
mov _HalpClockMcaQueueDpc, bl
|
|
|
|
;
|
|
; Queue MCA Dpc
|
|
;
|
|
|
|
push eax
|
|
stdCall _HalpMcaQueueDpc ; Queue MCA Dpc
|
|
pop eax
|
|
|
|
CheckTimerRate:
|
|
;
|
|
; (esp) = OldIrql
|
|
; (esp+4) = Vector
|
|
; (esp+8) = base of trap frame
|
|
; ebp = trap frame
|
|
; eax = time increment of this tick
|
|
; ebx = 0
|
|
;
|
|
cmp _HalpClockSetMSRate, bl ; New clock rate desired?
|
|
jz _KeUpdateSystemTime@0 ; No, process tick
|
|
|
|
|
|
;
|
|
; Time of clock frequency is being changed. See if we have changed rates
|
|
; since the last tick
|
|
;
|
|
cmp _HalpPendingRate, ebx ; Was a new rate set durning last
|
|
jnz SetUpForNextTick ; tick? Yes, go update globals
|
|
|
|
ProgramTimerRate:
|
|
|
|
; (eax) = time increment for current tick
|
|
|
|
;
|
|
; A new clock rate needs to be set. Setting the rate here will
|
|
; cause the tick after the next tick to be at the new rate.
|
|
; (the next tick is already in progress and will occur at the same
|
|
; rate as this tick)
|
|
;
|
|
|
|
push eax
|
|
|
|
stdCall _HalpAcquireCmosSpinLock
|
|
|
|
mov eax, _HalpNextMSRate
|
|
mov _HalpPendingRate, eax ; pending rate
|
|
|
|
dec eax
|
|
mov ecx, TimeStrucSize
|
|
xor edx, edx
|
|
mul ecx
|
|
|
|
mov eax, _HalpRtcTimeIncrements[eax].RTCRegisterA
|
|
mov _HalpCurrentRTCRegisterA, eax
|
|
|
|
shl ax, 8 ; (ah) = (al)
|
|
mov al, 0AH ; Register A
|
|
CMOS_WRITE ; Set it
|
|
|
|
cmp _HalpTimerWatchdogEnabled, 0
|
|
jz short @f
|
|
;
|
|
; Timer latency watchdog: schedule to recalibrate TSC delta
|
|
;
|
|
rdtsc
|
|
mov _HalpWatchdogAvgCounter, COUNTER_TICKS_FOR_AVG
|
|
mov _HalpWatchdogTscLow, eax
|
|
mov _HalpWatchdogTscHigh, edx
|
|
|
|
xor eax,eax
|
|
mov _HalpWatchdogCountHigh, eax
|
|
mov _HalpWatchdogCountLow, eax
|
|
@@:
|
|
|
|
stdCall _HalpReleaseCmosSpinLock
|
|
|
|
pop eax
|
|
jmp _KeUpdateSystemTime@0 ; dispatch this tick
|
|
|
|
SetUpForNextTick:
|
|
|
|
;
|
|
; The next tick will occur at the rate which was programmed during the last
|
|
; tick. Update globals for new rate which starts with the next tick.
|
|
;
|
|
; We will get here if there is a request for a rate change. There could
|
|
; been two requests. That is why we are conmparing the Pending with the
|
|
; NextRate.
|
|
;
|
|
; (eax) = time increment for current tick
|
|
;
|
|
push eax
|
|
|
|
mov eax, _HalpPendingRate
|
|
dec eax
|
|
|
|
ifdef ACPI_HAL
|
|
ifdef NT_UP
|
|
;
|
|
; Update the index used by Piix4 workaround; this maps RTC system clock
|
|
; milisecond indices into PM Timer (PMT) milisecond indices
|
|
;
|
|
; RTC { 0=1ms, 1=2ms, 2=4ms, 3=8ms, 4=15.6ms }
|
|
;
|
|
; PMT { 0=1ms, 1=2ms, 2=3ms, ..., 14=15ms }
|
|
;
|
|
; So to convert from RTC index to PMT: PMT = (1 << RTC) - 1
|
|
;
|
|
; NOTE: Since the PM timer array only goes to 15ms, we map our last RTC
|
|
; index (4=15.6) to PMT index 14 (15ms) as a special case
|
|
;
|
|
mov edx, 1
|
|
mov cl, al
|
|
shl edx, cl
|
|
dec edx
|
|
cmp edx, 0fh ; Check for special case RTC 15.6ms -> PMT 15ms
|
|
jne short @f
|
|
dec edx
|
|
|
|
@@:
|
|
mov _HalpCurrentMSRateTableIndex, edx
|
|
endif
|
|
endif
|
|
|
|
mov ecx, TimeStrucSize
|
|
xor edx, edx
|
|
mul ecx
|
|
|
|
mov ebx, _HalpRtcTimeIncrements[eax].RateIn100ns
|
|
mov ecx, _HalpRtcTimeIncrements[eax].RateAdjustmentCnt
|
|
mov edx, _HalpRtcTimeIncrements[eax].IpiRate
|
|
mov _HalpCurrentClockRateIn100ns, ebx
|
|
mov _HalpCurrentClockRateAdjustment, ecx
|
|
mov _HalpCurrentIpiRate, edx
|
|
|
|
mov ebx, _HalpPendingRate
|
|
mov _HalpPendingRate, 0 ; no longer pending, clear it
|
|
|
|
pop eax
|
|
|
|
cmp ebx, _HalpNextMSRate ; new rate == NextRate?
|
|
jne ProgramTimerRate ; no, go set new pending rate
|
|
|
|
mov _HalpClockSetMSRate, 0 ; all done setting new rate
|
|
jmp _KeUpdateSystemTime@0 ; dispatch this tick
|
|
|
|
|
|
stdENDP _HalpClockInterrupt
|
|
|
|
page ,132
|
|
subttl "System Clock Interrupt - Non BSP"
|
|
;++
|
|
;
|
|
; Routine Description:
|
|
;
|
|
;
|
|
; This routine is entered as the result of an interrupt generated by
|
|
; CLOCK2. Its function is to dismiss the interrupt, raise system Irql
|
|
; to CLOCK2_LEVEL, transfer control to the standard system routine to
|
|
; the execution time of the current thread and process.
|
|
;
|
|
; This routine is executed on all processors other than P0
|
|
;
|
|
;
|
|
; Arguments:
|
|
;
|
|
; None
|
|
; Interrupt is disabled
|
|
;
|
|
; Return Value:
|
|
;
|
|
; Does not return, jumps directly to KeUpdateSystemTime, which returns
|
|
;
|
|
; Sets Irql = CLOCK2_LEVEL and dismisses the interrupt
|
|
;
|
|
;--
|
|
|
|
ENTER_DR_ASSIST HPn_a, HPn_t
|
|
|
|
cPublicProc _HalpClockInterruptPn ,0
|
|
|
|
;
|
|
; Save machine state in trap frame
|
|
;
|
|
|
|
ENTER_INTERRUPT HPn_a, HPn_t
|
|
|
|
;
|
|
; (esp) - base of trap frame
|
|
;
|
|
; dismiss interrupt and raise Irql
|
|
;
|
|
|
|
push APIC_CLOCK_VECTOR
|
|
sub esp, 4 ; allocate space to save OldIrql
|
|
stdCall _HalBeginSystemInterrupt, <CLOCK2_LEVEL,APIC_CLOCK_VECTOR,esp>
|
|
|
|
;
|
|
; All processors will update RunTime for current thread
|
|
;
|
|
|
|
sti
|
|
; TOS const PreviousIrql
|
|
stdCall _KeUpdateRunTime,<dword ptr [esp]>
|
|
|
|
INTERRUPT_EXIT ; lower irql to old value, iret
|
|
|
|
;
|
|
; We don't return here
|
|
;
|
|
|
|
stdENDP _HalpClockInterruptPn
|
|
|
|
|
|
page ,132
|
|
subttl "System Clock Interrupt - Stub"
|
|
;++
|
|
;
|
|
; Routine Description:
|
|
;
|
|
;
|
|
; This routine is entered as the result of an interrupt generated by
|
|
; CLOCK2. Its function is to interrupt and return.
|
|
;
|
|
; This routine is executed on P0 During Phase 0
|
|
;
|
|
;
|
|
; Arguments:
|
|
;
|
|
; None
|
|
; Interrupt is disabled
|
|
;
|
|
; Return Value:
|
|
;
|
|
;--
|
|
|
|
APIC_ICR_CLOCK equ (DELIVER_FIXED OR ICR_ALL_EXCL_SELF OR APIC_CLOCK_VECTOR)
|
|
|
|
ENTER_DR_ASSIST HStub_a, HStub_t
|
|
|
|
cPublicProc _HalpClockInterruptStub ,0
|
|
|
|
;
|
|
; Save machine state in trap frame
|
|
;
|
|
|
|
ENTER_INTERRUPT HStub_a, HStub_t
|
|
|
|
;
|
|
; (esp) - base of trap frame
|
|
;
|
|
|
|
;
|
|
; clear interrupt flag on RTC by banging on the CMOS. On some systems this
|
|
; doesn't work the first time we do it, so we do it twice. It is rumored that
|
|
; some machines require more than this, but that hasn't been observed with NT.
|
|
;
|
|
|
|
mov al,0CH ; Register C
|
|
CMOS_READ ; Read to initialize
|
|
mov al,0CH ; Register C
|
|
CMOS_READ ; Read to initialize
|
|
|
|
Hpi10: test al, 80h
|
|
jz short Hpi15
|
|
mov al,0CH ; Register C
|
|
CMOS_READ ; Read to initialize
|
|
jmp short Hpi10
|
|
Hpi15:
|
|
|
|
mov dword ptr APIC[LU_EOI], 0 ; send EOI to APIC local unit
|
|
|
|
;
|
|
; Do interrupt exit processing without EOI
|
|
;
|
|
|
|
SPURIOUS_INTERRUPT_EXIT
|
|
|
|
;
|
|
; We don't return here
|
|
;
|
|
|
|
stdENDP _HalpClockInterruptStub
|
|
|
|
ifdef MMTIMER
|
|
page ,132
|
|
subttl "Multi Media Event Timer System Clock Interrupt Stub"
|
|
;++
|
|
;
|
|
; Routine Description:
|
|
;
|
|
;
|
|
; This routine is entered as the result of an interrupt generated by
|
|
; CLOCK2, its function is to interrupt, call HalpmmTimerClockInterrupt
|
|
; to update performance counters and adjust the system clock frequency
|
|
; if necessary, to IPI other processors, and update system time
|
|
;
|
|
; This routine is executed on P0
|
|
;
|
|
; Arguments:
|
|
;
|
|
; None - Interrupt is disabled
|
|
;
|
|
; Return Value:
|
|
;
|
|
;--
|
|
|
|
ENTER_DR_ASSIST Hmmt_a, Hmmt_t
|
|
|
|
cPublicProc _HalpmmTimerClockInterruptStub
|
|
|
|
;
|
|
; Save machine state in trap frame
|
|
;
|
|
|
|
ENTER_INTERRUPT Hmmt_a, Hmmt_t
|
|
|
|
;
|
|
; (esp) - base of trap frame
|
|
;
|
|
; dismiss interrupt and raise Irql
|
|
;
|
|
|
|
push MMT_VECTOR
|
|
sub esp, 4 ; allocate space to save OldIrql
|
|
stdCall _HalBeginSystemInterrupt, <CLOCK2_LEVEL,MMT_VECTOR,esp>
|
|
|
|
;
|
|
; Update performace counter and adjust clock frequency if necessary
|
|
;
|
|
|
|
stdCall _HalpmmTimerClockInterrupt
|
|
|
|
INTERRUPT_EXIT ; lower irql to old value, iret
|
|
|
|
;
|
|
; We don't return here
|
|
;
|
|
|
|
stdENDP _HalpmmTimerClockInterruptStub
|
|
endif
|
|
|
|
ifdef ACPI_HAL
|
|
page ,132
|
|
subttl "Query 8254 Counter"
|
|
;++
|
|
;
|
|
; ULONG
|
|
; HalpQuery8254Counter(
|
|
; VOID
|
|
; )
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; This routine returns the current value of the 8254 counter
|
|
;
|
|
; Arguments:
|
|
;
|
|
; None
|
|
;
|
|
; Return Value:
|
|
;
|
|
; Current value of the 8254 counter is returned
|
|
;
|
|
;--
|
|
|
|
cPublicProc _HalpQuery8254Counter, 0
|
|
|
|
stdCall _HalpAcquireSystemHardwareSpinLock ; intr disabled
|
|
|
|
;
|
|
; Fetch the current counter value from the hardware
|
|
;
|
|
|
|
mov al, COMMAND_8254_LATCH_READ + COMMAND_8254_COUNTER0
|
|
; Latch PIT Ctr 0 command.
|
|
out TIMER1_CONTROL_PORT0, al
|
|
IODelay
|
|
in al, TIMER1_DATA_PORT0 ; Read PIT Ctr 0, LSByte.
|
|
IODelay
|
|
movzx ecx, al ; Zero upper bytes of (ECX).
|
|
in al, TIMER1_DATA_PORT0 ; Read PIT Ctr 0, MSByte.
|
|
mov ch, al ; (CX) = PIT Ctr 0 count.
|
|
|
|
mov eax, ecx
|
|
|
|
stdCall _HalpReleaseSystemHardwareSpinLock
|
|
|
|
stdRET _HalpQuery8254Counter
|
|
|
|
stdENDP _HalpQuery8254Counter
|
|
endif
|
|
|
|
_TEXT ends
|
|
|
|
end
|