Listing 6 The pseudocode for the NT APC dispatcher subroutine KiDeliverApc
//========================================================================= // // KiDeliverApc // // Copyright (C) 2002 Albert Almeida // // Pseudo-code for the NT/2000's APC dispatcher subroutine KiDeliverApc // in Ntoskrnl.exe. // // The kernel always executes this subroutine at APC Level. // Reserved is always passed as NULL. // //========================================================================= VOID KiDeliverApc(KPROCESSOR_MODE PreviousMode, PVOID Reserved, PKTRAP_FRAME TrapFrame) { // // Get a pointer to the currently executing thread from the // Processor Region Control Block (PRCB) // PKTHREAD CurrentThread = PCR->CurrentThread; // // Raises the processor's IRQL to Clock2 Level // OldIrql = _KeRaiseIrqlToSynchLevel(); CurrentThread->ApcState.KernelApcPending = FALSE; // // If kernel-mode APC list is empty then check user-mode APC list // if (!IsListEmpty(&CurrentThread->ApcState.ApcListhead[KernelMode])) { do { // // Get the APC object at the list head // entry = CurrentThread->ApcState.ApcListHead [KernelMode].Flink; Apc = CONTAINING_RECORD(entry, KAPC, ApcListEntry); // // Save the APC attributes in local variables so that // it's safe to free // the APC object in KernelRoutine // NormalRoutine = Apc->NormalRoutine; KernelRoutine = Apc->KernelRoutine; NormalContext = Apc->NormalContext; SystemArgument1 = Apc->SystemArgument1; SystemArgument2 = Apc->SystemArgument2; // // If NormalRoutine is NULL this is a special kernel-mode APC // if (NormalRoutine == NULL) { // // Dequeue the APC object from the list head // RemoveHeadlList(&CurrentThread->ApcState.ApcListHead[KernelMode]); Apc->Inserted = FALSE; // // Lower the IRQL to APC Level // _KfLowerIrql(OldIrql); // // Note that nothing prevents special kernel // APCs from executing // Kernelroutine(Apc, &NormalRoutine, &NormalContext, &SystemArgument1, &SystemArgument2); OldIrql = _KeRaiseIrqlToSynchLevel(); } else { // // We are here is NormalRoutine is non-NULL // which means this is // a regular kernel-mode APC. Then check is // there are any // regular kernel-mode APCs in progress or // the thread is inside a // critical region in which case // KernelApcDisable is TRUE. // If any of these conditions hold, return to // the caller // if (CurrentThread->ApcState.KernelApcInProgress || CurrentThread->KernelApcDisable) goto restore_irql_and_exit; // // Dequeue the APC object from the list head // RemoveHeadList(&CurrentThread->ApcState.ApcListHead[KernelMode]); // // Clear the kernel-mode Alerted flag // CurrentThread->Alerted[0] = FALSE; _KfLowerIrql(OldIrql); // // Call this regular-kernel mode APC // KernelRoutine // Kernelroutine(Apc, &NormalRoutine, &NormalContext, &SystemArgument1, &SystemArgument2); // // If the KernelRoutine of this regualr // kernel-mode APC did not // clear the NormalRoutine function pointer, // call this function // at Passive Level // if (NormalRoutine != NULL) { // // Because the NormalRoutine of a // regular kernel-mode APC // runs at Passive Level, it can be // preempted by // special kernel-mode APCs // CurrentThread->ApcState.KernelApcInProgress = TRUE; _KfLowerIrql(PASSIVE_LEVEL); NormalRoutine(NormalContext, SystemArgument1, SystemArgument2); OldIrql = _KfRaiseIrql(APC_LEVEL); } OldIrql = _KeRaiseIrqlToSynchLevel(); CurrentThread->ApcState.KernelApcInProgress = FALSE; } } while (!IsListEmpty(&CurrentThread->ApcState.ApcListhead [KernelMode])) } // // Check if user-mode APC list is empty // if (!IsListEmpty(&CurrentThread->ApcState.ApcListhead[UserMode])) { // // If PreviousMode is not user mode or there are // no pending user APCs, just return to the caller // if (PreviousMode != UserMode || CurrentThread->ApcState.UserApcPending == FALSE) goto restore_irql_and_exit; CurrentThread->ApcState.UserApcPending = FALSE; // // Get the APC object at the list head // entry = CurrentThread->ApcState.ApcListHead[UserMode].Flink; Apc = CONTAINING_RECORD(entry, KAPC, ApcListEntry); // // Save the APC attributes in local variables so that it×s // safe // to free the APC object in KernelRoutine // NormalRoutine = Apc->NormalRoutine; KernelRoutine = Apc->KernelRoutine; NormalContext = Apc->NormalContext; SystemArgument1 = Apc->SystemArgument1; SystemArgument2 = Apc->SystemArgument2; RemoveHeadList(&CurrentThread->ApcState.ApcListHead[UserMode]); // // Clear the kernel-mode Alerted flag // CurrentThread->Alerted[0] = FALSE; // // Calls this user APC's KernelRoutine // Kernelroutine(Apc, &NormalRoutine, &NormalContext, &SystemArgument1, &SystemArgument2); if (NormalRoutine == NULL) { // // This function checks whether there are more // pending user APCs // and in such a case, set the UserModeApcPendig // flag to TRUE. // _KeTestAlertThread(UserMode); return; } else { // // This function sets up the thread's the trap frame // to start // executing in user mode at KiUserApcDispatcher in // Ntdll.dll // _KiInitializeUserApc(Reserved, TrapFrame, NormalRoutine, NormalContext, SystemArgument1, SystemArgument2); return; } } restore_irql_and_exit: _KfLowerIrql(OldIrql); }