Merging r37048, r37051, r37052, r37055 from the-real-msvc branch
[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 _SEH_DEFINE_LOCALS(KiCopyInfo)
20 {
21 volatile EXCEPTION_RECORD SehExceptRecord;
22 };
23
24 _SEH_FILTER(KiCopyInformation2)
25 {
26 _SEH_ACCESS_LOCALS(KiCopyInfo);
27
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;
33 }
34
35 /*++
36 * @name KiInitializeUserApc
37 *
38 * Prepares the Context for a User-Mode APC called through NTDLL.DLL
39 *
40 * @param Reserved
41 * Pointer to the Exception Frame on non-i386 builds.
42 *
43 * @param TrapFrame
44 * Pointer to the Trap Frame.
45 *
46 * @param NormalRoutine
47 * Pointer to the NormalRoutine to call.
48 *
49 * @param NormalContext
50 * Pointer to the context to send to the Normal Routine.
51 *
52 * @param SystemArgument[1-2]
53 * Pointer to a set of two parameters that contain untyped data.
54 *
55 * @return None.
56 *
57 * @remarks None.
58 *
59 *--*/
60 VOID
61 NTAPI
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)
68 {
69 CONTEXT Context;
70 ULONG_PTR Stack, AlignedEsp;
71 ULONG ContextLength;
72 EXCEPTION_RECORD SehExceptRecord;
73 _SEH_DECLARE_LOCALS(KiCopyInfo);
74
75 /* Don't deliver APCs in V86 mode */
76 if (TrapFrame->EFlags & EFLAGS_V86_MASK) return;
77
78 /* Save the full context */
79 Context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;
80 KeTrapFrameToContext(TrapFrame, ExceptionFrame, &Context);
81
82 /* Protect with SEH */
83 _SEH_TRY
84 {
85 /* Sanity check */
86 ASSERT((TrapFrame->SegCs & MODE_MASK) != KernelMode);
87
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;
92
93 /* Probe the stack */
94 ProbeForWrite((PVOID)Stack, AlignedEsp - Stack, 1);
95 ASSERT(!(Stack & 3));
96
97 /* Copy data into it */
98 RtlCopyMemory((PVOID)(Stack + (4 * sizeof(ULONG_PTR))),
99 &Context,
100 sizeof(CONTEXT));
101
102 /* Run at APC dispatcher */
103 TrapFrame->Eip = (ULONG)KeUserApcDispatcher;
104 TrapFrame->HardwareEsp = Stack;
105
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;
114
115 /* Sanitize EFLAGS */
116 TrapFrame->EFlags = Ke386SanitizeFlags(Context.EFlags, UserMode);
117
118 /* Check if thread has IOPL and force it enabled if so */
119 if (KeGetCurrentThread()->Iopl) TrapFrame->EFlags |= EFLAGS_IOPL;
120
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;
126 }
127 _SEH_EXCEPT(KiCopyInformation2)
128 {
129 /* Dispatch the exception */
130 _SEH_VAR(SehExceptRecord).ExceptionAddress = (PVOID)TrapFrame->Eip;
131 KiDispatchException(&SehExceptRecord,
132 ExceptionFrame,
133 TrapFrame,
134 UserMode,
135 TRUE);
136 }
137 _SEH_END;
138 }
139
140 /* PUBLIC FUNCTIONS **********************************************************/
141
142 /*
143 * @implemented
144 */
145 NTSTATUS
146 NTAPI
147 KeUserModeCallback(IN ULONG RoutineIndex,
148 IN PVOID Argument,
149 IN ULONG ArgumentLength,
150 OUT PVOID *Result,
151 OUT PULONG ResultLength)
152 {
153 ULONG_PTR NewStack, OldStack;
154 PULONG UserEsp;
155 NTSTATUS CallbackStatus = STATUS_SUCCESS;
156 PEXCEPTION_REGISTRATION_RECORD ExceptionList;
157 PTEB Teb;
158 ULONG GdiBatchCount = 0;
159 ASSERT(KeGetCurrentThread()->ApcState.KernelApcInProgress == FALSE);
160 ASSERT(KeGetPreviousMode() == UserMode);
161
162 /* Get the current user-mode stack */
163 UserEsp = KiGetUserModeStackAddress();
164 OldStack = *UserEsp;
165
166 /* Enter a SEH Block */
167 _SEH_TRY
168 {
169 /* Calculate and align the stack size */
170 NewStack = (OldStack - ArgumentLength) & ~3;
171
172 /* Make sure it's writable */
173 ProbeForWrite((PVOID)(NewStack - 6 * sizeof(ULONG_PTR)),
174 ArgumentLength + 6 * sizeof(ULONG_PTR),
175 sizeof(CHAR));
176
177 /* Copy the buffer into the stack */
178 RtlCopyMemory((PVOID)NewStack, Argument, ArgumentLength);
179
180 /* Write the arguments */
181 NewStack -= 24;
182 *(PULONG)NewStack = 0;
183 *(PULONG)(NewStack + 4) = RoutineIndex;
184 *(PULONG)(NewStack + 8) = (NewStack + 24);
185 *(PULONG)(NewStack + 12) = ArgumentLength;
186
187 /* Save the exception list */
188 Teb = KeGetCurrentThread()->Teb;
189 ExceptionList = Teb->Tib.ExceptionList;
190
191 /* Jump to user mode */
192 *UserEsp = NewStack;
193 CallbackStatus = KiCallUserMode(Result, ResultLength);
194 if (CallbackStatus != STATUS_CALLBACK_POP_STACK)
195 {
196 /* Only restore the exception list if we didn't crash in ring 3 */
197 Teb->Tib.ExceptionList = ExceptionList;
198 CallbackStatus = STATUS_SUCCESS;
199 }
200 else
201 {
202 /* Otherwise, pop the stack */
203 OldStack = *UserEsp;
204 }
205
206 /* Read the GDI Batch count */
207 GdiBatchCount = Teb->GdiBatchCount;
208 }
209 _SEH_HANDLE
210 {
211 /* Get the SEH exception */
212 CallbackStatus = _SEH_GetExceptionCode();
213 }
214 _SEH_END;
215 if (!NT_SUCCESS(CallbackStatus)) return CallbackStatus;
216
217 /* Check if we have GDI Batch operations */
218 if (GdiBatchCount)
219 {
220 *UserEsp -= 256;
221 KeGdiFlushUserBatch();
222 }
223
224 /* Restore stack and return */
225 *UserEsp = OldStack;
226 return CallbackStatus;
227 }
228
229 /* EOF */