[TCPIP]
[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->ErrCode, &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 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;
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 OldIrql = 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 KfLowerIrql(OldIrql);
271 }
272
273 /* Check if the interrupt got handled and it's level */
274 if ((Handled) && (Interrupt->Mode == LevelSensitive)) break;
275
276 /* What's next? */
277 NextEntry = NextEntry->Flink;
278
279 /* Is this the last one? */
280 if (NextEntry == ListHead)
281 {
282 /* Level should not have gotten here */
283 if (Interrupt->Mode == LevelSensitive) break;
284
285 /* As for edge, we can only exit once nobody can handle the interrupt */
286 if (!Handled) break;
287 }
288
289 /* Get the interrupt object for the next pass */
290 Interrupt = CONTAINING_RECORD(NextEntry, KINTERRUPT, InterruptListEntry);
291 }
292
293 /* Now call the epilogue code */
294 KiExitInterrupt(TrapFrame, OldIrql, FALSE);
295 }
296 else
297 {
298 /* Now call the epilogue code */
299 KiExitInterrupt(TrapFrame, OldIrql, TRUE);
300 }
301 }
302
303 VOID
304 FASTCALL
305 KiInterruptTemplateHandler(IN PKTRAP_FRAME TrapFrame,
306 IN PKINTERRUPT Interrupt)
307 {
308 /* Enter interrupt frame */
309 KiEnterInterruptTrap(TrapFrame);
310
311 /* Call the correct dispatcher */
312 ((PKI_INTERRUPT_DISPATCH)Interrupt->DispatchAddress)(TrapFrame, Interrupt);
313 }
314
315
316 /* PUBLIC FUNCTIONS **********************************************************/
317
318 /*
319 * @implemented
320 */
321 VOID
322 NTAPI
323 KeInitializeInterrupt(IN PKINTERRUPT Interrupt,
324 IN PKSERVICE_ROUTINE ServiceRoutine,
325 IN PVOID ServiceContext,
326 IN PKSPIN_LOCK SpinLock,
327 IN ULONG Vector,
328 IN KIRQL Irql,
329 IN KIRQL SynchronizeIrql,
330 IN KINTERRUPT_MODE InterruptMode,
331 IN BOOLEAN ShareVector,
332 IN CHAR ProcessorNumber,
333 IN BOOLEAN FloatingSave)
334 {
335 ULONG i;
336 PULONG DispatchCode = &Interrupt->DispatchCode[0], Patch = DispatchCode;
337
338 /* Set the Interrupt Header */
339 Interrupt->Type = InterruptObject;
340 Interrupt->Size = sizeof(KINTERRUPT);
341
342 /* Check if we got a spinlock */
343 if (SpinLock)
344 {
345 Interrupt->ActualLock = SpinLock;
346 }
347 else
348 {
349 /* This means we'll be usin the built-in one */
350 KeInitializeSpinLock(&Interrupt->SpinLock);
351 Interrupt->ActualLock = &Interrupt->SpinLock;
352 }
353
354 /* Set the other settings */
355 Interrupt->ServiceRoutine = ServiceRoutine;
356 Interrupt->ServiceContext = ServiceContext;
357 Interrupt->Vector = Vector;
358 Interrupt->Irql = Irql;
359 Interrupt->SynchronizeIrql = SynchronizeIrql;
360 Interrupt->Mode = InterruptMode;
361 Interrupt->ShareVector = ShareVector;
362 Interrupt->Number = ProcessorNumber;
363 Interrupt->FloatingSave = FloatingSave;
364 Interrupt->TickCount = MAXULONG;
365 Interrupt->DispatchCount = MAXULONG;
366
367 /* Loop the template in memory */
368 for (i = 0; i < DISPATCH_LENGTH; i++)
369 {
370 /* Copy the dispatch code */
371 *DispatchCode++ = ((PULONG)KiInterruptTemplate)[i];
372 }
373
374 /* Jump to the last 4 bytes */
375 Patch = (PULONG)((ULONG_PTR)Patch +
376 ((ULONG_PTR)&KiInterruptTemplateObject -
377 (ULONG_PTR)KiInterruptTemplate) - 4);
378
379 /* Apply the patch */
380 *Patch = PtrToUlong(Interrupt);
381
382 /* Disconnect it at first */
383 Interrupt->Connected = FALSE;
384 }
385
386 /*
387 * @implemented
388 */
389 BOOLEAN
390 NTAPI
391 KeConnectInterrupt(IN PKINTERRUPT Interrupt)
392 {
393 BOOLEAN Connected, Error, Status;
394 KIRQL Irql, OldIrql;
395 UCHAR Number;
396 ULONG Vector;
397 DISPATCH_INFO Dispatch;
398
399 /* Get data from interrupt */
400 Number = Interrupt->Number;
401 Vector = Interrupt->Vector;
402 Irql = Interrupt->Irql;
403
404 /* Validate the settings */
405 if ((Irql > HIGH_LEVEL) ||
406 (Number >= KeNumberProcessors) ||
407 (Interrupt->SynchronizeIrql < Irql) ||
408 (Interrupt->FloatingSave))
409 {
410 return FALSE;
411 }
412
413 /* Set defaults */
414 Connected = FALSE;
415 Error = FALSE;
416
417 /* Set the system affinity and acquire the dispatcher lock */
418 KeSetSystemAffinityThread(1 << Number);
419 OldIrql = KiAcquireDispatcherLock();
420
421 /* Check if it's already been connected */
422 if (!Interrupt->Connected)
423 {
424 /* Get vector dispatching information */
425 KiGetVectorDispatch(Vector, &Dispatch);
426
427 /* Check if the vector is already connected */
428 if (Dispatch.Type == NoConnect)
429 {
430 /* Do the connection */
431 Interrupt->Connected = Connected = TRUE;
432
433 /* Initialize the list */
434 InitializeListHead(&Interrupt->InterruptListEntry);
435
436 /* Connect and enable the interrupt */
437 KiConnectVectorToInterrupt(Interrupt, NormalConnect);
438 Status = HalEnableSystemInterrupt(Vector, Irql, Interrupt->Mode);
439 if (!Status) Error = TRUE;
440 }
441 else if ((Dispatch.Type != UnknownConnect) &&
442 (Interrupt->ShareVector) &&
443 (Dispatch.Interrupt->ShareVector) &&
444 (Dispatch.Interrupt->Mode == Interrupt->Mode))
445 {
446 /* The vector is shared and the interrupts are compatible */
447 Interrupt->Connected = Connected = TRUE;
448
449 /* FIXME */
450 // ASSERT(Irql <= SYNCH_LEVEL);
451
452 /* Check if this is the first chain */
453 if (Dispatch.Type != ChainConnect)
454 {
455 /* This is not supported */
456 ASSERT(Dispatch.Interrupt->Mode != Latched);
457
458 /* Setup the chainned handler */
459 KiConnectVectorToInterrupt(Dispatch.Interrupt, ChainConnect);
460 }
461
462 /* Insert into the interrupt list */
463 InsertTailList(&Dispatch.Interrupt->InterruptListEntry,
464 &Interrupt->InterruptListEntry);
465 }
466 }
467
468 /* Unlock the dispatcher and revert affinity */
469 KiReleaseDispatcherLock(OldIrql);
470 KeRevertToUserAffinityThread();
471
472 /* Check if we failed while trying to connect */
473 if ((Connected) && (Error))
474 {
475 DPRINT1("HalEnableSystemInterrupt failed\n");
476 KeDisconnectInterrupt(Interrupt);
477 Connected = FALSE;
478 }
479
480 /* Return to caller */
481 return Connected;
482 }
483
484 /*
485 * @implemented
486 */
487 BOOLEAN
488 NTAPI
489 KeDisconnectInterrupt(IN PKINTERRUPT Interrupt)
490 {
491 KIRQL OldIrql, Irql;
492 ULONG Vector;
493 DISPATCH_INFO Dispatch;
494 PKINTERRUPT NextInterrupt;
495 BOOLEAN State;
496
497 /* Set the affinity */
498 KeSetSystemAffinityThread(1 << Interrupt->Number);
499
500 /* Lock the dispatcher */
501 OldIrql = KiAcquireDispatcherLock();
502
503 /* Check if it's actually connected */
504 State = Interrupt->Connected;
505 if (State)
506 {
507 /* Get the vector and IRQL */
508 Irql = Interrupt->Irql;
509 Vector = Interrupt->Vector;
510
511 /* Get vector dispatch data */
512 KiGetVectorDispatch(Vector, &Dispatch);
513
514 /* Check if it was chained */
515 if (Dispatch.Type == ChainConnect)
516 {
517 /* Check if the top-level interrupt is being removed */
518 ASSERT(Irql <= SYNCH_LEVEL);
519 if (Interrupt == Dispatch.Interrupt)
520 {
521 /* Get the next one */
522 Dispatch.Interrupt = CONTAINING_RECORD(Dispatch.Interrupt->
523 InterruptListEntry.Flink,
524 KINTERRUPT,
525 InterruptListEntry);
526
527 /* Reconnect it */
528 KiConnectVectorToInterrupt(Dispatch.Interrupt, ChainConnect);
529 }
530
531 /* Remove it */
532 RemoveEntryList(&Interrupt->InterruptListEntry);
533
534 /* Get the next one */
535 NextInterrupt = CONTAINING_RECORD(Dispatch.Interrupt->
536 InterruptListEntry.Flink,
537 KINTERRUPT,
538 InterruptListEntry);
539
540 /* Check if this is the only one left */
541 if (Dispatch.Interrupt == NextInterrupt)
542 {
543 /* Connect it in non-chained mode */
544 KiConnectVectorToInterrupt(Dispatch.Interrupt, NormalConnect);
545 }
546 }
547 else
548 {
549 /* Only one left, disable and remove it */
550 HalDisableSystemInterrupt(Interrupt->Vector, Irql);
551 KiConnectVectorToInterrupt(Interrupt, NoConnect);
552 }
553
554 /* Disconnect it */
555 Interrupt->Connected = FALSE;
556 }
557
558 /* Unlock the dispatcher and revert affinity */
559 KiReleaseDispatcherLock(OldIrql);
560 KeRevertToUserAffinityThread();
561
562 /* Return to caller */
563 return State;
564 }
565
566 /*
567 * @implemented
568 */
569 BOOLEAN
570 NTAPI
571 KeSynchronizeExecution(IN OUT PKINTERRUPT Interrupt,
572 IN PKSYNCHRONIZE_ROUTINE SynchronizeRoutine,
573 IN PVOID SynchronizeContext OPTIONAL)
574 {
575 NTSTATUS Status;
576 KIRQL OldIrql;
577
578 /* Raise IRQL */
579 OldIrql = KfRaiseIrql(Interrupt->SynchronizeIrql);
580
581 /* Acquire interrupt spinlock */
582 KeAcquireSpinLockAtDpcLevel(Interrupt->ActualLock);
583
584 /* Call the routine */
585 Status = SynchronizeRoutine(SynchronizeContext);
586
587 /* Release lock */
588 KeReleaseSpinLockFromDpcLevel(Interrupt->ActualLock);
589
590 /* Lower IRQL */
591 KfLowerIrql(OldIrql);
592
593 /* Return status */
594 return Status;
595 }
596
597 /* EOF */