421b7b22d8743fd108e0cef8a230d502fe0d788f
[reactos.git] / reactos / ntoskrnl / ke / i386 / irqobj.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ke/i386/irq.c
5 * PURPOSE: Manages the Kernel's IRQ support for external drivers,
6 * for the purpouses of connecting, disconnecting and setting
7 * up ISRs for drivers. The backend behind the Io* Interrupt
8 * routines.
9 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
10 */
11
12 /* INCLUDES *****************************************************************/
13
14 #include <ntoskrnl.h>
15 #define NDEBUG
16 #include <debug.h>
17
18 /* GLOBALS *******************************************************************/
19
20 ULONG KiISRTimeout = 55;
21 USHORT KiISROverflow = 30000;
22 extern ULONG NTAPI KiChainedDispatch2ndLvl(VOID);
23
24 /* PRIVATE FUNCTIONS *********************************************************/
25
26 VOID
27 NTAPI
28 KiGetVectorDispatch(IN ULONG Vector,
29 IN PDISPATCH_INFO Dispatch)
30 {
31 PKINTERRUPT_ROUTINE Handler;
32 ULONG Current;
33 UCHAR Type;
34 UCHAR Entry;
35
36 /* Check if this is a primary or 2nd-level dispatch */
37 Type = HalSystemVectorDispatchEntry(Vector,
38 &Dispatch->FlatDispatch,
39 &Dispatch->NoDispatch);
40 ASSERT(Type == 0);
41
42 /* Get the IDT entry for this vector */
43 Entry = HalVectorToIDTEntry(Vector);
44
45 /* Setup the unhandled dispatch */
46 Dispatch->NoDispatch = (PVOID)(((ULONG_PTR)&KiStartUnexpectedRange) +
47 (Entry - PRIMARY_VECTOR_BASE) *
48 KiUnexpectedEntrySize);
49
50 /* Setup the handlers */
51 Dispatch->InterruptDispatch = KiInterruptDispatch;
52 Dispatch->FloatingDispatch = NULL; // Floating Interrupts are not supported
53 Dispatch->ChainedDispatch = KiChainedDispatch;
54 Dispatch->FlatDispatch = NULL;
55
56 /* Get the current handler */
57 Current = ((((PKIPCR)KeGetPcr())->IDT[Entry].ExtendedOffset << 16)
58 & 0xFFFF0000) |
59 (((PKIPCR)KeGetPcr())->IDT[Entry].Offset & 0xFFFF);
60
61 /* Set the interrupt */
62 Dispatch->Interrupt = CONTAINING_RECORD(Current,
63 KINTERRUPT,
64 DispatchCode);
65
66 /* Check what this interrupt is connected to */
67 if ((PKINTERRUPT_ROUTINE)Current == Dispatch->NoDispatch)
68 {
69 /* Not connected */
70 Dispatch->Type = NoConnect;
71 }
72 else
73 {
74 /* Get the handler */
75 Handler = Dispatch->Interrupt->DispatchAddress;
76 if (Handler == Dispatch->ChainedDispatch)
77 {
78 /* It's a chained interrupt */
79 Dispatch->Type = ChainConnect;
80 }
81 else if ((Handler == Dispatch->InterruptDispatch) ||
82 (Handler == Dispatch->FloatingDispatch))
83 {
84 /* It's unchained */
85 Dispatch->Type = NormalConnect;
86 }
87 else
88 {
89 /* Unknown */
90 Dispatch->Type = UnknownConnect;
91 }
92 }
93 }
94
95 VOID
96 NTAPI
97 KiConnectVectorToInterrupt(IN PKINTERRUPT Interrupt,
98 IN CONNECT_TYPE Type)
99 {
100 DISPATCH_INFO Dispatch;
101 PKINTERRUPT_ROUTINE Handler;
102 PULONG Patch = &Interrupt->DispatchCode[0];
103 UCHAR Entry;
104
105 /* Get vector data */
106 KiGetVectorDispatch(Interrupt->Vector, &Dispatch);
107
108 /* Check if we're only disconnecting */
109 if (Type == NoConnect)
110 {
111 /* Set the handler to NoDispatch */
112 Handler = Dispatch.NoDispatch;
113 }
114 else
115 {
116 /* Get the right handler */
117 Handler = (Type == NormalConnect) ?
118 Dispatch.InterruptDispatch:
119 Dispatch.ChainedDispatch;
120 ASSERT(Interrupt->FloatingSave == FALSE);
121
122 /* Set the handler */
123 Interrupt->DispatchAddress = Handler;
124
125 /* Jump to the last 4 bytes */
126 Patch = (PULONG)((ULONG_PTR)Patch +
127 ((ULONG_PTR)&KiInterruptTemplateDispatch -
128 (ULONG_PTR)KiInterruptTemplate) - 4);
129
130 /* Apply the patch */
131 *Patch = (ULONG)((ULONG_PTR)Handler - ((ULONG_PTR)Patch + 4));
132
133 /* Now set the final handler address */
134 ASSERT(Dispatch.FlatDispatch == NULL);
135 Handler = (PVOID)&Interrupt->DispatchCode;
136 }
137
138 /* Get the IDT entry for this vector */
139 Entry = HalVectorToIDTEntry(Interrupt->Vector);
140
141 /* Set the pointer in the IDT */
142 ((PKIPCR)KeGetPcr())->IDT[Entry].ExtendedOffset =
143 (USHORT)(((ULONG_PTR)Handler >> 16) & 0xFFFF);
144 ((PKIPCR)KeGetPcr())->IDT[Entry].Offset =
145 (USHORT)PtrToUlong(Handler);
146 }
147
148 /* PUBLIC FUNCTIONS **********************************************************/
149
150 /*
151 * @implemented
152 */
153 VOID
154 NTAPI
155 KeInitializeInterrupt(IN PKINTERRUPT Interrupt,
156 IN PKSERVICE_ROUTINE ServiceRoutine,
157 IN PVOID ServiceContext,
158 IN PKSPIN_LOCK SpinLock,
159 IN ULONG Vector,
160 IN KIRQL Irql,
161 IN KIRQL SynchronizeIrql,
162 IN KINTERRUPT_MODE InterruptMode,
163 IN BOOLEAN ShareVector,
164 IN CHAR ProcessorNumber,
165 IN BOOLEAN FloatingSave)
166 {
167 ULONG i;
168 PULONG DispatchCode = &Interrupt->DispatchCode[0], Patch = DispatchCode;
169
170 /* Set the Interrupt Header */
171 Interrupt->Type = InterruptObject;
172 Interrupt->Size = sizeof(KINTERRUPT);
173
174 /* Check if we got a spinlock */
175 if (SpinLock)
176 {
177 Interrupt->ActualLock = SpinLock;
178 }
179 else
180 {
181 /* This means we'll be usin the built-in one */
182 KeInitializeSpinLock(&Interrupt->SpinLock);
183 Interrupt->ActualLock = &Interrupt->SpinLock;
184 }
185
186 /* Set the other settings */
187 Interrupt->ServiceRoutine = ServiceRoutine;
188 Interrupt->ServiceContext = ServiceContext;
189 Interrupt->Vector = Vector;
190 Interrupt->Irql = Irql;
191 Interrupt->SynchronizeIrql = SynchronizeIrql;
192 Interrupt->Mode = InterruptMode;
193 Interrupt->ShareVector = ShareVector;
194 Interrupt->Number = ProcessorNumber;
195 Interrupt->FloatingSave = FloatingSave;
196 Interrupt->TickCount = MAXULONG;
197 Interrupt->DispatchCount = MAXULONG;
198
199 /* Loop the template in memory */
200 for (i = 0; i < KINTERRUPT_DISPATCH_CODES; i++)
201 {
202 /* Copy the dispatch code */
203 *DispatchCode++ = KiInterruptTemplate[i];
204 }
205
206 /* Sanity check */
207 ASSERT((ULONG_PTR)&KiChainedDispatch2ndLvl -
208 (ULONG_PTR)KiInterruptTemplate <= (KINTERRUPT_DISPATCH_CODES * 4));
209
210 /* Jump to the last 4 bytes */
211 Patch = (PULONG)((ULONG_PTR)Patch +
212 ((ULONG_PTR)&KiInterruptTemplateObject -
213 (ULONG_PTR)KiInterruptTemplate) - 4);
214
215 /* Apply the patch */
216 *Patch = PtrToUlong(Interrupt);
217
218 /* Disconnect it at first */
219 Interrupt->Connected = FALSE;
220 }
221
222 /*
223 * @implemented
224 */
225 BOOLEAN
226 NTAPI
227 KeConnectInterrupt(IN PKINTERRUPT Interrupt)
228 {
229 BOOLEAN Connected, Error, Status;
230 KIRQL Irql, OldIrql;
231 UCHAR Number;
232 ULONG Vector;
233 DISPATCH_INFO Dispatch;
234
235 /* Get data from interrupt */
236 Number = Interrupt->Number;
237 Vector = Interrupt->Vector;
238 Irql = Interrupt->Irql;
239
240 /* Validate the settings */
241 if ((Irql > HIGH_LEVEL) ||
242 (Number >= KeNumberProcessors) ||
243 (Interrupt->SynchronizeIrql < Irql) ||
244 (Interrupt->FloatingSave))
245 {
246 return FALSE;
247 }
248
249 /* Set defaults */
250 Connected = FALSE;
251 Error = FALSE;
252
253 /* Set the system affinity and acquire the dispatcher lock */
254 KeSetSystemAffinityThread(1 << Number);
255 OldIrql = KiAcquireDispatcherLock();
256
257 /* Check if it's already been connected */
258 if (!Interrupt->Connected)
259 {
260 /* Get vector dispatching information */
261 KiGetVectorDispatch(Vector, &Dispatch);
262
263 /* Check if the vector is already connected */
264 if (Dispatch.Type == NoConnect)
265 {
266 /* Do the connection */
267 Interrupt->Connected = Connected = TRUE;
268
269 /* Initialize the list */
270 InitializeListHead(&Interrupt->InterruptListEntry);
271
272 /* Connect and enable the interrupt */
273 KiConnectVectorToInterrupt(Interrupt, NormalConnect);
274 Status = HalEnableSystemInterrupt(Vector, Irql, Interrupt->Mode);
275 if (!Status) Error = TRUE;
276 }
277 else if ((Dispatch.Type != UnknownConnect) &&
278 (Interrupt->ShareVector) &&
279 (Dispatch.Interrupt->ShareVector) &&
280 (Dispatch.Interrupt->Mode == Interrupt->Mode))
281 {
282 /* The vector is shared and the interrupts are compatible */
283 Interrupt->Connected = Connected = TRUE;
284
285 /* FIXME */
286 // ASSERT(Irql <= SYNCH_LEVEL);
287
288 /* Check if this is the first chain */
289 if (Dispatch.Type != ChainConnect)
290 {
291 /* This is not supported */
292 ASSERT(Dispatch.Interrupt->Mode != Latched);
293
294 /* Setup the chainned handler */
295 KiConnectVectorToInterrupt(Dispatch.Interrupt, ChainConnect);
296 }
297
298 /* Insert into the interrupt list */
299 InsertTailList(&Dispatch.Interrupt->InterruptListEntry,
300 &Interrupt->InterruptListEntry);
301 }
302 }
303
304 /* Unlock the dispatcher and revert affinity */
305 KiReleaseDispatcherLock(OldIrql);
306 KeRevertToUserAffinityThread();
307
308 /* Check if we failed while trying to connect */
309 if ((Connected) && (Error))
310 {
311 DPRINT1("HalEnableSystemInterrupt failed\n");
312 KeDisconnectInterrupt(Interrupt);
313 Connected = FALSE;
314 }
315
316 /* Return to caller */
317 return Connected;
318 }
319
320 /*
321 * @implemented
322 */
323 BOOLEAN
324 NTAPI
325 KeDisconnectInterrupt(IN PKINTERRUPT Interrupt)
326 {
327 KIRQL OldIrql, Irql;
328 ULONG Vector;
329 DISPATCH_INFO Dispatch;
330 PKINTERRUPT NextInterrupt;
331 BOOLEAN State;
332
333 /* Set the affinity */
334 KeSetSystemAffinityThread(1 << Interrupt->Number);
335
336 /* Lock the dispatcher */
337 OldIrql = KiAcquireDispatcherLock();
338
339 /* Check if it's actually connected */
340 State = Interrupt->Connected;
341 if (State)
342 {
343 /* Get the vector and IRQL */
344 Irql = Interrupt->Irql;
345 Vector = Interrupt->Vector;
346
347 /* Get vector dispatch data */
348 KiGetVectorDispatch(Vector, &Dispatch);
349
350 /* Check if it was chained */
351 if (Dispatch.Type == ChainConnect)
352 {
353 /* Check if the top-level interrupt is being removed */
354 ASSERT(Irql <= SYNCH_LEVEL);
355 if (Interrupt == Dispatch.Interrupt)
356 {
357 /* Get the next one */
358 Dispatch.Interrupt = CONTAINING_RECORD(Dispatch.Interrupt->
359 InterruptListEntry.Flink,
360 KINTERRUPT,
361 InterruptListEntry);
362
363 /* Reconnect it */
364 KiConnectVectorToInterrupt(Dispatch.Interrupt, ChainConnect);
365 }
366
367 /* Remove it */
368 RemoveEntryList(&Interrupt->InterruptListEntry);
369
370 /* Get the next one */
371 NextInterrupt = CONTAINING_RECORD(Dispatch.Interrupt->
372 InterruptListEntry.Flink,
373 KINTERRUPT,
374 InterruptListEntry);
375
376 /* Check if this is the only one left */
377 if (Dispatch.Interrupt == NextInterrupt)
378 {
379 /* Connect it in non-chained mode */
380 KiConnectVectorToInterrupt(Dispatch.Interrupt, NormalConnect);
381 }
382 }
383 else
384 {
385 /* Only one left, disable and remove it */
386 HalDisableSystemInterrupt(Interrupt->Vector, Irql);
387 KiConnectVectorToInterrupt(Interrupt, NoConnect);
388 }
389
390 /* Disconnect it */
391 Interrupt->Connected = FALSE;
392 }
393
394 /* Unlock the dispatcher and revert affinity */
395 KiReleaseDispatcherLock(OldIrql);
396 KeRevertToUserAffinityThread();
397
398 /* Return to caller */
399 return State;
400 }
401
402 /* EOF */