reactos/hal/halx86/generic/systimer.S
Amine Khaldi a00acb2a1b Sync with trunk head
svn path=/branches/header-work/; revision=46021
2010-03-09 00:19:55 +00:00

524 lines
10 KiB
ArmAsm

/*
* FILE: hal/halx86/generic/timer.S
* COPYRIGHT: See COPYING in the top level directory
* PURPOSE: System Timer Interrupt and Management
* PROGRAMMER: Alex Ionescu (alex@relsoft.net)
*/
/* INCLUDES ******************************************************************/
#include <asm.h>
.intel_syntax noprefix
/* GLOBALS *******************************************************************/
.globl _HalpPerfCounter
_HalpLastPerfCounterLow: .long 0
_HalpLastPerfCounterHigh: .long 0
_HalpPerfCounter:
_HalpPerfCounterLow: .long 0
_HalpPerfCounterHigh: .long 0
_HalpSystemHardwareFlags: .long 0
_UnhandledMsg:
.asciz "\n\x7\x7!!! Unhandled or Unexpected Code at line: %lx!!!\n"
/* FUNCTIONS *****************************************************************/
.global _HalpCalibrateStallExecution@0
.func HalpCalibrateStallExecution@0
_HalpCalibrateStallExecution@0:
/* Setup the stack frame */
push ebp
mov ebp, esp
sub esp, 12
/* Save EFLAGS and kill interrupts */
pushf
cli
/* Get the current interrupt mask on the PICs */
xor eax, eax
in al, 0xA1
shl eax, 8
in al, 0x21
/* Save it */
push eax
/* Now mask everything except the RTC and PIC 2 chain-interrupt */
mov eax, ~((1 << 2) | (1 << 8))
/* Program the PICs */
out 0x21, al
shr eax, 8
out 0xA1, al
/* Now get the IDT */
sidt [ebp-8]
mov ecx, [ebp-6]
/* Get the IDT entry for the RTC */
mov eax, 0x38
shl eax, 3
add ecx, eax
/* Save the original RTC ISR */
push [ecx]
push [ecx+4]
push ecx
/* Now load our new handler */
mov eax, offset OnlyOnePersonCanWriteHalCode
mov [ecx], ax
mov word ptr [ecx+2], KGDT_R0_CODE
mov word ptr [ecx+4], 0x8E00
shr eax, 16
mov [ecx+6], ax
/* Reset our counter */
mov dword ptr [ebp-12], 0
/* Acquire CMOS lock */
call _HalpAcquireSystemHardwareSpinLock@0
/* Now initialize register A on the CMOS */
mov ax, (0x2D << 8) | 0xA
out 0x70, al
jmp $+2
mov al, ah
out 0x71, al
jmp $+2
/* Read register B */
mov ax, 0xB
out 0x70, al
jmp $+2
in al, 0x71
jmp $+2
/* Don't touch the LastKnownGoodConfig hack */
and al, 1
mov ah, al
/* Enable the interrupt */
or ah, 0x42
/* Now write the register B */
mov al, 0xB
out 0x70, al
jmp $+2
mov al, ah
out 0x71, al
jmp $+2
/* Read register C */
mov al, 0xC
out 0x70, al
jmp $+2
in al, 0x71
jmp $+2
/* Read register D */
mov al, 0xD
out 0x70, al
jmp $+2
in al, 0x71
jmp $+2
/* Release CMOS lock */
mov dword ptr [ebp-12], 0
call _HalpReleaseCmosSpinLock@0
/* Initialize looper */
xor eax, eax
/* Align to 16 bytes */
.align 16
/* Enable interrupts! */
sti
jmp Looper
/* Align to 16 bytes */
.align 16
/* Subtract one count */
Looper:
sub eax, 1
jnz Looper
/* ASSERT: If we got here, then the RTC never fired */
call _DbgBreakPoint@0
jmp Looper
OnlyOnePersonCanWriteHalCode:
/*********************** THIS IS THE RTC HANDLER **************************/
/* Increment the interrupt count and check if this is the first one */
inc dword ptr [ebp-12]
cmp dword ptr [ebp-12], 1
jnz ComputeStall
/*
* It is the first one -- we'll ignore it, since it fires randomly!
* Get rid of the old return address and push the new one in (our looper)
*/
pop eax
push offset Looper
/* Acquire CMOS lock */
call _HalpAcquireSystemHardwareSpinLock@0
/* Now initialize register A on the CMOS */
mov ax, (0x2D << 8) | 0xA
out 0x70, al
jmp $+2
mov al, ah
out 0x71, al
jmp $+2
/* Read register B */
mov ax, 0xB
out 0x70, al
jmp $+2
in al, 0x71
jmp $+2
/* Don't touch the LastKnownGoodConfig hack */
and al, 1
mov ah, al
/* Enable the interrupt */
or ah, 0x42
/* Now write the register B */
mov al, 0xB
out 0x70, al
jmp $+2
mov al, ah
out 0x71, al
jmp $+2
/* Read register C */
mov al, 0xC
out 0x70, al
jmp $+2
in al, 0x71
jmp $+2
/* Read register D */
mov al, 0xD
out 0x70, al
jmp $+2
in al, 0x71
jmp $+2
/* Release CMOS lock */
call _HalpReleaseCmosSpinLock@0
/* Dismiss the interrupt */
mov al, 0x20
out 0xA0, al
mov al, 0x62
out 0x20, al
/* Reset the counter and return back to the looper */
xor eax, eax
iretd
/******************* THIS IS THE 2ND RTC HANDLER **************************/
ComputeStall:
/* Do the calculation */
neg eax
xor edx, edx
mov ecx, 125000 /* RTC fires every 125 ms */
div ecx
/* Is the remainder 0? */
cmp edx, 0
jz FoundFactor
/* Otherwise fix-up the loop count */
inc eax
FoundFactor:
/* Save the stall scale factor */
mov fs:[KPCR_STALL_SCALE_FACTOR], eax
/* Prepare for interrupt return */
pop eax
push offset AndItsNotYou
mov eax, 0x13
/* Acquire CMOS lock */
call _HalpAcquireSystemHardwareSpinLock@0
/* Now initialize register A on the CMOS */
mov ax, (0x2D << 8) | 0xA
out 0x70, al
jmp $+2
mov al, ah
out 0x71, al
jmp $+2
/* Read register B */
mov ax, 0xB
out 0x70, al
jmp $+2
in al, 0x71
jmp $+2
/* Don't touch the LastKnownGoodConfig hack */
and al, 1
mov ah, al
/* Disable the interrupt */
or ah, 0x2
/* Now write the register B */
mov al, 0xB
out 0x70, al
jmp $+2
mov al, ah
out 0x71, al
jmp $+2
/* Read register C */
mov al, 0xC
out 0x70, al
jmp $+2
in al, 0x71
jmp $+2
/* Release CMOS lock */
call _HalpReleaseCmosSpinLock@0
/* Dismiss the interrupt */
mov al, 0x20
out 0xA0, al
mov al, 0x62
out 0x20, al
/* Disable interrupts on return */
and word ptr [esp+8], ~EFLAGS_INTERRUPT_MASK
iretd
/************************* WE ARE BACK FROM RTC ***************************/
AndItsNotYou:
/* Restore the IDT */
pop ecx
pop [ecx+4]
pop [ecx]
/* Restore the mask */
pop eax
out 0x21, al
shr eax, 8
out 0xA1, al
/* Restore EFLAGS */
popf
/* Restore stack and return */
mov esp, ebp
pop ebp
ret
.endfunc
#ifndef _MINIHAL_
.globl _KeStallExecutionProcessor@4
.func KeStallExecutionProcessor@4
_KeStallExecutionProcessor@4:
/* Get the number of microseconds required */
mov ecx, [esp+4]
jecxz Done
/* Multiply by the stall factor */
mov eax, fs:[KPCR_STALL_SCALE_FACTOR]
mul ecx
/* Align to 16 bytes */
.align 16
/* Jump to subtraction loop */
jmp SubtractLoop
/* Align to 16 bytes */
.align 16
/* Subtract one count */
SubtractLoop:
sub eax, 1
jnz SubtractLoop
Done:
/* Return */
ret 4
.endfunc
#endif
.global _KeQueryPerformanceCounter@4
.func KeQueryPerformanceCounter@4
_KeQueryPerformanceCounter@4:
/* Check if we were called too early */
cmp dword ptr _HalpCurrentRollOver, 0
je NoCount
/* Save volatiles */
push ebx
push esi
LoopPreInt:
/* Disable interrupts */
pushf
cli
LoopPostInt:
/* Get the current value */
mov ebx, _HalpPerfCounterLow
mov esi, _HalpPerfCounterHigh
/* Read 8254 timer */
mov al, 0
out 0x43, al
jmp $+2
in al, 0x40
jmp $+2
movzx ecx, al
in al, 0x40
mov ch, al
/* Enable interrupts and do a short wait */
popf
nop
jmp $+2
/* Disable them again */
pushf
cli
/* Get the counter value again */
mov eax, _HalpPerfCounterLow
mov edx, _HalpPerfCounterHigh
/* Check if someone updated the counter */
cmp eax, ebx
jnz LoopPostInt
cmp edx, esi
jnz LoopPostInt
/* Check if the current 8254 value causes rollover */
neg ecx
add ecx, _HalpCurrentRollOver
jnb DoRollOver
SetSum:
/* Calculate the sum */
add eax, ecx
adc edx, 0
/* Check if we're above or below the last high value */
cmp edx, _HalpLastPerfCounterHigh
jb short BelowHigh
jnz short BelowLow
/* Check if we're above or below the last low value */
cmp eax, _HalpLastPerfCounterLow
jb BelowHigh
BelowLow:
/* Update the last value and bring back interrupts */
mov _HalpLastPerfCounterLow, eax
mov _HalpLastPerfCounterHigh, edx
popf
/* Check if caller wants frequency */
cmp dword ptr [esp+12], 0
jz ReturnNoFreq
/* Save hard-coded frequency */
mov ecx, dword ptr [esp+12]
mov dword ptr [ecx], 1193182
mov dword ptr [ecx+4], 0
ReturnNoFreq:
/* Restore volatiles */
pop esi
pop ebx
ret 4
NoCount:
/* Return empty, called too soon */
mov eax, 0
mov edx, 0
ret 4
DoRollOver:
/* We might have an incoming interrupt, save EFLAGS and reset rollover */
mov esi, [esp]
mov ecx, _HalpCurrentRollOver
popf
/* Check if interrupts were enabled and try again */
test esi, EFLAGS_INTERRUPT_MASK
jnz LoopPreInt
/* They're not, continue where we left */
pushf
jmp SetSum
BelowHigh:
/* Get the last counter values */
mov ebx, _HalpLastPerfCounterLow
mov esi, _HalpLastPerfCounterHigh
/* Check if the previous value was 0 and go back if yes */
mov ecx, ebx
or ecx, esi
jz BelowLow
/* Make sure that the count is still valid */
sub ebx, eax
sbb esi, edx
jnz InvalidCount
cmp ebx, _HalpCurrentRollOver
jg InvalidCount
/* Fixup the count with the last known value */
sub eax, ebx
sbb edx, esi
/* We might have an incoming interrupt, save EFLAGS */
mov ecx, [esp]
popf
/* Check if interrupts were enabled and try again */
test ecx, EFLAGS_INTERRUPT_MASK
jnz LoopPreInt
/* They're not, continue where we left */
pushf
jmp BelowLow
InvalidCount:
popf
xor eax, eax
mov _HalpLastPerfCounterLow, eax
mov _HalpLastPerfCounterHigh, eax
jmp LoopPreInt
.endfunc