[HAL]: Implement and document the HalpSpecialDismissTable. Explain how each IRQ should be handled and what the special cases are. Implement said special cases (based on ISA System Architecture, 3rd Edition).

[HAL]: Implement HalBeginSystemInterrupt in C instead of ASM, it jumps into one of the IRQ handlers registered in the HalpSpecialDismissTable.

svn path=/trunk/; revision=45237
This commit is contained in:
Sir Richard 2010-01-24 23:30:43 +00:00
parent f79bc2e7f0
commit 1215f71ed8
3 changed files with 314 additions and 224 deletions

View file

@ -84,38 +84,6 @@ FindHigherIrqlMask:
.long 0 /* IRQL 30 */
.long 0 /* IRQL 31 */
HalpSpecialDismissTable:
.rept 7
.long GenericIRQ /* IRQ 0-7 */
.endr
.long IRQ7 /* IRQ 7 */
.rept 5
.long GenericIRQ /* IRQ 8-12 */
.endr
.long IRQ13 /* IRQ 13 */
.long GenericIRQ /* IRQ 14 */
.long IRQ15 /* IRQ 15 */
.rept 20
.long GenericIRQ /* IRQ 16-35 */
.endr
#if DBG
.rept 172
.long InvalidIRQ /* IRQ 36-207 */
.endr
#endif
HalpSpecialDismissLevelTable:
.rept 7
.long GenericIRQLevel /* IRQ 0-7 */
.endr
.long IRQ7Level /* IRQ 7 */
.rept 5
.long GenericIRQLevel /* IRQ 8-12 */
.endr
.long IRQ13Level /* IRQ 13 */
.long GenericIRQLevel /* IRQ 14 */
.long IRQ15Level /* IRQ 15 */
SWInterruptLookUpTable:
.byte PASSIVE_LEVEL /* IRR 0 */
.byte PASSIVE_LEVEL /* IRR 1 */
@ -210,198 +178,6 @@ AfterCall:
ret
.endfunc
.globl _HalBeginSystemInterrupt@12
.func HalBeginSystemInterrupt@12
_HalBeginSystemInterrupt@12:
/* Convert to IRQ and call the handler */
xor ecx, ecx
mov cl, byte ptr [esp+8]
sub ecx, PRIMARY_VECTOR_BASE
jmp HalpSpecialDismissTable[ecx*4]
IRQ15:
/* This is IRQ 15, check if it's spurious */
mov al, 0xB
out 0xA0, al
jmp $+2
in al, 0xA0
test al, 0x80
jnz GenericIRQ
/* Cascaded interrupt... dismiss it and return FALSE */
CascadedInterrupt:
mov al, 0x62
out 0x20, al
mov eax, 0
ret 12
IRQ7:
/* This is IRQ 7, check if it's spurious */
mov al, 0xB
out 0x20, al
jmp $+2
in al, 0x20
test al, 0x80
jnz GenericIRQ
/* It is, return FALSE */
mov eax, 0
ret 12
IRQ13:
/* AT 80287 latch clear */
xor al, al
out 0xF0, al
GenericIRQ:
/* Get current and new IRQL */
xor eax, eax
mov al, byte ptr [esp+4]
mov ebx, PCR[KPCR_IRQL]
/* Set and save old */
mov PCR[KPCR_IRQL], eax
mov edx, [esp+12]
mov [edx], bl
/* Set IRQ mask in the PIC */
mov eax, KiI8259MaskTable[eax*4]
or eax, PCR[KPCR_IDR]
out 0x21, al
shr eax, 8
out 0xA1, al
/* Check to which PIC the EOI was sent */
mov eax, ecx
cmp eax, 8
jnb Pic1
/* Write mask to master PIC */
or al, 0x60
out 0x20, al
/* Enable interrupts and return TRUE */
sti
mov eax, 1
ret 12
Pic1:
/* Write mask to slave PIC */
mov al, 0x20
out 0xA0, al
mov al, 0x62
out 0x20, al
/* Enable interrupts and return TRUE */
sti
mov eax, 1
ret 12
#if DBG
InvalidIRQ:
/* Dismiss it */
mov eax, 0
ret 12
#endif
.endfunc
IRQ15Level:
/* This is IRQ 15, check if it's spurious */
mov al, 0xB
out 0xA0, al
jmp $+2
in al, 0xA0
test al, 0x80
jnz GenericIRQLevel
jmp CascadedInterrupt
IRQ7Level:
/* This is IRQ 7, check if it's spurious */
mov al, 0xB
out 0x20, al
jmp $+2
in al, 0x20
test al, 0x80
jnz GenericIRQLevel
/* It is, return FALSE */
SpuriousInterrupt:
mov eax, 0
ret 12
IRQ13Level:
/* AT 80287 latch clear */
xor al, al
out 0xF0, al
GenericIRQLevel:
/* Save IRQL */
xor eax, eax
mov al, [esp+4]
/* Set IRQ mask in the PIC */
mov eax, KiI8259MaskTable[eax*4]
or eax, PCR[KPCR_IDR]
out 0x21, al
shr eax, 8
out 0xA1, al
/* Compute new IRR */
mov eax, ecx
mov ebx, 1
add ecx, 4
shl ebx, cl
or PCR[KPCR_IRR], ebx
/* Get IRQLs */
mov cl, [esp+4]
mov bl, PCR[KPCR_IRQL]
mov edx, [esp+12]
/* Check to which PIC the EOI was sent */
cmp eax, 8
jnb Pic1Level
/* Write mask to master PIC */
or al, 0x60
out 0x20, al
/* Check for spurious */
cmp cl, bl
jbe SpuriousInterrupt
/* Write IRQL values */
movzx ecx, cl
mov PCR[KPCR_IRQL], ecx
mov [edx], bl
/* Enable interrupts and return TRUE */
sti
mov eax, 1
ret 12
Pic1Level:
/* Write mask to slave and master PIC */
add al, 0x58
out 0xA0, al
mov al, 0x62
out 0x20, al
/* Was this a lower interrupt? */
cmp cl, bl
jbe SpuriousInterrupt
/* Write IRQL values */
movzx ecx, cl
mov PCR[KPCR_IRQL], ecx
mov [edx], bl
/* Enable interrupts and return TRUE */
sti
mov eax, 1
ret 12
.globl _HalEndSystemInterrupt@8
.func HalEndSystemInterrupt@8
_HalEndSystemInterrupt@8:

