mirror of
https://github.com/reactos/reactos.git
synced 2024-12-27 09:34:43 +00:00
893 lines
21 KiB
C
893 lines
21 KiB
C
/*
|
|
* PROJECT: ReactOS HAL
|
|
* LICENSE: GNU GPL - See COPYING in the top level directory
|
|
* FILE: hal/halx86/apic/apic.c
|
|
* PURPOSE: HAL APIC Management and Control Code
|
|
* PROGRAMMERS: Timo Kreuzer (timo.kreuzer@reactos.org)
|
|
* REFERENCES: https://web.archive.org/web/20190407074221/http://www.joseflores.com/docs/ExploringIrql.html
|
|
* http://www.codeproject.com/KB/system/soviet_kernel_hack.aspx
|
|
* http://bbs.unixmap.net/thread-2022-1-1.html
|
|
* https://www.codemachine.com/article_interruptdispatching.html
|
|
* https://www.osronline.com/article.cfm%5Earticle=211.htm
|
|
*/
|
|
|
|
/* INCLUDES *******************************************************************/
|
|
|
|
#include <hal.h>
|
|
#include "apicp.h"
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
#ifndef _M_AMD64
|
|
#define APIC_LAZY_IRQL
|
|
#endif
|
|
|
|
/* GLOBALS ********************************************************************/
|
|
|
|
ULONG ApicVersion;
|
|
UCHAR HalpVectorToIndex[256];
|
|
|
|
#ifndef _M_AMD64
|
|
const UCHAR
|
|
HalpIRQLtoTPR[32] =
|
|
{
|
|
0x00, /* 0 PASSIVE_LEVEL */
|
|
0x3d, /* 1 APC_LEVEL */
|
|
0x41, /* 2 DISPATCH_LEVEL */
|
|
0x41, /* 3 \ */
|
|
0x51, /* 4 \ */
|
|
0x61, /* 5 | */
|
|
0x71, /* 6 | */
|
|
0x81, /* 7 | */
|
|
0x91, /* 8 | */
|
|
0xa1, /* 9 | */
|
|
0xb1, /* 10 | */
|
|
0xb1, /* 11 | */
|
|
0xb1, /* 12 | */
|
|
0xb1, /* 13 | */
|
|
0xb1, /* 14 | */
|
|
0xb1, /* 15 DEVICE IRQL */
|
|
0xb1, /* 16 | */
|
|
0xb1, /* 17 | */
|
|
0xb1, /* 18 | */
|
|
0xb1, /* 19 | */
|
|
0xb1, /* 20 | */
|
|
0xb1, /* 21 | */
|
|
0xb1, /* 22 | */
|
|
0xb1, /* 23 | */
|
|
0xb1, /* 24 | */
|
|
0xb1, /* 25 / */
|
|
0xb1, /* 26 / */
|
|
0xc1, /* 27 PROFILE_LEVEL */
|
|
0xd1, /* 28 CLOCK2_LEVEL */
|
|
0xe1, /* 29 IPI_LEVEL */
|
|
0xef, /* 30 POWER_LEVEL */
|
|
0xff, /* 31 HIGH_LEVEL */
|
|
};
|
|
|
|
const KIRQL
|
|
HalVectorToIRQL[16] =
|
|
{
|
|
0, /* 00 PASSIVE_LEVEL */
|
|
0xff, /* 10 */
|
|
0xff, /* 20 */
|
|
1, /* 3D APC_LEVEL */
|
|
2, /* 41 DISPATCH_LEVEL */
|
|
4, /* 50 \ */
|
|
5, /* 60 \ */
|
|
6, /* 70 | */
|
|
7, /* 80 DEVICE IRQL */
|
|
8, /* 90 | */
|
|
9, /* A0 / */
|
|
10, /* B0 / */
|
|
27, /* C1 PROFILE_LEVEL */
|
|
28, /* D1 CLOCK2_LEVEL */
|
|
29, /* E1 IPI_LEVEL / EF POWER_LEVEL */
|
|
31, /* FF HIGH_LEVEL */
|
|
};
|
|
#endif
|
|
|
|
/* PRIVATE FUNCTIONS **********************************************************/
|
|
|
|
FORCEINLINE
|
|
ULONG
|
|
IOApicRead(UCHAR Register)
|
|
{
|
|
/* Select the register, then do the read */
|
|
ASSERT(Register <= 0x3F);
|
|
WRITE_REGISTER_ULONG((PULONG)(IOAPIC_BASE + IOAPIC_IOREGSEL), Register);
|
|
return READ_REGISTER_ULONG((PULONG)(IOAPIC_BASE + IOAPIC_IOWIN));
|
|
}
|
|
|
|
FORCEINLINE
|
|
VOID
|
|
IOApicWrite(UCHAR Register, ULONG Value)
|
|
{
|
|
/* Select the register, then do the write */
|
|
ASSERT(Register <= 0x3F);
|
|
WRITE_REGISTER_ULONG((PULONG)(IOAPIC_BASE + IOAPIC_IOREGSEL), Register);
|
|
WRITE_REGISTER_ULONG((PULONG)(IOAPIC_BASE + IOAPIC_IOWIN), Value);
|
|
}
|
|
|
|
FORCEINLINE
|
|
VOID
|
|
ApicWriteIORedirectionEntry(
|
|
UCHAR Index,
|
|
IOAPIC_REDIRECTION_REGISTER ReDirReg)
|
|
{
|
|
ASSERT(Index < APIC_MAX_IRQ);
|
|
IOApicWrite(IOAPIC_REDTBL + 2 * Index, ReDirReg.Long0);
|
|
IOApicWrite(IOAPIC_REDTBL + 2 * Index + 1, ReDirReg.Long1);
|
|
}
|
|
|
|
FORCEINLINE
|
|
IOAPIC_REDIRECTION_REGISTER
|
|
ApicReadIORedirectionEntry(
|
|
UCHAR Index)
|
|
{
|
|
IOAPIC_REDIRECTION_REGISTER ReDirReg;
|
|
|
|
ASSERT(Index < APIC_MAX_IRQ);
|
|
ReDirReg.Long0 = IOApicRead(IOAPIC_REDTBL + 2 * Index);
|
|
ReDirReg.Long1 = IOApicRead(IOAPIC_REDTBL + 2 * Index + 1);
|
|
|
|
return ReDirReg;
|
|
}
|
|
|
|
FORCEINLINE
|
|
VOID
|
|
ApicRequestSelfInterrupt(IN UCHAR Vector, UCHAR TriggerMode)
|
|
{
|
|
ULONG Flags;
|
|
APIC_INTERRUPT_COMMAND_REGISTER Icr;
|
|
APIC_INTERRUPT_COMMAND_REGISTER IcrStatus;
|
|
|
|
/*
|
|
* The IRR registers are spaced 16 bytes apart and hold 32 status bits each.
|
|
* Pre-compute the register and bit that match our vector.
|
|
*/
|
|
ULONG VectorHigh = Vector / 32;
|
|
ULONG VectorLow = Vector % 32;
|
|
ULONG Irr = APIC_IRR + 0x10 * VectorHigh;
|
|
ULONG IrrBit = 1UL << VectorLow;
|
|
|
|
/* Setup the command register */
|
|
Icr.Long0 = 0;
|
|
Icr.Vector = Vector;
|
|
Icr.MessageType = APIC_MT_Fixed;
|
|
Icr.TriggerMode = TriggerMode;
|
|
Icr.DestinationShortHand = APIC_DSH_Self;
|
|
|
|
/* Disable interrupts so that we can change IRR without being interrupted */
|
|
Flags = __readeflags();
|
|
_disable();
|
|
|
|
/* Wait for the APIC to be idle */
|
|
do
|
|
{
|
|
IcrStatus.Long0 = ApicRead(APIC_ICR0);
|
|
} while (IcrStatus.DeliveryStatus);
|
|
|
|
/* Write the low dword to send the interrupt */
|
|
ApicWrite(APIC_ICR0, Icr.Long0);
|
|
|
|
/* Wait until we see the interrupt request.
|
|
* It will stay in requested state until we re-enable interrupts.
|
|
*/
|
|
while (!(ApicRead(Irr) & IrrBit))
|
|
{
|
|
NOTHING;
|
|
}
|
|
|
|
/* Finally, restore the original interrupt state */
|
|
if (Flags & EFLAGS_INTERRUPT_MASK)
|
|
{
|
|
_enable();
|
|
}
|
|
}
|
|
|
|
FORCEINLINE
|
|
VOID
|
|
ApicSendEOI(void)
|
|
{
|
|
ApicWrite(APIC_EOI, 0);
|
|
}
|
|
|
|
FORCEINLINE
|
|
KIRQL
|
|
ApicGetProcessorIrql(VOID)
|
|
{
|
|
/* Read the TPR and convert it to an IRQL */
|
|
return TprToIrql(ApicRead(APIC_PPR));
|
|
}
|
|
|
|
FORCEINLINE
|
|
KIRQL
|
|
ApicGetCurrentIrql(VOID)
|
|
{
|
|
#ifdef _M_AMD64
|
|
return (KIRQL)__readcr8();
|
|
#elif defined(APIC_LAZY_IRQL)
|
|
/* Return the field in the PCR */
|
|
return (KIRQL)__readfsbyte(FIELD_OFFSET(KPCR, Irql));
|
|
#else
|
|
/* Read the TPR and convert it to an IRQL */
|
|
return TprToIrql(ApicRead(APIC_TPR));
|
|
#endif
|
|
}
|
|
|
|
FORCEINLINE
|
|
VOID
|
|
ApicSetIrql(KIRQL Irql)
|
|
{
|
|
#ifdef _M_AMD64
|
|
__writecr8(Irql);
|
|
#elif defined(APIC_LAZY_IRQL)
|
|
__writefsbyte(FIELD_OFFSET(KPCR, Irql), Irql);
|
|
#else
|
|
/* Convert IRQL and write the TPR */
|
|
ApicWrite(APIC_TPR, IrqlToTpr(Irql));
|
|
#endif
|
|
}
|
|
#define ApicRaiseIrql ApicSetIrql
|
|
|
|
#ifdef APIC_LAZY_IRQL
|
|
FORCEINLINE
|
|
VOID
|
|
ApicLowerIrql(KIRQL Irql)
|
|
{
|
|
__writefsbyte(FIELD_OFFSET(KPCR, Irql), Irql);
|
|
|
|
/* Is the new Irql lower than set in the TPR? */
|
|
if (Irql < KeGetPcr()->IRR)
|
|
{
|
|
/* Save the new hard IRQL in the IRR field */
|
|
KeGetPcr()->IRR = Irql;
|
|
|
|
/* Need to lower it back */
|
|
ApicWrite(APIC_TPR, IrqlToTpr(Irql));
|
|
}
|
|
}
|
|
#else
|
|
#define ApicLowerIrql ApicSetIrql
|
|
#endif
|
|
|
|
UCHAR
|
|
FASTCALL
|
|
HalpIrqToVector(UCHAR Irq)
|
|
{
|
|
IOAPIC_REDIRECTION_REGISTER ReDirReg;
|
|
|
|
/* Read low dword of the redirection entry */
|
|
ReDirReg.Long0 = IOApicRead(IOAPIC_REDTBL + 2 * Irq);
|
|
|
|
/* Return the vector */
|
|
return (UCHAR)ReDirReg.Vector;
|
|
}
|
|
|
|
KIRQL
|
|
FASTCALL
|
|
HalpVectorToIrql(UCHAR Vector)
|
|
{
|
|
return TprToIrql(Vector);
|
|
}
|
|
|
|
UCHAR
|
|
FASTCALL
|
|
HalpVectorToIrq(UCHAR Vector)
|
|
{
|
|
return HalpVectorToIndex[Vector];
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
HalpSendEOI(VOID)
|
|
{
|
|
ApicSendEOI();
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
ApicInitializeLocalApic(ULONG Cpu)
|
|
{
|
|
APIC_BASE_ADDRESS_REGISTER BaseRegister;
|
|
APIC_SPURIOUS_INERRUPT_REGISTER SpIntRegister;
|
|
LVT_REGISTER LvtEntry;
|
|
|
|
/* Enable the APIC if it wasn't yet */
|
|
BaseRegister.LongLong = __readmsr(MSR_APIC_BASE);
|
|
BaseRegister.Enable = 1;
|
|
BaseRegister.BootStrapCPUCore = (Cpu == 0);
|
|
__writemsr(MSR_APIC_BASE, BaseRegister.LongLong);
|
|
|
|
/* Set spurious vector and SoftwareEnable to 1 */
|
|
SpIntRegister.Long = ApicRead(APIC_SIVR);
|
|
SpIntRegister.Vector = APIC_SPURIOUS_VECTOR;
|
|
SpIntRegister.SoftwareEnable = 1;
|
|
SpIntRegister.FocusCPUCoreChecking = 0;
|
|
ApicWrite(APIC_SIVR, SpIntRegister.Long);
|
|
|
|
/* Read the version and save it globally */
|
|
if (Cpu == 0) ApicVersion = ApicRead(APIC_VER);
|
|
|
|
/* Set the mode to flat (max 8 CPUs supported!) */
|
|
ApicWrite(APIC_DFR, APIC_DF_Flat);
|
|
|
|
/* Set logical apic ID */
|
|
ApicWrite(APIC_LDR, ApicLogicalId(Cpu) << 24);
|
|
|
|
/* Set the spurious ISR */
|
|
KeRegisterInterruptHandler(APIC_SPURIOUS_VECTOR, ApicSpuriousService);
|
|
|
|
/* Create a template LVT */
|
|
LvtEntry.Long = 0;
|
|
LvtEntry.Vector = APIC_FREE_VECTOR;
|
|
LvtEntry.MessageType = APIC_MT_Fixed;
|
|
LvtEntry.DeliveryStatus = 0;
|
|
LvtEntry.RemoteIRR = 0;
|
|
LvtEntry.TriggerMode = APIC_TGM_Edge;
|
|
LvtEntry.Mask = 1;
|
|
LvtEntry.TimerMode = 0;
|
|
|
|
/* Initialize and mask LVTs */
|
|
ApicWrite(APIC_TMRLVTR, LvtEntry.Long);
|
|
ApicWrite(APIC_THRMLVTR, LvtEntry.Long);
|
|
ApicWrite(APIC_PCLVTR, LvtEntry.Long);
|
|
ApicWrite(APIC_EXT0LVTR, LvtEntry.Long);
|
|
ApicWrite(APIC_EXT1LVTR, LvtEntry.Long);
|
|
ApicWrite(APIC_EXT2LVTR, LvtEntry.Long);
|
|
ApicWrite(APIC_EXT3LVTR, LvtEntry.Long);
|
|
|
|
/* LINT0 */
|
|
LvtEntry.Vector = APIC_SPURIOUS_VECTOR;
|
|
LvtEntry.MessageType = APIC_MT_ExtInt;
|
|
ApicWrite(APIC_LINT0, LvtEntry.Long);
|
|
|
|
/* Enable LINT1 (NMI) */
|
|
LvtEntry.Mask = 0;
|
|
LvtEntry.Vector = APIC_NMI_VECTOR;
|
|
LvtEntry.MessageType = APIC_MT_NMI;
|
|
LvtEntry.TriggerMode = APIC_TGM_Level;
|
|
ApicWrite(APIC_LINT1, LvtEntry.Long);
|
|
|
|
/* Enable error LVTR */
|
|
LvtEntry.Vector = APIC_ERROR_VECTOR;
|
|
LvtEntry.MessageType = APIC_MT_Fixed;
|
|
ApicWrite(APIC_ERRLVTR, LvtEntry.Long);
|
|
|
|
/* Set the IRQL from the PCR */
|
|
ApicSetIrql(KeGetPcr()->Irql);
|
|
#ifdef APIC_LAZY_IRQL
|
|
/* Save the new hard IRQL in the IRR field */
|
|
KeGetPcr()->IRR = KeGetPcr()->Irql;
|
|
#endif
|
|
}
|
|
|
|
UCHAR
|
|
NTAPI
|
|
HalpAllocateSystemInterrupt(
|
|
_In_ UCHAR Irq,
|
|
_In_ UCHAR Vector)
|
|
{
|
|
IOAPIC_REDIRECTION_REGISTER ReDirReg;
|
|
|
|
ASSERT(Irq < APIC_MAX_IRQ);
|
|
ASSERT(HalpVectorToIndex[Vector] == APIC_FREE_VECTOR);
|
|
|
|
/* Setup a redirection entry */
|
|
ReDirReg.Vector = Vector;
|
|
ReDirReg.MessageType = APIC_MT_LowestPriority;
|
|
ReDirReg.DestinationMode = APIC_DM_Logical;
|
|
ReDirReg.DeliveryStatus = 0;
|
|
ReDirReg.Polarity = 0;
|
|
ReDirReg.RemoteIRR = 0;
|
|
ReDirReg.TriggerMode = APIC_TGM_Edge;
|
|
ReDirReg.Mask = 1;
|
|
ReDirReg.Reserved = 0;
|
|
ReDirReg.Destination = 0;
|
|
|
|
/* Initialize entry */
|
|
ApicWriteIORedirectionEntry(Irq, ReDirReg);
|
|
|
|
/* Save irq in the table */
|
|
HalpVectorToIndex[Vector] = Irq;
|
|
|
|
return Vector;
|
|
}
|
|
|
|
ULONG
|
|
NTAPI
|
|
HalpGetRootInterruptVector(
|
|
_In_ ULONG BusInterruptLevel,
|
|
_In_ ULONG BusInterruptVector,
|
|
_Out_ PKIRQL OutIrql,
|
|
_Out_ PKAFFINITY OutAffinity)
|
|
{
|
|
UCHAR Vector;
|
|
KIRQL Irql;
|
|
|
|
/* Get the vector currently registered */
|
|
Vector = HalpIrqToVector(BusInterruptLevel);
|
|
|
|
/* Check if it's used */
|
|
if (Vector != APIC_FREE_VECTOR)
|
|
{
|
|
/* Calculate IRQL */
|
|
NT_ASSERT(HalpVectorToIndex[Vector] == BusInterruptLevel);
|
|
*OutIrql = HalpVectorToIrql(Vector);
|
|
}
|
|
else
|
|
{
|
|
ULONG Offset;
|
|
|
|
/* Outer loop to find alternative slots, when all IRQLs are in use */
|
|
for (Offset = 0; Offset < 15; Offset++)
|
|
{
|
|
/* Loop allowed IRQL range */
|
|
for (Irql = CLOCK_LEVEL - 1; Irql >= CMCI_LEVEL; Irql--)
|
|
{
|
|
/* Calculate the vactor */
|
|
Vector = IrqlToTpr(Irql) + Offset;
|
|
|
|
/* Check if the vector is free */
|
|
if (HalpVectorToIrq(Vector) == APIC_FREE_VECTOR)
|
|
{
|
|
/* Found one, allocate the interrupt */
|
|
Vector = HalpAllocateSystemInterrupt(BusInterruptLevel, Vector);
|
|
*OutIrql = Irql;
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
DPRINT1("Failed to get an interrupt vector for IRQ %lu\n", BusInterruptLevel);
|
|
*OutAffinity = 0;
|
|
*OutIrql = 0;
|
|
return 0;
|
|
}
|
|
|
|
Exit:
|
|
|
|
*OutAffinity = HalpDefaultInterruptAffinity;
|
|
ASSERT(HalpDefaultInterruptAffinity);
|
|
|
|
return Vector;
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
ApicInitializeIOApic(VOID)
|
|
{
|
|
PHARDWARE_PTE Pte;
|
|
IOAPIC_REDIRECTION_REGISTER ReDirReg;
|
|
UCHAR Index;
|
|
ULONG Vector;
|
|
|
|
/* Map the I/O Apic page */
|
|
Pte = HalAddressToPte(IOAPIC_BASE);
|
|
Pte->PageFrameNumber = IOAPIC_PHYS_BASE / PAGE_SIZE;
|
|
Pte->Valid = 1;
|
|
Pte->Write = 1;
|
|
Pte->Owner = 1;
|
|
Pte->CacheDisable = 1;
|
|
Pte->Global = 1;
|
|
_ReadWriteBarrier();
|
|
|
|
/* Setup a redirection entry */
|
|
ReDirReg.Vector = APIC_FREE_VECTOR;
|
|
ReDirReg.MessageType = APIC_MT_Fixed;
|
|
ReDirReg.DestinationMode = APIC_DM_Physical;
|
|
ReDirReg.DeliveryStatus = 0;
|
|
ReDirReg.Polarity = 0;
|
|
ReDirReg.RemoteIRR = 0;
|
|
ReDirReg.TriggerMode = APIC_TGM_Edge;
|
|
ReDirReg.Mask = 1;
|
|
ReDirReg.Reserved = 0;
|
|
ReDirReg.Destination = 0;
|
|
|
|
/* Loop all table entries */
|
|
for (Index = 0; Index < APIC_MAX_IRQ; Index++)
|
|
{
|
|
/* Initialize entry */
|
|
ApicWriteIORedirectionEntry(Index, ReDirReg);
|
|
}
|
|
|
|
/* Init the vactor to index table */
|
|
for (Vector = 0; Vector <= 255; Vector++)
|
|
{
|
|
HalpVectorToIndex[Vector] = APIC_FREE_VECTOR;
|
|
}
|
|
|
|
/* Enable the timer interrupt (but keep it masked) */
|
|
ReDirReg.Vector = APIC_CLOCK_VECTOR;
|
|
ReDirReg.MessageType = APIC_MT_Fixed;
|
|
ReDirReg.DestinationMode = APIC_DM_Physical;
|
|
ReDirReg.TriggerMode = APIC_TGM_Edge;
|
|
ReDirReg.Mask = 1;
|
|
ReDirReg.Destination = ApicRead(APIC_ID);
|
|
ApicWriteIORedirectionEntry(APIC_CLOCK_INDEX, ReDirReg);
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
HalpInitializePICs(IN BOOLEAN EnableInterrupts)
|
|
{
|
|
ULONG_PTR EFlags;
|
|
|
|
/* Save EFlags and disable interrupts */
|
|
EFlags = __readeflags();
|
|
_disable();
|
|
|
|
/* Initialize and mask the PIC */
|
|
HalpInitializeLegacyPICs();
|
|
|
|
/* Initialize the I/O APIC */
|
|
ApicInitializeIOApic();
|
|
|
|
/* Manually reserve some vectors */
|
|
HalpVectorToIndex[APC_VECTOR] = APIC_RESERVED_VECTOR;
|
|
HalpVectorToIndex[DISPATCH_VECTOR] = APIC_RESERVED_VECTOR;
|
|
HalpVectorToIndex[APIC_CLOCK_VECTOR] = 8;
|
|
HalpVectorToIndex[CLOCK_IPI_VECTOR] = APIC_RESERVED_VECTOR;
|
|
HalpVectorToIndex[APIC_SPURIOUS_VECTOR] = APIC_RESERVED_VECTOR;
|
|
|
|
/* Set interrupt handlers in the IDT */
|
|
KeRegisterInterruptHandler(APIC_CLOCK_VECTOR, HalpClockInterrupt);
|
|
KeRegisterInterruptHandler(CLOCK_IPI_VECTOR, HalpClockIpi);
|
|
#ifndef _M_AMD64
|
|
KeRegisterInterruptHandler(APC_VECTOR, HalpApcInterrupt);
|
|
KeRegisterInterruptHandler(DISPATCH_VECTOR, HalpDispatchInterrupt);
|
|
#endif
|
|
|
|
/* Register the vectors for APC and dispatch interrupts */
|
|
HalpRegisterVector(IDT_INTERNAL, 0, APC_VECTOR, APC_LEVEL);
|
|
HalpRegisterVector(IDT_INTERNAL, 0, DISPATCH_VECTOR, DISPATCH_LEVEL);
|
|
|
|
/* Restore interrupt state */
|
|
if (EnableInterrupts) EFlags |= EFLAGS_INTERRUPT_MASK;
|
|
__writeeflags(EFlags);
|
|
}
|
|
|
|
|
|
/* SOFTWARE INTERRUPT TRAPS ***************************************************/
|
|
|
|
#ifndef _M_AMD64
|
|
VOID
|
|
DECLSPEC_NORETURN
|
|
FASTCALL
|
|
HalpApcInterruptHandler(IN PKTRAP_FRAME TrapFrame)
|
|
{
|
|
KPROCESSOR_MODE ProcessorMode;
|
|
KIRQL OldIrql;
|
|
ASSERT(ApicGetProcessorIrql() == APC_LEVEL);
|
|
|
|
/* Enter trap */
|
|
KiEnterInterruptTrap(TrapFrame);
|
|
|
|
#ifdef APIC_LAZY_IRQL
|
|
if (!HalBeginSystemInterrupt(APC_LEVEL, APC_VECTOR, &OldIrql))
|
|
{
|
|
/* "Spurious" interrupt, exit the interrupt */
|
|
KiEoiHelper(TrapFrame);
|
|
}
|
|
#else
|
|
/* Save the old IRQL */
|
|
OldIrql = ApicGetCurrentIrql();
|
|
ASSERT(OldIrql < APC_LEVEL);
|
|
#endif
|
|
|
|
/* Raise to APC_LEVEL */
|
|
ApicRaiseIrql(APC_LEVEL);
|
|
|
|
/* End the interrupt */
|
|
ApicSendEOI();
|
|
|
|
/* Kernel or user APC? */
|
|
if (KiUserTrap(TrapFrame)) ProcessorMode = UserMode;
|
|
else if (TrapFrame->EFlags & EFLAGS_V86_MASK) ProcessorMode = UserMode;
|
|
else ProcessorMode = KernelMode;
|
|
|
|
/* Enable interrupts and call the kernel's APC interrupt handler */
|
|
_enable();
|
|
KiDeliverApc(ProcessorMode, NULL, TrapFrame);
|
|
|
|
/* Disable interrupts */
|
|
_disable();
|
|
|
|
/* Restore the old IRQL */
|
|
ApicLowerIrql(OldIrql);
|
|
|
|
/* Exit the interrupt */
|
|
KiEoiHelper(TrapFrame);
|
|
}
|
|
|
|
VOID
|
|
DECLSPEC_NORETURN
|
|
FASTCALL
|
|
HalpDispatchInterruptHandler(IN PKTRAP_FRAME TrapFrame)
|
|
{
|
|
KIRQL OldIrql;
|
|
ASSERT(ApicGetProcessorIrql() == DISPATCH_LEVEL);
|
|
|
|
/* Enter trap */
|
|
KiEnterInterruptTrap(TrapFrame);
|
|
|
|
#ifdef APIC_LAZY_IRQL
|
|
if (!HalBeginSystemInterrupt(DISPATCH_LEVEL, DISPATCH_VECTOR, &OldIrql))
|
|
{
|
|
/* "Spurious" interrupt, exit the interrupt */
|
|
KiEoiHelper(TrapFrame);
|
|
}
|
|
#else
|
|
/* Get the current IRQL */
|
|
OldIrql = ApicGetCurrentIrql();
|
|
ASSERT(OldIrql < DISPATCH_LEVEL);
|
|
#endif
|
|
|
|
/* Raise to DISPATCH_LEVEL */
|
|
ApicRaiseIrql(DISPATCH_LEVEL);
|
|
|
|
/* End the interrupt */
|
|
ApicSendEOI();
|
|
|
|
/* Enable interrupts and call the kernel's DPC interrupt handler */
|
|
_enable();
|
|
KiDispatchInterrupt();
|
|
_disable();
|
|
|
|
/* Restore the old IRQL */
|
|
ApicLowerIrql(OldIrql);
|
|
|
|
/* Exit the interrupt */
|
|
KiEoiHelper(TrapFrame);
|
|
}
|
|
#endif
|
|
|
|
|
|
/* SOFTWARE INTERRUPTS ********************************************************/
|
|
|
|
|
|
VOID
|
|
FASTCALL
|
|
HalRequestSoftwareInterrupt(IN KIRQL Irql)
|
|
{
|
|
/* Convert irql to vector and request an interrupt */
|
|
ApicRequestSelfInterrupt(IrqlToSoftVector(Irql), APIC_TGM_Edge);
|
|
}
|
|
|
|
VOID
|
|
FASTCALL
|
|
HalClearSoftwareInterrupt(
|
|
IN KIRQL Irql)
|
|
{
|
|
/* Nothing to do */
|
|
}
|
|
|
|
|
|
/* SYSTEM INTERRUPTS **********************************************************/
|
|
|
|
BOOLEAN
|
|
NTAPI
|
|
HalEnableSystemInterrupt(
|
|
IN ULONG Vector,
|
|
IN KIRQL Irql,
|
|
IN KINTERRUPT_MODE InterruptMode)
|
|
{
|
|
IOAPIC_REDIRECTION_REGISTER ReDirReg;
|
|
PKPRCB Prcb = KeGetCurrentPrcb();
|
|
UCHAR Index;
|
|
ASSERT(Irql <= HIGH_LEVEL);
|
|
ASSERT((IrqlToTpr(Irql) & 0xF0) == (Vector & 0xF0));
|
|
|
|
/* Get the irq for this vector */
|
|
Index = HalpVectorToIndex[Vector];
|
|
|
|
/* Check if its valid */
|
|
if (Index == APIC_FREE_VECTOR)
|
|
{
|
|
/* Interrupt is not in use */
|
|
return FALSE;
|
|
}
|
|
|
|
/* Read the redirection entry */
|
|
ReDirReg = ApicReadIORedirectionEntry(Index);
|
|
|
|
/* Check if the interrupt was unused */
|
|
if (ReDirReg.Vector != Vector)
|
|
{
|
|
ReDirReg.Vector = Vector;
|
|
ReDirReg.MessageType = APIC_MT_LowestPriority;
|
|
ReDirReg.DestinationMode = APIC_DM_Logical;
|
|
ReDirReg.Destination = 0;
|
|
}
|
|
|
|
/* Check if the destination is logical */
|
|
if (ReDirReg.DestinationMode == APIC_DM_Logical)
|
|
{
|
|
/* Set the bit for this cpu */
|
|
ReDirReg.Destination |= ApicLogicalId(Prcb->Number);
|
|
}
|
|
|
|
/* Set the trigger mode */
|
|
ReDirReg.TriggerMode = 1 - InterruptMode;
|
|
|
|
/* Now unmask it */
|
|
ReDirReg.Mask = FALSE;
|
|
|
|
/* Write back the entry */
|
|
ApicWriteIORedirectionEntry(Index, ReDirReg);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
HalDisableSystemInterrupt(
|
|
IN ULONG Vector,
|
|
IN KIRQL Irql)
|
|
{
|
|
IOAPIC_REDIRECTION_REGISTER ReDirReg;
|
|
UCHAR Index;
|
|
ASSERT(Irql <= HIGH_LEVEL);
|
|
ASSERT(Vector < RTL_NUMBER_OF(HalpVectorToIndex));
|
|
|
|
Index = HalpVectorToIndex[Vector];
|
|
|
|
/* Read lower dword of redirection entry */
|
|
ReDirReg.Long0 = IOApicRead(IOAPIC_REDTBL + 2 * Index);
|
|
|
|
/* Mask it */
|
|
ReDirReg.Mask = 1;
|
|
|
|
/* Write back lower dword */
|
|
IOApicWrite(IOAPIC_REDTBL + 2 * Index, ReDirReg.Long0);
|
|
}
|
|
|
|
BOOLEAN
|
|
NTAPI
|
|
HalBeginSystemInterrupt(
|
|
IN KIRQL Irql,
|
|
IN ULONG Vector,
|
|
OUT PKIRQL OldIrql)
|
|
{
|
|
KIRQL CurrentIrql;
|
|
|
|
/* Get the current IRQL */
|
|
CurrentIrql = ApicGetCurrentIrql();
|
|
|
|
#ifdef APIC_LAZY_IRQL
|
|
/* Check if this interrupt is allowed */
|
|
if (CurrentIrql >= Irql)
|
|
{
|
|
IOAPIC_REDIRECTION_REGISTER RedirReg;
|
|
UCHAR Index;
|
|
|
|
/* It is not, set the real Irql in the TPR! */
|
|
ApicWrite(APIC_TPR, IrqlToTpr(CurrentIrql));
|
|
|
|
/* Save the new hard IRQL in the IRR field */
|
|
KeGetPcr()->IRR = CurrentIrql;
|
|
|
|
/* End this interrupt */
|
|
ApicSendEOI();
|
|
|
|
/* Get the irq for this vector */
|
|
Index = HalpVectorToIndex[Vector];
|
|
|
|
/* Check if it's valid */
|
|
if (Index < APIC_MAX_IRQ)
|
|
{
|
|
/* Read the I/O redirection entry */
|
|
RedirReg = ApicReadIORedirectionEntry(Index);
|
|
|
|
/* Re-request the interrupt to be handled later */
|
|
ApicRequestSelfInterrupt(Vector, (UCHAR)RedirReg.TriggerMode);
|
|
}
|
|
else
|
|
{
|
|
/* This should be a reserved vector! */
|
|
ASSERT(Index == APIC_RESERVED_VECTOR);
|
|
|
|
/* Re-request the interrupt to be handled later */
|
|
ApicRequestSelfInterrupt(Vector, APIC_TGM_Edge);
|
|
}
|
|
|
|
/* Pretend it was a spurious interrupt */
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
/* Save the current IRQL */
|
|
*OldIrql = CurrentIrql;
|
|
|
|
/* Set the new IRQL */
|
|
ApicRaiseIrql(Irql);
|
|
|
|
/* Turn on interrupts */
|
|
_enable();
|
|
|
|
/* Success */
|
|
return TRUE;
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
HalEndSystemInterrupt(
|
|
IN KIRQL OldIrql,
|
|
IN PKTRAP_FRAME TrapFrame)
|
|
{
|
|
/* Send an EOI */
|
|
ApicSendEOI();
|
|
|
|
/* Restore the old IRQL */
|
|
ApicLowerIrql(OldIrql);
|
|
}
|
|
|
|
|
|
/* IRQL MANAGEMENT ************************************************************/
|
|
|
|
#ifndef _M_AMD64
|
|
KIRQL
|
|
NTAPI
|
|
KeGetCurrentIrql(VOID)
|
|
{
|
|
/* Read the current TPR and convert it to an IRQL */
|
|
return ApicGetCurrentIrql();
|
|
}
|
|
|
|
VOID
|
|
FASTCALL
|
|
KfLowerIrql(
|
|
IN KIRQL OldIrql)
|
|
{
|
|
#if DBG
|
|
/* Validate correct lower */
|
|
if (OldIrql > ApicGetCurrentIrql())
|
|
{
|
|
/* Crash system */
|
|
KeBugCheck(IRQL_NOT_LESS_OR_EQUAL);
|
|
}
|
|
#endif
|
|
/* Set the new IRQL */
|
|
ApicLowerIrql(OldIrql);
|
|
}
|
|
|
|
KIRQL
|
|
FASTCALL
|
|
KfRaiseIrql(
|
|
IN KIRQL NewIrql)
|
|
{
|
|
KIRQL OldIrql;
|
|
|
|
/* Read the current IRQL */
|
|
OldIrql = ApicGetCurrentIrql();
|
|
#if DBG
|
|
/* Validate correct raise */
|
|
if (OldIrql > NewIrql)
|
|
{
|
|
/* Crash system */
|
|
KeBugCheck(IRQL_NOT_GREATER_OR_EQUAL);
|
|
}
|
|
#endif
|
|
/* Convert the new IRQL to a TPR value and write the register */
|
|
ApicRaiseIrql(NewIrql);
|
|
|
|
/* Return old IRQL */
|
|
return OldIrql;
|
|
}
|
|
|
|
KIRQL
|
|
NTAPI
|
|
KeRaiseIrqlToDpcLevel(VOID)
|
|
{
|
|
return KfRaiseIrql(DISPATCH_LEVEL);
|
|
}
|
|
|
|
KIRQL
|
|
NTAPI
|
|
KeRaiseIrqlToSynchLevel(VOID)
|
|
{
|
|
return KfRaiseIrql(SYNCH_LEVEL);
|
|
}
|
|
|
|
#endif /* !_M_AMD64 */
|
|
|