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