reactos/ntoskrnl/ke/amd64/interrupt.c
Timo Kreuzer 2ce5d43a33 [NTOS/x64] Gracefully handle invalid Vector in KeConnectInterrupt
Fail instead of asserting. This is needed because a kmtest triggers it.
2024-03-02 07:30:06 +01:00

261 lines
8.5 KiB
C

/*
* PROJECT: ReactOS Kernel
* LICENSE: GPL - See COPYING in the top level directory
* FILE: ntoskrnl/ke/amd64/interrupt.c
* PURPOSE: Manages the Kernel's IRQ support for external drivers,
* for the purpopses of connecting, disconnecting and setting
* up ISRs for drivers. The backend behind the Io* Interrupt
* routines.
* PROGRAMMERS: Timo Kreuzer (timo.kreuzer@reactos.org)
* Alex Ionescu (alex.ionescu@reactos.org)
*/
/* INCLUDES *****************************************************************/
#include <ntoskrnl.h>
#define NDEBUG
#include <debug.h>
extern UCHAR KiInterruptDispatchTemplate[16];
extern KI_INTERRUPT_DISPATCH_ENTRY KiUnexpectedRange[256];
extern KI_INTERRUPT_DISPATCH_ENTRY KiUnexpectedRangeEnd[];
void KiInterruptDispatch(void);
/* FUNCTIONS ****************************************************************/
VOID
NTAPI
KeInitializeInterrupt(
IN PKINTERRUPT Interrupt,
IN PKSERVICE_ROUTINE ServiceRoutine,
IN PVOID ServiceContext,
IN PKSPIN_LOCK SpinLock,
IN ULONG Vector,
IN KIRQL Irql,
IN KIRQL SynchronizeIrql,
IN KINTERRUPT_MODE InterruptMode,
IN BOOLEAN ShareVector,
IN CHAR ProcessorNumber,
IN BOOLEAN FloatingSave)
{
/* Initialize the header */
Interrupt->Type = InterruptObject;
Interrupt->Size = sizeof(KINTERRUPT);
/* If no Spinlock is given, use the internal */
if (!SpinLock) SpinLock = &Interrupt->SpinLock;
KeInitializeSpinLock(&Interrupt->SpinLock);
/* Set the given parameters */
Interrupt->ServiceRoutine = ServiceRoutine;
Interrupt->ServiceContext = ServiceContext;
Interrupt->ActualLock = SpinLock;
Interrupt->Vector = Vector;
Interrupt->Irql = Irql;
Interrupt->SynchronizeIrql = SynchronizeIrql;
Interrupt->Mode = InterruptMode;
Interrupt->ShareVector = ShareVector;
Interrupt->Number = ProcessorNumber;
Interrupt->FloatingSave = FloatingSave;
/* Set initial values */
Interrupt->TickCount = 0;
Interrupt->Connected = FALSE;
Interrupt->ServiceCount = 0;
Interrupt->DispatchCount = 0;
Interrupt->TrapFrame = NULL;
Interrupt->Reserved = 0;
/* Copy the dispatch code (its location independent, no need to patch it) */
RtlCopyMemory(Interrupt->DispatchCode,
KiInterruptDispatchTemplate,
sizeof(Interrupt->DispatchCode));
Interrupt->DispatchAddress = 0;
}
BOOLEAN
NTAPI
KeConnectInterrupt(IN PKINTERRUPT Interrupt)
{
PVOID CurrentHandler;
PKINTERRUPT ConnectedInterrupt;
KIRQL OldIrql;
/* Validate the vector */
if ((Interrupt->Vector < PRIMARY_VECTOR_BASE) ||
(Interrupt->Vector > MAXIMUM_IDTVECTOR))
{
DPRINT1("Invalid interrupt vector: %lu\n", Interrupt->Vector);
return FALSE;
}
ASSERT(Interrupt->Number < KeNumberProcessors);
ASSERT(Interrupt->Irql <= HIGH_LEVEL);
ASSERT(Interrupt->SynchronizeIrql >= Interrupt->Irql);
ASSERT(Interrupt->Irql == (Interrupt->Vector >> 4));
/* Check if its already connected */
if (Interrupt->Connected) return TRUE;
/* Set the system affinity and acquire the dispatcher lock */
KeSetSystemAffinityThread(1ULL << Interrupt->Number);
OldIrql = KiAcquireDispatcherLock();
/* Query the current handler */
CurrentHandler = KeQueryInterruptHandler(Interrupt->Vector);
/* Check if the vector is unused */
if ((CurrentHandler >= (PVOID)KiUnexpectedRange) &&
(CurrentHandler <= (PVOID)KiUnexpectedRangeEnd))
{
/* Initialize the list for chained interrupts */
InitializeListHead(&Interrupt->InterruptListEntry);
/* Set normal dispatch address */
Interrupt->DispatchAddress = KiInterruptDispatch;
/* Set the new handler */
KeRegisterInterruptHandler(Interrupt->Vector,
Interrupt->DispatchCode);
/* Enable the interrupt */
if (!HalEnableSystemInterrupt(Interrupt->Vector,
Interrupt->Irql,
Interrupt->Mode))
{
/* Didn't work, restore old handler */
DPRINT1("HalEnableSystemInterrupt failed\n");
KeRegisterInterruptHandler(Interrupt->Vector, CurrentHandler);
goto Cleanup;
}
}
else
{
/* Get the connected interrupt */
ConnectedInterrupt = CONTAINING_RECORD(CurrentHandler, KINTERRUPT, DispatchCode);
/* Check if sharing is ok */
if ((Interrupt->ShareVector == 0) ||
(ConnectedInterrupt->ShareVector == 0) ||
(Interrupt->Mode != ConnectedInterrupt->Mode))
{
goto Cleanup;
}
/* Insert the new interrupt into the connected interrupt's list */
InsertTailList(&ConnectedInterrupt->InterruptListEntry,
&Interrupt->InterruptListEntry);
}
/* Mark as connected */
Interrupt->Connected = TRUE;
Cleanup:
/* Release the dispatcher lock and restore the thread affinity */
KiReleaseDispatcherLock(OldIrql);
KeRevertToUserAffinityThread();
return Interrupt->Connected;
}
BOOLEAN
NTAPI
KeDisconnectInterrupt(IN PKINTERRUPT Interrupt)
{
KIRQL OldIrql;
PVOID VectorHandler, UnexpectedHandler;
PKINTERRUPT VectorFirstInterrupt, NextInterrupt;
PLIST_ENTRY HandlerHead;
/* Set the system affinity and acquire the dispatcher lock */
KeSetSystemAffinityThread(1ULL << Interrupt->Number);
OldIrql = KiAcquireDispatcherLock();
/* Check if the interrupt was connected - otherwise there's nothing to do */
if (Interrupt->Connected)
{
/* Get the handler for this interrupt vector */
VectorHandler = KeQueryInterruptHandler(Interrupt->Vector);
/* Get the first interrupt for this handler */
VectorFirstInterrupt = CONTAINING_RECORD(VectorHandler, KINTERRUPT, DispatchCode);
/* The first interrupt list entry is the interrupt list head */
HandlerHead = &VectorFirstInterrupt->InterruptListEntry;
/* If the list is empty, this is the only interrupt for this vector */
if (IsListEmpty(HandlerHead))
{
/* If the list is empty, and the head is not from this interrupt,
* this interrupt is somehow incorrectly connected */
ASSERT(VectorFirstInterrupt == Interrupt);
UnexpectedHandler = &KiUnexpectedRange[Interrupt->Vector]._Op_push;
/* This is the only interrupt, the handler can be disconnected */
HalDisableSystemInterrupt(Interrupt->Vector, Interrupt->Irql);
KeRegisterInterruptHandler(Interrupt->Vector, UnexpectedHandler);
}
/* If the interrupt to be disconnected is the list head, but some others follow */
else if (VectorFirstInterrupt == Interrupt)
{
/* Relocate the head to the next element */
HandlerHead = HandlerHead->Flink;
RemoveTailList(HandlerHead);
/* Get the next interrupt from the list head */
NextInterrupt = CONTAINING_RECORD(HandlerHead,
KINTERRUPT,
InterruptListEntry);
/* Set the next interrupt as the handler for this vector */
KeRegisterInterruptHandler(Interrupt->Vector,
NextInterrupt->DispatchCode);
}
/* If the interrupt to be disconnected is not the list head */
else
{
/* Remove the to be disconnected interrupt from the interrupt list */
RemoveEntryList(&Interrupt->InterruptListEntry);
}
/* Mark as not connected */
Interrupt->Connected = FALSE;
}
/* Release the dispatcher lock and restore the thread affinity */
KiReleaseDispatcherLock(OldIrql);
KeRevertToUserAffinityThread();
return TRUE;
}
BOOLEAN
NTAPI
KeSynchronizeExecution(IN OUT PKINTERRUPT Interrupt,
IN PKSYNCHRONIZE_ROUTINE SynchronizeRoutine,
IN PVOID SynchronizeContext OPTIONAL)
{
BOOLEAN Success;
KIRQL OldIrql;
/* Raise IRQL */
OldIrql = KfRaiseIrql(Interrupt->SynchronizeIrql);
/* Acquire interrupt spinlock */
KeAcquireSpinLockAtDpcLevel(Interrupt->ActualLock);
/* Call the routine */
Success = SynchronizeRoutine(SynchronizeContext);
/* Release lock */
KeReleaseSpinLockFromDpcLevel(Interrupt->ActualLock);
/* Lower IRQL */
KeLowerIrql(OldIrql);
/* Return status */
return Success;
}