mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
382 lines
9.7 KiB
382 lines
9.7 KiB
|
|
title "Stall Execution Support"
|
|
;++
|
|
;
|
|
; Copyright (c) 1989 Microsoft Corporation
|
|
; Copyright (c) 1993 Sequent Computer Systems, Inc.
|
|
;
|
|
; Module Name:
|
|
;
|
|
; w3stall.asm
|
|
;
|
|
; Abstract:
|
|
;
|
|
; This module implements the code necessary to stall the processor
|
|
; for some specified period of time.
|
|
;
|
|
; Author:
|
|
;
|
|
; Phil Hochstetler ([email protected])
|
|
;
|
|
; Environment:
|
|
;
|
|
; Kernel mode only.
|
|
;
|
|
; Revision History:
|
|
;
|
|
;--
|
|
|
|
.386p
|
|
.xlist
|
|
include hal386.inc
|
|
include callconv.inc
|
|
include i386\kimacro.inc
|
|
include mac386.inc
|
|
include i386\apic.inc
|
|
include i386\ixcmos.inc
|
|
include i386\w3.inc
|
|
|
|
|
|
EXTRNP _DbgBreakPoint,0,IMPORT
|
|
extrn _HalpLocalUnitBase:DWORD
|
|
|
|
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:
|
|
;
|
|
; This routine is called from the HalInitSystem routine in w3hal.c.
|
|
; It is only called during Phase 0 init on P0
|
|
;
|
|
;--
|
|
|
|
;
|
|
; Local Variables - These are valid even in the Isr because we're the only thing
|
|
; running on this processor and no-one else will change the ebp
|
|
; register.
|
|
|
|
StallIDTPointer equ [ebp-6]
|
|
StallIDTArea equ [ebp-8]
|
|
StallInterruptCount equ [ebp-12]
|
|
StallLVTentry equ dword ptr [ebp-16]
|
|
StallDummyentry0 equ dword ptr [ebp-20]
|
|
StallDummyentry1 equ [ebp-24]
|
|
StallApicTpr equ dword ptr [ebp-28]
|
|
|
|
|
|
_TEXT SEGMENT DWORD PUBLIC 'CODE'
|
|
ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
|
|
|
|
cPublicProc _HalpInitializeStallExecution,1
|
|
|
|
push ebp ; save ebp
|
|
mov ebp, esp ; set up 28 bytes for local use
|
|
sub esp, 32
|
|
|
|
pushfd ; save caller's eflag
|
|
|
|
;
|
|
; --- For an APIC implementaion we use the APIC timer0 and install a
|
|
; --- Local Vector Table entry to point to a high priority
|
|
; --- vector for the Timer0 Interrupt. Then use the TPR in the local APIC
|
|
; --- to mask out every thing below it. To this end we've reserved a vector
|
|
; --- in the highest priority group (0F8) to be used here.
|
|
;
|
|
|
|
;
|
|
; Since APIC timer interrupt will come from APIC_STALL_VECTOR, we need to
|
|
; Save original APIC_STALL_VECTOR descriptor and set the descriptor
|
|
; to point to our own handler.
|
|
;
|
|
sidt fword ptr StallIDTArea ; get IDT address
|
|
mov edx, StallIDTPointer ; (edx)->IDT
|
|
|
|
;
|
|
; --- Save Original Descriptor on Stack
|
|
;
|
|
push dword ptr [edx+8*APIC_STALL_VECTOR]; (TOS) = orig. Vector
|
|
push dword ptr [edx+8*APIC_STALL_VECTOR + 4]
|
|
push edx ; (TOS) -> IDT
|
|
;
|
|
; --- Install our IDT entry
|
|
;
|
|
mov eax, offset FLAT:ApicTimer0Handler
|
|
mov word ptr [edx+8*APIC_STALL_VECTOR], ax ; Low half handler addr
|
|
mov word ptr [edx+8*APIC_STALL_VECTOR+2], KGDT_R0_CODE ; set up selector
|
|
mov word ptr [edx+8*APIC_STALL_VECTOR+4], D_INT032 ; 386 interrupt gate
|
|
shr eax, 16 ; (ax)=higher half of handler addr
|
|
mov word ptr [edx+8*APIC_STALL_VECTOR+6], ax
|
|
;
|
|
; --- Init. interrupt Flag
|
|
;
|
|
mov dword ptr StallinterruptCount, 0 ; set no interrupt yet
|
|
|
|
;
|
|
; Get the Local Vector Table Timer Zero entry and save it
|
|
;
|
|
mov edx, _HalpLocalUnitBase ; get the current TPR
|
|
mov eax, [edx+LU_TIMER_VECTOR] ; get Timer zero LVT
|
|
mov StallLVTentry, eax ; Save LVT
|
|
|
|
;
|
|
; --- Set the Inital Timer count
|
|
; --- Note: we will use TMBASE with No Divider
|
|
; --- which runs at 11 Mhz
|
|
;
|
|
|
|
mov eax,(PeriodInUsec*11) ; Set the initial TIMER0 count
|
|
mov [edx+LU_INITIAL_COUNT], eax
|
|
;
|
|
; Save then set the Local APIC's TPR to mask all interrupts except the
|
|
; highest priority group
|
|
;
|
|
|
|
mov eax, [edx+LU_TPR] ; get TPR
|
|
mov StallApicTpr, eax ; save TPR for later
|
|
|
|
;
|
|
; Set TPR (Priority of CPU) = TPR (VECTOR - 16). So that all interrupts
|
|
; in VECTOR's priority group will be allowed in.
|
|
;
|
|
mov eax, APIC_STALL_VECTOR-10H
|
|
mov [edx+LU_TPR], eax ; Write the new TPR
|
|
;
|
|
;
|
|
; --- Create Timer zero entry and store in Local Vector Table
|
|
; --- STARTING the clock
|
|
;
|
|
mov eax,(00040000H OR PERIODIC_TIMER)
|
|
or eax,(INTERRUPT_MOT_MASKED OR APIC_STALL_VECTOR)
|
|
@@:
|
|
test [edx+LU_TIMER_VECTOR], DELIVERY_PENDING
|
|
jnz @b
|
|
|
|
mov [edx+LU_TIMER_VECTOR], eax
|
|
;
|
|
; --- Now enable the interrupt and start the counter
|
|
;
|
|
|
|
xor eax, eax ; (eax) = 0, initialize loopcount
|
|
;
|
|
; --- ENABLE TIMER ZERO INTERRUPT
|
|
;
|
|
sti
|
|
;
|
|
; --- BEGIN SPIN Calibration LOOP
|
|
;
|
|
|
|
Stall10:
|
|
add eax, 1 ; increment the loopcount
|
|
jnz short Stall10
|
|
;
|
|
; Counter overflowed
|
|
;
|
|
|
|
stdCall _DbgBreakPoint
|
|
|
|
;++
|
|
;
|
|
; VOID
|
|
; ApicTimer0Handler(
|
|
; );
|
|
;
|
|
;Routine Description:
|
|
;
|
|
; APIC Timer zero interrupt Handler for Calibrating Spin Loop for
|
|
; KeStallExecution();
|
|
;
|
|
; 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.
|
|
;
|
|
;--
|
|
Public ApicTimer0Handler
|
|
|
|
ApicTimer0Handler:
|
|
|
|
inc dword ptr StallInterruptCount ; increment interrupt count
|
|
cmp dword ptr StallInterruptCount,1 ; Is this the first interrupt?
|
|
jnz Stall25 ; no, its the second go process it
|
|
|
|
pop eax ; get rid of original ret addr
|
|
push offset FLAT:Stall10 ; set new ret addr --> top of loop
|
|
|
|
;
|
|
; EOI the Local Apic, the value written is immaterial
|
|
;
|
|
|
|
mov eax, _HalpLocalUnitBase ; write to local unit EOI register
|
|
mov dword ptr [eax+LU_EOI], 0
|
|
;
|
|
xor eax, eax ; reset loop counter
|
|
;
|
|
iretd
|
|
;
|
|
; --- Process EAX for Spin Count
|
|
;
|
|
;
|
|
Stall25:
|
|
|
|
ifdef DBG
|
|
cmp eax, 0
|
|
jnz short Stall30
|
|
|
|
stdCall _DbgBreakPoint
|
|
endif
|
|
;
|
|
Stall30:
|
|
|
|
xor edx, edx ; (edx:eax) = dividend
|
|
mov ecx, PeriodInUSec; ; (ecx) = time spent in the loop
|
|
div ecx ; (eax) = loop count per microsecond
|
|
cmp edx, 0 ; Is remainder =0?
|
|
jz short Stall40 ; yes, go Stall40
|
|
inc eax ; increment loopcount by 1
|
|
;
|
|
Stall40:
|
|
movzx ecx, byte ptr [ebp+8] ; Current processor number
|
|
|
|
mov PCR[PcStallScaleFactor], eax
|
|
;
|
|
; Reset return address to kexit
|
|
;
|
|
|
|
pop eax ; discard original return address
|
|
push offset FLAT:kexit ; return to kexit
|
|
|
|
;
|
|
; EOI the Local Apic, the value written is immaterial
|
|
;
|
|
|
|
mov eax, _HalpLocalUnitBase ; write the local unit EOI register
|
|
mov dword ptr [eax+LU_EOI], 0
|
|
|
|
and word ptr [esp+8], NOT 0200H ; Disable interrupt upon return
|
|
;
|
|
iretd
|
|
;
|
|
;
|
|
; --- Calibration is Done Restore all values and exit
|
|
; --- Interrupts are disabled
|
|
;
|
|
kexit:
|
|
;
|
|
; Turn OFF TIMER ZERO
|
|
;
|
|
;
|
|
mov edx, _HalpLocalUnitBase
|
|
mov eax, StallLVTentry ; get Saved LVT
|
|
|
|
@@:
|
|
test [edx+LU_TIMER_VECTOR], DELIVERY_PENDING
|
|
jnz @b
|
|
|
|
mov [edx+LU_TIMER_VECTOR], eax
|
|
;
|
|
; --- Restore the interrupt vector we used
|
|
;
|
|
pop edx ; (edx)->IDT
|
|
pop [edx+8*APIC_STALL_VECTOR+4] ; higher half of desc
|
|
pop [edx+8*APIC_STALL_VECTOR] ; lower half of desc
|
|
|
|
;
|
|
; --- Restore the Local APIC's TPR
|
|
;
|
|
|
|
mov eax, StallApicTpr ; get the saved TPR
|
|
mov edx, _HalpLocalUnitBase ; write old value to TPR
|
|
mov [edx+LU_TPR], eax
|
|
|
|
|
|
sub esp, 32
|
|
popfd ; restore caller's eflags
|
|
mov esp, ebp
|
|
pop ebp ; restore ebp
|
|
|
|
stdRET _HalpInitializeStallExecution
|
|
|
|
stdENDP _HalpInitializeStallExecution
|
|
|
|
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
|
|
|
|
mov ecx, MicroSeconds ; (ecx) = Microseconds
|
|
jecxz short kese10 ; return if no loop needed
|
|
|
|
mov eax, PCR[PcStallScaleFactor] ; get per microsecond
|
|
|
|
mul ecx ; (eax) = desired loop count
|
|
|
|
ifdef 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 4
|
|
@@:
|
|
sub eax, 1 ; (eax) = (eax) - 1
|
|
jnz short @b
|
|
kese10:
|
|
stdRET _KeStallExecutionProcessor
|
|
|
|
stdENDP _KeStallExecutionProcessor
|
|
|
|
_TEXT ends
|
|
|
|
end
|