[BRANCHES]
[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 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->ErrCode, &OldIrql))
172 {
173 /* Warn user */
174 DPRINT1("\n\x7\x7!!! Unexpected Interrupt 0x%02lx !!!\n", TrapFrame->ErrCode);
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 VOID
188 (FASTCALL *PKI_INTERRUPT_DISPATCH)(
189 IN PKTRAP_FRAME TrapFrame,
190 IN PKINTERRUPT Interrupt
191 );
192
193 VOID
194 FASTCALL
195 KiInterruptDispatch(IN PKTRAP_FRAME TrapFrame,
196 IN PKINTERRUPT Interrupt)
197 {
198 KIRQL OldIrql;
199
200 /* Increase interrupt count */
201 KeGetCurrentPrcb()->InterruptCount++;
202
203 /* Begin the interrupt, making sure it's not spurious */
204 if (HalBeginSystemInterrupt(Interrupt->SynchronizeIrql,
205 Interrupt->Vector,
206 &OldIrql))
207 {
208 /* Acquire interrupt lock */
209 KxAcquireSpinLock(Interrupt->ActualLock);
210
211 /* Call the ISR */
212 Interrupt->ServiceRoutine(Interrupt, Interrupt->ServiceContext);
213
214 /* Release interrupt lock */
215 KxReleaseSpinLock(Interrupt->ActualLock);
216
217 /* Now call the epilogue code */
218 KiExitInterrupt(TrapFrame, OldIrql, FALSE);
219 }
220 else
221 {
222 /* Now call the epilogue code */
223 KiExitInterrupt(TrapFrame, OldIrql, TRUE);
224 }
225 }
226
227 VOID
228 FASTCALL
229 KiChainedDispatch(IN PKTRAP_FRAME TrapFrame,
230 IN PKINTERRUPT Interrupt)
231 {
232 KIRQL OldIrql, OldInterruptIrql = 0;
233 BOOLEAN Handled;
234 PLIST_ENTRY NextEntry, ListHead;
235
236 /* Increase interrupt count */
237 KeGetCurrentPrcb()->InterruptCount++;
238
239 /* Begin the interrupt, making sure it's not spurious */
240 if (HalBeginSystemInterrupt(Interrupt->Irql,
241 Interrupt->Vector,
242 &OldIrql))
243 {
244 /* Get list pointers */
245 ListHead = &Interrupt->InterruptListEntry;
246 NextEntry = ListHead; /* The head is an entry! */
247 while (TRUE)
248 {
249 /* Check if this interrupt's IRQL is higher than the current one */
250 if (Interrupt->SynchronizeIrql > Interrupt->Irql)
251 {
252 /* Raise to higher IRQL */
253 OldInterruptIrql = KfRaiseIrql(Interrupt->SynchronizeIrql);
254 }
255
256 /* Acquire interrupt lock */
257 KxAcquireSpinLock(Interrupt->ActualLock);
258
259 /* Call the ISR */
260 Handled = Interrupt->ServiceRoutine(Interrupt,
261 Interrupt->ServiceContext);
262
263 /* Release interrupt lock */
264 KxReleaseSpinLock(Interrupt->ActualLock);
265
266 /* Check if this interrupt's IRQL is higher than the current one */
267 if (Interrupt->SynchronizeIrql > Interrupt->Irql)
268 {
269 /* Lower the IRQL back */
270 ASSERT(OldInterruptIrql == Interrupt->Irql);
271 KfLowerIrql(OldInterruptIrql);
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
317 /* PUBLIC FUNCTIONS **********************************************************/
318
319 /*
320 * @implemented
321 */
322 VOID
323 NTAPI
324 KeInitializeInterrupt(IN PKINTERRUPT Interrupt,
325 IN PKSERVICE_ROUTINE ServiceRoutine,
326 IN PVOID ServiceContext,
327 IN PKSPIN_LOCK SpinLock,
328 IN ULONG Vector,
329 IN KIRQL Irql,
330 IN KIRQL SynchronizeIrql,
331 IN KINTERRUPT_MODE InterruptMode,
332 IN BOOLEAN ShareVector,
333 IN CHAR ProcessorNumber,
334 IN BOOLEAN FloatingSave)
335 {
336 ULONG i;
337 PULONG DispatchCode = &Interrupt->DispatchCode[0], Patch = DispatchCode;
338
339 /* Set the Interrupt Header */
340 Interrupt->Type = InterruptObject;
341 Interrupt->Size = sizeof(KINTERRUPT);
342
343 /* Check if we got a spinlock */
344 if (SpinLock)
345 {
346 Interrupt->ActualLock = SpinLock;
347 }
348 else
349 {
350 /* This means we'll be usin the built-in one */
351 KeInitializeSpinLock(&Interrupt->SpinLock);
352 Interrupt->ActualLock = &Interrupt->SpinLock;
353 }
354
355 /* Set the other settings */
356 Interrupt->ServiceRoutine = ServiceRoutine;
357 Interrupt->ServiceContext = ServiceContext;
358 Interrupt->Vector = Vector;
359 Interrupt->Irql = Irql;
360 Interrupt->SynchronizeIrql = SynchronizeIrql;
361 Interrupt->Mode = InterruptMode;
362 Interrupt->ShareVector = ShareVector;
363 Interrupt->Number = ProcessorNumber;
364 Interrupt->FloatingSave = FloatingSave;
365 Interrupt->TickCount = MAXULONG;
366 Interrupt->DispatchCount = MAXULONG;
367
368 /* Loop the template in memory */
369 for (i = 0; i < DISPATCH_LENGTH; i++)
370 {
371 /* Copy the dispatch code */
372 *DispatchCode++ = ((PULONG)KiInterruptTemplate)[i];
373 }
374
375 /* Jump to the last 4 bytes */
376 Patch = (PULONG)((ULONG_PTR)Patch +
377 ((ULONG_PTR)&KiInterruptTemplateObject -
378 (ULONG_PTR)KiInterruptTemplate) - 4);
379
380 /* Apply the patch */
381 *Patch = PtrToUlong(Interrupt);
382
383 /* Disconnect it at first */
384 Interrupt->Connected = FALSE;
385 }
386
387 /*
388 * @implemented
389 */
390 BOOLEAN
391 NTAPI
392 KeConnectInterrupt(IN PKINTERRUPT Interrupt)
393 {
394 BOOLEAN Connected, Error, Status;
395 KIRQL Irql, OldIrql;
396 UCHAR Number;
397 ULONG Vector;
398 DISPATCH_INFO Dispatch;
399
400 /* Get data from interrupt */
401 Number = Interrupt->Number;
402 Vector = Interrupt->Vector;
403 Irql = Interrupt->Irql;
404
405 /* Validate the settings */
406 if ((Irql > HIGH_LEVEL) ||
407 (Number >= KeNumberProcessors) ||
408 (Interrupt->SynchronizeIrql < Irql) ||
409 (Interrupt->FloatingSave))
410 {
411 return FALSE;
412 }
413
414 /* Set defaults */
415 Connected = FALSE;
416 Error = FALSE;
417
418 /* Set the system affinity and acquire the dispatcher lock */
419 KeSetSystemAffinityThread(1 << Number);
420 OldIrql = KiAcquireDispatcherLock();
421
422 /* Check if it's already been connected */
423 if (!Interrupt->Connected)
424 {
425 /* Get vector dispatching information */
426 KiGetVectorDispatch(Vector, &Dispatch);
427
428 /* Check if the vector is already connected */
429 if (Dispatch.Type == NoConnect)
430 {
431 /* Do the connection */
432 Interrupt->Connected = Connected = TRUE;
433
434 /* Initialize the list */
435 InitializeListHead(&Interrupt->InterruptListEntry);
436
437 /* Connect and enable the interrupt */
438 KiConnectVectorToInterrupt(Interrupt, NormalConnect);
439 Status = HalEnableSystemInterrupt(Vector, Irql, Interrupt->Mode);
440 if (!Status) Error = TRUE;
441 }
442 else if ((Dispatch.Type != UnknownConnect) &&
443 (Interrupt->ShareVector) &&
444 (Dispatch.Interrupt->ShareVector) &&
445 (Dispatch.Interrupt->Mode == Interrupt->Mode))
446 {
447 /* The vector is shared and the interrupts are compatible */
448 Interrupt->Connected = Connected = TRUE;
449
450 /* FIXME */
451 // ASSERT(Irql <= SYNCH_LEVEL);
452
453 /* Check if this is the first chain */
454 if (Dispatch.Type != ChainConnect)
455 {
456 /* This is not supported */
457 ASSERT(Dispatch.Interrupt->Mode != Latched);
458
459 /* Setup the chainned handler */
460 KiConnectVectorToInterrupt(Dispatch.Interrupt, ChainConnect);
461 }
462
463 /* Insert into the interrupt list */
464 InsertTailList(&Dispatch.Interrupt->InterruptListEntry,
465 &Interrupt->InterruptListEntry);
466 }
467 }
468
469 /* Unlock the dispatcher and revert affinity */
470 KiReleaseDispatcherLock(OldIrql);
471 KeRevertToUserAffinityThread();
472
473 /* Check if we failed while trying to connect */
474 if ((Connected) && (Error))
475 {
476 DPRINT1("HalEnableSystemInterrupt failed\n");
477 KeDisconnectInterrupt(Interrupt);
478 Connected = FALSE;
479 }
480
481 /* Return to caller */
482 return Connected;
483 }
484
485 /*
486 * @implemented
487 */
488 BOOLEAN
489 NTAPI
490 KeDisconnectInterrupt(IN PKINTERRUPT Interrupt)
491 {
492 KIRQL OldIrql, Irql;
493 ULONG Vector;
494 DISPATCH_INFO Dispatch;
495 PKINTERRUPT NextInterrupt;
496 BOOLEAN State;
497
498 /* Set the affinity */
499 KeSetSystemAffinityThread(1 << Interrupt->Number);
500
501 /* Lock the dispatcher */
502 OldIrql = KiAcquireDispatcherLock();
503
504 /* Check if it's actually connected */
505 State = Interrupt->Connected;
506 if (State)
507 {
508 /* Get the vector and IRQL */
509 Irql = Interrupt->Irql;
510 Vector = Interrupt->Vector;
511
512 /* Get vector dispatch data */
513 KiGetVectorDispatch(Vector, &Dispatch);
514
515 /* Check if it was chained */
516 if (Dispatch.Type == ChainConnect)
517 {
518 /* Check if the top-level interrupt is being removed */
519 ASSERT(Irql <= SYNCH_LEVEL);
520 if (Interrupt == Dispatch.Interrupt)
521 {
522 /* Get the next one */
523 Dispatch.Interrupt = CONTAINING_RECORD(Dispatch.Interrupt->
524 InterruptListEntry.Flink,
525 KINTERRUPT,
526 InterruptListEntry);
527
528 /* Reconnect it */
529 KiConnectVectorToInterrupt(Dispatch.Interrupt, ChainConnect);
530 }
531
532 /* Remove it */
533 RemoveEntryList(&Interrupt->InterruptListEntry);
534
535 /* Get the next one */
536 NextInterrupt = CONTAINING_RECORD(Dispatch.Interrupt->
537 InterruptListEntry.Flink,
538 KINTERRUPT,
539 InterruptListEntry);
540
541 /* Check if this is the only one left */
542 if (Dispatch.Interrupt == NextInterrupt)
543 {
544 /* Connect it in non-chained mode */
545 KiConnectVectorToInterrupt(Dispatch.Interrupt, NormalConnect);
546 }
547 }
548 else
549 {
550 /* Only one left, disable and remove it */
551 HalDisableSystemInterrupt(Interrupt->Vector, Irql);
552 KiConnectVectorToInterrupt(Interrupt, NoConnect);
553 }
554
555 /* Disconnect it */
556 Interrupt->Connected = FALSE;
557 }
558
559 /* Unlock the dispatcher and revert affinity */
560 KiReleaseDispatcherLock(OldIrql);
561 KeRevertToUserAffinityThread();
562
563 /* Return to caller */
564 return State;
565 }
566
567 /*
568 * @implemented
569 */
570 BOOLEAN
571 NTAPI
572 KeSynchronizeExecution(IN OUT PKINTERRUPT Interrupt,
573 IN PKSYNCHRONIZE_ROUTINE SynchronizeRoutine,
574 IN PVOID SynchronizeContext OPTIONAL)
575 {
576 BOOLEAN Success;
577 KIRQL OldIrql;
578
579 /* Raise IRQL */
580 OldIrql = KfRaiseIrql(Interrupt->SynchronizeIrql);
581
582 /* Acquire interrupt spinlock */
583 KeAcquireSpinLockAtDpcLevel(Interrupt->ActualLock);
584
585 /* Call the routine */
586 Success = SynchronizeRoutine(SynchronizeContext);
587
588 /* Release lock */
589 KeReleaseSpinLockFromDpcLevel(Interrupt->ActualLock);
590
591 /* Lower IRQL */
592 KfLowerIrql(OldIrql);
593
594 /* Return status */
595 return Success;
596 }
597
598 /* EOF */