diff options
Diffstat (limited to 'private/ntos/nthals/halws3/i386/w3stall.asm')
-rw-r--r-- | private/ntos/nthals/halws3/i386/w3stall.asm | 382 |
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 |