458 lines
13 KiB
NASM
458 lines
13 KiB
NASM
title "Interval Clock Interrupt"
|
|
;++
|
|
;
|
|
; Copyright (c) 1989 Microsoft Corporation
|
|
;
|
|
; Module Name:
|
|
;
|
|
; spprofil.asm
|
|
;
|
|
; Abstract:
|
|
;
|
|
; This module implements the code necessary to initialize,
|
|
; field and process the profile interrupt.
|
|
;
|
|
; Author:
|
|
;
|
|
; Shie-Lin Tzong (shielint) 12-Jan-1990
|
|
;
|
|
; Environment:
|
|
;
|
|
; Kernel mode only.
|
|
;
|
|
; Revision History:
|
|
;
|
|
; bryanwi 20-Sep-90
|
|
;
|
|
; Add KiSetProfileInterval, KiStartProfileInterrupt,
|
|
; KiStopProfileInterrupt procedures.
|
|
; KiProfileInterrupt ISR.
|
|
; KiProfileList, KiProfileLock are delcared here.
|
|
;
|
|
; shielint 10-Dec-90
|
|
; Add performance counter support.
|
|
; Move system clock to irq8, ie we now use RTC to generate system
|
|
; clock. Performance count and Profile use timer 1 counter 0.
|
|
; The interval of the irq0 interrupt can be changed by
|
|
; KiSetProfileInterval. Performance counter does not care about the
|
|
; interval of the interrupt as long as it knows the rollover count.
|
|
; Note: Currently I implemented 1 performance counter for the whole
|
|
; i386 NT.
|
|
;
|
|
; John Vert (jvert) 11-Jul-1991
|
|
; Moved from ke\i386 to hal\i386. Removed non-HAL stuff
|
|
;
|
|
; shie-lin tzong (shielint) 13-March-92
|
|
; Move System clock back to irq0 and use RTC (irq8) to generate
|
|
; profile interrupt. Performance counter and system clock use time1
|
|
; counter 0 of 8254.
|
|
;
|
|
;--
|
|
|
|
.386p
|
|
.xlist
|
|
include hal386.inc
|
|
include callconv.inc ; calling convention macros
|
|
include i386\kimacro.inc
|
|
include mac386.inc
|
|
include i386\ix8259.inc
|
|
include i386\ixcmos.inc
|
|
.list
|
|
|
|
EXTRNP _DbgBreakPoint,0,IMPORT
|
|
EXTRNP _KeProfileInterrupt,1,IMPORT
|
|
EXTRNP Kei386EoiHelper,0,IMPORT
|
|
EXTRNP _HalEndSystemInterrupt,2
|
|
EXTRNP _HalBeginSystemInterrupt,3
|
|
EXTRNP _HalpAcquireCmosSpinLock ,0
|
|
EXTRNP _HalpReleaseCmosSpinLock ,0
|
|
|
|
;
|
|
; Constants used to initialize CMOS/Real Time Clock
|
|
;
|
|
|
|
D_INT032 EQU 8E00h ; access word for 386 ring 0 interrupt gate
|
|
REGISTER_B_ENABLE_PERIODIC_INTERRUPT EQU 01000010B
|
|
; RT/CMOS Register 'B' Init byte
|
|
; Values for byte shown are
|
|
; Bit 7 = Update inhibit
|
|
; Bit 6 = Periodic interrupt enable
|
|
; Bit 5 = Alarm interrupt disable
|
|
; Bit 4 = Update interrupt disable
|
|
; Bit 3 = Square wave disable
|
|
; Bit 2 = BCD data format
|
|
; Bit 1 = 24 hour time mode
|
|
; Bit 0 = Daylight Savings disable
|
|
|
|
REGISTER_B_DISABLE_PERIODIC_INTERRUPT EQU 00000010B
|
|
|
|
_TEXT SEGMENT DWORD PUBLIC 'DATA'
|
|
|
|
align 4
|
|
ProfileIntervalTable dd 1221 ; unit = 100 ns
|
|
dd 2441
|
|
dd 4883
|
|
dd 9766
|
|
dd 19531
|
|
dd 39063
|
|
dd 78125
|
|
dd 156250
|
|
dd 312500
|
|
dd 625000
|
|
dd 1250000
|
|
dd 2500000
|
|
dd 5000000
|
|
dd 5000000 OR 80000000H
|
|
|
|
ProfileIntervalInitTable db 00100011B
|
|
db 00100100B
|
|
db 00100101B
|
|
db 00100110B
|
|
db 00100111B
|
|
db 00101000B
|
|
db 00101001B
|
|
db 00101010B
|
|
db 00101011B
|
|
db 00101100B
|
|
db 00101101B
|
|
db 00101110B
|
|
db 00101111B
|
|
db 00101111B
|
|
|
|
;
|
|
; HALs wishing to reuse the code in this module should set the HAL
|
|
; global variable IxProfileVector to their profile vector.
|
|
;
|
|
public _IxProfileVector
|
|
_IxProfileVector dd PROFILE_VECTOR
|
|
|
|
_TEXT ends
|
|
|
|
_DATA SEGMENT DWORD PUBLIC 'DATA'
|
|
|
|
RegisterAProfileValue db 00101000B ; default interval = 3.90625 ms
|
|
|
|
;
|
|
; The following array stores the per microsecond loop count for each
|
|
; central processor.
|
|
;
|
|
|
|
HalpProfileInterval dd -1
|
|
HalpProfilingStopped dd 1
|
|
|
|
_DATA ends
|
|
|
|
|
|
_TEXT SEGMENT DWORD PUBLIC 'CODE'
|
|
ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
|
|
|
|
;++
|
|
;
|
|
; HalStartProfileInterrupt(
|
|
; IN ULONG Reserved
|
|
; );
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; What we do here is change the interrupt
|
|
; rate from the slowest thing we can get away with to the value
|
|
; that's been KeSetProfileInterval
|
|
;
|
|
; All processors will run this routine, but it doesn't hurt to have
|
|
; each one reinitialize the CMOS, since none of them will be let go
|
|
; from the stall until they all finish.
|
|
;
|
|
;--
|
|
|
|
cPublicProc _HalStartProfileInterrupt ,1
|
|
|
|
;
|
|
; On the SystemPro there is only one profile device, so starting/stopping
|
|
; is only done from one processor.
|
|
;
|
|
; Note: This code uses PbNumber so it doesn't touch any SystemPro specific
|
|
; PCR value (so the code can be re-used by other hals)
|
|
;
|
|
|
|
mov eax, PCR[PcPrcb]
|
|
cmp byte ptr [eax].PbNumber, 0
|
|
jne epi_exit
|
|
|
|
|
|
; Mark profiling as active
|
|
;
|
|
|
|
mov HalpProfilingStopped, 0
|
|
|
|
;
|
|
; Set the interrupt rate to what is actually needed
|
|
;
|
|
stdCall _HalpAcquireCmosSpinLock ; intr disabled
|
|
|
|
mov al, RegisterAProfileValue
|
|
shl ax, 8
|
|
mov al, 0AH ; Register A
|
|
CMOS_WRITE ; Initialize it
|
|
;
|
|
; Don't clobber the Daylight Savings Time bit in register B, because we
|
|
; stash the LastKnownGood "environment variable" there.
|
|
;
|
|
mov ax, 0bh
|
|
CMOS_READ
|
|
and al, 1
|
|
mov ah, al
|
|
or ah, REGISTER_B_ENABLE_PERIODIC_INTERRUPT
|
|
mov al, 0bh
|
|
CMOS_WRITE ; Initialize it
|
|
mov al,0CH ; Register C
|
|
CMOS_READ ; Read to initialize
|
|
mov al,0DH ; Register D
|
|
CMOS_READ ; Read to initialize
|
|
|
|
stdCall _HalpReleaseCmosSpinLock
|
|
epi_exit:
|
|
stdRET _HalStartProfileInterrupt
|
|
|
|
stdENDP _HalStartProfileInterrupt
|
|
|
|
|
|
|
|
;++
|
|
;
|
|
; HalStopProfileInterrupt(
|
|
; IN ULONG Reserved
|
|
; );
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; What we do here is change the interrupt
|
|
; rate from the high profiling rate to the slowest thing we
|
|
; can get away with for PerformanceCounter rollover notification.
|
|
;
|
|
;--
|
|
|
|
cPublicProc _HalStopProfileInterrupt ,1
|
|
|
|
;
|
|
; On the SystemPro there is only one profile device, so starting/stopping
|
|
; is only done from one processor.
|
|
;
|
|
; Note: This code uses PbNumber so it doesn't touch any SystemPro specific
|
|
; PCR value (so the code can be re-used by other hals)
|
|
;
|
|
|
|
mov eax, PCR[PcPrcb]
|
|
cmp byte ptr [eax].PbNumber, 0
|
|
jne dpi_exit
|
|
|
|
;
|
|
; Turn off profiling hit computation and profile interrupt
|
|
;
|
|
|
|
;
|
|
; Don't clobber the Daylight Savings Time bit in register B, because we
|
|
; stash the LastKnownGood "environment variable" there.
|
|
|
|
stdCall _HalpAcquireCmosSpinLock ; intr disabled
|
|
mov ax, 0bh
|
|
CMOS_READ
|
|
and al, 1
|
|
mov ah, al
|
|
or ah, REGISTER_B_DISABLE_PERIODIC_INTERRUPT
|
|
mov al, 0bh
|
|
CMOS_WRITE ; Initialize it
|
|
mov al,0CH ; Register C
|
|
CMOS_READ ; dismiss pending profiling interrupt
|
|
mov HalpProfilingStopped, 1
|
|
stdCall _HalpReleaseCmosSpinLock
|
|
dpi_exit:
|
|
stdRET _HalStopProfileInterrupt
|
|
|
|
stdENDP _HalStopProfileInterrupt
|
|
|
|
;++
|
|
; ULONG
|
|
; HalSetProfileInterval (
|
|
; ULONG Interval
|
|
; );
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; This procedure sets the interrupt rate (and thus the sampling
|
|
; interval) for the profiling interrupt.
|
|
;
|
|
; If profiling is active (KiProfilingStopped == 0) the actual
|
|
; hardware interrupt rate will be set. Otherwise, a simple
|
|
; rate validation computation is done.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; (TOS+4) - Interval in 100ns unit.
|
|
;
|
|
; Return Value:
|
|
;
|
|
; Interval actually used by system.
|
|
;--
|
|
|
|
cPublicProc _HalSetProfileInterval ,1
|
|
|
|
mov edx, [esp+4] ; [edx] = interval in 100ns unit
|
|
and edx, 7FFFFFFFh ; Remove highest bit.
|
|
mov ecx, 0 ; index = 0
|
|
|
|
Hspi00:
|
|
mov eax, ProfileIntervalTable[ecx * 4]
|
|
cmp edx, eax ; if request interval < suport interval
|
|
jbe short Hspi10 ; if be, find supported interval
|
|
inc ecx
|
|
jmp short Hspi00
|
|
|
|
Hspi10:
|
|
and eax, 7FFFFFFFh ; remove highest bit from supported interval
|
|
jecxz short Hspi20 ; If first entry then use it
|
|
|
|
push esi ; See which is closer to requested
|
|
mov esi, eax ; rate - current entry, or preceeding
|
|
sub esi, edx
|
|
|
|
sub edx, ProfileIntervalTable[ecx * 4 - 4]
|
|
cmp esi, edx
|
|
pop esi
|
|
jc short Hspi20
|
|
|
|
dec ecx ; use preceeding entry
|
|
mov eax, ProfileIntervalTable[ecx * 4]
|
|
|
|
Hspi20:
|
|
push eax ; save interval value
|
|
mov al, ProfileIntervalInitTable[ecx]
|
|
mov RegisterAProfileValue, al
|
|
test HalpProfilingStopped,-1
|
|
jnz short Hspi90
|
|
|
|
stdCall _HalStartProfileInterrupt,<0> ; Re-start profile interrupt
|
|
; with the new interval
|
|
|
|
Hspi90: pop eax
|
|
stdRET _HalSetProfileInterval ; (eax) = cReturn interval
|
|
|
|
stdENDP _HalSetProfileInterval
|
|
|
|
page ,132
|
|
subttl "System Profile Interrupt"
|
|
;++
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; This routine is entered as the result of a profile interrupt.
|
|
; Its function is to dismiss the interrupt, raise system Irql to
|
|
; PROFILE_LEVEL and transfer control to
|
|
; the standard system routine to process any active profiles.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; None
|
|
; Interrupt is disabled
|
|
;
|
|
; Return Value:
|
|
;
|
|
; Does not return, jumps directly to KeProfileInterrupt, which returns
|
|
;
|
|
; Sets Irql = PROFILE_LEVEL and dismisses the interrupt
|
|
;
|
|
;--
|
|
ENTER_DR_ASSIST Hpi_a, Hpi_t
|
|
|
|
cPublicProc _HalpProfileInterrupt ,0
|
|
|
|
;
|
|
; Save machine state in trap frame
|
|
;
|
|
|
|
ENTER_INTERRUPT Hpi_a, Hpi_t
|
|
|
|
;
|
|
; (esp) - base of trap frame
|
|
;
|
|
; HalBeginSystemInterrupt must be called before any sti's
|
|
;
|
|
;
|
|
|
|
push _IxProfileVector
|
|
sub esp, 4 ; allocate space to save OldIrql
|
|
stdCall _HalBeginSystemInterrupt, <PROFILE_LEVEL,_IxProfileVector,esp>
|
|
|
|
or al,al ; check for spurious interrupt
|
|
jz Hpi100
|
|
|
|
;
|
|
; If profiling not enabled, then don't ack device or count this interrupt.
|
|
; (this occurs during bootup when other processors sync PcStallScaleFactor)
|
|
;
|
|
cmp HalpProfilingStopped,0
|
|
jne short Hpi90
|
|
|
|
;
|
|
; On the SystemPro there is only one profile device, so the CMOS is only
|
|
; EOIed once.
|
|
;
|
|
; Note: This code uses PbNumber so it doesn't touch any SystemPro specific
|
|
; PCR value (so the code can be re-used by other hals)
|
|
;
|
|
mov eax, PCR[PcPrcb]
|
|
cmp byte ptr [eax].PbNumber, 0
|
|
jne short _HalpProfileInterrupt2ndEntry@0
|
|
|
|
|
|
;
|
|
; This is the RTC interrupt, so we have to clear the
|
|
; interrupt flag on the RTC.
|
|
;
|
|
stdCall _HalpAcquireCmosSpinLock
|
|
|
|
;
|
|
; clear interrupt flag on RTC by banging on the CMOS. On some systems this
|
|
; doesn't work the first time we do it, so we do it twice. It is rumored that
|
|
; some machines require more than this, but that hasn't been observed with NT.
|
|
;
|
|
|
|
mov al,0CH ; Register C
|
|
CMOS_READ ; Read to initialize
|
|
mov al,0CH ; Register C
|
|
CMOS_READ ; Read to initialize
|
|
if DBG
|
|
Hpi10: test al, 80h
|
|
jz short Hpi15
|
|
mov al,0CH ; Register C
|
|
CMOS_READ ; Read to initialize
|
|
jmp short Hpi10
|
|
Hpi15:
|
|
endif ; DBG
|
|
|
|
stdCall _HalpReleaseCmosSpinLock
|
|
|
|
; This entry point is provided for symmetric multiprocessor HALs.
|
|
; Since it only makes sense for one processor to clear the CMOS,
|
|
; all other processors can instead jmp into this entry point.
|
|
;
|
|
|
|
align 4
|
|
public _HalpProfileInterrupt2ndEntry@0
|
|
_HalpProfileInterrupt2ndEntry@0:
|
|
|
|
stdCall _KeProfileInterrupt,<ebp> ; (ebp) = trap frame
|
|
|
|
Hpi90:
|
|
INTERRUPT_EXIT
|
|
|
|
Hpi100:
|
|
add esp, 8 ; spurious, no EndOfInterrupt
|
|
SPURIOUS_INTERRUPT_EXIT ; exit interrupt without eoi
|
|
|
|
stdENDP _HalpProfileInterrupt
|
|
|
|
|
|
_TEXT ends
|
|
|
|
end
|