View file

@ -14,6 +14,58 @@
/* GLOBALS ********************************************************************/
/*
* This table basically keeps track of level vs edge triggered interrupts.
* Windows has 250+ entries, but it seems stupid to replicate that since the PIC
* can't actually have that many.
*
* When a level interrupt is registered, the respective pointer in this table is
* modified to point to a dimiss routine for level interrupts instead.
*
* The other thing this table does is special case IRQ7, IRQ13 and IRQ15:
*
* - If an IRQ line is deasserted before it is acknowledged due to a noise spike
* generated by an expansion device (since the IRQ line is low during the 1st
* acknowledge bus cycle), the i8259 will keep the line low for at least 100ns
* When the spike passes, a pull-up resistor will return the IRQ line to high.
* Since the PIC requires the input be high until the first acknowledge, the
* i8259 knows that this was a spurious interrupt, and on the second interrupt
* acknowledge cycle, it reports this to the CPU. Since no valid interrupt has
* actually happened Intel hardcoded the chip to report IRQ7 on the master PIC
* and IRQ15 on the slave PIC (IR7 either way).
*
* "ISA System Architecture", 3rd Edition, states that these cases should be
* handled by reading the respective Interrupt Service Request (ISR) bits from
* the affected PIC, and validate whether or not IR7 is set. If it isn't, then
* the interrupt is spurious and should be ignored.
*
* Note that for a spurious IRQ15, we DO have to send an EOI to the master for
* IRQ2 since the line was asserted by the slave when it received the spurious
* IRQ15!
*
* - When the 80287/80387 math co-processor generates an FPU/NPX trap, this is
* connected to IRQ13, so we have to clear the busy latch on the NPX port.
*/
PHAL_DISMISS_INTERRUPT HalpSpecialDismissTable[16] =
{
HalpDismissIrqGeneric,
HalpDismissIrqGeneric,
HalpDismissIrqGeneric,
HalpDismissIrqGeneric,
HalpDismissIrqGeneric,
HalpDismissIrqGeneric,
HalpDismissIrqGeneric,
HalpDismissIrq07,
HalpDismissIrqGeneric,
HalpDismissIrqGeneric,
HalpDismissIrqGeneric,
HalpDismissIrqGeneric,
HalpDismissIrqGeneric,
HalpDismissIrq13,
HalpDismissIrqGeneric,
HalpDismissIrq15
};
/* This table contains the static x86 PIC mapping between IRQLs and IRQs */
ULONG KiI8259MaskTable[32] =
{
@ -342,6 +394,139 @@ HalClearSoftwareInterrupt(IN KIRQL Irql)
KeGetPcr()->IRR &= ~(1 << Irql);
}
/* INTERRUPT DISMISSAL FUNCTIONS **********************************************/
BOOLEAN
FORCEINLINE
_HalpDismissIrqGeneric(IN KIRQL Irql,
IN ULONG Irq,
OUT PKIRQL OldIrql)
{
PIC_MASK Mask;
KIRQL CurrentIrql;
I8259_OCW2 Ocw2;
PKPCR Pcr = KeGetPcr();
/* First save current IRQL and compare it to the requested one */
CurrentIrql = Pcr->Irql;
/* Set the new IRQL and return the current one */
Pcr->Irql = Irql;
*OldIrql = CurrentIrql;
/* Set new PIC mask */
Mask.Both = KiI8259MaskTable[Irql] | Pcr->IDR;
__outbyte(PIC1_DATA_PORT, Mask.Master);
__outbyte(PIC2_DATA_PORT, Mask.Slave);
/* Prepare OCW2 for EOI */
Ocw2.Bits = 0;
Ocw2.EoiMode = SpecificEoi;
/* Check which PIC needs the EOI */
if (Irq > 8)
{
/* Send the EOI for the IRQ */
__outbyte(PIC2_CONTROL_PORT, Ocw2.Bits | (Irq - 8));
/* Send the EOI for IRQ2 on the master because this was cascaded */
__outbyte(PIC1_CONTROL_PORT, Ocw2.Bits | 2);
}
else
{
/* Send the EOI for the IRQ */
__outbyte(PIC1_CONTROL_PORT, Ocw2.Bits | Irq);
}
/* Enable interrupts and return success */
_enable();
return TRUE;
}
BOOLEAN
__attribute__((regparm(3)))
HalpDismissIrqGeneric(IN KIRQL Irql,
IN ULONG Irq,
OUT PKIRQL OldIrql)
{
/* Run the inline code */
return _HalpDismissIrqGeneric(Irql, Irq, OldIrql);
}
BOOLEAN
__attribute__((regparm(3)))
HalpDismissIrq15(IN KIRQL Irql,
IN ULONG Irq,
OUT PKIRQL OldIrql)
{
I8259_OCW3 Ocw3;
I8259_OCW2 Ocw2;
I8259_ISR Isr;
/* Request the ISR */
Ocw3.Bits = 0;
Ocw3.Sbo = 1; /* This encodes an OCW3 vs. an OCW2 */
Ocw3.ReadRequest = ReadIsr;
__outbyte(PIC2_CONTROL_PORT, Ocw3.Bits);
/* Read the ISR */
Isr.Bits = __inbyte(PIC2_CONTROL_PORT);
/* Is IRQ15 really active (this is IR7) */
if (Isr.Irq7 == FALSE)
{
/* It isn't, so we have to EOI IRQ2 because this was cascaded */
Ocw2.Bits = 0;
Ocw2.EoiMode = SpecificEoi;
__outbyte(PIC1_CONTROL_PORT, Ocw2.Bits | 2);
/* And now fail since this was spurious */
return FALSE;
}
/* Do normal interrupt dismiss */
return _HalpDismissIrqGeneric(Irql, Irq, OldIrql);
}
BOOLEAN
__attribute__((regparm(3)))
HalpDismissIrq13(IN KIRQL Irql,
IN ULONG Irq,
OUT PKIRQL OldIrql)
{
/* Clear the FPU busy latch */
__outbyte(0xF0, 0);
/* Do normal interrupt dismiss */
return _HalpDismissIrqGeneric(Irql, Irq, OldIrql);
}
BOOLEAN
__attribute__((regparm(3)))
HalpDismissIrq07(IN KIRQL Irql,
IN ULONG Irq,
OUT PKIRQL OldIrql)
{
I8259_OCW3 Ocw3;
I8259_ISR Isr;
/* Request the ISR */
Ocw3.Bits = 0;
Ocw3.Sbo = 1;
Ocw3.ReadRequest = ReadIsr;
__outbyte(PIC1_CONTROL_PORT, Ocw3.Bits);
/* Read the ISR */
Isr.Bits = __inbyte(PIC1_CONTROL_PORT);
/* Is IRQ 7 really active? If it isn't, this is spurious so fail */
if (Isr.Irq7 == FALSE) return FALSE;
/* Do normal interrupt dismiss */
return _HalpDismissIrqGeneric(Irql, Irq, OldIrql);
}
/* SYSTEM INTERRUPTS **********************************************************/
/*
@ -420,3 +605,19 @@ HalDisableSystemInterrupt(IN UCHAR Vector,
/* Bring interrupts back */
_enable();
}
/*
* @implemented
*/
BOOLEAN
NTAPI
HalBeginSystemInterrupt(IN KIRQL Irql,
IN UCHAR Vector,
OUT PKIRQL OldIrql)
{
ULONG Irq;
/* Get the IRQ and call the proper routine to handle it */
Irq = Vector - PRIMARY_VECTOR_BASE;
return HalpSpecialDismissTable[Irq](Irql, Irq, OldIrql);
}

