windows-nt/Source/XPSP1/NT/base/hals/halx86/i386/ixstall.asm

496 lines
14 KiB
NASM
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
title "Stall Execution Support"
;++
;
; Copyright (c) 1989 Microsoft Corporation
;
; Module Name:
;
; ixstall.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 delcared 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
; Created this module by moving routines from ixclock.asm to here.
;
;--
.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
.list
EXTRNP _DbgBreakPoint,0,IMPORT
EXTRNP _HalpAcquireCmosSpinLock ,0
EXTRNP _HalpReleaseCmosSpinLock ,0
;
; Constants used to initialize CMOS/Real Time Clock
;
D_INT032 EQU 8E00h ; access word for 386 ring 0 interrupt gate
RTCIRQ EQU 8 ; IRQ number for RTC interrupt
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 ;
_DATA SEGMENT DWORD PUBLIC 'DATA'
HalpStallCount dd 0
_DATA ends
INIT SEGMENT PARA PUBLIC 'CODE'
ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
page ,132
subttl "Initialize Stall Execution Counter"
;++
;
; VOID
; HalpInitializeStallExecution (
; IN CCHAR ProcessorNumber
; )
;
; Routine Description:
;
; This routine initialize the per Microsecond counter for
; KeStallExecutionProcessor
;
; Arguments:
;
; ProcessorNumber - Processor Number
;
; Return Value:
;
; None.
;
; Note:
;
; Current implementation assumes that all the processors share
; the same Real Time Clock. So, the dispatcher database lock is
; acquired before entering this routine to guarantee only one
; processor can access the routine.
;
;--
KiseInterruptCount equ [ebp-12] ; local variable
cPublicProc _HalpInitializeStallExecution ,1
ifndef NT_UP
;;
;; This function currently doesn't work from any processor but the
;; boot processor - for now stub out the others
;;
mov eax, PCR[PcPrcb]
cmp byte ptr [eax].PbNumber, 0
je @f
mov eax, HalpStallCount
mov PCR[PcStallScaleFactor], eax
stdRET _HalpInitializeStallExecution
@@:
endif
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 for every 125ms at
; IRQ 8.
;
cli ; make sure interrupts are disabled
;
; Get and save current 8259 masks
;
xor eax,eax
;
; Assume there is no third and fourth PICs
;
; Get interrupt Mask on PIC2
;
in al,PIC2_PORT1
shl eax, 8
;
; Get interrupt Mask on PIC1
;
in al,PIC1_PORT1
push eax ; save the masks
mov eax, NOT (( 1 SHL PIC_SLAVE_IRQ) + (1 SHL RTCIRQ))
; Mask all the irqs except irq 2 and 8
SET_8259_MASK ; Set 8259's int mask register
;
; Since RTC interrupt will come from IRQ 8, we need to
; Save 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] ; (ecx)->IDT
mov eax, (RTCIRQ+PRIMARY_VECTOR_BASE)
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.
;
push ecx ; (TOS) -> &IDT[HalProfileVector]
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
;
; 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
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 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
;
; 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
;
; Dismiss the interrupt.
;
mov al, OCW2_NON_SPECIFIC_EOI ; send non specific eoi to slave
out PIC2_PORT0, al
mov al, PIC2_EOI ; specific eoi to master for pic2 eoi
out PIC1_PORT0, al ; send irq2 specific eoi to master
xor eax, eax ; reset loop counter
iretd
kise25:
;
; ** temporary - check for incorrect KeStallExecutionProcessorLoopCount
;
if DBG
cmp eax, 0
jnz short kise30
stdCall _DbgBreakPoint
endif
; never return
;
; ** End temporay code
;
kise30:
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
kise40:
mov PCR[PcStallScaleFactor], eax
mov HalpStallCount, eax
;
; Reset return address to kexit
;
pop eax ; discard original return address
push offset FLAT:kexit ; return to kexit
mov eax, (HIGHEST_LEVEL_FOR_8259 - RTCIRQ)
;
; Shutdown periodic 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
;
; Dismiss the interrupt.
;
mov eax, RTCIRQ
mov al, OCW2_NON_SPECIFIC_EOI ; send non specific eoi to slave
out PIC2_PORT0, al
mov al, PIC2_EOI ; specific eoi to master for pic2 eoi
out PIC1_PORT0, al ; send irq2 specific eoi to master
and word ptr [esp+8], NOT 0200H ; Disable interrupt upon return
iretd
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
pop eax ; (eax) = origianl 8259 int masks
SET_8259_MASK
popfd ; restore caller's eflags
mov esp, ebp
pop ebp ; restore ebp
stdRET _HalpInitializeStallExecution
stdENDP _HalpInitializeStallExecution
cPublicProc _HalpRemoveFences
mov word ptr fence1, 0c98bh
stdRET _HalpRemoveFences
stdENDP _HalpRemoveFences
INIT ends
_TEXT SEGMENT PARA PUBLIC 'CODE'
ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
page ,132
subttl "Stall Execution"
;++
;
; VOID
; KeStallExecutionProcessor (
; IN ULONG MicroSeconds
; )
;
; Routine Description:
;
; This function stalls execution for the specified number of microseconds.
; KeStallExecutionProcessor
;
; Arguments:
;
; MicroSeconds - Supplies the number of microseconds that execution is to be
; stalled.
;
; Return Value:
;
; None.
;
;--
MicroSeconds equ [esp + 4]
cPublicProc _KeStallExecutionProcessor ,1
cPublicFpo 1, 0
;
; Issue a CPUID to implement a "fence"
;
push ebx ; cpuid uses eax, ebx, ecx, edx
xor eax, eax ; Processor zero
.586p
fence1: cpuid
.386p
pop ebx
mov ecx, MicroSeconds ; (ecx) = Microseconds
jecxz short kese10 ; return if no loop needed
mov eax, PCR[PcStallScaleFactor] ; get per microsecond
; loop count for the processor
mul ecx ; (eax) = desired loop count
if DBG
;
; Make sure we the loopcount is less than 4G and is not equal to zero
;
cmp edx, 0
jz short @f
int 3
@@: cmp eax,0
jnz short @f
int 3
@@:
endif
ALIGN 16
jmp kese05
ALIGN 16
kese05: sub eax, 1 ; (eax) = (eax) - 1
jnz short kese05
kese10:
stdRET _KeStallExecutionProcessor
stdENDP _KeStallExecutionProcessor
_TEXT ends
end