2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ke/i386/usercall.c
5 * PURPOSE: User-mode Callout Mechanisms (APC and Win32K Callbacks)
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
9 /* INCLUDES ******************************************************************/
13 #include <internal/debug.h>
15 extern PGDI_BATCHFLUSH_ROUTINE
KeGdiFlushUserBatch();
17 /* PRIVATE FUNCTIONS *********************************************************/
19 _SEH_DEFINE_LOCALS(KiCopyInfo
)
21 volatile EXCEPTION_RECORD SehExceptRecord
;
24 _SEH_FILTER(KiCopyInformation2
)
26 _SEH_ACCESS_LOCALS(KiCopyInfo
);
28 /* Copy the exception records and return to the handler */
29 RtlCopyMemory((PVOID
)&_SEH_VAR(SehExceptRecord
),
30 _SEH_GetExceptionPointers()->ExceptionRecord
,
31 sizeof(EXCEPTION_RECORD
));
32 return EXCEPTION_EXECUTE_HANDLER
;
36 * @name KiInitializeUserApc
38 * Prepares the Context for a User-Mode APC called through NTDLL.DLL
41 * Pointer to the Exception Frame on non-i386 builds.
44 * Pointer to the Trap Frame.
46 * @param NormalRoutine
47 * Pointer to the NormalRoutine to call.
49 * @param NormalContext
50 * Pointer to the context to send to the Normal Routine.
52 * @param SystemArgument[1-2]
53 * Pointer to a set of two parameters that contain untyped data.
62 KiInitializeUserApc(IN PKEXCEPTION_FRAME ExceptionFrame
,
63 IN PKTRAP_FRAME TrapFrame
,
64 IN PKNORMAL_ROUTINE NormalRoutine
,
65 IN PVOID NormalContext
,
66 IN PVOID SystemArgument1
,
67 IN PVOID SystemArgument2
)
70 ULONG_PTR Stack
, AlignedEsp
;
72 EXCEPTION_RECORD SehExceptRecord
;
73 _SEH_DECLARE_LOCALS(KiCopyInfo
);
75 /* Don't deliver APCs in V86 mode */
76 if (TrapFrame
->EFlags
& EFLAGS_V86_MASK
) return;
78 /* Save the full context */
79 Context
.ContextFlags
= CONTEXT_FULL
| CONTEXT_DEBUG_REGISTERS
;
80 KeTrapFrameToContext(TrapFrame
, ExceptionFrame
, &Context
);
82 /* Protect with SEH */
86 ASSERT((TrapFrame
->SegCs
& MODE_MASK
) != KernelMode
);
88 /* Get the aligned size */
89 AlignedEsp
= Context
.Esp
& ~3;
90 ContextLength
= CONTEXT_ALIGNED_SIZE
+ (4 * sizeof(ULONG_PTR
));
91 Stack
= ((AlignedEsp
- 8) & ~3) - ContextLength
;
94 ProbeForWrite((PVOID
)Stack
, AlignedEsp
- Stack
, 1);
97 /* Copy data into it */
98 RtlCopyMemory((PVOID
)(Stack
+ (4 * sizeof(ULONG_PTR
))),
102 /* Run at APC dispatcher */
103 TrapFrame
->Eip
= (ULONG
)KeUserApcDispatcher
;
104 TrapFrame
->HardwareEsp
= Stack
;
106 /* Setup Ring 3 state */
107 TrapFrame
->SegCs
= Ke386SanitizeSeg(KGDT_R3_CODE
, UserMode
);
108 TrapFrame
->HardwareSegSs
= Ke386SanitizeSeg(KGDT_R3_DATA
, UserMode
);
109 TrapFrame
->SegDs
= Ke386SanitizeSeg(KGDT_R3_DATA
, UserMode
);
110 TrapFrame
->SegEs
= Ke386SanitizeSeg(KGDT_R3_DATA
, UserMode
);
111 TrapFrame
->SegFs
= Ke386SanitizeSeg(KGDT_R3_TEB
, UserMode
);
112 TrapFrame
->SegGs
= 0;
113 TrapFrame
->ErrCode
= 0;
115 /* Sanitize EFLAGS */
116 TrapFrame
->EFlags
= Ke386SanitizeFlags(Context
.EFlags
, UserMode
);
118 /* Check if thread has IOPL and force it enabled if so */
119 if (KeGetCurrentThread()->Iopl
) TrapFrame
->EFlags
|= 0x3000;
121 /* Setup the stack */
122 *(PULONG_PTR
)(Stack
+ 0 * sizeof(ULONG_PTR
)) = (ULONG_PTR
)NormalRoutine
;
123 *(PULONG_PTR
)(Stack
+ 1 * sizeof(ULONG_PTR
)) = (ULONG_PTR
)NormalContext
;
124 *(PULONG_PTR
)(Stack
+ 2 * sizeof(ULONG_PTR
)) = (ULONG_PTR
)SystemArgument1
;
125 *(PULONG_PTR
)(Stack
+ 3 * sizeof(ULONG_PTR
)) = (ULONG_PTR
)SystemArgument2
;
127 _SEH_EXCEPT(KiCopyInformation2
)
129 /* Dispatch the exception */
130 _SEH_VAR(SehExceptRecord
).ExceptionAddress
= (PVOID
)TrapFrame
->Eip
;
131 KiDispatchException(&SehExceptRecord
,
140 /* PUBLIC FUNCTIONS **********************************************************/
147 KeUserModeCallback(IN ULONG RoutineIndex
,
149 IN ULONG ArgumentLength
,
151 OUT PULONG ResultLength
)
153 ULONG_PTR NewStack
, OldStack
;
155 NTSTATUS CallbackStatus
= STATUS_SUCCESS
;
156 PEXCEPTION_REGISTRATION_RECORD ExceptionList
;
158 ULONG GdiBatchCount
= 0;
159 ASSERT(KeGetCurrentThread()->ApcState
.KernelApcInProgress
== FALSE
);
160 ASSERT(KeGetPreviousMode() == UserMode
);
162 /* Get the current user-mode stack */
163 UserEsp
= KiGetUserModeStackAddress();
166 /* Enter a SEH Block */
169 /* Calculate and align the stack size */
170 NewStack
= (OldStack
- ArgumentLength
) & ~3;
172 /* Make sure it's writable */
173 ProbeForWrite((PVOID
)(NewStack
- 6 * sizeof(ULONG_PTR
)),
174 ArgumentLength
+ 6 * sizeof(ULONG_PTR
),
177 /* Copy the buffer into the stack */
178 RtlCopyMemory((PVOID
)NewStack
, Argument
, ArgumentLength
);
180 /* Write the arguments */
182 *(PULONG
)NewStack
= 0;
183 *(PULONG
)(NewStack
+ 4) = RoutineIndex
;
184 *(PULONG
)(NewStack
+ 8) = (NewStack
+ 24);
185 *(PULONG
)(NewStack
+ 12) = ArgumentLength
;
187 /* Save the exception list */
188 Teb
= KeGetCurrentThread()->Teb
;
189 ExceptionList
= Teb
->Tib
.ExceptionList
;
191 /* Jump to user mode */
193 CallbackStatus
= KiCallUserMode(Result
, ResultLength
);
194 if (CallbackStatus
!= STATUS_CALLBACK_POP_STACK
)
196 /* Only restore the exception list if we didn't crash in ring 3 */
197 Teb
->Tib
.ExceptionList
= ExceptionList
;
198 CallbackStatus
= STATUS_SUCCESS
;
202 /* Otherwise, pop the stack */
206 /* Read the GDI Batch count */
207 GdiBatchCount
= Teb
->GdiBatchCount
;
211 /* Get the SEH exception */
212 CallbackStatus
= _SEH_GetExceptionCode();
215 if (!NT_SUCCESS(CallbackStatus
)) return CallbackStatus
;
217 /* Check if we have GDI Batch operations */
220 /* Shouldn't happen in ROS yet */
223 // KeGdiFlushUserBatch();
226 /* Restore stack and return */
228 return CallbackStatus
;