1348 lines
38 KiB
NASM
1348 lines
38 KiB
NASM
|
|
title "Interval Clock Interrupt"
|
|
;++
|
|
;
|
|
; Copyright (c) 1989 Microsoft Corporation
|
|
;
|
|
; Module Name:
|
|
;
|
|
; cb2stall.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:
|
|
;
|
|
; bryanwi 20-Sep-90
|
|
;
|
|
; Add KiSetProfileInterval, KiStartProfileInterrupt,
|
|
; KiStopProfileInterrupt procedures.
|
|
; KiProfileInterrupt ISR.
|
|
; KiProfileList, KiProfileLock are declared here.
|
|
;
|
|
; shielint 10-Dec-90
|
|
; Add performance counter support.
|
|
; Move system clock to irq8, ie we now use RTC to generate system
|
|
; clock. Performance count and Profile use timer 1 counter 0.
|
|
; The interval of the irq0 interrupt can be changed by
|
|
; KiSetProfileInterval. Performance counter does not care about the
|
|
; interval of the interrupt as long as it knows the rollover count.
|
|
; Note: Currently I implemented 1 performance counter for the whole
|
|
; i386 NT.
|
|
;
|
|
; John Vert (jvert) 11-Jul-1991
|
|
; Moved from ke\i386 to hal\i386. Removed non-HAL stuff
|
|
;
|
|
; shie-lin tzong (shielint) 13-March-92
|
|
; Move System clock back to irq0 and use RTC (irq8) to generate
|
|
; profile interrupt. Performance counter and system clock use time1
|
|
; counter 0 of 8254.
|
|
;
|
|
; Landy Wang (corollary!landy) 04-Dec-92
|
|
; Move much code into separate modules for easy inclusion by various
|
|
; HAL builds.
|
|
;
|
|
;--
|
|
|
|
.386p
|
|
.xlist
|
|
include hal386.inc
|
|
include callconv.inc ; calling convention macros
|
|
include i386\ix8259.inc
|
|
include i386\kimacro.inc
|
|
include mac386.inc
|
|
include i386\ixcmos.inc
|
|
include cbus.inc
|
|
.list
|
|
|
|
EXTRNP _HalEndSystemInterrupt,2
|
|
EXTRNP _HalBeginSystemInterrupt,3
|
|
if DBG
|
|
EXTRNP _HalDisplayString,1
|
|
endif
|
|
ifdef CBC_REV1
|
|
EXTRNP _Cbus2RequestSoftwareInterrupt,1
|
|
endif
|
|
|
|
;
|
|
; Constants used to initialize timer 0
|
|
;
|
|
|
|
TIMER1_DATA_PORT0 EQU 40H ; Timer1, channel 0 data port
|
|
TIMER1_CONTROL_PORT0 EQU 43H ; Timer1, channel 0 control port
|
|
TIMER2_DATA_PORT0 EQU 48H ; Timer1, channel 0 data port
|
|
TIMER2_CONTROL_PORT0 EQU 4BH ; Timer1, channel 0 control port
|
|
TIMER1_IRQ EQU 0 ; Irq 0 for timer1 interrupt
|
|
|
|
COMMAND_8254_COUNTER0 EQU 00H ; Select count 0
|
|
COMMAND_8254_RW_16BIT EQU 30H ; Read/Write LSB firt then MSB
|
|
COMMAND_8254_MODE2 EQU 4 ; Use mode 2
|
|
COMMAND_8254_MODE3 EQU 6 ; Use mode 3
|
|
COMMAND_8254_BCD EQU 0 ; Binary count down
|
|
COMMAND_8254_LATCH_READ EQU 0 ; Latch read command
|
|
|
|
PERFORMANCE_FREQUENCY EQU 10000000
|
|
|
|
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 ;
|
|
|
|
CMOS_CONTROL_PORT EQU 70h ; command port for cmos
|
|
CMOS_DATA_PORT EQU 71h ; cmos data port
|
|
D_INT032 EQU 8E00h ; access word for 386 ring 0 int gate
|
|
|
|
;
|
|
; ==== Values used for System Clock ====
|
|
;
|
|
|
|
|
|
_DATA SEGMENT DWORD PUBLIC 'DATA'
|
|
|
|
;
|
|
; The following array stores the per microsecond loop count for each
|
|
; central processor.
|
|
;
|
|
|
|
public HalpPerfCounterLow, HalpPerfCounterHigh
|
|
if DBG
|
|
public CbusClockCount
|
|
public CbusClockLate
|
|
public _CbusCatchClock
|
|
endif
|
|
Cbus2PerfInit dd 0
|
|
HalpPerfCounterLow dd 0
|
|
HalpPerfCounterHigh dd 0
|
|
if DBG
|
|
CbusClockCount dd 0
|
|
CbusClockLate dd 0
|
|
_CbusCatchClock dd 0
|
|
endif
|
|
|
|
public Cbus2NumberSpuriousClocks
|
|
Cbus2NumberSpuriousClocks dd 0
|
|
|
|
public HalpCurrentRollOver, HalpCurrentTimeIncrement
|
|
HalpCurrentRollOver dd 0
|
|
HalpCurrentTimeIncrement dd 0
|
|
|
|
|
|
;
|
|
; Convert the interval to rollover count for 8254 Timer1 device.
|
|
; Timer1 counts down a 16 bit value at a rate of 1.193181667M counts-per-sec.
|
|
;
|
|
; The best fit value closest to 10ms is 10.0144012689ms:
|
|
; ROLLOVER_COUNT 11949
|
|
; TIME_INCREMENT 100144
|
|
; Calculated error is -.0109472 s/day
|
|
;
|
|
;
|
|
; The following table contains 8254 values timer values to use at
|
|
; any given ms setting from 1ms - 15ms. All values work out to the
|
|
; same error per day (-.0109472 s/day).
|
|
;
|
|
|
|
public HalpRollOverTable
|
|
|
|
; RollOver Time
|
|
; Count Increment MS
|
|
HalpRollOverTable dd 1197, 10032 ; 1 ms
|
|
dd 2394, 20064 ; 2 ms
|
|
dd 3591, 30096 ; 3 ms
|
|
dd 4767, 39952 ; 4 ms
|
|
dd 5964, 49984 ; 5 ms
|
|
dd 7161, 60016 ; 6 ms
|
|
dd 8358, 70048 ; 7 ms
|
|
dd 9555, 80080 ; 8 ms
|
|
dd 10731, 89936 ; 9 ms
|
|
dd 11949, 100144 ; 10 ms
|
|
dd 13125, 110000 ; 11 ms
|
|
dd 14322, 120032 ; 12 ms
|
|
dd 15519, 130064 ; 13 ms
|
|
dd 16695, 139920 ; 14 ms
|
|
dd 17892, 149952 ; 15 ms
|
|
|
|
TimeIncr equ 4
|
|
RollOver equ 0
|
|
|
|
public HalpLargestClockMS, HalpNextMSRate, HalpPendingMSRate
|
|
HalpLargestClockMS dd 10 ; Table goes to 15MS, but since we
|
|
; use only 486 & above, limit to 10ms.
|
|
HalpNextMSRate dd 0
|
|
HalpPendingMSRate dd 0
|
|
|
|
;
|
|
; This value is used by CPUx (x>0) during the clock interrupt
|
|
; routine to re-trigger the call to KeUpdateRunTime. Processors
|
|
; calling KeUpdateRunTime call at the maximum supported rate as
|
|
; reported by KeSetTimeIncrement.
|
|
; (HAL Development Reference Guide 5.12)
|
|
;
|
|
public HalpMaxTimeIncrement
|
|
HalpMaxTimeIncrement dd 0
|
|
|
|
;
|
|
; Holds the next time increment. HalpCurrentTimeIncrement is
|
|
; initialized with this value just before calling KeUpdateSystemTime.
|
|
;
|
|
public HalpNewTimeIncrement
|
|
HalpNewTimeIncrement dd 0
|
|
|
|
if DBG
|
|
;
|
|
; Cbus2MaxTimeStamp... max value of time stamp ever.
|
|
; Cbus2MinTimeStamp... min value of time stamp ever.
|
|
; Cbus2CountOverflowTimeStamp...# of overflows of time stamp.
|
|
; Cbus2SumTimeStampHi... Sum of all time stamps.
|
|
; Cbus2SumTimeStampLow...
|
|
; Cbus2CountClockInt... # of clock interrupts.
|
|
;
|
|
public Cbus2MaxTimeStamp, Cbus2MinTimeStamp,
|
|
Cbus2CountOverflowTimeStamp, Cbus2SumTimeStampHi,
|
|
Cbus2SumTimeStampLow, Cbus2CountClockInt
|
|
|
|
Cbus2MaxTimeStamp dd 0
|
|
Cbus2MinTimeStamp dd -1
|
|
Cbus2CountOverflowTimeStamp dd 0
|
|
Cbus2SumTimeStampHi dd 0
|
|
Cbus2SumTimeStampLow dd 0
|
|
Cbus2CountClockInt dd 0
|
|
endif
|
|
|
|
_DATA ends
|
|
|
|
|
|
INIT SEGMENT PARA PUBLIC 'CODE'
|
|
ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
|
|
|
|
if DBG
|
|
RTC_toofast db 'RTC IRQ8 HARDWARE ERROR\n', 0
|
|
endif
|
|
|
|
page ,132
|
|
subttl "Initialize Clock"
|
|
;++
|
|
;
|
|
; VOID
|
|
; Cbus2InitializeClock (
|
|
; )
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; This routine initialize system time clock using 8254 timer1 counter 0
|
|
; to generate an interrupt at every 15ms interval at 8259 irq0.
|
|
;
|
|
; See the definitions of TIME_INCREMENT and ROLLOVER_COUNT if clock rate
|
|
; needs to be changed.
|
|
;
|
|
; All processors call this routine, but only the first call needs
|
|
; to do anything.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; None
|
|
;
|
|
; Return Value:
|
|
;
|
|
; None.
|
|
;
|
|
;--
|
|
cPublicProc _Cbus2InitializeClock ,0
|
|
cmp dword ptr PCR[PcHal.PcrNumber], 0
|
|
|
|
jz @f
|
|
|
|
;
|
|
; Initialize the 'TickOffset' field for CPUx (x>0).
|
|
; It indicates the number of nsec left before calling the
|
|
; KeUpdateRunTime routine. This routine is called at the
|
|
; maximum supported rate as reported by KeSetTimeIncrement.
|
|
;
|
|
mov eax, HalpMaxTimeIncrement
|
|
mov PCR[PcHal.PcrTickOffset], eax
|
|
|
|
stdRET _Cbus2InitializeClock
|
|
|
|
@@:
|
|
mov eax, HalpLargestClockMS
|
|
mov ecx, HalpRollOverTable.TimeIncr
|
|
mov edx, HalpRollOverTable[eax*8-8].TimeIncr
|
|
mov eax, HalpRollOverTable[eax*8-8].RollOver
|
|
|
|
mov HalpCurrentTimeIncrement, edx
|
|
|
|
;
|
|
; (ecx) = Min time_incr
|
|
; (edx) = Max time_incr
|
|
; (eax) = max roll over count
|
|
;
|
|
|
|
;
|
|
; This value is used by CPUx (x>0) during the clock interrupt
|
|
; routine to re-trigger the call to KeUpdateRunTime. Processors
|
|
; calling KeUpdateRunTime call at the maximum supported rate as
|
|
; reported by KeSetTimeIncrement.
|
|
; (HAL Development Reference Guide 5.12)
|
|
;
|
|
mov HalpMaxTimeIncrement, edx
|
|
|
|
push eax
|
|
stdCall _KeSetTimeIncrement, <edx, ecx>
|
|
pop ecx
|
|
|
|
pushfd ; save caller's eflag
|
|
cli ; make sure interrupts are disabled
|
|
|
|
;
|
|
; Set clock rate
|
|
; (ecx) = RollOverCount
|
|
;
|
|
|
|
;
|
|
; use a 50% duty cycle for the system clock
|
|
;
|
|
mov al,COMMAND_8254_COUNTER0+COMMAND_8254_RW_16BIT+COMMAND_8254_MODE3
|
|
out TIMER1_CONTROL_PORT0, al ;program count mode of timer 0
|
|
IoDelay
|
|
mov al, cl
|
|
out TIMER1_DATA_PORT0, al ; program timer 0 LSB count
|
|
IoDelay
|
|
mov al,ch
|
|
out TIMER1_DATA_PORT0, al ; program timer 0 MSB count
|
|
|
|
popfd ; restore caller's eflag
|
|
mov HalpCurrentRollOver, ecx ; Set RollOverCount & initialized
|
|
|
|
;
|
|
; Set up the performance counter mechanism as well:
|
|
; Zero the global system timer for all of the processors.
|
|
;
|
|
mov eax, dword ptr [_CbusTimeStamp]
|
|
mov dword ptr [eax], 0
|
|
mov Cbus2PerfInit, 1 ; calibration done
|
|
|
|
stdRET _Cbus2InitializeClock
|
|
|
|
stdENDP _Cbus2InitializeClock
|
|
|
|
INIT ends
|
|
|
|
_TEXT$03 SEGMENT DWORD 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 CLOCK.
|
|
; 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
|
|
;
|
|
;--
|
|
ENTER_DR_ASSIST Hci_a, Hci_t
|
|
|
|
cPublicProc _Cbus2ClockInterrupt ,0
|
|
|
|
;
|
|
; Save machine state in trap frame
|
|
;
|
|
|
|
ENTER_INTERRUPT Hci_a, Hci_t
|
|
|
|
;
|
|
; (esp) - base of trap frame
|
|
;
|
|
|
|
;
|
|
; Note that during this phase the interrupts are already disabled.
|
|
; This is important because we don't want to take an IPI during this phase
|
|
; which may force us to discard the next clock interrupt.
|
|
; The call to HalBeginSystemInterrupt below will enable the interrupts.
|
|
;
|
|
|
|
ifdef MCA
|
|
;
|
|
; Special hack for MCA machines
|
|
;
|
|
|
|
in al, 61h
|
|
jmp $+2
|
|
or al, 80h
|
|
out 61h, al
|
|
jmp $+2
|
|
|
|
endif ; MCA
|
|
|
|
cmp [_Cbus2CheckSpuriousClock], 1 ; Check for spurious clock?
|
|
je Hci200
|
|
Hci05:
|
|
|
|
ifdef CBC_REV1
|
|
;
|
|
; because we can miss an interrupt due to a hardware bug in the
|
|
; CBC rev 1 silicon, send ourselves an IPI on every clock.
|
|
; since we don't know when we've missed one, this will ensure
|
|
; we don't cause lock timeouts if nothing else!
|
|
;
|
|
|
|
stdCall _Cbus2RequestSoftwareInterrupt, <IPI_LEVEL>
|
|
endif
|
|
|
|
;
|
|
; Dismiss interrupt and raise irq level to clock2 level
|
|
;
|
|
|
|
Hci10:
|
|
push _CbusClockVector
|
|
sub esp, 4 ; allocate space to save OldIrql
|
|
stdCall _HalBeginSystemInterrupt, <CLOCK2_LEVEL, _CbusClockVector, esp>
|
|
POKE_LEDS eax, edx
|
|
|
|
if DBG
|
|
inc dword ptr [CbusClockCount]
|
|
endif
|
|
;
|
|
; Update our software 64-bit performance counter and zero the
|
|
; 32-bit high resolution CBC timestamp counter. we'd like to
|
|
; read off the real amount of elapsed time, ie:
|
|
;
|
|
; mov eax, dword ptr [ecx]
|
|
; add HalpPerfCounterLow, eax
|
|
;
|
|
; but we can't because when the debugger is enabled, NT holds off
|
|
; interrupts for long periods of time, ie: like in DbgLoadImageSymbols()
|
|
; for over 700 _milliseconds_ !!!. so just fib like all the other
|
|
; HALs do, and tell NT only 10ms have gone by.
|
|
;
|
|
if DBG
|
|
;
|
|
; we had a problem where clock interrupts are getting
|
|
; held off for approximately 700 ms once per second! here is
|
|
; the debug code which caught DbgLoadImageSymbols.
|
|
;
|
|
;mov ecx, dword ptr [_CbusTimeStamp]
|
|
;mov eax, dword ptr [ecx]
|
|
|
|
;cmp _CbusCatchClock, 0 ; debug breakpoint desired?
|
|
;je short @f
|
|
|
|
;cmp eax, 2000000 ; if more than 200 milliseconds since
|
|
; the last clockintr, then trigger
|
|
; the analyzer and go into debug
|
|
;jb short @f
|
|
;inc dword ptr [CbusClockLate] ; trigger analyzer
|
|
;int 3
|
|
@@:
|
|
endif
|
|
|
|
mov eax, HalpCurrentTimeIncrement ; current time increment
|
|
mov HalpNewTimeIncrement, eax ; next time increment
|
|
|
|
Hci30:
|
|
|
|
;
|
|
; (esp) = OldIrql
|
|
; (esp+4) = Vector
|
|
; (esp+8) = base of trap frame
|
|
; ebp = trap frame
|
|
; eax = current time increment
|
|
;
|
|
|
|
cmp HalpNextMSRate, 0 ; New clock rate desired?
|
|
jnz short Hci60 ; Yes, program timer h/w.
|
|
|
|
Hci40:
|
|
|
|
mov ebx, HalpNewTimeIncrement
|
|
|
|
;
|
|
; Update performance counters.
|
|
; Do not change without reason the order of the following instractions.
|
|
; They are crafted in this way for the HalQueryPerformanceCounter
|
|
; routine (it can run at the same time on a different processor).
|
|
; The interrupts are disabled because we don't want to take an IPI
|
|
; during this process.
|
|
;
|
|
|
|
;
|
|
; eax = current time increment
|
|
; ebx = next time increment
|
|
;
|
|
|
|
mov ecx, dword ptr [_CbusTimeStamp]
|
|
cmp [_Cbus2CheckSpuriousClock], 1 ; Calculate new SystemTimer.
|
|
pushfd ; Save and disable flags.
|
|
cli
|
|
je Hci210
|
|
sub edx, edx
|
|
Hci50:
|
|
|
|
;
|
|
; Awkward ordering done to place the resetting of the SystemTimer
|
|
; as close to the update of the 64-bit performance counter --
|
|
;
|
|
|
|
mov dword ptr [ecx], edx ; New TimeStamp value.
|
|
add HalpPerfCounterLow, eax
|
|
adc HalpPerfCounterHigh, dword ptr 0
|
|
mov HalpCurrentTimeIncrement, ebx ; Next time increment.
|
|
|
|
popfd ; Restore flags.
|
|
|
|
;
|
|
; (esp) = OldIrql
|
|
; (esp+4) = Vector
|
|
; (esp+8) = base of trap frame
|
|
; ebp = trap frame
|
|
; eax = time increment
|
|
;
|
|
|
|
jmp _KeUpdateSystemTime@0 ; dispatch this tick
|
|
|
|
Hci60:
|
|
;
|
|
; Time of clock frequency is being changed. See if the 8254 was
|
|
; was reprogrammed for a new rate during last tick
|
|
;
|
|
; (eax) = time increment for current tick
|
|
|
|
cmp HalpPendingMSRate, 0 ; Was a new rate set durning last
|
|
jnz short Hci80 ; tick? Yes, go update globals
|
|
|
|
Hci70:
|
|
|
|
;
|
|
; 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 by the 8254 and will occur
|
|
; at the following rate : ~(old rate + new rate)/2 because the new
|
|
; count will be loaded at the end of the current half-cycle.
|
|
;
|
|
; (eax) = time increment for current tick
|
|
|
|
mov ebx, HalpNextMSRate
|
|
mov HalpPendingMSRate, ebx ; pending rate
|
|
|
|
;
|
|
; Next tick increment = ~(current TimeIncr + new TimeIncr)/2
|
|
;
|
|
mov ecx, HalpNewTimeIncrement
|
|
add ecx, HalpRollOverTable[ebx*8-8].TimeIncr
|
|
shr ecx, 1
|
|
mov HalpNewTimeIncrement, ecx
|
|
|
|
mov ecx, HalpRollOverTable[ebx*8-8].RollOver
|
|
|
|
;
|
|
; Set clock rate
|
|
; (ecx) = RollOverCount
|
|
;
|
|
|
|
push eax ; save current tick's rate
|
|
|
|
;
|
|
; use a 50% duty cycle for the system clock
|
|
;
|
|
mov al,COMMAND_8254_COUNTER0+COMMAND_8254_RW_16BIT+COMMAND_8254_MODE3
|
|
out TIMER1_CONTROL_PORT0, al ;program count mode of timer 0
|
|
IoDelay
|
|
mov al, cl
|
|
out TIMER1_DATA_PORT0, al ; program timer 0 LSB count
|
|
IoDelay
|
|
mov al,ch
|
|
out TIMER1_DATA_PORT0, al ; program timer 0 MSB count
|
|
|
|
pop eax
|
|
jmp short Hci40 ; dispatch this tick
|
|
|
|
Hci80:
|
|
|
|
;
|
|
; 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.
|
|
;
|
|
; (eax) = time increment for current tick
|
|
;
|
|
mov ebx, HalpPendingMSRate
|
|
mov ecx, HalpRollOverTable[ebx*8-8].RollOver
|
|
mov edx, HalpRollOverTable[ebx*8-8].TimeIncr
|
|
|
|
mov HalpCurrentRollOver, ecx
|
|
mov HalpNewTimeIncrement, edx ; next tick rate
|
|
mov HalpPendingMSRate, 0 ; no longer pending, clear it
|
|
|
|
cmp ebx, HalpNextMSRate ; new rate == NextRate?
|
|
jne short Hci70 ; no, go set new pending rate
|
|
|
|
mov HalpNextMSRate, 0 ; we are at this rate, clear it
|
|
jmp Hci30 ; process this tick
|
|
|
|
Hci100:
|
|
add esp, 8 ; spurious, no EndOfInterrupt
|
|
SPURIOUS_INTERRUPT_EXIT ; exit interrupt without eoi
|
|
|
|
;
|
|
; Check if this is a spurious interrupt.
|
|
;
|
|
Hci200:
|
|
mov ecx, dword ptr [_CbusTimeStamp]
|
|
mov edx, dword ptr [ecx] ; Get its value
|
|
sub edx, HalpCurrentTimeIncrement ; In range ?
|
|
jb short Hci240 ; No, this is spurious.
|
|
jmp Hci05
|
|
|
|
;
|
|
; Ensure that the time stamp is no larger than HalpCurrentTimeIncrement.
|
|
; We couldn't perform this code earlier when Hci200 was called
|
|
; because it would open a window where the System Timer has been
|
|
; reset, but the 64-bit performance counter has not been updated.
|
|
; An additional CPU, in this case, could get a smaller value than
|
|
; previously requested. To minimize this case, the resetting of
|
|
; the SystemTimer should be as close to the 64-bit performance counter
|
|
; as possible.
|
|
;
|
|
; in: ecx = time stamp address.
|
|
; eax = current time increment.
|
|
; ebx = next time increment.
|
|
;
|
|
; out: ecx = time stamp address.
|
|
; eax = current time increment.
|
|
; ebx = next time increment.
|
|
; edx = new time stamp value to set.
|
|
;
|
|
|
|
Hci210:
|
|
mov edx, dword ptr [ecx] ; Get its value
|
|
sub edx, eax ; Remove current increment.
|
|
if DBG
|
|
;
|
|
; Collect data for average.
|
|
;
|
|
cmp edx, 20000000 ; Debugging if above 2 sec.
|
|
ja short @f
|
|
inc Cbus2CountClockInt
|
|
add Cbus2SumTimeStampLow, edx
|
|
adc Cbus2SumTimeStampHi, 0
|
|
@@:
|
|
;
|
|
; Compute minimum.
|
|
;
|
|
cmp Cbus2MinTimeStamp, edx
|
|
jbe short @f
|
|
mov Cbus2MinTimeStamp, edx
|
|
@@:
|
|
endif
|
|
cmp edx, eax ; Time stamp must be <= unit.
|
|
jbe Hci50 ; Yes, process the int.
|
|
if DBG
|
|
;
|
|
; Compute maximum.
|
|
;
|
|
cmp edx, 20000000 ; Debugging if above 2 sec.
|
|
ja short @f
|
|
inc Cbus2CountOverflowTimeStamp
|
|
cmp Cbus2MaxTimeStamp, edx
|
|
ja short @f
|
|
mov Cbus2MaxTimeStamp, edx
|
|
@@:
|
|
endif
|
|
|
|
;
|
|
; Use the whole time increment.
|
|
;
|
|
mov edx, eax
|
|
jmp Hci50
|
|
|
|
;
|
|
; This is a spurious interrupt.
|
|
;
|
|
Hci240:
|
|
inc [Cbus2NumberSpuriousClocks]
|
|
mov eax, [_Cbus2ClockVector] ; get the interrupting vector
|
|
CBUS_EOI eax, edx ; ack the interrupt controller
|
|
|
|
SPURIOUS_INTERRUPT_EXIT ; exit interrupt without eoi
|
|
|
|
stdENDP _Cbus2ClockInterrupt
|
|
|
|
page ,132
|
|
subttl "System Clock Interrupt for Additional Processors"
|
|
;++
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; This routine is entered as the result of an interrupt generated by CLOCK.
|
|
; This routine is entered only by additional (non-boot) processors, so it
|
|
; must NOT update the performance counter or update the system time.
|
|
;
|
|
; instead, it just dismisses the interrupt, raises system Irql to
|
|
; CLOCK2_LEVEL and transfers control to the standard system routine
|
|
; to update the execution time of the current thread and process.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; None
|
|
; Interrupt is disabled
|
|
;
|
|
; Return Value:
|
|
;
|
|
; Does not return, jumps directly to KeUpdateRunTime, which returns
|
|
;
|
|
; Sets Irql = CLOCK2_LEVEL and dismisses the interrupt
|
|
;
|
|
;--
|
|
ENTER_DR_ASSIST Hcix2_a, Hcix2_t
|
|
|
|
cPublicProc _Cbus2ClockInterruptPx ,0
|
|
|
|
;
|
|
; Save machine state in trap frame
|
|
; (esp) - base of trap frame
|
|
;
|
|
|
|
ENTER_INTERRUPT Hcix2_a, Hcix2_t
|
|
|
|
mov eax, HalpCurrentTimeIncrement ; Get the clock period.
|
|
mov ecx, dword ptr [_CbusTimeStamp] ; Get the time stamp address.
|
|
|
|
;
|
|
; Note that during this phase the interrupts are already disabled.
|
|
; This is important because we don't want to take an IPI during this
|
|
; phase which may force us to discard the next clock interrupt.
|
|
; The call to HalBeginSystemInterrupt below will enable the interrupts.
|
|
;
|
|
|
|
cmp [_Cbus2CheckSpuriousClock], 1 ; Check for spurious clock?
|
|
je short Hcix70
|
|
sub edx, edx ;
|
|
Hcix50:
|
|
mov dword ptr [ecx], edx ; New SystemTimer value.
|
|
|
|
;
|
|
; We call the KeUpdateRunTime routine only at the maximum
|
|
; rate reported by KeSetTimeIncrement.
|
|
;
|
|
sub PCR[PcHal.PcrTickOffset], eax ; subtract time increment.
|
|
jg short Hcix100 ; if greater, not complete tick.
|
|
mov eax, HalpMaxTimeIncrement ; Re-trigger the call.
|
|
add PCR[PcHal.PcrTickOffset], eax ;
|
|
|
|
ifdef CBC_REV1
|
|
;
|
|
; because we can miss an interrupt due to a hardware bug in the
|
|
; CBC rev 1 silicon, send ourselves an IPI on every clock.
|
|
; since we don't know when we've missed one, this will ensure
|
|
; we don't cause lock timeouts if nothing else!
|
|
;
|
|
|
|
stdCall _Cbus2RequestSoftwareInterrupt, <IPI_LEVEL>
|
|
endif
|
|
|
|
;
|
|
; Dismiss interrupt and raise irq level to clock2 level
|
|
;
|
|
|
|
push _CbusClockVector
|
|
sub esp, 4 ; allocate space to save OldIrql
|
|
stdCall _HalBeginSystemInterrupt, <CLOCK2_LEVEL, _CbusClockVector, esp>
|
|
|
|
; Spurious interrupts on Corollary hardware are
|
|
; directed to a different interrupt gate, so no need
|
|
; to check return value above.
|
|
|
|
POKE_LEDS eax, edx
|
|
|
|
;
|
|
; (esp) = OldIrql
|
|
; (esp+4) = Vector
|
|
; (esp+8) = base of trap frame
|
|
;
|
|
; (ebp) = base of trap frame for KeUpdateRunTime, this was set
|
|
; up by the ENTER_INTERRUPT macro above
|
|
|
|
stdCall _KeUpdateRunTime,<dword ptr [esp]>
|
|
|
|
INTERRUPT_EXIT
|
|
;
|
|
; Check if this is a spurious interrupt.
|
|
;
|
|
; in: ecx = time stamp address.
|
|
; eax = current time increment.
|
|
;
|
|
; out: ecx = time stamp address.
|
|
; eax = current time increment.
|
|
; edx = new time stamp value to set.
|
|
;
|
|
Hcix70:
|
|
mov edx, dword ptr [ecx] ; Get its value
|
|
sub edx, eax ; In range ?
|
|
jb short Hcix100 ; No, this is spurious.
|
|
cmp edx, eax ; Time stamp must be <= unit.
|
|
jbe short Hcix50 ; Yes, process the int.
|
|
;
|
|
; Use the whole time-stamp unit.
|
|
;
|
|
mov edx, eax
|
|
jmp short Hcix50
|
|
|
|
;
|
|
; This is a spurious interrupt.
|
|
;
|
|
Hcix100:
|
|
SPURIOUS_INTERRUPT_EXIT ; exit interrupt without EOI
|
|
|
|
stdENDP _Cbus2ClockInterruptPx
|
|
|
|
;++
|
|
;
|
|
; ULONG
|
|
; Cbus2SetTimeIncrement (
|
|
; IN ULONG DesiredIncrement
|
|
; )
|
|
;
|
|
; /*++
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; This routine initializes the system time clock to generate an
|
|
; interrupt at every DesiredIncrement interval. No lock synchronization
|
|
; is needed here, since it is done by the executive prior to calling us.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; DesiredIncrement - desired interval between every timer tick (in
|
|
; 100ns unit.)
|
|
;
|
|
; Return Value:
|
|
;
|
|
; The *REAL* time increment set.
|
|
;--
|
|
cPublicProc _Cbus2SetTimeIncrement,1
|
|
|
|
mov eax, [esp+4] ; desired setting
|
|
xor edx, edx
|
|
mov ecx, 10000
|
|
div ecx ; round to MS
|
|
|
|
cmp eax, HalpLargestClockMS ; MS > max?
|
|
jc short @f
|
|
mov eax, HalpLargestClockMS ; yes, use max
|
|
@@:
|
|
or eax, eax ; MS < min?
|
|
jnz short @f
|
|
inc eax ; yes, use min
|
|
@@:
|
|
mov HalpNextMSRate, eax
|
|
|
|
mov eax, HalpRollOverTable[eax*8-8].TimeIncr
|
|
stdRET _Cbus2SetTimeIncrement
|
|
|
|
stdENDP _Cbus2SetTimeIncrement
|
|
|
|
page ,132
|
|
subttl "Query Performance Counter"
|
|
;++
|
|
;
|
|
; LARGE_INTEGER
|
|
; Cbus2QueryPerformanceCounter (
|
|
; OUT PLARGE_INTEGER PerformanceFrequency OPTIONAL
|
|
; )
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; This routine returns current 64-bit performance counter and,
|
|
; optionally, the Performance Frequency.
|
|
;
|
|
; Note this routine can NOT be called at Profiling interrupt
|
|
; service routine. Because this routine depends on IRR0 to determine
|
|
; the actual count.
|
|
;
|
|
; Also note that the performace counter returned by this routine
|
|
; is not necessary the value when this routine is just entered.
|
|
; The value returned is actually the counter value at any point
|
|
; between the routine is entered and is exited.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; PerformanceFrequency [TOS+4] - optionally, supplies the address
|
|
; of a variable to receive the performance counter frequency.
|
|
;
|
|
; Return Value:
|
|
;
|
|
; Current value of the performance counter will be returned.
|
|
;
|
|
;--
|
|
|
|
|
|
;
|
|
; Parameter definitions
|
|
;
|
|
|
|
KqpcFrequency EQU [esp+4] ; User supplied Performance Frequency
|
|
|
|
cPublicProc _Cbus2QueryPerformanceCounter ,1
|
|
|
|
;
|
|
; First check to see if the performance counter has been initialized
|
|
; yet. Since the kernel debugger calls KeQueryPerformanceCounter to
|
|
; support the !timer command, we need to return something reasonable
|
|
; before performance counter calibration has occured.
|
|
;
|
|
cmp Cbus2PerfInit, 0 ; calibration finished yet?
|
|
jne @f ; yes, we can read the perf counter
|
|
|
|
;
|
|
; Initialization hasn't occurred yet, so just return zeroes.
|
|
;
|
|
mov eax, 0
|
|
mov edx, 0
|
|
jmp ret_freq
|
|
|
|
@@:
|
|
;
|
|
; Merge our software 64-bit performance counter and the
|
|
; 32-bit high resolution CBC timestamp counter into edx:eax
|
|
;
|
|
|
|
push ebx ; ebx will be destroyed
|
|
@@:
|
|
mov ebx, HalpCurrentTimeIncrement
|
|
|
|
mov edx, HalpPerfCounterHigh
|
|
mov eax, HalpPerfCounterLow
|
|
mov ecx, dword ptr [_Cbus2TimeStamp0]
|
|
mov ecx, dword ptr [ecx]
|
|
|
|
;
|
|
; re-read the global counters until we see we didn't get a torn read.
|
|
;
|
|
cmp ebx, HalpCurrentTimeIncrement
|
|
jne short @b
|
|
|
|
cmp edx, HalpPerfCounterHigh
|
|
jne short @b
|
|
|
|
cmp eax, HalpPerfCounterLow
|
|
jne short @b
|
|
|
|
;
|
|
; This code can run on any CPU while the 'perf counters'
|
|
; are updated only by CPU 0. If the first CPU is unable
|
|
; to receive the clock interrupt because it is 'cli-ed'
|
|
; or 'ipi-ed' for a long time, we need to return a value
|
|
; greater than the provious returned value but less or equal
|
|
; to any future value.
|
|
;
|
|
cmp ecx, ebx ; Time stamp must be <= unit
|
|
jbe short @f ; Yes, it is
|
|
mov ecx, ebx ; else use unit
|
|
@@:
|
|
pop ebx ; Restore ebx
|
|
|
|
;
|
|
; since the time stamp register and our 64-bit software register
|
|
; are already both in 100ns units, there's no need for conversion here.
|
|
;
|
|
add eax, ecx
|
|
adc edx, dword ptr 0
|
|
|
|
ret_freq:
|
|
|
|
;
|
|
; return value is in edx:eax, return the performance counter
|
|
; frequency if the caller wants it.
|
|
;
|
|
|
|
or dword ptr KqpcFrequency, 0 ; is it a NULL variable?
|
|
jz short @f ; it's NULL, so bail
|
|
|
|
mov ecx, KqpcFrequency ; (ecx)-> Frequency variable
|
|
|
|
;
|
|
; Set frequency to a hardcoded clock speed of 66Mhz for now.
|
|
;
|
|
mov DWORD PTR [ecx], PERFORMANCE_FREQUENCY
|
|
mov DWORD PTR [ecx+4], 0
|
|
|
|
@@:
|
|
stdRET _Cbus2QueryPerformanceCounter
|
|
|
|
stdENDP _Cbus2QueryPerformanceCounter
|
|
|
|
_TEXT$03 ends
|
|
|
|
; CMOS_READ
|
|
;
|
|
; Description: This macro read a byte from the CMOS register specified
|
|
; in (AL).
|
|
;
|
|
; Parameter: (AL) = address/register to read
|
|
; Return: (AL) = data
|
|
;
|
|
|
|
CMOS_READ MACRO
|
|
OUT CMOS_CONTROL_PORT,al ; ADDRESS LOCATION AND DISABLE NMI
|
|
IODelay ; I/O DELAY
|
|
IN AL,CMOS_DATA_PORT ; READ IN REQUESTED CMOS DATA
|
|
IODelay ; I/O DELAY
|
|
ENDM
|
|
|
|
;
|
|
; CMOS_WRITE
|
|
;
|
|
; Description: This macro read a byte from the CMOS register specified
|
|
; in (AL).
|
|
;
|
|
; Parameter: (AL) = address/register to read
|
|
; (AH) = data to be written
|
|
;
|
|
; Return: None
|
|
;
|
|
|
|
CMOS_WRITE MACRO
|
|
OUT CMOS_CONTROL_PORT,al ; ADDRESS LOCATION AND DISABLE NMI
|
|
IODelay ; I/O DELAY
|
|
MOV AL,AH ; (AL) = DATA
|
|
OUT CMOS_DATA_PORT,AL ; PLACE IN REQUESTED CMOS LOCATION
|
|
IODelay ; I/O DELAY
|
|
ENDM
|
|
|
|
INIT SEGMENT PARA PUBLIC 'CODE' ; Start 32 bit code
|
|
ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
|
|
|
|
|
|
page ,132
|
|
subttl "Initialize Stall Execution Counter"
|
|
;++
|
|
;
|
|
; VOID
|
|
; Cbus2InitializeStall (
|
|
; IN CCHAR ProcessorNumber
|
|
; )
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; This routine initialize the per Microsecond counter for
|
|
; KeStallExecutionProcessor. Note that the additional processors
|
|
; execute this loop in Phase1, so they are already getting clock
|
|
; interrupts as well as the RTC interrupts they expect from this
|
|
; routine. We should disable clock interrupts during this period
|
|
; to ensure a really accurate result, but it should be "good enough"
|
|
; for now.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; ProcessorNumber - Processor Number
|
|
;
|
|
; Return Value:
|
|
;
|
|
; None.
|
|
;
|
|
;--
|
|
|
|
KiseInterruptCount equ [ebp-12] ; local variable
|
|
|
|
cPublicProc _Cbus2InitializeStall ,1
|
|
|
|
push ebp ; save ebp
|
|
mov ebp, esp ; set up 12 bytes for local use
|
|
sub esp, 12
|
|
|
|
pushfd ; save caller's eflag
|
|
|
|
;
|
|
; Initialize Real Time Clock to interrupt us every 125ms at
|
|
; IRQ 8.
|
|
;
|
|
|
|
cli ; make sure interrupts are disabled
|
|
|
|
;
|
|
; Since RTC interrupt will come from IRQ 8, we need to save
|
|
; the original irq 8 descriptor and set the descriptor to point to
|
|
; our own handler.
|
|
;
|
|
|
|
sidt fword ptr [ebp-8] ; get IDT address
|
|
mov ecx, [ebp-6] ; (edx)->IDT
|
|
|
|
;
|
|
; the profile vector varies for each platform
|
|
;
|
|
mov eax, dword ptr [_ProfileVector]
|
|
|
|
shl eax, 3 ; 8 bytes per IDT entry
|
|
add ecx, eax ; now at the correct IDT RTC entry
|
|
|
|
push dword ptr [ecx] ; (TOS) = original desc of IRQ 8
|
|
push dword ptr [ecx + 4] ; each descriptor has 8 bytes
|
|
|
|
;
|
|
; Pushing the appropriate entry address now (instead of
|
|
; the IDT start address later) to make the pop at the end simpler.
|
|
; we actually will retrieve this value twice, but only pop it once.
|
|
;
|
|
push ecx ; (TOS) -> &IDT[HalProfileVector]
|
|
|
|
;
|
|
; No need to get and save current interrupt masks - only IPI
|
|
; and software interrupts are enabled at this point in the kernel
|
|
; startup. since other processors are still in reset, the profile
|
|
; interrupt is the only one we will see. we really don't need to save
|
|
; the old IDT[PROFILE_VECTOR] entry either, by the way - it's just
|
|
; going to be overwritten in HalInitSystem Phase 1 anyway.
|
|
;
|
|
; note we are not saving edx before calling this function
|
|
|
|
stdCall _HalEnableSystemInterrupt,<dword ptr [_ProfileVector],PROFILE_LEVEL,0>
|
|
|
|
mov ecx, dword ptr [esp] ; restore IDT pointer
|
|
|
|
mov eax, offset FLAT:RealTimeClockHandler
|
|
|
|
mov word ptr [ecx], ax ; Lower half of handler addr
|
|
mov word ptr [ecx+2], KGDT_R0_CODE ; set up selector
|
|
mov word ptr [ecx+4], D_INT032 ; 386 interrupt gate
|
|
|
|
shr eax, 16 ; (ax)=higher half of handler addr
|
|
mov word ptr [ecx+6], ax
|
|
|
|
mov dword ptr KiseinterruptCount, 0 ; set no interrupt yet
|
|
|
|
stdCall _HalpAcquireCmosSpinLock ; intr disabled
|
|
|
|
mov ax,(RegisterAInitByte SHL 8) OR 0AH ; Register A
|
|
CMOS_WRITE ; Initialize it
|
|
|
|
;
|
|
; register C _MUST_ be read before register B is initialized,
|
|
; otherwise an interrupt which was already pending will happen
|
|
; immediately.
|
|
;
|
|
mov al,0CH ; Register C
|
|
CMOS_READ ; Read to initialize
|
|
;
|
|
; 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,0DH ; Register D
|
|
CMOS_READ ; Read to initialize
|
|
mov dword ptr [KiseInterruptCount], 0
|
|
|
|
stdCall _HalpReleaseCmosSpinLock
|
|
|
|
;
|
|
; Now enable the interrupt and start the counter
|
|
; (As a matter of fact, only IRQ8 can come through.)
|
|
;
|
|
|
|
xor eax, eax ; (eax) = 0, initialize loopcount
|
|
ALIGN 16
|
|
sti
|
|
jmp kise10
|
|
|
|
ALIGN 16
|
|
kise10:
|
|
sub eax, 1 ; increment the loopcount
|
|
jnz short kise10
|
|
|
|
if DBG
|
|
;
|
|
; Counter overflowed
|
|
;
|
|
|
|
stdCall _DbgBreakPoint
|
|
endif
|
|
jmp short kise10
|
|
|
|
;
|
|
; Our RealTimeClock interrupt handler. The control comes here through
|
|
; irq 8.
|
|
; Note: we discard the first real time clock interrupt and compute the
|
|
; permicrosecond loopcount on receiving of the second real time
|
|
; interrupt. This is because the first interrupt is generated
|
|
; based on the previous real time tick interval.
|
|
;
|
|
|
|
RealTimeClockHandler:
|
|
|
|
inc dword ptr KiseInterruptCount ; increment interrupt count
|
|
cmp dword ptr KiseInterruptCount,1 ; Is this the first interrupt?
|
|
jnz kise25 ; no, its the second go process it
|
|
pop eax ; get rid of original ret addr
|
|
push offset FLAT:kise10 ; set new return addr
|
|
|
|
|
|
stdCall _HalpAcquireCmosSpinLock ; intr disabled
|
|
|
|
mov ax,(RegisterAInitByte SHL 8) OR 0AH ; Register A
|
|
CMOS_WRITE ; Initialize it
|
|
|
|
;
|
|
; register C _MUST_ be read before register B is initialized,
|
|
; otherwise an interrupt which was already pending will happen
|
|
; immediately.
|
|
;
|
|
mov al,0CH ; Register C
|
|
CMOS_READ ; Read to initialize
|
|
|
|
;
|
|
; 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,0DH ; Register D
|
|
CMOS_READ ; Read to initialize
|
|
|
|
mov eax, _ProfileVector ; mark interrupting vec
|
|
CBUS_EOI eax, ecx ; destroy eax & ecx
|
|
|
|
xor eax, eax ; reset loop counter
|
|
|
|
stdCall _HalpReleaseCmosSpinLock
|
|
|
|
iretd
|
|
|
|
align 4
|
|
kise25:
|
|
|
|
|
|
if DBG
|
|
;
|
|
; ** temporary - check for incorrect KeStallExecutionProcessorLoopCount
|
|
;
|
|
cmp eax, 0
|
|
jnz short kise30
|
|
stdCall _DbgBreakPoint
|
|
|
|
; never return
|
|
;
|
|
; ** End temporary code
|
|
;
|
|
|
|
kise30:
|
|
endif
|
|
neg eax
|
|
xor edx, edx ; (edx:eax) = divident
|
|
mov ecx, PeriodInMicroSecond; (ecx) = time spent in the loop
|
|
div ecx ; (eax) = loop count per microsecond
|
|
cmp edx, 0 ; Is remainder =0?
|
|
jz short kise40 ; yes, go kise40
|
|
inc eax ; increment loopcount by 1
|
|
|
|
align 4
|
|
kise40:
|
|
|
|
mov PCR[PcStallScaleFactor], eax
|
|
|
|
;
|
|
; Reset return address to kexit
|
|
;
|
|
|
|
pop eax ; discard original return address
|
|
push offset FLAT:kexit ; return to kexit
|
|
|
|
;
|
|
; Shutdown the periodic RTC interrupt
|
|
;
|
|
stdCall _HalpAcquireCmosSpinLock
|
|
mov ax,(RegisterAInitByte SHL 8) OR 0AH ; Register A
|
|
CMOS_WRITE ; Initialize it
|
|
mov ax, 0bh
|
|
CMOS_READ
|
|
and al, 1
|
|
mov ah, al
|
|
or ah, REGISTER_B_DISABLE_PERIODIC_INTERRUPT
|
|
mov al, 0bh
|
|
CMOS_WRITE ; Initialize it
|
|
mov al,0CH ; Register C
|
|
CMOS_READ ; dismiss pending interrupt
|
|
stdCall _HalpReleaseCmosSpinLock
|
|
|
|
stdCall _HalDisableSystemInterrupt,<dword ptr [_ProfileVector],PROFILE_LEVEL>
|
|
mov eax, _ProfileVector ; mark interrupting vec
|
|
CBUS_EOI eax, ecx ; destroy eax & ecx
|
|
|
|
and word ptr [esp+8], NOT 0200H ; Disable interrupt upon return
|
|
iretd
|
|
|
|
align 4
|
|
kexit: ; Interrupts are disabled
|
|
|
|
pop ecx ; (ecx) -> &IDT[HalProfileVector]
|
|
pop [ecx+4] ; restore higher half of RTC desc
|
|
pop [ecx] ; restore lower half of RTC desc
|
|
|
|
popfd ; restore caller's eflags
|
|
mov esp, ebp
|
|
pop ebp ; restore ebp
|
|
stdRET _Cbus2InitializeStall
|
|
|
|
stdENDP _Cbus2InitializeStall
|
|
|
|
INIT ends
|
|
|
|
end
|