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 /* Validate the vector */ 88 if ((Interrupt->Vector < PRIMARY_VECTOR_BASE) || 89 (Interrupt->Vector > MAXIMUM_IDTVECTOR)) 90 { 91 DPRINT1("Invalid interrupt vector: %lu\n", Interrupt->Vector); 92 return FALSE; 93 } 94 95 ASSERT(Interrupt->Number < KeNumberProcessors); 96 ASSERT(Interrupt->Irql <= HIGH_LEVEL); 97 ASSERT(Interrupt->SynchronizeIrql >= Interrupt->Irql); 98 ASSERT(Interrupt->Irql == (Interrupt->Vector >> 4)); 99 100 /* Check if its already connected */ 101 if (Interrupt->Connected) return TRUE; 102 103 /* Set the system affinity and acquire the dispatcher lock */ 104 KeSetSystemAffinityThread(1ULL << Interrupt->Number); 105 OldIrql = KiAcquireDispatcherLock(); 106 107 /* Query the current handler */ 108 CurrentHandler = KeQueryInterruptHandler(Interrupt->Vector); 109 110 /* Check if the vector is unused */ 111 if ((CurrentHandler >= (PVOID)KiUnexpectedRange) && 112 (CurrentHandler <= (PVOID)KiUnexpectedRangeEnd)) 113 { 114 /* Initialize the list for chained interrupts */ 115 InitializeListHead(&Interrupt->InterruptListEntry); 116 117 /* Set normal dispatch address */ 118 Interrupt->DispatchAddress = KiInterruptDispatch; 119 120 /* Set the new handler */ 121 KeRegisterInterruptHandler(Interrupt->Vector, 122 Interrupt->DispatchCode); 123 124 /* Enable the interrupt */ 125 if (!HalEnableSystemInterrupt(Interrupt->Vector, 126 Interrupt->Irql, 127 Interrupt->Mode)) 128 { 129 /* Didn't work, restore old handler */ 130 DPRINT1("HalEnableSystemInterrupt failed\n"); 131 KeRegisterInterruptHandler(Interrupt->Vector, CurrentHandler); 132 goto Cleanup; 133 } 134 } 135 else 136 { 137 /* Get the connected interrupt */ 138 ConnectedInterrupt = CONTAINING_RECORD(CurrentHandler, KINTERRUPT, DispatchCode); 139 140 /* Check if sharing is ok */ 141 if ((Interrupt->ShareVector == 0) || 142 (ConnectedInterrupt->ShareVector == 0) || 143 (Interrupt->Mode != ConnectedInterrupt->Mode)) 144 { 145 goto Cleanup; 146 } 147 148 /* Insert the new interrupt into the connected interrupt's list */ 149 InsertTailList(&ConnectedInterrupt->InterruptListEntry, 150 &Interrupt->InterruptListEntry); 151 } 152 153 /* Mark as connected */ 154 Interrupt->Connected = TRUE; 155 156 Cleanup: 157 /* Release the dispatcher lock and restore the thread affinity */ 158 KiReleaseDispatcherLock(OldIrql); 159 KeRevertToUserAffinityThread(); 160 return Interrupt->Connected; 161 } 162 163 BOOLEAN 164 NTAPI 165 KeDisconnectInterrupt(IN PKINTERRUPT Interrupt) 166 { 167 KIRQL OldIrql; 168 PVOID VectorHandler, UnexpectedHandler; 169 PKINTERRUPT VectorFirstInterrupt, NextInterrupt; 170 PLIST_ENTRY HandlerHead; 171 172 /* Set the system affinity and acquire the dispatcher lock */ 173 KeSetSystemAffinityThread(1ULL << Interrupt->Number); 174 OldIrql = KiAcquireDispatcherLock(); 175 176 /* Check if the interrupt was connected - otherwise there's nothing to do */ 177 if (Interrupt->Connected) 178 { 179 /* Get the handler for this interrupt vector */ 180 VectorHandler = KeQueryInterruptHandler(Interrupt->Vector); 181 182 /* Get the first interrupt for this handler */ 183 VectorFirstInterrupt = CONTAINING_RECORD(VectorHandler, KINTERRUPT, DispatchCode); 184 185 /* The first interrupt list entry is the interrupt list head */ 186 HandlerHead = &VectorFirstInterrupt->InterruptListEntry; 187 188 /* If the list is empty, this is the only interrupt for this vector */ 189 if (IsListEmpty(HandlerHead)) 190 { 191 /* If the list is empty, and the head is not from this interrupt, 192 * this interrupt is somehow incorrectly connected */ 193 ASSERT(VectorFirstInterrupt == Interrupt); 194 195 UnexpectedHandler = &KiUnexpectedRange[Interrupt->Vector]._Op_push; 196 197 /* This is the only interrupt, the handler can be disconnected */ 198 HalDisableSystemInterrupt(Interrupt->Vector, Interrupt->Irql); 199 KeRegisterInterruptHandler(Interrupt->Vector, UnexpectedHandler); 200 } 201 /* If the interrupt to be disconnected is the list head, but some others follow */ 202 else if (VectorFirstInterrupt == Interrupt) 203 { 204 /* Relocate the head to the next element */ 205 HandlerHead = HandlerHead->Flink; 206 RemoveTailList(HandlerHead); 207 208 /* Get the next interrupt from the list head */ 209 NextInterrupt = CONTAINING_RECORD(HandlerHead, 210 KINTERRUPT, 211 InterruptListEntry); 212 213 /* Set the next interrupt as the handler for this vector */ 214 KeRegisterInterruptHandler(Interrupt->Vector, 215 NextInterrupt->DispatchCode); 216 } 217 /* If the interrupt to be disconnected is not the list head */ 218 else 219 { 220 /* Remove the to be disconnected interrupt from the interrupt list */ 221 RemoveEntryList(&Interrupt->InterruptListEntry); 222 } 223 224 /* Mark as not connected */ 225 Interrupt->Connected = FALSE; 226 } 227 228 /* Release the dispatcher lock and restore the thread affinity */ 229 KiReleaseDispatcherLock(OldIrql); 230 KeRevertToUserAffinityThread(); 231 232 return TRUE; 233 } 234 235 BOOLEAN 236 NTAPI 237 KeSynchronizeExecution(IN OUT PKINTERRUPT Interrupt, 238 IN PKSYNCHRONIZE_ROUTINE SynchronizeRoutine, 239 IN PVOID SynchronizeContext OPTIONAL) 240 { 241 BOOLEAN Success; 242 KIRQL OldIrql; 243 244 /* Raise IRQL */ 245 OldIrql = KfRaiseIrql(Interrupt->SynchronizeIrql); 246 247 /* Acquire interrupt spinlock */ 248 KeAcquireSpinLockAtDpcLevel(Interrupt->ActualLock); 249 250 /* Call the routine */ 251 Success = SynchronizeRoutine(SynchronizeContext); 252 253 /* Release lock */ 254 KeReleaseSpinLockFromDpcLevel(Interrupt->ActualLock); 255 256 /* Lower IRQL */ 257 KeLowerIrql(OldIrql); 258 259 /* Return status */ 260 return Success; 261 } 262