xref: /reactos/ntoskrnl/ke/amd64/interrupt.c (revision 84344399)
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