1 /* 2 * PROJECT: ReactOS Kernel 3 * LICENSE: GPL - See COPYING in the top level directory 4 * FILE: ntoskrnl/ke/apc.c 5 * PURPOSE: Implements the Asynchronous Procedure Call mechanism 6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) 7 */ 8 9 /* INCLUDES *****************************************************************/ 10 11 #include <ntoskrnl.h> 12 #define NDEBUG 13 #include <debug.h> 14 15 /* PRIVATE FUNCTIONS *********************************************************/ 16 17 /*++ 18 * @name KiCheckForKernelApcDelivery 19 * @implemented NT 5.2 20 * 21 * The KiCheckForKernelApcDelivery routine is called whenever APCs have 22 * just been re-enabled in Kernel Mode, such as after leaving a Critical or 23 * Guarded Region. It delivers APCs if the environment is right. 24 * 25 * @param None. 26 * 27 * @return None. 28 * 29 * @remarks This routine allows KeLeave/EnterCritical/GuardedRegion to be used 30 * as macros from inside WIN32K or other Drivers, which will then only 31 * have to do an Import API call in the case where APCs are enabled again. 32 * 33 *--*/ 34 VOID 35 NTAPI 36 KiCheckForKernelApcDelivery(VOID) 37 { 38 KIRQL OldIrql; 39 40 /* We should only deliver at passive */ 41 if (KeGetCurrentIrql() == PASSIVE_LEVEL) 42 { 43 /* Raise to APC and Deliver APCs, then lower back to Passive */ 44 KeRaiseIrql(APC_LEVEL, &OldIrql); 45 KiDeliverApc(KernelMode, 0, 0); 46 KeLowerIrql(PASSIVE_LEVEL); 47 } 48 else 49 { 50 /* 51 * If we're not at passive level it means someone raised IRQL 52 * to APC level before the critical or guarded section was entered 53 * (e.g) by a fast mutex). This implies that the APCs shouldn't 54 * be delivered now, but after the IRQL is lowered to passive 55 * level again. 56 */ 57 KeGetCurrentThread()->ApcState.KernelApcPending = TRUE; 58 HalRequestSoftwareInterrupt(APC_LEVEL); 59 } 60 } 61 62 /*++ 63 * @name KiInsertQueueApc 64 * 65 * The KiInsertQueueApc routine queues a APC for execution when the right 66 * scheduler environment exists. 67 * 68 * @param Apc 69 * Pointer to an initialized control object of type APC for which the 70 * caller provides the storage. 71 * 72 * @param PriorityBoost 73 * Priority Boost to apply to the Thread. 74 * 75 * @return None 76 * 77 * @remarks The APC will execute at APC_LEVEL for the KernelRoutine registered, 78 * and at PASSIVE_LEVEL for the NormalRoutine registered. 79 * 80 * Callers of this routine must have locked the dipatcher database. 81 * 82 *--*/ 83 VOID 84 FASTCALL 85 KiInsertQueueApc(IN PKAPC Apc, 86 IN KPRIORITY PriorityBoost) 87 { 88 PKTHREAD Thread = Apc->Thread; 89 PKAPC_STATE ApcState; 90 KPROCESSOR_MODE ApcMode; 91 PLIST_ENTRY ListHead, NextEntry; 92 PKAPC QueuedApc; 93 PKGATE Gate; 94 NTSTATUS Status; 95 BOOLEAN RequestInterrupt = FALSE; 96 97 /* 98 * Check if the caller wanted this APC to use the thread's environment at 99 * insertion time. 100 */ 101 if (Apc->ApcStateIndex == InsertApcEnvironment) 102 { 103 /* Copy it over */ 104 Apc->ApcStateIndex = Thread->ApcStateIndex; 105 } 106 107 /* Get the APC State for this Index, and the mode too */ 108 ApcState = Thread->ApcStatePointer[(UCHAR)Apc->ApcStateIndex]; 109 ApcMode = Apc->ApcMode; 110 111 /* The APC must be "inserted" already */ 112 ASSERT(Apc->Inserted == TRUE); 113 114 /* Three scenarios: 115 * 1) Kernel APC with Normal Routine or User APC = Put it at the end of the List 116 * 2) User APC which is PsExitSpecialApc = Put it at the front of the List 117 * 3) Kernel APC without Normal Routine = Put it at the end of the No-Normal Routine Kernel APC list 118 */ 119 if (Apc->NormalRoutine) 120 { 121 /* Normal APC; is it the Thread Termination APC? */ 122 if ((ApcMode != KernelMode) && 123 (Apc->KernelRoutine == PsExitSpecialApc)) 124 { 125 /* Set User APC pending to true */ 126 Thread->ApcState.UserApcPending = TRUE; 127 128 /* Insert it at the top of the list */ 129 InsertHeadList(&ApcState->ApcListHead[ApcMode], 130 &Apc->ApcListEntry); 131 } 132 else 133 { 134 /* Regular user or kernel Normal APC */ 135 InsertTailList(&ApcState->ApcListHead[ApcMode], 136 &Apc->ApcListEntry); 137 } 138 } 139 else 140 { 141 /* Special APC, find the last one in the list */ 142 ListHead = &ApcState->ApcListHead[ApcMode]; 143 NextEntry = ListHead->Blink; 144 while (NextEntry != ListHead) 145 { 146 /* Get the APC */ 147 QueuedApc = CONTAINING_RECORD(NextEntry, KAPC, ApcListEntry); 148 149 /* Is this a No-Normal APC? If so, break */ 150 if (!QueuedApc->NormalRoutine) break; 151 152 /* Move to the previous APC in the Queue */ 153 NextEntry = NextEntry->Blink; 154 } 155 156 /* Insert us here */ 157 InsertHeadList(NextEntry, &Apc->ApcListEntry); 158 } 159 160 /* Now check if the Apc State Indexes match */ 161 if (Thread->ApcStateIndex == Apc->ApcStateIndex) 162 { 163 /* Check that the thread matches */ 164 if (Thread == KeGetCurrentThread()) 165 { 166 /* Sanity check */ 167 ASSERT(Thread->State == Running); 168 169 /* Check if this is kernel mode */ 170 if (ApcMode == KernelMode) 171 { 172 /* All valid, a Kernel APC is pending now */ 173 Thread->ApcState.KernelApcPending = TRUE; 174 175 /* Check if Special APCs are disabled */ 176 if (!Thread->SpecialApcDisable) 177 { 178 /* They're not, so request the interrupt */ 179 HalRequestSoftwareInterrupt(APC_LEVEL); 180 } 181 } 182 } 183 else 184 { 185 /* Acquire the dispatcher lock */ 186 KiAcquireDispatcherLock(); 187 188 /* Check if this is a kernel-mode APC */ 189 if (ApcMode == KernelMode) 190 { 191 /* Kernel-mode APC, set us pending */ 192 Thread->ApcState.KernelApcPending = TRUE; 193 194 /* Are we currently running? */ 195 if (Thread->State == Running) 196 { 197 /* The thread is running, so remember to send a request */ 198 RequestInterrupt = TRUE; 199 } 200 else if ((Thread->State == Waiting) && 201 (Thread->WaitIrql == PASSIVE_LEVEL) && 202 !(Thread->SpecialApcDisable) && 203 (!(Apc->NormalRoutine) || 204 (!(Thread->KernelApcDisable) && 205 !(Thread->ApcState.KernelApcInProgress)))) 206 { 207 /* We'll unwait with this status */ 208 Status = STATUS_KERNEL_APC; 209 210 /* Wake up the thread */ 211 KiUnwaitThread(Thread, Status, PriorityBoost); 212 } 213 else if (Thread->State == GateWait) 214 { 215 /* Lock the thread */ 216 KiAcquireThreadLock(Thread); 217 218 /* Essentially do the same check as above */ 219 if ((Thread->State == GateWait) && 220 (Thread->WaitIrql == PASSIVE_LEVEL) && 221 !(Thread->SpecialApcDisable) && 222 (!(Apc->NormalRoutine) || 223 (!(Thread->KernelApcDisable) && 224 !(Thread->ApcState.KernelApcInProgress)))) 225 { 226 /* We were in a gate wait. Handle this. */ 227 DPRINT1("A thread was in a gate wait\n"); 228 229 /* Get the gate */ 230 Gate = Thread->GateObject; 231 232 /* Lock the gate */ 233 KiAcquireDispatcherObject(&Gate->Header); 234 235 /* Remove it from the waiters list */ 236 RemoveEntryList(&Thread->WaitBlock[0].WaitListEntry); 237 238 /* Unlock the gate */ 239 KiReleaseDispatcherObject(&Gate->Header); 240 241 /* Increase the queue counter if needed */ 242 if (Thread->Queue) Thread->Queue->CurrentCount++; 243 244 /* Put into deferred ready list with this status */ 245 Thread->WaitStatus = STATUS_KERNEL_APC; 246 KiInsertDeferredReadyList(Thread); 247 } 248 249 /* Release the thread lock */ 250 KiReleaseThreadLock(Thread); 251 } 252 } 253 else if ((Thread->State == Waiting) && 254 (Thread->WaitMode == UserMode) && 255 ((Thread->Alertable) || 256 (Thread->ApcState.UserApcPending))) 257 { 258 /* Set user-mode APC pending */ 259 Thread->ApcState.UserApcPending = TRUE; 260 Status = STATUS_USER_APC; 261 262 /* Wake up the thread */ 263 KiUnwaitThread(Thread, Status, PriorityBoost); 264 } 265 266 /* Release dispatcher lock */ 267 KiReleaseDispatcherLockFromSynchLevel(); 268 269 /* Check if an interrupt was requested */ 270 KiRequestApcInterrupt(RequestInterrupt, Thread->NextProcessor); 271 } 272 } 273 } 274 275 /*++ 276 * @name KiDeliverApc 277 * @implemented @NT4 278 * 279 * The KiDeliverApc routine is called from IRQL switching code if the 280 * thread is returning from an IRQL >= APC_LEVEL and Kernel-Mode APCs are 281 * pending. 282 * 283 * @param DeliveryMode 284 * Specifies the current processor mode. 285 * 286 * @param ExceptionFrame 287 * Pointer to the Exception Frame on non-i386 builds. 288 * 289 * @param TrapFrame 290 * Pointer to the Trap Frame. 291 * 292 * @return None. 293 * 294 * @remarks First, Special APCs are delivered, followed by Kernel-Mode APCs and 295 * User-Mode APCs. Note that the TrapFrame is only valid if the 296 * delivery mode is User-Mode. 297 * Upon entry, this routine executes at APC_LEVEL. 298 * 299 *--*/ 300 VOID 301 NTAPI 302 KiDeliverApc(IN KPROCESSOR_MODE DeliveryMode, 303 IN PKEXCEPTION_FRAME ExceptionFrame, 304 IN PKTRAP_FRAME TrapFrame) 305 { 306 PKTHREAD Thread = KeGetCurrentThread(); 307 PKPROCESS Process = Thread->ApcState.Process; 308 PKTRAP_FRAME OldTrapFrame; 309 PLIST_ENTRY ApcListEntry; 310 PKAPC Apc; 311 KLOCK_QUEUE_HANDLE ApcLock; 312 PKKERNEL_ROUTINE KernelRoutine; 313 PVOID NormalContext; 314 PKNORMAL_ROUTINE NormalRoutine; 315 PVOID SystemArgument1; 316 PVOID SystemArgument2; 317 ASSERT_IRQL_EQUAL(APC_LEVEL); 318 319 /* Save the old trap frame and set current one */ 320 OldTrapFrame = Thread->TrapFrame; 321 Thread->TrapFrame = TrapFrame; 322 323 /* Clear Kernel APC Pending */ 324 Thread->ApcState.KernelApcPending = FALSE; 325 326 /* Check if Special APCs are disabled */ 327 if (Thread->SpecialApcDisable) goto Quickie; 328 329 /* Do the Kernel APCs first */ 330 while (!IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode])) 331 { 332 /* Lock the APC Queue */ 333 KiAcquireApcLockRaiseToDpc(Thread, &ApcLock); 334 335 /* Check if the list became empty now */ 336 if (IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode])) 337 { 338 /* It is, release the lock and break out */ 339 KiReleaseApcLock(&ApcLock); 340 break; 341 } 342 343 /* Kernel APC is not pending anymore */ 344 Thread->ApcState.KernelApcPending = FALSE; 345 346 /* Get the next Entry */ 347 ApcListEntry = Thread->ApcState.ApcListHead[KernelMode].Flink; 348 Apc = CONTAINING_RECORD(ApcListEntry, KAPC, ApcListEntry); 349 350 /* Save Parameters so that it's safe to free the Object in the Kernel Routine*/ 351 NormalRoutine = Apc->NormalRoutine; 352 KernelRoutine = Apc->KernelRoutine; 353 NormalContext = Apc->NormalContext; 354 SystemArgument1 = Apc->SystemArgument1; 355 SystemArgument2 = Apc->SystemArgument2; 356 357 /* Special APC */ 358 if (!NormalRoutine) 359 { 360 /* Remove the APC from the list */ 361 RemoveEntryList(ApcListEntry); 362 Apc->Inserted = FALSE; 363 364 /* Release the APC lock */ 365 KiReleaseApcLock(&ApcLock); 366 367 /* Call the Special APC */ 368 KernelRoutine(Apc, 369 &NormalRoutine, 370 &NormalContext, 371 &SystemArgument1, 372 &SystemArgument2); 373 374 /* Make sure it returned correctly */ 375 if (KeGetCurrentIrql() != ApcLock.OldIrql) 376 { 377 KeBugCheckEx(IRQL_UNEXPECTED_VALUE, 378 (KeGetCurrentIrql() << 16) | 379 (ApcLock.OldIrql << 8), 380 (ULONG_PTR)KernelRoutine, 381 (ULONG_PTR)Apc, 382 (ULONG_PTR)NormalRoutine); 383 } 384 } 385 else 386 { 387 /* Normal Kernel APC, make sure it's safe to deliver */ 388 if ((Thread->ApcState.KernelApcInProgress) || 389 (Thread->KernelApcDisable)) 390 { 391 /* Release lock and return */ 392 KiReleaseApcLock(&ApcLock); 393 goto Quickie; 394 } 395 396 /* Dequeue the APC */ 397 RemoveEntryList(ApcListEntry); 398 Apc->Inserted = FALSE; 399 400 /* Go back to APC_LEVEL */ 401 KiReleaseApcLock(&ApcLock); 402 403 /* Call the Kernel APC */ 404 KernelRoutine(Apc, 405 &NormalRoutine, 406 &NormalContext, 407 &SystemArgument1, 408 &SystemArgument2); 409 410 /* Make sure it returned correctly */ 411 if (KeGetCurrentIrql() != ApcLock.OldIrql) 412 { 413 KeBugCheckEx(IRQL_UNEXPECTED_VALUE, 414 (KeGetCurrentIrql() << 16) | 415 (ApcLock.OldIrql << 8), 416 (ULONG_PTR)KernelRoutine, 417 (ULONG_PTR)Apc, 418 (ULONG_PTR)NormalRoutine); 419 } 420 421 /* Check if there still is a Normal Routine */ 422 if (NormalRoutine) 423 { 424 /* At Passive Level, an APC can be prempted by a Special APC */ 425 Thread->ApcState.KernelApcInProgress = TRUE; 426 KeLowerIrql(PASSIVE_LEVEL); 427 428 /* Call and Raise IRQL back to APC_LEVEL */ 429 NormalRoutine(NormalContext, SystemArgument1, SystemArgument2); 430 KeRaiseIrql(APC_LEVEL, &ApcLock.OldIrql); 431 } 432 433 /* Set Kernel APC in progress to false and loop again */ 434 Thread->ApcState.KernelApcInProgress = FALSE; 435 } 436 } 437 438 /* Now we do the User APCs */ 439 if ((DeliveryMode == UserMode) && 440 !(IsListEmpty(&Thread->ApcState.ApcListHead[UserMode])) && 441 (Thread->ApcState.UserApcPending)) 442 { 443 /* Lock the APC Queue */ 444 KiAcquireApcLockRaiseToDpc(Thread, &ApcLock); 445 446 /* It's not pending anymore */ 447 Thread->ApcState.UserApcPending = FALSE; 448 449 /* Check if the list became empty now */ 450 if (IsListEmpty(&Thread->ApcState.ApcListHead[UserMode])) 451 { 452 /* It is, release the lock and break out */ 453 KiReleaseApcLock(&ApcLock); 454 goto Quickie; 455 } 456 457 /* Get the actual APC object */ 458 ApcListEntry = Thread->ApcState.ApcListHead[UserMode].Flink; 459 Apc = CONTAINING_RECORD(ApcListEntry, KAPC, ApcListEntry); 460 461 /* Save Parameters so that it's safe to free the Object in the Kernel Routine*/ 462 NormalRoutine = Apc->NormalRoutine; 463 KernelRoutine = Apc->KernelRoutine; 464 NormalContext = Apc->NormalContext; 465 SystemArgument1 = Apc->SystemArgument1; 466 SystemArgument2 = Apc->SystemArgument2; 467 468 /* Remove the APC from Queue, and release the lock */ 469 RemoveEntryList(ApcListEntry); 470 Apc->Inserted = FALSE; 471 KiReleaseApcLock(&ApcLock); 472 473 /* Call the kernel routine */ 474 KernelRoutine(Apc, 475 &NormalRoutine, 476 &NormalContext, 477 &SystemArgument1, 478 &SystemArgument2); 479 480 /* Check if there's no normal routine */ 481 if (!NormalRoutine) 482 { 483 /* Check if more User APCs are Pending */ 484 KeTestAlertThread(UserMode); 485 } 486 else 487 { 488 /* Set up the Trap Frame and prepare for Execution in NTDLL.DLL */ 489 KiInitializeUserApc(ExceptionFrame, 490 TrapFrame, 491 NormalRoutine, 492 NormalContext, 493 SystemArgument1, 494 SystemArgument2); 495 } 496 } 497 498 Quickie: 499 /* Make sure we're still in the same process */ 500 if (Process != Thread->ApcState.Process) 501 { 502 /* Erm, we got attached or something! BAD! */ 503 KeBugCheckEx(INVALID_PROCESS_ATTACH_ATTEMPT, 504 (ULONG_PTR)Process, 505 (ULONG_PTR)Thread->ApcState.Process, 506 Thread->ApcStateIndex, 507 KeGetCurrentPrcb()->DpcRoutineActive); 508 } 509 510 /* Restore the trap frame */ 511 Thread->TrapFrame = OldTrapFrame; 512 } 513 514 FORCEINLINE 515 VOID 516 RepairList(IN PLIST_ENTRY Original, 517 IN PLIST_ENTRY Copy, 518 IN KPROCESSOR_MODE Mode) 519 { 520 /* Check if the list for this mode is empty */ 521 if (IsListEmpty(&Original[Mode])) 522 { 523 /* It is, all we need to do is initialize it */ 524 InitializeListHead(&Copy[Mode]); 525 } 526 else 527 { 528 /* Copy the lists */ 529 Copy[Mode].Flink = Original[Mode].Flink; 530 Copy[Mode].Blink = Original[Mode].Blink; 531 Original[Mode].Flink->Blink = &Copy[Mode]; 532 Original[Mode].Blink->Flink = &Copy[Mode]; 533 } 534 } 535 536 VOID 537 NTAPI 538 KiMoveApcState(PKAPC_STATE OldState, 539 PKAPC_STATE NewState) 540 { 541 /* Restore backup of Original Environment */ 542 RtlCopyMemory(NewState, OldState, KAPC_STATE_ACTUAL_LENGTH); 543 544 /* Repair Lists */ 545 RepairList(OldState->ApcListHead, NewState->ApcListHead, KernelMode); 546 RepairList(OldState->ApcListHead, NewState->ApcListHead, UserMode); 547 } 548 549 /* PUBLIC FUNCTIONS **********************************************************/ 550 551 /*++ 552 * @name KeEnterCriticalRegion 553 * @implemented NT4 554 * 555 * The KeEnterCriticalRegion routine temporarily disables the delivery of 556 * normal kernel APCs; special kernel-mode APCs are still delivered. 557 * 558 * @param None. 559 * 560 * @return None. 561 * 562 * @remarks Highest-level drivers can call this routine while running in the 563 * context of the thread that requested the current I/O operation. 564 * Any caller of this routine should call KeLeaveCriticalRegion as 565 * quickly as possible. 566 * 567 * Callers of KeEnterCriticalRegion must be running at IRQL <= 568 * APC_LEVEL. 569 * 570 *--*/ 571 VOID 572 NTAPI 573 _KeEnterCriticalRegion(VOID) 574 { 575 /* Use inlined function */ 576 KeEnterCriticalRegion(); 577 } 578 579 /*++ 580 * KeLeaveCriticalRegion 581 * @implemented NT4 582 * 583 * The KeLeaveCriticalRegion routine reenables the delivery of normal 584 * kernel-mode APCs that were disabled by a call to KeEnterCriticalRegion. 585 * 586 * @param None. 587 * 588 * @return None. 589 * 590 * @remarks Highest-level drivers can call this routine while running in the 591 * context of the thread that requested the current I/O operation. 592 * 593 * Callers of KeLeaveCriticalRegion must be running at IRQL <= 594 * DISPATCH_LEVEL. 595 * 596 *--*/ 597 VOID 598 NTAPI 599 _KeLeaveCriticalRegion(VOID) 600 { 601 /* Use inlined version */ 602 KeLeaveCriticalRegion(); 603 } 604 605 /*++ 606 * KeInitializeApc 607 * @implemented NT4 608 * 609 * The KeInitializeApc routine initializes an APC object, and registers 610 * the Kernel, Rundown and Normal routines for that object. 611 * 612 * @param Apc 613 * Pointer to a KAPC structure that represents the APC object to 614 * initialize. The caller must allocate storage for the structure 615 * from resident memory. 616 * 617 * @param Thread 618 * Thread to which to deliver the APC. 619 * 620 * @param TargetEnvironment 621 * APC Environment to be used. 622 * 623 * @param KernelRoutine 624 * Points to the KernelRoutine to associate with the APC. 625 * This routine is executed for all APCs. 626 * 627 * @param RundownRoutine 628 * Points to the RundownRoutine to associate with the APC. 629 * This routine is executed when the Thread exits during APC execution. 630 * 631 * @param NormalRoutine 632 * Points to the NormalRoutine to associate with the APC. 633 * This routine is executed at PASSIVE_LEVEL. If this is not specifed, 634 * the APC becomes a Special APC and the Mode and Context parameters are 635 * ignored. 636 * 637 * @param Mode 638 * Specifies the processor mode at which to run the Normal Routine. 639 * 640 * @param Context 641 * Specifices the value to pass as Context parameter to the registered 642 * routines. 643 * 644 * @return None. 645 * 646 * @remarks The caller can queue an initialized APC with KeInsertQueueApc. 647 * 648 *--*/ 649 VOID 650 NTAPI 651 KeInitializeApc(IN PKAPC Apc, 652 IN PKTHREAD Thread, 653 IN KAPC_ENVIRONMENT TargetEnvironment, 654 IN PKKERNEL_ROUTINE KernelRoutine, 655 IN PKRUNDOWN_ROUTINE RundownRoutine OPTIONAL, 656 IN PKNORMAL_ROUTINE NormalRoutine, 657 IN KPROCESSOR_MODE Mode, 658 IN PVOID Context) 659 { 660 /* Sanity check */ 661 ASSERT(TargetEnvironment <= InsertApcEnvironment); 662 663 /* Set up the basic APC Structure Data */ 664 Apc->Type = ApcObject; 665 Apc->Size = sizeof(KAPC); 666 667 /* Set the Environment */ 668 if (TargetEnvironment == CurrentApcEnvironment) 669 { 670 /* Use the current one for the thread */ 671 Apc->ApcStateIndex = Thread->ApcStateIndex; 672 } 673 else 674 { 675 /* Sanity check */ 676 ASSERT((TargetEnvironment <= Thread->ApcStateIndex) || 677 (TargetEnvironment == InsertApcEnvironment)); 678 679 /* Use the one that was given */ 680 Apc->ApcStateIndex = TargetEnvironment; 681 } 682 683 /* Set the Thread and Routines */ 684 Apc->Thread = Thread; 685 Apc->KernelRoutine = KernelRoutine; 686 Apc->RundownRoutine = RundownRoutine; 687 Apc->NormalRoutine = NormalRoutine; 688 689 /* Check if this is a special APC */ 690 if (NormalRoutine) 691 { 692 /* It's a normal one. Set the context and mode */ 693 Apc->ApcMode = Mode; 694 Apc->NormalContext = Context; 695 } 696 else 697 { 698 /* It's a special APC, which can only be kernel mode */ 699 Apc->ApcMode = KernelMode; 700 Apc->NormalContext = NULL; 701 } 702 703 /* The APC is not inserted */ 704 Apc->Inserted = FALSE; 705 } 706 707 /*++ 708 * @name KeInsertQueueApc 709 * @implemented NT4 710 * 711 * The KeInsertQueueApc routine queues a APC for execution when the right 712 * scheduler environment exists. 713 * 714 * @param Apc 715 * Pointer to an initialized control object of type APC for which the 716 * caller provides the storage. 717 * 718 * @param SystemArgument[1,2] 719 * Pointer to a set of two parameters that contain untyped data. 720 * 721 * @param PriorityBoost 722 * Priority Boost to apply to the Thread. 723 * 724 * @return If the APC is already inserted or APC queueing is disabled, FALSE. 725 * Otherwise, TRUE. 726 * 727 * @remarks The APC will execute at APC_LEVEL for the KernelRoutine registered, 728 * and at PASSIVE_LEVEL for the NormalRoutine registered. 729 * 730 * Callers of this routine must be running at IRQL <= DISPATCH_LEVEL. 731 * 732 *--*/ 733 BOOLEAN 734 NTAPI 735 KeInsertQueueApc(IN PKAPC Apc, 736 IN PVOID SystemArgument1, 737 IN PVOID SystemArgument2, 738 IN KPRIORITY PriorityBoost) 739 { 740 PKTHREAD Thread = Apc->Thread; 741 KLOCK_QUEUE_HANDLE ApcLock; 742 BOOLEAN State = TRUE; 743 ASSERT_APC(Apc); 744 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL); 745 746 /* Get the APC lock */ 747 KiAcquireApcLockRaiseToSynch(Thread, &ApcLock); 748 749 /* Make sure we can Queue APCs and that this one isn't already inserted */ 750 if (!(Thread->ApcQueueable) || (Apc->Inserted)) 751 { 752 /* Fail */ 753 State = FALSE; 754 } 755 else 756 { 757 /* Set the System Arguments and set it as inserted */ 758 Apc->SystemArgument1 = SystemArgument1; 759 Apc->SystemArgument2 = SystemArgument2; 760 Apc->Inserted = TRUE; 761 762 /* Call the Internal Function */ 763 KiInsertQueueApc(Apc, PriorityBoost); 764 } 765 766 /* Release the APC lock and return success */ 767 KiReleaseApcLockFromSynchLevel(&ApcLock); 768 KiExitDispatcher(ApcLock.OldIrql); 769 return State; 770 } 771 772 /*++ 773 * @name KeFlushQueueApc 774 * @implemented NT4 775 * 776 * The KeFlushQueueApc routine flushes all APCs of the given processor mode 777 * from the specified Thread's APC queue. 778 * 779 * @param Thread 780 * Pointer to the thread whose APC queue will be flushed. 781 * 782 * @param PreviousMode 783 * Specifies which APC Queue to flush. 784 * 785 * @return A pointer to the first entry in the flushed APC queue. 786 * 787 * @remarks If the routine returns NULL, it means that no APCs were flushed. 788 * Callers of this routine must be running at DISPATCH_LEVEL or lower. 789 * 790 *--*/ 791 PLIST_ENTRY 792 NTAPI 793 KeFlushQueueApc(IN PKTHREAD Thread, 794 IN KPROCESSOR_MODE PreviousMode) 795 { 796 PKAPC Apc; 797 PLIST_ENTRY FirstEntry, CurrentEntry; 798 KLOCK_QUEUE_HANDLE ApcLock; 799 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL); 800 801 /* Check if this was user mode */ 802 if (PreviousMode == UserMode) 803 { 804 /* Get the APC lock */ 805 KiAcquireApcLockRaiseToSynch(Thread, &ApcLock); 806 807 /* Select user list and check if it's empty */ 808 if (IsListEmpty(&Thread->ApcState.ApcListHead[UserMode])) 809 { 810 /* Don't return anything */ 811 FirstEntry = NULL; 812 goto FlushDone; 813 } 814 } 815 else 816 { 817 /* Select kernel list and check if it's empty */ 818 if (IsListEmpty( &Thread->ApcState.ApcListHead[KernelMode])) 819 { 820 /* Don't return anything */ 821 return NULL; 822 } 823 824 /* Otherwise, acquire the APC lock */ 825 KiAcquireApcLockRaiseToSynch(Thread, &ApcLock); 826 } 827 828 /* Get the first entry and check if the list is empty now */ 829 FirstEntry = Thread->ApcState.ApcListHead[PreviousMode].Flink; 830 if (FirstEntry == &Thread->ApcState.ApcListHead[PreviousMode]) 831 { 832 /* It is, clear the returned entry */ 833 FirstEntry = NULL; 834 } 835 else 836 { 837 /* It's not, remove the first entry */ 838 RemoveEntryList(&Thread->ApcState.ApcListHead[PreviousMode]); 839 840 /* Loop all the entries */ 841 CurrentEntry = FirstEntry; 842 do 843 { 844 /* Get the APC and make it un-inserted */ 845 Apc = CONTAINING_RECORD(CurrentEntry, KAPC, ApcListEntry); 846 Apc->Inserted = FALSE; 847 848 /* Get the next entry */ 849 CurrentEntry = CurrentEntry->Flink; 850 } while (CurrentEntry != FirstEntry); 851 852 /* Re-initialize the list */ 853 InitializeListHead(&Thread->ApcState.ApcListHead[PreviousMode]); 854 } 855 856 /* Release the lock */ 857 FlushDone: 858 KiReleaseApcLock(&ApcLock); 859 860 /* Return the first entry */ 861 return FirstEntry; 862 } 863 864 /*++ 865 * @name KeRemoveQueueApc 866 * @implemented NT4 867 * 868 * The KeRemoveQueueApc routine removes a given APC object from the system 869 * APC queue. 870 * 871 * @param Apc 872 * Pointer to an initialized APC object that was queued by calling 873 * KeInsertQueueApc. 874 * 875 * @return TRUE if the APC Object is in the APC Queue. Otherwise, no operation 876 * is performed and FALSE is returned. 877 * 878 * @remarks If the given APC Object is currently queued, it is removed from the 879 * queue and any calls to the registered routines are cancelled. 880 * 881 * Callers of this routine must be running at IRQL <= DISPATCH_LEVEL. 882 * 883 *--*/ 884 BOOLEAN 885 NTAPI 886 KeRemoveQueueApc(IN PKAPC Apc) 887 { 888 PKTHREAD Thread = Apc->Thread; 889 PKAPC_STATE ApcState; 890 BOOLEAN Inserted; 891 KLOCK_QUEUE_HANDLE ApcLock; 892 ASSERT_APC(Apc); 893 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL); 894 895 /* Get the APC lock (this raises IRQL to SYNCH_LEVEL) */ 896 KiAcquireApcLockRaiseToSynch(Thread, &ApcLock); 897 898 /* Check if it's inserted */ 899 Inserted = Apc->Inserted; 900 if (Inserted) 901 { 902 /* Set it as non-inserted and get the APC state */ 903 Apc->Inserted = FALSE; 904 ApcState = Thread->ApcStatePointer[(UCHAR)Apc->ApcStateIndex]; 905 906 /* Acquire the dispatcher lock and remove it from the list */ 907 KiAcquireDispatcherLockAtSynchLevel(); 908 if (RemoveEntryList(&Apc->ApcListEntry)) 909 { 910 /* Set the correct state based on the APC Mode */ 911 if (Apc->ApcMode == KernelMode) 912 { 913 /* No more pending kernel APCs */ 914 ApcState->KernelApcPending = FALSE; 915 } 916 else 917 { 918 /* No more pending user APCs */ 919 ApcState->UserApcPending = FALSE; 920 } 921 } 922 923 /* Release dispatcher lock */ 924 KiReleaseDispatcherLockFromSynchLevel(); 925 } 926 927 /* Release the lock and return */ 928 KiReleaseApcLock(&ApcLock); 929 return Inserted; 930 } 931 932 /*++ 933 * @name KeAreApcsDisabled 934 * @implemented NT4 935 * 936 * The KeAreApcsDisabled routine returns whether kernel APC delivery is 937 * disabled for the current thread. 938 * 939 * @param None. 940 * 941 * @return KeAreApcsDisabled returns TRUE if the thread is within a critical 942 * region or a guarded region, and FALSE otherwise. 943 * 944 * @remarks A thread running at IRQL = PASSIVE_LEVEL can use KeAreApcsDisabled 945 * to determine if normal kernel APCs are disabled. 946 * 947 * A thread that is inside critical region has both user APCs and 948 * normal kernel APCs disabled, but not special kernel APCs. 949 * 950 * A thread that is inside a guarded region has all APCs disabled, 951 * including special kernel APCs. 952 * 953 * Callers of this routine must be running at IRQL <= DISPATCH_LEVEL. 954 * 955 *--*/ 956 BOOLEAN 957 NTAPI 958 KeAreApcsDisabled(VOID) 959 { 960 /* Return the Kernel APC State */ 961 return KeGetCurrentThread()->CombinedApcDisable ? TRUE : FALSE; 962 } 963 964 /*++ 965 * @name KeAreAllApcsDisabled 966 * @implemented NT5.1 967 * 968 * The KeAreAllApcsDisabled routine returns whether the calling thread is 969 * inside a guarded region or running at IRQL >= APC_LEVEL, which disables 970 * all APC delivery. 971 * 972 * @param None. 973 * 974 * @return KeAreAllApcsDisabled returns TRUE if the thread is within a guarded 975 * guarded region or running at IRQL >= APC_LEVEL, and FALSE otherwise. 976 * 977 * @remarks A thread running at IRQL = PASSIVE_LEVEL can use this routine to 978 * determine if all APC delivery is disabled. 979 * 980 * Callers of this routine must be running at IRQL <= DISPATCH_LEVEL. 981 * 982 *--*/ 983 BOOLEAN 984 NTAPI 985 KeAreAllApcsDisabled(VOID) 986 { 987 /* Return the Special APC State */ 988 return ((KeGetCurrentThread()->SpecialApcDisable) || 989 (KeGetCurrentIrql() >= APC_LEVEL)) ? TRUE : FALSE; 990 } 991