496 lines
14 KiB
NASM
496 lines
14 KiB
NASM
|
|
||
|
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
|