mirror of
https://github.com/reactos/reactos.git
synced 2024-11-10 00:34:39 +00:00
e1ef078741
The idea then would be to have the following behaviour (when specifying the following options in the kernel command line): /DEBUGPORT=COMi --> load KDCOM.DLL and use COMi port (i == 1,2,3,4) if possible. /DEBUGPORT=FOO --> load KDFOO.DLL (useful for KDUSB.DLL, KD1394.DLL, KDBAZIS.DLL for VirtualKD, etc...) /DEBUGPORT=ROSDBG:[COMi|SCREEN|FILE|GDB|...] --> load KDROSDBG.DLL which contains the ROS kernel debugger, and use COMi or SCREEN or... as output port. svn path=/branches/kd++/; revision=58883
1407 lines
40 KiB
C
1407 lines
40 KiB
C
/*
|
|
* PROJECT: ReactOS HAL
|
|
* LICENSE: BSD - See COPYING.ARM in the top level directory
|
|
* FILE: hal/halx86/generic/pic.c
|
|
* PURPOSE: HAL PIC Management and Control Code
|
|
* PROGRAMMERS: ReactOS Portable Systems Group
|
|
*/
|
|
|
|
/* INCLUDES *******************************************************************/
|
|
|
|
#include <hal.h>
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
/* 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,
|
|
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] =
|
|
{
|
|
#if defined(__GNUC__) && \
|
|
(__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
|
|
};
|
|
|
|
/* This table indicates which IRQs, if pending, can preempt a given IRQL level */
|
|
ULONG FindHigherIrqlMask[32] =
|
|
{
|
|
#if defined(__GNUC__) && \
|
|
(__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
|
|
};
|
|
|
|
/* 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 HalpHardwareInterrupt##x(VOID); \
|
|
VOID \
|
|
HalpHardwareInterrupt##x(VOID) \
|
|
{ \
|
|
asm volatile ("int $%c0\n"::"i"(PRIMARY_VECTOR_BASE + x)); \
|
|
}
|
|
|
|
#elif defined(_MSC_VER)
|
|
|
|
#define HalpDelayedHardwareInterrupt(x) \
|
|
VOID HalpHardwareInterrupt##x(VOID); \
|
|
VOID \
|
|
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] =
|
|
{
|
|
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 ******************************************************************/
|
|
|
|
VOID
|
|
NTAPI
|
|
HalpInitializePICs(IN BOOLEAN EnableInterrupts)
|
|
{
|
|
ULONG EFlags;
|
|
I8259_ICW1 Icw1;
|
|
I8259_ICW2 Icw2;
|
|
I8259_ICW3 Icw3;
|
|
I8259_ICW4 Icw4;
|
|
EISA_ELCR Elcr;
|
|
ULONG i, j;
|
|
|
|
/* Save EFlags and disable interrupts */
|
|
EFlags = __readeflags();
|
|
_disable();
|
|
|
|
/* 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;
|
|
Icw1.InterruptVectorAddress = 0; /* This is only used in MCS80/85 mode */
|
|
__outbyte(PIC1_CONTROL_PORT, Icw1.Bits);
|
|
|
|
/* Set interrupt vector base */
|
|
Icw2.Bits = PRIMARY_VECTOR_BASE;
|
|
__outbyte(PIC1_DATA_PORT, Icw2.Bits);
|
|
|
|
/* Connect slave to IRQ 2 */
|
|
Icw3.Bits = 0;
|
|
Icw3.SlaveIrq2 = TRUE;
|
|
__outbyte(PIC1_DATA_PORT, Icw3.Bits);
|
|
|
|
/* Enable 8086 mode, non-automatic EOI, non-buffered mode, non special fully nested mode */
|
|
Icw4.Reserved = 0;
|
|
Icw4.SystemMode = New8086Mode;
|
|
Icw4.EoiMode = NormalEoi;
|
|
Icw4.BufferedMode = NonBuffered;
|
|
Icw4.SpecialFullyNestedMode = FALSE;
|
|
__outbyte(PIC1_DATA_PORT, Icw4.Bits);
|
|
|
|
/* Mask all interrupts */
|
|
__outbyte(PIC1_DATA_PORT, 0xFF);
|
|
|
|
/* 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;
|
|
Icw1.InterruptVectorAddress = 0; /* This is only used in MCS80/85 mode */
|
|
__outbyte(PIC2_CONTROL_PORT, Icw1.Bits);
|
|
|
|
/* Set interrupt vector base */
|
|
Icw2.Bits = PRIMARY_VECTOR_BASE + 8;
|
|
__outbyte(PIC2_DATA_PORT, Icw2.Bits);
|
|
|
|
/* Slave ID */
|
|
Icw3.Bits = 0;
|
|
Icw3.SlaveId = 2;
|
|
__outbyte(PIC2_DATA_PORT, Icw3.Bits);
|
|
|
|
/* Enable 8086 mode, non-automatic EOI, non-buffered mode, non special fully nested mode */
|
|
Icw4.Reserved = 0;
|
|
Icw4.SystemMode = New8086Mode;
|
|
Icw4.EoiMode = NormalEoi;
|
|
Icw4.BufferedMode = NonBuffered;
|
|
Icw4.SpecialFullyNestedMode = FALSE;
|
|
__outbyte(PIC2_DATA_PORT, Icw4.Bits);
|
|
|
|
/* Mask all interrupts */
|
|
__outbyte(PIC2_DATA_PORT, 0xFF);
|
|
|
|
/* Read EISA Edge/Level Register for master and slave */
|
|
Elcr.Bits = (__inbyte(EISA_ELCR_SLAVE) << 8) | __inbyte(EISA_ELCR_MASTER);
|
|
|
|
/* IRQs 0, 1, 2, 8, and 13 are system-reserved and must be edge */
|
|
if (!(Elcr.Master.Irq0Level) && !(Elcr.Master.Irq1Level) && !(Elcr.Master.Irq2Level) &&
|
|
!(Elcr.Slave.Irq8Level) && !(Elcr.Slave.Irq13Level))
|
|
{
|
|
/* 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];
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Register IRQ 2 */
|
|
HalpRegisterVector(IDT_INTERNAL,
|
|
PRIMARY_VECTOR_BASE + 2,
|
|
PRIMARY_VECTOR_BASE + 2,
|
|
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);
|
|
}
|
|
|
|
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 & 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 */
|
|
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) & 0xFF));
|
|
|
|
/* 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 &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
|
|
REGISTERCALL
|
|
HalpDismissIrqGeneric(IN KIRQL Irql,
|
|
IN ULONG Irq,
|
|
OUT PKIRQL OldIrql)
|
|
{
|
|
/* Run the inline code */
|
|
return _HalpDismissIrqGeneric(Irql, Irq, OldIrql);
|
|
}
|
|
|
|
BOOLEAN
|
|
REGISTERCALL
|
|
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
|
|
REGISTERCALL
|
|
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
|
|
REGISTERCALL
|
|
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) & 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)
|
|
{
|
|
/* Send the EOI for the IRQ */
|
|
__outbyte(PIC2_CONTROL_PORT, Ocw2.Bits | ((Irq - 8) & 0xFF));
|
|
|
|
/* 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 & 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
|
|
REGISTERCALL
|
|
HalpDismissIrqLevel(IN KIRQL Irql,
|
|
IN ULONG Irq,
|
|
OUT PKIRQL OldIrql)
|
|
{
|
|
/* Run the inline code */
|
|
return _HalpDismissIrqLevel(Irql, Irq, OldIrql);
|
|
}
|
|
|
|
BOOLEAN
|
|
REGISTERCALL
|
|
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
|
|
REGISTERCALL
|
|
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
|
|
REGISTERCALL
|
|
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 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
|
|
*/
|
|
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 & 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;
|
|
|
|
/* 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
|
|
DECLSPEC_NORETURN
|
|
FASTCALL
|
|
HalpApcInterrupt2ndEntry(IN PKTRAP_FRAME TrapFrame)
|
|
{
|
|
/* Do the work */
|
|
_HalpApcInterruptHandler(TrapFrame);
|
|
}
|
|
|
|
VOID
|
|
DECLSPEC_NORETURN
|
|
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);
|
|
}
|
|
|
|
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
|
|
DECLSPEC_NORETURN
|
|
FASTCALL
|
|
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 & 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]();
|
|
}
|
|
}
|
|
|
|
#else
|
|
|
|
KIRQL
|
|
NTAPI
|
|
KeGetCurrentIrql(VOID)
|
|
{
|
|
return PASSIVE_LEVEL;
|
|
}
|
|
|
|
VOID
|
|
FASTCALL
|
|
KfLowerIrql(
|
|
IN KIRQL OldIrql)
|
|
{
|
|
}
|
|
|
|
KIRQL
|
|
FASTCALL
|
|
KfRaiseIrql(
|
|
IN KIRQL NewIrql)
|
|
{
|
|
return NewIrql;
|
|
}
|
|
|
|
#endif
|