- 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
This commit is contained in:
Alex Ionescu 2006-09-11 00:54:12 +00:00
parent 4d94e0557f
commit 1462b71058
4 changed files with 470 additions and 408 deletions

View file

@ -381,6 +381,7 @@ Author:
#define EFLAG_ZERO 0x4000
#define EFLAG_SELECT (EFLAG_SIGN + EFLAG_ZERO)
#endif
#define EFLAGS_USER_SANITIZE 0x3F4DD7
//
// CR0

View file

@ -12,7 +12,7 @@
#define NDEBUG
#include <internal/debug.h>
/* 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);
}

View file

@ -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 <ntoskrnl.h>
#define NDEBUG
#include <debug.h>
/* 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;
}

View file

@ -40,6 +40,7 @@
<file>thread.c</file>
<file>trap.s</file>
<file>usercall_asm.S</file>
<file>userapc.c</file>
<file>v86vdm.c</file>
<file>v86m_sup.S</file>
</directory>