1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ke/i386/irqobj.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
KiGetVectorDispatch(IN ULONG Vector,IN PDISPATCH_INFO Dispatch)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
KiConnectVectorToInterrupt(IN PKINTERRUPT Interrupt,IN CONNECT_TYPE Type)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 FORCEINLINE
133 DECLSPEC_NORETURN
134 VOID
KiExitInterrupt(IN PKTRAP_FRAME TrapFrame,IN KIRQL OldIrql,IN BOOLEAN Spurious)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 DECLSPEC_NORETURN
152 VOID
153 __cdecl
KiUnexpectedInterrupt(VOID)154 KiUnexpectedInterrupt(VOID)
155 {
156 /* Crash the machine */
157 KeBugCheck(TRAP_CAUSE_UNKNOWN);
158 }
159
160 VOID
161 FASTCALL
KiUnexpectedInterruptTailHandler(IN PKTRAP_FRAME TrapFrame)162 KiUnexpectedInterruptTailHandler(IN PKTRAP_FRAME TrapFrame)
163 {
164 KIRQL OldIrql;
165
166 /* Enter trap */
167 KiEnterInterruptTrap(TrapFrame);
168
169 /* Increase interrupt count */
170 KeGetCurrentPrcb()->InterruptCount++;
171
172 /* Start the interrupt */
173 if (HalBeginSystemInterrupt(HIGH_LEVEL, TrapFrame->ErrCode, &OldIrql))
174 {
175 /* Warn user */
176 DPRINT1("\n\x7\x7!!! Unexpected Interrupt 0x%02lx !!!\n", TrapFrame->ErrCode);
177
178 /* Now call the epilogue code */
179 KiExitInterrupt(TrapFrame, OldIrql, FALSE);
180 }
181 else
182 {
183 /* Now call the epilogue code */
184 KiExitInterrupt(TrapFrame, OldIrql, TRUE);
185 }
186 }
187
188 typedef
189 VOID
190 (FASTCALL *PKI_INTERRUPT_DISPATCH)(
191 IN PKTRAP_FRAME TrapFrame,
192 IN PKINTERRUPT Interrupt
193 );
194
195 VOID
196 FASTCALL
KiInterruptDispatch(IN PKTRAP_FRAME TrapFrame,IN PKINTERRUPT Interrupt)197 KiInterruptDispatch(IN PKTRAP_FRAME TrapFrame,
198 IN PKINTERRUPT Interrupt)
199 {
200 KIRQL OldIrql;
201
202 /* Increase interrupt count */
203 KeGetCurrentPrcb()->InterruptCount++;
204
205 /* Begin the interrupt, making sure it's not spurious */
206 if (HalBeginSystemInterrupt(Interrupt->SynchronizeIrql,
207 Interrupt->Vector,
208 &OldIrql))
209 {
210 /* Acquire interrupt lock */
211 KxAcquireSpinLock(Interrupt->ActualLock);
212
213 /* Call the ISR */
214 Interrupt->ServiceRoutine(Interrupt, Interrupt->ServiceContext);
215
216 /* Release interrupt lock */
217 KxReleaseSpinLock(Interrupt->ActualLock);
218
219 /* Now call the epilogue code */
220 KiExitInterrupt(TrapFrame, OldIrql, FALSE);
221 }
222 else
223 {
224 /* Now call the epilogue code */
225 KiExitInterrupt(TrapFrame, OldIrql, TRUE);
226 }
227 }
228
229 VOID
230 FASTCALL
KiChainedDispatch(IN PKTRAP_FRAME TrapFrame,IN PKINTERRUPT Interrupt)231 KiChainedDispatch(IN PKTRAP_FRAME TrapFrame,
232 IN PKINTERRUPT Interrupt)
233 {
234 KIRQL OldIrql, OldInterruptIrql = 0;
235 BOOLEAN Handled;
236 PLIST_ENTRY NextEntry, ListHead;
237
238 /* Increase interrupt count */
239 KeGetCurrentPrcb()->InterruptCount++;
240
241 /* Begin the interrupt, making sure it's not spurious */
242 if (HalBeginSystemInterrupt(Interrupt->Irql,
243 Interrupt->Vector,
244 &OldIrql))
245 {
246 /* Get list pointers */
247 ListHead = &Interrupt->InterruptListEntry;
248 NextEntry = ListHead; /* The head is an entry! */
249 while (TRUE)
250 {
251 /* Check if this interrupt's IRQL is higher than the current one */
252 if (Interrupt->SynchronizeIrql > Interrupt->Irql)
253 {
254 /* Raise to higher IRQL */
255 OldInterruptIrql = KfRaiseIrql(Interrupt->SynchronizeIrql);
256 }
257
258 /* Acquire interrupt lock */
259 KxAcquireSpinLock(Interrupt->ActualLock);
260
261 /* Call the ISR */
262 Handled = Interrupt->ServiceRoutine(Interrupt,
263 Interrupt->ServiceContext);
264
265 /* Release interrupt lock */
266 KxReleaseSpinLock(Interrupt->ActualLock);
267
268 /* Check if this interrupt's IRQL is higher than the current one */
269 if (Interrupt->SynchronizeIrql > Interrupt->Irql)
270 {
271 /* Lower the IRQL back */
272 ASSERT(OldInterruptIrql == Interrupt->Irql);
273 KfLowerIrql(OldInterruptIrql);
274 }
275
276 /* Check if the interrupt got handled and it's level */
277 if ((Handled) && (Interrupt->Mode == LevelSensitive)) break;
278
279 /* What's next? */
280 NextEntry = NextEntry->Flink;
281
282 /* Is this the last one? */
283 if (NextEntry == ListHead)
284 {
285 /* Level should not have gotten here */
286 if (Interrupt->Mode == LevelSensitive) break;
287
288 /* As for edge, we can only exit once nobody can handle the interrupt */
289 if (!Handled) break;
290 }
291
292 /* Get the interrupt object for the next pass */
293 Interrupt = CONTAINING_RECORD(NextEntry, KINTERRUPT, InterruptListEntry);
294 }
295
296 /* Now call the epilogue code */
297 KiExitInterrupt(TrapFrame, OldIrql, FALSE);
298 }
299 else
300 {
301 /* Now call the epilogue code */
302 KiExitInterrupt(TrapFrame, OldIrql, TRUE);
303 }
304 }
305
306 VOID
307 FASTCALL
KiInterruptTemplateHandler(IN PKTRAP_FRAME TrapFrame,IN PKINTERRUPT Interrupt)308 KiInterruptTemplateHandler(IN PKTRAP_FRAME TrapFrame,
309 IN PKINTERRUPT Interrupt)
310 {
311 /* Enter interrupt frame */
312 KiEnterInterruptTrap(TrapFrame);
313
314 /* Call the correct dispatcher */
315 ((PKI_INTERRUPT_DISPATCH)Interrupt->DispatchAddress)(TrapFrame, Interrupt);
316 }
317
318
319 /* PUBLIC FUNCTIONS **********************************************************/
320
321 /*
322 * @implemented
323 */
324 VOID
325 NTAPI
KeInitializeInterrupt(IN PKINTERRUPT Interrupt,IN PKSERVICE_ROUTINE ServiceRoutine,IN PVOID ServiceContext,IN PKSPIN_LOCK SpinLock,IN ULONG Vector,IN KIRQL Irql,IN KIRQL SynchronizeIrql,IN KINTERRUPT_MODE InterruptMode,IN BOOLEAN ShareVector,IN CHAR ProcessorNumber,IN BOOLEAN FloatingSave)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 /* Use 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 < DISPATCH_LENGTH; 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
KeConnectInterrupt(IN PKINTERRUPT Interrupt)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 /*
453 * Verify the IRQL for chained connect,
454 */
455 #if defined(CONFIG_SMP)
456 ASSERT(Irql <= SYNCH_LEVEL);
457 #else
458 ASSERT(Irql <= (IPI_LEVEL - 2));
459 #endif
460
461 /* Check if this is the first chain */
462 if (Dispatch.Type != ChainConnect)
463 {
464 /* This is not supported */
465 ASSERT(Dispatch.Interrupt->Mode != Latched);
466
467 /* Setup the chainned handler */
468 KiConnectVectorToInterrupt(Dispatch.Interrupt, ChainConnect);
469 }
470
471 /* Insert into the interrupt list */
472 InsertTailList(&Dispatch.Interrupt->InterruptListEntry,
473 &Interrupt->InterruptListEntry);
474 }
475 }
476
477 /* Unlock the dispatcher and revert affinity */
478 KiReleaseDispatcherLock(OldIrql);
479 KeRevertToUserAffinityThread();
480
481 /* Check if we failed while trying to connect */
482 if ((Connected) && (Error))
483 {
484 DPRINT1("HalEnableSystemInterrupt failed\n");
485 KeDisconnectInterrupt(Interrupt);
486 Connected = FALSE;
487 }
488
489 /* Return to caller */
490 return Connected;
491 }
492
493 /*
494 * @implemented
495 */
496 BOOLEAN
497 NTAPI
KeDisconnectInterrupt(IN PKINTERRUPT Interrupt)498 KeDisconnectInterrupt(IN PKINTERRUPT Interrupt)
499 {
500 KIRQL OldIrql, Irql;
501 ULONG Vector;
502 DISPATCH_INFO Dispatch;
503 PKINTERRUPT NextInterrupt;
504 BOOLEAN State;
505
506 /* Set the affinity */
507 KeSetSystemAffinityThread(1 << Interrupt->Number);
508
509 /* Lock the dispatcher */
510 OldIrql = KiAcquireDispatcherLock();
511
512 /* Check if it's actually connected */
513 State = Interrupt->Connected;
514 if (State)
515 {
516 /* Get the vector and IRQL */
517 Irql = Interrupt->Irql;
518 Vector = Interrupt->Vector;
519
520 /* Get vector dispatch data */
521 KiGetVectorDispatch(Vector, &Dispatch);
522
523 /* Check if it was chained */
524 if (Dispatch.Type == ChainConnect)
525 {
526 /* Check if the top-level interrupt is being removed */
527 #if defined(CONFIG_SMP)
528 ASSERT(Irql <= SYNCH_LEVEL);
529 #else
530 ASSERT(Irql <= (IPI_LEVEL - 2));
531 #endif
532 if (Interrupt == Dispatch.Interrupt)
533 {
534 /* Get the next one */
535 Dispatch.Interrupt = CONTAINING_RECORD(Dispatch.Interrupt->
536 InterruptListEntry.Flink,
537 KINTERRUPT,
538 InterruptListEntry);
539
540 /* Reconnect it */
541 KiConnectVectorToInterrupt(Dispatch.Interrupt, ChainConnect);
542 }
543
544 /* Remove it */
545 RemoveEntryList(&Interrupt->InterruptListEntry);
546
547 /* Get the next one */
548 NextInterrupt = CONTAINING_RECORD(Dispatch.Interrupt->
549 InterruptListEntry.Flink,
550 KINTERRUPT,
551 InterruptListEntry);
552
553 /* Check if this is the only one left */
554 if (Dispatch.Interrupt == NextInterrupt)
555 {
556 /* Connect it in non-chained mode */
557 KiConnectVectorToInterrupt(Dispatch.Interrupt, NormalConnect);
558 }
559 }
560 else
561 {
562 /* Only one left, disable and remove it */
563 HalDisableSystemInterrupt(Interrupt->Vector, Irql);
564 KiConnectVectorToInterrupt(Interrupt, NoConnect);
565 }
566
567 /* Disconnect it */
568 Interrupt->Connected = FALSE;
569 }
570
571 /* Unlock the dispatcher and revert affinity */
572 KiReleaseDispatcherLock(OldIrql);
573 KeRevertToUserAffinityThread();
574
575 /* Return to caller */
576 return State;
577 }
578
579 /*
580 * @implemented
581 */
582 BOOLEAN
583 NTAPI
KeSynchronizeExecution(IN OUT PKINTERRUPT Interrupt,IN PKSYNCHRONIZE_ROUTINE SynchronizeRoutine,IN PVOID SynchronizeContext OPTIONAL)584 KeSynchronizeExecution(IN OUT PKINTERRUPT Interrupt,
585 IN PKSYNCHRONIZE_ROUTINE SynchronizeRoutine,
586 IN PVOID SynchronizeContext OPTIONAL)
587 {
588 BOOLEAN Success;
589 KIRQL OldIrql;
590
591 /* Raise IRQL */
592 KeRaiseIrql(Interrupt->SynchronizeIrql,
593 &OldIrql);
594
595 /* Acquire interrupt spinlock */
596 KeAcquireSpinLockAtDpcLevel(Interrupt->ActualLock);
597
598 /* Call the routine */
599 Success = SynchronizeRoutine(SynchronizeContext);
600
601 /* Release lock */
602 KeReleaseSpinLockFromDpcLevel(Interrupt->ActualLock);
603
604 /* Lower IRQL */
605 KeLowerIrql(OldIrql);
606
607 /* Return status */
608 return Success;
609 }
610
611 /* EOF */
612