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 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 #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 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 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