title "System Interrupt" ;++ ; ; Copyright (c) 1991 Microsoft Corporation ; Copyright (c) 1993 Sequent Computer Systems, Inc. ; ; Module Name: ; ; w3sysint.asm ; ; Abstract: ; ; This module implements the HAL routines to begin a system interrupt, ; end a system interrupt, and enable/disable system interrupts ; for the WinServer 3000. ; ; Author: ; ; Phil Hochstetler (phil@sequent.com) 3-30-93 ; ; Environment: ; ; Kernel Mode ; ; Revision History: ; ;-- .386p .xlist include hal386.inc include callconv.inc ; calling convention macros include i386\kimacro.inc include mac386.inc include i386\apic.inc include i386\w3.inc .list EXTRNP _KeBugCheck,1 EXTRNP _KeLowerIrql,1 extrn _HalpIrql2TPR:byte extrn _HalpLocalUnitBase:dword extrn _HalpIOunitTwoBase:dword extrn _HalpIOunitBase:dword extrn _HalpK2Rdir2Irq:byte extrn _HalpELCRImage:word extrn _HalpK2EbsIOunitRedirectionTable:dword extrn _HalpBeginW3InterruptTable:dword extrn _HalpK2Vector2RdirTabEntry:byte extrn _HalpK2EISAIrq2Irql:byte extrn _HalpK2Irql2Eisa:byte extrn _HalpK2Vector2EISA:byte extrn FindHigherIrqlMask:dword extrn HalpDispatchInterrupt2ndEntry:NEAR extrn HalpApcInterrupt2ndEntry:NEAR extrn _HalpMASKED:WORD _DATA SEGMENT DWORD PUBLIC 'DATA' _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 handles the initial interrupt sequence for all ; external APIC interrupts for both single processor and multiple ; processor WinServer 3000 systems. On the WinServer 3000 we use ; the APIC to generate all I/O interrupts and their respective ; priorities. We do not abstract the prioritization of interrupts ; from the hardware since the APIC accomodates all Windows NT ; requirements for IRQLs. In addition to I/O interrupts the APIC ; is used to generate software interrupts for APCs and DPCs. ; The APCs and DPCs are generated by a CPU via it's ; local APIC interrupt command register. The interrupts are sent ; as a self directed interrupt. It is required by NT that software ; interrupts are always handled by the CPU which issues them. ; ; Note: Wakeup is included also in the above fashion ; ; This routine does not process an 8259 generated interrupt which ; is not tied to the APIC. This processing is done in the w3ipi.asm ; module. A subset of IRQs 0-15 are not wired through the EBS APIC. ; This is because the APIC on the EBS only accomodates 16 interrupts ; We have a total of 24 on a WS3000. We chose to have all system bus ; generated interrupts 16-23 tied to the EBS APIC. The remaining ; 8 are divided up for EISA cards. TheHalpK2EbsIounitRedirectionTable ; in k2space.asm describes the EISA IRQs chosen for the EBS APIC. ; ; Prioritization of 8259 interrupts is done by generating an APIC ; interrupt with the appropriate APIC vector which corresponds to ; the incoming 8259 IRQ number. Thus, 2 interrupts are always ; generated by an 8259 interrupt. The first is the 8259 interrupt ; and the second is the APIC interrupt. ; ; The APIC interrupt is then appropriately prioritized by the hardware ; and received accordingly via this routine. ; ;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 - Valid interrupt and Irql raised appropriately. ; ;-- HbsiOldIrql equ dword ptr [esp+16] HbsiVector equ byte ptr [esp+12] HbsiIrql equ byte ptr [esp+8] align 4 cPublicProc _HalBeginSystemInterrupt ,3 push ebx movzx ebx, HbsiVector ; (ebx) = System Vector jmp _HalpBeginW3InterruptTable[ebx*4] ; ; --- Process APIC external interrupt, i.e., generated via EBS I/O APIC ; - simply raise the Irql to the interrupt level of the ; current interrupt, EOI APIC, and return TRUE ; align 4 public _HalpBeginW3APICInterrupt _HalpBeginW3APICInterrupt: mov edx, _HalpLocalUnitBase ; Get address of Local APIC movzx eax, HbsiIrql ; Get New IRQL mov cl, PCR[PcHal.ProcIrql] ; Get current irql cli ; Don't trust caller.. if DBG cmp al, cl ja @f ; New IRQL is > Current push eax ; New IRQL push ebx ; Vector push ecx ; Old IRQL push [edx+LU_TPR] ; mov dword ptr [edx+LU_TPR], 0FFH mov PCR[PcHal.ProcIrql], HIGH_LEVEL ; Set to avoid recursive error stdCall _KeBugCheck, <0Bh> @@: endif ; ; --- Do to the fact that some drivers like the i8042prt driver ; and possibly other foolhardy drivers which don't use the IRQL ; returned by the HalGetInterrupt routine when they do an IoConnectInt ; we have to use the IRQL passed and not use the vector to set the ; TPR...THESE TYPE OF DRIVERS HAVE TO BE EDGE SENSITIVE DEVICES TO ; WORK IF THEY DO THIS CRAP....We allow changes in the IRQL as long ; as it is a higher IRQL than the one returned by the HAL ; mov PCR[PcHal.ProcIrql], al ; Set new irql movzx eax, _HalpIrql2TPR[eax] mov [edx+LU_TPR], eax ; Set task priority register mov eax, [edx+LU_TPR] ; Read it to make sure write occurs mov eax, HbsiOldIrql ; get addr to store old irql mov [eax], cl ; save old irql in the return variable ; ; --- EOI the APIC if the interrupt is not a level sensitive EISA interrupt ; Level sensitive interrupts are EOI'ed in EndSystemInterrupt ; xor ah, ah mov al, _HalpK2Vector2EISA[ebx] ; Get EISA IRQ # mov cx, _HalpELCRImage ; Get edge/level register value mov bx, _HalpMASKED ; get PIC level interrupts not bx and cx, bx ; clear level bits from PIC bt cx, ax ; test appropriate bit jc short @f mov [edx+LU_EOI], eax ; EOI APIC @@: mov eax, 1 ; True interrupt sti ; Let interrupts go pop ebx stdRET _HalBeginSystemInterrupt ; ; --- Invalid or illegal vector ; align 4 public _HalpBeginW3InvalidInterrupt _HalpBeginW3InvalidInterrupt: mov eax,0 ; return False pop ebx stdRET _HalBeginSystemInterrupt stdENDP _HalBeginSystemInterrupt ;++ ;VOID ;HalDisableSystemInterrupt( ; IN CCHAR Vector, ; IN KIRQL Irql ; ) ; ; ; ;Routine Description: ; ; Disables a system interrupt via the EBS APIC Redirection table ; entries or the 8259 Interrupt mask registers ; ;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 cPublicFpo 2, 0 ; ; --- Exit if this is not the base processor ; movzx ecx, byte ptr [esp+4] ; (ecx) = Vector movzx eax, byte ptr PCR[PcHal.PcrNumber] cmp al, 0 jne HalDisableInt999 ; Exit if not base proc cPublicFpo 2, 1 pushfd cli ; Disable interrupts ; ; --- Enable/Disable does not get called for the interrupt vectors ; which are used for the 8259 generated interrupts. It only gets ; called for the the APIC vectors which correspond to the 8259 ints... ; cmp ecx, APIC_DMA_VECTOR ; Is this for the 8259 DMA? jne short HalDisableInt10 ; No - branch mov ecx, 13 ; EISA DMA IRQ jmp HalDisableInt60 ; Enable 8259 Int HalDisableInt10: cmp ecx, APIC_KBD_VECTOR ; Is this the keyboard vector? jne short HalDisableInt20 ; Yes - branch mov ecx, 1 ; Keyboard IRQ jmp HalDisableInt60 ; Enable 8259 Int HalDisableInt20: cmp ecx, APIC_CLOCK_VECTOR ; 8259 clock? jne short HalDisableInt30 ; Yes - branch mov ecx, 0 ; Clock IRQ jmp HalDisableInt60 HalDisableInt30: cmp ecx, APIC_RTC_VECTOR ; Real-time clock? jne short HalDisableInt31 ; No -branch mov ecx, 8 ; RTC IRQ jmp HalDisableInt60 HalDisableInt31: cmp ecx, APIC_FLOPPY_VECTOR ; Floppy? jne short HalDisableInt32 ; No -branch mov ecx, 6 ; Floppy IRQ jmp HalDisableInt60 HalDisableInt32: cmp ecx, APIC_IDE_VECTOR ; IDE Disk? jne short HalDisableInt33 ; No -branch mov ecx, 14 ; IDE Disk IRQ jmp HalDisableInt60 HalDisableInt33: cmp ecx, APIC_MOUSE_VECTOR ; IRQ12 vector jne short HalDisableInt34 ; No -branch mov ecx, 12 ; IRQ12 jmp HalDisableInt60 HalDisableInt34: ; ; --- Process other APIC vectors ; xor eax,eax mov al, _HalpK2Vector2RdirTabEntry[ecx] ; Get EBS RDIR entry cmp al, 0FFH ; No RDIR entry..exit je HalDisableInt50 ; ..exit cmp al, 0 ; Not used vector je HalDisableInt50 ; ..exit btr eax, 7 ; EBS I/O APIC RDIR?? jnc HalDisableInt35 ; Yes mov edx, _HalpIOunitTwoBase ; Boot processor I/O APIC RDIR jmp HalDisableInt40 ; continue HalDisableInt35: mov edx, _HalpIOunitBase ; Address of EBS I/O APIC HalDisableInt40: ; ; No need for a lock here since only the base manipulates these registers ; mov [edx+IO_REGISTER_SELECT], eax ; Select register or dword ptr [edx+IO_REGISTER_WINDOW], IOMPIC_RT_MASK ; mask it HalDisableInt50: cPublicFpo 2, 0 popfd stdRET _HalDisableSystemInterrupt ; ; --- 8259 interrupt enable... ; HalDisableInt60: ; ; --- 8259 interrupt disable... ; mov edx, 1 shl edx, cl ; (ebx) = bit in IMR to disable xor eax, eax ; ; Get the current interrupt mask register from the 8259 ; in al, PIC2_PORT1 shl eax, 8 in al, PIC1_PORT1 ; ; Mask off the interrupt to be disabled ; or eax, edx ; ; Write the new interrupt mask register back to the 8259 ; out PIC1_PORT1, al shr eax, 8 out PIC2_PORT1, al PIC2DELAY cPublicFpo 2, 0 popfd HalDisableInt999: stdRET _HalDisableSystemInterrupt stdENDP _HalDisableSystemInterrupt ;++ ; ;BOOLEAN ;HalEnableSystemInterrupt( ; IN ULONG Vector, ; IN KIRQL Irql, ; IN KINTERRUPT_MODE InterruptMode ; ) ; ; ;Routine Description: ; ; Enables a system interrupt via PIC or APIC hardware masks ; ;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 cPublicFpo 3, 0 ; ; --- The base processor controls enabling/disabling of all ints ; movzx ecx, byte ptr [esp+4] ; (ecx) = Vector movzx eax, byte ptr PCR[PcHal.PcrNumber] cmp al, 0 jne HalEnableInt999 ; Exit if not base proc cPublicFpo 3, 1 pushfd cli ; ; --- Enable/Disable does not get called for the interrupt vectors ; which are used for the 8259 generated interrupts. It only gets ; called for the the APIC vectors which correspond to the 8259 ints... ; cmp ecx, APIC_DMA_VECTOR ; Is this for the 8259 DMA? jne short HalEnableInt10 ; No - branch mov ecx, 13 ; EISA DMA IRQ jmp HalEnableInt60 ; Enable 8259 Int HalEnableInt10: cmp ecx, APIC_KBD_VECTOR ; Is this the keyboard vector? jne short HalEnableInt20 ; No - branch mov ecx, 1 ; Keyboard IRQ jmp HalEnableInt60 ; Enable 8259 Int HalEnableInt20: cmp ecx, APIC_CLOCK_VECTOR ; 8259 clock? jne short HalEnableInt30 ; No - branch mov ecx, 0 ; Clock IRQ jmp HalEnableInt60 HalEnableInt30: cmp ecx, APIC_RTC_VECTOR ; Real-time clock? jne short HalEnableInt31 ; No -branch mov ecx, 8 ; RTC IRQ jmp HalEnableInt60 HalEnableInt31: cmp ecx, APIC_FLOPPY_VECTOR ; Floppy? jne short HalEnableInt32 ; No -branch mov ecx, 6 ; Floppy IRQ jmp HalEnableInt60 HalEnableInt32: cmp ecx, APIC_IDE_VECTOR ; IDE Disk? jne short HalEnableInt33 ; No -branch mov ecx, 14 ; IDE Disk IRQ jmp HalEnableInt60 HalEnableInt33: cmp ecx, APIC_MOUSE_VECTOR ; IRQ12 vector jne short HalEnableInt34 ; No -branch mov ecx, 12 ; IRQ12 jmp HalEnableInt60 HalEnableInt34: ; ; --- Process APIC vectors ; xor eax,eax mov al, _HalpK2Vector2RdirTabEntry[ecx] ; Get EBS RDIR entry cmp al, 0FFH ; No RDIR entry..exit je HalEnableInt50 ; ..exit cmp al, 0 ; Not used vector je HalEnableInt50 ; ..exit btr eax, 7 ; EBS I/O APIC RDIR?? jnc HalEnableInt35 ; Yes mov edx, _HalpIOunitTwoBase ; Boot processor I/O APIC RDIR jmp HalEnableInt40 ; continue HalEnableInt35: mov edx, _HalpIOunitBase ; Address of EBS I/O APIC HalEnableInt40: ; ; No need for a lock here since only the base manipulates these registers ; mov [edx+IO_REGISTER_SELECT], eax ; Select register and dword ptr [edx+IO_REGISTER_WINDOW], NOT IOMPIC_RT_MASK ; Unmask it HalEnableInt50: cPublicFpo 3, 0 popfd stdRET _HalEnableSystemInterrupt ; ; --- 8259 interrupt enable... ; HalEnableInt60: xor eax, eax ; ; Get the current interrupt mask register from the 8259 ; in al, PIC2_PORT1 shl eax, 8 in al, PIC1_PORT1 ; ; Unmask the interrupt to be enabled ; btr eax, ecx btr eax, 2 ; Enable cascaded IRQ2 ; ; Write the new interrupt mask register back to the 8259 ; out PIC1_PORT1, al shr eax, 8 out PIC2_PORT1, al PIC2DELAY cPublicFpo 3, 0 popfd HalEnableInt999: stdRET _HalEnableSystemInterrupt stdENDP _HalEnableSystemInterrupt ; HalpEndSystemInterrupt ; IN KIRQL NewIrql, ; IN ULONG Vector ; ) ; ; Routine Description: ; ; This routine is used to lower IRQL to the specified value just prior ; to ending interrupt processing. ; ; Arguments: ; ; NewIrql - the new irql to be set. ; ; Vector - Vector number of the interrupt ; ; Note that esp+8 is the beginning of interrupt/trap frame and upon ; entering to this routine. ; ; Return Value: ; ; None. ; ;-- HeiVector equ [esp+8] HeiNewIrql equ [esp+4] cPublicProc _HalEndSystemInterrupt ,2 cPublicFpo 2, 0 mov eax, HeiVector ; Get the current vector mov edx, _HalpLocalUnitBase ; Local APIC address if DBG xor ecx, ecx mov cl, byte ptr HeiNewIrql cmp cl, PCR[PcHal.ProcIrql] jbe short @f push dword ptr ecx ; new irql for debugging push dword ptr PCR[PcHal.ProcIrql] ; old irql for debugging mov dword ptr [edx+LU_TPR], 0FFH mov PCR[PcHal.ProcIrql], HIGH_LEVEL ; Set to avoid recursive error stdCall _KeBugCheck, @@: endif ; ; EOI the APIC on level EISA interrupts ; mov al, _HalpK2Vector2EISA[eax] ; Get EISA IRQ # mov cx, _HalpELCRImage ; Get edge/level register value bt cx, ax ; test appropriate bit jnc short NoEOI ; 1 means level interrupt ; ; Level interrupt so we need to determine if the source ; is the PIC or APIC. If the PIC, then we need to unmask the ; source, if the APIC, then we need to do an EOI to the APIC. ; mov cx, _HalpMASKED ; get PIC level ints bt cx, ax ; test appropriate bit jnc short @f ; CF=1 means PIC level int lock btr _HalpMASKED, ax ; atomic clear of bit mov cx, ax mov ax, 1 shl ax, cl or al, ah not al mov cl, al in al, PIC2_PORT1 ; unmask bit in PIC and al, cl out PIC2_PORT1, al jmp short NoEOI @@: mov [edx+LU_EOI], eax ; EOI APIC NoEOI: xor eax, eax mov al, byte ptr HeiNewIrql ; ; Write new lower priority level into the APIC Task Priority Register ; pushfd cli mov PCR[PcHal.ProcIrql], al ; set new IRQL mov al, _HalpIrql2TPR[eax] ; get new TPR mov [edx+LU_TPR], eax ; write new TPR mov eax, [edx+LU_TPR] ; Flush CPU write buffer popfd mov al, byte ptr HeiNewIrql ; get current irql cmp al, DISPATCH_LEVEL ; SW ints possible? jb short @f stdRET _HalEndSystemInterrupt ; ; Check for any pending software interrupts ; @@: mov edx, PCR[PcIRR] ; get current IRR and edx, FindHigherIrqlMask[eax*4] jnz short @f stdRET _HalEndSystemInterrupt ; ; Software interrupt(s) pending, figure out which one(s) and take it ; @@: cli test dword ptr PCR[PcIRR], (1 SHL DISPATCH_LEVEL) jz short @f add esp, 12 jmp HalpDispatchInterrupt2ndEntry @@: cmp al, APC_LEVEL jae short @f test dword ptr PCR[PcIRR], (1 SHL APC_LEVEL) jz short @f add esp, 12 jmp HalpApcInterrupt2ndEntry @@: sti stdRET _HalEndSystemInterrupt stdENDP _HalEndSystemInterrupt _TEXT ENDS END