2020-09-30 17:12:29 +02:00

616 lines
18 KiB
NASM

;++
;
;Copyright (c) 1991-1993 Microsoft Corporation
;Copyright (c) 1992, 1993 Wyse Technology
;
;Module Name:
;
; wysysint.asm
;
;Abstract:
;
; This module implements the HAL routines to enable/disable system
; interrupts, for the MP Wyse7000i implementation
;
;Author:
;
; John Vert (jvert) 22-Jul-1991
;
;Environment:
;
; Kernel Mode
;
;Revision History:
;
;--
.386p
.xlist
include hal386.inc
include callconv.inc ; calling convention macros
include i386\ix8259.inc
include i386\kimacro.inc
include mac386.inc
include i386\wy7000mp.inc
.list
EXTRNP _KeBugCheck,1,IMPORT
extrn WriteMyCpuReg:NEAR
EXTRNP _KeLowerIrql,1
extrn _HalpVectorToIRQL:BYTE
extrn _HalpIRQLtoVector:BYTE
extrn _HalpIRQLtoCPL:BYTE
extrn _HalpLocalInts:DWORD
;
; Constants used to initialize CMOS/Real Time Clock
;
CMOS_CONTROL_PORT EQU 70h ; command port for cmos
CMOS_DATA_PORT EQU 71h ; cmos data port
;
; Macros to Read/Write/Reset CMOS to initialize RTC
;
; CMOS_READ
;
; Description: This macro read a byte from the CMOS register specified
; in (AL).
;
; Parameter: (AL) = address/register to read
; Return: (AL) = data
;
; NOTE: IODelay's are not needed on EISA machines.
CMOS_READ MACRO
OUT CMOS_CONTROL_PORT,al ; ADDRESS LOCATION AND DISABLE NMI
; IODelay ; I/O DELAY
IN AL,CMOS_DATA_PORT ; READ IN REQUESTED CMOS DATA
; IODelay ; I/O DELAY
ENDM
_DATA SEGMENT DWORD PUBLIC 'DATA'
align dword
public _HalpICUlock ;spinlock for rebroadcasting timer & global IPI's
_HalpICUlock dd 0
public _Halp8259Lock ;spinlock for accessing 8259 mask registers
_Halp8259Lock dd 0
public _i8259_IMR ;global 8259 interrupt mask
_i8259_IMR dd not (1 shl PIC_SLAVE_IRQ)
public _i8259_ISR ;global 8259 interrupts in services
_i8259_ISR dd 0
public _Halp8259Counts ;count of cpu's attached to each 8259 interrupt
_Halp8259Counts db 16 dup(0)
;
; HalDismissSystemInterrupt does an indirect jump through this table so it
; can quickly execute specific code for different interrupts.
;
public HalpSpecialDismissTable
HalpSpecialDismissTable label dword
dd offset FLAT:HalpDismissNormal ; irq 0
dd offset FLAT:HalpDismissNormal ; irq 1
dd offset FLAT:HalpDismissSpurious ; irq 2
dd offset FLAT:HalpDismissNormal ; irq 3
dd offset FLAT:HalpDismissNormal ; irq 4
dd offset FLAT:HalpDismissNormal ; irq 5
dd offset FLAT:HalpDismissNormal ; irq 6
dd offset FLAT:HalpDismissIrq07 ; irq 7
dd offset FLAT:HalpDismissNormal ; irq 8
dd offset FLAT:HalpDismissNormal ; irq 9
dd offset FLAT:HalpDismissNormal ; irq A
dd offset FLAT:HalpDismissNormal ; irq B
dd offset FLAT:HalpDismissNormal ; irq C
dd offset FLAT:HalpDismissNormal ; irq D
dd offset FLAT:HalpDismissNormal ; irq E
dd offset FLAT:HalpDismissIrq0f ; irq F
dd offset FLAT:HalpDismissSpurious ; irq 10
dd offset FLAT:HalpDismissIPIlevel ; irq 11
dd offset FLAT:HalpDismissIPIlevel ; irq 12
dd offset FLAT:HalpDismissIPIlevel ; irq 13
dd offset FLAT:HalpDismissIPIlevel ; irq 14
dd offset FLAT:HalpDismissIPIlevel ; irq 15
dd offset FLAT:HalpDismissIPIlevel ; irq 16
dd offset FLAT:HalpDismissIPIlevel ; irq 17
dd offset FLAT:HalpDismissSpurious ; irq 18
dd offset FLAT:HalpDismissSpurious ; irq 19
dd offset FLAT:HalpDismissSpurious ; irq 1A
dd offset FLAT:HalpDismissSpurious ; irq 1B
dd offset FLAT:HalpDismissSpurious ; irq 1C
dd offset FLAT:HalpDismissSpurious ; irq 1D
dd offset FLAT:HalpDismissSpurious ; irq 1E
dd offset FLAT:HalpDismissSpurious ; irq 1F
dd offset FLAT:HalpDismissSpurious ; irq 20
dd offset FLAT:HalpDismissSpurious ; irq 21
dd offset FLAT:HalpDismissSpurious ; irq 22
dd offset FLAT:HalpDismissSpurious ; irq 23
_DATA ENDS
_TEXT SEGMENT DWORD PUBLIC 'CODE'
ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
;++
;BOOLEAN
;HalBeginSystemInterrupt(
; IN KIRQL Irql
; IN CCHAR Vector,
; OUT PKIRQL OldIrql
; )
;
;
;
;Routine Description:
;
; This routine is used to dismiss the specified vector number. It is called
; before any interrupt service routine code is executed.
;
; N.B. This routine does NOT preserve EAX or EBX
;
;Arguments:
;
; Irql - Supplies the IRQL to raise to
;
; Vector - Supplies the vector of the interrupt to be dismissed
;
; OldIrql- Location to return OldIrql
;
;
;Return Value:
;
; FALSE - Interrupt is spurious and should be ignored
;
; TRUE - Interrupt successfully dismissed and Irql raised.
;
;--
align dword
HbsiIrql equ byte ptr [esp+4+8]
HbsiVector equ byte ptr [esp+8+8]
HbsiOldIrql equ dword ptr [esp+12+8]
cPublicProc _HalBeginSystemInterrupt ,3
push ecx
push edx
mov ebx, (-PRIMARY_VECTOR_BASE) and 0FFh
add bl, HbsiVector ; (ebx) = 8259 IRQ #
if DBG
cmp ebx, 23h
jbe hbsi00
int 3
hbsi00:
endif
xor ecx, ecx
mov cl, _HalpVectorToIRQL[ebx] ; get h/w Irql of interrupt
cmp cl, Fs:PcIrql ; int after raise Irql?
jbe short HalpIntBelowIRQL ; jump if it is
jmp HalpSpecialDismissTable[ebx*4]
HalpIntBelowIRQL:
xor eax, eax
mov al, Fs:PcIrql
mov dx, My+CpuPriortyLevel
mov Fs:PcHal.pchHwIrql, al
mov al, _HalpIRQLtoCPL[eax]
out dx, ax
bt _HalpLocalInts, ecx ; EISA or Local interrupt?
jnc short HalpRebroadcastEISAint ; jump if EISA interrupt
cmp cl, IPI_LEVEL ; IPI's are easy
jne HalpEmulateClockOrGlobal ; jmp if not an IPI
mov cl, byte ptr fs:PcHal.pchPrSlot ; IPIs get resent to
or cl, ICU_IPI_SLOT ; the current processor
jmp short HalpRebroadcastIPI
HalpRebroadcastEISAint:
mov cl, _HalpIRQLtoCPL[ecx] ; get hw interrupt level
add cl, ICU_XMT_INT_SND ; make ICU command
HalpRebroadcastIPI:
mov dx, My+CpuIntCmd ; point to ICU command register
@@: in ax, dx
test eax, ICU_CMD_BUSY
jnz short @B ; wait for ICU not busy
xchg eax, ecx ; get ICU command
out dx, ax ; rebroadcast EISA interrupt
jmp HalpDismissSpurious ; clear ICU in service bit
HalpEmulateClockOrGlobal:
; The following code resends a clock or global interrupt by mapping the
; appropriate ICU_LIPTR value to zero and then broadcasting the level.
; This must be spinlocked since it will not work it two CPUs try it at
; the same time.
cmp cl, CLOCK2_LEVEL ; is this CLOCK or GLOBAL?
mov cl, _HalpIRQLtoCPL[ecx] ; (get this int's level #)
mov ax, not lipTimer ; (assume timer interrupt)
je short @F ; jump if timer interrupt
mov ax, not (lipGlobal+lipSerial) ; it is global interrupt
@@: and ax, fs:word ptr PcHal.pchCurLiptr
push eax ; save temp LIPTR
lea eax, _HalpICUlock ; Serialize access to
Hec10: ACQUIRE_SPINLOCK eax, HecSpin ; remappings
push ICU_LIPTR ; Point global interrupt to
call WriteMyCpuReg ; make it a clock interrupt
mov dx, My+CpuIntCmd ; wait for ICU not busy
@@: in ax, dx
test eax, ICU_CMD_BUSY
jnz short @B
xchg eax, ecx ; get int level back
add al, ICU_XMT_INT_SND ; make send level command
out dx, ax ; to make to clock interrupt
@@: in ax, dx ; wait for ICU not busy
test eax, ICU_CMD_BUSY ; before resetting GlobalIpi
jnz short @B ; value
push fs:PcHal.pchCurLiptr
push ICU_LIPTR ; Put GlobalIpi back to
call WriteMyCpuReg ; normal setting
lea eax, _HalpICUlock
RELEASE_SPINLOCK eax
jmp HalpDismissSpurious
HecSpin:
SPIN_ON_SPINLOCK eax, Hec10
HalpDismissSpinF:
SPIN_ON_SPINLOCK eax, Hbsi10
align dword
HalpDismissIrq0f:
;
; Check to see if this is a spurious interrupt
;
lea eax, _Halp8259Lock
Hbsi10: ACQUIRE_SPINLOCK eax, HalpDismissSpinF
mov al, OCW3_READ_ISR ; tell 8259 we want to read ISR
out PIC2_PORT0, al
; IODelay ; delay
in al, PIC2_PORT0 ; (al) = content of PIC 1 ISR
test al, 10000000B ; Is In-Service register set?
jnz HalpDismissNormal2 ; No, this is NOT a spurious int,
; go do the normal interrupt stuff
;
; This is a spurious interrupt.
; Because the slave PIC is cascaded to irq2 of master PIC, we need to
; dismiss the interupt on master PIC's irq2.
;
mov al, PIC2_EOI ; Specific eoi to master for pic2 eoi
out PIC1_PORT0, al ; send irq2 specific eoi to master
lea eax, _Halp8259Lock
RELEASE_SPINLOCK eax
HalpDismissSpurious:
mov dx, My+CpuIntCmd
@@: in ax, dx
test eax, ICU_CMD_BUSY
jnz @B
mov al, ICU_CLR_INSERV1
out dx, ax
xor eax, eax ; return FALSE
; sti
pop edx
pop ecx
stdRET _HalBeginSystemInterrupt
HalpDismissSpin:
SPIN_ON_SPINLOCK eax, HalpDismissNormal
HalpDismissSpin7:
SPIN_ON_SPINLOCK eax, Hbsi20
align dword
HalpDismissIrq07:
;
; Check to see if this is a spurious interrupt
;
lea eax, _Halp8259Lock
Hbsi20: ACQUIRE_SPINLOCK eax, HalpDismissSpin7
mov al, OCW3_READ_ISR ; tell 8259 we want to read ISR
out PIC1_PORT0, al
; IODelay ; delay
in al, PIC1_PORT0 ; (al) = content of PIC 1 ISR
test al, 10000000B ; Is In-Service register set?
jnz short HalpDismissNormal2 ; No, so this is a spurious int
lea eax, _Halp8259Lock
RELEASE_SPINLOCK eax
jmp HalpDismissSpurious
align dword
HalpDismissNormal:
lea eax, _Halp8259Lock
ACQUIRE_SPINLOCK eax, HalpDismissSpin
align dword
HalpDismissNormal2:
mov eax, _i8259_IMR ;get current 8259 masks
bts _i8259_ISR, ebx ;mark this int as in service
or eax, _i8259_ISR ;also mask in service ints
SET_8259_MASK ;tell 8259's the news
lea eax, _Halp8259Lock
RELEASE_SPINLOCK eax
;
; Dismiss interrupt. Current interrupt is already masked off.
;
mov eax, ebx ; (eax) = IRQ #
cmp eax, 8 ; EOI to master or slave?
jae short Hbsi100 ; EIO to both master and slave
or al, PIC1_EOI_MASK ; create specific eoi mask for master
out PIC1_PORT0, al ; dismiss the interrupt
jmp short Hbsi200 ; IO delay
Hbsi100:
mov al, OCW2_NON_SPECIFIC_EOI ; send non specific eoi to slave
out PIC2_PORT0, al
mov al, PIC2_EOI ; specific eoi to master for pic2 eoi
out PIC1_PORT0, al ; send irq2 specific eoi to master
Hbsi200:
PIC1DELAY
HalpDismissIPIlevel:
mov bl, HbsiIrql
mov dx, My+CpuPriortyLevel
mov al, _HalpIRQLtoCPL[ebx]
mov cl, Fs:PcIrql ; (cl) = Old Irql
out dx, ax
mov Fs:PcIrql, bl ; set new irql
mov edx, HbsiOldIrql ; get addr to store old irql
mov Fs:PcHal.pchHwIrql, bl
mov [edx], cl ; save old irql in the return variable
ifdef IRQL_METRICS
lock inc HalRaiseIrqlCount
endif
mov dx, My+CpuIntCmd
@@: in ax, dx
test eax, ICU_CMD_BUSY
jnz @B
mov al, ICU_CLR_INSERV1 ; clear this processor's in service bit
out dx, ax
sti
mov eax, 1 ; return TRUE, interrupt dismissed
pop edx
pop ecx
stdRET _HalBeginSystemInterrupt
stdENDP _HalBeginSystemInterrupt
;++
;BOOLEAN
;HalEndSystemInterrupt(
; IN KIRQL Irql
; IN CCHAR Vector,
; )
;
;
;
;Routine Description:
;
; This routine is used to complete any interrupt h/w processing and to
; lower the IRQL to the original value.
;
;Arguments:
;
; Irql - Supplies the IRQL to raise to
;
; Vector - Supplies the vector of the interrupt to be dismissed
;
;
;Return Value:
;
; FALSE - Interrupt is spurious and should be ignored
;
; TRUE - Interrupt successfully dismissed and Irql raised.
;
;--
align dword
cPublicProc _HalEndSystemInterrupt ,2
movzx ecx, byte ptr [esp+8]
; change stack to be for KeLowerIrql
pop eax ;(eax) = ret addr
mov edx, [esp] ;(edx) = new irql
mov [esp], eax
mov [esp+4], edx
sub cl, PRIMARY_VECTOR_BASE
cmp cl, 16
jnb _KeLowerIrql@4 ;jump if not 8259 interrupt
pushfd
lea eax, _Halp8259Lock
HesiAquireLock:
cli
ACQUIRE_SPINLOCK eax, HesiSpin
mov eax, _i8259_IMR
btr _i8259_ISR, ecx
or eax, _i8259_ISR
SET_8259_MASK
lea eax, _Halp8259Lock
RELEASE_SPINLOCK eax
popfd
jmp _KeLowerIrql@4
HesiSpin:
popfd
pushfd
SPIN_ON_SPINLOCK eax, HesiAquireLock
stdENDP _HalEndSystemInterrupt
;++
;VOID
;HalDisableSystemInterrupt(
; IN CCHAR Vector,
; IN KIRQL Irql
; )
;
;
;
;Routine Description:
;
; Disables a system interrupt.
;
;Arguments:
;
; Vector - Supplies the vector of the interrupt to be disabled
;
; Irql - Supplies the interrupt level of the interrupt to be disabled
;
;Return Value:
;
; None.
;
;--
cPublicProc _HalDisableSystemInterrupt ,2
enproc 9
;
push ebx
movzx ebx, byte ptr [esp+12] ;get IRQL
movzx ecx, _HalpIRQLtoVector[ebx]
or ecx, ecx
jz DisSysIntExit ;jump if not H/W interrupt
sub cl, PRIMARY_VECTOR_BASE
movzx eax, _HalpIRQLtoCPL[ebx]
cli
bts fs:PcIDR, eax ;disable int locally
jc DisSysIntExit ;jump if already disabled
push fs:PcIDR
push ICU_IMR0 ;write low interrupt masks
call WriteMyCpuReg
shr eax, 16
out dx, ax ;shortcut to high interrupt masks
cmp cl, 16 ;is this an 8259 interrupt?
jnb DisSysIntExit ;jump if not
lea eax, _Halp8259Lock
DisSysIntAquire:
ACQUIRE_SPINLOCK eax, DisSysIntSpin
dec _Halp8259Counts[ecx]
jnz short DisSysIntRelease
mov eax, _i8259_ISR
bts _i8259_IMR, ecx
or eax, _i8259_IMR
SET_8259_MASK
lea eax, _Halp8259Lock
DisSysIntRelease:
RELEASE_SPINLOCK eax
DisSysIntExit:
exproc 9
sti
pop ebx
stdRET _HalDisableSystemInterrupt
DisSysIntSpin:
SPIN_ON_SPINLOCK eax, DisSysIntAquire
stdENDP _HalDisableSystemInterrupt
;++
;
;BOOLEAN
;HalEnableSystemInterrupt(
; IN ULONG Vector,
; IN KIRQL Irql,
; IN KINTERRUPT_MODE InterruptMode
; )
;
;
;Routine Description:
;
; Enables a system interrupt
;
;Arguments:
;
; Vector - Supplies the vector of the interrupt to be enabled
;
; Irql - Supplies the interrupt level of the interrupt to be enabled.
;
;Return Value:
;
; None.
;
;--
cPublicProc _HalEnableSystemInterrupt ,3
enproc 0Ah
push ebx
movzx ebx, byte ptr [esp+12] ;get IRQL
movzx ecx, _HalpIRQLtoVector[ebx]
or ecx, ecx
jz EnbSysIntError ;jump if not H/W interrupt
sub cl, PRIMARY_VECTOR_BASE
movzx eax, _HalpIRQLtoCPL[ebx]
cli
btr fs:PcIDR, eax ;enable int locally
jnc EnbSysIntExit ;jump if already enabled
push fs:PcIDR
push ICU_IMR0
call WriteMyCpuReg ;write low interrupt masks
shr eax, 16
out dx, ax ;shortcut to high interrupt masks
cmp cl, 16 ;is this an 8259 interrupt?
jnb EnbSysIntExit ;jump if not
lea eax, _Halp8259Lock
EnbSysIntAquire:
ACQUIRE_SPINLOCK eax, EnbSysIntSpin
inc _Halp8259Counts[ecx]
cmp _Halp8259Counts[ecx], 1
jnz short EnbSysIntRelease
mov eax, _i8259_ISR
btr _i8259_IMR, ecx
or eax, _i8259_IMR
SET_8259_MASK
lea eax, _Halp8259Lock
EnbSysIntRelease:
RELEASE_SPINLOCK eax
EnbSysIntExit:
exproc 0Ah
sti
pop ebx
mov eax, 1
stdRET _HalEnableSystemInterrupt
EnbSysIntError:
if DBG
int 3
endif
exproc 0Ah
sti
pop ebx
xor eax,eax
stdRET _HalEnableSystemInterrupt
EnbSysIntSpin:
SPIN_ON_SPINLOCK eax, EnbSysIntAquire
stdENDP _HalEnableSystemInterrupt
_TEXT ENDS
END