NT4/private/ntos/nthals/halcbus/i386/cb1stall.asm
2020-09-30 17:12:29 +02:00

933 lines
27 KiB
NASM

title "Initialize Stall Execution for the Corollary MP machines"
;++
;
;Copyright (c) 1992, 1993, 1994 Corollary Inc
;
;Module Name:
;
; cb1stall.asm
;
;Abstract:
;
; This module contains various stall initialization, clock and performance
; counter routines.
;
;Author:
;
; Landy Wang (landy@corollary.com) 05-Oct-1992
;
;Revision History:
;
;--
.386p
.xlist
include hal386.inc
include i386\kimacro.inc
include callconv.inc ; calling convention macros
include cbus.inc
include mac386.inc
include i386\ix8259.inc
EXTRNP _HalBeginSystemInterrupt,3
EXTRNP _HalEndSystemInterrupt,2
.list
;
; the CLKIN pin of the 82489DX is clocking at 32MHz (32000000) on the bridge,
; and 33-1/3 (333333333) MHz on the additional processor cards.
;
; divide this by 1,000,000 to get clocking in a microsecond --> ~33.
; 33 * 16 microseconds == 528 == 0x210
;
CLKIN_ONE_SECOND EQU 32000000 ; in APIC CLKIN units
; CLKIN_ONE_SECOND EQU 33333333 ; in APIC CLKIN units
CLKIN_SIXTEEN_MS EQU 210h ; 16 microseconds (CLKIN)
CLKIN_SIXTEEN_MS_SHIFT EQU 4 ; shift 16 microseconds to 1ms
CLKIN_ENABLE_ONESHOT EQU 0h ; enable one-shot CLKIN interrupt
CLKIN_ENABLE_PERIODIC EQU 20000h ; enable periodic CLKIN interrupts
CLKIN_DISABLE EQU 10000h ; mask off CLKIN interrupts
APIC_TIMER_MILLISECOND EQU 32000 ; timer units to == 1 millisecond
APIC_TIMER_MICROSECOND EQU 33 ; timer units to == 1 microsecond
TIMER_VECTOR_ENTRY EQU 320h ; timer vector table entry 0 address
INITIAL_COUNT_REG EQU 380h ; poke here to set the initial count
CURRENT_COUNT_REG EQU 390h ; current counter countdown is here
D_INT032 EQU 8E00h ; access word for 386 ring 0 int gate
CBUS1_PERF_TASKPRI EQU 0DDh ; vector generated by 8254 timer
;
; The default interval between clock interrupts is
; 10 milliseconds == 10000 microseconds == 100000 (in 100 ns units).
; Remember the NT executive expects this value to be in
; 100 nanosecond units (not microseconds), so multiply
; accordingly when referencing KeTimeIncrement.
;
; the maxclock rates below are not the slowest that the hardware can support -
; they are the slowest we want NT to restore it whenever it is rolled back.
;
MAXCLOCK_RATE_IN_MS EQU 10 ; specify in milliseconds
;
; We will support clock interrupt generation with a minimum of 300
; nanosecond separations to as much as 10 milliseconds apart.
;
MINCLOCK_DELTA_IN_100NS EQU 3 ; specify in 100-nanosecond units
MAXCLOCK_DELTA_IN_100NS EQU 100000 ; specify in 100-nanosecond units
;
; "Convert" the interval to rollover count for 8254 Timer1 device.
; timer1 counts down a 16 bit value at a rate of 1.193M counts-per-sec.
; So that's what we'll use to get the finest granularity. Note that
; this is solely for the performance counter and has NOTHING to do
; with the system clock, which is driven directly from the APIC.
; We'd like for the interrupt rate to be even lower (ie: once per
; second rather than approximately 17 times per second, but the 8254
; only gives us two bytes to feed in the interrupt counter.
;
ROLLOVER_COUNT EQU 0ffffH ; must fit in the 8254's two bytes
PERFORMANCE_FREQUENCY EQU 1193000
TIMER1_DATA_PORT0 EQU 40H ; Timer1, channel 0 data port
TIMER1_CONTROL_PORT0 EQU 43H ; Timer1, channel 0 control port
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_BCD EQU 0 ; Binary count down
COMMAND_8254_LATCH_READ EQU 0 ; Latch read command
_DATA SEGMENT DWORD PUBLIC 'DATA'
;
; default clock rate is 10 milliseconds
;
CbusClockRateInMillis dd 10 ; in milliseconds
Cbus1PerfCounterInit dd 0
Cbus1PerfCounterLow dd 0
Cbus1PerfCounterHigh dd 0
Cbus1LastReadPerfLow dd 0
Cbus1LastReadPerfHigh dd 0
Cbus18254Late dd 0
Cbus1CurrentTimeIncrement dd 0
if DBG
Cbus18254LateCount dd 0
Cbus1Queries dd 0
endif
_DATA ends
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
; Cbus1InitializeStall (
; IN CCHAR ProcessorNumber
; )
;
; Routine Description:
;
; This routine initializes the per Microsecond counter for
; KeStallExecutionProcessor.
;
; All Corollary processors have their own local APIC and
; their own local timers. each processor independently
; calibrates himself here, thus supporting processors of
; varying speeds.
;
; Arguments:
;
; ProcessorNumber - Processor Number
;
; Return Value:
;
; None.
;
;--
cPublicProc _Cbus1InitializeStall ,1
cPublicFpo 1,2
push ebp ; save ebp
mov ebp, esp
sub esp, 8 ; save room for idtr
pushfd ; save caller's eflag
cli ; disable interrupts
;
; save the current CbusClockVector IDT entry, as we are
; going to temporarily repoint it at a private handler.
;
sidt fword ptr [ebp-8] ; get IDTR (base & limit)
mov ecx, [ebp-6] ; get IDTR base value
mov eax, [_CbusClockVector]
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
push ecx ; (TOS) -> &IDT[CbusClockVector]
mov eax, offset FLAT:TimerExpired
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 eax, [_CbusClockVector] ; we expect this vector to...
or eax, CLKIN_ENABLE_ONESHOT ; use one-shot CLKIN to interrupt us
; get the base of APIC space, so we can then access
; the addr of hardware interrupt command register below
mov ecx, [_CbusLocalApic]
; the count register appears to decrement approx 0x30 per
; asm instruction on a 486/33, just fyi. so load it up with
; a "real high value" so we don't get an interrupt whilst setting
; up the local time vector entry, etc, and then at the last
; possible moment, fill it in with the desired starting value.
mov dword ptr INITIAL_COUNT_REG[ecx], CLKIN_ONE_SECOND
; initialize the local timer vector table entry with
; appropriate Vector, Timer Base, Timer Mode and Mask.
mov TIMER_VECTOR_ENTRY[ecx], eax
; poke initial count reg to interrupt us in 16 microseconds
mov dword ptr INITIAL_COUNT_REG[ecx], CLKIN_SIXTEEN_MS
xor eax, eax ; initialize our register counter
ALIGN 16
sti ; enable the interrupt
jmp kise10
ALIGN 16
kise10:
sub eax, 1 ; increment the loopcount
jnz short kise10
if DBG
stdCall _DbgBreakPoint ; Counter overflowed!
endif
jmp short kise10
TimerExpired:
; take the timer expiration interrupt here
if DBG
cmp eax, 0
jnz short kise30
stdCall _DbgBreakPoint ; Counter was never bumped!
; never return
kise30:
endif
neg eax
shr eax, CLKIN_SIXTEEN_MS_SHIFT ; convert to microseconds
mov dword ptr PCR[PcStallScaleFactor], eax
mov eax, [_CbusLocalApic]
; mask off local timer vector table entry now that we're done with it.
mov dword ptr TIMER_VECTOR_ENTRY[eax], CLKIN_DISABLE
;
; Dismiss the interrupt AFTER disabling the timer entry so
; we don't get an extra interrupt later that was already pending.
;
mov eax, _CbusClockVector ; mark interrupting vector
CBUS_EOI eax, ecx ; destroy eax & ecx
add esp, 12 ; unload flags, cs, ip
pop ecx ; (ecx) -> &IDT[CbusClockVector]
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 _Cbus1InitializeStall
stdENDP _Cbus1InitializeStall
page ,132
subttl "Cbus1 Initialize Performance Counter"
;++
;
; VOID
; Cbus1InitializePerf (
; )
;
; Routine Description:
;
; Initialize the 8254 to interrupt the minimum number of times
; per second on the boot processor only to support the performance counter.
;
; Arguments:
;
; None
;
; Return Value:
;
; None.
;
;--
cPublicProc _Cbus1InitializePerf ,0
;
; Since ke\i386\allproc.c no longer boots all the available
; processors in the machine, the first processor to boot must
; initialize the 8254 and take the extra 8254 ticks.
;
mov eax, PCR[PcHal.PcrNumber]
cmp eax, 0
jne short @f
pushfd ; save caller's eflag
cli ; make sure interrupts are disabled
;
; Initialize the APIC so that 8254 interrupts go only to the boot
; processor - there is no need for all of them to get this interrupt
; when all it is doing is updating a global counter.
;
; stdCall _HalEnableSystemInterrupt,<CBUS1_PERF_TASKPRI,CLOCK2_LEVEL,0>
; no need to enable - it's done by our caller
;
; Set clock rate at the 8254
;
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 ecx, ROLLOVER_COUNT
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 Cbus1PerfCounterInit, 1
align 4
@@:
stdRET _Cbus1InitializePerf
stdENDP _Cbus1InitializePerf
INIT ends
_TEXT SEGMENT DWORD PUBLIC 'CODE' ; Start 32 bit code
ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
page ,132
subttl "Cbus1 Initialize Clock"
;++
;
; VOID
; Cbus1InitializeClock (
; )
;
; Routine Description:
;
; Initializes the clock and the kernel variable for the amount of
; time between timer ticks. The kernel needs this information by the end
; of phase 0.
;
; Arguments:
;
; None
;
; Return Value:
;
; None.
;
;--
cPublicProc _Cbus1InitializeClock,0
;
; Fill in value with the time between clock ticks,
; remember the NT executive expects this value to be in
; 100 nanosecond units, not microseconds, so multiply accordingly.
;
mov eax, CbusClockRateInMillis ; current rate in milliseconds
mov ecx, 10000 ; converting back to 100 ns
mul ecx ; eax == 100ns unit value
;
; The Cbus1CurrentTimeIncrement value is used by the clock
; interrupt routine to pass to the kernel, so set it now.
;
mov Cbus1CurrentTimeIncrement, eax
stdCall _Cbus1ProgramClock
stdCall _KeSetTimeIncrement, <MAXCLOCK_DELTA_IN_100NS, MINCLOCK_DELTA_IN_100NS>
stdRET _Cbus1InitializeClock
stdENDP _Cbus1InitializeClock
;++
;
; VOID
; Cbus1ProgramClock (
; )
;
; Routine Description:
;
; This routine initializes the system time clock for each calling
; processor to generate periodic interrupts at the specified rate using
; this processor's local APIC timer. Thus, each processor must call
; this routine to set or change his clock rate. During startup, each
; processor will call this routine to set his own clock rate. Later,
; when the system is running, the HalSetTimerResolution API is only
; supposed to change the clock rate for the boot processor - this is
; for multimedia apps that want timeouts serviced at a better than
; 5 millisecond granularity.
;
; Arguments:
;
; None
;
; Return Value:
;
; None.
;
;--
cPublicProc _Cbus1ProgramClock ,0
mov ecx, CbusClockRateInMillis ; new rate in milliseconds
mov eax, APIC_TIMER_MILLISECOND ; counter per microsecond
mul ecx ; eax == new APIC countdown val
mov edx, [_CbusClockVector] ; we expect this vector to...
or edx, CLKIN_ENABLE_PERIODIC ; use CLKIN to interrupt us
mov ecx, [_CbusLocalApic]
pushfd
cli
; as a frame of reference, the count register decrements
; approximately 0x30 per asm instruction on a 486/33.
; initialize the local timer vector table entry with
; appropriate Vector, Timer Base, Timer Mode and Mask.
mov TIMER_VECTOR_ENTRY[ecx], edx
; poke initial count reg to start the periodic clock timer interrupts.
; the IDT entry is valid & enabled on entry to this routine.
mov dword ptr INITIAL_COUNT_REG[ecx], eax
mov eax, CbusClockRateInMillis ; new rate in milliseconds
mov ecx, 10000 ; converting back to 100 ns
xor edx, edx
mul ecx ; eax == 100ns unit value
;
; store it here so the clock ISR can tell it to the kernel
;
mov Cbus1CurrentTimeIncrement, eax
popfd
stdRET _Cbus1ProgramClock
stdENDP _Cbus1ProgramClock
page ,132
subttl "Query Performance Counter"
;++
;
; LARGE_INTEGER
; Cbus1QueryPerformanceCounter (
; OUT PLARGE_INTEGER PerformanceFrequency OPTIONAL
; )
;
; Routine Description:
;
; This routine returns the current 64-bit performance counter.
; The Performance Frequency is also returned if asked for.
;
; Also note that the performance counter returned by this routine
; is not necessarily the value exactly when this routine was entered.
; The value returned is actually the counter value at any point
; between the routine's entrance and exit times.
;
; This routine is not susceptible to the 2 problems that plague most
; multiprocessor HALs. Their problems are as follows:
;
; a) If the boot processor (or whoever updates the global performance
; counters) misses an interrupt, the counter rolls over undetected,
; and the performance counter returned can actually roll backwards!
;
; b) If you are on an additional processor and for some reason,
; the boot processor is holding off a pending clock interrupt for
; the entire time the additional processor is executing in
; KeQueryPerfomanceCounter. The boot processor hasn't necessarily
; lost a clock interrupt, it's just that he hasn't dropped IRQL
; enough to get it yet, and there isn't any good way for the
; additional processor to force him to. Since the boot processor
; is the only one maintaining the PerfCounter[High,Low], this can
; result in processors returning potentially non-uniform snapshots,
; which can even roll backwards!
;
; Both of these problems have been solved in the Corollary HAL by
; using separate clocks for the system timer and the performance counter.
; Ie: the APIC timer is used for the system timer and interrupts each
; processor every 15 milliseconds. The 8254 interrupts ONLY the boot
; processor once every second. Thus, case a) above cannot happen
; unless the boot processor disables interrupts for more than one second.
; If this happens, the entire system will be badly broken. case b)
; is also bypassed by using a global lock in the performance counter
; interrupt handler. Since the interrupt only occurs once per second
; and only on one processor, the global lock will not hinder performance.
; This allows us to take globally synchronized performance counter
; snapshots and also detect in software if case b) is happening, and
; handle it in software. Because the 8254 timer register is only 16 bits
; wide, the actual 8254 synchronization interrupt will occur approximately
; seventeen times per second instead of once. This is still at least 4
; times better than the system timer (approximately 70 times per second).
;
; 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 _Cbus1QueryPerformanceCounter ,1
;
; First check to see if the performance counter has been
; initialized yet. Since the kernel debugger calls
; KeQueryPerformanceCounter to support the !timer command
; _VERY_ early on, we need to return something reasonable
; even though timer initialization hasn't occured yet.
;
cmp Cbus1PerfCounterInit, 0
jne short @f ; ok, perf counter has been initialized
;
; Initialization hasn't occured yet, so just return zeros.
;
mov eax, 0
mov edx, 0
jmp retfreq ; retfreq too far away for short jmp
align 4
@@:
if DBG
inc dword ptr [Cbus1Queries]
endif
push ebx
push esi
;
; all interrupts must be disabled prior to lock acquisition to
; prevent deadlock.
;
pushfd
cli
lea esi, _Halp8254Lock
align 4
Kqpc00:
ACQUIRE_SPINLOCK esi, Kqpc01
;
; get the global timer counters which are incremented
; one processor only.
;
mov esi, Cbus1PerfCounterLow
mov ebx, Cbus1PerfCounterHigh ; [ebx:esi] = Performance counter
;
; 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.
neg ecx ; PIT counts down to 0h
add ecx, ROLLOVER_COUNT
add esi, ecx
adc ebx, 0 ; [ebx:esi] = Final result
;
; Check whether case b) could be happening right now - this is
; possible no matter which processor is currently calling us.
;
cmp ebx, dword ptr [Cbus1LastReadPerfHigh]
jl short caseb
cmp esi, dword ptr [Cbus1LastReadPerfLow]
jl short caseb
jmp short notpending
align 4
caseb:
;
; Detected case b) happening RIGHT NOW!
; Update the Cbus1 performance counter NOW, and
; set a flag so the interrupt handler will NOT.
; This case actually happens fairly frequently,
; ie: at least every few seconds on an idle
; uniprocessor, so this code is quite useful.
;
add Cbus1PerfCounterLow, ROLLOVER_COUNT ; update performance counter
adc Cbus1PerfCounterHigh, 0
inc dword ptr [Cbus18254Late]
if DBG
inc dword ptr [Cbus18254LateCount]
endif
align 4
notpending:
;
; save last performance counter read so future callers can compare
;
mov Cbus1LastReadPerfLow, esi
mov Cbus1LastReadPerfHigh, ebx
mov edx, ebx ; save return value
mov eax, esi ; save return value
lea esi, _Halp8254Lock
RELEASE_SPINLOCK esi
popfd
pop esi
pop ebx
;
; if asked to, return the frequency in units/second
;
align 4
retfreq:
or dword ptr KqpcFrequency, 0 ; is it NULL?
jz short @f ; if z, yes, go exit
mov ecx, KqpcFrequency ; frequency pointer
mov dword ptr [ecx], PERFORMANCE_FREQUENCY ; set frequency
mov dword ptr [ecx+4], 0 ; currently < 4Gig!
align 4
@@:
stdRET _Cbus1QueryPerformanceCounter
align 4
Kqpc01:
SPIN_ON_SPINLOCK esi,<Kqpc00>
stdENDP _Cbus1QueryPerformanceCounter
page ,132
subttl "Cbus1 Perf Interrupt"
;++
;
; VOID
; Cbus1PerfInterrupt(
; VOID
; );
;
; Routine Description:
;
; This routine is the interrupt handler for the Cbus1 performance
; counter interrupt at a priority just below that of normal clocks.
; Its function is to update the global performance counter so that
; KeQueryPerformanceCounter can return meaningful values.
;
; This routine is executed only by one processor at a rate of seventeen
; times per second, as there is no need for all processors to update
; the same global counter. The only reason the rate is so high is
; because the interrupt interval must fit into a 16 bit register in the
; 8254. Otherwise, it would have been more like once per second.
;
; Since this routine is entered directly via an interrupt gate, interrupt
; protection via cli is not necessary.
;
; Arguments:
;
; None
;
; Return Value:
;
; None.
;
;--
ENTER_DR_ASSIST hipi_a, hipi_t
cPublicProc _Cbus1PerfInterrupt ,0
;
; Save machine state on trap frame
;
ENTER_INTERRUPT hipi_a, hipi_t
; keep it simple, just issue the EOI right now.
; no changing of taskpri/irql is needed here.
; Thus, the EOI serves as the HalEndSystemInterrupt.
mov eax, CBUS1_PERF_TASKPRI ; mark interrupting vector
CBUS_EOI eax, ecx ; destroy eax & ecx
;
; (esp) - base of trap frame
;
ifdef MCA
;
; Special hack for MCA machines
;
in al, 61h
jmp $+2
or al, 80h
out 61h, al
jmp $+2
endif ; MCA
lea esi, _Halp8254Lock
align 4
Kcpi00:
ACQUIRE_SPINLOCK esi, Kcpi01
;
; Update Cbus1 performance counter if a performance counter query
; hasn't done so already.
;
cmp dword ptr [Cbus18254Late], 0
jne short @f
add Cbus1PerfCounterLow, ROLLOVER_COUNT ; update performance counter
adc Cbus1PerfCounterHigh, 0
jmp short noroll
align 4
@@:
dec dword ptr [Cbus18254Late]
align 4
noroll:
RELEASE_SPINLOCK esi
;
; Call this directly instead of through INTERRUPT_EXIT
; because the HalEndSystemInterrupt has already been done,
; and must only be done ONCE per interrupt.
;
SPURIOUS_INTERRUPT_EXIT ; exit interrupt without eoi
align 4
Kcpi01:
SPIN_ON_SPINLOCK esi,<Kcpi00>
stdENDP _Cbus1PerfInterrupt
;++
;
; ULONG
; Cbus1SetTimeIncrement (
; IN ULONG DesiredIncrement
; )
;
; /*++
;
; Routine Description:
;
; This routine initializes the system time clock to generate an
; interrupt at every DesiredIncrement interval.
;
; Arguments:
;
; DesiredIncrement - desired interval between every timer tick in
; 100ns units.
;
; Return Value:
;
; The *REAL* time increment set - this can be different from what he
; requested due to hardware limitations - currently to keep the math
; simple, we limit the interval to between 1 and 32000 milliseconds,
; on millisecond boundaries (ie 1.3 milliseconds is rounded to 1
; millisecond).
;--
cPublicProc _Cbus1SetTimeIncrement,1
mov eax, [esp+4] ; caller's desired setting
xor edx, edx
mov ecx, 10000
div ecx ; round to milliseconds
cmp eax, MAXCLOCK_RATE_IN_MS ; desired > max?
jc short @f
mov eax, MAXCLOCK_RATE_IN_MS ; yes, use max
@@:
or eax, eax ; MS < min?
jnz short @f
inc eax ; yes, use min
@@:
mov CbusClockRateInMillis, eax ; set new rate in milliseconds
;
; inform this processor's local APIC hardware of the change.
; and then tell all the other processors to update theirs too...
;
stdCall _Cbus1ProgramClock
mov eax, Cbus1CurrentTimeIncrement
stdRET _Cbus1SetTimeIncrement
stdENDP _Cbus1SetTimeIncrement
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.
;
; Note spurious interrupts are always sent to the spurious interrupt IDT
; entry, and hence, no need to check for one here.
;
; See also Cbus1ClockInterruptPx() - it's used by the non-boot processors,
; since additional processors don't need to bump any global counters.
;
; Arguments:
;
; None
; Interrupt is disabled
;
; Return Value:
;
; must set up eax with the increment value, also leave ebp pointing at
; base of trap frame.
; Does not return, jumps directly to KeUpdateSystemTime, which returns
;
; Sets Irql = CLOCK2_LEVEL and dismisses the interrupt
;
;--
ENTER_DR_ASSIST Hck_a, Hck_t
cPublicProc _Cbus1ClockInterrupt ,0
;
; Save machine state in trap frame
; (esp) - base of trap frame
;
ENTER_INTERRUPT Hck_a, Hck_t
;
; 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>
POKE_LEDS eax, edx
;
; (esp) = OldIrql
; (esp+4) = Vector
; (esp+8) = base of trap frame
; (ebp) = address of trap frame
; (eax) = time increment
;
mov eax, Cbus1CurrentTimeIncrement
jmp _KeUpdateSystemTime@0
stdENDP _Cbus1ClockInterrupt
_TEXT ends
end