- Use _SEH2_YIELD when returning from an exception instead of returning outside the...
[reactos.git] / reactos / ntoskrnl / ke / i386 / usercall.c
1 /*
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)
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 extern PGDI_BATCHFLUSH_ROUTINE KeGdiFlushUserBatch;
16
17 /* PRIVATE FUNCTIONS *********************************************************/
18
19 /*++
20 * @name KiInitializeUserApc
21 *
22 * Prepares the Context for a User-Mode APC called through NTDLL.DLL
23 *
24 * @param Reserved
25 * Pointer to the Exception Frame on non-i386 builds.
26 *
27 * @param TrapFrame
28 * Pointer to the Trap Frame.
29 *
30 * @param NormalRoutine
31 * Pointer to the NormalRoutine to call.
32 *
33 * @param NormalContext
34 * Pointer to the context to send to the Normal Routine.
35 *
36 * @param SystemArgument[1-2]
37 * Pointer to a set of two parameters that contain untyped data.
38 *
39 * @return None.
40 *
41 * @remarks None.
42 *
43 *--*/
44 VOID
45 NTAPI
46 KiInitializeUserApc(IN PKEXCEPTION_FRAME ExceptionFrame,
47 IN PKTRAP_FRAME TrapFrame,
48 IN PKNORMAL_ROUTINE NormalRoutine,
49 IN PVOID NormalContext,
50 IN PVOID SystemArgument1,
51 IN PVOID SystemArgument2)
52 {
53 CONTEXT Context;
54 ULONG_PTR Stack, AlignedEsp;
55 ULONG ContextLength;
56 EXCEPTION_RECORD SehExceptRecord;
57
58 /* Don't deliver APCs in V86 mode */
59 if (TrapFrame->EFlags & EFLAGS_V86_MASK) return;
60
61 /* Save the full context */
62 Context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;
63 KeTrapFrameToContext(TrapFrame, ExceptionFrame, &Context);
64
65 /* Protect with SEH */
66 _SEH2_TRY
67 {
68 /* Sanity check */
69 ASSERT((TrapFrame->SegCs & MODE_MASK) != KernelMode);
70
71 /* Get the aligned size */
72 AlignedEsp = Context.Esp & ~3;
73 ContextLength = CONTEXT_ALIGNED_SIZE + (4 * sizeof(ULONG_PTR));
74 Stack = ((AlignedEsp - 8) & ~3) - ContextLength;
75
76 /* Probe the stack */
77 ProbeForWrite((PVOID)Stack, AlignedEsp - Stack, 1);
78 ASSERT(!(Stack & 3));
79
80 /* Copy data into it */
81 RtlCopyMemory((PVOID)(Stack + (4 * sizeof(ULONG_PTR))),
82 &Context,
83 sizeof(CONTEXT));
84
85 /* Run at APC dispatcher */
86 TrapFrame->Eip = (ULONG)KeUserApcDispatcher;
87 TrapFrame->HardwareEsp = Stack;
88
89 /* Setup Ring 3 state */
90 TrapFrame->SegCs = Ke386SanitizeSeg(KGDT_R3_CODE, UserMode);
91 TrapFrame->HardwareSegSs = Ke386SanitizeSeg(KGDT_R3_DATA, UserMode);
92 TrapFrame->SegDs = Ke386SanitizeSeg(KGDT_R3_DATA, UserMode);
93 TrapFrame->SegEs = Ke386SanitizeSeg(KGDT_R3_DATA, UserMode);
94 TrapFrame->SegFs = Ke386SanitizeSeg(KGDT_R3_TEB, UserMode);
95 TrapFrame->SegGs = 0;
96 TrapFrame->ErrCode = 0;
97
98 /* Sanitize EFLAGS */
99 TrapFrame->EFlags = Ke386SanitizeFlags(Context.EFlags, UserMode);
100
101 /* Check if thread has IOPL and force it enabled if so */
102 if (KeGetCurrentThread()->Iopl) TrapFrame->EFlags |= EFLAGS_IOPL;
103
104 /* Setup the stack */
105 *(PULONG_PTR)(Stack + 0 * sizeof(ULONG_PTR)) = (ULONG_PTR)NormalRoutine;
106 *(PULONG_PTR)(Stack + 1 * sizeof(ULONG_PTR)) = (ULONG_PTR)NormalContext;
107 *(PULONG_PTR)(Stack + 2 * sizeof(ULONG_PTR)) = (ULONG_PTR)SystemArgument1;
108 *(PULONG_PTR)(Stack + 3 * sizeof(ULONG_PTR)) = (ULONG_PTR)SystemArgument2;
109 }
110 _SEH2_EXCEPT((RtlCopyMemory(&SehExceptRecord, _SEH2_GetExceptionInformation()->ExceptionRecord, sizeof(EXCEPTION_RECORD)), EXCEPTION_EXECUTE_HANDLER))
111 {
112 /* Dispatch the exception */
113 SehExceptRecord.ExceptionAddress = (PVOID)TrapFrame->Eip;
114 KiDispatchException(&SehExceptRecord,
115 ExceptionFrame,
116 TrapFrame,
117 UserMode,
118 TRUE);
119 }
120 _SEH2_END;
121 }
122
123 /* PUBLIC FUNCTIONS **********************************************************/
124
125 /*
126 * @implemented
127 */
128 NTSTATUS
129 NTAPI
130 KeUserModeCallback(IN ULONG RoutineIndex,
131 IN PVOID Argument,
132 IN ULONG ArgumentLength,
133 OUT PVOID *Result,
134 OUT PULONG ResultLength)
135 {
136 ULONG_PTR NewStack, OldStack;
137 PULONG UserEsp;
138 NTSTATUS CallbackStatus;
139 PEXCEPTION_REGISTRATION_RECORD ExceptionList;
140 PTEB Teb;
141 ULONG GdiBatchCount = 0;
142 ASSERT(KeGetCurrentThread()->ApcState.KernelApcInProgress == FALSE);
143 ASSERT(KeGetPreviousMode() == UserMode);
144
145 /* Get the current user-mode stack */
146 UserEsp = KiGetUserModeStackAddress();
147 OldStack = *UserEsp;
148
149 /* Enter a SEH Block */
150 _SEH2_TRY
151 {
152 /* Calculate and align the stack size */
153 NewStack = (OldStack - ArgumentLength) & ~3;
154
155 /* Make sure it's writable */
156 ProbeForWrite((PVOID)(NewStack - 6 * sizeof(ULONG_PTR)),
157 ArgumentLength + 6 * sizeof(ULONG_PTR),
158 sizeof(CHAR));
159
160 /* Copy the buffer into the stack */
161 RtlCopyMemory((PVOID)NewStack, Argument, ArgumentLength);
162
163 /* Write the arguments */
164 NewStack -= 24;
165 *(PULONG)NewStack = 0;
166 *(PULONG)(NewStack + 4) = RoutineIndex;
167 *(PULONG)(NewStack + 8) = (NewStack + 24);
168 *(PULONG)(NewStack + 12) = ArgumentLength;
169
170 /* Save the exception list */
171 Teb = KeGetCurrentThread()->Teb;
172 ExceptionList = Teb->Tib.ExceptionList;
173
174 /* Jump to user mode */
175 *UserEsp = NewStack;
176 CallbackStatus = KiCallUserMode(Result, ResultLength);
177 if (CallbackStatus != STATUS_CALLBACK_POP_STACK)
178 {
179 /* Only restore the exception list if we didn't crash in ring 3 */
180 Teb->Tib.ExceptionList = ExceptionList;
181 CallbackStatus = STATUS_SUCCESS;
182 }
183 else
184 {
185 /* Otherwise, pop the stack */
186 OldStack = *UserEsp;
187 }
188
189 /* Read the GDI Batch count */
190 GdiBatchCount = Teb->GdiBatchCount;
191 }
192 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
193 {
194 /* Get the SEH exception */
195 _SEH2_YIELD(return _SEH2_GetExceptionCode());
196 }
197 _SEH2_END;
198
199 /* Check if we have GDI Batch operations */
200 if (GdiBatchCount)
201 {
202 *UserEsp -= 256;
203 KeGdiFlushUserBatch();
204 }
205
206 /* Restore stack and return */
207 *UserEsp = OldStack;
208 return CallbackStatus;
209 }
210
211 /* EOF */