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