[HAL]: Rewrite IRQL handling. Alex's original code (lately translated to C) was a...
[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 purposes 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 PVOID 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 = (PVOID)KiInterruptDispatch;
52 Dispatch->FloatingDispatch = NULL; // Floating Interrupts are not supported
53 Dispatch->ChainedDispatch = (PVOID)KiChainedDispatch;
54 Dispatch->FlatDispatch = NULL;
55
56 /* Get the current handler */
57 Current = KeQueryInterruptHandler(Vector);
58
59 /* Set the interrupt */
60 Dispatch->Interrupt = CONTAINING_RECORD(Current,
61 KINTERRUPT,
62 DispatchCode);
63
64 /* Check what this interrupt is connected to */
65 if ((PKINTERRUPT_ROUTINE)Current == Dispatch->NoDispatch)
66 {
67 /* Not connected */
68 Dispatch->Type = NoConnect;
69 }
70 else
71 {
72 /* Get the handler */
73 Handler = Dispatch->Interrupt->DispatchAddress;
74 if (Handler == Dispatch->ChainedDispatch)
75 {
76 /* It's a chained interrupt */
77 Dispatch->Type = ChainConnect;
78 }
79 else if ((Handler == Dispatch->InterruptDispatch) ||
80 (Handler == Dispatch->FloatingDispatch))
81 {
82 /* It's unchained */
83 Dispatch->Type = NormalConnect;
84 }
85 else
86 {
87 /* Unknown */
88 Dispatch->Type = UnknownConnect;
89 }
90 }
91 }
92
93 VOID
94 NTAPI
95 KiConnectVectorToInterrupt(IN PKINTERRUPT Interrupt,
96 IN CONNECT_TYPE Type)
97 {
98 DISPATCH_INFO Dispatch;
99 PKINTERRUPT_ROUTINE Handler;
100
101 /* Get vector data */
102 KiGetVectorDispatch(Interrupt->Vector, &Dispatch);
103
104 /* Check if we're only disconnecting */
105 if (Type == NoConnect)
106 {
107 /* Set the handler to NoDispatch */
108 Handler = Dispatch.NoDispatch;
109 }
110 else
111 {
112 /* Get the right handler */
113 Handler = (Type == NormalConnect) ?
114 Dispatch.InterruptDispatch:
115 Dispatch.ChainedDispatch;
116 ASSERT(Interrupt->FloatingSave == FALSE);
117
118 /* Set the handler */
119 Interrupt->DispatchAddress = Handler;
120
121 /* Read note in trap.s -- patching not needed since JMP is static */
122
123 /* Now set the final handler address */
124 ASSERT(Dispatch.FlatDispatch == NULL);
125 Handler = (PVOID)&Interrupt->DispatchCode;
126 }
127
128 /* Register the interrupt */
129 KeRegisterInterruptHandler(Interrupt->Vector, Handler);
130 }
131
132 VOID
133 FORCEINLINE
134 DECLSPEC_NORETURN
135 KiExitInterrupt(IN PKTRAP_FRAME TrapFrame,
136 IN KIRQL OldIrql,
137 IN BOOLEAN Spurious)
138 {
139 /* Check if this was a real interrupt */
140 if (!Spurious)
141 {
142 /* It was, disable interrupts and restore the IRQL */
143 _disable();
144 HalEndSystemInterrupt(OldIrql, TrapFrame);
145 }
146
147 /* Now exit the trap */
148 KiEoiHelper(TrapFrame);
149 }
150
151 VOID
152 KiUnexpectedInterrupt(VOID)
153 {
154 /* Crash the machine */
155 KeBugCheck(TRAP_CAUSE_UNKNOWN);
156 }
157
158 VOID
159 FASTCALL
160 KiUnexpectedInterruptTailHandler(IN PKTRAP_FRAME TrapFrame)
161 {
162 KIRQL OldIrql;
163
164 /* Enter trap */
165 KiEnterInterruptTrap(TrapFrame);
166
167 /* Increase interrupt count */
168 KeGetCurrentPrcb()->InterruptCount++;
169
170 /* Start the interrupt */
171 if (HalBeginSystemInterrupt(HIGH_LEVEL, TrapFrame->Eax, &OldIrql))
172 {
173 /* Warn user */
174 DPRINT1("\n\x7\x7!!! Unexpected Interrupt %02lx !!!\n");
175
176 /* Now call the epilogue code */
177 KiExitInterrupt(TrapFrame, OldIrql, FALSE);
178 }
179 else
180 {
181 /* Now call the epilogue code */
182 KiExitInterrupt(TrapFrame, OldIrql, TRUE);
183 }
184 }
185
186 typedef
187 FASTCALL
188 VOID
189 (PKI_INTERRUPT_DISPATCH)(
190 IN PKTRAP_FRAME TrapFrame,
191 IN PKINTERRUPT Interrupt
192 );
193
194 VOID
195 FASTCALL
196 KiInterruptDispatch(IN PKTRAP_FRAME TrapFrame,
197 IN PKINTERRUPT Interrupt)
198 {
199 KIRQL OldIrql;
200
201 /* Increase interrupt count */
202 KeGetCurrentPrcb()->InterruptCount++;
203
204 /* Begin the interrupt, making sure it's not spurious */
205 if (HalBeginSystemInterrupt(Interrupt->SynchronizeIrql,
206 Interrupt->Vector,
207 &OldIrql))
208 {
209 /* Acquire interrupt lock */
210 KxAcquireSpinLock(Interrupt->ActualLock);
211
212 /* Call the ISR */
213 Interrupt->ServiceRoutine(Interrupt, Interrupt->ServiceContext);
214
215 /* Release interrupt lock */
216 KxReleaseSpinLock(Interrupt->ActualLock);
217
218 /* Now call the epilogue code */
219 KiExitInterrupt(TrapFrame, OldIrql, FALSE);
220 }
221 else
222 {
223 /* Now call the epilogue code */
224 KiExitInterrupt(TrapFrame, OldIrql, TRUE);
225 }
226 }
227
228 VOID
229 FASTCALL
230 KiChainedDispatch(IN PKTRAP_FRAME TrapFrame,
231 IN PKINTERRUPT Interrupt)
232 {
233 KIRQL OldIrql;
234 BOOLEAN Handled;
235 PLIST_ENTRY NextEntry, ListHead;
236
237 /* Increase interrupt count */
238 KeGetCurrentPrcb()->InterruptCount++;
239
240 /* Begin the interrupt, making sure it's not spurious */
241 if (HalBeginSystemInterrupt(Interrupt->Irql,
242 Interrupt->Vector,
243 &OldIrql))
244 {
245 /* Get list pointers */
246 ListHead = &Interrupt->InterruptListEntry;
247 NextEntry = ListHead; /* The head is an entry! */
248 while (TRUE)
249 {
250 /* Check if this interrupt's IRQL is higher than the current one */
251 if (Interrupt->SynchronizeIrql > Interrupt->Irql)
252 {
253 /* Raise to higher IRQL */
254 OldIrql = KfRaiseIrql(Interrupt->Irql);
255 }
256
257 /* Acquire interrupt lock */
258 KxAcquireSpinLock(Interrupt->ActualLock);
259
260 /* Call the ISR */
261 Handled = Interrupt->ServiceRoutine(Interrupt,
262 Interrupt->ServiceContext);
263
264 /* Release interrupt lock */
265 KxReleaseSpinLock(Interrupt->ActualLock);
266
267 /* Check if this interrupt's IRQL is higher than the current one */
268 if (Interrupt->SynchronizeIrql > Interrupt->Irql)
269 {
270 /* Lower the IRQL back */
271 KfLowerIrql(OldIrql);
272 }
273
274 /* Check if the interrupt got handled and it's level */
275 if ((Handled) && (Interrupt->Mode == LevelSensitive)) break;
276
277 /* What's next? */
278 NextEntry = NextEntry->Flink;
279
280 /* Is this the last one? */
281 if (NextEntry == ListHead)
282 {
283 /* Level should not have gotten here */
284 if (Interrupt->Mode == LevelSensitive) break;
285
286 /* As for edge, we can only exit once nobody can handle the interrupt */
287 if (!Handled) break;
288 }
289
290 /* Get the interrupt object for the next pass */
291 Interrupt = CONTAINING_RECORD(NextEntry, KINTERRUPT, InterruptListEntry);
292 }
293
294 /* Now call the epilogue code */
295 KiExitInterrupt(TrapFrame, OldIrql, FALSE);
296 }
297 else
298 {
299 /* Now call the epilogue code */
300 KiExitInterrupt(TrapFrame, OldIrql, TRUE);
301 }
302 }
303
304 VOID
305 FASTCALL
306 KiInterruptTemplateHandler(IN PKTRAP_FRAME TrapFrame,
307 IN PKINTERRUPT Interrupt)
308 {
309 /* Enter interrupt frame */
310 KiEnterInterruptTrap(TrapFrame);
311
312 /* Call the correct dispatcher */
313 ((PKI_INTERRUPT_DISPATCH*)Interrupt->DispatchAddress)(TrapFrame, Interrupt);
314 }
315
316 KiTrap(KiInterruptTemplate, KI_PUSH_FAKE_ERROR_CODE | KI_HARDWARE_INT);
317 KiTrap(KiUnexpectedInterruptTail, KI_PUSH_FAKE_ERROR_CODE);
318
319 /* PUBLIC FUNCTIONS **********************************************************/
320
321 /*
322 * @implemented
323 */
324 VOID
325 NTAPI
326 KeInitializeInterrupt(IN PKINTERRUPT Interrupt,
327 IN PKSERVICE_ROUTINE ServiceRoutine,
328 IN PVOID ServiceContext,
329 IN PKSPIN_LOCK SpinLock,
330 IN ULONG Vector,
331 IN KIRQL Irql,
332 IN KIRQL SynchronizeIrql,
333 IN KINTERRUPT_MODE InterruptMode,
334 IN BOOLEAN ShareVector,
335 IN CHAR ProcessorNumber,
336 IN BOOLEAN FloatingSave)
337 {
338 ULONG i;
339 PULONG DispatchCode = &Interrupt->DispatchCode[0], Patch = DispatchCode;
340
341 /* Set the Interrupt Header */
342 Interrupt->Type = InterruptObject;
343 Interrupt->Size = sizeof(KINTERRUPT);
344
345 /* Check if we got a spinlock */
346 if (SpinLock)
347 {
348 Interrupt->ActualLock = SpinLock;
349 }
350 else
351 {
352 /* This means we'll be usin the built-in one */
353 KeInitializeSpinLock(&Interrupt->SpinLock);
354 Interrupt->ActualLock = &Interrupt->SpinLock;
355 }
356
357 /* Set the other settings */
358 Interrupt->ServiceRoutine = ServiceRoutine;
359 Interrupt->ServiceContext = ServiceContext;
360 Interrupt->Vector = Vector;
361 Interrupt->Irql = Irql;
362 Interrupt->SynchronizeIrql = SynchronizeIrql;
363 Interrupt->Mode = InterruptMode;
364 Interrupt->ShareVector = ShareVector;
365 Interrupt->Number = ProcessorNumber;
366 Interrupt->FloatingSave = FloatingSave;
367 Interrupt->TickCount = MAXULONG;
368 Interrupt->DispatchCount = MAXULONG;
369
370 /* Loop the template in memory */
371 for (i = 0; i < KINTERRUPT_DISPATCH_CODES; i++)
372 {
373 /* Copy the dispatch code */
374 *DispatchCode++ = ((PULONG)KiInterruptTemplate)[i];
375 }
376
377 /* Jump to the last 4 bytes */
378 Patch = (PULONG)((ULONG_PTR)Patch +
379 ((ULONG_PTR)&KiInterruptTemplateObject -
380 (ULONG_PTR)KiInterruptTemplate) - 4);
381
382 /* Apply the patch */
383 *Patch = PtrToUlong(Interrupt);
384
385 /* Disconnect it at first */
386 Interrupt->Connected = FALSE;
387 }
388
389 /*
390 * @implemented
391 */
392 BOOLEAN
393 NTAPI
394 KeConnectInterrupt(IN PKINTERRUPT Interrupt)
395 {
396 BOOLEAN Connected, Error, Status;
397 KIRQL Irql, OldIrql;
398 UCHAR Number;
399 ULONG Vector;
400 DISPATCH_INFO Dispatch;
401
402 /* Get data from interrupt */
403 Number = Interrupt->Number;
404 Vector = Interrupt->Vector;
405 Irql = Interrupt->Irql;
406
407 /* Validate the settings */
408 if ((Irql > HIGH_LEVEL) ||
409 (Number >= KeNumberProcessors) ||
410 (Interrupt->SynchronizeIrql < Irql) ||
411 (Interrupt->FloatingSave))
412 {
413 return FALSE;
414 }
415
416 /* Set defaults */
417 Connected = FALSE;
418 Error = FALSE;
419
420 /* Set the system affinity and acquire the dispatcher lock */
421 KeSetSystemAffinityThread(1 << Number);
422 OldIrql = KiAcquireDispatcherLock();
423
424 /* Check if it's already been connected */
425 if (!Interrupt->Connected)
426 {
427 /* Get vector dispatching information */
428 KiGetVectorDispatch(Vector, &Dispatch);
429
430 /* Check if the vector is already connected */
431 if (Dispatch.Type == NoConnect)
432 {
433 /* Do the connection */
434 Interrupt->Connected = Connected = TRUE;
435
436 /* Initialize the list */
437 InitializeListHead(&Interrupt->InterruptListEntry);
438
439 /* Connect and enable the interrupt */
440 KiConnectVectorToInterrupt(Interrupt, NormalConnect);
441 Status = HalEnableSystemInterrupt(Vector, Irql, Interrupt->Mode);
442 if (!Status) Error = TRUE;
443 }
444 else if ((Dispatch.Type != UnknownConnect) &&
445 (Interrupt->ShareVector) &&
446 (Dispatch.Interrupt->ShareVector) &&
447 (Dispatch.Interrupt->Mode == Interrupt->Mode))
448 {
449 /* The vector is shared and the interrupts are compatible */
450 Interrupt->Connected = Connected = TRUE;
451
452 /* FIXME */
453 // ASSERT(Irql <= SYNCH_LEVEL);
454
455 /* Check if this is the first chain */
456 if (Dispatch.Type != ChainConnect)
457 {
458 /* This is not supported */
459 ASSERT(Dispatch.Interrupt->Mode != Latched);
460
461 /* Setup the chainned handler */
462 KiConnectVectorToInterrupt(Dispatch.Interrupt, ChainConnect);
463 }
464
465 /* Insert into the interrupt list */
466 InsertTailList(&Dispatch.Interrupt->InterruptListEntry,
467 &Interrupt->InterruptListEntry);
468 }
469 }
470
471 /* Unlock the dispatcher and revert affinity */
472 KiReleaseDispatcherLock(OldIrql);
473 KeRevertToUserAffinityThread();
474
475 /* Check if we failed while trying to connect */
476 if ((Connected) && (Error))
477 {
478 DPRINT1("HalEnableSystemInterrupt failed\n");
479 KeDisconnectInterrupt(Interrupt);
480 Connected = FALSE;
481 }
482
483 /* Return to caller */
484 return Connected;
485 }
486
487 /*
488 * @implemented
489 */
490 BOOLEAN
491 NTAPI
492 KeDisconnectInterrupt(IN PKINTERRUPT Interrupt)
493 {
494 KIRQL OldIrql, Irql;
495 ULONG Vector;
496 DISPATCH_INFO Dispatch;
497 PKINTERRUPT NextInterrupt;
498 BOOLEAN State;
499
500 /* Set the affinity */
501 KeSetSystemAffinityThread(1 << Interrupt->Number);
502
503 /* Lock the dispatcher */
504 OldIrql = KiAcquireDispatcherLock();
505
506 /* Check if it's actually connected */
507 State = Interrupt->Connected;
508 if (State)
509 {
510 /* Get the vector and IRQL */
511 Irql = Interrupt->Irql;
512 Vector = Interrupt->Vector;
513
514 /* Get vector dispatch data */
515 KiGetVectorDispatch(Vector, &Dispatch);
516
517 /* Check if it was chained */
518 if (Dispatch.Type == ChainConnect)
519 {
520 /* Check if the top-level interrupt is being removed */
521 ASSERT(Irql <= SYNCH_LEVEL);
522 if (Interrupt == Dispatch.Interrupt)
523 {
524 /* Get the next one */
525 Dispatch.Interrupt = CONTAINING_RECORD(Dispatch.Interrupt->
526 InterruptListEntry.Flink,
527 KINTERRUPT,
528 InterruptListEntry);
529
530 /* Reconnect it */
531 KiConnectVectorToInterrupt(Dispatch.Interrupt, ChainConnect);
532 }
533
534 /* Remove it */
535 RemoveEntryList(&Interrupt->InterruptListEntry);
536
537 /* Get the next one */
538 NextInterrupt = CONTAINING_RECORD(Dispatch.Interrupt->
539 InterruptListEntry.Flink,
540 KINTERRUPT,
541 InterruptListEntry);
542
543 /* Check if this is the only one left */
544 if (Dispatch.Interrupt == NextInterrupt)
545 {
546 /* Connect it in non-chained mode */
547 KiConnectVectorToInterrupt(Dispatch.Interrupt, NormalConnect);
548 }
549 }
550 else
551 {
552 /* Only one left, disable and remove it */
553 HalDisableSystemInterrupt(Interrupt->Vector, Irql);
554 KiConnectVectorToInterrupt(Interrupt, NoConnect);
555 }
556
557 /* Disconnect it */
558 Interrupt->Connected = FALSE;
559 }
560
561 /* Unlock the dispatcher and revert affinity */
562 KiReleaseDispatcherLock(OldIrql);
563 KeRevertToUserAffinityThread();
564
565 /* Return to caller */
566 return State;
567 }
568
569 /*
570 * @implemented
571 */
572 BOOLEAN
573 NTAPI
574 KeSynchronizeExecution(IN OUT PKINTERRUPT Interrupt,
575 IN PKSYNCHRONIZE_ROUTINE SynchronizeRoutine,
576 IN PVOID SynchronizeContext OPTIONAL)
577 {
578 NTSTATUS Status;
579 KIRQL OldIrql;
580
581 /* Raise IRQL */
582 OldIrql = KfRaiseIrql(Interrupt->SynchronizeIrql);
583
584 /* Acquire interrupt spinlock */
585 KeAcquireSpinLockAtDpcLevel(Interrupt->ActualLock);
586
587 /* Call the routine */
588 Status = SynchronizeRoutine(SynchronizeContext);
589
590 /* Release lock */
591 KeReleaseSpinLockFromDpcLevel(Interrupt->ActualLock);
592
593 /* Lower IRQL */
594 KfLowerIrql(OldIrql);
595
596 /* Return status */
597 return Status;
598 }
599
600 /* EOF */