From 1462b71058741274c8679c1a0bf902270d4c2af7 Mon Sep 17 00:00:00 2001 From: Alex Ionescu Date: Mon, 11 Sep 2006 00:54:12 +0000 Subject: [PATCH] - Re-organize apc.c into private/public functions (no code change). - Move KiInitializeUserApc to ke/i386/userapc.c. The routine is non-portable. - Force Ring 3 state into User APC TrapFrame to maintain system integrity and coherency. Also respect IOPL. - Use SEH handling routine just like when handling exceptions, to properly re-direct a crash here. Thanks to KJK again for showing me how to write these kinds of routines. svn path=/trunk/; revision=24050 --- reactos/include/ndk/asm.h | 1 + reactos/ntoskrnl/ke/apc.c | 741 +++++++++++++---------------- reactos/ntoskrnl/ke/i386/userapc.c | 135 ++++++ reactos/ntoskrnl/ntoskrnl.rbuild | 1 + 4 files changed, 470 insertions(+), 408 deletions(-) create mode 100644 reactos/ntoskrnl/ke/i386/userapc.c diff --git a/reactos/include/ndk/asm.h b/reactos/include/ndk/asm.h index 57ecee2eca1..5fbcd49b081 100644 --- a/reactos/include/ndk/asm.h +++ b/reactos/include/ndk/asm.h @@ -381,6 +381,7 @@ Author: #define EFLAG_ZERO 0x4000 #define EFLAG_SELECT (EFLAG_SIGN + EFLAG_ZERO) #endif +#define EFLAGS_USER_SANITIZE 0x3F4DD7 // // CR0 diff --git a/reactos/ntoskrnl/ke/apc.c b/reactos/ntoskrnl/ke/apc.c index 4e39c01a5c2..6ae0679cf67 100644 --- a/reactos/ntoskrnl/ke/apc.c +++ b/reactos/ntoskrnl/ke/apc.c @@ -12,7 +12,7 @@ #define NDEBUG #include -/* FUNCTIONS *****************************************************************/ +/* PRIVATE FUNCTIONS *********************************************************/ /*++ * @name KiCheckForKernelApcDelivery @@ -57,154 +57,6 @@ KiCheckForKernelApcDelivery(VOID) } } -/*++ - * @name KeEnterCriticalRegion - * @implemented NT4 - * - * The KeEnterCriticalRegion routine temporarily disables the delivery of - * normal kernel APCs; special kernel-mode APCs are still delivered. - * - * @param None. - * - * @return None. - * - * @remarks Highest-level drivers can call this routine while running in the - * context of the thread that requested the current I/O operation. - * Any caller of this routine should call KeLeaveCriticalRegion as - * quickly as possible. - * - * Callers of KeEnterCriticalRegion must be running at IRQL <= - * APC_LEVEL. - * - *--*/ -VOID -NTAPI -_KeEnterCriticalRegion(VOID) -{ - /* Use inlined function */ - KeEnterCriticalRegion(); -} - -/*++ - * KeLeaveCriticalRegion - * @implemented NT4 - * - * The KeLeaveCriticalRegion routine reenables the delivery of normal - * kernel-mode APCs that were disabled by a call to KeEnterCriticalRegion. - * - * @param None. - * - * @return None. - * - * @remarks Highest-level drivers can call this routine while running in the - * context of the thread that requested the current I/O operation. - * - * Callers of KeLeaveCriticalRegion must be running at IRQL <= - * DISPATCH_LEVEL. - * - *--*/ -VOID -NTAPI -_KeLeaveCriticalRegion(VOID) -{ - /* Use inlined version */ - KeLeaveCriticalRegion(); -} - -/*++ - * KeInitializeApc - * @implemented NT4 - * - * The The KeInitializeApc routine initializes an APC object, and registers - * the Kernel, Rundown and Normal routines for that object. - * - * Params: - * Apc - Pointer to a KAPC structure that represents the APC object to - * initialize. The caller must allocate storage for the structure - * from resident memory. - * - * Thread - Thread to which to deliver the APC. - * - * TargetEnvironment - APC Environment to be used. - * - * KernelRoutine - Points to the KernelRoutine to associate with the APC. - * This routine is executed for all APCs. - * - * RundownRoutine - Points to the RundownRoutine to associate with the APC. - * This routine is executed when the Thread exists with - * the APC executing. - * - * NormalRoutine - Points to the NormalRoutine to associate with the APC. - * This routine is executed at PASSIVE_LEVEL. If this is - * not specifed, the APC becomes a Special APC and the - * Mode and Context parameters are ignored. - * - * Mode - Specifies the processor mode at which to run the Normal Routine. - * - * Context - Specifices the value to pass as Context parameter to the - * registered routines. - * - * Returns: - * None. - * - * Remarks: - * The caller can queue an initialized APC with KeInsertQueueApc. - * - * Storage for the APC object must be resident, such as nonpaged pool - * allocated by the caller. - * - * Callers of this routine must be running at IRQL = PASSIVE_LEVEL. - * - *--*/ -VOID -STDCALL -KeInitializeApc(IN PKAPC Apc, - IN PKTHREAD Thread, - IN KAPC_ENVIRONMENT TargetEnvironment, - IN PKKERNEL_ROUTINE KernelRoutine, - IN PKRUNDOWN_ROUTINE RundownRoutine OPTIONAL, - IN PKNORMAL_ROUTINE NormalRoutine, - IN KPROCESSOR_MODE Mode, - IN PVOID Context) -{ - DPRINT("KeInitializeApc(Apc %x, Thread %x, Environment %d, " - "KernelRoutine %x, RundownRoutine %x, NormalRoutine %x, Mode %d, " - "Context %x)\n",Apc,Thread,TargetEnvironment,KernelRoutine,RundownRoutine, - NormalRoutine,Mode,Context); - - /* Set up the basic APC Structure Data */ - RtlZeroMemory(Apc, sizeof(KAPC)); - Apc->Type = ApcObject; - Apc->Size = sizeof(KAPC); - - /* Set the Environment */ - if (TargetEnvironment == CurrentApcEnvironment) { - - Apc->ApcStateIndex = Thread->ApcStateIndex; - - } else { - - Apc->ApcStateIndex = TargetEnvironment; - } - - /* Set the Thread and Routines */ - Apc->Thread = Thread; - Apc->KernelRoutine = KernelRoutine; - Apc->RundownRoutine = RundownRoutine; - Apc->NormalRoutine = NormalRoutine; - - /* Check if this is a Special APC, in which case we use KernelMode and no Context */ - if (ARGUMENT_PRESENT(NormalRoutine)) { - - Apc->ApcMode = Mode; - Apc->NormalContext = Context; - - } else { - - Apc->ApcMode = KernelMode; - } -} - static __inline VOID @@ -426,183 +278,6 @@ Unwait: return; } -/*++ - * KeInsertQueueApc - * @implemented NT4 - * - * The KeInsertQueueApc routine queues a APC for execution when the right - * scheduler environment exists. - * - * Params: - * Apc - Pointer to an initialized control object of type DPC for which the - * caller provides the storage. - * - * SystemArgument[1,2] - Pointer to a set of two parameters that contain - * untyped data. - * - * PriorityBoost - Priority Boost to apply to the Thread. - * - * Returns: - * If the APC is already inserted or APC queueing is disabled, FALSE. - * Otherwise, TRUE. - * - * Remarks: - * The APC will execute at APC_LEVEL for the KernelRoutine registered, and - * at PASSIVE_LEVEL for the NormalRoutine registered. - * - * Callers of this routine must be running at IRQL = PASSIVE_LEVEL. - * - *--*/ -BOOLEAN -STDCALL -KeInsertQueueApc(PKAPC Apc, - PVOID SystemArgument1, - PVOID SystemArgument2, - KPRIORITY PriorityBoost) -{ - PKTHREAD Thread = Apc->Thread; - KLOCK_QUEUE_HANDLE ApcLock; - BOOLEAN State = TRUE; - - /* Get the APC lock */ - KiAcquireApcLock(Thread, &ApcLock); - - /* Make sure we can Queue APCs and that this one isn't already inserted */ - if ((Thread->ApcQueueable == FALSE) && (Apc->Inserted == TRUE)) - { - /* Fail */ - State = FALSE; - } - else - { - /* Set the System Arguments and set it as inserted */ - Apc->SystemArgument1 = SystemArgument1; - Apc->SystemArgument2 = SystemArgument2; - Apc->Inserted = TRUE; - - /* Call the Internal Function */ - KiInsertQueueApc(Apc, PriorityBoost); - } - - /* Release the APC lock and return success */ - KiReleaseApcLock(&ApcLock); - KiExitDispatcher(ApcLock.OldIrql); - return State; -} - -/*++ - * KeFlushQueueApc - * - * The KeFlushQueueApc routine flushes all APCs of the given processor mode - * from the specified Thread's APC queue. - * - * Params: - * Thread - Pointer to the thread whose APC queue will be flushed. - * - * PreviousMode - Specifies which APC Queue to flush. - * - * Returns: - * A pointer to the first entry in the flushed APC queue. - * - * Remarks: - * If the routine returns NULL, it means that no APCs were to be flushed. - * - * Callers of KeFlushQueueApc must be running at DISPATCH_LEVEL or lower. - * - *--*/ -PLIST_ENTRY -STDCALL -KeFlushQueueApc(IN PKTHREAD Thread, - IN KPROCESSOR_MODE PreviousMode) -{ - PKAPC Apc; - PLIST_ENTRY FirstEntry, CurrentEntry; - KLOCK_QUEUE_HANDLE ApcLock; - - /* Get the APC lock */ - KiAcquireApcLock(Thread, &ApcLock); - - if (IsListEmpty(&Thread->ApcState.ApcListHead[PreviousMode])) { - FirstEntry = NULL; - } else { - FirstEntry = Thread->ApcState.ApcListHead[PreviousMode].Flink; - RemoveEntryList(&Thread->ApcState.ApcListHead[PreviousMode]); - CurrentEntry = FirstEntry; - do { - Apc = CONTAINING_RECORD(CurrentEntry, KAPC, ApcListEntry); - Apc->Inserted = FALSE; - CurrentEntry = CurrentEntry->Flink; - } while (CurrentEntry != FirstEntry); - } - - /* Release the lock */ - KiReleaseApcLock(&ApcLock); - - /* Return the first entry */ - return FirstEntry; -} - -/*++ - * KeRemoveQueueApc - * - * The KeRemoveQueueApc routine removes a given APC object from the system - * APC queue. - * - * Params: - * APC - Pointer to an initialized APC object that was queued by calling - * KeInsertQueueApc. - * - * Returns: - * TRUE if the APC Object is in the APC Queue. If it isn't, no operation is - * performed and FALSE is returned. - * - * Remarks: - * If the given APC Object is currently queued, it is removed from the queue - * and any calls to the registered routines are cancelled. - * - * Callers of KeLeaveCriticalRegion can be running at any IRQL. - * - *--*/ -BOOLEAN -STDCALL -KeRemoveQueueApc(PKAPC Apc) -{ - PKTHREAD Thread = Apc->Thread; - PKAPC_STATE ApcState; - BOOLEAN Inserted; - KLOCK_QUEUE_HANDLE ApcLock; - - /* Get the APC lock */ - KiAcquireApcLock(Thread, &ApcLock); - - /* Check if it's inserted */ - if ((Inserted = Apc->Inserted)) - { - /* Remove it from the Queue*/ - Apc->Inserted = FALSE; - ApcState = Thread->ApcStatePointer[(int)Apc->ApcStateIndex]; - RemoveEntryList(&Apc->ApcListEntry); - - /* If the Queue is completely empty, then no more APCs are pending */ - if (IsListEmpty(&Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->ApcListHead[(int)Apc->ApcMode])) - { - /* Set the correct State based on the Apc Mode */ - if (Apc->ApcMode == KernelMode) - { - ApcState->KernelApcPending = FALSE; - } - else - { - ApcState->UserApcPending = FALSE; - } - } - } - - /* Release the lock and return */ - KiReleaseApcLock(&ApcLock); - return Inserted; -} - /*++ * KiDeliverApc * @implemented @NT4 @@ -835,81 +510,363 @@ KiFreeApcRoutine(PKAPC Apc, ExFreePool(Apc); } +static __inline +VOID RepairList(PLIST_ENTRY Original, + PLIST_ENTRY Copy, + KPROCESSOR_MODE Mode) +{ + /* Copy Source to Desination */ + if (IsListEmpty(&Original[(int)Mode])) { + + InitializeListHead(&Copy[(int)Mode]); + + } else { + + Copy[(int)Mode].Flink = Original[(int)Mode].Flink; + Copy[(int)Mode].Blink = Original[(int)Mode].Blink; + Original[(int)Mode].Flink->Blink = &Copy[(int)Mode]; + Original[(int)Mode].Blink->Flink = &Copy[(int)Mode]; + } +} + +VOID +STDCALL +KiMoveApcState(PKAPC_STATE OldState, + PKAPC_STATE NewState) +{ + /* Restore backup of Original Environment */ + *NewState = *OldState; + + /* Repair Lists */ + RepairList(NewState->ApcListHead, OldState->ApcListHead, KernelMode); + RepairList(NewState->ApcListHead, OldState->ApcListHead, UserMode); +} + +/* PUBLIC FUNCTIONS **********************************************************/ + /*++ - * KiInitializeUserApc + * @name KeEnterCriticalRegion + * @implemented NT4 * - * Prepares the Context for a User-Mode APC called through NTDLL.DLL + * The KeEnterCriticalRegion routine temporarily disables the delivery of + * normal kernel APCs; special kernel-mode APCs are still delivered. + * + * @param None. + * + * @return None. + * + * @remarks Highest-level drivers can call this routine while running in the + * context of the thread that requested the current I/O operation. + * Any caller of this routine should call KeLeaveCriticalRegion as + * quickly as possible. + * + * Callers of KeEnterCriticalRegion must be running at IRQL <= + * APC_LEVEL. + * + *--*/ +VOID +NTAPI +_KeEnterCriticalRegion(VOID) +{ + /* Use inlined function */ + KeEnterCriticalRegion(); +} + +/*++ + * KeLeaveCriticalRegion + * @implemented NT4 + * + * The KeLeaveCriticalRegion routine reenables the delivery of normal + * kernel-mode APCs that were disabled by a call to KeEnterCriticalRegion. + * + * @param None. + * + * @return None. + * + * @remarks Highest-level drivers can call this routine while running in the + * context of the thread that requested the current I/O operation. + * + * Callers of KeLeaveCriticalRegion must be running at IRQL <= + * DISPATCH_LEVEL. + * + *--*/ +VOID +NTAPI +_KeLeaveCriticalRegion(VOID) +{ + /* Use inlined version */ + KeLeaveCriticalRegion(); +} + +/*++ + * KeInitializeApc + * @implemented NT4 + * + * The The KeInitializeApc routine initializes an APC object, and registers + * the Kernel, Rundown and Normal routines for that object. * * Params: - * Reserved - Pointer to the Exception Frame on non-i386 builds. + * Apc - Pointer to a KAPC structure that represents the APC object to + * initialize. The caller must allocate storage for the structure + * from resident memory. * - * TrapFrame - Pointer to the Trap Frame. + * Thread - Thread to which to deliver the APC. * - * NormalRoutine - Pointer to the NormalRoutine to call. + * TargetEnvironment - APC Environment to be used. * - * NormalContext - Pointer to the context to send to the Normal Routine. + * KernelRoutine - Points to the KernelRoutine to associate with the APC. + * This routine is executed for all APCs. * - * SystemArgument[1-2] - Pointer to a set of two parameters that contain - * untyped data. + * RundownRoutine - Points to the RundownRoutine to associate with the APC. + * This routine is executed when the Thread exists with + * the APC executing. + * + * NormalRoutine - Points to the NormalRoutine to associate with the APC. + * This routine is executed at PASSIVE_LEVEL. If this is + * not specifed, the APC becomes a Special APC and the + * Mode and Context parameters are ignored. + * + * Mode - Specifies the processor mode at which to run the Normal Routine. + * + * Context - Specifices the value to pass as Context parameter to the + * registered routines. * * Returns: * None. * * Remarks: - * None. + * The caller can queue an initialized APC with KeInsertQueueApc. + * + * Storage for the APC object must be resident, such as nonpaged pool + * allocated by the caller. + * + * Callers of this routine must be running at IRQL = PASSIVE_LEVEL. * *--*/ VOID STDCALL -KiInitializeUserApc(IN PKEXCEPTION_FRAME ExceptionFrame, - IN PKTRAP_FRAME TrapFrame, - IN PKNORMAL_ROUTINE NormalRoutine, - IN PVOID NormalContext, - IN PVOID SystemArgument1, - IN PVOID SystemArgument2) +KeInitializeApc(IN PKAPC Apc, + IN PKTHREAD Thread, + IN KAPC_ENVIRONMENT TargetEnvironment, + IN PKKERNEL_ROUTINE KernelRoutine, + IN PKRUNDOWN_ROUTINE RundownRoutine OPTIONAL, + IN PKNORMAL_ROUTINE NormalRoutine, + IN KPROCESSOR_MODE Mode, + IN PVOID Context) { - CONTEXT Context; - ULONG_PTR Stack; - ULONG Size; + DPRINT("KeInitializeApc(Apc %x, Thread %x, Environment %d, " + "KernelRoutine %x, RundownRoutine %x, NormalRoutine %x, Mode %d, " + "Context %x)\n",Apc,Thread,TargetEnvironment,KernelRoutine,RundownRoutine, + NormalRoutine,Mode,Context); - DPRINT("KiInitializeUserApc(TrapFrame %x/%x)\n", TrapFrame, - KeGetCurrentThread()->TrapFrame); + /* Set up the basic APC Structure Data */ + RtlZeroMemory(Apc, sizeof(KAPC)); + Apc->Type = ApcObject; + Apc->Size = sizeof(KAPC); - /* Don't deliver APCs in V86 mode */ - if (TrapFrame->EFlags & X86_EFLAGS_VM) return; + /* Set the Environment */ + if (TargetEnvironment == CurrentApcEnvironment) { - /* Save the full context */ - Context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS; - KeTrapFrameToContext(TrapFrame, ExceptionFrame, &Context); + Apc->ApcStateIndex = Thread->ApcStateIndex; - /* Protect with SEH */ - _SEH_TRY - { - /* Get the aligned size */ - Size = ((sizeof(CONTEXT) + 3) & ~3) + 4 * sizeof(ULONG_PTR); - Stack = (Context.Esp & ~3) - Size; + } else { - /* Probe and copy */ - ProbeForWrite((PVOID)Stack, Size, 4); - RtlMoveMemory((PVOID)(Stack + 4 * sizeof(ULONG_PTR)), - &Context, - sizeof(CONTEXT)); - - /* Run at APC dispatcher */ - TrapFrame->Eip = (ULONG)KeUserApcDispatcher; - TrapFrame->HardwareEsp = Stack; - - /* Setup the stack */ - *(PULONG_PTR)(Stack + 0 * sizeof(ULONG_PTR)) = (ULONG_PTR)NormalRoutine; - *(PULONG_PTR)(Stack + 1 * sizeof(ULONG_PTR)) = (ULONG_PTR)NormalContext; - *(PULONG_PTR)(Stack + 2 * sizeof(ULONG_PTR)) = (ULONG_PTR)SystemArgument1; - *(PULONG_PTR)(Stack + 3 * sizeof(ULONG_PTR)) = (ULONG_PTR)SystemArgument2; + Apc->ApcStateIndex = TargetEnvironment; } - _SEH_HANDLE - { - /* FIXME: Get the record and raise an exception */ + + /* Set the Thread and Routines */ + Apc->Thread = Thread; + Apc->KernelRoutine = KernelRoutine; + Apc->RundownRoutine = RundownRoutine; + Apc->NormalRoutine = NormalRoutine; + + /* Check if this is a Special APC, in which case we use KernelMode and no Context */ + if (ARGUMENT_PRESENT(NormalRoutine)) { + + Apc->ApcMode = Mode; + Apc->NormalContext = Context; + + } else { + + Apc->ApcMode = KernelMode; } - _SEH_END; +} + +/*++ + * KeInsertQueueApc + * @implemented NT4 + * + * The KeInsertQueueApc routine queues a APC for execution when the right + * scheduler environment exists. + * + * Params: + * Apc - Pointer to an initialized control object of type DPC for which the + * caller provides the storage. + * + * SystemArgument[1,2] - Pointer to a set of two parameters that contain + * untyped data. + * + * PriorityBoost - Priority Boost to apply to the Thread. + * + * Returns: + * If the APC is already inserted or APC queueing is disabled, FALSE. + * Otherwise, TRUE. + * + * Remarks: + * The APC will execute at APC_LEVEL for the KernelRoutine registered, and + * at PASSIVE_LEVEL for the NormalRoutine registered. + * + * Callers of this routine must be running at IRQL = PASSIVE_LEVEL. + * + *--*/ +BOOLEAN +STDCALL +KeInsertQueueApc(PKAPC Apc, + PVOID SystemArgument1, + PVOID SystemArgument2, + KPRIORITY PriorityBoost) +{ + PKTHREAD Thread = Apc->Thread; + KLOCK_QUEUE_HANDLE ApcLock; + BOOLEAN State = TRUE; + + /* Get the APC lock */ + KiAcquireApcLock(Thread, &ApcLock); + + /* Make sure we can Queue APCs and that this one isn't already inserted */ + if ((Thread->ApcQueueable == FALSE) && (Apc->Inserted == TRUE)) + { + /* Fail */ + State = FALSE; + } + else + { + /* Set the System Arguments and set it as inserted */ + Apc->SystemArgument1 = SystemArgument1; + Apc->SystemArgument2 = SystemArgument2; + Apc->Inserted = TRUE; + + /* Call the Internal Function */ + KiInsertQueueApc(Apc, PriorityBoost); + } + + /* Release the APC lock and return success */ + KiReleaseApcLock(&ApcLock); + KiExitDispatcher(ApcLock.OldIrql); + return State; +} + +/*++ + * KeFlushQueueApc + * + * The KeFlushQueueApc routine flushes all APCs of the given processor mode + * from the specified Thread's APC queue. + * + * Params: + * Thread - Pointer to the thread whose APC queue will be flushed. + * + * PreviousMode - Specifies which APC Queue to flush. + * + * Returns: + * A pointer to the first entry in the flushed APC queue. + * + * Remarks: + * If the routine returns NULL, it means that no APCs were to be flushed. + * + * Callers of KeFlushQueueApc must be running at DISPATCH_LEVEL or lower. + * + *--*/ +PLIST_ENTRY +STDCALL +KeFlushQueueApc(IN PKTHREAD Thread, + IN KPROCESSOR_MODE PreviousMode) +{ + PKAPC Apc; + PLIST_ENTRY FirstEntry, CurrentEntry; + KLOCK_QUEUE_HANDLE ApcLock; + + /* Get the APC lock */ + KiAcquireApcLock(Thread, &ApcLock); + + if (IsListEmpty(&Thread->ApcState.ApcListHead[PreviousMode])) { + FirstEntry = NULL; + } else { + FirstEntry = Thread->ApcState.ApcListHead[PreviousMode].Flink; + RemoveEntryList(&Thread->ApcState.ApcListHead[PreviousMode]); + CurrentEntry = FirstEntry; + do { + Apc = CONTAINING_RECORD(CurrentEntry, KAPC, ApcListEntry); + Apc->Inserted = FALSE; + CurrentEntry = CurrentEntry->Flink; + } while (CurrentEntry != FirstEntry); + } + + /* Release the lock */ + KiReleaseApcLock(&ApcLock); + + /* Return the first entry */ + return FirstEntry; +} + +/*++ + * KeRemoveQueueApc + * + * The KeRemoveQueueApc routine removes a given APC object from the system + * APC queue. + * + * Params: + * APC - Pointer to an initialized APC object that was queued by calling + * KeInsertQueueApc. + * + * Returns: + * TRUE if the APC Object is in the APC Queue. If it isn't, no operation is + * performed and FALSE is returned. + * + * Remarks: + * If the given APC Object is currently queued, it is removed from the queue + * and any calls to the registered routines are cancelled. + * + * Callers of KeLeaveCriticalRegion can be running at any IRQL. + * + *--*/ +BOOLEAN +STDCALL +KeRemoveQueueApc(PKAPC Apc) +{ + PKTHREAD Thread = Apc->Thread; + PKAPC_STATE ApcState; + BOOLEAN Inserted; + KLOCK_QUEUE_HANDLE ApcLock; + + /* Get the APC lock */ + KiAcquireApcLock(Thread, &ApcLock); + + /* Check if it's inserted */ + if ((Inserted = Apc->Inserted)) + { + /* Remove it from the Queue*/ + Apc->Inserted = FALSE; + ApcState = Thread->ApcStatePointer[(int)Apc->ApcStateIndex]; + RemoveEntryList(&Apc->ApcListEntry); + + /* If the Queue is completely empty, then no more APCs are pending */ + if (IsListEmpty(&Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->ApcListHead[(int)Apc->ApcMode])) + { + /* Set the correct State based on the Apc Mode */ + if (Apc->ApcMode == KernelMode) + { + ApcState->KernelApcPending = FALSE; + } + else + { + ApcState->UserApcPending = FALSE; + } + } + } + + /* Release the lock and return */ + KiReleaseApcLock(&ApcLock); + return Inserted; } /*++ @@ -1034,35 +991,3 @@ NtQueueApcThread(HANDLE ThreadHandle, return Status; } -static __inline -VOID RepairList(PLIST_ENTRY Original, - PLIST_ENTRY Copy, - KPROCESSOR_MODE Mode) -{ - /* Copy Source to Desination */ - if (IsListEmpty(&Original[(int)Mode])) { - - InitializeListHead(&Copy[(int)Mode]); - - } else { - - Copy[(int)Mode].Flink = Original[(int)Mode].Flink; - Copy[(int)Mode].Blink = Original[(int)Mode].Blink; - Original[(int)Mode].Flink->Blink = &Copy[(int)Mode]; - Original[(int)Mode].Blink->Flink = &Copy[(int)Mode]; - } -} - -VOID -STDCALL -KiMoveApcState(PKAPC_STATE OldState, - PKAPC_STATE NewState) -{ - /* Restore backup of Original Environment */ - *NewState = *OldState; - - /* Repair Lists */ - RepairList(NewState->ApcListHead, OldState->ApcListHead, KernelMode); - RepairList(NewState->ApcListHead, OldState->ApcListHead, UserMode); -} - diff --git a/reactos/ntoskrnl/ke/i386/userapc.c b/reactos/ntoskrnl/ke/i386/userapc.c new file mode 100644 index 00000000000..cfc2a0aa8f5 --- /dev/null +++ b/reactos/ntoskrnl/ke/i386/userapc.c @@ -0,0 +1,135 @@ +/* + * PROJECT: ReactOS Kernel + * LICENSE: GPL - See COPYING in the top level directory + * FILE: ntoskrnl/ke/i386/userapc.c + * PURPOSE: Implements User-Mode APC Initialization + * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) + */ + +/* INCLUDES *****************************************************************/ + +#include +#define NDEBUG +#include + +/* PRIVATE FUNCTIONS *********************************************************/ + +_SEH_DEFINE_LOCALS(KiCopyInfo) +{ + volatile EXCEPTION_RECORD SehExceptRecord; +}; + +_SEH_FILTER(KiCopyInformation2) +{ + _SEH_ACCESS_LOCALS(KiCopyInfo); + + /* Copy the exception records and return to the handler */ + RtlMoveMemory((PVOID)&_SEH_VAR(SehExceptRecord), + _SEH_GetExceptionPointers()->ExceptionRecord, + sizeof(EXCEPTION_RECORD)); + return EXCEPTION_EXECUTE_HANDLER; +} + +/*++ + * KiInitializeUserApc + * + * Prepares the Context for a User-Mode APC called through NTDLL.DLL + * + * Params: + * Reserved - Pointer to the Exception Frame on non-i386 builds. + * + * TrapFrame - Pointer to the Trap Frame. + * + * NormalRoutine - Pointer to the NormalRoutine to call. + * + * NormalContext - Pointer to the context to send to the Normal Routine. + * + * SystemArgument[1-2] - Pointer to a set of two parameters that contain + * untyped data. + * + * Returns: + * None. + * + * Remarks: + * None. + * + *--*/ +VOID +STDCALL +KiInitializeUserApc(IN PKEXCEPTION_FRAME ExceptionFrame, + IN PKTRAP_FRAME TrapFrame, + IN PKNORMAL_ROUTINE NormalRoutine, + IN PVOID NormalContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2) +{ + CONTEXT Context; + ULONG_PTR Stack; + ULONG Size; + EXCEPTION_RECORD SehExceptRecord; + _SEH_DECLARE_LOCALS(KiCopyInfo); + + /* Don't deliver APCs in V86 mode */ + if (TrapFrame->EFlags & X86_EFLAGS_VM) return; + + /* Save the full context */ + Context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS; + KeTrapFrameToContext(TrapFrame, ExceptionFrame, &Context); + + /* Protect with SEH */ + _SEH_TRY + { + /* Sanity check */ + ASSERT((TrapFrame->SegCs & MODE_MASK) != KernelMode); + + /* Get the aligned size */ + Size = ((sizeof(CONTEXT) + 3) & ~3) + 4 * sizeof(ULONG_PTR); + Stack = (Context.Esp & ~3) - Size; + + /* Probe and copy */ + ProbeForWrite((PVOID)Stack, Size, 4); + RtlMoveMemory((PVOID)(Stack + (4 * sizeof(ULONG_PTR))), + &Context, + sizeof(CONTEXT)); + + /* Run at APC dispatcher */ + TrapFrame->Eip = (ULONG)KeUserApcDispatcher; + TrapFrame->HardwareEsp = Stack; + + /* Setup Ring 3 state */ + TrapFrame->SegCs = KGDT_R3_CODE | RPL_MASK; + TrapFrame->HardwareSegSs = KGDT_R3_DATA | RPL_MASK; + TrapFrame->SegDs = KGDT_R3_DATA | RPL_MASK; + TrapFrame->SegEs = KGDT_R3_DATA | RPL_MASK; + TrapFrame->SegGs = 0; + + /* Sanitize EFLAGS */ + TrapFrame->EFlags = Context.EFlags & EFLAGS_USER_SANITIZE; + TrapFrame->EFlags |= EFLAGS_INTERRUPT_MASK; + + /* Check if user-mode has IO privileges */ + if (KeGetCurrentThread()->Iopl) + { + /* Enable them*/ + TrapFrame->EFlags |= (0x3000); + } + + /* Setup the stack */ + *(PULONG_PTR)(Stack + 0 * sizeof(ULONG_PTR)) = (ULONG_PTR)NormalRoutine; + *(PULONG_PTR)(Stack + 1 * sizeof(ULONG_PTR)) = (ULONG_PTR)NormalContext; + *(PULONG_PTR)(Stack + 2 * sizeof(ULONG_PTR)) = (ULONG_PTR)SystemArgument1; + *(PULONG_PTR)(Stack + 3 * sizeof(ULONG_PTR)) = (ULONG_PTR)SystemArgument2; + } + _SEH_EXCEPT(KiCopyInformation2) + { + /* Dispatch the exception */ + _SEH_VAR(SehExceptRecord).ExceptionAddress = (PVOID)TrapFrame->Eip; + KiDispatchException(&SehExceptRecord, + ExceptionFrame, + TrapFrame, + UserMode, + TRUE); + } + _SEH_END; +} + diff --git a/reactos/ntoskrnl/ntoskrnl.rbuild b/reactos/ntoskrnl/ntoskrnl.rbuild index d21d1cee21a..30f4e239246 100644 --- a/reactos/ntoskrnl/ntoskrnl.rbuild +++ b/reactos/ntoskrnl/ntoskrnl.rbuild @@ -40,6 +40,7 @@ thread.c trap.s usercall_asm.S + userapc.c v86vdm.c v86m_sup.S