diff options
Diffstat (limited to 'private/ntos/nthals/halcbus/i386/cb1stall.asm')
-rw-r--r-- | private/ntos/nthals/halcbus/i386/cb1stall.asm | 932 |
1 files changed, 932 insertions, 0 deletions
diff --git a/private/ntos/nthals/halcbus/i386/cb1stall.asm b/private/ntos/nthals/halcbus/i386/cb1stall.asm new file mode 100644 index 000000000..719627aef --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/cb1stall.asm @@ -0,0 +1,932 @@ + 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 |