1 /* 2 * PROJECT: ReactOS Kernel 3 * LICENSE: GPL - See COPYING in the top level directory 4 * FILE: ntoskrnl/ke/amd64/interrupt.c 5 * PURPOSE: Manages the Kernel's IRQ support for external drivers, 6 * for the purpopses of connecting, disconnecting and setting 7 * up ISRs for drivers. The backend behind the Io* Interrupt 8 * routines. 9 * PROGRAMMERS: Timo Kreuzer (timo.kreuzer@reactos.org) 10 * Alex Ionescu (alex.ionescu@reactos.org) 11 */ 12 13 /* INCLUDES *****************************************************************/ 14 15 #include <ntoskrnl.h> 16 #define NDEBUG 17 #include <debug.h> 18 19 extern UCHAR KiInterruptDispatchTemplate[16]; 20 extern KI_INTERRUPT_DISPATCH_ENTRY KiUnexpectedRange[256]; 21 extern KI_INTERRUPT_DISPATCH_ENTRY KiUnexpectedRangeEnd[]; 22 void KiInterruptDispatch(void); 23 24 25 /* FUNCTIONS ****************************************************************/ 26 27 VOID 28 NTAPI 29 KeInitializeInterrupt( 30 IN PKINTERRUPT Interrupt, 31 IN PKSERVICE_ROUTINE ServiceRoutine, 32 IN PVOID ServiceContext, 33 IN PKSPIN_LOCK SpinLock, 34 IN ULONG Vector, 35 IN KIRQL Irql, 36 IN KIRQL SynchronizeIrql, 37 IN KINTERRUPT_MODE InterruptMode, 38 IN BOOLEAN ShareVector, 39 IN CHAR ProcessorNumber, 40 IN BOOLEAN FloatingSave) 41 { 42 43 /* Initialize the header */ 44 Interrupt->Type = InterruptObject; 45 Interrupt->Size = sizeof(KINTERRUPT); 46 47 /* If no Spinlock is given, use the internal */ 48 if (!SpinLock) SpinLock = &Interrupt->SpinLock; 49 KeInitializeSpinLock(&Interrupt->SpinLock); 50 51 /* Set the given parameters */ 52 Interrupt->ServiceRoutine = ServiceRoutine; 53 Interrupt->ServiceContext = ServiceContext; 54 Interrupt->ActualLock = SpinLock; 55 Interrupt->Vector = Vector; 56 Interrupt->Irql = Irql; 57 Interrupt->SynchronizeIrql = SynchronizeIrql; 58 Interrupt->Mode = InterruptMode; 59 Interrupt->ShareVector = ShareVector; 60 Interrupt->Number = ProcessorNumber; 61 Interrupt->FloatingSave = FloatingSave; 62 63 /* Set initial values */ 64 Interrupt->TickCount = 0; 65 Interrupt->Connected = FALSE; 66 Interrupt->ServiceCount = 0; 67 Interrupt->DispatchCount = 0; 68 Interrupt->TrapFrame = NULL; 69 Interrupt->Reserved = 0; 70 71 /* Copy the dispatch code (its location independent, no need to patch it) */ 72 RtlCopyMemory(Interrupt->DispatchCode, 73 KiInterruptDispatchTemplate, 74 sizeof(Interrupt->DispatchCode)); 75 76 Interrupt->DispatchAddress = 0; 77 } 78 79 BOOLEAN 80 NTAPI 81 KeConnectInterrupt(IN PKINTERRUPT Interrupt) 82 { 83 PVOID CurrentHandler; 84 PKINTERRUPT ConnectedInterrupt; 85 KIRQL OldIrql; 86 87 ASSERT(Interrupt->Vector >= PRIMARY_VECTOR_BASE); 88 ASSERT(Interrupt->Vector <= MAXIMUM_IDTVECTOR); 89 ASSERT(Interrupt->Number < KeNumberProcessors); 90 ASSERT(Interrupt->Irql <= HIGH_LEVEL); 91 ASSERT(Interrupt->SynchronizeIrql >= Interrupt->Irql); 92 ASSERT(Interrupt->Irql == (Interrupt->Vector >> 4)); 93 94 /* Check if its already connected */ 95 if (Interrupt->Connected) return TRUE; 96 97 /* Set the system affinity and acquire the dispatcher lock */ 98 KeSetSystemAffinityThread(1ULL << Interrupt->Number); 99 OldIrql = KiAcquireDispatcherLock(); 100 101 /* Query the current handler */ 102 CurrentHandler = KeQueryInterruptHandler(Interrupt->Vector); 103 104 /* Check if the vector is unused */ 105 if ((CurrentHandler >= (PVOID)KiUnexpectedRange) && 106 (CurrentHandler <= (PVOID)KiUnexpectedRangeEnd)) 107 { 108 /* Initialize the list for chained interrupts */ 109 InitializeListHead(&Interrupt->InterruptListEntry); 110 111 /* Set normal dispatch address */ 112 Interrupt->DispatchAddress = KiInterruptDispatch; 113 114 /* Set the new handler */ 115 KeRegisterInterruptHandler(Interrupt->Vector, 116 Interrupt->DispatchCode); 117 118 /* Enable the interrupt */ 119 if (!HalEnableSystemInterrupt(Interrupt->Vector, 120 Interrupt->Irql, 121 Interrupt->Mode)) 122 { 123 /* Didn't work, restore old handler */ 124 DPRINT1("HalEnableSystemInterrupt failed\n"); 125 KeRegisterInterruptHandler(Interrupt->Vector, CurrentHandler); 126 goto Cleanup; 127 } 128 } 129 else 130 { 131 /* Get the connected interrupt */ 132 ConnectedInterrupt = CONTAINING_RECORD(CurrentHandler, KINTERRUPT, DispatchCode); 133 134 /* Check if sharing is ok */ 135 if ((Interrupt->ShareVector == 0) || 136 (ConnectedInterrupt->ShareVector == 0) || 137 (Interrupt->Mode != ConnectedInterrupt->Mode)) 138 { 139 goto Cleanup; 140 } 141 142 /* Insert the new interrupt into the connected interrupt's list */ 143 InsertTailList(&ConnectedInterrupt->InterruptListEntry, 144 &Interrupt->InterruptListEntry); 145 } 146 147 /* Mark as connected */ 148 Interrupt->Connected = TRUE; 149 150 Cleanup: 151 /* Release the dispatcher lock and restore the thread affinity */ 152 KiReleaseDispatcherLock(OldIrql); 153 KeRevertToUserAffinityThread(); 154 return Interrupt->Connected; 155 } 156 157 BOOLEAN 158 NTAPI 159 KeDisconnectInterrupt(IN PKINTERRUPT Interrupt) 160 { 161 KIRQL OldIrql; 162 PVOID VectorHandler, UnexpectedHandler; 163 PKINTERRUPT VectorFirstInterrupt, NextInterrupt; 164 PLIST_ENTRY HandlerHead; 165 166 /* Set the system affinity and acquire the dispatcher lock */ 167 KeSetSystemAffinityThread(1ULL << Interrupt->Number); 168 OldIrql = KiAcquireDispatcherLock(); 169 170 /* Check if the interrupt was connected - otherwise there's nothing to do */ 171 if (Interrupt->Connected) 172 { 173 /* Get the handler for this interrupt vector */ 174 VectorHandler = KeQueryInterruptHandler(Interrupt->Vector); 175 176 /* Get the first interrupt for this handler */ 177 VectorFirstInterrupt = CONTAINING_RECORD(VectorHandler, KINTERRUPT, DispatchCode); 178 179 /* The first interrupt list entry is the interrupt list head */ 180 HandlerHead = &VectorFirstInterrupt->InterruptListEntry; 181 182 /* If the list is empty, this is the only interrupt for this vector */ 183 if (IsListEmpty(HandlerHead)) 184 { 185 /* If the list is empty, and the head is not from this interrupt, 186 * this interrupt is somehow incorrectly connected */ 187 ASSERT(VectorFirstInterrupt == Interrupt); 188 189 UnexpectedHandler = &KiUnexpectedRange[Interrupt->Vector]._Op_push; 190 191 /* This is the only interrupt, the handler can be disconnected */ 192 HalDisableSystemInterrupt(Interrupt->Vector, Interrupt->Irql); 193 KeRegisterInterruptHandler(Interrupt->Vector, UnexpectedHandler); 194 } 195 /* If the interrupt to be disconnected is the list head, but some others follow */ 196 else if (VectorFirstInterrupt == Interrupt) 197 { 198 /* Relocate the head to the next element */ 199 HandlerHead = HandlerHead->Flink; 200 RemoveTailList(HandlerHead); 201 202 /* Get the next interrupt from the list head */ 203 NextInterrupt = CONTAINING_RECORD(HandlerHead, 204 KINTERRUPT, 205 InterruptListEntry); 206 207 /* Set the next interrupt as the handler for this vector */ 208 KeRegisterInterruptHandler(Interrupt->Vector, 209 NextInterrupt->DispatchCode); 210 } 211 /* If the interrupt to be disconnected is not the list head */ 212 else 213 { 214 /* Remove the to be disconnected interrupt from the interrupt list */ 215 RemoveEntryList(&Interrupt->InterruptListEntry); 216 } 217 218 /* Mark as not connected */ 219 Interrupt->Connected = FALSE; 220 } 221 222 /* Release the dispatcher lock and restore the thread affinity */ 223 KiReleaseDispatcherLock(OldIrql); 224 KeRevertToUserAffinityThread(); 225 226 return TRUE; 227 } 228 229 BOOLEAN 230 NTAPI 231 KeSynchronizeExecution(IN OUT PKINTERRUPT Interrupt, 232 IN PKSYNCHRONIZE_ROUTINE SynchronizeRoutine, 233 IN PVOID SynchronizeContext OPTIONAL) 234 { 235 BOOLEAN Success; 236 KIRQL OldIrql; 237 238 /* Raise IRQL */ 239 OldIrql = KfRaiseIrql(Interrupt->SynchronizeIrql); 240 241 /* Acquire interrupt spinlock */ 242 KeAcquireSpinLockAtDpcLevel(Interrupt->ActualLock); 243 244 /* Call the routine */ 245 Success = SynchronizeRoutine(SynchronizeContext); 246 247 /* Release lock */ 248 KeReleaseSpinLockFromDpcLevel(Interrupt->ActualLock); 249 250 /* Lower IRQL */ 251 KeLowerIrql(OldIrql); 252 253 /* Return status */ 254 return Success; 255 } 256