mirror of
https://github.com/reactos/reactos.git
synced 2025-01-12 09:07:54 +00:00
c424146e2c
svn path=/branches/cmake-bringup/; revision=48236
523 lines
10 KiB
ArmAsm
523 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
|