/* GLOBALS ********************************************************************/
-USHORT HalpEisaELCR;
+/*
+ * 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
+};
+
+/*
+ * 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,
+ HalpDismissIrqLevel,
+ HalpDismissIrqLevel,
+ HalpDismissIrqLevel,
+ HalpDismissIrqLevel,
+ HalpDismissIrqLevel,
+ HalpDismissIrq13Level,
+ HalpDismissIrqLevel,
+ HalpDismissIrq15Level
+};
+
+/* This table contains the static x86 PIC mapping between IRQLs and IRQs */
+ULONG KiI8259MaskTable[32] =
+{
+#ifdef __GNUC__
+#if __GNUC__ * 100 + __GNUC_MINOR__ >= 404
+ /*
+ * It Device IRQLs only start at 4 or higher, so these are just software
+ * IRQLs that don't really change anything on the hardware
+ */
+ 0b00000000000000000000000000000000, /* IRQL 0 */
+ 0b00000000000000000000000000000000, /* IRQL 1 */
+ 0b00000000000000000000000000000000, /* IRQL 2 */
+ 0b00000000000000000000000000000000, /* IRQL 3 */
+
+ /*
+ * These next IRQLs are actually useless from the PIC perspective, because
+ * with only 2 PICs, the mask you can send them is only 8 bits each, for 16
+ * bits total, so these IRQLs are masking off a phantom PIC.
+ */
+ 0b11111111100000000000000000000000, /* IRQL 4 */
+ 0b11111111110000000000000000000000, /* IRQL 5 */
+ 0b11111111111000000000000000000000, /* IRQL 6 */
+ 0b11111111111100000000000000000000, /* IRQL 7 */
+ 0b11111111111110000000000000000000, /* IRQL 8 */
+ 0b11111111111111000000000000000000, /* IRQL 9 */
+ 0b11111111111111100000000000000000, /* IRQL 10 */
+ 0b11111111111111110000000000000000, /* IRQL 11 */
+
+ /*
+ * Okay, now we're finally starting to mask off IRQs on the slave PIC, from
+ * IRQ15 to IRQ8. This means the higher-level IRQs get less priority in the
+ * IRQL sense.
+ */
+ 0b11111111111111111000000000000000, /* IRQL 12 */
+ 0b11111111111111111100000000000000, /* IRQL 13 */
+ 0b11111111111111111110000000000000, /* IRQL 14 */
+ 0b11111111111111111111000000000000, /* IRQL 15 */
+ 0b11111111111111111111100000000000, /* IRQL 16 */
+ 0b11111111111111111111110000000000, /* IRQL 17 */
+ 0b11111111111111111111111000000000, /* IRQL 18 */
+ 0b11111111111111111111111000000000, /* IRQL 19 */
+
+ /*
+ * Now we mask off the IRQs on the master. Notice the 0 "droplet"? You might
+ * have also seen that IRQL 18 and 19 are essentially equal as far as the
+ * PIC is concerned. That bit is actually IRQ8, which happens to be the RTC.
+ * The RTC will keep firing as long as we don't reach PROFILE_LEVEL which
+ * actually kills it. The RTC clock (unlike the system clock) is used by the
+ * profiling APIs in the HAL, so that explains the logic.
+ */
+ 0b11111111111111111111111010000000, /* IRQL 20 */
+ 0b11111111111111111111111011000000, /* IRQL 21 */
+ 0b11111111111111111111111011100000, /* IRQL 22 */
+ 0b11111111111111111111111011110000, /* IRQL 23 */
+ 0b11111111111111111111111011111000, /* IRQL 24 */
+ 0b11111111111111111111111011111000, /* IRQL 25 */
+ 0b11111111111111111111111011111010, /* IRQL 26 */
+ 0b11111111111111111111111111111010, /* IRQL 27 */
+
+ /*
+ * IRQL 24 and 25 are actually identical, so IRQL 28 is actually the last
+ * IRQL to modify a bit on the master PIC. It happens to modify the very
+ * last of the IRQs, IRQ0, which corresponds to the system clock interval
+ * timer that keeps track of time (the Windows heartbeat). We only want to
+ * turn this off at a high-enough IRQL, which is why IRQLs 24 and 25 are the
+ * same to give this guy a chance to come up higher. Note that IRQL 28 is
+ * called CLOCK2_LEVEL, which explains the usage we just explained.
+ */
+ 0b11111111111111111111111111111011, /* IRQL 28 */
+
+ /*
+ * We have finished off with the PIC so there's nothing left to mask at the
+ * level of these IRQLs, making them only logical IRQLs on x86 machines.
+ * Note that we have another 0 "droplet" you might've caught since IRQL 26.
+ * In this case, it's the 2nd bit that never gets turned off, which is IRQ2,
+ * the cascade IRQ that we use to bridge the slave PIC with the master PIC.
+ * We never want to turn it off, so no matter the IRQL, it will be set to 0.
+ */
+ 0b11111111111111111111111111111011, /* IRQL 29 */
+ 0b11111111111111111111111111111011, /* IRQL 30 */
+ 0b11111111111111111111111111111011 /* IRQL 31 */
+#else
+ 0, /* IRQL 0 */
+ 0, /* IRQL 1 */
+ 0, /* IRQL 2 */
+ 0, /* IRQL 3 */
+ 0xFF800000, /* IRQL 4 */
+ 0xFFC00000, /* IRQL 5 */
+ 0xFFE00000, /* IRQL 6 */
+ 0xFFF00000, /* IRQL 7 */
+ 0xFFF80000, /* IRQL 8 */
+ 0xFFFC0000, /* IRQL 9 */
+ 0xFFFE0000, /* IRQL 10 */
+ 0xFFFF0000, /* IRQL 11 */
+ 0xFFFF8000, /* IRQL 12 */
+ 0xFFFFC000, /* IRQL 13 */
+ 0xFFFFE000, /* IRQL 14 */
+ 0xFFFFF000, /* IRQL 15 */
+ 0xFFFFF800, /* IRQL 16 */
+ 0xFFFFFC00, /* IRQL 17 */
+ 0xFFFFFE00, /* IRQL 18 */
+ 0xFFFFFE00, /* IRQL 19 */
+ 0xFFFFFE80, /* IRQL 20 */
+ 0xFFFFFEC0, /* IRQL 21 */
+ 0xFFFFFEE0, /* IRQL 22 */
+ 0xFFFFFEF0, /* IRQL 23 */
+ 0xFFFFFEF8, /* IRQL 24 */
+ 0xFFFFFEF8, /* IRQL 25 */
+ 0xFFFFFEFA, /* IRQL 26 */
+ 0xFFFFFFFA, /* IRQL 27 */
+ 0xFFFFFFFB, /* IRQL 28 */
+ 0xFFFFFFFB, /* IRQL 29 */
+ 0xFFFFFFFB, /* IRQL 30 */
+ 0xFFFFFFFB /* IRQL 31 */
+#endif
+#endif
+};
+
+/* This table indicates which IRQs, if pending, can preempt a given IRQL level */
+ULONG FindHigherIrqlMask[32] =
+{
+#ifdef __GNUC__
+#if __GNUC__ * 100 + __GNUC_MINOR__ >= 404
+ /*
+ * Software IRQLs, at these levels all hardware interrupts can preempt.
+ * Each higher IRQL simply enables which software IRQL can preempt the
+ * current level.
+ */
+ 0b11111111111111111111111111111110, /* IRQL 0 */
+ 0b11111111111111111111111111111100, /* IRQL 1 */
+ 0b11111111111111111111111111111000, /* IRQL 2 */
+
+ /*
+ * IRQL3 means only hardware IRQLs can now preempt. These last 4 zeros will
+ * then continue throughout the rest of the list, trickling down.
+ */
+ 0b11111111111111111111111111110000, /* IRQL 3 */
+
+ /*
+ * Just like in the previous list, these masks don't really mean anything
+ * since we've only got two PICs with 16 possible IRQs total
+ */
+ 0b00000111111111111111111111110000, /* IRQL 4 */
+ 0b00000011111111111111111111110000, /* IRQL 5 */
+ 0b00000001111111111111111111110000, /* IRQL 6 */
+ 0b00000000111111111111111111110000, /* IRQL 7 */
+ 0b00000000011111111111111111110000, /* IRQL 8 */
+ 0b00000000001111111111111111110000, /* IRQL 9 */
+ 0b00000000000111111111111111110000, /* IRQL 10 */
+
+ /*
+ * Now we start progressivly limiting which slave PIC interrupts have the
+ * right to preempt us at each level.
+ */
+ 0b00000000000011111111111111110000, /* IRQL 11 */
+ 0b00000000000001111111111111110000, /* IRQL 12 */
+ 0b00000000000000111111111111110000, /* IRQL 13 */
+ 0b00000000000000011111111111110000, /* IRQL 14 */
+ 0b00000000000000001111111111110000, /* IRQL 15 */
+ 0b00000000000000000111111111110000, /* IRQL 16 */
+ 0b00000000000000000011111111110000, /* IRQL 17 */
+ 0b00000000000000000001111111110000, /* IRQL 18 */
+ 0b00000000000000000001111111110000, /* IRQL 19 */
+
+ /*
+ * Also recall from the earlier table that IRQL 18/19 are treated the same
+ * in order to spread the masks better thoughout the 32 IRQLs and to reflect
+ * the fact that some bits will always stay on until much higher IRQLs since
+ * they are system-critical. One such example is the 1 bit that you start to
+ * see trickling down here. This is IRQ8, the RTC timer used for profiling,
+ * so it will always preempt until we reach PROFILE_LEVEL.
+ */
+ 0b00000000000000000001011111110000, /* IRQL 20 */
+ 0b00000000000000000001001111110000, /* IRQL 20 */
+ 0b00000000000000000001000111110000, /* IRQL 22 */
+ 0b00000000000000000001000011110000, /* IRQL 23 */
+ 0b00000000000000000001000001110000, /* IRQL 24 */
+ 0b00000000000000000001000000110000, /* IRQL 25 */
+ 0b00000000000000000001000000010000, /* IRQL 26 */
+
+ /* At this point, only the clock (IRQ0) can still preempt... */
+ 0b00000000000000000000000000010000, /* IRQL 27 */
+
+ /* And any higher than that there's no relation with hardware PICs anymore */
+ 0b00000000000000000000000000000000, /* IRQL 28 */
+ 0b00000000000000000000000000000000, /* IRQL 29 */
+ 0b00000000000000000000000000000000, /* IRQL 30 */
+ 0b00000000000000000000000000000000, /* IRQL 31 */
+#else
+ 0xFFFFFFFE, /* IRQL 0 */
+ 0xFFFFFFFC, /* IRQL 1 */
+ 0xFFFFFFF8, /* IRQL 2 */
+ 0xFFFFFFF0, /* IRQL 3 */
+ 0x7FFFFF0, /* IRQL 4 */
+ 0x3FFFFF0, /* IRQL 5 */
+ 0x1FFFFF0, /* IRQL 6 */
+ 0x0FFFFF0, /* IRQL 7 */
+ 0x7FFFF0, /* IRQL 8 */
+ 0x3FFFF0, /* IRQL 9 */
+ 0x1FFFF0, /* IRQL 10 */
+ 0x0FFFF0, /* IRQL 11 */
+ 0x7FFF0, /* IRQL 12 */
+ 0x3FFF0, /* IRQL 13 */
+ 0x1FFF0, /* IRQL 14 */
+ 0x0FFF0, /* IRQL 15 */
+ 0x7FF0, /* IRQL 16 */
+ 0x3FF0, /* IRQL 17 */
+ 0x1FF0, /* IRQL 18 */
+ 0x1FF0, /* IRQL 19 */
+ 0x17F0, /* IRQL 20 */
+ 0x13F0, /* IRQL 21 */
+ 0x11F0, /* IRQL 22 */
+ 0x10F0, /* IRQL 23 */
+ 0x1070, /* IRQL 24 */
+ 0x1030, /* IRQL 25 */
+ 0x1010, /* IRQL 26 */
+ 0x10, /* IRQL 27 */
+ 0, /* IRQL 28 */
+ 0, /* IRQL 29 */
+ 0, /* IRQL 30 */
+ 0 /* IRQL 31 */
+#endif
+#endif
+};
+
+/* 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 */
+};
+
+#define HalpDelayedHardwareInterrupt(x) \
+ VOID HalpHardwareInterrupt##x(VOID); \
+ VOID \
+ HalpHardwareInterrupt##x(VOID) \
+ { \
+ asm volatile ("int $%c0\n"::"i"(PRIMARY_VECTOR_BASE + x)); \
+ }
+
+/* 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] =
+{
+ KiUnexpectedInterrupt,
+ HalpApcInterrupt,
+ HalpDispatchInterrupt2,
+ 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)KiUnexpectedInterrupt,
+ HalpApcInterrupt2ndEntry,
+ HalpDispatchInterrupt2ndEntry
+};
+
+LONG HalpEisaELCR;
/* FUNCTIONS ******************************************************************/
/* Initialize ICW1 for master, interval 8, edge-triggered mode with ICW4 */
Icw1.NeedIcw4 = TRUE;
+ Icw1.InterruptMode = EdgeTriggered;
Icw1.OperatingMode = Cascade;
Icw1.Interval = Interval8;
Icw1.Init = TRUE;
/* Initialize ICW1 for master, interval 8, edge-triggered mode with ICW4 */
Icw1.NeedIcw4 = TRUE;
+ Icw1.InterruptMode = EdgeTriggered;
Icw1.OperatingMode = Cascade;
Icw1.Interval = Interval8;
Icw1.Init = TRUE;
{
/* ELCR is as it's supposed to be, save it */
HalpEisaELCR = Elcr.Bits;
- DPRINT1("HAL Detected EISA Interrupt Controller (ELCR: %lx)\n", HalpEisaELCR);
/* Scan for level interrupts */
for (i = 1, j = 0; j < 16; i <<= 1, j++)
{
- /* Warn the user ReactOS does not (and has never) supported this */
- if (HalpEisaELCR & i) DPRINT1("WARNING: IRQ %d is SHARED and LEVEL-SENSITIVE. This is unsupported!\n", j);
+ if (HalpEisaELCR & i)
+ {
+ /* Switch handler to level */
+ SWInterruptHandlerTable[j + 4] = HalpHardwareInterruptLevel;
+
+ /* Switch dismiss to level */
+ HalpSpecialDismissTable[j] = HalpSpecialDismissLevelTable[j];
+ }
}
}
__writeeflags(EFlags);
}
+/* 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;
+
+#ifdef IRQL_DEBUG
+ /* 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;
+
+#ifdef IRQL_DEBUG
+ /* 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;
+
+#ifdef IRQL_DEBUG
+ /* 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;
+
+#ifdef IRQL_DEBUG
+ /* 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;
+ __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);
+}
+
+VOID
+NTAPI
+HalpEndSoftwareInterrupt(IN KIRQL OldIrql,
+ IN PKTRAP_FRAME TrapFrame)
+{
+ ULONG PendingIrql, PendingIrqlMask, PendingIrqMask;
+ PKPCR Pcr = KeGetPcr();
+ PIC_MASK Mask;
+
+ /* 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;
+
+ /* Check for in-service delayed interrupt */
+ if (Pcr->IrrActive & 0xFFFFFFF0) return;
+
+ /* Check if pending IRQL affects hardware state */
+ BitScanReverse(&PendingIrql, PendingIrqlMask);
+ if (PendingIrql > DISPATCH_LEVEL)
+ {
+ /* Set new PIC mask */
+ Mask.Both = Pcr->IDR;
+ __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 */
+ SWInterruptHandlerTable2[PendingIrql](TrapFrame);
+ }
+ }
+}
+
+/* EDGE 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;
+
+ /* 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)
+ {
+ /* 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;
+ }
+
+ /* 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;
+ __outbyte(PIC1_DATA_PORT, Mask.Master);
+ __outbyte(PIC2_DATA_PORT, Mask.Slave);
+
+ /* Now lie and say this was spurious */
+ return FALSE;
+}
+
+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);
+}
+
+/* LEVEL INTERRUPT DISMISSAL FUNCTIONS ****************************************/
+
+BOOLEAN
+FORCEINLINE
+_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;
+ __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)
+ {
+ /* 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);
+ }
+
+ /* 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
+__attribute__((regparm(3)))
+HalpDismissIrqLevel(IN KIRQL Irql,
+ IN ULONG Irq,
+ OUT PKIRQL OldIrql)
+{
+ /* Run the inline code */
+ return _HalpDismissIrqLevel(Irql, Irq, OldIrql);
+}
+
+BOOLEAN
+__attribute__((regparm(3)))
+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 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 _HalpDismissIrqLevel(Irql, Irq, OldIrql);
+}
+
+BOOLEAN
+__attribute__((regparm(3)))
+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);
+}
+
+BOOLEAN
+__attribute__((regparm(3)))
+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);
+}
+
+VOID
+HalpHardwareInterruptLevel(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;
+
+ /* Check if pending IRQL affects hardware state */
+ BitScanReverse(&PendingIrql, PendingIrqlMask);
+
+ /* Clear IRR bit */
+ Pcr->IRR ^= (1 << PendingIrql);
+
+ /* Now handle pending interrupt */
+ SWInterruptHandlerTable[PendingIrql]();
+ }
+}
+
+/* SYSTEM INTERRUPTS **********************************************************/
+
+/*
+ * @implemented
+ */
+BOOLEAN
+NTAPI
+HalEnableSystemInterrupt(IN UCHAR 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;
+ __outbyte(PIC1_DATA_PORT, PicMask.Master);
+ __outbyte(PIC2_DATA_PORT, PicMask.Slave);
+
+ /* Enable interrupts and exit */
+ _enable();
+ return TRUE;
+}
+
+/*
+ * @implemented
+ */
+VOID
+NTAPI
+HalDisableSystemInterrupt(IN UCHAR 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 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);
+}
+
+/*
+ * @implemented
+ */
+VOID
+NTAPI
+HalEndSystemInterrupt(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;
+
+ /* 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;
+ __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;
+
+ /* 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 */
+ SWInterruptHandlerTable2[PendingIrql](TrapFrame);
+ }
+ }
+ }
+}
+
+/* SOFTWARE INTERRUPT TRAPS ***************************************************/
+
+VOID
+FORCEINLINE
+DECLSPEC_NORETURN
+_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);
+}
+
+VOID
+FASTCALL
+DECLSPEC_NORETURN
+HalpApcInterrupt2ndEntry(IN PKTRAP_FRAME TrapFrame)
+{
+ /* Do the work */
+ _HalpApcInterruptHandler(TrapFrame);
+}
+
+VOID
+FASTCALL
+DECLSPEC_NORETURN
+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);
+}
+
+KIRQL
+FORCEINLINE
+_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;
+}
+
+VOID
+FASTCALL
+DECLSPEC_NORETURN
+HalpDispatchInterrupt2ndEntry(IN PKTRAP_FRAME TrapFrame)
+{
+ KIRQL CurrentIrql;
+
+ /* Do the work */
+ CurrentIrql = _HalpDispatchInterruptHandler();
+
+ /* End the interrupt */
+ HalpEndSoftwareInterrupt(CurrentIrql, TrapFrame);
+
+ /* Exit the interrupt */
+ KiEoiHelper(TrapFrame);
+}
+
+VOID
+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;
+ __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]();
+ }
+}
+
+KiTrap(HalpApcInterrupt, KI_SOFTWARE_TRAP);