reactos/hal/halx86/up/pic.c

1261 lines
32 KiB
C

/*
* PROJECT: ReactOS HAL
* LICENSE: BSD - See COPYING.ARM in the top level directory
* PURPOSE: HAL PIC Management and Control Code
* PROGRAMMERS: ReactOS Portable Systems Group
*/
/* INCLUDES *******************************************************************/
#include <hal.h>
#define NDEBUG
#include <debug.h>
VOID
NTAPI
HalpEndSoftwareInterrupt(IN KIRQL OldIrql,
IN PKTRAP_FRAME TrapFrame);
/* GLOBALS ********************************************************************/
#ifndef _MINIHAL_
/*
* 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,
#if defined(SARCH_PC98)
HalpDismissIrq08,
#else
HalpDismissIrqGeneric,
#endif
HalpDismissIrqGeneric,
HalpDismissIrqGeneric,
HalpDismissIrqGeneric,
HalpDismissIrqGeneric,
#if defined(SARCH_PC98)
HalpDismissIrqGeneric,
#else
HalpDismissIrq13,
#endif
HalpDismissIrqGeneric,
HalpDismissIrq15
};
/*
* These are the level IRQ dismissal functions that get copied in the table
* above if the given IRQ is actually level triggered.
*/
PHAL_DISMISS_INTERRUPT HalpSpecialDismissLevelTable[16] =
{
HalpDismissIrqLevel,
HalpDismissIrqLevel,
HalpDismissIrqLevel,
HalpDismissIrqLevel,
HalpDismissIrqLevel,
HalpDismissIrqLevel,
HalpDismissIrqLevel,
HalpDismissIrq07Level,
#if defined(SARCH_PC98)
HalpDismissIrq08Level,
#else
HalpDismissIrqLevel,
#endif
HalpDismissIrqLevel,
HalpDismissIrqLevel,
HalpDismissIrqLevel,
HalpDismissIrqLevel,
#if defined(SARCH_PC98)
HalpDismissIrqLevel,
#else
HalpDismissIrq13Level,
#endif
HalpDismissIrqLevel,
HalpDismissIrq15Level
};
/* This table contains the static x86 PIC mapping between IRQLs and IRQs */
extern ULONG KiI8259MaskTable[32];
/* This table indicates which IRQs, if pending, can preempt a given IRQL level */
extern ULONG FindHigherIrqlMask[32];
/* Denotes minimum required IRQL before we can process pending SW interrupts */
KIRQL SWInterruptLookUpTable[8] =
{
PASSIVE_LEVEL, /* IRR 0 */
PASSIVE_LEVEL, /* IRR 1 */
APC_LEVEL, /* IRR 2 */
APC_LEVEL, /* IRR 3 */
DISPATCH_LEVEL, /* IRR 4 */
DISPATCH_LEVEL, /* IRR 5 */
DISPATCH_LEVEL, /* IRR 6 */
DISPATCH_LEVEL /* IRR 7 */
};
#if defined(__GNUC__)
#define HalpDelayedHardwareInterrupt(x) \
VOID __cdecl HalpHardwareInterrupt##x(VOID); \
VOID \
__cdecl \
HalpHardwareInterrupt##x(VOID) \
{ \
asm volatile ("int $%c0\n"::"i"(PRIMARY_VECTOR_BASE + x)); \
}
#elif defined(_MSC_VER)
#define HalpDelayedHardwareInterrupt(x) \
VOID __cdecl HalpHardwareInterrupt##x(VOID); \
VOID \
__cdecl \
HalpHardwareInterrupt##x(VOID) \
{ \
__asm \
{ \
int PRIMARY_VECTOR_BASE + x \
} \
}
#else
#error Unsupported compiler
#endif
/* Pending/delayed hardware interrupt handlers */
HalpDelayedHardwareInterrupt(0);
HalpDelayedHardwareInterrupt(1);
HalpDelayedHardwareInterrupt(2);
HalpDelayedHardwareInterrupt(3);
HalpDelayedHardwareInterrupt(4);
HalpDelayedHardwareInterrupt(5);
HalpDelayedHardwareInterrupt(6);
HalpDelayedHardwareInterrupt(7);
HalpDelayedHardwareInterrupt(8);
HalpDelayedHardwareInterrupt(9);
HalpDelayedHardwareInterrupt(10);
HalpDelayedHardwareInterrupt(11);
HalpDelayedHardwareInterrupt(12);
HalpDelayedHardwareInterrupt(13);
HalpDelayedHardwareInterrupt(14);
HalpDelayedHardwareInterrupt(15);
/* Handlers for pending interrupts */
PHAL_SW_INTERRUPT_HANDLER SWInterruptHandlerTable[20] =
{
(PHAL_SW_INTERRUPT_HANDLER)KiUnexpectedInterrupt,
HalpApcInterrupt,
HalpDispatchInterrupt,
(PHAL_SW_INTERRUPT_HANDLER)KiUnexpectedInterrupt,
HalpHardwareInterrupt0,
HalpHardwareInterrupt1,
HalpHardwareInterrupt2,
HalpHardwareInterrupt3,
HalpHardwareInterrupt4,
HalpHardwareInterrupt5,
HalpHardwareInterrupt6,
HalpHardwareInterrupt7,
HalpHardwareInterrupt8,
HalpHardwareInterrupt9,
HalpHardwareInterrupt10,
HalpHardwareInterrupt11,
HalpHardwareInterrupt12,
HalpHardwareInterrupt13,
HalpHardwareInterrupt14,
HalpHardwareInterrupt15
};
/* Handlers for pending software interrupts when we already have a trap frame*/
PHAL_SW_INTERRUPT_HANDLER_2ND_ENTRY SWInterruptHandlerTable2[3] =
{
(PHAL_SW_INTERRUPT_HANDLER_2ND_ENTRY)(PVOID)KiUnexpectedInterrupt,
HalpApcInterrupt2ndEntry,
HalpDispatchInterrupt2ndEntry
};
LONG HalpEisaELCR;
/* FUNCTIONS ******************************************************************/
VOID
NTAPI
HalpInitializePICs(IN BOOLEAN EnableInterrupts)
{
ULONG EFlags;
EISA_ELCR Elcr;
ULONG i, j;
BOOLEAN ElcrFound;
/* Save EFlags and disable interrupts */
EFlags = __readeflags();
_disable();
/* Initialize and mask the PIC */
HalpInitializeLegacyPICs();
/* Read EISA Edge/Level Register for master and slave */
Elcr.Bits = (__inbyte(EISA_ELCR_SLAVE) << 8) | __inbyte(EISA_ELCR_MASTER);
#if defined(SARCH_PC98)
/* Force defaults when ELCR is not supported */
if (Elcr.Bits == 0xFFFF)
{
Elcr.Master.Irq0Level = 0;
Elcr.Master.Irq1Level = 0;
Elcr.Master.Irq7Level = 0;
Elcr.Slave.Irq8Level = 0;
}
ElcrFound = TRUE;
#else
/* IRQs 0, 1, 2, 8, and 13 are system-reserved and must be edge */
ElcrFound = (!(Elcr.Master.Irq0Level) && !(Elcr.Master.Irq1Level) && !(Elcr.Master.Irq2Level) &&
!(Elcr.Slave.Irq8Level) && !(Elcr.Slave.Irq13Level));
#endif
if (ElcrFound)
{
/* ELCR is as it's supposed to be, save it */
HalpEisaELCR = Elcr.Bits;
/* Scan for level interrupts */
for (i = 1, j = 0; j < 16; i <<= 1, j++)
{
if (HalpEisaELCR & i)
{
/* Switch handler to level */
SWInterruptHandlerTable[j + 4] = HalpHardwareInterruptLevel;
/* Switch dismiss to level */
HalpSpecialDismissTable[j] = HalpSpecialDismissLevelTable[j];
}
}
}
/* Report cascade IRQ usage */
HalpRegisterVector(IDT_INTERNAL,
PRIMARY_VECTOR_BASE + PIC_CASCADE_IRQ,
PRIMARY_VECTOR_BASE + PIC_CASCADE_IRQ,
HIGH_LEVEL);
/* Restore interrupt state */
if (EnableInterrupts) EFlags |= EFLAGS_INTERRUPT_MASK;
__writeeflags(EFlags);
}
UCHAR
FASTCALL
HalpIrqToVector(UCHAR Irq)
{
return (PRIMARY_VECTOR_BASE + Irq);
}
UCHAR
FASTCALL
HalpVectorToIrq(UCHAR Vector)
{
return (Vector - PRIMARY_VECTOR_BASE);
}
KIRQL
FASTCALL
HalpVectorToIrql(UCHAR Vector)
{
return (PROFILE_LEVEL - (Vector - PRIMARY_VECTOR_BASE));
}
/* IRQL MANAGEMENT ************************************************************/
/*
* @implemented
*/
KIRQL
NTAPI
KeGetCurrentIrql(VOID)
{
/* Return the IRQL */
return KeGetPcr()->Irql;
}
/*
* @implemented
*/
KIRQL
NTAPI
KeRaiseIrqlToDpcLevel(VOID)
{
PKPCR Pcr = KeGetPcr();
KIRQL CurrentIrql;
/* Save and update IRQL */
CurrentIrql = Pcr->Irql;
Pcr->Irql = DISPATCH_LEVEL;
#if DBG
/* Validate correct raise */
if (CurrentIrql > DISPATCH_LEVEL) KeBugCheck(IRQL_NOT_GREATER_OR_EQUAL);
#endif
/* Return the previous value */
return CurrentIrql;
}
/*
* @implemented
*/
KIRQL
NTAPI
KeRaiseIrqlToSynchLevel(VOID)
{
PKPCR Pcr = KeGetPcr();
KIRQL CurrentIrql;
/* Save and update IRQL */
CurrentIrql = Pcr->Irql;
Pcr->Irql = SYNCH_LEVEL;
#if DBG
/* Validate correct raise */
if (CurrentIrql > SYNCH_LEVEL)
{
/* Crash system */
KeBugCheckEx(IRQL_NOT_GREATER_OR_EQUAL,
CurrentIrql,
SYNCH_LEVEL,
0,
1);
}
#endif
/* Return the previous value */
return CurrentIrql;
}
/*
* @implemented
*/
KIRQL
FASTCALL
KfRaiseIrql(IN KIRQL NewIrql)
{
PKPCR Pcr = KeGetPcr();
KIRQL CurrentIrql;
/* Read current IRQL */
CurrentIrql = Pcr->Irql;
#if DBG
/* Validate correct raise */
if (CurrentIrql > NewIrql)
{
/* Crash system */
Pcr->Irql = PASSIVE_LEVEL;
KeBugCheck(IRQL_NOT_GREATER_OR_EQUAL);
}
#endif
/* Set new IRQL */
Pcr->Irql = NewIrql;
/* Return old IRQL */
return CurrentIrql;
}
/*
* @implemented
*/
VOID
FASTCALL
KfLowerIrql(IN KIRQL OldIrql)
{
ULONG EFlags;
ULONG PendingIrql, PendingIrqlMask;
PKPCR Pcr = KeGetPcr();
PIC_MASK Mask;
#if DBG
/* Validate correct lower */
if (OldIrql > Pcr->Irql)
{
/* Crash system */
Pcr->Irql = HIGH_LEVEL;
KeBugCheck(IRQL_NOT_LESS_OR_EQUAL);
}
#endif
/* Save EFlags and disable interrupts */
EFlags = __readeflags();
_disable();
/* Set old IRQL */
Pcr->Irql = OldIrql;
/* Check for pending software interrupts and compare with current IRQL */
PendingIrqlMask = Pcr->IRR & FindHigherIrqlMask[OldIrql];
if (PendingIrqlMask)
{
/* Check if pending IRQL affects hardware state */
BitScanReverse(&PendingIrql, PendingIrqlMask);
if (PendingIrql > DISPATCH_LEVEL)
{
/* Set new PIC mask */
Mask.Both = Pcr->IDR & 0xFFFF;
__outbyte(PIC1_DATA_PORT, Mask.Master);
__outbyte(PIC2_DATA_PORT, Mask.Slave);
/* Clear IRR bit */
Pcr->IRR ^= (1 << PendingIrql);
}
/* Now handle pending interrupt */
SWInterruptHandlerTable[PendingIrql]();
}
/* Restore interrupt state */
__writeeflags(EFlags);
}
/* SOFTWARE INTERRUPTS ********************************************************/
/*
* @implemented
*/
VOID
FASTCALL
HalRequestSoftwareInterrupt(IN KIRQL Irql)
{
ULONG EFlags;
PKPCR Pcr = KeGetPcr();
KIRQL PendingIrql;
/* Save EFlags and disable interrupts */
EFlags = __readeflags();
_disable();
/* Mask out the requested bit */
Pcr->IRR |= (1 << Irql);
/* Check for pending software interrupts and compare with current IRQL */
PendingIrql = SWInterruptLookUpTable[Pcr->IRR & 3];
if (PendingIrql > Pcr->Irql) SWInterruptHandlerTable[PendingIrql]();
/* Restore interrupt state */
__writeeflags(EFlags);
}
/*
* @implemented
*/
VOID
FASTCALL
HalClearSoftwareInterrupt(IN KIRQL Irql)
{
/* Mask out the requested bit */
KeGetPcr()->IRR &= ~(1 << Irql);
}
PHAL_SW_INTERRUPT_HANDLER_2ND_ENTRY
FASTCALL
HalpEndSoftwareInterrupt2(IN KIRQL OldIrql,
IN PKTRAP_FRAME TrapFrame)
{
ULONG PendingIrql, PendingIrqlMask, PendingIrqMask;
PKPCR Pcr = KeGetPcr();
PIC_MASK Mask;
UNREFERENCED_PARAMETER(TrapFrame);
/* Set old IRQL */
Pcr->Irql = OldIrql;
/* Loop checking for pending interrupts */
while (TRUE)
{
/* Check for pending software interrupts and compare with current IRQL */
PendingIrqlMask = Pcr->IRR & FindHigherIrqlMask[OldIrql];
if (!PendingIrqlMask) return NULL;
/* Check for in-service delayed interrupt */
if (Pcr->IrrActive & 0xFFFFFFF0) return NULL;
/* Check if pending IRQL affects hardware state */
BitScanReverse(&PendingIrql, PendingIrqlMask);
if (PendingIrql > DISPATCH_LEVEL)
{
/* Set new PIC mask */
Mask.Both = Pcr->IDR & 0xFFFF;
__outbyte(PIC1_DATA_PORT, Mask.Master);
__outbyte(PIC2_DATA_PORT, Mask.Slave);
/* Set active bit otherwise, and clear it from IRR */
PendingIrqMask = (1 << PendingIrql);
Pcr->IrrActive |= PendingIrqMask;
Pcr->IRR ^= PendingIrqMask;
/* Handle delayed hardware interrupt */
SWInterruptHandlerTable[PendingIrql]();
/* Handling complete */
Pcr->IrrActive ^= PendingIrqMask;
}
else
{
/* No need to loop checking for hardware interrupts */
return SWInterruptHandlerTable2[PendingIrql];
}
}
return NULL;
}
/* EDGE INTERRUPT DISMISSAL FUNCTIONS *****************************************/
FORCEINLINE
BOOLEAN
_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;
/* Check if this interrupt is really allowed to happen */
if (Irql > CurrentIrql)
{
/* Set the new IRQL and return the current one */
Pcr->Irql = Irql;
*OldIrql = CurrentIrql;
/* Prepare OCW2 for EOI */
Ocw2.Bits = 0;
Ocw2.EoiMode = SpecificEoi;
/* Check which PIC needs the EOI */
if (Irq >= 8)
{
#if defined(SARCH_PC98)
I8259_OCW3 Ocw3;
I8259_ISR Isr;
/* Send the EOI for the IRQ */
__outbyte(PIC2_CONTROL_PORT, Ocw2.Bits | ((Irq - 8) & 0xFF));
/* Request the ISR */
Ocw3.Bits = 0;
Ocw3.Sbo = 1;
Ocw3.ReadRequest = ReadIsr;
__outbyte(PIC2_CONTROL_PORT, Ocw3.Bits);
/* Read the ISR */
Isr.Bits = __inbyte(PIC2_CONTROL_PORT);
/* Check if the interrupt serviced was the only one from the slave PIC */
if (Isr.Bits == 0)
{
/* If ISR is empty, send the EOI for cascade IRQ on the master PIC */
__outbyte(PIC1_CONTROL_PORT, Ocw2.Bits | PIC_CASCADE_IRQ);
}
#else
/* Send the EOI for the IRQ */
__outbyte(PIC2_CONTROL_PORT, Ocw2.Bits | ((Irq - 8) & 0xFF));
/* Send the EOI for cascade IRQ on the master PIC */
__outbyte(PIC1_CONTROL_PORT, Ocw2.Bits | PIC_CASCADE_IRQ);
#endif
}
else
{
/* Send the EOI for the IRQ */
__outbyte(PIC1_CONTROL_PORT, Ocw2.Bits | (Irq & 0xFF));
}
/* Enable interrupts and return success */
_enable();
return TRUE;
}
/* Update the IRR so that we deliver this interrupt when the IRQL is proper */
Pcr->IRR |= (1 << (Irq + 4));
/* Set new PIC mask to real IRQL level, since the optimization is lost now */
Mask.Both = (KiI8259MaskTable[CurrentIrql] | Pcr->IDR) & 0xFFFF;
__outbyte(PIC1_DATA_PORT, Mask.Master);
__outbyte(PIC2_DATA_PORT, Mask.Slave);
/* Now lie and say this was spurious */
return FALSE;
}
BOOLEAN
NTAPI
HalpDismissIrqGeneric(IN KIRQL Irql,
IN ULONG Irq,
OUT PKIRQL OldIrql)
{
/* Run the inline code */
return _HalpDismissIrqGeneric(Irql, Irq, OldIrql);
}
BOOLEAN
NTAPI
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 cascade IRQ */
Ocw2.Bits = 0;
Ocw2.EoiMode = SpecificEoi;
__outbyte(PIC1_CONTROL_PORT, Ocw2.Bits | PIC_CASCADE_IRQ);
/* And now fail since this was spurious */
return FALSE;
}
/* Do normal interrupt dismiss */
return _HalpDismissIrqGeneric(Irql, Irq, OldIrql);
}
BOOLEAN
NTAPI
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);
}
#if defined(SARCH_PC98)
BOOLEAN
NTAPI
HalpDismissIrq08(
_In_ KIRQL Irql,
_In_ ULONG Irq,
_Out_ PKIRQL OldIrql)
{
/* Clear the FPU busy latch */
__outbyte(CPU_IO_o_FPU_BUSY_LATCH, 0);
/* Do normal interrupt dismiss */
return _HalpDismissIrqGeneric(Irql, Irq, OldIrql);
}
#endif
BOOLEAN
NTAPI
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);
}
/* LEVEL INTERRUPT DISMISSAL FUNCTIONS ****************************************/
FORCEINLINE
BOOLEAN
_HalpDismissIrqLevel(IN KIRQL Irql,
IN ULONG Irq,
OUT PKIRQL OldIrql)
{
PIC_MASK Mask;
KIRQL CurrentIrql;
I8259_OCW2 Ocw2;
PKPCR Pcr = KeGetPcr();
/* Update the PIC */
Mask.Both = (KiI8259MaskTable[Irql] | Pcr->IDR) & 0xFFFF;
__outbyte(PIC1_DATA_PORT, Mask.Master);
__outbyte(PIC2_DATA_PORT, Mask.Slave);
/* Update the IRR so that we clear this interrupt when the IRQL is proper */
Pcr->IRR |= (1 << (Irq + 4));
/* Save current IRQL */
CurrentIrql = Pcr->Irql;
/* Prepare OCW2 for EOI */
Ocw2.Bits = 0;
Ocw2.EoiMode = SpecificEoi;
/* Check which PIC needs the EOI */
if (Irq >= 8)
{
#if defined(SARCH_PC98)
I8259_OCW3 Ocw3;
I8259_ISR Isr;
/* Send the EOI for the IRQ */
__outbyte(PIC2_CONTROL_PORT, Ocw2.Bits | ((Irq - 8) & 0xFF));
/* Request the ISR */
Ocw3.Bits = 0;
Ocw3.Sbo = 1;
Ocw3.ReadRequest = ReadIsr;
__outbyte(PIC2_CONTROL_PORT, Ocw3.Bits);
/* Read the ISR */
Isr.Bits = __inbyte(PIC2_CONTROL_PORT);
/* Check if the interrupt serviced was the only one from the slave PIC */
if (Isr.Bits == 0)
{
/* If ISR is empty, send the EOI for cascade IRQ on the master PIC */
__outbyte(PIC1_CONTROL_PORT, Ocw2.Bits | PIC_CASCADE_IRQ);
}
#else
/* Send the EOI for the IRQ */
__outbyte(PIC2_CONTROL_PORT, Ocw2.Bits | ((Irq - 8) & 0xFF));
/* Send the EOI for cascade IRQ on the master PIC */
__outbyte(PIC1_CONTROL_PORT, Ocw2.Bits | PIC_CASCADE_IRQ);
#endif
}
else
{
/* Send the EOI for the IRQ */
__outbyte(PIC1_CONTROL_PORT, Ocw2.Bits | (Irq & 0xFF));
}
/* Check if this interrupt should be allowed to happen */
if (Irql > CurrentIrql)
{
/* Set the new IRQL and return the current one */
Pcr->Irql = Irql;
*OldIrql = CurrentIrql;
/* Enable interrupts and return success */
_enable();
return TRUE;
}
/* Now lie and say this was spurious */
return FALSE;
}
BOOLEAN
NTAPI
HalpDismissIrqLevel(IN KIRQL Irql,
IN ULONG Irq,
OUT PKIRQL OldIrql)
{
/* Run the inline code */
return _HalpDismissIrqLevel(Irql, Irq, OldIrql);
}
BOOLEAN
NTAPI
HalpDismissIrq15Level(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 cascade IRQ */
Ocw2.Bits = 0;
Ocw2.EoiMode = SpecificEoi;
__outbyte(PIC1_CONTROL_PORT, Ocw2.Bits | PIC_CASCADE_IRQ);
/* And now fail since this was spurious */
return FALSE;
}
/* Do normal interrupt dismiss */
return _HalpDismissIrqLevel(Irql, Irq, OldIrql);
}
BOOLEAN
NTAPI
HalpDismissIrq13Level(IN KIRQL Irql,
IN ULONG Irq,
OUT PKIRQL OldIrql)
{
/* Clear the FPU busy latch */
__outbyte(0xF0, 0);
/* Do normal interrupt dismiss */
return _HalpDismissIrqLevel(Irql, Irq, OldIrql);
}
#if defined(SARCH_PC98)
BOOLEAN
NTAPI
HalpDismissIrq08Level(
_In_ KIRQL Irql,
_In_ ULONG Irq,
_Out_ PKIRQL OldIrql)
{
/* Clear the FPU busy latch */
__outbyte(CPU_IO_o_FPU_BUSY_LATCH, 0);
/* Do normal interrupt dismiss */
return _HalpDismissIrqLevel(Irql, Irq, OldIrql);
}
#endif
BOOLEAN
NTAPI
HalpDismissIrq07Level(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 _HalpDismissIrqLevel(Irql, Irq, OldIrql);
}
PHAL_SW_INTERRUPT_HANDLER
__cdecl
HalpHardwareInterruptLevel2(VOID)
{
PKPCR Pcr = KeGetPcr();
ULONG PendingIrqlMask, PendingIrql;
/* Check for pending software interrupts and compare with current IRQL */
PendingIrqlMask = Pcr->IRR & FindHigherIrqlMask[Pcr->Irql];
if (PendingIrqlMask)
{
/* Check for in-service delayed interrupt */
if (Pcr->IrrActive & 0xFFFFFFF0) return NULL;
/* Check if pending IRQL affects hardware state */
BitScanReverse(&PendingIrql, PendingIrqlMask);
/* Clear IRR bit */
Pcr->IRR ^= (1 << PendingIrql);
/* Now handle pending interrupt */
return SWInterruptHandlerTable[PendingIrql];
}
return NULL;
}
/* SYSTEM INTERRUPTS **********************************************************/
/*
* @implemented
*/
BOOLEAN
NTAPI
HalEnableSystemInterrupt(IN ULONG Vector,
IN KIRQL Irql,
IN KINTERRUPT_MODE InterruptMode)
{
ULONG Irq;
PKPCR Pcr = KeGetPcr();
PIC_MASK PicMask;
/* Validate the IRQ */
Irq = Vector - PRIMARY_VECTOR_BASE;
if (Irq >= CLOCK2_LEVEL) return FALSE;
/* Check for level interrupt */
if (InterruptMode == LevelSensitive)
{
/* Switch handler to level */
SWInterruptHandlerTable[Irq + 4] = HalpHardwareInterruptLevel;
/* Switch dismiss to level */
HalpSpecialDismissTable[Irq] = HalpSpecialDismissLevelTable[Irq];
}
/* Disable interrupts */
_disable();
/* Update software IDR */
Pcr->IDR &= ~(1 << Irq);
/* Set new PIC mask */
PicMask.Both = (KiI8259MaskTable[Pcr->Irql] | Pcr->IDR) & 0xFFFF;
__outbyte(PIC1_DATA_PORT, PicMask.Master);
__outbyte(PIC2_DATA_PORT, PicMask.Slave);
/* Enable interrupts and exit */
_enable();
return TRUE;
}
/*
* @implemented
*/
VOID
NTAPI
HalDisableSystemInterrupt(IN ULONG Vector,
IN KIRQL Irql)
{
ULONG IrqMask;
PIC_MASK PicMask;
/* Compute new combined IRQ mask */
IrqMask = 1 << (Vector - PRIMARY_VECTOR_BASE);
/* Disable interrupts */
_disable();
/* Update software IDR */
KeGetPcr()->IDR |= IrqMask;
/* Read current interrupt mask */
PicMask.Master = __inbyte(PIC1_DATA_PORT);
PicMask.Slave = __inbyte(PIC2_DATA_PORT);
/* Add the new disabled interrupt */
PicMask.Both |= IrqMask;
/* Write new interrupt mask */
__outbyte(PIC1_DATA_PORT, PicMask.Master);
__outbyte(PIC2_DATA_PORT, PicMask.Slave);
/* Bring interrupts back */
_enable();
}
/*
* @implemented
*/
BOOLEAN
NTAPI
HalBeginSystemInterrupt(IN KIRQL Irql,
IN ULONG 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);
}
/*
* @implemented
*/
PHAL_SW_INTERRUPT_HANDLER_2ND_ENTRY
FASTCALL
HalEndSystemInterrupt2(IN KIRQL OldIrql,
IN PKTRAP_FRAME TrapFrame)
{
ULONG PendingIrql, PendingIrqlMask, PendingIrqMask;
PKPCR Pcr = KeGetPcr();
PIC_MASK Mask;
/* Set old IRQL */
Pcr->Irql = OldIrql;
/* Check for pending software interrupts and compare with current IRQL */
PendingIrqlMask = Pcr->IRR & FindHigherIrqlMask[OldIrql];
if (PendingIrqlMask)
{
/* Check for in-service delayed interrupt */
if (Pcr->IrrActive & 0xFFFFFFF0) return NULL;
/* Loop checking for pending interrupts */
while (TRUE)
{
/* Check if pending IRQL affects hardware state */
BitScanReverse(&PendingIrql, PendingIrqlMask);
if (PendingIrql > DISPATCH_LEVEL)
{
/* Set new PIC mask */
Mask.Both = Pcr->IDR & 0xFFFF;
__outbyte(PIC1_DATA_PORT, Mask.Master);
__outbyte(PIC2_DATA_PORT, Mask.Slave);
/* Now check if this specific interrupt is already in-service */
PendingIrqMask = (1 << PendingIrql);
if (Pcr->IrrActive & PendingIrqMask) return NULL;
/* Set active bit otherwise, and clear it from IRR */
Pcr->IrrActive |= PendingIrqMask;
Pcr->IRR ^= PendingIrqMask;
/* Handle delayed hardware interrupt */
SWInterruptHandlerTable[PendingIrql]();
/* Handling complete */
Pcr->IrrActive ^= PendingIrqMask;
/* Check if there's still interrupts pending */
PendingIrqlMask = Pcr->IRR & FindHigherIrqlMask[Pcr->Irql];
if (!PendingIrqlMask) break;
}
else
{
/* Now handle pending software interrupt */
return SWInterruptHandlerTable2[PendingIrql];
}
}
}
return NULL;
}
/* SOFTWARE INTERRUPT TRAPS ***************************************************/
FORCEINLINE
DECLSPEC_NORETURN
VOID
_HalpApcInterruptHandler(IN PKTRAP_FRAME TrapFrame)
{
KIRQL CurrentIrql;
PKPCR Pcr = KeGetPcr();
/* Save the current IRQL and update it */
CurrentIrql = Pcr->Irql;
Pcr->Irql = APC_LEVEL;
/* Remove DPC from IRR */
Pcr->IRR &= ~(1 << APC_LEVEL);
/* Enable interrupts and call the kernel's APC interrupt handler */
_enable();
KiDeliverApc(((KiUserTrap(TrapFrame)) || (TrapFrame->EFlags & EFLAGS_V86_MASK)) ?
UserMode : KernelMode,
NULL,
TrapFrame);
/* Disable interrupts and end the interrupt */
_disable();
HalpEndSoftwareInterrupt(CurrentIrql, TrapFrame);
/* Exit the interrupt */
KiEoiHelper(TrapFrame);
}
DECLSPEC_NORETURN
VOID
FASTCALL
HalpApcInterrupt2ndEntry(IN PKTRAP_FRAME TrapFrame)
{
/* Do the work */
_HalpApcInterruptHandler(TrapFrame);
}
DECLSPEC_NORETURN
VOID
FASTCALL
HalpApcInterruptHandler(IN PKTRAP_FRAME TrapFrame)
{
/* Set up a fake INT Stack */
TrapFrame->EFlags = __readeflags();
TrapFrame->SegCs = KGDT_R0_CODE;
TrapFrame->Eip = TrapFrame->Eax;
/* Build the trap frame */
KiEnterInterruptTrap(TrapFrame);
/* Do the work */
_HalpApcInterruptHandler(TrapFrame);
}
FORCEINLINE
KIRQL
_HalpDispatchInterruptHandler(VOID)
{
KIRQL CurrentIrql;
PKPCR Pcr = KeGetPcr();
/* Save the current IRQL and update it */
CurrentIrql = Pcr->Irql;
Pcr->Irql = DISPATCH_LEVEL;
/* Remove DPC from IRR */
Pcr->IRR &= ~(1 << DISPATCH_LEVEL);
/* Enable interrupts and call the kernel's DPC interrupt handler */
_enable();
KiDispatchInterrupt();
_disable();
/* Return IRQL */
return CurrentIrql;
}
DECLSPEC_NORETURN
VOID
FASTCALL
HalpDispatchInterrupt2ndEntry(IN PKTRAP_FRAME TrapFrame)
{
KIRQL CurrentIrql;
/* Do the work */
CurrentIrql = _HalpDispatchInterruptHandler();
/* End the interrupt */
HalpEndSoftwareInterrupt(CurrentIrql, TrapFrame);
/* Exit the interrupt */
KiEoiHelper(TrapFrame);
}
PHAL_SW_INTERRUPT_HANDLER
__cdecl
HalpDispatchInterrupt2(VOID)
{
ULONG PendingIrqlMask, PendingIrql;
KIRQL OldIrql;
PIC_MASK Mask;
PKPCR Pcr = KeGetPcr();
/* Do the work */
OldIrql = _HalpDispatchInterruptHandler();
/* Restore IRQL */
Pcr->Irql = OldIrql;
/* Check for pending software interrupts and compare with current IRQL */
PendingIrqlMask = Pcr->IRR & FindHigherIrqlMask[OldIrql];
if (PendingIrqlMask)
{
/* Check if pending IRQL affects hardware state */
BitScanReverse(&PendingIrql, PendingIrqlMask);
if (PendingIrql > DISPATCH_LEVEL)
{
/* Set new PIC mask */
Mask.Both = Pcr->IDR & 0xFFFF;
__outbyte(PIC1_DATA_PORT, Mask.Master);
__outbyte(PIC2_DATA_PORT, Mask.Slave);
/* Clear IRR bit */
Pcr->IRR ^= (1 << PendingIrql);
}
/* Now handle pending interrupt */
return SWInterruptHandlerTable[PendingIrql];
}
return NULL;
}
#else /* _MINIHAL_ */
KIRQL
NTAPI
KeGetCurrentIrql(VOID)
{
return PASSIVE_LEVEL;
}
VOID
FASTCALL
KfLowerIrql(
IN KIRQL OldIrql)
{
}
KIRQL
FASTCALL
KfRaiseIrql(
IN KIRQL NewIrql)
{
return NewIrql;
}
#endif /* !_MINIHAL_ */