summaryrefslogtreecommitdiffstats
path: root/private/ntos/nthals/halws3/i386/w3stall.asm
diff options
context:
space:
mode:
Diffstat (limited to 'private/ntos/nthals/halws3/i386/w3stall.asm')
-rw-r--r--private/ntos/nthals/halws3/i386/w3stall.asm382
1 files changed, 382 insertions, 0 deletions
diff --git a/private/ntos/nthals/halws3/i386/w3stall.asm b/private/ntos/nthals/halws3/i386/w3stall.asm
new file mode 100644
index 000000000..588878a83
--- /dev/null
+++ b/private/ntos/nthals/halws3/i386/w3stall.asm
@@ -0,0 +1,382 @@
+
+ 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 (phil@sequent.com)
+;
+; 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