View file

@ -179,6 +179,26 @@ typedef enum _I8259_ICW4_BUFFERED_MODE
BufferedMaster
} I8259_ICW4_BUFFERED_MODE;
typedef enum _I8259_READ_REQUEST
{
InvalidRequest,
InvalidRequest2,
ReadIdr,
ReadIsr
} I8259_READ_REQUEST;
typedef enum _I8259_EOI_MODE
{
RotateAutoEoiClear,
NonSpecificEoi,
InvalidEoiMode,
SpecificEoi,
RotateAutoEoiSet,
RotateNonSpecific,
SetPriority,
RotateSpecific
} I8259_EOI_MODE;
//
// Definitions for ICW Registers
//
@ -243,6 +263,52 @@ typedef union _I8259_ICW4
UCHAR Bits;
} I8259_ICW4, *PI8259_ICW4;
typedef union _I8259_OCW2
{
struct
{
UCHAR IrqNumber:3;
UCHAR Sbz:2;
I8259_EOI_MODE EoiMode:3;
};
UCHAR Bits;
} I8259_OCW2, *PI8259_OCW2;
typedef union _I8259_OCW3
{
struct
{
I8259_READ_REQUEST ReadRequest:2;
UCHAR PollCommand:1;
UCHAR Sbo:1;
UCHAR Sbz:1;
UCHAR SpecialMaskMode:2;
UCHAR Reserved:1;
};
UCHAR Bits;
} I8259_OCW3, *PI8259_OCW3;
typedef union _I8259_ISR
{
union
{
struct
{
UCHAR Irq0:1;
UCHAR Irq1:1;
UCHAR Irq2:1;
UCHAR Irq3:1;
UCHAR Irq4:1;
UCHAR Irq5:1;
UCHAR Irq6:1;
UCHAR Irq7:1;
};
};
UCHAR Bits;
} I8259_ISR, *PI8259_ISR;
typedef I8259_ISR I8259_IDR, *PI8259_IDR;
//
// See EISA System Architecture 2nd Edition (Tom Shanley, Don Anderson, John Swindle)
// P. 34, 35
@ -295,6 +361,53 @@ typedef struct _PIC_MASK
};
} PIC_MASK, *PPIC_MASK;
typedef
VOID
(*PHAL_SW_INTERRUPT_HANDLER)(
VOID
);
typedef
BOOLEAN
__attribute__((regparm(3)))
(*PHAL_DISMISS_INTERRUPT)(
IN KIRQL Irql,
IN ULONG Irq,
OUT PKIRQL OldIrql
);
BOOLEAN
__attribute__((regparm(3)))
HalpDismissIrqGeneric(
IN KIRQL Irql,
IN ULONG Irq,
OUT PKIRQL OldIrql
);
BOOLEAN
__attribute__((regparm(3)))
HalpDismissIrq15(
IN KIRQL Irql,
IN ULONG Irq,
OUT PKIRQL OldIrql
);
BOOLEAN
__attribute__((regparm(3)))
HalpDismissIrq13(
IN KIRQL Irql,
IN ULONG Irq,
OUT PKIRQL OldIrql
);
BOOLEAN
__attribute__((regparm(3)))
HalpDismissIrq07(
IN KIRQL Irql,
IN ULONG Irq,
OUT PKIRQL OldIrql
);
//
// Mm PTE/PDE to Hal PTE/PDE
//