a9483f18caa25e57025c76873f730fd113e8d0b3
[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 FORCEINLINE
133 DECLSPEC_NORETURN
134 VOID
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
154 KiUnexpectedInterrupt(VOID)
155 {
156 /* Crash the machine */
157 KeBugCheck(TRAP_CAUSE_UNKNOWN);
158 }
159
160 VOID
161 FASTCALL
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
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
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
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
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 < 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
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 #elif (NTDDI_VERSION >= NTDDI_WS03)
458 ASSERT(Irql <= (IPI_LEVEL - 2));
459 #else
460 ASSERT(Irql <= (IPI_LEVEL - 1));
461 #endif
462
463 /* Check if this is the first chain */
464 if (Dispatch.Type != ChainConnect)
465 {
466 /* This is not supported */
467 ASSERT(Dispatch.Interrupt->Mode != Latched);
468
469 /* Setup the chainned handler */
470 KiConnectVectorToInterrupt(Dispatch.Interrupt, ChainConnect);
471 }
472
473 /* Insert into the interrupt list */
474 InsertTailList(&Dispatch.Interrupt->InterruptListEntry,
475 &Interrupt->InterruptListEntry);
476 }
477 }
478
479 /* Unlock the dispatcher and revert affinity */
480 KiReleaseDispatcherLock(OldIrql);
481 KeRevertToUserAffinityThread();
482
483 /* Check if we failed while trying to connect */
484 if ((Connected) && (Error))
485 {
486 DPRINT1("HalEnableSystemInterrupt failed\n");
487 KeDisconnectInterrupt(Interrupt);
488 Connected = FALSE;
489 }
490
491 /* Return to caller */
492 return Connected;
493 }
494
495 /*
496 * @implemented
497 */
498 BOOLEAN
499 NTAPI
500 KeDisconnectInterrupt(IN PKINTERRUPT Interrupt)
501 {
502 KIRQL OldIrql, Irql;
503 ULONG Vector;
504 DISPATCH_INFO Dispatch;
505 PKINTERRUPT NextInterrupt;
506 BOOLEAN State;
507
508 /* Set the affinity */
509 KeSetSystemAffinityThread(1 << Interrupt->Number);
510
511 /* Lock the dispatcher */
512 OldIrql = KiAcquireDispatcherLock();
513
514 /* Check if it's actually connected */
515 State = Interrupt->Connected;
516 if (State)
517 {
518 /* Get the vector and IRQL */
519 Irql = Interrupt->Irql;
520 Vector = Interrupt->Vector;
521
522 /* Get vector dispatch data */
523 KiGetVectorDispatch(Vector, &Dispatch);
524
525 /* Check if it was chained */
526 if (Dispatch.Type == ChainConnect)
527 {
528 /* Check if the top-level interrupt is being removed */
529 ASSERT(Irql <= SYNCH_LEVEL);
530 if (Interrupt == Dispatch.Interrupt)
531 {
532 /* Get the next one */
533 Dispatch.Interrupt = CONTAINING_RECORD(Dispatch.Interrupt->
534 InterruptListEntry.Flink,
535 KINTERRUPT,
536 InterruptListEntry);
537
538 /* Reconnect it */
539 KiConnectVectorToInterrupt(Dispatch.Interrupt, ChainConnect);
540 }
541
542 /* Remove it */
543 RemoveEntryList(&Interrupt->InterruptListEntry);
544
545 /* Get the next one */
546 NextInterrupt = CONTAINING_RECORD(Dispatch.Interrupt->
547 InterruptListEntry.Flink,
548 KINTERRUPT,
549 InterruptListEntry);
550
551 /* Check if this is the only one left */
552 if (Dispatch.Interrupt == NextInterrupt)
553 {
554 /* Connect it in non-chained mode */
555 KiConnectVectorToInterrupt(Dispatch.Interrupt, NormalConnect);
556 }
557 }
558 else
559 {
560 /* Only one left, disable and remove it */
561 HalDisableSystemInterrupt(Interrupt->Vector, Irql);
562 KiConnectVectorToInterrupt(Interrupt, NoConnect);
563 }
564
565 /* Disconnect it */
566 Interrupt->Connected = FALSE;
567 }
568
569 /* Unlock the dispatcher and revert affinity */
570 KiReleaseDispatcherLock(OldIrql);
571 KeRevertToUserAffinityThread();
572
573 /* Return to caller */
574 return State;
575 }
576
577 /*
578 * @implemented
579 */
580 BOOLEAN
581 NTAPI
582 KeSynchronizeExecution(IN OUT PKINTERRUPT Interrupt,
583 IN PKSYNCHRONIZE_ROUTINE SynchronizeRoutine,
584 IN PVOID SynchronizeContext OPTIONAL)
585 {
586 BOOLEAN Success;
587 KIRQL OldIrql;
588
589 /* Raise IRQL */
590 KeRaiseIrql(Interrupt->SynchronizeIrql,
591 &OldIrql);
592
593 /* Acquire interrupt spinlock */
594 KeAcquireSpinLockAtDpcLevel(Interrupt->ActualLock);
595
596 /* Call the routine */
597 Success = SynchronizeRoutine(SynchronizeContext);
598
599 /* Release lock */
600 KeReleaseSpinLockFromDpcLevel(Interrupt->ActualLock);
601
602 /* Lower IRQL */
603 KeLowerIrql(OldIrql);
604
605 /* Return status */
606 return Success;
607 }
608
609 /* EOF */